//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigint.c (implementation of the lip interface) 
// Author      : Thomas Papanikolaou (TP)
// Last change : TP, Feb 7 1995, initial version
//               AH, Sep 25 1996,
//                   Changed the return-value type of all 
//                   yes/no-functions to bool. 
//               MM, Sep 25 1996
//                   Changed the return-value type of the
//                   postfix operators from bigint & to bigint
//                   because the return-value must be a local variable.
//		 MM, Sep 25 1996
//		     Modified power-functions to handle
//		     negative exponents.
//               MM, Oct 21 1996
//                   Added test for 0 in randomize.
//

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

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

bigint::bigint()
{ I = 0; } 

bigint::bigint(int i)
{ I = 0; zintoz(i, &I); } 

bigint::bigint(long l)
{ I = 0; zintoz(l, &I); }

bigint::bigint(unsigned long ul)
{
  register long i = 0;
  I = 0;
  zsetlength(&I, (long)SIZE, "in bigint(ul), 2nd arg");
  I[1] = 0;
  while (ul > 0)
  {
    I[++i] = ul & RADIXM;
    ul >>= NBITS;
  }
  if (i > 0)
    *I = i;
  else
    *I = 1;
}

void bigint::assign(double d)

{
  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)
  {
    I = 0;
  }
  else
  {
    zzero(&I);
    mantisse = frexp(d, &exponent);
    while (mantisse != 0.0)
    {
      zlshift(I, NBITS, &I);
      mantisse = ldexp(mantisse, NBITS);
      lmantisse = (unsigned long) mantisse;
      mantisse -= lmantisse;
      count++;
      zsadd(I, lmantisse, &I);
    }
    exponent -= (int)I[0] * NBITS;
    if (exponent >= 0)
        zlshift(I, exponent, &I);
    else
        zrshift(I, -exponent, &I);
  }
  if (sign)
    znegate(&I);
}

bigint::bigint(const bigint & a)
{ I = 0; zcopy(a.I, &I); }

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


/**
** inline member functions
**/

int bigint::bit(unsigned int i) const
{ return zbit(I, (long)i); }

lidia_size_t bigint::length() const
{
  long l = 0;
  if (I != 0)
  {
    l = I[0];
    if (l < 0)
      l = -l;
  }
  return (int)l;
}

lidia_size_t bigint::bit_length() const
{ return z2log(I); } 

int bigint::sign() const
{ return zsign(I); } 

bool bigint::is_positive() const
{ if (zsign(I) > 0) return true;
  return false; } 

bool bigint::is_negative() const
{ if (zsign(I) < 0) return true;
  return false; }

bool bigint::is_even() const
{ if (!zodd(I)) return true;
  return false; }

bool bigint::is_odd() const
{ if (zodd(I)) return true;
  return false; }

bool bigint::is_zero() const
{ if (zsign(I) == 0) return true;
  return false; }

bool bigint::is_gt_zero() const
{ if (zsign(I) > 0) return true;
  return false; }

bool bigint::is_ge_zero() const
{ if (zsign(I) >= 0) return true;
  return false; }

bool bigint::is_lt_zero() const
{ if (zsign(I) < 0) return true;
  return false; }

bool bigint::is_le_zero() const
{ if (zsign(I) <= 0) return true;
  return false; }

bool bigint::is_one() const
{ if ((I != 0) && (*I == 1) && (I[1] == 1)) return true;
  return false; } 

bool bigint::intify(int & i) const
{
  if (z2log(I) > (CHARL * sizeof(int)-1))
    return true;
  i = (int) ztoint(I);
    return false;
}

bool bigint::longify(long & i) const
{
  if (z2log(I) > (CHARL * SIZEOFLONG-1))
    return true;
  i = ztoint(I);
    return false;
}

int bigint::abs_compare(const bigint & a) const
{
  int result;
  verylong tI = 0, ta = 0;
  zcopy(I, &tI);
  zabs(&tI);
  zcopy(a.I, &ta);
  zabs(&ta);
  result = (int)zcompare(tI, ta);
  zfree(&tI);
  zfree(&ta);
  return result;
}

int bigint::compare(const bigint & a) const
{ return zcompare(I, a.I); }

unsigned long bigint::most_significant_digit() const
{
  unsigned long msd;
  long l;

  if (!I)
    return 0;

  l = I[0];
  l = (l < 0) ? -l : l;

  msd = I[l];

  return msd;
}

unsigned long bigint::least_significant_digit() const
{
  unsigned long lsd;

  if (!I)
    return 0;

  lsd = I[1];

  return lsd;
}

const double bigint::radix()
{ return (1.0 * RADIX); }

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

void bigint::absolute_value()
{ zabs(&I); }

void bigint::abs()
{ zabs(&I); }

void bigint::negate()
{ znegate(&I); }

void bigint::assign_zero()
{ zzero(&I); }

void bigint::assign_one()
{ zone(&I); }

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

void bigint::assign(long l)
{ zintoz(l, &I); }

void bigint::assign(unsigned long ui)
{
  register long i = 0;
  I[1] = 0;
  while (ui > 0)
  {
    I[++i] = ui & RADIXM;
    ui >>= NBITS;
  }
  if (i > 0)
    *I = i;
  else
    *I = 1;
}

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

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

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

/**
** Type checking
**/

bool is_char(const bigint & a)
{ if (z2log(a.I) <= (CHARL-1)) return true;
  return false; }

bool is_uchar(const bigint & a)
{ if (z2log(a.I) <= CHARL) return true;
  return false; }

bool is_short(const bigint & a)
{ if (z2log(a.I) <= (CHARL * sizeof(short)-1)) return true;
  return false; }

bool is_ushort(const bigint & a)
{ if (z2log(a.I) <= (CHARL * sizeof(unsigned short))) return true;
  return false; }
 
bool is_int(const bigint & a)
{ if (z2log(a.I) <= (CHARL * sizeof(int)-1)) return true;
  return false; }

bool is_uint(const bigint & a)
{ if (z2log(a.I) <= CHARL * sizeof(unsigned int)) return true;
  return false; }

bool is_long(const bigint & a)
{ if (z2log(a.I) <= (CHARL * sizeof(long)-1)) return true;
  return false; }

bool is_ulong(const bigint & a)
{ if (z2log(a.I) <= CHARL * sizeof(unsigned long)) return true;
  return false; }

/**
** assignments
**/


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

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

unsigned long bigint::operator = (unsigned long ul)
{
  register long i = 0;
  I[1] = 0;
  while (ul > 0)
  {
    I[++i] = ul & RADIXM;
    ul >>= NBITS;
  }
  if (i > 0)
    *I = i;
  else
    *I = 1;
  return ul;
}

double bigint::operator = (double d)
{
  double mantisse;
  int exponent;
  unsigned long lmantisse;
  int sign = 0, count = 0;

  zfree(&I);

  if (d < 0.0)
  {
   sign = 1;
   d = -d;
  }

  if (d >=0 && d < 0.5)
  {
    I = 0;
  }
  else
  {
    zzero(&I);
    mantisse = frexp(d, &exponent);
    while (mantisse != 0.0)
    {
      zlshift(I, NBITS, &I);
      mantisse = ldexp(mantisse, NBITS);
      lmantisse = (unsigned long) mantisse;
      mantisse -= lmantisse;
      count++;
      zsadd(I, lmantisse, &I);
    }
    exponent -= (int)I[0] * NBITS;
    if (exponent >= 0)
        zlshift(I, exponent, &I);
    else
        zrshift(I, -exponent, &I);
    if (sign)
      znegate(&I);
  }
  return d;
}

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

/**
** comparisons
**/

bool operator == (const bigint & a, const bigint & b)
{ if (zcompare(a.I, b.I) == 0) return true; else return false; }

bool operator != (const bigint & a, const bigint & b)
{ if (zcompare(a.I, b.I) != 0) return true; else return false; }

bool operator > (const bigint & a, const bigint & b)
{ if (zcompare(a.I, b.I) > 0) return true; else return false; }

bool operator >= (const bigint & a, const bigint & b)
{ if (zcompare(a.I, b.I) >= 0) return true; else return false; }

bool operator < (const bigint & a, const bigint & b)
{ if (zcompare(a.I, b.I) < 0) return true; else return false; }

bool operator <= (const bigint & a, const bigint & b)
{ if (zcompare(a.I, b.I) <= 0) return true; else return false; }

/**
** operator overloading
**/

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

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

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

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

bigint operator / (const bigint & a, const bigint & b)
{
  bigint q, r; 
  zdiv(a.I, b.I, &q.I, &r.I);
  if ((zsign(a.I) != zsign(b.I)) && zsign(r.I))
    zsadd(q.I, 1, &q.I);
  return q;
}

bigint operator % (const bigint & a, const bigint & b)
{ 
  bigint q, r;
  zdiv(a.I, b.I, &q.I, &r.I);
  if (zsign(a.I) != zsign(b.I) && zsign(r.I))
    zsub(r.I, b.I, &r.I);
  return r;
}

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

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

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

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

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

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

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

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

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

bigint & bigint::operator %= (const bigint & a)
{ *this = *this % a; return *this; }

bigint & bigint::operator <<= (long ui)
{ *this = *this << ui; return *this; }

bigint & bigint::operator >>= (long ui)
{ *this = *this >> ui; return *this; }

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

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

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

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

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

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

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

int bigint::operator ! () const
{ return ziszero(I); }

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

/**
** Procedural versions
**/

void negate(bigint & a, const bigint & b)
{ if (&a != &b) zcopy(b.I, &a.I); znegate(&a.I); }

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

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

void multiply(bigint & c, const bigint & a, const bigint & b)
{
  verylong aa, bb;
  static verylong tmp1 = 0, tmp2 = 0;
  if (&c == &a) {
      zcopy(a.I, &tmp1);
      aa = tmp1;
   }
   else
      aa = a.I;

   if (&c == &b) {
      zcopy(b.I, &tmp2);
      bb = tmp2;
   }
   else
      bb = b.I;

   zmul(aa, bb, &c.I);
}

void divide(bigint & c, const bigint & a, const bigint & b)
{ 
  bigint r;
  zdiv(a.I, b.I, &c.I, &r.I);
  if (zsign(a.I) != zsign(b.I) && zsign(r.I))
    zsadd(c.I, 1, &c.I);
}

void remainder(bigint & c, const bigint & a, const bigint & b)
{ 
  bigint q; 
  zdiv(a.I, b.I, &q.I, &c.I);
  if (zsign(a.I) != zsign(b.I) && zsign(c.I))
    zsub(c.I, b.I, &c.I);
}

void div_rem(bigint & q, bigint & r, const bigint & a, const bigint & b)
{ 
  zdiv(a.I, b.I, &q.I, &r.I);
  if (zsign(a.I) != zsign(b.I) && zsign(r.I))
  {
    zsadd(q.I, 1, &q.I);
    zsub(r.I, b.I, &r.I);
  }
} 

void invert(bigint & a, const bigint & b)
{ 
  if ((b.I != 0) &&
       ((b.I)[0] == 1 || (b.I)[0] == -1) &&
       ((b.I)[1] == 1) )
     zcopy(b.I, &(a.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.");
  zlshift(a.I, ui, &c.I);
}

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

void power(bigint & c, const bigint & a, const bigint & b)
{
  static verylong tmp1 = 0;

  if (zsign(b.I) == 0)   // b == 0 -> c = 1
     zone(&c.I);
  else if (zsign(b.I) < 0)
   {
     if ((a.I)[1] == 1)
      {
        if ((a.I)[0] == 1) // a == 1
      	   zone(&c.I);
        else if ((a.I)[0] == -1) // a == -1
 	 {
           zone(&c.I);
           int e = (int) zodd(b.I);
           if (e)  znegate(&c.I);
	 }
        else
           zzero(&c.I);
      }
     else
        zzero(&c.I);
   }
  else  if (zsign(a.I) < 0)
  {
    int e = (int) zodd(b.I);
    zcopy(a.I, &tmp1);
    zabs(&tmp1);
    zexp(tmp1, b.I, &c.I);
    if (e)
      znegate(&c.I);
  }
  else
    zexp(a.I, b.I, &c.I);
}

void power(bigint & c, const bigint & a, long i)
{
  static verylong tmp1 = 0;

  if (i == 0)	// i == 0 -> c = 1
     zone(&c.I);
  else if (i < 0) 
   {
     if ((a.I)[1] == 1)
      {
        if ((a.I)[0] == 1) // a == 1
           zone(&c.I);
        else if ((a.I)[0] == -1) // a == -1
         {
           zone(&c.I);
           if (i & 1) znegate(&c.I);
         }
        else
           zzero(&c.I);
      }
     else
        zzero(&c.I);
   }
  else if (zsign(a.I) < 0)
  {
    zcopy(a.I, &tmp1);
    zabs(&tmp1);
    zsexp(tmp1, i, &c.I);
    if (i & 1)
      znegate(&c.I);
  }
  else
    zsexp(a.I, i, &c.I);
}

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

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

void xor(bigint & c, const bigint & a, const bigint & b)
{ zxor(a.I, b.I, &c.I); }

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

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

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

void add(bigint & c, const bigint & a, long i)
{ zsadd(a.I, i, &c.I); }

void subtract(bigint & c, const bigint & a, long i)
{ zsadd(a.I, -i, &c.I); }

void multiply(bigint & c, const bigint & a, long i)
{ zsmul(a.I, i, &c.I); }

void divide(bigint & c, const bigint & a, long i)
{
  long si = (i < 0) ? -1 : (i > 0);
  long sc = zsign(c.I);
  long r = zsdiv(a.I, i, &c.I);
  if (sc != si && r != 0)
    zsadd(c.I, 1, &c.I);
}

void remainder(long &r, const bigint & a, long i)
{
  long si = (i < 0) ? -1 : (i > 0);
  long sa = zsign(a.I);
  long ii = i;
  r = zsmod(a.I, i);
  if (sa != si & r != 0)
    r -= ii;
}

long remainder(const bigint & a, long i)
{
  long si = (i < 0) ? -1 : (i > 0);
  long sa = zsign(a.I);
  long r = zsmod(a.I, i);
  if (sa != si && r != 0)
    r -= i;
  return r;
}

void div_rem(bigint & q, long &r, const bigint & a, long i)
{ 
  long si = (i < 0) ? -1 : (i > 0);
  long sa = zsign(a.I);
  long ii = i;
  r = zsdiv(a.I, i, &q.I);
  if (sa != si && r != 0)
  {
    zsadd(q.I, 1, &q.I);
    r -= ii;
  }
}

/**
** gcd's
**/

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

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

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

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

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

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

/**
** functions
**/

bigint abs(const bigint & a)
{ bigint c(a); zabs(&c.I); return c; }

void seed(const bigint & a)
{ zrstart(a.I); }

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

  if (a.is_zero())
     lidia_error_handler("bigint", "Bound must not be equal to zero.");
  else
   {
     if (a.is_gt_zero())
        zrandomb(a.I, &c.I);
     else
      {
        bigint d;
	negate(d,a);
	zrandomb(d.I,&c.I);
	c.negate();		
      }
   }

  return c;
 }

double dbl(const bigint & a)
{ return zdoub(a.I); }

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

void sqrt(bigint & a, const bigint & b)
{ zroot(b.I, 2, &a.I); }
      
void square(bigint & a, const bigint & b)
{ if (&a == &b) zsqin(&a.I); else zsq(b.I, &a.I); }

void swap(bigint & a, bigint & b)
{ zswap(&b.I, &a.I); }

/**
** 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 l = (a.length() * NBITS);
  char *s = new char[l/3 + 20];
  bigint_to_string(a, s);
  out << s;
  delete[] s;
  return out;
}

int string_to_bigint(const char *s, bigint & a)
{ return zstrtoz((char*)s, &a.I); }


int bigint_to_string(const bigint & n, char *s)
{	
        int count=0;
	bigint help;
	char *p, *q, c;
	long DECCONV=1, rem=0;
	long NCONV=0, i;

	while (DECCONV < ((((unsigned long)1)<<(NBITS))/5)) {
		DECCONV*=10;
		NCONV++;
	}

	if (n.is_zero()) {
		s[count]='0';
		count++;
		s[count]='\0';
		return count;
	}
	if (n.is_lt_zero()) {
		s[count]='-';
		count++;
	}
        help = n;
        help.absolute_value();
	while (!help.is_zero()) {
                div_rem(help, rem, help, DECCONV);
		if (help.is_zero()) {
			while ( rem ) {
				s[count]=(char)('0'+ rem % 10);
				count++;
				rem /= 10;
			}
		} else {		/* print leading 0 */
			for (i=0; i<NCONV; i++) {
				s[count]=(char)('0'+ rem % 10);
				count++; 
				rem /= 10; 
			}   
		}
	}
	if (n.is_lt_zero())
		p=&s[1];
	else
		p=&s[0];
	q=&s[count-1];
	while (p<q) {
		c=*p;
		*p++=*q;
		*q--=c;
	}
	s[count]='\0';
	return count;
}

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

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

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

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

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

void bigint::print_to_file(FILE * fp)
{ zfwrite(fp, I); }











