facmod.red 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. module facmod; % Modular factorization: discover the factor count mod p.
  2. % Authors: A. C. Norman and P. M. A. Moore, 1979.
  3. fluid '(current!-modulus
  4. dpoly
  5. dwork1
  6. dwork2
  7. known!-factors
  8. linear!-factors
  9. m!-image!-variable
  10. modular!-info
  11. null!-space!-basis
  12. number!-needed
  13. poly!-mod!-p
  14. poly!-vector
  15. safe!-flag
  16. split!-list
  17. work!-vector1
  18. work!-vector2);
  19. safe!-flag:= carcheck 0; % For speed of array access - important here.
  20. carcheck 0; % and again for fasl purposes (in case carcheck
  21. % is flagged EVAL).
  22. symbolic procedure get!-factor!-count!-mod!-p
  23. (n,poly!-mod!-p,p,x!-is!-factor);
  24. % gets the factor count mod p from the nth image using the
  25. % first half of Berlekamp's method;
  26. begin scalar old!-m,f!-count;
  27. old!-m:=set!-modulus p;
  28. % PRIN2 "prime = ";% prin2t current!-modulus;
  29. % PRIN2 "degree = ";% prin2t ldeg poly!-mod!-p;
  30. % trace!-time display!-time("Entered GET-FACTOR-COUNT after ",time());
  31. % wtime:=time();
  32. f!-count:=modular!-factor!-count();
  33. % trace!-time display!-time("Factor count obtained in ",time()-wtime);
  34. split!-list:=
  35. ((if x!-is!-factor then car f!-count#+1 else car f!-count) . n)
  36. . split!-list;
  37. putv(modular!-info,n,cdr f!-count);
  38. set!-modulus old!-m
  39. end;
  40. symbolic procedure modular!-factor!-count();
  41. begin
  42. scalar poly!-vector,wvec1,wvec2,x!-to!-p,
  43. n,w,lin!-f!-count,null!-space!-basis;
  44. known!-factors:=nil;
  45. dpoly:=ldeg poly!-mod!-p;
  46. wvec1:=mkvect (2#*dpoly);
  47. wvec2:=mkvect (2#*dpoly);
  48. x!-to!-p:=mkvect dpoly;
  49. poly!-vector:=mkvect dpoly;
  50. for i:=0:dpoly do putv(poly!-vector,i,0);
  51. poly!-to!-vector poly!-mod!-p;
  52. w:=count!-linear!-factors!-mod!-p(wvec1,wvec2,x!-to!-p);
  53. lin!-f!-count:=car w;
  54. if dpoly#<4 then return
  55. (if dpoly=0 then lin!-f!-count
  56. else lin!-f!-count#+1) .
  57. list(lin!-f!-count . cadr w,
  58. dpoly . poly!-vector,
  59. nil);
  60. % When I use Berlekamp I certainly know that the polynomial
  61. % involved has no linear factors;
  62. % wtime:=time();
  63. null!-space!-basis:=use!-berlekamp(x!-to!-p,caddr w,wvec1);
  64. % trace!-time display!-time("Berlekamp done in ",time()-wtime);
  65. n:=lin!-f!-count #+ length null!-space!-basis #+ 1;
  66. % there is always 1 more factor than the number of
  67. % null vectors we have picked up;
  68. return n . list(
  69. lin!-f!-count . cadr w,
  70. dpoly . poly!-vector,
  71. null!-space!-basis)
  72. end;
  73. %**********************************************************************;
  74. % Extraction of linear factors is done specially;
  75. symbolic procedure count!-linear!-factors!-mod!-p(wvec1,wvec2,x!-to!-p);
  76. % Compute gcd(x**p-x,u). It will be the product of all the
  77. % linear factors of u mod p;
  78. begin scalar dx!-to!-p,lin!-f!-count,linear!-factors;
  79. for i:=0:dpoly do putv(wvec2,i,getv(poly!-vector,i));
  80. dx!-to!-p:=make!-x!-to!-p(current!-modulus,wvec1,x!-to!-p);
  81. for i:=0:dx!-to!-p do putv(wvec1,i,getv(x!-to!-p,i));
  82. if dx!-to!-p#<1 then <<
  83. if dx!-to!-p#<0 then putv(wvec1,0,0);
  84. putv(wvec1,1,modular!-minus 1);
  85. dx!-to!-p:=1 >>
  86. else <<
  87. putv(wvec1,1,modular!-difference(getv(wvec1,1),1));
  88. if dx!-to!-p=1 and getv(wvec1,1)=0 then
  89. if getv(wvec1,0)=0 then dx!-to!-p:=-1
  90. else dx!-to!-p:=0 >>;
  91. if dx!-to!-p#<0 then
  92. lin!-f!-count:=copy!-vector(wvec2,dpoly,wvec1)
  93. else lin!-f!-count:=gcd!-in!-vector(wvec1,dx!-to!-p,
  94. wvec2,dpoly);
  95. linear!-factors:=mkvect lin!-f!-count;
  96. for i:=0:lin!-f!-count do
  97. putv(linear!-factors,i,getv(wvec1,i));
  98. dpoly:=quotfail!-in!-vector(poly!-vector,dpoly,
  99. linear!-factors,lin!-f!-count);
  100. return list(lin!-f!-count,linear!-factors,dx!-to!-p)
  101. end;
  102. symbolic procedure make!-x!-to!-p(p,wvec1,x!-to!-p);
  103. begin scalar dx!-to!-p,dw1;
  104. if p#<dpoly then <<
  105. for i:=0:p#-1 do putv(x!-to!-p,i,0);
  106. putv(x!-to!-p,p,1);
  107. return p >>;
  108. dx!-to!-p:=make!-x!-to!-p(p/2,wvec1,x!-to!-p);
  109. dw1:=times!-in!-vector(x!-to!-p,dx!-to!-p,x!-to!-p,dx!-to!-p,wvec1);
  110. dw1:=remainder!-in!-vector(wvec1,dw1,poly!-vector,dpoly);
  111. if not(iremainder(p,2)=0) then <<
  112. for i:=dw1 step -1 until 0 do putv(wvec1,i#+1,getv(wvec1,i));
  113. putv(wvec1,0,0);
  114. dw1:=remainder!-in!-vector(wvec1,dw1#+1,poly!-vector,dpoly)>>;
  115. for i:=0:dw1 do putv(x!-to!-p,i,getv(wvec1,i));
  116. return dw1
  117. end;
  118. symbolic procedure find!-linear!-factors!-mod!-p(p,n);
  119. % P is a vector representing a polynomial of degree N which has
  120. % only linear factors. Find all the factors and return a list of
  121. % them;
  122. begin
  123. scalar root,var,w,vec1;
  124. if n#<1 then return nil;
  125. vec1:=mkvect 1;
  126. putv(vec1,1,1);
  127. root:=0;
  128. while (n#>1) and not (root #> current!-modulus) do <<
  129. w:=evaluate!-in!-vector(p,n,root);
  130. if w=0 then << %a factor has been found!!;
  131. if var=nil then
  132. var:=mksp(m!-image!-variable,1) . 1;
  133. w:=!*f2mod
  134. adjoin!-term(car var,cdr var,!*n2f modular!-minus root);
  135. known!-factors:=w . known!-factors;
  136. putv(vec1,0,modular!-minus root);
  137. n:=quotfail!-in!-vector(p,n,vec1,1) >>;
  138. root:=root#+1 >>;
  139. known!-factors:=
  140. vector!-to!-poly(p,n,m!-image!-variable) . known!-factors
  141. end;
  142. %**********************************************************************;
  143. % Berlekamp's algorithm part 1: find null space basis giving factor
  144. % count;
  145. symbolic procedure use!-berlekamp(x!-to!-p,dx!-to!-p,wvec1);
  146. % Set up a basis for the set of remaining (nonlinear) factors
  147. % using Berlekamp's algorithm;
  148. begin
  149. scalar berl!-m,berl!-m!-size,w,dcurrent,current!-power;
  150. berl!-m!-size:=dpoly#-1;
  151. berl!-m:=mkvect berl!-m!-size;
  152. for i:=0:berl!-m!-size do <<
  153. w:=mkvect berl!-m!-size;
  154. for j:=0:berl!-m!-size do putv(w,j,0); %initialize to zero;
  155. putv(berl!-m,i,w) >>;
  156. % Note that column zero of the matrix (as used in the
  157. % standard version of Berlekamp's algorithm) is not in fact
  158. % needed and is not used here;
  159. % I want to set up a matrix that has entries
  160. % x**p, x**(2*p), ... , x**((n-1)*p)
  161. % as its columns,
  162. % where n is the degree of poly-mod-p
  163. % and all the entries are reduced mod poly-mod-p;
  164. % Since I computed x**p I have taken out some linear factors,
  165. % so reduce it further;
  166. dx!-to!-p:=remainder!-in!-vector(x!-to!-p,dx!-to!-p,
  167. poly!-vector,dpoly);
  168. dcurrent:=0;
  169. current!-power:=mkvect berl!-m!-size;
  170. putv(current!-power,0,1);
  171. for i:=1:berl!-m!-size do <<
  172. if current!-modulus#>dpoly then
  173. dcurrent:=times!-in!-vector(
  174. current!-power,dcurrent,
  175. x!-to!-p,dx!-to!-p,
  176. wvec1)
  177. else << % Multiply by shifting;
  178. for i:=0:current!-modulus#-1 do
  179. putv(wvec1,i,0);
  180. for i:=0:dcurrent do
  181. putv(wvec1,current!-modulus#+i,
  182. getv(current!-power,i));
  183. dcurrent:=dcurrent#+current!-modulus >>;
  184. dcurrent:=remainder!-in!-vector(
  185. wvec1,dcurrent,
  186. poly!-vector,dpoly);
  187. for j:=0:dcurrent do
  188. putv(getv(berl!-m,j),i,putv(current!-power,j,
  189. getv(wvec1,j)));
  190. % also I need to subtract 1 from the diagonal of the matrix;
  191. putv(getv(berl!-m,i),i,
  192. modular!-difference(getv(getv(berl!-m,i),i),1)) >>;
  193. % wtime:=time();
  194. % print!-m("Q matrix",berl!-m,berl!-m!-size);
  195. w := find!-null!-space(berl!-m,berl!-m!-size);
  196. % trace!-time display!-time("Null space found in ",time()-wtime);
  197. return w
  198. end;
  199. symbolic procedure find!-null!-space(berl!-m,berl!-m!-size);
  200. % Diagonalize the matrix to find its rank and hence the number of
  201. % factors the input polynomial had;
  202. begin scalar null!-space!-basis;
  203. % find a basis for the null-space of the matrix;
  204. for i:=1:berl!-m!-size do
  205. null!-space!-basis:=
  206. clear!-column(i,null!-space!-basis,berl!-m,berl!-m!-size);
  207. % print!-m("Null vectored",berl!-m,berl!-m!-size);
  208. return
  209. tidy!-up!-null!-vectors(null!-space!-basis,berl!-m,berl!-m!-size)
  210. end;
  211. symbolic procedure print!-m(m,berl!-m,berl!-m!-size);
  212. << prin2t m;
  213. for i:=0:berl!-m!-size do <<
  214. for j:=0:berl!-m!-size do <<
  215. prin2 getv(getv(berl!-m,i),j);
  216. ttab((4#*j)#+4) >>;
  217. terpri() >> >>;
  218. symbolic procedure clear!-column(i,
  219. null!-space!-basis,berl!-m,berl!-m!-size);
  220. % Process column I of the matrix so that (if possible) it
  221. % just has a '1' in row I and zeros elsewhere;
  222. begin
  223. scalar ii,w;
  224. % I want to bring a non-zero pivot to the position (i,i)
  225. % and then add multiples of row i to all other rows to make
  226. % all but the i'th element of column i zero. First look for
  227. % a suitable pivot;
  228. ii:=0;
  229. search!-for!-pivot:
  230. if getv(getv(berl!-m,ii),i)=0 or
  231. ((ii#<i) and not(getv(getv(berl!-m,ii),ii)=0)) then
  232. if (ii:=ii#+1)#>berl!-m!-size then
  233. return (i . null!-space!-basis)
  234. else go to search!-for!-pivot;
  235. % Here ii references a row containing a suitable pivot element for
  236. % column i. Permute rows in the matrix so as to bring the pivot onto
  237. % the diagonal;
  238. w:=getv(berl!-m,ii);
  239. putv(berl!-m,ii,getv(berl!-m,i));
  240. putv(berl!-m,i,w);
  241. % swop rows ii and i ;
  242. w:=modular!-minus modular!-reciprocal getv(getv(berl!-m,i),i);
  243. % w = -1/pivot, and is used in zeroing out the rest of column i;
  244. for row:=0:berl!-m!-size do
  245. if row neq i then begin
  246. scalar r; %process one row;
  247. r:=getv(getv(berl!-m,row),i);
  248. if not(r=0) then <<
  249. r:=modular!-times(r,w);
  250. %that is now the multiple of row i that must be added to row ii;
  251. for col:=i:berl!-m!-size do
  252. putv(getv(berl!-m,row),col,
  253. modular!-plus(getv(getv(berl!-m,row),col),
  254. modular!-times(r,getv(getv(berl!-m,i),col)))) >>
  255. end;
  256. for col:=i:berl!-m!-size do
  257. putv(getv(berl!-m,i),col,
  258. modular!-times(getv(getv(berl!-m,i),col),w));
  259. return null!-space!-basis
  260. end;
  261. symbolic procedure tidy!-up!-null!-vectors(null!-space!-basis,
  262. berl!-m,berl!-m!-size);
  263. begin
  264. scalar row!-to!-use;
  265. row!-to!-use:=berl!-m!-size#+1;
  266. null!-space!-basis:=
  267. for each null!-vector in null!-space!-basis collect
  268. build!-null!-vector(null!-vector,
  269. getv(berl!-m,row!-to!-use:=row!-to!-use#-1),berl!-m);
  270. berl!-m:=nil; % Release the store for full matrix;
  271. % prin2 "Null vectors: ";
  272. % print null!-space!-basis;
  273. return null!-space!-basis
  274. end;
  275. symbolic procedure build!-null!-vector(n,vec1,berl!-m);
  276. % At the end of the elimination process (the CLEAR-COLUMN loop)
  277. % certain columns, indicated by the entries in NULL-SPACE-BASIS
  278. % will be null vectors, save for the fact that they need a '1'
  279. % inserted on the diagonal of the matrix. This procedure copies
  280. % these null-vectors into some of the vectors that represented
  281. % rows of the Berlekamp matrix;
  282. begin
  283. % putv(vec1,0,0); % Not used later!!;
  284. for i:=1:n#-1 do
  285. putv(vec1,i,getv(getv(berl!-m,i),n));
  286. putv(vec1,n,1);
  287. % for i:=n#+1:berl!-m!-size do
  288. % putv(vec1,i,0);
  289. return vec1 . n
  290. end;
  291. %**********************************************************************;
  292. % Berlekamp's algorithm part 2: retrieving the factors mod p;
  293. symbolic procedure get!-factors!-mod!-p(n,p);
  294. % given the modular info (for the nth image) generated by the
  295. % previous half of Berlekamp's method we can reconstruct the
  296. % actual factors mod p;
  297. begin scalar nth!-modular!-info,old!-m;
  298. nth!-modular!-info:=getv(modular!-info,n);
  299. old!-m:=set!-modulus p;
  300. % wtime:=time();
  301. putv(modular!-info,n,
  302. convert!-null!-vectors!-to!-factors nth!-modular!-info);
  303. % trace!-time display!-time("Factors constructed in ",time()-wtime);
  304. set!-modulus old!-m
  305. end;
  306. symbolic procedure convert!-null!-vectors!-to!-factors m!-info;
  307. % Using the null space found, complete the job
  308. % of finding modular factors by taking gcd's of the
  309. % modular input polynomial and variants on the
  310. % null space generators;
  311. begin
  312. scalar number!-needed,factors,
  313. work!-vector1,dwork1,work!-vector2,dwork2;
  314. known!-factors:=nil;
  315. % wtime:=time();
  316. find!-linear!-factors!-mod!-p(cdar m!-info,caar m!-info);
  317. % trace!-time display!-time("Linear factors found in ",time()-wtime);
  318. dpoly:=caadr m!-info;
  319. poly!-vector:=cdadr m!-info;
  320. null!-space!-basis:=caddr m!-info;
  321. if dpoly=0 then return known!-factors; % All factors were linear;
  322. if null null!-space!-basis then
  323. return known!-factors:=
  324. vector!-to!-poly(poly!-vector,dpoly,m!-image!-variable) .
  325. known!-factors;
  326. number!-needed:=length null!-space!-basis;
  327. % count showing how many more factors I need to find;
  328. work!-vector1:=mkvect dpoly;
  329. work!-vector2:=mkvect dpoly;
  330. factors:=list (poly!-vector . dpoly);
  331. try!-next!-null:
  332. if null!-space!-basis=nil then
  333. errorf "RAN OUT OF NULL VECTORS TOO EARLY";
  334. % wtime:=time();
  335. factors:=try!-all!-constants(factors,
  336. caar null!-space!-basis,cdar null!-space!-basis);
  337. % trace!-time display!-time("All constants tried in ",time()-wtime);
  338. if number!-needed=0 then
  339. return known!-factors:=append!-new!-factors(factors,
  340. known!-factors);
  341. null!-space!-basis:=cdr null!-space!-basis;
  342. go to try!-next!-null
  343. end;
  344. symbolic procedure try!-all!-constants(list!-of!-polys,v,dv);
  345. % use gcd's of v, v+1, v+2, ... to try to split up the
  346. % polynomials in the given list;
  347. begin
  348. scalar a,b,aa,s;
  349. % aa is a list of factors that can not be improved using this v,
  350. % b is a list that might be;
  351. aa:=nil; b:=list!-of!-polys;
  352. s:=0;
  353. try!-next!-constant:
  354. putv(v,0,s); % Fix constant term of V to be S;
  355. % wtime:=time();
  356. a:=split!-further(b,v,dv);
  357. % trace!-time display!-time("Polys split further in ",time()-wtime);
  358. b:=cdr a; a:=car a;
  359. aa:=nconc(a,aa);
  360. % Keep aa up to date as a list of polynomials that this poly
  361. % v can not help further with;
  362. if b=nil then return aa; % no more progress possible here;
  363. if number!-needed=0 then return nconc(b,aa);
  364. % no more progress needed;
  365. s:=s#+1;
  366. if s#<current!-modulus then go to try!-next!-constant;
  367. % Here I have run out of choices for the constant
  368. % coefficient in v without splitting everything;
  369. return nconc(b,aa)
  370. end;
  371. symbolic procedure split!-further(list!-of!-polys,v,dv);
  372. % list-of-polys is a list of polynomials. try to split
  373. % its members further by taking gcd's with the polynomial
  374. % v. return (a . b) where the polys in a can not possibly
  375. % be split using v+constant, but the polys in b might
  376. % be;
  377. if null list!-of!-polys then nil . nil
  378. else begin
  379. scalar a,b,gg,q;
  380. a:=split!-further(cdr list!-of!-polys,v,dv);
  381. b:=cdr a; a:=car a;
  382. if number!-needed=0 then go to no!-split;
  383. % if all required factors have been found there is no need to
  384. % search further;
  385. dwork1:=copy!-vector(v,dv,work!-vector1);
  386. dwork2:=copy!-vector(caar list!-of!-polys,cdar list!-of!-polys,
  387. work!-vector2);
  388. dwork1:=gcd!-in!-vector(work!-vector1,dwork1,
  389. work!-vector2,dwork2);
  390. if dwork1=0 or dwork1=cdar list!-of!-polys then go to no!-split;
  391. dwork2:=copy!-vector(caar list!-of!-polys,cdar list!-of!-polys,
  392. work!-vector2);
  393. dwork2:=quotfail!-in!-vector(work!-vector2,dwork2,
  394. work!-vector1,dwork1);
  395. % Here I have a splitting;
  396. gg:=mkvect dwork1;
  397. copy!-vector(work!-vector1,dwork1,gg);
  398. a:=((gg . dwork1) . a);
  399. copy!-vector(work!-vector2,dwork2,q:=mkvect dwork2);
  400. b:=((q . dwork2) . b);
  401. number!-needed:=number!-needed#-1;
  402. return (a . b);
  403. no!-split:
  404. return (a . ((car list!-of!-polys) . b))
  405. end;
  406. symbolic procedure append!-new!-factors(a,b);
  407. % Convert to REDUCE (rather than vector) form;
  408. if null a then b
  409. else
  410. vector!-to!-poly(caar a,cdar a,m!-image!-variable) .
  411. append!-new!-factors(cdr a,b);
  412. carcheck safe!-flag; % Restore status quo.
  413. endmodule;
  414. end;