+(eval-when (:compile-toplevel :load-toplevel :execute)
+ ;; The asserts in the GCD implementation are way too expensive to
+ ;; check in normal use, and are disabled here.
+ (sb!xc:defmacro gcd-assert (&rest args)
+ (if nil
+ `(assert ,@args)))
+ ;; We'll be doing a lot of modular arithmetic.
+ (sb!xc:defmacro M (form)
+ `(logand all-ones-digit ,form)))
+
+;;; I'm not sure why I need this FTYPE declaration. Compiled by the
+;;; target compiler, it can deduce the return type fine, but without
+;;; it, we pay a heavy price in BIGNUM-GCD when compiled by the
+;;; cross-compiler. -- CSR, 2004-07-19
+(declaim (ftype (sfunction (bignum-type bignum-index bignum-type bignum-index)
+ sb!vm::positive-fixnum)
+ bignum-factors-of-two))
+(defun bignum-factors-of-two (a len-a b len-b)
+ (declare (type bignum-index len-a len-b) (type bignum-type a b))
+ (do ((i 0 (1+ i))
+ (end (min len-a len-b)))
+ ((= i end) (error "Unexpected zero bignums?"))
+ (declare (type bignum-index i end))
+ (let ((or-digits (%logior (%bignum-ref a i) (%bignum-ref b i))))
+ (unless (zerop or-digits)
+ (return (do ((j 0 (1+ j))
+ (or-digits or-digits (%ashr or-digits 1)))
+ ((oddp or-digits) (+ (* i digit-size) j))
+ (declare (type (mod #.sb!vm:n-word-bits) j))))))))
+
+;;; Multiply a bignum buffer with a fixnum or a digit, storing the
+;;; result in another bignum buffer, and without using any
+;;; temporaries. Inlined to avoid boxing smallnum if it's actually a
+;;; digit. Needed by GCD, should possibly OAOO with
+;;; MULTIPLY-BIGNUM-AND-FIXNUM.
+(declaim (inline multiply-bignum-buffer-and-smallnum-to-buffer))
+(defun multiply-bignum-buffer-and-smallnum-to-buffer (bignum bignum-len
+ smallnum res)
+ (declare (type bignum-type bignum))
+ (let* ((bignum-plus-p (%bignum-0-or-plusp bignum bignum-len))
+ (smallnum-plus-p (not (minusp smallnum)))
+ (smallnum (if smallnum-plus-p smallnum (- smallnum)))
+ (carry-digit 0))
+ (declare (type bignum-type bignum res)
+ (type bignum-index bignum-len)
+ (type bignum-element-type smallnum carry-digit))
+ (unless bignum-plus-p
+ (negate-bignum-buffer-in-place bignum bignum-len))
+ (dotimes (index bignum-len)
+ (declare (type bignum-index index))
+ (multiple-value-bind (next-digit low)
+ (%multiply-and-add (%bignum-ref bignum index)
+ smallnum
+ carry-digit)
+ (declare (type bignum-element-type next-digit low))
+ (setf carry-digit next-digit)
+ (setf (%bignum-ref res index) low)))
+ (setf (%bignum-ref res bignum-len) carry-digit)
+ (unless bignum-plus-p
+ (negate-bignum-buffer-in-place bignum bignum-len))
+ (let ((res-len (%normalize-bignum-buffer res (1+ bignum-len))))
+ (unless (eq bignum-plus-p smallnum-plus-p)
+ (negate-bignum-buffer-in-place res res-len))
+ res-len)))
+
+;;; Given U and V, return U / V mod 2^32. Implements the algorithm in the
+;;; paper, but uses some clever bit-twiddling nicked from Nickle to do it.
+(declaim (inline bmod))
+(defun bmod (u v)
+ (let ((ud (%bignum-ref u 0))
+ (vd (%bignum-ref v 0))
+ (umask 0)
+ (imask 1)
+ (m 0))
+ (declare (type (unsigned-byte #.sb!vm:n-word-bits) ud vd umask imask m))
+ (dotimes (i digit-size)
+ (setf umask (logior umask imask))
+ (unless (zerop (logand ud umask))
+ (setf ud (M (- ud vd)))
+ (setf m (M (logior m imask))))
+ (setf imask (M (ash imask 1)))
+ (setf vd (M (ash vd 1))))
+ m))
+
+(defun dmod (u u-len v v-len tmp1)
+ (loop while (> (bignum-buffer-integer-length u u-len)
+ (+ (bignum-buffer-integer-length v v-len)
+ digit-size))
+ do
+ (unless (zerop (%bignum-ref u 0))
+ (let* ((bmod (bmod u v))
+ (tmp1-len (multiply-bignum-buffer-and-smallnum-to-buffer v v-len
+ bmod
+ tmp1)))
+ (setf u-len (subtract-bignum-buffers u u-len
+ tmp1 tmp1-len
+ u))
+ (bignum-abs-buffer u u-len)))
+ (gcd-assert (zerop (%bignum-ref u 0)))
+ (setf u-len (bignum-buffer-ashift-right u u-len digit-size)))
+ (let* ((d (+ 1 (- (bignum-buffer-integer-length u u-len)
+ (bignum-buffer-integer-length v v-len))))
+ (n (1- (ash 1 d))))
+ (declare (type (unsigned-byte #.(integer-length #.sb!vm:n-word-bits)) d)
+ (type (unsigned-byte #.sb!vm:n-word-bits) n))
+ (gcd-assert (>= d 0))
+ (unless (zerop (logand (%bignum-ref u 0) n))
+ (let ((tmp1-len
+ (multiply-bignum-buffer-and-smallnum-to-buffer v v-len
+ (logand n (bmod u
+ v))
+ tmp1)))
+ (setf u-len (subtract-bignum-buffers u u-len
+ tmp1 tmp1-len
+ u))
+ (bignum-abs-buffer u u-len)))
+ u-len))
+
+(defconstant lower-ones-digit (1- (ash 1 (truncate sb!vm:n-word-bits 2))))
+
+;;; Find D and N such that (LOGAND ALL-ONES-DIGIT (- (* D X) (* N Y))) is 0,
+;;; (< 0 N LOWER-ONES-DIGIT) and (< 0 (ABS D) LOWER-ONES-DIGIT).
+(defun reduced-ratio-mod (x y)
+ (let* ((c (bmod x y))
+ (n1 c)
+ (d1 1)
+ (n2 (M (1+ (M (lognot n1)))))
+ (d2 (M -1)))
+ (declare (type (unsigned-byte #.sb!vm:n-word-bits) n1 d1 n2 d2))
+ (loop while (> n2 (expt 2 (truncate digit-size 2))) do
+ (loop for i of-type (mod #.sb!vm:n-word-bits)
+ downfrom (- (integer-length n1) (integer-length n2))
+ while (>= n1 n2) do
+ (when (>= n1 (M (ash n2 i)))
+ (psetf n1 (M (- n1 (M (ash n2 i))))
+ d1 (M (- d1 (M (ash d2 i)))))))
+ (psetf n1 n2
+ d1 d2
+ n2 n1
+ d2 d1))
+ (values n2 (if (>= d2 (expt 2 (1- digit-size)))
+ (lognot (logand most-positive-fixnum (lognot d2)))
+ (logand lower-ones-digit d2)))))
+
+
+(defun copy-bignum (a &optional (len (%bignum-length a)))
+ (let ((b (%allocate-bignum len)))
+ (bignum-replace b a)
+ (%bignum-set-length b len)
+ b))
+
+;;; Allocate a single word bignum that holds fixnum. This is useful when
+;;; we are trying to mix fixnum and bignum operands.
+#!-sb-fluid (declaim (inline make-small-bignum))
+(defun make-small-bignum (fixnum)
+ (let ((res (%allocate-bignum 1)))
+ (setf (%bignum-ref res 0) (%fixnum-to-digit fixnum))
+ res))
+
+;; When the larger number is less than this many bignum digits long, revert
+;; to old algorithm.
+(defparameter *accelerated-gcd-cutoff* 3)
+
+;;; Alternate between k-ary reduction with the help of
+;;; REDUCED-RATIO-MOD and digit modulus reduction via DMOD. Once the
+;;; arguments get small enough, drop through to BIGNUM-MOD-GCD (since
+;;; k-ary reduction can introduce spurious factors, which need to be
+;;; filtered out). Reference: Kenneth Weber, "The accelerated integer
+;;; GCD algorithm", ACM Transactions on Mathematical Software, volume
+;;; 21, number 1, March 1995, epp. 111-122.
+(defun bignum-gcd (u0 v0)
+ (declare (type bignum-type u0 v0))
+ (let* ((u1 (if (%bignum-0-or-plusp u0 (%bignum-length u0))
+ u0
+ (negate-bignum u0 nil)))
+ (v1 (if (%bignum-0-or-plusp v0 (%bignum-length v0))
+ v0
+ (negate-bignum v0 nil))))
+ (if (zerop v1)
+ (return-from bignum-gcd u1))
+ (when (> u1 v1)
+ (rotatef u1 v1))
+ (let ((n (mod v1 u1)))
+ (setf v1 (if (fixnump n)
+ (make-small-bignum n)
+ n)))
+ (if (and (= 1 (%bignum-length v1))
+ (zerop (%bignum-ref v1 0)))
+ (return-from bignum-gcd (%normalize-bignum u1
+ (%bignum-length u1))))
+ (let* ((buffer-len (+ 2 (%bignum-length u1)))
+ (u (%allocate-bignum buffer-len))
+ (u-len (%bignum-length u1))
+ (v (%allocate-bignum buffer-len))
+ (v-len (%bignum-length v1))
+ (tmp1 (%allocate-bignum buffer-len))
+ (tmp1-len 0)
+ (tmp2 (%allocate-bignum buffer-len))
+ (tmp2-len 0)
+ (factors-of-two
+ (bignum-factors-of-two u1 (%bignum-length u1)
+ v1 (%bignum-length v1))))
+ (declare (type (or null bignum-index)
+ buffer-len u-len v-len tmp1-len tmp2-len))
+ (bignum-replace u u1)
+ (bignum-replace v v1)
+ (setf u-len
+ (make-gcd-bignum-odd u
+ (bignum-buffer-ashift-right u u-len
+ factors-of-two)))
+ (setf v-len
+ (make-gcd-bignum-odd v
+ (bignum-buffer-ashift-right v v-len
+ factors-of-two)))
+ (loop until (or (< u-len *accelerated-gcd-cutoff*)
+ (not v-len)
+ (zerop v-len)
+ (and (= 1 v-len)
+ (zerop (%bignum-ref v 0))))
+ do
+ (gcd-assert (= buffer-len (%bignum-length u)
+ (%bignum-length v)
+ (%bignum-length tmp1)
+ (%bignum-length tmp2)))
+ (if (> (bignum-buffer-integer-length u u-len)
+ (+ #.(truncate sb!vm:n-word-bits 4)
+ (bignum-buffer-integer-length v v-len)))
+ (setf u-len (dmod u u-len
+ v v-len
+ tmp1))
+ (multiple-value-bind (n d) (reduced-ratio-mod u v)
+ (setf tmp1-len
+ (multiply-bignum-buffer-and-smallnum-to-buffer v v-len
+ n tmp1))
+ (setf tmp2-len
+ (multiply-bignum-buffer-and-smallnum-to-buffer u u-len
+ d tmp2))
+ (gcd-assert (= (copy-bignum tmp2 tmp2-len)
+ (* (copy-bignum u u-len) d)))
+ (gcd-assert (= (copy-bignum tmp1 tmp1-len)
+ (* (copy-bignum v v-len) n)))
+ (setf u-len
+ (subtract-bignum-buffers-with-len tmp1 tmp1-len
+ tmp2 tmp2-len
+ u
+ (1+ (max tmp1-len
+ tmp2-len))))
+ (gcd-assert (or (zerop (- (copy-bignum tmp1 tmp1-len)
+ (copy-bignum tmp2 tmp2-len)))
+ (= (copy-bignum u u-len)
+ (- (copy-bignum tmp1 tmp1-len)
+ (copy-bignum tmp2 tmp2-len)))))
+ (bignum-abs-buffer u u-len)
+ (gcd-assert (zerop (M u)))))
+ (setf u-len (make-gcd-bignum-odd u u-len))
+ (rotatef u v)
+ (rotatef u-len v-len))
+ (setf u (copy-bignum u u-len))
+ (let ((n (bignum-mod-gcd v1 u)))
+ (ash (bignum-mod-gcd u1 (if (fixnump n)
+ (make-small-bignum n)
+ n))
+ factors-of-two)))))
+
+(defun bignum-mod-gcd (a b)