polynomial.cc

Go to the documentation of this file.
00001 // polynomial arithmetic
00002 // written by Thorsten Reinecke, Bernd Edler & Frank Heyder, 1999-12-10
00003 // some changes: 1999-12-30
00004 // quick review: 2003-07-02 by Thorsten Reinecke
00005 // last change: 2005-03-30
00006 
00007 
00028 #ifndef POLYNOMIAL_cc_
00029 #define POLYNOMIAL_cc_
00030 
00031 #include <cstdlib>
00032 #include <iostream>
00033 #include <new>
00034 #include "utils.H"
00035 
00036 #include "polynomial.H"
00037 
00038 
00040 namespace polynomial
00041 {
00042 
00043 //#define DEBUG    /* for explicit DEBUGGING-sessions */
00044 //#define VERBOSE  /* be more verbose */
00045 
00046 // this define should be triggered by Makefile
00047 // #define USE_DFT /* enable use of discrete fourier transform */
00048 
00049 using std::cout;
00050 using std::cerr;
00051 using std::endl;
00052 using std::cin;
00053 
00054 
00055 void print (const TconstPolynom P, int k)
00056 {
00057   //cout << "polynomial, k=" << k << " :" << flush;
00058   while (--k>0) 
00059    {
00060      mpz_out_str(stdout,10,P[k]);
00061      cout << "*x^" << k << " + ";
00062    }
00063   if (k==0) mpz_out_str(stdout,10,P[0]);
00064   else cout << "0";
00065 }
00066 
00067 void eval(mpz_t res, const TconstPolynom P, const int k, const mpz_t x, const mpz_t m)
00068 // computes res = P(x) mod m  using the Horner scheme
00069 {
00070   mpz_t y; // temporary variable needed for &res=&x or &res=&m
00071   mpz_init_set(y,P[k-1]);
00072   for (int i=k-2; i>=0; --i)
00073    {
00074      mpz_mul(y,y,x); mpz_add(y,y,P[i]); mpz_mod(y,y,m);
00075    }
00076   mpz_set(res,y);
00077   mpz_clear(y);
00078 }
00079 
00080 
00081 // a small template (now and then)
00082 template<typename T> inline T ld(T n)
00083 {
00084   // 2er-logarithm (truncated)
00085   T r = 0;
00086   while(n>>=1) ++r;
00087   return r;
00088 }
00089 
00090 } // namespace polynomial
00091 // leave the namespace to include optional polynomial implementations
00092 // possibly including header files in other namespaces...
00093 
00094 
00095 #ifdef USE_DFT
00096  #include "dft.cc" // fast discrete fourier transformation
00097 #endif
00098 
00099 
00100 // enter the namespace again
00101 namespace polynomial
00102 {
00103 
00104 int classic_mul(const TPolynom __restrict__ Pr, const int kr,
00105                 const TconstPolynom P1, const int k1,
00106                 const TconstPolynom P2, const int k2)
00107 // Pres = P1*P2, &Pres must be different from &P1,&P2
00108 // returns degree of the resulting polynomial
00109 // complexity: O(k1*k2)
00110 // (classical method, implemented alternatively to Karatsuba for small polynomials)
00111 {
00112   if (kr<k1+k2-1) cerr << "classic_mul: Not enough memory for result polynomial" << endl;
00113   for (int i=0; i<kr; ++i) mpz_set_ui(Pr[i],0);
00114 
00115   for (int i=0; i<k1; ++i)
00116      for (int j=0; j<k2; ++j)
00117         mpz_addmul(Pr[i+j],P1[i],P2[j]);
00118 
00119   return k1+k2-1;
00120 }
00121 
00122 int classic_mul(const TPolynom __restrict__ Pr, const int kr,
00123                 const TconstPolynom P1, const int k1,
00124                 const TconstPolynom P2, const int k2, const mpz_t m)
00125 // Pres = P1*P2, &Pres must be different from &P1,&P2
00126 // returns degree of the resulting polynomial
00127 // complexity: O(k1*k2)
00128 // (classical method, implemented alternatively to Karatsuba for small polynomials)
00129 {
00130   if (kr<k1+k2-1) cerr << "classic_mul: Not enough memory for result polynomial" << endl;
00131   for (int i=0; i<kr; ++i) mpz_set_ui(Pr[i],0);
00132 
00133   for (int i=0; i<k1; ++i)
00134      for (int j=0; j<k2; ++j)
00135         mpz_addmul(Pr[i+j],P1[i],P2[j]);
00136 
00137   // mod besser auslagern:
00138   for (int i=k1+k2-2; i>=0; --i)
00139     mpz_mod(Pr[i],Pr[i],m);
00140   return k1+k2-1;
00141 }
00142 
00143 
00144 static int square_rek(const TPolynom R, const int kR,
00145                       const TconstPolynom P, const int k,
00146                       const TPolynom temp)
00147 // R = P^2, &R must be different from &P
00148 // returns degree of resulting polynomial
00149 // the result polynomial must provide enough temporary memory!
00150 // complexity: O(k^1.59) -> Karatsuba
00151 {
00152 #if 1
00153   if (k==2) // manually optimized...
00154    {
00155      mpz_mul(R[0],P[0],P[0]);
00156      mpz_mul(R[1],P[0],P[1]); mpz_mul_2exp(R[1],R[1],1);
00157      mpz_mul(R[2],P[1],P[1]);
00158      return 3;
00159    }
00160 #endif
00161   if (k==1) // this is the real base case for the recursion
00162    {
00163      mpz_mul(R[0],P[0],P[0]);
00164      return 1;
00165    }
00166 
00167 
00168   const int Middle=k/2; // Middle separates LO/HI for divide & conquer
00169 
00170   // Pr=P^2
00171   // P split into in PL=P[0]..P[Middle-1], PH=P[Middle]..P[k-1]
00172   //                 L->Lower part        H->Higher part
00173 
00174   const TconstPolynom PL=&P[0]; const int kPL=Middle;
00175   const TconstPolynom PH=&P[kPL]; const int kPH=k-Middle;
00176 
00177   const int kH1=MAX(kPL,kPH);
00178   const TPolynom H1=&R[0];
00179   for (int i=0; i<kPL; ++i) mpz_add(H1[i],PH[i],PL[i]);
00180   for (int i=kPL; i<kH1; ++i) mpz_set(H1[i], PH[i]);
00181 
00182   int kM=2*kH1-1;
00183   const TPolynom M=&R[Middle]; // mittleres Polynom
00184   const TPolynom temp2 = &temp[2*kH1-1];
00185   kM=square_rek(temp,kM,H1,kH1,temp2);
00186 
00187   for (int i=kR-1; i>=0; --i) mpz_set_ui(R[i],0); // result polynomial: fill coefficients with zeros.
00188 
00189   int kL=kPL+kPL-1;
00190   const TPolynom L=R;
00191   kL=square_rek(L,kL,PL,kPL,temp2);
00192 
00193   int kH=kPH+kPH-1;
00194   const TPolynom H=&R[Middle*2];
00195   kH=square_rek(H,kH,PH,kPH,temp2);
00196 
00197   for (int i=0; i<kL; ++i) { mpz_sub(temp[i], temp[i], L[i]); }
00198   for (int i=0; i<kH; ++i) { mpz_sub(temp[i], temp[i], H[i]); }
00199   for (int i=0; i<kM; ++i) { mpz_add(M[i], M[i], temp[i]); }
00200 
00201   return 2*k-1;
00202 }
00203 
00204 
00205 static int mul_rek(const TPolynom R, const int kR,
00206                    const TconstPolynom P1, const int k1, const TconstPolynom P2, const int k2,
00207                    const TPolynom temp)
00208 // Pres = P1*P2, &R must be different from &P1,&P2
00209 // returns degree of resulting polynomial
00210 // the result polynomial must provide enough temporary memory!
00211 // complexity: O(max(k1,k2)^1.59) -> Karatsuba
00212 {
00213   if (k1==2)
00214    {
00215     if (k2==2) // hand-optimized code
00216      {
00217        // (a0+a1*x)(b0+b1*x)=a0*b0+((a0+a1)(b0+b1)-a0*b0-a1*b1)*x+(a1*b1)*x^2
00218        mpz_add(temp[0],P1[0],P1[1]); mpz_add(temp[1],P2[0],P2[1]);
00219        mpz_mul(R[0],P1[0],P2[0]); mpz_mul(R[2],P1[1],P2[1]);
00220        mpz_mul(R[1],temp[0],temp[1]);
00221        mpz_sub(R[1],R[1],R[0]); mpz_sub(R[1],R[1],R[2]);
00222        return 3;
00223      }
00224     if (k2==3)
00225      {
00226        // toom-cook-like (without recursion)
00227        // result should be: P1*P2=R=w0+w1*x+w2*x^2+w3*x^3
00228 
00229        mpz_add(temp[0],P1[0],P1[1]); mpz_add(temp[1],P2[0],P2[2]);
00230        mpz_add(temp[2],temp[1],P2[1]); 
00231        mpz_mul(R[1],temp[0],temp[2]); // R[1]=w0+w1+w2+w3
00232        mpz_sub(temp[1],temp[1],P2[1]); mpz_sub(temp[0],P1[0],P1[1]);
00233        mpz_mul(R[2],temp[0],temp[1]); // R[2]=w0-w1+w2-w3
00234        mpz_mul(R[0],P1[0],P2[0]); // R[0]=w0
00235        mpz_mul(R[3],P1[1],P2[2]); // R[3]=w3
00236 
00237        mpz_add(R[2],R[2],R[1]); // R[2]=2*(w0+w2);
00238        mpz_tdiv_q_2exp(R[2],R[2],1); // R[2]=w0+w2
00239        mpz_sub(R[1],R[1],R[2]); // R[1]=w1+w3
00240        mpz_sub(R[2],R[2],R[0]); // R[2]=w2
00241        mpz_sub(R[1],R[1],R[3]); // R[1]=w1
00242 
00243        return 4;
00244      }
00245 #ifdef VERBOSE
00246     cout << __FUNCTION__ << ": no hand-optimized code for " << k1 << "," << k2 << "." << endl;
00247 #endif
00248    } // k1==2
00249 
00250   if (k1==1 || k2==1)
00251    {
00252     if (k1==1 && k2==1)
00253      {
00254        mpz_mul(R[0],P1[0],P2[0]);
00255        return 1;
00256      }
00257     if (k1==1)
00258      {
00259        for (int i=0; i<k2; ++i) mpz_mul(R[i],P1[0],P2[i]);
00260        return k2;
00261      }
00262     if (k2==1)
00263      {
00264        for (int i=0; i<k1; ++i) mpz_mul(R[i],P2[0],P1[i]);
00265        return k1;
00266      }
00267    } // k1==1 || k2==1
00268 
00269 
00270 #ifdef VERBOSE
00271   if (k1<4 || k2<4)
00272    {
00273      if (k1==3 && k2==3) 
00274       cout << __FUNCTION__ << " : 3x3!" << endl;
00275      else
00276       cout << __FUNCTION__  << ": " << k1 << ", " << k2 << endl;
00277    }
00278 #endif
00279 
00280   const int Middle=MIN(k1,k2)/2; // Middle separates LO/HI for divide & conquer
00281   // important: Middle must be lower than minimum!
00282 
00283   // Pr=P1*P2
00284   // P1 gesplittet in P1L=P1[0]..P1[Middle-1], P1H=P1[Middle]..P1[k1-1]
00285   // P2 gesplittet in P2L=P2[0]..P2[Middle-1], P2H=P2[Middle]..P2[k2-1]
00286 
00287   //if (k1!=k2) cout << "k1=" << k1 << " , " << "k2=" << k2 << endl;
00288 
00289   const TconstPolynom P1L=&P1[0]; const int kP1L=Middle;
00290   const TconstPolynom P1H=&P1[kP1L]; const int kP1H=k1-Middle;
00291   const TconstPolynom P2L=&P2[0]; const int kP2L=Middle;
00292   const TconstPolynom P2H=&P2[kP2L]; const int kP2H=k2-Middle;
00293 
00294   const int kH1=MAX(kP1L,kP1H), kH2=MAX(kP2L,kP2H);
00295   const TPolynom H1=&R[0], H2=&R[kH1];
00296   for (int i=0; i<kP1L; ++i) mpz_add(H1[i],P1H[i],P1L[i]);
00297   for (int i=kP1L; i<kH1; ++i) mpz_set(H1[i], P1H[i]);
00298   for (int i=0; i<kP2L; ++i) mpz_add(H2[i],P2H[i],P2L[i]);
00299   for (int i=kP2L; i<kH2; ++i) mpz_set(H2[i], P2H[i]);
00300 
00301   int kM=kH1+kH2-1;
00302   const TPolynom M=&R[Middle]; // middle polynomial
00303   const TPolynom temp2 = &temp[kH1+kH2-1];
00304   kM=mul_rek(temp,kM,H1,kH1,H2,kH2,temp2);
00305 
00306   for (int i=kR-1; i>=0; --i) mpz_set_ui(R[i],0); // result polynomial: fill with zeroes.
00307 
00308   int kL=kP1L+kP2L-1;
00309   const TPolynom L=R;
00310   kL=mul_rek(L,kL,P1L,kP1L,P2L,kP2L,temp2);
00311 
00312   int kH=kP1H+kP2H-1;
00313   const TPolynom H=&R[Middle*2];
00314   kH=mul_rek(H,kH,P1H,kP1H,P2H,kP2H,temp2);
00315 
00316   for (int i=0; i<kL; ++i) { mpz_sub(temp[i], temp[i], L[i]); }
00317   for (int i=0; i<kH; ++i) { mpz_sub(temp[i], temp[i], H[i]); }
00318   for (int i=0; i<kM; ++i) { mpz_add(M[i], M[i], temp[i]); }
00319 
00320   return k1+k2-1;
00321 }
00322 
00323 
00324 // forward declarations...
00325 int monic_mul(const TPolynom R, const int kR,
00326               const TconstPolynom P1, int k1,
00327               const TconstPolynom P2, int k2, const mpz_t m);
00328 int monic_square(const TPolynom R, const int kR,
00329                  const TconstPolynom P, int k, const mpz_t m);
00330 
00331 
00332 int square(const TPolynom R, const int kR, const TconstPolynom P, const int k, const mpz_t m)
00333 // R = P^2, &R must be different from &P
00334 // returns degree of resulting polynomial
00335 // complexity: O(k^1.59) -> Karatsuba
00336 {
00337   if (kR<2*k-1)
00338    {
00339      MARK;
00340      cerr << "Not enough memory for target polynomial!" << endl;
00341      cerr << "-> k=" << k << ", kR=" << kR << endl;
00342    }
00343 
00344   //if (((2*k-1)&1) && mpz_cmp_ui(P[k-1],1)==0) return monic_square(R,kR,P,k,m);
00345   //if (mpz_cmp_ui(P[k-1],1)==0) { MARK; return monic_square(R,kR,P,k,m); }
00346 
00347 #ifdef USE_DFT
00348   if (dft_square_is_recommended(k))
00349    {
00350      return get_dft(2*k-1,m)->squaremod(R,kR,P,k);
00351    }
00352 #endif
00353 
00354   const int estimated_operand_size = 2*mpz_sizeinbase(m,2)+5;
00355   const int tempsize = 2*2*k;
00356   const TTempPolynom temp(tempsize,estimated_operand_size);
00357 
00358   const int ret=square_rek(R,kR,P,k,temp);
00359 
00360   for (int i=0; i<ret; ++i) 
00361     mpz_mod(R[i],R[i],m); // normalize result polynomial
00362  
00363   return ret;
00364 }
00365 
00366 
00367 int square(const TPolynom R, const int kR, const TconstPolynom P, const int k)
00368 // R = P^2, &R must be different from &P
00369 // returns degree of resulting polynomial
00370 // complexity: O(k^1.59) -> Karatsuba
00371 {
00372   if (kR<2*k-1)
00373    {
00374      MARK;
00375      cerr << "Not enough memory for result polynomial!" << endl;
00376      cerr << "-> k=" << k << ", kR=" << kR << endl;
00377    }
00378 
00379 #if 0 && defined(USE_DFT)
00380   // we need a valid "modulo m" value to do dft;
00381   // we could calculate m, but I think this takes too much time...
00382   // calculate m would be something like this:
00383   //  mpz_t m,h; mpz_init(m); mpz_init(h);
00384   //  for (int i=0; i<k; ++i)
00385   //    {  mpz_abs(h,P[i]); if (mpz_cmp(P[i],m)>0) mpz_swap(m,h); }
00386   //  ...
00387   //  mpz_clear(h); mpz_clear(m);
00388   if (dft_square_is_recommended(k))
00389    {
00390      if (mpz_cmp_ui(P[k-1],1)==0) { MARK; cout << k << " -> " << 2*k-1 << endl; }
00391      return get_dft(2*k-1,m)->square(R,kR,P,k);
00392    }
00393 #endif
00394 
00395   const int tempsize = 2*2*k;
00396   const TTempPolynom temp(tempsize);
00397 
00398   const int ret=square_rek(R,kR,P,k,temp);
00399 
00400   return ret;
00401 }
00402 
00403 
00404 int mul(const TPolynom R, const int kR,
00405         TconstPolynom P1, int k1,
00406         TconstPolynom P2, int k2, const mpz_t m)
00407 // Pres = P1*P2, &R must be different from &P1,&P2
00408 // returns degree of the resulting polynomial
00409 // complexity: O(max(k1,k2)^1.59) -> Karatsuba
00410 // resp. complexity: O((k1+k2)*ld(k1+k2)) -> using (optimal) dft
00411 {
00412   //cout << "mul_mod " << k1 << "x" << k2 << endl;
00413 
00414   if (kR<k1+k2-1)
00415    {
00416      MARK; cerr << "Not enough memory for result polynomial!" << endl;
00417      cerr << "-> k1=" << k1 << ", k2=" << k2 << ", kR=" << kR << endl;
00418    }
00419 
00420 #if 1
00421   if ( ((k1+k2-1)&1) && mpz_cmp_ui(P1[k1-1],1)==0 && mpz_cmp_ui(P2[k2-1],1)==0 )
00422    {
00423      // we call monic mul for better performance ;-)
00424      //MARK; cout << "monic MUL!" << endl;
00425      return monic_mul(R,kR,P1,k1,P2,k2,m);
00426    }
00427 #endif
00428 
00429 #ifdef USE_DFT
00430   if (dft_mul_is_recommended(k1,k2))
00431    {
00432      return get_dft(k1+k2-1,m)->mulmod(R,kR,P1,k1,P2,k2);
00433    }
00434 #endif
00435 
00436   if (k2<k1) { std::swap(k1,k2); std::swap(P1,P2); }
00437   // now k1<=k2
00438 
00439   if (2*k2>3*k1)
00440    {
00441 #if defined(VERBOSE)
00442      MARK;
00443      cout << "using classic mul for " << k1 << ", " << k2 << endl;
00444 #endif
00445      return classic_mul(R,kR,P1,k1,P2,k2,m);
00446    }
00447   //cout << "Hint: " << k1 << ", " << k2 << endl;
00448 
00449 
00450   const int estimated_operand_size = 2*mpz_sizeinbase(m,2)+5;
00451   const int tempsize = 4*k2;
00452   const TTempPolynom temp(tempsize,estimated_operand_size);
00453  
00454   const int ret = mul_rek(R,kR,P1,k1,P2,k2,temp);
00455   for (int i=0; i<ret; ++i)
00456     mpz_mod(R[i],R[i],m); // normalize result polynomial
00457  
00458   return ret;
00459 }
00460 
00461 int mul(const TPolynom R, const int kR,
00462         TconstPolynom P1, int k1,
00463         TconstPolynom P2, int k2)
00464 // Pres = P1*P2, &R must be different from &P1,&P2
00465 // returns degree of resulting polynomial
00466 // complexity: O(max(k1,k2)^1.59) -> Karatsuba
00467 {
00468   //cout << "mul " << k1 << "x" << k2 << endl;
00469 
00470   if (kR<k1+k2-1)
00471    {
00472      cerr << "karamul: Not enough memory for result polynomial!" << endl;
00473      cerr << "-> k1=" << k1 << ", k2=" << k2 << ", kR=" << kR << endl;
00474    }
00475 
00476   if (k2<k1) { std::swap(k1,k2); std::swap(P1,P2); }
00477   // now k1<=k2
00478 
00479   if (2*k2>3*k1)
00480    {
00481 #if defined(VERBOSE)
00482      MARK;
00483      cout << "using classic mul for " << k1 << ", " << k2 << endl;
00484 #endif
00485      return classic_mul(R,kR,P1,k1,P2,k2);
00486    }
00487   //cout << "Hint: " << k1 << ", " << k2 << endl;
00488 
00489   const int tempsize = 4*k2;
00490   const TTempPolynom temp(tempsize);
00491  
00492   const int ret = mul_rek(R,kR,P1,k1,P2,k2,temp);
00493  
00494   return ret;
00495 }
00496 
00497 
00498 int monic_mul(const TPolynom R, const int kR,
00499               const TconstPolynom P1, int k1,
00500               const TconstPolynom P2, int k2, const mpz_t m)
00501 // multiplies two polynomials, whose highest coefficients are P1[k1-1]=P2[k2-1]=1.
00502 // monic polynomials, whose leading coefficients are zero, are also allowed.
00503 // This should be somewhat faster than normal mul...
00504 {
00505   if (kR<k1+k2-1)
00506    {
00507      cerr << "monic mul: Not enough memory for result polynomial!" << endl;
00508      cerr << "-> k1=" << k1 << ", k2=" << k2 << ", kR=" << kR << endl;
00509    }
00510   
00511   k1--; while (mpz_cmp_ui(P1[k1],0)==0) --k1;
00512   k2--; while (mpz_cmp_ui(P2[k2],0)==0) --k2;
00513   
00514   if (mpz_cmp_ui(P1[k1],1)!=0 || mpz_cmp_ui(P2[k2],1)!=0)
00515    {
00516      cerr << __FUNCTION__ << ": polynomial is not monic!" << endl;
00517      cout << "P1="; print(P1,k1+1); cout << endl;
00518      cout << "P2="; print(P2,k2+1); cout << endl;
00519      exit(1);
00520    }
00521 
00522 #if 1
00523   // special case (easy and fast)
00524   if (k1==1 && k2==1)
00525    {
00526      mpz_mul(R[0],P1[0],P2[0]); mpz_mod(R[0],R[0],m);
00527      mpz_add(R[1],P1[0],P2[0]); mpz_mod(R[1],R[1],m);
00528      mpz_set_ui(R[2],1); 
00529      return 3;
00530    }
00531 #endif
00532 
00533 
00534   /*
00535     (x^k1 +PR1)*(x^k2 +PR2) = X^(k1+k2) + x^(k1) *PR2 + x^(k2) *PR1 + PR1*PR2
00536     strategy:
00537      1. compute PR1*PR2
00538   */ 
00539 
00540 #ifdef USE_DFT
00541   int ret;
00542   if (dft_mul_is_recommended(k1,k2))
00543    {
00544      if (mpz_cmp_ui(P1[k1-1],1)==0 && mpz_cmp_ui(P2[k2-1],1)==0) { MARK; cout << k1+k2-1 << endl; }
00545      ret=get_dft(k1+k2-1,m)->mul(R,kR,P1,k1,P2,k2);
00546    }
00547   else
00548    ret=mul(R,kR,P1,k1,P2,k2);
00549 #else
00550   int ret=mul(R,kR,P1,k1,P2,k2); // multiply one-degree-decremented polynomials
00551 #endif
00552 
00553   /*
00554      2. complete result by x^(k1+k2) (and insert zeroes, if necessary)
00555   */
00556   while (ret<k1+k2) mpz_set_ui(R[ret++],0);
00557   mpz_set_ui(R[ret++],1);
00558 
00559   /*
00560      3. add x^(k1) *PR2
00561      4. add x^(k2) *PR1
00562   */
00563   for (int i=0; i<k1; ++i) mpz_add(R[i+k2],R[i+k2],P1[i]);
00564   for (int i=0; i<k2; ++i) mpz_add(R[i+k1],R[i+k1],P2[i]);
00565   for (int i=0; i<ret-1; ++i) mpz_mod(R[i],R[i],m);
00566   return ret;
00567 }
00568 
00569 int monic_square(const TPolynom R, const int kR,
00570                  const TconstPolynom P, int k, const mpz_t m)
00571 // multiplies two polynomials, whose highest coefficients are P1[k1-1]=P2[k2-1]=1.
00572 // monic polynomials, whose leading coefficients are zero, are also allowed.
00573 // This should be somewhat faster than normal mul...
00574 {
00575   if (kR<2*k-1)
00576    {
00577      cerr << "monic square: Not enough memory for result polynomial!" << endl;
00578      cerr << "-> k=" << k << ", kR=" << kR << endl;
00579    }
00580   
00581   k--; while (k && mpz_cmp_ui(P[k],0)==0) --k;
00582   
00583   if (mpz_cmp_ui(P[k],1)!=0)
00584    {
00585      cerr << __FUNCTION__ << ": polynomial is not monic!" << endl;
00586      cout << "P="; print(P,k+1); cout << endl;
00587      exit(1);
00588    }
00589 
00590   // besonderer Spezialfall: P=x^0
00591   if (k==0) 
00592    {
00593      mpz_set_ui(R[0],1); return 1;
00594    }
00595 
00596 #if 1
00597   // special case (easy and fast)
00598   if (k==1)
00599    {
00600      mpz_mul(R[0],P[0],P[0]); mpz_mod(R[0],R[0],m);
00601      mpz_add(R[1],P[0],P[0]); mpz_mod(R[1],R[1],m);
00602      mpz_set_ui(R[2],1); 
00603      return 3;
00604    }
00605 #endif
00606 
00607 
00608   /*
00609     (x^k1 +PR1)*(x^k2 +PR2) = X^(k1+k2) + x^(k1) *PR2 + x^(k2) *PR1 + PR1*PR2
00610     strategy:
00611      1. compute PR1*PR2
00612   */ 
00613 
00614 #ifdef USE_DFT
00615   int ret;
00616   if (dft_square_is_recommended(k))
00617    {
00618      ret=get_dft(2*k-1,m)->square(R,kR,P,k);
00619    }
00620   else
00621    {
00622      ret=square(R,kR,P,k);
00623    }
00624 #else
00625   int ret=square(R,kR,P,k); // square the one-degree-decremented polynomial
00626 #endif
00627   /*
00628      2. complete result by x^(k1+k2) (and insert zeroes, if necessary)
00629   */
00630   while (ret<2*k) mpz_set_ui(R[ret++],0);
00631   mpz_set_ui(R[ret++],1);
00632 
00633   /*
00634      3. add x^(k1) *PR2
00635      4. add x^(k2) *PR1
00636   */
00637   for (int i=0; i<k; ++i) mpz_addmul_ui(R[i+k],P[i],2);
00638   for (int i=0; i<ret-1; ++i) mpz_mod(R[i],R[i],m);
00639   return ret;
00640 }
00641 
00642 
00643 void reciprocal2p1(TPolynom R, int &kR, const TconstPolynom f, const int np1, const mpz_t m)
00644 {
00645   const int n=np1-1;
00646   const TPolynom R1 = &R[1];
00647   TTempPolynom R2(2*np1), H(2*np1);
00648   mpz_t e, fni, x;
00649   mpz_init(e); mpz_init(fni); mpz_init(x);
00650 
00651   mpz_invert(fni,f[n],m); mpz_mod(fni,fni,m);
00652   kR=1; mpz_set(R1[0],fni);
00653   mpz_neg(e,f[n-1]); mpz_mul(e,e,fni); mpz_mod(e,e,m);
00654 
00655   for (int k=2; k<=n; k*=2)
00656    {
00657      const int kR2=square(R2,2*np1,R1,kR,m); // R2=R1^2
00658 //     if (kR2<k) { MARK; cerr << kR2 << "," << k << endl; }
00659      mul(H,2*np1,R2,kR2,&f[n-(k-1)],k,m); // H=R1^2 * f[n-(k-1)],k
00660      for (int i=0; i<k/2; ++i) 
00661       {
00662         mpz_mul_2exp(R1[k/2+i],R1[i],1);
00663         mpz_set_ui(R1[i],0);
00664       }
00665      for (int i=0; i<k; ++i) mpz_sub(R1[i],R1[i],H[i+k-2]);
00666 
00667      mpz_mul(e,e,e); mpz_mod(e,e,m);
00668      if (k==2)
00669       mpz_set_ui(x,0);
00670      else
00671       {
00672         mpz_mul(x,H[k-3],f[n]); mpz_mod(x,x,m);
00673       }
00674      mpz_sub(e,e,x);
00675      mpz_mul(x,f[n-k],fni); mpz_mod(x,x,m);
00676      mpz_sub(e,e,x); mpz_mod(e,e,m);
00677      kR=k;
00678    }
00679   
00680   // man beachte &R1 = &R[1] !
00681   mpz_mul(x,e,fni); mpz_mod(R[0],x,m);
00682   kR++;
00683 
00684   mpz_clear(e); mpz_clear(fni); mpz_clear(x);
00685 }
00686 
00687 
00688 void reciprocal2(TPolynom R, int &kR, const TconstPolynom P, const int k, const mpz_t m)
00689 {
00690   // computes the reciprocal polynomial of P,
00691   // k must be a power of two!
00692   // R must provide enough memory!
00693   // P must not have any leading zeros!
00694   if (k==1)
00695    {
00696      if (mpz_invert(R[0],P[0],m)==0)
00697       {
00698         cerr << __FUNCTION__ << ": inverse does not exist!" << endl;
00699         cout << "P="; print(P,k); cout << endl;
00700         char ch; cin >> ch;
00701       }
00702      kR=1;
00703    }
00704   else
00705    {
00706      // Polynom Q bereitstellen
00707      const int mem_kQ=3*(k>>1); TTempPolynom Q(mem_kQ);
00708      int kQ=mem_kQ;
00709      reciprocal2(Q,kQ,&P[k>>1],k>>1,m); // and compute
00710 
00711      // compute square of Q
00712      const int mem_kQQ=2*kQ; TTempPolynom QQ(mem_kQQ);
00713      int kQQ=square(QQ,mem_kQQ,Q,kQ,m);
00714 
00715      // B=QQ(x)*P(x)
00716      const int mem_kB=kQQ+k; TTempPolynom B(mem_kB);
00717      int kB=mul(B,mem_kB,QQ,kQQ,P,k,m);
00718           
00719      // compute result
00720      for (int i=0; i<kR; ++i) mpz_set_ui(R[i],0); // fill with zeroes
00721      
00722      const int d = k>>1;
00723      for (int i=0; i<kQ; ++i) mpz_mul_ui(R[d+i],Q[i],2);
00724      for (int i=k-2; i<kB; ++i) mpz_sub(R[i-(k-2)],R[i-(k-2)],B[i]);
00725      kR = d+kQ >= kB-(k-2) ? d+kQ : kB-(k-2);
00726      kR--;
00727      while (kR>0 && (mpz_cmp_ui(R[kR],0)==0)) kR--; // normalize
00728      kR++;
00729      for (int i=0; i<kR; ++i) mpz_mod(R[i],R[i],m);
00730      //cout << "is "; print(R,kR); cout << endl;
00731    }
00732 }
00733 
00734 void reciprocal(TPolynom R, int &kR, const TconstPolynom P, const int k, const mpz_t m, const unsigned int scale /* =0 */)
00735 {
00736   // computes the reciprocal polynomial of P,
00737   // R must provide enough memory!
00738   // the given polynomial will be scaled using x^scale as multiplier,
00739   // thereby shifting the coefficients by scale places
00740 
00741   int z=1, h=k+scale;
00742   while (z<h) z<<=1;
00743   
00744   int d=z-h;
00745   if (d==0 && scale==0) 
00746    {
00747      // Zweierpotenz
00748 #if defined(VERBOSE)
00749      cout << "calling recip with power of 2" << endl;
00750 #endif
00751      reciprocal2(R,kR,P,k,m);
00752    }
00753   if (k==d+2 && scale==0)
00754    {
00755      // power of two plus 1
00756 #if defined(VERBOSE)
00757      cout << "calling recip with 1+power of 2" << endl;
00758 #endif
00759      reciprocal2p1(R,kR,P,k,m);
00760    }
00761   else
00762    {
00763      // not a power of two (or scale!=0)
00764      d+=scale;
00765 #if defined(VERBOSE)
00766      if (scale) cout << "scaling polynomial!" << endl;
00767      else cout << "no power of 2: " << k << ", " << z << ", " << d << endl;
00768 #endif
00769      // allocate temporary memory & call reciprocal (using a displacement)
00770      const int kH=z; TTempPolynom H(kH);
00771      for (int i=0; i<k; ++i) mpz_set(H[i+d],P[i]);
00772      int kR2=2*kH; TTempPolynom R2(kR2);
00773 #ifdef VERBOSE
00774      cout << __FUNCTION__ << ": calling recip" << endl;
00775 #endif
00776      reciprocal2(R2,kR2,H,kH,m);
00777 #ifdef VERBOSE
00778      cout << __FUNCTION__ << ": ...back (of calling recip)" << endl;
00779 #endif
00780 #ifdef DEBUG
00781      cout << "->"; print(R2,kR2); cout << endl;
00782 #endif
00783      d-=scale;
00784      if (kR<kR2-d) cerr << __FUNCTION__ << ": too little memory in target polynomial!" << endl;
00785      kR=kR2-d;
00786      for (int i=0; i<kR; ++i) mpz_set_ui(R[i],0);
00787      for (int i=d; i<kR2; ++i) mpz_set(R[i-d],R2[i]);
00788    }
00789 }
00790 
00791 
00792 void classic_div(TPolynom Q, int &kQ, TPolynom R, int &kR,
00793          const TconstPolynom P1, int k1,
00794          const TconstPolynom P2, int k2, const mpz_t m)
00795 {
00796   // classical polynomial division, O(n^2)
00797   // returns P1/P2 in Q
00798   // returns P1 mod P2 in R
00799 
00800   for (int i=0; i<kQ; ++i) mpz_set_ui(Q[i],0);
00801   for (int i=0; i<kR; ++i) mpz_set(R[i],P1[i]);
00802   kR=k1; 
00803   mpz_t inv,x,y;
00804   mpz_init(inv); mpz_init(x); mpz_init(y);
00805 
00806   if (k2==0)
00807     {
00808       cout << endl;
00809       MARK; cerr << "polynomial division: Division by zero (empty polynomial)!" << endl;
00810       exit(1);
00811     }
00812   kR--; k2--; k1--;
00813   while (k2>=0  && (mpz_cmp_ui(P2[k2],0))==0) k2--;
00814   if (k2<0)
00815     {
00816       cout << endl;
00817       MARK; cerr << "polynomial division: Division by zero (empty polynomial)!" << endl;
00818       exit(1);
00819     }
00820   if (mpz_invert(inv,P2[k2],m)==0)
00821    {
00822      cout << "--->";
00823      print(P2,k2+1);
00824      cout << endl;
00825      MARK; cerr << "polynomial division: Inverse doesn't exist!" << endl;
00826      exit(1);
00827    }
00828   if (k1<0) // dividing the empty polynomial -> 0
00829     {
00830       MARK; cout << "dividend polynomial is empty." << endl;
00831       kQ=kR=0;    
00832       goto done;
00833     }
00834 
00835   while (kR>=k2)
00836    {
00837     const int Graddifferenz=kR-k2;
00838 
00839     //Skalar=R[kR]/P2[k2] 
00840     mpz_mul(x,inv,R[kR]); mpz_mod(x,x,m);
00841     mpz_set(Q[Graddifferenz],x);
00842     mpz_set_ui(R[kR],0);
00843     kR--;
00844     for (int i=k2-1; i>=0; --i )
00845      {
00846        int j = kR+i-(k2-1);
00847        mpz_mul(y,P2[i],x);
00848        mpz_sub(y,R[j],y);
00849        mpz_mod(R[j],y,m);
00850      } 
00851    }
00852 
00853   while (kR>=0 && mpz_cmp_ui(R[kR],0)==0) kR--;
00854 
00855   kQ=k1-k2+1; kR++;
00856  done:
00857   mpz_clear(inv); mpz_clear(x); mpz_clear(y);
00858 #if 1
00859   cout << "polynomdivision Result:" << endl;
00860   cout << "A(x) = "; print(P1,k1+1); cout << endl;
00861   cout << "B(x) = "; print(P2,k2+1); cout << endl;
00862   cout << "Q(x) = "; print(Q,kQ); cout << endl;
00863   cout << "R(x) = "; print(R,kR); cout << endl;
00864 #endif
00865 }
00866 
00867 
00868 void classic_mod(TPolynom R, int &kR,
00869             const TconstPolynom P1, int k1,
00870             const TconstPolynom P2, int k2, const mpz_t m)
00871 // Remainder of polynomial division, O(n^2)
00872 // returns P1 mod P2 in R
00873 {
00874 #ifdef DEBUG
00875   cout << "POLMOD IN (old)" << endl;
00876 #endif
00877   for (int i=0; i<kR; ++i) mpz_set(R[i],P1[i]);
00878   kR=k1; 
00879   /* one could minimize memory consumption in R by
00880       - precomputing the result size
00881       - using a ring buffer
00882         (taking care, that the coefficients are at the right place
00883          when leaving the loop)
00884      -> isn't implemented (yet)
00885   */
00886 
00887   mpz_t inv,x;
00888   mpz_init(inv); mpz_init(x);
00889 
00890   if (k2==0)
00891     {
00892       cout << endl;
00893       cerr << "polynomial division: division by zero polynomial!" << endl;
00894       exit(1);
00895     }
00896   kR--; k2--; k1--;
00897   while (k2>=0 && (mpz_cmp_ui(P2[k2],0))==0) k2--;
00898   if (k2<0)
00899     {
00900       cout << endl;
00901       cerr << "polynomial division: division by  zero polynomial! (-1)" << endl;
00902       exit(1);
00903     }
00904   if (mpz_invert(inv,P2[k2],m)==0)
00905    {
00906      cout << "--->";
00907      print(P2,k2+1);
00908      cout << endl;
00909      cerr << "polynomial division: inverse does not exist!" << endl;
00910      exit(1);
00911    }
00912   if (k1<0) // dividend is 0 (zero polynomial)
00913    {
00914      cout << "Division using zero-polynomial as dividend!" << endl;
00915      kR=0;    
00916      goto done;
00917    }
00918 
00919   while (kR>=k2)
00920    {
00921     //Skalar=R[kR]/P2[k2] 
00922     mpz_mod(R[kR],R[kR],m); mpz_mul(x,inv,R[kR]); mpz_mod(x,x,m);
00923     mpz_set_ui(R[kR],0);
00924     for (int i=k2-1; i>=0; --i)
00925      {
00926        mpz_submul(R[kR-k2+i],P2[i],x);
00927      }
00928     --kR;
00929    }
00930   // now normalize the result...
00931   for (int i=kR; i>=0; --i)
00932     mpz_mod(R[i],R[i],m);
00933   while (kR>=0 && mpz_cmp_ui(R[kR],0)==0) kR--;
00934   kR++;
00935  done:
00936   mpz_clear(inv); mpz_clear(x);
00937 #ifdef DEBUG
00938   cout << "polynomial division (MODULO) Result:" << endl;
00939   cout << "A(x) = "; print(P1,k1+1); cout << endl;
00940   cout << "B(x) = "; print(P2,k2+1); cout << endl;
00941   cout << "R(x) = "; print(R,kR); cout << endl;
00942   cout << "POLMOD OUT (old)" << endl;
00943 #endif
00944 }
00945 
00946 
00947 void div(TPolynom Q, int &kQ,
00948          const TconstPolynom P1, const int k1,
00949          const TconstPolynom P2, const int k2, const mpz_t m)
00950 {
00951   // (fast) polynomial division by multiplying with the reciprocal polynomial
00952   /* method:
00953      compute reciprocal polynomial for P2;
00954      multiply this with P1, and
00955      the (k1-k2+1) leading coefficients of the product are the desired result.
00956      This method should be asymptotically faster than any other method,
00957      because the time complexity to compute the reciprocal polynomial
00958      depends (essentially) on the complexity of the multiplication algorithm
00959      (Karatsuba, etc.).
00960   */
00961 
00962 #ifdef DEBUG
00963   cout << "div-in" << " k1=" << k1 << ", k2=" << k2 << endl;
00964   cout << "div:P1="; print(P1,k1); cout << endl;
00965   cout << "div:P2="; print(P2,k2); cout << endl;
00966 #endif
00967 
00968   if ( kQ < k1-k2+1 ) cerr << "div: Zielpolynom zu klein!" << endl;
00969   if (k2<1) cerr << "Warnung: Null-Polynome bei Division!" << endl;
00970   if ( (k1>0 && mpz_cmp_ui(P1[k1-1],0)==0) ||
00971        (k2>0 && mpz_cmp_ui(P2[k2-1],0)==0) )
00972     {
00973       cerr << "div: leading zeroes! (not wanted!)" << endl;
00974       exit(1);
00975     }
00976 
00977   if (k1==k2)
00978    {
00979      // special case: division of two polynomials with the same degree:
00980      cout << "div: Grad P1=P2 -> easy-div" << endl;
00981      // Q[0]=P1[k1-1]/P2[k2-1], kQ=1;
00982      if (kQ<1) cerr << "Speicherproblem!" << endl;
00983      mpz_invert(Q[0],P2[k2-1],m);
00984      mpz_mul(Q[0],Q[0],P1[k1-1]); mpz_mod(Q[0],Q[0],m);
00985      kQ=(mpz_cmp_ui(Q[0],0)==0) ? 0 : 1;
00986      return;
00987    }
00988 
00989   int scale=0; // used to scale the reciprocal polynomial
00990   if (k1>=2*k2)
00991    {
00992 #ifdef VERBOSE
00993       cout << __FUNCTION__ << ": problems with reciprocal polynomial: k1>=2*k2!"
00994            << " k1,k2=" << k1 << "," << k2 << endl;
00995 #endif
00996       scale=k1-k2-1;
00997    }
00998 
00999   // allocate a helper polynomial: RP2
01000   const int mem_kRP2=k2+scale; TTempPolynom RP2(mem_kRP2);
01001   int kRP2 = mem_kRP2;
01002   reciprocal(RP2,kRP2,P2,k2,m,scale); // and compute
01003   // -> RP2(x) = x^(2n-2) / P2(x)
01004 
01005   // as we need only the k1-k2 leading coefficients later,
01006   // wo can spare to compute anything more...
01007   const int startP1=k1-(k1-k2+1);
01008   const int startRP2=kRP2-(k1-k2+1);
01009 #ifdef VERBOSE
01010   cout << "div-shortcut: " << startP1 << ", " << startRP2 << endl;
01011 #endif
01012   const int mem_kH=(k1-startP1)+(kRP2-startRP2)-1; TTempPolynom H(mem_kH);
01013 
01014   const int kH=mul(H,mem_kH,&P1[startP1],k1-startP1,&RP2[startRP2],kRP2-startRP2,m);
01015   // -> H(x)=P1(x)*RP2(x)
01016 
01017 #if 0
01018   cout << "Divisionsergebnis: " << endl;
01019   cout << "full: "; print(H,kH); cout << endl;
01020   cout << "kRP2=" << kRP2 << ", k2=" << k2 << endl;
01021 #endif
01022 
01023   // now get the leading k1-k2+1 coefficients, they are the result
01024   for (int i=0; i<=k1-k2; ++i) mpz_set(Q[i],H[kH-1-(k1-k2)+i]);
01025   kQ=k1-k2;
01026   while (kQ>=0 && mpz_cmp_ui(Q[kQ],0)==0) kQ--; // normalize polynomial
01027   kQ++; // size=degree of polynomial +1 !!
01028 
01029 #ifdef DEBUG
01030   cout << "div-out" << endl;
01031 #endif
01032 }
01033 
01034 
01035 void mod(TPolynom R, int &kR,
01036          const TconstPolynom P1, int k1,
01037          const TconstPolynom P2, int k2, const mpz_t m)
01038 // polynomial remainder, fast(er) method
01039 // returns P1 modulo P2 in R
01040 /* asymptotically faster than the "naive modulo", because we can make use of
01041    fast multiplication algorithms. */
01042 {
01043 
01044 #if 0 || defined(DEBUG)
01045   cout << "POLMOD IN (new)" << endl;
01046   cout << "k1=" << k1 << ", k2=" << k2 << ", kR=" << kR << endl;
01047 //  cout << "mod:P1="; print(P1,k1); cout << endl;
01048 //  cout << "mod:P2="; print(P2,k2); cout << endl;
01049 #endif
01050 
01051   if (k2<1) cerr << "Warning: Null-Polynomials in mod!" << endl;
01052 
01053 #ifdef VERBOSE
01054   if ( (k1>0 && mpz_cmp_ui(P1[k1-1],0)==0) ||
01055        (k2>0 && mpz_cmp_ui(P2[k2-1],0)==0) )
01056    {
01057      cerr << __FUNCTION__ << ": Unwanted leading zeros!" << "(for " <<  k1 << "x" << k2 << ")" <<endl;
01058    }
01059 #endif
01060 
01061   k1--; while (k1>=0 && mpz_cmp_ui(P1[k1],0)==0) k1--; k1++; // normalize
01062   k2--; while (k2>=0 && mpz_cmp_ui(P2[k2],0)==0) k2--; k2++; // normalize
01063 
01064 #ifdef DEBUG
01065   cout << "k1=" << k1 << ", k2=" << k2 << ", kR=" << kR << endl;
01066   cout << "mod:P1="; print(P1,k1); cout << endl;
01067   cout << "mod:P2="; print(P2,k2); cout << endl;
01068 #endif
01069 
01070   if (k2>k1)
01071    { 
01072      cout << "trivial mod:  P1 mod P2 -> P1  for P2>P1" << endl;
01073      if (kR<k1) cerr << "Too little memory in target polynomial!" << endl;
01074      for (int i=0; i<k1; ++i) mpz_set(R[i],P1[i]);
01075      kR=k1;
01076      return;
01077    }
01078 
01079 #if 1 
01080   /* speedup analogous to quicksort: if you are near the leafs, then use
01081      a asymptotic worse, but for small values faster method. */
01082   if (k1<150 || k2<16)
01083    {
01084      //cout << "calling classic_mod" << endl;
01085      classic_mod(R,kR,P1,k1,P2,k2,m);
01086      return; 
01087    }
01088 #endif
01089 
01090   // Quotientenpolynom Q ermitteln
01091   const int mem_kQ=k1-k2+1; TTempPolynom Q(mem_kQ); 
01092   int kQ=mem_kQ;
01093   div(Q,kQ,P1,k1,P2,k2,m); // important: using fast division!
01094   
01095   // compute P1-Q*P2
01096   const int mem_kM=kQ+k2-1; TTempPolynom M(mem_kM); 
01097   if (kQ>k2)
01098    {
01099 #ifdef VERBOSE_INFO
01100      cout << "shortcut possible! kQ=" << kQ << ", k2=" << k2 << endl;
01101 #endif
01102      kQ=k2;
01103      //cout << "kQ=k2=" << k2 << endl;
01104     }
01105 
01106  //cout << "FASTmod: " << kQ << "x" << k2 << endl; 
01107 #if 1
01108   mul(M,mem_kM,Q,kQ,P2,k2,m); // important: using fast multiplication!
01109 #else
01110   int kM=mul(M,mem_kM,Q,kQ,P2,k2,m); // important: using fast multiplication!
01111   if (kM<k2-1) cerr << "mod: problems with remainder" << endl;
01112   cout << "prod: "; print(M,kM); cout << endl;
01113 #endif
01114 
01115   // shortcut: polynomial remainder can have at most k2-1 coefficients
01116   {
01117     int i=k2-2; while (i>=0 && mpz_cmp(P1[i],M[i])==0) --i; // normalize result
01118     if (i+1>kR) cerr << "mod: not enough memory in target polynomial!" << endl;
01119     kR=i+1;
01120     while (i>=0) 
01121      {
01122        mpz_sub(R[i],P1[i],M[i]); mpz_mod(R[i],R[i],m);
01123        --i;
01124      }
01125   }
01126 
01127 #ifdef DEBUG
01128   cout << "polynomial division (fast MODULO) Result:" << endl;
01129   cout << "A(x) = "; print(P1,k1); cout << endl;
01130   cout << "B(x) = "; print(P2,k2); cout << endl;
01131   cout << "R(x) = "; print(R,kR); cout << endl;
01132   cout << "POLMOD OUT (new)" << endl;
01133 #endif
01134 }
01135 
01136 
01137 static void multipoint_eval_rek(const TPolynom* R, const TconstPolynom P, const int k, TPolynom* A, const int h,
01138                          const mpz_t m, mpz_t* &res, int* const pos, const int* const step, const int* const stop)
01139 // this function is called by multipoint_eval
01140 {
01141 #ifdef DEBUG
01142   cout << "recursive call for h=" << h << endl;
01143 #endif
01144   if (h==0) // leaf entered -> evaluate!
01145     {
01146       // direkte Auswertung des Blattes: (a*x+b) mod (x+c) = b-a*c
01147       if (k==1)
01148          mpz_mod(res[0],P[0],m);
01149       else
01150        {
01151          mpz_mul(res[0],P[1],A[h][pos[h]]);
01152          mpz_sub(res[0],P[0],res[0]);
01153          mpz_mod(res[0],res[0],m);
01154        }
01155       res++;
01156     }
01157   else
01158     {
01159       int kR=k;
01160       mod(&R[h][0],kR,P,k,&A[h][pos[h]],step[h],m);
01161       multipoint_eval_rek(R,R[h],kR, A,h-1, m,res,pos,step,stop);
01162       if (pos[h-1]<stop[h-1])
01163         multipoint_eval_rek(R,R[h],kR, A,h-1, m,res,pos,step,stop);
01164     }
01165   pos[h]+=step[h];
01166 } 
01167 
01168 
01169 void multipoint_eval(mpz_t* res,
01170                      const TconstPolynom P, const int k, 
01171                      const mpz_t* const array_of_arguments, const int size,
01172                      const mpz_t m)
01173 {
01174   if ( k<3 || size<3 || size>k-1 )
01175    {
01176      cerr << "multipoint_eval: invalid parameters!" << endl;
01177      exit(1);
01178    }
01179 
01180   const int MaxHoehe = ld(size)+1;
01181 
01182   TPolynom* const A = new TPolynom[MaxHoehe];
01183   int* const pos       = new int[MaxHoehe];
01184   int* const steparray = new int[MaxHoehe];
01185   int* const stop      = new int[MaxHoehe];
01186 
01187   for (int i=0; i<MaxHoehe; ++i) pos[i]=steparray[i]=stop[i]=0; // initialize
01188 
01189   // for optimizing mpz-memory-allocation & fragmentation we will use mpz_init2
01190   // therefore we need to estimate our operand size (wrong estimate cause no harm
01191   // except for triggering more unnecessary reallocations)
01192   const unsigned int estimated_operand_size = 2*mpz_sizeinbase(m,2)+5;
01193 
01194   /*
01195     A[h][s] is an array of polynomials of depth h
01196     (h=0 -> leafs=monic polynomials of degree 1)
01197   */
01198 
01199   int h = 0, anz=size, step=2, s=anz*step;
01200   steparray[h]=step; stop[h]=s;
01201 
01202 #ifdef VERBOSE
01203   cout << "calculating polynomials for depth " << h << endl;
01204 #endif
01205   A[h] = new mpz_t[s]; // allocate space for polynomials in depth h
01206   for (int i=0, j=0; i<anz; ++i, j+=step)
01207     {
01208       // create monic polynomials of degree 1
01209       mpz_init_set_ui(A[h][j+1],1); // 1*x
01210       mpz_init_set(A[h][j],array_of_arguments[i]); mpz_neg(A[h][j],A[h][j]);
01211     }
01212 
01213                      // handle this special case efficiently 
01214   while ( anz>2 || (anz==2 && k==2*size+1) )
01215     {
01216       ++h;
01217       if (h>=MaxHoehe)
01218        {
01219          cout << __FILE__ << ", " << __FUNCTION__ << ": line " <<  __LINE__ << endl;
01220          cerr << "too many iteration steps..." << endl;
01221          cerr << "please increase MaxHoehe and recompile!" << endl;
01222          exit(1);
01223        }
01224 
01225       const int oldstep = step;
01226       const int rest = anz % 2;
01227       steparray[h]=step=step*2-1; anz=(anz/2); stop[h]=s=(anz+rest)*step;
01228 #ifdef VERBOSE
01229       cout << "calculating polynomials for depth " << h << endl;
01230 #endif
01231       A[h] = new mpz_t[s]; // allocate memory for polynomials in depth h
01232       for (int j=0; j<s; ++j) mpz_init2(A[h][j],estimated_operand_size); // initialize mpz_t
01233 
01234       for (int i=0, j=0; i<anz; ++i, j+=step)
01235         {
01236           monic_mul(&A[h][j],step,
01237               &A[h-1][2*i*oldstep],oldstep, &A[h-1][(2*i+1)*oldstep],oldstep,m);
01238         }
01239       if (rest) 
01240         {
01241           for (int i=0; i<oldstep; i++)
01242             mpz_set(A[h][s-step+i],A[h-1][stop[h-1]-oldstep+i]);
01243         }
01244       anz+=rest;
01245     }
01246 
01247   //cout << "READY for recursion..." << endl;
01248 
01249   // now the particular polynomials are ready for polynomial division
01250   TPolynom* const R = new TPolynom[MaxHoehe]; // polynomial remainder
01251   for (int i=0; i<MaxHoehe; ++i)
01252     {
01253       R[i]=new mpz_t[k];
01254       for (int j=0; j<k; ++j) mpz_init2(R[i][j],estimated_operand_size);
01255     }
01256 
01257   for (int i=0; i<anz; i++) multipoint_eval_rek(R,P,k,A,h,m,res,pos,steparray,stop);
01258 
01259   for (int i=MaxHoehe-1; i>=0; --i)
01260     {
01261       for (int j=k-1; j>=0; --j) mpz_clear(R[i][j]);
01262       delete [] R[i];
01263     }
01264   delete [] R;
01265 
01266   while (h>=0)
01267     {
01268       for (int i=stop[h]-1; i>=0; --i) mpz_clear(A[h][i]);
01269       delete [] A[h]; // release memory
01270       --h;
01271     }
01272   delete [] pos;
01273   delete [] steparray;
01274   delete [] stop;
01275   delete [] A;
01276 }
01277 
01278 
01279 int construct_polynomial_from_roots
01280       (TPolynom &res,
01281        const mpz_t* const roots_array, const int size,
01282        const mpz_t m)
01283 {
01284   // task:
01285   // Creates a new polynomial using the zeros given in "roots_array";
01286   // this polynomial will be placed in "res" and its size will be returned.
01287   // To avoid memory leaks (due to erroneous calls), we expect that
01288   // "res" is initially an NULL-pointer.
01289   // additional remark:
01290   // Sure, it would be possible to return this pointer instead of using a reference
01291   // parameter, but what if you forget to delete it later? -- Using this technique,
01292   // the programmer is at least urged to provide a valid container for our data!
01293 
01294   if (res!=NULL)
01295    {
01296      cerr << __FILE__ << ", " << __FUNCTION__ << ": line " <<  __LINE__ << endl;
01297      cerr << "First parameter is a call by reference," << endl;
01298      cerr << "it should initially point to NULL (to avoid memory-leaks)," << endl;
01299      cerr << "because a new pointer to new data will be generated and" << endl;
01300      cerr << "there is no need for initially data pointed by res!" << endl;
01301      exit(1);
01302    }
01303 
01304   if ( size < 3  )
01305    {
01306      cerr << __FILE__ << ", " << __FUNCTION__ << ": line " <<  __LINE__ << endl;
01307      cerr << "Invalid parameters!" << endl;
01308      exit(1);
01309    }  
01310 
01311   // for optimizing mpz-memory-allocation & fragmentation we will use mpz_init2
01312   // therefore we need to estimate our operand size (wrong estimate cause no harm
01313   // except for triggering more unnecessary reallocations)
01314   const unsigned int estimated_operand_size = 2*mpz_sizeinbase(m,2)+5;
01315 
01316   /*
01317     A contains the polynomials of depth h (h=0 -> leafs = monic polynomials of degree 1)
01318     B contains the polynomials of A of the previous round
01319   */
01320   TPolynom A,B;
01321   int h = 0, anz=size, step=2, s=anz*step;
01322 #ifdef VERBOSE
01323   cout << "calculating polynomials for depth " << h << endl;
01324 #endif
01325   A = new mpz_t[s]; // allocate memory for polynomials in depth h
01326   for (int i=0, j=0; i<anz; ++i, j+=step)
01327     {
01328       // create monic polynomials of degree 1
01329       mpz_init_set_ui(A[j+1],1); // 1*x
01330       mpz_init_set(A[j],roots_array[i]); mpz_neg(A[j],A[j]);
01331     }
01332 
01333   while (anz>1)
01334     {
01335       ++h; B=A; // next iteration step
01336       const int oldstep = step;
01337       const int olds    = s;
01338       const int rest = anz % 2;
01339       step=step*2-1; anz=(anz/2); s=(anz+rest)*step;
01340 #ifdef VERBOSE
01341       cout << "calculating polynomials for depth " << h << endl;
01342 #endif
01343       A = new mpz_t[s]; // allocate memory for polynomials in depth h
01344       for (int j=0; j<s; ++j) mpz_init2(A[j],estimated_operand_size); // initialize mpz_t
01345       for (int i=0, j=0; i<anz; ++i, j+=step)
01346         {
01347           monic_mul(&A[j],step,
01348               &B[2*i*oldstep],oldstep, &B[(2*i+1)*oldstep],oldstep,m);
01349         }
01350       if (rest) 
01351         {
01352           for (int i=0; i<oldstep; i++)
01353             mpz_set(A[s-step+i],B[olds-oldstep+i]);
01354         }
01355 
01356       // release (only the) memory which is no more needed
01357       // d.h. das Polynom in B
01358       for (int i=0; i<olds; ++i) mpz_clear(B[i]);
01359       delete [] B;
01360 
01361       anz+=rest;
01362     }
01363 
01364   --s; // normalize resulting polynomial
01365   while (s>=0 && mpz_cmp_ui(A[s],0)==0) { mpz_clear(A[s]); --s; }
01366   ++s;
01367   res=A; // result polynomial
01368   return s; // size of result polynomial
01369 }
01370 
01371 
01372 } // namespace polynomial
01373 
01374 #endif /* POLYNOMIAL_cc_ */

Generated on Wed Nov 7 23:29:26 2007 for Qsieve by  doxygen 1.5.4