The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "gmp.h"

typedef mpz_t mpz_t_ornull;

/* for Perl prior to v5.7.1 */
#ifndef SvUOK
#  define SvUOK(sv) SvIOK_UV(sv)
#endif

#ifndef PERL_UNUSED_ARG
#  define PERL_UNUSED_ARG(x) ((void)x)
#endif

#ifndef gv_stashpvs
#  define gv_stashpvs(name, create) gv_stashpvn(name, sizeof(name) - 1, create)
#endif

#ifndef PERL_MAGIC_ext
#  define PERL_MAGIC_ext '~'
#endif

#if defined(USE_ITHREADS) && defined(MGf_DUP)
#  define GMP_THREADSAFE 1
#else
#  define GMP_THREADSAFE 0
#endif

#ifdef sv_magicext
#  define GMP_HAS_MAGICEXT 1
#else
#  define GMP_HAS_MAGICEXT 0
#endif

#define NEW_GMP_MPZ_T      RETVAL = malloc (sizeof(mpz_t));
#define NEW_GMP_MPZ_T_INIT RETVAL = malloc (sizeof(mpz_t)); mpz_init(*RETVAL);
#define GMP_GET_ARG_0      TEMP = mpz_from_sv(x);
#define GMP_GET_ARG_1      TEMP_1 = mpz_from_sv(y);
#define GMP_GET_ARGS_0_1   GMP_GET_ARG_0; GMP_GET_ARG_1;

#if GMP_THREADSAFE
STATIC int
dup_gmp_mpz (pTHX_ MAGIC *mg, CLONE_PARAMS *params)
{
  mpz_t *RETVAL;
  PERL_UNUSED_ARG(params);
  NEW_GMP_MPZ_T;
  mpz_init_set(*RETVAL, *((mpz_t *)mg->mg_ptr));
  mg->mg_ptr = (char *)RETVAL;
  return 0;
}
#endif

#if GMP_HAS_MAGICEXT
STATIC MGVTBL vtbl_gmp = {
  NULL, /* get */
  NULL, /* set */
  NULL, /* len */
  NULL, /* clear */
  NULL, /* free */
# ifdef MGf_COPY
  NULL, /* copy */
# endif
# ifdef MGf_DUP
#  if GMP_THREADSAFE
  dup_gmp_mpz,
#  else
  NULL, /* dup */
#  endif
# endif
# ifdef MGf_LOCAL
  NULL, /* local */
# endif
};
#endif

STATIC void
attach_mpz_to_sv (SV *sv, mpz_t *mpz)
{
#if GMP_THREADSAFE
  MAGIC *mg;
#endif
#if !GMP_HAS_MAGICEXT
  SV *refaddr = sv_2mortal(newSViv(PTR2IV(mpz)));
#endif

  sv_bless(sv, gv_stashpvs("Math::BigInt::GMP", 0));

#if GMP_THREADSAFE && GMP_HAS_MAGICEXT
  mg =
#endif
#if GMP_HAS_MAGICEXT
    sv_magicext(SvRV(sv), NULL, PERL_MAGIC_ext, &vtbl_gmp, (void *)mpz, 0);
#else
  sv_magic(SvRV(sv), NULL, PERL_MAGIC_ext, (void *)refaddr, HEf_SVKEY);
#endif

#if GMP_THREADSAFE && GMP_HAS_MAGICEXT
  mg->mg_flags |= MGf_DUP;
#endif
}

STATIC SV *
sv_from_mpz (mpz_t *mpz)
{
  SV *sv = newSV(0);
  SV *obj = newRV_noinc(sv);

  attach_mpz_to_sv(obj, mpz);

  return obj;
}

STATIC mpz_t *
mpz_from_sv_nofail (SV *sv)
{
  MAGIC *mg;

  if (!sv_derived_from(sv, "Math::BigInt::GMP"))
    croak("not of type Math::BigInt::GMP");

  for (mg = SvMAGIC(SvRV(sv)); mg; mg = mg->mg_moremagic) {
    if (mg->mg_type == PERL_MAGIC_ext
#if GMP_HAS_MAGICEXT
        && mg->mg_virtual == &vtbl_gmp
#endif
        ) {
#if GMP_HAS_MAGICEXT
      return (mpz_t *)mg->mg_ptr;
#else
      return INT2PTR(mpz_t *, SvIV((SV *)mg->mg_ptr));
#endif
    }
  }

  return (mpz_t *)NULL;
}

STATIC mpz_t *
mpz_from_sv (SV *sv)
{
  mpz_t *mpz;

  if (!(mpz = mpz_from_sv_nofail(sv)))
    croak("failed to fetch mpz pointer");

  return mpz;
}

/*
Math::BigInt::GMP XS code, loosely based on Math::GMP, a Perl module for
high-speed arbitrary size integer calculations (C) 2000 James H. Turner
*/

MODULE = Math::BigInt::GMP              PACKAGE = Math::BigInt::GMP
PROTOTYPES: ENABLE

##############################################################################
# _new()

mpz_t *
_new(Class,x)
        SV*     x

  CODE:
    NEW_GMP_MPZ_T;
    /* using the IV directly is a bit faster */
    if (SvUOK(x))
      {
      mpz_init_set_si(*RETVAL, (UV)SvUV(x));
      }
    else
      {
      mpz_init_set_str(*RETVAL, SvPV_nolen(x), 10);
      }
  OUTPUT:
    RETVAL

##############################################################################
# _new_attach()

void
_new_attach(Class,sv,x)
    SV *sv
    SV *x
  PREINIT:
    mpz_t *mpz;
  CODE:
    mpz = malloc (sizeof(mpz_t));
    if (SvUOK(x)) {
      mpz_init_set_si(*mpz, (UV)SvUV(x));
    }
    else {
      mpz_init_set_str(*mpz, SvPV_nolen(x), 10);
    }
    attach_mpz_to_sv(sv, mpz);

##############################################################################
# _from_bin()

mpz_t *
_from_bin(Class,x)
        SV*     x

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_str(*RETVAL, SvPV_nolen(x), 0);
  OUTPUT:
    RETVAL

##############################################################################
# _from_hex()

mpz_t *
_from_hex(Class,x)
        SV*     x

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_str(*RETVAL, SvPV_nolen(x), 0);
  OUTPUT:
    RETVAL

##############################################################################
# _from_oct()

mpz_t *
_from_oct(Class,x)
        SV*     x

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_str(*RETVAL, SvPV_nolen(x), 0);
  OUTPUT:
    RETVAL

##############################################################################
# _set() - set an already existing object to the given scalar value

void
_set(Class,n,x)
        mpz_t*  n
        SV*     x

  CODE:
    mpz_init_set_ui(*n, SvIV(x));

##############################################################################
# _zero()

mpz_t *
_zero(Class)

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_ui(*RETVAL, 0);
  OUTPUT:
    RETVAL

##############################################################################
# _one()

mpz_t *
_one(Class)

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_ui(*RETVAL, 1);
  OUTPUT:
    RETVAL

##############################################################################
# _two()

mpz_t *
_two(Class)

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_ui(*RETVAL, 2);
  OUTPUT:
    RETVAL

##############################################################################
# _ten()

mpz_t *
_ten(Class)

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_ui(*RETVAL, 10);
  OUTPUT:
    RETVAL

##############################################################################
# _1ex()

mpz_t *
_1ex(Class,x)
    int x;

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set_ui(*RETVAL, 10);
    mpz_pow_ui(*RETVAL, *RETVAL, x);
  OUTPUT:
    RETVAL

##############################################################################
# DESTROY() - free memory of a GMP number

void
DESTROY(n)
        mpz_t_ornull*   n

  PPCODE:
    if (n) {
        mpz_clear(*n);
        free(n);
    }

##############################################################################
# _str() - return string so that atof() and atoi() can use it

SV *
_str(Class, n)
        mpz_t*  n
  PREINIT:
    int len;
    char *buf;
    char *buf_end;

  CODE:
    /* len is always >= 1, and might be off (greater) by one than real len */
    len = mpz_sizeinbase(*n, 10);
    RETVAL = newSV(len);                /* alloc len +1 bytes */
    SvPOK_on(RETVAL);
    buf = SvPVX(RETVAL);                /* get ptr to storage */
    buf_end = buf + len - 1;            /* end of storage (-1)*/
    mpz_get_str(buf, 10, *n);           /* convert to decimal string */
    if (*buf_end == 0)
      {
      len --;                           /* got one shorter than expected */
      }
    SvCUR_set(RETVAL, len);             /* so set real length */
   OUTPUT:
     RETVAL

##############################################################################
# _len() - return the length of the number in base 10 (costly)

int
_len(Class, n)
        mpz_t*  n
  PREINIT:
    char *buf;
    char *buf_end;

  CODE:
    /* len is always >= 1, and might be off (greater) by one than real len */
    RETVAL = mpz_sizeinbase(*n, 10);
    if (RETVAL > 1)                     /* is at least 10? */
      {
      New(0, buf, RETVAL + 1, I8);      /* alloc scratch buffer (len+1) bytes */
      buf_end = buf + RETVAL - 1;       /* end of storage (-1)*/
      mpz_get_str(buf, 10, *n);         /* convert to decimal string */
      if (*buf_end == 0)
        {
        RETVAL --;                      /* got one shorter than expected */
        }
      Safefree(buf);                    /* free the scratch buffer */
      }
   OUTPUT:
     RETVAL

##############################################################################
# _alen() - return the approx. length of the number in base 10 (fast)

int
_alen(Class, n)
        mpz_t*  n

  CODE:
    /* len is always >= 1, and might be off (greater) by one than real len */
    RETVAL = mpz_sizeinbase(*n, 10);
   OUTPUT:
     RETVAL

##############################################################################
# _zeros() - return number of trailing zeros (in decimal form)
# This is costly, since it needs O(N*N) to convert the number to decimal,
# even though for most cases the number does not have many trailing zeros.
# For numbers longer than X digits (10?) we could divide repeatable by 1e5
# or something and see if we get zeros.

int
_zeros(Class,n)
        mpz_t*  n

  PREINIT:
    int len;
    char *buf;
    char *buf_end;

  CODE:
    /* odd numbers can not have trailing zeros */
    RETVAL = 1 - mpz_tstbit(*n,0);

    if (RETVAL != 0)                    /* was even */
      {
      /* len is always >= 1, and might be off (greater) by one than real len */
      RETVAL = 0;
      len = mpz_sizeinbase(*n, 10);
      if (len > 1)                      /* '0' has no trailing zeros! */
        {
        New(0, buf, len + 1, I8);
        mpz_get_str(buf, 10, *n);       /* convert to decimal string */
        buf_end = buf + len - 1;
        if (*buf_end == 0)              /* points to terminating zero? */
          {
          buf_end--;                    /* ptr to last real digit */
          len--;                        /* got one shorter than expected */
          }
        while (len-- > 0)               /* actually, we should hit a non-zero before the end */
          {
          if (*buf_end-- != '0')
            {
            break;
            }
          RETVAL++;
          }
        Safefree(buf);                  /* free the scratch buffer */
        }
      } /* end if n was even */
  OUTPUT:
    RETVAL

##############################################################################
# _as_hex() - return ref to hexadecimal string (prefixed with 0x)

SV *
_as_hex(Class,n)
        mpz_t * n

  PREINIT:
    int len;
    char *buf;

  CODE:
    /* len is always >= 1, and accurate (unlike in decimal) */
    len = mpz_sizeinbase(*n, 16) + 2;
    RETVAL = newSV(len);                /* alloc len +1 (+2 for '0x') bytes */
    SvPOK_on(RETVAL);
    buf = SvPVX(RETVAL);                /* get ptr to storage */
    *buf++ = '0'; *buf++ = 'x';         /* prepend '0x' */
    mpz_get_str(buf, 16, *n);           /* convert to hexadecimal string */
    SvCUR_set(RETVAL, len);             /* so set real length */
  OUTPUT:
    RETVAL

##############################################################################
# _as_bin() - return ref to binary string (prefixed with 0b)

SV *
_as_bin(Class,n)
        mpz_t * n

  PREINIT:
    int len;
    char *buf;

  CODE:
    /* len is always >= 1, and accurate (unlike in decimal) */
    len = mpz_sizeinbase(*n, 2) + 2;
    RETVAL = newSV(len);                /* alloc len +1 (+2 for '0b') bytes */
    SvPOK_on(RETVAL);
    buf = SvPVX(RETVAL);                /* get ptr to storage */
    *buf++ = '0'; *buf++ = 'b';         /* prepend '0b' */
    mpz_get_str(buf, 2, *n);            /* convert to binary string */
    SvCUR_set(RETVAL, len);             /* so set real length */
  OUTPUT:
    RETVAL

##############################################################################
# _as_oct() - return ref to octal string (prefixed with 0)

SV *
_as_oct(Class,n)
        mpz_t * n

  PREINIT:
    int len;
    char *buf;

  CODE:
    /* len is always >= 1, and accurate (unlike in decimal) */
    len = mpz_sizeinbase(*n, 8) + 1;
    RETVAL = newSV(len);                /* alloc len +1 (+1 for '0') bytes */
    SvPOK_on(RETVAL);
    buf = SvPVX(RETVAL);                /* get ptr to storage */
    *buf++ = '0';                       /* prepend '0' */
    mpz_get_str(buf, 8, *n);            /* convert to binary string */
    SvCUR_set(RETVAL, len);             /* so set real length */
  OUTPUT:
    RETVAL

##############################################################################
# _modpow() - ($n ** $exp) % $mod

mpz_t *
_modpow(Class, n, exp, mod)
       mpz_t*   n
       mpz_t*   exp
       mpz_t*   mod

  CODE:
    NEW_GMP_MPZ_T_INIT;
    mpz_powm(*RETVAL, *n, *exp, *mod);
  OUTPUT:
    RETVAL

##############################################################################
# _modinv() - compute the inverse of x % y
#
# int mpz_invert (mpz_t rop, mpz_t op1, mpz_t op2)      Function
# Compute the inverse of op1 modulo op2 and put the result in rop. If the
# inverse exists, the return value is non-zero and rop will satisfy
# 0 <= rop < op2. If an inverse doesn't exist the return value is zero and rop
# is undefined.

void
_modinv(Class,x,y)
        mpz_t*  x
        mpz_t*  y

  PREINIT:
    int rc, sign;
    SV* s;
    mpz_t* RETVAL;
  PPCODE:
    NEW_GMP_MPZ_T_INIT;
    rc = mpz_invert(*RETVAL, *x, *y);
    EXTEND(SP, 2);      /* we return two values */
    if (rc == 0)
      {
      /* Inverse doesn't exist. Return both values undefined. */
      PUSHs ( &PL_sv_undef );
      PUSHs ( &PL_sv_undef );
      }
    else
      {
      /* Inverse exists. When the modulus to mpz_invert() is positive,
       * the returned value is also positive. */
      PUSHs(sv_2mortal(sv_from_mpz(RETVAL)));
        s = sv_newmortal();
        sv_setpvn (s, "+", 1);
      PUSHs ( s );
      }

##############################################################################
# _add() - add $y to $x in place

void
_add(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_add(*TEMP, *TEMP, *TEMP_1);
    PUSHs( x );


##############################################################################
# _inc() - modify x inline by doing x++

void
_inc(Class,x)
        SV*     x
  PREINIT:
        mpz_t* TEMP;
  PPCODE:
    GMP_GET_ARG_0;      /* TEMP =  mpz_t(x)  */
    mpz_add_ui(*TEMP, *TEMP, 1);
    PUSHs( x );

##############################################################################
# _dec() - modify x inline by doing x--

void
_dec(Class,x)
        SV*     x
  PREINIT:
        mpz_t* TEMP;
  PPCODE:
    GMP_GET_ARG_0;      /* TEMP =  mpz_t(x)  */
    mpz_sub_ui(*TEMP, *TEMP, 1);
    PUSHs( x );

##############################################################################
# _sub() - $x - $y
# $x is always larger than $y! So overflow/underflow can not happen here.
# Formerly this code was:
# # if ($_[3])
#    {
#    $_[2] = Math::BigInt::GMP::sub_two($_[1],$_[2]); return $_[2];
#    }
#  Math::BigInt::GMP::_sub_in_place($_[1],$_[2]);
#  }

void
_sub(Class,x,y, ...)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    if ( items == 4 && SvTRUE(ST(3)) )
      {
      /* y -= x */
      mpz_sub(*TEMP_1, *TEMP, *TEMP_1);
      PUSHs( y );
      }
    else
      {
      /* x -= y */
      mpz_sub(*TEMP, *TEMP, *TEMP_1);
      PUSHs( x );
      }

##############################################################################
# _rsft()

void
_rsft(Class,x,y,base_sv)
        SV*     x
        SV*     y
        SV*     base_sv
  PREINIT:
        unsigned long   y_ui;
        mpz_t*  TEMP;
        mpz_t*  TEMP_1;
        mpz_t*  BASE;

  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */

    y_ui = mpz_get_ui(*TEMP_1);
    BASE = malloc (sizeof(mpz_t));
    mpz_init_set_ui(*BASE,SvUV(base_sv));

    mpz_pow_ui(*BASE, *BASE, y_ui); /* ">> 3 in base 4" => "x / (4 ** 3)" */
    mpz_div(*TEMP, *TEMP, *BASE);
    mpz_clear(*BASE);
    free(BASE);
    PUSHs( x );

##############################################################################
# _lsft()

void
_lsft(Class,x,y,base_sv)
        SV*     x
        SV*     y
        SV*     base_sv
  PREINIT:
        unsigned long   y_ui;
        mpz_t*  TEMP;
        mpz_t*  TEMP_1;
        mpz_t*  BASE;

  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */

    y_ui = mpz_get_ui(*TEMP_1);
    BASE = malloc (sizeof(mpz_t));
    mpz_init_set_ui(*BASE,SvUV(base_sv));

    mpz_pow_ui(*BASE, *BASE, y_ui); /* "<< 3 in base 4" => "x * (4 ** 3)" */
    mpz_mul(*TEMP, *TEMP, *BASE);
    mpz_clear(*BASE);
    free(BASE);
    PUSHs ( x );

##############################################################################
# _mul()

void
_mul(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_mul(*TEMP, *TEMP, *TEMP_1);
    PUSHs( x );

##############################################################################
# _div(): x /= y or (x,rem) = x / y
# was in perl:
#sub _div
#  {
#  i f (wantarray)
#    {
#    # return (a/b,a%b)
#    my $r;
#    ($_[1],$r) = Math::BigInt::GMP::bdiv_two($_[1],$_[2]);
#    return ($_[1], $r);
#    }
#  # return a / b
#  Math::BigInt::GMP::div_two($_[1],$_[2]);
#  }

void
_div(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
    mpz_t* TEMP;
    mpz_t* TEMP_1;
    mpz_t * rem;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    if (GIMME_V == G_ARRAY)
      {
      /* former bdiv_two() routine */
      rem = malloc (sizeof(mpz_t));
      mpz_init(*rem);
      mpz_tdiv_qr(*TEMP, *rem, *TEMP, *TEMP_1);
      EXTEND(SP, 2);
      PUSHs( x );
      PUSHs(sv_2mortal(sv_from_mpz(rem)));
      }
    else
      {
      /* former div_two() routine */
      mpz_div(*TEMP, *TEMP, *TEMP_1);                   /* x /= y */
      PUSHs( x );
      }

##############################################################################
# _mod() - x %= y

void
_mod(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_mod(*TEMP, *TEMP, *TEMP_1);
    PUSHs( x );

##############################################################################
# _acmp() - cmp two numbers

int
_acmp(Class,m,n)
        mpz_t * m
        mpz_t * n

  CODE:
    RETVAL = mpz_cmp(*m, *n);
    if ( RETVAL < 0) { RETVAL = -1; }
    if ( RETVAL > 0) { RETVAL = 1; }
  OUTPUT:
    RETVAL

##############################################################################
# _is_zero()

int
_is_zero(Class,x)
        mpz_t * x

  CODE:
    RETVAL = mpz_cmp_ui(*x, 0);
    if ( RETVAL != 0) { RETVAL = 0; } else { RETVAL = 1; }
  OUTPUT:
    RETVAL

##############################################################################
# _is_one()

int
_is_one(Class,x)
        mpz_t * x

  CODE:
    RETVAL = mpz_cmp_ui(*x, 1);
    if ( RETVAL != 0) { RETVAL = 0; } else { RETVAL = 1; }
  OUTPUT:
    RETVAL

##############################################################################
# _is_two()

int
_is_two(Class,x)
        mpz_t * x

  CODE:
    RETVAL = mpz_cmp_ui(*x, 2);
    if ( RETVAL != 0) { RETVAL = 0; } else { RETVAL = 1; }
  OUTPUT:
    RETVAL

##############################################################################
# _is_ten()

int
_is_ten(Class,x)
        mpz_t * x

  CODE:
    RETVAL = mpz_cmp_ui(*x, 10);
    if ( RETVAL != 0) { RETVAL = 0; } else { RETVAL = 1; }
  OUTPUT:
    RETVAL

##############################################################################
# _pow() - x **= y

void
_pow(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_pow_ui(*TEMP, *TEMP, mpz_get_ui( *TEMP_1 ) );
    PUSHs( x );

##############################################################################
# _gcd() - gcd(m,n)

mpz_t *
_gcd(Class,x,y)
        mpz_t*  x
        mpz_t*  y

  CODE:
    NEW_GMP_MPZ_T_INIT;
    mpz_gcd(*RETVAL, *x, *y);
  OUTPUT:
    RETVAL

##############################################################################
# _and() - m &= n

void
_and(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_and(*TEMP, *TEMP, *TEMP_1);
    PUSHs( x );


##############################################################################
# _xor() - m =^ n

void
_xor(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_xor(*TEMP, *TEMP, *TEMP_1);
    PUSHs( x );


##############################################################################
# _or() - m =| n

void
_or(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_ior(*TEMP, *TEMP, *TEMP_1);
    PUSHs( x );


##############################################################################
# _fac() - n! (factorial)

void
_fac(Class,x)
        SV*     x
  PREINIT:
        mpz_t* TEMP;
  PPCODE:
    GMP_GET_ARG_0;   /* TEMP = x */
    mpz_fac_ui(*TEMP, mpz_get_ui(*TEMP));
    PUSHs( x );


##############################################################################
# _copy()

mpz_t *
_copy(Class,m)
        mpz_t*  m

  CODE:
    NEW_GMP_MPZ_T;
    mpz_init_set(*RETVAL, *m);
  OUTPUT:
    RETVAL


##############################################################################
# _is_odd() - test for number being odd

int
_is_odd(Class,n)
        mpz_t*  n

  CODE:
   RETVAL = mpz_tstbit(*n,0);
  OUTPUT:
    RETVAL

##############################################################################
# _is_even() - test for number being even

int
_is_even(Class,n)
        mpz_t*  n

  CODE:
     RETVAL = ! mpz_tstbit(*n,0);
  OUTPUT:
    RETVAL

##############################################################################
# _sqrt() - square root

void
_sqrt(Class,x)
        SV*     x
  PREINIT:
        mpz_t* TEMP;
  PPCODE:
    GMP_GET_ARG_0;   /* TEMP = x */
    mpz_sqrt(*TEMP, *TEMP);
    PUSHs( x );


##############################################################################
# _root() - integer roots

void
_root(Class,x,y)
        SV*     x
        SV*     y
  PREINIT:
        mpz_t* TEMP;
        mpz_t* TEMP_1;
  PPCODE:
    GMP_GET_ARGS_0_1;   /* (TEMP, TEMP_1) = (x,y)  */
    mpz_root(*TEMP, *TEMP, mpz_get_ui(*TEMP_1));
    PUSHs( x );