heugcd.red 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. module heugcd;
  2. %Authors: James Davenport & Julian Padget
  3. % For full details of the algorithms see first Char et al. from the
  4. % Proceedings of EUROSAM 84 (Springer LNCS #174), then Davenport and
  5. % Padget in the Proceedings of EUROCAL 85 (Springer LNCS #204) and
  6. % Davenport and Padget in the proceedings of Calcul Formel (Homage a
  7. % Noel Gastinel) published by Masson-Wiley (France).
  8. %exports heu!-gcd, heu!-gcd!-list;
  9. %imports to be determined
  10. %internal-functions
  11. % univariatep, univariatep1, htc, kontent, kontent1,
  12. % horner!-eval!-rat, horner!-eval!-rat!-and!-gcdl,
  13. % horner!-eval!-rat!-and!-gcdl1, heu!-quotfl, heu!-quotfl1,
  14. % heu!-quotf, xceiling, next!-even!-value, next!-odd!-value,
  15. % heu!-gcdl, analyse!-polynomials, negshiftz, gen!-poly,
  16. % gen!-poly!-forward, gen!-poly!-backward, gcdlist2, gcdf2
  17. fluid '(!*heugcd reduction!-count);
  18. global '(ee);
  19. % ****************** Various polynomial utilities **********************
  20. symbolic smacro procedure univariatep p; univariatep1(p,mvar p);
  21. symbolic procedure univariatep1(p,v);
  22. % checks that p is univariate in v;
  23. if atom p then t
  24. else if mvar p neq v then nil
  25. else if atom lc p then univariatep1(red p,v)
  26. else nil;
  27. symbolic procedure htc p;
  28. if atom p then p
  29. else if null red p then lc p
  30. else htc red p;
  31. symbolic procedure kontent p;
  32. % extract integer content of polynomial p
  33. if domainp p then
  34. if numberp p then p
  35. else if null p then 1
  36. else rederr "HEUGCD(kontent): unsupported domain element"
  37. else if domainp red p then
  38. if numberp red p then gcdn(lc p,red p)
  39. else if null red p then lc p
  40. else rederr "HEUGCD(kontent): unsupported domain element"
  41. else kontent1(red red p,gcdn(lc p,lc red p));
  42. symbolic procedure kontent1(p,a);
  43. if a=1 then 1
  44. else if domainp p then
  45. if numberp p then gcdn(p,a)
  46. else if null p then a
  47. else rederr "HEUGCD(kontent1): unsupported domain element"
  48. else kontent1(red p,gcdn(remainder(lc p,a),a));
  49. symbolic procedure horner!-eval!-rat(p,v);
  50. % evaluate the (sparse univariate) polynomial p at a rational v using
  51. % Horner's scheme. Denominators are cleared by in fact calculating the
  52. % following:
  53. %
  54. % for i:=min:max sum (a[i] * n**(i-min) * d**(max-min-i))
  55. %
  56. % note that if the polynomial does not end in a non-zero constant
  57. % the routine it return the evaluation of p/(trailing exponent)
  58. % s accumulates d**(max-min-i)
  59. % ans accumulates the sum
  60. % m is degree difference between current and previous term
  61. % See specific routines below for further detail
  62. if (numr v)=1 then horner!-eval!-integer(p,denr v,1,0)
  63. else if (denr v)=1 then horner!-eval!-reciprocal(p,numr v,0,0)
  64. else horner!-eval!-rational(p,numr v,denr v,0,1,0);
  65. symbolic procedure horner!-eval!-rational(p,n,d,m,s,ans);
  66. % general case of an arbitrary rational
  67. if domainp p then
  68. if p then ans*n**m+s*p else ans
  69. else (lambda mp;
  70. horner!-eval!-rational(red p,n,d,mp,s*d**mp,ans*n**m+s*lc p))
  71. (ldeg p)-(if domainp red p then 0 else ldeg red p);
  72. symbolic procedure horner!-eval!-integer(p,d,s,ans);
  73. % simple sub case of an integer (n/1)
  74. if domainp p then
  75. if p then ans+s*p else ans
  76. else horner!-eval!-integer(red p,d,
  77. s*d**((ldeg p)-(if domainp red p then 0 else ldeg red p)),
  78. ans+s*lc p);
  79. symbolic procedure horner!-eval!-reciprocal(p,n,m,ans);
  80. % simpler sub case of a straight reciprocal of an integer (1/n)
  81. if domainp p then
  82. if p then ans*n**m+p else ans
  83. else horner!-eval!-reciprocal(red p,n,
  84. (ldeg p)-(if domainp red p then 0 else ldeg red p),
  85. ans*n**m+lc p);
  86. symbolic procedure horner!-eval!-rat!-and!-gcdl(l,v);
  87. % l is a list of polynomials to be evaluated at the point v
  88. % and then take the GCD of these evaluations. We use an auxiliary
  89. % routine with an accumulator variable to make the computation
  90. % tail-recursive
  91. if null cdr l then horner!-eval!-rat(car l,v)
  92. else if null cddr l then
  93. gcdn(horner!-eval!-rat(car l,v),horner!-eval!-rat(cadr l,v))
  94. else horner!-eval!-rat!-and!-gcdl1(cddr l,v,
  95. gcdn(horner!-eval!-rat(car l,v),horner!-eval!-rat(cadr l,v)));
  96. symbolic procedure horner!-eval!-rat!-and!-gcdl1(l,v,a);
  97. if a=1 then 1
  98. else if null l then a
  99. else horner!-eval!-rat!-and!-gcdl1(cdr l,v,
  100. gcdn(horner!-eval!-rat(car l,v),a));
  101. %*********** Polynomial division utilities and extensions *************
  102. symbolic procedure heu!-quotfl(l,d);
  103. % test division of each of a list of SF's (l) by the SF d
  104. if null cdr l then heu!-quotf(car l,d)
  105. else heu!-quotfl1(cdr l,d,heu!-quotf(car l,d));
  106. symbolic procedure heu!-quotfl1(l,d,flag);
  107. if null flag then nil
  108. else if null cdr l then heu!-quotf(car l,d)
  109. else heu!-quotfl1(cdr l,d,heu!-quotf(car l,d));
  110. symbolic procedure heu!-quotf(p,q);
  111. if domainp q then
  112. if domainp p then
  113. if null p then nil
  114. else if null q then
  115. rederr "HEUGCD(heu-quotf): division by zero"
  116. else (lambda temp; if cdr temp=0 then car temp else nil)
  117. divide(p,q)
  118. else quotf(p,q)
  119. else if domainp p then nil
  120. else if ldeg p<ldeg q then nil
  121. else if (cdr divide(lc p,lc q)) neq 0 then nil
  122. else if p=q then 1
  123. else (lambda qv;
  124. if qv=0 then quotf(p,q)
  125. else if remainder(horner!-eval!-rat(p,'(2 . 1)),qv)=0 then
  126. quotf(p,q)
  127. else nil)
  128. horner!-eval!-rat(q,'(2 . 1));
  129. %****************** Z-adic polynomial GCD routines ********************
  130. symbolic smacro procedure xceiling(n,d); (n+d-1)/d;
  131. symbolic smacro procedure force!-even x;
  132. if evenp x then x else x+1;
  133. symbolic smacro procedure force!-odd x;
  134. if evenp x then x+1 else x;
  135. symbolic smacro procedure next!-even!-value x;
  136. if (denr x)=1 then force!-even fix(numr x * ee) ./ 1
  137. else 1 ./ force!-even fix(denr x * ee);
  138. symbolic smacro procedure next!-odd!-value x;
  139. if (denr x)=1 then force!-odd fix(numr x * ee) ./ 1
  140. else 1 ./ force!-odd fix(denr x * ee);
  141. symbolic smacro procedure first!-value(inp,inq,lcp,lcq,tcp,tcq);
  142. % Initial evaluation is based on Cauchy's inequality.
  143. if lcp<tcp then
  144. if lcq<tcq then
  145. if (inp*tcq)<(inq*tcp) then 1 . (2+2*xceiling(inp,tcp))
  146. else 1 . (2+2*xceiling(inq,tcq))
  147. else if (inp*lcq)<(inq*tcp) then 1 . (2+2*xceiling(inp,tcp))
  148. else (2+2*xceiling(inq,lcq)) . 1
  149. else if lcq<tcq then
  150. if (inp*tcq)<(inq*lcp) then (2+2*xceiling(inp,lcp)) . 1
  151. else 1 . (2+2*xceiling(inq,tcq))
  152. else if (inp*lcq)<(inq*lcp) then (2+2*xceiling(inp,lcp)) . 1
  153. else (2+2*xceiling(inq,lcq)) . 1;
  154. symbolic smacro procedure
  155. second!-value(inp,inq,lcp,lcq,lgcd,tcp,tcq,tgcd);
  156. % The second evaluation point is from a modified Mignotte bound.
  157. (lambda (inp,inq,lcp,lcq,tcp,tcq);
  158. if lcp<tcp then
  159. if lcq<tcq then
  160. if (inp*tcq)<(inq*tcp) then
  161. 1 . force!-even (2+xceiling(inp,tcp))
  162. else
  163. 1 . force!-even (2+xceiling(inq,tcq))
  164. else if (inp*lcq)<(inq*tcp) then
  165. 1 . force!-even (2+xceiling(inp,tcp))
  166. else force!-even (2+xceiling(inq,lcq)) . 1
  167. else if lcq<tcq then
  168. if (inp*tcq)<(inq*lcp) then
  169. force!-even (2+xceiling(inp,lcp)) . 1
  170. else 1 . force!-even (2+xceiling(inq,tcq))
  171. else if (inp*lcq)<(inq*lcp) then
  172. force!-even (2+xceiling(inp,lcp)) . 1
  173. else force!-even (2+xceiling(inq,lcq)) . 1)
  174. (inp,inq,max(2,lcp/lgcd),max(2,lcq/lgcd),
  175. max(2,tcp/tgcd),max(2,tcq/tgcd));
  176. symbolic procedure heu!-gcd!-list l;
  177. if null cdr l then car l
  178. else if null cddr l then heu!-gcd(car l,cadr l)
  179. else heu!-gcdl sort(l,function (lambda (p1,p2); ldeg p1<ldeg p2));
  180. symbolic procedure heu!-gcdl l;
  181. % Heuristic univariate polynomial GCD after Davenport & Padgets'
  182. % extensions of Geddes' algorithm (EUROSAM 84) for a list of polynomials
  183. begin scalar k,value,dval,d,xsx,inp,inq,lcp,lcq,lgcd,tcp,tcq,tgcd,tmp;
  184. % check if first one is linear (input is sorted by leading degree)
  185. if (ldeg car l=1) then return
  186. (lambda pcarl; if heu!-quotfl(cdr l,pcarl) then pcarl else 1)
  187. quotf(car l,kontent car l);
  188. % general case - compute GCD of all of them at Cauchy's bound
  189. tmp:=analyse!-polynomial car l;
  190. if tmp then <<
  191. inp:=car tmp; lcp:=lc car l; xsx:=cadr tmp; tcp:=caddr tmp;
  192. tmp:=analyse!-polynomial cadr l;
  193. if tmp then <<
  194. inq:=car tmp; lcq:=lc cadr l;
  195. xsx:=min(xsx,cadr tmp); tcq:=caddr tmp>>
  196. else return nil>>
  197. else return nil;
  198. value:=first!-value(inp,inq,lcp,lcq,tcp,tcq);
  199. % first check for trivial GCD
  200. d:=gen!-poly(horner!-eval!-rat!-and!-gcdl(l,value),
  201. value,mvar car l,xsx);
  202. if heu!-quotfl(l,d) then return d;
  203. % since that failed we pick a much higher evaluation point
  204. % courtesy of a modified Mignotte inequality and just work on the
  205. % first two
  206. lgcd:=gcdn(lcp,lcq);
  207. for each x in cddr l do lgcd:=gcdn(lc x,lgcd);
  208. tgcd:=gcdn(tcp,tcq);
  209. for each x in cddr l do tgcd:=gcdn(htc x,tgcd);
  210. value:=second!-value(inp,inq,lcp,lcq,lgcd,tcp,tcq,tgcd);
  211. loop:
  212. d:=gen!-poly(horner!-eval!-rat!-and!-gcdl(l,value),
  213. value,mvar car l,xsx);
  214. if heu!-quotfl(l,d) then return d;
  215. value:=next!-odd!-value value;
  216. k:=k+1;
  217. d:=gen!-poly(horner!-eval!-rat!-and!-gcdl(l,value),
  218. value,mvar car l,xsx);
  219. if heu!-quotfl(l,d) then return d;
  220. value:=next!-even!-value value;
  221. k:=k+1;
  222. if k < 10 then goto loop;
  223. print "(HEUGCD):heu-gcd-list fails";
  224. return nil
  225. end;
  226. symbolic procedure heu!-gcd(p,q);
  227. % Heuristic univariate polynomial GCD after Davenport & Padgets'
  228. % extensions of Geddes' algorithm (EUROSAM 84)
  229. % the method of choosing the evaluation point is quite complex (but not
  230. % as general as it ought to be). It is
  231. %
  232. % min(infinity!-norm p/lc p, infinity!-norm p/htc p,
  233. % infinity!-norm q/lc q, infinity!-norm q/htc q)
  234. %
  235. begin scalar k,value,d,dval,xsx,inp,inq,lcp,lcq,lgcd,tcp,tcq,tgcd,tmp;
  236. % check if one of p and q is linear
  237. if (ldeg q=1) or (ldeg p=1) then return
  238. if univariatep p and univariatep q then
  239. (lambda (pp,pq);
  240. if (ldeg pq)=1 then
  241. (lambda h; if null h then 1 else pq) heu!-quotf(pp,pq)
  242. else
  243. (lambda h; if null h then 1 else pp) heu!-quotf(pq,pp))
  244. (quotf(p, kontent p), quotf(q, kontent q))
  245. else nil;
  246. % general case
  247. if (ldeg p)>(ldeg q) then return heu!-gcd(q,p);
  248. tmp:=analyse!-polynomial p;
  249. if tmp then <<
  250. inp:=car tmp; lcp:=lc p; xsx:=cadr tmp; tcp:=caddr tmp;
  251. tmp:=analyse!-polynomial q;
  252. if tmp then <<
  253. inq:=car tmp; lcq:=lc q; xsx:=min(xsx,cadr tmp); tcq:=caddr tmp>>
  254. else return nil>>
  255. else return nil;
  256. value:=first!-value(inp,inq,lcp,lcq,tcp,tcq);
  257. % first check for trivial GCD
  258. dval:=gcdn(horner!-eval!-rat(p,value),horner!-eval!-rat(q,value));
  259. d:=gen!-poly(dval,value,mvar p,xsx);
  260. if heu!-quotf(p,d) and heu!-quotf(q,d) then return d;
  261. % if that failed we pick a much higher evaluation point
  262. lgcd:=gcdn(lcp,lcq);
  263. tgcd:=gcdn(lcp,lcq);
  264. value:=second!-value(inp,inq,lcp,lcq,lgcd,tcp,tcq,tgcd);
  265. k:=0;
  266. loop:
  267. dval:=gcdn(horner!-eval!-rat(p,value),horner!-eval!-rat(q,value));
  268. d:=gen!-poly(dval,value,mvar p,xsx);
  269. if heu!-quotf(p,d) and heu!-quotf(q,d) then return d;
  270. value:=next!-odd!-value value;
  271. k:=k+1;
  272. dval:=gcdn(horner!-eval!-rat(p,value),horner!-eval!-rat(q,value));
  273. d:=gen!-poly(dval,value,mvar p,xsx);
  274. if heu!-quotf(p,d) and heu!-quotf(q,d) then return d;
  275. value:=next!-even!-value value;
  276. k:=k+1;
  277. if k < 10 then goto loop;
  278. print "(HEUGCD):heu-gcd fails";
  279. return nil
  280. end;
  281. symbolic procedure analyse!-polynomial p;
  282. % Determine the infinity norm of p and take note of the trailing
  283. % coefficient, simultaneously check that p is univariate and take note
  284. % of any trailing powers of the main variable. The result is a triple
  285. % of (infinity-norm,excess powers,trailing coefficient)
  286. analyse!-polynomial1(p,1,lc p,0,mvar p);
  287. symbolic procedure analyse!-polynomial1 (p,inp,tcp,xsxp,mvarp);
  288. if domainp p then
  289. if p then list(max(inp,abs p),0,abs p)
  290. else list(inp,xsxp,abs tcp)
  291. else if ((mvar p) neq mvarp) then nil
  292. else if domainp lc p then
  293. analyse!-polynomial1(red p,max(inp,abs lc p),lc p,ldeg p,mvarp)
  294. else nil;
  295. %********** Reconstruction from the Z-adic representation *************
  296. % given a number in [0,modulus), return the equivalent
  297. % member of [-modulus/2,modulus/2)
  298. % LAMBDA to ensure only one evaluation of arguments;
  299. symbolic smacro procedure negshiftz(n,modulus);
  300. (lambda (nn,mmodulus);
  301. if nn>quotient(mmodulus,2) then nn-mmodulus else nn)
  302. (n,modulus);
  303. symbolic procedure gen!-poly(dval,value,var,xsx);
  304. if (numr value)=1 then gen!-poly!-backward(dval,denr value,var,xsx)
  305. else if (denr value)=1 then gen!-poly!-forward(dval,numr value,var,xsx)
  306. else rederr "HEUGCD(gen-poly):point must be integral or reciprocal";
  307. symbolic procedure gen!-poly!-forward(dval,value,var,xsx);
  308. % generate a new polynomial in var from the value-adic representation
  309. % provided by dval
  310. begin scalar i,d,val,val1,kont;
  311. kont:=0;
  312. val:=dval;
  313. i:=xsx;
  314. if zerop i then <<
  315. % an x**0 term is represented specially;
  316. val1:=negshiftz(remainder(val,value),value);
  317. if not zerop val1 then kont:=d:=val1;
  318. val:=quotient(val-val1,value);
  319. i:=1
  320. >>;
  321. while not zerop val do <<
  322. val1:=negshiftz(remainder(val,value),value);
  323. if not zerop val1 then <<
  324. kont:=gcdn(val1,kont);
  325. d:=var .** i .* val1 .+ d
  326. >>;
  327. val:=quotient(val-val1,value);
  328. i:=1+i
  329. >>;
  330. return quotf(d,kont)
  331. end;
  332. symbolic procedure gen!-poly!-backward(dval,value,var,xsx);
  333. % generate a new polynomial in var from the 1/value-adic representation
  334. % provided by dval
  335. begin scalar i,d,ans,val,val1,kont;
  336. kont:=0;
  337. val:=dval;
  338. % because we are at the 1/value representation
  339. % we need the implicit REVERSE that the two-loop strategy here
  340. % provides;
  341. while not zerop val do <<
  342. val1:=negshiftz(remainder(val,value),value);
  343. d:=val1 . d;
  344. val:=quotient(val-val1,value)
  345. >>;
  346. i:=xsx;
  347. if (zerop i and not zerop car d) then <<
  348. % Handle x**0 term specially;
  349. kont:=ans:=car d;
  350. d:=cdr d;
  351. i:=1
  352. >>;
  353. while d do <<
  354. if not zerop car d then <<
  355. kont:=gcdn(car d,kont);
  356. ans:= var .** i .* car d .+ ans
  357. >>;
  358. d:=cdr d;
  359. i:=i+1
  360. >>;
  361. return quotf(ans,kont)
  362. end;
  363. endmodule;
  364. end;