/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/scalar.pmc 31911 2008-10-11T12:46:29.370039Z allison  $

=head1 NAME

src/pmc/scalar.pmc - Scalar Abstract Superclass

=head1 DESCRIPTION

These are the vtable functions for the scalar base PMC class

=head2 Methods

=over 4

=cut

*/

#include "parrot/parrot.h"
#include "pmc_bigint.h"


static PMC *
bitwise_left_shift_internal(PARROT_INTERP, PMC *self,
                            PMC *dest, INTVAL shift_amount)
{
    /* This handles both left and right shifts; the sign of shift_amount
       controls the direction, shifting left if positive and right if negative.
       If we get an error (which can only happen for left shifts) and
       PARROT_ERRORS_OVERFLOW_FLAG is set, then we throw an error, else the
       destination is promoted to a BigInt before shifting.  If dest == self,
       then the shift is done in place.  */
    const INTVAL base = VTABLE_get_integer(interp, self);
    INTVAL result     = 0;
    int overflow_p    = 0;

    /* Compute the shift. */
    if (shift_amount >= 8*INTVAL_SIZE) {
        /* Extreme left shift; no need to do an integer shift first. */
        overflow_p = 1;
    }
    else if (shift_amount >= 0) {
        /* Left shift. */
        result = base << shift_amount;
        overflow_p = (result >> shift_amount) != base;
    }
    else if (shift_amount > -8*INTVAL_SIZE) {
        /* Right shift.  By definition, this can never overflow. */
        result = base >> -shift_amount;
    }
    else {
        /* Extreme right shift. */
        result = 0;
    }

    /* Store the result. */
    if (! overflow_p) {
        /* If SELF is the destination, we won't want to create a
           new PMC, because then we won't have the result in SELF.
           Only create a new PMC if we aren't saving the result
           to SELF, or if they are both NULL */
        if (dest != self || dest == NULL)
            dest = pmc_new(interp, VTABLE_type(interp, self));

        VTABLE_set_integer_native(interp, dest, result);
    }
    else if (PARROT_ERRORS_test(interp, PARROT_ERRORS_OVERFLOW_FLAG)) {
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_ERR_OVERFLOW,
            "Integer overflow");
    }
    else {
        /* Overflow; must promote dest to BigInt, and do a BigInt shift. */
        if (self == dest)
            VTABLE_morph(interp, self, enum_class_BigInt);
        else
            dest = pmc_new(interp, enum_class_BigInt);

        VTABLE_set_integer_native(interp, dest, base);
        Parrot_BigInt_bitwise_shl_int(interp, dest, shift_amount, dest);
    }

    return dest;
}

pmclass scalar {

/*

=item C<void morph(INTVAL type)>

Morphs the scalar to the specified type.

=cut

*/

    VTABLE void morph(INTVAL type) {
        if (SELF->vtable->base_type == type)
            return;
        pmc_reuse(INTERP, SELF, type, 0);
    }
/*

=item C<void assign_pmc(PMC *value)>

Sets the PMC C<*value>, calling the appropriate C<set_*> method
according to the type of C<*value>.

=cut

*/

    VTABLE void assign_pmc(PMC *value) {
        STRING * const s_int = CONST_STRING(INTERP, "Integer");
        STRING        *s_num;
        STRING        *s_str;

        if (SELF->vtable->base_type == enum_class_Boolean) {
            /* doesn't morph */
            SELF.morph(value->vtable->base_type);
        }

        if (value->vtable->base_type == enum_class_Undef) {
            SELF.morph(value->vtable->base_type);
            return;
        }

        if (VTABLE_isa(INTERP, value, s_int)) {
            const INTVAL v = VTABLE_get_integer(INTERP, value);
            SELF.set_integer_native(v);
            return;
        }

        s_num = CONST_STRING(INTERP, "Float");

        if (VTABLE_isa(INTERP, value, s_num)) {
            const FLOATVAL v = VTABLE_get_number(INTERP, value);
            SELF.set_number_native(v);
            return;
        }

        s_str = CONST_STRING(INTERP, "String");

        if (VTABLE_isa(INTERP, value, s_str)) {
            STRING * const v = VTABLE_get_string(INTERP, value);
            SELF.set_string_native(v);
            return;
        }
        SELF.morph(enum_class_Ref);
        SELF.set_pmc(value);
    }

/*

=item C<PMC *clone()>

Creates and returns a clone of the scalar.

=cut

*/

    VTABLE PMC *clone() {
        PMC * const dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));
        memcpy(&PMC_union(dest), &PMC_union(SELF), sizeof (UnionVal));
        return dest;
    }


/*

=back

=head1 Mathematical Methods

=over 4

=item C<PMC *subtract(PMC *value, PMC *dest)>

=cut

*/

/*

=item C<void add(PMC *value, PMC *dest)>

=item C<void add_int(INTVAL value, PMC *dest)>

=item C<void add_float(FLOATVAL value, PMC *dest)>

Adds C<value> to the number and returns the result in C<*dest>.
If C<dest> is NULL it's created.

=item C<void i_add(PMC *value)>

=item C<void i_add(INTVAL value)>

=item C<void i_add(FLOATVAL value)>

Adds C<value> to C<SELF> inplace.

=cut

*/

    MULTI PMC *add(Complex value, PMC *dest) {
        const FLOATVAL a = SELF.get_number();

        dest = pmc_new(INTERP, VTABLE_type(INTERP, value));

        VTABLE_set_number_native(INTERP, dest,
                a + VTABLE_get_number_keyed_int(INTERP, value, 0));
        VTABLE_set_number_keyed_int(INTERP, dest, 1,
                VTABLE_get_number_keyed_int(INTERP, value, 1));
        return dest;
    }

    MULTI PMC *add(DEFAULT value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                SELF.get_number() + VTABLE_get_number(INTERP, value));
        return dest;
    }

    VTABLE PMC *add_int(INTVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                SELF.get_number() + (FLOATVAL)value);
        return dest;
    }

    VTABLE PMC *add_float(FLOATVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                SELF.get_number() + value);
        return dest;
    }

    MULTI void i_add(Complex value) {
        const FLOATVAL a = SELF.get_number();

        VTABLE_morph(INTERP, SELF, value->vtable->base_type);
        VTABLE_set_number_native(INTERP, SELF,
                a + VTABLE_get_number_keyed_int(INTERP, value, 0));
        VTABLE_set_number_keyed_int(INTERP, SELF, 1,
                VTABLE_get_number_keyed_int(INTERP, value, 1));
    }

    MULTI void i_add(DEFAULT value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() + VTABLE_get_number(INTERP, value));
    }

    VTABLE void i_add_int(INTVAL value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() + (FLOATVAL)value);
    }

    VTABLE void i_add_float(FLOATVAL value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() + value);
    }

/*

=item C<PMC *subtract(PMC *value, PMC *dest)>

=item C<PMC *subtract_int(INTVAL value, PMC *dest)>

=item C<PMC *subtract_float(FLOATVAL value, PMC *dest)>

Subtracts C<value> from the number and returns the result in C<*dest>.
If C<dest> doesn't exist a new C<Float> is created.

=item C<void i_subtract(PMC *value)>

=item C<void i_subtract_int(INTVAL value)>

=item C<void i_subtract_float(FLOATVAL value)>

Subtracts C<value> from C<SELF> inplace.

=cut

*/

    MULTI PMC *subtract(Complex value, PMC *dest) {
        const FLOATVAL a = SELF.get_number();
        if (dest)
            VTABLE_morph(INTERP, dest, value->vtable->base_type);
        else
            dest = pmc_new(INTERP, VTABLE_type(INTERP, value));

        VTABLE_set_number_native(INTERP, dest,
                a - VTABLE_get_number_keyed_int(INTERP, value, 0));
        VTABLE_set_number_keyed_int(INTERP, dest, 1,
                -VTABLE_get_number_keyed_int(INTERP, value, 1));
        return dest;
    }

    MULTI PMC *subtract(DEFAULT value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));
        VTABLE_set_number_native(INTERP, dest,
                SELF.get_number() - VTABLE_get_number(INTERP, value));
        return dest;
    }

    VTABLE PMC *subtract_int(INTVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                SELF.get_number() - (FLOATVAL)value);
        return dest;
    }

    VTABLE PMC *subtract_float(FLOATVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                SELF.get_number() - value);
        return dest;
    }

    MULTI void i_subtract(Complex value) {
        const FLOATVAL a = SELF.get_number();

        VTABLE_morph(INTERP, SELF, value->vtable->base_type);
        VTABLE_set_number_native(INTERP, SELF,
                a - VTABLE_get_number_keyed_int(INTERP, value, 0));
        VTABLE_set_number_keyed_int(INTERP, SELF, 1,
                -VTABLE_get_number_keyed_int(INTERP, value, 1));
    }

    MULTI void i_subtract(DEFAULT value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() - VTABLE_get_number(INTERP, value));
    }

    VTABLE void i_subtract_int(INTVAL value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() - (FLOATVAL)value);
    }

    VTABLE void i_subtract_float(FLOATVAL value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() - value);
    }

/*

=item C<PMC *multiply(PMC *value, PMC *dest)>

=item C<PMC *multiply_int(INTVAL value, PMC *dest)>

=item C<PMC *multiply_float(FLOATVAL value, PMC *dest)>

Multiplies the number by C<value> and returns the result in C<*dest>.

=cut

*/

    MULTI PMC *multiply(Complex value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED, "TODO mul<Float, Complex>");
    }

    MULTI PMC *multiply(DEFAULT value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
            SELF.get_number() * VTABLE_get_number(INTERP, value));
        return dest;
    }

    VTABLE PMC *multiply_int(INTVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
            SELF.get_number() * value);
        return dest;
    }

    VTABLE PMC *multiply_float(FLOATVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
            SELF.get_number() * value);
        return dest;
    }

    MULTI void i_multiply(Complex value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "TODO i_mul<Float, Complex>");
    }

    MULTI void i_multiply(DEFAULT value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() * VTABLE_get_number(INTERP, value));
    }

    VTABLE void i_multiply_int(INTVAL value) {
        VTABLE_set_number_native(INTERP, SELF,
            SELF.get_number() * (FLOATVAL)value);
    }

    VTABLE void i_multiply_float(FLOATVAL value) {
        VTABLE_set_number_native(INTERP, SELF, SELF.get_number() * value);
    }

/*

=item C<PMC *divide(PMC *value, PMC *dest)>

=item C<PMC *divide_int(INTVAL value, PMC *dest)>

=item C<PMC *divide_float(FLOATVAL value, PMC *dest)>

Divides the number by C<value> and returns the result in C<*dest>.

=item C<void i_divide(PMC *value)>

=item C<void i_divide_int(INTVAL value)>

=item C<void i_divide_float(FLOATVAL value)>

Divides C<SELF> by C<value> inplace.

=cut

*/

    MULTI PMC *divide(PMC *value, PMC *dest) {
        FLOATVAL d = VTABLE_get_number(INTERP, value);

        if (FLOAT_IS_ZERO(d))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest, SELF.get_number() / d);
        return dest;
    }

    VTABLE PMC *divide_int(INTVAL value, PMC *dest) {
        if (value == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest, SELF.get_number() / value);
        return dest;
    }

    VTABLE PMC *divide_float(FLOATVAL value, PMC *dest) {
        if (FLOAT_IS_ZERO(value))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest, SELF.get_number() / value);
        return dest;
    }

    VTABLE void i_divide(PMC *value) {
        FLOATVAL d = VTABLE_get_number(INTERP, value);

        if (FLOAT_IS_ZERO(d))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        VTABLE_set_number_native(INTERP, SELF, SELF.get_number() / d);
    }

    VTABLE void i_divide_int(INTVAL value) {
        if (value == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        VTABLE_set_number_native(INTERP, SELF, SELF.get_number() / value);
    }

    VTABLE void i_divide_float(FLOATVAL value) {
        if (FLOAT_IS_ZERO(value))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        VTABLE_set_number_native(INTERP, SELF, SELF.get_number() / value);
    }

/*

=item C<PMC *floor_divide(PMC *value, PMC *dest)>

=item C<PMC *floor_divide_int(INTVAL value, PMC *dest)>

=item C<PMC *floor_divide_float(FLOATVAL value, PMC *dest)>

Divides the number by C<value> and returns the result in C<*dest>.

=item C<void i_floor_divide(PMC *value)>

=item C<void i_floor_divide_int(INTVAL value)>

=item C<void i_floor_divide_float(FLOATVAL value)>

Divides C<SELF> by C<value> inplace.

=cut

*/

    MULTI PMC *floor_divide(PMC *value, PMC *dest) {
        FLOATVAL d = VTABLE_get_number(INTERP, value);

        if (FLOAT_IS_ZERO(d))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));
        d    = floor(SELF.get_number() / d);

        VTABLE_set_integer_native(INTERP, dest, (INTVAL)d);
        return dest;
    }

    VTABLE PMC *floor_divide_int(INTVAL value, PMC *dest) {
        if (value == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                floor(SELF.get_number() / value));
        return dest;
    }

    VTABLE PMC *floor_divide_float(FLOATVAL value, PMC *dest) {
        if (FLOAT_IS_ZERO(value))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                floor(SELF.get_number() / value));
        return dest;
    }

    VTABLE void i_floor_divide(PMC *value) {
        FLOATVAL d = VTABLE_get_number(INTERP, value);

        if (FLOAT_IS_ZERO(d))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        VTABLE_set_number_native(INTERP, SELF,
                floor(SELF.get_number() / d));
    }

    VTABLE void i_floor_divide_int(INTVAL value) {
        if (value == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        VTABLE_set_number_native(INTERP, SELF,
                floor(SELF.get_number() / value));
    }

    VTABLE void i_floor_divide_float(FLOATVAL value) {
        if (FLOAT_IS_ZERO(value))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float division by zero");

        VTABLE_set_number_native(INTERP, SELF,
                floor(SELF.get_number() / value));
    }

/*

=item C<PMC *modulus(PMC *value, PMC *dest)>

=item C<PMC *modulus(INTVAL value, PMC *dest)>

=item C<PMC *modulus(FLOATVAL value, PMC *dest)>

Calculates the value of corrected C<mod> C<value> and returns
the result in C<dest>. See also ops/math.ops.

=item C<void i_modulus(PMC *value)>

=item C<void i_modulus(INTVAL value)>

=item C<void i_modulus(FLOATVAL value)>

Calculates modulus inplace

=cut

*/

    MULTI PMC *modulus(PMC *value, PMC *dest) {
        FLOATVAL d = VTABLE_get_number(INTERP, value);

        if (FLOAT_IS_ZERO(d))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float modulus by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                floatval_mod(SELF.get_number(), d));
        return dest;
    }

    VTABLE PMC *modulus_int(INTVAL value, PMC *dest) {
        if (value == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float modulus by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                floatval_mod(SELF.get_number(), (FLOATVAL)value));
        return dest;
    }

    VTABLE PMC *modulus_float(FLOATVAL value, PMC *dest) {
        if (FLOAT_IS_ZERO(value))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float modulus by zero");

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                floatval_mod(SELF.get_number(), value));
        return dest;
    }

    VTABLE void i_modulus(PMC *value) {
        FLOATVAL d = VTABLE_get_number(INTERP, value);

        if (FLOAT_IS_ZERO(d))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float modulus by zero");

        VTABLE_set_number_native(INTERP, SELF,
                floatval_mod(SELF.get_number(), d));
    }

    VTABLE void i_modulus_int(INTVAL value) {
        if (value == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float modulus by zero");

        VTABLE_set_number_native(INTERP, SELF,
                floatval_mod(SELF.get_number(), (FLOATVAL)value));
    }

    VTABLE void i_modulus_float(FLOATVAL value) {
        if (FLOAT_IS_ZERO(value))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_DIV_BY_ZERO,
                "float modulus by zero");

        VTABLE_set_number_native(INTERP, SELF,
                floatval_mod(SELF.get_number(), value));
    }

/*

=item C<PMC *pow(PMC *value, PMC *dest)>

=item C<PMC *pow_int(INTVAL value, PMC *dest)>

=item C<PMC *pow_float(FLOATVAL value, PMC *dest)>

Calculates  C<SELF pow value> and returns
the result in C<dest>. See also ops/math.ops.

=item C<void i_pow(PMC *value)>

=item C<void i_pow_int(INTVAL value)>

=item C<void i_pow_float(FLOATVAL value)>

Calculates pow inplace

=cut

*/

    MULTI PMC *pow(PMC *value, PMC *dest) {
        const FLOATVAL d = VTABLE_get_number(INTERP, value);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest, pow(SELF.get_number(), d));
        return dest;
    }

    VTABLE PMC *pow_int(INTVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                pow(SELF.get_number(), (double)value));

        return dest;
    }

    VTABLE PMC *pow_float(FLOATVAL value, PMC *dest) {
        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_number_native(INTERP, dest,
                pow(SELF.get_number(), value));
        return dest;
    }

    VTABLE void i_pow(PMC *value) {
        const FLOATVAL d = VTABLE_get_number(INTERP, value);

        VTABLE_set_number_native(INTERP, SELF,
                pow(SELF.get_number(), d));
    }

    VTABLE void i_pow_int(INTVAL value) {
        VTABLE_set_number_native(INTERP, SELF,
                pow(SELF.get_number(), (float)value));
    }

    VTABLE void i_pow_float(FLOATVAL value) {
        VTABLE_set_number_native(INTERP, SELF,
                pow(SELF.get_number(), value));
    }

/*

=item C<PMC *neg(PMC *dest)>

=item C<void i_neg()>

Set C<dest> to the negated value of C<SELF>.

=cut

*/

    VTABLE PMC *neg(PMC *dest) {
        const INTVAL a = -SELF.get_integer();

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, a);
        return dest;
    }

    VTABLE void i_neg() {
        VTABLE_set_integer_native(INTERP, SELF, -SELF.get_integer());
    }

/*

=back

=head2 Bitwise Methods

=over 4

=item C<PMC *bitwise_or(PMC *value, PMC *dest)>

=item C<PMC *bitwise_or_int(INTVAL value, PMC *dest)>

Returns in C<*dest> the bitwise C<OR> of the scalar and C<value>.

=item C<void i_bitwise_or(PMC *value)>

=item C<void i_bitwise_or_int(INTVAL value)>

Inplace bitwise or.

=cut

*/

    VTABLE PMC *bitwise_or(PMC *value, PMC *dest) {
        const INTVAL result = SELF.get_integer() | VTABLE_get_integer(INTERP, value);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }


    VTABLE PMC *bitwise_or_int(INTVAL value, PMC *dest) {
        const INTVAL result = SELF.get_integer() | value;

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }

    VTABLE void i_bitwise_or(PMC *value) {
        const INTVAL result =
            SELF.get_integer() | VTABLE_get_integer(INTERP, value);

        SELF.set_integer_native(result);
    }


    VTABLE void i_bitwise_or_int(INTVAL value) {
        const INTVAL result = SELF.get_integer() | value;
        SELF.set_integer_native(result);
    }

/*

=item C<PMC *bitwise_and(PMC *value, PMC *dest)>

=item C<PMC *bitwise_and_int(INTVAL value, PMC *dest)>

Returns in C<*dest> the bitwise C<AND> of the scalar and C<value>.

=item C<void i_bitwise_and(PMC *value)>

=item C<void i_bitwise_and_int(INTVAL value)>

Inplace bitwise and.

=cut

*/

    VTABLE PMC *bitwise_and(PMC *value, PMC *dest) {
        const INTVAL result =
            SELF.get_integer() & VTABLE_get_integer(INTERP, value);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }

    VTABLE PMC *bitwise_and_int(INTVAL value, PMC *dest) {
        const INTVAL result = SELF.get_integer() & value;

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }

    VTABLE void i_bitwise_and(PMC *value) {
        const INTVAL result =
            SELF.get_integer() & VTABLE_get_integer(INTERP, value);
        SELF.set_integer_native(result);
    }


    VTABLE void i_bitwise_and_int(INTVAL value) {
        const INTVAL result = SELF.get_integer() & value;
        SELF.set_integer_native(result);
    }

/*

=item C<void bitwise_xor(PMC *value, PMC *dest)>

=item C<void bitwise_xor_int(INTVAL value, PMC *dest)>

Returns in C<*dest> the bitwise C<XOR> of the scalar and C<*value>.

=item C<void i_bitwise_xor(PMC *value)>

=item C<void i_bitwise_xor_int(INTVAL value)>

Inplace bitwise and.

=cut

*/

    MULTI PMC *bitwise_xor(PMC *value, PMC *dest) {
        const INTVAL result =
            SELF.get_integer() ^ VTABLE_get_integer(INTERP, value);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }

    MULTI PMC *bitwise_xor_int(INTVAL value, PMC *dest) {
        const INTVAL result = SELF.get_integer() ^ value;

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }

    VTABLE void i_bitwise_xor(PMC *value) {
        const INTVAL result =
            SELF.get_integer() ^ VTABLE_get_integer(INTERP, value);
        SELF.set_integer_native(result);
    }

    VTABLE void i_bitwise_xor_int(INTVAL value) {
        const INTVAL result = SELF.get_integer() ^ value;
        SELF.set_integer_native(result);
    }

/*

=item C<PMC *bitwise_not(PMC *dest)>

=item C<void i_bitwise_not()>

Returns in C<*dest> the bitwise negation of the scalar and C<value>.

=cut

*/

    VTABLE PMC *bitwise_not(PMC *dest) {
        const INTVAL a = ~SELF.get_integer();

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, a);
        return dest;
    }

    VTABLE void i_bitwise_not() {
        VTABLE_set_integer_native(INTERP, SELF, ~SELF.get_integer());
    }

/*

=item C<PMC *bitwise_shr(PMC *value, PMC *dest)>

=item C<PMC *bitwise_shr_int(INTVAL value, PMC *dest)>

Returns in C<*dest> the arithmetic shift right of the scalar by C<value>.
A negative C<value> shifts left.
The destination may become a C<BigInt> as a result (but only if the shift amount
is less than zero).

=item C<void i_bitwise_shr(PMC *value)>

=item C<void i_bitwise_shr_int(INTVAL value)>

Inplace shift right.  A negative C<value> shifts left.
SELF may become a C<BigInt> as a result (but only if the shift amount is less
than zero).

=cut

*/

    VTABLE PMC *bitwise_shr(PMC *value, PMC *dest) {
        return bitwise_left_shift_internal(INTERP, SELF, dest,
                                           -VTABLE_get_integer(INTERP, value));
    }

    VTABLE PMC *bitwise_shr_int(INTVAL value, PMC *dest) {
        return bitwise_left_shift_internal(INTERP, SELF, dest, -value);
    }

    VTABLE void i_bitwise_shr(PMC *value) {
        bitwise_left_shift_internal(INTERP, SELF, SELF,
                                    -VTABLE_get_integer(INTERP, value));
    }

    VTABLE void i_bitwise_shr_int(INTVAL value) {
        bitwise_left_shift_internal(INTERP, SELF, SELF, -value);
    }

/*

=item C<PMC *bitwise_lsr(PMC *value, PMC *dest)>

=item C<PMC *bitwise_lsr_int(INTVAL value, PMC *dest)>

Returns in C<*dest> the logical shift right of the scalar by C<*value>.

=item C<void i_bitwise_lsr(PMC *value)>

=item C<void i_bitwise_lsr_int(INTVAL value)>

Inplace shift right.

=cut

*/

    VTABLE PMC *bitwise_lsr(PMC *value, PMC *dest) {
        const INTVAL result = (UINTVAL)SELF.get_integer() >>
            VTABLE_get_integer(INTERP, value);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }

    VTABLE PMC *bitwise_lsr_int(INTVAL value, PMC *dest) {
        const INTVAL result = (UINTVAL)SELF.get_integer() >> value;

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_integer_native(INTERP, dest, result);
        return dest;
    }

    VTABLE void i_bitwise_lsr(PMC *value) {
        const INTVAL result = (UINTVAL)SELF.get_integer() >>
                VTABLE_get_integer(INTERP, value);
        SELF.set_integer_native(result);
    }

    VTABLE void i_bitwise_lsr_int(INTVAL value) {
        const INTVAL result = (UINTVAL)SELF.get_integer() >> value;
        SELF.set_integer_native(result);
    }

/*

=item C<PMC *bitwise_shl(PMC *value, PMC *dest)>

=item C<PMC *bitwise_shl_int(INTVAL value, PMC *dest)>

Returns in C<*dest> the shift left of the scalar by C<*value>.  A negative
C<value> shifts right.  The destination may become a C<BigInt> as a result.

=item C<void i_bitwise_shl(PMC *value)>

=item C<void i_bitwise_shl_int(INTVAL value)>

Inplace shift left.  A negative C<value> shifts right.  SELF may become a
C<BigInt> as a result.

=cut

*/

    VTABLE PMC *bitwise_shl(PMC *value, PMC *dest) {
        return bitwise_left_shift_internal(INTERP, SELF, dest,
                                           VTABLE_get_integer(INTERP, value));
    }

    VTABLE PMC *bitwise_shl_int(INTVAL value, PMC *dest) {
        return bitwise_left_shift_internal(INTERP, SELF, dest, value);
    }

    VTABLE void i_bitwise_shl(PMC *value) {
        bitwise_left_shift_internal(INTERP, SELF, SELF,
                                    VTABLE_get_integer(INTERP, value));
    }

    VTABLE void i_bitwise_shl_int(INTVAL value) {
        bitwise_left_shift_internal(INTERP, SELF, SELF, value);
    }

/*

=back

=head2 String Methods

=over 4

=item C<PMC *concatenate(PMC *value, PMC *dest)>

=item C<PMC *concatenate_str(STRING *value, PMC *dest)>

Returns in C<*dest> the result of concatenating the scalar and C<*value>.

=item C<void concatenate(PMC *value)>

=item C<void concatenate_str(STRING *value)>

Concatenate the string C<value> in place.

=cut

*/

    VTABLE PMC *concatenate(PMC *value, PMC *dest) {
        STRING * const s = string_concat(INTERP, SELF.get_string(),
            VTABLE_get_string(INTERP, value), 0);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_string_native(INTERP, dest, s);
        return dest;
    }

    VTABLE PMC *concatenate_str(STRING *value, PMC *dest) {
        STRING * const s = string_concat(INTERP,
            SELF.get_string(), value, 0);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_string_native(INTERP, dest, s);
        return dest;
    }

    VTABLE void i_concatenate(PMC *value) {
        STRING * const s = SELF.get_string();
        STRING * const v = VTABLE_get_string(INTERP, value);
        SELF.set_string_native(string_append(INTERP, s, v));
    }

    VTABLE void i_concatenate_str(STRING *value) {
        STRING * const s = SELF.get_string();
        SELF.set_string_native(string_append(INTERP, s, value));
    }

/*

=item C<PMC *repeat(PMC *value, PMC *dest)>

=item C<PMC *repeat_int(INTVAL value, PMC *dest)>

Returns in C<*dest> the result of repeating the scalar C<value> times.

=item C<void i_repeat(PMC *value)>

=item C<void i_repeat_int(INTVAL value)>

Repeat the string C<SELF> in place C<value> times.

=cut

*/

    VTABLE PMC *repeat(PMC *value, PMC *dest) {
        STRING * const s = SELF.get_string();
        const UINTVAL  n = (UINTVAL)VTABLE_get_integer(INTERP, value);

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_string_native(INTERP, dest,
            string_repeat(INTERP, s, n, NULL));
        return dest;
    }

    VTABLE PMC *repeat_int(INTVAL value, PMC *dest) {
        STRING * const s = SELF.get_string();
        const UINTVAL  n = value;

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_string_native(INTERP, dest,
            string_repeat(INTERP, s, n, NULL));
        return dest;
    }


    VTABLE void i_repeat(PMC *value) {
        STRING * const s = SELF.get_string();
        const UINTVAL  n = (UINTVAL)VTABLE_get_integer(INTERP, value);
        SELF.set_string_native(string_repeat(INTERP, s, n, NULL));
    }

    VTABLE void i_repeat_int(INTVAL value) {
        STRING * const s = SELF.get_string();
        const UINTVAL  n = value;
        SELF.set_string_native(string_repeat(INTERP, s, n, NULL));
    }



/*

=back

=head2 Compare Methods

=over 4

=item C<INTVAL cmp_num(PMC *value)>

Returns the result of comparing the floating-point values of the scalar
and C<*value>.

=cut

*/

    MULTI INTVAL cmp_num(PMC *value) {
        /* XXX - Floating-point precision errors possible? */
        FLOATVAL diff = VTABLE_get_number(INTERP, value) -
                        SELF.get_number();

        return FLOAT_IS_ZERO(diff) ? 0 : diff < 0.0 ? -1 : 1;
    }

/*

=item C<INTVAL cmp_string(PMC *value)>

Returns the result of comparing the string values of the scalar and
C<*value>.

=cut

*/

    MULTI INTVAL cmp_string(PMC *value) {
        return string_compare(INTERP, SELF.get_string(),
            VTABLE_get_string(INTERP, value));
    }

/*

=item C<INTVAL is_equal(PMC *value)>

Returns whether the PMC is equal to C<*value>.

=cut

*/

    MULTI INTVAL is_equal(PMC *value) {
        return (SELF->vtable         == value->vtable
            &&  PMC_struct_val(SELF) == PMC_struct_val(value)
            &&  PMC_data(SELF)       == PMC_data(value));
    }

/*

=item C<INTVAL is_equal_num(PMC *value)>

Returns whether the PMC is numerically equal to C<*value>.

=cut

*/

    MULTI INTVAL is_equal_num(PMC *value) {
        return (VTABLE_get_number(INTERP, SELF) ==
                VTABLE_get_number(INTERP, value));
    }

/*

=item C<INTVAL is_equal_string(PMC *value)>

Returns whether the PMC has string equality with C<*value>.

=cut

*/

    MULTI INTVAL is_equal_string(PMC *value) {
        return (0 == string_equal(INTERP, VTABLE_get_string(INTERP, SELF),
                                          VTABLE_get_string(INTERP, value)));
    }


/*

=back

=head2 Logical Methods

=over 4

=item C<PMC *logical_or(PMC *value, PMC *dest)>

Returns the result of the logical C<OR> of C<SELF> and C<value>, i.e. returns
C<SELF> it is true or C<value>: C<dest> is alway ignored.

=cut

*/

    VTABLE PMC *logical_or(PMC *value, PMC *dest) {
        if (SELF.get_bool())
            return SELF;

        return value;
    }

/*

=item C< PMC *logical_and(PMC *value, PMC *dest)>

Returns the result of the logical C<AND> of C<SELF> and C<value>, i.e.
returns C<value> if C<SELF> is true else C<SELF>. C<dest> is always ignored.

=cut

*/

    VTABLE PMC *logical_and(PMC *value, PMC *dest) {
        if (SELF.get_bool())
            return value;

        return SELF;
    }

/*

=item C<PMC *logical_xor(PMC *value, PMC *dest)>

Returns the result of the logical C<XOR> of C<SELF> and C<*value>.

=cut

*/

    VTABLE PMC *logical_xor(PMC *value, PMC *dest) {
        const INTVAL my_bool    = SELF.get_bool();
        const INTVAL value_bool = VTABLE_get_bool(INTERP, value);

        if (my_bool && ! value_bool)
            return SELF;
        else if (value_bool && ! my_bool)
            return value;

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));
        VTABLE_set_bool(INTERP, dest, 0);
        return dest;
    }

/*

=item C<PMC *logical_not(PMC *dest)>

=item C<void i_logical_not()>

Returns in C<*dest> the result of the logical negation of the scalar and
C<*value>.

=cut

*/

    VTABLE PMC *logical_not(PMC *dest) {
        const INTVAL a = ! SELF.get_bool();

        dest = pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        VTABLE_set_bool(INTERP, dest, a);
        return dest;
    }

    VTABLE void i_logical_not() {
        VTABLE_set_bool(INTERP, SELF, ! SELF.get_bool());
    }

/*

=item C<INTVAL defined()>

Always returns true.

=cut

*/

    VTABLE INTVAL defined() {
        return 1;
    }

/*

=item C<STRING *substr_str(INTVAL offset, INTVAL length)>

Returns the substring of length C<length> of the scalar starting at
C<offset>.

=cut

*/

    VTABLE STRING *substr_str(INTVAL offset, INTVAL length) {
        return string_substr(INTERP, VTABLE_get_string(INTERP, SELF),
            offset, length, NULL, 0);
    }

/*

=item C<void share_ro()>

Sets this PMC as shared and read-only.

=cut

*/
    VTABLE PMC *share_ro() {
        PMC         *ret  = pt_shared_fixup(INTERP, SELF);
        PMC * const _true = pmc_new(INTERP, enum_class_Integer);

        VTABLE_set_integer_native(INTERP, _true, 1);

        /* first set readonly */
        VTABLE_setprop(INTERP, ret, CONST_STRING(INTERP, "_ro"), _true);

        /* now share; we add a PMC_EXT so we can identify the owning interp */
        add_pmc_sync(INTERP, ret);
        PObj_is_PMC_shared_SET(ret);

        /* XXX FIXME workaround lack of metadata sharing*/
        PMC_metadata(SELF) = NULL;

        return ret;
    }

}

/*

=back

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
