//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : alg_number_appl.c (based on bigrational_appl.c,
//                                  based on the GNU Integer Test)
// Author      : Stefan Neis
//

#include <assert.h>
#include <stdlib.h>
#include <fstream.h>

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:alg_number.h>
#include <LiDIA:timer.h>
#else
#include <LiDIA/alg_number.h>
#include <LiDIA/timer.h>
#endif

ifstream in("alg_appl_input");

void identitytest(const module& a, const module& b, const module& c)
{
  debug_handler_c("module_appl::identitytest", "preparing assertions 1-9",0,
                  cout <<a<<b<<c);
  assert( (a + b) ==  (b + a));
  assert( (a & b) ==  (b & a));
  assert( (a * b) ==  (b * a));

  debug_handler_l("module_appl::identitytest","assertions 1 to 3 succeded",3);

  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) & c));
  assert( (a & (b & c)) ==  ((c & a) & b));

  debug_handler_l("module_appl::identitytest","assertions 4 to 9 succeded",3);

  debug_handler_c("module_appl::identitytest", "preparing assertions 10-13",0,
                  cout <<a<<b<<(a+b)<<(a&b));
  assert( (a + b) >= a);
  assert( (a + b) >= b);

  assert( (a & b) <= a);
  assert( (a & b) <= b);

  debug_handler_l("module_appl::identitytest","assertions 10 to 13 succeded",3);
  debug_handler_c("module_appl::identitytest", "preparing assertion 14",0,
                  cout <<a<<b<<c<<b+c<< a*(b+c)<<a*b <<a*c<<(a*b)+(a*c));
  assert( (a * (b + c)) ==  ((a * b) + (a * c)));
  debug_handler_c("module_appl::identitytest","preparing assertion 15",3,
                  cout<<a<<b<<c<<(b&c)<<(a*(b&c))<<(a*b)<<(a*c)<<((a*b)&(a*c)));
  assert( (a * (b & c)) <=  ((a * b) & (a * c)));
  debug_handler_c("module_appl::identitytest","preparing assertion 16",3,
                  cout<<a<<b<<c<<(b+c)<<(a&(b+c))<<(a&b)<<(a&c)<<((a&b)+(a&c)));
  assert( (a & (b + c)) ==  ((a & b) + (a & c)));

  debug_handler_l("module_appl::identitytest",
                  "assertions 14 to 16 succeded",3);

  debug_handler_c("module_appl::identitytest","preparing assertion 17",3,
                  cout<<a<<b<<(a&b)<<((a&b)+b));
  assert( ((a & b) + b) ==  b);
  debug_handler_c("module_appl::identitytest","preparing assertion 18",3,
                  cout<<a<<b<<(a+b)<<((a+b)&b));
  assert( ((a + b) & b) ==  b);
  debug_handler_c("module_appl::identitytest","preparing assertion 19",3,
                  cout<<a<<b<<(a*b)<<((a*b)&b)<<(a&b)<<((a&b)*b));
  //  assert( ((a * b) & b) ==  ((a&b)*b));
  debug_handler_c("module_appl::identitytest","preparing assertion 20",3,
                  cout<<a<<b<<(a*b)<<((a*b)&a)<<(a&b)<<(a*(a&b)));
  //  assert( ((a * b) & a) ==  (a*(a&b)));
  debug_handler_c("module_appl::identitytest","preparing assertion 21",3,
                  cout<<a<<b<<(a&b)<<((a&b)&a));
  assert( ((a & b) & a) ==  (a & b));

  debug_handler_l("module_appl::identitytest",
                  "assertions 17 to 21 succeded",3);

  if (!exp(b).is_zero()){
    debug_handler_c("module_appl::identitytest","preparing assertion 22",3,
                    cout<<a<<b<<(a*b)<<(a*b)/b);
    assert(((a * b) / b) >= a);
    debug_handler_c("module_appl::identitytest","preparing assertion 23",3,
                    cout<<a<<b<<(a/b)<<flush; cout<<(a/b)*b<<flush);
    assert(((a / b) * b) <= a);
  }
  if (!exp(a).is_zero()){
    debug_handler_c("module_appl::identitytest","preparing assertion 24",3,
                    cout<<a<<b<<(a*b)<<flush; cout<<(a*b)/a<<flush);
    assert(((a * b) / a) >= b);
    debug_handler_c("module_appl::identitytest","preparing assertion 25",3,
                    cout<<a<<b<<(b/a)<<(b/a)*a<<flush);
    assert(((b / a) * a) <= b);
  }

  debug_handler_l("module_appl::identitytest",
                  "assertions 22 to 25 succeded",3);

  module d;

  add(d, b, a);  assert( (a + b) ==  d);
  intersect(d, a , b);  assert( (a & b) ==  d);
  multiply(d, a, b); assert( d ==  (b * a));
  d.assign(b); multiply(d, a, d); assert( d ==  (a * b));
  intersect(d, a, b); assert( (a & b) ==  d);
  if (!exp(b).is_zero()){
    divide(d, a, b); assert( (a / b) ==  d);
  }
  add(d, b, c); multiply(d, a, d);  assert(d ==  ((a * b) + (a * c)));
  debug_handler_l("module_appl::identitytest",
                  "procedural versions completed successfully", 3);
}


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

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

  module x = alg_number(1), y = alg_number(1);

  for (int i = 0; i < 10; ++i){
    power(b, a, i);
    debug_handler_c("module_appl::utiltest", "power", 2,
                    cout << a <<" ^ "<<i << " is "<<b<<endl);
    assert(b == x);
    x *= a;
    y = a * y;
    assert(y == x);
  }
  x.assign_one();
  assert(x.is_one());
}


void accumtest( module& a,  module& b,  module& c)
{
  module x = a;
  x *= b;
  assert(x == (b * a));
  x += c;
  assert(x == (c+(b * a)));
  x &= a;
  assert(x == ((c & a) + ((b * a) & a)));
  if (!exp(b).is_zero()){
    x /= b;
    debug_handler_c("module_appl::acummtest","preparing assertion 4",3,
                    cout<<x<<(((c+b*a)&a)/b)<<((c&a)/b+((b*a)&a)/b)<<flush);
    assert(x == (((c & a) + ((b * a)& a))/b));
  }

  x.assign(a);
  assert(x == a);

  debug_handler_l("module_appl::accumtest",
                  "assertions 1 to 5 succeded",3);

  multiply(x, x, b);
  debug_handler_c("module_appl::acummtest","preparing assertion 6",3,
                  cout<<a<<b<<x<<(b*a)<<flush);
  assert(x == (b * a));
  add(x, x, c);
  debug_handler_c("module_appl::acummtest","preparing assertion 7",3,
                  cout<<c<<x<<c+(b*a)<<flush);
  assert(x == (c+(b * a)));
  intersect(x, x, a);
  debug_handler_c("module_appl::acummtest","preparing assertion 8",3,
                  cout<<a<<x<<((c+(b*a))&a)<<flush);
  assert(x == ((c + (b * a))&a));
  debug_handler_c("module_appl::acummtest","preparing assertion 9",3,
                  cout<<((c&a)+((b*a)&a))<<flush);
  assert(x == ((c & a) + ((b * a) & a)));
  if (!exp(b).is_zero()){
    divide(x, x, b);
    debug_handler_c("module_appl::acummtest","preparing assertion 10",3,
                    cout<<x<<(((c+(b*a))&a)/b)<<flush);
    assert(x == (((c + (b * a))&a)/b));
  }
}

void anothertest()
{
  module pow64;
  power(pow64, module(bigint(2)), 64);

  bigint s64 = 1;
  s64 <<= 64;

  assert(alg_number(s64) == pow64);
  assert(!(alg_number(s64) != pow64));

  bigint s32 = s64 >> 32;
  assert(!(pow64 == alg_number(s32)));
  assert(pow64 != alg_number(s32));

  debug_handler_l("module_appl::anothertest",
                  "simple tests succeded, starting identitytest",4);
  identitytest(module(s64), module(s32), pow64);
  debug_handler_l("module_appl::anothertest",
                  "identitytest succeded, starting accumtest",4);
  //  accumtest(pow64, alg_number(s32), pow64);
  debug_handler_l("module_appl::anothertest",
                  "accumtest succeded, starting utiltest",4);
  utiltest(alg_number(s32));
}

// void iotest()
// {
//   module result;

//   cout << "\nenter an module: ";
//   cin >> result;
//   cout << "module = " << result << "\n";
// }


void all_test(const bigint & ii)
{
  module a,b;

  debug_handler_l("module_appl", "start all_test(const bigint &)",4);
  a.randomize(ii);
  b.randomize(ii);
//   cin >> a >> b;

  cout << "\n\n Test with \na = " << a << "\nb = " << b<<endl;

  debug_handler_l("module_appl", "call utiltest from all_test",4);
  utiltest(a);
  debug_handler_l("module_appl", "call identitytest from all_test",4);
  identitytest(a, b, b);
  debug_handler_l("module_appl", "call identitytest again from all_test",4);
  identitytest(a, a, b);

  debug_handler_l("module_appl", "call accumtest from all_test",4);
  //  accumtest(a, a, b);
  debug_handler_l("module_appl", "call accumtest again from all_test",4);
  //  accumtest(b, a, b);

  debug_handler_l("module_appl", "call utiltest again from all_test",4);
  utiltest(a);
  debug_handler_l("module_appl", "call utiltest (3) from all_test",4);
  utiltest(b);
}


int main()
{
  order O;
  cout << "Order:\n"<<flush;
  in >> O;

  cout << "Computing the maximal order needs:"<<flush;
  timer T;
  T.start_timer();
  order ext = O.maximize();
  T.stop_timer();
  cout << T <<  endl << flush;

// The following 3 lines lead to an internal compiler error,
// if gcc-2.6.2 is used:
  module one(alg_number(bigint(1)),alg_number(bigint(0)));
  cout << "one = " << one << "\n";
  assert(one == one + alg_number(bigint(1)));
  assert(one.is_one());
  assert(!one.is_zero());

  module n(alg_number(bigint(0)),alg_number(bigint(0)));
  assert(n.is_zero());
  assert(!n.is_one());
  debug_handler_l("module_appl",
                  "simple tests succeded, starting anothertest",5);
  anothertest();
  debug_handler_l("module_appl","anothertest succeded, starting iotest",5);
  // iotest();
  debug_handler_l("module_appl","simple tests succeded, "
                  "starting random tests",5);

  bigint i=129099121;

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

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


