//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigint.c (implementation of the libI interface) 
// Author      : Thomas Papanikolaou (TP)
// Last change : TP, Feb 7 1995, initial version
//               MM, Sep 17 1996, 
//                   Modified the code of the power functions to
//                   handle the special cases (a = +-1) correctly.
//		 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 26 1996
//		     Check for zero in divide_by_2() to handle -1 correctly.
//		 MM, Oct 21 1996
//	    	     Added test for 0 in randomize.
//	         VM, Oct 31 1996
//		     Changed code in multiply and square for greater efficiency


#include <LiDIA/bigint.h>

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

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

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

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

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

void bigint::assign(double d)
{
  cImaxlength(&I, sizeof(double)/sizeof(DigitType));
  Iasdbl(&I, d); 
}

bigint::bigint(const bigint & a)
{ cIasI(&I, &a.I); }

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

/**
** inline member functions
**/

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


lidia_size_t bigint::length() const
{ return I.length; }

lidia_size_t bigint::bit_length() const
{ return Ilog(&I) + 1; }

int bigint::sign() const
{ return ((I.sign) ? -1 : (I.length != 0)); }

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

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

bool bigint::is_even() const
{ if ((I.length == 0) || (!(*(I.vec) & 1))) return true;
  return false; }

bool bigint::is_odd() const
{ if (I.length && (*(I.vec) & 1)) return true;
  return false; }

bool bigint::is_zero() const
{ if (!I.length) return true;
  return false; }

bool bigint::is_gt_zero() const
{ if ((I.sign == PLUS) && I.length) return true;
  return false; }

bool bigint::is_ge_zero() const
{ if (I.sign == PLUS) return true;
  return false; }

bool bigint::is_lt_zero() const
{ if (I.sign == MINUS) return true;
  return false; }

bool bigint::is_le_zero() const
{ if ((!I.length) || (I.sign == MINUS)) return true;
  return false; }

bool bigint::is_one() const
{ if ((I.sign == PLUS) && (I.length == 1) && (*(I.vec) == 1)) return true;
  return false; }

bool bigint::intify(int & i) const
{ 
  if (Iisint(&I)) 
  { 
    i = intasI(&I); 
    return false; 
  }
  else
    return true;
}

bool bigint::longify(long & i) const
{ 
  if (Iislong(&I)) 
  { 
    i = longasI(&I); 
    return false; 
  }
  else
    return true;
}

int bigint::abs_compare(const bigint & a) const
{
  int lg_I = I.length, lg_a = a.I.length;
 
  if ((lg_a == lg_I) && DigitVecEq(a.I.vec, I.vec, lg_I))
    return 0;
  if ((lg_a > lg_I) || ((lg_a == lg_I) && DigitVecGt(a.I.vec, I.vec, lg_I)))
    return -1;
  else
    return 1;
}

int bigint::compare(const bigint & a) const
{
  if (IgtI(&a.I, &I))
    return -1;
  else
    return !IeqI(&a.I, &I);
}

unsigned long bigint::most_significant_digit() const
{
  unsigned long msd;
  long l = I.length;

  if (!l)
    return 0;

  msd = I.vec[l - 1];

  return msd;
}

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

  if (!I.length)
    return 0;

  lsd = I.vec[0];

  return lsd;
}

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

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

void bigint::absolute_value()
{ I.sign = PLUS; }

void bigint::abs()
{ I.sign = PLUS; }

void bigint::negate()
{ if (I.length) I.sign ^= MINUS; }

void bigint::assign_zero()
{ I.sign = PLUS; I.length = 0; }

void bigint::assign_one()
{ I.sign = PLUS; I.length = 1; *(I.vec) = 1; }

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

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

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

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

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

void bigint::divide_by_2()
{ Isr1(&I); if (I.length == 0) I.sign=PLUS; }

/**
** Type checking
**/

bool is_char(const bigint & a)
{ if ((Ilog(&a.I) + 1) <= 7) return true;
  return false; }

bool is_uchar(const bigint & a)
{ if ((Ilog(&a.I) + 1) <= 8) return true;
  return false; }

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

bool is_ushort(const bigint & a)
{ if ((Ilog(&a.I) + 1) <= (8 * sizeof(unsigned short))) return true;
  return false; }

bool is_int(const bigint & a)
{ if ((Ilog(&a.I) + 1) <= (8 * sizeof(int)-1)) return true;
  return false; }

bool is_uint(const bigint & a)
{ if ((Ilog(&a.I) + 1) <= (8 * sizeof(unsigned int))) return true;
  return false; }

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

bool is_ulong(const bigint & a)
{ if ((Ilog(&a.I) + 1) <= (8 * sizeof(unsigned long))) return true;
  return false; }

/**
** assignments
**/


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

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

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

double bigint::operator = (double d)
{ Iasdbl(&I, d); return d; } 

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

/**
** comparisons
**/

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

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

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

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

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

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

/**
** operator overloading
**/

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

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

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

bigint operator * (const bigint & a, const bigint & b)
{ 
  bigint c; 
  if (&a == &b)
  {
    IasI(&c.I, &a.I);
    ImuasI(&c.I, &a.I);
  }
  else
    IasImuI(&c.I, &a.I, &b.I); 
  return c; 
}

bigint operator / (const bigint & a, const bigint & b)
{ 
  bigint c, r; 
  uIdiv(&c.I, &r.I, &a.I, &b.I); 
  if (c.I.length)
    c.I.sign = a.I.sign ^ b.I.sign;
  return c; 
}

bigint operator % (const bigint & a, const bigint & b)
{ 
  bigint c, q; 
  uIdiv(&q.I, &c.I, &a.I, &b.I); 
  if (c.I.length)
     c.I.sign = a.I.sign;
  return c; 
}

bigint operator << (const bigint & a, long ui)
{ 
  bigint c;
  if (ui < 0) 
    lidia_error_handler("bigint", "operator<<::index is negative.");
  IasIslD(&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.");
  IasIsrD(&c.I, &a.I, ui); 
  return c;
}

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

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

bigint operator ^ (const bigint & a, const bigint & b)
{ bigint c; IasIxorI(&c.I, &a.I, &b.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++ ()
{ Iinc(&I); return *this; }

bigint & bigint::operator-- ()
{ Idec(&I); return *this; }

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

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

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

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

/**
** Procedural versions
**/

void negate(bigint & a, const bigint & b)
{ IasI(&a.I, &b.I); if (a.I.length) a.I.sign ^= MINUS; }

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

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

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

void divide(bigint & c, const bigint & a, const bigint & b)
{ 
  bigint r;
  int sa = a.I.sign;
  int sb = b.I.sign;
  uIdiv(&c.I, &r.I, &a.I, &b.I); 
  if (c.I.length)
    c.I.sign = sa ^ sb;
}

void remainder(bigint & c, const bigint & a, const bigint & b)
{ 
  bigint q; 
  int sa = a.I.sign;
  uIdiv(&q.I, &c.I, &a.I, &b.I); 
  if (c.I.length)
      c.I.sign = sa;
}

void div_rem(bigint & q, bigint & r, const bigint & a, const bigint & b)
{ 
  int sa = a.I.sign;
  int sb = b.I.sign;
  uIdiv(&q.I, &r.I, &a.I, &b.I); 
  if (q.I.length)
    q.I.sign = sa ^ sb;
  if (r.I.length)
      r.I.sign = sa;
}

void invert(bigint & a, const bigint & b)
{ 
  if ((b.I.length == 1) && (*(b.I.vec) == 1))
     IasI(&a.I, &b.I) ;
  else
     lidia_error_handler("bigint", "invert::inverting 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.");
  IasIslD(&c.I, &a.I, (unsigned long)ui); 
}

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

void power(bigint & c, const bigint & a, const bigint & b)
{
  bigint exponent, multiplier;
  if ((a.I.length == 1) &&
      (*(a.I.vec) == 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.length == 1) &&
      (*(a.I.vec) == 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 
    IasIpowD(&c.I, &a.I, i); 
} 

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

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

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

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

void inc(bigint & c)
{ Iinc(&c.I); }

void dec(bigint & c)
{ Idec(&c.I); }

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

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

void multiply(bigint & c, const bigint & a, long i)
{
  int s = 0;
  if (i < 0)
  {
    s = 1;
    i = -i;
  }
  IasImuD(&c.I, &a.I, i);
  if (s && c.I.length)
    c.I.sign ^= MINUS;
}

void divide(bigint & c, const bigint & a, long i)
{
  int s = 0;
  int sa = a.I.sign;
  if (i < 0)
  {
    s = 1;
    i = -i;
  }
  uIasIdiD(&c.I, &a.I, i);
  if (c.I.length)
    c.I.sign = sa ^ s;
}
  

void remainder(long &r, const bigint & a, long i)
{
  bigint q; 
  if (i < 0)
    i = -i;
  r = uIasIdiD(&q.I, &a.I, i);
  if (r)
  {
      if (a.I.sign)
        r = -r;
  }
}

long remainder(const bigint & a, long i)
{
  long r; 
  remainder(r, a, i);
  return r;
}

void div_rem(bigint & q, long &r, const bigint & a, long i)
{
  int s = 0;
  int sa = a.I.sign;
  if (i < 0)
  {
    s = 1;
    i = -i;
  }
  r = uIasIdiD(&q.I, &a.I, i);
  if (q.I.length)
    q.I.sign = sa ^ s;
  if (r)
  {
      if (sa)
        r = -r;
  }
}

/**
** gcd's
**/

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

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

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

bigint xgcd(bigint & u, bigint & v, const bigint & a, const bigint & b)
{ bigint c; Ixgcd(&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, v; Ixgcd(&c.I, &u.I, &v.I, &a.I, &b.I); return c; }

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

/**
** functions
**/

bigint abs(const bigint & a)
{ bigint c(a); c.I.sign = PLUS; return c;}

void seed(const bigint & a)
{ bigint c; IasrandomI(&c.I, &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
     IasrandomI(&c.I, &a.I);
 
  return c; 
}

#ifdef USE_OLD_DBL

double dbl(const bigint & a)
{
  double d; 
  char *s = new char[Ilog(&a.I)/3+10];
  Itoa(&a.I, s); 
  d = atof(s);
  delete[] s;
  return d;
}

xdouble xdbl(const bigint & a)
{
  xdouble d; 
  char *s = new char[Ilog(&a.I)/3+10];
  Itoa(&a.I, s); 
  d = string_to_xdouble(s);
  delete[] s;
  return d;
}

#else

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

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

#endif

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

void swap(bigint & a, bigint & b)
{
  int tmp;
  DigitType *tmpp;

  tmp = a.I.sign;
  a.I.sign = b.I.sign;
  b.I.sign = tmp;

  tmp = a.I.length;
  a.I.length = b.I.length;
  b.I.length = tmp;

  tmp = a.I.maxlength;
  a.I.maxlength = b.I.maxlength;
  b.I.maxlength = tmp;

  tmpp = a.I.vec;
  a.I.vec = b.I.vec;
  b.I.vec = tmpp;
}

/**
** 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 = Ilog(&a.I) + 10;
  char *s = new char[l / 3 + 2];
  Itoa(&a.I, s);
  out << s;
  delete[] s;
  return out;
}

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

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

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

void bigint::read_from_file(FILE * fp)
{
  int soi, soD;
  soi = sizeof(int);
  soD = sizeof(DigitType);
  if (feof(fp)) return;
  delDigitVec(I.vec, I.maxlength);
  fread ((char *)&I.maxlength, soi, 3, fp ); 
  I.maxlength = I.length;
  I.vec = (DigitType *) newDigitVec (&I.maxlength);
  fread ((char *)I.vec , soD, I.length, fp);
}

void bigint::write_to_file(FILE * fp)
{
  int soi, soD;
  soi = sizeof(int);
  soD = sizeof(DigitType);
  fwrite ((char *)&I.maxlength, soi, 3, fp); 
  fwrite ((char *)I.vec, soD, I.length, fp); 
}

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

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

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

