//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigrational.c 
// Author      : Thomas Papanikolaou (TP)
// Last change : TP, Jan 29 1995, initial version 
//               VM, Feb 7 1995, modified operators +, -, *, /
//                   to use the procedures add(), subtract(), ...
//                   Added a space ' ' in the output routine.
//		 MM, Sep 11 1996, added 
//		     assign(const bigint&,const bigint&)
//

#include <LiDIA/bigrational.h>

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

bigrational::bigrational()
{
  num.assign_zero();
  den.assign_one();
}

bigrational::bigrational(int i)
{
  num = i;
  den.assign_one();
}

bigrational::bigrational(long l)
{
  num = l;
  den.assign_one();
}

bigrational::bigrational(unsigned long ul)
{
  num = ul;
  den.assign_one();
}

bigrational::bigrational(double d)
{
  int exponent, sign = (d < 0.0);
  double fraction;
  base_digit m;
  long ndigits = 0;

  num.assign_zero();
  den.assign_one();

  if (d)
  {
    if (sign)
      d = -d;

    fraction = frexp(d, &exponent);

    while (fraction != 0)
    {
      shift_left(num, num, bigint::bits_per_digit()); 
      fraction = ldexp(fraction, bigint::bits_per_digit());
      m = (base_digit) fraction;
      fraction -= m;
      ndigits++;
      add(num, num, bigint(m));
    }
    // the product on the right side is never > MAXINT
    exponent -= (int)(ndigits * bigint::bits_per_digit());
    if (exponent < 0)
      shift_left(den, den, -exponent);
    else
      shift_left(num, num, exponent);
    if (sign)
      num.negate();
  }
  this->normalize();
}

bigrational::bigrational(const bigint & n)
{
  num.assign(n);
  den.assign_one();
}

bigrational::bigrational(const bigint & n, const bigint & d)
{
  if (d.is_zero())
    lidia_error_handler("bigrational", "constructor(n,d)::division by zero.");
  num.assign(n);
  den.assign(d);
  this->normalize();
}

bigrational::bigrational(const bigrational & a)
{
  num.assign(a.num);
  den.assign(a.den);
}

bigrational::~bigrational()
{
}

/**
 ** inline member functions
 **/

int bigrational::
sign() const
{
  return num.sign();
}

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

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

bool bigrational::
is_zero() const
{
  if (num.sign() == 0) return true;
  return false;
}

bool bigrational::
is_gt_zero() const
{
  if (num.sign() > 0) return true;
  return false;
}

bool bigrational::
is_ge_zero() const
{
  if (num.sign() >= 0) return true;
  return false;
}

bool bigrational::
is_lt_zero() const
{
  if (num.sign() < 0) return true;
  return false;
}

bool bigrational::
is_le_zero() const
{
  if (num.sign() <= 0) return true;
  return false;
}

bool bigrational::
is_one() const
{
  if (num.is_one() && den.is_one()) return true;
  return false;
}

bool bigrational::
intify(int &i) const
{
  bigint I;
  divide(I, num, den);
  return I.intify(i);
}

bool bigrational::
longify(long &i) const
{
  bigint I;
  divide(I, num, den);
  return I.longify(i);
}

int bigrational::
abs_compare(const bigrational & a) const
{
  bigint r1, r2;
  multiply(r1, num, a.den);
  multiply(r2, den, a.num);
  return r1.abs_compare(r2);
}

int bigrational::
compare(const bigrational & a) const
{
  bigint r1, r2;
  multiply(r1, num, a.den);
  multiply(r2, den, a.num);
  return r1.compare(r2);
}

void bigrational::
normalize()
{
  int s = den.sign();
  if (s == 0)
    lidia_error_handler("bigrational", "normalize()::division by zero.");
  if (s < 0)
  {
    num.negate();
    den.negate();
  }
  bigint g = bgcd(num, den);
  num /= g;
  den /= g;
}

void bigrational::
absolute_value()
{
  num.absolute_value();
}

void bigrational::
negate()
{
  num.negate();
}

void bigrational::
invert()
{
  if (num.is_zero())
    lidia_error_handler("bigrational", "invert()::division by zero.");
  swap(num, den);
  if (den.is_negative())
  {
    num.negate();
    den.negate();
  }
}

bigint bigrational::
numerator() const
{
  return num;
}

bigint bigrational::
denominator() const
{
  return den;
}

void bigrational::
assign_zero()
{
  num.assign_zero();
  den.assign_one();
}

void bigrational::
assign_one()
{
  num.assign_one();
  den.assign_one();
}

void bigrational::
assign(const bigint & n,
       const bigint & d)
{
  if (d.is_zero())
    lidia_error_handler("bigrational", "assign(n,d)::division by zero.");
  num.assign(n);
  den.assign(d);
  this->normalize();
}

void bigrational::
assign(const bigrational & a)
{
  num.assign(a.num);
  den.assign(a.den);
}

void bigrational::
multiply_by_2()
{
  if (den.is_even())
    den.divide_by_2();
  else
    num.multiply_by_2();
}

void bigrational::
divide_by_2()
{
  if (num.is_even())
    num.divide_by_2();
  else
    den.multiply_by_2();
}

/**
** Type checking
**/

bool
is_bigint(const bigrational & a)
{
  return a.den.is_one();
}

/**
** assignments
**/

int bigrational::operator = (int i)
{
  num = i;
  den.assign_one();
  return i;
}

long bigrational::operator = (long l)
{
  num = l;
  den.assign_one();
  return l;
}

unsigned long bigrational::operator = (unsigned long ul)
{
  num = ul;
  den.assign_one();
  return ul;
}

double bigrational::operator = (double d)
{
  int exponent, sign = (d < 0.0);
  double fraction;
  base_digit m;
  long ndigits = 0;

  num.assign_zero();
  den.assign_one();

  if (d)
  {
    if (sign)
      d = -d;

    fraction = frexp(d, &exponent);

    while (fraction != 0)
    {
      shift_left(num, num, bigint::bits_per_digit()); 
      fraction = ldexp(fraction, bigint::bits_per_digit());
      m = (base_digit) fraction;
      fraction -= m;
      ndigits++;
      add(num, num, bigint(m));
    }
    // the product on the right side is never > MAXINT
    exponent -= (int)(ndigits * bigint::bits_per_digit());
    if (exponent < 0)
      shift_left(den, den, -exponent);
    else
      shift_left(num, num, exponent);
    if (sign)
      num.negate();
  }
  this->normalize();
  return d;
}

bigint bigrational::operator = (const bigint & a)
{
  num.assign(a);
  den.assign_one();
  return a;
}

bigrational & bigrational::operator = (const bigrational & a)
{
  num.assign(a.num);
  den.assign(a.den);
  return *this;
}

/**
** comparisons
**/

bool operator == (const bigrational & a, const bigrational & b)
{ if (a.compare(b) == 0) return true; 
  return false; }

bool operator != (const bigrational & a, const bigrational & b)
{ if (a.compare(b) != 0) return true; 
  return false; }

bool operator > (const bigrational & a, const bigrational & b)
{ if (a.compare(b) > 0) return true; 
  return false; }

bool operator >= (const bigrational & a, const bigrational & b)
{ if (a.compare(b) >= 0) return true;
  return false; }

bool operator < (const bigrational & a, const bigrational & b)
{ if (a.compare(b) < 0) return true; 
  return false; }

bool operator <= (const bigrational & a, const bigrational & b)
{ if (a.compare(b) <= 0) return true;
  return false; }

/**
** operator overloading
**/

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

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

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

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

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

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

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

bigrational & bigrational::operator += (const bigrational & a)
{
  add(*this, *this, a);
  return *this;
}

bigrational & bigrational::operator -= (const bigrational & a)
{
  subtract(*this, *this, a);
  return *this;
}

bigrational & bigrational::operator *= (const bigrational & a)
{
  multiply(*this, *this, a);
  return *this;
}

bigrational & bigrational::operator /= (const bigrational & a)
{
  divide(*this, *this, a);
  return *this;
}

bigrational & bigrational::operator <<= (unsigned long ui)
{
  shift_left(*this, *this, ui);
  return *this;
}

bigrational & bigrational::operator >>= (unsigned long ui)
{
  shift_right(*this, *this, ui);
  return *this;
}

bigrational & bigrational::operator++ ()
{
  add(num, num, den);
  return *this;
}

bigrational & bigrational::operator-- ()
{
  subtract(num, num, den);
  return *this;
}

int bigrational::operator ! ()
{
  return num.is_zero();
}

/**
 ** Procedural versions
 **/

void 
invert(bigrational & a, const bigrational & b)
{
  a.assign(b);
  a.invert();
}

void 
negate(bigrational & a, const bigrational & b)
{
  a.assign(b);
  a.negate();
}

void multiply_by_two(bigrational & c, const bigrational & a)
{
  c.assign(a);
  c.multiply_by_2();
}

void divide_by_two(bigrational & c, const bigrational & a)
{
  c.assign(a);
  c.divide_by_2();
}

void
add(bigrational & c, const bigrational & a, const bigrational & b)
{
  if (a.is_zero())
  {
    c.num.assign(b.num);
    c.den.assign(b.den);
  }
  else if (b.is_zero())
  {
    c.num.assign(a.num);
    c.den.assign(a.den);
  }
  else
  {
    bigint g = gcd(a.den, b.den), h;
    if (g.is_one())
    {
      // c.num   a.num * b.den + a.den * b.num;
      // ----- = -----------------------------
      // c.den         a.den * b.den;

      multiply(h, a.num, b.den);
      multiply(c.num, a.den, b.num);
      add(c.num, c.num, h);
      multiply(c.den, a.den, b.den);
    }
    else
    {
      // bigint t = a.num * (b.den / g) + b.num * (a.den / g);
      // bigint h = gcd(t, g);
      // c.num = t / h;
      // c.den = (a.den / g) * (b.den / h);

      bigint t, s, ss;
      divide(s, b.den, g);
      multiply(t, a.num, s);
      divide(s, a.den, g);
      multiply(ss, s, b.num);
      add(t, t, ss);
      h = gcd(t, g);
      divide(c.num, t, h);
      divide(t, b.den, h);
      multiply(c.den, s, t);
    }
    if (c.den.is_negative())
    {
      c.num.negate();
      c.den.negate();
    }
  }
}

void
subtract(bigrational & c, const bigrational & a, const bigrational & b)
{
  if (a.is_zero())
  {
    c.num.assign(b.num);
    c.num.negate();
    c.den.assign(b.den);
  }
  else if (b.is_zero())
  {
    c.num.assign(a.num);
    c.den.assign(a.den);
  }
  else
  {
    bigint g = gcd(a.den, b.den), h;
    if (g.is_one())
    {
      // c.num   a.num * b.den - a.den * b.num;
      // ----- = -----------------------------
      // c.den         a.den * b.den;

      multiply(h, a.num, b.den);
      multiply(g, a.den, b.num);
      subtract(c.num, h, g);
      multiply(c.den, a.den, b.den);
    }
    else
    {
      // bigint t = a.num * (b.den / g) - b.num * (a.den / g);
      // bigint h = gcd(t, g);
      // c.num = t / h;
      // c.den = (a.den / g) * (b.den / h);

      bigint t, s, ss;
      divide(s, b.den, g);
      multiply(t, a.num, s);
      divide(s, a.den, g);
      multiply(ss, s, b.num);
      subtract(t, t, ss);
      h = gcd(t, g);
      divide(c.num, t, h);
      divide(t, b.den, h);
      multiply(c.den, s, t);
    }
    if (c.den.is_negative())
    {
      c.num.negate();
      c.den.negate();
    }
  }
}

void
multiply(bigrational & c, const bigrational & a, const bigrational & b)
{
  bigint g = gcd(a.num, b.den);
  bigint h = gcd(a.den, b.num);
  bigint s, t;

  switch (g.is_one() * 2 + h.is_one())
  {
    case 0:
      // c.num   (a.num / g) * (b.num / h)
      // ----- = -------------------------
      // c.den   (a.den / h) * (b.den / g)
      divide(s, a.num, g);
      divide(t, b.num, h);
      multiply(c.num, s, t);

      divide(s, a.den, h);
      divide(t, b.den, g);
      multiply(c.den, s, t);
      break;
    case 1:
      // c.num   (a.num / g) * b.num
      // ----- = -------------------
      // c.den   a.den * (b.den / g)
      divide(s, a.num, g);
      multiply(c.num, s, b.num);
      divide(t, b.den, g);
      multiply(c.den, a.den, t);
      break;
    case 2:
      // c.num   a.num * (b.num / h)
      // ----- = -------------------
      // c.den   (a.den / h) * b.den
      divide(t, b.num, h);
      multiply(c.num, a.num, t);
      divide(s, a.den, h);
      multiply(c.den, s, b.den);
      break;
    case 3:
      // c.num   a.num * b.num
      // ----- = -------------
      // c.den   a.den * b.den
      multiply(c.num, a.num, b.num);
      multiply(c.den, a.den, b.den);
      break;
  }
  if (c.den.is_negative())
  {
    c.num.negate();
    c.den.negate();
  }
}

void
divide(bigrational & c, const bigrational & a, const bigrational & b)
{
  bigint g = gcd(a.num, b.num);
  bigint h = gcd(a.den, b.den);
  bigint s, t;

  switch (g.is_one() * 2 + h.is_one())
  {
    case 0:
      // c.num   (a.num / g) * (b.den / h)
      // ----- = -------------------------
      // c.den   (a.den / h) * (b.num / g)
      divide(s, a.num, g);
      divide(t, b.den, h);
      multiply(c.num, s, t);

      divide(s, a.den, h);
      divide(t, b.num, g);
      multiply(c.den, s, t);
      break;
    case 1:
      // c.num   (a.num / g) * b.den
      // ----- = -------------------
      // c.den   a.den * (b.num / g)
      divide(s, a.num, g);
      multiply(c.num, s, b.den);
      divide(t, b.num, g);
      multiply(c.den, a.den, t);
      break;
    case 2:
      // c.num   a.num * (b.den / h)
      // ----- = -------------------
      // c.den   (a.den / h) * b.num
      divide(t, b.den, h);
      multiply(c.num, a.num, t);
      divide(s, a.den, h);
      multiply(c.den, s, b.num);
      break;
    case 3:
      // c.num   a.num * b.den
      // ----- = -------------
      // c.den   a.den * b.num
      multiply(c.num, a.num, b.den);
      multiply(c.den, a.den, b.num);
      break;
  }
  if (c.den.is_negative())
  {
    c.num.negate();
    c.den.negate();
  }
}


void
shift_left(bigrational & c, const bigrational & a, long ui)
{
  if (ui < 0) 
      lidia_error_handler("bigrational", "shift_left()::index is negative.");
  c.den.assign(a.den);
  shift_left(c.num, a.num, ui);
  c.normalize();
}

void
shift_right(bigrational & c, const bigrational & a, long ui)
{
  if (ui < 0) 
      lidia_error_handler("bigrational", "shift_right()::index is negative.");
  c.num.assign(a.num);
  shift_left(c.den, a.den, ui);
  c.normalize();
}

void
power(bigrational & c, const bigrational & a, const bigint & b)
{
  bigint n = 1, d = 1;
  if (!b.is_zero())
  {
    if (b.is_gt_zero())
    {
      power(n, a.num, b);
      power(d, a.den, b);
    }
    else
    {
      bigint abs_b = -b;
      power(n, a.den, abs_b);
      power(d, a.num, abs_b);
      if (d.is_negative())
      {
	n.negate();
	d.negate();
      }
    }
  }
  c.num.assign(n);
  c.den.assign(d);
}

void
power(bigrational & c, const bigrational & a, long i)
{
  bigint n = 1, d = 1;

  if (i)
  {
    if (i > 0)
    {
      power(n, a.num, i);
      power(d, a.den, i);
    }
    else
    {
      i = -i;
      power(n, a.den, i);
      power(d, a.num, i);
      if (d.is_negative())
      {
	n.negate();
	d.negate();
      }
    }
  }
  c.num.assign(n);
  c.den.assign(d);
}

void
inc(bigrational & c)
{
  add(c.num, c.num, c.den);
}

void
dec(bigrational & c)
{
  subtract(c.num, c.num, c.den);
}

void
add(bigrational & c, const bigrational & a, long i)
{
  bigint tmp;
  multiply(tmp, a.den, i);
  add(c.num, a.num, tmp);
  c.den.assign(a.den);
}

void
add(bigrational & c, long i, const bigrational & a)
{
  bigint tmp;
  multiply(tmp, a.den, i);
  add(c.num, a.num, tmp);
  c.den.assign(a.den);
}

void
subtract(bigrational & c, const bigrational & a, long i)
{
  bigint tmp;
  multiply(tmp, a.den, i);
  subtract(c.num, a.num, tmp);
  c.den.assign(a.den);
}

void
subtract(bigrational & c, long i, const bigrational & a)
{
  bigint tmp;
  multiply(tmp, a.den, i);
  subtract(c.num, a.num, tmp);
  c.num.negate();
  c.den.assign(a.den);
}

void
multiply(bigrational & c, const bigrational & a, long i)
{
  multiply(c.num, a.num, i);
  c.den.assign(a.den);
  c.normalize();
}

void
multiply(bigrational & c, long i, const bigrational & a)
{
  multiply(c.num, a.num, i);
  c.den.assign(a.den);
  c.normalize();
}

void
divide(bigrational & c, const bigrational & a, long i)
{
  c.num.assign(a.num);
  multiply(c.den, a.den, i);
  c.normalize();
}

void
divide(bigrational & c, long i, const bigrational & a)
{
  if (&c == &a)
  {
    c.invert();
    multiply(c.num, c.num, i);
  }
  else
  {
    multiply(c.num, a.den, i);
    c.den.assign(a.num);
  }
  c.normalize();
}

/**
 ** functions
 **/

bigrational
abs(const bigrational & a)
{
  bigrational c(a);
  c.absolute_value();
  return c;
}

bigrational
inverse(const bigrational & a)
{
  bigrational c(a);
  c.invert();
  return c;
}

bigint
numerator(const bigrational & a)
{
  bigint c(a.num);
  return c;
}

bigint
denominator(const bigrational & a)
{
  bigint c(a.den);
  return c;
}

bigint
round(const bigrational & a)
{
  bigrational c;
  bigint rn(a.num), rd(a.den);
  rn.multiply_by_2();
  add(rn, rn, rd);
  rd.multiply_by_2();
  c.num.assign(rn);
  c.den.assign(rd);
  return floor(c);
}

bigint
floor(const bigrational & a)
{
  bigint q, r;
  div_rem(q, r, abs(a.num), abs(a.den));
  if (a.sign() < 0 && r.sign() != 0)
  {
    if (a.sign() < 0)
      q.negate();
    dec(q);
  }
  else
  {
    if (a.sign() < 0)
      q.negate();
  }
  return q;
}

bigint
ceiling(const bigrational & a)
{
  bigint q, r;
  div_rem(q, r, abs(a.num), abs(a.den));
  r.multiply_by_2();
  if (a.sign() >= 0 && r.sign() != 0 && r.compare(q) >= 0)
    inc(q);
  if (a.sign() < 0)
    q.negate();
  return q;
}

bigint
truncate(const bigrational & a)
{
  bigint q, r;
  div_rem(q, r, abs(a.num), abs(a.den));
  if (a.sign() < 0)
    q.negate();
  return q;
}

double
dbl(const bigrational & a)
{
  if (a.num.is_zero())
    return 0.0;

  long ln = a.num.bit_length();
  long ld = a.den.bit_length();
  long en = 0, ed = 0;

  if (ln > 1023)
    en = ln - 1023;
  if (ld > 1023)
    ed = ld - 1023;

  bigint an = a.num >> en;
  bigint ad = a.den >> ed;

  // an = (a/2^en)*2^en
  // ad = (a/2^ed)*2^ed

  double d = dbl(an) / dbl(ad);
  d = ldexp(d, (int)(en - ed));

  return d;
}

void
square(bigrational & a, const bigrational & b)
{
  square(a.num, b.num);
  square(a.den, b.den);
}

void
swap(bigrational & a, bigrational & b)
{
  swap(a.num, b.num);
  swap(a.den, b.den);
}

/**
** input / output
**/

istream & operator >> (istream & in, bigrational & a)
{
  char s[10000];
  char *p = s;
  char c;

  a.num.assign_zero();
  a.den.assign_one();

  do
  {
    in.get(c);
  } while (isspace(c));
  if ((c == '+') || (c == '-'))
  {
    *p++ = c;
    do
    {
      in.get(c);
    } while (isspace(c));
  }
  if (!isdigit(c))
    cerr << "digit expected";
  while (isdigit(c))
  {
    *p++ = c;
    in.get(c);
  }
  *p = '\0';
  string_to_bigint(s, a.num);

  if (c != '\n')
  {
    *s = '\0';
    p = s;
    while (isspace(c))
    {
      in.get(c);
    }

    if (c == '/')
    {
      do
      {
        in.get(c);
      } while (isspace(c));
      if ((c == '+') || (c == '-'))
      {
	*p++ = c;
	do
	{
          in.get(c);
	} while (isspace(c));
      }
      if (!isdigit(c))
	cerr << "digit expected";
      while (isdigit(c))
      {
	*p++ = c;
        in.get(c);
      }
      *p = '\0';
      string_to_bigint(s, a.den);
    }
  }
  if (c != '\n' && c != '\r')
    in.putback(c);
  a.normalize();
  return in;
}

ostream & operator << (ostream & out, const bigrational & a)
{
  bigint n = a.num;
  bigint d = a.den;

  if (n.is_zero() || d.is_one())
    out << n;
  else
    out << n << "/" << d;
  return out;
}

int
string_to_bigrational(char *s, char *t, bigrational & a)
{
  long i = string_to_bigint(s, a.num);
  long j = string_to_bigint(t, a.den);
  a.normalize();
  return (i + j);
}

int
bigrational_to_string(const bigrational & a, char *s, char *t)
{
  int i = bigint_to_string(a.num, s);
  int j = bigint_to_string(a.den, t);
  return (i + j);
}

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

void bigrational::
read_from_file(FILE * fp)
{
  num.read_from_file(fp);
  den.read_from_file(fp);
  this->normalize();
}

void bigrational::
write_to_file(FILE * fp)
{
  num.write_to_file(fp);
  den.write_to_file(fp);
}

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

void bigrational::
scan_from_file(FILE * fp)
{
  char s[10000];
  char *p = s;
  char c;

  num.assign_zero();
  den.assign_one();

  do
  {
    c = getc(fp);
  } while (isspace(c));
  if ((c == '+') || (c == '-'))
  {
    *p++ = c;
    do
    {
      c = getc(fp);
    } while (isspace(c));
  }
  else
  {
    while (isspace(c))
    {
      c = getc(fp);
    }
  }
  if (!isdigit(c))
    cerr << "digit expected";
  while (isdigit(c))
  {
    *p++ = c;
    c = getc(fp);
  }
  *p = '\0';
  string_to_bigint(s, num);

  if (c != '\n')
  {
    *s = '\0';
    p = s;
    while (isspace(c))
    {
      c = getc(fp);
    }

    if (c == '/')
    {
      do
      {
	c = getc(fp);
      } while (isspace(c));
      if ((c == '+') || (c == '-'))
      {
	*p++ = c;
	do
	{
	  c = getc(fp);
	} while (isspace(c));
      }
      else
      {
	while (isspace(c))
	{
	  c = getc(fp);
	}
      }
      if (!isdigit(c))
	cerr << "digit expected";
      while (isdigit(c))
      {
	*p++ = c;
	c = getc(fp);
      }
      *p = '\0';
      string_to_bigint(s, den);
    }
  }
  if (c != '\n' && c != '\r')
    ungetc(c, fp);
  this->normalize();
}

void bigrational::
print_to_file(FILE * fp)
{
  long l, k;
  char *s;

  if (num.is_zero() || den.is_one())
  {
    l = num.bit_length();
    s = new char[l / 3 + 20];
    bigint_to_string(num, s);
  }
  else
  {
    l = num.bit_length();
    k = den.bit_length();
    if (l < k)
      l = k;
    s = new char[l / 3 + 20];
    bigint_to_string(num, s);
    fputs(s, fp);
    fputs("/", fp);
    bigint_to_string(den, s);
  }
  fputs(s, fp);
  delete[] s;
}
