X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fbignum.lisp;h=1ed59421957f6bd375ae648610a70552404aaad3;hb=17532463fa19f2fc2aba53b65c32e200a27ccd6a;hp=ad30b2fcd76090c48b84cfbe3f3221739b9fec4a;hpb=b8fdebe9715aad2300f21f33eac147d9ca3951ed;p=sbcl.git diff --git a/src/code/bignum.lisp b/src/code/bignum.lisp index ad30b2f..1ed5942 100644 --- a/src/code/bignum.lisp +++ b/src/code/bignum.lisp @@ -377,13 +377,13 @@ ;;; results, such as GCD, use this. It assumes Result is big enough for the ;;; 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)) + (declare (type bignum-type a b result) + (type bignum-index len-a len-b len-res)) (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) + (declare (type bignum-type a b result) (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)) @@ -605,7 +605,7 @@ (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)) + (when (logtest ud umask) (setf ud (modularly (- ud vd))) (setf m (modularly (logior m imask)))) (setf imask (modularly (ash imask 1))) @@ -634,7 +634,7 @@ (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)) + (when (logtest (%bignum-ref u 0) n) (let ((tmp1-len (multiply-bignum-buffer-and-smallnum-to-buffer v v-len (logand n (bmod u @@ -1277,7 +1277,7 @@ (cond ;; Round down if round bit is 0. - ((zerop (logand round-bit low)) + ((not (logtest round-bit low)) (float-from-bits shifted len)) ;; If only round bit is set, then round to even. ((and (= low round-bit) @@ -1315,21 +1315,22 @@ (floor index digit-size) (if (>= word-index len) (not (bignum-plus-p bignum)) - (not (zerop (logand (%bignum-ref bignum word-index) - (ash 1 bit-index)))))))) + (logbitp bit-index (%bignum-ref bignum word-index)))))) (defun bignum-logcount (bignum) (declare (type bignum-type bignum)) - (let* ((length (%bignum-length bignum)) - (plusp (%bignum-0-or-plusp bignum length)) - (result 0)) + (let ((length (%bignum-length bignum)) + (result 0)) (declare (type bignum-index length) (fixnum result)) (do ((index 0 (1+ index))) - ((= index length) result) + ((= index length) + (if (%bignum-0-or-plusp bignum length) + result + (- (* length digit-size) result))) (let ((digit (%bignum-ref bignum index))) (declare (type bignum-element-type digit)) - (incf result (logcount (if plusp digit (%lognot digit)))))))) + (incf result (logcount digit)))))) ;;;; logical operations @@ -1473,463 +1474,9 @@ (setf (%bignum-ref res i) (%logxor sign (%bignum-ref b i)))) (%normalize-bignum res len-b)) -;;;; LDB (load byte) - -#| -FOR NOW WE DON'T USE LDB OR DPB. WE USE SHIFTS AND MASKS IN NUMBERS.LISP WHICH -IS LESS EFFICIENT BUT EASIER TO MAINTAIN. BILL SAYS THIS CODE CERTAINLY WORKS! - -(defconstant maximum-fixnum-bits (- sb!vm:n-word-bits sb!vm:n-lowtag-bits)) - -(defun bignum-load-byte (byte bignum) - (declare (type bignum-type bignum)) - (let ((byte-len (byte-size byte)) - (byte-pos (byte-position byte))) - (if (< byte-len maximum-fixnum-bits) - (bignum-ldb-fixnum-res bignum byte-len byte-pos) - (bignum-ldb-bignum-res bignum byte-len byte-pos)))) - -;;; This returns a fixnum result of loading a byte from a bignum. In order, we -;;; check for the following conditions: -;;; Insufficient bignum digits to start loading a byte -- -;;; Return 0 or byte-len 1's depending on sign of bignum. -;;; One bignum digit containing the whole byte spec -- -;;; Grab 'em, shift 'em, and mask out what we don't want. -;;; Insufficient bignum digits to cover crossing a digit boundary -- -;;; Grab the available bits in the last digit, and or in whatever -;;; virtual sign bits we need to return a full byte spec. -;;; Else (we cross a digit boundary with all bits available) -- -;;; Make a couple masks, grab what we want, shift it around, and -;;; LOGIOR it all together. -;;; Because (< maximum-fixnum-bits digit-size) and -;;; (< byte-len maximum-fixnum-bits), -;;; we only cross one digit boundary if any. -(defun bignum-ldb-fixnum-res (bignum byte-len byte-pos) - (multiple-value-bind (skipped-digits pos) (truncate byte-pos digit-size) - (let ((bignum-len (%bignum-length bignum)) - (s-digits+1 (1+ skipped-digits))) - (declare (type bignum-index bignum-len s-digits+1)) - (if (>= skipped-digits bignum-len) - (if (%bignum-0-or-plusp bignum bignum-len) - 0 - (%make-ones byte-len)) - (let ((end (+ pos byte-len))) - (cond ((<= end digit-size) - (logand (ash (%bignum-ref bignum skipped-digits) (- pos)) - ;; Must LOGAND after shift here. - (%make-ones byte-len))) - ((>= s-digits+1 bignum-len) - (let* ((available-bits (- digit-size pos)) - (res (logand (ash (%bignum-ref bignum skipped-digits) - (- pos)) - ;; LOGAND should be unnecessary here - ;; with a logical right shift or a - ;; correct digit-sized one. - (%make-ones available-bits)))) - (if (%bignum-0-or-plusp bignum bignum-len) - res - (logior (%ashl (%make-ones (- end digit-size)) - available-bits) - res)))) - (t - (let* ((high-bits-in-first-digit (- digit-size pos)) - (high-mask (%make-ones high-bits-in-first-digit)) - (low-bits-in-next-digit (- end digit-size)) - (low-mask (%make-ones low-bits-in-next-digit))) - (declare (type bignum-element-type high-mask low-mask)) - (logior (%ashl (logand (%bignum-ref bignum s-digits+1) - low-mask) - high-bits-in-first-digit) - (logand (ash (%bignum-ref bignum skipped-digits) - (- pos)) - ;; LOGAND should be unnecessary here with - ;; a logical right shift or a correct - ;; digit-sized one. - high-mask)))))))))) - -;;; This returns a bignum result of loading a byte from a bignum. In order, we -;;; check for the following conditions: -;;; Insufficient bignum digits to start loading a byte -- -;;; Byte-pos starting on a digit boundary -- -;;; Byte spec contained in one bignum digit -- -;;; Grab the bits we want and stick them in a single digit result. -;;; Since we know byte-pos is non-zero here, we know our single digit -;;; will have a zero high sign bit. -;;; Else (unaligned multiple digits) -- -;;; This is like doing a shift right combined with either masking -;;; out unwanted high bits from bignum or filling in virtual sign -;;; bits if bignum had insufficient bits. We use SHIFT-RIGHT-ALIGNED -;;; and reference lots of local variables this macro establishes. -(defun bignum-ldb-bignum-res (bignum byte-len byte-pos) - (multiple-value-bind (skipped-digits pos) (truncate byte-pos digit-size) - (let ((bignum-len (%bignum-length bignum))) - (declare (type bignum-index bignum-len)) - (cond - ((>= skipped-digits bignum-len) - (make-bignum-virtual-ldb-bits bignum bignum-len byte-len)) - ((zerop pos) - (make-aligned-ldb-bignum bignum bignum-len byte-len skipped-digits)) - ((< (+ pos byte-len) digit-size) - (let ((res (%allocate-bignum 1))) - (setf (%bignum-ref res 0) - (logand (%ashr (%bignum-ref bignum skipped-digits) pos) - (%make-ones byte-len))) - res)) - (t - (make-unaligned-ldb-bignum bignum bignum-len - byte-len skipped-digits pos)))))) - -;;; This returns bits from bignum that don't physically exist. These are -;;; all zero or one depending on the sign of the bignum. -(defun make-bignum-virtual-ldb-bits (bignum bignum-len byte-len) - (if (%bignum-0-or-plusp bignum bignum-len) - 0 - (multiple-value-bind (res-len-1 extra) (truncate byte-len digit-size) - (declare (type bignum-index res-len-1)) - (let* ((res-len (1+ res-len-1)) - (res (%allocate-bignum res-len))) - (declare (type bignum-index res-len)) - (do ((j 0 (1+ j))) - ((= j res-len-1) - (setf (%bignum-ref res j) (%make-ones extra)) - (%normalize-bignum res res-len)) - (declare (type bignum-index j)) - (setf (%bignum-ref res j) all-ones-digit)))))) - -;;; Since we are picking up aligned digits, we just copy the whole digits -;;; we want and fill in extra bits. We might have a byte-len that extends -;;; off the end of the bignum, so we may have to fill in extra 1's if the -;;; bignum is negative. -(defun make-aligned-ldb-bignum (bignum bignum-len byte-len skipped-digits) - (multiple-value-bind (res-len-1 extra) (truncate byte-len digit-size) - (declare (type bignum-index res-len-1)) - (let* ((res-len (1+ res-len-1)) - (res (%allocate-bignum res-len))) - (declare (type bignum-index res-len)) - (do ((i skipped-digits (1+ i)) - (j 0 (1+ j))) - ((or (= j res-len-1) (= i bignum-len)) - (cond ((< i bignum-len) - (setf (%bignum-ref res j) - (logand (%bignum-ref bignum i) - (the bignum-element-type (%make-ones extra))))) - ((%bignum-0-or-plusp bignum bignum-len)) - (t - (do ((j j (1+ j))) - ((= j res-len-1) - (setf (%bignum-ref res j) (%make-ones extra))) - (setf (%bignum-ref res j) all-ones-digit)))) - (%normalize-bignum res res-len)) - (declare (type bignum-index i j)) - (setf (%bignum-ref res j) (%bignum-ref bignum i)))))) - -;;; This grabs unaligned bignum bits from bignum assuming byte-len causes at -;;; least one digit boundary crossing. We use SHIFT-RIGHT-UNALIGNED referencing -;;; lots of local variables established by it. -(defun make-unaligned-ldb-bignum (bignum - bignum-len - byte-len - skipped-digits - pos) - (multiple-value-bind (res-len-1 extra) (truncate byte-len digit-size) - (shift-right-unaligned - bignum skipped-digits pos (1+ res-len-1) - ((or (= j res-len-1) (= i+1 bignum-len)) - (cond ((= j res-len-1) - (cond - ((< extra high-bits-in-first-digit) - (setf (%bignum-ref res j) - (logand (ash (%bignum-ref bignum i) minus-start-pos) - ;; Must LOGAND after shift here. - (%make-ones extra)))) - (t - (setf (%bignum-ref res j) - (logand (ash (%bignum-ref bignum i) minus-start-pos) - ;; LOGAND should be unnecessary here with a logical - ;; right shift or a correct digit-sized one. - high-mask)) - (when (%bignum-0-or-plusp bignum bignum-len) - (setf (%bignum-ref res j) - (logior (%bignum-ref res j) - (%ashl (%make-ones - (- extra high-bits-in-first-digit)) - high-bits-in-first-digit))))))) - (t - (setf (%bignum-ref res j) - (logand (ash (%bignum-ref bignum i) minus-start-pos) - ;; LOGAND should be unnecessary here with a logical - ;; right shift or a correct digit-sized one. - high-mask)) - (unless (%bignum-0-or-plusp bignum bignum-len) - ;; Fill in upper half of this result digit with 1's. - (setf (%bignum-ref res j) - (logior (%bignum-ref res j) - (%ashl low-mask high-bits-in-first-digit))) - ;; Fill in any extra 1's we need to be byte-len long. - (do ((j (1+ j) (1+ j))) - ((>= j res-len-1) - (setf (%bignum-ref res j) (%make-ones extra))) - (setf (%bignum-ref res j) all-ones-digit))))) - (%normalize-bignum res res-len)) - res))) - -;;;; DPB (deposit byte) - -(defun bignum-deposit-byte (new-byte byte-spec bignum) - (declare (type bignum-type bignum)) - (let* ((byte-len (byte-size byte-spec)) - (byte-pos (byte-position byte-spec)) - (bignum-len (%bignum-length bignum)) - (bignum-plusp (%bignum-0-or-plusp bignum bignum-len)) - (byte-end (+ byte-pos byte-len)) - (res-len (1+ (max (ceiling byte-end digit-size) bignum-len))) - (res (%allocate-bignum res-len))) - (declare (type bignum-index bignum-len res-len)) - ;; Fill in an extra sign digit in case we set what would otherwise be the - ;; last digit's last bit. Normalize at the end in case this was - ;; unnecessary. - (unless bignum-plusp - (setf (%bignum-ref res (1- res-len)) all-ones-digit)) - (multiple-value-bind (end-digit end-bits) (truncate byte-end digit-size) - (declare (type bignum-index end-digit)) - ;; Fill in bits from bignum up to byte-pos. - (multiple-value-bind (pos-digit pos-bits) (truncate byte-pos digit-size) - (declare (type bignum-index pos-digit)) - (do ((i 0 (1+ i)) - (end (min pos-digit bignum-len))) - ((= i end) - (cond ((< i bignum-len) - (unless (zerop pos-bits) - (setf (%bignum-ref res i) - (logand (%bignum-ref bignum i) - (%make-ones pos-bits))))) - (bignum-plusp) - (t - (do ((i i (1+ i))) - ((= i pos-digit) - (unless (zerop pos-bits) - (setf (%bignum-ref res i) (%make-ones pos-bits)))) - (setf (%bignum-ref res i) all-ones-digit))))) - (setf (%bignum-ref res i) (%bignum-ref bignum i))) - ;; Fill in bits from new-byte. - (if (typep new-byte 'fixnum) - (deposit-fixnum-bits new-byte byte-len pos-digit pos-bits - end-digit end-bits res) - (deposit-bignum-bits new-byte byte-len pos-digit pos-bits - end-digit end-bits res))) - ;; Fill in remaining bits from bignum after byte-spec. - (when (< end-digit bignum-len) - (setf (%bignum-ref res end-digit) - (logior (logand (%bignum-ref bignum end-digit) - (%ashl (%make-ones (- digit-size end-bits)) - end-bits)) - ;; DEPOSIT-FIXNUM-BITS and DEPOSIT-BIGNUM-BITS only store - ;; bits from new-byte into res's end-digit element, so - ;; we don't need to mask out unwanted high bits. - (%bignum-ref res end-digit))) - (do ((i (1+ end-digit) (1+ i))) - ((= i bignum-len)) - (setf (%bignum-ref res i) (%bignum-ref bignum i))))) - (%normalize-bignum res res-len))) - -;;; This starts at result's pos-digit skipping pos-bits, and it stores bits -;;; from new-byte, a fixnum, into result. It effectively stores byte-len -;;; number of bits, but never stores past end-digit and end-bits in result. -;;; The first branch fires when all the bits we want from new-byte are present; -;;; if byte-len crosses from the current result digit into the next, the last -;;; argument to DEPOSIT-FIXNUM-DIGIT is a mask for those bits. The second -;;; branch handles the need to grab more bits than the fixnum new-byte has, but -;;; new-byte is positive; therefore, any virtual bits are zero. The mask for -;;; bits that don't fit in the current result digit is simply the remaining -;;; bits in the bignum digit containing new-byte; we don't care if we store -;;; some extra in the next result digit since they will be zeros. The last -;;; branch handles the need to grab more bits than the fixnum new-byte has, but -;;; new-byte is negative; therefore, any virtual bits must be explicitly filled -;;; in as ones. We call DEPOSIT-FIXNUM-DIGIT to grab what bits actually exist -;;; and to fill in the current result digit. -(defun deposit-fixnum-bits (new-byte byte-len pos-digit pos-bits - end-digit end-bits result) - (declare (type bignum-index pos-digit end-digit)) - (let ((other-bits (- digit-size pos-bits)) - (new-byte-digit (%fixnum-to-digit new-byte))) - (declare (type bignum-element-type new-byte-digit)) - (cond ((< byte-len maximum-fixnum-bits) - (deposit-fixnum-digit new-byte-digit byte-len pos-digit pos-bits - other-bits result - (- byte-len other-bits))) - ((or (plusp new-byte) (zerop new-byte)) - (deposit-fixnum-digit new-byte-digit byte-len pos-digit pos-bits - other-bits result pos-bits)) - (t - (multiple-value-bind (digit bits) - (deposit-fixnum-digit new-byte-digit byte-len pos-digit pos-bits - other-bits result - (if (< (- byte-len other-bits) digit-size) - (- byte-len other-bits) - digit-size)) - (declare (type bignum-index digit)) - (cond ((< digit end-digit) - (setf (%bignum-ref result digit) - (logior (%bignum-ref result digit) - (%ashl (%make-ones (- digit-size bits)) bits))) - (do ((i (1+ digit) (1+ i))) - ((= i end-digit) - (setf (%bignum-ref result i) (%make-ones end-bits))) - (setf (%bignum-ref result i) all-ones-digit))) - ((> digit end-digit)) - ((< bits end-bits) - (setf (%bignum-ref result digit) - (logior (%bignum-ref result digit) - (%ashl (%make-ones (- end-bits bits)) - bits)))))))))) - -;;; This fills in the current result digit from new-byte-digit. The first case -;;; handles everything we want fitting in the current digit, and other-bits is -;;; the number of bits remaining to be filled in result's current digit. This -;;; number is digit-size minus pos-bits. The second branch handles filling in -;;; result's current digit, and it shoves the unused bits of new-byte-digit -;;; into the next result digit. This is correct regardless of new-byte-digit's -;;; sign. It returns the new current result digit and how many bits already -;;; filled in the result digit. -(defun deposit-fixnum-digit (new-byte-digit byte-len pos-digit pos-bits - other-bits result next-digit-bits-needed) - (declare (type bignum-index pos-digit) - (type bignum-element-type new-byte-digit next-digit-mask)) - (cond ((<= byte-len other-bits) - ;; Bits from new-byte fit in the current result digit. - (setf (%bignum-ref result pos-digit) - (logior (%bignum-ref result pos-digit) - (%ashl (logand new-byte-digit (%make-ones byte-len)) - pos-bits))) - (if (= byte-len other-bits) - (values (1+ pos-digit) 0) - (values pos-digit (+ byte-len pos-bits)))) - (t - ;; Some of new-byte's bits go in current result digit. - (setf (%bignum-ref result pos-digit) - (logior (%bignum-ref result pos-digit) - (%ashl (logand new-byte-digit (%make-ones other-bits)) - pos-bits))) - (let ((pos-digit+1 (1+ pos-digit))) - ;; The rest of new-byte's bits go in the next result digit. - (setf (%bignum-ref result pos-digit+1) - (logand (ash new-byte-digit (- other-bits)) - ;; Must LOGAND after shift here. - (%make-ones next-digit-bits-needed))) - (if (= next-digit-bits-needed digit-size) - (values (1+ pos-digit+1) 0) - (values pos-digit+1 next-digit-bits-needed)))))) - -;;; This starts at result's pos-digit skipping pos-bits, and it stores bits -;;; from new-byte, a bignum, into result. It effectively stores byte-len -;;; number of bits, but never stores past end-digit and end-bits in result. -;;; When handling a starting bit unaligned with a digit boundary, we check -;;; in the second branch for the byte spec fitting into the pos-digit element -;;; after after pos-bits; DEPOSIT-UNALIGNED-BIGNUM-BITS expects at least one -;;; digit boundary crossing. -(defun deposit-bignum-bits (bignum-byte byte-len pos-digit pos-bits - end-digit end-bits result) - (declare (type bignum-index pos-digit end-digit)) - (cond ((zerop pos-bits) - (deposit-aligned-bignum-bits bignum-byte pos-digit end-digit end-bits - result)) - ((or (= end-digit pos-digit) - (and (= end-digit (1+ pos-digit)) - (zerop end-bits))) - (setf (%bignum-ref result pos-digit) - (logior (%bignum-ref result pos-digit) - (%ashl (logand (%bignum-ref bignum-byte 0) - (%make-ones byte-len)) - pos-bits)))) - (t (deposit-unaligned-bignum-bits bignum-byte pos-digit pos-bits - end-digit end-bits result)))) - -;;; This deposits bits from bignum-byte into result starting at pos-digit and -;;; the zero'th bit. It effectively only stores bits to end-bits in the -;;; end-digit element of result. The loop termination code takes care of -;;; picking up the last digit's bits or filling in virtual negative sign bits. -(defun deposit-aligned-bignum-bits (bignum-byte pos-digit end-digit end-bits - result) - (declare (type bignum-index pos-digit end-digit)) - (let* ((bignum-len (%bignum-length bignum-byte)) - (bignum-plusp (%bignum-0-or-plusp bignum-byte bignum-len))) - (declare (type bignum-index bignum-len)) - (do ((i 0 (1+ i )) - (j pos-digit (1+ j))) - ((or (= j end-digit) (= i bignum-len)) - (cond ((= j end-digit) - (cond ((< i bignum-len) - (setf (%bignum-ref result j) - (logand (%bignum-ref bignum-byte i) - (%make-ones end-bits)))) - (bignum-plusp) - (t - (setf (%bignum-ref result j) (%make-ones end-bits))))) - (bignum-plusp) - (t - (do ((j j (1+ j))) - ((= j end-digit) - (setf (%bignum-ref result j) (%make-ones end-bits))) - (setf (%bignum-ref result j) all-ones-digit))))) - (setf (%bignum-ref result j) (%bignum-ref bignum-byte i))))) - -;;; This assumes at least one digit crossing. -(defun deposit-unaligned-bignum-bits (bignum-byte pos-digit pos-bits - end-digit end-bits result) - (declare (type bignum-index pos-digit end-digit)) - (let* ((bignum-len (%bignum-length bignum-byte)) - (bignum-plusp (%bignum-0-or-plusp bignum-byte bignum-len)) - (low-mask (%make-ones pos-bits)) - (bits-past-pos-bits (- digit-size pos-bits)) - (high-mask (%make-ones bits-past-pos-bits)) - (minus-high-bits (- bits-past-pos-bits))) - (declare (type bignum-element-type low-mask high-mask) - (type bignum-index bignum-len)) - (do ((i 0 (1+ i)) - (j pos-digit j+1) - (j+1 (1+ pos-digit) (1+ j+1))) - ((or (= j end-digit) (= i bignum-len)) - (cond - ((= j end-digit) - (setf (%bignum-ref result j) - (cond - ((>= pos-bits end-bits) - (logand (%bignum-ref result j) (%make-ones end-bits))) - ((< i bignum-len) - (logior (%bignum-ref result j) - (%ashl (logand (%bignum-ref bignum-byte i) - (%make-ones (- end-bits pos-bits))) - pos-bits))) - (bignum-plusp - (logand (%bignum-ref result j) - ;; 0's between pos-bits and end-bits positions. - (logior (%ashl (%make-ones (- digit-size end-bits)) - end-bits) - low-mask))) - (t (logior (%bignum-ref result j) - (%ashl (%make-ones (- end-bits pos-bits)) - pos-bits)))))) - (bignum-plusp) - (t - (setf (%bignum-ref result j) - (%ashl (%make-ones bits-past-pos-bits) pos-bits)) - (do ((j j+1 (1+ j))) - ((= j end-digit) - (setf (%bignum-ref result j) (%make-ones end-bits))) - (declare (type bignum-index j)) - (setf (%bignum-ref result j) all-ones-digit))))) - (declare (type bignum-index i j j+1)) - (let ((digit (%bignum-ref bignum-byte i))) - (declare (type bignum-element-type digit)) - (setf (%bignum-ref result j) - (logior (%bignum-ref result j) - (%ashl (logand digit high-mask) pos-bits))) - (setf (%bignum-ref result j+1) - (logand (ash digit minus-high-bits) - ;; LOGAND should be unnecessary here with a logical right - ;; shift or a correct digit-sized one. - low-mask)))))) -|# +;;;; There used to be a bunch of code to implement "efficient" versions of LDB +;;;; and DPB here. But it apparently was never used, so it's been deleted. +;;;; --njf, 2007-02-04 ;;;; TRUNCATE @@ -2301,246 +1848,11 @@ IS LESS EFFICIENT BUT EASIER TO MAINTAIN. BILL SAYS THIS CODE CERTAINLY WORKS! (%normalize-bignum rem (%bignum-length rem)))))))))) -;;;; %FLOOR primitive for BIGNUM-TRUNCATE - -;;; When a machine leaves out a 2*digit-size by digit-size divide -;;; instruction (that is, two bignum-digits divided by one), we have to -;;; roll our own (the hard way). Basically, we treat the operation as -;;; four digit-size/2 digits divided by two digit-size/2 digits. This -;;; means we have duplicated most of the code above to do this nearly -;;; general digit-size/2 digit bignum divide, but we've unrolled loops -;;; and made use of other properties of this specific divide situation. - -;;;; %FLOOR for machines with a 32x32 divider. - -#!-sb-fluid -(declaim (inline 32x16-subtract-with-borrow 32x16-add-with-carry - 32x16-divide 32x16-multiply 32x16-multiply-split)) - -#!+32x16-divide -(defconstant 32x16-base-1 (1- (ash 1 (/ sb!vm:n-word-bits 2)))) - -#!+32x16-divide -(deftype bignum-half-element-type () `(unsigned-byte ,(/ sb!vm:n-word-bits 2))) -#!+32x16-divide -(defconstant half-digit-size (/ digit-size 2)) - -;;; This is similar to %SUBTRACT-WITH-BORROW. It returns a -;;; half-digit-size difference and a borrow. Returning a 1 for the -;;; borrow means there was no borrow, and 0 means there was one. -#!+32x16-divide -(defun 32x16-subtract-with-borrow (a b borrow) - (declare (type bignum-half-element-type a b) - (type (integer 0 1) borrow)) - (let ((diff (+ (- a b) borrow 32x16-base-1))) - (declare (type (unsigned-byte #.(1+ half-digit-size)) diff)) - (values (logand diff (1- (ash 1 half-digit-size))) - (ash diff (- half-digit-size))))) - -;;; This adds a and b, half-digit-size quantities, with the carry k. It -;;; returns a half-digit-size sum and a second value, 0 or 1, indicating -;;; whether there was a carry. -#!+32x16-divide -(defun 32x16-add-with-carry (a b k) - (declare (type bignum-half-element-type a b) - (type (integer 0 1) k)) - (let ((res (the fixnum (+ a b k)))) - (declare (type (unsigned-byte #.(1+ half-digit-size)) res)) - (if (zerop (the fixnum (logand (ash 1 half-digit-size) res))) - (values res 0) - (values (the bignum-half-element-type (logand (1- (ash 1 half-digit-size)) res)) - 1)))) - -;;; This is probably a digit-size by digit-size divide instruction. -#!+32x16-divide -(defun 32x16-divide (a b c) - (declare (type bignum-half-element-type a b c)) - (floor (the bignum-element-type - (logior (the bignum-element-type (ash a 16)) - b)) - c)) - -;;; This basically exists since we know the answer won't overflow -;;; bignum-element-type. It's probably just a basic multiply instruction, but -;;; it can't cons an intermediate bignum. The result goes in a non-descriptor -;;; register. -#!+32x16-divide -(defun 32x16-multiply (a b) - (declare (type bignum-half-element-type a b)) - (the bignum-element-type (* a b))) - -;;; This multiplies a and b, half-digit-size quantities, and returns the -;;; result as two half-digit-size quantities, high and low. -#!+32x16-divide -(defun 32x16-multiply-split (a b) - (let ((res (32x16-multiply a b))) - (declare (the bignum-element-type res)) - (values (the bignum-half-element-type (logand (1- (ash 1 half-digit-size)) (ash res (- half-digit-size)))) - (the bignum-half-element-type (logand (1- (ash 1 half-digit-size)) res))))) - -;;; The %FLOOR below uses this buffer the same way BIGNUM-TRUNCATE uses -;;; *truncate-x*. There's no y buffer since we pass around the two -;;; half-digit-size digits and use them slightly differently than the -;;; general truncation algorithm above. -#!+32x16-divide -(defvar *32x16-truncate-x* (make-array 4 :element-type 'bignum-half-element-type - :initial-element 0)) - -;;; This does the same thing as the %FLOOR above, but it does it at Lisp level -;;; when there is no 64x32-bit divide instruction on the machine. -;;; -;;; It implements the higher level tactics of BIGNUM-TRUNCATE, but it -;;; makes use of special situation provided, four half-digit-size digits -;;; divided by two half-digit-size digits. -#!+32x16-divide -(defun %floor (a b c) - (declare (type bignum-element-type a b c)) - ;; Setup *32x16-truncate-x* buffer from a and b. - (setf (aref *32x16-truncate-x* 0) - (the bignum-half-element-type (logand (1- (ash 1 half-digit-size)) b))) - (setf (aref *32x16-truncate-x* 1) - (the bignum-half-element-type - (logand (1- (ash 1 half-digit-size)) - (the bignum-half-element-type (ash b (- half-digit-size)))))) - (setf (aref *32x16-truncate-x* 2) - (the bignum-half-element-type (logand (1- (ash 1 half-digit-size)) a))) - (setf (aref *32x16-truncate-x* 3) - (the bignum-half-element-type - (logand (1- (ash 1 half-digit-size)) - (the bignum-half-element-type (ash a (- half-digit-size)))))) - ;; From DO-TRUNCATE, but unroll the loop. - (let* ((y1 (logand (1- (ash 1 half-digit-size)) (ash c (- half-digit-size)))) - (y2 (logand (1- (ash 1 half-digit-size)) c)) - (q (the bignum-element-type - (ash (32x16-try-bignum-truncate-guess - (32x16-truncate-guess y1 y2 - (aref *32x16-truncate-x* 3) - (aref *32x16-truncate-x* 2) - (aref *32x16-truncate-x* 1)) - y1 y2 1) - 16)))) - (declare (type bignum-element-type q) - (type bignum-half-element-type y1 y2)) - (values (the bignum-element-type - (logior q - (the bignum-half-element-type - (32x16-try-bignum-truncate-guess - (32x16-truncate-guess - y1 y2 - (aref *32x16-truncate-x* 2) - (aref *32x16-truncate-x* 1) - (aref *32x16-truncate-x* 0)) - y1 y2 0)))) - (the bignum-element-type - (logior (the bignum-element-type - (ash (aref *32x16-truncate-x* 1) 16)) - (the bignum-half-element-type - (aref *32x16-truncate-x* 0))))))) - -;;; This is similar to TRY-BIGNUM-TRUNCATE-GUESS, but this unrolls the two -;;; loops. This also substitutes for %DIGIT-0-OR-PLUSP the equivalent -;;; expression without any embellishment or pretense of abstraction. The first -;;; loop is unrolled, but we've put the body of the loop into the function -;;; 32X16-TRY-GUESS-ONE-RESULT-DIGIT. -#!+32x16-divide -(defun 32x16-try-bignum-truncate-guess (guess y-high y-low low-x-digit) - (declare (type bignum-index low-x-digit) - (type bignum-half-element-type guess y-high y-low)) - (let ((high-x-digit (+ 2 low-x-digit))) - ;; Multiply guess and divisor, subtracting from dividend simultaneously. - (multiple-value-bind (guess*y-hold carry borrow) - (32x16-try-guess-one-result-digit guess y-low 0 0 1 low-x-digit) - (declare (type bignum-half-element-type guess*y-hold) - (fixnum carry borrow)) - (multiple-value-bind (guess*y-hold carry borrow) - (32x16-try-guess-one-result-digit guess y-high guess*y-hold - carry borrow (1+ low-x-digit)) - (declare (type bignum-half-element-type guess*y-hold) - (fixnum borrow) - (ignore carry)) - (setf (aref *32x16-truncate-x* high-x-digit) - (32x16-subtract-with-borrow (aref *32x16-truncate-x* high-x-digit) - guess*y-hold borrow)))) - ;; See whether guess is off by one, adding one Y back in if necessary. - (cond ((zerop (logand (ash 1 (1- half-digit-size)) - (aref *32x16-truncate-x* high-x-digit))) - ;; The subtraction result is zero or positive. - guess) - (t - ;; If subtraction has negative result, add one divisor value back - ;; in. The guess was one too large in magnitude. - (multiple-value-bind (v carry) - (32x16-add-with-carry y-low - (aref *32x16-truncate-x* low-x-digit) - 0) - (declare (type bignum-half-element-type v)) - (setf (aref *32x16-truncate-x* low-x-digit) v) - (multiple-value-bind (v carry) - (32x16-add-with-carry y-high - (aref *32x16-truncate-x* - (1+ low-x-digit)) - carry) - (setf (aref *32x16-truncate-x* (1+ low-x-digit)) v) - (setf (aref *32x16-truncate-x* high-x-digit) - (32x16-add-with-carry (aref *32x16-truncate-x* high-x-digit) - carry 0)))) - (if (zerop (logand (ash 1 (1- half-digit-size)) guess)) - (1- guess) - (1+ guess)))))) - -;;; This is similar to the body of the loop in TRY-BIGNUM-TRUNCATE-GUESS that -;;; multiplies the guess by y and subtracts the result from x simultaneously. -;;; This returns the digit remembered as part of the multiplication, the carry -;;; from additions done on behalf of the multiplication, and the borrow from -;;; doing the subtraction. -#!+32x16-divide -(defun 32x16-try-guess-one-result-digit (guess y-digit guess*y-hold - carry borrow x-index) - (multiple-value-bind (high-digit low-digit) - (32x16-multiply-split guess y-digit) - (declare (type bignum-half-element-type high-digit low-digit)) - (multiple-value-bind (low-digit temp-carry) - (32x16-add-with-carry low-digit guess*y-hold carry) - (declare (type bignum-half-element-type low-digit)) - (multiple-value-bind (high-digit temp-carry) - (32x16-add-with-carry high-digit temp-carry 0) - (declare (type bignum-half-element-type high-digit)) - (multiple-value-bind (x temp-borrow) - (32x16-subtract-with-borrow (aref *32x16-truncate-x* x-index) - low-digit borrow) - (declare (type bignum-half-element-type x)) - (setf (aref *32x16-truncate-x* x-index) x) - (values high-digit temp-carry temp-borrow)))))) - -;;; This is similar to BIGNUM-TRUNCATE-GUESS, but instead of computing -;;; the guess exactly as described in the its comments (digit by digit), -;;; this massages the digit-size/2 quantities into digit-size quantities -;;; and performs the -#!+32x16-divide -(defun 32x16-truncate-guess (y1 y2 x-i x-i-1 x-i-2) - (declare (type bignum-half-element-type y1 y2 x-i x-i-1 x-i-2)) - (let ((guess (if (= x-i y1) - (1- (ash 1 half-digit-size)) - (32x16-divide x-i x-i-1 y1)))) - (declare (type bignum-half-element-type guess)) - (loop - (let* ((guess*y1 (the bignum-element-type - (ash (logand (1- (ash 1 half-digit-size)) - (the bignum-element-type - (32x16-multiply guess y1))) - 16))) - (x-y (%subtract-with-borrow - (the bignum-element-type - (logior (the bignum-element-type - (ash x-i-1 16)) - x-i-2)) - guess*y1 - 1)) - (guess*y2 (the bignum-element-type (%multiply guess y2)))) - (declare (type bignum-element-type guess*y1 x-y guess*y2)) - (if (%digit-greater guess*y2 x-y) - (decf guess) - (return guess)))))) +;;;; There used to be a pile of code for implementing division for bignum digits +;;;; for machines that don't have a 2*digit-size by digit-size divide instruction. +;;;; This happens to be most machines, but all the SBCL ports seem to be content +;;;; to implement SB-BIGNUM:%FLOOR as a VOP rather than using the code here. +;;;; So it's been deleted. --njf, 2007-02-04 ;;;; general utilities