//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigint.c (implementation of the gmp interface) 
// Author      : Thomas Papanikolaou (TP)
// Last change : TP, Feb  7 1995, initial version
//	         MM, Sep 17 1996, 
//		     In the gmp-1.0 version the interface functions
//		     rounded to zero. In the gmp-2.0 version these
//		     functions round to -infinity. Therefore I have
//		     replaced the interface functions by special
//		     functions which round to zero:
//		     -) replaced mpz_div by mpz_tdiv_q 
//		     -) replaced mpz_divmod by mpz_tdiv_qr
//		     -) replaced mpz_div_2exp by mpz_tdiv_q_2exp
//		     
//		     The gmp-2.0 mpz_mod function always delivers a
//		     non-negative rest, but LiDIA's rest r has sign:
//		     sign(r) = sign(a), if r = a % m. Therefore I added an
//		     if-then to compute the correct sign.
//
//		     Modified the code of the power functions to
//		     handle the special cases (a = +-1) correctly.
//
//               MM, Oct 21 1996
//                   Added test for 0 in randomize.
//      
//		 MM, Jan 9 1997
//		     Exchanged the code for print_to_file and write_to_file
//		     and also for scan_from_file and read_from_file.
//
//		 MM, Jan 10 1997 
//		     Changed the return value of string_to_bigint to the number of
//		     read characters instead of simply 0 or -1.
//


#include <LiDIA/bigint.h>
#include <math.h>
#include <ctype.h>
#include <stdio.h>

/**
** constructors and destructor; we could leave out some of these
**/

bigint::bigint()
{ mpz_init(&I); } 

bigint::bigint(int i)
{ mpz_init_set_si(&I, i); } 

bigint::bigint(long l)
{ mpz_init_set_si(&I, l); }

bigint::bigint(unsigned long ul)
{ mpz_init_set_ui(&I, ul); }

void bigint::assign(double d)
{
  MP_INT tmp;
  double mantisse;
  int exponent;
  unsigned long lmantisse;
  int sign = 0, count = 0;
 
  if (d < 0.0)
  {
   sign = 1;
   d = -d;
  }
 
  if (d >=0 && d < 0.5)
  {
    mpz_init(&I);
  }
  else 
  {
    mpz_init(&I);
    mpz_init(&tmp);
    mantisse = frexp(d, &exponent);
    while (mantisse != 0.0)
    {
      mpz_mul_2exp(&I, &I, BITS_PER_MP_LIMB);
      mantisse = ldexp(mantisse, BITS_PER_MP_LIMB);
      lmantisse = (unsigned long) mantisse;
      mantisse -= lmantisse;
      mpz_set_ui(&tmp, lmantisse);
      mpz_ior(&I, &I, &tmp);
      count++;
    }
    exponent -= count * BITS_PER_MP_LIMB;
    if (exponent >= 0)
        mpz_mul_2exp(&I, &I, exponent);
    else
        mpz_tdiv_q_2exp(&I, &I, -exponent);
    if (sign)
      mpz_neg(&I, &I);
  }
  mpz_clear(&tmp);
}
 
bigint::bigint(const bigint & a)
{ mpz_init_set(&I, &a.I); }

bigint::~bigint()
{ mpz_clear(&I); }

/**
** inline member functions
**/

int bigint::bit(unsigned int i) const
{ 
  if (i >= bit_length()) 
    return 0;
  else
    return ((I._mp_d[i/BITS_PER_MP_LIMB] >> (i % BITS_PER_MP_LIMB)) & 1);
}

int bigint::length() const
{ 
  int i = (int)I._mp_size;
  if (i < 0) i = -i;
  return i;
} 

int bigint::bit_length() const
{
  int i = this->length();
  if (i)
  {
    int cnt;
    count_leading_zeros (cnt, I._mp_d[i - 1]);
    i = i * BITS_PER_MP_LIMB - cnt;
  }
  return i;
} 

int bigint::sign() const
{
  if (I._mp_size < 0)
    return -1;
  else
    return (I._mp_size != 0); 
} 

bool bigint::is_positive() const
{ return (I._mp_size > 0); } 

bool bigint::is_negative() const
{ return (I._mp_size < 0); }

bool bigint::is_even() const
{ return (I._mp_size == 0 || (!(I._mp_d[0] & 1))); }

bool bigint::is_odd() const
{ return (I._mp_size && (I._mp_d[0] & 1)); }

bool bigint::is_zero() const
{ return (I._mp_size == 0); }

bool bigint::is_gt_zero() const
{ return (I._mp_size > 0); }

bool bigint::is_ge_zero() const
{ return (I._mp_size >= 0); }

bool bigint::is_lt_zero() const
{ return (I._mp_size < 0); }

bool bigint::is_le_zero() const
{ return (I._mp_size <= 0); }

bool bigint::is_one() const
{ return ((I._mp_size == 1) && (I._mp_d[0] == 1)); }

bool bigint::intify(int & i) const
{ 
  if (bit_length() > (BITS_PER_INT-1))
    return 1;
  i = (int) mpz_get_si(&I);
    return 0;
}

bool bigint::longify(long & i) const
{ 
  if (bit_length() > (BITS_PER_LONGINT-1))
    return 1;
  i = mpz_get_si(&I);
    return 0;
}

int bigint::abs_compare(const bigint & a) const
{
  mp_size_t this_size = ABS(I._mp_size);
  mp_size_t a_size = ABS(a.I._mp_size);
  if (this_size > a_size) return 1;
  if (this_size < a_size) return -1;
  if (this_size == 0) return 0;
  mp_srcptr tp = I._mp_d, ap = a.I._mp_d;
  return __mpn_cmp (tp, ap, this_size);
}

int bigint::compare(const bigint & a) const
{
  int cmp = mpz_cmp(&I, &a.I);
  if (cmp < 0) return -1;
  return (cmp > 0);
}

unsigned long bigint::most_significant_digit() const
{ 
  unsigned long msd;
  long l = I._mp_size;
  if (l == 0) return 0;
  l = (l < 0) ? -l : l;
  msd = I._mp_d[l - 1];
  return msd;
}

unsigned long bigint::least_significant_digit() const
{
  unsigned long lsd;
  if (I._mp_size == 0) return 0;
  lsd = I._mp_d[0];
  return lsd;
}

const double bigint::radix()
{ return ldexp(1.0, BITS_PER_MP_LIMB); }

const int bigint::bits_per_digit()
{ return (BITS_PER_MP_LIMB); }

void bigint::absolute_value()
{ mpz_abs(&I, &I); }

void bigint::negate()
{ mpz_neg(&I, &I); }

void bigint::assign_zero()
{  mpz_set_ui(&I, 0); }

void bigint::assign_one()
{ mpz_set_ui(&I, 1); }

void bigint::assign(int i)
{ mpz_set_si(&I, i); }

void bigint::assign(long i)
{ mpz_set_si(&I, i); }

void bigint::assign(unsigned long ui)
{ mpz_set_ui(&I, ui); }

void bigint::assign(const bigint & a) 
{ mpz_set(&I, &a.I); }

void bigint::multiply_by_2() 
{ mpz_mul_2exp(&I, &I, 1); }

void bigint::divide_by_2()
{ mpz_tdiv_q_2exp(&I, &I, 1); }

/**
** Type checking
**/

bool is_char(const bigint & a)
{ return a.bit_length() <= (BITS_PER_CHAR - 1); }

bool is_uchar(const bigint & a)
{ return a.bit_length() <= (BITS_PER_CHAR); }

bool is_short(const bigint & a)
{ return a.bit_length() <= (BITS_PER_SHORTINT - 1); }

bool is_ushort(const bigint & a)
{ return a.bit_length() <= (BITS_PER_SHORTINT); }

bool is_int(const bigint & a)
{ return a.bit_length() <= (BITS_PER_INT - 1); }

bool is_uint(const bigint & a)
{ return a.bit_length() <= (BITS_PER_INT); }

bool is_long(const bigint & a)
{ return a.bit_length() <= (BITS_PER_LONGINT - 1); }

bool is_ulong(const bigint & a)
{ return a.bit_length() <= (BITS_PER_LONGINT); }

/**
** assignments
**/


int bigint::operator = (int i)
{ mpz_set_si(&I, i); return i; }

long bigint::operator = (long l)
{ mpz_set_si(&I, l); return l; }

unsigned long bigint::operator = (unsigned long ul)
{ mpz_set_ui(&I, ul); return ul; }

double bigint::operator = (double d)
{
  MP_INT tmp;
  double mantisse;
  int exponent;
  unsigned long lmantisse;
  int sign = 0, count = 0;
 
  mpz_clear(&I);
 
  if (d < 0.0)
  {
   sign = 1;
   d = -d;
  }
 
  if (d >=0 && d < 0.5)
  {
    mpz_init(&I);
  }
  else
  {
    mpz_init(&I);
    mpz_init(&tmp);
    mantisse = frexp(d, &exponent);
    while (mantisse != 0.0)
    {
      mpz_mul_2exp(&I, &I, BITS_PER_MP_LIMB);
      mantisse = ldexp(mantisse, BITS_PER_MP_LIMB);
      lmantisse = (unsigned long) mantisse;
      mantisse -= lmantisse;
      mpz_set_ui(&tmp, lmantisse);
      mpz_ior(&I, &I, &tmp);
      count++;
    }
    exponent -= count * BITS_PER_MP_LIMB;
    if (exponent >= 0)
        mpz_mul_2exp(&I, &I, exponent);
    else
        mpz_tdiv_q_2exp(&I, &I, -exponent);
    if (sign)
      mpz_neg(&I, &I);
    mpz_clear(&tmp);
  }
  return d;
} 

bigint & bigint::operator = (const bigint & a)
{ mpz_set(&I, &a.I); return *this; }

/**
** comparisons
**/

bool operator == (const bigint & a, const bigint & b)
{ return mpz_cmp(&a.I, &b.I) == 0;}
 
bool operator != (const bigint & a, const bigint & b)
{ return mpz_cmp(&a.I, &b.I) != 0;}
 
bool operator > (const bigint & a, const bigint & b)
{ return mpz_cmp(&a.I, &b.I) > 0;}
 
bool operator >= (const bigint & a, const bigint & b)
{ return mpz_cmp(&a.I, &b.I) >= 0;}
 
bool operator < (const bigint & a, const bigint & b)
{ return mpz_cmp(&a.I, &b.I) < 0;}
 
bool operator <= (const bigint & a, const bigint & b)
{ return mpz_cmp(&a.I, &b.I) <= 0;}

/**
** operator overloading
**/

bigint operator - (const bigint & a)
{ bigint c; mpz_neg(&c.I, &a.I); return c; }

bigint operator + (const bigint & a, const bigint & b)
{ bigint c; mpz_add(&c.I, &a.I, &b.I); return c; }

bigint operator - (const bigint & a, const bigint & b)
{ bigint c; mpz_sub(&c.I, &a.I, &b.I); return c; }

bigint operator * (const bigint & a, const bigint & b)
{ bigint c; mpz_mul(&c.I, &a.I, &b.I); return c; }

bigint operator / (const bigint & a, const bigint & b)
{ bigint c; mpz_tdiv_q(&c.I, &a.I, &b.I); return c; }

bigint operator % (const bigint & a, const bigint & b)
{ 
  bigint c;
  mpz_mod(&c.I, &a.I, &b.I); // returns non-negative rest c

  if (a.I._mp_size < 0 && c.I._mp_size != 0)
   {
     // a is negative and rest != 0 -> create negative rest
     if (b.I._mp_size < 0)
        mpz_add(&c.I, &c.I, &b.I);
     else
        mpz_sub(&c.I, &c.I, &b.I);
   }
  return c;
}

bigint operator << (const bigint & a, long ui)
{ 
  bigint c;
  if (ui < 0) 
    lidia_error_handler("bigint", "operator<<::index is negative.");
  mpz_mul_2exp(&c.I, &a.I, ui);
  return c; 
}

bigint operator >> (const bigint & a, long ui)
{ 
  bigint c; 
  if (ui < 0) 
    lidia_error_handler("bigint", "operator>>::index is negative.");
  mpz_tdiv_q_2exp(&c.I, &a.I, ui); 
  return c; 
}

bigint operator & (const bigint & a, const bigint & b)
{ bigint c; mpz_and(&c.I, &a.I, &b.I); return c; }

bigint operator | (const bigint & a, const bigint & b)
{ bigint c; mpz_ior(&c.I, &a.I, &b.I); return c; }

bigint operator ^ (const bigint & a, const bigint & b)
{ bigint c = (a & ~b) | (~a & b); return c; }

bigint & bigint::operator += (const bigint & a)
{ mpz_add(&I, &I, &a.I); return *this; }

bigint & bigint::operator -= (const bigint & a)
{ mpz_sub(&I, &I, &a.I); return *this; }

bigint & bigint::operator *= (const bigint & a)
{ mpz_mul(&I, &I, &a.I); return *this; }

bigint & bigint::operator /= (const bigint & a)
{ mpz_tdiv_q(&I, &I, &a.I); return *this; }


bigint & bigint::operator %= (const bigint & a)
{
  // can be optimized by using static tmp
  //
  // Problem: see first MM-entry in Last Changes, mpz_mod

  if (I._mp_size < 0) // rest must be negative
   {
     if (&a == this)  // a will be overwritten
      {
	MP_INT tmp;   // set tmp = |a|
	if (a.I._mp_size < 0) // a is negative
	  {
	    mpz_init(&tmp);
	    mpz_neg(&tmp,&a.I);
	  }
	else
	    mpz_init_set(&tmp,&a.I);

        mpz_mod(&I, &I, &a.I); // compute non-negative rest

	if (I._mp_size != 0) // rest is not equal to zero
	 {
	   mpz_sub(&I, &I, &tmp); // create negative rest
  	   mpz_clear(&tmp);
	 }
      }
     else // a won't be overwritten
      {
        mpz_mod(&I, &I, &a.I); // compute non-negative rest

        if (I._mp_size != 0) // rest is not equal to zero
         {
	   if (a.I._mp_size < 0)  // create negative rest
              mpz_add(&I, &I, &a.I);
	   else
	      mpz_sub(&I, &I, &a.I);
         }
      }
   }
  else
     mpz_mod(&I, &I, &a.I);
  return *this;
}

bigint & bigint::operator <<= (long ui)
{ 
  if (ui < 0) 
    lidia_error_handler("bigint", "operator<<=::index is negative.");
  mpz_mul_2exp(&I, &I, ui); 
  return *this; 
}

bigint & bigint::operator >>= (long ui)
{ 
  if (ui < 0) 
    lidia_error_handler("bigint", "operator>>=::index is negative.");
  mpz_tdiv_q_2exp(&I, &I, ui);
  return *this;
}

bigint & bigint::operator &= (const bigint & a)
{ mpz_and(&I, &I, &a.I); return *this; }

bigint & bigint::operator |= (const bigint & a)
{ mpz_ior(&I, &I, &a.I); return *this; }

bigint & bigint::operator ^= (const bigint & a)
{ *this = (~*this & a) | (*this & ~a); return *this; }

bigint & bigint::operator++ ()
{ mpz_add_ui(&I, &I, 1); return *this; }

bigint & bigint::operator-- ()
{ mpz_sub_ui(&I, &I, 1); return *this; }

bigint  bigint::operator++ (int)
{ bigint a = *this; mpz_add_ui(&I, &I, 1); return a; }

bigint  bigint::operator-- (int)
{ bigint a =*this; mpz_sub_ui(&I, &I, 1); return a; }

int bigint::operator ! () const
{ return (I._mp_size == 0); }

bigint bigint::operator ~ () const
{ bigint c; mpz_com(&c.I, &I); return c; }

/**
** Procedural versions
**/

void negate(bigint & a, const bigint & b)
{ mpz_neg(&a.I, &b.I); }

void add(bigint & c, const bigint & a, const bigint & b)
{ mpz_add(&c.I, &a.I, &b.I); }

void subtract(bigint & c, const bigint & a, const bigint & b)
{ mpz_sub(&c.I, &a.I, &b.I); }

void multiply(bigint & c, const bigint & a, const bigint & b)
{ mpz_mul(&c.I, &a.I, &b.I); }

void divide(bigint & c, const bigint & a, const bigint & b)
{ mpz_tdiv_q(&c.I, &a.I, &b.I); }

void remainder(bigint & c, const bigint & a, const bigint & b)
{ 
  // can be optimized by using static tmp
  //
  // Problem: see first MM-entry in Last Changes, mpz_mod

  if (a.I._mp_size < 0) // rest must be negative
   {
     if (&b == &c)  // b will be overwritten
      {
	MP_INT tmp;   // set tmp = |b|
	if (b.I._mp_size < 0) // b is negative
	  {
	    mpz_init(&tmp);
	    mpz_neg(&tmp,&b.I);
	  }
	else
	    mpz_init_set(&tmp,&b.I);

        mpz_mod(&c.I, &a.I, &b.I); // compute non-negative rest

	if (c.I._mp_size != 0) // rest is not equal to zero
	 {
	   mpz_sub(&c.I, &c.I, &tmp); // create negative rest
  	   mpz_clear(&tmp);
	 }
      }
     else // b won't be overwritten
      {
        mpz_mod(&c.I, &a.I, &b.I); // compute non-negative rest

        if (c.I._mp_size != 0) // rest is not equal to zero
         {
	   if (b.I._mp_size < 0)  // create negative rest
              mpz_add(&c.I, &c.I, &b.I);
	   else
	      mpz_sub(&c.I, &c.I, &b.I);
         }
      }
   }
  else
     mpz_mod(&c.I, &a.I, &b.I);
}


void div_rem(bigint & q, bigint & r, const bigint & a, const bigint & b)
{ mpz_tdiv_qr(&q.I, &r.I, &a.I, &b.I); }

void invert(bigint & a, const bigint & b)
{
  if ((a.I._mp_size == 1 || a.I._mp_size == -1) && a.I._mp_d[0] == 1)
    mpz_set(&a.I, &b.I);
  else
    lidia_error_handler("bigint", "invert::inverting of a non-unit.");
}

void shift_left(bigint & c, const bigint & a, long ui)
{ 
  if (ui < 0) 
    lidia_error_handler("bigint", "shift_left()::index is negative.");
  mpz_mul_2exp(&c.I, &a.I, ui);
}

void shift_right(bigint & c, const bigint & a, long ui)
{ 
  if (ui < 0) 
    lidia_error_handler("bigint", "shift_right()::index is negative.");
  mpz_tdiv_q_2exp(&c.I, &a.I, ui);
}

void power(bigint & c, const bigint & a, const bigint & b)
{
  bigint exponent, multiplier;
  if (a.I._mp_d[0]  ==  1 &&
      (a.I._mp_size ==  1 ||
       a.I._mp_size == -1 )) // a = +-1 -> a^(|b|)
    {
      if (b.is_odd())
	c = a;
      else
	c.assign_one();
    }
  else if (b.is_negative()) // b < 0, a != +-1 -> 0
    c.assign_zero();
  else if (b.is_zero()) // b == 0 -> 1
    c.assign_one();
  else
  {
   exponent.assign(b);
   multiplier.assign(a);
   c.assign_one();
   while (exponent.is_gt_zero())
   {
    if (!exponent.is_even())
      multiply(c, c, multiplier);
    square(multiplier, multiplier);
    exponent.divide_by_2();
   }
  }
}

void power(bigint & c, const bigint & a, long i)
{
  if (a.I._mp_d[0]  ==  1 &&
      (a.I._mp_size ==  1 ||
       a.I._mp_size == -1 )) // a = +-1 -> a^(|i|)
    {
      if (i&1)
	c = a;
      else
	c.assign_one();
    }
  else if (i < 0)  // i < 0, a != +-1 -> 0
    c.assign_zero();
  else if (i == 0) // i == 0 -> 1
    c.assign_one();
  else 
    mpz_pow_ui(&c.I, &a.I, i); 
} 

void and(bigint & c, const bigint & a, const bigint & b)
{ mpz_and(&c.I, &a.I, &b.I); }

void or(bigint & c, const bigint & a, const bigint & b)
{ mpz_ior(&c.I, &a.I, &b.I); }

void xor(bigint & c, const bigint & a, const bigint & b)
{ c = (~a & b) | (~b & a); }

void not(bigint & b, const bigint & a)
{ mpz_com(&b.I, &a.I); }

void inc(bigint & c)
{ mpz_add_ui(&c.I, &c.I, 1); }

void dec(bigint & c)
{ mpz_sub_ui(&c.I, &c.I, 1); }

void add(bigint & c, const bigint & a, long i)
{
  if (i >= 0)
    mpz_add_ui(&c.I, &a.I, i);
  else
    mpz_sub_ui(&c.I, &a.I, -i);
}

void subtract(bigint & c, const bigint & a, long i)
{
  if (i >= 0)
    mpz_sub_ui(&c.I, &a.I, i);
  else
    mpz_add_ui(&c.I, &a.I, -i);
}

void multiply(bigint & c, const bigint & a, long i)
{
  if (i >= 0)
    mpz_mul_ui(&c.I, &a.I, i);
  else
  {
    mpz_mul_ui(&c.I, &a.I, -i);
    mpz_neg(&c.I, &c.I);
  }
}

void divide(bigint & c, const bigint & a, long i)
{ // optimize this by making tmp static
  MP_INT tmp;
  mpz_init_set_si(&tmp, i);
  mpz_tdiv_q(&c.I, &a.I, &tmp);
  mpz_clear(&tmp);
}
  

void remainder(long &r, const bigint & a, long i)
{ // optimize this by making tmp1, tmp2 static
  MP_INT tmp1, tmp2;
  mpz_init_set_si(&tmp1, i);
  mpz_init(&tmp2);
  mpz_mod(&tmp2, &a.I, &tmp1); // compute non-negative rest r
  r = mpz_get_si(&tmp2);
  mpz_clear(&tmp2);
  mpz_clear(&tmp1);

  if (r != 0 && a.I._mp_size < 0) // r must be negative
   {
     if (i < 0) 
      r += i;
     else
      r -= i;
   }
}

long remainder(const bigint & a, long i)
{ // optimize this by making tmp1, tmp2 static
  long r; 
  MP_INT tmp1, tmp2;
  mpz_init_set_si(&tmp1, i);
  mpz_init(&tmp2);
  mpz_mod(&tmp2, &a.I, &tmp1); // compute non-negative rest r
  r = mpz_get_si(&tmp2);
  mpz_clear(&tmp2);
  mpz_clear(&tmp1);

  if (r != 0 && a.I._mp_size < 0) // r must be negative
   {
     if (i < 0) 
      r += i;
     else
      r -= i;
   }

  return r;
}

void div_rem(bigint & q, long &r, const bigint & a, long i)
{ // optimize this by making tmp1, tmp2 static
  MP_INT tmp1, tmp2;
  mpz_init_set_si(&tmp1, i);
  mpz_init(&tmp2);
  mpz_tdiv_qr(&q.I, &tmp2, &a.I, &tmp1);
  r = mpz_get_si(&tmp2);
  mpz_clear(&tmp2);
  mpz_clear(&tmp1);
}

/**
** gcd's
**/

bigint gcd(const bigint & a, const bigint & b)
{ bigint c; mpz_gcd(&c.I, &a.I, &b.I); return c; }

bigint bgcd(const bigint & a, const bigint & b)
{ bigint c; mpz_gcd(&c.I, &a.I, &b.I); return c; }

bigint dgcd(const bigint & a, const bigint & b)
{ bigint c; mpz_gcd(&c.I, &a.I, &b.I); return c; }

bigint xgcd(bigint & u, bigint & v, const bigint & a, const bigint & b)
{ bigint c; mpz_gcdext(&c.I, &u.I, &v.I, &a.I, &b.I); return c; }

bigint xgcd_left(bigint & u, const bigint & a, const bigint & b)
{ bigint c; mpz_gcdext(&c.I, &u.I, 0, &a.I, &b.I); return c;}

bigint xgcd_right(bigint & v, const bigint & a, const bigint & b)
{ bigint c; mpz_gcdext(&c.I, &v.I, 0, &b.I, &a.I); return c; }

/**
** functions
**/

bigint abs(const bigint & a)
{ bigint c(a); c.I._mp_size = ABS(c.I._mp_size); return c; }

void seed(const bigint & a)
{ bigint c; mpz_random(&c.I, 1); }

bigint randomize(const bigint & a)
{ 
  bigint c; 

  if (a.is_zero())
     lidia_error_handler("bigint", "Bound must not be equal to zero.");
  else
   {
     mpz_random(&c.I, mpz_size(&a.I)); 
     if (c.is_lt_zero())
      {
        if (c < a)
	    remainder(c,c,a);
      }
     else if (c > a)
	remainder(c,c,a);
   }

  return c; 
}

#ifdef USE_OLD_DBL

double dbl(const bigint & a)
{
  char *s;
  int t = (int) mpz_sizeinbase(&a.I, 10) + 2;
  s = new char [t];
  mpz_get_str(s, 10, &a.I);
  double d;
  d = atof(s);
  delete[] s;
  return d;
}

xdouble xdbl(const bigint & a)
{
  char *s;
  int t = (int) mpz_sizeinbase(&a.I, 10) + 2;
  s = new char [t];
  mpz_get_str(s, 10, &a.I);
  xdouble d;
  d = string_to_xdouble(s);
  delete[] s;
  return d;
}

#else

double dbl(const bigint & a)
{
  long l = a.I._mp_size;
  l=((l<0)?-l:l);
  double d = 0.0;
  if (l)
  {
    int i = 1;
    d = a.I._mp_d[0]; 
    double base = bigint::radix();
    double power = base;
    while (i < l)
    {
     d += a.I._mp_d[i] * power;
     power *= base;
     i++;
    }
    if (a.I._mp_size < 0)
      d = -d;
  }
  return d;
}

xdouble xdbl(const bigint & a)
{
  long l = a.I._mp_size;
  l=((l<0)?-l:l);
  xdouble d = 0.0;
  if (l)
  {
    int i = 1;
    d = (double )a.I._mp_d[0]; 
    xdouble base = bigint::radix();
    xdouble power = base;
    while (i < l)
    {
     d += a.I._mp_d[i] * power;
     power *= base;
     i++;
    }
    if (a.I._mp_size < 0)
      d = -d;
  }
  return d;
}

#endif



void sqrt(bigint & a, const bigint & b)
{ mpz_sqrt(&a.I, &b.I); }
      
void square(bigint & a, const bigint & b)
{ mpz_mul(&a.I, &b.I, &b.I); }

void swap(bigint & a, bigint & b)
{
  MP_INT tmp;
  mpz_init_set(&tmp, &a.I);
  mpz_set(&a.I, &b.I);
  mpz_set(&b.I, &tmp);
  mpz_clear(&tmp); 
}

/**
** input / output
**/

istream & operator >> (istream & in, bigint & a)
{     
#ifdef __GNUG__
                if (!in.ipfx0())
#else
                if (!in.ipfx(0))
#endif
                  return in;

                char s[10000];
                int ndigits = 0;

                char* p = s;
                int c;
                *p = '\0';
                do {
                  c = in.get();
                  if (c == EOF)
                  {
                    in.setf(ios::failbit|ios::eofbit); 
                    return in;
                  }
                  if ((c == '+') || (c == '-')) { *p++ = c; }
                } while (isspace(c) || (c == '+') || (c == '-'));

                if (!isdigit(c)) { cerr << "digit expected" << flush; }
                while (isdigit(c)) {
                  ndigits++;
                  *p++ = c;
                  c = in.get();
                  if (c == EOF)
                  {
                    *p = '\0';
                    string_to_bigint(s, a);
                    return in;
                  }

                }
                if (ndigits == 0)
                  {
                    in.setf(ios::failbit);
                    return in;
                  }

                *p = '\0';
                string_to_bigint(s, a);
                // if (c != '\n' && c != '\r')
                  in.putback(c);
                return in;
}

ostream & operator << (ostream & out, const bigint & a)
{
  int t = (int) mpz_sizeinbase(&a.I, 10) + 10;
  char *s = new char [t];
  mpz_get_str(s, 10, &a.I);
  out << s;
  delete[] s;
  return out;
}

int string_to_bigint(const char *s, bigint & a)
{ 
  if (!mpz_set_str(&a.I,(char*)s, 10))
    return (strlen(s));
  else
    return 0;
}

int bigint_to_string(const bigint & a, char *s)
{ mpz_get_str(s, 10, &a.I); return strlen(s); }

/**
** using fread/fwrite
**/

void bigint::read_from_file(FILE * fp)
{ mpz_inp_raw(&I, fp); }

void bigint::write_to_file(FILE * fp)
{ mpz_out_raw(fp, &I); }

/**
** using fscanf/fprintf
**/

void bigint::scan_from_file(FILE * fp)
{ mpz_inp_str(&I, fp, 10); }

void bigint::print_to_file(FILE * fp)
{ mpz_out_str(fp, 10, &I); }
