ezgcdf.red 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. module ezgcdf; % Polynomial GCD algorithms.
  2. % Author: A. C. Norman, 1981.
  3. fluid '(!*exp
  4. !*gcd
  5. !*heugcd
  6. !*overview
  7. !*trfac
  8. alphalist
  9. bad!-case
  10. best!-known!-factors
  11. current!-modulus
  12. dmode!*
  13. factor!-level
  14. factor!-trace!-list
  15. full!-gcd
  16. hensel!-growth!-size
  17. image!-factors
  18. image!-set
  19. irreducible
  20. kord!*
  21. m!-image!-variable
  22. multivariate!-factors
  23. multivariate!-input!-poly
  24. non!-monic
  25. no!-of!-primes!-to!-try
  26. number!-of!-factors
  27. prime!-base
  28. reconstructing!-gcd
  29. reduced!-degree!-lclst
  30. reduction!-count
  31. target!-factor!-count
  32. true!-leading!-coeffts
  33. unlucky!-case);
  34. global '(erfg!*);
  35. symbolic procedure ezgcdf(u,v);
  36. % Entry point for REDUCE call in GCDF. We have to make sure that
  37. % the kernel order is correct if an error occurs in ezgcdf1.
  38. begin scalar erfgx,kordx,x;
  39. erfgx := erfg!*;
  40. kordx := kord!*;
  41. x := errorset2{'ezgcdf1,mkquote u,mkquote v};
  42. if null errorp x then return first x;
  43. % If ezgcdf fails, erfg!* can be set to t,
  44. % (e.g., in invlap(c/(p^3/8-9p^2/4+27/2*p-27)^2,p,t)), and
  45. % the kernel order not properly reset.
  46. erfg!* := erfgx;
  47. setkorder kordx;
  48. return gcdf1(u,v)
  49. end;
  50. symbolic procedure ezgcdf1(u,v);
  51. % Entry point for REDUCE call in GCDF.
  52. poly!-abs gcdlist list(u,v) where factor!-level=0;
  53. %symbolic procedure simpezgcd u;
  54. % calculate the gcd of the polynomials given as arguments;
  55. % begin
  56. % scalar factor!-level,w;
  57. % factor!-level:=0;
  58. % u := for each p in u collect <<
  59. % w := simp!* p;
  60. % if (denr w neq 1) then
  61. % rederr "EZGCD requires polynomial arguments";
  62. % numr w >>;
  63. % return (poly!-abs gcdlist u) ./ 1
  64. % end;
  65. %put('ezgcd,'simpfn,'simpezgcd);
  66. symbolic procedure simpnprimitive p;
  67. % Remove any simple numeric factors from the expression P;
  68. begin
  69. scalar np,dp;
  70. if atom p or not atom cdr p then
  71. rerror(ezgcd,2,"NPRIMITIVE requires just one argument");
  72. p := simp!* car p;
  73. if polyzerop(numr p) then return nil ./ 1;
  74. np := quotfail(numr p,numeric!-content numr p);
  75. dp := quotfail(denr p,numeric!-content denr p);
  76. return (np ./ dp)
  77. end;
  78. put('nprimitive,'simpfn,'simpnprimitive);
  79. symbolic procedure poly!-gcd(u,v);
  80. % U and V are standard forms.
  81. % Value is the gcd of U and V.
  82. begin scalar !*exp,z;
  83. if polyzerop u then return poly!-abs v
  84. else if polyzerop v then return poly!-abs u
  85. else if u=1 or v=1 then return 1;
  86. !*exp := t;
  87. % The case of one argument exactly dividing the other is
  88. % detected specially here because it is perhaps a fairly
  89. % common circumstance.
  90. if quotf1(u,v) then z := v
  91. else if quotf1(v,u) then z := u
  92. else if !*gcd then z := gcdlist list(u,v)
  93. else z := 1;
  94. return poly!-abs z
  95. end;
  96. % moved('gcdf,'poly!-gcd);
  97. symbolic procedure ezgcd!-comfac p;
  98. %P is a standard form
  99. %CAR of result is lowest common power of leading kernel in
  100. %every term in P (or NIL). CDR is gcd of all coefficients of
  101. %powers of leading kernel;
  102. if domainp p then nil . poly!-abs p
  103. else if null red p then lpow p . poly!-abs lc p
  104. else begin
  105. scalar power,coeflist,var;
  106. % POWER will be the first part of the answer returned,
  107. % COEFLIST will collect a list of all coefs in the polynomial
  108. % P viewed as a poly in its main variable,
  109. % VAR is the main variable concerned;
  110. var := mvar p;
  111. while mvar p=var and not domainp red p do <<
  112. coeflist := lc p . coeflist;
  113. p:=red p >>;
  114. if mvar p=var then <<
  115. coeflist := lc p . coeflist;
  116. if null red p then power := lpow p
  117. else coeflist := red p . coeflist >>
  118. else coeflist := p . coeflist;
  119. return power . gcdlist coeflist
  120. end;
  121. symbolic procedure gcd!-with!-number(n,a);
  122. % n is a number, a is a polynomial - return their gcd, given that
  123. % n is non-zero;
  124. if n=1 or not atom n or flagp(dmode!*,'field) then 1
  125. else if domainp a
  126. then if a=nil then abs n
  127. else if not atom a then 1
  128. else gcddd(n,a)
  129. else gcd!-with!-number(gcd!-with!-number(n,lc a),red a);
  130. % moved('gcdfd,'gcd!-with!-number);
  131. symbolic procedure contents!-with!-respect!-to(p,v);
  132. if domainp p then nil . poly!-abs p
  133. else if mvar p=v then ezgcd!-comfac p
  134. else begin
  135. scalar w,y;
  136. y := updkorder v;
  137. w := ezgcd!-comfac reorder p;
  138. setkorder y;
  139. return w
  140. end;
  141. symbolic procedure numeric!-content form;
  142. % Find numeric content of non-zero polynomial.
  143. if domainp form then absf form
  144. else if null red form then numeric!-content lc form
  145. else begin
  146. scalar g1;
  147. g1 := numeric!-content lc form;
  148. if not (g1=1) then g1 := gcddd(g1,numeric!-content red form);
  149. return g1
  150. end;
  151. symbolic procedure gcdlist l;
  152. % Return the GCD of all the polynomials in the list L.
  153. %
  154. % First find all variables mentioned in the polynomials in L,
  155. % and remove monomial content from them all. If in the process
  156. % a constant poly is found, take special action. If then there
  157. % is some variable that is mentioned in all the polys in L, and
  158. % which occurs only linearly in one of them establish that as
  159. % main variable and proceed to GCDLIST3 (which will take
  160. % a special case exit). Otherwise, if there are any variables that
  161. % do not occur in all the polys in L they can not occur in the GCD,
  162. % so take coefficients with respect to them to get a longer list of
  163. % smaller polynomials - restart. Finally we have a set of polys
  164. % all involving exactly the same set of variables;
  165. if null l then nil
  166. else if null cdr l then poly!-abs car l
  167. else if domainp car l then gcdld(cdr l,car l)
  168. else begin
  169. scalar l1,gcont,x;
  170. % Copy L to L1, but on the way detect any domain elements
  171. % and deal with them specially;
  172. while not null l do <<
  173. if null car l then l := cdr l
  174. else if domainp car l then <<
  175. l1 := list list gcdld(cdr l,gcdld(mapcarcar l1,car l));
  176. l := nil >>
  177. else <<
  178. l1 := (car l . powers1 car l) . l1;
  179. l := cdr l >> >>;
  180. if null l1 then return nil
  181. else if null cdr l1 then return poly!-abs caar l1;
  182. % Now L1 is a list where each polynomial is paired with information
  183. % about the powers of variables in it;
  184. gcont := nil; % Compute monomial content on things in L;
  185. x := nil; % First time round flag;
  186. l := for each p in l1 collect begin
  187. scalar gcont1,gcont2,w;
  188. % Set GCONT1 to least power information, and W to power
  189. % difference;
  190. w := for each y in cdr p
  191. collect << gcont1 := (car y . cddr y) . gcont1;
  192. car y . (cadr y-cddr y) >>;
  193. % Now get the monomial content as a standard form (in GCONT2);
  194. gcont2 := numeric!-content car p;
  195. if null x then << gcont := gcont1; x := gcont2 >>
  196. else << gcont := vintersection(gcont,gcont1);
  197. % Accumulate monomial gcd;
  198. x := gcddd(x,gcont2) >>;
  199. for each q in gcont1 do if not(cdr q=0) then
  200. gcont2 := multf(gcont2,!*p2f mksp(car q,cdr q));
  201. return quotfail1(car p,gcont2,"Term content division failed")
  202. . w
  203. end;
  204. % Here X is the numeric part of the final GCD.
  205. for each q in gcont do x := multf(x,!*p2f mksp(car q,cdr q));
  206. % trace!-time <<
  207. % prin2!* "Term gcd = ";
  208. % printsf x >>;
  209. return poly!-abs multf(x,gcdlist1 l)
  210. end;
  211. symbolic procedure gcdlist1 l;
  212. % Items in L are monomial-primitive, and paired with power information.
  213. % Find out what variables are common to all polynomials in L and
  214. % remove all others;
  215. begin
  216. scalar unionv,intersectionv,vord,x,l1,reduction!-count;
  217. unionv := intersectionv := cdar l;
  218. for each p in cdr l do <<
  219. unionv := vunion(unionv,cdr p);
  220. intersectionv := vintersection(intersectionv,cdr p) >>;
  221. if null intersectionv then return 1;
  222. for each v in intersectionv do
  223. unionv := vdelete(v,unionv);
  224. % Now UNIONV is list of those variables mentioned that
  225. % are not common to all polynomials;
  226. intersectionv := sort(intersectionv,function lesspcdr);
  227. if cdar intersectionv=1 then <<
  228. % I have found something that is linear in one of its variables;
  229. vord := mapcarcar append(intersectionv,unionv);
  230. l1 := setkorder vord;
  231. % trace!-time <<
  232. % prin2 "Selecting "; prin2 caar intersectionv;
  233. % prin2t " as main because some poly is linear in it" >>;
  234. x := gcdlist3(for each p in l collect reorder car p,nil,vord);
  235. setkorder l1;
  236. return reorder x >>
  237. else if null unionv then return gcdlist2(l,intersectionv);
  238. % trace!-time <<
  239. % prin2 "The variables "; prin2 unionv; prin2t " can be removed" >>;
  240. vord := setkorder mapcarcar append(unionv,intersectionv);
  241. l1 := nil;
  242. for each p in l do
  243. l1:=split!-wrt!-variables(reorder car p,mapcarcar unionv,l1);
  244. setkorder vord;
  245. return gcdlist1(for each p in l1 collect
  246. (reorder p . total!-degree!-in!-powers(p,nil)))
  247. end;
  248. symbolic procedure gcdlist2(l,vars);
  249. % Here all the variables in VARS are used in every polynomial
  250. % in L. Select a good variable ordering;
  251. begin
  252. scalar x,x1,gg,lmodp,onestep,vord,oldmod,image!-set,gcdpow,
  253. unlucky!-case;
  254. % In the univariate case I do not need to think very hard about
  255. % the selection of a main variable!! ;
  256. if null cdr vars
  257. then return if !*heugcd and (x := heu!-gcd!-list(mapcarcar l))
  258. then x
  259. else gcdlist3(mapcarcar l,nil,list caar vars);
  260. oldmod := set!-modulus nil;
  261. % If some variable appears at most to degree two in some pair of the
  262. % polynomials then that will do as a main variable. Note that this is
  263. % not so useful if the two polynomials happen to be duplicates of each
  264. % other, but still... ;
  265. vars := mapcarcar sort(vars,function greaterpcdr);
  266. % Vars is now arranged with the variable that appears to highest
  267. % degree anywhere in L first, and the rest in descending order;
  268. l := for each p in l collect car p .
  269. sort(cdr p,function lesspcdr);
  270. l := sort(l,function lesspcdadr);
  271. % Each list of degree information in L is sorted with lowest degree
  272. % vars first, and the polynomial with the lowest degree variable
  273. % of all will come first;
  274. x := intersection(deg2vars(cdar l),deg2vars(cdadr l));
  275. if not null x then <<
  276. % trace!-time << prin2 "Two inputs are at worst quadratic in ";
  277. % prin2t car x >>;
  278. go to x!-to!-top >>; % Here I have found two polys with a common
  279. % variable that they are quadratic in;
  280. % Now generate modular images of the gcd to guess its degree wrt
  281. % all possible variables;
  282. % If either (a) modular gcd=1 or (b) modular gcd can be computed with
  283. % just 1 reduction step, use that information to choose a main variable;
  284. try!-again: % Modular images may be degenerate;
  285. set!-modulus random!-prime();
  286. unlucky!-case := nil;
  287. image!-set := for each v in vars
  288. collect (v . modular!-number next!-random!-number());
  289. % trace!-time <<
  290. % prin2 "Select variable ordering using P=";
  291. % prin2 current!-modulus;
  292. % prin2 " and substitutions from ";
  293. % prin2t image!-set >>;
  294. x1 := vars;
  295. try!-vars:
  296. if null x1 then go to images!-tried;
  297. lmodp := for each p in l collect make!-image!-mod!-p(car p,car x1);
  298. if unlucky!-case then go to try!-again;
  299. lmodp := sort(lmodp,function lesspdeg);
  300. gg := gcdlist!-mod!-p(car lmodp,cdr lmodp);
  301. if domainp gg or (reduction!-count<2 and (onestep:=t)) then <<
  302. % trace!-time << prin2 "Select "; prin2t car x1 >>;
  303. x := list car x1; go to x!-to!-top >>;
  304. gcdpow := (car x1 . ldeg gg) . gcdpow;
  305. x1 := cdr x1;
  306. go to try!-vars;
  307. images!-tried:
  308. % In default of anything better to do, use image variable such that
  309. % degree of gcd wrt it is as large as possible;
  310. vord := mapcarcar sort(gcdpow,function greaterpcdr);
  311. % trace!-time << prin2 "Select order by degrees: ";
  312. % prin2t gcdpow >>;
  313. go to order!-chosen;
  314. x!-to!-top:
  315. for each v in x do vars := delete(v,vars);
  316. vord := append(x,vars);
  317. order!-chosen:
  318. % trace!-time << prin2 "Selected Var order = "; prin2t vord >>;
  319. set!-modulus oldmod;
  320. vars := setkorder vord;
  321. x := gcdlist3(for each p in l collect reorder car p,onestep,vord);
  322. setkorder vars;
  323. return reorder x
  324. end;
  325. symbolic procedure gcdlist!-mod!-p(gg,l);
  326. if null l then gg
  327. else if gg=1 then 1
  328. else gcdlist!-mod!-p(gcd!-mod!-p(gg,car l),cdr l);
  329. symbolic procedure deg2vars l;
  330. if null l then nil
  331. else if cdar l>2 then nil
  332. else caar l . deg2vars cdr l;
  333. symbolic procedure vdelete(a,b);
  334. if null b then nil
  335. else if car a=caar b then cdr b
  336. else car b . vdelete(a,cdr b);
  337. symbolic procedure vintersection(a,b);
  338. begin
  339. scalar c;
  340. return if null a then nil
  341. else if null (c:=assoc(caar a,b)) then vintersection(cdr a,b)
  342. else if cdar a>cdr c then
  343. if cdr c=0 then vintersection(cdr a,b)
  344. else c . vintersection(cdr a,b)
  345. else if cdar a=0 then vintersection(cdr a,b)
  346. else car a . vintersection(cdr a,b)
  347. end;
  348. symbolic procedure vunion(a,b);
  349. begin
  350. scalar c;
  351. return if null a then b
  352. else if null (c:=assoc(caar a,b)) then car a . vunion(cdr a,b)
  353. else if cdar a>cdr c then car a . vunion(cdr a,delete(c,b))
  354. else c . vunion(cdr a,delete(c,b))
  355. end;
  356. symbolic procedure mapcarcar l;
  357. for each x in l collect car x;
  358. symbolic procedure gcdld(l,n);
  359. % GCD of the domain element N and all the polys in L;
  360. if n=1 or n=-1 then 1
  361. else if l=nil then abs n
  362. else if car l=nil then gcdld(cdr l,n)
  363. else gcdld(cdr l,gcd!-with!-number(n,car l));
  364. symbolic procedure split!-wrt!-variables(p,vl,l);
  365. % Push all the coeffs in P wrt variables in VL onto the list L
  366. % Stop if 1 is found as a coeff;
  367. if p=nil then l
  368. else if not null l and car l=1 then l
  369. else if domainp p then abs p . l
  370. else if member(mvar p,vl) then
  371. split!-wrt!-variables(red p,vl,split!-wrt!-variables(lc p,vl,l))
  372. else p . l;
  373. symbolic procedure gcdlist3(l,onestep,vlist);
  374. % GCD of the nontrivial polys in the list L given that they all
  375. % involve all the variables that any of them mention,
  376. % and they are all monomial-primitive.
  377. % ONESTEP is true if it is predicted that only one PRS step
  378. % will be needed to compute the gcd - if so try that PRS step.
  379. begin
  380. scalar unlucky!-case,image!-set,gg,gcont,l1,w,w1,w2,
  381. reduced!-degree!-lclst,p1,p2;
  382. % Make all the polys primitive;
  383. l1:=for each p in l collect p . ezgcd!-comfac p;
  384. l:=for each c in l1 collect
  385. quotfail1(car c,comfac!-to!-poly cdr c,
  386. "Content divison in GCDLIST3 failed");
  387. % All polys in L are now primitive.
  388. % Because all polys were monomial-primitive, there should
  389. % be no power of V to go in the result.
  390. gcont:=gcdlist for each c in l1 collect cddr c;
  391. if domainp gcont then if not(gcont=1)
  392. then errorf "GCONT has numeric part";
  393. % GCD of contents complete now;
  394. % Now I will remove duplicates from the list;
  395. % trace!-time <<
  396. % prin2t "GCDLIST3 on the polynomials";
  397. % for each p in l do print p >>;
  398. l := sort(for each p in l collect poly!-abs p,function ordp);
  399. w := nil;
  400. while l do <<
  401. w := car l . w;
  402. repeat l := cdr l until null l or not(car w = car l)>>;
  403. l := reversip w;
  404. w := nil;
  405. % trace!-time <<
  406. % prin2t "Made positive, with duplicates removed...";
  407. % for each p in l do print p >>;
  408. if null cdr l then return multf(gcont,car l);
  409. % That left just one poly;
  410. if domainp (gg:=car (l:=sort(l,function degree!-order))) then
  411. return gcont;
  412. % Primitive part of one poly is a constant (must be +/-1);
  413. if ldeg gg=1 then <<
  414. % True gcd is either GG or 1;
  415. if division!-test(gg,l) then return multf(poly!-abs gg,gcont)
  416. else return gcont >>;
  417. % All polys are now primitive and nontrivial. Use a modular
  418. % method to extract GCD;
  419. if onestep then <<
  420. % Try to take gcd in just one pseudoremainder step, because some
  421. % previous modular test suggests it may be possible;
  422. p1 := poly!-abs car l; p2 := poly!-abs cadr l;
  423. % Because polynomials are primitive and they have been normalised
  424. % wrt sign the only way that just one PRS step could lead to zero
  425. % would be if the two polys are identical. In which case that
  426. % should be the GCD. Note that because I got to gcdlist3 at all
  427. % both polys should use (all of) the same set of variables, and
  428. % in particular should have the same main variable.
  429. if p1=p2 then <<
  430. if division!-test(p1,cddr l) then return multf(p1,gcont)>>
  431. else <<
  432. % trace!-time prin2t "Just one pseudoremainder step needed?";
  433. gg := poly!-gcd(lc p1,lc p2);
  434. w1 := multf(red p1, quotfail1(lc p2, gg,
  435. "Division failure when just one pseudoremainder step needed"));
  436. w2 := multf(red p2,negf quotfail1(lc p1, gg,
  437. "Division failure when just one pseudoremainder step needed"));
  438. w := ldeg p1 - ldeg p2;
  439. if w > 0 then w2 := multf(w2, (mksp(mvar p2, w) .* 1) .+ nil)
  440. else if w < 0
  441. then w1 := multf(w1, (mksp(mvar p1, -w) .* 1) .+ nil);
  442. gg := ezgcd!-pp addf(w1, w2);
  443. % trace!-time printsf gg;
  444. if division!-test(gg,l) then return multf(gg,gcont) >>>>;
  445. return gcdlist31(l,vlist,gcont,gg,l1)
  446. end;
  447. symbolic procedure gcdlist31(l,vlist,gcont,gg,l1);
  448. begin scalar cofactor,lcg,old!-modulus,prime,w,w1,zeros!-list;
  449. old!-modulus:=set!-modulus nil; %Remember modulus;
  450. lcg:=for each poly in l collect lc poly;
  451. % trace!-time << prin2t "L.C.S OF L ARE:";
  452. % for each lcpoly in lcg do printsf lcpoly >>;
  453. lcg:=gcdlist lcg;
  454. % trace!-time << prin2!* "LCG (=GCD OF THESE) = ";
  455. % printsf lcg >>;
  456. try!-again:
  457. unlucky!-case:=nil;
  458. image!-set:=nil;
  459. set!-modulus(prime:=random!-prime());
  460. % Produce random univariate modular images of all the
  461. % polynomials;
  462. w:=l;
  463. if not zeros!-list then <<
  464. image!-set:=
  465. zeros!-list:=try!-max!-zeros!-for!-image!-set(w,vlist);
  466. % trace!-time << prin2t image!-set;
  467. % prin2 " Zeros-list = ";
  468. % prin2t zeros!-list >>
  469. >>;
  470. % trace!-time prin2t list("IMAGE SET",image!-set);
  471. gg:=make!-image!-mod!-p(car w,car vlist);
  472. % trace!-time prin2t list("IMAGE SET",image!-set," GG",gg);
  473. if unlucky!-case then <<
  474. % trace!-time << prin2t "Unlucky case, try again";
  475. % print image!-set >>;
  476. go to try!-again >>;
  477. l1:=list(car w . gg);
  478. make!-images:
  479. if null (w:=cdr w) then go to images!-created!-successfully;
  480. l1:=(car w . make!-image!-mod!-p(car w,car vlist)) . l1;
  481. if unlucky!-case then <<
  482. % trace!-time << prin2t "UNLUCKY AGAIN...";
  483. % prin2t l1;
  484. % print image!-set >>;
  485. go to try!-again >>;
  486. gg:=gcd!-mod!-p(gg,cdar l1);
  487. if domainp gg then <<
  488. set!-modulus old!-modulus;
  489. % trace!-time print "Primitive parts are coprime";
  490. return gcont >>;
  491. go to make!-images;
  492. images!-created!-successfully:
  493. l1:=reversip l1; % Put back in order with smallest first;
  494. % If degree of gcd seems to be same as that of smallest item
  495. % in input list, that item should be the gcd;
  496. if ldeg gg=ldeg car l then <<
  497. gg:=poly!-abs car l;
  498. % trace!-time <<
  499. % prin2!* "Probable GCD = ";
  500. % printsf gg >>;
  501. go to result >>
  502. else if (ldeg car l=add1 ldeg gg) and
  503. (ldeg car l=ldeg cadr l) then <<
  504. % Here it seems that I have just one pseudoremainder step to
  505. % perform, so I might as well do it;
  506. % trace!-time <<
  507. % prin2t "Just one pseudoremainder step needed"
  508. % >>;
  509. gg := poly!-gcd(lc car l,lc cadr l);
  510. gg := ezgcd!-pp addf(multf(red car l,
  511. quotfail1(lc cadr l,gg,
  512. "Division failure when just one pseudoremainder step needed")),
  513. multf(red cadr l,negf quotfail1(lc car l,gg,
  514. "Divison failure when just one pseudoremainder step needed")));
  515. % trace!-time printsf gg;
  516. go to result >>;
  517. w:=l1;
  518. find!-good!-cofactor:
  519. if null w then go to special!-case; % No good cofactor available;
  520. if domainp gcd!-mod!-p(gg,cofactor:=quotient!-mod!-p(cdar w,gg))
  521. then go to good!-cofactor!-found;
  522. w:=cdr w;
  523. go to find!-good!-cofactor;
  524. good!-cofactor!-found:
  525. cofactor:=monic!-mod!-p cofactor;
  526. % trace!-time prin2t "*** Good cofactor found";
  527. w:=caar w;
  528. % trace!-time << prin2!* "W= ";
  529. % printsf w;
  530. % prin2!* "GG= ";
  531. % printsf gg;
  532. % prin2!* "COFACTOR= ";
  533. % printsf cofactor >>;
  534. image!-set:=sort(image!-set,function ordopcar);
  535. % trace!-time << prin2 "IMAGE-SET = ";
  536. % prin2t image!-set;
  537. % prin2 "PRIME= "; prin2t prime;
  538. % prin2t "L (=POLYLIST) IS:";
  539. % for each ll in l do printsf ll >>;
  540. gg:=reconstruct!-gcd(w,gg,cofactor,prime,image!-set,lcg);
  541. if gg='nogood then go to try!-again;
  542. go to result;
  543. special!-case: % Here I have to do the first step of a PRS method;
  544. % trace!-time << prin2t "*** SPECIAL CASE IN GCD ***";
  545. % prin2t l;
  546. % prin2t "----->";
  547. % prin2t gg >>;
  548. reduced!-degree!-lclst:=nil;
  549. try!-reduced!-degree!-again:
  550. % trace!-time << prin2t "L1 =";
  551. % for each ell in l1 do print ell >>;
  552. w1:=reduced!-degree(caadr l1,caar l1);
  553. w:=car w1; w1:=cdr w1;
  554. if not domainp w and
  555. (domainp w1 or ldeg w neq ldeg w1) then go to try!-again;
  556. % trace!-time << prin2 "REDUCED!-DEGREE = "; printsf w;
  557. % prin2 " and its image = "; printsf w1 >>;
  558. % reduce the degree of the 2nd poly using the 1st. Result is
  559. % a pair : (new poly . image new poly);
  560. if domainp w and not null w then <<
  561. set!-modulus old!-modulus; return gcont >>;
  562. % we're done as they're coprime;
  563. if w and ldeg w = ldeg gg then <<
  564. gg:=w; go to result >>;
  565. % possible gcd;
  566. if null w then <<
  567. % the first poly divided the second one;
  568. l1:=(car l1 . cddr l1); % discard second poly;
  569. if null cdr l1 then <<
  570. gg := poly!-abs caar l1;
  571. go to result >>;
  572. go to try!-reduced!-degree!-again >>;
  573. % haven't made progress yet so repeat with new polys;
  574. if ldeg w<=ldeg gg then <<
  575. gg := poly!-abs w;
  576. go to result >>
  577. else if domainp gcd!-mod!-p(gg,cofactor:=quotient!-mod!-p(w1,gg))
  578. then <<
  579. w := list list w;
  580. go to good!-cofactor!-found >>;
  581. l1:= if ldeg w <= ldeg caar l1 then
  582. ((w . w1) . (car l1 . cddr l1))
  583. else (car l1 . ((w . w1) . cddr l1));
  584. % replace first two polys by the reduced poly and the first
  585. % poly ordering according to degree;
  586. go to try!-reduced!-degree!-again;
  587. % need to repeat as we still haven't found a good cofactor;
  588. result: % Here GG holds a tentative gcd for the primitive parts of
  589. % all input polys, and GCONT holds a proper one for the content;
  590. if division!-test(gg,l) then <<
  591. set!-modulus old!-modulus;
  592. return multf(gg,gcont) >>;
  593. % trace!-time prin2t list("Trial division by ",gg," failed");
  594. go to try!-again
  595. end;
  596. symbolic procedure make!-a!-list!-of!-variables l;
  597. begin scalar vlist;
  598. for each ll in l do vlist:=variables!.in!.form(ll,vlist);
  599. return make!-order!-consistent(vlist,kord!*)
  600. end;
  601. symbolic procedure make!-order!-consistent(l,m);
  602. % L is a subset of M. Make its order consistent with that
  603. % of M;
  604. if null l then nil
  605. else if null m then errorf("Variable missing from KORD*")
  606. else if car m member l then car m .
  607. make!-order!-consistent(delete(car m,l),cdr m)
  608. else make!-order!-consistent(l,cdr m);
  609. symbolic procedure try!-max!-zeros!-for!-image!-set(l,vlist);
  610. if null vlist then error(50,"VLIST NOT SET IN TRY-MAX-ZEROS-...")
  611. else begin scalar z;
  612. z:=for each v in cdr vlist collect
  613. if domainp lc car l or null quotf(lc car l,!*k2f v) then
  614. (v . 0) else (v . modular!-number next!-random!-number());
  615. for each ff in cdr l do
  616. z:=for each w in z collect
  617. if zerop cdr w then
  618. if domainp lc ff or null quotf(lc ff,!*k2f car w) then w
  619. else (car w . modular!-number next!-random!-number())
  620. else w;
  621. return z
  622. end;
  623. symbolic procedure
  624. reconstruct!-gcd(full!-poly,gg,cofactor,p,imset,lcg);
  625. if null addf(full!-poly,negf multf(gg,cofactor)) then gg
  626. else (lambda factor!-level;
  627. begin scalar number!-of!-factors,image!-factors,
  628. true!-leading!-coeffts,multivariate!-input!-poly,
  629. no!-of!-primes!-to!-try,
  630. irreducible,non!-monic,bad!-case,target!-factor!-count,
  631. multivariate!-factors,hensel!-growth!-size,alphalist,
  632. best!-known!-factors,prime!-base,
  633. m!-image!-variable, reconstructing!-gcd,full!-gcd;
  634. if not(current!-modulus=p) then
  635. errorf("GCDLIST HAS NOT RESTORED THE MODULUS");
  636. % *WARNING* GCDLIST does not restore the modulus so
  637. % I had better reset it here! ;
  638. if poly!-minusp lcg then error(50,list("Negative GCD: ",lcg));
  639. full!-poly:=poly!-abs full!-poly;
  640. initialise!-hensel!-fluids(full!-poly,gg,cofactor,p,lcg);
  641. % trace!-time << prin2t "TRUE LEADING COEFFTS ARE:";
  642. % for i:=1:2 do <<
  643. % printsf getv(image!-factors,i);
  644. % prin2!* " WITH L.C.:";
  645. % printsf getv(true!-leading!-coeffts,i) >> >>;
  646. if determine!-more!-coeffts()='done then
  647. return full!-gcd;
  648. if null alphalist then alphalist:=alphas(2,
  649. list(getv(image!-factors,1),getv(image!-factors,2)),1);
  650. if alphalist='factors! not! coprime then
  651. errorf list("image factors not coprime?",image!-factors);
  652. if not !*overview then factor!-trace <<
  653. printstr
  654. "The following modular polynomials are chosen such that:";
  655. terpri();
  656. prin2!* " a(2)*f(1) + a(1)*f(2) = 1 mod ";
  657. printstr hensel!-growth!-size;
  658. terpri();
  659. printstr " where degree of a(1) < degree of f(1),";
  660. printstr " and degree of a(2) < degree of f(2),";
  661. printstr " and";
  662. for i:=1:2 do <<
  663. prin2!* " a("; prin2!* i; prin2!* ")=";
  664. printsf cdr get!-alpha getv(image!-factors,i);
  665. prin2!* "and f("; prin2!* i; prin2!* ")=";
  666. printsf getv(image!-factors,i);
  667. terpri!* t >>
  668. >>;
  669. reconstruct!-multivariate!-factors(
  670. for each v in imset collect (car v . modular!-number cdr v));
  671. if irreducible or bad!-case then return 'nogood
  672. else return full!-gcd
  673. end) (factor!-level+1) ;
  674. symbolic procedure initialise!-hensel!-fluids(fpoly,fac1,fac2,p,lcf1);
  675. % ... ;
  676. begin scalar lc1!-image,lc2!-image;
  677. reconstructing!-gcd:=t;
  678. multivariate!-input!-poly:=multf(fpoly,lcf1);
  679. no!-of!-primes!-to!-try := 5;
  680. prime!-base:=hensel!-growth!-size:=p;
  681. number!-of!-factors:=2;
  682. lc1!-image:=make!-numeric!-image!-mod!-p lcf1;
  683. lc2!-image:=make!-numeric!-image!-mod!-p lc fpoly;
  684. % Neither of the above leading coefficients will vanish;
  685. fac1:=times!-mod!-p(lc1!-image,fac1);
  686. fac2:=times!-mod!-p(lc2!-image,fac2);
  687. image!-factors:=mkvect 2;
  688. true!-leading!-coeffts:=mkvect 2;
  689. putv(image!-factors,1,fac1);
  690. putv(image!-factors,2,fac2);
  691. putv(true!-leading!-coeffts,1,lcf1);
  692. putv(true!-leading!-coeffts,2,lc fpoly);
  693. % If the GCD is going to be monic, we know the lc
  694. % of both cofactors exactly;
  695. non!-monic:=not(lcf1=1);
  696. m!-image!-variable:=mvar fpoly
  697. end;
  698. symbolic procedure division!-test(gg,l);
  699. % Predicate to test if GG divides all the polynomials in the list L;
  700. if null l then t
  701. else if null quotf(car l,gg) then nil
  702. else division!-test(gg,cdr l);
  703. symbolic procedure degree!-order(a,b);
  704. % Order standard forms using their degrees wrt main vars;
  705. if domainp a then t
  706. else if domainp b then nil
  707. else ldeg a<ldeg b;
  708. symbolic procedure make!-image!-mod!-p(p,v);
  709. % Form univariate image, set UNLUCKY!-CASE if leading coefficient
  710. % gets destroyed;
  711. begin
  712. scalar lp;
  713. lp := degree!-in!-variable(p,v);
  714. p := make!-univariate!-image!-mod!-p(p,v);
  715. if not(degree!-in!-variable(p,v)=lp) then unlucky!-case := t;
  716. return p
  717. end;
  718. symbolic procedure make!-univariate!-image!-mod!-p(p,v);
  719. % Make a modular image of P, keeping only the variable V;
  720. if domainp p then
  721. if p=nil then nil
  722. else !*n2f modular!-number p
  723. else if mvar p=v then
  724. adjoin!-term(lpow p,
  725. make!-univariate!-image!-mod!-p(lc p,v),
  726. make!-univariate!-image!-mod!-p(red p,v))
  727. else plus!-mod!-p(
  728. times!-mod!-p(image!-of!-power(mvar p,ldeg p),
  729. make!-univariate!-image!-mod!-p(lc p,v)),
  730. make!-univariate!-image!-mod!-p(red p,v));
  731. symbolic procedure image!-of!-power(v,n);
  732. begin
  733. scalar w;
  734. w := assoc(v,image!-set);
  735. if null w then <<
  736. w := modular!-number next!-random!-number();
  737. image!-set := (v . w) . image!-set >>
  738. else w := cdr w;
  739. return modular!-expt(w,n)
  740. end;
  741. symbolic procedure make!-numeric!-image!-mod!-p p;
  742. % Make a modular image of P;
  743. if domainp p then
  744. if p=nil then 0
  745. else modular!-number p
  746. else modular!-plus(
  747. modular!-times(image!-of!-power(mvar p,ldeg p),
  748. make!-numeric!-image!-mod!-p lc p),
  749. make!-numeric!-image!-mod!-p red p);
  750. symbolic procedure total!-degree!-in!-powers(form,powlst);
  751. % Returns a list where each variable mentioned in FORM is paired
  752. % with the maximum degree it has. POWLST collects the list, and should
  753. % normally be NIL on initial entry;
  754. if null form or domainp form then powlst
  755. else begin scalar x;
  756. if (x := atsoc(mvar form,powlst))
  757. then ldeg form>cdr x and rplacd(x,ldeg form)
  758. else powlst := (mvar form . ldeg form) . powlst;
  759. return total!-degree!-in!-powers(red form,
  760. total!-degree!-in!-powers(lc form,powlst))
  761. end;
  762. symbolic procedure powers1 form;
  763. % For each variable V in FORM collect (V . (MAX . MIN)) where
  764. % MAX and MIN are limits to the degrees V has in FORM;
  765. powers2(form,powers3(form,nil),nil);
  766. symbolic procedure powers3(form,l);
  767. % Start of POWERS1 by collecting power information for
  768. % the leading monomial in FORM;
  769. if domainp form then l
  770. else powers3(lc form,(mvar form . (ldeg form . ldeg form)) . l);
  771. symbolic procedure powers2(form,powlst,thismonomial);
  772. if domainp form then
  773. if null form then powlst else powers4(thismonomial,powlst)
  774. else powers2(lc form,
  775. powers2(red form,powlst,thismonomial),
  776. lpow form . thismonomial);
  777. symbolic procedure powers4(new,old);
  778. % Merge information from new monomial into old information,
  779. % updating MAX and MIN details;
  780. if null new then for each v in old collect (car v . (cadr v . 0))
  781. else if null old then for each v in new collect (car v . (cdr v . 0))
  782. else if caar new=caar old then <<
  783. % variables match - do MAX and MIN on degree information;
  784. if cdar new>cadar old then rplaca(cdar old,cdar new);
  785. if cdar new<cddar old then rplacd(cdar old,cdar new);
  786. rplacd(old,powers4(cdr new,cdr old)) >>
  787. else if ordop(caar new,caar old) then <<
  788. rplacd(cdar old,0); % Some variable not mentioned in new monomial;
  789. rplacd(old,powers4(new,cdr old)) >>
  790. else (caar new . (cdar new . 0)) . powers4(cdr new,old);
  791. symbolic procedure ezgcd!-pp u;
  792. %returns the primitive part of the polynomial U wrt leading var;
  793. quotf1(u,comfac!-to!-poly ezgcd!-comfac u);
  794. symbolic procedure ezgcd!-sqfrf p;
  795. %P is a primitive standard form;
  796. %value is a list of square free factors;
  797. begin
  798. scalar pdash,p1,d,v;
  799. pdash := diff(p,v := mvar p);
  800. d := poly!-gcd(p,pdash); % p2*p3**2*p4**3*... ;
  801. if domainp d then return list p;
  802. p := quotfail1(p,d,"GCD division in FACTOR-SQFRF failed");
  803. p1 := poly!-gcd(p,
  804. addf(quotfail1(pdash,d,"GCD division in FACTOR-SQFRF failed"),
  805. negf diff(p,v)));
  806. return p1 . ezgcd!-sqfrf d
  807. end;
  808. symbolic procedure reduced!-degree(u,v);
  809. %U and V are primitive polynomials in the main variable VAR;
  810. %result is pair: (reduced poly of U by V . its image) where by
  811. % reduced I mean using V to kill the leading term of U;
  812. begin scalar var,w,x;
  813. % trace!-time << prin2t "ARGS FOR REDUCED!-DEGREE ARE:";
  814. % printsf u; printsf v >>;
  815. if u=v or quotf1(u,v) then return (nil . nil)
  816. else if ldeg v=1 then return (1 . 1);
  817. % trace!-time prin2t "CASE NON-TRIVIAL SO TAKE A REDUCED!-DEGREE:";
  818. var := mvar u;
  819. if ldeg u=ldeg v then x := negf lc u
  820. else x:=(mksp(var,ldeg u - ldeg v) .* negf lc u) .+ nil;
  821. w:=addf(multf(lc v,u),multf(x,v));
  822. % trace!-time printsf w;
  823. if degr(w,var)=0 then return (1 . 1);
  824. % trace!-time << prin2 "REDUCED!-DEGREE-LCLST = ";
  825. % print reduced!-degree!-lclst >>;
  826. reduced!-degree!-lclst := addlc(v,reduced!-degree!-lclst);
  827. % trace!-time << prin2 "REDUCED!-DEGREE-LCLST = ";
  828. % print reduced!-degree!-lclst >>;
  829. if x := quotf1(w,lc w) then w := x
  830. else for each y in reduced!-degree!-lclst do
  831. while (x := quotf1(w,y)) do w := x;
  832. u := v; v := ezgcd!-pp w;
  833. % trace!-time << prin2t "U AND V ARE NOW:";
  834. % printsf u; printsf v >>;
  835. if degr(v,var)=0 then return (1 . 1)
  836. else return (v . make!-univariate!-image!-mod!-p(v,var))
  837. end;
  838. % moved('comfac,'ezgcd!-comfac);
  839. % moved('pp,'ezgcd!-pp);
  840. endmodule;
  841. end;