/*
Copyright (C) 2004-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/bigint.pmc 35701 2009-01-17T10:03:02.811855Z cotto  $

=head1 NAME

src/pmc/bigint.pmc - BigInt PMC class

=head1 DESCRIPTION

C<BigInt> provides arbitrary precision integer mathematic functions.

=head2 Functions

=over 4

=item C<static void bigint_set_long(Interp*, PMC *, long value)>

=item C<static void bigint_set_double(Interp*, PMC *, double value)>

=cut

*/

#include "parrot/parrot.h"

/* TODO split that out into a separate file */

#ifdef S_SPLINT_S
#  undef PARROT_HAS_GMP /* splint barfs on the gmp.h header */
#endif /* S_SPLINT_S */

#ifdef PARROT_HAS_GMP
#  include <gmp.h>
typedef struct BIGINT {
    mpz_t b;
} BIGINT;


static void
bigint_init(PARROT_INTERP, PMC *self) {
    Parrot_BigInt_attributes *attrs =
        mem_allocate_zeroed_typed(Parrot_BigInt_attributes);
    attrs->bi = mem_allocate_zeroed_typed(BIGINT);
    mpz_init(attrs->bi->b);
    PMC_data(self) = attrs;
}

static void
bigint_clear(PARROT_INTERP, PMC *self) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_clear(bi->b);
}

static void
bigint_set(PARROT_INTERP, PMC *dest, PMC *src) {
    BIGINT *bi_dest, *bi_src;
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    GETATTR_BigInt_bi(interp, src,  bi_src);
    mpz_clear(bi_dest->b);
    mpz_init(bi_dest->b);
    mpz_set(bi_dest->b, bi_src->b);
}

static void
bigint_set_long(PARROT_INTERP, PMC *self, long value) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set_si(bi->b, value);
}

static void
bigint_set_double(PARROT_INTERP, PMC *self, double value) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set_d(bi->b, value);
}

static void
bigint_set_str(PARROT_INTERP, PMC *self, char *value, int base) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set_str(bi->b, value, base);
}

static BIGNUM*
bigint_get_self(PARROT_INTERP, PMC *self) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    return bi;
}

static void
bigint_set_self(PARROT_INTERP, PMC *self, BIGNUM *value) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set(bi->b, (mpz_srcptr)((BIGINT*)value)->b);
}

static long
bigint_get_long(PARROT_INTERP, PMC *self) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    if (mpz_fits_slong_p(bi->b))
        return mpz_get_si(bi->b);

    Parrot_ex_throw_from_c_args(interp, NULL, 1, "bigint_get_long: number too big");
}

static int
bigint_get_bool(PARROT_INTERP, PMC *self) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    if (mpz_sgn(bi->b) != 0)
        return 1;
    else
        return 0;
}

static char *
bigint_get_string(PARROT_INTERP, PMC *self, int base) {
    BIGINT *bi;
    size_t  n;
    char   *s;

    GETATTR_BigInt_bi(interp, self, bi);
    n = mpz_sizeinbase(bi->b, base) + 2;
    s = (char *)mem_sys_allocate(n);
    return mpz_get_str(s, base, bi->b);
}

static double
bigint_get_double(PARROT_INTERP, PMC *self) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    return mpz_get_d(bi->b);
}

static void
bigint_add_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    BIGINT *bi_self, *bi_value, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, value, bi_value);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_add(bi_dest->b, bi_self->b, bi_value->b);
}

static void
bigint_add_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    if (value < 0)
        mpz_sub_ui(bi_dest->b, bi_self->b, (unsigned long int)-value);
    else
        mpz_add_ui(bi_dest->b, bi_self->b, (unsigned long int)value);
}

static void
bigint_sub_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    BIGINT *bi_self, *bi_value, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, value, bi_value);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_sub(bi_dest->b, bi_self->b, bi_value->b);
}

static void
bigint_sub_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    if (value < 0)
        mpz_add_ui(bi_dest->b, bi_self->b, (unsigned long int)-value);
    else
        mpz_sub_ui(bi_dest->b, bi_self->b, (unsigned long int)value);
}

static void
bigint_mul_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    BIGINT *bi_self, *bi_value, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, value, bi_value);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_mul(bi_dest->b, bi_self->b, bi_value->b);
}

static void
bigint_mul_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_mul_si(bi_dest->b, bi_self->b, value);
}

static void
bigint_pow_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_pow_ui(bi_dest->b, bi_self->b, (unsigned long int)value);
}

static void
int_check_divide_zero(PARROT_INTERP, INTVAL value) {
    if (value == 0)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
}

static void
bigint_check_divide_zero(PARROT_INTERP, PMC *value) {
    /* Throw an exception if we are dividing by zero. */
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, value, bi);
    if (mpz_cmp_si(bi->b, 0) == 0)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
}

static void
bigint_div_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    BIGINT *bi_self, *bi_value, *bi_dest;
    bigint_check_divide_zero(interp, value);
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, value, bi_value);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    /* this is mpz_fdiv_q */
    mpz_div(bi_dest->b, bi_self->b, bi_value->b);
}

static void
bigint_div_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    int_check_divide_zero(interp, value);

    /* this is mpz_fdiv_q */
    if (value < 0) {
        mpz_div_ui(bi_dest->b, bi_self->b, (unsigned long int)-value);
        mpz_neg(bi_dest->b, bi_dest->b);
    }
    else
        mpz_div_ui(bi_dest->b, bi_self->b, (unsigned long int)value);
}

static void
bigint_fdiv_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    BIGINT *bi_self, *bi_value, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, value, bi_value);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    bigint_check_divide_zero(interp, value);
    mpz_fdiv_q(bi_dest->b, bi_self->b, bi_value->b);
}

static void
bigint_fdiv_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    int_check_divide_zero(interp, value);

    if (value < 0) {
        mpz_fdiv_q_ui(bi_dest->b, bi_self->b, (unsigned long int)-value);
        mpz_neg(bi_dest->b, bi_dest->b);
    }
    else
        mpz_fdiv_q_ui(bi_dest->b, bi_self->b, (unsigned long int)value);
}

static void
bigint_mod_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    BIGINT *bi_self, *bi_value, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, value, bi_value);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    bigint_check_divide_zero(interp, value);
    mpz_mod(bi_dest->b, bi_self->b, bi_value->b);
}

static void
bigint_mod_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    int_check_divide_zero(interp, value);

    if (value < 0) {
        mpz_mod_ui(bi_dest->b, bi_self->b, (unsigned long int)-value);
    }
    else
        mpz_mod_ui(bi_dest->b, bi_self->b, (unsigned long int)value);
}

static INTVAL
bigint_cmp(PARROT_INTERP, PMC *self, PMC *value) {
    BIGINT *bi_self, *bi_value;
    GETATTR_BigInt_bi(interp, self,  bi_self);
    GETATTR_BigInt_bi(interp, value, bi_value);
    return mpz_cmp(bi_self->b, bi_value->b);
}

static INTVAL
bigint_cmp_int(PARROT_INTERP, PMC *self, INTVAL value) {
    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    return mpz_cmp_si(bi->b, value);
}

static void
bigint_abs(PARROT_INTERP, PMC *self, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    VTABLE_morph(interp, dest, enum_class_BigInt);
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_abs(bi_dest->b, bi_self->b);
}

static void
bigint_neg(PARROT_INTERP, PMC *self, PMC *dest) {
    BIGINT *bi_self, *bi_dest;
    VTABLE_morph(interp, dest, enum_class_BigInt);
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_neg(bi_dest->b, bi_self->b);
}

static void
bigint_bitwise_shl_bigint_int(PARROT_INTERP, PMC *self,
                              INTVAL value, PMC *dest)
{
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    /* The third args to mpz_mul_2exp and mpz_tdiv_q_2exp are unsigned, so we
       need to do something sensible with negative values. */
    if (value >= 0)
        mpz_mul_2exp(bi_dest->b, bi_self->b, (unsigned long int)value);
    else
        mpz_tdiv_q_2exp(bi_dest->b, bi_self->b, (unsigned long int)-value);
}

static void
bigint_bitwise_shr_bigint_int(PARROT_INTERP, PMC *self,
                              INTVAL value, PMC *dest)
{
    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    /* The third args to mpz_mul_2exp and mpz_tdiv_q_2exp are unsigned, so we
       need to do something sensible with negative values. */
    if (value >= 0)
        mpz_tdiv_q_2exp(bi_dest->b, bi_self->b, (unsigned long int)value);
    else
        mpz_mul_2exp(bi_dest->b, bi_self->b, (unsigned long int)-value);
}

#else /* ifdef PARROT_HAS_GMP */

typedef struct BIGINT {
    INTVAL b; /* bogus definition for users without libgmp*/
} BIGINT;

static void
bigint_init(PARROT_INTERP, PMC *self) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_clear(PARROT_INTERP, PMC *self) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_set_long(PARROT_INTERP, PMC *self, long value) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_set(PARROT_INTERP, PMC *dest, PMC *src) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_set_double(PARROT_INTERP, PMC *self, double value) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_set_str(PARROT_INTERP, PMC *self, char *value, int base) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_set_self(PARROT_INTERP, PMC *self, BIGNUM *value) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static BIGNUM*
bigint_get_self(PARROT_INTERP, PMC *self) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static char *
bigint_get_string(PARROT_INTERP, PMC *self, int base) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static long
bigint_get_long(PARROT_INTERP, PMC *self) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static long
bigint_get_bool(PARROT_INTERP, PMC *self) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static double
bigint_get_double(PARROT_INTERP, PMC *self) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_add_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_add_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_sub_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_sub_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_mul_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_mul_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_pow_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_div_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_div_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_fdiv_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_fdiv_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_mod_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_mod_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static INTVAL
bigint_cmp(PARROT_INTERP, PMC *self, PMC *value) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static INTVAL
bigint_cmp_int(PARROT_INTERP, PMC *self, INTVAL value) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_abs(PARROT_INTERP, PMC *self, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_neg(PARROT_INTERP, PMC *self, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_bitwise_shl_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_bitwise_shl_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest)
{
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_bitwise_shr_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

static void
bigint_bitwise_shr_bigint_int(PARROT_INTERP, PMC *self, INTVAL value, PMC *dest)
{
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

#endif /* ifdef PARROT_HAS_GMP */

pmclass BigInt {
    ATTR struct BIGINT * bi; /*bigint val*/

/*

=back

=head2 Methods

=over 4

=item C<METHOD version()>

Return GMP version string "x.y.z".

=cut

*/

    METHOD version() {
        STRING *version;
#ifdef PARROT_HAS_GMP
        version = string_from_cstring(INTERP, gmp_version, 0);
#else
        version = CONST_STRING(INTERP, "0.0.0");
#endif
        RETURN(STRING *version);
    }

/*

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

Object constructor. SELF is a BigInt Class object. Return a new
BigInt object.

=cut

*/
    VTABLE PMC *instantiate(PMC *sig) {
        return PMCNULL;

        /* TODO -- actually build this thing */
#if 0
        int argcP = REG_INT(interp, 3);
        int base;
        PMC *res;
        STRING *num;

        res = pmc_new(INTERP, enum_class_BigInt);
        if (!argcP) {
            return res;
        }
        base = 10;
        if (argcP == 2)
            base = VTABLE_get_integer(INTERP, REG_PMC(interp, 6));
        num = VTABLE_get_string(INTERP, REG_PMC(interp, 5));
        VTABLE_set_string_keyed_int(INTERP, res, base, num);
        return res;
#endif
    }

    VTABLE void init() {
        bigint_init(INTERP, SELF);
        PObj_active_destroy_SET(SELF);
    }

    VTABLE PMC *clone() {
        PMC *res = pmc_new(INTERP, SELF->vtable->base_type);
        bigint_set(INTERP, res, SELF);
        return res;
    }

    VTABLE void destroy() {
        BIGINT                   *bi;
        Parrot_BigInt_attributes *attrs;

        bigint_clear(INTERP, SELF);

        attrs = (Parrot_BigInt_attributes*)PMC_data(SELF);
        mem_sys_free(attrs->bi);
        mem_sys_free(attrs);
    }

/*

=item C<void set_integer_native(INTVAL value)>

=cut

*/

    VTABLE void set_integer_native(INTVAL value) {
        bigint_set_long(INTERP, SELF, (long)value);
    }

/*

=item C<void set_number_native(FLOATVAL value)>

Sets the value of the bigint to C<value>.

=cut

*/

    VTABLE void set_number_native(FLOATVAL value) {
        bigint_set_double(INTERP, SELF, (double)value);
    }

/*

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

Sets the value of the integer to the result of converting C<*value> to a
number.

=item C<void set_string_keyed_int(INTVAL base, STRING *value)>

Same assume number base C<base>.

=cut

*/

    VTABLE void set_string_native(STRING *value) {
        char * const s = string_to_cstring(INTERP, value);
        bigint_set_str(INTERP, SELF, s, 10);
        string_cstring_free(s);
    }

    VTABLE void set_string_keyed_int(INTVAL base, STRING *value) {
        char * const s = string_to_cstring(INTERP, value);
        bigint_set_str(INTERP, SELF, s, base);
        string_cstring_free(s);
    }
/*

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

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

=cut

*/

    VTABLE void set_pmc(PMC *value) {
        bigint_set_self(INTERP, SELF, bigint_get_self(INTERP, value));
    }

/*

=item C<FLOATVAL get_number()>

Returns the value of the integer as a floating point number.

=cut

*/

    VTABLE FLOATVAL get_number() {
        return bigint_get_double(INTERP, SELF);
    }

/*

=item C<INTVAL get_integer()>

Returns the value of the integer.

=cut

*/

    VTABLE INTVAL get_integer() {
        return bigint_get_long(INTERP, SELF);
    }

/*

=item C<PMC *get_bignum()>

Returns SELF

=cut

*/

    VTABLE PMC *get_bignum() {
        return SELF;
    }

/*

=item C<INTVAL get_bool()>

Returns the boolean value of the integer.

=cut

*/

    VTABLE INTVAL get_bool() {
        return bigint_get_bool(INTERP, SELF);
    }

/*

=item C<STRING *get_string()>

Returns the string representation of the integer.

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

Returns the string representation of the integer in base C<base>.

=item C<STRING *get_repr()>

Returns the string representation of the integer with the letter 'L'
appended.

=cut

*/

    VTABLE STRING *get_string() {
        char   *s  = bigint_get_string(INTERP, SELF, 10);
        STRING *ps = string_from_cstring(INTERP, s, 0);
        mem_sys_free(s);
        return ps;
    }

    VTABLE STRING *get_string_keyed_int(INTVAL base) {
        char   *s  = bigint_get_string(INTERP, SELF, base);
        STRING *ps = string_from_cstring(INTERP, s, 0);
        mem_sys_free(s);
        return ps;
    }

    VTABLE STRING *get_repr() {
        STRING *s = SELF.get_string();
        return string_append(INTERP, s, CONST_STRING(INTERP, "L"));
    }
/*

=item C<void increment()>

Increments the integer.

=cut

*/

    VTABLE void increment() {
        bigint_add_bigint_int(INTERP, SELF, 1, SELF);
    }

/*

=item C<void decrement()>

Decrements the integer.

=cut

*/

    VTABLE void decrement() {
        bigint_sub_bigint_int(INTERP, SELF, 1, SELF);
    }

    MULTI PMC *add(BigInt value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_add_bigint(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI PMC *add(Integer value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_add_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), dest);
        return dest;
    }

    MULTI PMC *add(DEFAULT value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'add' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE PMC *add_int(INTVAL value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_add_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI void i_add(BigInt value) {
        bigint_add_bigint(INTERP, SELF, value, SELF);
    }

    MULTI void i_add(Integer value) {
        bigint_add_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), SELF);
    }

    MULTI void i_add(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_add' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE void i_add_int(INTVAL value) {
        bigint_add_bigint_int(INTERP, SELF, value, SELF);
    }

    VTABLE void i_add_float(FLOATVAL value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_add_float' for FLOATVAL");
    }


    MULTI PMC *subtract(BigInt value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_sub_bigint(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI PMC *subtract(Integer value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_sub_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), dest);
        return dest;
    }

    MULTI PMC *subtract(DEFAULT value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'subtract' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE PMC *subtract_int(INTVAL value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_sub_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI void i_subtract(BigInt value) {
        bigint_sub_bigint(INTERP, SELF, value, SELF);
    }

    MULTI void i_subtract(Integer value) {
        bigint_sub_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), SELF);
    }

    MULTI void i_subtract(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_subtract' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE void i_subtract_int(INTVAL value) {
        bigint_sub_bigint_int(INTERP, SELF, value, SELF);
    }

    VTABLE void i_subtract_float(FLOATVAL value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_subtract_float' for FLOATVAL");
    }


    MULTI PMC *multiply(BigInt value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_mul_bigint(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI PMC *multiply(Integer value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_mul_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), dest);
        return dest;
    }

    MULTI PMC *multiply(DEFAULT value, PMC *dest) {
         Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'multiply' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE PMC *multiply_int(INTVAL value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_mul_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI void i_multiply(BigInt value) {
        bigint_mul_bigint(INTERP, SELF, value, SELF);
    }
    MULTI void i_multiply(Integer value) {
        bigint_mul_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), SELF);
    }
    MULTI void i_multiply(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_multiply' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE void i_multiply_int(INTVAL value) {
        bigint_mul_bigint_int(INTERP, SELF, value, SELF);
    }

    VTABLE void i_multiply_float(FLOATVAL value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_multiply_float' for FLOATVAL");
    }

    VTABLE PMC *pow_int(INTVAL value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_pow_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI PMC *pow(PMC *value, PMC *dest) {
        /* XXX only Integer RHS currently */
        INTVAL r = VTABLE_get_integer(INTERP, value);
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_pow_bigint_int(INTERP, SELF, r, dest);
        return dest;
    }

    MULTI PMC *divide(BigInt value, PMC *dest) {
        BIGINT *bi;
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_div_bigint(INTERP, SELF, value, dest);
#if 0
        /* to downgrade or not that's the question */
        GETATTR_BigInt_bi(interp, dest, bi);
        if (mpz_fits_slong_p(bi->b)) {
            long iresult = mpz_get_si(bi->b);
            VTABLE_morph(interp, dest, enum_class_Integer);
            VTABLE_set_integer_native(interp, dest, iresult);
        }
#endif
        return dest;
    }

    MULTI PMC *divide(Integer value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_div_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), dest);
        return dest;
    }

    MULTI PMC *divide(DEFAULT value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'divide' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE PMC *divide_int(INTVAL value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_div_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI void i_divide(BigInt value) {
        bigint_div_bigint(INTERP, SELF, value, SELF);
    }
    MULTI void i_divide(Integer value) {
        bigint_div_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), SELF);
    }
    MULTI void i_divide(DEFAULT value) {
         Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_divide' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE void i_divide_int(INTVAL value) {
        bigint_div_bigint_int(INTERP, SELF, value, SELF);
    }

    MULTI PMC *floor_divide(BigInt value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_fdiv_bigint(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI PMC *floor_divide(Integer value, PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_fdiv_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), dest);
        return dest;
    }

    MULTI PMC *floor_divide(DEFAULT value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'floor_divide' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE PMC *floor_divide_int(INTVAL value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_fdiv_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI void i_floor_divide(BigInt value) {
        bigint_fdiv_bigint(INTERP, SELF, value, SELF);
    }

    MULTI void i_floor_divide(Integer value) {
        bigint_fdiv_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), SELF);
    }

    MULTI void i_floor_divide(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_floor_divide' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE void i_floor_divide_int(INTVAL value) {
        bigint_fdiv_bigint_int(INTERP, SELF, value, SELF);
    }

    MULTI PMC *modulus(BigInt value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_mod_bigint(INTERP, SELF, value, dest);
        return dest;
    }

    MULTI PMC *modulus(Integer value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_mod_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), dest);
        return dest;
    }

    MULTI PMC *modulus(DEFAULT value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'modulus' for %Ss",
            VTABLE_name(interp, value));
    }

    MULTI void i_modulus(BigInt value) {
        bigint_mod_bigint(INTERP, SELF, value, SELF);
    }
    MULTI void i_modulus(Integer value) {
        bigint_mod_bigint_int(INTERP, SELF, VTABLE_get_integer(interp, value), SELF);
    }
    MULTI void i_modulus(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_modulus' for %Ss",
            VTABLE_name(interp, value));
    }

    MULTI INTVAL cmp(BigInt value) {
        return bigint_cmp(INTERP, SELF, value);
    }

    MULTI INTVAL cmp(Integer value) {
        return bigint_cmp_int(INTERP, SELF, VTABLE_get_integer(interp, value));
    }

    MULTI INTVAL cmp(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'cmp' for %Ss",
            VTABLE_name(interp, value));
    }

    MULTI INTVAL is_equal(BigInt value) {
        return bigint_cmp(INTERP, SELF, value) == 0;
    }

    MULTI INTVAL is_equal(Integer value) {
        return bigint_cmp_int(INTERP, SELF, VTABLE_get_integer(interp, value)) == 0;
    }

    MULTI INTVAL is_equal(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'is_equal' for %Ss",
            VTABLE_name(interp, value));
    }

/*

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

=item C<void i_absolute()>

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

=cut

*/

    VTABLE PMC *absolute(PMC *dest) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_abs(INTERP, SELF, dest);
        return dest;
    }

    VTABLE void i_absolute() {
        bigint_abs(INTERP, SELF, SELF);
    }


/*

=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) {
        dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_neg(INTERP, SELF, dest);
        return dest;
    }

    VTABLE void i_neg() {
        bigint_neg(INTERP, SELF, SELF);
    }

/*

=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 BigInt by C<*value>.

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

=item C<void i_bitwise_shl_int(INTVAL value)>

Inplace shift left.

=cut

*/

    MULTI PMC *bitwise_shl(BigInt value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_bitwise_shl_bigint_int(INTERP, SELF,
                                      VTABLE_get_integer(INTERP, value),
                                      dest);
        return dest;
    }

    MULTI PMC *bitwise_shl(Integer value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_bitwise_shl_bigint_int(INTERP, SELF,
                VTABLE_get_integer(interp, value), dest);
        return dest;
    }
    MULTI PMC *bitwise_shl(DEFAULT value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'bitwise_shl' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE PMC *bitwise_shl_int(INTVAL value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_bitwise_shl_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }


    MULTI void i_bitwise_shl(BigInt value) {
        bigint_bitwise_shl_bigint_int(INTERP, SELF,
                                      VTABLE_get_integer(INTERP, value),
                                      SELF);
    }

    MULTI void i_bitwise_shl(Integer value) {
        bigint_bitwise_shl_bigint_int(INTERP, SELF,
                VTABLE_get_integer(interp, value), SELF);
    }

    MULTI void i_bitwise_shl(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_bitwise_shl' for %Ss",
            VTABLE_name(interp, value));
    }

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

/*

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

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

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

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

=item C<void i_bitwise_shr_int(INTVAL value)>

Inplace shift left.

=cut

*/

    MULTI PMC *bitwise_shr(BigInt value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_bitwise_shr_bigint_int(INTERP, SELF,
                                      VTABLE_get_integer(INTERP, value),
                                      dest);
        return dest;
    }

    MULTI PMC *bitwise_shr(Integer value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_bitwise_shr_bigint_int(INTERP, SELF,
                VTABLE_get_integer(interp, value), dest);
        return dest;
    }

    MULTI PMC *bitwise_shr(DEFAULT value, PMC *dest) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'bitwise_shr' for %Ss",
            VTABLE_name(interp, value));
    }

    VTABLE PMC *bitwise_shr_int(INTVAL value, PMC *dest) {
        if (dest)
            VTABLE_morph(interp, dest, SELF->vtable->base_type);
        else
            dest = pmc_new(INTERP, SELF->vtable->base_type);

        bigint_bitwise_shr_bigint_int(INTERP, SELF, value, dest);
        return dest;
    }


    MULTI void i_bitwise_shr(BigInt value) {
        bigint_bitwise_shr_bigint_int(INTERP, SELF,
                                      VTABLE_get_integer(INTERP, value),
                                      SELF);
    }

    MULTI void i_bitwise_shr(Integer value) {
        bigint_bitwise_shr_bigint_int(INTERP, SELF,
                VTABLE_get_integer(interp, value), SELF);
    }

    MULTI void i_bitwise_shr(DEFAULT value) {
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_bitwise_shr' for %Ss",
            VTABLE_name(interp, value));
    }

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

/*

=back

=cut

*/

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