//*****************************************************************************/
//                                                                            */
//  Software zum Buch "Kryptographie in C und C++"                            */
//  Autor: Michael Welschenbach                                               */
//                                                                            */
//  Quelldatei testari.cpp  Stand: 10.12.2000                                 */
//                                                                            */
//  Copyright (C) 1998-2001 by Michael Welschenbach                           */
//  Copyright (C) 2001 by Springer-Verlag Berlin, Heidelberg                  */
//                                                                            */
//  All Rights Reserved                                                       */
//                                                                            */
//  Die Software darf fuer nichtkommerzielle Zwecke frei verwendet und        */
//  veraendert werden, solange die folgenden Bedingungen anerkannt werden:    */
//                                                                            */
//  (1) Alle Aenderungen muessen als solche kenntlich gemacht werden, so dass */
//      nicht der Eindruck erweckt wird, es handle sich um die Original-      */
//      Software.                                                             */
//                                                                            */
//  (2) Die Copyright-Hinweise und die uebrigen Angaben in diesem Feld        */
//      duerfen weder entfernt noch veraendert werden.                        */
//                                                                            */
//  (3) Weder der Verlag noch der Autor uebernehmen irgendeine Haftung fuer   */
//      eventuelle Schaeden, die aus der Verwendung oder aus der Nicht-       */
//      verwendbarkeit der Software, gleich fuer welchen Zweck, entstehen     */
//      sollten, selbst dann nicht, wenn diese Schaeden durch Fehler verur-   */
//      sacht wurden, die in der Software enthalten sein sollten.             */
//                                                                            */
//*****************************************************************************/

#include <stdlib.h>
#include "flintpp.h"

static void report_error (LINT&, LINT&, LINT&, LINT&, int);
static void report_error (LINT&, LINT&, LINT&, int);
static void report_error (LINT&, LINT&, int);
static void testadd (int);
static void testsub (int);
static void testmul (int);
static void testsqr (int);
static void testdivr (int);
static void testmequ (int);
static void testmexp (int);
static void testfswap (int);
static int testset (int);
static int testshift (int);
static int testmax (void);

#define MAXTESTLEN (CLINTMAXBIT/2)
#define CLINTRNDLN (1 + ulrand64_l() % MAXTESTLEN)
#define nzrand_l(n_l,bits) do { rand_l((n_l),(bits)); } while (eqz_l(n_l))

int main ()
{
  cout << "Testmodul " << __FILE__
       << " compiliert fuer FLINT/C-Library Version "
       << verstr_l () << endl;

  initrand64_lt ();
  testadd (1000);
  testsub (1000);
  testmul (1000);
  testsqr (1000);
  testdivr (1000);
  testmequ (1000);
  testmexp (10);
  testfswap (1000);
  testset (1000);
  testshift (1000);
  testmax ();

  cout << "Alle Tests in " << __FILE__ << " fehlerfrei durchlaufen." << endl;
  return 0;
}


static void testadd (int no_of_tests)
{
  LINT a, b, c, d, e, m;
  int i;

  cout << "Testfunktion testadd() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      c = a = randl (CLINTRNDLN);
      d = b = randl (CLINTRNDLN);
      if ((( e = ( a + b )) != ( b + a )) ||
                   ( e != c.add (b))      ||
                   ( e != d.add (a))      ||
                   ( e != add (a, b))     ||
                   ( e != add (b, a)))
          report_error (a, b, c, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      d = b = randl (CLINTRNDLN);
      if (( a += b ) != ( c += d ))
          report_error (a, b, c, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      d = b = randl (CLINTRNDLN);
      do
        {
          m = randl (CLINTRNDLN);
        } while (0 == m);

      if ((( e = (( a + b ) % m )) != ( b + a ) % m ) ||
                   ( e != c.madd (b, m))              ||
                   ( e != d.madd (a, m))              ||
                   ( e != madd (a, b, m))             ||
                   ( e != madd (b, a, m)))
          report_error (a, b, c, d, __LINE__);
    }
}


static void testsub (int no_of_tests)
{
  LINT a, b, c, d, e, m;
  int i;

  cout << "Testfunktion testsub() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      c = a = randl (CLINTRNDLN);
      d = b = randl (CLINTRNDLN);
      if ((( e = ( a - b )) != 0 - ( b - a )) ||
                   ( e != c.sub (b))          ||
                   ( e != sub (a, b)))
        {
          cerr << "a - b = " << a - b;
          cerr << "0 - (b - a) = " << 0 - ( b - a );
          cerr << "sub(a,b) = " << sub (a, b);
          cerr << "a.sub(b) = " << a.sub (b);
          report_error (a, b, c, d, __LINE__);

          c = a = randl (CLINTRNDLN);
          d = b = randl (CLINTRNDLN);
          if (a < b)
            {
              fswap (a, b);
              fswap (c, d);
            }

          if (( a -= b ) != ( c -= d ))
              report_error (a, b, c, d, __LINE__);

          c = ( a = randl (CLINTRNDLN));
          d = ( b = randl (CLINTRNDLN));
          do
            {
              m = randl (CLINTRNDLN);
            } while (0 == m);

          if (c.msub (b, m) != msub (a, b, m))
              report_error (a, b, c, d, __LINE__);
        }
    }
}


static void testmul (int no_of_tests)
{
  LINT a, b, c, d, e, m;
  int i;

  cout << "Testfunktion testmul() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      c = a = randl (CLINTRNDLN);
      d = b = randl (CLINTRNDLN);
      if ((( e = ( a * b )) != ( b * a )) ||
                   ( e != c.mul (b))      ||
                   ( e != d.mul (a))      ||
                   ( e != mul (a, b))     ||
                   ( e != mul (b, a)))
          report_error (a, b, c, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      d = b = randl (CLINTRNDLN);
      if (( a *= b ) != ( c *= d ))
          report_error (a, b, c, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      d = b = randl (CLINTRNDLN);
      do
        {
          m = randl (CLINTRNDLN);
        } while (0 == m);

      if ((( e = (( a * b ) % m )) != ( b * a ) % m ) ||
                   ( e != c.mmul (b, m))              ||
                   ( e != d.mmul (a, m))              ||
                   ( e != mmul (a, b, m))             ||
                   ( e != mmul (b, a, m)))
          report_error (a, b, c, d, __LINE__);
    }
}


static void testsqr (int no_of_tests)
{
  LINT a, b, c, d, e, m;
  int i;

  cout << "Testfunktion testsqr() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      a = randl (CLINTRNDLN);
      b = a;
      if (( b * b ) != a.sqr ())
          report_error (a, b, c, d, __LINE__);

      a = randl (CLINTRNDLN);
      b = a;
      do
        {
          m = randl (CLINTRNDLN);
        } while (0 == m);

      if (( b = ( msqr (b, m))) != a.msqr (m))
          report_error (a, b, c, d, __LINE__);
    }
}


static void testdivr (int no_of_tests)
{
  LINT a, b, c, d, e, r, r1, r2;
  int i;

  cout << "Testfunktion testdivr() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      c = a = randl (CLINTRNDLN);
      do
        {
          d = b = randl (CLINTRNDLN);
        } while (0 == b);

      if ((( e = ( a / b )) != divr (a, b, r)) || ( e != c.divr (b, r)))
          report_error (a, b, c, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      do
        {
          d = b = randl (CLINTRNDLN);
        } while (0 == b);
      if (( a /= b ) != ( c /= d ))
          report_error (a, b, c, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      do
        {
          d = b = randl (CLINTRNDLN);
        } while (0 == b);

      divr (a, b, r1);
      a.divr (b, r2);
      if ((( e = ( c % d )) != r1 ) || ( e != r2 ))
          report_error (a, b, c, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      do
        {
          d = b = randl (CLINTRNDLN);
        } while (0 == b);
      if (( a %= b ) != ( c %= d ))
          report_error (a, b, c, d, __LINE__);
    }
}


static void testmequ (int no_of_tests)
{
  LINT a, b, k, m;
  int i;

  cout << "Testfunktion testmequ() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      a = randl (CLINTRNDLN);
      b = randl (CLINTRNDLN);
      do
        {
          m = randl (CLINTRNDLN);
        }
      while (m == 0);

      if ((mequ (a, b, m) && !(a % m == b % m)) || (!mequ (a, b, m) && (a % m == b % m)))
        report_error (a, b, m, __LINE__);

      if ((a.mequ (b, m) && !(a % m == b % m)) || (!a.mequ (b, m) && (a % m == b % m)))
        report_error (a, b, m, __LINE__);
    }

  for (i = 1; i < no_of_tests; i++)
    {

      b = randl (CLINTRNDLN);
      do
        {
          k = root (randl (CLINTRNDLN));
          m = root (randl (CLINTRNDLN));
        }
      while (m == 0 || k == 0);

      a = k * m + b;

      if ((mequ (a, b, m) && !(a % m == b % m)) || (!mequ (a, b, m) && (a % m == b % m)))
        report_error (a, b, m, __LINE__);

      if ((a.mequ (b, m) && !(a % m == b % m)) || (!a.mequ (b, m) && (a % m == b % m)))
        report_error (a, b, m, __LINE__);
    }
}


static void testmexp (int no_of_tests)
{
  LINT a, b, c, d, e, f, g, m;
  USHORT u;
  int i;

  cout << "Testfunktion testmexp() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      c = a = randl (CLINTRNDLN);
      e = b = randl (CLINTRNDLN);
      m = randl (CLINTRNDLN) | 1;

      if ((( d = mexp (a, b, m)) != mexp5m (a, b, m)) || ( d != mexpkm (a, b, m)))
          report_error (a, b, m, d, __LINE__);


      f = g = c = a = randl (CLINTRNDLN);
      b = randl (CLINTRNDLN);
      m = randl (CLINTRNDLN) | 1;

      if ((( d = a.mexp (b, m)) != c.mexp (b, m)) ||
                          (d != f.mexp5m (b, m)) ||
                          (d != g.mexpkm (b, m)))
          report_error (a, b, m, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      e = u = usrand64_l();
      if (a.mexp (u, m) != (d  = mexp (c, e, m)) ||
                           (d != mexp (c, u, m)))
          report_error (a, b, m, d, __LINE__);

      a = randl (CLINTRNDLN);
      e = u = usrand64_l();
      if (mexp (u, a, m) != (d = mexp (e, a, m)))
          report_error (a, e, m, d, __LINE__);

      c = a = randl (CLINTRNDLN);
      e = u = usrand64_l () % 512;
      if (a.mexp2 (u, m) != (d = mexp2 (c, u, m)))
          report_error (a, e, m, d, __LINE__);
    }
}


static void testfswap (int no_of_tests)
{
  LINT a, b, c, d;
  int i;

  cout << "Testfunktion testfswap() ..." << endl;

  for (i = 1; i < no_of_tests; i++)
    {
      a = c = randl (CLINTRNDLN);
      b = d = randl (CLINTRNDLN);
      a.fswap (b);
      if (( a != d ) || ( b != c ))
          report_error (a, b, d, c, __LINE__);
      fswap (a, b);
      if (( a != c ) || ( b != d ))
          report_error (a, b, c, d, __LINE__);
    }
}


static int testset (int no_of_tests)
{
  LINT s = 0, t;
  unsigned long pos;

  cout << "Testfunktion testset() ..." << endl;

  for (int i = 0; i < no_of_tests; i++)
    {
      s = randl (ulrand64_l () % CLINTMAXBIT);
      pos = ulrand64_l () % CLINTMAXBIT;
      t = s;
      s.setbit (pos);

      if (!s.testbit (pos))
        {
          cerr << "Fehler bei setbit, testbit:" << endl << s << endl << "Pos = " << pos << endl;
          exit (1);
        }
      s.clearbit (pos);

      if (s.testbit (pos))
        {
          cerr << "Fehler bei clearbit, testbit:" << endl << s << endl << "Pos = " << pos << endl;
          exit (1);
        }

      if (t.testbit (pos))
          t.clearbit (pos);

      if (s != t)
        {
          cerr << "Fehler bei setbit, clearbit:" << endl << s << endl << "Pos = " << pos << endl;
          exit (1);
        }
    }
  return 0;
}


static int testshift (int no_of_tests)
{
  LINT s = 0, t;
  unsigned long pos, shft;

  cout << "Testfunktion testshift() ..." << endl;

  s.setbit (CLINTMAXBIT - 1);

  t = s;
  t >>= ( CLINTMAXBIT - 1 );
  if (t != 1 || t != ( s >> ( CLINTMAXBIT - 1 )))
    {
      cerr << "Fehler bei shift: t != 1 || t != s" << endl << "t = " << t << endl;
      exit (1);
    }

  if (t << ( CLINTMAXBIT - 1 ) != s || ( t <<= ( CLINTMAXBIT - 1 )) != s)
    {
      cerr << "Fehler bei shift: t != s" << endl << "t = " << t << endl;
      exit (1);
    }


  for (int i = 0; i < no_of_tests; i++)
    {
      s = 0;
      pos = ulrand64_l () % CLINTMAXBIT;
      shft = ulrand64_l () % ( CLINTMAXBIT - pos );

      s.setbit (pos);

      t = s << shft;
      s <<= shft;
      if (!s.testbit (pos + shft) || s != t)
        {
          cerr << "Fehler bei shift:" << endl << s << endl << "pos = " << pos << endl;
          cerr << "pos + shft = " << pos + shft << endl;
          exit (1);
        }

      t = s >> shft;
      s >>= shft;

      if (!s.testbit (pos) || s != t)
        {
          cerr << "Fehler bei shift:" << endl << s << endl << "pos = " << pos << endl;
          cerr << "pos + shft = " << pos + shft << endl;
          exit (1);
        }
    }
  return 0;
}


static int testmax (void)
{
  cout << "Testfunktion testmax() ..." << endl;

  LINT a = randl (ulrand64_l () % CLINTMAXBIT);
  LINT b = randl (ulrand64_l () % CLINTMAXBIT);

  if (a > b)
      fswap (a, b);

  if (( min (a, b) != a ) || ( max (a, b) != b ))
    {
      cerr << "Fehler bei min oder max vor Zeile __LINE__" << endl;
      exit (1);
    }

  a = 1;
  b = 2;

  if (( min (a, LINT (2)) != 1 ) || ( max (LINT (1), b) != 2 ))
    {
      cerr << "Fehler bei min oder max vor Zeile __LINE__" << endl;
      exit (1);
    }
  return 0;
}



static void report_error (LINT& a, LINT& b, LINT& c, LINT& d, int line)
{
  cerr << "Fehler in Zeile " << line << endl;
  cerr << "a = " << a << endl;
  cerr << "b = " << b << endl;
  cerr << "c = " << c << endl;
  cerr << "d = " << d << endl;
  abort ();
}


static void report_error (LINT& a, LINT& b, LINT& c, int line)
{
  cerr << "Fehler in Zeile " << line << endl;
  cerr << "a = " << a << endl;
  cerr << "b = " << b << endl;
  abort ();
}


static void report_error (LINT& a, LINT& b, int line)
{
  cerr << "Fehler in Zeile " << line << endl;
  cerr << "a = " << a << endl;
  cerr << "b = " << b << endl;
  abort ();
}

