genmod.red 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. module genmod; % Modular arithmetic where the modulus may be any size.
  2. % Authors: A. C. Norman and P. M. A. Moore, 1981.
  3. % Modifications by: John Abbott.
  4. % Note: when balanced_mod is on, the results here are not always in
  5. % range. *modular2f is used to correct this. However, these routines
  6. % should be updated to give balanced results.
  7. fluid '(!*balanced_mod current!-modulus modulus!/2);
  8. global '(largest!-small!-modulus);
  9. symbolic procedure set!-general!-modulus p;
  10. if not numberp p or p=0 then current!-modulus
  11. else begin
  12. scalar previous!-modulus;
  13. previous!-modulus:=current!-modulus;
  14. current!-modulus:=p;
  15. modulus!/2 := p/2;
  16. % Allow for use of small moduli where appropriate.
  17. if p <= largest!-small!-modulus then set!-small!-modulus p;
  18. return previous!-modulus
  19. end;
  20. symbolic procedure general!-modular!-plus(a,b);
  21. begin scalar result;
  22. result:=a+b;
  23. if result >= current!-modulus then result:=result-current!-modulus;
  24. return result
  25. end;
  26. symbolic procedure general!-modular!-difference(a,b);
  27. begin scalar result;
  28. result := a - b;
  29. if result < 0 then result:=result+current!-modulus;
  30. return result
  31. end;
  32. symbolic procedure general!-modular!-number a;
  33. begin
  34. a:=remainder(a,current!-modulus);
  35. if a < 0 then a:=a+current!-modulus;
  36. return a
  37. end;
  38. symbolic procedure general!-modular!-times(a,b);
  39. begin scalar result;
  40. result:=remainder(a*b,current!-modulus);
  41. if result<0
  42. then result := result+current!-modulus; %can this happen?
  43. return result
  44. end;
  45. symbolic procedure general!-modular!-reciprocal a;
  46. % Note this returns a positive result.
  47. if !*balanced_mod and a<0
  48. then general!-reciprocal!-by!-gcd(current!-modulus,
  49. a + current!-modulus,0,1)
  50. else general!-reciprocal!-by!-gcd(current!-modulus,a,0,1);
  51. symbolic procedure general!-modular!-quotient(a,b);
  52. general!-modular!-times(a,general!-modular!-reciprocal b);
  53. symbolic procedure general!-modular!-minus a;
  54. if a=0 then a else current!-modulus - a;
  55. symbolic procedure general!-reciprocal!-by!-gcd(a,b,x,y);
  56. %On input A and B should be coprime. This routine then
  57. %finds X and Y such that A*X+B*Y=1, and returns the value Y
  58. %on input A > B;
  59. if b=0 then rerror(alg,8,"Invalid modular division")
  60. else if b=1 then if y < 0 then y+current!-modulus else y
  61. else begin scalar w;
  62. %N.B. Invalid modular division is either:
  63. % a) attempt to divide by zero directly
  64. % b) modulus is not prime, and input is not
  65. % coprime with it;
  66. w:=quotient(a,b); %Truncated integer division;
  67. return general!-reciprocal!-by!-gcd(b,a-b*w,y,x-y*w)
  68. end;
  69. % The next two functions compute the "reverse" of a binary number.
  70. % This is the number obtained when writing down the binary expansion
  71. % in reverse order. If 2^r divides n (but 2^(r+1) does not) then
  72. % reverse-num(reverse-num(n)) = abs(n)/2^r. r can be computed using
  73. % height2.
  74. symbolic procedure reverse!-num(n);
  75. if n = 0 then n
  76. else if n<0 then -reverse!-num1(-n,ilog2(-n)+1)
  77. else reverse!-num1(n,ilog2(n)+1);
  78. global '(reverse!-num!-table!*);
  79. reverse!-num!-table!* := mkvect 16;
  80. putv(reverse!-num!-table!*,1,8);
  81. putv(reverse!-num!-table!*,2,4);
  82. putv(reverse!-num!-table!*,3,12);
  83. putv(reverse!-num!-table!*,4,2);
  84. putv(reverse!-num!-table!*,5,10);
  85. putv(reverse!-num!-table!*,6,6);
  86. putv(reverse!-num!-table!*,7,14);
  87. putv(reverse!-num!-table!*,8,1);
  88. putv(reverse!-num!-table!*,9,9);
  89. putv(reverse!-num!-table!*,10,5);
  90. putv(reverse!-num!-table!*,11,13);
  91. putv(reverse!-num!-table!*,12,3);
  92. putv(reverse!-num!-table!*,13,11);
  93. putv(reverse!-num!-table!*,14,7);
  94. putv(reverse!-num!-table!*,15,15);
  95. symbolic procedure reverse!-num1(n,bits);
  96. if n = 0 then 0
  97. else if bits = 1 then n
  98. else if bits = 2 then getv(reverse!-num!-table!*,4*n)
  99. else if bits = 3 then getv(reverse!-num!-table!*,2*n)
  100. else if bits = 4 then getv(reverse!-num!-table!*,n)
  101. else begin scalar shift,qr;
  102. shift := 2**(bits/2);
  103. qr := divide(n,shift);
  104. if not evenp bits then shift := shift*2;
  105. return reverse!-num1(cdr qr,bits/2)*shift +
  106. reverse!-num1(car qr,(bits+1)/2)
  107. end;
  108. % Interface to algebraic mode.
  109. flag('(reverse!-num),'integer);
  110. deflist('((reverse!-num rnreverse!-num!*)),'!:rn!:);
  111. %put('fibonacci,'!:rn!:,'rnfibonacci!*);
  112. put('reverse!-num,'number!-of!-args,1);
  113. put('reverse!-num,'simpfn,'simpiden);
  114. symbolic procedure rnreverse!-num!*(x);
  115. (if fixp y then reverse!-num(y)
  116. else !*p2f mksp(list('reverse!-num,y),1))
  117. where y=rnfixchk x;
  118. % Interface to algebraic mode.
  119. put('reverse!-num, 'simpfn, 'simpreverse!-num);
  120. symbolic procedure simpreverse!-num(u);
  121. begin scalar arg;
  122. if length(u) neq 1 then typerr(u,"integer");
  123. arg := simpcar u;
  124. if denr(arg) neq 1 or not fixp(numr(arg))
  125. then rederr("reverse!-num: argument should be an integer");
  126. return reverse!-num(numr(arg)) ./ 1
  127. end;
  128. % This is an iterative version of general!-modular!-expt.
  129. % Its principal advantage over the (simpler) recursive implementation
  130. % is that it avoids excessive memory consumption when both n and the
  131. % modulus are quite large -- try primep(2^10007-1) if you don't believe
  132. % it!
  133. symbolic procedure general!-modular!-expt(a,n);
  134. % Computes a**n modulo current-modulus. Uses Fermat's Little
  135. % Theorem where appropriate for a prime modulus.
  136. if a=0 then if n=0 then rerror(alg,101,"0^0 formed") else 0
  137. else if n=0 then 1
  138. else if n=1 then a
  139. else if n>=current!-modulus-1 and primep current!-modulus
  140. then general!-modular!-expt(a,remainder(n,current!-modulus-1))
  141. else begin scalar x, revn;
  142. while evenp n do <<n := n/2; a := general!-modular!-times(a,a)>>;
  143. revn := reverse!-num n;
  144. x := 1;
  145. while revn>0 do
  146. <<x := general!-modular!-times(x,x);
  147. if not evenp(revn) then x := general!-modular!-times(x,a);
  148. revn := revn/2>>;
  149. return x
  150. end;
  151. endmodule;
  152. end;