Index: src/code/bignum.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/bignum.lisp,v
retrieving revision 1.14
diff u r1.14 bignum.lisp
 src/code/bignum.lisp 8 Aug 2004 03:13:53 0000 1.14
+++ src/code/bignum.lisp 10 Aug 2004 03:11:48 0000
@@ 1751,274 +1987,303 @@
;;;
;;; Normalize quotient and remainder. Cons result if necessary.
;;; These are used by BIGNUMTRUNCATE and friends in the general case.
(defvar *truncatex*)
(defvar *truncatey*)

;;; 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 *TRUNCATEX* and *TRUNCATEY*. 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
+;;; (UNSIGNEDBYTE 32)'s going on, which this refactoring
+;;; eliminated. This improves the performance on some CLBENCH tests
+;;; by up to 50%, which is probably signigicant enough to justify the
+;;; reduction in readability that was introduced. JES, 20040807
(defun bignumtruncate (x y)
(declare (type bignumtype x y))
 (let* ((xplusp (%bignum0orplusp x (%bignumlength x)))
 (yplusp (%bignum0orplusp y (%bignumlength y)))
 (x (if xplusp x (negatebignum x nil)))
 (y (if yplusp y (negatebignum y nil)))
 (lenx (%bignumlength x))
 (leny (%bignumlength y)))
 (multiplevaluebind (q r)
 (cond ((< leny 2)
 (bignumtruncatesingledigit x lenx y))
 ((plusp (bignumcompare y x))
 (let ((res (%allocatebignum lenx)))
 (dotimes (i lenx)
 (setf (%bignumref res i) (%bignumref x i)))
 (values 0 res)))
 (t
 (let ((lenx+1 (1+ lenx)))
 (withbignumbuffers ((*truncatex* lenx+1)
 (*truncatey* (1+ leny)))
 (let ((yshift (shiftyfortruncate y)))
 (shiftandstoretruncatebuffers x lenx y leny yshift)
 (values (returnquotientleavingremainder lenx+1 leny)
 ;; Now that RETURNQUOTIENTLEAVINGREMAINDER
 ;; has executed, we just tidy up the remainder
 ;; (in *TRUNCATEX*) and return it.
 (cond
 ((zerop yshift)
 (let ((res (%allocatebignum leny)))
 (declare (type bignumtype res))
 (bignumreplace res *truncatex* :end2 leny)
 (%normalizebignum res leny)))
 (t
 (shiftrightunaligned
 *truncatex* 0 yshift leny
 ((= j reslen1)
 (setf (%bignumref res j)
 (%ashr (%bignumref *truncatex* i)
 yshift))
 (%normalizebignum res reslen))
 res)))))))))
 (let ((quotient (cond ((eq xplusp yplusp) q)
 ((typep q 'fixnum) (the fixnum ( q)))
 (t (negatebignuminplace q))))
 (rem (cond (xplusp r)
 ((typep r 'fixnum) (the fixnum ( r)))
 (t (negatebignuminplace r)))))
 (values (if (typep quotient 'fixnum)
 quotient
 (%normalizebignum quotient (%bignumlength quotient)))
 (if (typep rem 'fixnum)
 rem
 (%normalizebignum rem (%bignumlength rem))))))))

;;; Divide X by Y when Y is a single bignum digit. BIGNUMTRUNCATE
;;; 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 digitsize
;;; quantities for the qdigit and rdigit. 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 bignumtruncatesingledigit (x lenx y)
 (declare (type bignumindex lenx))
 (let ((q (%allocatebignum lenx))
 (r 0)
 (y (%bignumref y 0)))
 (declare (type bignumelementtype r y))
 (do ((i (1 lenx) (1 i)))
 ((minusp i))
 (multiplevaluebind (qdigit rdigit) (%floor r (%bignumref x i) y)
 (declare (type bignumelementtype qdigit rdigit))
 (setf (%bignumref q i) qdigit)
 (setf r rdigit)))
 (let ((rem (%allocatebignum 1)))
 (setf (%bignumref rem 0) r)
 (values q rem))))

;;; a helper function for BIGNUMTRUNCATE
;;;
;;; Divide *TRUNCATEX* by *TRUNCATEY*, returning the quotient
;;; and destructively modifying *TRUNCATEX* so that it holds
;;; the remainder.
;;;
;;; LENX and LENY tell us how much of the buffers we care about.
;;;
;;; *TRUNCATEX* definitely has at least three digits, and it has one
;;; more than *TRUNCATEY*. This keeps i, i1, i2, and lowxdigit
;;; happy. Thanks to SHIFTANDSTORETRUNCATEBUFFERS.
(defun returnquotientleavingremainder (lenx leny)
 (declare (type bignumindex lenx leny))
 (let* ((lenq ( lenx leny))
 ;; Add one for extra sign digit in case high bit is on.
 (q (%allocatebignum (1+ lenq)))
 (k (1 lenq))
 (y1 (%bignumref *truncatey* (1 leny)))
 (y2 (%bignumref *truncatey* ( leny 2)))
 (i (1 lenx))
 (i1 (1 i))
 (i2 (1 i1))
 (lowxdigit ( i leny)))
 (declare (type bignumindex lenq k i i1 i2 lowxdigit)
 (type bignumelementtype y1 y2))
 (loop
 (setf (%bignumref q k)
 (trybignumtruncateguess
 ;; This modifies *TRUNCATEX*. Must access elements each pass.
 (bignumtruncateguess y1 y2
 (%bignumref *truncatex* i)
 (%bignumref *truncatex* i1)
 (%bignumref *truncatex* i2))
 leny lowxdigit))
 (cond ((zerop k) (return))
 (t (decf k)
 (decf lowxdigit)
 (shiftf i i1 i2 (1 i2)))))
 q))

;;; This takes a digit guess, multiplies it by *TRUNCATEY* for a
;;; result one greater in length than LENY, and subtracts this result
;;; from *TRUNCATEX*. LOWXDIGIT is the first digit of X to start
;;; the subtraction, and we know X is long enough to subtract a LENY
;;; 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 digitsize power)
;;;  pretty rarely.
(defun trybignumtruncateguess (guess leny lowxdigit)
 (declare (type bignumindex lowxdigit leny)
 (type bignumelementtype guess))
 (let ((carrydigit 0)
 (borrow 1)
 (i lowxdigit))
 (declare (type bignumelementtype carrydigit)
 (type bignumindex i)
 (fixnum borrow))
 ;; Multiply guess and divisor, subtracting from dividend simultaneously.
 (dotimes (j leny)
 (multiplevaluebind (highdigit lowdigit)
 (%multiplyandadd guess
 (%bignumref *truncatey* j)
 carrydigit)
 (declare (type bignumelementtype highdigit lowdigit))
 (setf carrydigit highdigit)
 (multiplevaluebind (x tempborrow)
 (%subtractwithborrow (%bignumref *truncatex* i)
 lowdigit
 borrow)
 (declare (type bignumelementtype x)
 (fixnum tempborrow))
 (setf (%bignumref *truncatex* i) x)
 (setf borrow tempborrow)))
 (incf i))
 (setf (%bignumref *truncatex* i)
 (%subtractwithborrow (%bignumref *truncatex* i)
 carrydigit borrow))
 ;; See whether guess is off by one, adding one Y back in if necessary.
 (cond ((%digit0orplusp (%bignumref *truncatex* i))
 guess)
 (t
 ;; If subtraction has negative result, add one divisor value back
 ;; in. The guess was one too large in magnitude.
 (let ((i lowxdigit)
 (carry 0))
+ (let (truncatex truncatey)
+ (labels
+ ;;; Divide X by Y when Y is a single bignum digit. BIGNUMTRUNCATE
+ ;;; 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
+ ;;; digitsize quantities for the qdigit and rdigit. 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.
+ ((bignumtruncatesingledigit (x lenx y)
+ (declare (type bignumindex lenx))
+ (let ((q (%allocatebignum lenx))
+ (r 0)
+ (y (%bignumref y 0)))
+ (declare (type bignumelementtype r y))
+ (do ((i (1 lenx) (1 i)))
+ ((minusp i))
+ (multiplevaluebind (qdigit rdigit)
+ (%floor r (%bignumref x i) y)
+ (declare (type bignumelementtype qdigit rdigit))
+ (setf (%bignumref q i) qdigit)
+ (setf r rdigit)))
+ (let ((rem (%allocatebignum 1)))
+ (setf (%bignumref 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 xi and xi1
+ ;;; divided by y1, depending on whether xi 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:
+ ;;; 
+ ;;;  xi  xi1  xi2 
+ ;;; 
+ ;;; 
+ ;;;   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.
+ (bignumtruncateguess (y1 y2 xi xi1 xi2)
+ (declare (type bignumelementtype y1 y2 xi xi1 xi2))
+ (let ((guess (if (%digitcompare xi y1)
+ allonesdigit
+ (%floor xi xi1 y1))))
+ (declare (type bignumelementtype guess))
+ (loop
+ (multiplevaluebind (highguess*y1 lowguess*y1)
+ (%multiply guess y1)
+ (declare (type bignumelementtype lowguess*y1
+ highguess*y1))
+ (multiplevaluebind (highguess*y2 lowguess*y2)
+ (%multiply guess y2)
+ (declare (type bignumelementtype highguess*y2
+ lowguess*y2))
+ (multiplevaluebind (middledigit borrow)
+ (%subtractwithborrow xi1 lowguess*y1 1)
+ (declare (type bignumelementtype middledigit)
+ (fixnum borrow))
+ ;; Supplying borrow of 1 means there was no
+ ;; borrow, and we know xi2 minus 0 requires
+ ;; no borrow.
+ (let ((highdigit (%subtractwithborrow xi
+ highguess*y1
+ borrow)))
+ (declare (type bignumelementtype highdigit))
+ (if (and (%digitcompare highdigit 0)
+ (or (%digitgreater highguess*y2
+ middledigit)
+ (and (%digitcompare middledigit
+ highguess*y2)
+ (%digitgreater lowguess*y2
+ xi2))))
+ (setf guess (%subtractwithborrow guess 1 1))
+ (return guess)))))))))
+ ;;; Divide TRUNCATEX by TRUNCATEY, returning the quotient
+ ;;; and destructively modifying TRUNCATEX so that it holds
+ ;;; the remainder.
+ ;;;
+ ;;; LENX and LENY tell us how much of the buffers we care about.
+ ;;;
+ ;;; TRUNCATEX definitely has at least three digits, and it has one
+ ;;; more than TRUNCATEY. This keeps i, i1, i2, and lowxdigit
+ ;;; happy. Thanks to SHIFTANDSTORETRUNCATEBUFFERS.
+ (returnquotientleavingremainder (lenx leny)
+ (declare (type bignumindex lenx leny))
+ (let* ((lenq ( lenx leny))
+ ;; Add one for extra sign digit in case high bit is on.
+ (q (%allocatebignum (1+ lenq)))
+ (k (1 lenq))
+ (y1 (%bignumref truncatey (1 leny)))
+ (y2 (%bignumref truncatey ( leny 2)))
+ (i (1 lenx))
+ (i1 (1 i))
+ (i2 (1 i1))
+ (lowxdigit ( i leny)))
+ (declare (type bignumindex lenq k i i1 i2 lowxdigit)
+ (type bignumelementtype y1 y2))
+ (loop
+ (setf (%bignumref q k)
+ (trybignumtruncateguess
+ ;; This modifies TRUNCATEX. Must access
+ ;; elements each pass.
+ (bignumtruncateguess y1 y2
+ (%bignumref truncatex i)
+ (%bignumref truncatex i1)
+ (%bignumref truncatex i2))
+ leny lowxdigit))
+ (cond ((zerop k) (return))
+ (t (decf k)
+ (decf lowxdigit)
+ (shiftf i i1 i2 (1 i2)))))
+ q))
+ ;;; This takes a digit guess, multiplies it by TRUNCATEY for a
+ ;;; result one greater in length than LENY, and subtracts this result
+ ;;; from TRUNCATEX. LOWXDIGIT is the first digit of X to start
+ ;;; the subtraction, and we know X is long enough to subtract a LENY
+ ;;; 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 digitsize power)
+ ;;;  pretty rarely.
+ (trybignumtruncateguess (guess leny lowxdigit)
+ (declare (type bignumindex lowxdigit leny)
+ (type bignumelementtype guess))
+ (let ((carrydigit 0)
+ (borrow 1)
+ (i lowxdigit))
+ (declare (type bignumelementtype carrydigit)
+ (type bignumindex i)
+ (fixnum borrow))
+ ;; Multiply guess and divisor, subtracting from dividend
+ ;; simultaneously.
(dotimes (j leny)
 (multiplevaluebind (v k)
 (%addwithcarry (%bignumref *truncatey* j)
 (%bignumref *truncatex* i)
 carry)
 (declare (type bignumelementtype v))
 (setf (%bignumref *truncatex* i) v)
 (setf carry k))
+ (multiplevaluebind (highdigit lowdigit)
+ (%multiplyandadd guess
+ (%bignumref truncatey j)
+ carrydigit)
+ (declare (type bignumelementtype highdigit lowdigit))
+ (setf carrydigit highdigit)
+ (multiplevaluebind (x tempborrow)
+ (%subtractwithborrow (%bignumref truncatex i)
+ lowdigit
+ borrow)
+ (declare (type bignumelementtype x)
+ (fixnum tempborrow))
+ (setf (%bignumref truncatex i) x)
+ (setf borrow tempborrow)))
(incf i))
 (setf (%bignumref *truncatex* i)
 (%addwithcarry (%bignumref *truncatex* i) 0 carry)))
 (%subtractwithborrow guess 1 1)))))

;;; 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 xi and xi1 divided by y1,
;;; depending on whether xi 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:
;;; 
;;;  xi  xi1  xi2 
;;; 
;;; 
;;;   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 bignumtruncateguess (y1 y2 xi xi1 xi2)
 (declare (type bignumelementtype y1 y2 xi xi1 xi2))
 (let ((guess (if (%digitcompare xi y1)
 allonesdigit
 (%floor xi xi1 y1))))
 (declare (type bignumelementtype guess))
 (loop
 (multiplevaluebind (highguess*y1 lowguess*y1) (%multiply guess y1)
 (declare (type bignumelementtype lowguess*y1 highguess*y1))
 (multiplevaluebind (highguess*y2 lowguess*y2)
 (%multiply guess y2)
 (declare (type bignumelementtype highguess*y2 lowguess*y2))
 (multiplevaluebind (middledigit borrow)
 (%subtractwithborrow xi1 lowguess*y1 1)
 (declare (type bignumelementtype middledigit)
 (fixnum borrow))
 ;; Supplying borrow of 1 means there was no borrow, and we know
 ;; xi2 minus 0 requires no borrow.
 (let ((highdigit (%subtractwithborrow xi highguess*y1 borrow)))
 (declare (type bignumelementtype highdigit))
 (if (and (%digitcompare highdigit 0)
 (or (%digitgreater highguess*y2 middledigit)
 (and (%digitcompare middledigit highguess*y2)
 (%digitgreater lowguess*y2 xi2))))
 (setf guess (%subtractwithborrow 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 digitsize to get a one where we want. Otherwise, we count how many
;;; right shifts it takes to get zero; subtracting this value from digitsize
;;; 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 integerlength of the
;;; last digit subtracted from the digitsize.
;;;
;;; We shift y to make it sufficiently large that doing the 2*digitsize
;;; by digitsize %FLOOR calls ensures the quotient and remainder fit in
;;; digitsize.
(defun shiftyfortruncate (y)
 (let* ((len (%bignumlength y))
 (last (%bignumref y (1 len))))
 (declare (type bignumindex len)
 (type bignumelementtype last))
 ( digitsize (integerlength 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 *truncatex* and *truncatey* are one digit longer than x and y.
(defun shiftandstoretruncatebuffers (x lenx y leny shift)
 (declare (type bignumindex lenx leny)
 (type (integer 0 (#.digitsize)) shift))
 (cond ((zerop shift)
 (bignumreplace *truncatex* x :end1 lenx)
 (bignumreplace *truncatey* y :end1 leny))
 (t
 (bignumashiftleftunaligned x 0 shift (1+ lenx) *truncatex*)
 (bignumashiftleftunaligned y 0 shift (1+ leny) *truncatey*))))
+ (setf (%bignumref truncatex i)
+ (%subtractwithborrow (%bignumref truncatex i)
+ carrydigit borrow))
+ ;; See whether guess is off by one, adding one
+ ;; Y back in if necessary.
+ (cond ((%digit0orplusp (%bignumref truncatex i))
+ guess)
+ (t
+ ;; If subtraction has negative result, add one
+ ;; divisor value back in. The guess was one too
+ ;; large in magnitude.
+ (let ((i lowxdigit)
+ (carry 0))
+ (dotimes (j leny)
+ (multiplevaluebind (v k)
+ (%addwithcarry (%bignumref truncatey j)
+ (%bignumref truncatex i)
+ carry)
+ (declare (type bignumelementtype v))
+ (setf (%bignumref truncatex i) v)
+ (setf carry k))
+ (incf i))
+ (setf (%bignumref truncatex i)
+ (%addwithcarry (%bignumref truncatex i)
+ 0 carry)))
+ (%subtractwithborrow 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 digitsize
+ ;;; to get a one where we want. Otherwise, we count how many
+ ;;; right shifts it takes to get zero; subtracting this value
+ ;;; from digitsize 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
+ ;;; integerlength of the last digit subtracted from the
+ ;;; digitsize.
+ ;;;
+ ;;; We shift y to make it sufficiently large that doing the
+ ;;; 2*digitsize by digitsize %FLOOR calls ensures the quotient and
+ ;;; remainder fit in digitsize.
+ (shiftyfortruncate (y)
+ (let* ((len (%bignumlength y))
+ (last (%bignumref y (1 len))))
+ (declare (type bignumindex len)
+ (type bignumelementtype last))
+ ( digitsize (integerlength 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
+ ;;; truncatex and truncatey are one digit longer than x and
+ ;;; y.
+ (shiftandstoretruncatebuffers (x lenx y leny shift)
+ (declare (type bignumindex lenx leny)
+ (type (integer 0 (#.digitsize)) shift))
+ (cond ((zerop shift)
+ (bignumreplace truncatex x :end1 lenx)
+ (bignumreplace truncatey y :end1 leny))
+ (t
+ (bignumashiftleftunaligned x 0 shift (1+ lenx)
+ truncatex)
+ (bignumashiftleftunaligned y 0 shift (1+ leny)
+ truncatey))))) ;; 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* ((xplusp (%bignum0orplusp x (%bignumlength x)))
+ (yplusp (%bignum0orplusp y (%bignumlength y)))
+ (x (if xplusp x (negatebignum x nil)))
+ (y (if yplusp y (negatebignum y nil)))
+ (lenx (%bignumlength x))
+ (leny (%bignumlength y)))
+ (multiplevaluebind (q r)
+ (cond ((< leny 2)
+ (bignumtruncatesingledigit x lenx y))
+ ((plusp (bignumcompare y x))
+ (let ((res (%allocatebignum lenx)))
+ (dotimes (i lenx)
+ (setf (%bignumref res i) (%bignumref x i)))
+ (values 0 res)))
+ (t
+ (let ((lenx+1 (1+ lenx)))
+ (setf truncatex (%allocatebignum lenx+1))
+ (setf truncatey (%allocatebignum (1+ leny)))
+ (let ((yshift (shiftyfortruncate y)))
+ (shiftandstoretruncatebuffers x lenx y
+ leny yshift)
+ (values (returnquotientleavingremainder lenx+1
+ leny)
+ ;; Now that RETURNQUOTIENTLEAVINGREMAINDER
+ ;; has executed, we just tidy up the remainder
+ ;; (in TRUNCATEX) and return it.
+ (cond
+ ((zerop yshift)
+ (let ((res (%allocatebignum leny)))
+ (declare (type bignumtype res))
+ (bignumreplace res truncatex :end2 leny)
+ (%normalizebignum res leny)))
+ (t
+ (shiftrightunaligned
+ truncatex 0 yshift leny
+ ((= j reslen1)
+ (setf (%bignumref res j)
+ (%ashr (%bignumref truncatex i)
+ yshift))
+ (%normalizebignum res reslen))
+ res))))))))
+ (let ((quotient (cond ((eq xplusp yplusp) q)
+ ((typep q 'fixnum) (the fixnum ( q)))
+ (t (negatebignuminplace q))))
+ (rem (cond (xplusp r)
+ ((typep r 'fixnum) (the fixnum ( r)))
+ (t (negatebignuminplace r)))))
+ (values (if (typep quotient 'fixnum)
+ quotient
+ (%normalizebignum quotient (%bignumlength quotient)))
+ (if (typep rem 'fixnum)
+ rem
+ (%normalizebignum rem (%bignumlength rem))))))))))
+
;;;; %FLOOR primitive for BIGNUMTRUNCATE