factor.red 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078
  1. module bigmodp; % Modular polynomial arithmetic where the modulus may
  2. % be a bignum.
  3. % Authors: A. C. Norman and P. M. A. Moore, 1981;
  4. fluid '(current!-modulus modulus!/2);
  5. symbolic procedure general!-plus!-mod!-p(a,b);
  6. % form the sum of the two polynomials a and b
  7. % working over the ground domain defined by the routines
  8. % general!-modular!-plus, general!-modular!-times etc. the inputs to
  9. % this routine are assumed to have coefficients already
  10. % in the required domain;
  11. if null a then b
  12. else if null b then a
  13. else if isdomain a then
  14. if isdomain b then !*num2f general!-modular!-plus(a,b)
  15. else (lt b) .+ general!-plus!-mod!-p(a,red b)
  16. else if isdomain b then (lt a) .+ general!-plus!-mod!-p(red a,b)
  17. else if lpow a = lpow b then
  18. adjoin!-term(lpow a,
  19. general!-plus!-mod!-p(lc a,lc b),
  20. general!-plus!-mod!-p(red a,red b))
  21. else if comes!-before(lpow a,lpow b) then
  22. (lt a) .+ general!-plus!-mod!-p(red a,b)
  23. else (lt b) .+ general!-plus!-mod!-p(a,red b);
  24. symbolic procedure general!-times!-mod!-p(a,b);
  25. if (null a) or (null b) then nil
  26. else if isdomain a then gen!-mult!-by!-const!-mod!-p(b,a)
  27. else if isdomain b then gen!-mult!-by!-const!-mod!-p(a,b)
  28. else if mvar a=mvar b then general!-plus!-mod!-p(
  29. general!-plus!-mod!-p(general!-times!-term!-mod!-p(lt a,b),
  30. general!-times!-term!-mod!-p(lt b,red a)),
  31. general!-times!-mod!-p(red a,red b))
  32. else if ordop(mvar a,mvar b) then
  33. adjoin!-term(lpow a,general!-times!-mod!-p(lc a,b),
  34. general!-times!-mod!-p(red a,b))
  35. else adjoin!-term(lpow b,
  36. general!-times!-mod!-p(a,lc b),general!-times!-mod!-p(a,red b));
  37. symbolic procedure general!-times!-term!-mod!-p(term,b);
  38. %multiply the given polynomial by the given term;
  39. if null b then nil
  40. else if isdomain b then
  41. adjoin!-term(tpow term,
  42. gen!-mult!-by!-const!-mod!-p(tc term,b),nil)
  43. else if tvar term=mvar b then
  44. adjoin!-term(mksp(tvar term,iplus(tdeg term,ldeg b)),
  45. general!-times!-mod!-p(tc term,lc b),
  46. general!-times!-term!-mod!-p(term,red b))
  47. else if ordop(tvar term,mvar b) then
  48. adjoin!-term(tpow term,general!-times!-mod!-p(tc term,b),nil)
  49. else adjoin!-term(lpow b,
  50. general!-times!-term!-mod!-p(term,lc b),
  51. general!-times!-term!-mod!-p(term,red b));
  52. symbolic procedure gen!-mult!-by!-const!-mod!-p(a,n);
  53. % multiply the polynomial a by the constant n;
  54. if null a then nil
  55. else if n=1 then a
  56. else if isdomain a then !*num2f general!-modular!-times(a,n)
  57. else adjoin!-term(lpow a,gen!-mult!-by!-const!-mod!-p(lc a,n),
  58. gen!-mult!-by!-const!-mod!-p(red a,n));
  59. symbolic procedure general!-difference!-mod!-p(a,b);
  60. general!-plus!-mod!-p(a,general!-minus!-mod!-p b);
  61. symbolic procedure general!-minus!-mod!-p a;
  62. if null a then nil
  63. else if isdomain a then general!-modular!-minus a
  64. else (lpow a .* general!-minus!-mod!-p lc a) .+
  65. general!-minus!-mod!-p red a;
  66. symbolic procedure general!-reduce!-mod!-p a;
  67. %converts a multivariate poly from normal into modular polynomial;
  68. if null a then nil
  69. else if isdomain a then !*num2f general!-modular!-number a
  70. else adjoin!-term(lpow a,
  71. general!-reduce!-mod!-p lc a,
  72. general!-reduce!-mod!-p red a);
  73. symbolic procedure general!-make!-modular!-symmetric a;
  74. % input is a multivariate MODULAR poly A with nos in the range 0->(p-1).
  75. % This folds it onto the symmetric range (-p/2)->(p/2);
  76. if null a then nil
  77. else if domainp a then
  78. if a>modulus!/2 then !*num2f(a - current!-modulus)
  79. else a
  80. else adjoin!-term(lpow a,
  81. general!-make!-modular!-symmetric lc a,
  82. general!-make!-modular!-symmetric red a);
  83. endmodule;
  84. module degsets; % degree set processing.
  85. % Authors: A. C. Norman and P. M. A. Moore, 1981.
  86. fluid '(!*trallfac
  87. !*trfac
  88. bad!-case
  89. best!-set!-pointer
  90. dpoly
  91. factor!-level
  92. factor!-trace!-list
  93. factored!-lc
  94. irreducible
  95. modular!-info
  96. one!-complete!-deg!-analysis!-done
  97. previous!-degree!-map
  98. split!-list
  99. valid!-image!-sets);
  100. symbolic procedure check!-degree!-sets(n,multivariate!-case);
  101. % MODULAR!-INFO (vector of size N) contains the modular factors now.
  102. begin scalar degree!-sets,w,x!-is!-factor,degs;
  103. w:=split!-list;
  104. for i:=1:n do <<
  105. if multivariate!-case then
  106. x!-is!-factor:=not numberp get!-image!-content
  107. getv(valid!-image!-sets,cdar w);
  108. degs:=for each v in getv(modular!-info,cdar w) collect ldeg v;
  109. degree!-sets:=
  110. (if x!-is!-factor then 1 . degs else degs)
  111. . degree!-sets;
  112. w:=cdr w >>;
  113. check!-degree!-sets!-1 degree!-sets;
  114. best!-set!-pointer:=cdar split!-list;
  115. if multivariate!-case and factored!-lc then <<
  116. while null(w:=get!-f!-numvec
  117. getv(valid!-image!-sets,best!-set!-pointer))
  118. and (split!-list:=cdr split!-list) do
  119. best!-set!-pointer:=cdar split!-list;
  120. if null w then bad!-case:=t >>;
  121. % make sure the set is ok for distributing the
  122. % leading coefft where necessary;
  123. end;
  124. symbolic procedure check!-degree!-sets!-1 l;
  125. % L is a list of degree sets. Try to discover if the entries
  126. % in it are consistent, or if they imply that some of the
  127. % modular splittings were 'false';
  128. begin
  129. scalar i,degree!-map,degree!-map1,dpoly,
  130. plausible!-split!-found,target!-count;
  131. factor!-trace <<
  132. printc "Degree sets are:";
  133. for each s in l do <<
  134. prin2 " ";
  135. for each n in s do <<
  136. prin2 " "; prin2 n >>;
  137. terpri() >> >>;
  138. dpoly:=sum!-list car l;
  139. target!-count:=length car l;
  140. for each s in cdr l do
  141. target!-count:=min(target!-count,length s);
  142. % This used to be IMIN, but since it was the only use, it was
  143. % eliminated.
  144. if null previous!-degree!-map then <<
  145. degree!-map:=mkvect dpoly;
  146. % To begin with all degrees of factors may be possible;
  147. for i:=0:dpoly do putv(degree!-map,i,t) >>
  148. else <<
  149. factor!-trace "Refine an existing degree map";
  150. degree!-map:=previous!-degree!-map >>;
  151. degree!-map1:=mkvect dpoly;
  152. for each s in l do <<
  153. % For each degree set S I will collect in DEGREE-MAP1 a
  154. % bitmap showing what degree factors would be consistent
  155. % with that set. By ANDing together all these maps
  156. % (into DEGREE-MAP) I find what degrees for factors are
  157. % consistent with the whole of the information I have;
  158. for i:=0:dpoly do putv(degree!-map1,i,nil);
  159. putv(degree!-map1,0,t);
  160. putv(degree!-map1,dpoly,t);
  161. for each d in s do for i:=dpoly#-d#-1 step -1 until 0 do
  162. if getv(degree!-map1,i) then
  163. putv(degree!-map1,i#+d,t);
  164. for i:=0:dpoly do
  165. putv(degree!-map,i,getv(degree!-map,i) and
  166. getv(degree!-map1,i)) >>;
  167. factor!-trace <<
  168. printc "Possible degrees for factors are: ";
  169. for i:=1:dpoly#-1 do
  170. if getv(degree!-map,i) then << prin2 i; prin2 " " >>;
  171. terpri() >>;
  172. i:=dpoly#-1;
  173. while i#>0 do if getv(degree!-map,i) then i:=-1
  174. else i:=i#-1;
  175. if i=0 then <<
  176. factor!-trace
  177. printc "Degree analysis proves polynomial irreducible";
  178. return irreducible:=t >>;
  179. for each s in l do if length s=target!-count then begin
  180. % Sets with too many factors are not plausible anyway;
  181. i:=s;
  182. while i and getv(degree!-map,car i) do i:=cdr i;
  183. % If I drop through with I null it was because the set was
  184. % consistent, otherwise it represented a false split;
  185. if null i then plausible!-split!-found:=t end;
  186. previous!-degree!-map:=degree!-map;
  187. if plausible!-split!-found or one!-complete!-deg!-analysis!-done
  188. then return nil;
  189. % PRINTC "Going to try getting some more images";
  190. return bad!-case:=t
  191. end;
  192. symbolic procedure sum!-list l;
  193. if null cdr l then car l
  194. else car l #+ sum!-list cdr l;
  195. endmodule;
  196. module facmod; % Modular factorization: discover the factor count mod p.
  197. % Authors: A. C. Norman and P. M. A. Moore, 1979.
  198. fluid '(!*timings
  199. current!-modulus
  200. dpoly
  201. dwork1
  202. dwork2
  203. known!-factors
  204. linear!-factors
  205. m!-image!-variable
  206. modular!-info
  207. null!-space!-basis
  208. number!-needed
  209. poly!-mod!-p
  210. poly!-vector
  211. safe!-flag
  212. split!-list
  213. work!-vector1
  214. work!-vector2);
  215. safe!-flag:=carcheck 0; % For speed of array access - important here;
  216. symbolic procedure get!-factor!-count!-mod!-p
  217. (n,poly!-mod!-p,p,x!-is!-factor);
  218. % gets the factor count mod p from the nth image using the
  219. % first half of Berlekamp's method;
  220. begin scalar old!-m,f!-count,wtime;
  221. old!-m:=set!-modulus p;
  222. % PRIN2 "prime = ";% printc current!-modulus;
  223. % PRIN2 "degree = ";% printc ldeg poly!-mod!-p;
  224. trace!-time display!-time("Entered GET-FACTOR-COUNT after ",time());
  225. wtime:=time();
  226. f!-count:=modular!-factor!-count();
  227. trace!-time display!-time("Factor count obtained in ",time()-wtime);
  228. split!-list:=
  229. ((if x!-is!-factor then car f!-count#+1 else car f!-count) . n)
  230. . split!-list;
  231. putv(modular!-info,n,cdr f!-count);
  232. set!-modulus old!-m
  233. end;
  234. symbolic procedure modular!-factor!-count();
  235. begin
  236. scalar poly!-vector,wvec1,wvec2,x!-to!-p,
  237. n,wtime,w,lin!-f!-count,null!-space!-basis;
  238. known!-factors:=nil;
  239. dpoly:=ldeg poly!-mod!-p;
  240. wvec1:=mkvect (2#*dpoly);
  241. wvec2:=mkvect (2#*dpoly);
  242. x!-to!-p:=mkvect dpoly;
  243. poly!-vector:=mkvect dpoly;
  244. for i:=0:dpoly do putv(poly!-vector,i,0);
  245. poly!-to!-vector poly!-mod!-p;
  246. w:=count!-linear!-factors!-mod!-p(wvec1,wvec2,x!-to!-p);
  247. lin!-f!-count:=car w;
  248. if dpoly#<4 then return
  249. (if dpoly=0 then lin!-f!-count
  250. else lin!-f!-count#+1) .
  251. list(lin!-f!-count . cadr w,
  252. dpoly . poly!-vector,
  253. nil);
  254. % When I use Berlekamp I certainly know that the polynomial
  255. % involved has no linear factors;
  256. wtime:=time();
  257. null!-space!-basis:=use!-berlekamp(x!-to!-p,caddr w,wvec1);
  258. trace!-time display!-time("Berlekamp done in ",time()-wtime);
  259. n:=lin!-f!-count #+ length null!-space!-basis #+ 1;
  260. % there is always 1 more factor than the number of
  261. % null vectors we have picked up;
  262. return n . list(
  263. lin!-f!-count . cadr w,
  264. dpoly . poly!-vector,
  265. null!-space!-basis)
  266. end;
  267. %**********************************************************************;
  268. % Extraction of linear factors is done specially;
  269. symbolic procedure count!-linear!-factors!-mod!-p(wvec1,wvec2,x!-to!-p);
  270. % Compute gcd(x**p-x,u). It will be the product of all the
  271. % linear factors of u mod p;
  272. begin scalar dx!-to!-p,lin!-f!-count,linear!-factors;
  273. for i:=0:dpoly do putv(wvec2,i,getv(poly!-vector,i));
  274. dx!-to!-p:=make!-x!-to!-p(current!-modulus,wvec1,x!-to!-p);
  275. for i:=0:dx!-to!-p do putv(wvec1,i,getv(x!-to!-p,i));
  276. if dx!-to!-p#<1 then <<
  277. if dx!-to!-p#<0 then putv(wvec1,0,0);
  278. putv(wvec1,1,modular!-minus 1);
  279. dx!-to!-p:=1 >>
  280. else <<
  281. putv(wvec1,1,modular!-difference(getv(wvec1,1),1));
  282. if dx!-to!-p=1 and getv(wvec1,1)=0 then
  283. if getv(wvec1,0)=0 then dx!-to!-p:=-1
  284. else dx!-to!-p:=0 >>;
  285. if dx!-to!-p#<0 then
  286. lin!-f!-count:=copy!-vector(wvec2,dpoly,wvec1)
  287. else lin!-f!-count:=gcd!-in!-vector(wvec1,dx!-to!-p,
  288. wvec2,dpoly);
  289. linear!-factors:=mkvect lin!-f!-count;
  290. for i:=0:lin!-f!-count do
  291. putv(linear!-factors,i,getv(wvec1,i));
  292. dpoly:=quotfail!-in!-vector(poly!-vector,dpoly,
  293. linear!-factors,lin!-f!-count);
  294. return list(lin!-f!-count,linear!-factors,dx!-to!-p)
  295. end;
  296. symbolic procedure make!-x!-to!-p(p,wvec1,x!-to!-p);
  297. begin scalar dx!-to!-p,dw1;
  298. if p#<dpoly then <<
  299. for i:=0:p#-1 do putv(x!-to!-p,i,0);
  300. putv(x!-to!-p,p,1);
  301. return p >>;
  302. dx!-to!-p:=make!-x!-to!-p(p/2,wvec1,x!-to!-p);
  303. dw1:=times!-in!-vector(x!-to!-p,dx!-to!-p,x!-to!-p,dx!-to!-p,wvec1);
  304. dw1:=remainder!-in!-vector(wvec1,dw1,
  305. poly!-vector,dpoly);
  306. if not(iremainder(p,2)=0) then <<
  307. for i:=dw1 step -1 until 0 do
  308. putv(wvec1,i#+1,getv(wvec1,i));
  309. putv(wvec1,0,0);
  310. dw1:=remainder!-in!-vector(wvec1,dw1#+1,
  311. poly!-vector,dpoly) >>;
  312. for i:=0:dw1 do putv(x!-to!-p,i,getv(wvec1,i));
  313. return dw1
  314. end;
  315. symbolic procedure find!-linear!-factors!-mod!-p(p,n);
  316. % P is a vector representing a polynomial of degree N which has
  317. % only linear factors. Find all the factors and return a list of
  318. % them;
  319. begin
  320. scalar root,var,w,vec1;
  321. if n#<1 then return nil;
  322. vec1:=mkvect 1;
  323. putv(vec1,1,1);
  324. root:=0;
  325. while (n#>1) and not (root #> current!-modulus) do <<
  326. w:=evaluate!-in!-vector(p,n,root);
  327. if w=0 then << %a factor has been found!!;
  328. if var=nil then
  329. var:=mksp(m!-image!-variable,1) . 1;
  330. w:=!*f2mod
  331. adjoin!-term(car var,cdr var,!*n2f modular!-minus root);
  332. known!-factors:=w . known!-factors;
  333. putv(vec1,0,modular!-minus root);
  334. n:=quotfail!-in!-vector(p,n,vec1,1) >>;
  335. root:=root#+1 >>;
  336. known!-factors:=
  337. vector!-to!-poly(p,n,m!-image!-variable) . known!-factors
  338. end;
  339. %**********************************************************************;
  340. % Berlekamp's algorithm part 1: find null space basis giving factor
  341. % count;
  342. symbolic procedure use!-berlekamp(x!-to!-p,dx!-to!-p,wvec1);
  343. % Set up a basis for the set of remaining (nonlinear) factors
  344. % using Berlekamp's algorithm;
  345. begin
  346. scalar berl!-m,berl!-m!-size,w,
  347. dcurrent,current!-power,wtime;
  348. berl!-m!-size:=dpoly#-1;
  349. berl!-m:=mkvect berl!-m!-size;
  350. for i:=0:berl!-m!-size do <<
  351. w:=mkvect berl!-m!-size;
  352. for j:=0:berl!-m!-size do putv(w,j,0); %initialize to zero;
  353. putv(berl!-m,i,w) >>;
  354. % Note that column zero of the matrix (as used in the
  355. % standard version of Berlekamp's algorithm) is not in fact
  356. % needed and is not used here;
  357. % I want to set up a matrix that has entries
  358. % x**p, x**(2*p), ... , x**((n-1)*p)
  359. % as its columns,
  360. % where n is the degree of poly-mod-p
  361. % and all the entries are reduced mod poly-mod-p;
  362. % Since I computed x**p I have taken out some linear factors,
  363. % so reduce it further;
  364. dx!-to!-p:=remainder!-in!-vector(x!-to!-p,dx!-to!-p,
  365. poly!-vector,dpoly);
  366. dcurrent:=0;
  367. current!-power:=mkvect berl!-m!-size;
  368. putv(current!-power,0,1);
  369. for i:=1:berl!-m!-size do <<
  370. if current!-modulus#>dpoly then
  371. dcurrent:=times!-in!-vector(
  372. current!-power,dcurrent,
  373. x!-to!-p,dx!-to!-p,
  374. wvec1)
  375. else << % Multiply by shifting;
  376. for i:=0:current!-modulus#-1 do
  377. putv(wvec1,i,0);
  378. for i:=0:dcurrent do
  379. putv(wvec1,current!-modulus#+i,
  380. getv(current!-power,i));
  381. dcurrent:=dcurrent#+current!-modulus >>;
  382. dcurrent:=remainder!-in!-vector(
  383. wvec1,dcurrent,
  384. poly!-vector,dpoly);
  385. for j:=0:dcurrent do
  386. putv(getv(berl!-m,j),i,putv(current!-power,j,
  387. getv(wvec1,j)));
  388. % also I need to subtract 1 from the diagonal of the matrix;
  389. putv(getv(berl!-m,i),i,
  390. modular!-difference(getv(getv(berl!-m,i),i),1)) >>;
  391. wtime:=time();
  392. % print!-m("Q matrix",berl!-m,berl!-m!-size);
  393. w := find!-null!-space(berl!-m,berl!-m!-size);
  394. trace!-time display!-time("Null space found in ",time()-wtime);
  395. return w
  396. end;
  397. symbolic procedure find!-null!-space(berl!-m,berl!-m!-size);
  398. % Diagonalize the matrix to find its rank and hence the number of
  399. % factors the input polynomial had;
  400. begin scalar null!-space!-basis;
  401. % find a basis for the null-space of the matrix;
  402. for i:=1:berl!-m!-size do
  403. null!-space!-basis:=
  404. clear!-column(i,null!-space!-basis,berl!-m,berl!-m!-size);
  405. % print!-m("Null vectored",berl!-m,berl!-m!-size);
  406. return
  407. tidy!-up!-null!-vectors(null!-space!-basis,berl!-m,berl!-m!-size)
  408. end;
  409. symbolic procedure print!-m(m,berl!-m,berl!-m!-size);
  410. << printc m;
  411. for i:=0:berl!-m!-size do <<
  412. for j:=0:berl!-m!-size do <<
  413. prin2 getv(getv(berl!-m,i),j);
  414. ttab((4#*j)#+4) >>;
  415. terpri() >> >>;
  416. symbolic procedure clear!-column(i,
  417. null!-space!-basis,berl!-m,berl!-m!-size);
  418. % Process column I of the matrix so that (if possible) it
  419. % just has a '1' in row I and zeros elsewhere;
  420. begin
  421. scalar ii,w;
  422. % I want to bring a non-zero pivot to the position (i,i)
  423. % and then add multiples of row i to all other rows to make
  424. % all but the i'th element of column i zero. First look for
  425. % a suitable pivot;
  426. ii:=0;
  427. search!-for!-pivot:
  428. if getv(getv(berl!-m,ii),i)=0 or
  429. ((ii#<i) and not(getv(getv(berl!-m,ii),ii)=0)) then
  430. if (ii:=ii#+1)#>berl!-m!-size then
  431. return (i . null!-space!-basis)
  432. else go to search!-for!-pivot;
  433. % Here ii references a row containing a suitable pivot element for
  434. % column i. Permute rows in the matrix so as to bring the pivot onto
  435. % the diagonal;
  436. w:=getv(berl!-m,ii);
  437. putv(berl!-m,ii,getv(berl!-m,i));
  438. putv(berl!-m,i,w);
  439. % swop rows ii and i ;
  440. w:=modular!-minus modular!-reciprocal getv(getv(berl!-m,i),i);
  441. % w = -1/pivot, and is used in zeroing out the rest of column i;
  442. for row:=0:berl!-m!-size do
  443. if row neq i then begin
  444. scalar r; %process one row;
  445. r:=getv(getv(berl!-m,row),i);
  446. if not(r=0) then <<
  447. r:=modular!-times(r,w);
  448. %that is now the multiple of row i that must be added to row ii;
  449. for col:=i:berl!-m!-size do
  450. putv(getv(berl!-m,row),col,
  451. modular!-plus(getv(getv(berl!-m,row),col),
  452. modular!-times(r,getv(getv(berl!-m,i),col)))) >>
  453. end;
  454. for col:=i:berl!-m!-size do
  455. putv(getv(berl!-m,i),col,
  456. modular!-times(getv(getv(berl!-m,i),col),w));
  457. return null!-space!-basis
  458. end;
  459. symbolic procedure tidy!-up!-null!-vectors(null!-space!-basis,
  460. berl!-m,berl!-m!-size);
  461. begin
  462. scalar row!-to!-use;
  463. row!-to!-use:=berl!-m!-size#+1;
  464. null!-space!-basis:=
  465. for each null!-vector in null!-space!-basis collect
  466. build!-null!-vector(null!-vector,
  467. getv(berl!-m,row!-to!-use:=row!-to!-use#-1),berl!-m);
  468. berl!-m:=nil; % Release the store for full matrix;
  469. % prin2 "Null vectors: ";
  470. % print null!-space!-basis;
  471. return null!-space!-basis
  472. end;
  473. symbolic procedure build!-null!-vector(n,vec,berl!-m);
  474. % At the end of the elimination process (the CLEAR-COLUMN loop)
  475. % certain columns, indicated by the entries in NULL-SPACE-BASIS
  476. % will be null vectors, save for the fact that they need a '1'
  477. % inserted on the diagonal of the matrix. This procedure copies
  478. % these null-vectors into some of the vectors that represented
  479. % rows of the Berlekamp matrix;
  480. begin
  481. % putv(vec,0,0); % Not used later!!;
  482. for i:=1:n#-1 do
  483. putv(vec,i,getv(getv(berl!-m,i),n));
  484. putv(vec,n,1);
  485. % for i:=n#+1:berl!-m!-size do
  486. % putv(vec,i,0);
  487. return vec . n
  488. end;
  489. %**********************************************************************;
  490. % Berlekamp's algorithm part 2: retrieving the factors mod p;
  491. symbolic procedure get!-factors!-mod!-p(n,p);
  492. % given the modular info (for the nth image) generated by the
  493. % previous half of Berlekamp's method we can reconstruct the
  494. % actual factors mod p;
  495. begin scalar nth!-modular!-info,old!-m,wtime;
  496. nth!-modular!-info:=getv(modular!-info,n);
  497. old!-m:=set!-modulus p;
  498. wtime:=time();
  499. putv(modular!-info,n,
  500. convert!-null!-vectors!-to!-factors nth!-modular!-info);
  501. trace!-time display!-time("Factors constructed in ",time()-wtime);
  502. set!-modulus old!-m
  503. end;
  504. symbolic procedure convert!-null!-vectors!-to!-factors m!-info;
  505. % Using the null space found, complete the job
  506. % of finding modular factors by taking gcd's of the
  507. % modular input polynomial and variants on the
  508. % null space generators;
  509. begin
  510. scalar number!-needed,factors,
  511. work!-vector1,dwork1,work!-vector2,dwork2,wtime;
  512. known!-factors:=nil;
  513. wtime:=time();
  514. find!-linear!-factors!-mod!-p(cdar m!-info,caar m!-info);
  515. trace!-time display!-time("Linear factors found in ",time()-wtime);
  516. dpoly:=caadr m!-info;
  517. poly!-vector:=cdadr m!-info;
  518. null!-space!-basis:=caddr m!-info;
  519. if dpoly=0 then return known!-factors; % All factors were linear;
  520. if null null!-space!-basis then
  521. return known!-factors:=
  522. vector!-to!-poly(poly!-vector,dpoly,m!-image!-variable) .
  523. known!-factors;
  524. number!-needed:=length null!-space!-basis;
  525. % count showing how many more factors I need to find;
  526. work!-vector1:=mkvect dpoly;
  527. work!-vector2:=mkvect dpoly;
  528. factors:=list (poly!-vector . dpoly);
  529. try!-next!-null:
  530. if null!-space!-basis=nil then
  531. errorf "RAN OUT OF NULL VECTORS TOO EARLY";
  532. wtime:=time();
  533. factors:=try!-all!-constants(factors,
  534. caar null!-space!-basis,cdar null!-space!-basis);
  535. trace!-time display!-time("All constants tried in ",time()-wtime);
  536. if number!-needed=0 then
  537. return known!-factors:=append!-new!-factors(factors,
  538. known!-factors);
  539. null!-space!-basis:=cdr null!-space!-basis;
  540. go to try!-next!-null
  541. end;
  542. symbolic procedure try!-all!-constants(list!-of!-polys,v,dv);
  543. % use gcd's of v, v+1, v+2, ... to try to split up the
  544. % polynomials in the given list;
  545. begin
  546. scalar a,b,aa,s;
  547. % aa is a list of factors that can not be improved using this v,
  548. % b is a list that might be;
  549. aa:=nil; b:=list!-of!-polys;
  550. s:=0;
  551. try!-next!-constant:
  552. putv(v,0,s); % Fix constant term of V to be S;
  553. % wtime:=time();
  554. a:=split!-further(b,v,dv);
  555. % trace!-time display!-time("Polys split further in ",time()-wtime);
  556. b:=cdr a; a:=car a;
  557. aa:=nconc(a,aa);
  558. % Keep aa up to date as a list of polynomials that this poly
  559. % v can not help further with;
  560. if b=nil then return aa; % no more progress possible here;
  561. if number!-needed=0 then return nconc(b,aa);
  562. % no more progress needed;
  563. s:=s#+1;
  564. if s#<current!-modulus then go to try!-next!-constant;
  565. % Here I have run out of choices for the constant
  566. % coefficient in v without splitting everything;
  567. return nconc(b,aa)
  568. end;
  569. symbolic procedure split!-further(list!-of!-polys,v,dv);
  570. % list-of-polys is a list of polynomials. try to split
  571. % its members further by taking gcd's with the polynomial
  572. % v. return (a . b) where the polys in a can not possibly
  573. % be split using v+constant, but the polys in b might
  574. % be;
  575. if null list!-of!-polys then nil . nil
  576. else begin
  577. scalar a,b,gg,q;
  578. a:=split!-further(cdr list!-of!-polys,v,dv);
  579. b:=cdr a; a:=car a;
  580. if number!-needed=0 then go to no!-split;
  581. % if all required factors have been found there is no need to
  582. % search further;
  583. dwork1:=copy!-vector(v,dv,work!-vector1);
  584. dwork2:=copy!-vector(caar list!-of!-polys,cdar list!-of!-polys,
  585. work!-vector2);
  586. dwork1:=gcd!-in!-vector(work!-vector1,dwork1,
  587. work!-vector2,dwork2);
  588. if dwork1=0 or dwork1=cdar list!-of!-polys then go to no!-split;
  589. dwork2:=copy!-vector(caar list!-of!-polys,cdar list!-of!-polys,
  590. work!-vector2);
  591. dwork2:=quotfail!-in!-vector(work!-vector2,dwork2,
  592. work!-vector1,dwork1);
  593. % Here I have a splitting;
  594. gg:=mkvect dwork1;
  595. copy!-vector(work!-vector1,dwork1,gg);
  596. a:=((gg . dwork1) . a);
  597. copy!-vector(work!-vector2,dwork2,q:=mkvect dwork2);
  598. b:=((q . dwork2) . b);
  599. number!-needed:=number!-needed#-1;
  600. return (a . b);
  601. no!-split:
  602. return (a . ((car list!-of!-polys) . b))
  603. end;
  604. symbolic procedure append!-new!-factors(a,b);
  605. % Convert to REDUCE (rather than vector) form;
  606. if null a then b
  607. else
  608. vector!-to!-poly(caar a,cdar a,m!-image!-variable) .
  609. append!-new!-factors(cdr a,b);
  610. carcheck safe!-flag; % Restore status quo;
  611. endmodule;
  612. module factrr; % Full factorization of polynomials.
  613. % Author: P. M. A. Moore, 1979.
  614. fluid '(!*all!-contents
  615. !*exp
  616. !*ezgcd
  617. !*force!-prime
  618. !*gcd
  619. !*kernreverse
  620. !*mcd
  621. !*timings
  622. !*trfac
  623. base!-time
  624. current!-modulus
  625. dmode!*
  626. factor!-count
  627. factor!-level
  628. factor!-trace!-list
  629. gc!-base!-time
  630. last!-displayed!-gc!-time
  631. last!-displayed!-time
  632. m!-image!-variable
  633. modulus!/2
  634. polynomial!-to!-factor
  635. polyzero);
  636. global '(!*ifactor);
  637. symbolic procedure factoreval u;
  638. % Factorize the polynomial in the car of u, returning the factors found.
  639. % If cadr u exists then if it is a number, use it as a force prime.
  640. % Otherwise, use cadr u as a fill object, and check to see if caddr u
  641. % is now a force prime.
  642. begin scalar p,w,!*force!-prime,x,z,factor!-count;
  643. p := length u;
  644. if p<1 or p>3
  645. then rederr "FACTORIZE called with wrong number of arguments";
  646. p := !*q2f simp!* car u;
  647. if cdr u then
  648. <<w := cadr u;
  649. if fixp w then <<!*force!-prime := w; w := nil>>
  650. else if cddr u and fixp caddr u
  651. then !*force!-prime := caddr u;
  652. if !*force!-prime and not primep !*force!-prime
  653. then typerr(!*force!-prime,"prime")>>;
  654. x := if dmode!*
  655. then if z := get(dmode!*,'factorfn) then apply1(z,p)
  656. else rederr
  657. list("Factorization not supported over domain",
  658. get(dmode!*,'dname))
  659. else factorf1(p,!*force!-prime);
  660. % Note that car x is expected to be a number.
  661. z:= (0 . car x) . nil;
  662. x := reversip!* cdr x; % This puts factors in better order.
  663. factor!-count:=0;
  664. for each fff in x do
  665. for i:=1:cdr fff do
  666. z:=((factor!-count:=factor!-count+1) .
  667. mk!*sq(car fff ./ 1)) . z;
  668. z := multiple!-result(z,w);
  669. if numberp z then return z % old style input
  670. else if numberp cadr z and cadr z<0 and cddr z
  671. then z := car z .
  672. (- cadr z) . mk!*sq negsq simp caddr z . cdddr z;
  673. % make numerical coefficient positive.
  674. return if cadr z = 1 then car z . cddr z
  675. else if !*ifactor and numberp cadr z and fixp cadr z
  676. then car z .
  677. append(pairlist2list reversip zfactor cadr z,
  678. cddr z)
  679. else z
  680. end;
  681. put('factorize,'psopfn,'factoreval);
  682. symbolic procedure pairlist2list u;
  683. for each x in u conc nlist(car x,cdr x);
  684. symbolic procedure factorf u;
  685. % This is the entry to the factorizer that is to be used by programmers
  686. % working at the symbolic level. U is to be a standard form. FACTORF
  687. % hands back a list giving the factors of U. The format of said list is
  688. % described below in the comments with FACTORIZE!-FORM. Entry to the
  689. % factorizer at any level other than this is at the programmers own
  690. % risk!! ;
  691. factorf1(u,nil);
  692. symbolic procedure factorf1(u,!*force!-prime);
  693. % This entry to the factorizer allows one to force
  694. % the code to use some particular prime for its
  695. % modular factorization. It is not for casual
  696. % use;
  697. begin
  698. scalar factor!-level,base!-time,last!-displayed!-time,
  699. gc!-base!-time,last!-displayed!-gc!-time,current!-modulus,
  700. modulus!/2,expsave,!*ezgcd,!*gcd;
  701. if null !*mcd then rederr "Factorization invalid with MCD off";
  702. expsave := !*exp;
  703. !*exp := !*gcd := t; % This code will not work otherwise;
  704. !*ezgcd := t;
  705. if null expsave then u := !*q2f resimp !*f2q u;
  706. set!-time();
  707. factor!-level := 0;
  708. u := factorize!-form u;
  709. !*exp := expsave;
  710. return u
  711. end;
  712. symbolic procedure factorize!-form p;
  713. % input:
  714. % p is a reduce standard form that is to be factorized
  715. % over the integers
  716. % result: (nc . l)
  717. % where nc is numeric (may be just 1)
  718. % and l is list of the form:
  719. % ((p1 . x1) (p2 . x2) .. (pn . xn))
  720. % where p<i> are standard forms and x<i> are integers,
  721. % and p= product<i> p<i>**x<i>;
  722. %
  723. % method:
  724. % (a) reorder polynomial to make the variable of lowest maximum
  725. % degree the main one and the rest ordered similarly;
  726. % (b) use contents and primitive parts to split p up as far as possible
  727. % (c) use square-free decomposition to continue the process
  728. % (c.1) detect & perform special processing on cyclotomic polynomials
  729. % (d) use modular-based method to find factors over integers;
  730. begin scalar new!-korder,old!-korder;
  731. new!-korder:=kernord(p,polyzero);
  732. if !*kernreverse then new!-korder:=reverse new!-korder;
  733. old!-korder:=setkorder new!-korder;
  734. p:=reorder p; % Make var of lowest degree the main one;
  735. p:=factorize!-form1(p,new!-korder);
  736. setkorder old!-korder;
  737. p := (car p . for each w in cdr p collect
  738. (reorder car w . cdr w));
  739. return p
  740. end;
  741. symbolic procedure factorize!-form1(p,given!-korder);
  742. % input:
  743. % p is a reduce standard form that is to be factorized
  744. % over the integers
  745. % given-korder is a list of kernels in the order of importance
  746. % (ie when finding leading terms etc. we use this list)
  747. % See FACTORIZE-FORM above;
  748. if domainp p then (p . nil)
  749. else begin scalar m!-image!-variable,var!-list,
  750. polynomial!-to!-factor,n;
  751. if !*all!-contents then var!-list:=given!-korder
  752. else <<
  753. m!-image!-variable:=car given!-korder;
  754. var!-list:=list m!-image!-variable >>;
  755. return (lambda factor!-level;
  756. << factor!-trace <<
  757. prin2!* "FACTOR : "; printsf p;
  758. prin2!* "Chosen main variable is ";
  759. printvar m!-image!-variable >>;
  760. polynomial!-to!-factor:=p;
  761. n:=numeric!-content p;
  762. p:=quotf(p,n);
  763. if poly!-minusp p then <<
  764. p:=negf p;
  765. n:=-n >>;
  766. factor!-trace <<
  767. prin2!* "Numeric content = ";
  768. printsf n >>;
  769. p:=factorize!-by!-contents(p,var!-list);
  770. p:=n . sort!-factors p;
  771. factor!-trace <<
  772. terpri(); terpri();
  773. printstr "Final result is:"; fac!-printfactors p >>;
  774. p >>)
  775. (factor!-level+1)
  776. end;
  777. symbolic procedure factorize!-form!-recursion p;
  778. % this is essentially the same as FACTORIZE!-FORM except that
  779. % we must be careful of stray minus signs due to a possible
  780. % reordering in the recursive factoring;
  781. begin scalar s,n,x,res,new!-korder,old!-korder;
  782. new!-korder:=kernord(p,polyzero);
  783. if !*kernreverse then new!-korder:=reverse new!-korder;
  784. old!-korder:=setkorder new!-korder;
  785. p:=reorder p; % Make var of lowest degree the main one;
  786. x:=factorize!-form1(p,new!-korder);
  787. setkorder old!-korder;
  788. n := car x;
  789. x := for each p in cdr x collect (reorder car p . cdr p);
  790. if minusp n then << s:=-1; n:=-n >> else s:=1;
  791. res:=for each ff in x collect
  792. if poly!-minusp car ff then <<
  793. s:=s*((-1)**cdr ff);
  794. (negf car ff . cdr ff) >>
  795. else ff;
  796. if minusp s then errorf list(
  797. "Stray minus sign in recursive factorisation:",x);
  798. return (n . res)
  799. end;
  800. symbolic procedure sort!-factors l;
  801. %sort factors as found into some sort of standard order. The order
  802. %used here is more or less random, but will be self-consistent;
  803. sort(l,function orderfactors);
  804. % ***** Contents and primitive parts as applied to factorization *****
  805. symbolic procedure factorize!-by!-contents(p,v);
  806. %use contents wrt variables in list v to split the
  807. %polynomial p. return a list of factors;
  808. % specification is that on entry p *must* be positive;
  809. if domainp p then
  810. errorf list("FACTORIZE-BY-CONTENTS HANDED DOMAIN ELT:",p)
  811. else if null v then square!.free!.factorize p
  812. else begin scalar c,w,l,wtime;
  813. w:=contents!-with!-respect!-to(p,car v);
  814. % contents!-with!-respect!-to returns a pair (g . c) where
  815. % if g=nil the content is just c, otherwise g is a power
  816. % [ x ** n ] and g*c is the content;
  817. if not null car w then <<
  818. % here a power of v divides p;
  819. l:=(!*k2f caar w . cdar w) . nil;
  820. p:=quotfail(p,!*p2f car w);
  821. if p=1 then return l
  822. else if domainp p then
  823. errorf "P SHOULD NOT BE CONSTANT HERE" >>;
  824. c:=cdr w;
  825. if c=1 then << %no progress here;
  826. if null l then
  827. factor!-trace << prin2!* "Polynomial is primitive wrt ";
  828. prinvar car v; terpri!*(nil) >>
  829. else factor!-trace << printstr "Content is: ";
  830. fac!-printfactors(1 . l) >>;
  831. return if !*all!-contents then
  832. append(factorize!-by!-contents(p,cdr v),l)
  833. else append(square!.free!.factorize p,l) >>;
  834. p:=quotfail(p,c); %primitive part;
  835. % p is now primitive, so if it is not a real polynomial it
  836. % must be a unit. since input was +ve it had better be +1 !! ;
  837. if p=-1 then
  838. errorf "NEGATIVE PRIMITIVE PART IN FACTORIZE-BY-CONTENTS";
  839. trace!-time printc "Factoring the content:";
  840. wtime:=time();
  841. l:=append(cdr1 factorize!-form!-recursion c,l);
  842. trace!-time display!-time("Content factored in ",
  843. time()-wtime);
  844. factor!-trace <<
  845. prin2!* "Content wrt "; prinvar car v; prin2!* " is: ";
  846. printsf comfac!-to!-poly w;
  847. printstr "Factors of content are: ";
  848. fac!-printfactors(1 . l) >>;
  849. if p=1 then return l
  850. else if !*all!-contents then
  851. return append(factorize!-by!-contents(p,cdr v),l)
  852. else return append(square!.free!.factorize p,l)
  853. end;
  854. symbolic procedure cdr1 a;
  855. if car a=1 then cdr a
  856. else errorf list("NUMERIC CONTENT NOT EXTRACTED:",car a);
  857. endmodule;
  858. module facuni;
  859. % Authors: A. C. Norman and P. M. A. Moore, 1979;
  860. fluid '(!*force!-prime
  861. !*trfac
  862. alphalist
  863. bad!-case
  864. best!-factor!-count
  865. best!-known!-factors
  866. best!-modulus
  867. best!-set!-pointer
  868. chosen!-prime
  869. factor!-level
  870. factor!-trace!-list
  871. forbidden!-primes
  872. hensel!-growth!-size
  873. input!-leading!-coefficient
  874. input!-polynomial
  875. irreducible
  876. known!-factors
  877. m!-image!-variable
  878. modular!-info
  879. no!-of!-best!-primes
  880. no!-of!-random!-primes
  881. non!-monic
  882. null!-space!-basis
  883. number!-of!-factors
  884. one!-complete!-deg!-analysis!-done
  885. poly!-mod!-p
  886. previous!-degree!-map
  887. reduction!-count
  888. split!-list
  889. target!-factor!-count
  890. univariate!-factors
  891. univariate!-input!-poly
  892. valid!-primes);
  893. symbolic procedure univariate!-factorize poly;
  894. % input poly a primitive square-free univariate polynomial at least
  895. % quadratic and with +ve lc. output is a list of the factors of poly
  896. % over the integers ;
  897. if testx!*!*n!+1 poly then
  898. factorizex!*!*n!+1(m!-image!-variable,ldeg poly,1)
  899. else if testx!*!*n!-1 poly then
  900. factorizex!*!*n!-1(m!-image!-variable,ldeg poly,1)
  901. else univariate!-factorize1 poly;
  902. symbolic procedure univariate!-factorize1 poly;
  903. begin scalar
  904. valid!-primes,univariate!-input!-poly,best!-set!-pointer,
  905. number!-of!-factors,irreducible,forbidden!-primes,
  906. no!-of!-best!-primes,no!-of!-random!-primes,bad!-case,
  907. target!-factor!-count,modular!-info,univariate!-factors,
  908. hensel!-growth!-size,alphalist,previous!-degree!-map,
  909. one!-complete!-deg!-analysis!-done,reduction!-count,
  910. multivariate!-input!-poly;
  911. %note that this code works by using a local database of
  912. %fluid variables that are updated by the subroutines directly
  913. %called here. this allows for the relativly complicated
  914. %interaction between flow of data and control that occurs in
  915. %the factorization algorithm;
  916. factor!-trace <<
  917. prin2!* "Univariate polynomial="; printsf poly;
  918. printstr
  919. "The polynomial is univariate, primitive and square-free";
  920. printstr "so we can treat it slightly more specifically. We";
  921. printstr "factorise mod several primes,then pick the best one";
  922. printstr "to use in the Hensel construction." >>;
  923. initialize!-univariate!-fluids poly;
  924. % set up the fluids to start things off;
  925. tryagain:
  926. get!-some!-random!-primes();
  927. choose!-the!-best!-prime();
  928. if irreducible then <<
  929. univariate!-factors:=list univariate!-input!-poly;
  930. goto exit >>
  931. else if bad!-case then <<
  932. bad!-case:=nil; goto tryagain >>;
  933. reconstruct!-factors!-over!-integers();
  934. if irreducible then <<
  935. univariate!-factors:=list univariate!-input!-poly;
  936. goto exit >>;
  937. exit:
  938. factor!-trace <<
  939. printstr "The univariate factors are:";
  940. for each ff in univariate!-factors do printsf ff >>;
  941. return univariate!-factors
  942. end;
  943. %**********************************************************************
  944. % univariate factorization part 1. initialization and setting fluids;
  945. symbolic procedure initialize!-univariate!-fluids u;
  946. % Set up the fluids to be used in factoring primitive poly;
  947. begin
  948. if !*force!-prime then <<
  949. no!-of!-random!-primes:=1;
  950. no!-of!-best!-primes:=1 >>
  951. else <<
  952. no!-of!-random!-primes:=5;
  953. % we generate this many modular images and calculate
  954. % their factor counts;
  955. no!-of!-best!-primes:=3;
  956. % we find the modular factors of this many;
  957. >>;
  958. univariate!-input!-poly:=u;
  959. target!-factor!-count:=ldeg u
  960. end;
  961. %**********************************************************************;
  962. % univariate factorization part 2. creating modular images and picking
  963. % the best one;
  964. symbolic procedure get!-some!-random!-primes();
  965. % here we create a number of random primes to reduce the input mod p;
  966. begin scalar chosen!-prime,poly!-mod!-p,i;
  967. valid!-primes:=mkvect no!-of!-random!-primes;
  968. i:=0;
  969. while i < no!-of!-random!-primes do <<
  970. poly!-mod!-p:=
  971. find!-a!-valid!-prime(lc univariate!-input!-poly,
  972. univariate!-input!-poly,nil);
  973. if not(poly!-mod!-p='not!-square!-free) then <<
  974. i:=iadd1 i;
  975. putv(valid!-primes,i,chosen!-prime . poly!-mod!-p);
  976. forbidden!-primes:=chosen!-prime . forbidden!-primes
  977. >>
  978. >>
  979. end;
  980. symbolic procedure choose!-the!-best!-prime();
  981. % given several random primes we now choose the best by factoring
  982. % the poly mod its chosen prime and taking one with the
  983. % lowest factor count as the best for hensel growth;
  984. begin scalar split!-list,poly!-mod!-p,null!-space!-basis,
  985. known!-factors,w,n;
  986. modular!-info:=mkvect no!-of!-random!-primes;
  987. for i:=1:no!-of!-random!-primes do <<
  988. w:=getv(valid!-primes,i);
  989. get!-factor!-count!-mod!-p(i,cdr w,car w,nil) >>;
  990. split!-list:=sort(split!-list,function lessppair);
  991. % this now contains a list of pairs (m . n) where
  992. % m is the no: of factors in set no: n. the list
  993. % is sorted with best split (smallest m) first;
  994. if caar split!-list = 1 then <<
  995. irreducible:=t; return nil >>;
  996. w:=split!-list;
  997. for i:=1:no!-of!-best!-primes do <<
  998. n:=cdar w;
  999. get!-factors!-mod!-p(n,car getv(valid!-primes,n));
  1000. w:=cdr w >>;
  1001. % pick the best few of these and find out their
  1002. % factors mod p;
  1003. split!-list:=delete(w,split!-list);
  1004. % throw away the other sets;
  1005. check!-degree!-sets(no!-of!-best!-primes,nil);
  1006. % the best set is pointed at by best!-set!-pointer;
  1007. one!-complete!-deg!-analysis!-done:=t;
  1008. factor!-trace <<
  1009. w:=getv(valid!-primes,best!-set!-pointer);
  1010. prin2!* "The chosen prime is "; printstr car w;
  1011. prin2!* "The polynomial mod "; prin2!* car w;
  1012. printstr ", made monic, is:";
  1013. printsf cdr w;
  1014. printstr "and the factors of this modular polynomial are:";
  1015. for each x in getv(modular!-info,best!-set!-pointer)
  1016. do printsf x;
  1017. >>
  1018. end;
  1019. %**********************************************************************;
  1020. % univariate factorization part 3. reconstruction of the
  1021. % chosen image over the integers;
  1022. symbolic procedure reconstruct!-factors!-over!-integers();
  1023. % the hensel construction from modular case to univariate
  1024. % over the integers;
  1025. begin scalar best!-modulus,best!-factor!-count,input!-polynomial,
  1026. input!-leading!-coefficient,best!-known!-factors,s;
  1027. s:=getv(valid!-primes,best!-set!-pointer);
  1028. best!-known!-factors:=getv(modular!-info,best!-set!-pointer);
  1029. input!-leading!-coefficient:=lc univariate!-input!-poly;
  1030. best!-modulus:=car s;
  1031. best!-factor!-count:=length best!-known!-factors;
  1032. input!-polynomial:=univariate!-input!-poly;
  1033. univariate!-factors:=reconstruct!.over!.integers();
  1034. if irreducible then return t;
  1035. number!-of!-factors:=length univariate!-factors;
  1036. if number!-of!-factors=1 then return irreducible:=t
  1037. end;
  1038. symbolic procedure reconstruct!.over!.integers();
  1039. begin scalar w,lclist,non!-monic;
  1040. set!-modulus best!-modulus;
  1041. for i:=1:best!-factor!-count do
  1042. lclist:=input!-leading!-coefficient . lclist;
  1043. if not (input!-leading!-coefficient=1) then <<
  1044. best!-known!-factors:=
  1045. for each ff in best!-known!-factors collect
  1046. multf(input!-leading!-coefficient,!*mod2f ff);
  1047. non!-monic:=t;
  1048. factor!-trace <<
  1049. printstr
  1050. "(a) Now the polynomial is not monic so we multiply each";
  1051. printstr
  1052. "of the modular factors, f(i), by the absolute value of";
  1053. prin2!* "the leading coefficient: ";
  1054. prin2!* input!-leading!-coefficient; printstr '!.;
  1055. printstr "To bring the polynomial into agreement with this, we";
  1056. prin2!* "multiply it by ";
  1057. if best!-factor!-count > 2 then
  1058. << prin2!* input!-leading!-coefficient; prin2!* "**";
  1059. printstr isub1 best!-factor!-count >>
  1060. else printstr input!-leading!-coefficient >> >>;
  1061. w:=uhensel!.extend(input!-polynomial,
  1062. best!-known!-factors,lclist,best!-modulus);
  1063. if irreducible then return t;
  1064. if car w ='ok then return cdr w
  1065. else errorf w
  1066. end;
  1067. % Now some special treatment for cyclotomic polynomials;
  1068. symbolic procedure testx!*!*n!+1 u;
  1069. not domainp u and (
  1070. lc u=1 and
  1071. red u = 1);
  1072. symbolic procedure testx!*!*n!-1 u;
  1073. not domainp u and (
  1074. lc u=1 and
  1075. red u = -1);
  1076. symbolic procedure factorizex!*!*n!+1(var,degree,vorder);
  1077. % Deliver factors of (VAR**VORDER)**DEGREE+1 given that it is
  1078. % appropriate to treat VAR**VORDER as a kernel;
  1079. if evenp degree then factorizex!*!*n!+1(var,degree/2,2*vorder)
  1080. else begin
  1081. scalar w;
  1082. w := factorizex!*!*n!-1(var,degree,vorder);
  1083. w := negf car w . cdr w;
  1084. return for each p in w collect negate!-variable(var,2*vorder,p)
  1085. end;
  1086. symbolic procedure negate!-variable(var,vorder,p);
  1087. % VAR**(VORDER/2) -> -VAR**(VORDER/2) in the polynomial P;
  1088. if domainp p then p
  1089. else if mvar p=var then
  1090. if remainder(ldeg p,vorder)=0 then
  1091. lt p .+ negate!-variable(var,vorder,red p)
  1092. else (lpow p .* negf lc p) .+ negate!-variable(var,vorder,red p)
  1093. else (lpow p .* negate!-variable(var,vorder,lc p)) .+
  1094. negate!-variable(var,vorder,red p);
  1095. symbolic procedure integer!-factors n;
  1096. % Return integer factors of N, with attached multiplicities. Assumes
  1097. % that N is fairly small;
  1098. begin
  1099. scalar l,q,m,w;
  1100. % L is list of results generated so far, Q is current test divisor,
  1101. % and M is associated multiplicity;
  1102. if n=1 then return '((1 . 1));
  1103. q := 2; m := 0;
  1104. % Test divide by 2,3,5,7,9,11,13,...
  1105. top:
  1106. w := divide(n,q);
  1107. while cdr w=0 do << n := car w; w := divide(n,q); m := m+1 >>;
  1108. if not m=0 then l := (q . m) . l;
  1109. if q>car w then <<
  1110. if not n=1 then l := (n . 1) . l;
  1111. return reversewoc l >>;
  1112. % q := ilogor(1,iadd1 q);
  1113. q := iadd1 q;
  1114. if q #> 3 then q := iadd1 q;
  1115. m := 0;
  1116. go to top
  1117. end;
  1118. symbolic procedure factored!-divisors fl;
  1119. % FL is an association list of primes and exponents. Return a list
  1120. % of all subsets of this list, i.e. of numbers dividing the
  1121. % original integer. Exclude '1' from the list;
  1122. if null fl then nil
  1123. else begin
  1124. scalar l,w;
  1125. w := factored!-divisors cdr fl;
  1126. l := w;
  1127. for i := 1:cdar fl do <<
  1128. l := list (caar fl . i) . l;
  1129. for each p in w do
  1130. l := ((caar fl . i) . p) . l >>;
  1131. return l
  1132. end;
  1133. symbolic procedure factorizex!*!*n!-1(var,degree,vorder);
  1134. if evenp degree then append(factorizex!*!*n!+1(var,degree/2,vorder),
  1135. factorizex!*!*n!-1(var,degree/2,vorder))
  1136. else if degree=1 then list((mksp(var,vorder) .* 1) .+ (-1))
  1137. else begin
  1138. scalar facdeg;
  1139. facdeg := '((1 . 1)) . factored!-divisors integer!-factors degree;
  1140. return for each fl in facdeg
  1141. collect cyclotomic!-polynomial(var,fl,vorder)
  1142. end;
  1143. symbolic procedure cyclotomic!-polynomial(var,fl,vorder);
  1144. % Create Psi<degree>(var**order)
  1145. % where degree is given by the association list of primes and
  1146. % multiplicities FL;
  1147. if not cdar fl=1 then
  1148. cyclotomic!-polynomial(var,(caar fl . sub1 cdar fl) . cdr fl,
  1149. vorder*caar fl)
  1150. else if cdr fl=nil then
  1151. if caar fl=1 then (mksp(var,vorder) .* 1) .+ (-1)
  1152. else quotfail((mksp(var,vorder*caar fl) .* 1) .+ (-1),
  1153. (mksp(var,vorder) .* 1) .+ (-1))
  1154. else quotfail(cyclotomic!-polynomial(var,cdr fl,vorder*caar fl),
  1155. cyclotomic!-polynomial(var,cdr fl,vorder));
  1156. endmodule;
  1157. module imageset;
  1158. % Authors: A. C. Norman and P. M. A. Moore, 1979;
  1159. fluid '(!*force!-prime
  1160. !*force!-zero!-set
  1161. !*timings
  1162. !*trfac
  1163. bad!-case
  1164. chosen!-prime
  1165. current!-modulus
  1166. f!-numvec
  1167. factor!-level
  1168. factor!-trace!-list
  1169. factor!-x
  1170. factored!-lc
  1171. forbidden!-primes
  1172. forbidden!-sets
  1173. image!-content
  1174. image!-lc
  1175. image!-mod!-p
  1176. image!-poly
  1177. image!-set
  1178. image!-set!-modulus
  1179. kord!*
  1180. m!-image!-variable
  1181. modulus!/2
  1182. multivariate!-input!-poly
  1183. no!-of!-primes!-to!-try
  1184. othervars
  1185. polyzero
  1186. save!-zset
  1187. usable!-set!-found
  1188. vars!-to!-kill
  1189. zero!-set!-tried
  1190. zerovarset
  1191. zset);
  1192. %*******************************************************************;
  1193. %
  1194. % this section deals with the image sets used in
  1195. % factorising multivariate polynomials according
  1196. % to wang's theories.
  1197. % ref: math. comp. vol.32 no.144 oct 1978 pp 1217-1220
  1198. % 'an improved multivariate polynomial factoring algorithm'
  1199. %
  1200. %*******************************************************************;
  1201. %*******************************************************************;
  1202. % first we have routines for generating the sets
  1203. %*******************************************************************;
  1204. symbolic procedure generate!-an!-image!-set!-with!-prime
  1205. good!-set!-needed;
  1206. % given a multivariate poly (in a fluid) we generate an image set
  1207. % to make it univariate and also a random prime to use in the
  1208. % modular factorization. these numbers are random except that
  1209. % we will not allow anything in forbidden!-sets or forbidden!-primes;
  1210. begin scalar currently!-forbidden!-sets,u,wtime;
  1211. u:=multivariate!-input!-poly;
  1212. % a bit of a handful to type otherwise!!!! ;
  1213. image!-set:=nil;
  1214. currently!-forbidden!-sets:=forbidden!-sets;
  1215. tryanotherset:
  1216. if image!-set then
  1217. currently!-forbidden!-sets:=image!-set .
  1218. currently!-forbidden!-sets;
  1219. wtime:=time();
  1220. image!-set:=get!-new!-set currently!-forbidden!-sets;
  1221. % princ "Trying imageset= ";
  1222. % printc image!-set;
  1223. trace!-time <<
  1224. display!-time(" New image set found in ",time()-wtime);
  1225. wtime:=time() >>;
  1226. image!-lc:=make!-image!-lc!-list(lc u,image!-set);
  1227. % list of image lc's wrt different variables in IMAGE-SET;
  1228. % princ "Image set to try is:";% printc image!-set;
  1229. % prin2!* "L.C. of poly is:";% printsf lc u;
  1230. % printc "Image l.c.s with variables substituted on order:";
  1231. % for each imlc in image!-lc do printsf imlc;
  1232. trace!-time
  1233. display!-time(" Image of lc made in ",time()-wtime);
  1234. if (caar image!-lc)=0 then goto tryanotherset;
  1235. wtime:=time();
  1236. image!-poly:=make!-image(u,image!-set);
  1237. trace!-time <<
  1238. display!-time(" Image poly made in ",time()-wtime);
  1239. wtime:=time() >>;
  1240. image!-content:=get!.content image!-poly;
  1241. % note: the content contains the image variable if it
  1242. % is a factor of the image poly;
  1243. trace!-time
  1244. display!-time(" Content found in ",time()-wtime);
  1245. image!-poly:=quotfail(image!-poly,image!-content);
  1246. % make sure the image polynomial is primitive which includes
  1247. % making the leading coefft positive (-ve content if
  1248. % necessary).
  1249. % If the image polynomial was of the form k*v^2 where v is
  1250. % the image variable then GET.CONTENT will have taken out
  1251. % one v and the k leaving the polynomial v here.
  1252. % Divisibility by v here thus indicates that the image was
  1253. % not square free, and so we will not be able to find a
  1254. % sensible prime to use.
  1255. if not didntgo quotf(image!-poly,!*k2f m!-image!-variable) then
  1256. go to tryanotherset;
  1257. wtime:=time();
  1258. image!-mod!-p:=find!-a!-valid!-prime(image!-lc,image!-poly,
  1259. not numberp image!-content);
  1260. if image!-mod!-p='not!-square!-free then goto tryanotherset;
  1261. trace!-time <<
  1262. display!-time(" Prime and image mod p found in ",time()-wtime);
  1263. wtime:=time() >>;
  1264. if factored!-lc then
  1265. if f!-numvec:=unique!-f!-nos(factored!-lc,image!-content,
  1266. image!-set) then <<
  1267. usable!-set!-found:=t;
  1268. trace!-time
  1269. display!-time(" Nos for lc found in ",time()-wtime) >>
  1270. else <<
  1271. trace!-time display!-time(" Nos for lc failed in ",
  1272. time()-wtime);
  1273. if (not usable!-set!-found) and good!-set!-needed then
  1274. goto tryanotherset >>
  1275. end;
  1276. symbolic procedure get!-new!-set forbidden!-s;
  1277. % associate each variable in vars-to-kill with a random no. mod
  1278. % image-set-modulus. If the boolean tagged with a variable is true then
  1279. % a value of 1 or 0 is no good and so rejected, however all other
  1280. % variables can take these values so they are tried exhaustively before
  1281. % using truly random values. sets in forbidden!-s not allowed;
  1282. begin scalar old!.m,alist,n,nextzset,w;
  1283. if zero!-set!-tried then <<
  1284. if !*force!-zero!-set then
  1285. errorf "Zero set tried - possibly it was invalid";
  1286. image!-set!-modulus:=iadd1 image!-set!-modulus;
  1287. old!.m:=set!-modulus image!-set!-modulus;
  1288. alist:=for each v in vars!-to!-kill collect
  1289. << n:=modular!-number next!-random!-number();
  1290. if n>modulus!/2 then n:=n-current!-modulus;
  1291. if cdr v then <<
  1292. while n=0
  1293. or n=1
  1294. or (n = (isub1 current!-modulus)) do
  1295. n:=modular!-number next!-random!-number();
  1296. if n>modulus!/2 then n:=n-current!-modulus >>;
  1297. car v . n >> >>
  1298. else <<
  1299. old!.m:=set!-modulus image!-set!-modulus;
  1300. nextzset:=car zset;
  1301. alist:=for each zv in zerovarset collect <<
  1302. w:=zv . car nextzset;
  1303. nextzset:=cdr nextzset;
  1304. w >>;
  1305. if othervars then alist:=
  1306. append(alist,for each v in othervars collect <<
  1307. n:=modular!-number next!-random!-number();
  1308. while n=0
  1309. or n=1
  1310. or (n = (isub1 current!-modulus)) do
  1311. n:=modular!-number next!-random!-number();
  1312. if n>modulus!/2 then n:=n-current!-modulus;
  1313. v . n >>);
  1314. if null(zset:=cdr zset) then
  1315. if null save!-zset then zero!-set!-tried:=t
  1316. else zset:=make!-next!-zset save!-zset;
  1317. alist:=for each v in cdr kord!* collect atsoc(v,alist);
  1318. % Puts the variables in alist in the right order;
  1319. >>;
  1320. set!-modulus old!.m;
  1321. return if member(alist,forbidden!-s) then
  1322. get!-new!-set forbidden!-s
  1323. else alist
  1324. end;
  1325. %**********************************************************************
  1326. % now given an image/univariate polynomial find a suitable random prime;
  1327. symbolic procedure find!-a!-valid!-prime(lc!-u,u,factor!-x);
  1328. % finds a suitable random prime for reducing a poly mod p.
  1329. % u is the image/univariate poly. we are not allowed to use
  1330. % any of the primes in forbidden!-primes (fluid).
  1331. % lc!-u is either numeric or (in the multivariate case) a list of
  1332. % images of the lc;
  1333. begin scalar currently!-forbidden!-primes,res,prime!-count,v,w;
  1334. if factor!-x then u:=multf(u,v:=!*k2f m!-image!-variable);
  1335. chosen!-prime:=nil;
  1336. currently!-forbidden!-primes:=forbidden!-primes;
  1337. prime!-count:=1;
  1338. tryanotherprime:
  1339. if chosen!-prime then
  1340. currently!-forbidden!-primes:=chosen!-prime .
  1341. currently!-forbidden!-primes;
  1342. chosen!-prime:=get!-new!-prime currently!-forbidden!-primes;
  1343. set!-modulus chosen!-prime;
  1344. if not atom lc!-u then <<
  1345. w:=lc!-u;
  1346. while w and
  1347. ((domainp caar w and not(modular!-number caar w = 0))
  1348. or not (domainp caar w or
  1349. modular!-number l!-numeric!-c(caar w,cdar w)=0)) do
  1350. w:=cdr w;
  1351. if w then goto tryanotherprime >>
  1352. else if modular!-number lc!-u=0 then goto tryanotherprime;
  1353. res:=monic!-mod!-p reduce!-mod!-p u;
  1354. if not square!-free!-mod!-p res then
  1355. if multivariate!-input!-poly
  1356. and (prime!-count:=prime!-count+1)>no!-of!-primes!-to!-try
  1357. then <<no!-of!-primes!-to!-try := no!-of!-primes!-to!-try+1;
  1358. res:='not!-square!-free>>
  1359. else goto tryanotherprime;
  1360. if factor!-x and not(res='not!-square!-free) then
  1361. res:=quotfail!-mod!-p(res,!*f2mod v);
  1362. return res
  1363. end;
  1364. symbolic procedure get!-new!-prime forbidden!-p;
  1365. % get a small prime that is not in the list forbidden!-p;
  1366. % we pick one of the first 10 primes if we can;
  1367. if !*force!-prime then !*force!-prime
  1368. else begin scalar p,primes!-done;
  1369. for each pp in forbidden!-p do
  1370. if pp<32 then primes!-done:=pp.primes!-done;
  1371. tryagain:
  1372. if null(p:=random!-teeny!-prime primes!-done) then <<
  1373. p:=random!-small!-prime();
  1374. primes!-done:='all >>
  1375. else primes!-done:=p . primes!-done;
  1376. if member(p,forbidden!-p) then goto tryagain;
  1377. return p
  1378. end;
  1379. %***********************************************************************
  1380. % find the numbers associated with each factor of the leading
  1381. % coefficient of our multivariate polynomial. this will help
  1382. % to distribute the leading coefficient later.;
  1383. symbolic procedure unique!-f!-nos(v,cont!.u0,im!.set);
  1384. % given an image set (im!.set), this finds the numbers associated with
  1385. % each factor in v subject to wang's condition (2) on the image set.
  1386. % this is an implementation of his algorithm n. if the condition
  1387. % is met the result is a vector containing the images of each factor
  1388. % in v, otherwise the result is nil;
  1389. begin scalar d,k,q,r,lc!.image!.vec;
  1390. % v's integer factor is at the front: ;
  1391. k:=length cdr v;
  1392. % no. of non-trivial factors of v;
  1393. if not numberp cont!.u0 then cont!.u0:=lc cont!.u0;
  1394. putv(d:=mkvect k,0,abs(cont!.u0 * car v));
  1395. % d will contain the special numbers to be used in the
  1396. % loop below;
  1397. putv(lc!.image!.vec:=mkvect k,0,abs(cont!.u0 * car v));
  1398. % vector for result with 0th entry filled in;
  1399. v:=cdr v;
  1400. % throw away integer factor of v;
  1401. % k is no. of non-trivial factors (say f(i)) in v;
  1402. % d will contain the nos. associated with each f(i);
  1403. % v is now a list of the f(i) (and their multiplicities);
  1404. for i:=1:k do
  1405. << q:=abs make!-image(caar v,im!.set);
  1406. putv(lc!.image!.vec,i,q);
  1407. v:=cdr v;
  1408. for j:=isub1 i step -1 until 0 do
  1409. << r:=getv(d,j);
  1410. while not onep r do
  1411. << r:=gcd(r,q); q:=q/r >>;
  1412. if onep q then <<lc!.image!.vec:=nil; j := -1>>
  1413. % if q=1 here then we have failed the condition so exit;
  1414. >>;
  1415. if null lc!.image!.vec then i := k+1 else putv(d,i,q);
  1416. % else q is the ith number we want;
  1417. >>;
  1418. return lc!.image!.vec
  1419. end;
  1420. symbolic procedure get!.content u;
  1421. % u is a univariate square free poly. gets the content of u (=integer);
  1422. % if lc u is negative then the minus sign is pulled out as well;
  1423. % nb. the content includes the variable if it is a factor of u;
  1424. begin scalar c;
  1425. c:=if poly!-minusp u then -(numeric!-content u)
  1426. else numeric!-content u;
  1427. if not didntgo quotf(u,!*k2f m!-image!-variable) then
  1428. c:=adjoin!-term(mksp(m!-image!-variable,1),c,polyzero);
  1429. return c
  1430. end;
  1431. %********************************************************************;
  1432. % finally we have the routines that use the numbers generated
  1433. % by unique.f.nos to determine the true leading coeffts in
  1434. % the multivariate factorization we are doing and which image
  1435. % factors will grow up to have which true leading coefft.
  1436. %********************************************************************;
  1437. symbolic procedure distribute!.lc(r,im!.factors,s,v);
  1438. % v is the factored lc of a poly, say u, whose image factors (r of
  1439. % them) are in the vector im.factors. s is a list containing the
  1440. % image information including the image set, the image poly etc.
  1441. % this uses wang's ideas for distributing the factors in v over
  1442. % those in im.factors. result is (delta . vector of the lc's of
  1443. % the full factors of u) , where delta is the remaining integer part
  1444. % of the lc that we have been unable to distribute. ;
  1445. (lambda factor!-level;
  1446. begin scalar k,delta,div!.count,q,uf,i,d,max!.mult,f,numvec,
  1447. dvec,wvec,dtwid,w;
  1448. delta:=get!-image!-content s;
  1449. % the content of the u image poly;
  1450. dist!.lc!.msg1(delta,im!.factors,r,s,v);
  1451. v:=cdr v;
  1452. % we are not interested in the numeric factors of v;
  1453. k:=length v;
  1454. % number of things to distribute;
  1455. numvec:=get!-f!-numvec s;
  1456. % nos. associated with factors in v;
  1457. dvec:=mkvect r;
  1458. wvec:=mkvect r;
  1459. for j:=1:r do <<
  1460. putv(dvec,j,1);
  1461. putv(wvec,j,delta*lc getv(im!.factors,j)) >>;
  1462. % result lc's will go into dvec which we initialize to 1's;
  1463. % wvec is a work vector that we use in the division process
  1464. % below;
  1465. v:=reverse v;
  1466. for j:=k step -1 until 1 do
  1467. << % (for each factor in v, call it f(j) );
  1468. f:=caar v;
  1469. % f(j) itself;
  1470. max!.mult:=cdar v;
  1471. % multiplicity of f(j) in v (=lc u);
  1472. v:=cdr v;
  1473. d:=getv(numvec,j);
  1474. % number associated with f(j);
  1475. i:=1; % we trial divide d into lc of each image
  1476. % factor starting with 1st;
  1477. div!.count:=0;
  1478. % no. of d's that have been distributed;
  1479. factor!-trace <<
  1480. prin2!* "f("; prin2!* j; prin2!* ")= "; printsf f;
  1481. prin2!* "There are "; prin2!* max!.mult;
  1482. printstr " of these in the leading coefficient.";
  1483. prin2!* "The absolute value of the image of f("; prin2!* j;
  1484. prin2!* ")= "; printstr d >>;
  1485. while ilessp(div!.count,max!.mult)
  1486. and not igreaterp(i,r) do
  1487. << q:=divide(getv(wvec,i),d);
  1488. % first trial division;
  1489. factor!-trace <<
  1490. prin2!* " Trial divide into ";
  1491. prin2!* getv(wvec,i); printstr " :" >>;
  1492. while (zerop cdr q) and ilessp(div!.count,max!.mult) do
  1493. << putv(dvec,i,multf(getv(dvec,i),f));
  1494. % f(j) belongs in lc of ith factor;
  1495. factor!-trace <<
  1496. prin2!* " It goes so an f("; prin2!* j;
  1497. prin2!* ") belongs in ";
  1498. printsf getv(im!.factors,i);
  1499. printstr " Try again..." >>;
  1500. div!.count:=iadd1 div!.count;
  1501. % another d done;
  1502. putv(wvec,i,car q);
  1503. % save the quotient for next factor to distribute;
  1504. q:=divide(car q,d);
  1505. % try again;
  1506. >>;
  1507. i:=iadd1 i;
  1508. % as many d's as possible have gone into that
  1509. % factor so now try next factor;
  1510. factor!-trace
  1511. <<printstr " no good so try another factor ..." >>>>;
  1512. % at this point the whole of f(j) should have been
  1513. % distributed by dividing d the maximum no. of times
  1514. % (= max!.mult), otherwise we have an extraneous factor;
  1515. if ilessp(div!.count,max!.mult) then
  1516. <<bad!-case:=t; div!.count := max!.mult>>
  1517. >>;
  1518. if bad!-case then return;
  1519. dist!.lc!.msg2(dvec,im!.factors,r);
  1520. if onep delta then
  1521. << for j:=1:r do <<
  1522. w:=lc getv(im!.factors,j) /
  1523. evaluate!-in!-order(getv(dvec,j),get!-image!-set s);
  1524. if w<0 then begin
  1525. scalar oldpoly;
  1526. delta:= -delta;
  1527. oldpoly:=getv(im!.factors,j);
  1528. putv(im!.factors,j,negf oldpoly);
  1529. % to keep the leading coefficients positive we negate the
  1530. % image factors when necessary;
  1531. multiply!-alphas(-1,oldpoly,getv(im!.factors,j));
  1532. % remember to fix the alphas as well;
  1533. end;
  1534. putv(dvec,j,multf(abs w,getv(dvec,j))) >>;
  1535. dist!.lc!.msg3(dvec,im!.factors,r);
  1536. return (delta . dvec)
  1537. >>;
  1538. % if delta=1 then we know the true lc's exactly so put in their
  1539. % integer contents and return with result.
  1540. % otherwise try spreading delta out over the factors: ;
  1541. dist!.lc!.msg4 delta;
  1542. for j:=1:r do
  1543. << dtwid:=evaluate!-in!-order(getv(dvec,j),get!-image!-set s);
  1544. uf:=getv(im!.factors,j);
  1545. d:=gcddd(lc uf,dtwid);
  1546. putv(dvec,j,multf(lc uf/d,getv(dvec,j)));
  1547. putv(im!.factors,j,multf(dtwid/d,uf));
  1548. % have to fiddle the image factors by an integer multiple;
  1549. multiply!-alphas!-recip(dtwid/d,uf,getv(im!.factors,j));
  1550. % fix the alphas;
  1551. delta:=delta/(dtwid/d)
  1552. >>;
  1553. % now we've done all we can to distribute delta so we return with
  1554. % what's left: ;
  1555. if delta<=0 then
  1556. errorf list("FINAL DELTA IS -VE IN DISTRIBUTE!.LC",delta);
  1557. factor!-trace <<
  1558. printstr " Finally we have:";
  1559. for j:=1:r do <<
  1560. prinsf getv(im!.factors,j);
  1561. prin2!* " with l.c. ";
  1562. printsf getv(dvec,j) >> >>;
  1563. return (delta . dvec)
  1564. end) (factor!-level * 10);
  1565. symbolic procedure dist!.lc!.msg1(delta,im!.factors,r,s,v);
  1566. factor!-trace <<
  1567. terpri(); terpri();
  1568. printstr "We have a polynomial whose image factors (call";
  1569. printstr "them the IM-factors) are:";
  1570. prin2!* delta; printstr " (= numeric content, delta)";
  1571. printvec(" f(",r,")= ",im!.factors);
  1572. prin2!* " wrt the image set: ";
  1573. for each x in get!-image!-set s do <<
  1574. prinvar car x; prin2!* "="; prin2!* cdr x; prin2!* ";" >>;
  1575. terpri!*(nil);
  1576. printstr "We also have its true multivariate leading";
  1577. printstr "coefficient whose factors (call these the";
  1578. printstr "LC-factors) are:";
  1579. fac!-printfactors v;
  1580. printstr "We want to determine how these LC-factors are";
  1581. printstr "distributed over the leading coefficients of each";
  1582. printstr "IM-factor. This enables us to feed the resulting";
  1583. printstr "image factors into a multivariate Hensel";
  1584. printstr "construction.";
  1585. printstr "We distribute each LC-factor in turn by dividing";
  1586. printstr "its image into delta times the leading coefficient";
  1587. printstr "of each IM-factor until it finds one that it";
  1588. printstr "divides exactly. The image set is chosen such that";
  1589. printstr "this will only happen for the IM-factors to which";
  1590. printstr "this LC-factor belongs - (there may be more than";
  1591. printstr "one if the LC-factor occurs several times in the";
  1592. printstr "leading coefficient of the original polynomial).";
  1593. printstr "This choice also requires that we distribute the";
  1594. printstr "LC-factors in a specific order:"
  1595. >>;
  1596. symbolic procedure dist!.lc!.msg2(dvec,im!.factors,r);
  1597. factor!-trace <<
  1598. printstr "The leading coefficients are now correct to within an";
  1599. printstr "integer factor and are as follows:";
  1600. for j:=1:r do <<
  1601. prinsf getv(im!.factors,j);
  1602. prin2!* " with l.c. ";
  1603. printsf getv(dvec,j) >> >>;
  1604. symbolic procedure dist!.lc!.msg3(dvec,im!.factors,r);
  1605. factor!-trace <<
  1606. printstr "Since delta=1, we have no non-trivial content of the";
  1607. printstr
  1608. "image to deal with so we know the true leading coefficients";
  1609. printstr
  1610. "exactly. We fix the signs of the IM-factors to match those";
  1611. printstr "of their true leading coefficients:";
  1612. for j:=1:r do <<
  1613. prinsf getv(im!.factors,j);
  1614. prin2!* " with l.c. ";
  1615. printsf getv(dvec,j) >> >>;
  1616. symbolic procedure dist!.lc!.msg4 delta;
  1617. factor!-trace <<
  1618. prin2!* " Here delta is not 1 meaning that we have a content, ";
  1619. printstr delta;
  1620. printstr "of the image to distribute among the factors somehow.";
  1621. printstr "For each IM-factor we can divide its leading";
  1622. printstr "coefficient by the image of its determined leading";
  1623. printstr "coefficient and see if there is a non-trivial result.";
  1624. printstr "This will indicate a factor of delta belonging to this";
  1625. printstr "IM-factor's leading coefficient." >>;
  1626. endmodule;
  1627. module pfactor; % Factorization of polynomials modulo p.
  1628. % Author: A. C. Norman, 1978.
  1629. fluid '(!*backtrace
  1630. !*gcd
  1631. base!-time
  1632. current!-modulus
  1633. gc!-base!-time
  1634. last!-displayed!-gc!-time
  1635. last!-displayed!-time
  1636. m!-image!-variable
  1637. modular!-info
  1638. modulus!/2
  1639. user!-prime);
  1640. symbolic procedure pfactor(q,p);
  1641. % Q is a standard form. Factorize and return the factors mod p.
  1642. begin scalar base!-time,last!-displayed!-time,
  1643. gc!-base!-time,last!-displayed!-gc!-time,
  1644. user!-prime,current!-modulus,modulus!/2,r,x;
  1645. set!-time();
  1646. if not numberp p then typerr(p,"number")
  1647. else if not primep p then typerr(p,"prime");
  1648. user!-prime:=p;
  1649. set!-modulus p;
  1650. if domainp q or null reduce!-mod!-p lc q then
  1651. printc "*** Degenerate case in modular factorization";
  1652. if not (length variables!-in!-form q=1) then
  1653. rederr "Multivariate input to modular factorization";
  1654. r:=reduce!-mod!-p q;
  1655. % LNCOEFF := LC R;
  1656. x := lnc r;
  1657. r :=monic!-mod!-p r;
  1658. print!-time "About to call FACTOR-FORM-MOD-P";
  1659. r:=errorset(list('factor!-form!-mod!-p,mkquote r),t,!*backtrace);
  1660. print!-time "FACTOR-FORM-MOD-P returned";
  1661. if not errorp r then return x . car r;
  1662. printc "****** FACTORIZATION FAILED******";
  1663. return list(1,prepf q) % 1 needed by factorize.
  1664. end;
  1665. symbolic procedure factor!-form!-mod!-p p;
  1666. % input:
  1667. % p is a reduce standard form that is to be factorized
  1668. % mod prime;
  1669. % result:
  1670. % ((p1 . x1) (p2 . x2) .. (pn . xn))
  1671. % where p<i> are standard forms and x<i> are integers,
  1672. % and p= product<i> p<i>**x<i>;
  1673. sort!-factors factorize!-by!-square!-free!-mod!-p p;
  1674. symbolic procedure factorize!-by!-square!-free!-mod!-p p;
  1675. if p=1 then nil
  1676. else if domainp p then (p . 1) . nil
  1677. else
  1678. begin
  1679. scalar dp,v;
  1680. v:=(mksp(mvar p,1).* 1) .+ nil;
  1681. dp:=0;
  1682. while evaluate!-mod!-p(p,mvar v,0)=0 do <<
  1683. p:=quotfail!-mod!-p(p,v);
  1684. dp:=dp+1 >>;
  1685. if dp>0 then return ((v . dp) .
  1686. factorize!-by!-square!-free!-mod!-p p);
  1687. dp:=derivative!-mod!-p p;
  1688. if dp=nil then <<
  1689. %here p is a something to the power current!-modulus;
  1690. p:=divide!-exponents!-by!-p(p,current!-modulus);
  1691. p:=factorize!-by!-square!-free!-mod!-p p;
  1692. return multiply!-multiplicities(p,current!-modulus) >>;
  1693. dp:=gcd!-mod!-p(p,dp);
  1694. if dp=1 then return factorize!-pp!-mod!-p p;
  1695. %now p is not square-free;
  1696. p:=quotfail!-mod!-p(p,dp);
  1697. %factorize p and dp separately;
  1698. p:=factorize!-pp!-mod!-p p;
  1699. dp:=factorize!-by!-square!-free!-mod!-p dp;
  1700. % i feel that this scheme is slightly clumsy, but
  1701. % square-free decomposition mod p is not as straightforward
  1702. % as square free decomposition over the integers, and pfactor
  1703. % is probably not going to be slowed down too badly by
  1704. % this;
  1705. return mergefactors(p,dp)
  1706. end;
  1707. %**********************************************************************;
  1708. % code to factorize primitive square-free polynomials mod p;
  1709. symbolic procedure divide!-exponents!-by!-p(p,n);
  1710. if isdomain p then p
  1711. else (mksp(mvar p,exactquotient(ldeg p,n)) .* lc p) .+
  1712. divide!-exponents!-by!-p(red p,n);
  1713. symbolic procedure exactquotient(a,b);
  1714. begin
  1715. scalar w;
  1716. w:=divide(a,b);
  1717. if cdr w=0 then return car w;
  1718. error(50,list("Inexact division",list(a,b,w)))
  1719. end;
  1720. symbolic procedure multiply!-multiplicities(l,n);
  1721. if null l then nil
  1722. else (caar l . (n*cdar l)) .
  1723. multiply!-multiplicities(cdr l,n);
  1724. symbolic procedure mergefactors(a,b);
  1725. % a and b are lists of factors (with multiplicities),
  1726. % merge them so that no factor occurs more than once in
  1727. % the result;
  1728. if null a then b
  1729. else mergefactors(cdr a,addfactor(car a,b));
  1730. symbolic procedure addfactor(a,b);
  1731. %add factor a into list b;
  1732. if null b then list a
  1733. else if car a=caar b then
  1734. (car a . (cdr a + cdar b)) . cdr b
  1735. else car b . addfactor(a,cdr b);
  1736. symbolic procedure factorize!-pp!-mod!-p p;
  1737. %input a primitive square-free polynomial p,
  1738. % output a list of irreducible factors of p;
  1739. begin
  1740. scalar vars;
  1741. if p=1 then return nil
  1742. else if isdomain p then return (p . 1) . nil;
  1743. % now I am certain that p is not degenerate;
  1744. print!-time "primitive square-free case detected";
  1745. vars:=variables!-in!-form p;
  1746. if length vars=1 then return unifac!-mod!-p p;
  1747. errorf "SHAMBLED IN PFACTOR - MULTIVARIATE CASE RESURFACED"
  1748. end;
  1749. symbolic procedure unifac!-mod!-p p;
  1750. %input p a primitive square-free univariate polynomial
  1751. %output a list of the factors of p over z mod p;
  1752. begin
  1753. scalar modular!-info,m!-image!-variable;
  1754. if isdomain p then return nil
  1755. else if ldeg p=1 then return (p . 1) . nil;
  1756. modular!-info:=mkvect 1;
  1757. m!-image!-variable:=mvar p;
  1758. get!-factor!-count!-mod!-p(1,p,user!-prime,nil);
  1759. print!-time "Factor counts obtained";
  1760. get!-factors!-mod!-p(1,user!-prime);
  1761. print!-time "Actual factors extracted";
  1762. return for each z in getv(modular!-info,1) collect (z . 1)
  1763. end;
  1764. endmodule;
  1765. module vecpoly;
  1766. % Authors: A. C. Norman and P. M. A. Moore, 1979;
  1767. fluid '(current!-modulus safe!-flag);
  1768. %**********************************************************************;
  1769. % Routines for working with modular univariate polynomials
  1770. % stored as vectors. Used to avoid unwarranted storage management
  1771. % in the mod-p factorization process;
  1772. safe!-flag:=carcheck 0;
  1773. symbolic procedure copy!-vector(a,da,b);
  1774. % Copy A into B;
  1775. << for i:=0:da do
  1776. putv(b,i,getv(a,i));
  1777. da >>;
  1778. symbolic procedure times!-in!-vector(a,da,b,db,c);
  1779. % Put the product of A and B into C and return its degree.
  1780. % C must not overlap with either A or B;
  1781. begin
  1782. scalar dc,ic,w;
  1783. if da#<0 or db#<0 then return minus!-one;
  1784. dc:=da#+db;
  1785. for i:=0:dc do putv(c,i,0);
  1786. for ia:=0:da do <<
  1787. w:=getv(a,ia);
  1788. for ib:=0:db do <<
  1789. ic:=ia#+ib;
  1790. putv(c,ic,modular!-plus(getv(c,ic),
  1791. modular!-times(w,getv(b,ib)))) >> >>;
  1792. return dc
  1793. end;
  1794. symbolic procedure quotfail!-in!-vector(a,da,b,db);
  1795. % Overwrite A with (A/B) and return degree of result.
  1796. % The quotient must be exact;
  1797. if da#<0 then da
  1798. else if db#<0 then errorf "Attempt to divide by zero"
  1799. else if da#<db then errorf "Bad degrees in QUOTFAIL-IN-VECTOR"
  1800. else begin
  1801. scalar dc;
  1802. dc:=da#-db; % Degree of result;
  1803. for i:=dc step -1 until 0 do begin
  1804. scalar q;
  1805. q:=modular!-quotient(getv(a,db#+i),getv(b,db));
  1806. for j:=0:db#-1 do
  1807. putv(a,i#+j,modular!-difference(getv(a,i#+j),
  1808. modular!-times(q,getv(b,j))));
  1809. putv(a,db#+i,q)
  1810. end;
  1811. for i:=0:db#-1 do if getv(a,i) neq 0 then
  1812. errorf "Quotient not exact in QUOTFAIL!-IN!-VECTOR";
  1813. for i:=0:dc do
  1814. putv(a,i,getv(a,db#+i));
  1815. return dc
  1816. end;
  1817. symbolic procedure remainder!-in!-vector(a,da,b,db);
  1818. % Overwrite the vector A with the remainder when A is
  1819. % divided by B, and return the degree of the result;
  1820. begin
  1821. scalar delta,db!-1,recip!-lc!-b,w;
  1822. if db=0 then return minus!-one
  1823. else if db=minus!-one then errorf "ATTEMPT TO DIVIDE BY ZERO";
  1824. recip!-lc!-b:=modular!-minus modular!-reciprocal getv(b,db);
  1825. db!-1:=db#-1; % Leading coeff of B treated specially, hence this;
  1826. while not((delta:=da#-db) #< 0) do <<
  1827. w:=modular!-times(recip!-lc!-b,getv(a,da));
  1828. for i:=0:db!-1 do
  1829. putv(a,i#+delta,modular!-plus(getv(a,i#+delta),
  1830. modular!-times(getv(b,i),w)));
  1831. da:=da#-1;
  1832. while not(da#<0) and getv(a,da)=0 do da:=da#-1 >>;
  1833. return da
  1834. end;
  1835. symbolic procedure evaluate!-in!-vector(a,da,n);
  1836. % Evaluate A at N;
  1837. begin
  1838. scalar r;
  1839. r:=getv(a,da);
  1840. for i:=da#-1 step -1 until 0 do
  1841. r:=modular!-plus(getv(a,i),
  1842. modular!-times(r,n));
  1843. return r
  1844. end;
  1845. symbolic procedure gcd!-in!-vector(a,da,b,db);
  1846. % Overwrite A with the gcd of A and B. On input A and B are
  1847. % vectors of coefficients, representing polynomials
  1848. % of degrees DA and DB. Return DG, the degree of the gcd;
  1849. begin
  1850. scalar w;
  1851. if da=0 or db=0 then << putv(a,0,1); return 0 >>
  1852. else if da#<0 or db#<0 then errorf "GCD WITH ZERO NOT ALLOWED";
  1853. top:
  1854. % Reduce the degree of A;
  1855. da:=remainder!-in!-vector(a,da,b,db);
  1856. if da=0 then << putv(a,0,1); return 0 >>
  1857. else if da=minus!-one then <<
  1858. w:=modular!-reciprocal getv(b,db);
  1859. for i:=0:db do putv(a,i,modular!-times(getv(b,i),w));
  1860. return db >>;
  1861. % Now reduce degree of B;
  1862. db:=remainder!-in!-vector(b,db,a,da);
  1863. if db=0 then << putv(a,0,1); return 0 >>
  1864. else if db=minus!-one then <<
  1865. w:=modular!-reciprocal getv(a,da);
  1866. if not (w=1) then
  1867. for i:=0:da do putv(a,i,modular!-times(getv(a,i),w));
  1868. return da >>;
  1869. go to top
  1870. end;
  1871. carcheck safe!-flag;
  1872. endmodule;
  1873. end;