1 // mwprocs.cc: implementation of class mw for Mordell-Weil basis
2 //////////////////////////////////////////////////////////////////////////
3 //
4 // Copyright 1990-2012 John Cremona
5 //
6 // This file is part of the eclib package.
7 //
8 // eclib is free software; you can redistribute it and/or modify it
9 // under the terms of the GNU General Public License as published by the
10 // Free Software Foundation; either version 2 of the License, or (at your
11 // option) any later version.
12 //
13 // eclib is distributed in the hope that it will be useful, but WITHOUT
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 // for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with eclib; if not, write to the Free Software Foundation,
20 // Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 //
22 //////////////////////////////////////////////////////////////////////////
23
24
25 //#define DEBUG_QSIEVE
26
27 #include <eclib/interface.h>
28 #include <eclib/compproc.h>
29
30 #include <eclib/method.h>
31
32 #include <eclib/points.h>
33 #include <eclib/polys.h>
34 #include <eclib/curvemod.h>
35 #include <eclib/pointsmod.h>
36 #include <eclib/ffmod.h>
37 #include <eclib/divpol.h>
38 #include <eclib/tlss.h>
39 #include <eclib/elog.h>
40 #include <eclib/saturate.h>
41
42 #include <eclib/sieve_search.h>
43
44 #include <eclib/mwprocs.h>
45
46 //
47 // some locally called general functions, belong in library maybe:
48 //
49
50 // unlikely to be called by anything but find_inf:
roots_of_cubic(const Curve & E)51 vector<bigcomplex> roots_of_cubic(const Curve& E)
52 {
53 bigint a1,a2,a3,a4,a6;
54 E.getai(a1,a2,a3,a4,a6);
55
56 bigfloat ra1=I2bigfloat(a1),
57 ra2=I2bigfloat(a2),
58 ra3=I2bigfloat(a3),
59 ra4=I2bigfloat(a4),
60 ra6=I2bigfloat(a6);
61
62 bigcomplex c1 = ra2 + ra1*(ra1/4) ;
63 bigcomplex c2 = ra4 + ra1*(ra3/2) ;
64 bigcomplex c3 = ra6 + ra3*(ra3/4) ;
65 return solvecubic(c1,c2,c3);
66 }
67
min_real(vector<bigcomplex> array)68 bigfloat min_real(vector<bigcomplex> array)
69 {
70 //cout<<"In min_real() with array:\t"<<array<<endl;
71 bigfloat minr, r; int first=1; minr=0;
72 for (unsigned int i=0; i<array.size(); i++)
73 { if(abs(imag(array[i]))<0.001) // then the root is regarded as real
74 {
75 r = real(array[i]);
76 if (first||(minr > r)) {minr = r; first=0;}
77 }
78 }
79 //cout<<"minr finally " << minr << "\n";
80 return minr;
81 }
82
83 int order_real_roots(vector<double>& bnd, vector<bigcomplex> roots);
84 //checks (and returns) how many roots are actually real, and puts those in
85 //bnd, in increasing order, by calling set_the_bound
86 int set_the_bounds(vector<double>& bnd, bigfloat x0, bigfloat x1, bigfloat x2);
87 //This transforms (if possible) x0, x1 and x1 into double; the search
88 //should be made on [x0,x1]U[x2,infty] so if x1 or x2 overflows, the search
89 //is on [x0,infty]. The function returns 3 in the first case, 1 in the second.
90 //If x0 overflows, it returns 0. A warning is printed out.
91
92 #define matentry(m,i,j) *((m)+((i)*MAXRANK)+(j))
93
94 bigfloat det(bigfloat *m, long m_size);
95 // fwd declaration: det and detminor jointly recursive
96
get_minor(bigfloat * m,long m_size,long i0,long j0)97 bigfloat* get_minor(bigfloat *m, long m_size, long i0, long j0)
98 {
99 long i, j, ii, jj;
100 bigfloat *minor = new bigfloat[MAXRANK*MAXRANK];
101 for (i=0; i<m_size-1; i++)
102 {
103 ii=i; if(i>=i0)ii++;
104 for (j=0; j<m_size-1; j++)
105 {
106 jj=j; if(j>=j0) jj++;
107 matentry(minor,i,j) = matentry(m,ii,jj);
108 }
109 }
110 return minor;
111 }
112
113
det_minor(bigfloat * m,long m_size,long i0,long j0)114 bigfloat det_minor(bigfloat *m, long m_size, long i0, long j0)
115 {
116 bigfloat *minor = get_minor(m,m_size,i0,j0);
117 bigfloat det_return = det(minor, m_size-1);
118 delete [] minor;
119 return det_return;
120 }
121
det(bigfloat * m,long m_size)122 bigfloat det(bigfloat *m, long m_size)
123 {
124 switch (m_size) {
125 case 0:
126 return to_bigfloat(1); break;
127 case 1:
128 return matentry(m,0,0); break;
129 case 2:
130 return matentry(m,0,0)*matentry(m,1,1) - matentry(m,1,0)*matentry(m,0,1);
131 break;
132 default:
133 // use recursion
134 /* // Old naive minor-expansion method:
135 bigfloat ans = 0;
136 long sign = 1, j;
137 for (j=0; j<m_size; j++)
138 { ans += sign * matentry(m,0,j) * det_minor(m, m_size, 0, j);
139 sign *= -1;
140 }
141 return ans;
142 */
143 // New Gaussian method (20/1/95)
144 long i,j,i0;
145 bigfloat ans=to_bigfloat(1), pivot=matentry(m,0,0), piv, temp,
146 eps=to_bigfloat(1.0e-6);
147 for(i0=0; i0<m_size && abs(pivot)<eps; i0++) pivot=matentry(m,i0,0);
148 if(i0==m_size) return to_bigfloat(0); // first column all 0
149 if(i0>0) // swap rows 0, i0:
150 {
151 ans=to_bigfloat(-1);
152 for(j=0; j<m_size; j++)
153 {
154 temp=matentry(m,i0,j);
155 matentry(m,i0,j)=matentry(m,0,j);
156 matentry(m,0,j)=temp;
157 }
158 }
159 // eliminate first column
160 pivot=matentry(m,0,0);
161 for(i=1; i<m_size; i++)
162 {
163 piv=matentry(m,i,0)/pivot;
164 for(j=0; j<m_size; j++)
165 matentry(m,i,j) = matentry(m,i,j)-matentry(m,0,j)*piv;
166 }
167 return ans*pivot*det_minor(m,m_size,0,0);
168 break;
169 }
170 return to_bigfloat(1); // shouldn't get here in fact
171 }
172
173
174 //#define DEBUG 1
175
cleardenoms(vector<bigfloat> alpha)176 vector<long> cleardenoms(vector<bigfloat> alpha)
177 {
178 long len = alpha.size();
179 vector<long> nlist(len); // returned
180 vector<long> dlist(len);
181 long i, lcmd = 1;
182 bigfloat x, last=alpha[len-1];
183 for (i=0; i < len-1; i++) // i doesn't include rank (new value)
184 { x = alpha[i] / last;
185 ratapprox(x, nlist[i], dlist[i]);
186 lcmd = (lcmd*dlist[i]) / ::gcd(lcmd, dlist[i]); // ie lcm(d, dlist[i])
187 // ie we find the lcm of whole of dlist
188 #ifdef DEBUG
189 cout<<"ratapprox: of "<< x <<" is "<<nlist[i]<<" / "<<dlist[i]<<endl;
190 cout<<" lcm of denoms so far: "<<lcmd<<endl;
191 #endif
192 }
193 for (i=0; i < len-1; i++)
194 nlist[i] *= (lcmd / dlist[i]); // clear the denominators
195 nlist[len-1] = lcmd;
196 return nlist;
197 }
198
mw(Curvedata * EE,int verb,int pp,int maxr)199 mw::mw(Curvedata *EE, int verb, int pp, int maxr)
200 :E(EE), rank(0), maxrank(maxr), reg(to_bigfloat(1)), verbose(verb), process_points(pp), satsieve(EE,verb)
201 {
202 #ifdef DEBUG
203 verbose=1;
204 #endif
205 height_pairs = new bigfloat[MAXRANK*MAXRANK];
206 }
207
~mw()208 mw::~mw()
209 {
210 delete [] height_pairs;
211 }
212
213 // NB We cannot use the default parameter mechanism as this must fit
214 // the template for the virtual function declared in class
215 // point_processor!
process(const bigint & x,const bigint & y,const bigint & z)216 int mw::process(const bigint& x, const bigint& y, const bigint& z)
217 {
218 return process(x,y,z,MAXSATPRIME);
219 }
220
process(const bigint & x,const bigint & y,const bigint & z,int sat)221 int mw::process(const bigint& x, const bigint& y, const bigint& z, int sat)
222 {
223 #ifdef DEBUG
224 cout<<"mw::process with x = "<< x <<", y = "<<y<<", z = "<<z<<endl;
225 #endif
226 bigint rz; isqrt(z,rz);
227 bigint x1=x*rz, y1=y, z1=z*rz;
228 if(iso)
229 {
230 y1 -= (a1*x1+4*a3*z1);
231 x1 *= 2;
232 z1 *= 8;
233 }
234 Point P(E, x1,y1,z1);
235 if(P.isvalid()) return process(P,sat);
236
237 // error:
238 cout<<"Raw point x,y,z = "<<x<<", "<<y<<", "<<z<<endl;
239 cout<<"converted point x,y,z = "<<x1<<", "<<y1<<", "<<z1<<"\t";
240 cout<<"--not on curve!"<<endl;
241 return 0;
242 }
243
process(const vector<Point> & Plist,int sat)244 int mw::process(const vector<Point>& Plist, int sat)
245 {
246 // process the points without saturation, do that at the end
247
248 if(verbose)
249 cout<<"Processing "<<Plist.size()<<" points ..."<<endl;
250
251 int flag=0;
252 for(vector<Point>::const_iterator P=Plist.begin(); P!=Plist.end(); P++)
253 flag = (process(*P,0));
254
255 if(verbose)
256 cout<<"Finished processing the points (which had rank "<<rank<<")"<<endl;
257
258 if((sat>0)&&(rank>0))
259 {
260 if (verbose) cout<<"saturating up to "<<sat<<"..."<<flush;
261 satsieve.set_points(basis);
262 long index;
263 vector<long> unsat;
264 int sat_ok = satsieve.saturate(unsat, index, sat);
265 if(verbose)
266 {
267 cout<<"done";
268 if (!sat_ok)
269 {
270 cout<<" (saturation failed for "<<unsat<<")";
271 }
272 cout<<endl;
273 }
274 if(index>1)
275 {
276 basis = satsieve.getgens();
277 if(verbose)
278 cout<<"Gained index "<<index<<", new generators = "<<basis<<endl;
279 }
280 // compute the height pairing matrix and regulator
281 int i, j;
282 for (i=0; i < rank; i++)
283 {
284 mat_entry(i,i) = height(basis[i]);
285 for (j=0; j < i; j++)
286 {
287 mat_entry(i,j)
288 = mat_entry(j,i)
289 = height_pairing(basis[i], basis[j]);
290 }
291 }
292 reg = det(height_pairs,rank);
293 if(verbose)
294 cout<<"Regulator = "<<reg<<endl;
295 }
296 return flag;
297 }
298
process(const Point & PP,int sat)299 int mw::process(const Point& PP, int sat)
300 {
301 #ifdef DEBUG
302 cout<<"mw::process with P = "<< PP <<endl;
303 #endif
304 Point P = PP; // so we can process const points
305 long ord = order(P);
306 long i, j, rank1=rank+1;
307 #ifdef DEBUG
308 cout<<"P = "<< P <<" has order "<<ord<<endl;
309 bigfloat hP=height(P);
310 cout<<"P = "<< P <<" has height "<<hP<<endl;
311 #endif
312
313 if (verbose)
314 {cout<<"P"<<rank1<<" = "<<P;
315 #ifdef DEBUG
316 cout<<" (height "<<height(P)<<")";
317 #endif
318 cout << "\t" << flush;
319 #ifdef DEBUG
320 cout << "\n";
321 if(!P.isvalid()) cout << "###Warning### Not on curve!\n";
322 #endif
323 }
324
325 if (ord > 0)
326 {
327 if (verbose) cout<<" is torsion point, order "<<ord<<endl;
328 return 0;
329 } // we're not interested in torsion points
330
331 if(!process_points)
332 {
333 basis.push_back(P); rank++;
334 if(verbose) cout<<"P = "<<P<<", ht(P) = "<<height(P)<<endl;
335 return 0;
336 }
337
338 if (rank==0) // first non-torsion point
339 {
340 reg = height(P);
341 mat_entry(0,0) = reg; // ie height_pairs[0][0] = reg
342 basis.push_back(P); rank=1;
343 if (verbose) cout<<" is generator number 1\n";
344 #ifdef DEBUG
345 cout << "first non-torsion point, reg so far = "<<reg<<"\n";
346 // cout << "returning "<<(maxrank<2)<<endl;
347 #endif
348 if(sat>0)
349 {
350 satsieve.set_points(basis);
351 if (verbose) cout<<"saturating up to "<<sat<<"..."<<flush;
352 long index;
353 vector<long> unsat;
354 int sat_ok = satsieve.saturate(unsat, index, sat);
355 if(verbose)
356 {
357 cout<<"done";
358 if (!sat_ok)
359 {
360 cout<<" (saturation failed for "<<unsat<<")";
361 }
362 cout<<endl;
363 }
364 if(index>1)
365 {
366 basis = satsieve.getgens();
367 if(verbose) cout<<"Gained index "<<index<<", new generator = "<<basis[0]<<endl;
368 reg = height(basis[0]);
369 mat_entry(0,0) = reg;
370 }
371 }
372 return (maxrank<2); // 1 if max reached
373 }
374
375 // otherwise general procedure:
376
377 #ifdef DEBUG
378 cout<<"additional non-torsion point..."<<endl;
379 #endif
380 // update the height pairing matrix (at least for now)
381 // but don't add point yet
382 mat_entry(rank,rank) = height(P); // also sets height in P
383 for (i=0; i < rank; i++)
384 {
385 Point Q = basis[i];
386 bigfloat hp = height_pairing(Q, P);
387 mat_entry(i,rank) = hp;
388 mat_entry(rank,i) = hp;
389 }
390
391 // compute cofactors of new last column
392 vector<bigfloat> alpha(rank1); // to store cofactors
393 long detsign = ( odd(rank) ? +1 : -1 ); //set for flip before first use
394 for (i=0; i < rank; i++)
395 {
396 detsign = -detsign;
397 alpha[i] = det_minor(height_pairs, rank1, i, rank) * detsign;
398 #ifdef DEBUG
399 cout<<"alpha["<<i<<"] = "<<alpha[i]<<"\n";
400 #endif
401 }
402 alpha[rank] = reg; // ie the previous value, before this point
403 #ifdef DEBUG
404 cout<<"alpha["<<rank<<"] = "<<alpha[rank]<<"\n";
405 #endif
406
407 // find the new determinant
408 bigfloat newreg = to_bigfloat(0);
409 for (i=0; i <= rank; i++) newreg += mat_entry(i,rank) * alpha[i];
410 #ifdef DEBUG
411 cout<<"After adding P, new height pairing matrix:\n";
412 for(i=0; i<=rank; i++)
413 {
414 for(j=0; j<=rank; j++) cout << mat_entry(i,j) << "\t";
415 cout << "\n";
416 }
417 cout<<"\nreg is now " << newreg << "\t";
418 #endif
419
420 // test for simple case, new point is indep previous
421 if ( abs(newreg/reg) > 1.0e-4 )
422 {
423 reg = newreg;
424 #ifdef DEBUG
425 cout << "treating as NON-zero" << "\n";
426 #endif
427 basis.push_back(P); rank=rank1;
428 if (verbose) cout<<" is generator number "<<rank<<endl;
429 if(sat>0)
430 {
431 satsieve.reset_points(basis);
432 if (verbose) cout<<"saturating up to "<<sat<<"..."<<flush;
433 long index;
434 vector<long> unsat;
435 int sat_ok = satsieve.saturate(unsat, index, sat);
436 if(verbose)
437 {
438 cout<<"done, index = "<<index<<".";
439 if (!sat_ok)
440 {
441 cout<<" (saturation failed for "<<unsat<<")";
442 }
443 cout<<endl;
444 }
445 if(index>1)
446 {
447 basis = satsieve.getgens();
448 if(verbose) cout<<"Gained index "<<index<<", new generators = "<<basis<<endl;
449 // completely recompute the height pairing matrix
450 for (i=0; i < rank; i++)
451 {
452 mat_entry(i,i) = height(basis[i]);
453 for (j=0; j < i; j++)
454 {
455 mat_entry(i,j)
456 = mat_entry(j,i)
457 = height_pairing(basis[i], basis[j]);
458 }
459 }
460 reg /= (index*index);
461 }
462 }
463 #ifdef DEBUG
464 cout << "about to return, rank = "<<rank<<", maxrank = "<<maxrank<<": returning "<<(maxrank==rank)<<endl;
465 #endif
466 return (maxrank==rank); // 1 if max reached
467 }
468
469 // otherwise, express new point as lin-comb of the previous Now that
470 // we are saturating as we go, this should not happen (unless the
471 // index is divisible by a prime > sat)
472 #ifdef DEBUG
473 cout << "treating as ZERO" << "\n";
474 cout << "Finding a linear relation between P and current basis\n";
475 #endif
476
477 vector<long> nlist = cleardenoms(alpha);
478 long index = nlist[rank];
479
480 #ifdef DEBUG
481 cout<<"index = "<<index<<endl;
482 #endif
483
484 // test simple case when new is just Z-linear comb of old
485 if ( index == 1 )
486 { if (verbose)
487 {
488 cout<<" = "<<-nlist[0]<<"*P1";
489 for(i=1; i<rank; i++)
490 cout<<" + "<<-nlist[i]<<"*P"<<(i+1);
491 cout<<" (mod torsion)\n";
492 }
493 // Check:
494 Point Q = P;
495 for(i=0; i<rank; i++) Q += nlist[i]*basis[i];
496 int oQ = order(Q);
497 if(oQ==-1) // infinite order, problem!
498 {
499 cout<<"Problem in mw::process(), bad linear combination!\n";
500 cout<<"Difference = "<<Q<<" with height "<<height(Q)<<endl;
501 }
502 else if(verbose>1)
503 {
504 cout<<"Difference = "<<Q<<" with height "<<height(Q)<<endl;
505 }
506
507 return 0; // with regulator and basis (and h_p matrix) unchanged
508 } // end of if(index==1)
509
510 // otherwise add P to the list now, compute to gain index
511 basis.push_back(P);
512
513 if (verbose)
514 {
515 for (i=0; i < rank; i++)
516 cout<<nlist[i]<<"*P"<<(i+1)<<" + ";
517 cout<<index<<"*"<<"P"<<(rank1)<<" = 0 (mod torsion)\n";
518 }
519 // Check:
520 Point Q = index*P;
521 for(i=0; i<rank; i++) Q += nlist[i]*basis[i];
522 int oQ = order(Q);
523 if(oQ==-1) // infinite order, problem!
524 {
525 cout<<"Problem in mw::process(), bad linear combination!\n";
526 cout<<"Difference = "<<Q<<" with height "<<height(Q)<<endl;
527 }
528 else if(verbose>1)
529 {
530 cout<<"Difference = "<<Q<<" with height "<<height(Q)<<endl;
531 }
532
533
534 // find minimum coeff. |ai|
535 long min = 0, ni;
536 long imin = -1;
537 for (i=0; i <= rank; i++)
538 { ni = abs(nlist[i]);
539 if ( (ni>0) && ( (imin==-1) || (ni<min) ) )
540 { min = ni; imin = i; }
541 }
542
543 // find aj with ai ndiv aj, write aj = ai*q + r with 0<r<|ai|
544 // since then ai*Pi + aj*Pj = ai(Pi + q*Pj) + r*Pj,
545 // replace generator Pi with Pi + q*Pj
546 // and replace aj by r, and i by j
547 // after finite no. steps obtain some minimal ai = 1
548 // then we can just discard Pi.
549
550 long r, q;
551 while ( min > 1 )
552 {
553 for (i=0; i <= rank; i++)
554 {
555 r = mod(nlist[i], min);
556 q = (nlist[i] - r) / nlist[imin];
557 if ( r!=0 )
558 {
559 basis[imin] += q*basis[i];
560 imin=i; min=abs(r); nlist[imin]=r;
561 #ifdef DEBUG
562 if (verbose)
563 { for (j=0; j < rank; j++)
564 cout<<nlist[j]<<"*"<<basis[j]<<" + ";
565 cout<<nlist[rank]<<"*"<<basis[rank]<<" = 0 (mod torsion)\n";
566 }
567 #endif
568 break; // out of the for loop, back into the while
569 }
570 } // ends for
571 } // ends while
572
573 if (verbose)
574 cout<<"Gaining index "<<index<<"; ";
575
576 // delete basis[imin]
577 basis.erase(basis.begin()+imin);
578 // for(j=imin; j<rank; j++) basis[j]=basis[j+1];
579
580 // completely recompute the height pairing matrix
581 for (i=0; i < rank; i++) // 19/8/02: this was <=rank
582 { mat_entry(i,i) = height(basis[i]);
583 for (j=0; j < i; j++)
584 {
585 mat_entry(i,j)
586 = mat_entry(j,i)
587 = height_pairing(basis[i], basis[j]);
588 }
589 }
590
591 reg /= (index*index);
592 if (verbose)
593 {
594 cout<<"\nNew set of generators: \n";
595 for(i=0; i<rank; i++)
596 {
597 if(i)cout<<", ";
598 cout<<"P"<<(i+1)<<" = "<< basis[i];
599 }
600 cout<<endl;
601 }
602 return 0; // rank did not increase
603 } // end of function mw::process(Point)
604
saturate(long & index,vector<long> & unsat,long sat_bd,long sat_low_bd)605 int mw::saturate(long& index, vector<long>& unsat, long sat_bd, long sat_low_bd)
606 {
607 if (verbose) cout<<"saturating basis..."<<flush;
608
609 // This code does a dummy call to index_bound() in order to get
610 // points from the search done there into the mwbasis. But it was
611 // decided to let the user do some searching if relevant instead
612 #if(0)
613 vector<Point> pts;
614 bigint ib = index_bound(E,basis,pts,0,(verbose>1));
615 // Must make sure that the new points have the correct Curvedata pointer!
616 for(unsigned int i=0; i<pts.size(); i++)
617 pts[i].init(E,getX(pts[i]),getY(pts[i]),getZ(pts[i]));
618 bigfloat oldreg = reg;
619 int oldrank=rank;
620 process(pts,0); //no saturation here! This may update basis
621 bigint ind = Iround(sqrt(oldreg/reg));
622 if(verbose&&(ind>1))
623 {
624 cout<<"after search, gained index "<<ind
625 <<", regulator = "<<reg<<endl;
626 }
627 if((rank>oldrank))
628 {
629 cout<<"after search, rank increases to "<<rank
630 <<", regulator = "<<reg<<endl;
631 }
632 #endif
633 satsieve.set_points(basis);
634 int ok = 1;
635 if(rank>0) ok=satsieve.saturate(unsat,index,sat_bd,1,10, sat_low_bd);
636 if(verbose) cout<<"done"<<endl;
637 if(!ok)
638 cout<<"Failed to saturate MW basis at primes "<<unsat<<endl;
639 if(index>1)
640 {
641 basis = satsieve.getgens();
642 // completely recompute the height pairing matrix
643 for (int i=0; i < rank; i++)
644 {
645 mat_entry(i,i) = height(basis[i]);
646 for (int j=0; j < i; j++)
647 {
648 mat_entry(i,j)
649 = mat_entry(j,i)
650 = height_pairing(basis[i], basis[j]);
651 }
652 }
653 reg /= (index*index);
654 if(verbose)
655 {
656 cout<<"Gained index "<<index<<endl;
657 cout<<"New regulator = "<<reg<<endl;
658 }
659 }
660 return ok;
661 }
662
search(bigfloat h_lim,int moduli_option,int verb)663 void mw::search(bigfloat h_lim, int moduli_option, int verb)
664 {
665 #ifdef DEBUG
666 cout<<"In mw::search; maxrank = "<<maxrank<<endl;
667 #endif
668 if(moduli_option)
669 {
670 sieve s(E, this, moduli_option, verb);
671 s.search(h_lim);
672 }
673 else // use Stoll's sieve, Sophie Labour's conversion:
674 {
675 vector<bigint> c(4);
676 E -> getai(a1,a2,a3,a4,a6);
677 iso = !((a1==0)&&(a3==0));
678 if(iso)
679 {
680 c[0]=16*getb6(*E);
681 c[1]= 8*getb4(*E);
682 c[2]= getb2(*E);
683 c[3]=1;
684 }
685 else
686 {
687 c[0]=a6;
688 c[1]=a4;
689 c[2]=a2;
690 c[3]=1;
691 }
692 if(iso) h_lim+=2.08;
693 // if(iso) cout<<"Adding log(8) to h_lim, increasing it to "<<h_lim<<endl;
694 qsieve s(this, 3, c, h_lim, verb);
695 bigcomplex c1(I2bigfloat(c[2])),
696 c2(I2bigfloat(c[1])),
697 c3(I2bigfloat(c[0]));
698 vector<bigcomplex> roots=solvecubic(c1,c2,c3);
699 vector<double> bnd(3);
700 int nrr=order_real_roots(bnd,roots);
701 #ifdef DEBUG_QSIEVE
702 cout<<endl;
703 cout<<"cubic "<<c[0]<<" "<<c[1]<<" "<<c[2]<<" "<<c[3]<<endl;
704 cout<<"coeff "<<c1<<" "<<c2<<" "<<c3<<endl;
705 cout<<"roots "<<roots<<endl;
706 cout<<"bnd "<<bnd<<endl;
707 cout<<"smallest "<<bnd[0]<<endl;
708 #endif
709 s.set_intervals(bnd,nrr,1);
710 s.search(); //searches and processes
711 }
712 }
713
search_range(bigfloat xmin,bigfloat xmax,bigfloat h_lim,int moduli_option,int verb)714 void mw::search_range(bigfloat xmin, bigfloat xmax, bigfloat h_lim,
715 int moduli_option, int verb)
716 {
717 sieve s(E, this, moduli_option, verb);
718 s.search_range(xmin,xmax,h_lim);
719 }
720
721 //#define DEBUG_SIEVE
722
sieve(Curvedata * EE,mw * mwb,int moduli_option,int verb)723 sieve::sieve(Curvedata * EE, mw* mwb, int moduli_option, int verb)
724 : E(EE), mwbasis(mwb), verbose(verb)
725 {
726 E->getai(a1,a2,a3,a4,a6);
727 int ncomp = getconncomp(*E);
728 posdisc = ncomp==2;
729 long i, j;
730
731 // find pt of order two in E(R) with minimal x-coord
732
733 vector<bigcomplex> roots = roots_of_cubic(*E);
734 if(posdisc)
735 {
736 x1=real(roots[0]);
737 x2=real(roots[1]);
738 x3=real(roots[2]);
739 orderreal(x3,x2,x1); // so x1<x2<x3
740 xmin=x1;
741 }
742 else
743 x3=xmin = min_real(roots);
744
745 if (verbose)
746 {
747 cout << "sieve: real points have ";
748 if(posdisc) cout<<x1<<" <= x <= " << x2 << " or ";
749 cout << x3 << " <= x; xmin = " << xmin << endl;
750 }
751 // set up list of auxiliary moduli
752 // and a list of which residues modulo each of these are squares
753
754 switch(moduli_option) {
755 case 1:
756 num_aux = 10;
757 auxs = new long[num_aux];
758 auxs[0]=3;
759 auxs[1]=5;
760 auxs[2]=7;
761 auxs[3]=11;
762 auxs[4]=13;
763 auxs[5]=17;
764 auxs[6]=19;
765 auxs[7]=23;
766 auxs[8]=29;
767 auxs[9]=31;
768 break;
769
770 case 2:// the following taken from Gebel's scheme
771 num_aux = 3;
772 auxs = new long[num_aux];
773 auxs[0]=5184; // = (2^6)*(3^4) // old: 6624; // = (2^5)*(3^2)*23
774 auxs[1]=5929; // = (7^2)*(11^2) // old: 8075; // = (5^2)*17*19
775 auxs[2]=4225; // = (5^2)*(13^2) // old: 7007; // = (7^2)*11*13
776 break;
777
778 case 3:
779 default:
780 num_aux = 9;
781 auxs = new long[num_aux];
782 auxs[0]=32;
783 auxs[1]= 9;
784 auxs[2]=25;
785 auxs[3]=49;
786 auxs[4]=11;
787 auxs[5]=13;
788 auxs[6]=17;
789 auxs[7]=19;
790 auxs[8]=23;
791 break;
792 }
793
794 #ifdef DEBUG_SIEVE
795 if(verbose)
796 {
797 cout<<"Using "<<num_aux<<" sieving moduli:\n";
798 for(i=0; i<num_aux; i++) cout << auxs[i]<<"\t";
799 cout<<endl;
800 }
801 #endif
802
803 xgood_mod_aux = new int*[num_aux];
804 // x1good_mod_aux = new int*[num_aux];
805 squares = new int*[num_aux];
806 amod = new long[num_aux];
807
808 for (i = 0; i < num_aux; i++)
809 {
810 long aux = auxs[i];
811 long half_aux = ((aux + 1) / 2);
812 squares[i] = new int[aux];
813 for (j = 0; j < aux; j++) squares[i][j]=0;
814 for (j = 0; j < half_aux; j++) squares[i][(j*j)%aux]=1;
815 xgood_mod_aux[i] = new int[aux];
816
817 // x1good_mod_aux[i] = new int[aux];
818 //
819 // // set the flag matrix for c=1:
820 //
821 // long pd1 = posmod(a1, aux);
822 // long pd2 = posmod(a2, aux);
823 // long pd3 = posmod(a3, aux);
824 // long pd4 = posmod(a4, aux);
825 // long pd6 = posmod(a6, aux);
826 //
827 // long disc, temp, temp2, x=0;
828 //
829 // long dddf= posmod(24,aux);
830 // long ddf = posmod(2*(pd1*pd1)%aux + 8*pd2+24 , aux);
831 // long df = posmod(pd1*(pd1+2*pd3)%aux + 4*(pd4+pd2+1) , aux);
832 // long f = posmod(((pd3*pd3)%aux+4*pd6) , aux);
833 //
834 // while(x<aux)
835 // {
836 // x1good_mod_aux[i][x] = squares[i][f];
837 // x++;
838 // f += df; if(f >=aux) f -=aux;
839 // df += ddf; if(df >=aux) df -=aux;
840 // ddf +=dddf; if(ddf>=aux) ddf-=aux;
841 // }
842 } // end of aux loop
843 #ifdef DEBUG_SIEVE
844 if(verbose)
845 {
846 cout<<"squares lists:\n";
847 for(i=0; i<num_aux; i++)
848 {
849 cout << auxs[i]<<":\t";
850 for(j=0; j<auxs[i]; j++) if(squares[i][j]) cout<<j<<"\t";
851 cout<<endl;
852 }
853 }
854 #endif
855
856 // variables for collecting efficiency data:
857
858 modhits = new long[num_aux];
859 ascore=0; npoints=0;
860 for(i=0; i<num_aux; i++) modhits[i]=0;
861
862 // if(verbose)
863 // {
864 // cout << "Finished constructing sieve, using ";
865 // switch(moduli_option)
866 // {
867 // case 1: cout << "ten primes 3..31"; break;
868 // case 2: cout << "Gebel's three moduli"; break;
869 // case 3: cout << "prime powers"; break;
870 // }
871 // cout << endl;
872 // }
873 }
874
~sieve()875 sieve::~sieve()
876 {
877 delete [] auxs;
878 for(long i=0; i<num_aux; i++)
879 {
880 delete [] xgood_mod_aux[i];
881 // delete [] x1good_mod_aux[i];
882 delete [] squares[i];
883 }
884 delete [] xgood_mod_aux;
885 // delete [] x1good_mod_aux;
886 delete [] squares;
887 delete [] amod;
888 delete [] modhits;
889 }
890
search(bigfloat h_lim)891 void sieve::search(bigfloat h_lim)
892 {
893 // N.B. On 32-bit machines, h_lim MUST be < 21.48 else exp(h_lim)>2^31
894 // and overflows
895 // On 64-bit machines, h_lim must be < 43.668.
896
897 long i,j;
898
899 // set initial bounds for point coefficients
900 alim = I2long(Ifloor(exp(h_lim)));
901 clim = clim1 = clim2 = clim0 = I2long(Ifloor(exp(h_lim / 2)));
902 long temp;
903
904 if(posdisc)
905 {
906 if(x2<-1)
907 {
908 temp = I2long(Ifloor(sqrt(alim/(-x2))));
909 if(clim1>temp) clim1=temp;
910 }
911 if(x1>1)
912 {
913 temp = I2long(Ifloor(sqrt(alim/x1)));
914 if(clim1>temp) clim1=temp;
915 }
916 }
917 if (x3>1)
918 {
919 temp = I2long(Ifloor(sqrt(alim/x3)));
920 if(clim2>temp) clim2=temp;
921 }
922 clim=clim2;
923 if(posdisc) if(clim1>clim2) clim=clim1;
924
925 if (verbose)
926 cout<< "sieve::search: trying a up to "<<alim<<" and c up to "<<clim<<endl;
927
928 // declare and initialize other loop variables
929 long pd1,pd2,pd3,pd4,pd6, csq, aux;
930 cflag = new int[10000]; // max needed; only used up to c each time,
931 // and only when c<=10000 (use_cflag==1)
932
933 //
934 // MAIN LOOP
935 //
936
937 #define FIRSTC 1 // for debugging etc
938
939 for (c = FIRSTC; c <= clim; c++)
940 {
941 // some preliminary calculations of multiples of c etc.
942 csq = c*c /* long */;
943 c2 = csq /* bigint */;
944 c3 = c*c2; c4 = c2*c2; c6 = c2*c4;
945 d1 = a1*c; d2 = a2*c2; d3 = a3*c3; d4 = a4*c4; d6 = a6*c6;
946
947 #ifdef DEBUG_SIEVE
948 if(verbose)
949 {
950 cout<<"c = "<<c<<"\n";
951 cout<<"d1,...,d6 = "<<d1<<", "<<d2<<", "<<d3<<", "<<d4<<", "<<d6<<"\n";
952 }
953 #endif
954 use_cflag = (c<=10000);
955 if(use_cflag)
956 {
957 // set up flag array of residues coprime to c
958 cflag[0]=(c==1);
959 for(i=1; i<c; i++) cflag[i] = cflag[c-i] = (::gcd(i,c)==1);
960 }
961
962 // set the main flag matrix
963 for (long index = 0; index < num_aux; index++)
964 {
965 aux = auxs[index];
966
967 // if(gcd(c,aux)==1) // the easy case
968 // {
969 // long xcc=0, csqm=csq%aux;;
970 // int* flag = xgood_mod_aux[index];
971 // int* flag1 = x1good_mod_aux[index];
972 // x=aux;
973 // while(x--)
974 // {
975 // *flag = *flag1++;
976 // xcc+=csqm; flag+=csqm;
977 // if(xcc>=aux) {xcc-=aux; flag-=aux;}
978 // }
979 // }
980 // else // c, aux have common factor
981 // {
982 // if(odd(aux)&&((csq%aux)==0))
983 // {
984 // int* flag = xgood_mod_aux[index];
985 // int* sqs = squares[index];
986 // x=aux;
987 // while(x--) *flag++ = *sqs++;
988 // } // end of if(odd(aux))
989 // else //default: full recomputation
990 {
991 pd1 = posmod(d1 , aux);
992 pd2 = posmod(d2 , aux);
993 pd3 = posmod(d3 , aux);
994 pd4 = posmod(d4 , aux);
995 pd6 = posmod(d6 , aux);
996
997 long dddf= 24%aux;
998 long ddf = posmod(2*(pd1*pd1)%aux + 8*pd2+24 , aux);
999 long df = posmod(pd1*(pd1+2*pd3)%aux + 4*(pd4+pd2+1) , aux);
1000 long f = posmod(((pd3*pd3)%aux+4*pd6) , aux);
1001
1002 int* flag = xgood_mod_aux[index];
1003 int* sqs = squares[index];
1004 long x=aux;
1005 #ifdef DEBUG_SIEVE
1006 if(verbose)
1007 {
1008 cout<<"aux = "<< aux <<"\n ";
1009 cout<<"pd1,...,pd6 = "<<pd1<<", "<<pd2<<", "<<pd3<<", "<<pd4<<", "<<pd6<<"\n";
1010 }
1011 #endif
1012 while(x--)
1013 {
1014 *flag++ = sqs[f];
1015 #ifdef DEBUG_SIEVE
1016 if(verbose)
1017 {
1018 cout<<"x = "<< aux-x-1 <<", f(x) = "<<f<<": flag = "<<sqs[f]<<"\n";
1019 }
1020 #endif
1021 f += df; if(f >=aux) f -=aux;
1022 df += ddf; if(df >=aux) df -=aux;
1023 ddf +=dddf; if(ddf>=aux) ddf-=aux;
1024 }
1025
1026 } // end of default case
1027
1028 // } // end of non-coprime case
1029 } // end of aux loop
1030 #ifdef DEBUG_SIEVE
1031 if(verbose)
1032 {
1033 for(i=0; i<num_aux; i++)
1034 {
1035 cout<<"possible x mod "<<auxs[i]<<": ";
1036 for(j=0; j<auxs[i]; j++) if(xgood_mod_aux[i][j]) cout<<j<<"\t";
1037 cout<<endl;
1038 }
1039 }
1040 #endif
1041 if(verbose>1)
1042 {
1043 for(i=0; i<num_aux; i++)
1044 {
1045 int n=0;
1046 cout<<"Number of possible x mod "<<auxs[i]<<": ";
1047 for(j=0; j<auxs[i]; j++) if(xgood_mod_aux[i][j]) n++;
1048 cout<<n<<" ("<<100.0*n/auxs[i]<<" percent)";
1049 cout<<endl;
1050 }
1051 }
1052
1053 // set up for a-loop(s)
1054
1055 if(posdisc&&(c<=clim1))
1056 {
1057 long amin = -alim, amax = alim;
1058 long temp = I2long(Ifloor(csq*x1));
1059 if(temp>amin) amin=temp;
1060 temp = I2long(Ifloor(csq*x2));
1061 if(temp<amax) amax=temp;
1062 a_search(amin,amax);
1063 }
1064
1065 if(c<=clim2)
1066 {
1067 long temp = I2long(Ifloor(csq*x3));
1068 long amin = -alim, amax = alim;
1069 if(temp>amin) amin=temp;
1070 a_search(amin,amax);
1071 }
1072 } // ends c- loop
1073 delete []cflag;
1074 } // end of sieve::search()
1075
search_range(bigfloat xmin,bigfloat xmax,bigfloat h_lim)1076 void sieve::search_range(bigfloat xmin, bigfloat xmax, bigfloat h_lim)
1077 {
1078 // N.B. h_lim MUST be < 21.48 else exp(h_lim)>2^31 and overflows
1079 long i;
1080
1081 // set initial bounds for point coefficients
1082 alim = I2long(Ifloor(exp(h_lim)));
1083 clim = clim1 = clim2 = clim0 = I2long(Ifloor(exp(h_lim / 2)));
1084 long temp;
1085
1086 if(xmax<-1)
1087 {
1088 temp = I2long(Ifloor(sqrt(alim/(-xmax))));
1089 if(clim1>temp) clim1=temp;
1090 }
1091 if(xmin>1)
1092 {
1093 temp = I2long(Ifloor(sqrt(alim/xmin)));
1094 if(clim1>temp) clim1=temp;
1095 }
1096 clim=clim2;
1097 if(clim1>clim2) clim=clim1;
1098
1099 if (verbose)
1100 cout<< "sieve::search: trying a up to "<<alim<<" and c up to "<<clim<<endl;
1101
1102 // declare and initialize other loop variables
1103 long pd1,pd2,pd3,pd4,pd6, csq, aux;
1104 cflag = new int[10000]; // max needed; only used up to c each time,
1105 // and only when c<=10000 (use_cflag==1)
1106
1107 //
1108 // MAIN LOOP
1109 //
1110
1111 for (c = 1; c <= clim; c++)
1112 {
1113 if(c>clim1) continue;
1114
1115 // some preliminary calculations of multiples of c etc.
1116 csq = c*c /* long */;
1117 c2 = csq /* bigint */;
1118 c3 = c*c2; c4 = c2*c2; c6 = c2*c4;
1119 d1 = a1*c; d2 = a2*c2; d3 = a3*c3; d4 = a4*c4; d6 = a6*c6;
1120
1121 long amin = -alim, amax = alim;
1122 long temp = I2long(Iceil(csq*xmin));
1123 if(temp>amin) amin=temp;
1124 temp = I2long(Ifloor(csq*xmax));
1125 if(temp<amax) amax=temp;
1126 cout<<"amin = " << amin << ", amax = " << amax << endl;
1127
1128 if(amin>amax) continue; // skip this c
1129
1130 if((amax-amin)<10) // don't both sieving for a
1131 {
1132 a_simple_search(amin,amax);
1133 }
1134 else
1135 {
1136 // set up flag array of residues coprime to c
1137 use_cflag = (c<=10000);
1138 if(use_cflag)
1139 {
1140 cflag[0]=(c==1);
1141 for(i=1; i<c; i++) cflag[i] = cflag[c-i] = (::gcd(i,c)==1);
1142 }
1143
1144 // set the main flag matrix
1145 for (long index = 0; index < num_aux; index++)
1146 {
1147 aux = auxs[index];
1148 pd1 = posmod(d1 , aux);
1149 pd2 = posmod(d2 , aux);
1150 pd3 = posmod(d3 , aux);
1151 pd4 = posmod(d4 , aux);
1152 pd6 = posmod(d6 , aux);
1153
1154 long dddf= 24%aux;
1155 long ddf = posmod(2*(pd1*pd1)%aux + 8*pd2+24 , aux);
1156 long df = posmod(pd1*(pd1+2*pd3)%aux + 4*(pd4+pd2+1) , aux);
1157 long f = posmod(((pd3*pd3)%aux+4*pd6) , aux);
1158
1159 int* flag = xgood_mod_aux[index];
1160 int* sqs = squares[index];
1161 long x=aux;
1162 while(x--)
1163 {
1164 *flag++ = sqs[f];
1165 f += df; if(f >=aux) f -=aux;
1166 df += ddf; if(df >=aux) df -=aux;
1167 ddf +=dddf; if(ddf>=aux) ddf-=aux;
1168 }
1169 } // end of aux loop
1170 a_search(amin,amax);
1171 }
1172 } // ends c- loop
1173 delete []cflag;
1174 } // end of sieve::search() version 2 (explicit xmin, xmax)
1175
a_search(const long & amin,const long & amax)1176 void sieve::a_search(const long& amin, const long& amax)
1177 {
1178 bigint pb,qb,db,rdb,rdb2,b,ac;
1179 long i, a=amin;
1180 a--;
1181 if (verbose) cout<<"sieve::search: trying c = "<<c<<"\t"
1182 <<"("<<amin<<" <= a <= "<<amax<<")"<<endl;
1183
1184 for (i=0; i < num_aux; i++) amod[i] = posmod(a, auxs[i]);
1185 amodc = posmod(a,c);
1186 #ifdef DEBUG_SIEVE
1187 if(verbose)
1188 {
1189 cout<<"Initial a = "<<a<<" modulo moduli = \t";
1190 for(i=0; i<num_aux; i++) cout << amod[i]<<"\t";
1191 cout<<endl;
1192 }
1193 #endif
1194
1195 while (a < amax)
1196 {
1197 a++;
1198 // check that a is good for all the auxiliaries
1199 amodc++; if(amodc==c) amodc=0;
1200 int try_x;
1201 if(use_cflag) try_x = cflag[amodc];
1202 else try_x = (::gcd(a,c)==1);
1203
1204 if(try_x)
1205 {
1206 ascore++;
1207 }
1208 // DON'T add "else continue; (with next a)
1209 // as the amod[i] are not yet updated!
1210 // for ( i=0; (i<num_aux); i++)
1211 for ( i=num_aux-1; (i>=0); i--)
1212 { long& amodi = amod[i];
1213 amodi++;
1214 if (amodi == auxs[i]) amodi = 0;
1215 if(try_x)
1216 {
1217 try_x = xgood_mod_aux[i][amodi];
1218 if(!try_x) modhits[i]++;
1219 }
1220 }
1221 if (!try_x) continue;
1222
1223 pb=a; pb*=d1; pb+=d3; // pb = a*d1 + d3;
1224 // qb = d6 + a*(d4 + a*(d2 + a));
1225 qb=a; qb+=d2; qb*=a; qb+=d4; qb*=a; qb+=d6;
1226 db = sqr(pb); db += (4*qb);
1227 if(isqrt(db,rdb))
1228 {
1229 b = rdb-pb; b/=2; ac = a*c;
1230 Point P(*E, ac, b, c3);
1231 mwbasis->process(P);
1232 npoints++;
1233 }
1234 } // ends a-loop
1235 }
1236
a_simple_search(const long & amin,const long & amax)1237 void sieve::a_simple_search(const long& amin, const long& amax)
1238 {
1239 bigint pb,qb,db,rdb,rdb2,b,ac;
1240 long a;
1241 if (verbose) cout<<"sieve::search: trying c = "<<c<<"\t"
1242 <<"("<<amin<<" <= a <= "<<amax<<")\n";
1243
1244 for (a=amin; a<amax; a++)
1245 {
1246 // pb = a*d1 + d3;
1247 pb=a; pb*=d1; pb+=d3;
1248 // qb = d6 + a*(d4 + a*(d2 + a));
1249 qb=a; qb+=d2; qb*=a; qb+=d4; qb*=a; qb+=d6;
1250 db = sqr(pb); db += (4*qb);
1251 if(isqrt(db,rdb))
1252 {
1253 b = rdb-pb; b/=2; ac = a*c;
1254 Point P(*E, ac, b, c3);
1255 mwbasis->process(P);
1256 npoints++;
1257 }
1258 } // ends a-loop
1259 }
1260
1261
stats(void)1262 void sieve::stats(void)
1263 {
1264 cout << "\nNumber of points found: "<<npoints<<"\n";
1265 cout << "\nNumber of a tested: "<<ascore<<"\n";
1266 cout<<"Numbers eliminated by each modulus:\n";
1267 long nmodhits=0;
1268 for(long i=0; i<num_aux; i++)
1269 {
1270 cout<<auxs[i]<<": "<<modhits[i]<<"\n";
1271 nmodhits+=modhits[i];
1272 }
1273 cout<<"Number eliminated by all moduli: "<<nmodhits<<"\n";
1274 bigfloat eff = to_bigfloat(nmodhits*100.0)/(ascore-npoints);
1275 cout<<"Sieve efficiency: "<<eff<<"\n";
1276 }
1277
order_real_roots(vector<double> & bnd,vector<bigcomplex> roots)1278 int order_real_roots(vector<double>& bnd, vector<bigcomplex> roots)
1279 {//checks (and returns) how many roots are actually real, and puts those in
1280 //bnd, in increasing order, by calling set_the_bound
1281 long i,nrr=0;
1282 vector<bigfloat> real_roots;
1283
1284 for (i=0;i<3;i++)
1285 {
1286 if (is_approx_zero(roots[i].imag()))
1287 {
1288 real_roots.push_back(roots[i].real());
1289 if (is_approx_zero(real_roots[nrr])) real_roots[nrr]=0;
1290 nrr++;
1291 }
1292 }
1293 // cout<<"nrr = "<<nrr<<endl;
1294 // cout<<"real_roots = "<<real_roots<<endl;
1295 // cout<<"Now ordering them..."<<endl;
1296 switch (nrr)
1297 {
1298 case 1: // possible overflow in assignment from bigfloat to double
1299 return !doublify(real_roots[0],bnd[0]);
1300 break;
1301 case 3:
1302 orderreal(real_roots[2],real_roots[1],real_roots[0]);
1303 return set_the_bounds(bnd,real_roots[0],real_roots[1],real_roots[2]);
1304 default:
1305 cout<<"mw_info::set_the_bounds: two real roots for the elliptic curve...\n";
1306 }
1307 return 0; //we should not get here...
1308 }
1309
1310 //This transforms (if possible) x0, x1 and x2 into double; the search
1311 //should be made on [x0,x1]U[x2,infty] so if x1 or x2 overflows, the
1312 //search is made on [x0,infty]. The function returns 3 in the first
1313 //case, 1 in the second. If x0 overflows, it returns 0. A warning is
1314 //printed out.
set_the_bounds(vector<double> & bnd,bigfloat x0,bigfloat x1,bigfloat x2)1315 int set_the_bounds(vector<double>& bnd, bigfloat x0, bigfloat x1, bigfloat x2)
1316 {
1317 if (doublify(x0,bnd[0]))
1318 {
1319 cout<<"##WARNING##: lowest bound "<<x0<<" is not a double.\n";
1320 cout<<"Search will be made over [-height,height]."<<endl;
1321 return 0;
1322 }
1323 else
1324 {
1325 if (doublify(x1,bnd[1]) || doublify(x2,bnd[2]))
1326 {
1327 cout<<"##WARNING##: second or third root is not a double.\n";
1328 cout<<"]x2,x3[ not excluded in search."<<endl;
1329 return 1;
1330 }
1331 else
1332 {
1333 return 3;
1334 }
1335 }
1336 }
1337
1338
1339 //end of file mwprocs.cc
1340
1341
1342
1343
1344
1345