0.8.18.13:
[sbcl.git] / src / code / bignum.lisp
index e50ec32..d05a6fa 100644 (file)
 
 (defconstant maximum-bignum-length (1- (ash 1 (- sb!vm:n-word-bits
                                                  sb!vm:n-widetag-bits))))
+
+(defconstant all-ones-digit (1- (ash 1 sb!vm:n-word-bits)))
 \f
 ;;;; internal inline routines
 
 ;;; Operations requiring a subtraction without the overhead of intermediate
 ;;; results, such as GCD, use this. It assumes Result is big enough for the
 ;;; result.
-(defun subtract-bignum-buffers (a len-a b len-b result)
+(defun subtract-bignum-buffers-with-len (a len-a b len-b result len-res)
   (declare (type bignum-type a b)
           (type bignum-index len-a len-b))
-  (let ((len-res (max len-a len-b)))
-    (subtract-bignum-loop a len-a b len-b result len-res
-                         %normalize-bignum-buffer)))
+  (subtract-bignum-loop a len-a b len-b result len-res
+                       %normalize-bignum-buffer))
+
+(defun subtract-bignum-buffers (a len-a b len-b result)
+  (declare (type bignum-type a b)
+          (type bignum-index len-a len-b))  
+  (subtract-bignum-loop a len-a b len-b result (max len-a len-b)
+                       %normalize-bignum-buffer))
 \f
 ;;;; multiplication
 
 \f
 ;;;; GCD
 
+(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
                    ((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))
     (setf (%bignum-ref res 0) (%fixnum-to-digit fixnum))
     res))
 
-(defun bignum-gcd (a b)
-  (let* ((a (if (%bignum-0-or-plusp a (%bignum-length a))
-               a
-               (negate-bignum a nil)))
-        (b (if (%bignum-0-or-plusp b (%bignum-length b))
-               b
-               (negate-bignum b nil))))
-    (declare (type bignum-type a b))
-    (when (< a b)
-      (rotatef a b))
-    ;; While the length difference of A and B is sufficiently large,
-    ;; reduce using MOD (slowish, but it should equalize the sizes of
-    ;; A and B pretty quickly). After that, use the binary GCD
-    ;; algorithm to handle the rest. The initial reduction using MOD
-    ;; is sufficient to get rid of the embarrasing order of magnitude
-    ;; difference in GCD/LCM performance between SBCL and most other
-    ;; lisps.
-    ;;
-    ;; FIXME: Using a better algorithm (for example Weber's accelerated
-    ;; integer GCD) would be nice.
-    ;;   -- JES, 2004-07-31
-    (loop until (and (= (%bignum-length b) 1) (zerop (%bignum-ref b 0))) do
-         (when (<= (%bignum-length a) (1+ (%bignum-length b)))
-           (return-from bignum-gcd (bignum-binary-gcd a b)))
-         (let ((rem (mod a b)))
-           (if (fixnump rem)
-               (setf a (make-small-bignum rem))
-               (setf a rem))
-           (rotatef a b)))
-    (if (= (%bignum-length a) 1)
-       (%normalize-bignum a 1)
-       a)))
-  
+;; 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)
+  (declare (type bignum-type a b))
+  (when (< a b)
+    (rotatef a b))
+  ;; While the length difference of A and B is sufficiently large,
+  ;; reduce using MOD (slowish, but it should equalize the sizes of
+  ;; A and B pretty quickly). After that, use the binary GCD
+  ;; algorithm to handle the rest.
+  (loop until (and (= (%bignum-length b) 1) (zerop (%bignum-ref b 0))) do
+       (when (<= (%bignum-length a) (1+ (%bignum-length b)))
+         (return-from bignum-mod-gcd (bignum-binary-gcd a b)))
+       (let ((rem (mod a b)))
+         (if (fixnump rem)
+             (setf a (make-small-bignum rem))
+             (setf a rem))
+         (rotatef a b)))
+  (if (= (%bignum-length a) 1)
+      (%normalize-bignum a 1)
+      a))
+
 (defun bignum-binary-gcd (a b)
   (declare (type bignum-type a b))
   (let* ((len-a (%bignum-length a))
                   ((%digit-greater a-digit b-digit)
                    (return
                     (values a b len-b res
-                            (subtract-bignum-buffers a len-a b len-b res))))
+                            (subtract-bignum-buffers a len-a b len-b
+                                                     res))))
                   (t
                    (return
                     (values b a len-a res
                     (bignum-buffer-ashift-right a len-a
                                                 (+ (* index digit-size)
                                                    increment)))))))
+
 \f
 ;;;; negation
 
 
 ;;; This assumes bignum is positive; that is, the result of negating it will
 ;;; stay in the provided allocated bignum.
-(defun negate-bignum-in-place (bignum)
-  (bignum-negate-loop bignum (%bignum-length bignum) bignum)
+(defun negate-bignum-buffer-in-place (bignum bignum-len)
+  (bignum-negate-loop bignum bignum-len bignum)
   bignum)
+
+(defun negate-bignum-in-place (bignum)
+  (declare (inline negate-bignum-buffer-in-place))
+  (negate-bignum-buffer-in-place bignum (%bignum-length bignum)))
+
+(defun bignum-abs-buffer (bignum len)
+  (unless (%bignum-0-or-plusp bignum len)
+    (negate-bignum-in-place bignum len)))
 \f
 ;;;; shifting
 
-(defconstant all-ones-digit (1- (ash 1 sb!vm:n-word-bits)))
-
 (eval-when (:compile-toplevel :execute)
 
 ;;; This macro is used by BIGNUM-ASHIFT-RIGHT, BIGNUM-BUFFER-ASHIFT-RIGHT, and
 \f
 ;;;; integer length and logbitp/logcount
 
-(defun bignum-integer-length (bignum)
+(defun bignum-buffer-integer-length (bignum len)
   (declare (type bignum-type bignum))
-  (let* ((len (%bignum-length bignum))
-        (len-1 (1- len))
+  (let* ((len-1 (1- len))
         (digit (%bignum-ref bignum len-1)))
     (declare (type bignum-index len len-1)
             (type bignum-element-type digit))
     (+ (integer-length (%fixnum-digit-with-correct-sign digit))
        (* len-1 digit-size))))
 
+(defun bignum-integer-length (bignum)
+  (declare (type bignum-type bignum))
+  (bignum-buffer-integer-length bignum (%bignum-length bignum)))
+
 (defun bignum-logbitp (index bignum)
   (declare (type bignum-type bignum))
   (let ((len (%bignum-length bignum)))
@@ -1761,274 +2002,303 @@ IS LESS EFFICIENT BUT EASIER TO MAINTAIN. BILL SAYS THIS CODE CERTAINLY WORKS!
 ;;;
 ;;; Normalize quotient and remainder. Cons result if necessary.
 
-;;; These are used by BIGNUM-TRUNCATE and friends in the general case.
-(defvar *truncate-x*)
-(defvar *truncate-y*)
-
-;;; Divide X by Y returning the quotient and remainder. In the
-;;; general case, we shift Y to set up for the algorithm, and we use
-;;; two buffers to save consing intermediate values. X gets
-;;; destructively modified to become the remainder, and we have to
-;;; shift it to account for the initial Y shift. After we multiple
-;;; bind q and r, we first fix up the signs and then return the
-;;; normalized results.
+
+;;; This used to be split into multiple functions, which shared state
+;;; in special variables *TRUNCATE-X* and *TRUNCATE-Y*. Having so many
+;;; special variable accesses in tight inner loops was having a large
+;;; effect on performance, so the helper functions have now been
+;;; refactored into local functions and the special variables into
+;;; lexicals.  There was also a lot of boxing and unboxing of
+;;; (UNSIGNED-BYTE 32)'s going on, which this refactoring
+;;; eliminated. This improves the performance on some CL-BENCH tests
+;;; by up to 50%, which is probably signigicant enough to justify the
+;;; reduction in readability that was introduced. --JES, 2004-08-07
 (defun bignum-truncate (x y)
   (declare (type bignum-type x y))
-  (let* ((x-plusp (%bignum-0-or-plusp x (%bignum-length x)))
-        (y-plusp (%bignum-0-or-plusp y (%bignum-length y)))
-        (x (if x-plusp x (negate-bignum x nil)))
-        (y (if y-plusp y (negate-bignum y nil)))
-        (len-x (%bignum-length x))
-        (len-y (%bignum-length y)))
-    (multiple-value-bind (q r)
-       (cond ((< len-y 2)
-              (bignum-truncate-single-digit x len-x y))
-             ((plusp (bignum-compare y x))
-              (let ((res (%allocate-bignum len-x)))
-                (dotimes (i len-x)
-                  (setf (%bignum-ref res i) (%bignum-ref x i)))
-                (values 0 res)))
-             (t
-              (let ((len-x+1 (1+ len-x)))
-                (with-bignum-buffers ((*truncate-x* len-x+1)
-                                      (*truncate-y* (1+ len-y)))
-                  (let ((y-shift (shift-y-for-truncate y)))
-                    (shift-and-store-truncate-buffers x len-x y len-y y-shift)
-                    (values (return-quotient-leaving-remainder len-x+1 len-y)
-                            ;; Now that RETURN-QUOTIENT-LEAVING-REMAINDER
-                            ;; has executed, we just tidy up the remainder
-                            ;; (in *TRUNCATE-X*) and return it.
-                            (cond
-                             ((zerop y-shift)
-                              (let ((res (%allocate-bignum len-y)))
-                                (declare (type bignum-type res))
-                                (bignum-replace res *truncate-x* :end2 len-y)
-                                (%normalize-bignum res len-y)))
-                             (t
-                              (shift-right-unaligned
-                               *truncate-x* 0 y-shift len-y
-                               ((= j res-len-1)
-                                (setf (%bignum-ref res j)
-                                      (%ashr (%bignum-ref *truncate-x* i)
-                                             y-shift))
-                                (%normalize-bignum res res-len))
-                               res)))))))))
-      (let ((quotient (cond ((eq x-plusp y-plusp) q)
-                           ((typep q 'fixnum) (the fixnum (- q)))
-                           (t (negate-bignum-in-place q))))
-           (rem (cond (x-plusp r)
-                      ((typep r 'fixnum) (the fixnum (- r)))
-                      (t (negate-bignum-in-place r)))))
-       (values (if (typep quotient 'fixnum)
-                   quotient
-                   (%normalize-bignum quotient (%bignum-length quotient)))
-               (if (typep rem 'fixnum)
-                   rem
-                   (%normalize-bignum rem (%bignum-length rem))))))))
-
-;;; Divide X by Y when Y is a single bignum digit. BIGNUM-TRUNCATE
-;;; fixes up the quotient and remainder with respect to sign and
-;;; normalization.
-;;;
-;;; We don't have to worry about shifting Y to make its most
-;;; significant digit sufficiently large for %FLOOR to return digit-size
-;;; quantities for the q-digit and r-digit. If Y is a single digit
-;;; bignum, it is already large enough for %FLOOR. That is, it has
-;;; some bits on pretty high in the digit.
-(defun bignum-truncate-single-digit (x len-x y)
-  (declare (type bignum-index len-x))
-  (let ((q (%allocate-bignum len-x))
-       (r 0)
-       (y (%bignum-ref y 0)))
-    (declare (type bignum-element-type r y))
-    (do ((i (1- len-x) (1- i)))
-       ((minusp i))
-      (multiple-value-bind (q-digit r-digit) (%floor r (%bignum-ref x i) y)
-       (declare (type bignum-element-type q-digit r-digit))
-       (setf (%bignum-ref q i) q-digit)
-       (setf r r-digit)))
-    (let ((rem (%allocate-bignum 1)))
-      (setf (%bignum-ref rem 0) r)
-      (values q rem))))
-
-;;; a helper function for BIGNUM-TRUNCATE
-;;;
-;;; Divide *TRUNCATE-X* by *TRUNCATE-Y*, returning the quotient
-;;; and destructively modifying *TRUNCATE-X* so that it holds
-;;; the remainder.
-;;;
-;;; LEN-X and LEN-Y tell us how much of the buffers we care about.
-;;;
-;;; *TRUNCATE-X* definitely has at least three digits, and it has one
-;;; more than *TRUNCATE-Y*. This keeps i, i-1, i-2, and low-x-digit
-;;; happy. Thanks to SHIFT-AND-STORE-TRUNCATE-BUFFERS.
-(defun return-quotient-leaving-remainder (len-x len-y)
-  (declare (type bignum-index len-x len-y))
-  (let* ((len-q (- len-x len-y))
-        ;; Add one for extra sign digit in case high bit is on.
-        (q (%allocate-bignum (1+ len-q)))
-        (k (1- len-q))
-        (y1 (%bignum-ref *truncate-y* (1- len-y)))
-        (y2 (%bignum-ref *truncate-y* (- len-y 2)))
-        (i (1- len-x))
-        (i-1 (1- i))
-        (i-2 (1- i-1))
-        (low-x-digit (- i len-y)))
-    (declare (type bignum-index len-q k i i-1 i-2 low-x-digit)
-            (type bignum-element-type y1 y2))
-    (loop
-      (setf (%bignum-ref q k)
-           (try-bignum-truncate-guess
-            ;; This modifies *TRUNCATE-X*. Must access elements each pass.
-            (bignum-truncate-guess y1 y2
-                                   (%bignum-ref *truncate-x* i)
-                                   (%bignum-ref *truncate-x* i-1)
-                                   (%bignum-ref *truncate-x* i-2))
-            len-y low-x-digit))
-      (cond ((zerop k) (return))
-           (t (decf k)
-              (decf low-x-digit)
-              (shiftf i i-1 i-2 (1- i-2)))))
-    q))
-
-;;; This takes a digit guess, multiplies it by *TRUNCATE-Y* for a
-;;; result one greater in length than LEN-Y, and subtracts this result
-;;; from *TRUNCATE-X*. LOW-X-DIGIT is the first digit of X to start
-;;; the subtraction, and we know X is long enough to subtract a LEN-Y
-;;; plus one length bignum from it. Next we check the result of the
-;;; subtraction, and if the high digit in X became negative, then our
-;;; guess was one too big. In this case, return one less than GUESS
-;;; passed in, and add one value of Y back into X to account for
-;;; subtracting one too many. Knuth shows that the guess is wrong on
-;;; the order of 3/b, where b is the base (2 to the digit-size power)
-;;; -- pretty rarely.
-(defun try-bignum-truncate-guess (guess len-y low-x-digit)
-  (declare (type bignum-index low-x-digit len-y)
-          (type bignum-element-type guess))
-  (let ((carry-digit 0)
-       (borrow 1)
-       (i low-x-digit))
-    (declare (type bignum-element-type carry-digit)
-            (type bignum-index i)
-            (fixnum borrow))
-    ;; Multiply guess and divisor, subtracting from dividend simultaneously.
-    (dotimes (j len-y)
-      (multiple-value-bind (high-digit low-digit)
-         (%multiply-and-add guess
-                            (%bignum-ref *truncate-y* j)
-                            carry-digit)
-       (declare (type bignum-element-type high-digit low-digit))
-       (setf carry-digit high-digit)
-       (multiple-value-bind (x temp-borrow)
-           (%subtract-with-borrow (%bignum-ref *truncate-x* i)
-                                  low-digit
-                                  borrow)
-         (declare (type bignum-element-type x)
-                  (fixnum temp-borrow))
-         (setf (%bignum-ref *truncate-x* i) x)
-         (setf borrow temp-borrow)))
-      (incf i))
-    (setf (%bignum-ref *truncate-x* i)
-         (%subtract-with-borrow (%bignum-ref *truncate-x* i)
-                                carry-digit borrow))
-    ;; See whether guess is off by one, adding one Y back in if necessary.
-    (cond ((%digit-0-or-plusp (%bignum-ref *truncate-x* i))
-          guess)
-         (t
-          ;; If subtraction has negative result, add one divisor value back
-          ;; in. The guess was one too large in magnitude.
-          (let ((i low-x-digit)
-                (carry 0))
+  (let (truncate-x truncate-y)
+    (labels           
+        ;;; Divide X by Y when Y is a single bignum digit. BIGNUM-TRUNCATE
+        ;;; fixes up the quotient and remainder with respect to sign and
+        ;;; normalization.
+       ;;;
+       ;;; We don't have to worry about shifting Y to make its most
+       ;;; significant digit sufficiently large for %FLOOR to return
+       ;;; digit-size quantities for the q-digit and r-digit. If Y is
+       ;;; a single digit bignum, it is already large enough for
+       ;;; %FLOOR. That is, it has some bits on pretty high in the
+       ;;; digit.
+       ((bignum-truncate-single-digit (x len-x y)
+          (declare (type bignum-index len-x))
+          (let ((q (%allocate-bignum len-x))
+                (r 0)
+                (y (%bignum-ref y 0)))
+            (declare (type bignum-element-type r y))
+            (do ((i (1- len-x) (1- i)))
+                ((minusp i))
+              (multiple-value-bind (q-digit r-digit)
+                  (%floor r (%bignum-ref x i) y)
+                (declare (type bignum-element-type q-digit r-digit))
+                (setf (%bignum-ref q i) q-digit)
+                (setf r r-digit)))
+            (let ((rem (%allocate-bignum 1)))
+              (setf (%bignum-ref rem 0) r)
+              (values q rem))))         
+       ;;; This returns a guess for the next division step. Y1 is the
+       ;;; highest y digit, and y2 is the second to highest y
+       ;;; digit. The x... variables are the three highest x digits
+       ;;; for the next division step.
+       ;;;
+       ;;; From Knuth, our guess is either all ones or x-i and x-i-1
+       ;;; divided by y1, depending on whether x-i and y1 are the
+       ;;; same. We test this guess by determining whether guess*y2
+       ;;; is greater than the three high digits of x minus guess*y1
+       ;;; shifted left one digit:
+       ;;;    ------------------------------
+       ;;;   |    x-i    |   x-i-1  | x-i-2 |
+       ;;;    ------------------------------
+       ;;;    ------------------------------
+       ;;; - | g*y1 high | g*y1 low |   0   |
+       ;;;    ------------------------------
+       ;;;             ...               <   guess*y2     ???   
+       ;;; If guess*y2 is greater, then we decrement our guess by one
+       ;;; and try again.  This returns a guess that is either
+       ;;; correct or one too large.
+        (bignum-truncate-guess (y1 y2 x-i x-i-1 x-i-2)
+          (declare (type bignum-element-type y1 y2 x-i x-i-1 x-i-2))
+          (let ((guess (if (%digit-compare x-i y1)
+                           all-ones-digit
+                           (%floor x-i x-i-1 y1))))
+            (declare (type bignum-element-type guess))
+            (loop
+                (multiple-value-bind (high-guess*y1 low-guess*y1)
+                    (%multiply guess y1)
+                  (declare (type bignum-element-type low-guess*y1
+                                 high-guess*y1))
+                  (multiple-value-bind (high-guess*y2 low-guess*y2)
+                      (%multiply guess y2)
+                    (declare (type bignum-element-type high-guess*y2
+                                   low-guess*y2))
+                    (multiple-value-bind (middle-digit borrow)
+                        (%subtract-with-borrow x-i-1 low-guess*y1 1)
+                      (declare (type bignum-element-type middle-digit)
+                               (fixnum borrow))
+                      ;; Supplying borrow of 1 means there was no
+                      ;; borrow, and we know x-i-2 minus 0 requires
+                      ;; no borrow.
+                      (let ((high-digit (%subtract-with-borrow x-i
+                                                               high-guess*y1
+                                                               borrow)))
+                        (declare (type bignum-element-type high-digit))
+                        (if (and (%digit-compare high-digit 0)
+                                 (or (%digit-greater high-guess*y2
+                                                     middle-digit)
+                                     (and (%digit-compare middle-digit
+                                                          high-guess*y2)
+                                          (%digit-greater low-guess*y2
+                                                          x-i-2))))
+                            (setf guess (%subtract-with-borrow guess 1 1))
+                            (return guess)))))))))
+       ;;; Divide TRUNCATE-X by TRUNCATE-Y, returning the quotient
+       ;;; and destructively modifying TRUNCATE-X so that it holds
+       ;;; the remainder.
+       ;;;
+       ;;; LEN-X and LEN-Y tell us how much of the buffers we care about.
+       ;;;
+       ;;; TRUNCATE-X definitely has at least three digits, and it has one
+       ;;; more than TRUNCATE-Y. This keeps i, i-1, i-2, and low-x-digit
+       ;;; happy. Thanks to SHIFT-AND-STORE-TRUNCATE-BUFFERS.
+        (return-quotient-leaving-remainder (len-x len-y)
+          (declare (type bignum-index len-x len-y))
+          (let* ((len-q (- len-x len-y))
+                 ;; Add one for extra sign digit in case high bit is on.
+                 (q (%allocate-bignum (1+ len-q)))
+                 (k (1- len-q))
+                 (y1 (%bignum-ref truncate-y (1- len-y)))
+                 (y2 (%bignum-ref truncate-y (- len-y 2)))
+                 (i (1- len-x))
+                 (i-1 (1- i))
+                 (i-2 (1- i-1))
+                 (low-x-digit (- i len-y)))
+            (declare (type bignum-index len-q k i i-1 i-2 low-x-digit)
+                     (type bignum-element-type y1 y2))
+            (loop
+                (setf (%bignum-ref q k)
+                      (try-bignum-truncate-guess
+                       ;; This modifies TRUNCATE-X. Must access
+                       ;; elements each pass.
+                       (bignum-truncate-guess y1 y2
+                                              (%bignum-ref truncate-x i)
+                                              (%bignum-ref truncate-x i-1)
+                                              (%bignum-ref truncate-x i-2))
+                       len-y low-x-digit))
+                (cond ((zerop k) (return))
+                      (t (decf k)
+                         (decf low-x-digit)
+                         (shiftf i i-1 i-2 (1- i-2)))))
+            q))
+       ;;; This takes a digit guess, multiplies it by TRUNCATE-Y for a
+       ;;; result one greater in length than LEN-Y, and subtracts this result
+       ;;; from TRUNCATE-X. LOW-X-DIGIT is the first digit of X to start
+       ;;; the subtraction, and we know X is long enough to subtract a LEN-Y
+       ;;; plus one length bignum from it. Next we check the result of the
+       ;;; subtraction, and if the high digit in X became negative, then our
+       ;;; guess was one too big. In this case, return one less than GUESS
+       ;;; passed in, and add one value of Y back into X to account for
+       ;;; subtracting one too many. Knuth shows that the guess is wrong on
+       ;;; the order of 3/b, where b is the base (2 to the digit-size power)
+       ;;; -- pretty rarely.
+        (try-bignum-truncate-guess (guess len-y low-x-digit)
+          (declare (type bignum-index low-x-digit len-y)
+                   (type bignum-element-type guess))
+          (let ((carry-digit 0)
+                (borrow 1)
+                (i low-x-digit))
+            (declare (type bignum-element-type carry-digit)
+                     (type bignum-index i)
+                     (fixnum borrow))
+            ;; Multiply guess and divisor, subtracting from dividend
+            ;; simultaneously.
             (dotimes (j len-y)
-              (multiple-value-bind (v k)
-                  (%add-with-carry (%bignum-ref *truncate-y* j)
-                                   (%bignum-ref *truncate-x* i)
-                                   carry)
-                (declare (type bignum-element-type v))
-                (setf (%bignum-ref *truncate-x* i) v)
-                (setf carry k))
+              (multiple-value-bind (high-digit low-digit)
+                  (%multiply-and-add guess
+                                     (%bignum-ref truncate-y j)
+                                     carry-digit)
+                (declare (type bignum-element-type high-digit low-digit))
+                (setf carry-digit high-digit)
+                (multiple-value-bind (x temp-borrow)
+                    (%subtract-with-borrow (%bignum-ref truncate-x i)
+                                           low-digit
+                                           borrow)
+                  (declare (type bignum-element-type x)
+                           (fixnum temp-borrow))
+                  (setf (%bignum-ref truncate-x i) x)
+                  (setf borrow temp-borrow)))
               (incf i))
-            (setf (%bignum-ref *truncate-x* i)
-                  (%add-with-carry (%bignum-ref *truncate-x* i) 0 carry)))
-          (%subtract-with-borrow guess 1 1)))))
+            (setf (%bignum-ref truncate-x i)
+                  (%subtract-with-borrow (%bignum-ref truncate-x i)
+                                         carry-digit borrow))
+            ;; See whether guess is off by one, adding one
+            ;; Y back in if necessary.
+            (cond ((%digit-0-or-plusp (%bignum-ref truncate-x i))
+                   guess)
+                  (t
+                   ;; If subtraction has negative result, add one
+                   ;; divisor value back in. The guess was one too
+                   ;; large in magnitude.
+                   (let ((i low-x-digit)
+                         (carry 0))
+                     (dotimes (j len-y)
+                       (multiple-value-bind (v k)
+                           (%add-with-carry (%bignum-ref truncate-y j)
+                                            (%bignum-ref truncate-x i)
+                                            carry)
+                         (declare (type bignum-element-type v))
+                         (setf (%bignum-ref truncate-x i) v)
+                         (setf carry k))
+                       (incf i))
+                     (setf (%bignum-ref truncate-x i)
+                           (%add-with-carry (%bignum-ref truncate-x i)
+                                            0 carry)))
+                   (%subtract-with-borrow guess 1 1)))))
+       ;;; This returns the amount to shift y to place a one in the
+       ;;; second highest bit. Y must be positive. If the last digit
+       ;;; of y is zero, then y has a one in the previous digit's
+       ;;; sign bit, so we know it will take one less than digit-size
+       ;;; to get a one where we want. Otherwise, we count how many
+       ;;; right shifts it takes to get zero; subtracting this value
+       ;;; from digit-size tells us how many high zeros there are
+       ;;; which is one more than the shift amount sought.
+       ;;;
+       ;;; Note: This is exactly the same as one less than the
+       ;;; integer-length of the last digit subtracted from the
+       ;;; digit-size.
+       ;;;
+       ;;; We shift y to make it sufficiently large that doing the
+       ;;; 2*digit-size by digit-size %FLOOR calls ensures the quotient and
+       ;;; remainder fit in digit-size.
+        (shift-y-for-truncate (y)
+          (let* ((len (%bignum-length y))
+                 (last (%bignum-ref y (1- len))))
+            (declare (type bignum-index len)
+                     (type bignum-element-type last))
+            (- digit-size (integer-length last) 1)))
+        ;;; Stores two bignums into the truncation bignum buffers,
+        ;;; shifting them on the way in. This assumes x and y are
+        ;;; positive and at least two in length, and it assumes
+        ;;; truncate-x and truncate-y are one digit longer than x and
+        ;;; y.
+        (shift-and-store-truncate-buffers (x len-x y len-y shift)
+          (declare (type bignum-index len-x len-y)
+                   (type (integer 0 (#.digit-size)) shift))
+          (cond ((zerop shift)
+                 (bignum-replace truncate-x x :end1 len-x)
+                 (bignum-replace truncate-y y :end1 len-y))
+                (t
+                 (bignum-ashift-left-unaligned x 0 shift (1+ len-x)
+                                               truncate-x)
+                 (bignum-ashift-left-unaligned y 0 shift (1+ len-y)
+                                               truncate-y))))) ;; LABELS
+      ;;; Divide X by Y returning the quotient and remainder. In the
+      ;;; general case, we shift Y to set up for the algorithm, and we
+      ;;; use two buffers to save consing intermediate values. X gets
+      ;;; destructively modified to become the remainder, and we have
+      ;;; to shift it to account for the initial Y shift. After we
+      ;;; multiple bind q and r, we first fix up the signs and then
+      ;;; return the normalized results.
+      (let* ((x-plusp (%bignum-0-or-plusp x (%bignum-length x)))
+            (y-plusp (%bignum-0-or-plusp y (%bignum-length y)))
+            (x (if x-plusp x (negate-bignum x nil)))
+            (y (if y-plusp y (negate-bignum y nil)))
+            (len-x (%bignum-length x))
+            (len-y (%bignum-length y)))
+       (multiple-value-bind (q r)
+           (cond ((< len-y 2)
+                  (bignum-truncate-single-digit x len-x y))
+                 ((plusp (bignum-compare y x))
+                  (let ((res (%allocate-bignum len-x)))
+                    (dotimes (i len-x)
+                      (setf (%bignum-ref res i) (%bignum-ref x i)))
+                    (values 0 res)))
+                 (t
+                  (let ((len-x+1 (1+ len-x)))
+                    (setf truncate-x (%allocate-bignum len-x+1))
+                    (setf truncate-y (%allocate-bignum (1+ len-y)))
+                    (let ((y-shift (shift-y-for-truncate y)))
+                      (shift-and-store-truncate-buffers x len-x y
+                                                        len-y y-shift)
+                      (values (return-quotient-leaving-remainder len-x+1
+                                                                 len-y)
+                              ;; Now that RETURN-QUOTIENT-LEAVING-REMAINDER
+                              ;; has executed, we just tidy up the remainder
+                              ;; (in TRUNCATE-X) and return it.
+                              (cond
+                                ((zerop y-shift)
+                                 (let ((res (%allocate-bignum len-y)))
+                                   (declare (type bignum-type res))
+                                   (bignum-replace res truncate-x :end2 len-y)
+                                   (%normalize-bignum res len-y)))
+                                (t
+                                 (shift-right-unaligned
+                                  truncate-x 0 y-shift len-y
+                                  ((= j res-len-1)
+                                   (setf (%bignum-ref res j)
+                                         (%ashr (%bignum-ref truncate-x i)
+                                                y-shift))
+                                   (%normalize-bignum res res-len))
+                                  res))))))))
+         (let ((quotient (cond ((eq x-plusp y-plusp) q)
+                               ((typep q 'fixnum) (the fixnum (- q)))
+                               (t (negate-bignum-in-place q))))
+               (rem (cond (x-plusp r)
+                          ((typep r 'fixnum) (the fixnum (- r)))
+                          (t (negate-bignum-in-place r)))))
+           (values (if (typep quotient 'fixnum)
+                       quotient
+                       (%normalize-bignum quotient (%bignum-length quotient)))
+                   (if (typep rem 'fixnum)
+                       rem
+                       (%normalize-bignum rem (%bignum-length rem))))))))))
 
-;;; This returns a guess for the next division step. Y1 is the highest y
-;;; digit, and y2 is the second to highest y digit. The x... variables are
-;;; the three highest x digits for the next division step.
-;;;
-;;; From Knuth, our guess is either all ones or x-i and x-i-1 divided by y1,
-;;; depending on whether x-i and y1 are the same. We test this guess by
-;;; determining whether guess*y2 is greater than the three high digits of x
-;;; minus guess*y1 shifted left one digit:
-;;;    ------------------------------
-;;;   |    x-i    |   x-i-1  | x-i-2 |
-;;;    ------------------------------
-;;;    ------------------------------
-;;; - | g*y1 high | g*y1 low |   0   |
-;;;    ------------------------------
-;;;            ...               <   guess*y2     ???
-;;; If guess*y2 is greater, then we decrement our guess by one and try again.
-;;; This returns a guess that is either correct or one too large.
-(defun bignum-truncate-guess (y1 y2 x-i x-i-1 x-i-2)
-  (declare (type bignum-element-type y1 y2 x-i x-i-1 x-i-2))
-  (let ((guess (if (%digit-compare x-i y1)
-                  all-ones-digit
-                  (%floor x-i x-i-1 y1))))
-    (declare (type bignum-element-type guess))
-    (loop
-      (multiple-value-bind (high-guess*y1 low-guess*y1) (%multiply guess y1)
-       (declare (type bignum-element-type low-guess*y1 high-guess*y1))
-       (multiple-value-bind (high-guess*y2 low-guess*y2)
-           (%multiply guess y2)
-         (declare (type bignum-element-type high-guess*y2 low-guess*y2))
-         (multiple-value-bind (middle-digit borrow)
-             (%subtract-with-borrow x-i-1 low-guess*y1 1)
-           (declare (type bignum-element-type middle-digit)
-                    (fixnum borrow))
-           ;; Supplying borrow of 1 means there was no borrow, and we know
-           ;; x-i-2 minus 0 requires no borrow.
-           (let ((high-digit (%subtract-with-borrow x-i high-guess*y1 borrow)))
-             (declare (type bignum-element-type high-digit))
-             (if (and (%digit-compare high-digit 0)
-                      (or (%digit-greater high-guess*y2 middle-digit)
-                          (and (%digit-compare middle-digit high-guess*y2)
-                               (%digit-greater low-guess*y2 x-i-2))))
-                 (setf guess (%subtract-with-borrow guess 1 1))
-                 (return guess)))))))))
-
-;;; This returns the amount to shift y to place a one in the second highest
-;;; bit. Y must be positive. If the last digit of y is zero, then y has a
-;;; one in the previous digit's sign bit, so we know it will take one less
-;;; than digit-size to get a one where we want. Otherwise, we count how many
-;;; right shifts it takes to get zero; subtracting this value from digit-size
-;;; tells us how many high zeros there are which is one more than the shift
-;;; amount sought.
-;;;
-;;; Note: This is exactly the same as one less than the integer-length of the
-;;; last digit subtracted from the digit-size.
-;;;
-;;; We shift y to make it sufficiently large that doing the 2*digit-size
-;;; by digit-size %FLOOR calls ensures the quotient and remainder fit in
-;;; digit-size.
-(defun shift-y-for-truncate (y)
-  (let* ((len (%bignum-length y))
-        (last (%bignum-ref y (1- len))))
-    (declare (type bignum-index len)
-            (type bignum-element-type last))
-    (- digit-size (integer-length last) 1)))
-
-;;; Stores two bignums into the truncation bignum buffers, shifting them on the
-;;; way in. This assumes x and y are positive and at least two in length, and
-;;; it assumes *truncate-x* and *truncate-y* are one digit longer than x and y.
-(defun shift-and-store-truncate-buffers (x len-x y len-y shift)
-  (declare (type bignum-index len-x len-y)
-          (type (integer 0 (#.digit-size)) shift))
-  (cond ((zerop shift)
-        (bignum-replace *truncate-x* x :end1 len-x)
-        (bignum-replace *truncate-y* y :end1 len-y))
-       (t
-        (bignum-ashift-left-unaligned x 0 shift (1+ len-x) *truncate-x*)
-        (bignum-ashift-left-unaligned y 0 shift (1+ len-y) *truncate-y*))))
 \f
 ;;;; %FLOOR primitive for BIGNUM-TRUNCATE