#ifdef  __MINGW32__
#ifndef __USE_MINGW_ANSI_STDIO
#define __USE_MINGW_ANSI_STDIO 1
#endif
#endif

#define PERL_NO_GET_CONTEXT 1

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"


#include "math_gmpq_include.h"
#include "math_gmpq_unused.h"

int _is_infstring(char * s) {
  int sign = 1;

  if(s[0] == '-') {
    sign = -1;
    s++;
  }
  else {
    if(s[0] == '+') s++;
  }

  if((s[0] == 'i' || s[0] == 'I') && (s[1] == 'n' || s[1] == 'N') && (s[2] == 'f' || s[2] == 'F'))
    return sign;

#ifdef _WIN32_BIZARRE_INFNAN /* older Win32 perls stringify infinities as(-)1.#INF */

   if(!strcmp(s, "1.#INF")) return sign;

#endif

  return 0;
}

void Rmpq_canonicalize (mpq_t * p) {
     mpq_canonicalize(*p);
}

SV * Rmpq_init(pTHX) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;

     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in Rmpq_init function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init (*mpq_t_obj);

     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);
     return obj_ref;
}

SV * Rmpq_init_nobless(pTHX) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;

     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in Rmpq_init_nobless function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, NULL);
     mpq_init (*mpq_t_obj);

     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);
     return obj_ref;
}

void DESTROY(pTHX_ mpq_t * p) {
/*     printf("Destroying mpq "); */
     mpq_clear(*p);
     Safefree(p);
/*     printf("...destroyed\n"); */
}

void Rmpq_clear(pTHX_ mpq_t * p) {
     mpq_clear(*p);
     Safefree(p);
}

void Rmpq_clear_mpq(mpq_t * p) {
     mpq_clear(*p);
}

void Rmpq_clear_ptr(pTHX_ mpq_t * p) {
     Safefree(p);
}

void Rmpq_set(mpq_t * p1, mpq_t * p2) {
     mpq_set(*p1, *p2);
}

void Rmpq_swap(mpq_t * p1, mpq_t * p2) {
     mpq_swap(*p1, *p2);
}

void Rmpq_set_z(mpq_t * p1, mpz_t * p2) {
     mpq_set_z(*p1, *p2);
}

void Rmpq_set_ui(mpq_t * p1, unsigned long p2, unsigned long p3) {
     mpq_set_ui(*p1, p2, p3);
}

void Rmpq_set_si(mpq_t * p1, long p2, long p3) {
     mpq_set_si(*p1, p2, p3);
}

void _Rmpq_set_str(pTHX_ mpq_t * p1, SV * p2, int base) {
     if(base < 0 || base == 1 || base > 62) croak ("%d is not a valid base in Rmpq_set_str", base);
     if(mpq_set_str(*p1, SvPV_nolen(p2), base))
       croak("String supplied to Rmpq_set_str function (%s) is not a valid base %d number", SvPV_nolen(p2), base);
}


double Rmpq_get_d(mpq_t * p) {
     return mpq_get_d(*p);
}

void Rmpq_set_d(mpq_t * p, double d){
     if(d != d) croak ("In Rmpq_set_d, cannot coerce a NaN to a Math::GMPq value");
     if(d != 0 && d / d != 1) croak ("In Rmpq_set_d, cannot coerce an Inf to a Math::GMPq value");
     mpq_set_d(*p, d);
}

void _mpf_set_doubledouble(mpf_t * q, SV * p) {
#if defined(USE_LONG_DOUBLE) && REQUIRED_LDBL_MANT_DIG == 2098
     double msd;                     /* Most Significant Double */
     mpf_t t, d;
     long double lsd;                /* Will be assigned the Least Siginficant Double */

     msd = (double)SvNV(p);
     if(msd != 0.0) {
       if(msd != msd) croak("In _mpf_set_doubledouble (called from Rmpq_set_NV), cannot coerce a NaN to a Math::GMPf object");
       if(msd / msd != 1.0) croak("In _mpf_set_doubledouble (called from Rmpq_set_NV), cannot coerce an Inf to a Math::GMPf object");
     }

     lsd = SvNV(p) - (long double)msd;

     mpf_init2(t, 2098);
     mpf_init2(d, 53);
     mpf_set_d(t, msd);
     mpf_set_d(d, (double)lsd);
     mpf_add(t, t, d);

     mpf_clear(d);

     mpf_set(*q, t);

     mpf_clear(t);

#else
     PERL_UNUSED_ARG2(q, p);
     croak("_mpf_set_doubledouble not implemented because NV is not a doubledouble");

#endif
}

void Rmpq_set_NV(pTHX_ mpq_t * copy, SV * original) {

#if defined(USE_QUADMATH)

     char * buffer;
     int exp, exp2 = 0;
     __float128 ld, buffer_size;
     int returned;

     if(!SV_IS_NOK(original))
       croak("In Rmpq_set_NV, 2nd argument is not an NV");

     ld = (__float128)SvNV(original);
     if(ld != ld) croak("In Rmpq_set_NV, cannot coerce a NaN to a Math::GMPq value");
     if(ld != 0 && (ld / ld != 1))
       croak("In Rmpq_set_NV, cannot coerce an Inf to a Math::GMPq value");

     ld = frexpq((__float128)SvNV(original), &exp);

     while(ld != floorq(ld)) {
          ld *= 2;
          exp2 += 1;
     }

     buffer_size = ld < 0.0Q ? ld * -1.0Q : ld;
     buffer_size = ceill(logq(buffer_size + 1) / 2.30258509299404568401799145468436418Q);

     Newxz(buffer, (int)buffer_size + 5, char);

     returned = quadmath_snprintf(buffer, (size_t)buffer_size + 5, "%.0Qf", ld);
     if(returned < 0) croak("In Rmpq_set_NV, encoding error in quadmath_snprintf function");
     if(returned >= buffer_size + 5) croak("In Rmpq_set_NV, buffer given to quadmath_snprintf function was too small");
     mpq_set_str(*copy, buffer, 10);
     Safefree(buffer);

     if (exp2 > exp) mpq_div_2exp(*copy, *copy, exp2 - exp);
     else mpq_mul_2exp(*copy, *copy, exp - exp2);

#elif defined(USE_LONG_DOUBLE)

#  if REQUIRED_LDBL_MANT_DIG == 2098
     mpf_t t;

     if(!SV_IS_NOK(original))
       croak("In Rmpq_set_NV, 2nd argument is not an NV");

     mpf_init2(t, 2098);
     _mpf_set_doubledouble(&t, original);
     mpq_set_f(*copy, t);
     mpf_clear(t);

#  else
     char * buffer;
     int exp, exp2 = 0;
     long double ld, buffer_size;

     if(!SV_IS_NOK(original))
       croak("In Rmpq_set_NV, 2nd argument is not an NV");

     ld = (long double)SvNV(original);
     if(ld != ld) croak("In Rmpq_set_NV, cannot coerce a NaN to a Math::GMPq value");
     if(ld != 0 && (ld / ld != 1))
       croak("In Rmpq_set_NV, cannot coerce an Inf to a Math::GMPq value");

     ld = frexpl((long double)SvNV(original), &exp);

     while(ld != floorl(ld)) {
          ld *= 2;
          exp2 += 1;
     }

     buffer_size = ld < 0.0L ? ld * -1.0L : ld;
     buffer_size = ceill(logl(buffer_size + 1) / 2.30258509299404568401799145468436418L);

     Newxz(buffer, (int)buffer_size + 5, char);

     if(sprintf(buffer, "%.0Lf", ld) >= (int)buffer_size + 5) croak("In Rmpq_set_NV, buffer overflow in sprintf function");

     mpq_set_str(*copy, buffer, 10);
     Safefree(buffer);

     if (exp2 > exp) mpq_div_2exp(*copy, *copy, exp2 - exp);
     else mpq_mul_2exp(*copy, *copy, exp - exp2);
#  endif

#else
     double d;

     if(!SV_IS_NOK(original))
       croak("In Rmpq_set_NV, 2nd argument is not an NV");

     d = SvNV(original);
     if(d != d) croak("In Rmpq_set_NV, cannot coerce a NaN to a Math::GMPq value");
     if(d != 0 && (d / d != 1))
       croak("In Rmpq_set_NV, cannot coerce an Inf to a Math::GMPq value");

     mpq_set_d(*copy, d);
#endif
}

int Rmpq_cmp_NV(pTHX_ mpq_t * a, SV * b) {

     mpq_t t;
     int returned;

     if(!SvNOK(b))
       croak("In Rmpq_cmp_NV, 2nd argument is not an NV");

#if defined(USE_QUADMATH)

     char * buffer;
     int exp, exp2 = 0;
     __float128 ld, buffer_size;

     ld = (__float128)SvNV(b);
     if(ld != ld) croak("In Rmpq_cmp_NV, cannot compare a NaN to a Math::GMPq value");
     if(ld != 0 && (ld / ld != 1)) {
       if(ld > 0) return -1;
       return 1;
     }

     ld = frexpq((__float128)SvNV(b), &exp);

     while(ld != floorq(ld)) {
          ld *= 2;
          exp2 += 1;
     }

     buffer_size = ld < 0.0Q ? ld * -1.0Q : ld;
     buffer_size = ceilq(logq(buffer_size + 1) / 2.30258509299404568401799145468436418Q);

     Newxz(buffer, (int)buffer_size + 5, char);

     returned = quadmath_snprintf(buffer, (size_t)buffer_size + 5, "%.0Qf", ld);
     if(returned < 0) croak("In Rmpq_cmp_NV, encoding error in quadmath_snprintf function");
     if(returned >= buffer_size + 5) croak("In Rmpq_cmp_NV, buffer given to quadmath_snprintf function was too small");
     mpq_init(t);
     mpq_set_str(t, buffer, 10);
     Safefree(buffer);

     if (exp2 > exp) mpq_div_2exp(t, t, exp2 - exp);
     else mpq_mul_2exp(t, t, exp - exp2);

#elif defined(USE_LONG_DOUBLE)
# if REQUIRED_LDBL_MANT_DIG == 2098
     mpf_t temp;
     long double ld = (long double)SvNV(b);
     if(ld != ld) croak("In Rmpq_cmp_NV, cannot compare a NaN to a Math::GMPq value");
     if(ld != 0 && ld / ld != 1) {
       if(ld > 0) return -1;
       return 1;
     }
     mpq_init(t);
     mpf_init2(temp, 2098);
     _mpf_set_doubledouble(&temp, b);
     mpq_set_f(t, temp);
     mpf_clear(temp);

#  else
     char * buffer;
     int exp, exp2 = 0;
     long double ld, buffer_size;

     ld = (long double)SvNV(b);
     if(ld != ld) croak("In Rmpq_cmp_NV, cannot compare a NaN to a Math::GMPq value");
     if(ld != 0 && (ld / ld != 1)) {
       if(ld > 0) return -1;
       return 1;
     }

     ld = frexpl((long double)SvNV(b), &exp);

     while(ld != floorl(ld)) {
          ld *= 2;
          exp2 += 1;
     }

     buffer_size = ld < 0.0L ? ld * -1.0L : ld;
     buffer_size = ceill(logl(buffer_size + 1) / 2.30258509299404568401799145468436418L);

     Newxz(buffer, (int)buffer_size + 5, char);

     if(sprintf(buffer, "%.0Lf", ld) >= (int)buffer_size + 5) croak("In Rmpq_cmp_NV, buffer overflow in sprintf function");
     mpq_init(t);
     mpq_set_str(t, buffer, 10);
     Safefree(buffer);

     if (exp2 > exp) mpq_div_2exp(t, t, exp2 - exp);
     else mpq_mul_2exp(t, t, exp - exp2);

#  endif
#else
     double d = SvNV(b);
     if(d != d) croak("In Rmpq_cmp_NV, cannot compare a NaN to a Math::GMPq value");
     if(d != 0 && (d / d != 1)) {
       if(d > 0) return -1;
       return 1;
     }
     mpq_init(t);
     mpq_set_d(t, d);
#endif

     returned = mpq_cmp(*a, t);
     mpq_clear(t);
     return returned;
}

void Rmpq_set_f(mpq_t * p, mpf_t * f) {
     mpq_set_f(*p, *f);
}

SV * Rmpq_get_str(pTHX_ mpq_t * p, int base){
     char * out;
     SV * outsv;

     New(123, out, mpz_sizeinbase(mpq_numref(*p), base) + mpz_sizeinbase(mpq_denref(*p), base) + 3, char);
     if(out == NULL) croak ("Failed to allocate memory in Rmpq_get_str function");

     mpq_get_str(out, base, *p);
     outsv = newSVpv(out, 0);
     Safefree(out);
     return outsv;
}

int Rmpq_cmp(mpq_t * p1, mpq_t * p2) {
     return mpq_cmp(*p1, *p2);
}

int Rmpq_cmp_ui(mpq_t * p1, unsigned long n, unsigned long d) {
     return mpq_cmp_ui(*p1, n, d);
}

int Rmpq_cmp_si(mpq_t * p1, long n, unsigned long d) {
     return mpq_cmp_si(*p1, n, d);
}

int Rmpq_cmp_z(mpq_t * p, mpz_t * z) {
#if __GNU_MP_RELEASE >= 60099
     return mpq_cmp_z(*p, *z);
#else
     int ret;
     mpz_t temp;

     mpz_init_set(temp, *z);
     mpz_mul(temp, temp, mpq_denref(*p));
     ret = mpz_cmp(mpq_numref(*p), temp);
     mpz_clear(temp);
     return ret;

#endif
}

int Rmpq_sgn(mpq_t * p) {
     return mpq_sgn(*p);
}

int Rmpq_equal(mpq_t * p1, mpq_t * p2) {
     return mpq_equal(*p1, *p2);
}

void Rmpq_add(mpq_t * p1, mpq_t * p2, mpq_t * p3) {
     mpq_add(*p1, *p2, *p3);
}

void Rmpq_sub(mpq_t * p1, mpq_t * p2, mpq_t * p3) {
     mpq_sub(*p1, *p2, *p3);
}

void Rmpq_mul(mpq_t * p1, mpq_t * p2, mpq_t * p3) {
     mpq_mul(*p1, *p2, *p3);
}

void Rmpq_div(mpq_t * p1, mpq_t * p2, mpq_t * p3) {
     if(!mpq_cmp_ui(*p3, 0, 1))
       croak("Division by 0 not allowed in Math::GMPq::Rmpq_div");
     mpq_div(*p1, *p2, *p3);
}

void Rmpq_mul_2exp(pTHX_ mpq_t * p1, mpq_t * p2, SV * p3) {
     mpq_mul_2exp(*p1, *p2, (mp_bitcnt_t)SvUV(p3));
}

void Rmpq_div_2exp(pTHX_ mpq_t * p1, mpq_t * p2, SV * p3) {
     mpq_div_2exp(*p1, *p2, (mp_bitcnt_t)SvUV(p3));
}

void Rmpq_neg(mpq_t * p1, mpq_t * p2) {
     mpq_neg(*p1, *p2);
}

void Rmpq_abs(mpq_t * p1, mpq_t * p2) {
     mpq_abs(*p1, *p2);
}

void Rmpq_inv(mpq_t * p1, mpq_t * p2) {
     mpq_inv(*p1, *p2);
}

SV * _Rmpq_out_str(pTHX_ mpq_t * p, int base){
     size_t ret;
     if(base < 2 || base > 36)
       croak("2nd argument supplied to Rmpq_out_str is out of allowable range (must be between 2 and 36 inclusive)");
     ret = mpq_out_str(NULL, base, *p);
     fflush(stdout);
     return newSVuv(ret);
}

SV * _Rmpq_out_strS(pTHX_ mpq_t * p, int base, SV * suff) {
     size_t ret;
     if(base < 2 || base > 36)
       croak("2nd argument supplied to Rmpq_out_str is out of allowable range (must be between 2 and 36 inclusive)");
     ret = mpq_out_str(NULL, base, *p);
     printf("%s", SvPV_nolen(suff));
     fflush(stdout);
     return newSVuv(ret);
}

SV * _Rmpq_out_strP(pTHX_ SV * pre, mpq_t * p, int base) {
     size_t ret;
     if(base < 2 || base > 36)
       croak("2nd argument supplied to Rmpq_out_str is out of allowable range (must be between 2 and 36 inclusive)");
     printf("%s", SvPV_nolen(pre));
     ret = mpq_out_str(NULL, base, *p);
     fflush(stdout);
     return newSVuv(ret);
}

SV * _Rmpq_out_strPS(pTHX_ SV * pre, mpq_t * p, int base, SV * suff) {
     size_t ret;
     if(base < 2 || base > 36)
       croak("2nd argument supplied to Rmpq_out_str is out of allowable range (must be between 2 and 36 inclusive)");
     printf("%s", SvPV_nolen(pre));
     ret = mpq_out_str(NULL, base, *p);
     printf("%s", SvPV_nolen(suff));
     fflush(stdout);
     return newSVuv(ret);
}



SV * _TRmpq_out_str(pTHX_ FILE * stream, int base, mpq_t * p) {
     size_t ret;
     ret = mpq_out_str(stream, base, *p);
     fflush(stream);
     return newSVuv(ret);
}

SV * _TRmpq_out_strS(pTHX_ FILE * stream, int base, mpq_t * p, SV * suff) {
     size_t ret;
     ret = mpq_out_str(stream, base, *p);
     fflush(stream);
     fprintf(stream, "%s", SvPV_nolen(suff));
     fflush(stream);
     return newSVuv(ret);
}

SV * _TRmpq_out_strP(pTHX_ SV * pre, FILE * stream, int base, mpq_t * p) {
     size_t ret;
     fprintf(stream, "%s", SvPV_nolen(pre));
     fflush(stream);
     ret = mpq_out_str(stream, base, *p);
     fflush(stream);
     return newSVuv(ret);
}

SV * _TRmpq_out_strPS(pTHX_ SV * pre, FILE * stream, int base, mpq_t * p, SV * suff) {
     size_t ret;
     fprintf(stream, "%s", SvPV_nolen(pre));
     fflush(stream);
     ret = mpq_out_str(stream, base, *p);
     fflush(stream);
     fprintf(stream, "%s", SvPV_nolen(suff));
     fflush(stream);
     return newSVuv(ret);
}

SV * TRmpq_inp_str(pTHX_ mpq_t * p, FILE * stream, SV * base) {
     size_t ret;
     ret = mpq_inp_str(*p, stream, (int)SvIV(base));
     /* fflush(stream); */
     return newSVuv(ret);
}

SV * Rmpq_inp_str(pTHX_ mpq_t * p, int base){
     size_t ret;
     ret = mpq_inp_str(*p, NULL, base);
     /* fflush(stdin); */
     return newSVuv(ret);
}

void Rmpq_numref(mpz_t * z, mpq_t * r) {
     mpz_set(*z, mpq_numref(*r));
}

void Rmpq_denref(mpz_t * z, mpq_t * r) {
     mpz_set(*z, mpq_denref(*r));
}

void Rmpq_get_num(mpz_t * z, mpq_t * r) {
     mpq_get_num(*z, *r);
}

void Rmpq_get_den(mpz_t * z, mpq_t * r) {
     mpq_get_den(*z, *r);
}

void Rmpq_set_num(mpq_t * r, mpz_t * z) {
     mpq_set_num(*r, *z);
}

void Rmpq_set_den(mpq_t * r, mpz_t * z) {
     mpq_set_den(*r, *z);
}

SV * get_refcnt(pTHX_ SV * s) {
     return newSVuv(SvREFCNT(s));
}

void Rmpq_add_z(mpq_t * rop, mpq_t * op, mpz_t * z) {
     if(rop != op) mpq_set(*rop, *op);
     mpz_addmul(mpq_numref(*rop), mpq_denref(*rop), *z);
}

void Rmpq_sub_z(mpq_t * rop, mpq_t * op, mpz_t * z) {
     if(rop != op) mpq_set(*rop, *op);
     mpz_submul(mpq_numref(*rop), mpq_denref(*rop), *z);
}

void Rmpq_z_sub(mpq_t * rop, mpz_t * z, mpq_t * op) {
     if(rop != op) mpq_set(*rop, *op);
     mpz_submul(mpq_numref(*rop), mpq_denref(*rop), *z);
     mpz_neg(mpq_numref(*rop), mpq_numref(*rop));
}

void Rmpq_mul_z(mpq_t * rop, mpq_t * op, mpz_t * z) {
     if(rop != op) mpq_set(*rop, *op);
     mpz_mul(mpq_numref(*rop), mpq_numref(*rop), *z);
     mpq_canonicalize(*rop);
}

void Rmpq_div_z(mpq_t * rop, mpq_t * op, mpz_t * z) {
     if(!mpz_cmp_ui(*z, 0))
       croak("Division by 0 not allowed in Math::GMPq::Rmpq_div_z");
     if(rop != op) mpq_set(*rop, *op);
     mpz_mul(mpq_denref(*rop), mpq_denref(*rop), *z);
     mpq_canonicalize(*rop);
}

void Rmpq_z_div(mpq_t * rop, mpz_t * z, mpq_t * op) {
     if(!mpq_cmp_ui(*op, 0, 1))
       croak("Division by 0 not allowed in Math::GMPq::Rmpq_z_div");
     if(rop != op) mpq_set(*rop, *op);
     mpq_inv(*rop, *rop);
     mpz_mul(mpq_numref(*rop), mpq_numref(*rop), *z);
     mpq_canonicalize(*rop);
}

void Rmpq_pow_ui(mpq_t * rop, mpq_t * op, unsigned long ui) {
     mpz_pow_ui(mpq_numref(*rop), mpq_numref(*op), ui);
     mpz_pow_ui(mpq_denref(*rop), mpq_denref(*op), ui);
}

/* Also handles UV values */
void Rmpq_set_IV(pTHX_ mpq_t * a, SV * my_iv1, SV * my_iv2) {
     mpq_t temp;

     if(SV_IS_IOK(my_iv1) && SV_IS_IOK(my_iv2)) {
       mpq_init(temp);

#ifdef MATH_GMPQ_NEED_LONG_LONG_INT
       mpq_set_str(*a,   SvPV_nolen(my_iv1), 10);
       mpq_set_str(temp, SvPV_nolen(my_iv2), 10);
       mpq_div(*a, *a, temp);

#else
       if(SvUOK(my_iv1)) mpq_set_ui(*a, SvUVX(my_iv1), SvUV(my_iv2));
       else mpq_set_si(*a, SvIVX(my_iv1), SvUV(my_iv2));

#endif
       mpq_clear(temp);
     }

     else croak("Arg provided to Rmpq_set_IV not an IV");
}

int Rmpq_cmp_IV(pTHX_ mpq_t * q, SV * iv1, SV * iv2) {
    mpq_t temp;
    int ret;

    if(!SvIOK(iv1) || !SvIOK(iv2))
      croak("Arg provided to Rmpq_cmp_IV is not an IV");

    mpq_init(temp);

    Rmpq_set_IV(aTHX_ &temp, iv1, iv2);
    ret = mpq_cmp(*q, temp);

    mpq_clear(temp);
    return ret;
}

/* Finish typemapping - typemap 1st arg only */

SV * _overload_mul(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     const char * h;
     int object = 0;
     PERL_UNUSED_ARG(third);

     if(sv_isobject(b)) {
       object = 1;
       h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::MPFR")) { /* will return */
         _overload_callback("Math::MPFR::overload_mul", "Math::GMPq::overload_mul", newSViv(0));
       }
     }

     /* create new object */
     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_mul function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);
     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);

     if(SV_IS_IOK(b)) {
       Rmpq_set_IV(aTHX_ mpq_t_obj, b, newSViv(1));
       mpq_mul(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       if(mpq_set_str(*mpq_t_obj, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_mul");
       mpq_canonicalize(*mpq_t_obj);
       mpq_mul(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(SV_IS_NOK(b)) {

       Rmpq_set_NV(aTHX_ mpq_t_obj, b);

       mpq_mul(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(object) {
       if(strEQ(h, "Math::GMPq")) {
         mpq_mul(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return obj_ref;
       }
       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         Rmpq_mul_z(mpq_t_obj, INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         return obj_ref;
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_mul");
}

void Rmpq_and (mpq_t * rop, mpq_t * a, mpq_t * b) {
     mpz_t az, bz;
     mpz_init(az);
     mpz_init(bz);

     mpz_set_q( az, *a );
     mpz_set_q( bz, *b );
     mpz_and  ( az, az, bz );

     mpq_set_z(*rop, az);
     mpz_clear(az);
     mpz_clear(bz);
}

void Rmpq_ior (mpq_t * rop, mpq_t * a, mpq_t * b) {
     mpz_t az, bz;
     mpz_init(az);
     mpz_init(bz);

     mpz_set_q( az, *a );
     mpz_set_q( bz, *b );
     mpz_ior  ( az, az, bz );

     mpq_set_z(*rop, az);
     mpz_clear(az);
     mpz_clear(bz);
}

void Rmpq_xor (mpq_t * rop, mpq_t * a, mpq_t * b) {
     mpz_t az, bz;
     mpz_init(az);
     mpz_init(bz);

     mpz_set_q( az, *a );
     mpz_set_q( bz, *b );
     mpz_xor  ( az, az, bz );

     mpq_set_z(*rop, az);
     mpz_clear(az);
     mpz_clear(bz);
}

void Rmpq_com (mpq_t * rop, mpq_t * a) {
     mpz_t az;
     mpz_init(az);

     mpz_set_q( az, *a );
     mpz_com  (az, az);
     mpq_set_z(*rop, az);
     mpz_clear(az);
}

SV * _overload_add(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     const char *h;
     int object = 0;
     PERL_UNUSED_ARG(third);

     if(sv_isobject(b)) {
       object = 1;
       h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::MPFR")) { /* will return */
         _overload_callback("Math::MPFR::overload_add", "Math::GMPq::overload_add", newSViv(0));
       }
     }

     /* create new object */
     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_add function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);
     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);

     if(SV_IS_IOK(b)) {
       Rmpq_set_IV(aTHX_ mpq_t_obj, b, newSViv(1));
       mpq_add(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       if(mpq_set_str(*mpq_t_obj, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_add");
       mpq_canonicalize(*mpq_t_obj);
       mpq_add(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(SV_IS_NOK(b)) {

       Rmpq_set_NV(aTHX_ mpq_t_obj, b);

       mpq_add(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(object) {
       if(strEQ(h, "Math::GMPq")) {
         mpq_add(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return obj_ref;
       }
       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         Rmpq_add_z(mpq_t_obj, INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         return obj_ref;
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_add");
}

SV * _overload_sub(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     const char *h;
     int object = 0;

     if(sv_isobject(b)) {
       object = 1;
       h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::MPFR")) { /* will return */
         _overload_callback("Math::MPFR::overload_sub", "Math::GMPq::overload_sub", &PL_sv_yes);
       }
     }

     /* create new object */
     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_sub function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);
     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);

     if(SV_IS_IOK(b)) {
       Rmpq_set_IV(aTHX_ mpq_t_obj, b, newSViv(1));
       if(SWITCH_ARGS) mpq_sub(*mpq_t_obj, *mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))));
       else mpq_sub(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       if(mpq_set_str(*mpq_t_obj, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_sub");
       mpq_canonicalize(*mpq_t_obj);
       if(SWITCH_ARGS) mpq_sub(*mpq_t_obj, *mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))));
       else mpq_sub(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(SV_IS_NOK(b)) {

       Rmpq_set_NV(aTHX_ mpq_t_obj, b);

       if(SWITCH_ARGS) mpq_sub(*mpq_t_obj, *mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))));
       else mpq_sub(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(object) {
       if(strEQ(h, "Math::GMPq")) {
         mpq_sub(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return obj_ref;
       }
       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         if(SWITCH_ARGS) {
           Rmpq_z_sub(mpq_t_obj, INT2PTR(mpz_t *, SvIVX(SvRV(b))), INT2PTR(mpq_t *, SvIVX(SvRV(a))));
         }
         else {
           Rmpq_sub_z(mpq_t_obj, INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         }
         return obj_ref;
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_sub function");

}

SV * _overload_div(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     const char *h;
     int object = 0;

     if(sv_isobject(b)) {
       object = 1;
       h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::MPFR")) {      /* will return */
         /* divby0 is allowed here */
         _overload_callback("Math::MPFR::overload_div", "Math::GMPq::overload_div", &PL_sv_yes);
       }
     }

     /* create new object */
     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_div function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);
     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);

     if(SV_IS_IOK(b)) {
       if(SvIV(b) == 0)
         croak("Division by 0 not allowed in Math::GMPq::overload_div");
       Rmpq_set_IV(aTHX_ mpq_t_obj, b, newSViv(1));
       if(SWITCH_ARGS) mpq_div(*mpq_t_obj, *mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))));
       else mpq_div(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       if(mpq_set_str(*mpq_t_obj, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_div");
       mpq_canonicalize(*mpq_t_obj);
       if(!mpq_cmp_ui(*mpq_t_obj, 0, 1))
         croak("Division by 0 not allowed in Math::GMPq::overload_div");
       if(SWITCH_ARGS) mpq_div(*mpq_t_obj, *mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))));
       else mpq_div(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(SV_IS_NOK(b)) {

       if(SvNV(b) == 0)
         croak("Division by 0 not allowed in Math::GMPq::overload_div");

       /* If SvNV(b) is Inf or Nan, this will be caught by Rmpq_set_NV */

       Rmpq_set_NV(aTHX_ mpq_t_obj, b);

       if(SWITCH_ARGS) mpq_div(*mpq_t_obj, *mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))));
       else mpq_div(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *mpq_t_obj);
       return obj_ref;
     }

     if(object) {
       if(strEQ(h, "Math::GMPq")) {
         if(!mpq_cmp_ui(*(INT2PTR(mpq_t *, SvIVX(SvRV(b)))), 0, 1))
           croak("Division by 0 not allowed in Math::GMPq::overload_div");
         mpq_div(*mpq_t_obj, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return obj_ref;
       }
       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         if(SWITCH_ARGS) {
           /* Rmpq_z_div will catch divby0 */
           Rmpq_z_div(mpq_t_obj, INT2PTR(mpz_t *, SvIVX(SvRV(b))), INT2PTR(mpq_t *, SvIVX(SvRV(a))));
         }
         else {
           /* Rmpq_z_div will catch divby0 */
           Rmpq_div_z(mpq_t_obj, INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         }
         return obj_ref;
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_div function");

}

SV * overload_string(pTHX_ mpq_t * p, SV * second, SV * third) {
     char * out;
     SV * outsv;
     PERL_UNUSED_ARG2(second, third);

     New(123, out, mpz_sizeinbase(mpq_numref(*p), 10) + mpz_sizeinbase(mpq_denref(*p), 10) + 3, char);
     if(out == NULL) croak ("Failed to allocate memory in overload_string function");

     mpq_get_str(out, 10, *p);
     outsv = newSVpv(out, 0);
     Safefree(out);
     return outsv;
}

NV _mpf_get_float128(mpf_t * x) {

#if defined(USE_QUADMATH)
     mpf_t t;
     long i, exp, retract = 0, bits = 113;
     char *out;
     __float128 ret = 0.0Q, sign = 1.0Q;
     __float128 add_on[113] = {
      5192296858534827628530496329220096e0Q, 2596148429267413814265248164610048e0Q,
      1298074214633706907132624082305024e0Q, 649037107316853453566312041152512e0Q,
      324518553658426726783156020576256e0Q, 162259276829213363391578010288128e0Q,
      81129638414606681695789005144064e0Q, 40564819207303340847894502572032e0Q,
      20282409603651670423947251286016e0Q, 10141204801825835211973625643008e0Q,
      5070602400912917605986812821504e0Q, 2535301200456458802993406410752e0Q,
      1267650600228229401496703205376e0Q, 633825300114114700748351602688e0Q,
      316912650057057350374175801344e0Q, 158456325028528675187087900672e0Q, 79228162514264337593543950336e0Q,
      39614081257132168796771975168e0Q, 19807040628566084398385987584e0Q, 9903520314283042199192993792e0Q,
      4951760157141521099596496896e0Q, 2475880078570760549798248448e0Q, 1237940039285380274899124224e0Q,
      618970019642690137449562112e0Q, 309485009821345068724781056e0Q, 154742504910672534362390528e0Q,
      77371252455336267181195264e0Q, 38685626227668133590597632e0Q, 19342813113834066795298816e0Q,
      9671406556917033397649408e0Q, 4835703278458516698824704e0Q, 2417851639229258349412352e0Q,
      1208925819614629174706176e0Q, 604462909807314587353088e0Q, 302231454903657293676544e0Q,
      151115727451828646838272e0Q, 75557863725914323419136e0Q, 37778931862957161709568e0Q,
      18889465931478580854784e0Q, 9444732965739290427392e0Q, 4722366482869645213696e0Q,
      2361183241434822606848e0Q, 1180591620717411303424e0Q, 590295810358705651712e0Q, 295147905179352825856e0Q,
      147573952589676412928e0Q, 73786976294838206464e0Q, 36893488147419103232e0Q, 18446744073709551616e0Q,
      9223372036854775808e0Q, 4611686018427387904e0Q, 2305843009213693952e0Q, 1152921504606846976e0Q,
      576460752303423488e0Q, 288230376151711744e0Q, 144115188075855872e0Q, 72057594037927936e0Q,
      36028797018963968e0Q, 18014398509481984e0Q, 9007199254740992e0Q, 4503599627370496e0Q,
      2251799813685248e0Q, 1125899906842624e0Q, 562949953421312e0Q, 281474976710656e0Q, 140737488355328e0Q,
      70368744177664e0Q, 35184372088832e0Q, 17592186044416e0Q, 8796093022208e0Q, 4398046511104e0Q,
      2199023255552e0Q, 1099511627776e0Q, 549755813888e0Q, 274877906944e0Q, 137438953472e0Q, 68719476736e0Q,
      34359738368e0Q, 17179869184e0Q, 8589934592e0Q, 4294967296e0Q, 2147483648e0Q, 1073741824e0Q, 536870912e0Q,
      268435456e0Q, 134217728e0Q, 67108864e0Q, 33554432e0Q, 16777216e0Q, 8388608e0Q, 4194304e0Q, 2097152e0Q,
      1048576e0Q, 524288e0Q, 262144e0Q, 131072e0Q, 65536e0Q, 32768e0Q, 16384e0Q, 8192e0Q, 4096e0Q, 2048e0Q,
      1024e0Q, 512e0Q, 256e0Q, 128e0Q, 64e0Q, 32e0Q, 16e0Q, 8e0Q, 4e0Q, 2e0Q, 1e0Q };

     mpf_init2(t, mpf_get_prec(*x));
     mpf_set(t, *x);

     Newxz(out, mpf_get_prec(t) + 2, char);
     if(out == NULL) croak("Failed to allocate memory in _mpf_get_float128 function");

     mpf_get_str(out, &exp, 2, mpf_get_prec(t), t);

     mpf_clear(t);

     if(exp < -16493) {
       Safefree(out);
       return 0.0Q;
     }

     if(out[0] == '-') {
       sign = -1.0Q;
       out++;
       retract++;
     }
     else {
       if(out[0] == '+') {
         out++;
         retract++;
       }
     }

     if(exp > -16494 && exp < -16381)
       bits = exp + 16494;

     for(i = 0; i < bits; i++) {
       if(out[i] == '1') ret += add_on[i];
       if(out[i] == 0) break; /* end of string */
     }

     if(retract) out--;
     Safefree(out);

     /* re-using the 'i' variable */
     i = exp < -16381 ? exp + 16381 : 0;	/* function has already returned if exp < -16493 */

     if(i) { 				/* powq(2.0Q, exp) will be zero - so do the calculation in 2 steps */
       ret *= powq(2.0Q, i);
       exp -= i;			/* exp += abs(i) */
     }

     ret *= powq(2.0Q, exp - 113);
     return ret * sign;

#else
     PERL_UNUSED_ARG(x);
     croak("_mpf_get_float128 not implemented for this build of Math::GMPq");

#endif

}

int _rndaz(char *a, IV exponent, UV prec, int display) {
  size_t len, i, ulp_pos = ULP_INDEX;
  PERL_UNUSED_ARG(prec);

  if(exponent < LOW_SUBNORMAL_EXP) return 0;

  if(exponent < HIGH_SUBNORMAL_EXP) ulp_pos -= HIGH_SUBNORMAL_EXP - exponent;

  len = strlen(a);

  if(a[0] == '-' || a[0] == '+') ++ulp_pos;

  if(len <= ulp_pos + 1) return 0;          /* no rounding required */

  if(display) printf("len: %u ULP index: %d\n", (unsigned int)len, (int)ulp_pos);

  if(a[ulp_pos + 1] == '0') return 0;       /* no rounding required */

  /* will get to here only if a[ulp_pos + 1] == '1' */

  if(a[ulp_pos] == '1') return 1; /* rnda */

  if(len > ulp_pos + 2) {

    for(i = ulp_pos + 2; i < len; ++i) {
      if(a[i] == '1') return 1;         /* rnda */
    }
  }

  return 0;                             /* no rounding required */
}

double _mpf_get_d_rndn(mpf_t * p) {
  char * buf;
  mp_exp_t exponent;
  size_t n_digits;
  mpf_t temp, dbl_min;
  double d;

  n_digits = (size_t)mpf_get_prec(*p);

  Newxz(buf, n_digits + 2, char);

  mpf_get_str(buf, &exponent, 2, n_digits, *p);

  /* printf("exponent: %d\n", exponent); */

  if(_rndaz(buf, (IV)exponent, (UV)n_digits, 0)) {
    /* printf("ROUNDING AWAY FROM ZERO\n"); */
    Safefree(buf);
    mpf_init2(temp, (mp_bitcnt_t)n_digits);
    mpf_set_ui(temp, 1);
    if(exponent <= 53) mpf_div_2exp(temp, temp, 53 - exponent);
    else mpf_mul_2exp(temp, temp, exponent - 53);

    /***********************************************

    For the (subnormal) exponent range -1074 ..-1021, rounding away
    from zero will be achieved by simply adding the smallest
    representable (subnormal) value (0.1e-1073)

    ***********************************************/

    if(exponent < -1021 && exponent > -1075) { /* handle subnormal doubles */
      mpf_init2(dbl_min, 64);
      mpf_set_ui(dbl_min, 1);

      mpf_div_2exp(dbl_min, dbl_min, 1074); /*********************************
                                             dbl_min set to smallest non-zero
                                             positive (subnormal) value
                                             ********************************/

      if(mpf_sgn(*p) > 0) mpf_add(temp, *p, dbl_min);
      else mpf_sub(temp, *p, dbl_min);
      mpf_clear(dbl_min);
    }
    else { /* handle normal doubles */
      if(mpf_sgn(*p) > 0) mpf_add(temp, *p, temp);
      else mpf_sub(temp, *p, temp);
    }

    d = mpf_get_d(temp);
    mpf_clear(temp);
    return d;
  }

  Safefree(buf);
  return mpf_get_d(*p);

}

NV _mpf_get_ld(mpf_t * x) {

/* we replicate rounding towards zero because this is what mpf_get_d does */

#if defined(USE_LONG_DOUBLE) || defined(USE_QUADMATH)
#if REQUIRED_LDBL_MANT_DIG == 2098

     double msd, lsd;
     long double ret;
     mpf_t t, d;

     msd = _mpf_get_d_rndn(x);

     if(msd == 0.0 || msd != msd || msd / msd != 1)
       return (long double)msd;

     if(mpf_get_prec(*x) > 2112) mpf_init2(t, mpf_get_prec(*x));
     else mpf_init2(t, 2112);
     mpf_set(t, *x);

     mpf_init2(d, 64);
     mpf_set_d(d, msd);

     mpf_sub(t, t, d);
     mpf_clear(d);

     lsd = mpf_get_d(t); /* round to zero for least siginificant double only */

     mpf_clear(t);

     ret = (long double)msd + lsd;

     /* return newSVnv(ret); */
     return ret;

#else

     mpf_t t;
     long i, exp, retract = 0, bits = REQUIRED_LDBL_MANT_DIG;
     char *out;
     long double ret = 0.0L, sign = 1.0L;
     long double add_on[113] = {
      5192296858534827628530496329220096e0L, 2596148429267413814265248164610048e0L,
      1298074214633706907132624082305024e0L, 649037107316853453566312041152512e0L,
      324518553658426726783156020576256e0L, 162259276829213363391578010288128e0L,
      81129638414606681695789005144064e0L, 40564819207303340847894502572032e0L,
      20282409603651670423947251286016e0L, 10141204801825835211973625643008e0L,
      5070602400912917605986812821504e0L, 2535301200456458802993406410752e0L,
      1267650600228229401496703205376e0L, 633825300114114700748351602688e0L,
      316912650057057350374175801344e0L, 158456325028528675187087900672e0L, 79228162514264337593543950336e0L,
      39614081257132168796771975168e0L, 19807040628566084398385987584e0L, 9903520314283042199192993792e0L,
      4951760157141521099596496896e0L, 2475880078570760549798248448e0L, 1237940039285380274899124224e0L,
      618970019642690137449562112e0L, 309485009821345068724781056e0L, 154742504910672534362390528e0L,
      77371252455336267181195264e0L, 38685626227668133590597632e0L, 19342813113834066795298816e0L,
      9671406556917033397649408e0L, 4835703278458516698824704e0L, 2417851639229258349412352e0L,
      1208925819614629174706176e0L, 604462909807314587353088e0L, 302231454903657293676544e0L,
      151115727451828646838272e0L, 75557863725914323419136e0L, 37778931862957161709568e0L,
      18889465931478580854784e0L, 9444732965739290427392e0L, 4722366482869645213696e0L,
      2361183241434822606848e0L, 1180591620717411303424e0L, 590295810358705651712e0L, 295147905179352825856e0L,
      147573952589676412928e0L, 73786976294838206464e0L, 36893488147419103232e0L, 18446744073709551616e0L,
      9223372036854775808e0L, 4611686018427387904e0L, 2305843009213693952e0L, 1152921504606846976e0L,
      576460752303423488e0L, 288230376151711744e0L, 144115188075855872e0L, 72057594037927936e0L,
      36028797018963968e0L, 18014398509481984e0L, 9007199254740992e0L, 4503599627370496e0L,
      2251799813685248e0L, 1125899906842624e0L, 562949953421312e0L, 281474976710656e0L, 140737488355328e0L,
      70368744177664e0L, 35184372088832e0L, 17592186044416e0L, 8796093022208e0L, 4398046511104e0L,
      2199023255552e0L, 1099511627776e0L, 549755813888e0L, 274877906944e0L, 137438953472e0L, 68719476736e0L,
      34359738368e0L, 17179869184e0L, 8589934592e0L, 4294967296e0L, 2147483648e0L, 1073741824e0L, 536870912e0L,
      268435456e0L, 134217728e0L, 67108864e0L, 33554432e0L, 16777216e0L, 8388608e0L, 4194304e0L, 2097152e0L,
      1048576e0L, 524288e0L, 262144e0L, 131072e0L, 65536e0L, 32768e0L, 16384e0L, 8192e0L, 4096e0L, 2048e0L,
      1024e0L, 512e0L, 256e0L, 128e0L, 64e0L, 32e0L, 16e0L, 8e0L, 4e0L, 2e0L, 1e0L };

     mpf_init2(t, mpf_get_prec(*x));
     mpf_set(t, *x);

     Newxz(out, mpf_get_prec(t) + 2, char);
     if(out == NULL) croak("Failed to allocate memory in _mpf_get_ld function");

     mpf_get_str(out, &exp, 2, mpf_get_prec(t), t);

     if(exp < LOW_SUBNORMAL_EXP + 1) {
       PERL_UNUSED_ARG(x);
       Safefree(out);
       return 0.0L;
     }

     mpf_clear(t);

     if(out[0] == '-') {
       sign = -1.0L;
       out++;
       retract++;
     }
     else {
       if(out[0] == '+') {
         out++;
         retract++;
       }
     }

     if(exp > LOW_SUBNORMAL_EXP && exp < HIGH_SUBNORMAL_EXP)
       bits = exp - LOW_SUBNORMAL_EXP;

     for(i = 0; i < bits; i++) {
       if(out[i] == '1') ret += add_on[i];
       if(out[i] == 0) break; /* end of string */
     }

     if(retract) out--;
     Safefree(out);

     i = HIGH_SUBNORMAL_EXP + 113 - REQUIRED_LDBL_MANT_DIG;

     /* re-using the 'bits' variable */
     bits = exp < i ? exp - i : 0;	/* function has already returned if exp < low_subnormal_exp */

     if(bits) { 			/* powl(2.0L, exp) will be zero - so do the calculation in 2 steps */
       ret *= powl(2.0L, bits);
       exp -= bits;			/* exp += abs(bits) */
     }

     ret *= powl(2.0L, exp - 113);
     /* return newSVnv(ret * sign); */
     return ret * sign;

#endif
#else
     PERL_UNUSED_ARG(x);
     croak("_mpf_get_ld not implemented for this build of Math::GMPq");

#endif

}

SV * Rmpq_get_NV(pTHX_ mpq_t * x) {
     mpf_t t;
     NV ret;

#if defined(USE_LONG_DOUBLE) && REQUIRED_LDBL_MANT_DIG == 2098
     mpf_init2(t, 2098);
#else
     mpf_init2(t, 128);
#endif
     mpf_set_q(t, *x) ;

#if defined(USE_QUADMATH)
     ret = (NV)_mpf_get_float128(&t);
#elif defined(USE_LONG_DOUBLE)
     ret = (NV)_mpf_get_ld(&t);
#else
     ret = (NV)mpf_get_d(t);
#endif
     mpf_clear(t);

     return newSVnv(ret);
}

SV * overload_num(pTHX_ mpq_t * p, SV * second, SV * third) {
     PERL_UNUSED_ARG2(second, third);
  return Rmpq_get_NV(aTHX_ p);
}

SV * overload_copy(pTHX_ mpq_t * p, SV * second, SV * third) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     PERL_UNUSED_ARG2(second, third);

     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_copy function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");

     mpq_init(*mpq_t_obj);
     mpq_set(*mpq_t_obj, *p);
     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);
     return obj_ref;
}

SV * overload_abs(pTHX_ mpq_t * p, SV * second, SV * third) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     PERL_UNUSED_ARG2(second, third);

     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_abs function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);

     mpq_abs(*mpq_t_obj, *p);
     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);
     return obj_ref;
}

SV * _overload_gt(pTHX_ mpq_t * a, SV * b, SV * third) {
     mpq_t t;
     int ret;

     if(SV_IS_IOK(b)) {
       ret = Rmpq_cmp_IV(aTHX_ a, b, newSViv(1));
       if(SWITCH_ARGS) ret *= -1;
       if(ret > 0) return newSViv(1);
       return newSViv(0);
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       ret = _is_infstring(SvPV_nolen(b));
       if(ret) {
         if(ret > 0) return newSViv(0);
         return newSViv(1);
       }
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_gt");
       mpq_canonicalize(t);
       ret = mpq_cmp(*a, t);
       mpq_clear(t);
       if(SWITCH_ARGS) ret *= -1;
       if(ret > 0) return newSViv(1);
       return newSViv(0);
     }

     if(SV_IS_NOK(b)) {
       ret = Rmpq_cmp_NV(aTHX_ a, b);
       if(SWITCH_ARGS) ret *= -1;
       if(ret > 0) return newSViv(1);
       return newSViv(0);
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPq")) {
         ret = mpq_cmp(*a, *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         if(ret > 0) return newSViv(1);
         return newSViv(0);
       }

       if(strEQ(h, "Math::GMPz")) {
#if __GNU_MP_RELEASE < 60099
         ret = Rmpq_cmp_z(a, INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         if(ret > 0) return newSViv(1);
         return newSViv(0);
#else
         ret = mpq_cmp_z(*a, *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         if(ret > 0) return newSViv(1);
         return newSViv(0);
#endif
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_gt");
}

SV * _overload_gte(pTHX_ mpq_t * a, SV * b, SV * third) {
     mpq_t t;
     int ret;

     if(SV_IS_IOK(b)) {
       ret = Rmpq_cmp_IV(aTHX_ a, b, newSViv(1));
       if(SWITCH_ARGS) ret *= -1;
       if(ret >= 0) return newSViv(1);
       return newSViv(0);
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       ret = _is_infstring(SvPV_nolen(b));
       if(ret) {
         if(ret > 0) return newSViv(0);
         return newSViv(1);
       }
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_gte");
       mpq_canonicalize(t);
       ret = mpq_cmp(*a, t);
       mpq_clear(t);
       if(SWITCH_ARGS) ret *= -1;
       if(ret >= 0) return newSViv(1);
       return newSViv(0);
     }

     if(SV_IS_NOK(b)) {
       ret = Rmpq_cmp_NV(aTHX_ a, b);
       if(SWITCH_ARGS) ret *= -1;
       if(ret >= 0) return newSViv(1);
       return newSViv(0);
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPq")) {
         ret = mpq_cmp(*a, *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         if(ret >= 0) return newSViv(1);
         return newSViv(0);
       }

       if(strEQ(h, "Math::GMPz")) {
#if __GNU_MP_RELEASE < 60099
         ret = Rmpq_cmp_z(a, INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         if(ret >= 0) return newSViv(1);
         return newSViv(0);
#else
         ret = mpq_cmp_z(*a, *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         if(ret >= 0) return newSViv(1);
         return newSViv(0);
#endif
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_gte");
}

SV * _overload_lt(pTHX_ mpq_t * a, SV * b, SV * third) {
     mpq_t t;
     int ret;

     if(SV_IS_IOK(b)) {
       ret = Rmpq_cmp_IV(aTHX_ a, b, newSViv(1));
       if(SWITCH_ARGS) ret *= -1;
       if(ret < 0) return newSViv(1);
       return newSViv(0);
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       ret = _is_infstring(SvPV_nolen(b));
       if(ret) {
         if(ret > 0) return newSViv(1);
         return newSViv(0);
       }
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_lt");
       mpq_canonicalize(t);
       ret = mpq_cmp(*a, t);
       mpq_clear(t);
       if(SWITCH_ARGS) ret *= -1;
       if(ret < 0) return newSViv(1);
       return newSViv(0);
     }

     if(SV_IS_NOK(b)) {
       ret = Rmpq_cmp_NV(aTHX_ a, b);
       if(SWITCH_ARGS) ret *= -1;
       if(ret < 0) return newSViv(1);
       return newSViv(0);
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPq")) {
         ret = mpq_cmp(*a, *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         if(ret < 0) return newSViv(1);
         return newSViv(0);
       }

       if(strEQ(h, "Math::GMPz")) {
#if __GNU_MP_RELEASE < 60099
         ret = Rmpq_cmp_z(a, INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         if(ret < 0) return newSViv(1);
         return newSViv(0);
#else
         ret = mpq_cmp_z(*a, *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         if(ret < 0) return newSViv(1);
         return newSViv(0);
#endif
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_lt");
}

SV * _overload_lte(pTHX_ mpq_t * a, SV * b, SV * third) {
     mpq_t t;
     int ret;

     if(SV_IS_IOK(b)) {
       ret = Rmpq_cmp_IV(aTHX_ a, b, newSViv(1));
       if(SWITCH_ARGS) ret *= -1;
       if(ret <= 0) return newSViv(1);
       return newSViv(0);
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       ret = _is_infstring(SvPV_nolen(b));
       if(ret) {
         if(ret > 0) return newSViv(1);
         return newSViv(0);
       }
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_lte");
       mpq_canonicalize(t);
       ret = mpq_cmp(*a, t);
       mpq_clear(t);
       if(SWITCH_ARGS) ret *= -1;
       if(ret <= 0) return newSViv(1);
       return newSViv(0);
     }

     if(SV_IS_NOK(b)) {
       ret = Rmpq_cmp_NV(aTHX_ a, b);
       if(SWITCH_ARGS) ret *= -1;
       if(ret <= 0) return newSViv(1);
       return newSViv(0);
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPq")) {
         ret = mpq_cmp(*a, *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         if(ret <= 0) return newSViv(1);
         return newSViv(0);
       }

       if(strEQ(h, "Math::GMPz")) {
#if __GNU_MP_RELEASE < 60099
         ret = Rmpq_cmp_z(a, INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         if(ret <= 0) return newSViv(1);
         return newSViv(0);
#else
         ret = mpq_cmp_z(*a, *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         if(ret <= 0) return newSViv(1);
         return newSViv(0);
#endif
       }

     }

     croak("Invalid argument supplied to Math::GMPq::overload_lte");
}

SV * _overload_spaceship(pTHX_ mpq_t * a, SV * b, SV * third) {
     mpq_t t;
     int ret;

     if(SV_IS_IOK(b)) {
       ret = Rmpq_cmp_IV(aTHX_ a, b, newSViv(1));
       /*
          spaceship operator should return 1, -1 for unequal
          operands but mpq_cmp* functions are documented to
          return only >0 or <0. See:
          https://rt.cpan.org/Ticket/Display.html?id=168076
       */
       if(ret > 0) ret = 1;
       if(ret < 0) ret = -1;
       if(SWITCH_ARGS) ret *= -1;
       return newSViv(ret);
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       ret = _is_infstring(SvPV_nolen(b));
       if(ret) {
         if(ret > 0) return newSViv(-1);
         return newSViv(1);
       }
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_spaceship");
       mpq_canonicalize(t);
       ret = mpq_cmp(*a, t);
       if(ret > 0) ret = 1;
       if(ret < 0) ret = -1;
       mpq_clear(t);
       if(SWITCH_ARGS) ret *= -1;
       return newSViv(ret);
     }

     if(SV_IS_NOK(b)) {
       ret = Rmpq_cmp_NV(aTHX_ a, b);
       if(ret > 0) ret = 1;
       if(ret < 0) ret = -1;
       if(SWITCH_ARGS) ret *= -1;
       return newSViv(ret);
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPq")) {
         ret = mpq_cmp(*a, *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         if(ret > 0) ret = 1;
         if(ret < 0) ret = -1;
         return newSViv(ret);
       }
       if(strEQ(h, "Math::GMPz")) {
#if __GNU_MP_RELEASE < 60099
         ret = Rmpq_cmp_z(a, INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         if(ret > 0) ret = 1;
         if(ret < 0) ret = -1;
         return newSViv(ret);
#else
         ret = mpq_cmp_z(*a, *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         if(ret > 0) ret = 1;
         if(ret < 0) ret = -1;
         return newSViv(ret);
#endif
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_spaceship");
}

SV * _overload_equiv(pTHX_ mpq_t * a, SV * b, SV * third) {
     mpq_t t;
     int ret = 0;
     PERL_UNUSED_ARG(third);

     if(SV_IS_IOK(b)) {
       if(Rmpq_cmp_IV(aTHX_ a, b, newSViv(1))) return newSViv(0);
       return newSViv(1);
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       if(_is_infstring(SvPV_nolen(b))) return newSViv(0);
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_equiv");
       mpq_canonicalize(t);
       ret = mpq_equal(*a, t);
       mpq_clear(t);
       return newSViv(ret);
     }

     if(SV_IS_NOK(b)) {
       if(Rmpq_cmp_NV(aTHX_ a, b)) return newSViv(0);
       return newSViv(1);
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPq")) {
         return newSViv(mpq_equal(*a, *(INT2PTR(mpq_t *, SvIVX(SvRV(b))))));
       }

       if(strEQ(h, "Math::GMPz")) {
#if __GNU_MP_RELEASE < 60099
         if(Rmpq_cmp_z(a, INT2PTR(mpz_t *, SvIVX(SvRV(b))))) return newSViv(0);
         return newSViv(1);
#else
         if(mpq_cmp_z(*a, *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))))) return newSViv(0);
         return newSViv(1);
#endif
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_equiv");
}

SV * _overload_not_equiv(pTHX_ mpq_t * a, SV * b, SV * third) {
     mpq_t t;
     int ret = 0;
     PERL_UNUSED_ARG(third);


     if(SV_IS_IOK(b)) {
       if(Rmpq_cmp_IV(aTHX_ a, b, newSViv(1))) return newSViv(1);
       return newSViv(0);
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       if(_is_infstring(SvPV_nolen(b))) return newSViv(1);
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0))
         croak("Invalid string supplied to Math::GMPq::overload_not_equiv");
       mpq_canonicalize(t);
       ret = mpq_equal(*a, t);
       mpq_clear(t);
       if(ret) return newSViv(0);
       return newSViv(1);
     }

     if(SV_IS_NOK(b)) {
       if(Rmpq_cmp_NV(aTHX_ a, b)) return newSViv(1);
       return newSViv(0);
     }


     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPq")) {
         ret = mpq_equal(*a, *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         if(ret) return newSViv(0);
         return newSViv(1);
       }

       if(strEQ(h, "Math::GMPz")) {
#if __GNU_MP_RELEASE < 60099
         if(Rmpq_cmp_z(a, INT2PTR(mpz_t *, SvIVX(SvRV(b))))) return newSViv(1);
         return newSViv(0);
#else
         if(mpq_cmp_z(*a, *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))))) return newSViv(1);
         return newSViv(0);
#endif
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_not_equiv");
}

SV * overload_not(pTHX_ mpq_t * a, SV * second, SV * third) {
     PERL_UNUSED_ARG2(second, third);
     if(mpq_cmp_ui(*a, 0, 1)) return newSViv(0);
     return newSViv(1);
}

SV * overload_int(pTHX_ mpq_t * p, SV * second, SV * third) {
     mpz_t z;
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     PERL_UNUSED_ARG2(second, third);

     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_int function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);

     if( !mpz_cmp_ui(mpq_denref(*p), 1) ) {
       mpq_set(*mpq_t_obj, *p);
     }
     else {
       mpz_init(z);
       mpz_set_q(z, *p);
       mpq_set_z(*mpq_t_obj, z);
       mpz_clear(z);
     }

     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);
     return obj_ref;
}

SV * _overload_lshift(pTHX_ mpq_t * p, SV * ls, SV * third) {
     mpz_t z;
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;

     PERL_UNUSED_ARG(third);

     CHECK_MP_BITCNT_T_OVERFLOW(ls)
     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_int function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);

     mpz_init(z);
     mpz_set_q(z, *p);
     mpz_mul_2exp(z, z, (mp_bitcnt_t)SvUV(ls)); /* ls is always >= 0 */
     mpq_set_z(*mpq_t_obj, z);
     mpz_clear(z);

     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);
     return obj_ref;
}

SV * _overload_lshift_eq(pTHX_ SV * p, SV * ls, SV * third) {
     mpz_t z;

     PERL_UNUSED_ARG(third);

     CHECK_MP_BITCNT_T_OVERFLOW(ls)
     SvREFCNT_inc(p);
     mpz_init(z);
     mpz_set_q(z, *(INT2PTR(mpq_t *, SvIVX(SvRV(p)))));
     mpz_mul_2exp(z, z, (mp_bitcnt_t)SvUV(ls));  /* ls is always >= 0 */
     mpq_set_z(*(INT2PTR(mpq_t *, SvIVX(SvRV(p)))), z);
     mpz_clear(z);

     return p;
}

SV * _overload_rshift(pTHX_ mpq_t * p, SV * rs, SV * third) {
     mpz_t z;
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;

     PERL_UNUSED_ARG(third);

     CHECK_MP_BITCNT_T_OVERFLOW(rs)
     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_int function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);

     mpz_init(z);
     mpz_set_q(z, *p);
     mpz_div_2exp(z, z, (mp_bitcnt_t)SvUV(rs));  /* rs is always >= 0 */
     mpq_set_z(*mpq_t_obj, z);
     mpz_clear(z);

     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);
     return obj_ref;
}

SV * _overload_rshift_eq(pTHX_ SV * p, SV * rs, SV * third) {
     mpz_t z;

     PERL_UNUSED_ARG(third);

     CHECK_MP_BITCNT_T_OVERFLOW(rs)
     SvREFCNT_inc(p);
     mpz_init(z);
     mpz_set_q(z, *(INT2PTR(mpq_t *, SvIVX(SvRV(p)))));
     mpz_div_2exp(z, z, (mp_bitcnt_t)SvUV(rs));  /* rs is always >= 0 */
     mpq_set_z(*(INT2PTR(mpq_t *, SvIVX(SvRV(p)))), z);
     mpz_clear(z);

     return p;
}

/* Finish typemapping */

SV * _overload_mul_eq(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t t;
     PERL_UNUSED_ARG(third);

     SvREFCNT_inc(a);

     if(SV_IS_IOK(b)) {
       mpq_init(t);
       Rmpq_set_IV(aTHX_ &t, b, newSViv(1));
       mpq_mul(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0)) {
         SvREFCNT_dec(a);
         croak("Invalid string supplied to Math::GMPq::overload_mul_eq");
       }
       mpq_canonicalize(t);
       mpq_mul(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(SV_IS_NOK(b)) {

       mpq_init(t);
       Rmpq_set_NV(aTHX_ &t, b);
       mpq_mul(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::GMPq")) {
         mpq_mul(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return a;
       }

       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         Rmpq_mul_z(INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         return a;
       }
       /* Relocated to GMPq.pm
       if(strEQ(h, "Math::MPFR")) {
         _overload_callback("Math::MPFR::overload_mul", "Math::GMPq::overload_mul", newSViv(0));
       }
       */
     }

     SvREFCNT_dec(a);
     croak("Invalid argument supplied to Math::GMPq::overload_mul_eq");

}

SV * _overload_add_eq(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t t;
     PERL_UNUSED_ARG(third);

     SvREFCNT_inc(a);

     if(SV_IS_IOK(b)) {
       mpq_init(t);
       Rmpq_set_IV(aTHX_ &t, b, newSViv(1));
       mpq_add(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0)) {
         SvREFCNT_dec(a);
         croak("Invalid string supplied to Math::GMPq::overload_add_eq");
       }
       mpq_canonicalize(t);
       mpq_add(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(SV_IS_NOK(b)) {
       mpq_init(t);
       Rmpq_set_NV(aTHX_ &t, b);
       mpq_add(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::GMPq")) {
         mpq_add(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return a;
       }

       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         Rmpq_add_z(INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         return a;
       }
       /* Relocated to GMPq.pm
       if(strEQ(h, "Math::MPFR")) {
         _overload_callback("Math::MPFR::overload_add", "Math::GMPq::overload_add", newSViv(0));
       }
       */
     }

     SvREFCNT_dec(a);
     croak("Invalid argument supplied to Math::GMPq::overload_add_eq");
}

SV * _overload_sub_eq(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t t;
     PERL_UNUSED_ARG(third);

     SvREFCNT_inc(a);

     if(SV_IS_IOK(b)) {
       mpq_init(t);
       Rmpq_set_IV(aTHX_ &t, b, newSViv(1));
       mpq_sub(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0)) {
         SvREFCNT_dec(a);
         croak("Invalid string supplied to Math::GMPq::overload_sub_eq");
       }
       mpq_canonicalize(t);
       mpq_sub(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(SV_IS_NOK(b)) {

       mpq_init(t);
       Rmpq_set_NV(aTHX_ &t, b);
       mpq_sub(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::GMPq")) {
         mpq_sub(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return a;
       }

       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         Rmpq_sub_z(INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         return a;
       }
       /* Relocated to GMPq.pm
       if(strEQ(h, "Math::MPFR")) {
         _overload_callback("Math::MPFR::overload_sub", "Math::GMPq::overload_sub", &PL_sv_yes);
       }
       */
     }

     SvREFCNT_dec(a);
     croak("Invalid argument supplied to Math::GMPq::overload_sub_eq function");

}

SV * _overload_div_eq(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t t;
     PERL_UNUSED_ARG(third);

     SvREFCNT_inc(a);

     if(SV_IS_IOK(b)) {
       if(SvIV(b) == 0)
         croak("Division by 0 not allowed in Math::GMPq::overload_div_eq");
       mpq_init(t);
       Rmpq_set_IV(aTHX_ &t, b, newSViv(1));
       mpq_div(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

#if defined(GMPQ_PV_NV_BUG)
     if( (SV_IS_POK(b) && !SV_IS_NOK(b))
           ||
         (SV_IS_POK(b) && SV_IS_NOK(b) && SvIOKp(b)) ) {
#else
     if(SV_IS_POK(b)) {
#endif
       mpq_init(t);
       if(mpq_set_str(t, SvPV_nolen(b), 0)) {
         SvREFCNT_dec(a);
         croak("Invalid string supplied to Math::GMPq::overload_div_eq");
       }
       mpq_canonicalize(t);
       if(!mpq_cmp_ui(t, 0, 1))
         croak("Division by 0 not allowed in Math::GMPq::overload_div_eq");
       mpq_div(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(SV_IS_NOK(b)) {

       if(SvNV(b) == 0)
         croak("Division by 0 not allowed in Math::GMPq::overload_div_eq");

       /* If SvNV(b) is Inf or Nan, this will be caught by Rmpq_set_NV */

       mpq_init(t);
       Rmpq_set_NV(aTHX_ &t, b);
       mpq_div(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);
       mpq_clear(t);
       return a;
     }

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));

       if(strEQ(h, "Math::GMPq")) {
         if(!mpq_cmp_ui(*(INT2PTR(mpq_t *, SvIVX(SvRV(b)))), 0, 1))
           croak("Division by 0 not allowed in Math::GMPq::overload_div_eq");
         mpq_div(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         return a;
       }

       if(strEQ(h, "Math::GMPz") || strEQ(h, "Math::GMP")) {
         /* Rmpq_div_z will catch divby0 */
         Rmpq_div_z(INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpz_t *, SvIVX(SvRV(b))));
         return a;
       }
       /* Relocated to GMPq.pm
       if(strEQ(h, "Math::MPFR")) {
         _overload_callback("Math::MPFR::overload_div", "Math::GMPq::overload_div", &PL_sv_yes);
       }
       */
     }

     SvREFCNT_dec(a);
     croak("Invalid argument supplied to Math::GMPq::overload_div_eq function");

}

SV * _overload_pow_eq(pTHX_ SV * a, SV * b, SV * third) {
     PERL_UNUSED_ARG(third);
     SvREFCNT_inc(a);
     if(SvUOK(b) || (SV_IS_IOK(b) && SvIVX(b) >= 0)) {
       Rmpq_pow_ui(INT2PTR(mpq_t *, SvIVX(SvRV(a))), INT2PTR(mpq_t *, SvIVX(SvRV(a))), (unsigned long)SvUVX(b));
       return a;
     }
     /* Relocated to GMPq.pm
     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::MPFR")) {
         _overload_callback("Math::MPFR::overload_pow", "Math::GMPq::overload_pow", &PL_sv_yes);
       }
     }
     */

     SvREFCNT_dec(a);
     croak("Invalid argument supplied to Math::GMPq::overload_pow_eq function");
}

SV * gmp_v(pTHX) {
#if __GNU_MP_VERSION >= 4
     return newSVpv(gmp_version, 0);
#else
     warn("From Math::GMPq::gmp_v(aTHX): 'gmp_version' is not implemented - returning '0'");
     return newSVpv("0", 0);
#endif
}

SV * wrap_gmp_printf(pTHX_ SV * a, SV * b) {
     int ret;
     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPz") ||
         strEQ(h, "Math::GMP") ||
         strEQ(h, "GMP::Mpz")) {
         ret = gmp_printf(SvPV_nolen(a), *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         fflush(stdout);
         return newSViv(ret);
       }
       if(strEQ(h, "Math::GMPq") ||
         strEQ(h, "GMP::Mpq")) {
         ret = gmp_printf(SvPV_nolen(a), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         fflush(stdout);
         return newSViv(ret);
       }
       if(strEQ(h, "Math::GMPf") ||
         strEQ(h, "GMP::Mpf")) {
         ret = gmp_printf(SvPV_nolen(a), *(INT2PTR(mpf_t *, SvIVX(SvRV(b)))));
         fflush(stdout);
         return newSViv(ret);
       }

       croak("Unrecognised object supplied as argument to Rmpq_printf");
     }

     if(SV_IS_IOK(b)) {
       if(SvUOK(b)) {
         ret = gmp_printf(SvPV_nolen(a), SvUVX(b));
         fflush(stdout);
         return newSViv(ret);
       }

       ret = gmp_printf(SvPV_nolen(a), SvIVX(b));
       fflush(stdout);
       return newSViv(ret);
     }

     if(SV_IS_POK(b)) {
       ret = gmp_printf(SvPV_nolen(a), SvPV_nolen(b));
       fflush(stdout);
       return newSViv(ret);
     }

     if(SV_IS_NOK(b)) {
       ret = gmp_printf(SvPV_nolen(a), SvNVX(b));
       fflush(stdout);
       return newSViv(ret);
     }

     croak("Unrecognised type supplied as argument to Rmpq_printf");
}

SV * wrap_gmp_fprintf(pTHX_ FILE * stream, SV * a, SV * b) {
     int ret;
     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPz") ||
         strEQ(h, "Math::GMP") ||
         strEQ(h, "GMP::Mpz")) {
         ret = gmp_fprintf(stream, SvPV_nolen(a), *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         fflush(stream);
         return newSViv(ret);
       }
       if(strEQ(h, "Math::GMPq") ||
         strEQ(h, "GMP::Mpq")) {
         ret = gmp_fprintf(stream, SvPV_nolen(a), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         fflush(stream);
         return newSViv(ret);
       }
       if(strEQ(h, "Math::GMPf") ||
         strEQ(h, "GMP::Mpf")) {
         ret = gmp_fprintf(stream, SvPV_nolen(a), *(INT2PTR(mpf_t *, SvIVX(SvRV(b)))));
         fflush(stream);
         return newSViv(ret);
       }

       else croak("Unrecognised object supplied as argument to Rmpq_fprintf");
     }

     if(SV_IS_IOK(b)) {
       if(SvUOK(b)) {
         ret = gmp_fprintf(stream, SvPV_nolen(a), SvUVX(b));
         fflush(stream);
         return newSViv(ret);
       }

       ret = gmp_fprintf(stream, SvPV_nolen(a), SvIVX(b));
       fflush(stream);
       return newSViv(ret);
     }

     if(SV_IS_POK(b)) {
       ret = gmp_fprintf(stream, SvPV_nolen(a), SvPV_nolen(b));
       fflush(stream);
       return newSViv(ret);
     }

     if(SV_IS_NOK(b)) {
       ret = gmp_fprintf(stream, SvPV_nolen(a), SvNVX(b));
       fflush(stream);
       return newSViv(ret);
     }

     croak("Unrecognised type supplied as argument to Rmpq_fprintf");
}

SV * wrap_gmp_sprintf(pTHX_ SV * s, SV * a, SV * b, int buflen) {
     int ret;
     char * stream;

     Newx(stream, buflen, char);

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPz") ||
         strEQ(h, "Math::GMP") ||
         strEQ(h, "GMP::Mpz")) {
         ret = gmp_sprintf(stream, SvPV_nolen(a), *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       if(strEQ(h, "Math::GMPq") ||
         strEQ(h, "GMP::Mpq")) {
         ret = gmp_sprintf(stream, SvPV_nolen(a), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       if(strEQ(h, "Math::GMPf") ||
         strEQ(h, "GMP::Mpf")) {
         ret = gmp_sprintf(stream, SvPV_nolen(a), *(INT2PTR(mpf_t *, SvIVX(SvRV(b)))));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       Safefree(stream); /* In case the ensuing croak() is encased in an eval{} block */
       croak("Unrecognised object supplied as argument to Rmpq_sprintf");
     }

     if(SV_IS_IOK(b)) {
       if(SvUOK(b)) {
         ret = gmp_sprintf(stream, SvPV_nolen(a), SvUVX(b));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       ret = gmp_sprintf(stream, SvPV_nolen(a), SvIVX(b));
       sv_setpv(s, stream);
       Safefree(stream);
       return newSViv(ret);
     }

     if(SV_IS_POK(b)) {
       ret = gmp_sprintf(stream, SvPV_nolen(a), SvPV_nolen(b));
       sv_setpv(s, stream);
       Safefree(stream);
       return newSViv(ret);
     }

     if(SV_IS_NOK(b)) {
       ret = gmp_sprintf(stream, SvPV_nolen(a), SvNVX(b));
       sv_setpv(s, stream);
       Safefree(stream);
       return newSViv(ret);
     }

     Safefree(stream); /* In case the ensuing croak() is encased in an eval{} block */
     croak("Unrecognised type supplied as argument to Rmpq_sprintf");
}

SV * wrap_gmp_snprintf(pTHX_ SV * s, SV * bytes, SV * a, SV * b, int buflen) {
     int ret;
     char * stream;

     Newx(stream, buflen, char);

     if(sv_isobject(b)) {
       const char *h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::GMPz") ||
         strEQ(h, "Math::GMP") ||
         strEQ(h, "GMP::Mpz")) {
         ret = gmp_snprintf(stream, (size_t)SvUV(bytes), SvPV_nolen(a), *(INT2PTR(mpz_t *, SvIVX(SvRV(b)))));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       if(strEQ(h, "Math::GMPq") ||
         strEQ(h, "GMP::Mpq")) {
         ret = gmp_snprintf(stream, (size_t)SvUV(bytes), SvPV_nolen(a), *(INT2PTR(mpq_t *, SvIVX(SvRV(b)))));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       if(strEQ(h, "Math::GMPf") ||
         strEQ(h, "GMP::Mpf")) {
         ret = gmp_snprintf(stream, (size_t)SvUV(bytes), SvPV_nolen(a), *(INT2PTR(mpf_t *, SvIVX(SvRV(b)))));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       Safefree(stream); /* In case the ensuing croak() is encased in an eval{} block */
       croak("Unrecognised object supplied as argument to Rmpq_snprintf");
     }

     if(SV_IS_IOK(b)) {
       if(SvUOK(b)) {
         ret = gmp_snprintf(stream, (size_t)SvUV(bytes), SvPV_nolen(a), SvUV(b));
         sv_setpv(s, stream);
         Safefree(stream);
         return newSViv(ret);
       }

       ret = gmp_snprintf(stream, (size_t)SvUV(bytes), SvPV_nolen(a), SvIV(b));
       sv_setpv(s, stream);
       Safefree(stream);
       return newSViv(ret);
     }

     if(SV_IS_POK(b)) {
       ret = gmp_snprintf(stream, (size_t)SvUV(bytes), SvPV_nolen(a), SvPV_nolen(b));
       sv_setpv(s, stream);
       Safefree(stream);
       return newSViv(ret);
     }

     if(SV_IS_NOK(b)) {
       ret = gmp_snprintf(stream, (size_t)SvUV(bytes), SvPV_nolen(a), SvNV(b));
       sv_setpv(s, stream);
       Safefree(stream);
       return newSViv(ret);
     }

     Safefree(stream); /* In case the ensuing croak() is encased in an eval{} block */
     croak("Unrecognised type supplied as argument to Rmpq_snprintf");
}


SV * _itsa(pTHX_ SV * a) {
  if(SV_IS_IOK(a)) {
    if(SvUOK(a)) return newSVuv(1);
    return newSVuv(2);
  }
  if(SV_IS_POK(a)) {
#if defined(GMPQ_PV_NV_BUG)        /* perl can set the POK flag when it should not */
    if(SvNOK(a) && !SvIOKp(a))
      return newSVuv(3);        /* designate it as NV */
#endif
    return newSVuv(4);          /* designate it as PV */
  }
  if(SV_IS_NOK(a)) return newSVuv(3);
  if(sv_isobject(a)) {
    const char* h = HvNAME(SvSTASH(SvRV(a)));

    if(strEQ(h, "Math::MPFR")) return newSVuv(5);
    if(strEQ(h, "Math::GMPf")) return newSVuv(6);
    if(strEQ(h, "Math::GMPq")) return newSVuv(7);
    if(strEQ(h, "Math::GMPz")) return newSVuv(8);
    if(strEQ(h, "Math::GMP")) return newSVuv(9);
  }
  return newSVuv(0);
}

int _has_longlong(void) {
#ifdef MATH_GMPQ_NEED_LONG_LONG_INT
    return 1;
#else
    return 0;
#endif
}

int _has_longdouble(void) {
#ifdef USE_LONG_DOUBLE
    return 1;
#else
    return 0;
#endif
}

/* Has inttypes.h been included ? */
int _has_inttypes(void) {

#if defined MATH_GMPQ_NEED_LONG_LONG_INT
return 1;
#else
return 0;
#endif
}

SV * ___GNU_MP_VERSION(pTHX) {
     return newSVuv(__GNU_MP_VERSION);
}

SV * ___GNU_MP_VERSION_MINOR(pTHX) {
     return newSVuv(__GNU_MP_VERSION_MINOR);
}

SV * ___GNU_MP_VERSION_PATCHLEVEL(pTHX) {
     return newSVuv(__GNU_MP_VERSION_PATCHLEVEL);
}

SV * ___GNU_MP_RELEASE(pTHX) {
#if defined(__GNU_MP_RELEASE)
     return newSVuv(__GNU_MP_RELEASE);
#else
     return &PL_sv_undef;
#endif
}

SV * ___GMP_CC(pTHX) {
#ifdef __GMP_CC
     char * ret = __GMP_CC;
     return newSVpv(ret, 0);
#else
     return &PL_sv_undef;
#endif
}

SV * ___GMP_CFLAGS(pTHX) {
#ifdef __GMP_CFLAGS
     char * ret = __GMP_CFLAGS;
     return newSVpv(ret, 0);
#else
     return &PL_sv_undef;
#endif
}

void overload_inc(pTHX_ SV * p, SV * second, SV * third) {
     mpq_t one;
     PERL_UNUSED_ARG2(second, third);

     mpq_init(one);
     mpq_set_ui(one, 1, 1);

     mpq_add(*(INT2PTR(mpq_t *, SvIVX(SvRV(p)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(p)))), one);
     mpq_clear(one);
}

void overload_dec(pTHX_ SV * p, SV * second, SV * third) {
     mpq_t one;
     PERL_UNUSED_ARG2(second, third);

     mpq_init(one);
     mpq_set_ui(one, 1, 1);

     mpq_sub(*(INT2PTR(mpq_t *, SvIVX(SvRV(p)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(p)))), one);
     mpq_clear(one);
}

SV * _wrap_count(pTHX) {
     return newSVuv(PL_sv_count);
}

SV * _overload_pow(pTHX_ SV * a, SV * b, SV * third) {
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;
     const char *h;

     if(SWITCH_ARGS) croak("Raising a value to an mpq_t power is not allowed in '**' operation in Math::GMPq::overload_pow");

     if(SvUOK(b) || (SV_IS_IOK(b) && SvIVX(b) >= 0)) {
       New(1, mpq_t_obj, 1, mpq_t);
       if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_pow function");
       obj_ref = newSV(0);
       obj = newSVrv(obj_ref, "Math::GMPq");
       mpq_init(*mpq_t_obj);
       sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
       SvREADONLY_on(obj);
       Rmpq_pow_ui(mpq_t_obj, INT2PTR(mpq_t *, SvIVX(SvRV(a))), (unsigned long)SvUVX(b));
       return obj_ref;
     }
     if(sv_isobject(b)) {
       h = HvNAME(SvSTASH(SvRV(b)));
       if(strEQ(h, "Math::MPFR")) {
         _overload_callback("Math::MPFR::overload_pow", "Math::GMPq:overload_pow", &PL_sv_yes);
       }
     }

     croak("Invalid argument supplied to Math::GMPq::overload_pow");
}

SV * _get_xs_version(pTHX) {
     return newSVpv(XS_VERSION, 0);
}

int Rmpq_integer_p(mpq_t * q) {
    if(mpz_cmp_si(mpq_denref(*q), 1)) return 0;
    return 1;
}

SV * _GMP_LIMB_BITS(pTHX) {
#ifdef GMP_LIMB_BITS
     return newSVuv(GMP_LIMB_BITS);
#else
     return &PL_sv_undef;
#endif
}

SV * _GMP_NAIL_BITS(pTHX) {
#ifdef GMP_NAIL_BITS
     return newSVuv(GMP_NAIL_BITS);
#else
     return &PL_sv_undef;
#endif
}

long _long_min(void) {
   return (long)LONG_MIN;
}

long _long_max(void) {
   return (long)LONG_MAX;
}

unsigned long _ulong_max(void) {
   return (unsigned long)ULONG_MAX;
}

int _int_min(void) {
   return (int)INT_MIN;
}

int _int_max(void) {
   return (int)INT_MAX;
}

unsigned int _uint_max(void) {
   return (unsigned int)UINT_MAX;
}

int _SvPOK(pTHX_ SV * in) {
   if(SV_IS_POK(in)) return 1;
   return 0;
}

int _required_ldbl_mant_dig(void) {
    return REQUIRED_LDBL_MANT_DIG;
}

int IOK_flag(SV * sv) {
  if(SvUOK(sv)) return 2;
  if(SvIOK(sv)) return 1;
  return 0;
}

int POK_flag(SV * sv) {
  if(SvPOK(sv)) return 1;
  return 0;
}

int NOK_flag(SV * sv) {
  if(SvNOK(sv)) return 1;
  return 0;
}

int _has_pv_nv_bug(void) {
#if defined(GMPQ_PV_NV_BUG)
     return 1;
#else
     return 0;
#endif
}

int _looks_like_number(pTHX_ SV * in) {
  if(looks_like_number(in)) return 1;
  return 0;
}

SV * _overload_fmod (pTHX_ mpq_t * a, mpq_t *b, SV * third) {
     mpz_t z;
     mpq_t * mpq_t_obj;
     SV * obj_ref, * obj;

     mpz_init(z);

     /* create new object */
     New(1, mpq_t_obj, 1, mpq_t);
     if(mpq_t_obj == NULL) croak("Failed to allocate memory in overload_div function");
     obj_ref = newSV(0);
     obj = newSVrv(obj_ref, "Math::GMPq");
     mpq_init(*mpq_t_obj);
     sv_setiv(obj, INT2PTR(IV, mpq_t_obj));
     SvREADONLY_on(obj);

     if(SWITCH_ARGS) {
       mpq_div(*mpq_t_obj, *b, *a);
       mpz_set_q(z, *mpq_t_obj);
       mpq_set_z(*mpq_t_obj, z);
       mpq_mul(*mpq_t_obj, *a, *mpq_t_obj);
       mpq_sub(*mpq_t_obj, *b, *mpq_t_obj);
     }
     else {
       mpq_div(*mpq_t_obj, *a, *b);
       mpz_set_q(z, *mpq_t_obj);
       mpq_set_z(*mpq_t_obj, z);
       mpq_mul(*mpq_t_obj, *b, *mpq_t_obj);
       mpq_sub(*mpq_t_obj, *a, *mpq_t_obj);
     }
     mpz_clear(z);
     return obj_ref;
}

SV * _overload_fmod_eq (pTHX_ SV * a, mpq_t *b, SV * third) {
     mpq_t t;
     mpz_t z;

     PERL_UNUSED_ARG(third);
     SvREFCNT_inc(a);

     mpq_init(t);
     mpz_init(z);
     mpq_div(t, *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *b);
     mpz_set_q(z, t);
     mpq_set_z(t, z);
     mpq_mul(t, *b, t);
     mpq_sub(*(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpq_t *, SvIVX(SvRV(a)))), t);

     mpq_clear(t);
     mpz_clear(z);

     return a;
}

SV * _to_base(pTHX_ SV * str, int base) {
    /* Convert the value held in the string *
     * from the specified base to base 10   */

    char buf[16];
    mpz_t t;

    if(mpz_init_set_str(t, SvPV_nolen(str), base))
      croak("Exponent portion of string supplied to Rmpq_set_str function (%s) is not a valid base %d number", SvPV_nolen(str), base);
    gmp_sprintf (buf, "%Zd", t);

    mpz_clear(t);
    return newSVpv(buf, 0);
}






MODULE = Math::GMPq  PACKAGE = Math::GMPq

PROTOTYPES: DISABLE


int
_is_infstring (s)
	char *	s

void
Rmpq_canonicalize (p)
	mpq_t *	p
        PPCODE:
        Rmpq_canonicalize(p);
        XSRETURN_EMPTY; /* return empty stack */

SV *
Rmpq_init ()
CODE:
  RETVAL = Rmpq_init (aTHX);
OUTPUT:  RETVAL


SV *
Rmpq_init_nobless ()
CODE:
  RETVAL = Rmpq_init_nobless (aTHX);
OUTPUT:  RETVAL


void
DESTROY (p)
	mpq_t *	p
        PPCODE:
        DESTROY(aTHX_ p);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_clear (p)
	mpq_t *	p
        PPCODE:
        Rmpq_clear(aTHX_ p);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_clear_mpq (p)
	mpq_t *	p
        PPCODE:
        Rmpq_clear_mpq(p);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_clear_ptr (p)
	mpq_t *	p
        PPCODE:
        Rmpq_clear_ptr(aTHX_ p);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set (p1, p2)
	mpq_t *	p1
	mpq_t *	p2
        PPCODE:
        Rmpq_set(p1, p2);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_swap (p1, p2)
	mpq_t *	p1
	mpq_t *	p2
        PPCODE:
        Rmpq_swap(p1, p2);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set_z (p1, p2)
	mpq_t *	p1
	mpz_t *	p2
        PPCODE:
        Rmpq_set_z(p1, p2);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set_ui (p1, p2, p3)
	mpq_t *	p1
	unsigned long	p2
	unsigned long	p3
        PPCODE:
        Rmpq_set_ui(p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set_si (p1, p2, p3)
	mpq_t *	p1
	long	p2
	long	p3
        PPCODE:
        Rmpq_set_si(p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
_Rmpq_set_str (p1, p2, base)
	mpq_t *	p1
	SV *	p2
	int	base
        PPCODE:
        _Rmpq_set_str(aTHX_ p1, p2, base);
        XSRETURN_EMPTY; /* return empty stack */

double
Rmpq_get_d (p)
	mpq_t *	p

void
Rmpq_set_d (p, d)
	mpq_t *	p
	double	d
        PPCODE:
        Rmpq_set_d(p, d);
        XSRETURN_EMPTY; /* return empty stack */

void
_mpf_set_doubledouble (q, p)
	mpf_t *	q
	SV *	p
        PPCODE:
        _mpf_set_doubledouble(q, p);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set_NV (copy, original)
	mpq_t *	copy
	SV *	original
        PPCODE:
        Rmpq_set_NV(aTHX_ copy, original);
        XSRETURN_EMPTY; /* return empty stack */

int
Rmpq_cmp_NV (a, b)
	mpq_t *	a
	SV *	b
CODE:
  RETVAL = Rmpq_cmp_NV (aTHX_ a, b);
OUTPUT:  RETVAL

void
Rmpq_set_f (p, f)
	mpq_t *	p
	mpf_t *	f
        PPCODE:
        Rmpq_set_f(p, f);
        XSRETURN_EMPTY; /* return empty stack */

SV *
Rmpq_get_str (p, base)
	mpq_t *	p
	int	base
CODE:
  RETVAL = Rmpq_get_str (aTHX_ p, base);
OUTPUT:  RETVAL

int
Rmpq_cmp (p1, p2)
	mpq_t *	p1
	mpq_t *	p2

int
Rmpq_cmp_ui (p1, n, d)
	mpq_t *	p1
	unsigned long	n
	unsigned long	d

int
Rmpq_cmp_si (p1, n, d)
	mpq_t *	p1
	long	n
	unsigned long	d

int
Rmpq_cmp_z (p, z)
	mpq_t *	p
	mpz_t *	z

int
Rmpq_sgn (p)
	mpq_t *	p

int
Rmpq_equal (p1, p2)
	mpq_t *	p1
	mpq_t *	p2

void
Rmpq_add (p1, p2, p3)
	mpq_t *	p1
	mpq_t *	p2
	mpq_t *	p3
        PPCODE:
        Rmpq_add(p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_sub (p1, p2, p3)
	mpq_t *	p1
	mpq_t *	p2
	mpq_t *	p3
        PPCODE:
        Rmpq_sub(p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_mul (p1, p2, p3)
	mpq_t *	p1
	mpq_t *	p2
	mpq_t *	p3
        PPCODE:
        Rmpq_mul(p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_div (p1, p2, p3)
	mpq_t *	p1
	mpq_t *	p2
	mpq_t *	p3
        PPCODE:
        Rmpq_div(p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_mul_2exp (p1, p2, p3)
	mpq_t *	p1
	mpq_t *	p2
	SV *	p3
        PPCODE:
        Rmpq_mul_2exp(aTHX_ p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_div_2exp (p1, p2, p3)
	mpq_t *	p1
	mpq_t *	p2
	SV *	p3
        PPCODE:
        Rmpq_div_2exp(aTHX_ p1, p2, p3);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_neg (p1, p2)
	mpq_t *	p1
	mpq_t *	p2
        PPCODE:
        Rmpq_neg(p1, p2);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_abs (p1, p2)
	mpq_t *	p1
	mpq_t *	p2
        PPCODE:
        Rmpq_abs(p1, p2);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_inv (p1, p2)
	mpq_t *	p1
	mpq_t *	p2
        PPCODE:
        Rmpq_inv(p1, p2);
        XSRETURN_EMPTY; /* return empty stack */

SV *
_Rmpq_out_str (p, base)
	mpq_t *	p
	int	base
CODE:
  RETVAL = _Rmpq_out_str (aTHX_ p, base);
OUTPUT:  RETVAL

SV *
_Rmpq_out_strS (p, base, suff)
	mpq_t *	p
	int	base
	SV *	suff
CODE:
  RETVAL = _Rmpq_out_strS (aTHX_ p, base, suff);
OUTPUT:  RETVAL

SV *
_Rmpq_out_strP (pre, p, base)
	SV *	pre
	mpq_t *	p
	int	base
CODE:
  RETVAL = _Rmpq_out_strP (aTHX_ pre, p, base);
OUTPUT:  RETVAL

SV *
_Rmpq_out_strPS (pre, p, base, suff)
	SV *	pre
	mpq_t *	p
	int	base
	SV *	suff
CODE:
  RETVAL = _Rmpq_out_strPS (aTHX_ pre, p, base, suff);
OUTPUT:  RETVAL

SV *
_TRmpq_out_str (stream, base, p)
	FILE *	stream
	int	base
	mpq_t *	p
CODE:
  RETVAL = _TRmpq_out_str (aTHX_ stream, base, p);
OUTPUT:  RETVAL

SV *
_TRmpq_out_strS (stream, base, p, suff)
	FILE *	stream
	int	base
	mpq_t *	p
	SV *	suff
CODE:
  RETVAL = _TRmpq_out_strS (aTHX_ stream, base, p, suff);
OUTPUT:  RETVAL

SV *
_TRmpq_out_strP (pre, stream, base, p)
	SV *	pre
	FILE *	stream
	int	base
	mpq_t *	p
CODE:
  RETVAL = _TRmpq_out_strP (aTHX_ pre, stream, base, p);
OUTPUT:  RETVAL

SV *
_TRmpq_out_strPS (pre, stream, base, p, suff)
	SV *	pre
	FILE *	stream
	int	base
	mpq_t *	p
	SV *	suff
CODE:
  RETVAL = _TRmpq_out_strPS (aTHX_ pre, stream, base, p, suff);
OUTPUT:  RETVAL

SV *
TRmpq_inp_str (p, stream, base)
	mpq_t *	p
	FILE *	stream
	SV *	base
CODE:
  RETVAL = TRmpq_inp_str (aTHX_ p, stream, base);
OUTPUT:  RETVAL

SV *
Rmpq_inp_str (p, base)
	mpq_t *	p
	int	base
CODE:
  RETVAL = Rmpq_inp_str (aTHX_ p, base);
OUTPUT:  RETVAL

void
Rmpq_numref (z, r)
	mpz_t *	z
	mpq_t *	r
        PPCODE:
        Rmpq_numref(z, r);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_denref (z, r)
	mpz_t *	z
	mpq_t *	r
        PPCODE:
        Rmpq_denref(z, r);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_get_num (z, r)
	mpz_t *	z
	mpq_t *	r
        PPCODE:
        Rmpq_get_num(z, r);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_get_den (z, r)
	mpz_t *	z
	mpq_t *	r
        PPCODE:
        Rmpq_get_den(z, r);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set_num (r, z)
	mpq_t *	r
	mpz_t *	z
        PPCODE:
        Rmpq_set_num(r, z);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set_den (r, z)
	mpq_t *	r
	mpz_t *	z
        PPCODE:
        Rmpq_set_den(r, z);
        XSRETURN_EMPTY; /* return empty stack */

SV *
get_refcnt (s)
	SV *	s
CODE:
  RETVAL = get_refcnt (aTHX_ s);
OUTPUT:  RETVAL

void
Rmpq_add_z (rop, op, z)
	mpq_t *	rop
	mpq_t *	op
	mpz_t *	z
        PPCODE:
        Rmpq_add_z(rop, op, z);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_sub_z (rop, op, z)
	mpq_t *	rop
	mpq_t *	op
	mpz_t *	z
        PPCODE:
        Rmpq_sub_z(rop, op, z);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_z_sub (rop, z, op)
	mpq_t *	rop
	mpz_t *	z
	mpq_t *	op
        PPCODE:
        Rmpq_z_sub(rop, z, op);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_mul_z (rop, op, z)
	mpq_t *	rop
	mpq_t *	op
	mpz_t *	z
        PPCODE:
        Rmpq_mul_z(rop, op, z);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_div_z (rop, op, z)
	mpq_t *	rop
	mpq_t *	op
	mpz_t *	z
        PPCODE:
        Rmpq_div_z(rop, op, z);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_z_div (rop, z, op)
	mpq_t *	rop
	mpz_t *	z
	mpq_t *	op
        PPCODE:
        Rmpq_z_div(rop, z, op);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_pow_ui (rop, op, ui)
	mpq_t *	rop
	mpq_t *	op
	unsigned long	ui
        PPCODE:
        Rmpq_pow_ui(rop, op, ui);
        XSRETURN_EMPTY; /* return empty stack */

void
Rmpq_set_IV (a, my_iv1, my_iv2)
	mpq_t *	a
	SV *	my_iv1
	SV *	my_iv2
        PPCODE:
        Rmpq_set_IV(aTHX_ a, my_iv1, my_iv2);
        XSRETURN_EMPTY; /* return empty stack */

int
Rmpq_cmp_IV (q, iv1, iv2)
	mpq_t *	q
	SV *	iv1
	SV *	iv2
CODE:
  RETVAL = Rmpq_cmp_IV (aTHX_ q, iv1, iv2);
OUTPUT:  RETVAL

SV *
_overload_mul (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_mul (aTHX_ a, b, third);
OUTPUT:  RETVAL

void
Rmpq_and (rop, a, b)
	mpq_t *	rop
	mpq_t *	a
	mpq_t *	b
        PREINIT:
        I32* temp;
        PPCODE:
        temp = PL_markstack_ptr++;
        Rmpq_and(rop, a, b);
        if (PL_markstack_ptr != temp) {
          /* truly void, because dXSARGS not invoked */
          PL_markstack_ptr = temp;
          XSRETURN_EMPTY; /* return empty stack */
        }
        /* must have used dXSARGS; list context implied */
        return;

void
Rmpq_ior (rop, a, b)
	mpq_t *	rop
	mpq_t *	a
	mpq_t *	b
        PREINIT:
        I32* temp;
        PPCODE:
        temp = PL_markstack_ptr++;
        Rmpq_ior(rop, a, b);
        if (PL_markstack_ptr != temp) {
          /* truly void, because dXSARGS not invoked */
          PL_markstack_ptr = temp;
          XSRETURN_EMPTY; /* return empty stack */
        }
        /* must have used dXSARGS; list context implied */
        return;

void
Rmpq_xor (rop, a, b)
	mpq_t *	rop
	mpq_t *	a
	mpq_t *	b
        PREINIT:
        I32* temp;
        PPCODE:
        temp = PL_markstack_ptr++;
        Rmpq_xor(rop, a, b);
        if (PL_markstack_ptr != temp) {
          /* truly void, because dXSARGS not invoked */
          PL_markstack_ptr = temp;
          XSRETURN_EMPTY; /* return empty stack */
        }
        /* must have used dXSARGS; list context implied */
        return;

void
Rmpq_com (rop, a)
	mpq_t *	rop
	mpq_t *	a
        PREINIT:
        I32* temp;
        PPCODE:
        temp = PL_markstack_ptr++;
        Rmpq_com(rop, a);
        if (PL_markstack_ptr != temp) {
          /* truly void, because dXSARGS not invoked */
          PL_markstack_ptr = temp;
          XSRETURN_EMPTY; /* return empty stack */
        }
        /* must have used dXSARGS; list context implied */
        return;

SV *
_overload_add (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_add (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_sub (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_sub (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_div (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_div (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
overload_string (p, second, third)
	mpq_t *	p
	SV *	second
	SV *	third
CODE:
  RETVAL = overload_string (aTHX_ p, second, third);
OUTPUT:  RETVAL

NV
_mpf_get_float128 (x)
	mpf_t *	x

int
_rndaz (a, exponent, prec, display)
	char *	a
	IV	exponent
	UV	prec
	int	display

double
_mpf_get_d_rndn (p)
	mpf_t *	p

NV
_mpf_get_ld (x)
	mpf_t *	x

SV *
Rmpq_get_NV (x)
	mpq_t *	x
CODE:
  RETVAL = Rmpq_get_NV (aTHX_ x);
OUTPUT:  RETVAL

SV *
overload_num (p, second, third)
	mpq_t *	p
	SV *	second
	SV *	third
CODE:
  RETVAL = overload_num (aTHX_ p, second, third);
OUTPUT:  RETVAL

SV *
overload_copy (p, second, third)
	mpq_t *	p
	SV *	second
	SV *	third
CODE:
  RETVAL = overload_copy (aTHX_ p, second, third);
OUTPUT:  RETVAL

SV *
overload_abs (p, second, third)
	mpq_t *	p
	SV *	second
	SV *	third
CODE:
  RETVAL = overload_abs (aTHX_ p, second, third);
OUTPUT:  RETVAL

SV *
_overload_gt (a, b, third)
	mpq_t *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_gt (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_gte (a, b, third)
	mpq_t *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_gte (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_lt (a, b, third)
	mpq_t *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_lt (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_lte (a, b, third)
	mpq_t *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_lte (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_spaceship (a, b, third)
	mpq_t *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_spaceship (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_equiv (a, b, third)
	mpq_t *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_equiv (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_not_equiv (a, b, third)
	mpq_t *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_not_equiv (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
overload_not (a, second, third)
	mpq_t *	a
	SV *	second
	SV *	third
CODE:
  RETVAL = overload_not (aTHX_ a, second, third);
OUTPUT:  RETVAL

SV *
overload_int (p, second, third)
	mpq_t *	p
	SV *	second
	SV *	third
CODE:
  RETVAL = overload_int (aTHX_ p, second, third);
OUTPUT:  RETVAL

SV *
_overload_lshift (p, ls, third)
	mpq_t *	p
	SV *	ls
	SV *	third
CODE:
  RETVAL = _overload_lshift (aTHX_ p, ls, third);
OUTPUT:  RETVAL

SV *
_overload_lshift_eq (p, ls, third)
	SV *	p
	SV *	ls
	SV *	third
CODE:
  RETVAL = _overload_lshift_eq (aTHX_ p, ls, third);
OUTPUT:  RETVAL

SV *
_overload_rshift (p, rs, third)
	mpq_t *	p
	SV *	rs
	SV *	third
CODE:
  RETVAL = _overload_rshift (aTHX_ p, rs, third);
OUTPUT:  RETVAL

SV *
_overload_rshift_eq (p, rs, third)
	SV *	p
	SV *	rs
	SV *	third
CODE:
  RETVAL = _overload_rshift_eq (aTHX_ p, rs, third);
OUTPUT:  RETVAL

SV *
_overload_mul_eq (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_mul_eq (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_add_eq (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_add_eq (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_sub_eq (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_sub_eq (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_div_eq (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_div_eq (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_pow_eq (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_pow_eq (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
gmp_v ()
CODE:
  RETVAL = gmp_v (aTHX);
OUTPUT:  RETVAL


SV *
wrap_gmp_printf (a, b)
	SV *	a
	SV *	b
CODE:
  RETVAL = wrap_gmp_printf (aTHX_ a, b);
OUTPUT:  RETVAL

SV *
wrap_gmp_fprintf (stream, a, b)
	FILE *	stream
	SV *	a
	SV *	b
CODE:
  RETVAL = wrap_gmp_fprintf (aTHX_ stream, a, b);
OUTPUT:  RETVAL

SV *
wrap_gmp_sprintf (s, a, b, buflen)
	SV *	s
	SV *	a
	SV *	b
	int	buflen
CODE:
  RETVAL = wrap_gmp_sprintf (aTHX_ s, a, b, buflen);
OUTPUT:  RETVAL

SV *
wrap_gmp_snprintf (s, bytes, a, b, buflen)
	SV *	s
	SV *	bytes
	SV *	a
	SV *	b
	int	buflen
CODE:
  RETVAL = wrap_gmp_snprintf (aTHX_ s, bytes, a, b, buflen);
OUTPUT:  RETVAL

SV *
_itsa (a)
	SV *	a
CODE:
  RETVAL = _itsa (aTHX_ a);
OUTPUT:  RETVAL

int
_has_longlong ()


int
_has_longdouble ()


int
_has_inttypes ()


SV *
___GNU_MP_VERSION ()
CODE:
  RETVAL = ___GNU_MP_VERSION (aTHX);
OUTPUT:  RETVAL


SV *
___GNU_MP_VERSION_MINOR ()
CODE:
  RETVAL = ___GNU_MP_VERSION_MINOR (aTHX);
OUTPUT:  RETVAL


SV *
___GNU_MP_VERSION_PATCHLEVEL ()
CODE:
  RETVAL = ___GNU_MP_VERSION_PATCHLEVEL (aTHX);
OUTPUT:  RETVAL


SV *
___GNU_MP_RELEASE ()
CODE:
  RETVAL = ___GNU_MP_RELEASE (aTHX);
OUTPUT:  RETVAL


SV *
___GMP_CC ()
CODE:
  RETVAL = ___GMP_CC (aTHX);
OUTPUT:  RETVAL


SV *
___GMP_CFLAGS ()
CODE:
  RETVAL = ___GMP_CFLAGS (aTHX);
OUTPUT:  RETVAL


void
overload_inc (p, second, third)
	SV *	p
	SV *	second
	SV *	third
        PPCODE:
        overload_inc(aTHX_ p, second, third);
        XSRETURN_EMPTY; /* return empty stack */

void
overload_dec (p, second, third)
	SV *	p
	SV *	second
	SV *	third
        PPCODE:
        overload_dec(aTHX_ p, second, third);
        XSRETURN_EMPTY; /* return empty stack */

SV *
_wrap_count ()
CODE:
  RETVAL = _wrap_count (aTHX);
OUTPUT:  RETVAL


SV *
_overload_pow (a, b, third)
	SV *	a
	SV *	b
	SV *	third
CODE:
  RETVAL = _overload_pow (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_get_xs_version ()
CODE:
  RETVAL = _get_xs_version (aTHX);
OUTPUT:  RETVAL


int
Rmpq_integer_p (q)
	mpq_t *	q

SV *
_GMP_LIMB_BITS ()
CODE:
  RETVAL = _GMP_LIMB_BITS (aTHX);
OUTPUT:  RETVAL


SV *
_GMP_NAIL_BITS ()
CODE:
  RETVAL = _GMP_NAIL_BITS (aTHX);
OUTPUT:  RETVAL


long
_long_min ()


long
_long_max ()


unsigned long
_ulong_max ()


int
_int_min ()


int
_int_max ()


unsigned int
_uint_max ()


int
_SvPOK (in)
	SV *	in
CODE:
  RETVAL = _SvPOK (aTHX_ in);
OUTPUT:  RETVAL

int
_required_ldbl_mant_dig ()


int
IOK_flag (sv)
	SV *	sv

int
POK_flag (sv)
	SV *	sv

int
NOK_flag (sv)
	SV *	sv

int
_has_pv_nv_bug ()


int
_looks_like_number (in)
	SV *	in
CODE:
  RETVAL = _looks_like_number (aTHX_ in);
OUTPUT:  RETVAL

SV *
_overload_fmod (a, b, third)
	mpq_t *	a
	mpq_t *	b
	SV *	third
CODE:
  RETVAL = _overload_fmod (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_overload_fmod_eq (a, b, third)
	SV *	a
	mpq_t *	b
	SV *	third
CODE:
  RETVAL = _overload_fmod_eq (aTHX_ a, b, third);
OUTPUT:  RETVAL

SV *
_to_base (str, base)
	SV *	str
	int	base
CODE:
  RETVAL = _to_base (aTHX_ str, base);
OUTPUT:  RETVAL