modular.red 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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 general!-reciprocal!-by!-degree in genmod
  56. % without error call.
  57. if b=0 then 0
  58. else if b=1 then if y < 0 then y+current!-modulus else y
  59. else begin scalar w;
  60. w := a/b;
  61. return !:mod!:units(b,a-b*w,y,x-y*w)
  62. end;
  63. symbolic procedure !*i2mod u;
  64. % Converts integer U to modular form.
  65. % if (u := general!-modular!-number u)=0 then nil else '!:mod!: . u;
  66. !*modular2f general!-modular!-number u;
  67. symbolic procedure modcnv u;
  68. rerror(poly,13,list("Conversion between modular integers and",
  69. get(car u,'dname),"not defined"));
  70. symbolic procedure modminusp!: u;
  71. if !*balanced_mod then 2*cdr u > current!-modulus else nil;
  72. symbolic procedure modplus!:(u,v);
  73. !*modular2f general!-modular!-plus(cdr u,cdr v);
  74. symbolic procedure modtimes!:(u,v);
  75. !*modular2f general!-modular!-times(cdr u,cdr v);
  76. symbolic procedure moddifference!:(u,v);
  77. !*modular2f general!-modular!-difference(cdr u,cdr v);
  78. symbolic procedure moddivide!:(u,v); !*i2mod 0 . u;
  79. symbolic procedure modgcd!:(u,v); !*i2mod 1;
  80. symbolic procedure modquotient!:(u,v);
  81. !*modular2f general!-modular!-times(cdr u,
  82. general!-modular!-reciprocal cdr v);
  83. symbolic procedure modzerop!: u; cdr u=0;
  84. symbolic procedure modonep!: u; cdr u=1;
  85. symbolic procedure factormod!: u;
  86. begin scalar alglist!*,dmode!*;
  87. % 1 is needed since factorize expects first factor to be a number.
  88. return pfactor(!*q2f resimp(u ./ 1),current!-modulus)
  89. end;
  90. symbolic procedure modprep!: u;
  91. cdr u;
  92. initdmode 'modular;
  93. % Modular routines are defined in the GENMOD module with the exception
  94. % of the following:
  95. symbolic procedure setmod u;
  96. % Returns value of CURRENT!-MODULUS on entry unless an error
  97. % occurs. It crudely distinguishes between prime moduli, for which
  98. % division is possible, and others, for which it possibly is not.
  99. % The code should really distinguish prime powers and composites as
  100. % well.
  101. begin scalar dmode!*;
  102. if not atom u then u := car u; % Since setmod is a psopfn.
  103. u := reval u; % dmode* is NIL, so this won't be reduced wrt
  104. % current modulus.
  105. if fixp u and u>0
  106. then <<if primep u then flag('(!:mod!:),'field)
  107. else remflag('(!:mod!:),'field);
  108. return set!-general!-modulus u>>
  109. else if u=0 or null u then return current!-modulus
  110. else typerr(u,"modulus")
  111. end;
  112. put('setmod, 'psopfn, 'setmod);
  113. % A more general definition of general-modular-number.
  114. %symbolic procedure general!-modular!-number m;
  115. % Returns normalized M.
  116. % (lambda n; %if n<0 then n+current!-modulus else n)
  117. % if atom m then remainder(m,current!-modulus)
  118. % else begin scalar x;
  119. % x := dcombine(m,current!-modulus,'divide);
  120. % return cdr x
  121. % end;
  122. % Support for "mod" as an infix operator.
  123. infix mod;
  124. precedence mod,over;
  125. put('mod,'psopfn,'evalmod);
  126. symbolic procedure evalmod u;
  127. begin scalar dm,cp,m,mm,w,!*rounded,!*modular;
  128. if !*complex then
  129. <<cp:=t; setdmode('complex,nil); !*complex:=nil>>;
  130. if (dm:=get(dmode!*,'dname)) then setdmode(dm,nil);
  131. m:=ieval cadr u;
  132. setdmode('modular,t); !*modular:=t;
  133. mm:=apply1('setmod,{m});
  134. w:=aeval!* car u;
  135. apply1('setmod,{mm});
  136. if dm neq 'modular then
  137. <<setdmode('modular,nil); if dm then setdmode(dm,t)>>;
  138. if cp then <<setdmode('complex,t); !*complex :=t>>;
  139. return w;
  140. end;
  141. % Support for function evaluation in the modular domain.
  142. % At present only rational exponentiation, including surds.
  143. put('!:mod!:,'domainvalchk,'mod!-domainvalchk);
  144. symbolic procedure mod!-domainvalchk(fn,u);
  145. begin scalar w;
  146. w:=if fn='expt then mod!-expt!-fract(car u,cadr u)
  147. else nil;
  148. return if w='failed then nil else w ./1;
  149. end;
  150. symbolic procedure mod!-expt!-fract(u,x);
  151. % Modular u**x where x is a rational number n/m. Compute a solution of
  152. % q^n=u^m. If *precise on, expressions with non-unique are not
  153. % simplified. Non existing surds are mapped to an error message.
  154. begin scalar n,m,w;
  155. if denr u =1 then u:=numr u else go to done;
  156. if eqcar(u,'!:mod!:) then t
  157. else if fixp u then u:= '!:mod!: . u else go to done;
  158. if u='(!:mod!: . 1) then return 1;
  159. n:=numr x; m:=denr x;
  160. if not fixp n or not fixp m then go to done;
  161. if m=1 then return exptmod!:(u,n);
  162. load!-package 'modsr;
  163. w := msolve {{'equal,{'expt,'x,m},{'expt,cdr u,n}}};
  164. if w eq 'failed then return w else w := cdr w;
  165. if null w then mod!-error({'expt,u,{'quotient,n,m}});
  166. if null cdr w or null !*precise then return caddr cadr car w;
  167. % value is not unique - prevent the default integer
  168. % handling that would compute an incorrect value.
  169. % e.g. sqrt(4) mod 9 is not 2 but {2,7}.
  170. return !*k2f car fkern {'expt,cdr u,{'quotient,n,m}};
  171. done:
  172. return if null w or cdr w then 'failed else caddr car w;
  173. end;
  174. symbolic procedure mod!-error u;
  175. typerr(u, {"expression mod", current!-modulus});
  176. endmodule;
  177. end;