//*****************************************************************************/
//                                                                            */
//  Software zum Buch "Kryptographie in C und C++"                            */
//  Autor: Michael Welschenbach                                               */
//                                                                            */
//  Quelldatei testchin.cpp Stand: 22.01.2001                                 */
//                                                                            */
//  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"

#define MAXTESTLEN (CLINTMAXBIT/2)
#define CLINTRNDLN (1 + ulrand64_l() % MAXTESTLEN/2)

static int simple_tests (void);
static int general_tests (int);

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

  initrand64_lt ();
  initrandBBS_lt ();
  simple_tests ();
  general_tests (10000);

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


static int simple_tests ()
{
  LINT a, b, c, m, n, o, x, y;
  LINT **koeff = new LINT *[8];
  unsigned int l;

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

  koeff[0] = &a;
  koeff[1] = &m;
  koeff[2] = &b;
  koeff[3] = &n;

  // Test mit n = 0

  l = CLINTRNDLN;
  a = randl (usrand64_l () % l / 4);
  m = randl (l);
  b = randl (usrand64_l () % l / 4);
  n = 0;

  x = chinrem (2, koeff);
  if (x.Get_Warning_Status () == E_LINT_OK)
      cerr << "Fehler in Friend-Funktion chinrem in Zeile " << __LINE__ << endl;

  y = a.chinrem (m, b, n);
  if (y.Get_Warning_Status () == E_LINT_OK)
      cerr << "Fehler in Member-Funktion chinrem in Zeile " << __LINE__ << endl;


  // Test mit m = 1

  do
    {
      l = CLINTRNDLN;
    }
  while (4 > l);

  a = randl (usrand64_l () % l / 4);
  m = 1;
  b = 0;
  n = randl (l);

  x = chinrem (2, koeff);
  if (x.Get_Warning_Status () == E_LINT_ERR)
      cerr << "Fehler in Friend-Funktion chinrem in Zeile " << __LINE__ << endl;

  y = a.chinrem (m, b, n);
  if (y.Get_Warning_Status () == E_LINT_ERR)
      cerr << "Fehler in Member-Funktion chinrem in Zeile " << __LINE__ << endl;

  delete [] koeff;
  return 0;
}


static int general_tests (int no_of_tests)
{
  LINT a, b, c, m, n, o, x, y;
  LINT **koeff = new LINT *[8];
  unsigned int l;

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

  koeff[0] = &a;
  koeff[1] = &m;
  koeff[2] = &b;
  koeff[3] = &n;
  koeff[4] = &c;
  koeff[5] = &o;

  for (int i = 1; i < no_of_tests; i++)
    {
      do
        {
          l = CLINTRNDLN;
          m = randl (l);
        }
      while (10 >= m);

      do
        {
          n = randl (usrand64_l () % l / 3);
        } while (gcd (m, n) != 1);

      do
        {
          o = randl (usrand64_l () % l / 3);
        } while (( gcd (m, o) != 1 ) || ( gcd (n, o) != 1 ));

      a = randl (usrand64_l () % l / 4);
      b = randl (usrand64_l () % l / 4);
      c = randl (usrand64_l () % l / 4);

      x = chinrem (3, koeff);
      if (msub (x, a, m) != 0 || msub (x, b, n) != 0 || msub (x, c, o) != 0)
        {
          cerr << "Fehler in friend-Funktion chinrem" << endl;
          cerr << "x = " << x << endl;
          cerr << "a = " << a;
          cerr << "b = " << b;
          cerr << "c = " << c;
          cerr << "x - a = " << x - a;
          cerr << "x - b = " << x - b;
          cerr << "x - c = " << x - c;
          cerr << "x - a % m = " << ( x - a ) % m;
          cerr << "x - b % n = " << ( x - b ) % n;
          cerr << "x - c % o = " << ( x - c ) % o;
          cerr << "x.status = " << (int) x.Get_Warning_Status () << endl;
          exit (-1);
        }
      else
        {
          // cout << "Loesung der Kongruenz:  " <<  x << endl;
        }

      y = a.chinrem (m, b, n);

      if (msub (y, a, m) != 0 || msub (y, b, n) != 0)
        {
          cerr << "Fehler in member-Funktion chinrem" << endl;
          cerr << "y = " << y;
          cerr << "m = " << m;
          cerr << "n = " << n;
          cerr << "y.status = " << (int) y.Get_Warning_Status () << endl;
          cerr << "a = " << a;
          cerr << "b = " << b;
          cerr << "y - a = " << y - a;
          cerr << "y - b = " << y - b;
          cerr << "y - a % m = " << ( y - a ) % m << endl;
          cerr << "y - b % n = " << ( y - b ) % n << endl;
          exit (-1);
        }
      else
        {
          // cout << "Loesung der Kongruenz:  " <<  y << endl;
        }
    }

  delete [] koeff;
  return 0;
}

