//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigrational_appl.c (based on the GNU Integer Test) 
// Author      : Volker M"uller (VM)
// Last change : VM, Feb 7 1995, initial version
//               TP, Feb 10 1995, changed x = rand() to 
//                    x = rand() & 0x7fff; to avoid the overflow in 
//		 MM, added test for assign(const bigint&, const bigint&)
//

#include <LiDIA/bigrational.h>
#include <assert.h>

#include <LiDIA/random.h>

void identitytest(bigrational& a, bigrational& b, bigrational& c)
{
  assert( -(-a) ==  a);
  assert( (a + b) ==  (b + a));
  assert( (a + (-b)) ==  (a - b));
  assert( (a + (-b)) ==  (b - a+2*a - 2*b));
  assert( (a * b) ==  (b * a));
  assert( (a * (-b)) ==  -(a * b));
  assert( (a / (-b)) ==  -(a / b));
  assert( (a - b) ==  -(b - a));
  assert( (a + (b + c)) ==  ((a + b) + c));
  assert( (a + (b + c)) ==  ((a + c) + b));
  assert( (a * (b * c)) ==  ((a * b) * c));
  assert( (a * (b * c)) ==  ((c * a) * b));
  assert( (a * (b + c)) ==  ((a * b) + (a * c)));
  assert( ((a - b) + b) ==  a);
  assert( ((a + b) - b) ==  a);
  assert( ((a * b) / b) ==  a);
  assert( ((a * b) / a) ==  b);
  assert( ((a / b) / a) ==  (1/b));

  bigrational d;

  negate(d, a);  assert( -(d) ==  a);
  add(d, b, a);  assert( (a + b) ==  d);
  subtract(d, a , b);  assert( (a + (-b)) ==  d);
  negate(d, b); assert( (a + d) ==  (b - a+2*a - 2*b));
  d.assign(a); d.multiply_by_2(); assert( (a + a) ==  d);
  multiply(d, a, b); assert( d ==  (b * a));
  d.assign(b); d.negate(); multiply(d, a, d); assert( d ==  -(a * b));
  divide(d, a, b); assert( (a / (-b)) ==  -d);
  add(d, b, c); multiply(d, a, d);  assert(d ==  ((a * b) + (a * c)));
  d = b; d.invert(); assert( ((a / b) / a) == d);
  d = b; d.invert(); d.assign(b.numerator(),b.denominator()); assert(b == d);
}


void utiltest( bigrational& a)
{
  bigrational b, c;

  square(b, a);
  multiply(c, a, a);
  assert(b == c);

  bigrational x = 1, y = 1;

  for (int i = 0; i < 10; ++i)
  {
    power(b, a, i);
    assert(b == x);
    x *= a;
    y = a * y;
    assert(y == x);
  }
  x.assign_one();
  assert(x.is_one());
  x.assign(4);
  assert(x.is_positive());
  x.negate();
  assert(x.is_negative());
}


void accumtest( bigrational& a,  bigrational& b,  bigrational& c)
{
  bigrational x = a;
  x *= b;
  assert(x == (b * a));
  x += c;
  assert(x == (c+(b * a)));
  x -= a;
  assert(x == (-a + c + (b * a)));
  x /= b;
  assert(x == (((b *a) -a + c) / b));

  x.assign(a);
  assert(x == a);
  multiply(x, x, b);
  assert(x == (b * a));
  add(x, x, c);
  assert(x == (c+(b * a)));
  subtract(x, x, a);
  assert(x == (-a + c + (b * a)));
  divide(x, x, b);
  assert(x == (((b *a) -a + c) / b));
}

void longidentitytest( bigrational& a, long b, long c)
{
  assert( (a + b) ==  (b + a));
  assert( (a + (-b)) ==  (a - b));
  assert( (a * b) ==  (b * a));
  assert( (a * (-b)) ==  -(a * b));
  assert( (a / (-b)) ==  -(a / b));
  assert( (a - b) ==  -(b - a));
  assert( (a + (b + c)) ==  ((a + b) + c));
  assert( (a + (b + c)) ==  ((c + a) + b));
  assert( (a * (b * c)) ==  ((b * a) * c));
  assert( (a * (b + c)) ==  ((a * b) + (a * c)));
  assert( ((a - b) + b) ==  a);
  assert( ((a + b) - b) ==  a);
  assert( ((a * b) / b) ==  a);
  assert( (b * (a / b) ) ==  a);

  bigrational d;

  negate(d, a); assert( -(d) ==  a);
  add(d, b, a);  assert( (a + b) ==  d);
  subtract(d, a , b); assert( (a + (-b)) ==  d);
  negate(d, b); assert( (a + d) ==  (b - a+2*a - 2*b));
  d.assign(a); d.multiply_by_2(); assert( (a + a) ==  d);
  multiply(d, a, b); assert( d ==  (b * a));
  d.assign(b); d.negate(); multiply(d, a, d); assert( d ==  -(a * b));
  divide(d, a, b); assert( (a / (-b)) ==  -d);
  add(d, b, (bigrational)c); multiply(d, a, d);  
  assert(d ==  ((a * b) + (a * c)));
  add(d, (bigrational)b, c); multiply(d, a, d);  
  assert(d == ((a * b) + (a * c)));
  d = b; d.invert();  assert(((a / b) / a) == d);
}



void longaccumtest( bigrational& a, long b, long c)
{
  bigrational x = a;
  x *= b;
  assert(x == (a * b));
  x += c;
  assert(x == ((a * b) + c));
  x -= a;
  assert(x == (((a * b) + c) - a));
  x /= b;
  assert(x == ((((a * b) + c) - a) / b));

  x.assign(a);
  assert(x == a);
  multiply(x, x, b);
  assert(x == (a * b));
  add(x, x, c);
  assert(x == ((a * b) + c));
  subtract(x, x, a);
  assert(x == (((a * b) + c) - a));
  divide(x, x, b);
  assert(x == ((((a * b) + c) - a) / b));
}

 
void anothertest()
{
  bigrational pow64;
  power(pow64, 2, 64);
  cout << "power(pow64, 2, 64) = " << pow64 << "\n";
  
  bigrational s64 = 1;
  s64 <<= 64;
  cout << "s64 = 1 << 64 = " << s64 << "\n";

  assert(s64 == pow64);
  assert(s64 >= pow64);
  assert(s64 <= pow64);
  assert(!(s64 != pow64));
  assert(!(s64 > pow64));
  assert(!(s64 < pow64));
  
  bigrational s32 = s64 >> 32;
  cout << "s32 = (s64 >> 32) = " << s32 << "\n";
  assert(!(pow64 == s32));
  assert(!(pow64 < s32));
  assert(!(pow64 <= s32));
  assert(pow64 != s32);
  assert(pow64 >= s32);
  assert(pow64 > s32);

  identitytest(s64, s32, pow64);
  accumtest(pow64, s32, pow64);
  utiltest(s32);
  longidentitytest(s64, 1000, 50);
  longaccumtest(s32, 100000, 11);
}

void iotest()
{
  bigrational result;

  cout << "\nenter an bigrational: ";
  cin >> result;
  cout << "number = " << result << "\n";

// hier noch string-Fkt einfuegen

}


void all_test(const bigint & ii)
{
  bigrational a, b; 
  static long x=1, y=27;
  bigint aa, bb;

  aa = randomize(ii);
  bb = randomize(ii);
  divide(a, aa, bb);
  aa = randomize(ii);
  bb = randomize(ii);
  divide(b, aa, bb);

  srandom((unsigned int)x); srandom((unsigned int)y);
  x = random() & 0x7fff; y = random() & 0x7fff;

  cout << "\n\n Test with \na = " << a << "\nb = " << b;
  cout << "\nx = " << x << "\ny = " << y << flush;

  utiltest(a);
  identitytest(a, b, b);
  identitytest(a, a, b);

  accumtest(a, a, b);
  accumtest(b, a, b);

  utiltest(a);
  utiltest(b);

  longidentitytest(a, x, y);
  longidentitytest(b, x, x);
 
  longaccumtest(b, x, y);
  longaccumtest(a, x, x);
}


int main()
{
  bigrational one = 1;
  cout << "one = " << one << "\n";
  assert(one == 1);
  cout << "one + 1 = " << (one + 1) << "\n";

  bigrational two = 2;
  cout << "two = " << two << "\n";
  assert(two == 2);

  bigrational n(0);
  assert(n.is_zero());
  anothertest();
  iotest();

  bigint i=129099121;

  for(int j=0; j<10; j++)
        all_test(i);

  cout << "\nEnd of test\n";
  return 0;
}
