1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-11 00:00:49 +02:00
guile/module/slib/modular.scm
2001-04-14 11:24:45 +00:00

158 lines
4.7 KiB
Scheme

;;;; "modular.scm", modular fixnum arithmetic for Scheme
;;; Copyright (C) 1991, 1993, 1995 Aubrey Jaffer.
;
;Permission to copy this software, to redistribute it, and to use it
;for any purpose is granted, subject to the following restrictions and
;understandings.
;
;1. Any copy made of this software must include this copyright notice
;in full.
;
;2. I have made no warrantee or representation that the operation of
;this software will be error-free, and I am under no obligation to
;provide any services, by way of maintenance, update, or otherwise.
;
;3. In conjunction with products arising from the use of this
;material, there shall be no use of my name in any advertising,
;promotional, or sales literature without prior written consent in
;each case.
(define (symmetric:modulus n)
(cond ((or (not (number? n)) (not (positive? n)) (even? n))
(slib:error 'symmetric:modulus n))
(else (quotient (+ -1 n) -2))))
(define (modulus->integer m)
(cond ((negative? m) (- 1 m m))
((zero? m) #f)
(else m)))
(define (modular:normalize m k)
(cond ((positive? m) (modulo k m))
((zero? m) k)
((<= m k (- m)) k)
((or (provided? 'bignum)
(<= m (quotient (+ -1 most-positive-fixnum) 2)))
(let* ((pm (+ 1 (* -2 m)))
(s (modulo k pm)))
(if (<= s (- m)) s (- s pm))))
((positive? k) (+ (+ (+ k -1) m) m))
(else (- (- (+ k 1) m) m))))
;;;; NOTE: The rest of these functions assume normalized arguments!
(require 'logical)
(define (modular:extended-euclid x y)
(define q 0)
(do ((r0 x r1) (r1 y (remainder r0 r1))
(u0 1 u1) (u1 0 (- u0 (* q u1)))
(v0 0 v1) (v1 1 (- v0 (* q v1))))
;; (assert (= r0 (+ (* u0 x) (* v0 y))))
;; (assert (= r1 (+ (* u1 x) (* v1 y))))
((zero? r1) (list r0 u0 v0))
(set! q (quotient r0 r1))))
(define (modular:invertable? m a)
(eqv? 1 (gcd (or (modulus->integer m) 0) a)))
(define (modular:invert m a)
(cond ((eqv? 1 (abs a)) a) ; unit
(else
(let ((pm (modulus->integer m)))
(cond
(pm
(let ((d (modular:extended-euclid (modular:normalize pm a) pm)))
(if (= 1 (car d))
(modular:normalize m (cadr d))
(slib:error 'modular:invert "can't invert" m a))))
(else (slib:error 'modular:invert "can't invert" m a)))))))
(define (modular:negate m a)
(if (zero? a) 0
(if (negative? m) (- a)
(- m a))))
;;; Being careful about overflow here
(define (modular:+ m a b)
(cond ((positive? m)
(modulo (+ (- a m) b) m))
((zero? m) (+ a b))
((negative? a)
(if (negative? b)
(let ((s (+ (- a m) b)))
(if (negative? s)
(- s -1 m)
(+ s m)))
(+ a b)))
((negative? b) (+ a b))
(else (let ((s (+ (+ a m) b)))
(if (positive? s)
(+ s -1 m)
(- s m))))))
(define (modular:- m a b)
(cond ((positive? m) (modulo (- a b) m))
((zero? m) (- a b))
(else (modular:+ m a (- b)))))
;;; See: L'Ecuyer, P. and Cote, S. "Implementing a Random Number Package
;;; with Splitting Facilities." ACM Transactions on Mathematical
;;; Software, 17:98-111 (1991)
;;; modular:r = 2**((nb-2)/2) where nb = number of bits in a word.
(define modular:r
(ash 1 (quotient (integer-length most-positive-fixnum) 2)))
(define modular:*
(if (provided? 'bignum)
(lambda (m a b)
(cond ((zero? m) (* a b))
((positive? m) (modulo (* a b) m))
(else (modular:normalize m (* a b)))))
(lambda (m a b)
(let ((a0 a)
(p 0))
(cond
((zero? m) (* a b))
((negative? m)
"This doesn't work for the full range of modulus M;"
"Someone please create or convert the following"
"algorighm to work with symmetric representation"
(modular:normalize m (* a b)))
(else
(cond
((< a modular:r))
((< b modular:r) (set! a b) (set! b a0) (set! a0 a))
(else
(set! a0 (modulo a modular:r))
(let ((a1 (quotient a modular:r))
(qh (quotient m modular:r))
(rh (modulo m modular:r)))
(cond ((>= a1 modular:r)
(set! a1 (- a1 modular:r))
(set! p (modulo (- (* modular:r (modulo b qh))
(* (quotient b qh) rh)) m))))
(cond ((not (zero? a1))
(let ((q (quotient m a1)))
(set! p (- p (* (quotient b q) (modulo m a1))))
(set! p (modulo (+ (if (positive? p) (- p m) p)
(* a1 (modulo b q))) m)))))
(set! p (modulo (- (* modular:r (modulo p qh))
(* (quotient p qh) rh)) m)))))
(if (zero? a0)
p
(let ((q (quotient m a0)))
(set! p (- p (* (quotient b q) (modulo m a0))))
(modulo (+ (if (positive? p) (- p m) p)
(* a0 (modulo b q))) m)))))))))
(define (modular:expt m a b)
(cond ((= a 1) 1)
((= a (- m 1)) (if (odd? b) a 1))
((zero? a) 0)
((zero? m) (integer-expt a b))
(else
(logical:ipow-by-squaring a b 1
(lambda (c d) (modular:* m c d))))))
(define extended-euclid modular:extended-euclid)