//*****************************************************************************/
//                                                                            */
//  Software zum Buch "Kryptographie in C und C++"                            */
//  Autor: Michael Welschenbach                                               */
//                                                                            */
//  Quelldatei testcnst.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 <string.h>
#include <stdlib.h>
#include "flintpp.h"


#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))

static int testconstr2  (int);
static int testconstr3  (int);
static int testconstr4  (int);
static int testconstr5  (int);
static int testconstr6a (int);
static int testconstr6b (int);
static int testconstr6c (int);
static int testconstr7  (int);
static int testconstr8  (int);
static int testconstr9  (int);
static int testconstr10 (int);
static int testconstr11 (int);
static int testconstr12 (int);

static void check (LINT&, LINT&, int);
static void check (CLINT, LINT&, int);
static void ulcheck (ULONG, LINT&, int);

void local_error_handler (LINT_ERRORS, const char * const, const int, const int);


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

  initrand64_lt ();

  LINT::Set_LINT_Error_Handler (local_error_handler);

  testconstr2  (1000);
  testconstr3  (5000);
  testconstr4  (1000);
  testconstr5  (5000);
  testconstr6a (5000);
  testconstr6b (5000);
  testconstr6c (5000);
  testconstr7  (5000);
  testconstr8  (5000);
  testconstr9  (5000);
  testconstr10 (5000);
  testconstr11 (5000);
  testconstr12 (5000);

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


static int
testconstr2 (int nooftests)
{
  cout << "Teste Konstruktor 2 ..." << endl;

  char* ofl;
  int i;
  if ((ofl = new char[(sizeof (char) * ( CLINTMAXDIGIT * 4 + 2 ))]) == NULL)
    {
      cerr << "Fehler bei operator new in testcnstr.cpp, Zeile "
           << __LINE__ << endl;
      abort ();
    }


  // Test Konstruktor 2 mit Zahl > max_l
  // Erzeuge string "1000...0" mit CLINTMAXDIGIT*16/4 Hex-Nullen

  ofl[0] = '1';
  ofl[CLINTMAXDIGIT * 4 + 1] = '\0';
  for (i = 1; (unsigned)i <= CLINTMAXDIGIT * 4; i++)
    {
      ofl[i] = '0';
    }

  LINT checkval (ofl, 16);
  if (checkval.Get_Warning_Status () != E_LINT_OFL)
    {
      cerr << "Fehler in Konstruktor 2: Ueberlauf nicht erkannt." << endl;
      cerr << "checkval.Get_Warning_Status () = "
           << (int)checkval.Get_Warning_Status () << endl;
      exit (-1);
    }


  // Test Konstruktor 2 mit "0"

  ofl[0] = '0';
  ofl[1] = '\0';

  LINT nul (0);
  checkval = LINT (ofl, 16);
  check (nul_l, checkval, __LINE__);


  // Test Konstruktor 2 mit ""

  ofl[0] = '\0';

  checkval = LINT (ofl, 16);
  check (nul_l, checkval, __LINE__);

  delete [] ofl;


  // Tests mit zufaelligen Werten

  LINT test;

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      checkval = LINT (lint2str (test, 16), 16);
      check (test, checkval, __LINE__);
    }

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      checkval = LINT (lint2str (test, 2), 2);
      check (test, checkval, __LINE__);
    }

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      checkval = LINT (lint2str (test, 8), 8);
      check (test, checkval, __LINE__);
    }

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      checkval = LINT (lint2str (test, 10), 10);
      check (test, checkval, __LINE__);
    }

  return 0;
}


static int
testconstr3 (int nooftests)
{
  cout << "Teste Konstruktor 3 ..." << endl;

  UCHAR* ofl;
  int i, len;

  if ((ofl = new UCHAR[(sizeof (UCHAR) * ( CLINTMAXBYTE + 2 ))]) == NULL)
    {
      cerr << "Fehler bei operator new in testcnstr.cpp, Zeile "
           << __LINE__ << endl;
      abort ();
    }


  // Test Konstruktor 3 mit Zahl > max_l
  // Erzeuge Bytevektor 0xff,0xff,...,0xff,0x01 mit CLINTMAXBYTE+1 Stellen

  for (i = 0; (unsigned)i < CLINTMAXBYTE; i++)
    {
      ofl[i] = 0xff;
    }
  ofl[CLINTMAXBYTE] = 0x01;

  LINT checkval (ofl, CLINTMAXBYTE+1);
  if (checkval.Get_Warning_Status () != E_LINT_OFL)
    {
      cerr << "Fehler in Konstruktor 3: Ueberlauf nicht erkannt." << endl;
      cerr << "checkval.Get_Warning_Status () = "
           << (int)checkval.Get_Warning_Status () << endl;
      exit (-1);
    }


  // Test Konstruktor 3 mit 0x00

  ofl[0] = 0x00;

  checkval = LINT (ofl, 1);
  check (nul_l, checkval, __LINE__);


  // Test Konstruktor 3 mit Byte-Vektor der Laenge 0

  checkval = LINT (ofl, 0);
  check (nul_l, checkval, __LINE__);

  delete [] ofl;


  // Tests mit zufaelligen Werten

  LINT test;

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);        // LINT-Zufallszahl
      ofl = lint2byte (test, &len);     // transformiert in Byte-Vektor
      checkval = LINT (ofl, len);       // Konstruktor 3 konstruiert LINT-Zahl
      check (test, checkval, __LINE__);
    }

  return 0;
}


static int
testconstr4 (int nooftests)
{
  cout << "Teste Konstruktor 4 ..." << endl;

  char* ofl;
  int i;

  if ((ofl = new char[CLINTMAXBIT + 3]) == NULL)
    {
      cerr << "Fehler bei operator new in testcnstr.cpp, Zeile "
           << __LINE__ << endl;
      abort ();
    }


  ofl[0] = '0';                           // Erzeuge string "0x1000...0" mit
  ofl[1] = 'x';                           // CLINTMAXDIGIT*16/4 Nullen
  ofl[2] = '1';
  ofl[CLINTMAXDIGIT * 4 + 3] = '\0';
  for (i = 3; (unsigned)i <= CLINTMAXDIGIT * 4 + 2; i++)
    {
      ofl[i] = '0';
    }

  LINT checkval (ofl);
  if (checkval.Get_Warning_Status () != E_LINT_OFL)
    {
      cerr << "Fehler in Konstruktor 4: Ueberlauf nicht erkannt." << endl;
      cerr << "checkval.Get_Warning_Status () = "
           << (int)checkval.Get_Warning_Status () << endl;
      exit (-1);
    }


  // Test Konstruktor 4 mit "0"

  ofl[0] = '0';
  ofl[1] = '\0';

  checkval = LINT (ofl);
  check (nul_l, checkval, __LINE__);


  // Test Konstruktor 4 mit ""

  ofl[0] = '\0';

  checkval = LINT (ofl);
  check (nul_l, checkval, __LINE__);


  // Tests mit zufaelligen Werten

  LINT test;

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      checkval = LINT (lint2str (test, 10));
      check (test, checkval, __LINE__);
    }

  char* N;
  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      N = lint2str (test, 2);
      strcpy (ofl, "0b");
      strcat (ofl, N);
      checkval = LINT (ofl);
      check (test, checkval, __LINE__);
    }

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      N = lint2str (test, 2);
      strcpy (ofl, "0B");
      strcat (ofl, N);
      checkval = LINT (ofl);
      check (test, checkval, __LINE__);
    }

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      N = lint2str (test, 16);
      strcpy (ofl, "0x");
      strcat (ofl, N);
      checkval = LINT (ofl);
      check (test, checkval, __LINE__);
    }

  for (i = 1; i <= nooftests; i++)
    {
      test = randl (CLINTRNDLN);
      N = lint2str (test, 16);
      strcpy (ofl, "0X");
      strcat (ofl, N);
      checkval = LINT (ofl);
      check (test, checkval, __LINE__);
    }

  delete [] (ofl);
  return 0;
}


static int
testconstr5 (int nooftests)
{
  cout << "Teste Konstruktor 5 ..." << endl;


  // Test mit 0

  LINT checkval (LINT (0));
  check (nul_l, checkval, __LINE__);


  // Test mit max

  CLINT nmax_l;
  setmax_l (nmax_l);
  LINT nmax (nmax_l);                   // Hier kommt Konstruktor 12 ins Spiel
  checkval = LINT (nmax);
  check (nmax, checkval, __LINE__);


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      LINT test = randl (CLINTRNDLN);
      checkval = LINT (test);
      check (test, checkval, __LINE__);
    }

  return 0;
}


static int
testconstr6a (int nooftests)
{
  cout << "Teste Konstruktor 6 mit char ..." << endl;


  // Test mit 0

  char t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);      //lint !e732 Signed/unsigned mismatch
                                        // wird in Kauf genommen


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = (ucrand64_l () % 0x80);
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);  //lint !e732 s.o.
    }

  return 0;
}


static int
testconstr6b (int nooftests)
{
  cout << "Teste Konstruktor 6 mit short ..." << endl;


  // Test mit 0

  short t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);      //lint !e732 s.o.


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = (usrand64_l () % 0x8000);
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);  //lint !e732 s.o.
    }

  return 0;
}


static int
testconstr6c (int nooftests)
{
  cout << "Teste Konstruktor 6 mit int ..." << endl;


  // Test mit 0

  int t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);      //lint !e732 s.o.



  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = (ulrand64_l () % 0x80000000);
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);  //lint !e732 s.o.
    }

  return 0;
}


static int
testconstr7 (int nooftests)
{
  cout << "Teste Konstruktor 7 ..." << endl;


  // Test mit 0

  long t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);      //lint !e732 s.o.


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = (ulrand64_l () % 0x80000000);
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);  //lint !e732 s.o.
    }

  return 0;
}


static int
testconstr8 (int nooftests)
{
  cout << "Teste Konstruktor 8 ..." << endl;


  // Test mit 0

  UCHAR t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = ucrand64_l ();
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);
    }

  return 0;
}


static int
testconstr9 (int nooftests)
{
  cout << "Teste Konstruktor 9 ..." << endl;


  // Test mit 0

  USHORT t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = usrand64_l ();
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);
    }

  return 0;
}


static int
testconstr10 (int nooftests)
{
  cout << "Teste Konstruktor 10 ..." << endl;


  // Test mit 0

  unsigned int t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = ulrand64_l ();
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);
    }

  return 0;
}


static int
testconstr11 (int nooftests)
{
  cout << "Teste Konstruktor 11 ..." << endl;


  // Test mit 0

  ULONG t = 0;
  LINT checkval (t);
  ulcheck (t, checkval, __LINE__);


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      t = ulrand64_l ();
      checkval = LINT (t);
      ulcheck (t, checkval, __LINE__);
    }

  return 0;
}


static int
testconstr12 (int nooftests)
{
  cout << "Teste Konstruktor 12 ..." << endl;

  CLINT test_l;


  // Test mit 0

  LINT checkval (nul_l);
  check (nul_l, checkval, __LINE__);


  // Test mit max_l in testconstr5 ()


  // Tests mit zufaelligen Werten

  for (int i = 1; i <= nooftests; i++)
    {
      rand_l (test_l, CLINTRNDLN);
      checkval = LINT (test_l);
      check (test_l, checkval, __LINE__);
    }

  return 0;
}


static void
check (LINT &a, LINT& b, int line)
{
  if (!(b >= 0))                        // Teste implizit Wert von b.init
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "Objekt nicht initialisiert" << endl;
    }

  if (b.Get_Warning_Status () != E_LINT_OK)
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "Objekt-Status nicht OK" << endl;
    }

  if (a != b)
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "a = " << a << endl;
      cerr << "b = " << b << endl;
      exit (1);
    }
}


static void
check (CLINT a_l, LINT& b, int line)
{
  if (!(b >= 0))                        // Teste implizit Wert von b.init
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "Objekt nicht initialisiert" << endl;
    }

  if (b.Get_Warning_Status () != E_LINT_OK)
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "Objekt-Status nicht OK" << endl;
    }

  UCHAR* N1,* N2;
  int len1, len2;

  N1 = clint2byte_l (a_l, &len1);
  N2 = lint2byte (b, &len2);

  if (len1 != len2)
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "a = " << hexstr_l (a_l) << endl;
      cerr << "b = " << b << endl;
      exit (1);
    }

  if (strncmp ((const char*)N1, (const char*)N2, (unsigned)len1) != 0)
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "a = " << hexstr_l (a_l) << endl;
      cerr << "b = " << b << endl;
      exit (1);
    }
}


static void
ulcheck (ULONG a, LINT& b, int line)
{
  if (!(b >= 0))                        // Teste implizit Wert von b.init
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "Objekt nicht initialisiert" << endl;
    }

  if (b.Get_Warning_Status () != E_LINT_OK)
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "Objekt-Status nicht OK" << endl;
      cerr << "Warning-Status = " << (int)b.Get_Warning_Status() << endl;
    }

  if (LINT (a) != b)
    {
      cerr << "Fehler in Zeile " << line << endl;
      cerr << "a = " << a << endl;
      cerr << "b = " << b << endl;
      exit (1);
    }
}


void local_error_handler (LINT_ERRORS err, const char* const func, const int arg, const int line)
{
  return;
}



