-
Notifications
You must be signed in to change notification settings - Fork 20
/
mod-inverse.lisp
62 lines (59 loc) · 2.38 KB
/
mod-inverse.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
(defpackage :cp/mod-inverse
(:use :cl)
#+sbcl (:import-from #:sb-c #:defoptimizer #:lvar-type #:integer-type-numeric-bounds
#:derive-type #:flushable #:foldable)
#+sbcl (:import-from :sb-kernel #:specifier-type)
(:export #:mod-inverse))
(in-package :cp/mod-inverse)
#+sbcl
(eval-when (:compile-toplevel :load-toplevel :execute)
(sb-c:defknown %mod-inverse ((integer 0) (integer 1)) (integer 0)
(flushable foldable)
:overwrite-fndb-silently t)
(sb-c:defknown mod-inverse (integer (integer 1)) (integer 0)
(flushable foldable)
:overwrite-fndb-silently t)
(defun derive-mod (modulus)
(let ((high (nth-value 1 (integer-type-numeric-bounds (lvar-type modulus)))))
(specifier-type (if (integerp high)
`(integer 0 (,high))
`(integer 0)))))
(defoptimizer (%mod-inverse derive-type) ((integer modulus))
(declare (ignore integer))
(derive-mod modulus))
(defoptimizer (mod-inverse derive-type) ((integer modulus))
(declare (ignore integer))
(derive-mod modulus)))
(defun %mod-inverse (integer modulus)
(declare (optimize (speed 3) (safety 0))
#+sbcl (sb-ext:muffle-conditions sb-ext:compiler-note))
(macrolet ((frob (stype)
`(let ((a integer)
(b modulus)
(u 1)
(v 0))
(declare (,stype a b u v))
(loop until (zerop b)
for quot = (floor a b)
do (decf a (the ,stype (* quot b)))
(rotatef a b)
(decf u (the ,stype (* quot v)))
(rotatef u v))
(if (< u 0)
(+ u modulus)
u))))
(typecase modulus
((unsigned-byte 31) (frob (signed-byte 32)))
((unsigned-byte 62) (frob (signed-byte 63)))
(otherwise (frob integer)))))
(declaim (inline mod-inverse))
(defun mod-inverse (integer modulus)
"Solves ax = 1 mod m. Signals DIVISION-BY-ZERO when INTEGER and MODULUS are
not coprime."
(let* ((integer (mod integer modulus))
(result (%mod-inverse integer modulus)))
(unless (or (= 1 (mod (* integer result) modulus)) (= 1 modulus))
(error 'division-by-zero
:operands (list integer modulus)
:operation 'mod-inverse))
result))