//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigfloat_c 
// Author      : Thomas Papanikolaou (TP)
// Last change : TP, Feb 7 1995, initial version
//               TP, Oct 30 1995, alphabetical order
//		 WB, Oct 15 1996, added
//			bigfloat(xdouble xd);
//		 	int xdoublify(xdouble & d) const;
//			void assign(xdouble xd);
//			xdouble operator = (xdouble d);
//			


#include <LiDIA/bigfloat.h>
#include <LiDIA/bigfloat_config.h>
#include <stdlib.h>

void acos(bigfloat & y, const bigfloat & x)
{
  bigfloat p1(x), p2(1), p3;
  
  if (p1.compare(p2) == 0)
    {
      y.assign_zero();
      return;
    }
  
  p2.negate();
  if (p1.compare(p2) == 0)
    {
      constant_Pi(y);
      return;
    }
  
  multiply(p2, x, p1);
  dec(p2);
  p2.negate();
  if (p2.is_lt_zero())
    lidia_error_handler("bigfloat", "acos::cannot handle arguments > 1");
  sqrt(p2, p2);
  divide(p1, x, p2);
  atan(p3, p1);
  constant_Pi(p2);
  p2.e--;
  subtract(y, p2, p3);
}

bigfloat acos(const bigfloat & x)
{
  bigfloat y;
  acos(y, x);
  return y;
}


void acosh(bigfloat & y, const bigfloat & x)
{
  bigfloat p1;
  square(p1, x);
  dec(p1);
  if (p1.is_lt_zero())
    lidia_error_handler("bigfloat", "acosh::cannot handle arguments <= 1");
  sqrt(p1, p1);
  add(y, x, p1);
  log(y, y);
}

bigfloat acosh(const bigfloat & x)
{
  bigfloat y;
  acosh(y, x);
  return y;
}


void acot(bigfloat & y, const bigfloat & x)
{
  bigfloat p1, p2;
  atan(p1, x);
  constant_Pi(p2);
  p2.e--;
  subtract(y, p2, p1);
}

bigfloat acot(const bigfloat & x)
{
  bigfloat y;
  acot(y, x);
  return y;
}


void acoth(bigfloat & y, const bigfloat & x)
{
  bigfloat p1(x);
  dec(p1);
  divide(y, 2, p1);
  inc(p1);
  if (p1.is_lt_zero())
    lidia_error_handler("bigfloat", "acoth::cannot handle arguments >= -1 and <= 1");
  log(y, y);
  y.e--;
}

bigfloat acoth(const bigfloat & x)
{
  bigfloat y;
  acoth(y, x);
  return y;
}


void add(bigfloat & sum, const bigfloat & x, const bigfloat & y)
{
  const bigfloat *px, *py;
  long log_x = x.e + x.prec, log_y = y.e + y.prec;
  long log_dif = log_x - log_y;
  long ed, e1, e2;
  
  if (log_dif < 0)
    {
      px = &y;
      py = &x;
      log_dif = -log_dif;
    }
  else
    {
      px = &x;
      py = &y;
    }
 

  e1 = px->e;
  e2 = py->e;
  
  int exp_error = check_overflow(ed, e1, -e2);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("add", "exponent overflow");
    else
      lidia_error_handler("add", "exponent undeflow.");
  }

  if (log_dif > (bigfloat::binary_precision + rounding_bits))
  {
    sum.assign(*px);
    return;
  }
 
  if (ed > 0)
  {
    bigint tmp(px->m);
    shift_left(tmp, tmp, ed);
    add(sum.m, tmp, py->m);
    sum.e = e2;
  }
  else
  {
    bigint tmp(py->m);
    shift_left(tmp, tmp, -ed);
    add(sum.m, tmp, px->m);
    sum.e = e1;
  }
  sum.prec = sum.m.bit_length();
  sum.flag() = NotExact;
  sum.normalize();
}

void add(bigfloat & sum, const bigfloat & x, long i)
{
  bigfloat y(i);
  const bigfloat *px, *py;
  long log_x = x.e + x.prec, log_y = y.e + y.prec;
  long log_dif = log_x - log_y;
  long ed, e1, e2;
  
  if (log_dif < 0)
    {
      px = &y;
      py = &x;
      log_dif = -log_dif;
    }
  else
    {
      px = &x;
      py = &y;
    }
  
  e1 = px->e;
  e2 = py->e;
  
  int exp_error = check_overflow(ed, e1, -e2);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("add", "exponent overflow");
    else
      lidia_error_handler("add", "exponent undeflow.");
  }

  if (log_dif > (bigfloat::binary_precision + rounding_bits))
  {
    sum.assign(*px);
    return;
  }
  
  if (ed > 0)
  { 
    bigint tmp(px->m);
    shift_left(tmp, tmp, ed);
    add(sum.m, tmp, py->m);
    sum.e = e2;
  }
  else
  {
    bigint tmp(py->m);
    shift_left(tmp, tmp, -ed);
    add(sum.m, tmp, px->m);
    sum.e = e1;
  }
  sum.prec = sum.m.bit_length();
  sum.flag() = NotExact;
  sum.normalize();
}

void add(bigfloat & sum, long i, const bigfloat & y)
{
  bigfloat x(i);
  const bigfloat *px, *py;
  long log_x = x.e + x.prec, log_y = y.e + y.prec;
  long log_dif = log_x - log_y;
  long ed, e1, e2;
  
  if (log_dif < 0)
    {
      px = &y;
      py = &x;
    }
  else
    {
      px = &x;
      py = &y;
    }
  
  e1 = px->e;
  e2 = py->e;
  
  int exp_error = check_overflow(ed, e1, -e2);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("add", "exponent overflow");
    else
      lidia_error_handler("add", "exponent undeflow.");
  }

  if (log_dif > (bigfloat::binary_precision + rounding_bits))
  {
    sum.assign(*px);
    return;
  }
  
  if (ed > 0)
  { 
    bigint tmp(px->m);
    shift_left(tmp, tmp, ed);
    add(sum.m, tmp, py->m);
    sum.e = e2;
  }
  else
  {
    bigint tmp(py->m);
    shift_left(tmp, tmp, -ed);
    add(sum.m, tmp, px->m);
    sum.e = e1;
  }
  sum.prec = sum.m.bit_length();
  sum.flag() = NotExact;
  sum.normalize();
}

void inc(bigfloat & sum)
{
  bigfloat tmp(1);
  add(sum, sum, tmp);
}

bigfloat operator + (const bigfloat & a, const bigfloat & b)
{
  bigfloat c;
  add(c, a, b);
  return c;
}

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

bigfloat & bigfloat::operator++ ()
{
  inc(*this);
  return *this;
}


void asin(bigfloat & y, const bigfloat & x)
{
  bigfloat p1(x), p2(1);
  
  if (p1.compare(p2) == 0)
    {
      constant_Pi(y);
      y.e--;
      return;
    }
  
  p2.negate();
  if (p1.compare(p2) == 0)
    {
      constant_Pi(y);
      y.e--;
      y.negate();
      return;
    }
  
  multiply(p2, x, p1);
  dec(p2);
  p2.negate();
  if (p2.is_lt_zero())
    lidia_error_handler("bigfloat", "asin::cannot handle arguments > 1");
  sqrt(p2, p2);
  divide(p1, x, p2);
  atan(y, p1);
}

bigfloat asin(const bigfloat & x)
{
  bigfloat y;
  asin(y, x);
  return y;
}


void asinh(bigfloat & y, const bigfloat & x)
{
  bigfloat p1;
  square(p1, x);
  inc(p1);
  sqrt(p1, p1);
  add(y, x, p1);
  log(y, y);
}

bigfloat asinh(const bigfloat & x)
{
  bigfloat y;
  asinh(y, x);
  return y;
}


void bigfloat::assign(const bigint & y)
{
  m.assign(y);
  e = 0;
  prec = m.bit_length();
  this->base_digit_normalize();
  if (prec == 0) 
    this->flag() = PlusZero;
  else
    this->flag() = Exact;
}

void bigfloat::assign(const bigint & x, const bigint & y)
{
  bigint q, r;
  div_rem(q, r, x, y);
  if (r.is_zero())
  {
    m.assign(q);
    e = 0;
    prec = m.bit_length();
    this->base_digit_normalize();
    if (prec == 0) 
      this->flag() = PlusZero;
    else
      this->flag() = Exact;
  }
  else
  {
     bigfloat tmp(y);
     this->assign(x);
     divide(*this, *this, tmp);
  }
}

void bigfloat::assign(const bigfloat & y)
{
  m.assign(y.m);
  e = y.e;
  prec = y.prec;
  this->flag() = y.flag();
}

#ifdef USE_SLOW_FASDBL

void bigfloat::assign(double d)
{
  char s[32];
  sprintf(s, "%1.15e", d);
  string_to_bigfloat(s, *this);
}

#endif

#ifdef USE_IEEE_FASDBL

void bigfloat::assign(double d)
{
  base_digit *mant;
  int expo;
  double frac;

  if (d == 0.0)
    {
      this->assign_zero();
    }
  else
    {
      unsigned int hi, lo;
      frac = frexp(d, &expo);
      mant = (base_digit *) & frac;
#ifdef DOUBLES_HIGH_LOW
      hi = (mant[0] & 0x000fffff) | 0x00100000;
      lo = mant[1];
#endif
#ifdef DOUBLES_LOW_HIGH
      hi = (mant[1] & 0x000fffff) | 0x00100000;
      lo = mant[0];
#endif
      m.assign(hi);
      shift_left(m, m, 32);
      or(m, m, lo);
      if (d < 0)
        m.negate();
      e = expo - 53;
      prec = 53; 
      this->flag() = NotExact;
      this->base_digit_normalize();
    }
}
#endif

#define USE_PORTABLE_FASDBL
#ifdef USE_PORTABLE_FASDBL

void bigfloat::assign(double d)
{
  base_digit mant;
  int expo;
  int s = 0;
  int ndigits = 0;

  if (d == 0.0)
    {
      this->assign_zero();
    }
  else
    {
      if (d < 0.0)
      {
        s = 1;
        d = -d;
      }

    d = frexp(d, &expo);
    m.assign_zero();
    while (d != 0)
      {
        d = ldexp(d, bits_per_base_digit);
        mant = (base_digit) d;
        d -= mant;
        ndigits++;
        shift_left(m, m, bits_per_base_digit);
        add(m, m, bigint(mant));
      }
    if (s)
      m.negate();
    e = expo - (ndigits * bits_per_base_digit);
    prec = m.bit_length();
    this->base_digit_normalize();
    this->flag() = NotExact;
  }
}
#endif

void bigfloat::assign(xdouble xd)
{
  bigfloat temp(xd.l());
  assign(xd.h());
  add(*this, *this, temp);
//   char s[200];
//   xdouble_to_string(s, xd);
//   string_to_bigfloat(s, *this);
}


void bigfloat::assign(int i)
{
  m.assign(i);
  e = 0;
  prec = m.bit_length();
  this->base_digit_normalize();
  if (prec == 0) 
    this->flag() = PlusZero;
  else
    this->flag() = Exact;
}

void bigfloat::assign(long i)
{
  m.assign(i);
  e = 0;
  prec = m.bit_length();
  this->base_digit_normalize();
  if (prec == 0) 
    this->flag() = PlusZero;
  else
    this->flag() = Exact;
}

void bigfloat::assign(unsigned long ui)
{
  m.assign(ui);
  e = 0;
  prec = m.bit_length();
  this->base_digit_normalize();
  if (prec == 0) 
    this->flag() = PlusZero;
  else
    this->flag() = Exact;
}

void bigfloat::assign_exact_zero()
{
  m.assign_zero();
  e = 0;
  prec = 0;
  this->flag() = PlusZero;
}

void bigfloat::assign_zero()
{
  m.assign_zero();
  e = -bigfloat::binary_precision;
  prec = 0;
  this->flag() = NotExact;
}

void bigfloat::assign_one()
{
  m.assign_one();
  e = 0;
  prec = 1;
  this->base_digit_normalize();
  this->flag() = Exact;
}

int bigfloat::operator = (int i)
{
  this->assign(i);
  return i;
}
 
long bigfloat::operator = (long l)
{
  this->assign(l);
  return l;
}
 
unsigned long bigfloat::operator = (unsigned long ul)
{
  this->assign(ul);
  return ul;
}
 
double bigfloat::operator = (double d)
{
  this->assign(d);
  return d;
}

xdouble bigfloat::operator = (xdouble xd)
{
  this->assign(xd);
  return xd;
}
 
bigint bigfloat::operator = (const bigint & bi)
{
  this->assign(bi);
  return bi;
}
 
bigrational bigfloat::operator = (const bigrational & br)
{
  this->assign(br);
  return br;
}
 
bigfloat & bigfloat::operator = (const bigfloat & bf)
{
  this->assign(bf);
  return *this;
}

void atan(bigfloat & y, const bigfloat & x)
{
  long i, j, ex, t = bigfloat::binary_precision;
  long m, u, f;
  
  if (x.is_zero())
    {
      y.assign_zero();
      return;
    }
  
  ex = x.e + x.prec;
  if (ex > bigfloat::binary_precision)
    bigfloat::binary_precision = ex + bits_per_base_digit
      - ex % bits_per_base_digit;
  bigfloat a(1);
  bigfloat tmp(x);
  
  m = 0;
  if (tmp.is_lt_zero())
    {
      m = 1;
      tmp.negate();
    }
  
  if (tmp.compare(a) == 0)
    {
      constant_Pi(y);
      y.e -= 2;
      if (m)
        y.negate();
      return;
    }
  
  tmp.normalize();
  
  ex = tmp.e + bigfloat::binary_precision;
  u = 0;
  if (ex > 0)
    {
      divide(tmp, 1, tmp);
      u = 1;
    }
  
  ex = tmp.e + bigfloat::binary_precision;
  f = 0;
  bigfloat q(tmp);
  if (ex > -10)
    while (tmp.e + bigfloat::binary_precision > -10)
      {
        multiply(q, q, tmp);
        add(q, q, a);
        sqrt(q, q);
        add(q, q, a);
        divide(tmp, tmp, q);
        q.assign(tmp);
        f++;
      }
  square(a, tmp);
  
  ex = tmp.e + bigfloat::binary_precision;
  if (ex < 0)
    ex = -ex;
  ex <<= 1;
  j = bigfloat::binary_precision / ex;
  if (j & 1)
    j++;
  divide(y, 1, 2 * j + 1);
  bigfloat::binary_precision = 4 * ex;
  
  for (i = j; i >= 1; i--)
    {
      multiply(y, y, a);
      divide(q, 1, 2 * i - 1);
      bigfloat::binary_precision += 2 * ex;
      if (bigfloat::binary_precision > t)
        bigfloat::binary_precision = t;
      y.negate();
      add(y, y, q);
    }
  bigfloat::binary_precision = t;
  multiply(y, y, tmp);
  y.e += f;
  
  if (u)
    {
      constant_Pi(a);
      a.e--;
      subtract(y, y, a);
      y.negate();
    }
  if (m)
    y.negate();
  
}

void atan2(bigfloat & z, const bigfloat & y, const bigfloat & x)
{
  bigfloat w;
  int ys = y.sign(), xs = x.sign();
  int yss = (ys < 0), xss = (xs < 0);  
  char code = yss + (xss << 1);

  if (xs == 0)
    {
      if (ys == 0)
	z.assign_zero();
      else
	{
	  constant_Pi(z);
	  z.divide_by_2();
          if (ys < 0)
	    z.negate();
	}
      return;
    }

  if ( ys == 0)
    {
      if (xs < 0)
	constant_Pi(z);
      else
	z.assign_zero();
      return;
    }

  switch( code )
    {
    case 0:
    case 1: 
      w.assign_zero(); 
      break;
    case 2: 
      constant_Pi(w); 
      break;
    case 3: 
      constant_Pi(w); 
      w.negate(); 
      break;
    }

  z.assign(y);
  divide(z, z, x);
  atan(z, z);
  add(z, z, w);
}

bigfloat atan(const bigfloat & x)
{
  bigfloat y;
  atan(y, x);
  return y;
}
 
bigfloat atan2(const bigfloat & y, const bigfloat & x)
{
  bigfloat z;
  atan2(z, y, x);
  return z;
}

void atanh(bigfloat & y, const bigfloat & x)
{
  bigfloat p1(x);
  
  p1.negate();
  inc(p1);
  divide(y, 2, p1);
  dec(y);
  if (p1.is_lt_zero())
    lidia_error_handler("bigfloat", "atanh::cannot handle arguments <= -1 and >= 1");
  log(y, y);
  y.e--;
}

bigfloat atanh(const bigfloat & x)
{
  bigfloat y;
  atanh(y, x);
  return y;
}

void besselj(bigfloat & y, int n, const bigfloat & x)
{
  bigfloat e(1), f(1), s;
  long i, m = 0, ex, t = bigfloat::binary_precision, z;
  
  /**
   ** use sign identities to make n positiv
   **/
  
  if (n < 0)
    {
      n = -n;
      if (n % 2)
        m = 1;
    }
  
  /**
   ** calculate n!
   **/
  
  for (i = 2; i <= n; i++)
    multiply(f, f, i);
  z = t;
  
  /**
   ** increase the precision if needed
   **/
  
  ex = x.e + x.prec;
  if (ex > z)
    bigfloat::binary_precision = z = ex;
  
  /**
   ** calculate the series
   **/
  
  power(y, x, n);
  y.e -= n;
  divide(y, y, f);
  ex = z + y.e;
  if (ex < 0) 
    ex = -ex;
  ex += z;
  bigfloat::binary_precision = ex + bits_per_base_digit
    - (ex % bits_per_base_digit);
  f.assign(y);
  y.assign_one();
  e.assign_one();
  square(s, x);
  s.negate();
  s.e -= 2;
  
  i = 1;
  for (;;)
    {
      multiply(e, e, s);
      divide(e, e, i);
      divide(e, e, n + i);
      if (e.is_approx_zero())
        break;
      add(y, y, e);
      i++;
    }
  
  /**
   ** restore the precision; final multiplication; restore
   ** the sign and delete local variables
   **/
  
  bigfloat::binary_precision = t;
  multiply(y, y, f);
  if (m)
    y.negate();
}

bigfloat besselj(int n, const bigfloat & x)
{
  bigfloat y;
  besselj(y, n, x);
  return y;
}

bigfloat bigfloat::C_Catalan;

void constant_Catalan(bigfloat & y)
{
  
  if (bigfloat::C_Catalan.length() >= bigfloat::digit_precision)
    {
      y.assign(bigfloat::C_Catalan);
      y.normalize();
    }
  else
    {
      bigfloat temp;
      int i = 1, j = 3;
      
      y.assign(0.5);
      bigfloat term(0.5);
      bigfloat p(0.5);
      
      while (!term.is_approx_zero())
        {
          multiply(p.m, p.m, i);
          p.normalize();
          divide(p, p, j);
          multiply(term.m, term.m, i);
          term.normalize();
          divide(term, term, j);
          divide(temp, p, j);
          add(term, term, temp);
          add(y, y, term);
          i++;
          j += 2;
        }
      bigfloat::C_Catalan.assign(y);
    }
}

bigfloat Catalan()
{
  bigfloat x;
  constant_Catalan(x);
  return x;
}

bigfloat ceil(const bigfloat & x)
{
  bigfloat y;
  truncate(y, x);
 
  bigfloat fraction;
  subtract(fraction, x, y);
 
  if (fraction.is_gt_zero())
    inc(y);
  return y;
}

void ceil(bigfloat & y, const bigfloat & x)
{
  bigfloat fraction;
  if (&y == &x)
    {
      bigfloat tmp;
      truncate(tmp, x);
      subtract(fraction, x, tmp);
      y.assign(tmp);
    }
  else
    {
      truncate(y, x);
      subtract(fraction, x, y);
    }
  if (fraction.is_gt_zero())
    inc(y);
}

void ceil(bigint & y, const bigfloat & x)
{
  truncate(y, x);
  
  bigfloat fraction(y);
  subtract(fraction, fraction, x);
  fraction.negate();
  
  if (fraction.is_gt_zero())
    inc(y);
}

bool is_char(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(char)));

  if (b) return true;
  return false;
}

bool is_uchar(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(unsigned char
)));

  if (b) return true;
  return false;
}

bool is_short(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(short)));

  if (b) return true;
  return false;
}

bool is_ushort(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(unsigned short)));

  if (b) return true;
  return false;
}

bool is_int(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(int)));

  if (b) return true;
  return false;
}

bool is_uint(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(unsigned int)));

  if (b) return true;
  return false;
}

bool is_long(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(long)));

  if (b) return true;
  return false;
}

bool is_ulong(const bigfloat & x)
{

  if (x.is_zero())
    return true;

  bigfloat tmp;
  truncate(tmp, x);
  int b = (tmp.compare(x) == 0 && (tmp.prec <= bits_per_(unsigned long)));

  if (b) return true;
  return false;
}

bool is_double(const bigfloat & x)
{
  long ex = x.prec;
  if (ex == 0)
    return true;
  ex += x.e;
  if ((ex <= 0x3ff) && (ex > -1023)) return true;
  return false;
}

bool bigfloat::is_zero() const
{
  if (m.is_zero() || this->is_pm_zero()) return true;
  return false;
}
 
bool bigfloat::is_exact_zero() const
{
  if (m.is_zero() && this->is_exact()) return true;
  return false;
}
 
bool bigfloat::is_approx_zero() const
{
  if (m.is_zero() || ((e + prec) < (-bigfloat::binary_precision + rounding_bits))) return true;
return false;
}
 
bool bigfloat::is_gt_zero() const
{
  if (m.is_gt_zero()) return true;
  return false;
}
 
bool bigfloat::is_lt_zero() const
{
  if (m.is_lt_zero()) return true;
  return false;
}
 
bool bigfloat::is_ge_zero() const
{
  if (m.is_ge_zero()) return true;
  return false;
}
 
bool bigfloat::is_le_zero() const
{
  if (m.is_le_zero()) return true;
  return false;
}
 
bool bigfloat::is_one() const
{
  bigfloat tmp(1);
  subtract(tmp, tmp, *this);
  if  (tmp.is_zero()) return true;
  return false;
}
 
int bigfloat::abs_compare(const bigfloat & y) const
{
  bigfloat abs_this(*this), abs_y(y);
  abs_this.absolute_value();
  abs_y.absolute_value();
  int gt = (abs_y > abs_this);
  if (gt)
    return -1;
  gt = (abs_this > abs_y);
  return gt;
}

int bigfloat::compare(const bigfloat & y) const
{
  int gt = (y > *this);
  if (gt)
    return -1;
  gt = (*this > y);
  return gt;
}

bool operator == (const bigfloat & x, const bigfloat & y)
{

  if (x.m.sign() != y.m.sign())
    return false;

  if (x.m.is_zero())
    return y.m.is_zero();

  if (y.m.is_zero())
    return x.m.is_zero();

  if ((x.e + x.m.bit_length()) != (y.e + y.m.bit_length()))
    return false;

  bigfloat tmp;
  subtract(tmp, x, y);
  return (tmp.is_zero());
}

bool operator != (const bigfloat & x, const bigfloat & y)
{

  if (x.m.sign() != y.m.sign())
    return true;

  if (x.m.is_zero())
    return !y.m.is_zero();

  if (y.m.is_zero())
    return !x.m.is_zero();

  if ((x.e + x.m.bit_length()) != (y.e + y.m.bit_length()))
    return true;

  bigfloat tmp;
  subtract(tmp, x, y);
  return (!tmp.is_zero());
}

bool operator > (const bigfloat & x, const bigfloat & y)
{
  long sx, sy, ex, ey;

  sx = x.m.sign();
  sy = y.m.sign();

  // if (x == 0) return (y < 0)
  
  if (sx == 0)
    {
      if (sy < 0) return true;
      return false;
    }
  
  // if (y == 0) return (x > 0)
  
  if (sy == 0)
    {
      if (sx > 0) return true;
      return false;
    }

  // at this point is x != 0 && y != 0
  
  // if (x > 0) && (y < 0) return 1;
  
  if (sx > sy)
    return true;
  
  // if (x < 0) && (y > 0) return 0;
  
  if (sx < sy)
    return false;
  
  // at this point sx == sy
  
  ex = x.e + x.m.bit_length();
  ey = y.e + y.m.bit_length();
  
  // if (ex > ey) return (sx > 0);
  
  if (ex > ey)
    {
      if (sx > 0) return true;
      return false;
    }

  // if (ex < ey) return (sx < 0);
  
  if (ex < ey)
    {
      if (sx < 0) return true;
      return false;
    }

  // at this point ex == ey
  
  // return ( (x-y) > 0)
  
  bigfloat tmp;
  subtract(tmp, x, y);
  
  return (tmp.m.is_gt_zero());
  
}

bool operator >= (const bigfloat & x, const bigfloat & y)
{
  int gt = (x > y);
  int eq = (x == y);
  if (gt || eq) return true;
  return false;
}

bool operator < (const bigfloat & x, const bigfloat & y)
{
  long sx, sy, ex, ey;
  
  sx = x.m.sign();
  sy = y.m.sign();
  
  // if (x == 0) return (y > 0)
  
  if (sx == 0)
    {
      if (sy > 0) return true;
      return false;
    }

  // if (y == 0) return (x < 0)
  
  if (sy == 0)
    {
      if (sx < 0) return true;
      return false;
    }

  // at this point is x != 0 && y != 0
  
  // if (x > 0) && (y < 0) return 0;
  
  if (sx > sy)
    return false;
  
  // if (x < 0) && (y > 0) return 1;
  
  if (sx < sy)
    return true;
  
  // at this point sx == sy
  
  ex = x.e + x.m.bit_length();
  ey = y.e + y.m.bit_length();
  
  // if (ex > ey) return (sx < 0);
  
  if (ex > ey)
    {
      if (sx < 0) return true;
      return false;
    }
  
  // if (ex < ey) return (sx > 0);
  
  if (ex < ey)
    {
      if (sx > 0) return true;
      return false;
    }
  
  // at this point ex == ey
  
  // return ( (x-y) < 0)
  
  bigfloat tmp;
  subtract(tmp, x, y);
  
  return (tmp.m.is_lt_zero());
  
}

bool operator <= (const bigfloat & x, const bigfloat & y)
{
  int lt = (x < y);
  int eq = (x == y);
  if (lt || eq) return true;
  return false;
}

bool bigfloat::operator ! ()
{
  return this->is_zero();
}

bigfloat::bigfloat()
{
  this->assign_zero();
}

bigfloat::bigfloat(int i)
{
  this->assign(i);
}

bigfloat::bigfloat(long l)
{
  this->assign(l);
}

bigfloat::bigfloat(unsigned long ul)
{
  this->assign(ul);
}

bigfloat::bigfloat(double d)
{
  this->assign(d);
}

bigfloat::bigfloat(xdouble xd)
{
  this->assign(xd);
}

bigfloat::bigfloat(const bigint & bi)
{
  this->assign(bi);
}

bigfloat::bigfloat(const bigrational & br)
{
  this->assign(br.numerator(), br.denominator());
}

bigfloat::bigfloat(const bigfloat & bf)
{
  this->assign(bf);
}

bigfloat::bigfloat(const bigint & n, const bigint & d)
{
  this->assign(n, d);
}

bigfloat::~bigfloat()
{
}

void bigfloat::bigintify(bigint & y) const
{
  bigfloat r = round(*this);
  y.assign(r.m);
  if (r.e > 0) shift_left(y, y, r.e);
  else shift_right(y, y, -r.e);
}

bool bigfloat::doublify(double &d) const
{
  long l = m.bit_length(), expo = e + l;

  if (m.is_zero() || (expo < -1023))
    {
      d = 0.0;
      return false;
    }

  if (expo >= 0x3ff)
    {
      warning_handler("bigfloat", "impossible assignment double = bigfloat");
      return true;
    }

  if (l > (SIZEOF_DOUBLE * 8))
  {
    long ed = l - (SIZEOF_DOUBLE * 8);
    bigint bd = m >> ed;                     // bd = (m/2^ed)*2^ed
    d = dbl(bd) * ldexp(2.0, (int)(e + ed - 1));
  }
  else 
    d = dbl(m) * ldexp(2.0, (int)(e - 1));
  return false;
}

bool bigfloat::xdoublify(xdouble &xd) const
{
  long l = m.bit_length(), expo = e + l;

  if (m.is_zero() || (expo < -996))
    {
      xd = 0.0;
      return false;
    }

  if (expo >= 996)
    {
      warning_handler("bigfloat", "impossible assignment xdouble = bigfloat"
);
      return true;
    }

  if (l > (2*SIZEOF_DOUBLE * 8))
  {
    long ed = l - (2*SIZEOF_DOUBLE * 8);
    bigint bd = m >> ed;                     // bd = (m/2^ed)*2^ed
    xd = xdbl(bd) * exp(log((xdouble )2.0) * (int)(e + ed));
  }
  else 
    xd = xdbl(m) * exp(log((xdouble )2.0) * (int)(e));
  return false;
}


bool bigfloat::longify(long &i) const
{
  double d;
  int flag;
  flag = this->doublify(d);
  if (flag == 1 || (d > max_long))
    {
      warning_handler("bigfloat", "impossible assignment long = bigfloat");
      return true;
    }
  i = (long) d;
  return false;
}

bool bigfloat::intify(int &i) const
{
  double d;
  int flag;

  flag = this->doublify(d);
  if (flag == 1 || (d > max_int))
    {
      warning_handler("bigfloat", "impossible assignment int = bigfloat");
      return true;
    }
  i = (int) d;
  return false;
}

void cos(bigfloat & y, const bigfloat & x)
{
  bigfloat tmp;
  double d, df;
  long ex, f = 0, s = 0, t = bigfloat::digit_precision;
  long i, k, n, s1, t0, t1, t2;
  
  /**
   ** test for zero
   **/
  
  if (x.is_zero())
    {
      y.assign_one();
      return;
    }
  
  /**
   ** p0 = x; increase the precision to bigfloat::binary_precision + log2(p0)
   **/
  
  bigfloat p0(x);
  ex = p0.e + p0.prec;
  if (ex > 0)
    {
      bigfloat::binary_precision += ex + bits_per_base_digit
        - (ex % bits_per_base_digit);
      bigfloat::digit_precision = bigfloat::binary_precision / bits_per_base_digit;
    }
  
  /**
   ** normalize p0 and set p0 = abs(p0)
   **/
 
  p0.normalize();
  p0.absolute_value();
  

  /**
   ** calculate pi and reduce p0 according to the equation
   **     cos(p0) = cos(p0' + k pi) = (-1)^k cos(p0')
   **/
  
  constant_Pi(y);
  if (p0.compare(bigfloat::C_Pi) >= 0)
    {
      divide(y, p0, bigfloat::C_Pi);
      truncate(y.m, y);
      y.e = 0;
      s = y.m.is_odd();
      y.normalize();
      multiply(y, y, bigfloat::C_Pi);
      subtract(p0, p0, y);
    }
  
  /**
   ** calculate ex = log2(p0) exactly (as double); the calculate the
   ** reduction range k and find the number f of reduction steps needed
   ** so that
   **     p0 * 2 ^ f     <   p0 * 2 ^ k   =  p0 * 2 ^ (-sqrt(t/4)).
   ** Then reduce p0.
   **/
  
  ex = (long) (log(fabs(p0.m.most_significant_digit() / max_base_digit_2)) * INVLOG2 + (p0.e + p0.length() * bits_per_base_digit));
  k = (long) (-sqrt(bigfloat::binary_precision / 4.0));
  if (ex > k)
    {
      n = (long) (1 + sqrt(4.0 * bigfloat::binary_precision));
      f = 1 + ex - k;
      p0.e -= f;
    }
  else
    {
      n = (long) (1 + (bigfloat::binary_precision * 1.0) / ((ex > 0) ? ex : -ex));
      f = 0;
    }
  
  /**
   ** reduce further the number of steps using a fix-point iteration
   **/
  
  d = 0.0;
  for (i = 2; i <= n; i++)
    d += log((double) i) * INVLOG2;
  i = 0;
  df = i * k + d;
  while (df > 0)
    {
      i++;
      d -= log((double) n) * INVLOG2;
      df = i * k + d;
      n--;
    }
  n++;
  
  /**
   ** make n even
   **/
  
  if (n & 1)
    n++;
  
  /**
   ** Set p0 = -p0^2, y = 1; then evaluate the taylor series for
   **           cos(x)-1 = -x^2/2! + x^4/4! -+ ...
   ** using increasing precision at each step
   **/
  
  square(p0, p0);
  p0.negate();
  y.assign(1);
  
  /**
   ** starting precision
   **/
  
  t1 = ((-k) / bits_per_base_digit) << 1;
  t2 = bigfloat::digit_precision - 2;
  s1 = 0;
  bigfloat::binary_precision = (t1 + 2) * bits_per_base_digit;
  
  /**
   ** Use increasing precision at each step. l1 is the number
   ** of correct Digits at the current point. l2 ist the precision
   ** to be set before the next iteration occurs. The code for precision
   ** increasing is adapted from the PARI system.
   **/
  
  for (i = n; i > 2; i -= 2)
    {
      divide(y, y, i * (i - 1));
      if (p0.length() > t1)
        {
          tmp.cut(p0, t1 * bits_per_base_digit);
          multiply(y, y, tmp);
        }
      else
        multiply(y, y, p0);
      ex = y.e + bigfloat::binary_precision;
      
      /** GP/PARI : Start **/
      s1 = s1 - ex;
      t0 = s1 / bits_per_base_digit;
      t1 += t0;
      if (t1 > t2)
        t1 = t2;
      s1 %= bits_per_base_digit;
      /** GP/PARI : End **/
      
      bigfloat::binary_precision = (t1 + 2) * bits_per_base_digit;
      inc(y);
    }
  divide(y, y, i * (i - 1));
  multiply(y, y, p0);
  
  /**
   ** we have calculated y = cos(x/2^f) - 1. We will calculate now
   ** cos(x/2^(f-1)) - 1 = 2 * y * (y + 2). y is exact, (y+2) is exact
   ** so the final result (cos(x)-1) is exact. Error can only occur if
   ** x was very near to pi/2.
   **/
  
  if (f)
    {
      add(p0, y, 2);
      for (i = 0; i < f; i++)
        {
          multiply(y, y, p0);
          y.e++;
          add(p0, y, 2);
        }
    }
  
  /**
   ** Before adding 1 we check the result for very small y and increase
   ** the precision accordingly so that the final result after the addition
   ** is correct at bigfloat::binary_precision + abs(log2(y)) digits
   **/
  
  ex = y.e;
  if (ex < 0)
    ex = -ex;
  if (ex > bigfloat::binary_precision)
    {
      bigfloat::binary_precision = ex + bits_per_base_digit
        - ex % bits_per_base_digit;
      bigfloat::digit_precision = bigfloat::binary_precision / bits_per_base_digit;
    }
  inc(y);
  
  /**
   ** correct the sign and normalize the answer
   **/
  
  if (s)
    y.negate();
  
  /**
   ** set the old precision
   **/
  
  bigfloat::digit_precision = t;
  bigfloat::binary_precision = t * bits_per_base_digit;
}

bigfloat cos(const bigfloat & x)
{
  bigfloat y;
  cos(y, x);
  return y;
}

void cosh(bigfloat & y, const bigfloat & x)
{
  bigfloat tmp(x);
  exp(y, tmp);
  divide(tmp, 1, y);
  add(y, y, tmp);
  y.e--;
}

bigfloat cosh(const bigfloat & x)
{
  bigfloat y;
  cosh(y, x);
  return y;
}

void cot(bigfloat & y, const bigfloat & x)
{
  long s = 0, ex, t = bigfloat::digit_precision;
  
  ex = x.e + x.prec;
  if (ex < 0)
    {
      ex = -ex;
      bigfloat::binary_precision += 4 * (ex + bits_per_base_digit
                                         - ex % bits_per_base_digit);
      bigfloat::digit_precision = bigfloat::binary_precision / bits_per_base_digit;
    }
  bigfloat p0(x);
  if (p0.is_lt_zero())
    {
      s = 1;
      p0.negate();
    }
  constant_Pi(y);
  if (p0.compare(bigfloat::C_Pi) >= 0)
    {
      divide(y, p0, bigfloat::C_Pi);
      truncate(y, y);
      multiply(y, y, bigfloat::C_Pi);
      subtract(p0, p0, y);
    }
  cos(y, p0);
  square(p0, y);
  p0.negate();
  inc(p0);
  sqrt(p0, p0);
  divide(y, y, p0);
  if (s)
    y.negate();
  bigfloat::digit_precision = t;
  bigfloat::binary_precision = t * bits_per_base_digit;
}

bigfloat cot(const bigfloat & x)
{
  bigfloat y;
  cot(y, x);
  return y;
}

void coth(bigfloat & y, const bigfloat & x)
{
  bigfloat tmp1, tmp2;
  exp(y, x);
  divide(tmp1, 1, y);
  subtract(tmp2, y, tmp1);
  add(y, y, tmp1);
  divide(y, y, tmp2);
}

bigfloat coth(const bigfloat & x)
{
  bigfloat y;
  coth(y, x);
  return y;
}

void bigfloat::divide_by_2()
{ if (!m.is_zero()) e--; }
 
void bigfloat::invert()
{
  bigint one(1), mant(m);
  long t = bigfloat::binary_precision;
  t += ((mant.length() + 1) * bits_per_base_digit);
  ::shift_left(one, one, t);
  div_rem(m, one, one, mant);
  e = -t - e;
  prec = m.bit_length();
  this->flag() = NotExact;
  normalize();
}

void divide(bigfloat & quot, const bigfloat & x, const bigfloat & y)
{
  long ex = x.e, ey = y.e;
  bigint tmp1(1), tmp2;
  long t = bigfloat::binary_precision;
  t += ((y.m.length() + 1) * bits_per_base_digit);
  shift_left(tmp1, tmp1, t);
  div_rem(tmp1, tmp2, tmp1, y.m);
  multiply(quot.m, tmp1, x.m);
  quot.e = -t + ex - ey;
  quot.prec = quot.m.bit_length();
  quot.flag() = NotExact;
  quot.normalize();
}

void divide(bigfloat & quot, const bigfloat & x, long i)
{
  quot.assign(x);
  quot.flag() = NotExact;
  quot.normalize();
  shift_left(quot.m, quot.m, bits_per_base_digit);
  quot.e -= bits_per_base_digit;
  divide(quot.m, quot.m, i);
  quot.prec = quot.m.bit_length();
  quot.normalize();
}

void divide(bigfloat & quot, long i, const bigfloat & x)
{
  long t = bigfloat::binary_precision;
  bigint tmp(i);
  t += ((x.m.length() + 1) * bits_per_base_digit);
  shift_left(tmp, tmp, t);
  div_rem(quot.m, tmp, tmp, x.m);
  quot.e = -t - x.e;
  quot.prec = quot.m.bit_length();
  quot.flag() = NotExact;
  quot.normalize();
}

void divide(bigfloat & quot, long i, long j)
{
  long t = bigfloat::binary_precision + bits_per_base_digit;
  quot.m = i;
  shift_left(quot.m, quot.m, t);
  divide(quot.m, quot.m, j);
  quot.e = -t;
  quot.prec = quot.m.bit_length();
  quot.flag() = NotExact;
  quot.normalize();
}

void invert(bigfloat & x, const bigfloat & y)
{
  if (&x != &y)
     divide(x, 1, y);
  else
  {
     bigfloat tmp;
     divide(tmp, 1, y);
     x.assign(tmp);
  }
}

bigfloat inverse(const bigfloat & y)
{
     bigfloat x;
     divide(x, 1, y);
     return x;
}

bigfloat operator / (const bigfloat & a, const bigfloat & b)
{
  bigfloat c;
  divide(c, a, b);
  return c;
}

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

bigfloat bigfloat::C_E;

#ifndef USE_OLD_E

void bigint_exp(long a, long b, bigint & first, bigint & second)
{
  bigint tmp1_first, tmp1_second;
  bigint tmp2_first, tmp2_second;
  bigint tmp;
  
  if (b == a + 1)
    {
      first.assign(b);
      second.assign(b);
      return;
    }
  
  bigint_exp(a, (a + b) >> 1, tmp1_first, tmp1_second);
  bigint_exp((a + b) >> 1, b, tmp2_first, tmp2_second);
  
  multiply(tmp, tmp1_first, tmp2_second);
  add(first, tmp, tmp2_first);
  multiply(second, tmp1_second, tmp2_second);
}

void constant_E(bigfloat & x)
{
  static long lgamma_c[] = {40, 112, 289, 709, 1675, 3866, 8759, 19569,
			    43238, 94672, 205733, 444239, 954020, 
			    2039119, 4340390, 9205076, 19458735, 
			    41014632, 86223576, 180835770, 378448767, 
			    790451976}; 

  if (bigfloat::C_E.length() >= bigfloat::digit_precision)
    {
      x.assign(bigfloat::C_E);
      x.normalize();
    }
  else
    { 
      bigint ans_first;
      bigint ans_second;
      double y = 1.0 + (double) bigfloat::binary_precision * LOG2;
      long l, h;
      
      h = 16;
      l = 0;
      do
      {
        h <<= 1;
	l++;
      } while (lgamma_c[l] < y);
      bigint_exp(0, h, ans_first, ans_second); 
      x.assign(ans_first, ans_second);
      bigfloat::C_E.assign(x);
    }
}

#else

void constant_E(bigfloat & x)
{
  double f, t;
  long i, n;
  
  if (bigfloat::C_E.length() >= bigfloat::digit_precision)
    {
      x.assign(bigfloat::C_E);
      x.normalize();
    }
  else
    { 
      n = 2;
      f = 0.0;
      t = bigfloat::binary_precision;
      while (f < t)
        {
          f += log((double) n) * INVLOG2;
          n++;
        }
      n -= 2;
      x.assign(1 + n);
      bigfloat fac(n);
      
      for (i = 1; i < n; i++)
        {
          multiply(fac.m, fac.m, n - i);
          add(x.m, x.m, fac.m);
        }
      
      divide(x, x, fac);
      bigfloat::C_E.assign(x);
    }
}

#endif

bigfloat E()
{
  bigfloat x;
  constant_E(x);
  return x;
}

bigfloat bigfloat::C_Euler;

void constant_Euler(bigfloat & y)
{
  long l, n, k, x, x2;
  bigfloat u, v, a, b, c;
  
  if (bigfloat::C_Euler.length() >= bigfloat::digit_precision)
    {
      y.assign(bigfloat::C_Euler);
      y.normalize();
    }
  else
    {
      l = bigfloat::digit_precision;
      
      x = 1 + (long) ((0.25 * (l - 3)) * (bits_per_base_digit * LOG2));
      x2 = x * x;
      n = 1 + (long) (3.591 * x);
      
      a.assign(x);
      log(u, a);
      if (u.sign() > 0)
        u.negate();
      a.assign(u);
      b.assign_one();
      v.assign_one();
      
      for (k = 1; k <= n; k++)
        {
          multiply(b, b, x2);
          divide(b, b, (k * k));
          multiply(a, a, (x2));
          divide(a, a, k);
          add(c, a, b);
          divide(a, c, k);
          add(u, u, a);
          add(v, v, b);
        }
      divide(y, u, v);
      bigfloat::C_Euler.assign(y);
    }
}

bigfloat Euler()
{
  bigfloat x;
  constant_Euler(x);
  return x;
}

void exp(bigfloat & y, const bigfloat & x)
{
  long s, f = 0, i, ex, n, t = bigfloat::binary_precision - rounding_bits;
  double a, b;
  double reg;
  long l0, l1, l2, s1, ex1;
  
  /**
   ** if abs(x) < 2^(-bigfloat::binary_precision) then x = 1 to full precision
   **/
  
  if (x.is_zero())
    {
      y.assign_one();
      return;
    }
  
  /**
   ** save the sign of x and set y = abs(x)
   **/
  
  s = x.sign();
  y.assign(x);
  if (s < 0)
    y.negate();
  
  /**
   ** remove leading zeros and calculate the length of the integer
   ** part of y
   **/
  
  ex1 = y.e + y.length() * bits_per_base_digit;
  
  a = (double) y.m.most_significant_digit();
  a /= max_base_digit_2;
  a = log(a) * INVLOG2 + ex1;
  
  /**
   ** if x was a small number choose a better convergence domain
   **/
  
  if (y.length() > 2)
    reg = 1.246294;
  else
   reg = 5.0;
  
  /**
   ** increase the precision to avoid cancelation; calculate
   ** the number f of reduction steps needed so that
   ** y / (2 ^ f) < 2 ^ (-sqrt(t / reg)). Reduce y.
   **/
  
  t = t + (long) sqrt((double) t) - (ex1 < 0) * ex1;
  b = -sqrt(t / reg);
  if (a > b)
    {
      n = 1 - (long) (reg * b);
      f = 1 + (long) (a - b);
      y.e -= f;
    }
  else
    n = 1 - (long) (t / a);
  
  /**
   ** begin of the main calculation loop
   **/
  
  bigfloat tmp(1), tmp1;
  
  /**
   ** Use increasing precision at each step. l1 is the number
   ** of correct Digits at the current point. l2 ist the precision
   ** to be set before the next iteration occurs. The code for precision
   ** increasing is adapted from the PARI system.
   **/
  
  l1 = 2;
  l2 = 1 + (t / bits_per_base_digit);
  s1 = 0;
  bigfloat::binary_precision = (l1 + 2) * bits_per_base_digit;
  
  for (i = n; i; i--)
    {
      divide(tmp, tmp, i);
      if (y.length() > l1)
        {
          tmp1.cut(y, l1 * bits_per_base_digit);
          multiply(tmp, tmp, tmp1);
        }
      else
        multiply(tmp, tmp, y);
      
      /** GP/PARI : start **/
      
      ex = tmp.e + bigfloat::binary_precision;
      s1 = s1 - ex;
      l0 = s1 / bits_per_base_digit;
      l1 += l0;
      if (l1 > l2)
        l1 = l2;
      s1 %= bits_per_base_digit;
      
      /** GP/PARI : end **/
      
      bigfloat::binary_precision = (l1 + 2) * bits_per_base_digit;
      inc(tmp);
    }
  
  /**
   ** reconstruct the result by repeated squaring
   **/
  
  if (f)
    {
      while (f--)
        square(tmp, tmp);
    }
  
  /**
   ** if x was negativ we must invert the result
   **/
  
  if (s < 0)
    {
      divide(y, 1, tmp);
    }
  else
    {
      y.assign(tmp);
      y.normalize();
    }
  
  /**
   ** set the old precision
   **/
  
  bigfloat::binary_precision = bigfloat::digit_precision * bits_per_base_digit;
}

bigfloat exp(const bigfloat & x)
{
  bigfloat y;
  exp(y, x);
  return y;
}

void floor(bigint & y, const bigfloat & x)
{
  truncate(y, x);
  
  bigfloat fraction(y);
  subtract(fraction, fraction, x);
  fraction.negate();
  
  if (fraction.m.is_lt_zero())
    dec(y);
}

void floor(bigfloat & y, const bigfloat & x)
{
  bigfloat fraction;
  if (&y == &x)
    {
      bigfloat tmp;
      truncate(tmp, x);
      subtract(fraction, x, tmp);
      y.assign(tmp);
    }
  else
    {
      truncate(y, x);
      subtract(fraction, x, y);
    } 
  if (fraction.m.is_lt_zero())
    dec(y);
}

bigfloat floor(const bigfloat & x)
{
  bigfloat y;
  truncate(y, x);
 
  bigfloat fraction;
  subtract(fraction, x, y);
 
  if (fraction.m.is_lt_zero())
    dec(y);
  return y;
}

int bigfloat::print_to_file(FILE * fp)
{
  long sl = m.bit_length() + 1;
  if (e > 0)
    sl += e;
  sl = sl / 3 + 20;
  
  char *s = new char[sl];
  if (s == 0)
    lidia_error_handler("bigfloat", "print_to_file()::no memory available");
  
  int count = bigfloat_to_string(*this, s);
  
  for (long i = 0; i <= count; i++)
    putc(s[i], fp);
  
  delete[] s;
  
  return count;
}

int bigfloat::scan_from_file(FILE * fp)
{
  static char s[100000];
  int count = 0;
  char *p = s;
  char c;
  
  do
    {
      c = getc(fp);
      count++;
    } while (isspace(c) || iscntrl(c));
  if ((c == '+') || (c == '-'))
    {
      *p++ = c;
      c = getc(fp);
      count++;
    }
  while (c == ' ')
    {
      c = getc(fp);
      count++;
    }
  if (!isdigit(c) && c != '.')
    lidia_error_handler("bigfloat", "scan_from_file()::digit/point expected");
  while (isdigit(c))
    {
      *p++ = c;
      c = getc(fp);
      count++;
    }
  if (c == '.')
    {
      *p++ = c;
      c = getc(fp);
      count++;
      while (isdigit(c))
        {
          *p++ = c;
          c = getc(fp);
          count++;
        }
    }
  while (c == ' ')
    {
      c = getc(fp);
      count++;
    }
  if (c == 'E' || c == 'e')
    {
      *p++ = c;
      c = getc(fp);
      count++;
      while (c == ' ')
        {
          c = getc(fp);
          count++;
        }
      if ((c == '+') || (c == '-'))
        {
          *p++ = c;
          c = getc(fp);
          count++;
        }
      while (c == ' ')
        {
          c = getc(fp);
          count++;
        }
      if (!isdigit(c))
        lidia_error_handler("bigfloat", "scan_from_file()::digit expected");
      while (isdigit(c))
        {
          *p++ = c;
          c = getc(fp);
          count++;
        }
    }
  ungetc(c, fp);
  count--;
  *p = '\0';
  return (int) string_to_bigfloat(s, *this);
}

void bigfloat::print(char *msg)
{
  long l = m.bit_length() + 1;
  if (e > 0) l += e;
  if (l < bigfloat::binary_precision) l = bigfloat::binary_precision + 1;
  char *s = new char[l / 3 + 20];
  bigfloat_to_string(*this, s);
  cout << msg << s << flush;
  delete[] s;
}

ostream & operator << (ostream & out, const bigfloat & a)
{
  long l = a.m.bit_length() + 1;
  if (a.e > 0) l += a.e;
  if (l < bigfloat::binary_precision) l = bigfloat::binary_precision + 1;
  char *s = new char[l / 3 + 20];
  bigfloat_to_string(a, s);
  out << s;
  delete[] s;
  return out;
}

istream & operator >> (istream & in, bigfloat & a)
{
  char s[100000];
  char *p = s;
  char c;
  
  do
    {
      in.get(c);
    } while (isspace(c));
  if ((c == '+') || (c == '-'))
    {
      *p++ = c;
      do
        {
          in.get(c);
        } while (isspace(c));
    }
  else
    {
      while (isspace(c))
        {
          in.get(c);
        }
    }
  
  if (!isdigit(c) && c != '.')
    lidia_error_handler("bigfloat", "cin::digit/point expected");
  while (isdigit(c))
    {
      *p++ = c;
      in.get(c);
    }
  
  if (c == '.')
    {
      *p++ = c;
      in.get(c);
      while (isdigit(c))
        {
          *p++ = c;
          in.get(c);
        }
    }
  while (isspace(c) && c != '\n')
    {
      in.get(c);
    }
  
  if (c == 'E' || c == 'e')
    {
      *p++ = c;
      do
        {
          in.get(c);
        } while (isspace(c));
      if ((c == '+') || (c == '-'))
        {
          *p++ = c;
          do
            {
              in.get(c);
            } while (isspace(c));
        }
      if (!isdigit(c))
        lidia_error_handler("bigfloat", "cin::digit expected");
      while (isdigit(c))
        {
          *p++ = c;
          in.get(c);
        }
    }
  in.putback(c);
  *p = '\0';
  string_to_bigfloat(s, a);
  return in;
}






void log(bigfloat & y, const bigfloat & x)
{
  long i;
  long dt = bigfloat::digit_precision;
  long t = bigfloat::binary_precision;
  
  if (x.is_le_zero())
    lidia_error_handler("bigfloat", "log()::argument is <= 0");
  
  /**
   ** if (x - 1) == 0 then the result is 0
   **/
 
  bigfloat pr(x);
  dec(pr);
  
  long ex1 = pr.e + pr.prec, ex2;
  
  if (pr.is_zero())
    {
      y.assign_zero();
      return;
    }
  
  /**
   ** set y = x; if expo(x-1) < 0 increase the precision to
   ** bigfloat::binary_precision + abs(log2(x-1)) to avoid cancellation when
   ** summing the series for arctanh()
   **/
  
  y.assign(x);
  if (ex1 < 0)
    {
      ex1 = -ex1;
      ex1 = ex1 + bits_per_base_digit - ex1 % bits_per_base_digit;
      t = (bigfloat::binary_precision += ex1);
      bigfloat::digit_precision += (ex1 / bits_per_base_digit);
    }
    /*
  else if ((ex2 = y.e + y.prec) > bigfloat::binary_precision)
    {
      ex2 = ex2 + bits_per_base_digit - ex2 % bits_per_base_digit;
      t = (bigfloat::binary_precision += ex2);
      bigfloat::digit_precision += (ex2 / bits_per_base_digit);
    }
    */
  
  /**
   ** use the identity log(1/y) = -log(y) to make y > 0
   **/
  
  long s = 0;
  if ((y.e + y.prec) < 1)
    {
      s = 1;
      divide(y, 1, x);
    }
  
  /**
   ** first reduction using the identity log(x) = 2 * log(sqrt(x))
   **/
  
  long f1 = 0;
  while ((y.e + y.prec) > 1)
    {
      f1++;
      sqrt(y, y);
    }

  /**
   ** since we are calculating the arctanh() we calculate the number of
   ** reduction/calculation steps (f/n) (using the above identity) through (y-1)
   **/
  
  long f = 0, n;
  
  add(pr, y, -1);
  
  double xc = pr.m.most_significant_digit() / max_base_digit_2;
  xc = log(xc) * INVLOG2 + (pr.e + t);
  
  long l = bigfloat::digit_precision;
  double aa = -xc, bb = 2.75 * sqrt(l / 3.0);
  
  if (aa <= bb)
    {
      n = 1 + (long) ((bits_per_base_digit >> 1) / 2.75 * sqrt(3.0 * l));
      f = 1 + (long) (bb - aa);
    }
  else
    {
      double a = aa * LOG2, b = (bits_per_base_digit >> 1) * l * LOG2;
      n = 2 + (long) (b / a);
    }
  
  /**
   ** second reduction using the identity log(x) = 2 * log(sqrt(x))
   **/
  
  for (i = 0; i < f; i++)
    sqrt(y, y);
  
  /**
   ** now calculate log((y - 1) / (y + 1))
   **/

  dec(y);
  add(pr, y, 2);
  divide(y, y, pr);

  bigfloat sum(y);
  square(y, y);

  long ex = y.e + y.prec;
  
  /**
   ** Use increasing precision at each step. l1 is the number
   ** of correct Digits at the current point. l2 ist the precision
   ** to be set before the next iteration occurs. The code for precision
   ** increasing is adapted from the PARI system.
   **/
  
  bigfloat term, tmp;
  
  n = (n << 1) + 1;
  long l0, l1 = (f + f1) / (bits_per_base_digit >> 1) + 1;
  long hlp = 0;
  long l2 = bigfloat::digit_precision - 3 + (f / bits_per_base_digit);
  bigfloat::binary_precision = (l1 + 2) * bits_per_base_digit;
  divide(pr, pr, n);
  n -= 2;
  
  for (i = n; i >= 1; i -= 2)
    {
      if (y.length() > l1)
        {
          tmp.cut(y, l1 * bits_per_base_digit);
          multiply(pr, pr, tmp);
        }
      else
        multiply(pr, pr, y);
      divide(term, 1, i);
      /** GP/PARI : start **/
      hlp -= ex;
      l0 = hlp / bits_per_base_digit;
      l1 += l0;
      if (l1 > l2)
        l1 = l2;
      hlp %= bits_per_base_digit;
      /** GP/PARI : end **/
      bigfloat::binary_precision = (l1 + 2) * bits_per_base_digit;
      add(pr, pr, term);
    }
  
  /**
   ** restore precision and reconstruct (shift); set the sign;
   ** delete local variables
   **/
  
  bigfloat::digit_precision = dt;
  bigfloat::binary_precision = dt * bits_per_base_digit;
  multiply(y, sum, pr);
  y.e += 1 + f + f1;
  if (s)
    y.negate();
}

bigfloat log(const bigfloat & x)
{
  bigfloat y;
  log(y, x);
  return y;
}

void bigfloat::multiply_by_2()
{ if (!m.is_zero()) e++; }

void multiply(bigfloat & prod, const bigfloat & x, const bigfloat & y)
{
  long ex = x.e, ey = y.e;
  multiply(prod.m, x.m, y.m);
  prod.e = ex + ey;
  int exp_error = check_overflow(prod.e, ex, ey);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("multiply", "exponent overflow");
    else
      lidia_error_handler("multiply", "exponent undeflow.");
  }
  prod.prec = prod.m.bit_length();
  prod.flag() = NotExact;
  prod.normalize();
}

void multiply(bigfloat & prod, const bigfloat & x, long i)
{
  multiply(prod.m, x.m, i);
  prod.e = x.e;
  prod.prec = prod.m.bit_length();
  prod.flag() = NotExact;
  prod.normalize();
}

void multiply(bigfloat & prod, long i, const bigfloat & x)
{
  multiply(prod.m, x.m, i);
  prod.e = x.e;
  prod.prec = prod.m.bit_length();
  prod.flag() = NotExact;
  prod.normalize();
}

void square(bigfloat & c, const bigfloat & a)
{
  long ea = a.e;
  square(c.m, a.m);
  c.e = 2 * ea;
  int exp_error = check_overflow(c.e, ea, ea);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("square", "exponent overflow");
    else
      lidia_error_handler("square", "exponent undeflow.");
  }
  c.prec = c.m.bit_length();
  c.flag() = NotExact;
  c.normalize();
}
 
bigfloat square(const bigfloat & a)
{
  bigfloat c;
  square(c, a);
  return c;
}

bigfloat operator * (const bigfloat & a, const bigfloat & b)
{
  bigfloat c;
  multiply(c, a, b);
  return c;
}

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

int
bigfloat::leading_zeros() const
{
  int l = m.bit_length();
  l %= bits_per_base_digit;
  return bits_per_base_digit - l;
}

void
bigfloat::
base_digit_normalize()
{
  if (!m.is_zero())
    {
      int i = this->leading_zeros();
      shift_left(m, m, i);
      e -= i;
      prec += i;
    }
}

void bigfloat::normalize()
{
  // exact numbers are not normalized
  if (this->is_exact())
    return;

  long dif = prec - bigfloat::binary_precision;
  bigint tmp;

  // length of the number is greater than current precision
  if (dif > 0) 
    {
      // find and store the last bit before cutting
      int addt;
      dif--;
      shift_right(tmp, m, dif);
      e += dif;
      addt = tmp.is_odd();

      // cut
      tmp.divide_by_2();
      e++;

      // if the last bit is equal to 1
      if (addt)
        {
          int lx = tmp.bit_length();

          // round
          switch (bigfloat::rounding_mode)
            {
            case MP_RND:
              if (tmp.is_odd())
                {
                  if (tmp.is_lt_zero())
                    dec(tmp);
                  else
                    inc(tmp);
                }
              break;
            case MP_RND_UP:
	      if (tmp.is_gt_zero())
                inc(tmp);
              break;
            case MP_RND_DOWN:
	      if (tmp.is_lt_zero())
                dec(tmp);
              break;
            }
          if (tmp.bit_length() > lx)
            {
	      bigint tmp2;
	      shift_right(tmp2, tmp, 1);
	      swap(tmp2, tmp);
              e++;
            }
        }
        swap(tmp, m);
    }
  // length of the number is less than current precision; shift left
  else
    {
        dif = -dif;
        shift_left(m, m, dif);
        e -= dif;
    }
  prec = bigfloat::binary_precision;
}

void bigfloat::cut(long l)
{
  if (prec <= l) return;
  long dif = prec - l;
  shift_right(m, m, dif);
  e += dif;
  prec -= dif;
}

void bigfloat::cut(bigfloat &x, long l)
{
  this->flag() = x.flag(); 
  if (x.prec <= l)
  {
     m.assign(x.m);
     e = x.e;
     prec = x.prec;
     return;
  }
  long dif = x.prec - l;
  shift_right(m, x.m, dif);
  e = x.e + dif;
  prec = x.prec - dif;
}

bigfloat bigfloat::C_Pi;

void constant_Pi(bigfloat & x)
{
  long l, n, n1, hprec = bigfloat::binary_precision, prec;
  double alpha;
  
  const long k1 = 545140134;
  const long k2 = 13591409;
  const long k3 = 640320;
  const double alpha2 = (47.110413 / bits_per_base_digit);
  
  prec = bigfloat::digit_precision;
  if (bigfloat::C_Pi.length() >= prec)
    {
      x.assign(bigfloat::C_Pi);
      x.normalize();
    }
  else
    {
      bigfloat p1(k1), p2, p3, h;
      
      prec -= 3;
      n = (long)(1 + prec * (1 / alpha2));
      n1 = 6 * n - 1;
      
      multiply(h, p1, n);
      add(p2, h, k2);
      p1.assign(p2);
      
      if (prec >= 4)
        l = 4;
      else
        l = prec;
      alpha = l;
      p1.cut(l * bits_per_base_digit);
      
      while (n)
        {
          if (n > 1290)
            {
              if (n1 > 46340)
                {
                  multiply(h, p1, n1 - 2);
                  multiply(h, h, n1);
                  multiply(h, h, n1 - 4);
                  divide(h, h, (n * n));
                  divide(p3, h, n);
                }
              else
                {
                  multiply(h, p1, (n1 * (n1 - 2)));
                  multiply(h, h, (n1 - 4));
                  divide(h, h, (n * n));
                  divide(p3, h, n);
                }
            }
          else
            {
              multiply(h, p1, (n1 * (n1 - 2)));
              multiply(h, h, (n1 - 4));
              divide(p3, h, (n * n * n));
            }
          divide(p3, p3, 100100025);
          divide(p3, p3, 327843840);
          subtract(p2, p2, k1);
          subtract(p1, p2, p3);
          alpha += alpha2;
          l = (long) (1 + alpha);
          if (l > prec)
            l = prec;
          p1.cut(l * bits_per_base_digit);
          n--;
          n1 -= 6;
        }
      divide(x, 53360, p1);
      h.assign(k3);
      sqrt(h, h);
      bigfloat::binary_precision = hprec;
      multiply(x, x, h);
      bigfloat::C_Pi.assign(x);
    }
}

bigfloat Pi()
{
  bigfloat x;
  constant_Pi(x);
  return x;
}

void power(bigfloat & z, const bigfloat & x, const bigfloat & y)
{
  if (x.is_zero())
    {
      z.assign_zero();
      return;
    }
  if (y.is_zero())
    {
      z.assign_one();
      return;
    }
  if (x.sign() > 0)
    {
      bigfloat tmp;
      log(tmp, x);
      multiply(z, y, tmp);
      exp(z, z);
    }
  else
    {
      bigint tmp;
      truncate(tmp, y);
      power(z, x, tmp);
      if (tmp.is_odd())
        z.negate();
    }
}

void power(bigfloat & x, const bigfloat & a, long n)
{
  if (a.is_zero())
    {
      x.assign_zero();
      return;
    }
  if (n == 0)
    {
      x.assign_one();
      return;
    }
  
  bigfloat tmp;
  long m;
  
  if (n < 0)
    {
      m = -n;
      divide(tmp, 1, a);
    }
  else
    {
      m = n;
      tmp.assign(a);
    }
  
  x.assign_one();
  
  if (m & 1)
    multiply(x, x, tmp);
  m >>= 1;
  
  while (m > 0)
    {
      square(tmp, tmp);
      if (m & 1)
        multiply(x, x, tmp);
      m >>= 1;
    }
}

bigfloat power(const bigfloat & x, const bigfloat & y)
{
  bigfloat z;
  power(z, x, y);
  return z;
}
 
bigfloat power(const bigfloat & x, int n)
{
  bigfloat z;
  power(z, x, n);
  return z;
}

long bigfloat::decimal_precision = (long) ceil(2 * bits_per_base_digit * L2B10);
long bigfloat::binary_precision = 5 * bits_per_base_digit;
long bigfloat::digit_precision = 5;

void bigfloat::precision(long t)
{
  if (t > 0)
    {
      bigfloat::decimal_precision = t;
      bigfloat::digit_precision = 3 + (long) (ceil((t / (L2B10 * bits_per_base_digit))));
      bigfloat::binary_precision = bits_per_base_digit * bigfloat::digit_precision;
    }
  else
    {
      bigfloat::decimal_precision = (long) ceil(2*bits_per_base_digit * L2B10);
      bigfloat::binary_precision = 5 * bits_per_base_digit;
      bigfloat::digit_precision = 5;
    }
}

void bigfloat::set_precision(long t)
{
  if (t > 0)
    {
      bigfloat::decimal_precision = t;
      bigfloat::digit_precision = 3 + (long) (ceil((t / (L2B10 * bits_per_base_digit))));
      bigfloat::binary_precision = bits_per_base_digit * bigfloat::digit_precision;
    }
  else
    {
      bigfloat::decimal_precision = (long) ceil(2*bits_per_base_digit * L2B10);
      bigfloat::binary_precision = 5 * bits_per_base_digit;
      bigfloat::digit_precision = 5;
    }
}

long bigfloat::get_precision()
{
  return bigfloat::decimal_precision;
}

int bigfloat::rounding_mode = MP_RND;

void bigfloat::mode(int m)
{
  switch (m)
    {
    case MP_TRUNC:
      bigfloat::rounding_mode = MP_TRUNC;
      break;
    case MP_RND:
      bigfloat::rounding_mode = MP_RND;
      break;
    case MP_RND_UP:
      bigfloat::rounding_mode = MP_RND_UP;
      break;
    case MP_RND_DOWN:
      bigfloat::rounding_mode = MP_RND_DOWN;
      break;
    default:
      lidia_error_handler("bigfloat", "mode()::invalid rounding mode");
      break;
    }
}

int bigfloat::get_mode()
{
  return bigfloat::rounding_mode;
}

void round(bigint & y, const bigfloat & x)
{
  if (x.is_zero())
    {
      y.assign_zero();
      return;
    }

  bigfloat half_plus_x(0.5);
  add(half_plus_x, half_plus_x, x);
  floor(y, half_plus_x);

/*
  y.assign(x.m); 
  long ey = x.e;
  
  if (ey >= 0)
    shift_left(y, y, ey);
  else
    {
      int s = y.is_lt_zero(); 
      ey = -ey;
      shift_right(y, y, (ey - 1));
      long fract = y.is_odd();
      y.divide_by_2();
      if (fract)
	{
          if (s)
	    dec(y);
          else
            inc(y);
        }
    }
*/

}

void round(bigfloat & y, const bigfloat & x)
{
  if (x.is_zero())
    {
      y.assign_zero();
      return;
    }

  bigfloat half_plus_x(0.5);
  add(half_plus_x, half_plus_x, x);
  floor(y, half_plus_x);

/*
  if (&y != &x)
    y.assign(x);
  
  long ey = y.e;

  if (ey >= 0)
    shift_left(y.m, y.m, ey);
  else
    {
      int s = y.is_lt_zero(); 
      shift_right(y.m, y.m, (-ey - 1));
      long fract = y.m.is_odd();
      y.m.divide_by_2();
      if (fract)
	{
          if (s)
	    dec(y.m);
          else
            inc(y.m);
        }
    }
  y.e = 0;
  y.prec += ey; 
  y.normalize();
*/

}

bigfloat round(const bigfloat & x)
{
  bigfloat y(x);
  round(y, x);
  return y;
}

void shift_left(bigfloat & res, const bigfloat & x, long i)
{
  if (&res != &x)
    res.assign(x);
  res.e = x.e + i;
}

void shift_right(bigfloat & res, const bigfloat & x, long i)
{
  if (&res != &x)
    res.assign(x);
  res.e = x.e - i;
}

bigfloat operator << (const bigfloat & a, unsigned long ui)
{
  bigfloat c;
  shift_left(c, a, ui);
  return c;
}
 
bigfloat operator >> (const bigfloat & a, unsigned long ui)
{
  bigfloat c;
  shift_right(c, a, ui);
  return c;
}

bigfloat & bigfloat::operator <<= (unsigned long ui)
{
  *this = *this << ui;
  return *this;
}
 
bigfloat & bigfloat::operator >>= (unsigned long ui)
{
  *this = *this >> ui;
  return *this;
}

void sin(bigfloat & y, const bigfloat & x)
{
  bigfloat p0;
  long ex, t = bigfloat::digit_precision;
  
  ex = x.e + x.prec;
  if (ex <= 0)
    {
      int xs = x.sign();
      cos(y, x);
      p0.assign(y);
      inc(p0);
      
      if (bigfloat::binary_precision < (y.length() * bits_per_base_digit))
        bigfloat::binary_precision = y.length() * bits_per_base_digit;
      ex = -ex;
      bigfloat::binary_precision += 2 * (ex + bits_per_base_digit
                                         - (ex % bits_per_base_digit));
      bigfloat::digit_precision = bigfloat::binary_precision / bits_per_base_digit;
      dec(y);
      y.negate();
      bigfloat::digit_precision = t;
      bigfloat::binary_precision = t * bits_per_base_digit;
      
      multiply(y, y, p0);
      sqrt(y, y);
      if (xs < 0)
	y.negate();
      return;
    }
  
  constant_Pi(p0);
  p0.e--;
  add(y, x, p0);
  cos(y, y);
  y.negate();
 
  bigfloat::digit_precision = t;
  bigfloat::binary_precision = t * bits_per_base_digit;
}

bigfloat sin(const bigfloat & x)
{
  bigfloat y;
  sin(y, x);
  return y;
}

void sinh(bigfloat & y, const bigfloat & x)
{
  bigfloat tmp(x);
  exp(y, tmp);
  divide(tmp, 1, y);
  subtract(y, y, tmp);
  y.e--;
}

bigfloat sinh(const bigfloat & x)
{
  bigfloat y;
  sinh(y, x);
  return y;
}

void sqrt(bigfloat & y, const bigfloat & x)
{
  
  if (x.is_lt_zero())
    lidia_error_handler("bigfloat", "sqrt()::argument must be >= 0");
  if (x.is_zero())
    y.assign_zero();
  else
    {
      long t = bigfloat::binary_precision, l = bigfloat::digit_precision;
      long eps, ex, i, n, l1, l0, l2, l02;
      bigfloat p1, p2, p3;
    
      p1.assign(x);
      
      /**
       ** calculate the exponent ex of the result. If ex is odd
       ** multiply the first approximation by 2
       **/
      
      ex = x.e + x.prec;
      eps = ex % 2;
      ex /= 2;
      
      if (eps < 0)
        {
          eps += 2;
          ex--;
        }
      
      /**
       ** normalize before calculating the start value for
       ** the newton iteration. Set p1 = mantissa(x). Then
       ** take the square root of the leading words and store
       ** it in p2. Our approximation is at (bits_per_double - 1)
       ** bits correct, so 1 + log2(digit_precision) steps are sufficient
       **/
      
      p1.e = -(p1.length() * bits_per_base_digit) + eps;
      
      double beta = (double) p1.m.most_significant_digit();
      beta /= max_base_digit_2;
      beta = sqrt((eps + 1) * beta);
      y.assign(beta);
      
      l -= 2;
      n = 1 + (long) (log((double) l) * INVLOG2);
      
      /**
       ** Use increasing precision at each step. l1 is the number
       ** of correct Digits at the current point. l2 ist the precision
       ** to be set before the next iteration occurs. The code for
       ** precision increasing is adapted from the PARI system.
       **/
      
      l1 = 1;
      l2 = 3;
      for (i = 1; i <= n; i++)
        {
          /** from GP/PARI : start **/
          
          l0 = l1 << 1;
          if (l0 <= l)
            {
              l2 += l1;
              
              /**
               ** Use only the first l0 + 2 Digits of p3. Remember
               ** that the newton iteration is self correcting
               **/
              
              l02 = l0 + 2;
              p3.cut(l02 * bits_per_base_digit);
              
              /**
               ** Use only the first l0 + 2 Digits of y. Remember
               ** that the newton iteration is self correcting
               **/
              
              y.cut(l02 * bits_per_base_digit);
              l1 = l0;
            }
          else
            {
              l2 += -l1 + l + 1;
              l1 = l + 1;
            }
          bigfloat::binary_precision = l2 * bits_per_base_digit;
          
          /** from GP/PARI : end **/
          
          /**
           ** Use only the first l2 Digits of p1. Remember
           ** that the newton iteration is self correcting
           **/
         
          if (p1.length() > l2)
           {
             p2.cut(p1, l2 * bits_per_base_digit);
             divide(p3, p2, y);
           }
          else
            divide(p3, p1, y);
          add(y, y, p3);
          y.e--;
        }
      
      /**
       ** restore the precision and the exponent and
       ** normalize the result
       **/
      
      bigfloat::binary_precision = t;
      y.e += ex;
      y.normalize();
    }
}

bigfloat sqrt(const bigfloat & x)
{
  bigfloat y;
  sqrt(y, x);
  return y;
}

int string_to_bigfloat(char *s, bigfloat & n)
{
  int mi, ei, eii, count;
  int j, l = strlen(s), expo = 0;
  char *mant, *exs, c;
  
  /**
   ** allocate temporary space for the mantissa
   ** and the short exponent; clear both arrays
   **/
  
  mant = new char[l + 2];
  exs = new char[10];
  for (mi = 0; mi <= l + 1; mi++)
    mant[mi] = '\0';
  for (ei = 0; ei < 10; ei++)
    exs[ei] = '\0';
  
  /**
   ** mi    --> counts the digits of the mantissa (including sign)
   ** ei    --> counts the digits of the exponent (including sign)
   ** eii   --> counts the digits after the decimal point
   ** count --> is the counter on the array s
   **/
  
  mi = 0;
  ei = 0;
  eii = 0;
  count = 0;
  
  /*** remove leading spaces ***/
  c = s[count];
  while (isspace(c) || iscntrl(c))
    {
      count++;
      c = s[count];
    }
  
  /*** mantissa mign ***/
  if (c == '-')
    {
      mant[mi] = c;
      count++;
      mi++;
    }
  else if (c == '+')
    count++;
  
  /*** mart x ***/
  c = s[count];
  while (isdigit(c))
    {
      mant[mi] = c;
      count++;
      mi++;
      c = s[count];
    }
  
  /*** radixpoint ***/
  if (c == '.')
    {
      count++;
      c = s[count];
    }
  
  /*** part y ***/
  while (isdigit(c))
    {
      mant[mi] = c;
      count++;
      mi++;
      eii--;
      c = s[count];
    }
  
  /*** exponent ***/
  if (c == 'E' || c == 'e')
    {
      count++;
      c = s[count];
      
      /*** sign ***/
      if (c == '-')
        {
          exs[ei] = c;
          count++;
          ei++;
        }
      else if (c == '+')
        count++;
      
      /*** digits ***/
      c = s[count];
      while (isdigit(c))
        {
          exs[ei] = c;
          count++;
          ei++;
          c = s[count];
        }
    }
  
  /*** store mantissa and exponent in basis 10 ***/
  string_to_bigint(mant, n.m);
  expo = (int)(atol(exs) + eii);
  n.e = expo;

  /*** if exponent >= 0 we have an integer ***/
  if (expo >= 0)
    {
      /*** calculate n * 10^expo ***/
      long q = expo, r = q % log_max_pow_10;
      for (j = 0; j < r; j++)
        multiply(n.m, n.m, 10); 
      q -= r;
      while (q > 0)
        {
          multiply(n.m, n.m, max_pow_10);
          q -= log_max_pow_10;
        }
      n.e = 0;
      n.prec = n.m.bit_length();
      n.base_digit_normalize();
      n.flag() = Exact;
    }
  else
    {
      /*** we must convert a real ***/
      bigint tmp(1);
      long h, j, q, r;
      
      h = (long) ((strlen(mant) + (-expo)) / L2B10);
      q = -expo;
      r = q % log_max_pow_10;
      for (j = 0; j < r; j++)
        multiply(tmp, tmp, 10);
      q -= r;
      while (q > 0)
        {
          multiply(tmp, tmp, max_pow_10);
          q -= log_max_pow_10;
        }
      if (h < bigfloat::binary_precision)
        h = bigfloat::binary_precision;
      h <<= 1;
      shift_left(n.m, n.m, h);
      divide(n.m, n.m, tmp);
      n.e = -h;
      n.prec = n.m.bit_length();
      n.flag() = NotExact;
      n.normalize();
    }
  delete[] mant;
  delete[] exs;
  return l;
}

int bigfloat_to_string(const bigfloat & n, char *s)
{
  if (n.is_zero())
  {
    s[0] = '0';
    s[1] = '\0';
    return 1;
  }

  /**
   ** Normalize the result, so that we have a uniform output
   **/

  bigfloat h(n);
  h.flag() = NotExact;
  h.normalize();

  /**
   ** get the exponent of h and its sign
   **/

  long e1 = h.e, sign_exp = 0, e2;
  if (e1 == 0)
    return (int) bigint_to_string(h.m, s);
  if (e1 > 0)
    sign_exp = 1;
  else
    sign_exp = -1;

  /** set e1 = abs(e1), e2 = e1 * log10(2)
   ** case 1:
   **
   ** e1 > 0 <==> sign_exp = 1
   **
   ** binary_mantissa(h) * 2^e1 = decimal_mantissa(h) * 10^e2, i.e
   ** decimal_mantissa(h) = binary_mantissa(h) * 2^e1 / 10^e2
   **
   ** e1 < 0 <==> sign_exp = -1
   **
   ** binary_mantissa(h) * 2^e1 = decimal_mantissa(h) * 10^e2. Now
   ** e1 < 0 ==> binary_mantissa(h) * 2^e1 = binary_mantissa(h) / 2^abs(e1)
   ** e2 < 0 ==> decimal_mantissa(h) * 10^e2 = decimal_mantissa(h) / 10^abs(e2)
   **
   ** ==> binary_mantissa(h) / 2^abs(e1) = decimal_mantissa(h) / 10^abs(e2)
   ** ==> decimal_mantissa(h) = binary_mantissa(h) * 10^abs(e2) / 2^abs(e1)
   **
   **/

  e1 = sign_exp * e1;
  e2 = (long) (e1 * L2B10);

  long q = e2, r = q % log_max_pow_10, i;
  if (sign_exp == 1)
  {
    shift_left(h.m, h.m, e1);
    for (i = 0; i < r; i++)
      divide(h.m, h.m, 10);
    q -= r;
    while (q > 0)
    {
      divide(h.m, h.m, max_pow_10);
      q -= log_max_pow_10;
    }
  }
  else
  {
    for (i = 0; i < r; i++)
      multiply(h.m, h.m, 10);
    q -= r;
    while (q > 0)
    {
      multiply(h.m, h.m, max_pow_10);
      q -= log_max_pow_10;
    }
    shift_right(h.m, h.m, e1);
  }
  h.e = sign_exp * e2;

  /** get the sign ms and convert the decimal_mantissa(h) to a string
   ** Adjust the decimal exponent appropriately
   **/
  long ms = 0;
  if (h.m.is_lt_zero())
  {
    ms = 1;
    h.m.negate();
  }
  bigint_to_string(h.m, s);
  int sl = strlen(s);
  e1 = sl + sign_exp * e2;

  /**
   ** Now we are going to cut the output to have at most
   ** bigfloat::decimal_precision places. We do this fi
   ** and only if the string has a length sl greater than
   ** the current decimal precision.
   **/

  long cut;
  char ch, E[10];

  i = 0;
  if (sl > bigfloat::decimal_precision)
  {
    /**
     ** round by taking the last two places in account. This
     ** probably causes a carry (i = 1).
     **/
    cut = bigfloat::decimal_precision;
    ch = s[cut + 1];
    if (ch > '5')
      s[cut] += (s[cut] > '5');
    ch = s[cut];
    if (ch >= '5')
      i = 1;
    else
      i = 0;
    s[cut] = '\0';
    if (i)
    {
      /**
       ** We have a carry which add stringbolically
       **/
      cut--;
      while (cut >= 0 && i)
      {
	if (s[cut] == '9')
	{
	  s[cut] = '0';
	  cut--;
	  i = 1;
	}
	else
	{
	  s[cut] += 1;
	  i = 0;
	}
      }
      if (cut == -1 && i)
      {
	int m, l = strlen(s) + 1;
	for (m = l; m > 0; m--)
	  s[m] = s[m - 1];
	s[0] = '1';
      }
    }
    /**
     ** Adjust the exponent since there might have been a carry
     **/
    sl = (int) (strlen(s) + i);
    e1 += (sl > bigfloat::decimal_precision);
  }

  /**
   ** remove trailing zeroes to make ouput shorter
   **/

  i = sl - 1;
  while (s[i] == '0' && i > (e1 - 1))
  {
    s[i] = '\0';
    i--;
  }

  /**
   ** check if the decimal point lies at the end of the right side
   ** If true, do not print it.
   **/

  if ((i + 1) == e1)
  {
    s[i + 1] = '\0';
    sl = (int) (i + 1);
    if (ms)
    {
      for (i = sl; i >= 0; i--)
	s[i + 1] = s[i];
      s[0] = '-';
      sl++;
    }
    return (int) strlen(s);
  }

  /**
   ** if the decimal point lies in the middle of the number
   ** shift the fractional part of the number so that we
   ** get a character to put the decimal point in it.
   ** Else print 0. and the mantissa of the number. If we have
   ** an exponent != 0, print it in schientific notation.
   **/
  sl = strlen(s);
  if (e1 > 0 && e1 < sl)
  {
    for (i = sl; i >= e1; i--)
    {
      s[i + 1] = s[i];
    }
    s[e1] = '.';
    sl++;
    if (ms)
    {
      for (i = sl; i >= 0; i--)
	s[i + 1] = s[i];
      s[0] = '-';
      sl++;
    }
    return (int) strlen(s);
  }
  else
  {
    for (i = sl + 3 - (!ms); i >= 3 - (!ms); i--)
      s[i] = s[i - 3 + (!ms)];
    if (ms)
    {
      s[0] = '-';
      s[1] = '0';
      s[2] = '.';
      sl += 3;
    }
    else
    {
      s[0] = '0';
      s[1] = '.';
      sl += 2;
    }
    if (e1 != 0)
    {
      s[sl] = 'e';
      s[++sl] = '\0';
      sprintf(E, "%ld", e1);
      strcat(s, E);
    }
  }
  return (int) strlen(s);
}

void negate(bigfloat & x, const bigfloat & y)
{
  x.assign(y);
  x.negate();
}

void subtract(bigfloat & dif, const bigfloat & x, const bigfloat & y)
{
  const bigfloat *px, *py;
  long log_x = x.e + x.prec, log_y = y.e + y.prec;
  long log_dif = log_x - log_y;
  long ed, e1, e2, i;
 
 
  i = log_dif;
  if (log_dif < 0)
    {
      px = &y;
      py = &x;
      log_dif = -log_dif;
    }
  else
    {
      px = &x;
      py = &y;
    }
 
 
  e1 = px->e;
  e2 = py->e;
  
  int exp_error = check_overflow(ed, e1, -e2);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("subtract", "exponent overflow");
    else
      lidia_error_handler("subtract", "exponent undeflow.");
  }
  
  if (log_dif > (bigfloat::binary_precision + rounding_bits))
  {
    dif.assign(*px);
    if (i < 0)
      dif.m.negate();
    return;
  }
 
  if (ed > 0)
  {
    bigint tmp(px->m);
    shift_left(tmp, tmp, ed);
    subtract(dif.m, tmp, py->m);
    dif.e = e2;
  }
  else
  { 
    bigint tmp(py->m);
    shift_left(tmp, tmp, -ed);
    subtract(dif.m, px->m, tmp);
    dif.e = e1;
  }
  if (i < 0)
    dif.m.negate();
  dif.prec = dif.m.bit_length();
  dif.flag() = NotExact;
  dif.normalize();
}

void subtract(bigfloat & dif, const bigfloat & x, long i)
{
  bigfloat y(i);
  const bigfloat *px, *py;
  long log_x = x.e + x.prec, log_y = y.e + y.prec;
  long log_dif = log_x - log_y;
  long ed, e1, e2, j;
  
  j = log_dif;
  if (log_dif < 0)
    {
      px = &y;
      py = &x;
      log_dif = -log_dif;
    }
  else
    {
      px = &x;
      py = &y;
    }
  

  e1 = px->e;
  e2 = py->e;
  
  int exp_error = check_overflow(ed, e1, -e2);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("subtract", "exponent overflow");
    else
      lidia_error_handler("subtract", "exponent undeflow.");
  }
  
  if (log_dif > (bigfloat::binary_precision + rounding_bits))
  {
    dif.assign(*px);
    if (j < 0)
      dif.m.negate();
    return;
  }
 
  if (ed > 0)
  {
    bigint tmp(px->m);
    shift_left(tmp, tmp, ed);
    subtract(dif.m, tmp, py->m);
    dif.e = e2;
  }
  else
  {
    bigint tmp(py->m);
    shift_left(tmp, tmp, -ed);
    subtract(dif.m, px->m, tmp);
    dif.e = e1;
  }
  if (j < 0)
    dif.m.negate();
  dif.prec = dif.m.bit_length();
  dif.flag() = NotExact;
  dif.normalize();
}

void subtract(bigfloat & dif, long i, const bigfloat & y)
{
  bigfloat x(i);
  const bigfloat *px, *py;
  long log_x = x.e + x.prec, log_y = y.e + y.prec;
  long log_dif = log_x - log_y;
  long ed, e1, e2, j;
  
  j = log_dif;
  if (log_dif < 0)
    {
      px = &y;
      py = &x;
      log_dif = -log_dif;
    }
  else
    {
      px = &x;
      py = &y;
    }
  
  e1 = px->e;
  e2 = py->e;
  
  int exp_error = check_overflow(ed, e1, -e2);
  if (exp_error)
  {
    if (exp_error > 0)
      lidia_error_handler("subtract", "exponent overflow");
    else
      lidia_error_handler("subtract", "exponent undeflow.");
  }
  
  if (log_dif > (bigfloat::binary_precision + rounding_bits))
  {
    dif.assign(*px);
    if (j < 0)
      dif.m.negate();
    return;
  }
  
  if (ed > 0)
  {
    bigint tmp(px->m);
    shift_left(tmp, tmp, ed);
    subtract(dif.m, tmp, py->m);
    dif.e = e2;
  }
  else
  {
    bigint tmp(py->m);
    shift_left(tmp, tmp, -ed);
    subtract(dif.m, px->m, tmp);
    dif.e = e1;
  }
  if (j < 0)
    dif.m.negate();
  dif.prec = dif.m.bit_length();
  dif.flag() = NotExact;
  dif.normalize();
}

void dec(bigfloat & dif)
{
  bigfloat tmp(1);
  subtract(dif, dif, tmp);
}

bigfloat operator - (const bigfloat & a)
{
  bigfloat c(a);
  c.negate();
  return c;
}

bigfloat operator - (const bigfloat & a, const bigfloat & b)
{
  bigfloat c;
  subtract(c, a, b);
  return c;
}

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

bigfloat & bigfloat::operator-- ()
{
  dec(*this);
  return *this;
}

void tan(bigfloat & y, const bigfloat & x)
{
  long s = 0, ex, t = bigfloat::digit_precision;
  
  ex = x.e + x.prec;
  if (ex < 0)
    {
      ex = -ex;
      bigfloat::binary_precision += 4 * (ex + bits_per_base_digit
                                         - ex % bits_per_base_digit);
      bigfloat::digit_precision = bigfloat::binary_precision / bits_per_base_digit;
    }
  bigfloat p0(x);
  if (p0.is_lt_zero())
    {
      s = 1;
      p0.negate();
    }
  constant_Pi(y);
  if (p0.compare(bigfloat::C_Pi) >= 0)
    {
      divide(y, p0, bigfloat::C_Pi);
      truncate(y, y);
      multiply(y, y, bigfloat::C_Pi);
      subtract(p0, p0, y);
    }
  cos(p0, p0);
  square(y, p0);
  y.negate();
  inc(y);
  sqrt(y, y);
  divide(y, y, p0);
  if (s)
    y.negate();
  bigfloat::digit_precision = t;
  bigfloat::binary_precision = t * bits_per_base_digit;
}

bigfloat tan(const bigfloat & x)
{
  bigfloat y;
  tan(y, x);
  return y;
}

void tanh(bigfloat & y, const bigfloat & x)
{
  bigfloat tmp1, tmp2;
  exp(y, x);
  divide(tmp1, 1, y);
  add(tmp2, y, tmp1);
  subtract(y, y, tmp1);
  divide(y, y, tmp2);
}

bigfloat tanh(const bigfloat & x)
{
  bigfloat y;
  tanh(y, x);
  return y;
}

void truncate(bigint & y, const bigfloat & x)
{
  long ex = x.e;
  
  if (ex >= 0)
    shift_left(y, x.m, ex);
  else
    shift_right(y, x.m, -ex);
}

void truncate(bigfloat & y, const bigfloat & x)
{
  long ex = x.e;
  
  if (ex >= 0)
    shift_left(y.m, x.m, ex);
  else
    shift_right(y.m, x.m, -ex);
  y.e = 0;
  y.prec = y.m.bit_length(); 
  y.normalize();
}

bigfloat truncate(const bigfloat & x)
{
  bigfloat y(x);
  truncate(y, x);
  return y;
}

int bigfloat::sign() const
{
  return m.sign();
}
 
long bigfloat::exponent() const
{
  return e;
}
 
bigint bigfloat::mantissa() const
{
  return m;
}
 
lidia_size_t bigfloat::length() const
{
  return m.length();
}
 
lidia_size_t bigfloat::bit_length() const
{
  return prec;
}
 
void bigfloat::absolute_value()
{
  m.absolute_value();
}
 
void bigfloat::negate()
{
  m.negate();
}

void swap(bigfloat & c, bigfloat & a)
{
  long t = a.e;
  a.e = c.e;
  c.e = t;

  t = a.prec;
  a.prec = c.prec;
  c.prec = t;

  bigfloat_flag f;
  f.flag() = a.flag();
  a.flag() = c.flag();
  c.flag() = f.flag();
  swap(c.m, a.m);
}

int sign(const bigfloat & x)
{
  return x.m.sign();
}
 
long exponent(const bigfloat & x)
{
  return x.e;
}
 
bigint mantissa(const bigfloat & x)
{
  bigint y(x.mantissa());
  return y;
}
 
bigfloat abs(const bigfloat & x)
{
  bigfloat y(x);
  y.absolute_value();
  return y;
}

