FORM v5.0.0-35-g6318119
polygcd.cc
Go to the documentation of this file.
1
6/* #[ License : */
7/*
8 * Copyright (C) 1984-2026 J.A.M. Vermaseren
9 * When using this file you are requested to refer to the publication
10 * J.A.M.Vermaseren "New features of FORM" math-ph/0010025
11 * This is considered a matter of courtesy as the development was paid
12 * for by FOM the Dutch physics granting agency and we would like to
13 * be able to track its scientific use to convince FOM of its value
14 * for the community.
15 *
16 * This file is part of FORM.
17 *
18 * FORM is free software: you can redistribute it and/or modify it under the
19 * terms of the GNU General Public License as published by the Free Software
20 * Foundation, either version 3 of the License, or (at your option) any later
21 * version.
22 *
23 * FORM is distributed in the hope that it will be useful, but WITHOUT ANY
24 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
25 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
26 * details.
27 *
28 * You should have received a copy of the GNU General Public License along
29 * with FORM. If not, see <http://www.gnu.org/licenses/>.
30 */
31/* #] License : */
32/*
33 #[ include :
34*/
35
36#include "poly.h"
37#include "polygcd.h"
38
39#include <iostream>
40#include <vector>
41#include <cmath>
42#include <map>
43#include <algorithm>
44
45//#define DEBUG
46//#define DEBUGALL
47
48#ifdef DEBUG
49#include "mytime.h"
50#endif
51
52using namespace std;
53
54/*
55 #] include :
56 #[ ostream operator :
57*/
58
59#ifdef DEBUG
60// ostream operator for outputting vector<T>s for debugging purposes
61template<class T> ostream& operator<< (ostream &out, const vector<T> &x) {
62 out<<"{";
63 for (int i=0; i<(int)x.size(); i++) {
64 if (i>0) out<<",";
65 out<<x[i];
66 }
67 out<<"}";
68 return out;
69}
70#endif
71
72/*
73 #] ostream operator :
74 #[ integer_gcd :
75*/
76
91const poly polygcd::integer_gcd (const poly &a, const poly &b) {
92
93#ifdef DEBUGALL
94 cout << "*** [" << thetime() << "] CALL: integer_gcd(" << a << "," << b << ")" << endl;
95#endif
96
97 POLY_GETIDENTITY(a);
98
99 if (a.is_zero()) return b;
100 if (b.is_zero()) return a;
101
102 poly c(BHEAD 0, a.modp, a.modn);
103 WORD nc;
104
105 GcdLong(BHEAD
106 (UWORD *)&a[AN.poly_num_vars+2],a[a[0]-1],
107 (UWORD *)&b[AN.poly_num_vars+2],b[b[0]-1],
108 (UWORD *)&c[AN.poly_num_vars+2],&nc);
109
110 WORD x = 2 + AN.poly_num_vars + ABS(nc);
111 c[1] = x; // term length
112 c[0] = x+1; // total length
113 c[x] = nc; // coefficient length
114
115 for (int i=0; i<AN.poly_num_vars; i++)
116 c[2+i] = 0; // powers
117
118#ifdef DEBUGALL
119 cout << "*** [" << thetime() << "] RES : integer_gcd(" << a << "," << b << ") = " << c << endl;
120#endif
121
122 return c;
123}
124
125/*
126 #] integer_gcd :
127 #[ integer_content :
128*/
129
143const poly polygcd::integer_content (const poly &a) {
144
145#ifdef DEBUGALL
146 cout << "*** [" << thetime() << "] CALL: integer_content(" << a << ")" << endl;
147#endif
148
149 POLY_GETIDENTITY(a);
150
151 if (a.modp>0) return a.integer_lcoeff();
152
153 poly c(BHEAD 0, 0, 1);
154 WORD *d = (WORD *)NumberMalloc("polygcd::integer_content");
155 WORD nc=0;
156
157 for (int i=0; i<AN.poly_num_vars; i++)
158 c[2+i] = 0;
159
160 for (int i=1; i<a[0]; i+=a[i]) {
161
162 WCOPY(d,&c[2+AN.poly_num_vars],nc);
163
164 GcdLong(BHEAD (UWORD *)d, nc,
165 (UWORD *)&a[i+1+AN.poly_num_vars], a[i+a[i]-1],
166 (UWORD *)&c[2+AN.poly_num_vars], &nc);
167
168 WORD x = 2 + AN.poly_num_vars + ABS(nc);
169 c[1] = x; // term length
170 c[0] = x+1; // total length
171 c[x] = nc; // coefficient length
172 }
173
174 if (a.sign() != c.sign()) c *= poly(BHEAD -1);
175
176 NumberFree(d,"polygcd::integer_content");
177
178#ifdef DEBUGALL
179 cout << "*** [" << thetime() << "] RES : integer_content(" << a << ") = " << c << endl;
180#endif
181
182 return c;
183}
184
185/*
186 #] integer_content :
187 #[ content_univar :
188*/
189
205const poly polygcd::content_univar (const poly &a, int x) {
206
207#ifdef DEBUG
208 cout << "*** [" << thetime() << "] CALL: content_univar(" << a << "," << string(1,'a'+x) << ")" << endl;
209#endif
210
211 POLY_GETIDENTITY(a);
212
213 poly res(BHEAD 0, a.modp, a.modn);
214
215 for (int i=1; i<a[0];) {
216 poly b(BHEAD 0, a.modp, a.modn);
217 int deg = a[i+1+x];
218
219 for (; i<a[0] && a[i+1+x]==deg; i+=a[i]) {
220 b.check_memory(b[0]+a[i]);
221 b.termscopy(&a[i],b[0],a[i]);
222 b[b[0]+1+x] = 0;
223 b[0] += a[i];
224 }
225
226 res = gcd(res, b);
227
228 if (res.is_integer()) {
229 res = integer_content(a);
230 break;
231 }
232 }
233
234 if (a.sign() != res.sign()) res *= poly(BHEAD -1);
235
236#ifdef DEBUG
237 cout << "*** [" << thetime() << "] RES : content_univar(" << a << "," << string(1,'a'+x) << ") = " << res << endl;
238#endif
239
240 return res;
241}
242
243/*
244 #] content_univar :
245 #[ content_multivar :
246*/
247
263const poly polygcd::content_multivar (const poly &a, int x) {
264
265#ifdef DEBUGALL
266 cout << "*** [" << thetime() << "] CALL: content_multivar(" << a << "," << string(1,'a'+x) << ")" << endl;
267#endif
268
269 POLY_GETIDENTITY(a);
270
271 poly res(BHEAD 0, a.modp, a.modn);
272
273 for (int i=1,j; i<a[0]; i=j) {
274 poly b(BHEAD 0, a.modp, a.modn);
275
276 for (j=i; j<a[0]; j+=a[j]) {
277 bool same_powers = true;
278 for (int k=0; k<AN.poly_num_vars; k++)
279 if (k!=x && a[i+1+k]!=a[j+1+k]) {
280 same_powers = false;
281 break;
282 }
283 if (!same_powers) break;
284
285 b.check_memory(b[0]+a[j]);
286 b.termscopy(&a[j],b[0],a[j]);
287 for (int k=0; k<AN.poly_num_vars; k++)
288 if (k!=x) b[b[0]+1+k]=0;
289
290 b[0] += a[j];
291 }
292
293 res = gcd_Euclidean(res, b);
294
295 if (res.is_integer()) {
296 res = poly(BHEAD a.sign(),a.modp,a.modn);
297 break;
298 }
299 }
300
301#ifdef DEBUGALL
302 cout << "*** [" << thetime() << "] RES : content_multivar(" << a << "," << string(1,'a'+x) << ") = " << res << endl;
303#endif
304
305 return res;
306}
307
308/*
309 #] content_multivar :
310 #[ coefficient_list_gcd :
311*/
312
325const vector<WORD> polygcd::coefficient_list_gcd (const vector<WORD> &_a, const vector<WORD> &_b, WORD p) {
326
327#ifdef DEBUGALL
328 cout << "*** [" << thetime() << "] CALL: coefficient_list_gcd("<<_a<<","<<_b<<","<<p<<")"<<endl;
329#endif
330
331 vector<WORD> a(_a), b(_b);
332
333 while (b.size() != 0) {
334 a = poly::coefficient_list_divmod(a,b,p,1);
335 swap(a,b);
336 }
337
338 while (a.back()==0) a.pop_back();
339
340 WORD inv;
341 GetModInverses(a.back() + (a.back()<0?p:0), p, &inv, NULL);
342
343 for (int i=0; i<(int)a.size(); i++)
344 a[i] = (LONG)inv*a[i] % p;
345
346#ifdef DEBUGALL
347 cout << "*** [" << thetime() << "] RES : coefficient_list_gcd("<<_a<<","<<_b<<","<<p<<") = "<<a<<endl;
348#endif
349
350 return a;
351}
352
353/*
354 #] coefficient_list_gcd :
355 #[ gcd_Euclidean :
356*/
357
374const poly polygcd::gcd_Euclidean (const poly &a, const poly &b) {
375
376#ifdef DEBUG
377 cout << "*** [" << thetime() << "] CALL: gcd_Euclidean("<<a<<","<<b<<")"<<endl;
378#endif
379
380 POLY_GETIDENTITY(a);
381
382 if (a.is_zero()) return b;
383 if (b.is_zero()) return a;
384 if (a.is_integer() || b.is_integer()) return integer_gcd(a,b);
385
386 poly res(BHEAD 0);
387
388 if (a.is_dense_univariate()>=-1 && b.is_dense_univariate()>=-1) {
389 vector<WORD> coeff = coefficient_list_gcd(poly::to_coefficient_list(a),
390 poly::to_coefficient_list(b), a.modp);
391 res = poly::from_coefficient_list(BHEAD coeff, a.first_variable(), a.modp);
392 }
393 else {
394 res = a;
395 poly rem(b);
396 while (!rem.is_zero())
397 swap(res%=rem, rem);
398 res /= res.integer_lcoeff();
399 }
400
401#ifdef DEBUG
402 cout << "*** [" << thetime() << "] RES : gcd_Euclidean("<<a<<","<<b<<") = "<<res<<endl;
403#endif
404
405 return res;
406}
407
408/*
409 #] gcd_Euclidean :
410 #[ chinese_remainder :
411*/
412
426const poly polygcd::chinese_remainder (const poly &a1, const poly &m1, const poly &a2, const poly &m2) {
427
428#ifdef DEBUGALL
429 cout << "*** [" << thetime() << "] CALL: chinese_remainder(" << a1 << "," << m1 << "," << a2 << "," << m2 << ")" << endl;
430#endif
431
432 POLY_GETIDENTITY(a1);
433
434 WORD nx,ny,nz;
435 UWORD *x = (UWORD *)NumberMalloc("polygcd::chinese_remainder");
436 UWORD *y = (UWORD *)NumberMalloc("polygcd::chinese_remainder");
437 UWORD *z = (UWORD *)NumberMalloc("polygcd::chinese_remainder");
438
439 GetLongModInverses(BHEAD (UWORD *)&m1[2+AN.poly_num_vars], m1[m1[1]],
440 (UWORD *)&m2[2+AN.poly_num_vars], m2[m2[1]],
441 (UWORD *)x, &nx, NULL, NULL);
442
443 AddLong((UWORD *)&a2[2+AN.poly_num_vars], a2.is_zero() ? 0 : a2[a2[1]],
444 (UWORD *)&a1[2+AN.poly_num_vars], a1.is_zero() ? 0 : -a1[a1[1]],
445 y, &ny);
446
447 MulLong (x,nx,y,ny,z,&nz);
448 MulLong (z,nz,(UWORD *)&m1[2+AN.poly_num_vars],m1[m1[1]],x,&nx);
449
450 AddLong (x,nx,(UWORD *)&a1[2+AN.poly_num_vars], a1.is_zero() ? 0 : a1[a1[1]],y,&ny);
451
452 MulLong ((UWORD *)&m1[2+AN.poly_num_vars], m1[m1[1]],
453 (UWORD *)&m2[2+AN.poly_num_vars], m2[m2[1]],
454 (UWORD *)z,&nz);
455
456 TakeNormalModulus (y,&ny,(UWORD *)z,nz,NOUNPACK);
457
458 poly res(BHEAD y,ny);
459
460 NumberFree(x,"polygcd::chinese_remainder");
461 NumberFree(y,"polygcd::chinese_remainder");
462 NumberFree(z,"polygcd::chinese_remainder");
463
464#ifdef DEBUGALL
465 cout << "*** [" << thetime() << "] RES : chinese_remainder(" << a1 << "," << m1 << "," << a2 << "," << m2 << ") = " << res << endl;
466#endif
467
468 return res;
469}
470
471/*
472 #] chinese_remainder :
473 #[ substitute :
474*/
475
488const poly polygcd::substitute(const poly &a, int x, int c) {
489
490 POLY_GETIDENTITY(a);
491
492 poly b(BHEAD 0);
493
494 if (a.is_zero()) {
495 return b;
496 }
497
498 bool zero=true;
499 int bi=1;
500
501 // cache size is bounded by the degree in x, twice the number of terms of
502 // the polynomial and a constant
503 vector<WORD> cache(min(a.degree(x)+1,min(2*a.number_of_terms(),
504 POLYGCD_RAISPOWMOD_CACHE_SIZE)), 0);
505
506 for (int ai=1; ai<=a[0]; ai+=a[ai]) {
507 // last term or different power, then add term to b iff non-zero
508 if (!zero) {
509 bool add=false;
510 if (ai==a[0])
511 add=true;
512 else {
513 for (int i=0; i<AN.poly_num_vars; i++)
514 if (i!=x && a[ai+1+i]!=b[bi+1+i]) {
515 zero=true;
516 add=true;
517 break;
518 }
519 }
520
521 if (add) {
522 if (b[bi+AN.poly_num_vars+1] < 0)
523 b[bi+AN.poly_num_vars+1] += a.modp;
524 bi+=b[bi];
525 }
526
527 if (ai==a[0]) break;
528 }
529
530 b.check_memory(bi);
531
532 // create new term in b
533 if (zero) {
534 b[bi] = 3+AN.poly_num_vars;
535 for (int i=0; i<AN.poly_num_vars; i++)
536 b[bi+1+i] = a[ai+1+i];
537 b[bi+1+x] = 0;
538 b[bi+AN.poly_num_vars+1] = 0;
539 b[bi+AN.poly_num_vars+2] = 1;
540 }
541
542 // add term of a to the current term in b
543 LONG coeff = a[ai+1+AN.poly_num_vars] * a[ai+2+AN.poly_num_vars];
544 int pow = a[ai+1+x];
545
546 if (pow<(int)cache.size()) {
547 if (cache[pow]==0)
548 cache[pow] = RaisPowMod(c, pow, a.modp);
549 coeff = (coeff * cache[pow]) % a.modp;
550 }
551 else {
552 coeff = (coeff * RaisPowMod(c, pow, a.modp)) % a.modp;
553 }
554
555 b[bi+AN.poly_num_vars+1] = (coeff + b[bi+AN.poly_num_vars+1]) % a.modp;
556 if (b[bi+AN.poly_num_vars+1] != 0) zero=false;
557 }
558
559 b[0]=bi;
560 b.setmod(a.modp);
561
562 return b;
563}
564
565/*
566 #] substitute :
567 #[ sparse_interpolation helper functions :
568*/
569
570// Returns a list of size #terms(a) with entries PROD(ci^powi, i=2..n)
571const vector<int> polygcd::sparse_interpolation_get_mul_list (const poly &a, const vector<int> &x, const vector<int> &c) {
572 // cache size for variable x is bounded by the degree in x, twice
573 // the number of terms of the polynomial and a constant
574 vector<vector<WORD> > cache(c.size());
575 int max_cache_size = min(2*a.number_of_terms(),POLYGCD_RAISPOWMOD_CACHE_SIZE);
576 for (int i=0; i<(int)c.size(); i++)
577 cache[i] = vector<WORD>(min(a.degree(x[i+1])+1,max_cache_size), 0);
578
579 vector<int> res;
580 for (int i=1; i<a[0]; i+=a[i]) {
581 LONG coeff=1;
582 for (int j=0; j<(int)c.size(); j++) {
583 int pow = a[i+1+x[j+1]];
584 if (pow<(int)cache[j].size()) {
585 if (cache[j][pow]==0)
586 cache[j][pow] = RaisPowMod(c[j], pow, a.modp);
587 coeff = (coeff * cache[j][pow]) % a.modp;
588 }
589 else {
590 coeff = (coeff * RaisPowMod(c[j], pow, a.modp)) % a.modp;
591 }
592 }
593 res.push_back(coeff);
594 }
595 return res;
596}
597
598// Multiplies the coefficients of a with the entries of mul
599void polygcd::sparse_interpolation_mul_poly (poly &a, const vector<int> &mul) {
600 for (int i=1,j=0; i<a[0]; i+=a[i],j++)
601 a[i+a[i]-2] = ((LONG)a[i+a[i]-2]*mul[j]) % a.modp;
602}
603
604// Sets all coefficients to the range 0..modp-1 and the powers of x2...xn to 0
605const poly polygcd::sparse_interpolation_reduce_poly (const poly &a, const vector<int> &x) {
606 poly res(a);
607 for (int i=1; i<a[0]; i+=a[i]) {
608 for (int j=1; j<(int)x.size(); j++)
609 res[i+1+x[j]]=0;
610 if (res[i+a[i]-1]==-1) {
611 res[i+a[i]-1]=1;
612 res[i+a[i]-2]=a.modp-res[i+a[i]-2];
613 }
614 }
615 return res;
616}
617
618// Collects entries with equal powers, so that the result is a proper polynomial
619const poly polygcd::sparse_interpolation_fix_poly (const poly &a, int x) {
620
621 POLY_GETIDENTITY(a);
622 poly res(BHEAD 0,a.modp,1);
623
624 int j=1;
625 bool newterm=true;
626
627 for (int i=1; i<a[0]; i+=a[i]) {
628 if (newterm)
629 res.termscopy(&a[i], j, a[i]);
630 else
631 res[j+res[j]-2] = ((LONG)res[j+res[j]-2] + a[i+a[i]-2]) % a.modp;
632
633 newterm = i+a[i] == a[0] || res[j+1+x] != a[i+a[i]+1+x];
634 if (newterm && res[j+res[j]-2]!=0) j += res[j];
635 }
636
637 res[0]=j;
638 return res;
639}
640
641/*
642 #] sparse_interpolation helper functions :
643 #[ gcd_modular_sparse_interpolation :
644*/
645
677const poly polygcd::gcd_modular_sparse_interpolation (const poly &origa, const poly &origb, const vector<int> &x, const poly &s) {
678
679#ifdef DEBUG
680 cout << "*** [" << thetime() << "] CALL: gcd_modular_sparse_interpolation("
681 << origa << "," << origb << "," << x << "," << "," << s <<")" << endl;
682#endif
683
684 POLY_GETIDENTITY(origa);
685
686 // strip multivariate content
687 poly conta(content_multivar(origa,x.back()));
688 poly contb(content_multivar(origb,x.back()));
689 poly gcdconts(gcd_Euclidean(conta,contb));
690 const poly& a = conta.is_one() ? origa : origa/conta;
691 const poly& b = contb.is_one() ? origb : origb/contb;
692
693 // for non-monic cases, we need to normalize with the gcd of the lcoeffs of a poly in x[0]
694 // or else the shape fitting does not work.
695 // FIXME: the current implementation still rejects some valid shapes.
696 poly lcgcd(BHEAD 1, a.modp);
697 if (!s.lcoeff_univar(x[0]).is_integer()) {
698 lcgcd = gcd_modular_dense_interpolation(a.lcoeff_univar(x[0]), b.lcoeff_univar(x[0]), x, poly(BHEAD 0));
699 }
700
701 // reduce polynomials
702 poly ared(sparse_interpolation_reduce_poly(a,x));
703 poly bred(sparse_interpolation_reduce_poly(b,x));
704 poly sred(sparse_interpolation_reduce_poly(s,x));
705 poly lred(sparse_interpolation_reduce_poly(lcgcd,x));
706
707 // set all coefficients to 1
708 for (int i=1; i<sred[0]; i+=sred[i]) {
709 sred[i+sred[i]-2] = sred[i+sred[i]-1] = 1;
710 }
711
712 // generate random numbers and check there this set doesn't result
713 // in a singular matrix
714 vector<int> c(x.size()-1);
715 vector<int> smul;
716
717 bool duplicates;
718 do {
719 for (int i=0; i<(int)c.size(); i++)
720 c[i] = 1 + wranf(BHEAD0) % (a.modp-1);
721 smul = sparse_interpolation_get_mul_list(s,x,c);
722
723 duplicates = false;
724
725 int fr=0,to=0;
726 for (int i=1; i<s[0];) {
727 int pow = s[i+1+x[0]];
728 while (i<s[0] && s[i+1+x[0]]==pow) i+=s[i], to++;
729 for (int j=fr; j<to; j++)
730 for (int k=fr; k<j; k++)
731 if (smul[j] == smul[k])
732 duplicates = true;
733 fr=to;
734 }
735 }
736 while (duplicates);
737
738 // get the lists to multiply the polynomials with every iteration
739 vector<int> amul(sparse_interpolation_get_mul_list(a,x,c));
740 vector<int> bmul(sparse_interpolation_get_mul_list(b,x,c));
741 vector<int> lmul(sparse_interpolation_get_mul_list(lcgcd,x,c));
742
743 vector<vector<vector<LONG> > > M;
744 vector<vector<LONG> > V;
745
746 int maxMsize=0;
747
748 // create (empty) matrices
749 for (int i=1; i<s[0]; i+=s[i]) {
750 if (i==1 || s[i+1+x[0]]!=s[i+1+x[0]-s[i]]) {
751 M.push_back(vector<vector<LONG> >());
752 V.push_back(vector<LONG>());
753 }
754 M.back().push_back(vector<LONG>());
755 V.back().push_back(0);
756 maxMsize = max(maxMsize, (int)M.back().size());
757 }
758
759 // generate linear equations
760 for (int numg=0; numg<maxMsize; numg++) {
761
762 poly amodI(sparse_interpolation_fix_poly(ared,x[0]));
763 poly bmodI(sparse_interpolation_fix_poly(bred,x[0]));
764 poly lmodI(sparse_interpolation_fix_poly(lred,x[0]));
765
766 // A fix for non-monic gcds. This could be slow if lmodI has many terms,
767 // since it overfits the gcd now. Another gcd has to be run to remove the
768 // extra terms.
769 poly gcd(lmodI * gcd_Euclidean(amodI,bmodI));
770
771 // if correct gcd
772 if (!gcd.is_zero() && gcd[2+x[0]]==sred[2+x[0]]) {
773
774 // for each power in the gcd, generate an equation if needed
775 int gi=1, midx=0;
776
777 for (int si=1; si<s[0];) {
778 // if the term exists, set Vi=coeff, otherwise Vi remains 0
779 if (gi<gcd[0] && gcd[gi+1+x[0]]==sred[si+1+x[0]]) {
780 if (numg < (int)V[midx].size())
781 V[midx][numg] = gcd[gi+gcd[gi]-1]*gcd[gi+gcd[gi]-2];
782 gi += gcd[gi];
783 }
784
785 // add the coefficients of s to the matrix M
786 for (int i=0; i<(int)M[midx].size(); i++) {
787 if (numg < (int)M[midx].size())
788 M[midx][numg].push_back(sred[si+1+AN.poly_num_vars]);
789 si += s[si];
790 }
791
792 midx++;
793 }
794 }
795 else {
796 // incorrect gcd
797 if (!gcd.is_zero() && gcd[2+x[0]]<sred[2+x[0]])
798 return poly(BHEAD 0);
799 numg--;
800 }
801
802 // multiply polynomials by the lists to obtain new ones
803 sparse_interpolation_mul_poly(ared,amul);
804 sparse_interpolation_mul_poly(bred,bmul);
805 sparse_interpolation_mul_poly(sred,smul);
806 sparse_interpolation_mul_poly(lred,lmul);
807 }
808
809 // solve the linear equations
810 for (int i=0; i<(int)M.size(); i++) {
811 int n = M[i].size();
812
813 // Gaussian elimination
814 for (int j=0; j<n; j++) {
815 for (int k=0; k<j; k++) {
816 LONG x = M[i][j][k];
817 for (int l=k; l<n; l++)
818 M[i][j][l] = (M[i][j][l] - M[i][k][l]*x) % a.modp;
819 V[i][j] = (V[i][j] - V[i][k]*x) % a.modp;
820 }
821
822 // normalize row
823 WORD x = M[i][j][j]; // WORD for GetModInverses
824 GetModInverses(x + (x<0?a.modp:0), a.modp, &x, NULL);
825 for (int k=0; k<n; k++)
826 M[i][j][k] = (M[i][j][k]*x) % a.modp;
827 V[i][j] = (V[i][j]*x) % a.modp;
828 }
829
830 // solve
831 for (int j=n-1; j>=0; j--)
832 for (int k=j+1; k<n; k++)
833 V[i][j] = (V[i][j] - M[i][j][k]*V[i][k]) % a.modp;
834 }
835
836 // create coefficient list
837 vector<LONG> coeff;
838 for (int i=0; i<(int)V.size(); i++)
839 for (int j=0; j<(int)V[i].size(); j++)
840 coeff.push_back(V[i][j]);
841
842 // create resulting polynomial
843 poly res(BHEAD 0);
844 int ri=1, i=0;
845 for (int si=1; si<s[0]; si+=s[si]) {
846 res.check_memory(ri);
847 res[ri] = 3 + AN.poly_num_vars; // term length
848 for (int j=0; j<AN.poly_num_vars; j++)
849 res[ri+1+j] = s[si+1+j]; // powers
850 res[ri+1+AN.poly_num_vars] = ABS(coeff[i]); // coefficient
851 res[ri+2+AN.poly_num_vars] = SGN(coeff[i]); // coefficient length
852 i++;
853 ri += res[ri];
854 }
855 res[0]=ri; // total length
856 res.setmod(a.modp,1);
857
858 if (!poly::divides(res, lcgcd * a) || !poly::divides(res, lcgcd * b)) {
859 return poly(BHEAD 0); // bad shape
860 } else {
861 // refine gcd
862 if (!poly::divides(res, a))
863 res = gcd_modular_dense_interpolation(res, a, x, poly(BHEAD 0));
864 if (!poly::divides(res, b))
865 res = gcd_modular_dense_interpolation(res, b, x, poly(BHEAD 0));
866 }
867
868#ifdef DEBUG
869 cout << "*** [" << thetime() << "] RES : gcd_modular_sparse_interpolation("
870 << a << "," << b << "," << x << "," << "," << s <<") = " << res << endl;
871#endif
872
873 return gcdconts * res;
874}
875
876/*
877 #] gcd_modular_sparse_interpolation :
878 #[ gcd_modular_dense_interpolation :
879*/
880
899const poly polygcd::gcd_modular_dense_interpolation (const poly &a, const poly &b, const vector<int> &x, const poly &s) {
900
901#ifdef DEBUG
902 cout << "*** [" << thetime() << "] CALL: gcd_modular_dense_interpolation(" << a << "," << b << "," << x << "," << s <<")" << endl;
903#endif
904
905 POLY_GETIDENTITY(a);
906
907 // if univariate, then use Euclidean algorithm
908 if (x.size() == 1) {
909 return gcd_Euclidean(a,b);
910 }
911
912 // if shape is known, use sparse interpolation
913 if (!s.is_zero()) {
914 poly res = gcd_modular_sparse_interpolation (a,b,x,s);
915 if (!res.is_zero()) return res;
916 // apparently the shape was not correct. continue.
917 }
918
919 // divide out multivariate content in last variable
920 int X = x.back();
921
922 poly conta(content_multivar(a,X));
923 poly contb(content_multivar(b,X));
924 poly gcdconts(gcd_Euclidean(conta,contb));
925 const poly& ppa = conta.is_one() ? a : poly(a/conta);
926 const poly& ppb = contb.is_one() ? b : poly(b/contb);
927
928 // gcd of leading coefficients
929 poly lcoeffa(ppa.lcoeff_multivar(X));
930 poly lcoeffb(ppb.lcoeff_multivar(X));
931 poly gcdlcoeffs(gcd_Euclidean(lcoeffa,lcoeffb));
932
933 // calculate the degree bound for each variable
934 int m = MiN(ppa.degree(x[x.size() - 2]),ppb.degree(x[x.size() - 2]));
935
936 poly res(BHEAD 0);
937 poly oldres(BHEAD 0);
938 poly newshape(BHEAD 0);
939 poly modpoly(BHEAD 1,a.modp);
940
941 while (true) {
942 // generate random constants and substitute it
943 int c = 1 + wranf(BHEAD0) % (a.modp-1);
944 if (substitute(gcdlcoeffs,X,c).is_zero()) continue;
945 if (substitute(modpoly,X,c).is_zero()) continue;
946
947 poly amodc(substitute(ppa,X,c));
948 poly bmodc(substitute(ppb,X,c));
949
950 // calculate gcd recursively
951 poly gcdmodc(gcd_modular_dense_interpolation(amodc,bmodc,vector<int>(x.begin(),x.end()-1), newshape));
952 int n = gcdmodc.degree(x[x.size() - 2]);
953
954 // normalize
955 gcdmodc = (gcdmodc * substitute(gcdlcoeffs,X,c)) / gcdmodc.integer_lcoeff();
956 poly simple(poly::simple_poly(BHEAD X,c,1,a.modp)); // (X-c) mod p
957
958 // if power is smaller, the old one was wrong
959 if ((res.is_zero() && n == m) || n < m) {
960 m = n;
961 res = gcdmodc;
962 newshape = gcdmodc; // set a new shape (interpolation does not change it)
963 modpoly = simple;
964 }
965 else if (n == m) {
966 oldres = res;
967 // equal powers, so interpolate results
968 poly coeff_poly(substitute(modpoly,X,c));
969 WORD coeff_word = coeff_poly[2+AN.poly_num_vars] * coeff_poly[3+AN.poly_num_vars];
970 if (coeff_word < 0) coeff_word += a.modp;
971
972 GetModInverses(coeff_word, a.modp, &coeff_word, NULL);
973
974 res.setmod(a.modp); // make sure the mod is set before substituting
975 res += poly(BHEAD coeff_word, a.modp, 1) * modpoly * (gcdmodc - substitute(res,X,c));
976 modpoly *= simple;
977 }
978
979 // check whether this is the complete gcd
980 if (!res.is_zero() && res == oldres) {
981 poly nres = res / content_multivar(res, X);
982 if (poly::divides(nres,ppa) && poly::divides(nres,ppb)) {
983#ifdef DEBUG
984 cout << "*** [" << thetime() << "] RES : gcd_modular_dense_interpolation(" << a << "," << b << ","
985 << x << "," << "," << s <<") = " << gcdconts * nres << endl;
986#endif
987 return gcdconts * nres;
988 }
989
990 // At this point, the gcd may be too large due to bad luck
991 // TODO: create an efficient fail state that tries to find a smaller
992 // polynomial without interpolating bad ones?
993 newshape = poly(BHEAD 0); // reset the shape, important!
994 }
995 }
996}
997
998/*
999 #] gcd_modular_dense_interpolation : :
1000 #[ gcd_modular :
1001*/
1002
1019const poly polygcd::gcd_modular (const poly &origa, const poly &origb, const vector<int> &x) {
1020
1021#ifdef DEBUG
1022 cout << "*** [" << thetime() << "] CALL: gcd_modular(" << origa << "," << origb << "," << x << ")" << endl;
1023#endif
1024
1025 POLY_GETIDENTITY(origa);
1026
1027 if (origa.is_zero()) return origa.modp==0 ? origb : origb / origb.integer_lcoeff();
1028 if (origb.is_zero()) return origa.modp==0 ? origa : origa / origa.integer_lcoeff();
1029 if (origa==origb) return origa.modp==0 ? origa : origa / origa.integer_lcoeff();
1030
1031 poly ac = integer_content(origa);
1032 poly bc = integer_content(origb);
1033 const poly& a = ac.is_one() ? origa : poly(origa/ac);
1034 const poly& b = bc.is_one() ? origb : poly(origb/bc);
1035 poly ic = integer_gcd(ac, bc);
1036 poly g = integer_gcd(a.integer_lcoeff(), b.integer_lcoeff());
1037
1038 int pnum=0;
1039
1040 poly d(BHEAD 0);
1041 poly m1(BHEAD 1);
1042 int mindeg=MAXPOSITIVE;
1043
1044 while (true) {
1045 // choose a prime and solve modulo the prime
1046 WORD p = NextPrime(BHEAD pnum++);
1047 if (poly(a.integer_lcoeff(),p).is_zero()) continue;
1048 if (poly(b.integer_lcoeff(),p).is_zero()) continue;
1049
1050 poly c(gcd_modular_dense_interpolation(poly(a,p),poly(b,p),x,poly(d,p)));
1051 c = (c * poly(g,p)) / c.integer_lcoeff(); // normalize so that lcoeff(c) = g mod p
1052
1053 if (c.is_zero()) {
1054 // unlucky choices somewhere, so start all over again
1055 d = poly(BHEAD 0);
1056 m1 = poly(BHEAD 1);
1057 mindeg = MAXPOSITIVE;
1058 continue;
1059 }
1060
1061 if (!(poly(a,p)%c).is_zero()) continue;
1062 if (!(poly(b,p)%c).is_zero()) continue;
1063
1064 int deg = c.degree(x[0]);
1065
1066 if (deg < mindeg) {
1067 // small degree, so the old one is wrong
1068 d=c;
1069 d.modp=a.modp;
1070 d.modn=a.modn;
1071 m1 = poly(BHEAD p);
1072 mindeg=deg;
1073 }
1074 else if (deg == mindeg) {
1075 // same degree, so use Chinese Remainder Algorithm
1076 poly newd(BHEAD 0);
1077
1078 for (int ci=1,di=1; ci<c[0]||di<d[0]; ) {
1079 int comp = ci==c[0] ? -1 : di==d[0] ? +1 : poly::monomial_compare(BHEAD &c[ci],&d[di]);
1080 poly a1(BHEAD 0),a2(BHEAD 0);
1081
1082 newd.check_memory(newd[0]);
1083
1084 if (comp <= 0) {
1085 newd.termscopy(&d[di],newd[0],1+AN.poly_num_vars);
1086 a1 = poly(BHEAD (UWORD *)&d[di+1+AN.poly_num_vars],d[di+d[di]-1]);
1087 di+=d[di];
1088 }
1089 if (comp >= 0) {
1090 newd.termscopy(&c[ci],newd[0],1+AN.poly_num_vars);
1091 a2 = poly(BHEAD (UWORD *)&c[ci+1+AN.poly_num_vars],c[ci+c[ci]-1]);
1092 ci+=c[ci];
1093 }
1094
1095 poly e(chinese_remainder(a1,m1,a2,poly(BHEAD p)));
1096 newd.termscopy(&e[2+AN.poly_num_vars], newd[0]+1+AN.poly_num_vars, ABS(e[e[1]])+1);
1097 newd[newd[0]] = 2 + AN.poly_num_vars + ABS(e[e[1]]);
1098 newd[0] += newd[newd[0]];
1099 }
1100
1101 m1 *= poly(BHEAD p);
1102 d=newd;
1103 }
1104
1105 // divide out spurious integer content
1106 poly ppd(d / integer_content(d));
1107
1108 // check whether this is the complete gcd
1109 if (poly::divides(ppd,a) && poly::divides(ppd,b)) {
1110#ifdef DEBUG
1111 cout << "*** [" << thetime() << "] RES : gcd_modular(" << origa << "," << origb << "," << x << ") = "
1112 << ic * ppd << endl;
1113#endif
1114 return ic * ppd;
1115 }
1116#ifdef DEBUG
1117 cout << "*** [" << thetime() << "] Retrying modular_gcd with new prime" << endl;
1118#endif
1119 }
1120}
1121
1122/*
1123 #] gcd_modular :
1124 #[ gcd_heuristic_possible :
1125*/
1126
1143
1144 POLY_GETIDENTITY(a);
1145
1146 double prod_deg = 1;
1147 for (int j=0; j<AN.poly_num_vars; j++)
1148 prod_deg *= a[2+j]+1;
1149
1150 double digits = ABS(a[1+a[1]-1]);
1151 double lead = a[1+1+AN.poly_num_vars];
1152
1153 return prod_deg*(digits-1+log(2*ABS(lead))/log(2.0)/(BITSINWORD/2)) < POLYGCD_HEURISTIC_MAX_DIGITS;
1154}
1155
1156/*
1157 #] gcd_heuristic_possible :
1158 #[ gcd_heuristic :
1159*/
1160
1161
1188const poly polygcd::gcd_heuristic (const poly &a, const poly &b, const vector<int> &x, int max_tries) {
1189
1190#ifdef DEBUG
1191 cout << "*** [" << thetime() << "] CALL: gcd_heuristic("<<a<<","<<b<<","<<x<<")\n";
1192#endif
1193
1194 if (a.is_integer()) return integer_gcd(a,integer_content(b));
1195 if (b.is_integer()) return integer_gcd(integer_content(a),b);
1196
1197 POLY_GETIDENTITY(a);
1198
1199 // Calculate xi = 2*min(max(coefficients a),max(coefficients b))+2
1200
1201 UWORD *pxi=NULL;
1202 WORD nxi=0;
1203
1204 for (int i=1; i<a[0]; i+=a[i]) {
1205 WORD na = ABS(a[i+a[i]-1]);
1206 if (BigLong((UWORD *)&a[i+a[i]-1-na], na, pxi, nxi) > 0) {
1207 pxi = (UWORD *)&a[i+a[i]-1-na];
1208 nxi = na;
1209 }
1210 }
1211
1212 for (int i=1; i<b[0]; i+=b[i]) {
1213 WORD nb = ABS(b[i+b[i]-1]);
1214 if (BigLong((UWORD *)&b[i+b[i]-1-nb], nb, pxi, nxi) > 0) {
1215 pxi = (UWORD *)&b[i+b[i]-1-nb];
1216 nxi = nb;
1217 }
1218 }
1219
1220 poly xi(BHEAD pxi,nxi);
1221
1222 // Addition of another random factor gives better performance
1223 xi = xi*poly(BHEAD 2) + poly(BHEAD 2 + wranf(BHEAD0)%POLYGCD_HEURISTIC_MAX_ADD_RANDOM);
1224
1225 // If degree*digits(xi) is too large, throw exception
1226 if (max(a.degree(x[0]),b.degree(x[0])) * xi[xi[1]] >= MiN(AM.MaxTal, POLYGCD_HEURISTIC_MAX_DIGITS)) {
1227#ifdef DEBUG
1228 cout << "*** [" << thetime() << "] RES : gcd_heuristic("<<a<<","<<b<<","<<x<<") = overflow\n";
1229#endif
1230 throw(gcd_heuristic_failed());
1231 }
1232
1233 for (int times=0; times<max_tries; times++) {
1234
1235 // Recursively calculate the gcd
1236
1237 poly gamma(gcd_heuristic(a % poly::simple_poly(BHEAD x[0],xi),
1238 b % poly::simple_poly(BHEAD x[0],xi),
1239 vector<int>(x.begin()+1,x.end()),1));
1240
1241 // If a gcd is found, reconstruct the powers of x
1242 if (!gamma.is_zero()) {
1243 // res is construct is reverse order. idx/len are for reversing
1244 // it in the correct order
1245 poly res(BHEAD 0), c(BHEAD 0);
1246 vector<int> idx, len;
1247
1248 for (int power=0; !gamma.is_zero(); power++) {
1249
1250 // calculate c = gamma % xi (c and gamma are polynomials, xi is integer)
1251 c = gamma;
1252 c.coefficients_modulo((UWORD *)&xi[2+AN.poly_num_vars], xi[xi[0]-1], false);
1253
1254 // Add the terms c * x^power to res
1255 res.check_memory(res[0]+c[0]);
1256 res.termscopy(&c[1],res[0],c[0]-1);
1257 for (int i=1; i<c[0]; i+=c[i])
1258 res[res[0]-1+i+1+x[0]] = power;
1259
1260 // Store idx/len for reversing
1261 if (!c.is_zero()) {
1262 idx.push_back(res[0]);
1263 len.push_back(c[0]-1);
1264 }
1265
1266 res[0] += c[0]-1;
1267
1268 // Divide gamma by xi
1269 gamma = (gamma - c) / xi;
1270 }
1271
1272 // Reverse the resulting polynomial
1273 poly rev(BHEAD 0);
1274 rev.check_memory(res[0]);
1275
1276 rev[0] = 1;
1277 for (int i=idx.size()-1; i>=0; i--) {
1278 rev.termscopy(&res[idx[i]], rev[0], len[i]);
1279 rev[0] += len[i];
1280 }
1281 res = rev;
1282
1283 poly ppres = res / integer_content(res);
1284
1285 if ((a%ppres).is_zero() && (b%ppres).is_zero()) {
1286#ifdef DEBUG
1287 cout << "*** [" << thetime() << "] RES : gcd_heuristic("<<a<<","<<b<<","<<x<<") = "<<res<<"\n";
1288#endif
1289 return res;
1290 }
1291 }
1292
1293 // Next xi by multiplying with the golden ratio to avoid correlated errors
1294 xi = xi * poly(BHEAD 28657) / poly(BHEAD 17711) + poly(BHEAD wranf(BHEAD0) % POLYGCD_HEURISTIC_MAX_ADD_RANDOM);
1295 }
1296
1297#ifdef DEBUG
1298 cout << "*** [" << thetime() << "] RES : gcd_heuristic("<<a<<","<<b<<","<<x<<") = failed\n";
1299#endif
1300
1301 return poly(BHEAD 0);
1302}
1303
1304/*
1305 #] gcd_heuristic :
1306 #[ bracket :
1307*/
1308
1309const map<vector<int>,poly> polygcd::full_bracket(const poly &a, const vector<int>& filter) {
1310 POLY_GETIDENTITY(a);
1311
1312 map<vector<int>,poly> bracket;
1313 for (int ai=1; ai<a[0]; ai+=a[ai]) {
1314 vector<int> varpattern(AN.poly_num_vars);
1315 for (int i=0; i<AN.poly_num_vars; i++)
1316 if (filter[i] == 1 && a[ai + i + 1] > 0)
1317 varpattern[i] = a[ai + i + 1];
1318
1319 // create monomial
1320 poly mon(BHEAD 1);
1321 mon.setmod(a.modp);
1322 mon[0] = a[ai] + 1;
1323 for (int i=0; i<a[ai]; i++)
1324 if (i > 0 && i <= AN.poly_num_vars && varpattern[i - 1])
1325 mon[1+i] = 0;
1326 else
1327 mon[1+i] = a[ai+i];
1328
1329 map<vector<int>,poly>::iterator i = bracket.find(varpattern);
1330 if (i == bracket.end()) {
1331 bracket.insert(std::make_pair(varpattern, mon));
1332 } else {
1333 i->second += mon;
1334 }
1335 }
1336
1337 return bracket;
1338}
1339
1340const poly polygcd::bracket(const poly &a, const vector<int>& pattern, const vector<int>& filter) {
1341 POLY_GETIDENTITY(a);
1342
1343 poly bracket(BHEAD 0);
1344 for (int ai=1; ai<a[0]; ai+=a[ai]) {
1345 bool ispat = true;
1346 for (int i=0; i<AN.poly_num_vars; i++)
1347 if (filter[i] == 1 && pattern[i] != a[ai + i + 1]) {
1348 ispat = false;
1349 break;
1350 }
1351
1352 if (ispat) {
1353 poly mon(BHEAD 1);
1354 mon.setmod(a.modp);
1355 mon[0] = a[ai] + 1;
1356 for (int i=0; i<a[ai]; i++)
1357 if (i > 0 && i <= AN.poly_num_vars && pattern[i - 1])
1358 mon[1+i] = 0;
1359 else
1360 mon[1+i] = a[ai+i];
1361 bracket += mon;
1362 }
1363 }
1364
1365 return bracket;
1366}
1367
1368const map<vector<int>,int> polygcd::bracket_count(const poly &a, const vector<int>& filter) {
1369 POLY_GETIDENTITY(a);
1370
1371 map<vector<int>,int> bracket;
1372 for (int ai=1; ai<a[0]; ai+=a[ai]) {
1373 vector<int> varpattern(AN.poly_num_vars);
1374 for (int i=0; i<AN.poly_num_vars; i++)
1375 if (filter[i] == 1 && a[ai + i + 1] > 0)
1376 varpattern[i] = a[ai + i + 1];
1377
1378 map<vector<int>,int>::iterator i = bracket.find(varpattern);
1379 if (i == bracket.end()) {
1380 bracket.insert(std::make_pair(varpattern, 0));
1381 } else {
1382 i->second++;
1383 }
1384 }
1385
1386 return bracket;
1387}
1388
1390 std::vector<int> pattern;
1391 int num_terms, dummy = 0;
1392 const poly* p;
1393
1394 BracketInfo(const std::vector<int>& pattern, int num_terms, const poly* p) : pattern(pattern), num_terms(num_terms), p(p) {}
1395 bool operator<(const BracketInfo& rhs) const { return num_terms > rhs.num_terms; } // biggest should be first!
1396};
1397
1398/*
1399 #] bracket :
1400 #[ gcd_linear:
1401*/
1402
1403const poly gcd_linear_helper (const poly &a, const poly &b) {
1404 POLY_GETIDENTITY(a);
1405
1406 for (int i = 0; i < AN.poly_num_vars; i++)
1407 if (a.degree(i) == 1) {
1408 vector<int> filter(AN.poly_num_vars);
1409 filter[i] = 1;
1410
1411 // bracket the linear variable
1412 map<vector<int>,poly> ba = polygcd::full_bracket(a, filter);
1413
1414 poly subgcd(BHEAD 1);
1415 if (ba.size() == 2)
1416 subgcd = polygcd::gcd_linear(ba.begin()->second, (++ba.begin())->second);
1417 else
1418 subgcd = ba.begin()->second;
1419
1420 poly linfac = a / subgcd;
1421 if (poly::divides(linfac,b))
1422 return linfac * polygcd::gcd_linear(subgcd, b / linfac);
1423
1424 return polygcd::gcd_linear(subgcd, b);
1425 }
1426
1427 return poly(BHEAD 0);
1428}
1429
1434const poly polygcd::gcd_linear (const poly &a, const poly &b) {
1435#ifdef DEBUG
1436 cout << "*** [" << thetime() << "] CALL: gcd_linear("<<a<<","<<b<<")\n";
1437#endif
1438
1439 POLY_GETIDENTITY(a);
1440
1441 if (a.is_zero()) return a.modp==0 ? b : b / b.integer_lcoeff();
1442 if (b.is_zero()) return a.modp==0 ? a : a / a.integer_lcoeff();
1443 if (a==b) return a.modp==0 ? a : a / a.integer_lcoeff();
1444
1445 if (a.is_integer() || b.is_integer()) {
1446 if (a.modp > 0) return poly(BHEAD 1,a.modp,a.modn);
1447 return poly(integer_gcd(integer_content(a),integer_content(b)),0,1);
1448 }
1449
1450 poly h = gcd_linear_helper(a, b);
1451 if (!h.is_zero()) return h;
1452
1453 h = gcd_linear_helper(b, a);
1454 if (!h.is_zero()) return h;
1455
1456 vector<int> xa = a.all_variables();
1457 vector<int> xb = b.all_variables();
1458
1459 vector<int> used(AN.poly_num_vars,0);
1460 for (int i=0; i<(int)xa.size(); i++) used[xa[i]]++;
1461 for (int i=0; i<(int)xb.size(); i++) used[xb[i]]++;
1462 vector<int> x;
1463 for (int i=0; i<AN.poly_num_vars; i++)
1464 if (used[i]) x.push_back(i);
1465
1466 return gcd_modular(a,b,x);
1467}
1468
1469/*
1470 #] gcd_linear:
1471 #[ gcd :
1472*/
1473
1494const poly polygcd::gcd (const poly &a, const poly &b) {
1495
1496#ifdef DEBUG
1497 cout << "*** [" << thetime() << "] CALL: gcd("<<a<<","<<b<<")\n";
1498#endif
1499
1500 POLY_GETIDENTITY(a);
1501
1502 if (a.is_zero()) return a.modp==0 ? b : b / b.integer_lcoeff();
1503 if (b.is_zero()) return a.modp==0 ? a : a / a.integer_lcoeff();
1504 if (a==b) return a.modp==0 ? a : a / a.integer_lcoeff();
1505
1506 if (a.is_integer() || b.is_integer()) {
1507 if (a.modp > 0) return poly(BHEAD 1,a.modp,a.modn);
1508 return poly(integer_gcd(integer_content(a),integer_content(b)),0,1);
1509 }
1510
1511 // Generate a list of variables of a and b
1512 vector<int> xa = a.all_variables();
1513 vector<int> xb = b.all_variables();
1514
1515 vector<int> used(AN.poly_num_vars,0);
1516 for (int i=0; i<(int)xa.size(); i++) used[xa[i]]++;
1517 for (int i=0; i<(int)xb.size(); i++) used[xb[i]]++;
1518 vector<int> x;
1519 for (int i=0; i<AN.poly_num_vars; i++)
1520 if (used[i]) x.push_back(i);
1521
1522 if (a.is_monomial() || b.is_monomial()) {
1523
1524 poly res(BHEAD 1,a.modp,a.modn);
1525 if (a.modp == 0) res = integer_gcd(integer_content(a),integer_content(b));
1526
1527 for (int i=0; i<(int)x.size(); i++)
1528 res[2+x[i]] = 1<<(BITSINWORD-2);
1529
1530 for (int i=1; i<a[0]; i+=a[i])
1531 for (int j=0; j<(int)x.size(); j++)
1532 res[2+x[j]] = MiN(res[2+x[j]], a[i+1+x[j]]);
1533
1534 for (int i=1; i<b[0]; i+=b[i])
1535 for (int j=0; j<(int)x.size(); j++)
1536 res[2+x[j]] = MiN(res[2+x[j]], b[i+1+x[j]]);
1537
1538 return res;
1539 }
1540
1541 // Calculate the contents, their gcd and the primitive parts
1542 poly conta(x.size()==1 ? integer_content(a) : content_univar(a,x[0]));
1543 poly contb(x.size()==1 ? integer_content(b) : content_univar(b,x[0]));
1544 poly gcdconts(x.size()==1 ? integer_gcd(conta,contb) : gcd(conta,contb));
1545 const poly& ppa = conta.is_one() ? a : poly(a/conta);
1546 const poly& ppb = contb.is_one() ? b : poly(b/contb);
1547
1548 if (ppa == ppb)
1549 return ppa * gcdconts;
1550
1551 poly res(BHEAD 0);
1552
1553#ifdef POLYGCD_USE_HEURISTIC
1554 // Try the heuristic gcd algorithm
1555 if (a.modp==0 && gcd_heuristic_possible(a) && gcd_heuristic_possible(b)) {
1556 try {
1557 res = gcd_heuristic(ppa,ppb,x);
1558 if (!res.is_zero()) res /= integer_content(res);
1559 }
1560 catch (gcd_heuristic_failed) {}
1561 }
1562#endif
1563
1564 // If gcd==0, the heuristic algorithm failed, so we do more extensive checks.
1565 // First, we filter out variables that appear in only one of the expressions.
1566 if (res.is_zero()) {
1567 bool unusedVars = false;
1568 for (unsigned int i = 0; i < used.size(); i++) {
1569 if (used[i] == 1) {
1570 unusedVars = true;
1571 break;
1572 }
1573 }
1574
1575 // if there are no unused variables, go to the linear routine directly
1576 if (!unusedVars) {
1577 res = gcd_linear(ppa,ppb);
1578#ifdef DEBUG
1579 cout << "New GCD attempt (unused vars): " << res << endl;
1580#endif
1581 }
1582
1583 // if res is not the gcd, it is 0 or larger than the gcd.
1584 // we bracket the expression in all the variables that appear only in one expr.
1585 // and we refine the gcd.
1586 bool diva = !res.is_zero() && poly::divides(res,ppa);
1587 bool divb = !res.is_zero() && poly::divides(res,ppb);
1588 if (!diva || !divb) {
1589 vector<BracketInfo> bracketinfo;
1590
1591 if (!diva) {
1592 map<vector<int>,int> ba = bracket_count(ppa, used);
1593 for(map<vector<int>,int>::iterator it = ba.begin(); it != ba.end(); it++)
1594 bracketinfo.push_back(BracketInfo(it->first, it->second, &ppa));
1595 }
1596
1597 if (!divb) {
1598 map<vector<int>,int> bb = bracket_count(ppb, used);
1599 for(map<vector<int>,int>::iterator it = bb.begin(); it != bb.end(); it++)
1600 bracketinfo.push_back(BracketInfo(it->first, it->second, &ppb));
1601 }
1602
1603 // sort so that the smallest bracket will be last
1604 sort(bracketinfo.begin(), bracketinfo.end());
1605
1606 if (res.is_zero()) {
1607 res = bracket(*bracketinfo.back().p, bracketinfo.back().pattern, used);
1608 bracketinfo.pop_back();
1609 }
1610
1611 while (bracketinfo.size() > 0) {
1612 poly subpoly(bracket(*bracketinfo.back().p, bracketinfo.back().pattern, used));
1613 if (!poly::divides(res,subpoly)) {
1614 // if we can filter out more variables, call gcd again
1615 if (res.all_variables() != subpoly.all_variables())
1616 res = gcd(subpoly,res);
1617 else
1618 res = gcd_linear(subpoly,res);
1619 }
1620
1621 bracketinfo.pop_back();
1622 }
1623 }
1624
1625 if (res.is_zero() || !poly::divides(res,ppa) || !poly::divides(res,ppb)) {
1626 MesPrint("Bad gcd found.");
1627 std::cout << "Bad gcd:" << res << " for " << ppa << " " << ppb << std::endl;
1628 Terminate(1);
1629 }
1630 }
1631
1632 res *= gcdconts * poly(BHEAD res.sign());
1633
1634#ifdef DEBUG
1635 cout << "*** [" << thetime() << "] RES : gcd("<<a<<","<<b<<") = "<<res<<endl;
1636#endif
1637
1638 return res;
1639}
1640
1641/*
1642 #] gcd :
1643*/
Definition poly.h:53
int is_dense_univariate() const
Definition poly.cc:2284
WORD NextPrime(PHEAD WORD)
Definition reken.c:3665
int GetModInverses(WORD, WORD, WORD *, WORD *)
Definition reken.c:1477
bool gcd_heuristic_possible(const poly &a)
Definition polygcd.cc:1142