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


rf_single_factor & rf_single_factor::operator = (const rf_single_factor & x)
{	
  single_base = x.single_base;
  single_exponent = x.single_exponent;
  factor_state = x.factor_state;
  return *this;
}

bool operator < (const rf_single_factor & a, const rf_single_factor & b)
{
   return (a.single_base < b.single_base);
}

bool operator <= (const rf_single_factor & a, const rf_single_factor & b)
{
   return (a.single_base <= b.single_base);
}
   
bool operator == (const rf_single_factor & a, const rf_single_factor & b)
{
   return (a.single_base == b.single_base);
}

void swap(rf_single_factor & a, rf_single_factor & b)
{
  rf_single_factor c;
  
  c = a;
  a = b;
  b = c;
}

istream & operator >> (istream &in, rf_single_factor &f)
{
   bigint hb;
   int he;
   char c;
   
   in >> c;
   
   if (c != '(')
      lidia_error_handler("rational_factorization", "operator>>::( expected");
   else
   {
      in >> hb >> c;
      if (c != ',')
	 lidia_error_handler("rational_factorization", "operator>>::, expected");
      in >> he >> c;
      while (c != ')')        in >> c;
   }
   
   if ( !he )
   {
      f.single_exponent = 1;
      f.single_base = 1;
      f.factor_state = prime;
   }
   else
   {
      f.factor_state = dont_know;
      f.single_exponent = he;
      
      if ( hb.is_zero() )    
	 lidia_error_handler("rational_factorization", "factor 0 not allowed");
      
      f.single_base = hb;
   }
   
   return in;
}

void rational_factorization::
pretty_print (ostream &out)
{
 int i;	
 
 int length=no_of_comp()-1;
 
 for (i=0; i<length; i++)
 {
   out << factors[i].single_base;
   
   if ( factors[i].single_exponent != 1 )
   {
     out << "^" << factors[i].single_exponent;
   }
   
   out << " * ";
 }

 out << factors[length].single_base;
   
 if ( factors[length].single_exponent != 1 )
 {
   out << "^" << factors[length].single_exponent;
 }
 
}

ostream & operator << (ostream &out, const rf_single_factor &f)
{
   out << "(";
   out << f.single_base;
   out << ",";
   out << f.single_exponent;
   out << ")";
   
   return out;
}


rational_factorization::rational_factorization() 
{
  factors.set_mode(EXPAND);
  isign = 0; info = 0;
  decomp_state = dont_know;
}


rational_factorization::rational_factorization(int n)
{
  factors.set_mode(EXPAND);
  
  if ( n == 0 )    
    lidia_error_handler("rational_factorization", "factorization of 0 not allowed");
  
  if ( n > 0 )
  {
    isign = 1;
    factors[0].single_base = bigint(n);
  }
  else
  {
    isign = -1;
    factors[0].single_base = bigint(-n);
  }

   if ( factors[0].single_base.is_one() )
  {
    factors[0].factor_state = prime;
    decomp_state = prime;
  }
  else
  {
    decomp_state = dont_know;
    factors[0].factor_state = dont_know;
  }

  info = 0;

  factors[0].single_exponent = 1;

} 
  

rational_factorization::rational_factorization(unsigned int n)
{
  factors.set_mode(EXPAND);

  if ( n == 0 )    
    lidia_error_handler("rational_factorization", "factorization of 0 not allowed");
  
  isign = 1;

  if (n==1)
   { factors[0].factor_state=prime;
     decomp_state=prime;
   }
  else
    {factors[0].factor_state = dont_know;
     decomp_state = dont_know;
    }

  info = 0;
  factors[0].single_base = bigint((unsigned long)n);
  factors[0].single_exponent = 1;

}

 
rational_factorization::rational_factorization(long n)
{
  factors.set_mode(EXPAND);

  if ( n == 0 )    
    lidia_error_handler("rational_factorization", "factorization of 0 not allowed");
  
  if ( n > 0 )
  {
    isign = 1;
    factors[0].single_base = bigint(n);
  }
  else
  {
    isign = -1;
    factors[0].single_base = bigint(-n);
  }

  info = 0;

  if ( factors[0].single_base.is_one() )
  {
    factors[0].factor_state = prime;
    decomp_state = prime;
  }
  else
  {
    decomp_state = dont_know;
    factors[0].factor_state = dont_know;
  }

  factors[0].single_exponent = 1;

} 
  

rational_factorization::rational_factorization(unsigned long n)
{
  factors.set_mode(EXPAND);

  if ( n == 0 )    
    lidia_error_handler("rational_factorization", "factorization of 0 not allowed");
  
  isign = 1;
  if (n==1)
   { decomp_state = prime;
     factors[0].factor_state=prime;
   }
  else 
    {
    decomp_state = dont_know;
    factors[0].factor_state = dont_know;
    }
  info = 0;

  factors[0].single_base = bigint(n);
  factors[0].single_exponent = 1;

} 

  
rational_factorization::rational_factorization(const bigint & n) 
{
  factors.set_mode(EXPAND);

  if ( n.is_zero() )    
    lidia_error_handler("rational_factorization", "factorization of 0 not allowed");
  
  if ( n.is_gt_zero() )
  {
    isign = 1;
    factors[0].single_base = n;
  }
  else
  {
    isign = -1;
    factors[0].single_base = -n;
  }
  

  if ( factors[0].single_base.is_one() )
  {
    factors[0].factor_state = prime;
    decomp_state = prime;
  }
  else
  {
    decomp_state = dont_know;
    factors[0].factor_state = dont_know;
  }

  info = 0;
  
  factors[0].single_exponent = 1;

}


rational_factorization::rational_factorization(const bigrational & n)
{
  factors.set_mode(EXPAND);
  
  isign = n.sign();
  info = 0;
  decomp_state = dont_know;
  
  factors[0].single_base = n.denominator();
  factors[0].single_exponent = -1;
  factors[0].factor_state = dont_know;
  
  factors[1].single_base = abs(n.numerator());
  factors[1].single_exponent = 1;
  factors[1].factor_state = dont_know;
  
  compose();
}
 
 
rational_factorization::rational_factorization(const rational_factorization & f)
{ 
  factors.set_mode(EXPAND);

  factors = f.factors;
  isign = f.isign;
  info = f.info;
  decomp_state = f.decomp_state;
}  


rational_factorization::~rational_factorization()
{
}


rational_factorization & rational_factorization::operator = (const rational_factorization & f)
{ 
  factors = f.factors;
  isign = f.isign;
  info = f.info;
  decomp_state = f.decomp_state;
  
  return *this;
}  


void rational_factorization::
assign(long n)
{
  if ( n == 0 )    
    lidia_error_handler("rational_factorization", "factorization of 0 not allowed");
  
  factors.set_size(1);

  if ( n > 0 )
  {
    isign = 1;
    factors[0].single_base = bigint(n);
  }
  else
  {
    isign = -1;
    factors[0].single_base = bigint(-n);
  }

   if ( factors[0].single_base.is_one() )
  {
    factors[0].factor_state = prime;
    decomp_state = prime;
  }
  else
  {
    decomp_state = dont_know;
    factors[0].factor_state = dont_know;
  }


  factors[0].single_exponent = 1;

} 
  

void rational_factorization::
assign(const bigint &n)
{
  if ( n.is_zero() )    
    lidia_error_handler("rational_factorization", "factorization of 0 not allowed");
  
  factors.set_size(1);
  
  if ( n.is_gt_zero() )
  {
    isign = 1;
    factors[0].single_base = n;
  }
  else
  {
    isign = -1;
    factors[0].single_base = -n;
  }


if ( factors[0].single_base.is_one() )
  {
    factors[0].factor_state = prime;
    decomp_state = prime;
  }
  else
  {
    decomp_state = dont_know;
    factors[0].factor_state = dont_know;
  }
  
  factors[0].single_exponent = 1;

}

void rational_factorization::
convert(base_vector<bigint> & basis, base_vector<int> & exponent)
{ 
 lidia_size_t i,len;
 
 if (basis.size()!=exponent.size())
    lidia_error_handler("rational_factorization", "different sizes of basis and exponent vectors");
 else
   {len=basis.size();  
    factors.set_size(len) ;
    for (i=0; i<len; i++)
     { factors[i].single_base = basis[i];
       factors[i].single_exponent = exponent[i];
       factors[i].factor_state = dont_know;
     }
     compose();  
   }

} 


 

void rational_factorization::
assign(const bigrational & n)
{
  isign = n.sign();
  info = 0;
  decomp_state = dont_know;

  factors.set_size ( 2 ) ;
  
  factors[0].single_base = n.denominator();
  factors[0].single_exponent = -1;
  factors[0].factor_state = dont_know;

  factors[1].single_base = abs(n.numerator());
  factors[1].single_exponent = 1;
  factors[1].factor_state = dont_know;

  compose();
}

 
void rational_factorization::
assign(const rational_factorization &f)
{ 
  factors = f.factors;
  isign = f.isign;
  info = f.info;
  decomp_state = f.decomp_state;
}  


bigint  rational_factorization::
base(lidia_size_t index) const
{
  if ( (index < 0) || (index >= no_of_comp()) )
    lidia_error_handler("rational_factorization","base::index out of range");
  
  return bigint(factors[index].single_base);
}


int rational_factorization::
exponent(lidia_size_t index) const
{
  if ( (index < 0) || (index >= no_of_comp()) )
    lidia_error_handler("rational_factorization","exponent::index out of range");
  
  return factors[index].single_exponent;
}

void rational_factorization::
set_exponent(lidia_size_t index, int expo)
{
  if ( (index < 0) || (index >= no_of_comp()) )
    lidia_error_handler("rational_factorization","set_exponent::index out of range");
  
  factors[index].single_exponent = expo;
  
  if (expo == 0)
    {
      compose();
    }
}


bool rational_factorization::
is_prime_factor(lidia_size_t index) 
{
  if ( index < 0 || index >= no_of_comp() )
    lidia_error_handler("rational_factorization","is_prime_factor::index out of range");

  if ( state(factors[index].factor_state) == prime )
    return true;
    
  if ( state(factors[index].factor_state) == not_prime )
    return false;
    
  if ( is_prime( factors[index].single_base, 8 ) )
  {
    factors[index].factor_state = prime;
    return true;
  }
  else
  {
    factors[index].factor_state = not_prime;
    return false;
  }
}


bool rational_factorization::
is_prime_factorization()  
{

  if ( decomp_state == prime )
    return true;

  for (lidia_size_t i = no_of_comp()-1; i >= 0; i--)
  {
    if ( state(factors[i].factor_state) == not_prime )
    {
      decomp_state = not_prime;
      return false;
    }
    
    if ( state(factors[i].factor_state) == dont_know )
    {
      if ( is_prime( factors[i].single_base, 8 ) )
	factors[i].factor_state = prime;
      else
      {
	decomp_state = not_prime;
	factors[i].factor_state = not_prime;
	return false;
      }
    }
  }
  
  decomp_state = prime;
  return true;
}


void rational_factorization::
compose()
{
  lidia_size_t i, j, len = no_of_comp() - 1;  
  
  sort();
  
  for( i = 0; i < len ; i++)
  {
    j = i+1;
    while ( (j <= len) && (factors[i].single_base == factors[j].single_base) )
    {
      factors[i].single_exponent += factors[j].single_exponent;
      j++;
    }
        
    if ( (j = j - i - 1) > 0 )
    {
      factors.remove_from(i+1, j);
      len = no_of_comp() - 1;
    }
    if (factors[i].single_exponent == 0 || factors[i].single_base.is_one())
    {
       factors.remove_from(i);
       len --;
       i --;
    }
 } 
 
  if ( no_of_comp() == 0 )
  { 
     factors[0].single_base.assign(1);
     factors[0].single_exponent = 1;
     factors[0].factor_state = prime;
     decomp_state = prime;
  }
}

void rational_factorization::
compose( sort_vector< rf_single_factor > & v )
{
  lidia_size_t i, j, len = v.size() - 1;  

  v.sort();
  
  for( i = 0; i < len ; i++)
  {
    j = i+1;		
    while ( (j <= len) && (v[i].single_base == v[j].single_base) )
    {
      v[i].single_exponent += v[j].single_exponent;
      j++;
    }
        
    if ( (j = j - i - 1) > 0 )
    {
      v.remove_from(i+1, j);
      len = v.size() - 1;
    }
    if (v[i].single_exponent == 0 || v[i].single_base.is_one())
    {
       v.remove_from(i);
       len --;
    }
 } 
 
  if (v.size() == 0)
  { 
     v[0].single_base.assign(1);
     v[0].single_exponent = 1;
     v[0].factor_state = prime;
  }
 v.sort(); // Testing
}


istream & operator >> (istream & in, rational_factorization & f)
{
  lidia_size_t n = 0;
  int sg = 1;
  char c;

  f.decomp_state = dont_know;

  f.factors.set_size ( 0 ) ;
  
  in >> c;

  if (c != '[')
    lidia_error_handler("rational_factorization", "operator>>::[ expected");

  in >> c;

  while (c != ']')
  {
    in.putback(c);

    in >> f.factors[n];
    
    n++;
    
    in >> c;
  }
  
  for ( lidia_size_t i = 0; i < n; i++)
  {  
    if ( f.factors[i].single_base.is_lt_zero() )
    {
      if ( f.factors[i].single_exponent & 1 )
	sg = -sg;
      f.factors[i].single_base.negate();
    }
    
    if ( (f.factors[i].single_base).is_one() )
    {
      f.factors.remove_from(i);
      n--;
      i--;
    }
  }
  
  f.isign = sg;
  
  f.compose();

  return in;
}


ostream & operator << (ostream & out, const rational_factorization & f)
{
  lidia_size_t i, sz = f.no_of_comp();

  out << "  [";  
  
  if (f.isign == -1)
  out << "(-1,1)";
   
  for (i = 0; i < sz; i++)
    out << f.factors[i];

  out << "]";
  
  return out;
}


void rational_factorization::
invert()
{
  lidia_size_t i, len = no_of_comp();
  
  for (i=0; i< len; i++)
    factors[i].single_exponent = - factors[i].single_exponent;
}

void rational_factorization::
square()
{
  lidia_size_t i, len = no_of_comp();
  
  for (i=0; i< len; i++)
    factors[i].single_exponent <<= 1;
}


void multiply(rational_factorization & f, 
	      const rational_factorization & a, 
	      const rational_factorization & b)
{
  f.factors.concat(a.factors, b.factors);

  if ( a.decomp_state == prime && b.decomp_state == prime )
    f.decomp_state = prime;
  else
    f.decomp_state = dont_know;

  f.isign = a.isign * b.isign;   
  f.compose();   
}


void divide(rational_factorization & f, 
	    const rational_factorization & a, 
	    const rational_factorization & b)
{
  rational_factorization h(b);
  h.invert();
  f.factors.concat(a.factors, h.factors);

  if ( a.decomp_state == prime && b.decomp_state == prime )
    f.decomp_state = prime;
  else
    f.decomp_state = dont_know;

  f.isign = a.isign * b.isign;   
  f.compose();   
}


void rational_factorization::
refine2( sort_vector< rf_single_factor > & v , rf_single_factor & SF ,
	 const rf_single_factor & a, const rf_single_factor & b )

     // we assume vector v to be of expanding size !!!

     // compute refinement of {a,b} and store the
     // last rf_single_factor of this factorization individually
     // in SF
     // thus {v[0],...,v[n],SF} represents the refinement of {a,b}
{
  debug_handler ( "rational_factorization" , "refine2()" ) ;
  
  lidia_size_t i = 0, last_index = 2;
  int j;
  rf_single_factor sf;
  bigint q, r;
  
  v.set_size ( 2 ) ;   
  v[0] = a ;
  v[1] = b ;
  
  while( i < last_index-1 )
  {
    sf.single_base = gcd(v[i].single_base, v[i+1].single_base);
      
    if (sf.single_base.is_one())
      i++;
    else 
    {  
      div_rem(v[i].single_base, r, v[i].single_base, sf.single_base);
      j = 0;
      while (r.is_zero())
      {
	div_rem(q, r, v[i].single_base, sf.single_base);  
	if (r.is_zero())
	  v[i].single_base.assign(q);
	j++;
      }
      sf.single_exponent = j * v[i].single_exponent ;
      
      div_rem(v[i+1].single_base, r, v[i+1].single_base, sf.single_base);
      j = 0;
      while (r.is_zero())
      {
	div_rem(q, r, v[i+1].single_base, sf.single_base);  
	if (r.is_zero())
	  v[i+1].single_base.assign(q);
	j++;
      }
      sf.single_exponent += j * v[i+1].single_exponent;
      
      if (is_prime(v[i].single_base, 8))
	v[i].factor_state = prime;
      else v[i].factor_state = not_prime;
	  
      if (is_prime(v[i+1].single_base, 8))
	v[i+1].factor_state = prime;
      else v[i+1].factor_state = not_prime;
	  
      if (sf.single_exponent != 0)
      {
	if (is_prime(sf.single_base,8))
	  sf.factor_state = prime;
	else sf.factor_state = not_prime;
	
	if (v[i+1].single_base.is_one())
	  v[i+1] = sf;
	else
	{
	  v.insert_at ( sf , i+1 ) ;
	  last_index ++ ;
	}
      }
    }
  }
  
  // extract last element and return it as an individual

  i  = v.size()-1;
  SF = v[i];
  v.set_size( i );

  compose( v );
}


void rational_factorization::
refine ( void )
{
  debug_handler ( "rational_factorization" , "refine()" ) ;
  
  sort_vector< rf_single_factor > v(0,EXPAND), w(0,EXPAND) ;
  rf_single_factor b, c, sf ;
  
  lidia_size_t i, j, k, l = factors.size();

  if ( l >= 2 )
  {
    refine2 ( v , sf , factors[0] , factors[1] ) ;

    k    = v.size() ;
    v[k] = sf ;

    for ( i = 2 ; i < l ; i++ )
    {
      j = 0 ;
      b = v[0] ;
      c = factors[i] ;

      do
      {
	// compute refinement of {b,c}
	refine2 ( w , sf , b , c ) ;
	

	// insert w into v and proceed with {..,sf}
	k = w.size() ;
	
	v.shift_right ( j+1 , k-1 ) ;

	v.assign      ( j , w , 0 , k-1 ) ;
	
	j += k ;
	
	if ( j < v.size() )
	{
	  b = v[j] ;
	  c = sf   ;
	}	
	else  if ( ! sf.single_base.is_one() ) 
	{
	  v[j] = sf ;
	  j++ ;
	}
      }
      while ( j < v.size() && ! sf.single_base.is_one() ) ;
    }
    
    swap ( v , factors ) ;
    compose () ;
  }
}


bool rational_factorization::
refine ( const bigint & x )
{
  lidia_size_t i, j, len = no_of_comp() ;
  int e_akt;
  
  bool rc = false ;
  
  bigint old, n, res, d ;
  
  rf_single_factor sf ;
  
  j = len;
  
  for ( i = 0 ; i < len ; i++ )
  {
    e_akt = 0;
    old = base(i);	
    d = gcd ( old , x ) ;
    
    if ( ! d.is_one() && d != old )
    {
      rc = true ;

      div_rem(n, res, old, d);
      
      while( res.is_zero() )
      {
	e_akt++;
	old = n ;
	
	div_rem( n, res, old, d );
      }
      
      sf.single_base = d;
      sf.single_exponent = e_akt * exponent(i);
      
      if ( is_prime(d,8) )
	sf.factor_state = prime;
      else
	sf.factor_state = not_prime;
      
      
      if ( ! old.is_one() )
      {
	factors[i].single_base = old ;
	
	if ( is_prime(old,8) )
	  factors[i].factor_state = prime ;
	else
	  factors[i].factor_state = not_prime ;
	
	    // * * * append sf to the end of factors * * *

	factors[j] = sf ;
	j ++ ;
      }
      else
      {
	factors[i] = sf ;
      }
    }
  }			
  
  compose();
  
  return rc ;
}



bool rational_factorization::
refine_comp (lidia_size_t index, const bigint &x)
{ 	
  int e = 0;
  bigint old, n, res, d;
  
  rf_single_factor sf ;
  
  bool rc = false ;
  
  if ( (index < 0) || (index >= no_of_comp()) )
    lidia_error_handler("rational_factorization", "refine_comp( int, const bigint & )::index out of range");
  
  old = base(index);
  d = gcd ( old , x ) ;

  if ( ! d.is_one() && d != old )
  {
    rc = true ;
    
    div_rem(n, res, old, d);
    
    while ( res.is_zero() )
    {	
      e++;
      old = n ;
      div_rem(n, res, old, x);
    }

    sf.single_base = d ;
    sf.single_exponent = e * exponent(index);
    
    if ( is_prime(d,8) )
      sf.factor_state = prime;
    else
      sf.factor_state = not_prime;
    
    if ( ! old.is_one() )
    {
      factors[index].single_base = old ; 
      
      if ( is_prime(old,8) )
	factors[index].factor_state = prime ;
      else
	factors[index].factor_state = not_prime ;
      
      factors[no_of_comp()] = sf;
    }
    else
    {
      factors[index] = sf ;
    }
    
    compose();
  }
  
  return rc ;
}


bool operator == (const rational_factorization & a, 
		 const rational_factorization & b)
{
  rational_factorization f; 
  
  divide(f, a, b);
  f.refine();
  
  if (f.no_of_comp() == 1 && f.base(0).is_one() && f.isign == 1)
    return 1;
  else 
    return 0;
}




void rational_factorization::
factor_comp(lidia_size_t index, int upper_bound)
{
  if ( (index < 0) || (index >= no_of_comp()) )
    lidia_error_handler("rational_factorization", "factor_comp::index out of range");

  if ( is_prime_factor(index) )
  {
    if (info) 
      cout << "prime number " << factors[index].single_base << "\n";
    return;
  }
  
  if ( upper_bound > 34 )
  {
    lidia_warning_handler("rational_factorization", "factor_comp::incorrect parameters");
    upper_bound = 34;
  }

  int D, job_buffer[30][5], strategy[30];
  int jobs_avail, k; 
  int n = ( factors[index].single_base.bit_length() / 3 ) + 10;
  
                                    // try to factor component using TD
  if (upper_bound == 34) 
  {
    char *n_string;
    n_string = new char[n];
    upper_bound = (int) ( bigint_to_string(factors[index].single_base, n_string) * .28 ) + 1;
    delete [] n_string;

    if (upper_bound > 34)  upper_bound = 34;
    if (upper_bound < 6)   upper_bound = 6;
  }
  
  if ( (D = ecm_read_max(upper_bound)) < 1000000 )   D = 1000000;

  ecm_primes prim(1, D+200, 200000); 
  trialdiv(index, 1, 1000000, prim); 
  
  if ( is_prime_factor(index) )
  {
    if (info) 
      cout << "prime number " << factors[index].single_base << "\n";
    
    compose();
    return;
  }
                                     // check whether base[index] is an exact power
  bigint N; 

  do 
  {
    k = (int) power_test(N, factors[index].single_base);
    
    if ( k != -1 )
    {
      factors[index].single_base.assign(N);
      factors[index].single_exponent *= k;

      if ( is_prime(N, 8) )
      {
	factors[index].factor_state = prime;
	compose();
	return;
      }
      else
	factors[index].factor_state = not_prime;
    }
  }  while ( k != -1 ) ;

                                      // try to factor component using ECM
  if ( !(state(factors[index].factor_state) == prime) )
  {  
    k = 0; n = 6;                    // lower_bound = 6
     
    while ( n < upper_bound )
    {
      strategy[k++] = n;
      n += 3;
    }
    
    strategy[k] = upper_bound;
    strategy[k+1] = 0;
    
    jobs_avail = ecm_job_planing(strategy, job_buffer);

    N.assign(factors[index].single_base);
    int ecm_restlength = (int) (N.bit_length()*0.177);  // *0.30 wegen Dezimalstellen und * 0.59 wegen Restlaenge
      
    ecm(index, ecm_restlength, jobs_avail, job_buffer, prim);
  }
  
  if ( !(is_prime_factor(index)) )
  {
    prim.resetprimes(1);
    mpqs(index, prim);
  }
  
  compose();
}


void rational_factorization::
factor(int upper_bound)
{ 
  if ( is_prime_factorization() )
    return;
  
  if ( upper_bound > 34 )
  {
    lidia_warning_handler("rational_factorization", "factor::upper_bound > 34");
    upper_bound = 34;
  }

  lidia_size_t k = no_of_comp(), index;

  int D, job_buffer[30][5], strategy[30];
  int jobs_avail; 
  int n = ( factors[k-1].single_base.bit_length() / 3 ) + 10;

                                    // try to factor component using TD
  if (upper_bound == 34) 
  {
    char *n_string;
    n_string = new char[n];
    upper_bound = (int) ( bigint_to_string(factors[k-1].single_base, n_string) * .28) + 1;
    delete [] n_string;

    if (upper_bound > 34)  upper_bound = 34;
    if (upper_bound < 6)   upper_bound = 6;
  }
  
  if ( (D = ecm_read_max(upper_bound)) < 1000000 )   D = 1000000;

  ecm_primes prim(1, D+200, 200000);   
  bigint N;

  D = 0; n = 6;                // lower_bound = 6
  
  while ( n < upper_bound )
  {
    strategy[D++] = n;
    n += 3;
  }
  
  strategy[D] = upper_bound;
  strategy[D+1] = 0;
 
  jobs_avail = ecm_job_planing(strategy, job_buffer);

  for ( index = 0; index < k; index++)
  {
    if ( info )
    {
      cout << "\nworking on index " << index;
      cout << "\n==================\n";
      cout.flush();
    }
    
    if ( state(factors[index].factor_state) == dont_know )
      if ( is_prime_factor(index) == prime )
      {
	if (info) 
	  cout << "prime number " << factors[index].single_base << "\n";
	continue;
      }
  
    int ecm_restlength=(int) (factors[index].single_base.bit_length()*0.177);  // *0.30 wegen Dezimalstellen und * 0.59 wegen Restlaenge
      
    trialdiv(index, 1, 1000000, prim);

    if ( is_prime_factor(index) )
    {
      if (info) 
	cout << "prime number " << factors[index].single_base << "\n";
      continue;
    }

                               // check whether base[index] is an exact power
    do 
    {
      D = (int) power_test(N, factors[index].single_base);
      
      if ( D != -1 )
      {
	factors[index].single_base.assign(N);
	factors[index].single_exponent *= D;
	
	if ( is_prime(N, 8) )
	{
	  factors[index].factor_state = prime;
	  D = -1;
	}
	else
	  factors[index].factor_state = not_prime;
      }
    }  
    while ( D != -1 ) ;

                                          // try to factor component using ECM
    if ( !(state(factors[index].factor_state) == prime) )
    {
      if ( info )   cout << "ECM ";

      ecm(index, ecm_restlength, jobs_avail, job_buffer, prim);
    }

    for (D = 0; D < jobs_avail; D++)
      job_buffer[D][3] = job_buffer[D][4];
    
    if ( !(is_prime_factor(index) ) )
    {
      prim.resetprimes(1);
      mpqs(index, prim);
    }
  }
  
  compose();

  if (is_prime_factorization())
    return;
  
  int trials = 0;
  
  k = no_of_comp();

  for (index = 0; index < k; index++)
  {
    if ( state(factors[index].factor_state) == not_prime )
    {
      N.assign( factors[index].single_base);
      
       if (info)
	 cout << "\nindex " << index << " not prime: to factor " << N; 

      for (n = 0; n < jobs_avail; n++)
	job_buffer[n][3] = job_buffer[n][4];

      ecm(index, jobs_avail, job_buffer, prim);
      compose();
      
      if (N == factors[index].single_base)  
	trials ++;
      else trials = 0;
      k = no_of_comp();
      index = -1;
      
      if (trials >= 2) return;
    }     
  }
}


rational_factorization factor(const bigint &N)
{
  rational_factorization f(N);

  f.factor();

  return f;
}

