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



// Use portability-random now, MM, 21.11.96

// Changed this, so that it compiles on HP-UX, Thomas P., 17.02.95
// #if defined(HAVE_RANDOM)
// #include <stdlib.h>
// #else
// #if !defined(__i386) || defined(SOLARIS)
// #include <LiDIA/random.h>
// #endif
#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:random.h>
#else 
#include <LiDIA/random.h>
#endif

#if defined(__GNUG__) && !defined(__mips) && !defined(SOLARIS)
extern "C" int gettimeofday(struct timeval *, struct timezone *);
#endif

const unsigned int rational_factorization::ecm_params[29][3] = {{151, 1009, 3},
                                {211, 2003, 4}, 
                                {283, 3203, 5}, 
                                {349, 4297, 5},
                                {411, 8861, 6}, 
                                {659, 17981, 8},
                                {997, 31489, 10}, 
                                {1439, 54617, 13},
                                {2111, 89501, 17}, 
                                {3067, 144961, 21},
                                {4391, 245719, 27}, 
                                {6287, 382919, 33},
                                {8839, 577721, 42}, 
                                {12241, 845951, 54},
                                {16339, 1285633, 68}, 
                                {21977, 1788863, 88},
                                {29860, 2508269, 111}, 
                                {41011, 3550163, 137},
                                {53214, 5139913, 172}, 
                                {71537, 6996393, 215},
                                {97499, 9650917, 261}, 
                                {125653, 13767227, 325},
                                {168273, 18372001, 398},
                                {223559, 24108067, 489},
                                {280891, 33036147, 612},
                                {376513, 43533099, 736},
                                {489249, 54671451, 909},
                                {631461, 77351879, 1088},
                                {830049, 97901685, 1315}};


unsigned int rational_factorization::
ecm_read_max(int stell)               // returns the limit for prime numbers 
{                                     // to execute the given strategy
   return ( ecm_params[stell-6][1] );
}      


inline void init_random()             // initialization for the random 
{                                     // number generator 
//  struct timeval *tv;
//  tv = (struct timeval*) malloc (sizeof(struct timeval));
//  gettimeofday(tv, NULL);
//  srandom((int)(tv->tv_usec));
//  free (tv);

   struct timeval tv;
   gettimeofday(&tv, NULL);
   srandom((int)((&tv)->tv_usec));
}


int rational_factorization::
ecm_job_planing(int strat[30], int buf[30][5])
{                                     // read the parameters for ECM for
				      // a given strategy
  int i = 0, j = strat[0];
  unsigned int job = 1;
  
  if ( info )
  {
    cout << "Strategy for ECM : \n\n";
    cout << "   #    |             |     prime limits    | #curves \n";
    cout << " digits |   curves    |  step 1  |  cont    |  total \n";
    cout << "-------------------------------------------------------\n";
    cout.flush();
  }
  
  while ( j )
  {
//    if ( (j < 0) || (j > 34) )
//      error_handler("rational_factorization", "ecm_job_planing::parameters out of range");

    j -= 6;
    
    buf[i][0] = ecm_params[j][0];
    buf[i][1] = buf[i][0];
    buf[i][2] = ecm_params[j][1];
    buf[i][3] = ecm_params[j][2];
    buf[i][4] = buf[i][3];
    
    if ( info )  printf("   %2d   | %4d -", strat[i], job); 

    job += buf[i][3];

    if ( info ) 
      printf(" %4d |%9d | %8d | %4d\n", job-1, buf[i][0], buf[i][2], buf[i][3]);
	
    j = strat[++i];
  }
  
  if ( info ) 
  {
    fflush(stdout);
    cout << "========================================================\n";
    cout.flush();
  } 
  
  return(i);
}

void mecfind(ec_curve &me, ec_point_M &mp, bigint &factor, unsigned int B, unsigned int C, ecm_primes &prim)
{                       
                         // first step of ECM:
			 // compute  mp  <--  K * mp 
			 // K is the product of all primes powers up to B
   unsigned int p;

   bigmod a_2, m1, inv_4(4);
				    
   int k, count = 0, sqrt_C = (int) sqrt ((double) C);
  
   inv_4.invert(0);             // precompute the constant a_2 = (me.a+2)/4
   add(m1, me.A(), 2);         // for the given curve me
   multiply(a_2, inv_4, m1);   

   for (k=0; k < 31; k++)			 
     multiply_by_2(mp, mp, a_2);              // mp = 2^30 * mp 
    
                             //  successive computation of    mp = p^k * mp
                             //  for all primes p = 3, ..., B   and      
                             //  k integer with p^k <= C && p^(k+1) > C
   while ( (p = prim.getprimes()) <= sqrt_C )      // loop: exponent k > 1
   {
      k = (int)( (double)log((double)C)/(double)log((double)p) );
      multiply(mp, mp, a_2, p, power(p, k));
   }
  
   do                                        // loop: exponent k = 1
   {
      multiply(mp, mp, a_2, p, p); 

      if (count++ > 1000) 
      {
         m1.assign( mp.Z() );
         factor = m1.invert(1);
	 if ( !factor.is_one() ) 	    return;
      
	 count = 0;
	 multiply(m1, m1, mp.X());
	 mp.assign(m1, bigmod(1));
      }
   }
   while ( (p = prim.getprimes()) < B );
			 
   m1.assign(mp.Z());
   factor = m1.invert(1);
   if ( !factor.is_one() )     return;            
  
   multiply(m1, m1, mp.X());
   mp.assign(m1, bigmod(1)); 
}   


void cont(ec_point_W &wp, bigint &factor, unsigned int B, unsigned int D, ecm_primes & prim)
{   
  register int w, u, v, count;
  
  unsigned int p;
  
  bigmod * x, h, c(1);
  
  ec_point_W wq;
  
                             // compute the number of babysteps w 
			     // and an initial value v for then giantsteps
  u = (int) ceil( sqrt((double)D) );
  
  w = (u / 30) * 30;              
  if ( w + 30 - u < u - w )    w += 30; 
  D = w * w;
  v = (int) B / w;
  
  x = new bigmod[w+2];
  
                          // precompute babysteps:
                          // k * wp for k = 1, .., w
			       
  for ( count = 1; count <= w; count += 2)
  {
    add(wq, wp, wq, factor);
    if ( !factor.is_one() ) 
    {
      delete [] x;
      return;
    }
      
    if ( gcd(w, count) == 1 )
      x[count] = wq.X();
      
    add(wq, wp, wq, factor);
    if ( !factor.is_one() ) 
    {
      delete [] x;
      return;
    }
  }
  
                         // computed: wq = w * wp 
                         // initialization for giantsteps:
			 //     wp = v * wq = v * w * wp 
      
  multiply(wp, wq, v, factor);
  if ( !factor.is_one() ) 
  {
    delete [] x;
    return;
  }
   
  count = 1; 
  p = prim.getprimes();
                          // giantsteps
  while( p < D && p > 1 )
  {
    if ( (u = v*w - p) < 1 )
    {
      v++;
      add(wp, wp, wq, factor);
      if ( !factor.is_one() )
      {
	delete [] x;
	return;
      }
      
      u = v*w - p;
      if ( u < 0 )      u += w ;
    }
    
    subtract(h, wp.X(), x[u]);
    
    multiply(c, c, h);
    
    if ( count++ >= 500 )
    {
      count = 1;
      factor = c.invert(1);
      if( !factor.is_one() )
      {
	delete [] x;
	return;
      }
    }
    p = prim.getprimes();
  }
  
  factor = c.invert(1);
  delete [] x;
}



void rational_factorization::
ecm(lidia_size_t index, int ecm_restlength, int jobs_avail, int job_buffer[30][5], ecm_primes & prim) 
{
  bigint N (factors[index].single_base);
  int exp = factors[index].single_exponent;
  lidia_size_t n = no_of_comp();
  
  unsigned int B, C, D; 
  int uc, current_length, count = 0, m, job_nr = 0, job_grp = 0, qs_2;
  
  char *n_string = new char[( N.bit_length() / 3 ) + 10];

  current_length = bigint_to_string(N, n_string);
  uc = ( (current_length-30) / 3 ) << 1;
  
  if ( current_length <= ecm_restlength && uc <= 0)
  {
    if ( info ) 
    {
      cout << "\nrestlength less than ecm_restlength \n";
      cout.flush();
    }
    
    delete [] n_string;
    return;
  }

  if ( (factors[index].factor_state >> 2) > 0 )
  {
    unsigned int params_for_computed 
      = ecm_params[(factors[index].factor_state >> 2)-6][0];
    
    while(job_buffer[job_grp][0] < params_for_computed)
      job_grp ++;
    
    jobs_avail -= job_grp;
  }

  B = job_buffer[job_grp][0];        // first job
  C = job_buffer[job_grp][1];
  D = job_buffer[job_grp][2];

  bigint old_modul = bigmod::modulus();
  bigmod::set_modulus(N);
  
  rf_single_factor fact;
  bigint factor, Q, R;
  factor.assign_one();

  ec_curve ell_curve;
  ec_point_M mp;
  ec_point_W wp;
  
  timer ti;
  qs_2 = (int) h_zeitqs(current_length) >> 1;
  
  init_random();
  
  ti.start_timer();

  while ( !N.is_one() && jobs_avail > 0 )   
  {
    while ( jobs_avail> 0 )
    {
      prim.resetprimes(1);

      if ( job_buffer[job_grp][3] < 1 )
      {
	if ( --jobs_avail <= 0 )
	  break;
	
	job_grp++;
	B = job_buffer[job_grp][0];        // next job 
	C = job_buffer[job_grp][1];
	D = job_buffer[job_grp][2];
      }
      
      job_buffer[job_grp][3]--;

      if ( info )
      {
	cout << "\n" << ++job_nr << ".curve ";
	cout.flush();
      }

      m = (int) random();

      mecgen16(ell_curve, mp, factor, m); 
      if ( !factor.is_one() )                    break;
     
      mecfind(ell_curve, mp, factor, B, C, prim);
      if ( !factor.is_one() )                    break;
      
      factor = trans(ell_curve, wp, mp);
      if ( !factor.is_one() )                    break;
      
      cont(wp, factor, B, D, prim);
      if ( !factor.is_one() )                    break;
      
      ti.stop_timer();
      ti.cont_timer();

//      cout << " Time: " << (ti.real_time()+ti.sys_time())/100; 
//      cout.flush();

      if ( ( current_length <= ecm_restlength && --uc <= 0 ) ||
           ( (ti.user_time()+ti.sys_time())/100 > qs_2) )
      {
//	if (info) 
//	{
//	  cout << "\nfactor with QS\n";
//	  cout << "current_length = " << current_length << "  ecm_restlength = " << ecm_restlength << " uc = " << uc ;
//	  cout << " Time : " << (ti.user_time()+ti.sys_time())/100 << " qs_2 = " << qs_2 << " \n";
//	  cout.flush();
//	}
	
	fact.factor_state = not_prime;
	    
	fact.single_base = N;
	fact.single_exponent = exp;
	factors[index] = fact;
	
	delete [] n_string;
        if ( !old_modul.is_zero() ) bigmod::set_modulus(old_modul);
	return;
      }
    }

    if ( jobs_avail > 0 )
    {
      if ( !(factor.is_one() || factor.is_zero() || factor == N))
      {
	div_rem(Q, R, N, factor);
	
	while ( R.is_zero() )
        {
	  count++;
	  N.assign(Q);
	  div_rem(Q, R, N, factor);
	}

	if ( count )
        { 
	  if (info)
	  {
	    if ( count > 1 ) 
	      cout << "\nfactor " << factor << " ^ " << count;
	    else
	      cout << "\nfactor " << factor;
	    cout.flush();
	  }
	  
	  if ( is_prime(factor, 8) )
	    fact.factor_state = prime;
	  else
	    fact.factor_state = not_prime;
	  
	  fact.single_base = factor;
	  fact.single_exponent = count * exp;

          count=0; // ts

	  if ( N.is_one() )
	  {
	    factors[index] = fact;
	    break;
	  }
	  else
	    factors[n++] = fact;
	  
	  if ( is_prime(N, 8))
	  {
	    fact.factor_state = prime;
	    
	    fact.single_base = N;
	    fact.single_exponent = exp;
	    factors[index] = fact;
                  
	    if ( info )
            {
	      cout << "\nprime number " << N << "\n";
	      cout.flush();
	    }

	    N.assign_one();
	    break;
	  }
	  ti.stop_timer();
	  ti.cont_timer();
//	  cout << " Time: " << ti.real_time()/100; 
//	  cout.flush();

	  current_length = bigint_to_string(N, n_string);
	  uc = ( (current_length-30) / 3 ) << 1;
//	  cout << "\nnew uc = " << uc;
	  
	  qs_2 = (int) h_zeitqs(current_length) >> 1;
	  if ( ( current_length <= ecm_restlength && uc <= 0 ) ||
             ( (ti.user_time()+ti.sys_time())/100 > qs_2) )
    
	  {
//	    cout << "current_length = " << current_length << "  ecm_restlength = " << ecm_restlength << " uc = " << uc ;
//	    cout << " Time : " << (ti.user_time()+ti.sys_time())/100 << " qs_2 = " << qs_2 << " \n";
//	    cout.flush();

	    fact.factor_state = not_prime;
	    fact.single_base = N;
	    fact.single_exponent = exp;
	    factors[index] = fact;

	    delete [] n_string;
            if ( !old_modul.is_zero() ) bigmod::set_modulus(old_modul);
	    return;
	  }
	  
	  bigmod::set_modulus(N);

	} 
      }
    }
  }  
       

 if ( !N.is_one() )

  {
    int i=0;
    
    unsigned int table = job_buffer[job_grp][0];
    
    while( ecm_params[i][0] != table )
      i++;

    i += 6;
    
    fact.single_base = N;
    fact.single_exponent = exp;
    fact.factor_state = (not_prime) | (i << 2);
    factors[index] = fact;
  } 

  delete [] n_string;
  if ( !old_modul.is_zero() ) bigmod::set_modulus(old_modul);
}   


void rational_factorization::
ecm(lidia_size_t index, int jobs_avail, int job_buffer[30][5], ecm_primes & prim)
{
  bigint N (factors[index].single_base);
  int exp = factors[index].single_exponent;
  lidia_size_t n = no_of_comp();
  
  unsigned int B, C, D; 
  int count = 0, m, job_nr = 0, job_grp = 0;
  
  if ((factors[index].factor_state >> 2) > 0)
  {
    unsigned int params_for_computed 
      = ecm_params[(factors[index].factor_state >> 2)-6][0];
    
    while(job_buffer[job_grp][0] < params_for_computed)
      job_grp ++;
    
    jobs_avail -= job_grp;
  }
  
  B = job_buffer[job_grp][0];        // first job
  C = job_buffer[job_grp][1];
  D = job_buffer[job_grp][2];
  
  bigint old_modul = bigmod::modulus();
  bigmod::set_modulus(N);
  
  rf_single_factor fact;
  bigint factor, Q, R;
  factor.assign_one();
   
  ec_curve ell_curve;
  ec_point_M mp;
  ec_point_W wp;
  
  init_random();
  
  while ( !N.is_one() && jobs_avail > 0)   
  {
    while ( jobs_avail > 0)
    {
      prim.resetprimes(1);
      
      if ( info )
      {
	cout << "\n" << ++job_nr << ".curve ";
	cout.flush();
      }
      
      if ( job_buffer[job_grp][3] <= 0 )
      {	
	if ( --jobs_avail <= 0 )
	  break;
	
	job_grp++;
	B = job_buffer[job_grp][0];        // next job 
	C = job_buffer[job_grp][1];
	D = job_buffer[job_grp][2];
      }
      else
	job_buffer[job_grp][3]--;
	
      m = (int) random();
      
      mecgen16(ell_curve, mp, factor, m);      
      if ( !factor.is_one() )		    break;
      
      mecfind(ell_curve, mp, factor, B, C, prim);
      if ( !factor.is_one() )		    break;
      
      factor = trans(ell_curve, wp, mp);
      if ( !factor.is_one() )               break;
      
      cont(wp, factor, B, D, prim);
      if ( !factor.is_one() ) 	            break;
    }
    
    if ( jobs_avail > 0)
    {
      if ( !(factor.is_one() || factor.is_zero() || factor == N) )
      {
	div_rem(Q, R, N, factor);
	
	while ( R.is_zero() )
	{
	  count++;
	  N.assign(Q);
	  div_rem(Q, R, N, factor);
	}
	    
	if ( count )
	{  	
	  if (info)
	  {
	    if ( count > 1 ) 
	      cout << "\nfactor " << factor << " ^ " << count;
	    else
	      cout << "\nfactor " << factor;
	    cout.flush();
	  }
	  
	  if ( is_prime(factor, 8) )
	    fact.factor_state = prime;
	  else
	    fact.factor_state = not_prime;
	  
	  fact.single_base = factor;
	  fact.single_exponent = count * exp;

          count=0;   // ts
	  
	  if ( N.is_one() )
	  {
	    factors[index] = fact;
	    break;
	  }
	  else
	    factors[n++] = fact;
	  
	  if ( is_prime(N, 8))
	  {
	    fact.factor_state = prime;
	    
	    fact.single_base = N;
	    fact.single_exponent = exp;
	    factors[index] = fact;
	    
	    if ( info )
	    {
	      cout << "\nprime number " << N << "\n";
	      cout.flush();
	    }

	    N.assign(1);
	    break;
	  }
	  
	  count = 0;
    	  bigmod::set_modulus(N);
	} 
      }
    }
  }  
  
  if ( !N.is_one() )
  {
    int i=0;
    
    unsigned int table = job_buffer[job_grp][0];
    
    while( ecm_params[i][0] != table )
      i++;

    i += 6;
    
    fact.single_base = N;
    fact.single_exponent = exp;
    fact.factor_state = (not_prime) | (i << 2);
    factors[index] = fact;
  }

  if ( !old_modul.is_zero() ) bigmod::set_modulus(old_modul);
}   

   
void rational_factorization::
ecm_comp(lidia_size_t index, int upper_bound, int lower_bound, int step)
{
  if ( (index < 0) || (index >= no_of_comp()) )
    lidia_error_handler("rational_factorization", "ecm_comp::index out of range");
  
  if (  state(factors[index].factor_state) == prime )
  {
    if (info) 
      cout << "index " << index << " :  prime number " << factors[index].single_base << "\n";
    return;
  }
  
  if ( state(factors[index].factor_state) == dont_know )
  {
    if ( is_prime(factors[index].single_base, 8) )
    {
      if (info) 
	cout << "index " << index << " :  prime number " << factors[index].single_base << "\n";
      
      factors[index].factor_state = prime; 
      return;
    }
    else
      factors[index].factor_state = not_prime; 
  }
  
  if ( step <= 0 )
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::step < 0");
    step = 3;
  }
  
  if ( upper_bound > 34 ) 
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::upper_bound > 34");
    upper_bound = 34;
  }
  
  if ( lower_bound < 6 ) 
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::lower_bound < 6");
    lower_bound = 6;
  }
  
  if ( lower_bound > upper_bound ) 
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::lower_bound > upper_bound");
    upper_bound = lower_bound;
  }

  int k, D, jobs_avail, job_buffer[30][5], strategy[30];
  int n = ( (factors[index].single_base).bit_length() / 3 ) + 10;
   
  if (upper_bound == 34)
  {  
    char *n_string;
    n_string = new char[n];
    upper_bound = ( bigint_to_string(factors[index].single_base, n_string) >> 1 ) + 1;
    delete [] n_string;
    if (upper_bound > 34)  upper_bound = 34;
  }
   
  strategy[0] = lower_bound;
  n = lower_bound + step;
  k = 1;
   
  while ( n < upper_bound )
  {
    strategy[k++] = n;
    n += step;
  }
  
  if ( lower_bound < upper_bound )
    strategy[k++] = upper_bound;

  strategy[k] = 0;
  
  D = ecm_read_max(upper_bound);
  
  ecm_primes prim(1, (unsigned int)(D+200), 200000); 
  
  jobs_avail = ecm_job_planing(strategy, job_buffer);
  
  ecm(index, jobs_avail, job_buffer, prim);
  
  compose();
}



void rational_factorization::
ecm(int upper_bound, int lower_bound, int step)
{
  if (is_prime_factorization())
    return;
  lidia_size_t index, len = no_of_comp(); 
  
  int D, k, jobs_avail, job_buffer[30][5], strategy[30];
  int n = ( (factors[len-1].single_base).bit_length() / 3 ) + 10;
  
  if ( step <= 0 )
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::step < 0");
    step = 3;
  }
  
  if ( upper_bound > 34 ) 
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::upper_bound > 34");
    upper_bound = 34;
  }
  
  if ( lower_bound < 6 ) 
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::lower_bound < 6");
    lower_bound = 6;
  }
  
  if ( lower_bound > upper_bound ) 
  {
    lidia_warning_handler("rational_factorization", "ecm_comp::lower_bound > upper_bound");
    upper_bound = lower_bound;
  }

  if ( upper_bound == 34)
  { 
    char *n_string;
    n_string = new char[n];
    upper_bound = ( bigint_to_string(factors[len-1].single_base, n_string) >> 1 ) + 1;
    delete [] n_string;
    if (upper_bound > 34)  upper_bound = 34;
    if (upper_bound < 6)   upper_bound = 6;
  }
  
  D = ecm_read_max(upper_bound);
  
  ecm_primes prim(1, D+200, 200000); 
  
  strategy[0] = lower_bound; k = 1;
  n = lower_bound + step;
  
  while ( n < upper_bound )
  {
    strategy[k++] = n;
    n += step;
  }
  
  if (lower_bound < upper_bound) 
  {
    strategy[k] = upper_bound;
    strategy[k+1] = 0;
  }
  else strategy[k] = 0;
  
  if ( info )
  {
    cout << "\n ECM with same strategy for each number";
    cout << "\n --------------------------------------\n\n";
  }
  
  jobs_avail = ecm_job_planing(strategy, job_buffer);
  
  for ( index = 0; index < len; index++)
  {
    if ( info )
    {
      cout << "\nindex " << index;
      cout << "\n--------\n";
    }
    
    if ( state(factors[index].factor_state) == prime )
    {
      if (info) 
	cout << "index " << index << " :  prime number " << factors[index].single_base << "\n";
      continue;
    }
    
    if ( state(factors[index].factor_state) == dont_know )
    {
      if ( is_prime(factors[index].single_base, 8) )
      {
	if (info) 
	  cout << "index " << index << " :  prime number " << factors[index].single_base << "\n";
	
	factors[index].factor_state = prime;
	
	continue;
      }
      else
	factors[index].factor_state = not_prime; 
    }
    
    ecm(index, jobs_avail, job_buffer, prim);

    for (D = 0; D < jobs_avail; D++)
      job_buffer[D][3] = job_buffer[D][4];
  }  
  
  compose();
}


rational_factorization ecm (const bigint &N, int upper_bound, int lower_bound, int step)
{
  rational_factorization f(N);

  f.ecm(upper_bound, lower_bound, step);

  return f;
}



