modular.red 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. module modular; % *** Tables for modular integers ***.
  2. % Author: Anthony C. Hearn and Herbert Melenk.
  3. % Copyright (c) 1995 The RAND Corporation. All rights reserved.
  4. global '(domainlist!*);
  5. fluid '(!*balanced_mod !*modular !*precise current!-modulus alglist!*
  6. dmode!*);
  7. switch modular,balanced_mod;
  8. domainlist!* := union('(!:mod!:),domainlist!*);
  9. put('modular,'tag,'!:mod!:);
  10. put('!:mod!:,'dname,'modular);
  11. flag('(!:mod!:),'field);
  12. flag('(!:mod!:),'convert);
  13. put('!:mod!:,'i2d,'!*i2mod);
  14. put('!:mod!:,'!:ft!:,'modcnv);
  15. put('!:mod!:,'!:rn!:,'modcnv);
  16. put('!:mod!:,'minusp,'modminusp!:);
  17. put('!:mod!:,'plus,'modplus!:);
  18. put('!:mod!:,'times,'modtimes!:);
  19. put('!:mod!:,'difference,'moddifference!:);
  20. put('!:mod!:,'quotient,'modquotient!:);
  21. put('!:mod!:,'divide,'moddivide!:);
  22. put('!:mod!:,'gcd,'modgcd!:);
  23. put('!:mod!:,'zerop,'modzerop!:);
  24. put('!:mod!:,'onep,'modonep!:);
  25. put('!:mod!:,'factorfn,'factormod!:);
  26. put('!:mod!:,'sqfrfactorfn,'factormod!:);
  27. put('!:mod!:,'expt,'exptmod!:);
  28. put('!:mod!:,'prepfn,'modprep!:);
  29. put('!:mod!:,'prifn,'(lambda(x) (prin2!* (prepf x))));
  30. put('!:mod!:,'unitsfn,'!:mod!:unitconv);
  31. symbolic procedure !*modular2f u;
  32. % Convert u to a modular number. Treat 0 as special case, but not 1.
  33. % Also allow for !*balanced_mod.
  34. if u=0 then nil
  35. % else if u=1 then 1
  36. else if !*balanced_mod
  37. then if u+u>current!-modulus
  38. then '!:mod!: . (u - current!-modulus)
  39. else if u+u <= - current!-modulus
  40. then !*modular2f(u + current!-modulus)
  41. else '!:mod!: . u
  42. else '!:mod!: . u;
  43. symbolic procedure exptmod!:(u,n);
  44. % This procedure will check for cdr u > n-1 if n prime.
  45. % This used to treat 1 as a special case.
  46. !*modular2f general!-modular!-expt(cdr u,n);
  47. symbolic procedure !:mod!:unitconv(u,v);
  48. if v=1 then u else
  49. (if x then multd(x,numr u) ./ multd(x,denr u)
  50. else mod!-error {'quotient,1,cdr v})
  51. where x = !*modular2f !:mod!:units(current!-modulus,y,0,1)
  52. where y = if cdr v>0 or null !*balanced_mod then cdr v
  53. else current!-modulus+cdr v;
  54. symbolic procedure !:mod!:units(a,b,x,y);
  55. % Same procedure as in genmod without error call.
  56. if b=0 then 0
  57. else if b=1 then if y < 0 then y+current!-modulus else y
  58. else begin scalar w;
  59. w := a/b;
  60. return !:mod!:units(b,a-b*w,y,x-y*w)
  61. end;
  62. symbolic procedure !*i2mod u;
  63. % Converts integer U to modular form.
  64. % if (u := general!-modular!-number u)=0 then nil else '!:mod!: . u;
  65. !*modular2f general!-modular!-number u;
  66. symbolic procedure modcnv u;
  67. rerror(poly,13,list("Conversion between modular integers and",
  68. get(car u,'dname),"not defined"));
  69. symbolic procedure modminusp!: u;
  70. if !*balanced_mod then 2*cdr u > current!-modulus else nil;
  71. symbolic procedure modplus!:(u,v);
  72. !*modular2f general!-modular!-plus(cdr u,cdr v);
  73. symbolic procedure modtimes!:(u,v);
  74. !*modular2f general!-modular!-times(cdr u,cdr v);
  75. symbolic procedure moddifference!:(u,v);
  76. !*modular2f general!-modular!-difference(cdr u,cdr v);
  77. symbolic procedure moddivide!:(u,v); !*i2mod 0 . u;
  78. symbolic procedure modgcd!:(u,v); !*i2mod 1;
  79. symbolic procedure modquotient!:(u,v);
  80. !*modular2f general!-modular!-times(cdr u,
  81. general!-modular!-reciprocal cdr v);
  82. symbolic procedure modzerop!: u; cdr u=0;
  83. symbolic procedure modonep!: u; cdr u=1;
  84. symbolic procedure factormod!: u;
  85. begin scalar alglist!*,dmode!*;
  86. % 1 is needed since factorize expects first factor to be a number.
  87. return pfactor(!*q2f resimp(u ./ 1),current!-modulus)
  88. end;
  89. symbolic procedure modprep!: u;
  90. cdr u;
  91. initdmode 'modular;
  92. % Modular routines are defined in the GENMOD module with the exception
  93. % of the following:
  94. symbolic procedure setmod u;
  95. % Returns value of CURRENT!-MODULUS on entry unless an error
  96. % occurs. It crudely distinguishes between prime moduli, for which
  97. % division is possible, and others, for which it possibly is not.
  98. % The code should really distinguish prime powers and composites as
  99. % well.
  100. begin scalar dmode!*;
  101. if not atom u then u := car u; % Since setmod is a psopfn.
  102. u := reval u; % dmode* is NIL, so this won't be reduced wrt
  103. % current modulus.
  104. if fixp u and u>0
  105. then <<if primep u then flag('(!:mod!:),'field)
  106. else remflag('(!:mod!:),'field);
  107. return set!-general!-modulus u>>
  108. else if u=0 or null u then return current!-modulus
  109. else typerr(u,"modulus")
  110. end;
  111. put('setmod, 'psopfn, 'setmod);
  112. % A more general definition of general-modular-number.
  113. %symbolic procedure general!-modular!-number m;
  114. % Returns normalized M.
  115. % (lambda n; %if n<0 then n+current!-modulus else n)
  116. % if atom m then remainder(m,current!-modulus)
  117. % else begin scalar x;
  118. % x := dcombine(m,current!-modulus,'divide);
  119. % return cdr x
  120. % end;
  121. % Support for "mod" as an infix operator.
  122. infix mod;
  123. precedence mod,over;
  124. put('mod,'psopfn,'evalmod);
  125. symbolic procedure evalmod u;
  126. begin scalar dm,cp,m,mm,w,!*rounded,!*modular;
  127. if !*complex then
  128. <<cp:=t; setdmode('complex,nil); !*complex:=nil>>;
  129. if (dm:=get(dmode!*,'dname)) then setdmode(dm,nil);
  130. m:=ieval cadr u;
  131. setdmode('modular,t); !*modular:=t;
  132. mm:=apply1('setmod,{m});
  133. w:=aeval!* car u;
  134. apply1('setmod,{mm});
  135. if dm neq 'modular then
  136. <<setdmode('modular,nil); if dm then setdmode(dm,t)>>;
  137. if cp then <<setdmode('complex,t); !*complex :=t>>;
  138. return w;
  139. end;
  140. % Support for function evaluation in the modular domain.
  141. % At present only rational exponentiation, including surds.
  142. put('!:mod!:,'domainvalchk,'mod!-domainvalchk);
  143. symbolic procedure mod!-domainvalchk(fn,u);
  144. begin scalar w;
  145. w:=if fn='expt then mod!-expt!-fract(car u,cadr u)
  146. else nil;
  147. return if w='failed then nil else w ./1;
  148. end;
  149. symbolic procedure mod!-expt!-fract(u,x);
  150. % Modular u**x where x is a rational number n/m. Compute a solution of
  151. % q^n=u^m. If *precise on, expressions with non-unique are not
  152. % simplified. Non existing surds are mapped to an error message.
  153. begin scalar n,m,w;
  154. if denr u =1 then u:=numr u else go to done;
  155. if eqcar(u,'!:mod!:) then t
  156. else if fixp u then u:= '!:mod!: . u else go to done;
  157. if u='(!:mod!: . 1) then return 1;
  158. n:=numr x; m:=denr x;
  159. if not fixp n or not fixp m then go to done;
  160. if m=1 then return exptmod!:(u,n);
  161. load!-package 'modsr;
  162. w:=cdr msolve {{'equal,{'expt,'x,m},{'expt,cdr u,n}}};
  163. if null w then mod!-error({'expt,u,{'quotient,n,m}});
  164. if null cdr w or null !*precise then return caddr cadr car w;
  165. % value is not unique - prevent the default integer
  166. % handling that would compute an incorrect value.
  167. % e.g. sqrt(4) mod 9 is not 2 but {2,7}.
  168. return !*k2f car fkern {'expt,cdr u,{'quotient,n,m}};
  169. done:
  170. return if null w or cdr w then 'failed else caddr car w;
  171. end;
  172. symbolic procedure mod!-error u;
  173. typerr(u, {"expression mod", current!-modulus});
  174. endmodule;
  175. end;