modarith.scm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #| -*-Scheme-*-
  2. Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
  3. 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
  4. 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Massachusetts
  5. Institute of Technology
  6. This file is part of MIT/GNU Scheme.
  7. MIT/GNU Scheme is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or (at
  10. your option) any later version.
  11. MIT/GNU Scheme is distributed in the hope that it will be useful, but
  12. WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with MIT/GNU Scheme; if not, write to the Free Software
  17. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
  18. USA.
  19. |#
  20. ;;;; Modular Integer Arithmetic
  21. (declare (usual-integrations))
  22. (define modular-type-tag '*modular-integer*)
  23. (define (modint? x)
  24. (and (pair? x)
  25. (eq? (car x) modular-type-tag)))
  26. (define (mod:make n p)
  27. (mod:make-internal (mod:reduce n p) p))
  28. (define (mod:make-internal residue p)
  29. (list modular-type-tag residue p))
  30. (define (mod:residue modint)
  31. (list-ref modint 1))
  32. (define (mod:modulus modint)
  33. (list-ref modint 2))
  34. (define (mod:reduce n p)
  35. (modulo n p))
  36. (define (mod:unary-combine uop)
  37. (define (moduop x)
  38. (assert (modint? x)
  39. "Not a modular integer" (list uop x))
  40. (let ((modulus (mod:modulus x)))
  41. (mod:make-internal
  42. (uop (mod:residue x) modulus)
  43. modulus)))
  44. moduop)
  45. ;;; Given a, p finds b such that a*b=1 mod p
  46. ;;; by solving the linear Diophantine equation
  47. ;;; b*a - c*p = 1 for b and c, and returning only b.
  48. (define (modint:invert a p)
  49. (define (euclid a p cont)
  50. (if (int:= a 1)
  51. (cont 1 0)
  52. (let ((qr (integer-divide p a)))
  53. (let ((q (integer-divide-quotient qr))
  54. (r (integer-divide-remainder qr)))
  55. (euclid r a
  56. (lambda (x y)
  57. (cont (int:- y (int:* q x))
  58. x)))))))
  59. (euclid a p
  60. (lambda (x y)
  61. (mod:reduce x p))))
  62. #|
  63. (define (testinv n p)
  64. (= 1 (modint:* n (modint:invert n p) p)))
  65. (testinv 3 5)
  66. ;Value: #t
  67. |#
  68. (define mod:invert (mod:unary-combine modint:invert))
  69. (define (mod:binary-combine bop)
  70. (define (modbop x y)
  71. (assert (and (modint? x) (modint? y))
  72. "Not modular integers" (list bop x y))
  73. (let ((modulus (mod:modulus x)))
  74. (assert (int:= modulus (mod:modulus y))
  75. "Not same modulus" (list bop x y))
  76. (mod:make-internal
  77. (bop (mod:residue x)
  78. (mod:residue y)
  79. modulus)
  80. modulus)))
  81. modbop)
  82. (define (modint:+ x y p)
  83. (mod:reduce (int:+ x y) p))
  84. (define (modint:- x y p)
  85. (mod:reduce (int:- x y) p))
  86. (define (modint:* x y p)
  87. (mod:reduce (int:* x y) p))
  88. (define (modint:/ x y p)
  89. (mod:reduce (int:* x (modint:invert y p)) p))
  90. (define (modint:expt base exponent p)
  91. (define (square x)
  92. (modint:* x x p))
  93. (let lp ((exponent exponent))
  94. (cond ((int:= exponent 0) 1)
  95. ((even? exponent)
  96. (square (lp (quotient exponent 2))))
  97. (else
  98. (modint:* base (lp (int:- exponent 1)) p)))))
  99. (define mod:+ (mod:binary-combine modint:+))
  100. (define mod:- (mod:binary-combine modint:-))
  101. (define mod:* (mod:binary-combine modint:*))
  102. (define mod:/ (mod:binary-combine modint:/))
  103. (define mod:expt (mod:binary-combine modint:expt))
  104. (define (mod:= x y)
  105. (assert (and (modint? x) (modint? y))
  106. "Not modular integers -- =" (list x y))
  107. (let ((modulus (mod:modulus x)))
  108. (assert (int:= modulus (mod:modulus y))
  109. "Not same modulus -- =" (list x y))
  110. (int:= (modulo (mod:residue x) modulus)
  111. (modulo (mod:residue y) modulus))))
  112. ;;; Chinese Remainder Algorithm
  113. ;;; Takes a list of modular integers, m[i] (modulo p[i])
  114. ;;; where the p[i] are relatively prime.
  115. ;;; Finds x such that m[i] = x mod p[i]
  116. (define (mod:chinese-remainder . modints)
  117. (assert (for-all? modints modint?))
  118. (let ((moduli (map mod:modulus modints))
  119. (residues (map mod:residue modints)))
  120. ((modint:chinese-remainder moduli) residues)))
  121. (define (modint:chinese-remainder moduli)
  122. (let ((prod (apply * moduli)))
  123. (let ((cofactors
  124. (map (lambda (p)
  125. (quotient prod p))
  126. moduli)))
  127. (let ((f
  128. (map (lambda (c p)
  129. (* c (modint:invert c p)))
  130. cofactors
  131. moduli)))
  132. (lambda (residues)
  133. (mod:reduce
  134. (apply + (map * residues f))
  135. prod))))))
  136. #|
  137. (define a1 (mod:make 2 5))
  138. (define a2 (mod:make 3 13))
  139. (mod:chinese-remainder a1 a2)
  140. (mod:chinese-remainder a1 a2)
  141. ;Value: 42
  142. |#
  143. (define %kernel-modarith-dummy-1
  144. (begin
  145. (assign-operation 'invert mod:invert modint?)
  146. (assign-operation '+ mod:+ modint? modint?)
  147. (assign-operation '- mod:- modint? modint?)
  148. (assign-operation '* mod:* modint? modint?)
  149. (assign-operation '/ mod:/ modint? modint?)
  150. (assign-operation 'expt mod:expt modint? modint?)
  151. (assign-operation '= mod:= modint? modint?)))
  152. #|
  153. (define (test p)
  154. (let jlp ((j (- p)))
  155. (cond ((int:= j p) 'ok)
  156. (else
  157. (let ilp ((i (- p)))
  158. ;;(write-line `(trying ,i ,j))
  159. (cond ((int:= i p) (jlp (int:+ j 1)))
  160. ((int:= (modulo i p) 0) (ilp (int:+ i 1)))
  161. (else
  162. (let ((jp (mod:make j p))
  163. (ip (mod:make i p)))
  164. (let ((b (mod:/ jp ip)))
  165. (if (mod:= (mod:* b ip) jp)
  166. (ilp (int:+ i 1))
  167. (begin (write-line `(problem dividing ,j ,i))
  168. (write-line `((/ ,jp ,ip) = ,(mod:/ jp ip)))
  169. (write-line `((* ,b ,ip) = ,(mod:* b ip))))))))))))))
  170. (test 47)
  171. ;Value: ok
  172. |#