The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
Copyright (C) 2004-2011, Parrot Foundation.

=head1 NAME

src/pmc/bignum.pmc - BigNum PMC class

=head1 DESCRIPTION

C<BigNum> provides arbitrary precision floating point mathematic
functions, based on the GMP mpf library.

=head1 SYNOPSIS

Make mixing of classes work, like in:

        new $P0 ['BigInt']
        new $P1 ['BigNum']
        set $P0, 10
        set $P1, 2
        div P2, P0, P1  # $P2 = $P0 / $P1; (BigNum) 5.0

Make auto-upgrading/downgrading work.

        set $N1, $P0
        set $N1, $P1
        set $I1, $P0
        set $I1, $P1
        set $P0, $I1
        set $P0, $N1
        set $P1, $I1
        set $P1, $N1

  BigNum
    => BigInt => Integer
    => Number: float (can be long double) and double
    => Integer (unsigned long)

=head2 Functions

=over 4

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

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

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

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

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

=item C<static void bignum_set_float(PARROT_INTERP, PMC *self, FLOATVAL value)>

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

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

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

=item C<static BIGNUM* bignum_get_self(PARROT_INTERP, PMC *self)>

=item C<static void bignum_set_self(PARROT_INTERP, PMC *self, BIGNUM *value)>

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

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

=item C<static INTVAL bignum_get_bool(PARROT_INTERP, ARGIN(PMC *self))>

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

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

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

=item C<static FLOATVAL bignum_get_float(PARROT_INTERP, PMC *self)>

=item C<static BIGINT bignum_get_bigint(Interp*, ARGIN(PMC *self))>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

=item C<static INTVAL bignum_cmp_ulong(PARROT_INTERP, PMC *self, unsigned long
value)>

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

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

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

=item C<static void bignum_set_default_prec(PARROT_INTERP, PMC *self, INTVAL
prec)>

All the above functions are local to the bignum pmc and are there to provide
interface functions to The GNU MP Bignum Library (GMP)

If GMP is not available, at present these functions will throw a NYI exception.

(It would be appreciated if maintainers/implementors could improve the
 documentation here at some stage. possibly adding documentation for
 individual functions - at least the less obvious ones.)

=cut

*/

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


#ifdef PARROT_HAS_GMP
#  include "pmc/pmc_bigint.h"
#  include <gmp.h>
typedef struct BIGNUM {
    mpf_t b;
} BIGNUM;
#endif

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

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

static void bignum_add_bignum(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 bignum_add_bignum_float(PARROT_INTERP,
    ARGIN(PMC *self),
    FLOATVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bignum_add_bignum_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 bignum_clear(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

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

static INTVAL bignum_cmp_double(PARROT_INTERP,
    ARGIN(PMC *self),
    double value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

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

static INTVAL bignum_cmp_ulong(PARROT_INTERP,
    ARGIN(PMC *self),
    unsigned long value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bignum_div_bignum(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 bignum_div_bignum_float(PARROT_INTERP,
    ARGIN(PMC *self),
    FLOATVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bignum_div_bignum_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 bignum_fdiv_bignum(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 bignum_fdiv_bignum_int(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static INTVAL bignum_get_default_prec(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

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

PARROT_WARN_UNUSED_RESULT
static FLOATVAL bignum_get_float(PARROT_INTERP, ARGIN(PMC *self))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

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

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

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

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

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

static void bignum_mul_bignum(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 bignum_mul_bignum_float(PARROT_INTERP,
    ARGIN(PMC *self),
    FLOATVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

static void bignum_mul_bignum_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 bignum_neg(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*dest);

static void bignum_pow_bignum_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 bignum_set(PARROT_INTERP, ARGMOD(PMC *dest), ARGIN(PMC *src))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*dest);

static void bignum_set_default_prec(PARROT_INTERP,
    ARGIN(PMC *self),
    INTVAL prec)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bignum_set_double(PARROT_INTERP, ARGIN(PMC *self), double value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bignum_set_float(PARROT_INTERP,
    ARGIN(PMC *self),
    FLOATVAL value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bignum_set_si(PARROT_INTERP, ARGIN(PMC *self), long value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bignum_set_str(PARROT_INTERP,
    ARGIN(PMC *self),
    ARGIN(const STRING *value),
    int base)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3);

static void bignum_set_ui(PARROT_INTERP,
    ARGIN(PMC *self),
    unsigned long value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void bignum_sub_bignum(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 bignum_sub_bignum_float(PARROT_INTERP,
    ARGIN(PMC *self),
    FLOATVAL value,
    ARGMOD(PMC *dest))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*dest);

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

#define ASSERT_ARGS_bignum_abs __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_add_bignum __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_bignum_add_bignum_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_add_bignum_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_clear __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_cmp __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value))
#define ASSERT_ARGS_bignum_cmp_double __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_cmp_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_cmp_ulong __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_div_bignum __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_bignum_div_bignum_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_div_bignum_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_fdiv_bignum __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_bignum_fdiv_bignum_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_get_default_prec __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_get_double __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_get_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_get_si __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_get_string __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_get_string_size __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_get_ui __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_init __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_mul_bignum __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_bignum_mul_bignum_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_mul_bignum_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_neg __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_pow_bignum_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_set __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(dest) \
    , PARROT_ASSERT_ARG(src))
#define ASSERT_ARGS_bignum_set_default_prec __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_set_double __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_set_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_set_si __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_set_str __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(value))
#define ASSERT_ARGS_bignum_set_ui __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_bignum_sub_bignum __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_bignum_sub_bignum_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
#define ASSERT_ARGS_bignum_sub_bignum_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(self) \
    , PARROT_ASSERT_ARG(dest))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
/* HEADERIZER END: static */

/* Uncomment to easily disable it */
/*
#ifdef PARROT_HAS_GMP
#  undef PARROT_HAS_GMP
#endif
*/
#undef PARROT_BIGNUM_CAN_BIGINT

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

#ifdef PARROT_HAS_GMP
    BIGNUM * const bn = mem_gc_allocate_zeroed_typed(interp, BIGNUM);
    SETATTR_BigNum_bn(interp, self, bn);
    mpf_clear(bn->b);
    mpf_init(bn->b);
#else
    SETATTR_BigNum_bn(interp, self, NULL);
#endif
}


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

#ifdef PARROT_HAS_GMP
    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_clear(bn->b);
    mem_gc_free(interp, bn);
#endif
}


#ifdef PARROT_HAS_GMP

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

    const BIGNUM *bn_src;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    GETATTR_BigNum_bn(interp, src,  bn_src);
    mpf_set(bn_dest->b, bn_src->b);
}

static void
bignum_set_si(PARROT_INTERP, ARGIN(PMC *self), long value) {
    ASSERT_ARGS(bignum_set_si)

    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_set_si(bn->b, value);
}

static void
bignum_set_ui(PARROT_INTERP, ARGIN(PMC *self), unsigned long value) {
    ASSERT_ARGS(bignum_set_ui)

    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_set_ui(bn->b, value);
}

static void
bignum_set_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value) {
    ASSERT_ARGS(bignum_set_float)

    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_set_d(bn->b, (double)value);
}

static void
bignum_set_double(PARROT_INTERP, ARGIN(PMC *self), double value) {
    ASSERT_ARGS(bignum_set_double)

    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_set_d(bn->b, value);
}

static void
bignum_set_str(PARROT_INTERP, ARGIN(PMC *self), ARGIN(const STRING *value), int base) {
    ASSERT_ARGS(bignum_set_str)

    char * const s = Parrot_str_to_cstring(interp, value);
    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_set_str(bn->b, s, base);
    Parrot_str_free_cstring(s);
}


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

    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    if (mpf_fits_slong_p(bn->b))
        return mpf_get_si(bn->b);

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

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

    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    if (mpf_fits_slong_p(bn->b))
        return mpf_get_ui(bn->b);

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

PARROT_WARN_UNUSED_RESULT
static INTVAL
bignum_get_bool(PARROT_INTERP, ARGIN(PMC *self)) {
    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    if (mpf_sgn(bn->b) != 0)
        return 1;
    else
        return 0;
}

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

    const BIGNUM *bn;
    size_t  n;
    char   *s;
    mp_exp_t exponent;

    GETATTR_BigNum_bn(interp, self, bn);
    n = (mpf_get_prec(bn->b)) / log(base) * log(2);
    s = mem_gc_allocate_n_typed(interp, n + 5, char);
    return mpf_get_str(s, &exponent, base, 0, bn->b);
}

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

    const BIGNUM *bn;
    char   *s;
    mp_exp_t exponent;

    GETATTR_BigNum_bn(interp, self, bn);
    s = mem_gc_allocate_n_typed(interp, digits + 5, char);
    return mpf_get_str(s, &exponent, base, digits, bn->b);
}

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

    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    return mpf_get_d(bn->b);
}

PARROT_WARN_UNUSED_RESULT
static FLOATVAL
bignum_get_float(PARROT_INTERP, ARGIN(PMC *self)) {
    ASSERT_ARGS(bignum_get_float)

    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    return mpf_get_d(bn->b);
}

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

    const BIGNUM *bn_self;
    const BIGNUM *bn_value;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, value, bn_value);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_add(bn_dest->b, bn_self->b, bn_value->b);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    if (value < 0)
        mpf_sub_ui(bn_dest->b, bn_self->b, (unsigned long int)-value);
    else
        mpf_add_ui(bn_dest->b, bn_self->b, (unsigned long int)value);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_value;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_value);
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_set_d(bn_value->b, value);
    mpf_add(bn_dest->b, bn_self->b, bn_value->b);
}

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

    const BIGNUM *bn_self;
    const BIGNUM *bn_value;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, value, bn_value);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_sub(bn_dest->b, bn_self->b, bn_value->b);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    if (value < 0)
        mpf_add_ui(bn_dest->b, bn_self->b, (unsigned long int)-value);
    else
        mpf_sub_ui(bn_dest->b, bn_self->b, (unsigned long int)value);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_tmp;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_tmp);
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_set_d(bn_tmp->b, value);
    mpf_sub(bn_dest->b, bn_self->b, bn_tmp->b);
}

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

    const BIGNUM *bn_self;
    const BIGNUM *bn_value;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, value, bn_value);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_mul(bn_dest->b, bn_self->b, bn_value->b);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_mul_ui(bn_dest->b, bn_self->b, (unsigned long)value);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_tmp;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_tmp);
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_set_d(bn_tmp->b, value);
    mpf_mul(bn_dest->b, bn_self->b, bn_tmp->b);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_pow_ui(bn_dest->b, bn_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
bignum_check_divide_zero(PARROT_INTERP, ARGIN(PMC *value)) {
    /* Throw an exception if we are dividing by zero. */
    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, value, bn);
    if (mpf_cmp_si(bn->b, 0) == 0)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
}

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

    const BIGNUM *bn_self;
    const BIGNUM *bn_value;
    BIGNUM *bn_dest;
    bignum_check_divide_zero(interp, value);
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, value, bn_value);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_div(bn_dest->b, bn_self->b, bn_value->b);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    int_check_divide_zero(interp, value);

    /* this is mpz_fdiv_q */
    if (value < 0) {
        mpf_div_ui(bn_dest->b, bn_self->b, (unsigned long int)-value);
        mpf_neg(bn_dest->b, bn_dest->b);
    }
    else
        mpf_div_ui(bn_dest->b, bn_self->b, (unsigned long int)value);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    BIGNUM bn;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    int_check_divide_zero(interp, value);
    mpf_init(bn.b);

    if (value < 0) {
        mpf_set_d(bn.b, -value);
        mpf_div(bn_dest->b, bn_self->b, bn.b);
        mpf_neg(bn_dest->b, bn_dest->b);
    }
    else {
        mpf_set_d(bn.b, value);
        mpf_div(bn_dest->b, bn_self->b, bn.b);
    }
    mpf_clear(bn.b);
}

/* There's no such mpf_fdiv, only mpz_fdiv and mpf_div */
static void
bignum_fdiv_bignum(PARROT_INTERP, ARGIN(PMC *self), ARGIN(PMC *value), ARGMOD(PMC *dest)) {
    ASSERT_ARGS(bignum_fdiv_bignum)

    const BIGNUM *bn_self;
    const BIGNUM *bn_value;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, value, bn_value);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    bignum_check_divide_zero(interp, value);
    mpf_div(bn_dest->b, bn_self->b, bn_value->b);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    int_check_divide_zero(interp, value);

    if (value < 0) {
        mpf_div_ui(bn_dest->b, bn_self->b, (unsigned long int)-value);
        mpf_neg(bn_dest->b, bn_dest->b);
    }
    else
        mpf_div_ui(bn_dest->b, bn_self->b, (unsigned long int)value);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_value;
    GETATTR_BigNum_bn(interp, self,  bn_self);
    GETATTR_BigNum_bn(interp, value, bn_value);
    return mpf_cmp(bn_self->b, bn_value->b);
}

static INTVAL
bignum_cmp_double(PARROT_INTERP, ARGIN(PMC *self), double value) {
    ASSERT_ARGS(bignum_cmp_double)

    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    return mpf_cmp_d(bn->b, value);
}

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

    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    return mpf_cmp_si(bn->b, value);
}

static INTVAL
bignum_cmp_ulong(PARROT_INTERP, ARGIN(PMC *self), unsigned long value) {
    ASSERT_ARGS(bignum_cmp_ulong)

    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    return mpf_cmp_ui(bn->b, value);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_abs(bn_dest->b, bn_self->b);
}

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

    const BIGNUM *bn_self;
    BIGNUM *bn_dest;
    GETATTR_BigNum_bn(interp, self, bn_self);
    GETATTR_BigNum_bn(interp, dest, bn_dest);
    mpf_neg(bn_dest->b, bn_self->b);
}

static INTVAL
bignum_get_default_prec(PARROT_INTERP, ARGIN(PMC *self)) {
    ASSERT_ARGS(bignum_get_default_prec)

    return mpf_get_default_prec();
}

static void
bignum_set_default_prec(PARROT_INTERP, ARGIN(PMC *self), INTVAL prec) {
    ASSERT_ARGS(bignum_set_default_prec)

    mpf_set_default_prec(prec);
}

/* HEADERIZER STOP */
/* Don't headerize these BIGINT and BIGNUM funcs */

PARROT_WARN_UNUSED_RESULT
PARROT_CANNOT_RETURN_NULL
static BIGNUM*
bignum_get_self(PARROT_INTERP, ARGIN(PMC *self)) {
    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    return bn;
}

static void
bignum_set_self(PARROT_INTERP, ARGIN(PMC *self), ARGIN(BIGNUM *value)) {
    BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_set(bn->b, (mpf_srcptr)(value)->b);
}


#  ifdef PARROT_BIGNUM_CAN_BIGINT
PARROT_WARN_UNUSED_RESULT
static struct BIGINT
bignum_get_bigint(PARROT_INTERP, ARGIN(PMC *self)) {
    const BIGNUM *bn;
    struct BIGINT *bi_dest;
    GETATTR_BigNum_bn(interp, self, bn);
    mpz_clear(bi_dest->b);
    mpz_init(bi_dest->b);
    if (mpf_fits_slong_p(bn->b)) {
        bi_dest->b = mpf_get_ui(bn->b);
    }
    else {
        Parrot_ex_throw_from_c_args(interp, NULL, 1,
            "bignum_get_bigint: Precision loss");
    }
    return bi_dest;
}

static void
bignum_set_bigint(PARROT_INTERP, ARGIN(PMC *self), ARGIN(struct BIGINT *value)) {
    const BIGNUM *bn;
    GETATTR_BigNum_bn(interp, self, bn);
    mpf_set(bn->b, (mpf_srcptr)value->b);
}
#  endif

#else /* ifdef PARROT_HAS_GMP */

typedef struct BIGNUM {
    FLOATVAL b; /* bogus definition for users without libgmp*/
} BIGNUM;

#  define THROW_NYI Parrot_ex_throw_from_c_args(interp, NULL, \
                        EXCEPTION_LIBRARY_ERROR, "no bignum lib loaded")

static void
bignum_set(PARROT_INTERP, ARGMOD(PMC *dest), PMC *src) {
    THROW_NYI;
}

static void
bignum_set_si(PARROT_INTERP, ARGIN(PMC *self), long value) {
    THROW_NYI;
}

static void
bignum_set_double(PARROT_INTERP, ARGIN(PMC *self), double value) {
    THROW_NYI;
}

static void
bignum_set_str(PARROT_INTERP, ARGIN(PMC *self), ARGIN(const STRING *value), int base) {
    THROW_NYI;
}

static void
bignum_set_self(PARROT_INTERP, ARGIN(PMC *self), BIGNUM *value) {
    THROW_NYI;
}

static BIGNUM*
bignum_get_self(PARROT_INTERP, ARGIN(PMC *self)) {
    THROW_NYI;
}

static char *
bignum_get_string(PARROT_INTERP, ARGIN(PMC *self), int base) {
    THROW_NYI;
}

static char *
bignum_get_string_size(PARROT_INTERP, ARGIN(PMC *self), int base, int digits) {
    THROW_NYI;
}

static unsigned long
bignum_get_ui(PARROT_INTERP, ARGIN(PMC *self)) {
    THROW_NYI;
}

static long
bignum_get_si(PARROT_INTERP, ARGIN(PMC *self)) {
    THROW_NYI;
}

static INTVAL
bignum_get_bool(PARROT_INTERP, ARGIN(PMC *self)) {
    THROW_NYI;
}

static double
bignum_get_double(PARROT_INTERP, ARGIN(PMC *self)) {
    THROW_NYI;
}

static void
bignum_add_bignum(PARROT_INTERP, ARGIN(PMC *self), PMC *value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_add_bignum_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_add_bignum_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_sub_bignum(PARROT_INTERP, ARGIN(PMC *self), PMC *value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_sub_bignum_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_sub_bignum_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_mul_bignum(PARROT_INTERP, ARGIN(PMC *self), PMC *value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_mul_bignum_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_mul_bignum_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_pow_bignum_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_pow_bignum_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_div_bignum(PARROT_INTERP, ARGIN(PMC *self), PMC *value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_div_bignum_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_div_bignum_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_fdiv_bignum(PARROT_INTERP, ARGIN(PMC *self), PMC *value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_fdiv_bignum_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_fdiv_bignum_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value, ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static INTVAL
bignum_cmp(PARROT_INTERP, ARGIN(PMC *self), PMC *value) {
    THROW_NYI;
}

static INTVAL
bignum_cmp_int(PARROT_INTERP, ARGIN(PMC *self), INTVAL value) {
    THROW_NYI;
}

static INTVAL
bignum_cmp_float(PARROT_INTERP, ARGIN(PMC *self), FLOATVAL value) {
    THROW_NYI;
}

static void
bignum_abs(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static void
bignum_neg(PARROT_INTERP, ARGIN(PMC *self), ARGMOD(PMC *dest)) {
    THROW_NYI;
}

static INTVAL
bignum_get_default_prec(PARROT_INTERP, ARGIN(PMC *self)) {
    THROW_NYI;
}

static void
bignum_set_default_prec(PARROT_INTERP, ARGIN(PMC *self), INTVAL prec) {
    THROW_NYI;
}

#  undef THROW_NYI

#endif /* ifdef PARROT_HAS_GMP */

pmclass BigNum auto_attrs {
    ATTR struct BIGNUM * bn; /*bignum 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 = Parrot_str_new(INTERP, "0.0.0", 5);
#endif
        RETURN(STRING *version);
    }

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

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

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

/*

=item C<void set_integer_native(INTVAL value)>

=cut

*/

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

/*

=item C<void set_number_native(FLOATVAL value)>

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

=cut

*/

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

/*

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

Sets the value of the BigNum 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) {
        bignum_set_str(INTERP, SELF, value, 10);
    }

    VTABLE void set_string_keyed_int(INTVAL base, STRING *value) {
        bignum_set_str(INTERP, SELF, value, base);
    }
/*

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

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

=cut

*/

    VTABLE void set_pmc(PMC *value) {
        bignum_set_self(INTERP, SELF, bignum_get_self(INTERP, value));
    }

/*

=item C<FLOATVAL get_number()>

Down-converts the precise BigNum to an imprecise double.

=cut

*/

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

/*

=item C<INTVAL get_integer()>

Returns the integer conversion of the BigNum.

=cut

*/

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

/*

=item C<INTVAL get_ulong()>

Returns the unsigned long conversion of the BigNum.

=cut

*/

    VTABLE INTVAL get_ulong() {
        return bignum_get_ui(INTERP, SELF);
    }

/*

=item C<FLOATVAL get_bigint()>

Trunc the BigNum to an BigInt.

=cut

*/

    VTABLE BIGINT get_bigint() {
#if PARROT_BIGNUM_CAN_BIGINT
        return bignum_get_bigint(INTERP, SELF);
#else
        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_LIBRARY_ERROR,
            "no bigint support in bignum");
#endif
    }

/*

=item C<INTVAL get_bool()>

Returns the boolean value of the BigNum.

=cut

*/

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

/*

=item C<STRING *get_string()>

Returns the string representation of the BigNum.

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

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

=item C<STRING *get_string_keyed_int_int(INTVAL base, INTVAL digits)>

Returns the string representation of the BigNum in base C<base> with
C<digits> digits.

=item C<STRING *get_repr()>

Returns the string representation of the BigNum with the letter 'N'
appended.

=cut

*/

    VTABLE STRING *get_string() {
        char   * const s  = bignum_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  = bignum_get_string(INTERP, SELF, base);
        STRING * const ps = Parrot_str_new(INTERP, s, 0);
        mem_gc_free(INTERP, s);
        return ps;
    }

    VTABLE STRING *get_string_keyed_int_int(INTVAL base, INTVAL digits) {
        char   * const s  = bignum_get_string_size(INTERP, SELF, base, digits);
        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, Parrot_str_new(INTERP, "N", 1));
    }
/*

=item C<void increment()>

Increment the BigNum by 1.0.

=cut

*/

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

/*

=item C<void decrement()>

Decrement the BigNum by 1.0.

=cut

*/

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

/*

=item C<void add()>

=cut

*/

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

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

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

        bignum_add_bignum_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,
            "BigNum: no multiple dispatch variant 'add' for %Ss",
            VTABLE_name(INTERP, value));
    }

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

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

    MULTI void i_add(BigNum value) {
        bignum_add_bignum(INTERP, SELF, value, SELF);
    }

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

    MULTI void i_add(Float value) {
        bignum_add_bignum_float(INTERP, SELF, VTABLE_get_number(INTERP, value), SELF);
    }

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

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

    VTABLE void i_add_float(FLOATVAL value) {
        bignum_add_bignum_float(INTERP, SELF, value, SELF);
    }

/*

=item C<void substract()>

=cut

*/

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

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

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

        bignum_sub_bignum_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,
            "BigNum: 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));

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

    MULTI void i_subtract(BigNum value) {
        bignum_sub_bignum(INTERP, SELF, value, SELF);
    }

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

    MULTI void i_subtract(Float value) {
        bignum_sub_bignum_float(INTERP, SELF, VTABLE_get_number(INTERP, value), SELF);
    }

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

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

    VTABLE void i_subtract_float(FLOATVAL value) {
        bignum_sub_bignum_float(INTERP, SELF, value, SELF);
    }

/*

=item C<void multiply()>

=cut

*/

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

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

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

        bignum_mul_bignum_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,
            "BigNum: no multiple dispatch variant 'multiply' for %Ss",
            VTABLE_name(INTERP, value));
    }

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

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

    MULTI void i_multiply(BigNum value) {
        bignum_mul_bignum(INTERP, SELF, value, SELF);
    }

    MULTI void i_multiply(Float value) {
        bignum_mul_bignum_float(INTERP, SELF, VTABLE_get_number(INTERP, value), SELF);
    }

    MULTI void i_multiply(Integer value) {
        bignum_mul_bignum_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,
            "BigNum: no multiple dispatch variant 'i_multiply' for %Ss",
            VTABLE_name(INTERP, value));
    }

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

    VTABLE void i_multiply_float(FLOATVAL value) {
        bignum_mul_bignum_float(INTERP, SELF, value, SELF);
    }

/*

=item C<METHOD PMC *pow()>

Raises self to the power of value. Replacement method added when the pow()
vtable was removed

TODO: Needs testing.
TODO: Only supports Integer values for now.

=cut

*/

    METHOD PMC *pow(PMC *value) {
        /* only Integer RHS currently. TODO: check number and bignum types */
        const INTVAL r = VTABLE_get_integer(INTERP, value);
        PMC * const dest = Parrot_pmc_new(INTERP, SELF->vtable->base_type);

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

/*

=item C<void divide()>

=cut

*/

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

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

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

        bignum_div_bignum_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,
            "BigNum: 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));

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

    VTABLE PMC *divide_float(FLOATVAL value, PMC *dest) {
        dest = Parrot_pmc_new(INTERP, VTABLE_type(INTERP, SELF));

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

    MULTI void i_divide(BigNum value) {
        bignum_div_bignum(INTERP, SELF, value, SELF);
    }
    MULTI void i_divide(Integer value) {
        bignum_div_bignum_int(INTERP, SELF, VTABLE_get_integer(INTERP, value), SELF);
    }
    MULTI void i_divide(Float value) {
        bignum_div_bignum_float(INTERP, SELF, VTABLE_get_number(INTERP, value), SELF);
    }
    MULTI void i_divide(DEFAULT value) {
         Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INTERNAL_NOT_IMPLEMENTED,
            "BigNum: no multiple dispatch variant 'i_divide' for %Ss",
            VTABLE_name(INTERP, value));
    }

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

/*

=item C<void floor_divide()>

=cut

*/

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

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

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

        bignum_fdiv_bignum_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,
            "BigNum: 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));

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

    MULTI void i_floor_divide(BigNum value) {
        bignum_fdiv_bignum(INTERP, SELF, value, SELF);
    }

    MULTI void i_floor_divide(Integer value) {
        bignum_fdiv_bignum_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,
            "BigNum: no multiple dispatch variant 'i_floor_divide' for %Ss",
            VTABLE_name(INTERP, value));
    }

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

/*

=item C<void cmp()>

=cut

*/

    MULTI INTVAL cmp(BigNum value) {
        return bignum_cmp(INTERP, SELF, value);
    }

    MULTI INTVAL cmp(Integer value) {
        return bignum_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,
            "BigNum: no multiple dispatch variant 'cmp' for %Ss",
            VTABLE_name(INTERP, value));
    }

/*

=item C<void is_equal()>

=cut

*/

    MULTI INTVAL is_equal(BigNum value) {
        return bignum_cmp(INTERP, SELF, value) == 0;
    }

    MULTI INTVAL is_equal(Integer value) {
        return bignum_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,
            "BigNum: 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, SELF->vtable->base_type);

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

    VTABLE void i_absolute() {
        bignum_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, SELF->vtable->base_type);

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

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

}

/*

=back

=cut

*/

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