
//
// LiDIA - a library for computational number theory
//   Copyright (c) 1996 by the LiDIA Group
//
// File        : gf2n_init.c 
// Author      : Franz-Dieter Berger (FDB), Patric Kirsch (PK),
//		 Volker Mueller (VM)
// Last change : VM, Sep 26 1996, initial version
//		 MM, Sep 26 1996,
//		     Added this header and the if defined statements
//		     for MAC.
//

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

#include <fstream.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


/*********************************************************************/

inline unsigned int max(unsigned int a, unsigned int b)
{
  if (a >= b)
    return a;
  else return b;
}

inline unsigned int min(unsigned int a, unsigned int b)
{
  if (a <= b)
    return a;
  else return b;
}



/*********************************************************************/

char* gf2n::read_modulus_from_database (unsigned int in_degree)
{
  char name_database[1000];
  char line[1024];
  char *p = line;
  unsigned int d, i;
  char ch;
  bool found = false;
  
  if (getenv("LIDIA_GF2N") != NULL)
    strcpy(name_database, getenv("LIDIA_GF2N"));
  else 
    strcpy(name_database, LIDIA_GF2N_DB);

  ifstream database(name_database);

  if (database.fail())   
    lidia_error_handler("gf2n_init", "read_modulus_from_database::Database not found ");

  while((!database.eof()) && (found == false))
    {
      database >> d; 

      if (d == in_degree)
	{
	  sprintf(line,"%d",d);
	  i = 1;
	  do 
	    {
	      i++; 
	      database >> d;
	      sprintf(line,"%s %d",line, d);
	    } 
	  while (d !=0);
	  found = true;
	}
      else
	{
	  do 
	    {
	      database.get(ch);
	    } 
	  while (ch != '\n');
	}
    }

  if (found == true)
    {
      database.close();
      p = new char[strlen(line)+1];
      strcpy(p, line);
    }

  if (database.eof() && (found == false))
    lidia_error_handler("gf2n_init","read_modulus_from_database::Modulus not found in Database"); 
  return p;
}
      

/*******************************************************************
  the two possible initialization routines
  *****************************************************************/

static bool already_init = false;

void gf2n_init (unsigned int degree, gf2nIO::base base)
{
  char* poly;
  
  if (already_init == false)
    {
      poly = gf2n::read_modulus_from_database(degree);
      gf2n_init (poly, base);
    }
  else
    lidia_warning_handler("gf2n","gf2n_init::gf2n already initialized");
}

/**********************************************************************/

void gf2n_init (char * poly, gf2nIO::base mode) 
{
 char *str = poly;
 unsigned int i, h;
 bool constant_term = false;

 if (already_init == true)
   {
     lidia_warning_handler("gf2n","gf2n_init::gf2n already initialized");
     return;
   }
 else already_init = true;


 /*------- compute number of exponents and degree  ----------*/

 i = 1;
 sscanf(str,"%ud",&h);
 gf2n::degree = h;

 if (h == 0)
   constant_term = true;
 
 do 
   {
     str = strchr(str, ' ');
     if (str == (char*) NULL)
       break;
     else str++;
     sscanf(str,"%ud", &h);
     if (h > gf2n::degree)
       gf2n::degree = h;
     if (h == 0)
       constant_term = true;
     i++;
   }
 while (1);

 if (constant_term == false)
   lidia_error_handler("gf2n","gf2n_init::input polynomial not irreducible");

 gf2n::ord_is_fact = false;
 gf2n::anzBI = (gf2n::degree / (8*sizeof(udigit))) + 1;

 if (gf2n::anzBI > FIXMUL)
   gf2n::mulsel = FIXMUL;
 else
   gf2n::mulsel = gf2n::anzBI;
 
  gf2n::anz_exponents = i-2;
  gf2n::exponents = new unsigned int[gf2n::anz_exponents];

  str = poly;
  for (i=0; i<gf2n::anz_exponents; i++)
    {
      str = strchr(str, ' ');
      str++;
      sscanf(str,"%ud", & h);
      while (h == 0 || h == gf2n::degree)
	{
	  str = strchr(str, ' ');
	  str++;
	  sscanf(str,"%ud", & h);
	}
      gf2n::exponents[i] = h;
    }
 

 /*---- set invsel and initialize variables for inversion and reduction */

 if ((gf2n::anz_exponents == 1) && 
     ((gf2n::degree - gf2n::exponents[0]) >= 8*sizeof(udigit)) &&
     (gf2n::exponents[0] >= 8*sizeof(udigit)))
   {
     gf2n::invsel = 0;
     gf2n::exp1 = gf2n::exponents[0];
     delete gf2n::exponents;
   }
 else
   if (gf2n::anz_exponents == 3)
     {
       unsigned int ma, mi;
       
       ma = max(gf2n::exponents[0], max(gf2n::exponents[1], gf2n::exponents[2]));
       mi = min(gf2n::exponents[0], min(gf2n::exponents[1], gf2n::exponents[2]));
       if ((gf2n::degree - ma) >= 32 && mi >= 32)
	 {
	   gf2n::invsel = 1;
	   gf2n::exp1 = gf2n::exponents[0];
	   gf2n::exp2 = gf2n::exponents[1];
	   gf2n::exp3 = gf2n::exponents[2];
	   delete gf2n::exponents;
	 }
       else
	 gf2n::invsel = 2;
     }
 else
   gf2n::invsel = 2;


  /* ---- INITIIALIZE OPERATIONS  --------------------------*/

  gf2n::tmp_double = new udigit[2*gf2n::anzBI]; /* allocate memory for tmp var. */

  gf2n::generate_mul_table ();
  gf2n::generate_square_table ();
  
  
  
  /* ---------- Initialisation of  I/O ---------------- */
  
  gf2nIO::setbase (mode);
  gf2nIO::setprefix (mode);
}





