#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

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
extern "C" void qs_gauss(long);
extern "C" char* get_name(char*);

#if !defined(__GNUG__)
//extern "C" int atoi(char*);
//extern "C" int system(char*);
#endif

#if !defined(__GNUG__)
#include <signal.h>
#else
#define SIGHUP  1       /* hangup */
#define SIGINT  2       /* interrupt */
#define SIGQUIT 3       /* quit */
#define SIGILL  4       /* illegal instruction (not reset when caught) */
#define SIGTERM 15
#endif

//#define  LEN 100000
#define  LEN 33000
#define STLEN 6000
int treffer[LEN];
int kleinsize;
int counter_treff=0;
int counter_glob=0;
int small_cand=0;
int partials=0;
int cands=0, poly=0;
int wrong_small=0;


#ifdef TIMER
timer comp_t, sieb_t, teste_t, zeit_ges;
double teste_c=0, sieb_c=0, comp_c=0;
#endif

int out[50];

// SN: In DOS/OS/2/WIN Environent close files
//     before trying to call unlink!
#ifdef __EMX__
#define lidia_error_handler_n(f,m)\
{ fcloseall();\
  unlink(get_name("FAKM"));\
  unlink(get_name("ALLS"));\
  unlink(get_name("NEWR"));\
  unlink(get_name("ELIM"));\
  unlink(get_name("SOLU"));\
  unlink(get_name("sort"));\
  unlink(get_name("REL"));\
  unlink(get_name("HOWF"));\
  unlink(get_name("PART"));\
  unlink(get_name("NEUE"));\
  unlink(get_name("UMSP"));\
  unlink(get_name("ZUSA"));\
  lidia_error_handler(f,m); }
#else
#define lidia_error_handler_n(f,m)\
{ unlink(get_name("FAKM"));\
  unlink(get_name("ALLS"));\
  unlink(get_name("NEWR"));\
  unlink(get_name("ELIM"));\
  unlink(get_name("SOLU"));\
  unlink(get_name("sort"));\
  unlink(get_name("REL"));\
  unlink(get_name("HOWF"));\
  unlink(get_name("PART"));\
  unlink(get_name("NEUE"));\
  unlink(get_name("UMSP"));\
  unlink(get_name("ZUSA"));\
  lidia_error_handler(f,m); }
#endif
#define CAND_NUMBER 700


//#define HASH_TAB 100000
//#define HASH_TAB_L 1000000
#define HASH_TAB 75001
#define HASH_TAB_L 500000
#define FLENGTH 5000
#define ZUS_LENGTH 5000
#define GET_LENGTH 5000
typedef unsigned char SIEBTYP;

const float rational_factorization::qs_params[67][8] = { {9, 0.7, 500, 13,3,10,4,2},
                                 {10, 0.7, 500, 15,3,10,4,2},
                                 {11, 0.7, 600, 20,3,10,4,2},
                                 {12, 0.7, 700, 25,3,10,4,2},
                                 {13, 0.8, 800, 35,3,10,4,2}, // ??
                                 {14, 0.8, 1000, 35,3,10,4,2},
                                 {15, 0.8, 1200, 35,3,10,4,2},
                                 {16, 0.8, 1400, 35,3,10,4,2},
                                 {17, 0.8, 3000, 40,3,10,4,2},//
                                 {18, 0.8, 3000, 60,3,10,4,2},
                                 {19, 0.8, 3600, 80,3,10,4,2},
                                 {20, 0.8, 4000, 100,3,10,4,2}, //
                                 {21, 0.8, 4250, 100,3,10,4,2},
                                 {22, 0.8, 4500, 120,3,10,4,3},
                                 {23, 0.8, 4750, 140,3,10,4,3},
                                 {24, 0.8, 5000, 160,3,12,4,4},
                                 {25, 0.8, 5000, 180,3,12,4,4},
                                 {26, 0.9, 6000, 200,5,8,16,4}, //  ab hier T erhoeht
                                 {27, 1.17, 6000, 220,5,8,16,5},
                                 {28, 1.17, 6500, 240,5,8,16,5},
                                 {29, 1.17, 6500, 260,5,8,16,5},
                                 {30, 1.36, 7000, 325,5,8,16,5}, //
                                 {31, 1.36, 7000, 355,5,8,16,5},
                                 {32, 1.36, 7500, 375,5,8,16,5},
                                 {33, 1.43,  7500, 400,6,8,32,6},
                                 {34, 1.43,  7500, 425,6,8,32,6},
                                 {35, 1.43, 7500, 550,6,10,32,6}, //
                                 {36, 1.43, 8000, 650,6,10,32,6},
                                 {37, 1.92, 9000, 750,6,10,32,7},
                                 {38, 1.92, 10000, 850,6,10,32,7},
                                 {39, 1.92, 11000, 950,6,10,32,7},
                                 {40, 1.62, 14000, 1000,6,10,32,7}, //
                                 {41, 1.62, 15500, 1150,6,10,32,8},
                                 {42, 1.69, 12000, 1300,6,10,32,8}, //
                                 {43, 1.69, 12000, 1600,6,10,32,8},
                                 {44, 1.69, 15000, 1900,7,10,64,9},
                                 {45, 1.69, 15000, 2200,7,10,64,9},
                                 {46, 1.69, 15000, 2500,7,10,64,9}, //
                                 {47, 1.69, 25000, 2500,7,10,64,10},
                                 {48, 1.69, 25000, 2500,7,10,64,10},
                                 {49, 1.69, 30000, 3000,7,9,64,10},
                                 {50, 1.75, 50000, 3000,7,10,64,10},
                                 {51, 1.75, 50000, 3000,7,10,64,10},
                                 {52, 1.75, 50000, 3200,7,10,64,11}, //
                                 {53, 1.75, 50000, 3500,7,10,64,11}, //
                                 {54, 1.95, 80000, 3800,7,12,64,11},
                                 {55, 1.95, 90000, 4100,7,12,64,11},
                                 {56, 1.95, 100000, 4400,7,12,64,11},
                                 {57, 1.95, 80000, 4700,8,12,128,12},
                                 {58, 1.95, 80000, 5000,8,12,128,12},
                                 {59, 2.15, 130000, 5500,8,12,128,12},
                                 {60, 2.15, 140000, 5800,8,12,128,12},
                                 {61, 2.15, 150000, 6100,8,12,128,13},
                                 {62, 2.15, 160000, 6400,8,12,128,13},
                                 {63, 2.4, 170000, 6700,8,12,128,13},
                                 {64, 2.4, 180000, 7000,8,12,128,13},
                                 {65, 2.4, 190000, 7300,8,12,128,13},
                                 {66, 2.4, 200000, 7600,8,12,128,13},
                                 {67, 2.4, 75000, 7900,8,12,128,13},
                                 {68, 2.4, 75000, 8200,8,12,128,13},
                                 {69, 2.4, 100000, 8500,8,12,128,13},
                                 {70, 2.45,130000, 8800,8,12,128,13},
				 {71, 2.45, 130000,8800,9,14,256,13},
				 {72, 2.4, 260000, 9400,9,14,256,13},
                                 {73, 2.4, 270000, 9700,9,14,256,13},
                                 {74, 2.4, 280000, 9000,9,14,256,13},
			     	 {75, 2.6, 140000, 9000,9,14,256,13}};



inline int laenge(char *s, char c)        // computes the number of char c
{                                         // in string s
  int l = 0;

  while (*s++)
    if (*s == c)       l++;

  return(l);
}

int transform_relations(int back)
{
  static int position=0;

  if (back) {		// SN: Don't open the files in this case!
    position=0;
    return(0);
  }

  FILE *fpin  = fopen(get_name("FAKM"),"r");
  FILE *fpout = fopen(get_name("NEWR"),"w");

  char *p, *pp, zeile[GET_LENGTH];
  int i = 0, flag;

  if (!fpin || !fpout)
    { lidia_error_handler_n("rational_factorization", "transform_relations::can't open FAKTORENMATRIX or NEWRELATIONS");
      return(1);
    }

  fseek(fpin, position, 0);


  while (fgets(zeile, GET_LENGTH, fpin))
  {
    if ((pp = (char*) strchr(zeile, ':')))
    {
      pp += 2;

      flag = 0;

      fprintf(fpout, "%d %d 0 ", ++i, laenge(pp, ' '));

      p = (char*) strtok(pp, " \n");

      while (p != NULL)
      {
	fprintf(fpout, "%d ", abs(atoi(p) - flag));
	flag = !flag;
	p = (char*) strtok(NULL, " \n");
      }
      fprintf(fpout, "\n");
    }
  }

  position = (unsigned int) ftell(fpin);
  fclose(fpin);
  fclose(fpout);
  return(0);
}


#ifndef log2
#define log2(a)        ( log(a)*(1/log(2)) )
#endif

LIDIA_SIGNAL_FUNCTION(stop_it)
     {

     lidia_error_handler_n("rational_factorization", "the program was interrupted");

     transform_relations(1);
     }


void rational_factorization::qs_read_par(int stellen, double &T, int &M,
int &size_FB, int &P_ONCE, int &POLY, int &P_TOTAL, int &smallstart)
{
  int i=stellen-9;

  T = qs_params[i][1];
  M = (int) qs_params[i][2];             // length of array is 2*M
  size_FB = (int) qs_params[i][3];       // size of factor base
  P_ONCE= (int) qs_params[i][4];         // number of prime factors of coefficient A
  P_TOTAL=(int) qs_params[i][5];         // total number of primes the P_ONCEs are taken from
  POLY=(int) qs_params[i][6];            // number of coefficients Bs that are used for
                                         // one A; POLY=2^(P_ONCE-1)

  smallstart= (int) qs_params[i][7];     // begin sieving with FB[smallstart]

}


inline int power_test_complete(bigint &a, bigint &b)
{ bigint help;
  long merke,merke2=1;
  int pr;

  merke=power_test(a,b);

  if (merke!=-1)
      {

         pr=is_prime(a,4);

         while ((!pr) && (merke2!=-1))
           { merke2=power_test(help,a);
             if (merke2!=-1)
              { merke=merke*merke2;
                a=help;
                pr=is_prime(a,4);
              }
           }
      }
 return merke;
}


void addexp(char *s, short *exp)
{
  char  *pp, *p, hilf[STLEN];
  int   e;

  strcpy(hilf,s);

  if ((pp = strchr(hilf, ':')))
    {
      pp += 2;

      p = strtok(pp, " \n");
      while (p != NULL)
        {
          e = atoi(p);
          if (!e)
            break;
          p = strtok(NULL, " \n");
          exp[atoi(p)] += e;
          p = strtok(NULL, " \n");
        }
    }
}

void subexp(char *s, short *exp)
{
  char  *pp, *p, hilf[STLEN];
  int   e;

  strcpy(hilf,s);

  if ((pp = strchr(hilf, ':')))
    {
      pp += 2;

      p = strtok(pp, " \n");
      while (p != NULL)
        {
          e = atoi(p);
          if (!e)
            break;
          p = strtok(NULL, " \n");
          exp[atoi(p)] -= e;
          p = strtok(NULL, " \n");
        }
    }
}


void zusam(bigint &kN, int size_exp)

{FILE* fi=fopen(get_name("ZUSA"),"r");
 FILE* fneu;
 char s[ZUS_LENGTH], line[STLEN], *line_old=0, faktor[STLEN], Hstring[150], Hstring2[150];
 bigint h, h_old=1, h_inv, neu, test3;
 int large, large_old=1, i, neu_count=0;
 static short *exp=NULL;
  exp = new short[size_exp];
  char  command2[50], command3[50];

#if DEBUG
  cout<<"\n Start zusam \n"; cout.flush();
#endif

  fneu=fopen(get_name("NEUE"),"a"); // SN: Open "NEUE" only once !!

   while(fgets(line,GET_LENGTH,fi))

    { sscanf(line,"%d @ %s", &large, s);
      string_to_bigint(s, h);


      if (large==large_old)
	{
	    memset(exp, 0, size_exp*sizeof(short));
            addexp(line, exp);
            subexp(line_old,exp);
            test3= xgcd_left(h_inv,h_old,kN);
            neu = (h_inv*h) % kN;
            bigint_to_string(neu, Hstring);
            memset(faktor, 0, FLENGTH);

          for (i=1; i<size_exp; i++)
             if (exp[i]!=0)
               sprintf(faktor,"%s %d %d",faktor,exp[i],i);
          bigint_to_string(h,Hstring);
          bigint_to_string(h_old,Hstring2);
          fprintf(fneu, "%s %s :%s 0\n", Hstring, Hstring2, faktor);
          neu_count++;
        }
      else
	  {
            large_old=large;
            h_old=h;
            line_old=strdup(line);
          }


      if (neu_count>counter_treff)

      {

	fclose(fneu);  // SN: Close before reusing "NEUE"; we exit anyway !!
#ifdef __EMX__
	sprintf(command2,"type %s %s > %s",get_name("FAKM"), get_name("NEUE"), get_name("UMSP"));  // SN: use type insteadof cat on OS/2/DOS/WIN.
#else
	sprintf(command2,"cat %s %s > %s",get_name("FAKM"), get_name("NEUE"), get_name("UMSP"));
#endif
	sprintf(command3,"sort -u %s > %s",get_name("UMSP"), get_name("FAKM"));

         system(command2);
         system(command3);
//         cout<<"\n wrong_small="<<wrong_small; cout.flush();
         fclose(fi);
         return;

      }


    }
   fclose(fi);
   fclose(fneu);
}





double rational_factorization::
h_zeitqs(int i)
{
  double j, min;
  static int first_one=1;
  static double mul;

 if (first_one)              // during the first run find out how fast your machine is

     {
         static int prime[53] = { 11, 13, 15, 17, 19, 21, 23, 29, 31, 33, 35,
37, 39, 41, 43, 47, 51, 53, 55, 57, 59, 61, 65, 67, 71, 73, 79, 83, 85, 89, 91
, 93, 95, 97, 101, 103, 105, 107, 109, 113, 127, 131, 149, 151, 157, 163,179,
239,397, 401,439, 479, 641};

         timer test;
         int p, treffer=0;
         lidia_size_t x,j,jj;
         long lauf;
         SIEBTYP *sieb;

         test.start_timer();

         sieb = new SIEBTYP[100000];

         for (jj=0; jj<7; jj++)
          for (j=0; j<50; j++)
            {
              p=prime[j];
              x=0;
              while (x<100000)
	        { sieb[x]+=3;
                  x+=p;
                }
            }

          x=0;
          while (x<100000)
            { if (sieb[x]>30) treffer++;
              x++;
            }

         delete[] sieb;
         test.stop_timer();
         lauf=test.user_time()+test.sys_time();
         mul=lauf/63.0; // 63 is the time on a SPARC ELC
         first_one=0;
     }

if ( i <= 28 )                     // const time for small numbers
    return (12.0*mul);

  j = (double)( i - 40 ) / 3.0;
  min =  125.0 * pow(2, j)*mul;

 // cout<<"\n min="<<min; cout<<"\n in Minuten \n"; cout.flush();
  return (min);
}


void rational_factorization::
zeitqs(int i)
{
  double min;
  int year, day, st;

  min = h_zeitqs(i);

  min = min / 60;

  st = (int) min / 60;
  if ( st < 1 )
    cout <<min<<"minutes \n";
  else
  {
    day = (int) st / 24;
    min -= 60 * st;
    if ( day < 1 )
      cout<<st<<"\n hours "<<min<<" minutes \n";
    else
    {
      st -= 24 * day;
      year = (int) day / 360;
      if ( year < 1 )
         cout<<day<<" days "<<st<<" hours "<< min<<" minutes \n";
      else
      {
	day -= 360 * year;
         cout<<year<<" years "<<day<<" days "<<st<< " hours "<<min<< " minutes";
      }
    }
  }
}



inline void qs_sieve_interval(int *FB, SIEBTYP *LOGP, int *START1, int
*START2, SIEBTYP *sieb, SIEBTYP *ende, int M, int *CANDIDATE, int smallstart)
{
  register int p, l = 2, *fbp, *lsieb= (int*) sieb;
  register SIEBTYP logp;
  register SIEBTYP *begin;

  register int x, counter=0, M_2=M<<1;

  memset(sieb, 0, (M_2) * sizeof(SIEBTYP));
//  fbp = &FB[2];

   fbp=&FB[smallstart]; // NEU
   l = smallstart;      // NEU


  while ( (p = *fbp++) != 0 )
  {
    logp = LOGP[l];


    if ( START1[l] == START2[l] )
    {
      begin = sieb + START1[l];

      for(;;)
      {
	if (begin <= ende)
	{
	  (*begin) += logp;

	  begin += p;
	}
	else
	{
	  START1[l] = START2[l] = (begin - ende -1);
	  break;
	}
      }
    }
    else
    {
      begin = sieb + START1[l];
      for(;;)
      {
	if (begin <= ende)
	{
	  (*begin) += logp;
	  begin += p;
	}
	else
	{
	  START1[l] = (begin - ende -1);
	  break;
	}
      }
      begin = sieb + START2[l];
      for(;;)
      {
	if (begin <= ende)
	{
	  (*begin) += logp;
	  begin += p;
	}
	else
	{
	  START2[l] = (begin - ende -1);
	  break;
	}
      }
    }
    l++;

  }


  // NEU

   l=0;
   while (l<M_2)
       {
        if ((p= (*lsieb))& (0x80808080))    // check whether at least one of
                                            // four consequent sieve entries
                                            // is a candidate
	    { x=l;

#if WORDS_BIGENDIAN != 1

// on PCs the highest byte is the lowest
             if (p & (0x00000080))
#else

             if (p & (0x80000000))
#endif
              {

                  CANDIDATE[counter++] = x;

                  sieb[x] = 0;
                }
              else
                {

#if WORDS_BIGENDIAN != 1
                   if (p & (0x00008000))
#else
               if (p & (0x00800000))
#endif
                    {
                     x++;
                     CANDIDATE[counter++] = x;

                      sieb[x] = 0;
                    }
                  else
                    {

#if WORDS_BIGENDIAN != 1
                       if (p & (0x00800000))
#else
               if (p & (0x00008000))
#endif
                        {
                          x+= 2;

                          CANDIDATE[counter++] = x;

                          sieb[x] = 0;
                        }
                      else
                        {

#if WORDS_BIGENDIAN != 1
                        if (p & (0x80000000))
#else
              if (p & (0x00000080))
#endif
                            {
                              l+=3;

                              CANDIDATE[counter++] = l;

                              lsieb++;
                              l++;
                            }
                        }
                    }
                }
            }
          else
            {
              lsieb++;
              l+=4;
            }
       }


CANDIDATE[counter] = 0;


}





int rational_factorization::compute_multiplier(bigint &N, int bis,
ecm_primes &prim)
{
  static int cand[5] = { 1, 3, 5, 7, 11};
  bigint kN;
  register unsigned long plauf;
  register int p, j, i, k=1, nmod4;
  double wert, bestwert = 1, plus;

  nmod4 = (int) N.least_significant_digit() & 0x3;

  for (j = 0; j <= 4; j++)
  {
    if ( (((p = cand[j]) * nmod4) & 0x00000003) != 1 )
      continue;

    wert = - 0.7 *  log2((double)p);
    multiply(kN, N, p);

    if ( (kN.least_significant_digit() & 0x7) == 1 )
      wert += 1.38629;

    plauf = prim.getprimes();
    i = 0;

    while ( i <= bis )
    {
      if ( legendre((int)remainder(kN, plauf), (int) plauf) == 1)
      {
	i++;
	plus = log2((double)plauf)/(double)plauf;
	if ( (p % plauf) == 0 )     wert += plus;
	else                        wert += 2*plus;
      }

      plauf = prim.getprimes();
    }

    if (wert > bestwert)
    {
      bestwert = wert;
      k = p;
    }
    prim.resetprimes(1);
  }


 return(k);
}


int rational_factorization::create_FB(int size, const bigint &kN, int **FB,
ecm_primes &prim)
{

  register int old_size = 0, p = 2;
  register int *fbb;

#ifdef DEBUG
  char s[100];
  FILE *fbp;
  fbp=fopen("FAKTORBASIS", "w");
  fprintf(fbp,"%d \n",size);
  bigint_to_string(kN, s);
  fprintf(fbp, "%s \n", s);
  p=-1;
  fprintf(fbp, "%d \n",p);
  p=2;
#endif

  if ( *FB )        free(*FB);

    if (!(*FB=new int[size+3]))
      { lidia_error_handler_n("rational_factorization", "create_FB::can't allocate FB");
        transform_relations(1);
        return(1);
      }

  fbb = FB[0];


  *fbb++ = size;
  *fbb++ = -1;

  kleinsize=0;
  prim.resetprimes(1);

  while (old_size < size)
  {
    if ( legendre((int)remainder(kN, p), p) !=-1 )  // Multiplier is allowed in factor base!

    {
           *fbb++ = p;
#ifdef DEBUG
             fprintf(fbp,"%d \n",p);
#endif
             old_size++;
             if (p <32768)  kleinsize++;

    }
    p = prim.getprimes();
  }


  *fbb = 0;

#ifdef DEBUG
  p=0;
  fprintf(fbp, "%d \n",p);
  fclose(fbp);
#endif

  return(0);
}


int
Fbinul(int ul, int *out)  // write compute binary representation of ul
                           // to int[50]
 {
  int j = 1, counter=0;

 while (j<50)
 {
         while (ul>0)
           {
           if ( ul % 2)
             { out[j]=1; counter++; }
           else
               out[j]=0;

           ul >>=1;

           j++;
             }

          out[j]=0;
      j++;
      }

  return(counter);
}




inline void compute_coeff( bigint &A, bigint &kN, int *FB, int *SQRTkN, int
*START1, int *START2, int P_ONCE, int P_TOTAL, int SIEBSTART,  int **vorb,
int *Q_prime, int *Q_prime_glob, base_vector<bigint> &BG, base_vector<bigint>
&bbb, lidia_size_t &index_i, bool &change, int &bin_index, int start_fb,
int *a_inv, int *MERKE1, int *MERKE2)
{
 int p,l, size_FB=0, klein1, klein2, sub=0, result, lokal_i, h_p, SIEBS,tmp,
tmp2;
 lidia_size_t jj, theta=1, i;
 bigint reserve, TMP, TMP2, tmp_g;
long help_l, prod;

#ifdef DEBUG
bigint B_glob, A_glob, C, kN_glob, vergleich;
#endif



 if (change)
 {
     while (Fbinul(bin_index,out)!=P_ONCE)
       bin_index++;
     lokal_i=0;

     for (jj=1; jj<=P_TOTAL; jj++)
       { if (out[jj]==1)
         {
           Q_prime[lokal_i]=Q_prime_glob[jj-1];
           lokal_i++;

         }
       }


     A.assign(Q_prime[0]);

     for (i=1; i<P_ONCE; i++)
       { help_l=Q_prime[i];
         multiply(A,A,help_l);

       }
  bin_index++;
  change=false;
}

   if (index_i == 0)

     { // compute BG[0] to BG[P_ONCE-1]
       // each bbb[index_i] is of the form
       // v_0*BG[0]+v_1*BG[1]+...+v_(P_ONCE-1)*BG[(P_ONCE-1)],
       // where each v_j is +1 or -1

       // this has to be done only once (for index_1==0) for the
       // coefficient A; if index_1 >0 then there is a linear
       // recursion for the bbb[index_i]


       for (i=0; i<P_ONCE; i++)

         { divide(reserve, (bigint)4*A, Q_prime[i]);
           reserve*=4;
           TMP=xgcd_left(TMP2,reserve,Q_prime[i]);
           multiply(reserve,reserve,TMP2);
           remainder(reserve,reserve,A<<2);
           TMP.assign(kN);
           tmp2=(int)remainder(TMP,Q_prime[i]);
           if ((tmp=ressol(tmp2,Q_prime[i]))<0) tmp +=Q_prime[i];
           multiply(reserve,reserve,tmp);
           remainder(BG[i],reserve,(bigint)4*A);
           if (i==0)
	       {
                reserve.assign(A);
                tmp=(int) reserve.least_significant_digit();
                tmp=tmp & 3;
                if (( tmp2 = invert(tmp, 4))<0)
                    tmp2 +=4;
                multiply(reserve,A,tmp2);
                add(BG[0], BG[0],reserve);
               }
	  }

      bbb[0].assign(BG[0]);
      for (i=1; i<P_ONCE; i++)
         add(bbb[0],bbb[0],BG[i]);

         size_FB = FB[0]+1; // NEU


      for (i=2; i<=size_FB; i++)
        { remainder(prod, A,(long)FB[i]);
          a_inv[i] = invert((int)(2*prod), FB[i]);
        }

      for (l=0; l<P_ONCE; l++)
 	{
         for (jj=2; jj<=size_FB; jj++)
            {   reserve.assign(BG[l]);
                if ((result=(-2*a_inv[jj])% FB[jj])<0) result+=FB[jj];
                multiply(reserve, reserve, result);
                if ((result=(int)remainder(reserve, FB[jj]))<0) result+=FB[jj];
                vorb[l][jj]=result;
            }
        }

      for (jj=2; jj<=kleinsize; jj++)
        {
          p=FB[jj];
          SIEBS=SIEBSTART % p;
          reserve.assign(bbb[0]);
          reserve.negate();
          klein1= (int) remainder(reserve, p);
          klein2=klein1 -  SQRTkN[jj];
          klein1 = klein1 + SQRTkN[jj];
          tmp=klein1;
          tmp*=a_inv[jj];
          START1[jj] = tmp % p;
          START1[jj]=(START1[jj]+SIEBS) % p;
          if (START1[jj]<0)  START1[jj]+=p;
          MERKE1[jj] = START1[jj];
          tmp=klein2;
          tmp*=a_inv[jj];
	  START2[jj]=tmp % p;
          START2[jj]=(START2[jj]+SIEBS) %p;
          if (START2[jj]<0)  START2[jj]+=p;
          MERKE2[jj]=START2[jj];
	}

for (jj=kleinsize+1; jj<=size_FB; jj++)
        {
          p=FB[jj];
          SIEBS=SIEBSTART % p;
          reserve.assign(bbb[0]);
          reserve.negate();
          klein1= (int) remainder(reserve, p);
          klein2=klein1 -  SQRTkN[jj];
          klein1 = klein1 + SQRTkN[jj];
          tmp_g=klein1;
          tmp_g*=a_inv[jj];
          remainder(tmp_g,tmp_g, p);
          tmp_g.intify(tmp);
          START1[jj] = tmp % p;
          START1[jj]=(START1[jj]+SIEBS) % p;
          if (START1[jj]<0)  START1[jj]+=p;
          MERKE1[jj] = START1[jj];
          tmp_g=klein2;
          tmp_g*=a_inv[jj];
          remainder(tmp_g, tmp_g,p);
          tmp_g.intify(tmp);
	  START2[jj]=tmp % p;
          START2[jj]=(START2[jj]+SIEBS) %p;
          if (START2[jj]<0)  START2[jj]+=p;
          MERKE2[jj]=START2[jj];
	}

     }        // end of 'if index==1'



     if (index_i > 0)  // bbb[index_i] will be computed
      {
        size_FB=FB[0]+1; // NEU
        theta=1;

        tmp=index_i;
       while (((tmp & 1) == 0 ) && (tmp>0)) // while even
         {
           theta ++;
           tmp = tmp>>1;
         }
       jj=index_i;
       for (i=1; i<theta; i++)
          jj = jj >>1;
       jj++;
       jj = jj>>1;
       bbb[index_i].assign(BG[theta-1]);
       bbb[index_i].multiply_by_2();
       sub=1;
       if ((jj & 1) == 1)
        {
          sub=-1;
          bbb[index_i].negate();
        }
      add(bbb[index_i], bbb[index_i], bbb[index_i-1]);

    }
 // of 'if index > 1'

 if (index_i >0)
    {
      for (jj=2; jj<=size_FB; jj++)
         {
            p=FB[jj];

            if (theta<=P_ONCE)  // how much precomputation is needed
              {
                if (sub==-1)
                  {  START1[jj]=MERKE1[jj]-vorb[theta-1][jj];
                     if (START1[jj]<0) START1[jj]+=p;
                     START2[jj]=MERKE2[jj]-vorb[theta-1][jj];
                     if (START2[jj]<0) START2[jj]+=p;
                  }
                else
                  {  START1[jj]=MERKE1[jj]+vorb[theta-1][jj];
                     if (START1[jj]>=p) START1[jj]-=p;
                     START2[jj]=MERKE2[jj]+vorb[theta-1][jj];
                     if (START2[jj]>=p) START2[jj]-=p;
                  }
            MERKE1[jj]=START1[jj];
            MERKE2[jj]=START2[jj];
     	}
     else  // of  'if theta==1'
       {
         reserve.assign(BG[theta-1]);
         if ((result=((-2*sub*a_inv[jj]) % p))<0) result+=p;
         multiply(reserve, reserve, result);
         if ((result=(int)remainder(reserve, p))<0) result+=p;
         if ((START1[jj]=MERKE1[jj]+result)>=p) START1[jj]-=p;
         MERKE1[jj]=START1[jj];
         if ((START2[jj]=MERKE2[jj]+result)>=p) START2[jj]-=p;
         MERKE2[jj]=START2[jj];
       }

       }
      } // of  'if index > 0'


#ifdef DEBUG
B_glob=bbb[index_i];
A_glob=A;
kN_glob=kN;
C= (B_glob*B_glob-kN_glob)/(4*A_glob);
#endif

// now compute zeros of polynomials that have only one zero mod p
// because p divides coefficient A

   for (jj=1; jj<=P_TOTAL; jj++)
    if (out[jj]==1)
     {
        reserve.assign(bbb[index_i]);
        multiply(reserve,reserve, reserve);
        reserve.negate(); add(reserve, reserve, kN);
        TMP.assign(A);
        TMP.multiply_by_2();
        TMP.multiply_by_2();
        divide(TMP2, reserve, TMP);
        reserve.assign(bbb[index_i]);
        p=FB[start_fb+jj];
        SIEBS=SIEBSTART % p;
        tmp=(int)remainder(reserve, p);
        tmp2=invert(tmp, p);
        h_p=(int)remainder(TMP2, p);
        tmp_g=h_p * tmp2;
        tmp=(int)remainder(tmp_g, p);

//        tmp=(h_p*tmp2) % p;
        if (tmp<0) tmp+=p;
        START1[start_fb+jj]=(tmp +SIEBS) % p;
        START2[start_fb+jj]=(tmp +SIEBS) % p;

#ifdef DEBUG

	B_glob=bbb[index_i];
	A_glob=A;
	kN_glob=kN;
	C= (B_glob*B_glob-kN_glob)/(4*A_glob);
	p=FB[start_fb+jj];
	int  y=(START1[start_fb+jj]-SIEBSTART) % p;
	vergleich= ((A_glob*y*y+B_glob*y+C) % p);
	if (vergleich!=0) {
	  cout<<"\n Fehler_single START1 bei p="<<p;
	  cout<<"\n START1[i]="<<START1[start_fb+jj];
	  cout<<"\n A_glob="<<A_glob;

	  cout<<"\n B_glob="<<B_glob;
	  cout <<"\n C="<<C;
	  cout<<"\n kN_glob="<<kN_glob;
	  cout<<"\n SIEB="<<SIEBSTART; cout.flush();
	  cout<<"\n b_inv="<<tmp2;
	  cout<<"\n -c="<<h_p;
	}

#endif
     }









index_i++; // global increment
}


inline void best_remainder(bigint &a, const bigint &b, const bigint &mod)
{
 if (mod.is_one())
     a.assign_zero();
 else
     {
       bigint mod2;
       shift_right(mod2,mod,1);
       remainder(a,b,mod);
       if (a <= -mod2) add(a, a, mod);
       if (a > mod2) subtract(a, a, mod);
     }
}

inline int teste(bigint &kN, bigint &A, int *FB, int *START1, int *START2,
char *faktor, int M, double d_wurz, int *Q_prime, bigint &B, int start_fb, int P_ONCE,  int P_TOTAL, int *CANDIDATE, int *hash, int *hash_l, int smallstart)

{ short small=0;
  int fak_i, ii, M_2, counter;
  bigint H, Q_von_x, TMP, vier_a_inv, diff, sicher;
  int x =0, p, vorber, divides = 0, N1, N2;
  long rest;
  int geteilt=0, upper_bound, ready, rest_i;
  int adr;
  FILE  *fp, *fpla;
  double  a, b;
  char Hstring[150];
  long wert_p, wert_n;

  if ( !(fp = fopen(get_name("FAKM"),"a")) )
    { lidia_error_handler_n("rational_factorization", "teste::can't open FAKTORENMATRIX");
    transform_relations(1);
    return(-1);
    }

//   fplb=fopen("ANFANG","a");

//   if ( !(fpla = fopen("PART","a")) )
    if ( !(fpla = fopen(get_name("PART"),"a")) )
    { lidia_error_handler_n("rational_factorization", "teste::can't open PARTION");
    return(-1);
    }


                    // compute the roots of the polynomial
  b=dbl(B);
  a = dbl(A);
  a *= 2;
  N1 = (int) ((-b - d_wurz) / a);
  N2 = (int) ((-b + d_wurz) / a);
  TMP.assign(A);
  TMP.multiply_by_2();
  TMP.multiply_by_2();
  M_2 = M << 1;
  Q_von_x=xgcd_left(vier_a_inv,TMP,kN);
  if (vier_a_inv.is_lt_zero()) vier_a_inv+=kN;
  upper_bound=start_fb+P_TOTAL+1;
  counter=0;



  while ((x=CANDIDATE[counter++])!=0)   // while there are candidates to test
   {  cands++;
      counter_glob++;
      x-=M;
      multiply(Q_von_x, A, x<<1);
      add(Q_von_x, Q_von_x, B);
      H.assign(Q_von_x);
      div_rem(TMP,H,H,kN);
      square(Q_von_x, Q_von_x);
      div_rem(TMP,Q_von_x,Q_von_x,kN);
      multiply(Q_von_x, Q_von_x, vier_a_inv);
      div_rem(TMP,Q_von_x, Q_von_x,kN);




      memset(faktor, 0, 600);

      if (N1 < x && x < N2)
       {
       	subtract(Q_von_x, kN, Q_von_x);
     	sprintf(faktor," 1 1");
       }

      while ( Q_von_x.is_even() )
      { divides++;
	Q_von_x.divide_by_2();
      }

      if ( divides )
      {
	sprintf(faktor,"%s %d 2", faktor, divides+2); // '+2' because of 4*A
	divides = 0;
      }
      else
       sprintf(faktor,"%s %d 2", faktor, 2);

//      fak_i = 3;
      fak_i=2;  // NEU
      divides=0;
      ready=0;

 // smallstart - Behandlung
      // ACHTUNG: Annahme, dass smallstart kleiner als start_fb ist !!


     while (fak_i++ < smallstart-1)
       {
         p = FB[fak_i];

         vorber=(M + x ) %p;




         if ((vorber==START1[fak_i]) || ( vorber==START2[fak_i]))
	   { do
	     { div_rem(TMP, rest, Q_von_x, (long)p);
              if ( rest==0 )
                {
                   divides++;
                   Q_von_x.assign(TMP);
                }
             }
             while ( rest == 0 );

#ifdef DEBUG
           if (divides==0) { cout<<"\n Fehler in smallstart-Abschnitt";
                cout<<"\n Fehler bei p="<<p;
                cout<<"\n vorber="<<vorber;

	   }

#endif

             sprintf(faktor,"%s %d %d", faktor, divides, fak_i);

      divides = 0;
        }
     }




// Ende Behandlung smallstart

      while ( (p=FB[fak_i]) != 0 )
	{
         vorber = p - (M - x) % p;
                 if ( vorber == p )           vorber = 0;

        if ((fak_i<=upper_bound) && (fak_i> start_fb))
           {
            ii  =ready;
            while (ii<P_ONCE)
             { if (p==Q_prime[ii])
                 { geteilt=1; ready=ii+1; break; }
                   ii++;
             }
           }



         if (geteilt==1)  // if p divides coefficient A ...

	   {
            if (vorber==START1[fak_i]) // ... and divides Q_von_x

	      {
               do
		{ div_rem(TMP, rest, Q_von_x, (long) p);
                  if (rest==0)
	            {
                      divides++;
                      Q_von_x.assign(TMP);

                    }

		}
               while (rest==0);
              }
            sprintf(faktor,"%s %d %d",faktor,1+divides,fak_i);

            geteilt=0;
            divides=0;
	 }
     else
     {
       if ( (vorber == START1[fak_i]) || (vorber == START2[fak_i]) )
                                          // if p divides Q_von_x
	 {
	   do
	     {
	      div_rem(TMP, rest, Q_von_x, (long)p);
	      if ( rest==0 )
	        {
	           divides++;
       	           Q_von_x.assign(TMP);
	        }
	     }
	   while ( rest == 0 );


	   sprintf(faktor,"%s %d %d", faktor, divides, fak_i);

#ifdef DEBUG
               if (!divides) { cout<<"\n"<<fak_i;
                           cout<<"\n START1="<<START1[fak_i];
                           cout<<"\n START2="<<START2[fak_i];
                           cout<<"\n vorber="<<vorber;
                           cout<<"\n FB[fak_i]="<<FB[fak_i];
                           cout<<"\n fak_i="<<fak_i;
                           cout<<"\n x="<<x;

                         }
#endif
	   divides = 0;
	}
     }
	fak_i++;
   }
	




// to avoid duplication of relations (which might cause problems in the
// linear equation system) use hash function on H(x)

    if (  Q_von_x.is_one() )
      {
       small_cand++;
       wert_p=remainder(H,HASH_TAB);
       wert_n=remainder(-H,HASH_TAB);

       if (wert_p<0) wert_p+=HASH_TAB;
       if (wert_n<0) wert_n+=HASH_TAB;

        if ((hash[wert_p]==0) && (hash[wert_n]==0))

	 {
    	   small++;
   	   bigint_to_string(H, Hstring);
//           fprintf(fp, "%s :%s 0\n", Hstring, faktor);
           fprintf(fp, "%s 1 :%s 0\n", Hstring, faktor); // NEU
           hash[wert_p]=1;
           hash[wert_n]=1;

         }
       else wrong_small++;
      }

//  #ifdef LARGE
     else
          if ((Q_von_x.intify(rest_i))==0)
               if (rest_i<2000000)
		  {
#ifdef DEBUG
            if (rest_i<1000) cout<<"\n rest_i="<<rest_i;
#endif

           partials++;

            wert_p=remainder(H,HASH_TAB_L);
            wert_n=remainder(-H,HASH_TAB_L);

            if (wert_p<0) wert_p+=HASH_TAB_L;
            if (wert_n<0) wert_n+=HASH_TAB_L;


           if ((hash_l[wert_p]==0) && (hash_l[wert_n]==0))

    	       {
    	
                 hash_l[wert_p]=1;
                 hash_l[wert_n]=1;


                     bigint_to_string(H, Hstring);
                     fprintf(fpla, "%9d @ %s:%s 0\n", rest_i, Hstring, faktor);
                     adr=rest_i % LEN;
                     if (treffer[adr]==0)
                        treffer[adr]=rest_i;
                     else
                        if (treffer[adr]==rest_i)   // echter Treffer
                          counter_treff++;

               }
		
		  }

   }
	
  fclose(fp);
  fclose(fpla);
  return(small);
}


bool rational_factorization::qs_build_factors(bigint & N, bigint & kN,
int index, int *FB)
{  FILE *faktorenmatrix, *allsolutions;
  rf_single_factor fact;
  bigint X_quad, Y_quad, hilf, hilf2;
  int counter=0, k, end_FB = FB[0] + 1,e, eoftest, erg1, erg2;
  bool found=false;
  register int i, zeilenindex = 0;
  char zeile[GET_LENGTH], Hstring[150], Hstring2[150],rest[300];
  static long *expo = NULL;  // static
  long exp_i;
  lidia_size_t ii;
  char *p, *pp;


  if ( !(expo = new long[end_FB+1]))
    { lidia_error_handler_n("rational_factorization", "qs_build_factors::can't allocate expo");
      transform_relations(1);
      return(1);
    }


   memset(expo, 0, (end_FB+1) * sizeof(long));

  if ( !(faktorenmatrix=fopen(get_name("FAKM"), "r")) )
    { lidia_error_handler_n("rational_factorization", "qs_build_factors::can't open FAKTORENMATRIX");
      transform_relations(1);
      return(1);
    }

#ifdef DEBUG
   cout << "\n trying to solve the GLS \n\n"; cout.flush();
#endif

  qs_gauss((long)end_FB);

#ifdef DEBUG
    cout<<"\n after qs_gauss \n"; cout.flush();
#endif

  if ( !(allsolutions = fopen(get_name("ALLS"), "r")) )
   {
    fclose(faktorenmatrix);
    return(1);
   }


  while ( !found )         // verify all solutions
  {
    //cout<<"\n solution found"; cout.flush();
    X_quad.assign_one();
    Y_quad.assign_one();

    counter=1;
    fgets(zeile, GET_LENGTH, faktorenmatrix);   // skip .. relations

    while (1)                      // read a solution from ALLSOLUTIONS
    {

      if ((eoftest = fscanf(allsolutions, "%d\n", &zeilenindex)) == EOF) break;
      if (zeilenindex == 7654321) break;

      while (counter <= zeilenindex)
      {
	fgets(zeile, GET_LENGTH, faktorenmatrix);
	counter++;
      }


      sscanf(zeile, "%s %s : %s", Hstring, Hstring2, rest);
      string_to_bigint(Hstring, hilf);
      string_to_bigint(Hstring2,hilf2);

      multiply(X_quad, X_quad, hilf2);
      remainder(X_quad, X_quad, kN);

      multiply(Y_quad, Y_quad, hilf);
      remainder(Y_quad, Y_quad, kN);

      // read single exponents and sum them up in array for later use

      if ((pp = (char*) strchr(zeile, ':') ))
      {
	pp += 2;
	p = (char*) strtok(pp, " \n");
	while (p != NULL)
	{
	  e = atoi(p);
	  if ( !e )            break;
	  p = (char*) strtok(NULL, " \n");
	  expo[atoi(p)] += e;

	  p = (char*) strtok(NULL, " \n");
	}
      }

    } // solution read

    if (eoftest == EOF)         break;


     for (i = 2; i <= end_FB; i++)

      {
      exp_i = expo[i];
      if ( exp_i>0 )
      {

       exp_i=exp_i>>1;

	while ( exp_i !=0 )
	{  exp_i--;
	  multiply(X_quad, X_quad, FB[i]);
	  if ( X_quad.compare(kN) >= 0  )
	    remainder(X_quad, X_quad, kN);
	}


      }

      else
       if (exp_i<0)
         {
	      int help_e=-exp_i;
              help_e=help_e >>1;
              exp_i=-help_e;


           while (exp_i!=0)
            { exp_i++;
              Y_quad=Y_quad*FB[i];
            }

         Y_quad=Y_quad % kN;

	 }
      }

#ifdef DEBUG
     bigint links=X_quad*X_quad;
     bigint rechts=Y_quad*Y_quad;

     remainder(links, links, kN); if (links<0) links +=kN;
     remainder(rechts, rechts, kN); if (rechts<0) rechts +=kN;

    if (links.compare(rechts)==0) cout<<"\n GLEICH";
       else {
               cout<<"\n UNGLEICH";
//               cout<<"\n links="<<links;
//               cout<<"\n rechts="<<rechts;
       }

#endif

    add(hilf, X_quad, Y_quad);
    subtract(Y_quad, Y_quad, X_quad);
    X_quad.assign(hilf);

    if ( X_quad.compare(kN) > 0 ) subtract(X_quad, X_quad, kN);
    if ( Y_quad.compare(kN) > 0 ) subtract(Y_quad, Y_quad, kN);

    X_quad = bgcd(X_quad, N);
    Y_quad = bgcd(Y_quad, N);

    if ( !(X_quad.compare(N) && Y_quad.compare(N)) )
    {
      fseek(faktorenmatrix, 0, 0);
      memset(expo, 0, (end_FB+1) * sizeof(long));
    }

    else
    {
      erg1 = is_prime(X_quad, 4);
      erg2 = is_prime(Y_quad, 4);

      if ( erg1 || erg2 )
      {
	if ( erg1 && erg2 )
	{
	  if (info)
	  {
	    bigint_to_string(X_quad, zeile);
	    cout << "factor : " << zeile << "         (" << strlen(zeile) <<
" digits)\n";
	    bigint_to_string(Y_quad, zeile);
	    cout << "factor : " << zeile << "         (" << strlen(zeile) <<
 " digits)\n";
	  }
	
           k=no_of_comp();

	  fact.single_base = X_quad;
	  fact.single_exponent = factors[index].single_exponent;
	  fact.factor_state = prime;
	  factors[index] = fact;
	
	  fact.single_base = Y_quad;
	  fact.single_exponent = factors[index].single_exponent;
	  fact.factor_state = prime;
	  factors[k] = fact;

	  found = true;

	}
	else
	{
	  if (erg1)
	  {
	    kN.assign(Y_quad);

	    if (info)
	    {
	      bigint_to_string(X_quad, zeile);
	      cout << "factor : " << zeile << "         (" << strlen(zeile) <<
 " digits)\n";
	    }
	
            k=no_of_comp();

	    fact.single_base = X_quad;
	    fact.single_exponent = factors[index].single_exponent;
	    fact.factor_state = prime;
	    factors[k] = fact;

            i=power_test_complete(hilf,Y_quad);

            if (i!=-1)
		{
                   if (is_prime(hilf,4))
		      { fact.single_base=hilf;
                        fact.single_exponent=factors[index].single_exponent*i;
                        fact.factor_state=prime;
                        factors[index]=fact;
                        compose();
                        found=true;
                      }
                   else
		        { rational_factorization A_fact;
                          A_fact.assign(hilf);
                          A_fact.factor();
                          int kk=A_fact.no_of_comp();
                          factors[index].single_base=A_fact.factors[0].single_base;
                          factors[index].single_exponent=A_fact.factors[0].single_exponent*i;
                          factors[index].factor_state=A_fact.factors[0].factor_state;

                          k=no_of_comp();
                          lidia_size_t j;
                          for (j=1; j<=kk; j++)
                           { factors[k].single_base=A_fact.factors[j].single_base;
                             factors[k].single_exponent=A_fact.factors[j].single_exponent*i;
                             factors[k].factor_state=A_fact.factors[j].factor_state;
                             compose();
                             k++;
                           }
                         found=true;
		        }
                }

	    fseek(faktorenmatrix, 0, 0);
	    memset(expo, 0, (end_FB+1) * sizeof(long));
	  }
	  else
	  {
	    kN.assign(X_quad);
	    if (info)
	    {
	      bigint_to_string(Y_quad, zeile);
	      cout << "factor : " << zeile << "         (" << strlen(zeile) <<
" digits)\n";
	    }

            k=no_of_comp();

	    fact.single_base = Y_quad;
            counter=factors[index].single_exponent;

	    fact.single_exponent = counter;
	    fact.factor_state = prime;
	    factors[k] = fact;

            i=power_test_complete(hilf,X_quad);

            if (i!=-1)
		{
                  if (is_prime(hilf,4))
		      {
                        fact.single_base=hilf;
                        fact.single_exponent=counter*i;

                        fact.factor_state=prime;
                        factors[index]=fact;
                        compose();
                        found=true;
                      }

                   else

		    { rational_factorization A_fact;
                          A_fact.assign(hilf);
                          A_fact.factor();
                          int kk=A_fact.no_of_comp();
                          factors[index].single_base=A_fact.factors[0].single_base;
                          factors[index].single_exponent=A_fact.factors[0].single_exponent*i;
                          factors[index].factor_state=A_fact.factors[0].factor_state;

                          k=no_of_comp();
                          int j;

                          for (j=1; j<=kk; j++)
                           { factors[k].single_base=A_fact.factors[j].single_base;
                             factors[k].single_exponent=A_fact.factors[j].single_exponent*i;
                             factors[k].factor_state=A_fact.factors[j].factor_state;
                             compose();

                             k++;
                           }
                        found=true;

		      }


		}

	    fseek(faktorenmatrix, 0, 0);
	    memset(expo, 0, (end_FB+1) * sizeof(long));
	  }
	}
      }
      else
      {
        i=power_test_complete(hilf,X_quad); ii=power_test_complete(hilf2,Y_quad);

        if ((i!=-1) && (ii!=-1))
	    {
               if ((is_prime(hilf,4)) && (is_prime(hilf2,4)))
		      {
                        fact.single_base=hilf;
                        counter=factors[index].single_exponent;
                        fact.single_exponent=counter*i;
                        fact.factor_state=prime;
                        factors[index]=fact;
                        k=no_of_comp();
                        fact.single_base=hilf2;
                        fact.single_exponent=counter*ii;
                        fact.factor_state=prime;
                        factors[k]=fact;
                        compose();
                        found=true;
                      }
            }
       else
	   {

	fseek(faktorenmatrix, 0, 0);
	memset(expo, 0, (end_FB+1) * sizeof(long));

	   }
      }
    }
  }

  fclose(faktorenmatrix);
  fclose(allsolutions);

  return( !found );
}

rational_factorization & rational_factorization::
mpqs(lidia_size_t index, ecm_primes & prim)
{
  bigint N(factors[index].single_base);
  bigint A, kN;
  register int tmp, stellenzahl, i, k, p;
  FILE *fp;
  int small, smalls=0, size_FB, M, *SQRTkN = NULL, *FB = NULL, *START1 = NULL,
  *a_inv=NULL, *MERKE1=NULL, *MERKE2=NULL, *CANDIDATE=NULL,check=0, smallstart;
  int *START2 = NULL, vergleich =10,prozent, *Q_prime=NULL, *Q_prime_glob=NULL,
 start_fb, bin_index, starte_gls,P_ONCE, P_TOTAL, POLY, *hash=0, *hash_l=0, schief=0;
  SIEBTYP *sieb = NULL, *ende, *LOGP = NULL;
  char    *faktor, zeile[150];
  double  d_wurz, kN_double, T, wurz, LOGMUL;
  int **vorb;
//  base_vector< bigint > bbb, BG;
  rf_single_factor fact;
  bool change;
  lidia_size_t index_i;

  char command1[150];

  sprintf(command1,"sort -n %s >%s",get_name("PART"),get_name("ZUSA"));

  for (i=1; i<LEN; i++)
     treffer[i]=0;

  i=power_test_complete(A,N);

  if (i!=-1)             // perfect power handling
      {

          int k=no_of_comp();

	  fact.single_base = A;
	  fact.single_exponent = factors[index].single_exponent*i;

          if (is_prime(A,4))
             { fact.single_base = A;
   	      fact.single_exponent = factors[index].single_exponent*i;
              fact.factor_state = prime;
              factors[index] = fact;
            }
          else
             {  rational_factorization A_fact;
                A_fact.assign(A);
                A_fact.factor();

                int kk=A_fact.no_of_comp();
                factors[index].single_base=A_fact.factors[0].single_base;
                factors[index].single_exponent=A_fact.factors[0].single_exponent*i;
                factors[index].factor_state=A_fact.factors[0].factor_state;
                int j;
                for (j=1; j<=kk; j++)
                  { factors[k].single_base=A_fact.factors[j].single_base;
                    factors[k].single_exponent=A_fact.factors[j].single_exponent*i;
                    factors[k].factor_state=A_fact.factors[j].factor_state;
                    compose();

                    k++;
		
                   }

	     }
     return *this;
      }



  if ( info )
  {

    stellenzahl = bigint_to_string(N, zeile);
    cout << "\n\nQS                -- quadratic sieve\n";
    cout << "==\n\n";
    cout << "number to factor: " << zeile << "(" << stellenzahl << ")\n\n";
    cout.flush();
  }


    k = compute_multiplier(N, 5, prim);


   if (info)
    cout << "Multiplier " << k << "\n";

  multiply(kN, N, k);

  stellenzahl = bigint_to_string(kN, zeile);

//  if (stellenzahl<26)
      starte_gls=100;            // when 95 procent of relations are found,
                                 // start solving linear equation system
//  else
//      starte_gls=100;

if (info)
 {
     cout<<"\nestimated running time (usr-time):";
     cout.flush();
     zeitqs(stellenzahl);
     cout.flush();
 }

  if ( (stellenzahl < 10) || (stellenzahl > 75) )
   { lidia_error_handler_n("rational_factorization", "qs_read_par::parameters out of range - number too big");
     return(*this);
   }

  qs_read_par(stellenzahl, T, M, size_FB, P_ONCE,POLY, P_TOTAL,smallstart);

 if ( info )
  {
    cout << "\nNumber_Of_Factors of sieve interval " << M;
    cout << "\n#Factorsingle_base " << size_FB << "\n" ;
    cout.flush();
  }

  check=create_FB(size_FB, kN, &FB, prim);
  if (check) return(*this);          // error has occurred


  if ( LOGP )        delete[] LOGP;
  if ( SQRTkN )      delete[] SQRTkN;
  if ( START1 )      delete[] START1;
  if ( START2 )      delete[] START2;
  if ( sieb )        delete[] sieb;
  if ( CANDIDATE )   delete[] CANDIDATE;
  if (a_inv)         delete[] a_inv;
  if (MERKE1)        delete[] MERKE1;
  if (MERKE2)        delete[] MERKE2;


  //bbb.set_capacity(POLY);
//  BG.set_capacity(P_ONCE);

    base_vector<bigint> bbb(POLY, 'F');
    base_vector<bigint> BG(P_ONCE, 'F');

   if ( !(sieb = new SIEBTYP[M<<1]))
     { lidia_error_handler_n("rational_factorization", "mpqs::allocate SIEB");
       return(*this);
     }
  if (!(faktor = new char[600]))
    { lidia_error_handler_n("rational_factorization", "mpqs::allocate SIEB");
      return(*this);
    }
   if (!(LOGP = new SIEBTYP[size_FB + 2]))
     { lidia_error_handler_n("rational_factorization", "mpqs::allocate LOGP");
       return(*this);
     }
   if (!(SQRTkN = new int[size_FB + 2]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate SQRTkN");
        return(*this);
      }
   if (!(START1 = new int[size_FB + 2]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate START1");
        return(*this);
      }
   if (!(START2 = new int[size_FB + 2]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate START2");
         return(*this);
      }
   if (!(a_inv = new int[size_FB + 2]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate a_inv");
        return(*this);
      }
   if (!(MERKE1 = new int[size_FB + 2]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate MERKE1");
        return(*this);
      }
   if (!(MERKE2= new int[size_FB + 2]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate MERKE2");
      return(*this);
      }
   if (!(CANDIDATE=new int[CAND_NUMBER]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate CANDIDATE");
        return(*this);
      }
   if (!(Q_prime_glob= new int[P_TOTAL]))
       { lidia_error_handler_n("rational_factorization", "mpqs::allocate Q_prime");
         return(*this);
       }

   if (!(Q_prime= new int[P_ONCE]))
      { lidia_error_handler_n("rational_factorization", "mpqs::allocate Q_prime");
        return(*this);
      }

   if ( !(hash = new int[HASH_TAB]))
      { lidia_error_handler_n("rational_factorization", "teste::can't allocate hash");
        return(*this);
      }

   if ( !(hash_l = new int[HASH_TAB_L]))
        { lidia_error_handler_n("rational_factorization", "teste::can't allocate hash");
        return(*this);
      }

   vorb=new int*[P_TOTAL];

   for (i=0; i<=P_TOTAL-1; i++)
    {
      if (!(vorb[i]=new int[size_FB+2]))
          { lidia_error_handler_n("rational_factorization", "mpqs::allocate vorb");
            return(*this);
          }
   }

   ende = sieb + (M<<1) - 1;



#ifdef TIMER
  zeit_ges.cont_timer();
#endif

  memset(hash,0, HASH_TAB*sizeof(int));    // use hash function to avoid
                                           // duplicate relations

  memset(hash_l,0, HASH_TAB_L*sizeof(int));

   kN_double = dbl(kN);

  LOGMUL=127.0/(2 * (SIEBTYP)(0.5 * log2(kN_double) + log2((double)M)-T*log2((double)
FB[FB[0]+1])));


 tmp = size_FB + 2;

 for (i = 2; i < tmp; i++)
  {
    p = FB[i];
    LOGP[i] = (SIEBTYP)( LOGMUL*log2((double)p)*2 );

    if ( (SQRTkN[i] = ressol( (int)remainder(kN, p), p)) < 0 )
      SQRTkN[i] += p;     // compute sqrt(kN) modulo different moduls p
  }


  kN_double = dbl(kN);
  d_wurz = sqrt( kN_double );


                           // the size of coefficient A should be approximately
                           // sqrt(kN/M), so the size of the primes p dividing
                           // A should be approximately (sqrt(kN/M))^(1/P_ONCE)
  wurz=d_wurz/M;
  wurz=exp(log(wurz)/P_ONCE);
  if (wurz>FB[size_FB-1])
    { lidia_error_handler_n("rational_factorization","mpqs: P_ONCE too small");
      return(*this);
    }


  i=2;
  while (FB[i]<wurz) i++;
     start_fb=i;          // P_TOTAL consecutive primes are chosen from the
                          // factor base


   if (i>7) start_fb-=(P_ONCE>>1);

   if (k>=FB[start_fb])         // Multiplier must not occur in factor base
   { while (k>=FB[start_fb])
       start_fb++;
   }



  for (i=0; i<P_TOTAL; i++)
     Q_prime_glob[i]=FB[start_fb+i+1];  // collect prime numbers which will
                                        // build the A coefficents

  if ( (fp = fopen(get_name("FAKM"), "w")) == NULL )


    { lidia_error_handler_n("rational_factorization", "mpqs::can' open FAKTORENMATRIX");
     transform_relations(1);
      return(*this);
    }


  fprintf(fp, "%10d relations\n", NULL);
  fclose(fp);

  bin_index=1;
  change=true;
  index_i=0;
  poly=0;

  signal(SIGTERM,&stop_it);
  signal(SIGINT, &stop_it);
  signal(SIGHUP, &stop_it);
  signal(SIGQUIT, &stop_it);


  while ( 1 )                       // central loop of
                                    // - computing polynomials and zeros
                                    // - sieving
                                    // - testing candidates of the sieve array
  {
    if (index_i==POLY)              // when all of the B's have already
                                    // been used, choose new A
        {
          change=true;
          index_i=0;
        }

#ifdef TIMER
comp_t.cont_timer();
#endif

    compute_coeff(A, kN, FB, SQRTkN, START1, START2, P_ONCE, P_TOTAL, M, vorb,
 Q_prime, Q_prime_glob, BG, bbb, index_i,change,bin_index, start_fb, a_inv,
MERKE1, MERKE2);

#ifdef TIMER
comp_t.stop_timer();
#endif

#ifdef TIMER
comp_c+=(comp_t.user_time()+comp_t.sys_time())/10.0;
#endif
    poly++;

#ifdef TIMER
sieb_t.cont_timer();
#endif

   qs_sieve_interval(FB, LOGP, START1, START2, sieb, ende, M, CANDIDATE,smallstart);

#ifdef TIMER
sieb_t.stop_timer();
sieb_c+=(sieb_t.user_time()+sieb_t.sys_time())/10.0;
teste_t.cont_timer();
#endif

   small = teste(kN, A, FB, START1, START2, faktor, M, d_wurz, Q_prime,
 bbb[index_i-1], start_fb, P_ONCE, P_TOTAL, CANDIDATE, hash, hash_l, smallstart);
#ifdef TIMER
teste_t.stop_timer();
teste_c+=(teste_t.user_time()+teste_t.sys_time())/10.0;
#endif

   if (small==-1)  // error has occurred
     return(*this);

   smalls +=small;

   prozent = (int)((double)smalls/(double)size_FB*100);

   if (prozent >=vergleich)
    {
if (info)
    {
      cout << smalls << " (" << prozent << "%) relations found.\n"; cout.flush();
#ifdef DEBUG
     cout<<"\n counter_treff="<<counter_treff; cout.flush();
     cout<<"\n";
#endif
    }
      vergleich +=10;
    }



  if ((smalls+counter_treff)>size_FB)

     {
#ifdef DEBUG
       cout<<"\n case"; cout.flush();
       cout<<"\n smalls="<<smalls; cout.flush();
       cout<<"\n percent="<<prozent; cout.flush();
#endif

       system(command1);

       zusam(kN,size_FB+1);

       prozent=101; // start solving linear system immediately
     }



  if (prozent > starte_gls)
    {


     schief=transform_relations(0);
#ifdef DEBUG
     cout<<"\n smalls="<<smalls;
#endif
     if (schief) return(*this);        // error in transform_relations occurred
     if ( qs_build_factors(N, kN, index, FB) == false )
      break;
   }
  }


  transform_relations(1);  // setze static-Variable zurueck
  unlink(get_name("ELIM"));
  unlink(get_name("ALLS"));
  unlink(get_name("SOLU"));
  unlink(get_name("NEWR"));
  unlink(get_name("REL"));
  unlink(get_name("FAKM"));
  unlink(get_name("HOWF"));
  unlink(get_name("PART"));
  unlink(get_name("NEUE"));
  unlink(get_name("ZUSA"));
  unlink(get_name("UMSP"));

#ifdef TIMER
  zeit_ges.stop_timer();
  cout<<"\n Gesamt: "<<(double)((zeit_ges.user_time()+zeit_ges.sys_time())/100.0);
#endif

  return *this;



}


rational_factorization & rational_factorization::
mpqs_comp(lidia_size_t index)
{
  lidia_size_t old_no;

 if ( (index < 0) || (index > no_of_comp()) )
    lidia_error_handler_n("rational_factorization", "mpqs::index out of range");

 if ( factors[index].factor_state == prime || is_prime(factors[index].single_base, 8) )
  {
    if (info)
      cout << "\n prime number "  << factors[index].single_base << "\n";
    factors[index].factor_state = prime;
    return *this;
  }

  old_no=no_of_comp();

  ecm_primes prim(2, 200000, 100000);

  trialdiv(index, 1, 180000, prim);

  prim.resetprimes(1);

  if (old_no!=no_of_comp())
     if ( is_prime_factor(index) )
       return *this;

  mpqs(index, prim);

#ifdef TIMER
  cout<<"\n sieb_c="<<sieb_c;
  cout<<"\n teste_c="<<teste_c;
  cout<<"\n comp_c="<<comp_c;
#endif
#ifdef DEBUG
  cout<<"\n cands="<<cands;
  cout<<"\n partials="<<partials;
  cout<<"\n poly="<<poly;
  cout.flush();
#endif
 compose();


  return *this;
}

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

  f.verbose(0);
  f.mpqs_comp(0);

  return f;
}
