

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

#include <assert.h>


void identitytest(const gf2n & a, gf2n & b, const gf2n & c)
{
  assert( (a + b) ==  (b + a));
  assert( (a * b) ==  (b * a));
  assert( (a - b) ==  (b - a));
  assert( (a - b) ==  (a + b));
  assert( (a + (b + c)) ==  ((a + b) + c));
  assert( (a * (b * c)) ==  ((a * b) * c));
  assert( (a * (b + c)) ==  ((a * b) + (a * c)));
  assert( ((a - b) + b) ==  a);
  assert( ((a * b) / b) ==  a);
  assert( ((a / b) * b) ==  a);
}


void assigntest(unsigned long tests)
{
  gf2n a, b, c, d;
  bigint x, q;
  unsigned long xx;

  a.assign_zero(); b = gf2n(0); assert(a == b);
  b = gf2n(bigint(0)); assert(a == b);
  assert(a.is_zero() == true);
 

  a.assign_one(); b = gf2n(1);assert(a == b);
  b = gf2n(bigint(1)); assert(a == b);
  assert(a.is_one() == true);

  shift_left(q, bigint(1), gf2n::extension_degree());
  
  for (unsigned int i=0; i<tests; i++)
    {
      x = randomize(q);
      xx = x.least_significant_digit();

      a.assign(x);  b = gf2n(x);  assert(a == b); 
      c = b; assert(a == c);
      c.assign(b); assert(a == c);
      
      a.assign(xx);  b = gf2n(xx);  assert(a == b); 
      c = b; assert(a == c);
      c.assign(b); assert(a == c);
    }

  randomize(a); c = a;
  randomize(b); d = b;
  
  swap(a,b);
  assert(a == d);
  assert(b == c);
}  
  


void accumtest(const gf2n & a, const gf2n & b)
{
  gf2n c, d;
  
  c = a + b; add(d, a, b);  assert(c == d);
  c = a - b; subtract(d, a, b);  assert(c == d);
  c = a * b; multiply(d, a, b);  assert(c == d);
  c = a * a; square(d, a); assert(c == d);
  c = a / b; divide(d, a, b); assert(c == d);
  c.assign(a); c.invert();  assert(c == inverse(a));
  invert(c, b); assert((c*b) == gf2n(1));
  
  c.assign(a); c += b; add(d, a, b);  assert(c == d);
  c.assign(a); c -= b; subtract(d, a, b);  assert(c == d);
  c.assign(a); c *= b; multiply(d, a, b);  assert(c == d);
  c.assign(a); c /= b; divide(d, a, b); assert(c == d);
}


void utiltest(const gf2n & a)
{
  gf2n b;
  bigint bord, aord, q;

  square(b, a);
  sqrt(b, b);
  assert(b == a);
  assert(b == sqrt(a*a));

  aord = compute_order(a);
  power(b, a, aord);
  assert(b == gf2n(1));
  invert(b, a);
  bord = compute_order(b);
  assert(bord == aord);

  
  shift_left(q, bigint(1), gf2n::extension_degree());
  b = get_generator();
  bord = compute_order(b);
  assert(bord == q-(bigint)1);
}


void iotest()
{
  gf2n result;
  unsigned int i;

  for (i=0; i<5; i++)
    {
      cout << "\n\nEnter a gf2n-element : ";
      cin >> result;
      
      cout << "\noutput in predefined output mode = " << result;
      gf2nIO::setbase(gf2nIO::Hex);
      cout << "\noutput in 'Hex' mode = " << result;
      gf2nIO::setbase(gf2nIO::Dec);
      cout << "\noutput in 'Dec' mode = " << result;
      gf2nIO::noprefix();
      cout << "\noutput after noprefix = " << result;
      
      identitytest(result, result, result );
      accumtest(result, result);
    }
cout<<"\n\n";
}



int main()
{
  unsigned int  deg, tests, i;
  cout << "\nInput Degree: ";  cin >> deg;
  
  gf2n_init(deg);

  gf2n a, b, c;

  cout<<"\nNumber of tests: "; cin >> tests;
  

for(i=0; i<tests; i++)
  {
    randomize(a); randomize(b); randomize(c); 
    identitytest(a,b,c);
  }
cout << "\n\nIdentitytests finished\n" << flush;

assigntest(tests);
cout << "Assigntests finished\n" << flush;

for(i=0; i<tests; i++)
  {
    randomize(a); randomize(b); 
    accumtest(a, b);
  }
cout << "Accumtest finished\n" << flush; 
cout << "Starting Factorization of group order ....\n"<<flush;

for(i=0; i<10; i++)
  {
    randomize(a);
    utiltest(a);
  }
cout << "Utiltest finished\n" << flush; 


iotest();
cout << "Iotest finished\n" << flush; 

cout<<"\n\n --> Tests for degree "<<deg<<" finished without errors\n\n";
}
