/*
Copyright (C) 2004-2010, Parrot Foundation.
$Id: bigint.pmc 48526 2010-08-16 17:34:55Z NotFound $

=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(PARROT_INTERP, PMC *self, long value)>

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

=cut

*/

/* 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;


/* HEADERIZER BEGIN: static */
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */

static void bigint_abs(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*dest);

static void bigint_add_bigint(PARROT_INTERP,
    ARGIN(PMC *self),
    ARGIN(PMC *value),
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_add_bigint_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_check_divide_zero(PARROT_INTERP, ARGIN(PMC *value))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bigint_clear(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

PARROT_WARN_UNUSED_RESULT
static INTVAL bigint_cmp(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *value))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3);

PARROT_WARN_UNUSED_RESULT
static INTVAL bigint_cmp_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bigint_div_bigint(PARROT_INTERP,
    ARGIN(PMC *self),
    ARGIN(PMC *value),
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_div_bigint_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_fdiv_bigint(PARROT_INTERP,
    ARGIN(PMC *self),
    ARGMOD(PMC *value),
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*value)
        FUNC_MODIFIES(*dest);

static void bigint_fdiv_bigint_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

PARROT_WARN_UNUSED_RESULT
static int bigint_get_bool(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

PARROT_WARN_UNUSED_RESULT
static double bigint_get_double(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

PARROT_WARN_UNUSED_RESULT
static long bigint_get_long(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

PARROT_CANNOT_RETURN_NULL
PARROT_WARN_UNUSED_RESULT
static BIGINT* bigint_get_self(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

PARROT_WARN_UNUSED_RESULT
PARROT_CANNOT_RETURN_NULL
static char * bigint_get_string(PARROT_INTERP, ARGIN(PMC *self), int base)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bigint_init(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bigint_mod_bigint(PARROT_INTERP,
    ARGIN(PMC *self),
    ARGMOD(PMC *value),
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*value)
        FUNC_MODIFIES(*dest);

static void bigint_mod_bigint_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_mul_bigint(PARROT_INTERP,
    ARGIN(PMC *self),
    ARGIN(PMC *value),
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_mul_bigint_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_neg(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*dest);

static void bigint_pow_bigint_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_set(PARROT_INTERP, ARGMOD(PMC *dest), ARGIN(PMC *src))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*dest);

static void bigint_set_double(PARROT_INTERP,
    ARGMOD(PMC *self),
    double value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        FUNC_MODIFIES(*self);

static void bigint_set_long(PARROT_INTERP, ARGMOD(PMC *self), long value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        FUNC_MODIFIES(*self);

static void bigint_set_self(PARROT_INTERP,
    ARGMOD(PMC *self),
    ARGIN(BIGINT *value))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*self);

static void bigint_set_str(PARROT_INTERP,
    ARGMOD(PMC *self),
    ARGIN(const char *value),
    int base)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*self);

static void bigint_sub_bigint(PARROT_INTERP,
    ARGIN(PMC *self),
    ARGIN(PMC *value),
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bigint_sub_bigint_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void int_check_divide_zero(PARROT_INTERP, INTVAL value)
        __attribute__nonnull__(1);

#define ASSERT_ARGS_bigint_abs __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_add_bigint __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_add_bigint_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_check_divide_zero __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(value))
#define ASSERT_ARGS_bigint_clear __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_cmp __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value))
#define ASSERT_ARGS_bigint_cmp_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_div_bigint __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_div_bigint_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_fdiv_bigint __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_fdiv_bigint_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_get_bool __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_get_double __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_get_long __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_get_self __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_get_string __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_init __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_mod_bigint __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_mod_bigint_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_mul_bigint __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_mul_bigint_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_neg __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_pow_bigint_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_set __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(dest) \
    , PARROT_ASSERT_ARG(src))
#define ASSERT_ARGS_bigint_set_double __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_set_long __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bigint_set_self __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value))
#define ASSERT_ARGS_bigint_set_str __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value))
#define ASSERT_ARGS_bigint_sub_bigint __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bigint_sub_bigint_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_int_check_divide_zero __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
/* HEADERIZER END: static */
/* HEADERIZER HFILE: none */

/*

=item C<static void bigint_init(PARROT_INTERP, PMC *self)>

=cut

*/

static void
bigint_init(PARROT_INTERP, ARGIN(PMC *self))
{
    ASSERT_ARGS(bigint_init)

    BIGINT * const bi = mem_gc_allocate_zeroed_typed(interp, BIGINT);
    SETATTR_BigInt_bi(interp, self, bi);
    mpz_init(bi->b);
}

/*

=item C<static void bigint_clear(PARROT_INTERP, PMC *self)>

=cut

*/

static void
bigint_clear(PARROT_INTERP, ARGIN(PMC *self))
{
    ASSERT_ARGS(bigint_clear)

    BIGINT * bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_clear(bi->b);
    mem_gc_free(interp, bi);
}

/*

=item C<static void bigint_set(PARROT_INTERP, PMC *dest, PMC *src)>

=cut

*/

static void
bigint_set(PARROT_INTERP, ARGMOD(PMC *dest), ARGIN(PMC *src))
{
    ASSERT_ARGS(bigint_set)

    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);
}

/*

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




=cut

*/

static void
bigint_set_long(PARROT_INTERP, ARGMOD(PMC *self), long value)
{
    ASSERT_ARGS(bigint_set_long)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set_si(bi->b, value);
}

/*

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



=cut

*/

static void
bigint_set_double(PARROT_INTERP, ARGMOD(PMC *self), double value)
{
    ASSERT_ARGS(bigint_set_double)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set_d(bi->b, value);
}

/*

=item C<static void bigint_set_str(PARROT_INTERP, PMC *self, const char *value,
int base)>

=cut

*/

static void
bigint_set_str(PARROT_INTERP, ARGMOD(PMC *self), ARGIN(const char *value), int base)
{
    ASSERT_ARGS(bigint_set_str)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set_str(bi->b, value, base);
}

/*

=item C<static BIGINT* bigint_get_self(PARROT_INTERP, PMC *self)>

=cut

*/

PARROT_CANNOT_RETURN_NULL
PARROT_WARN_UNUSED_RESULT
static BIGINT*
bigint_get_self(PARROT_INTERP, ARGIN(PMC *self))
{
    ASSERT_ARGS(bigint_get_self)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    return bi;
}

/*

=item C<static void bigint_set_self(PARROT_INTERP, PMC *self, BIGINT *value)>

=cut

*/

static void
bigint_set_self(PARROT_INTERP, ARGMOD(PMC *self), ARGIN(BIGINT *value))
{
    ASSERT_ARGS(bigint_set_self)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    mpz_set(bi->b, (mpz_srcptr)((BIGINT*)value)->b);
}

/*

=item C<static long bigint_get_long(PARROT_INTERP, PMC *self)>

=cut

*/

PARROT_WARN_UNUSED_RESULT
static long
bigint_get_long(PARROT_INTERP, ARGIN(PMC *self))
{
    ASSERT_ARGS(bigint_get_long)

    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");
}

/*

=item C<static int bigint_get_bool(PARROT_INTERP, PMC *self)>

=cut

*/

PARROT_WARN_UNUSED_RESULT
static int
bigint_get_bool(PARROT_INTERP, ARGIN(PMC *self))
{
    ASSERT_ARGS(bigint_get_bool)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    if (mpz_sgn(bi->b) != 0)
        return 1;
    else
        return 0;
}

/*

=item C<static char * bigint_get_string(PARROT_INTERP, PMC *self, int base)>

=cut

*/

PARROT_WARN_UNUSED_RESULT
PARROT_CANNOT_RETURN_NULL
static char *
bigint_get_string(PARROT_INTERP, ARGIN(PMC *self), int base)
{
    ASSERT_ARGS(bigint_get_string)

    BIGINT *bi;
    size_t  n;
    char   *s;

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

/*

=item C<static double bigint_get_double(PARROT_INTERP, PMC *self)>

=cut

*/

PARROT_WARN_UNUSED_RESULT
static double
bigint_get_double(PARROT_INTERP, ARGIN(PMC *self))
{
    ASSERT_ARGS(bigint_get_double)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    return mpz_get_d(bi->b);
}


/*

=item C<static void bigint_add_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC
*dest)>

=cut

*/

static void
bigint_add_bigint(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *value), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_add_bigint)

    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);
}

/*

=item C<static void bigint_add_bigint_int(PARROT_INTERP, PMC *self, INTVAL
value, PMC *dest)>

=cut

*/

static void
bigint_add_bigint_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_add_bigint_int)

    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);
}

/*

=item C<static void bigint_sub_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC
*dest)>

=cut

*/

static void
bigint_sub_bigint(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *value), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_sub_bigint)

    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);
}

/*

=item C<static void bigint_sub_bigint_int(PARROT_INTERP, PMC *self, INTVAL
value, PMC *dest)>

=cut

*/

static void
bigint_sub_bigint_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_sub_bigint_int)

    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);
}

/*

=item C<static void bigint_mul_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC
*dest)>

=cut

*/

static void
bigint_mul_bigint(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *value), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_mul_bigint)

    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);
}

/*

=item C<static void bigint_mul_bigint_int(PARROT_INTERP, PMC *self, INTVAL
value, PMC *dest)>

=cut

*/

static void
bigint_mul_bigint_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_mul_bigint_int)

    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);
}

/*

=item C<static void bigint_pow_bigint_int(PARROT_INTERP, PMC *self, INTVAL
value, PMC *dest)>

=cut

*/

static void
bigint_pow_bigint_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_pow_bigint_int)

    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);
}

/*

=item C<static void int_check_divide_zero(PARROT_INTERP, INTVAL value)>

=cut

*/

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");
}

/*

=item C<static void bigint_check_divide_zero(PARROT_INTERP, PMC *value)>

=cut

*/

static void
bigint_check_divide_zero(PARROT_INTERP, ARGIN(PMC *value))
{
    ASSERT_ARGS(bigint_check_divide_zero)

    /* 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");
}

/*

=item C<static void bigint_div_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC
*dest)>

=cut

*/

static void
bigint_div_bigint(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *value), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_div_bigint)

    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);
}

/*

=item C<static void bigint_div_bigint_int(PARROT_INTERP, PMC *self, INTVAL
value, PMC *dest)>

=cut

*/

static void
bigint_div_bigint_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_div_bigint_int)

    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);
}

/*

=item C<static void bigint_fdiv_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC
*dest)>

=cut

*/

static void
bigint_fdiv_bigint(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *value), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_fdiv_bigint)

    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);
}

/*

=item C<static void bigint_fdiv_bigint_int(PARROT_INTERP, PMC *self, INTVAL
value, PMC *dest)>

=cut

*/

static void
bigint_fdiv_bigint_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_fdiv_bigint_int)

    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);
}

/*

=item C<static void bigint_mod_bigint(PARROT_INTERP, PMC *self, PMC *value, PMC
*dest)>

=cut

*/

static void
bigint_mod_bigint(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *value), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_mod_bigint)

    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);
}

/*

=item C<static void bigint_mod_bigint_int(PARROT_INTERP, PMC *self, INTVAL
value, PMC *dest)>

=cut

*/

static void
bigint_mod_bigint_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_mod_bigint_int)

    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);
}

/*

=item C<static INTVAL bigint_cmp(PARROT_INTERP, PMC *self, PMC *value)>

=cut

*/

PARROT_WARN_UNUSED_RESULT
static INTVAL
bigint_cmp(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *value))
{
    ASSERT_ARGS(bigint_cmp)

    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);
}

/*

=item C<static INTVAL bigint_cmp_int(PARROT_INTERP, PMC *self, INTVAL value)>

=cut

*/

PARROT_WARN_UNUSED_RESULT
static INTVAL
bigint_cmp_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value)
{
    ASSERT_ARGS(bigint_cmp_int)

    BIGINT *bi;
    GETATTR_BigInt_bi(interp, self, bi);
    return mpz_cmp_si(bi->b, value);
}

/*

=item C<static void bigint_abs(PARROT_INTERP, PMC *self, PMC *dest)>

=cut

*/

static void
bigint_abs(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_abs)

    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_abs(bi_dest->b, bi_self->b);
}

/*

=item C<static void bigint_neg(PARROT_INTERP, PMC *self, PMC *dest)>

=cut

*/

static void
bigint_neg(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *dest))
{
    ASSERT_ARGS(bigint_neg)

    BIGINT *bi_self, *bi_dest;
    GETATTR_BigInt_bi(interp, self, bi_self);
    GETATTR_BigInt_bi(interp, dest, bi_dest);
    mpz_neg(bi_dest->b, bi_self->b);
}

/* HEADERIZER STOP */

#else /* ifdef PARROT_HAS_GMP */

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

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

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

static void
bigint_set_long(PARROT_INTERP, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(PMC *self), BIGINT *value) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

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

static char *
bigint_get_string(PARROT_INTERP, ARGIN(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, ARGIN(PMC *self)) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

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

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

static void
bigint_add_bigint(PARROT_INTERP, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(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, ARGIN(PMC *self), PMC *dest) {
    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_LIBRARY_ERROR,
        "no bigint lib loaded");
}

#endif /* ifdef PARROT_HAS_GMP */

pmclass BigInt auto_attrs {
    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 = Parrot_str_new(INTERP, gmp_version, 0);
#else
        version = CONST_STRING(INTERP, "0.0.0");
#endif
        RETURN(STRING *version);
    }

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

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

    VTABLE void destroy() {
        bigint_clear(INTERP, SELF);
    }

/*

=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 = Parrot_str_to_cstring(INTERP, value);
        bigint_set_str(INTERP, SELF, s, 10);
        Parrot_str_free_cstring(s);
    }

    VTABLE void set_string_keyed_int(INTVAL base, STRING *value) {
        char * const s = Parrot_str_to_cstring(INTERP, value);
        bigint_set_str(INTERP, SELF, s, base);
        Parrot_str_free_cstring(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<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   * const s  = bigint_get_string(INTERP, SELF, 10);
        STRING * const ps = Parrot_str_new(INTERP, s, 0);
        mem_gc_free(INTERP, s);
        return ps;
    }

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

    VTABLE STRING *get_repr() {
        STRING * const s = SELF.get_string();
        return Parrot_str_concat(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 = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *add(Integer value, PMC *dest) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *add(DEFAULT value, PMC *dest) {
        UNUSED(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 = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        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) {
        UNUSED(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) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *subtract(Integer value, PMC *dest) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *subtract(DEFAULT value, PMC *dest) {
        UNUSED(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) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        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) {
        UNUSED(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 = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *multiply(Integer value, PMC *dest) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *multiply(DEFAULT value, PMC *dest) {
        UNUSED(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 = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        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) {
        UNUSED(value)
        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigInt: no multiple dispatch variant 'i_multiply_float' for FLOATVAL");
    }

    METHOD PMC *pow(PMC *value) {
        /* XXX only Integer RHS currently */
        const INTVAL r = VTABLE_get_integer(INTERP, value);
        PMC *dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        bigint_pow_bigint_int(INTERP, SELF, r, dest);
        RETURN(PMC *dest);
    }

    MULTI PMC *divide(BigInt value, PMC *dest) {
        BIGINT *bi;
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *divide(Integer value, PMC *dest) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *divide(DEFAULT value, PMC *dest) {
        UNUSED(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) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        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 = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *floor_divide(Integer value, PMC *dest) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *floor_divide(DEFAULT value, PMC *dest) {
        UNUSED(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) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        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) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *modulus(Integer value, PMC *dest) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI PMC *modulus(DEFAULT value, PMC *dest) {
        UNUSED(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 = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

        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 = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

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

/*

=back

=cut

*/

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