facmisc.red 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. module facmisc; % Miscellaneous routines used from several sections.
  2. % Authors: A. C. Norman and P. M. A. Moore, 1979.
  3. fluid '(current!-modulus
  4. image!-set!-modulus
  5. modulus!/2
  6. othervars
  7. polyzero
  8. % pt
  9. save!-zset
  10. zerovarset);
  11. global '(largest!-small!-modulus pseudo!-primes teeny!-primes);
  12. % (1) Investigate variables in polynomial.
  13. symbolic procedure multivariatep(a,v);
  14. if domainp a then nil
  15. else if not(mvar a eq v) then t
  16. else if multivariatep(lc a,v) then t
  17. else multivariatep(red a,v);
  18. symbolic procedure variables!-in!-form a;
  19. % collect variables that occur in the form a;
  20. variables!.in!.form(a,nil);
  21. symbolic procedure get!.coefft!.bound(poly,degbd);
  22. % Calculates a coefft bound for the factors of poly. This simple
  23. % bound is that suggested by Paul Wang and Linda p. Rothschild in
  24. % Math. Comp. Vol 29 July 75, p.940, due to Gel'fond.
  25. % Note that for tiny polynomials the bound is forced up to be
  26. % larger than any prime that will get used in the mod-p splitting;
  27. max(get!-height poly * fixexpfloat sumof degbd,110);
  28. symbolic procedure sumof degbd;
  29. if null degbd then 0 else cdar degbd + sumof cdr degbd;
  30. symbolic procedure fixexpfloat n;
  31. % Compute exponential function e**n for potentially large N,
  32. % rounding result up somewhat. Note that exp(10)=22027 or so,
  33. % so if the basic floating point exponential function is accurate
  34. % to 6 or so digits we are protected here against roundoff.
  35. % This could be replaced by ceiling exp n, but is written this
  36. % way to avoid floating point overflow.
  37. % if n>10 then (fixexpfloat(n2)*fixexpfloat(n-n2) where n2 = n/2)
  38. % else 1+fix exp n;
  39. if n>10 then 22027*fixexpfloat(n-10) else ceiling exp float n;
  40. % (2) Minor variations on ordinary algebraic operations.
  41. symbolic procedure quotfail(a,b);
  42. % version of quotf that fails if the division does;
  43. if polyzerop a then polyzero
  44. else begin scalar w;
  45. w:=quotf(a,b);
  46. if didntgo w then errorf list("UNEXPECTED DIVISION FAILURE",a,b)
  47. else return w
  48. end;
  49. symbolic procedure quotfail1(a,b,msg);
  50. % version of quotf that fails if the division does, and gives
  51. % custom message;
  52. if polyzerop a then polyzero
  53. else begin scalar w;
  54. w:=quotf(a,b);
  55. if didntgo w then errorf msg
  56. else return w
  57. end;
  58. % (3) pseudo-random prime numbers - small and large.
  59. symbolic procedure set!-teeny!-primes();
  60. begin scalar i;
  61. i:=-1;
  62. teeny!-primes:=mkvect 9;
  63. putv(teeny!-primes,i:=iadd1 i,3);
  64. putv(teeny!-primes,i:=iadd1 i,5);
  65. putv(teeny!-primes,i:=iadd1 i,7);
  66. putv(teeny!-primes,i:=iadd1 i,11);
  67. putv(teeny!-primes,i:=iadd1 i,13);
  68. putv(teeny!-primes,i:=iadd1 i,17);
  69. putv(teeny!-primes,i:=iadd1 i,19);
  70. putv(teeny!-primes,i:=iadd1 i,23);
  71. putv(teeny!-primes,i:=iadd1 i,29);
  72. putv(teeny!-primes,i:=iadd1 i,31)
  73. end;
  74. set!-teeny!-primes();
  75. symbolic procedure random!-small!-prime();
  76. begin
  77. scalar p;
  78. repeat <<p:=small!-random!-number(); if evenp p then p := iadd1 p>>
  79. until primep p;
  80. return p
  81. end;
  82. symbolic procedure small!-random!-number();
  83. % Returns a smallish number from a distribution strongly favouring
  84. % smaller numbers;
  85. begin scalar w;
  86. % The next lines generate a random value in the range 0 to 1000000.
  87. w := remainder(next!-random!-number(), 1000);
  88. w := remainder(next!-random!-number(),1000) + 1000*w;
  89. if w < 0 then w := w + 1000000;
  90. w:=1.0+1.5*float w/1000000.0; % 1.0 to 2.5
  91. w:=times(w,w); % In range 1.0 to 6.25
  92. return fix exp w; % Should be in range 3 to 518,
  93. % < 21 about half the time;
  94. end;
  95. % symbolic procedure fac!-exp u;
  96. % % Simple exp routine. Assumes that Lisp has a routine for
  97. % % exponentiation of floats by integers. Relative accuracy 4.e-5.
  98. % begin scalar x; integer n;
  99. % n := fix u;
  100. % if (x := (u - float n)) > 0.5 then <<x := x - 1.0; n := n + 1>>;
  101. % u := ee***n;
  102. % return u*((x+6.0)*x+12.0)/((x-6.0)*x+12.0)
  103. % end;
  104. symbolic procedure random!-teeny!-prime l;
  105. % get one of the first 10 primes at random providing it is
  106. % not in the list L or that L says we have tried them all;
  107. if l='all or (length l = 10) then nil
  108. else begin scalar p;
  109. repeat
  110. p:=getv(teeny!-primes,remainder(next!-random!-number(),10))
  111. until not member(p,l);
  112. return p
  113. end;
  114. % symbolic procedure primep n;
  115. % Test if prime. Only for use on small integers.
  116. % n=2 or
  117. % (n>2 and not evenp n and primetest(n,3));
  118. % symbolic procedure primetest(n,trial);
  119. % if igreaterp(itimes(trial,trial),n) then t
  120. % else if iremainder(n,trial)=0 then nil
  121. % else primetest(n,iplus2(trial,2));
  122. % PSEUDO-PRIMES will be a list of all composite numbers which are
  123. % less than 2^24 and where 2926^(n-1) = 3315^(n-1) = 1 mod n.
  124. pseudo!-primes:=mkvect 87;
  125. begin
  126. scalar i,l;
  127. i:=0;
  128. l:= '(2047 4033 33227 38503 56033
  129. 137149 145351 146611 188191 226801
  130. 252601 294409 328021 399001 410041
  131. 488881 512461 556421 597871 636641
  132. 665281 722261 742813 873181 950797
  133. 1047619 1084201 1141141 1152271 1193221
  134. 1373653 1398101 1461241 1584133 1615681
  135. 1627921 1755001 1857241 1909001 2327041
  136. 2508013 3057601 3363121 3542533 3581761
  137. 3828001 4069297 4209661 4335241 4510507
  138. 4588033 4650049 4877641 5049001 5148001
  139. 5176153 5444489 5481451 5892511 5968873
  140. 6186403 6189121 6733693 6868261 6955541
  141. 7398151 7519441 8086231 8134561 8140513
  142. 8333333 8725753 8927101 9439201 9494101
  143. 10024561 10185841 10267951 10606681 11972017
  144. 13390081 14063281 14469841 14676481 14913991
  145. 15247621 15829633 16253551);
  146. while l do <<
  147. putv(pseudo!-primes,i,car l);
  148. i:=i+1;
  149. l:=cdr l >>
  150. end;
  151. symbolic procedure random!-prime();
  152. begin
  153. % I want a random prime that is smaller than largest-small-modulus.
  154. % I do this by generating random odd integers in the range lsm/2 to
  155. % lsm and filtering them for primality. Prime testing is done using
  156. % a Fermat test followed by lookup in an exception table that was
  157. % laboriously precomputed. This process should be distinctly faster
  158. % than trial-division testing of candidate primes, but the exception
  159. % table is tedious to compute, so I limit lsm to 2**24 here. This is
  160. % both the value that Cambridge Lisp can support directly, an indication
  161. % of how large an exception table I computed using 48 hours of CPU time
  162. % and large enough that primes selected this way will hardly ever
  163. % be unlucky just through being too small.
  164. scalar p,w,oldmod,lsm, lsm2;
  165. lsm := largest!-small!-modulus;
  166. if lsm > 2**24 then lsm := 2**24;
  167. lsm2 := lsm/2;
  168. % W will become 1 when P is prime;
  169. oldmod := current!-modulus;
  170. while not (w=1) do <<
  171. p := remainder(next!-random!-number(), lsm);
  172. if p < lsm2 then p := p + lsm2;
  173. if evenp p then p := p + 1;
  174. set!-modulus p;
  175. w:=modular!-expt(modular!-number 2926,isub1 p);
  176. if w=1
  177. and (modular!-expt(modular!-number 3315,isub1 p) neq 1
  178. or pseudo!-prime!-p p)
  179. then w:=0>>;
  180. set!-modulus oldmod;
  181. return p
  182. end;
  183. symbolic procedure pseudo!-prime!-p n;
  184. begin
  185. scalar low,mid,high,v;
  186. low:=0;
  187. high:=87; % Size of vector of pseudo-primes;
  188. while not (high=low) do << % Binary search in table;
  189. mid:=iquotient(iplus2(iadd1 high,low),2);
  190. % Mid point of (low,high);
  191. v:=getv(pseudo!-primes,mid);
  192. if igreaterp(v,n) then high:=isub1 mid else low:=mid >>;
  193. return (getv(pseudo!-primes,low)=n)
  194. end;
  195. % (4) useful routines for vectors.
  196. symbolic procedure form!-sum!-and!-product!-mod!-p(avec,fvec,r);
  197. % sum over i (avec(i) * fvec(i));
  198. begin scalar s;
  199. s:=polyzero;
  200. for i:=1:r do
  201. s:=plus!-mod!-p(times!-mod!-p(getv(avec,i),getv(fvec,i)),
  202. s);
  203. return s
  204. end;
  205. symbolic procedure form!-sum!-and!-product!-mod!-m(avec,fvec,r);
  206. % Same as above but AVEC holds alphas mod p and want to work
  207. % mod m (m > p) so minor difference to change AVEC to AVEC mod m;
  208. begin scalar s;
  209. s:=polyzero;
  210. for i:=1:r do
  211. s:=plus!-mod!-p(times!-mod!-p(
  212. !*f2mod !*mod2f getv(avec,i),getv(fvec,i)),s);
  213. return s
  214. end;
  215. symbolic procedure reduce!-vec!-by!-one!-var!-mod!-p(v,pt,n);
  216. % Substitute for the given variable in all elements creating a
  217. % new vector for the result. (All arithmetic is mod p).
  218. begin scalar newv;
  219. newv:=mkvect n;
  220. for i:=1:n do
  221. putv(newv,i,evaluate!-mod!-p(getv(v,i),car pt,cdr pt));
  222. return newv
  223. end;
  224. symbolic procedure make!-bivariate!-vec!-mod!-p(v,imset,var,n);
  225. begin scalar newv;
  226. newv:=mkvect n;
  227. for i:=1:n do
  228. putv(newv,i,make!-bivariate!-mod!-p(getv(v,i),imset,var));
  229. return newv
  230. end;
  231. symbolic procedure times!-vector!-mod!-p(v,n);
  232. % product of all the elements in the vector mod p;
  233. begin scalar w;
  234. w:=1;
  235. for i:=1:n do w:=times!-mod!-p(getv(v,i),w);
  236. return w
  237. end;
  238. symbolic procedure make!-vec!-modular!-symmetric(v,n);
  239. % fold each elt of V which is current a modular poly in the
  240. % range 0->(p-1) onto the symmetric range (-p/2)->(p/2);
  241. for i:=1:n do putv(v,i,make!-modular!-symmetric getv(v,i));
  242. % (5) Combinatorial fns used in finding values for the variables.
  243. symbolic procedure make!-zerovarset vlist;
  244. % vlist is a list of pairs (v . tag) where v is a variable name and
  245. % tag is a boolean tag. The procedure splits the list into two
  246. % according to the tags: Zerovarset is set to a list of variables
  247. % whose tag is false and othervars contains the rest;
  248. for each w in vlist do
  249. if cdr w then othervars:= car w . othervars
  250. else zerovarset:= car w . zerovarset;
  251. symbolic procedure make!-zeroset!-list n;
  252. % Produces a list of lists each of length n with all combinations of
  253. % ones and zeroes;
  254. begin scalar w;
  255. for k:=0:n do w:=append(w,kcombns(k,n));
  256. return w
  257. end;
  258. symbolic procedure kcombns(k,m);
  259. % produces a list of all combinations of ones and zeroes with k ones
  260. % in each;
  261. if k=0 or k=m then begin scalar w;
  262. if k=m then k:=1;
  263. for i:=1:m do w:=k.w;
  264. return list w
  265. end
  266. else if k=1 or k=isub1 m then <<
  267. if k=isub1 m then k:=0;
  268. list!-with!-one!-a(k,1 #- k,m) >>
  269. else append(
  270. for each x in kcombns(isub1 k,isub1 m) collect (1 . x),
  271. for each x in kcombns(k,isub1 m) collect (0 . x) );
  272. symbolic procedure list!-with!-one!-a(a,b,m);
  273. % Creates list of all lists with one a and m-1 b's in;
  274. begin scalar w,x,r;
  275. for i:=1:isub1 m do w:=b . w;
  276. r:=list(a . w);
  277. for i:=1:isub1 m do <<
  278. x:=(car w) . x; w:=cdr w;
  279. r:=append(x,(a . w)) . r >>;
  280. return r
  281. end;
  282. symbolic procedure make!-next!-zset l;
  283. begin scalar k,w;
  284. image!-set!-modulus:=iadd1 image!-set!-modulus;
  285. set!-modulus image!-set!-modulus;
  286. w:=for each ll in cdr l collect
  287. for each n in ll collect
  288. if n=0 then n
  289. else <<
  290. k:=modular!-number next!-random!-number();
  291. while (zerop k) or (onep k) do
  292. k:=modular!-number next!-random!-number();
  293. if k>modulus!/2 then k:=k-current!-modulus;
  294. k >>;
  295. save!-zset:=nil;
  296. return w
  297. end;
  298. endmodule;
  299. end;