/*
Copyright (C) 2003-2006, The Perl Foundation.
$Id: integer.pmc 16275 2006-12-28 01:20:44Z particle $

=head1 NAME

src/pmc/integer.pmc - Integer PMC class

=head1 DESCRIPTION

C<Integer> provides an integer for languages
that want a value-restricted integer type without going to an I
register.

=head2 Functions

=over 4

=cut

*/

#include "parrot/parrot.h"
#include <assert.h>

/* XXX create MMD headers to in Pmc2c.pm */
extern INTVAL Parrot_BigInt_is_equal_BigInt(Interp*, PMC*, PMC*);
extern PMC* Parrot_BigInt_multiply_int(Interp *interp,
        PMC* pmc, INTVAL value, PMC* dest) ;

static PMC*
overflow(Interp *interp, PMC *self, INTVAL b, PMC *dest, int mmd)
{
    const INTVAL a = VTABLE_get_integer(interp, self);

    if (PARROT_ERRORS_test(interp,PARROT_ERRORS_OVERFLOW_FLAG)) {
        real_exception(interp, NULL, ERR_OVERFLOW,
                "Integer overflow");
    }
    if (self == dest) {
        /* TODO preserve type system */
        VTABLE_morph(interp, self, enum_class_BigInt);
        VTABLE_set_integer_native(interp, self, a);
        return mmd_dispatch_p_pip(interp, self, b, dest, mmd);
    }
    else {
        PMC * const temp = VTABLE_get_bignum(interp, self);
        return mmd_dispatch_p_pip(interp, temp, b, dest, mmd);
    }
}

static PMC*
overflow_p(Interp *interp, PMC *self, PMC *val, PMC *dest, int mmd)
{
    const INTVAL a = VTABLE_get_integer(interp, self);

    if (PARROT_ERRORS_test(interp,PARROT_ERRORS_OVERFLOW_FLAG)) {
        real_exception(interp, NULL, ERR_OVERFLOW,
                "Integer overflow");
    }
    if (self == dest) {
        VTABLE_morph(interp, self, enum_class_BigInt);
        VTABLE_set_integer_native(interp, self, a);
        return mmd_dispatch_p_ppp(interp, self, val, dest, mmd);
    }
    else {
        PMC * const temp = VTABLE_get_bignum(interp, self);
        return mmd_dispatch_p_ppp(interp, temp, val, dest, mmd);
    }
}

pmclass Integer extends scalar does integer does scalar {

/*
=item C<PMC instantiate(PMC* sig)>

Create a new Integer with arguments passed according to pdd03.


=item C<void init()>

Initializes the integer with a default value of C<0>.

=cut

*/
    PMC* instantiate(PMC* sig) {
        PMC* const ret = new_pmc_header(INTERP, 0);
        opcode_t *arg_op;
        INTVAL init = 0;

        ret->vtable = INTERP->vtables[SELF->vtable->base_type];
        arg_op = interp->current_args;
        if (SIG_ELEMS(sig) == 2) {
            switch (SIG_ITEM(sig, 1)) {
                case PARROT_ARG_I:
                    init = REG_INT(arg_op[3]);
                    break;
                case PARROT_ARG_IC:
                    init = arg_op[3];
                    break;
                default:
                    internal_exception(1,
                            "Integer.instantiate: unhandled initializer");
                    break;
            }
        }
        PMC_int_val(ret) = init;
        return ret;
    }

    void init() {
        PMC_int_val(SELF) = 0;
    }

/*

=item C<PMC new_from_string(STRING *rep)>

Class method to construct an Integer from the string representation C<rep>.

=cut

*/
    PMC* new_from_string(STRING *rep, INTVAL flags) {
        PMC *res;
        const INTVAL type = SELF->vtable->base_type;

        if (flags & PObj_constant_FLAG)
            res = constant_pmc_new(INTERP, type);
        else
            res = pmc_new(INTERP, type);
        /* TODO bigint overflow */
        PMC_int_val(res) = string_to_int(INTERP, rep);
        return res;
    }

/*

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

Sets the value of the integer to the value in C<*value>.

=cut

*/

    void set_pmc(PMC *value) {
        PMC_int_val(SELF) = VTABLE_get_integer(INTERP, value);
    }

/*

=item C<void share()>



=cut

*/
    void share() {
        /*
         * assume that the access to a long is atomic.
         * integers are most often (1) equal to C longs,
         * not C ints, and this makes a difference in 64-bit
         * platforms where longs are 64-bit but ints are 32-bit.
         * (1) Not equal when integers have been configured
         *     to be software-emulated long longs.
         */
        if (sizeof (INTVAL) != sizeof (long))
            SUPER();
    }

/*

=item C<INTVAL get_integer()>

Returns the integer value of the Integer.

=cut

*/
    INTVAL get_integer() {
        return PMC_int_val(SELF);
    }

/*

=item C<INTVAL get_bool()>

Returns the boolean value of the Integer.

=cut

*/
    INTVAL get_bool() {
        return PMC_int_val(SELF) ? 1 : 0;
    }

/*

=item C<FLOATVAL get_number()>

Returns the floating-point value of the integer.

=cut

*/
    FLOATVAL get_number() {
        return (FLOATVAL)PMC_int_val(SELF);
    }

/*

=item C<PMC *get_bignum()>

Return a new BigInt PMC with the value of C<SELF>.

=cut

*/
    PMC* get_bignum() {
        const INTVAL val = DYNSELF.get_integer();
        PMC * const ret = pmc_new(INTERP, enum_class_BigInt);
        VTABLE_set_integer_native(INTERP, ret, val);
        return ret;
    }

/*

=item C<STRING *get_string()>

Returns the string value of the integer.

=cut

*/
    STRING* get_string() {
        return string_from_int(INTERP, PMC_int_val(SELF));
    }
    STRING* get_repr() {
        return string_from_int(INTERP, PMC_int_val(SELF));
    }

/*

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

=item C<void set_integer_native(INTVAL value)>

Sets the value of the integer to the value of the C<Integer> C<*value>.

=cut

*/
    void set_integer_same(PMC * value) {
        PMC_int_val(SELF) = PMC_int_val(value);
    }

    void set_integer_native(INTVAL value) {
        PMC_int_val(SELF) = value;
    }



/*

=item C<void set_number_native(FLOATVAL value)>

Morphs the integer to a C<Float> and sets the value from C<value>.

=item C<void set_bool(INTVAL value)>

Morphs the integer to a C<Boolean> and sets the value from C<value>.

=item C<void set_bigint_int(INTVAL value)>

Morphs the integer to a C<BigInt> and sets the value from C<value>.

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

Morphs the integer to a C<String> and sets the value from C<value>.

=cut

*/
    void set_number_native(FLOATVAL value) {
        DYNSELF.morph(enum_class_Float);
        DYNSELF.set_number_native(value);
    }

    void set_bool(INTVAL value) {
        DYNSELF.morph(enum_class_Boolean);
        DYNSELF.set_bool(value);
    }

    void set_bignum_int(INTVAL value) {
        DYNSELF.morph(enum_class_BigInt);
        DYNSELF.set_integer_native(value);
    }

    void set_string_native(STRING* value) {
        DYNSELF.morph(enum_class_String);
        DYNSELF.set_string_native(value);
    }

/*

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

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

Adds C<value> to the integer and returns the result in C<*dest>.

Please note: the label syntax I<MMD_type:> denote the behavior, if
the right hand value is of that type. The part inside the braces is
converted to a distinct function and gets I<mmd_register>ed for these
two types.

=cut

*/
    PMC* add(PMC* value, PMC* dest) {
MMD_Integer: {
            /*
             * SELF and value can both be PMCs that inherit
             * from Integer:
             *   cl = subclass "Integer", "MyInt"
             * so we can't used PMC_int_val(SELF) in any of these
             * Integer methods
             *
             * TODO
             *   check for exact Integer type
             *   e.g. MMD_Integer_EXACT
             */

            const INTVAL a = DYNSELF.get_integer();
            const INTVAL b = VTABLE_get_integer(INTERP, value);
            const INTVAL c = a + b;
            if ((c^a) >= 0 || (c^b) >= 0) {
                if (!dest)
                    dest = pmc_new(INTERP, SELF->vtable->base_type);
                /* need this for e.g. Undef PMC */
                VTABLE_set_integer_native(INTERP, dest, c);
                return dest;
            }
            else
                return overflow(INTERP, SELF, b, dest, MMD_ADD);
        }
MMD_Complex: {
            const INTVAL a = DYNSELF.get_integer();
            if (dest)
                VTABLE_morph(INTERP, dest, value->vtable->base_type);
            else
                dest = pmc_new(INTERP, value->vtable->base_type);
            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;
        }
MMD_BigInt: {
            return overflow_p(INTERP, SELF, value, dest, MMD_ADD);
        }
MMD_DEFAULT: {
            if (!dest)
                dest = pmc_new(INTERP, value->vtable->base_type);
            VTABLE_set_number_native(INTERP, dest,
                DYNSELF.get_integer() + VTABLE_get_number(INTERP, value));
            return dest;
        }
    }

    PMC* add_int(INTVAL b, PMC* dest) {
        /* INTVAL a = PMC_int_val(SELF); */
        const INTVAL a = VTABLE_get_integer(INTERP, SELF);
        const INTVAL c = a + b;
        if ((c^a) >= 0 || (c^b) >= 0) {
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_integer_native(INTERP, dest, c);
            return dest;
        }
        else
            return overflow(INTERP, SELF, b, dest, MMD_ADD);
    }

/*

=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

*/

    void i_add(PMC* value) {
MMD_Integer: {
            SELF.i_add_int(VTABLE_get_integer(INTERP, value));
        }
MMD_Complex: {
            const INTVAL a = DYNSELF.get_integer();
            VTABLE_morph(INTERP, SELF, value->vtable->base_type);
            VTABLE_set_number_native(INTERP, SELF,
                    (FLOATVAL)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));
        }
MMD_DEFAULT: {
        VTABLE_set_number_native(INTERP, SELF,
            PMC_int_val(SELF) + VTABLE_get_number(INTERP, value));
        }
    }

    void i_add_int(INTVAL b) {
        const INTVAL a = DYNSELF.get_integer();
        const INTVAL c = a + b;
        if ((c^a) >= 0 || (c^b) >= 0) {
            VTABLE_set_integer_native(INTERP, SELF, c);
        }
        else
            overflow(INTERP, SELF, b, SELF, MMD_SUBTRACT);
    }

    void i_add_float(FLOATVAL value) {
        const INTVAL a = DYNSELF.get_integer();
        VTABLE_set_number_native(INTERP, SELF, a + value);
    }
/*

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

Subtracts C<*value> from the integer and returns the result in C<*dest>.
If C<dest> is NULL, a new int PMC is created.

Please note: as C<SELF> or C<value> maybe be subclassed, we have to
call C<get_integer> and C<set_integer_native> always.

=cut

*/
    PMC* subtract(PMC* value, PMC* dest) {
MMD_Integer: {
            const INTVAL a = DYNSELF.get_integer();
            const INTVAL b = VTABLE_get_integer(INTERP, value);
            const INTVAL c = a - b;
            if ((c^a) >= 0 || (c^~b) >= 0) {
                if (!dest)
                    dest = pmc_new(INTERP, SELF->vtable->base_type);
                VTABLE_set_integer_native(INTERP, dest, c);
                return dest;
            }
            else
                return overflow(INTERP, SELF, b, dest, MMD_SUBTRACT);
        }
MMD_Complex: {
            const INTVAL a = DYNSELF.get_integer();
            if (dest)
                VTABLE_morph(INTERP, dest, value->vtable->base_type);
            else
                dest = pmc_new(INTERP, value->vtable->base_type);
            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;
        }
MMD_BigInt: {
            return overflow_p(INTERP, SELF, value, dest, MMD_SUBTRACT);
        }
MMD_DEFAULT: {
            if (!dest)
                dest = pmc_new(INTERP, value->vtable->base_type);
            VTABLE_set_number_native(INTERP, dest,
                DYNSELF.get_integer() - VTABLE_get_number(INTERP, value));
            return dest;
        }
    }

/*

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

Subtracts C<value> from the integer and returns the result in C<*dest>.

=cut

*/
    PMC* subtract_int(INTVAL b, PMC* dest) {
        const INTVAL a = DYNSELF.get_integer();
        const INTVAL c = a - b;
        if ((c^a) >= 0 || (c^~b) >= 0) {
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_integer_native(INTERP, dest, c);
            return dest;
        }
        else
            return overflow(INTERP, SELF, b, dest, MMD_SUBTRACT);
    }

/*

=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

*/

    void i_subtract(PMC* value) {
MMD_Integer: {
            const INTVAL a = DYNSELF.get_integer();
            const INTVAL b = VTABLE_get_integer(INTERP, value);
            const INTVAL c = a - b;
            if ((c^a) >= 0 || (c^~b) >= 0) {
                VTABLE_set_integer_native(INTERP, SELF, c);
            }
            else
                overflow(INTERP, SELF, b, SELF, MMD_SUBTRACT);
        }
MMD_Complex: {
            const INTVAL a = DYNSELF.get_integer();
            VTABLE_morph(INTERP, SELF, value->vtable->base_type);
            VTABLE_set_number_native(INTERP, SELF,
                    (FLOATVAL)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));
        }
MMD_DEFAULT: {
        VTABLE_set_number_native(INTERP, SELF,
            DYNSELF.get_integer() - VTABLE_get_number(INTERP, value));
        }
    }

    void i_subtract_int(INTVAL b) {
        const INTVAL a = DYNSELF.get_integer();
        const INTVAL c = a - b;
        if ((c^a) >= 0 || (c^~b) >= 0) {
            VTABLE_set_integer_native(INTERP, SELF, c);
        }
        else
            overflow(INTERP, SELF, b, SELF, MMD_SUBTRACT);
    }

    void i_subtract_float(FLOATVAL value) {
        const INTVAL a = DYNSELF.get_integer();
        VTABLE_set_number_native(INTERP, SELF, a - value);
    }

/*

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

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

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

=cut

*/
    PMC* multiply(PMC* value, PMC* dest) {
MMD_Integer: {
            const INTVAL a = VTABLE_get_integer(INTERP, SELF);
            const INTVAL b = VTABLE_get_integer(INTERP, value);
            const double cf = (double)a * (double)b;
            const INTVAL c = a * b;
            if ((double) c == cf) {
                if (!dest)
                    dest = pmc_new(INTERP, SELF->vtable->base_type);
                VTABLE_set_integer_native(INTERP, dest, c);
                return dest;
            }
            else
                return overflow(INTERP, SELF, b, dest, MMD_MULTIPLY);
        }
MMD_Complex: {
                real_exception(INTERP, NULL, E_NotImplementedError,
                        "TODO mul<Integer, Complex>");
                return dest;
            }
MMD_BigInt:     {
            return Parrot_BigInt_multiply_int(INTERP, value,
                    DYNSELF.get_integer(), dest);
        }
MMD_String: {
            return Parrot_Integer_multiply_Integer(INTERP, SELF, value, dest);
        }
MMD_DEFAULT: {
            const FLOATVAL valf = VTABLE_get_number(INTERP, value);
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_number_native(INTERP, dest, valf * PMC_int_val(SELF));
            return dest;
        }
    }

    PMC* multiply_int(INTVAL b, PMC* dest) {
        const INTVAL a = DYNSELF.get_integer();
        const double cf = (double)a * (double)b;
        const INTVAL c = a * b;
        if ((double) c == cf) {
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_integer_native(INTERP, dest, c);
            return dest;
        }
        else
            return overflow(INTERP, SELF, b, dest, MMD_MULTIPLY);
    }

/*

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

=item C<void i_multiply_int(INTVAL value)>

=item C<void i_multiply_float(FLOATVAL value)>

Multiply C<value> with C<SELF> inplace.

=cut

*/

    void i_multiply(PMC* value) {
MMD_Integer: {
            SELF.i_multiply_int(VTABLE_get_integer(INTERP, value));
        }
MMD_BigInt: {
            Parrot_BigInt_multiply_int(INTERP, value,
                    DYNSELF.get_integer(), SELF);
        }
MMD_Complex: {
                real_exception(INTERP, NULL, E_NotImplementedError,
                        "TODO i_mul<Integer, Complex>");
        }
MMD_DEFAULT: {
        VTABLE_set_number_native(INTERP, SELF,
            PMC_int_val(SELF) * VTABLE_get_number(INTERP, value));
        }
    }

    void i_multiply_int(INTVAL b) {
        const INTVAL a = DYNSELF.get_integer();
        const double cf = (double)a * (double)b;
        const INTVAL c = a * b;
        if ((double) c == cf) {
            DYNSELF.set_integer_native(c);
        }
        else
            overflow(INTERP, SELF, b, SELF, MMD_MULTIPLY);
    }

    void i_multiply_float(FLOATVAL value) {
        const INTVAL a = DYNSELF.get_integer();
        VTABLE_set_number_native(INTERP, SELF, a * 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

*/

    PMC* divide(PMC* value, PMC* dest) {
MMD_BigInt:     {
            return overflow_p(INTERP, SELF, value, dest, MMD_DIVIDE);
        }
MMD_DEFAULT: {
            const FLOATVAL d = VTABLE_get_number(INTERP, value);

            if (d == 0.0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "float division by zero");
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_number_native(INTERP, dest, DYNSELF.get_number() / d);
            return dest;
        }
    }

    void i_divide(PMC* value) {
MMD_BigInt:     {
             overflow_p(INTERP, SELF, value, SELF, MMD_DIVIDE);
        }
MMD_DEFAULT: {
            const FLOATVAL d = VTABLE_get_number(INTERP, value);

            if (d == 0.0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "float division by zero");
            VTABLE_set_number_native(INTERP, SELF, DYNSELF.get_number() / d);
        }
    }

/*

=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

*/

    PMC* floor_divide(PMC* value, PMC* dest) {
MMD_BigInt:     {
            return overflow_p(INTERP, SELF, value, dest, MMD_FLOOR_DIVIDE);
        }
MMD_DEFAULT: {
            const FLOATVAL d = VTABLE_get_number(INTERP, value);

            if (d == 0.0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "float division by zero");
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_integer_native(INTERP, dest,
                    (INTVAL)floor(DYNSELF.get_number() / d));
            return dest;
        }
    }

    PMC* floor_divide_int(INTVAL value, PMC* dest) {
        if (value == 0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "float division by zero");
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest,
                (INTVAL)floor(DYNSELF.get_number() / value));
        return dest;
    }

    PMC* floor_divide_float(FLOATVAL value, PMC* dest) {
        if (value == 0.0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "float division by zero");
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest,
                (INTVAL)floor(DYNSELF.get_number() / value));
        return dest;
    }

    void i_floor_divide(PMC* value) {
MMD_BigInt:     {
            overflow_p(INTERP, SELF, value, SELF, MMD_FLOOR_DIVIDE);
        }
MMD_DEFAULT: {
            const FLOATVAL d = VTABLE_get_number(INTERP, value);

            if (d == 0.0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "float division by zero");
            VTABLE_set_integer_native(INTERP, SELF,
                    (INTVAL)floor(DYNSELF.get_number() / d));
        }
    }

    void i_floor_divide_int(INTVAL value) {
        if (value == 0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "float division by zero");
        VTABLE_set_integer_native(INTERP, SELF,
                (INTVAL)floor(DYNSELF.get_number() / value));
    }

    void i_floor_divide_float(FLOATVAL value) {
        if (value == 0.0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "float division by zero");
        VTABLE_set_integer_native(INTERP, SELF,
                (INTVAL)floor(DYNSELF.get_number() / value));
    }

/*

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

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

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

Calculates the value of the number C-style C<mod> C<value> and returns
the result in C<dest>.

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

=item C<void i_cmodulus(INTVAL value)>

=item C<void i_cmodulus(FLOATVAL value)>

Calculates the value of the number C-style C<mod> C<value> and returns
the result in C<dest>.

=cut

*/

    PMC* cmodulus(PMC* value, PMC* dest) {
MMD_BigInt: {
            return overflow_p(INTERP, SELF, value, dest, MMD_CMOD);
        }
MMD_DEFAULT: {
            INTVAL d = VTABLE_get_integer(INTERP, value);

            if (d == 0.0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "int cmodulus by zero");
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_integer_native(INTERP, dest,
                    DYNSELF.get_integer() % d);
            return dest;
        }
    }

    PMC* cmodulus_float(FLOATVAL value, PMC* dest) {
        if (value == 0.0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int cmodulus by zero");
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest,
                (INTVAL)fmod(DYNSELF.get_integer(), value));
        return dest;
    }

    PMC* cmodulus_int(INTVAL value, PMC* dest) {
        if (value == 0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int cmodulus by zero");
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest,
                DYNSELF.get_integer() % value);
        return dest;
    }

    void i_cmodulus(PMC* value) {
MMD_BigInt: {
             overflow_p(INTERP, SELF, value, SELF, MMD_CMOD);
        }
MMD_DEFAULT: {
            const INTVAL d = VTABLE_get_integer(INTERP, value);

            if (d == 0.0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "int cmodulus by zero");
            VTABLE_set_integer_native(INTERP, SELF,
                    DYNSELF.get_integer() % d);
        }
    }

    void i_cmodulus_int(INTVAL value) {
        if (value == 0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int cmodulus by zero");
        VTABLE_set_integer_native(INTERP, SELF,
                DYNSELF.get_integer() % value);
    }

    void i_cmodulus_float(FLOATVAL value) {
        if (value == 0.0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int cmodulus by zero");
        VTABLE_set_integer_native(INTERP, SELF,
                (INTVAL)fmod(DYNSELF.get_integer(), 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

*/

    PMC* modulus(PMC* value, PMC* dest) {
MMD_BigInt: {
            return overflow_p(INTERP, SELF, value, dest, MMD_MOD);
        }
MMD_DEFAULT: {
            const INTVAL d = VTABLE_get_integer(INTERP, value);

            if (d == 0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "int modulus by zero");
            if (!dest)
                dest = pmc_new(INTERP, SELF->vtable->base_type);
            VTABLE_set_integer_native(INTERP, dest,
                    intval_mod(DYNSELF.get_integer(), d));
            return dest;
        }
    }

    PMC* modulus_int(INTVAL value, PMC* dest) {
        if (value == 0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int modulus by zero");
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest,
                intval_mod(DYNSELF.get_integer(), value));
        return dest;
    }

    PMC* modulus_float(FLOATVAL value, PMC* dest) {
        if (value == 0.0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int modulus by zero");
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest,
                intval_mod(DYNSELF.get_integer(), (INTVAL)value));
        return dest;
    }
    void i_modulus(PMC* value) {
MMD_BigInt: {
             overflow_p(INTERP, SELF, value, SELF, MMD_MOD);
        }
MMD_DEFAULT: {
            const INTVAL d = VTABLE_get_integer(INTERP, value);

            if (d == 0.0)
                real_exception(INTERP, NULL, E_ZeroDivisionError,
                        "int modulus by zero");
            VTABLE_set_integer_native(INTERP, SELF,
                    intval_mod(DYNSELF.get_integer() , d));
        }
    }

    void i_modulus_int(INTVAL value) {
        if (value == 0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int modulus by zero");
        VTABLE_set_integer_native(INTERP, SELF,
                intval_mod(DYNSELF.get_integer() , value));
    }

    void i_modulus_float(FLOATVAL value) {
        if (value == 0.0)
            real_exception(INTERP, NULL, E_ZeroDivisionError,
                    "int modulus by zero");
        VTABLE_set_integer_native(INTERP, SELF,
                intval_mod(DYNSELF.get_integer() , (INTVAL)value));
    }

/*

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

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

Return SELF to the C<value>th power and return result in C<dest>.

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

=item C<void i_pow_int(INTVAL value)>

Rise SELF to the C<value>th power.

TODO Complex and BigInt rhs.

=cut

*/
    PMC* pow(PMC* value, PMC* dest) {
        const INTVAL v = VTABLE_get_integer(INTERP, value);
        return SELF.pow_int(v, dest);
    }

    PMC* pow_int(INTVAL b, PMC* dest) {
        const INTVAL a = DYNSELF.get_integer();
        const INTVAL orig_b = b;
        INTVAL r;

        if (b < 0)
            return SUPER(b, dest);
        r = 1;
        if (a) {
            INTVAL temp = a;
            while (b > 0) {
                INTVAL prev = r;
                if (b & 1) {
                    r *= temp;
                    if (r / temp != prev) {
                        return overflow(INTERP, SELF, orig_b, dest, MMD_POW);
                    }
                }
                b >>= 1;
                if (!b)
                    break;
                prev = temp;
                temp *= temp;
                if (prev != 0 && temp / prev != prev) {
                    return overflow(INTERP, SELF, orig_b, dest, MMD_POW);
                }
            }
        }
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest, r);
        return dest;

    }

    void i_pow(PMC* value) {
        SELF.pow(value, SELF);
    }

    void i_pow_int(INTVAL value) {
        SELF.pow_int(value, SELF);
    }


/*

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

The C<==> operation.

=cut

*/
    INTVAL is_equal(PMC* value) {
MMD_BigInt: {
        PMC* const temp = pmc_new(INTERP, enum_class_BigInt);
        VTABLE_set_integer_native(INTERP, temp, PMC_int_val(SELF));
        return Parrot_BigInt_is_equal_BigInt(INTERP, temp, value);
    }
MMD_DEFAULT: {
        return (INTVAL)(PMC_int_val(SELF) ==
            VTABLE_get_integer(INTERP, value));
        }
    }

/*

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

Returns the result of comparing the integer with C<*value>.

=cut

*/
    INTVAL cmp(PMC* value) {
MMD_String: {
            const FLOATVAL fdiff =
                PMC_int_val(SELF) - VTABLE_get_number(INTERP, value);
            if (fdiff == 0) {
                const INTVAL idiff =
                    PMC_int_val(SELF) - VTABLE_get_integer(INTERP, value);
                return idiff > 0 ? 1 : idiff < 0 ? -1 : 0;
            }
            else {
                return fdiff > 0 ? 1 : -1;
            }
        }
MMD_Float: {
            const FLOATVAL diff =
                (FLOATVAL)PMC_int_val(SELF) - VTABLE_get_number(INTERP, value);
            return diff > 0 ? 1 : diff < 0 ? -1 : 0;
        }
MMD_DEFAULT: {
            /* int or undef */
            const INTVAL diff = PMC_int_val(SELF)
                - VTABLE_get_integer(INTERP, value);
            return diff > 0 ? 1 : diff < 0 ? -1 : 0;
        }
    }

/*

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

Returns the result of numerically comparing the integer with C<*value>.

=cut

*/
    INTVAL cmp_num(PMC* value) {
MMD_String: {
            const FLOATVAL fdiff =
                PMC_int_val(SELF) - VTABLE_get_number(INTERP, value);
            if (fdiff == 0) {
                const INTVAL idiff =
                    PMC_int_val(SELF) - VTABLE_get_integer(INTERP, value);
                return idiff > 0 ? 1 : idiff < 0 ? -1 : 0;
            }
            else {
                return fdiff > 0 ? 1 : -1;
            }
        }
MMD_Float: {
            const FLOATVAL diff =
                (FLOATVAL)PMC_int_val(SELF) - VTABLE_get_number(INTERP, value);
            return diff > 0 ? 1 : diff < 0 ? -1 : 0;
        }
MMD_DEFAULT: {
            /* int or undef */
            const INTVAL diff =
                PMC_int_val(SELF) - VTABLE_get_integer(INTERP, value);
            return diff > 0 ? 1 : diff < 0 ? -1 : 0;
        }
    }

/*

=item C<void increment()>

Increments the integer.

=cut

*/
    void increment() {
        /* XXX overflow */
        PMC_int_val(SELF) ++;
    }

/*

=item C<void decrement()>

Decrements the integer.

=cut

*/
    void decrement() {
        /* XXX overflow */
        PMC_int_val(SELF) --;
    }
/*

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

=item C<void absolute()>

Sets C<dest> to the absolute value of SELF.

=cut

*/

    PMC* absolute(PMC *dest) {
        const INTVAL a = abs(DYNSELF.get_integer());
        /* XXX overlflow for -maxint */
        if (!dest)
            dest = pmc_new(INTERP, SELF->vtable->base_type);
        VTABLE_set_integer_native(INTERP, dest, a);
        return dest;

    }

    void i_absolute() {
        const INTVAL a = abs(DYNSELF.get_integer());
        VTABLE_set_integer_native(INTERP, SELF, a);
    }

/*

=item C<STRING* get_as_base(INTVAL base)>

Converts and returns the integer in base C<base>.
C<base> must be between 2 and 36, inclusize.

=cut

*/

    METHOD STRING* get_as_base(INTVAL base) {
        char buf[128];

        if ((base < 2) || (base > 36))
            real_exception(interp, NULL, E_RuntimeError,
                    "get_as_base: base out of bounds");

        return int_to_str(interp, buf,
                (HUGEINTVAL)VTABLE_get_integer(INTERP, SELF), (char)base);
    }

/*

=item C<void freeze(visit_info *info)>

Used to archive the integer.

=cut

*/
    void freeze(visit_info *info) {
        IMAGE_IO * const io = info->image_io;
        SUPER(info);
        io->vtable->push_integer(INTERP, io, PMC_int_val(SELF));
    }

/*

=item C<void thaw(visit_info *info)>

Used to unarchive the integer.

=cut

*/
    void thaw(visit_info *info) {
        IMAGE_IO * const io = info->image_io;
        SUPER(info);
        if (info->extra_flags == EXTRA_IS_NULL)
            PMC_int_val(SELF) = io->vtable->shift_integer(INTERP, io);
    }
}

/*

=back

=cut

*/

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