;;; Spice Lisp numeric package, SPNUM.			-*-Lisp-*-
;;;
;;; **********************************************************************
;;; This code was written as part of the Spice Lisp project at
;;; Carnegie-Mellon University, and has been placed in the public domain.
;;; Spice Lisp is currently incomplete and under active development.
;;; If you want to use this code or any part of Spice Lisp, please contact
;;; Scott Fahlman (FAHLMAN@CMUC). 
;;; **********************************************************************
;;;
;;;	Author : Brian G. Milnes (Milnes@CMU-20C)
;;;	Please report bugs, gripes or comments about this code to both
;;;	Scott Fahlman and me.

;;; 
;;;		I. ABSTRACT
;;;


;;;	Spnum is the spice lisp code implementing the non-transcendental 
;;; and non-irrational sections of the number chapter (12) of the
;;; common lisp manual. Spnum is used only for the spice lisp implementation on
;;; the perq. This package contains code for
;;;
;;; 	- fixnum operations that overflow to a bignum,
;;;	- integer operations that produce a ratio,
;;;	- bignum operations,
;;;	- fixnum bignum operations,
;;;	- and ratio operations.
;;;
;;;	Any functions which end in -escape are designed to be called
;;; by the PERQ micro code whenever it can not handle the arguments,
;;; or result, of an arithmetic operation.
;;;	

;;;
;;;		CONTENTS
;;;

;;;
;;;	I.	Abstract
;;;	IV.	Accessing macros
;;;	 1.	Random macros
;;;	V.	Relational operations
;;;	VI.	Arithmetic operations
;;;	 2.	Fixnum operations
;;;	 3.	Bignum operations
;;;	 4.	Ratio operations
;;;	 5.	Integer and mixed mode operations
;;;	 6.	Arithmetic bugout dispatch routines
;;;	VII.	Logical operations
;;;     VIII.	Exported routines (other)

;;;
;;;		IV. ACCESSING MACROS
;;;

;;;
;;;

;

;;;
;;;
;;;			1. Random macros
;;;


;;; Cons-a-Bignum generates a bignum, of size n subtype s.

(eval-when (compile)
(defmacro cons-a-bignum (n)
  `(%primitive alloc-bignum ,n 3))

(defmacro bignum-length (b)
  `(%sp-get-vector-length ,b))

(defmacro complement (X)
  `(if (zerop ,X) 1 0))

;;; One for negative, zero for positive.
(defmacro bignum-sign (b)
  `(let ((b ,b))
     (ldb (byte 1 7) (%sp-v-access b (1- (bignum-length b))))))

(defmacro integerize1 (n)
  `(let ((n ,n))
     (if (fixnump n) n (integerize n (bignum-length n)))))

;;; Fills the field with sign bits, and makes sure that it's large enough.
(defmacro create-bignum (n s)
  `(let ((byte (- ,s))
	 (size (max 4 ,n))) ;For Brian's code.
     (do ((res (cons-a-bignum size))
	  (i 0 (1+ i)))
	 ((= i size) res)
       (%sp-v-store res i byte))))

(defmacro integer-compare (x y)
  `(let ((x (integerize1 ,x))
	 (y (integerize1 ,y)))
     (typecase x
       (bignum (typecase y
		 (bignum (bignum-compare x y))
		 (fixnum (bignum-fixnum-compare x))))
       (fixnum (typecase y
		 (bignum (bignum-fixnum-compare y))
		 (fixnum (fixnum-compare x y)))))))



;;;
;;; Fixnum-Sign returns the sign of a fixnum as the fixnum 0 or 1.
;;; VM:T

(defmacro fixnum-sign (X)
  `(%sp-logldb 1 #,(1- %fixnum-length) ,X))

;;;
;;; Integer-sign returns the sign of any integer argument.
;;;
;;; VM:T

(defmacro integer-sign (x)
  `(cond ((bignump ,x) (bignum-sign ,x))
	 ((fixnump ,x) (fixnum-sign ,x))
	 (t (error "Non integer argument passed to integer-sign macro. ~%"))))

;;;
;;;	Get-byte (P S N) returns a field of bytes from the integer
;;; N, whose highest order byte is position P and whose lowest 
;;; order bit is in position P - S. Get-byte sign extends, and 
;;; fills righthand zeros.
;;; VM:T

(defmacro get-byte (p s n)
  `(if (> (- (1+ ,p) ,s) 0)
       (%sp-ldb ,s (- (1+ ,p) ,s) ,n)
       (if (< ,p 0) 0
	   (%sp-lsh (%sp-ldb (1+ ,p) 0 ,n)
		    (- ,s (1+ ,p))))))

;;;
;;;   Fixable (X) returns true iff X is a number (of any type)
;;; that can be represented as a fixnum.
;;; VM:T

(defmacro fixable (X)
  `(<= max-negative-fixnum ,x max-positive-fixnum))



;;;
;;;	Get-fixnum-byte (P S X) returns right packed the next s bits
;;; whose highest bit is P and whose lowest bit is P-S from the fixnum
;;; X. if the bits run off the right hand side of the fixnum, the spaces
;;; are padded with 0's. if P is too large, then an run time error will occur.
;;; VM:()

(defmacro get-fixnum-byte (p s x)
  `(cond ((< ,p 0) 0)
	 ((< (- ,p ,s) 0) (%sp-logdpb (%sp-logldb (1+ ,p) 0 ,x)
				  (1+ ,p) (- 7 ,p) 0))
	 (t (%sp-logdpb (%sp-logldb ,s (1+ (- ,p ,s)) ,x)
		    ,s (- 8 ,s) 0))))

)	       ;Close of eval-when(compile).


;;;
;;;
;;;			4. Not macros, but ...

(defun copy-xnum (n)
  (let ((res (cons-a-bignum (bignum-length n))))
    (%sp-byte-blt n 0 res 0 (bignum-length n))
    res))




;;; Byte-bash and friends?

;;; Returns a bignum of the specified size, "equal" to the given integer.
(defun sizify (n size)
  (let ((res (create-bignum size (if (minusp n) 1 0))))
    (cond ((fixnump n)
	   (do ((i 0 (1+ i))
		(n n (ash n -8)))
	       ((= i 4))
	     (%sp-v-store res i n)))
	  (t (bash-bytes n size 0 res)))
    res))

(defun nsizify (n size)
  (if (and (bignump n)
	   (>= (bignum-length n) size))
      (%sp-shrink-vector n size)
      (sizify n size)))

(defun bash-bytes (f i p n)
  "Replaces the i bytes at byte p of xnum n with the lower bytes of f."
  (let* ((i (min i (bignum-length f)))
	 (end (min (+ p i) (bignum-length n))))
    (%sp-byte-blt f 0 n p end)
    n))




;;;
;;;		V. RELATIONAL OPERATIONS
;;;

;;;

;;;

;;;
;;;
;;; Fixnum-Compare (X Y) returns '> '< or '= depending upon
;;; whether X = Y or X > Y or X < Y.
;;; VM:T

(eval-when (compile)
(defmacro fixnum-compare (X Y)
  `(or (and (= ,x ,y) '=)
       (and (> ,x ,y) '>)
       '<))
)

;;; bignum-fixnum-compare returns > or < but never =. 
;;; VM:T

(defun bignum-fixnum-compare (b)
  (if (= 0 (bignum-sign b)) '> '<))



;;;
;;; Bignum-compare (X Y) returns '= '> or '< depending upon
;;; whether X = Y, X > Y or  X < Y.
;;;
;;; VM:T

;;; Returns <, >, or =.
(defun bignum-compare (x y)
  (let ((trap nil)
	(sign-x (bignum-sign x))
	(sign-y (bignum-sign y))
	(length-x (bignum-length x))
	(length-y (bignum-length y)))
    (if (not (eq (setq trap (zerop sign-x))
		 (zerop sign-y)))
	(if trap '> '<)
	(if (not (eq '= (setq trap (fixnum-compare length-x length-y))))
	    (if (zerop sign-x)
		trap 
		(if (eq trap '>) '< '>))
	    (do ((byte-counter (1- length-x)
			       (1- byte-counter)))
		((or (not (eq '= (setq trap
				       (fixnum-compare
					(%sp-v-access x byte-counter)
					(%sp-v-access y byte-counter)))))
		     (eq '= (fixnum-compare byte-counter 0)))
		 
		 trap)
	      )))))





(defun numerator (x)
  "Returns the numerator of a rational."
  (%primitive numerator x))

(defun denominator (x)
  "Returns the denominator of a rational."
  (%primitive denominator x))

;;; ratio-compare returns >,< or = as a comparison of
;;; two ratios.

;;; Returns > < or =.
(defun ratio-compare (x y)
  (let* ((numx (%primitive numerator x))
	 (denx (%primitive denominator x))
	 (numy (%primitive numerator y))
	 (deny (%primitive denominator y))
	 (xsign (minusp numx)) ; Strange way to hold sign, huh ?
	 (ysign (minusp numy)))
    (cond ((not (eq xsign ysign)) (if xsign '< '>))
	  ((and (zerop numx) (zerop numy)) '=)
	  ((and (= numx numy) (= denx deny)) '=)
	  ((= numx numy)
	   (cond ((> denx deny) 
		  (if xsign '> '<))
		 (t (if xsign '< '>))))
	  ((= denx deny) (if (> numx numy) '> '<))
	  (t (compare-numbers (* numx deny) (* numy denx))))))

;;; ratio-integer-compare returns >,< or = as a comparison
;;; of the arguments x and y.
;;; vm:T


(defun ratio-integer-compare (x y)
  (let* ((numx (%primitive numerator x))
	 (denx (%primitive denominator x))
	 (xsign (minusp numx)))
    (cond ((not (eq xsign (minusp y)))
	   (if xsign '< '>))
	  ((and (= 1 denx))
	   (compare-numbers numx y))
	  (t (multiple-value-bind (xi xr) (truncate numx denx)
		    (case (compare-numbers xi y)
		      (> '>)
		      (< '<)
		      (= (cond ((zerop xr) '=)
			       ((minusp xr) '<)
			       (t '>)))))))))



;;;	 2. FIXNUM OPERATIONS
;;;
;;;

;;;  Integerize reduces any vector of type xnum into
;;; it's arithmetically correct integer representation.
;;; VM:T


(defun integerize (b v-length)
 (let* ((bit-length (integer-length b))
	(length-needed (1+ (truncate bit-length 8))))
    (if (< bit-length %fixnum-length)
	(%sp-logdpb (%sp-v-access b 3) 4 24
		    (%sp-logdpb (%sp-v-access b 2) 8 16
				(%sp-logdpb (%sp-v-access b 1) 8 8
					    (%sp-v-access b 0))))
	(if (> v-length length-needed)
	    (%sp-shrink-vector b length-needed) b))))

;;;  Add-in-byte multiplies two bytes together and adds 
;;; them plus the carry, into the bignum X at I and
;;; returns the carry. The index I is an 8 bit byte index into
;;; the vector.

(defun add-in-bytes (b1 b2 x i carry)
  (let ((sum (+ (%sp-lsh (%sp-v-access x (1+ i)) 8) (%sp-v-access x i)
		(* b1 b2) (if (zerop carry) 0 256)))) 
;; The carry is 16 bit, so I must slide it 8 bits left to get it 
;; in the right byte.
    (%sp-v-store x i sum)
    (%sp-v-store x (1+ i) (%sp-logldb 8 8 sum))
    (%sp-logldb 1 16 sum)))



;;;  Add-in-carry adds a carry bit into a bignum at a given byte.
;;;  VM:T

(defun add-in-carry (v i)
  (do ((j (1+ i) (1+ j))
       (sum (zerop (%sp-logldb 1 8 (%sp-v-store v i (1+ (%sp-v-access v i)))))
	    (zerop (%sp-logldb 1 8 (%sp-v-store v j (1+ (%sp-v-access v j))))))
       )
      (sum)))



;;; %sp-fixnum* multiplies two fixnums and returns the integer result,
;;; be it bignum or fixnum.
;;; VM:T

(defun %sp-fixnum* (x y)
  (let* ((xs (fixnum-sign x))
	 (ys (fixnum-sign y))
	 (new-s (if (= xs ys) 0 1))
	 (x (if (zerop xs) x (- x)))
	 (y (if (zerop ys) y (- y))))
    (cond ((not (and (fixnump x) (fixnump y)))
	   (if (zerop new-s) (* x y) (- (* x y))))
	  (t  (let ((xl (integer-length x))
		    (yl (integer-length y)))
		(cond ((or (zerop xl) (zerop yl)) 0)
		      (t (let* ((xb (1+ (truncate xl 8)))
				(yb (1+ (truncate yl 8)))
				(zb (+ xb yb))
				(z (cons-a-bignum zb))
				(carry 0))
			   (do ((xi 0 (1+ xi))
				(xbyte 0)
				(xbs 8. (if (= xi 2) 4 8)))
			       ((= xi xb))
			     (setq xbyte (%sp-ldb xbs (* 8 xi) x))
			     (unless (zerop xbyte)
				     (do ((yi 0 (1+ yi))
					  (yi+xi xi (1+ yi+xi))
					  (ybyte 0)
					  (ybs 8 (if (= yi 2) 4 8)))
					 ((= yi yb)
					  (unless (zerop carry)
						  (add-in-carry
						   z (+ yb xi 1))))
				       (setq ybyte (%sp-ldb ybs (* 8 yi) y))
				       (unless (zerop ybyte)
					       (setq carry (add-in-bytes
							    ybyte xbyte
							    z yi+xi carry))))))
			   (let ((z (integerize z zb)))
			     (if (zerop new-s) z (- z)))))))))))


;;; %sp-fixnum/  builds a ratio of the arguments x and y.
;;; It knows that any arguments the microcode has bugged out
;;; on must be an uneven division, or an fixnum division whose
;;; quotient is a bignum. So if args are most-negative-fixnum and -1, then it
;;; returns (1+ most-positive-fixnum) or it simply builds a ratio of them.
;;; VM:T

(defun %sp-fixnum/ (x y)
 (cond ((and (= -1 y) (= x most-negative-fixnum)) (1+ most-positive-fixnum))
       (t (let ((gcd-x-y (gcd x y)))
	    (unless (= gcd-x-y 1)
	      (setq x (/ x gcd-x-y)
		    y (/ y gcd-x-y)))
	    (cond ((minusp y)
		   (%primitive make-ratio (- x) (- y)))
		  (t
		   (%primitive make-ratio x y)))))))

;;; %sp-fixnum+ does an integer addition, into a bignum of length 4
;;; which is integerized into a fixnum or a bignum, or into
;;; a fixnum, which is sign extended.
;;; VM:t

(defun %sp-fixnum+ (x y)
  (let* ((xs (fixnum-sign x))
	 (ys (fixnum-sign y))
	 (same-sign (= xs ys)))
    (cond (same-sign
	   (let* ((z (cons-a-bignum 4))
		  (sum1 (+ (%sp-logldb 16 0 x)
			   (%sp-logldb 16 0 y)))
		  (sum2 (+ (%sp-logldb 12 16 x)
			   (%sp-logldb 12 16 y)
			   (%sp-logldb 1 16 sum1))))
	     (unless (zerop xs)
	       (setq sum2 (%sp-logdpb 255 3 13 sum2)))
	     (%sp-typed-v-store 4 z 0 sum1)
	     (%sp-typed-v-store 4 z 1 sum2)
	     (integerize z 4)))
	  (t (let* ((sum1 (+ (%sp-logldb 16 0 x)
			     (%sp-logldb 16 0 y)))
		    (sum2 (+ (%sp-logldb 12 16 x)
			     (%sp-logldb 12 16 y)
			     (%sp-logldb 1 16 sum1))))
	       (%sp-logdpb sum2 12 16 sum1))))))


;;; %sp-fixnum- does a subtraction into a 4 byte xnum, or a fixnum.
;;; VM:t


(defun %sp-fixnum- (x y)
  (let ((xs (fixnum-sign x))
	(ys (fixnum-sign y)))
    (cond ((= xs ys)
	   (let* ((diff1 (- (%sp-logldb 16 0 x)
			    (%sp-logldb 16 0 y)))
		  (diff2 (- (%sp-logldb 12 16 x)
			    (%sp-logldb 12 16 y)
			    (if (minusp diff1) 1 0))))
	     (%sp-logdpb diff2 12 16 diff1)))
	  (t (let* ((diff1 (- (%sp-logldb 16 0 x)
			      (%sp-logldb 16 0 y)))
		    (diff2 (- (%sp-logldb 12 16 x)
			      (%sp-logldb 12 16 y)
			      (if (minusp diff1) 1 0))))
	       (cond ((= xs (%sp-logldb 1 11 diff2)) ; No overflow.
		      (%sp-logdpb diff2 12 16 diff1))
		     (t (let ((z (cons-a-bignum 4)))
			  (%sp-typed-v-store 4 z 0 diff1)
			  (%sp-typed-v-store
			   4 z 1
			   (if (zerop xs) diff2
			       (%sp-logdpb 255 4 12 diff2)))
			  (integerize z 4)))))))))


;;;		 3. Bignum operations
;;;    
;;;

;;; Stuff used in bignum printing.

;;; 
;;; Differs from previous in that we do not need to check for 
;;; leading zeros.  The bignum may have any length (including zero).


(defun new-integerize (bignum)
  (let ((len (bignum-length bignum)))
    (cond ((or (> len 4) 
	       (and (= len 4) (>= (%sp-v-access bignum 3) 8)))
	   bignum)
	  (t (do ((i (1- len) (1- i))
		  (res (- (bignum-sign bignum))
		       (%sp-logdpb res 19 8 (%sp-v-access bignum i))))
		 ((minusp i) res))))))

;;; Special division for printing et al.
;;;
;;; Both divisor and dividend must be positive, and the divisor must
;;; be of integer-length <= 19 (the closer the better).
;;; 
;;; This uses two successive loops for convenience, the first collecting
;;; a remainder less than the fixnum divisor (might as well truncate),
;;; then a second to actually put the results back.  The bignum
;;; result may still be of fixnum size, and hence must be checked.
;;; Because of the first loop (and the shrink that follows it), this check
;;; is easier than previous ones.

(defun bignum-fixnum-divide-inplace (big fix)
  (do ((byte (1- (bignum-length big)) (1- byte))
       (remainder (- (bignum-sign big)))
       tmp)
      ((minusp byte)
       (values (integerize big (bignum-length big)) remainder))
    (multiple-value-setq (tmp remainder)
			 (truncate (%sp-logdpb remainder 19 8
					       (%sp-v-access big byte))
				   fix))
    (%sp-v-store big byte tmp)))

;
;(defun bignum-fixnum-divide-inplace (big fix)
;  (do ((tmp 0)
;       (remainder 0)
;       (byte (1- (bignum-length big)) (1- byte)))
;      ((not (zerop tmp))				;Plusp compares.
;       (%sp-shrink-vector big (+ byte 2))
;       (%sp-v-store big (1+ byte) tmp)
;       (do ((byte byte (1- byte)))
;	   ((minusp byte)
;	    (values (new-integerize big) remainder))
;	 (multiple-value-setq (tmp remainder)
;	   (truncate (%sp-logdpb remainder 19 8 (%sp-v-access big byte))
;		     fix))
;	 (%sp-v-store big byte tmp)))
;    (multiple-value-setq (tmp remainder) 
;      (truncate (%sp-logdpb remainder 19 8 (%sp-v-access big byte))
;		fix))))


;;;
;;; More esoterics.  Shifting in place.
;;;
;;; The result of a negative shift of ash-in-place may result in a fixnum,
;;; so the result should always be setq'd if it is to be kept
;;; (just like any other destructive operations).

(defun byte-ash-in-place (n c)
  (let* ((length (bignum-length n))
	 (newlen (max 0 (+ length c))))
    (cond ((zerop newlen) (- (bignum-sign n)))
	  (t (%sp-byte-blt n (max 0 (- c)) n (max 0 c) newlen)
	     (%sp-shrink-vector n newlen)
	     (new-integerize n)))))

  
(defun ash-in-place (n i)
  (if (fixnump n) (ash n i)
      (let* ((len (bignum-length n))
	     (sign (- (bignum-sign n)))
	     (top (%sp-logdpb (%sp-v-access n (1- len)) 8 0 sign)))
	(cond ((zerop i) n)
	      ((plusp i)
	       (if (> i (- 7 (integer-length top))) (ash n i)
		   (do ((byte 1 (1+ byte))
			(remainder (ash (%sp-v-store
					 n 0 (ash (%sp-v-access n 0) i)) -8)
				   (ash (%sp-v-store
					 n byte (%sp-logdpb
						 (%sp-v-access n byte)
						 8 i remainder))
					-8)))
		       ((= len byte) n))))
	      (t (multiple-value-bind (bytes bits) (truncate i 8)
		   (setq n (ash n bytes))
		   (if (fixnump n) (ash n bits)
		       (let ((newlen-1 (1- (bignum-length n)))
			     (-bits (- bits))
			     (bits+8 (+ 8 bits)))
			 (do ((byte newlen-1 (1- byte))
			      (previous sign))
			     ((minusp byte)
			      (if (> (- bits) (integer-length top))
				  (%sp-shrink-vector n newlen-1)
				  n))
			   (%sp-v-store
			    n byte (%sp-logdpb previous -bits bits+8
					       (ash (Setq previous
							  (%sp-v-access n
									byte))
						    bits))))))))))))

;;;  %sp-bignum* multiplies two bignums together.
;;; VM:T

(defun %sp-bignum* (a b)
  (let* ((as (bignum-sign a))
	 (bs (bignum-sign b))
	 (a (if (= as 0) a (- a)))	
	 (b (if (= bs 0) b (- b)))
	 (new-sign (if (= as bs) 0 1)))
    (cond ((or (fixnump a) (fixnump b)) 
	   (if (zerop new-sign) (* a b) (- (* a b))))
	  (t (let* ((length-a (bignum-length a))
		    (length-b (bignum-length b))
		    (length-c (+ length-a length-b))
		    (c (cons-a-bignum length-c)))
;; swap the longer bignum into a.
	       (if (> length-b length-a)
		   (psetq length-a length-b
			  length-b length-a
			  a b
			  b a))
;; byte by byte multiply with 16 bit add in's to the bignum c.
	       (do ((b←index 0 (1+ b←index))
		    (byte-in-b 0))
		   ((= b←index length-b) 
		    (if (zerop new-sign) (integerize c length-c)
			(- (integerize c length-c))))
		 (setq byte-in-b (%sp-v-access b b←index))
		 (unless (zerop byte-in-b)
			 (do ((carry 0)
			      (a←index 0 (1+ a←index))
			      (byte-in-a 0)
			      (A+b←index b←index (1+ a+b←index)))
			     ((= a←index length-a)
			      (unless (zerop carry)
				      (add-in-carry
				       c (+ length-a b←index 1))))
			   (setq byte-in-a (%sp-v-access a a←index))
			   (unless (= byte-in-a 0)
				   (setq carry (add-in-bytes
						byte-in-a byte-in-b
						c a+b←index carry)))))))))))


;;; Gb16 does a sign extending 16 bit vector access of vector.
;;; Pos is the 8 bit index into vector, length is 1- the number of
;;; 8 bit entries and sign-byte is a 16 bit byte of sign bits.
;;; VM:T

(defun gb16 (vector pos length sign-byte)
 (cond ((> pos length) sign-byte)
       ((= pos length)
	(%sp-logdpb sign-byte
		    8. 8. (%sp-v-access vector pos)))
       (t (%sp-typed-v-access 4 vector (truncate pos 2)))))

(defun gb8 (vector pos length sign-byte)
  (cond ((> pos length) (%sp-logldb 8 0 sign-byte))
	 (t (%sp-v-access vector pos))))



;;;  %sp-bignum+ is the internal bignum addition routine.
;;; VM:T

(defun %sp-bignum+ (a b)
  (let* ((length-a (bignum-length a))
	 (length-b (bignum-length b))
	 (mab (max length-a length-b))
	 (a-sign (bignum-sign a))
	 (b-sign (bignum-sign b))
	 (same-sign (if (= a-sign b-sign) t nil))
	 (carry 0)
	 (c (cons-a-bignum (if same-sign (1+ mab) mab)))
	 (la-1 (1- length-a))
	 (lb-1 (1- length-b))
	 (asb (if (zerop a-sign) 0 65535))
	 (bsb (if (zerop b-sign) 0 65535)))
    (do ((1-mab (1- mab))
	 (i 0 (+ i 2))
	 (sum 0))
	((>= i 1-mab)
	 (when (= i 1-mab)
	       (setq sum (+ (gb8 a i la-1 asb)
			    (gb8 b i lb-1 bsb) carry))
	       (%sp-v-store c i sum)))
      (setq sum (+ (gb16 a i la-1 asb)
		   (gb16 b i lb-1 bsb)
		   carry))
      (setq carry (%sp-logldb 1 16 sum))
      (%sp-typed-v-store 4 c (truncate i 2) sum))
    (cond (same-sign 
	   (cond ((not (= a-sign
			  (%sp-logldb
			   1 7 (%sp-v-access c (1- mab)))))
		  (%sp-v-store c mab asb)
		  C)                                        ;SKH.
		 (t (%sp-shrink-vector c mab))))
	  (t (integerize c mab)))))


;;; %sp-bignum- does a signed subtraction of two bignums.
;;;


(defun %sp-bignum- (a b)
  (let* ((la (bignum-length a))
	 (lb (bignum-length b))
	 (mab (max la lb))
	 (as (bignum-sign a))
	 (bs (bignum-sign b))
	 (la-1 (1- la))
	 (lb-1 (1- lb))
	 (asb (if (zerop as) 0 65535))
	 (bsb (if (zerop bs) 0 65535))
	 (carry 0))
    (cond ((eq as bs)
	   (let ((z (cons-a-bignum mab)))
	     (do ((i 0 (+ i 2))
		  (diff 0)
		  (mab-1 (1- mab)))
		 ((>= i mab-1)
		  (when (= i mab-1)
			(%sp-v-store z mab-1
					   (- (gb8 a i la-1 asb)
					      (gb8 b i lb-1 bsb)
					      carry)))
		  (integerize z mab))
	       (setq diff (- (gb16 a i la-1 asb)
			     (gb16 b i lb-1 bsb)
			     carry))
	       (setq carry (if (minusp diff) 1 0))
	       (%sp-typed-v-store 4 z (truncate i 2) diff))))
	  (t (let ((z (cons-a-bignum (1+ mab))))
	       (do ((i 0 (+ i 2))
		    (diff 0))
		 ((>= i mab)
		    (unless (zerop as)
			    (%sp-v-store z mab 255))
		    (integerize z (1+ mab)))
		 (setq diff (- (gb16 a i la-1 asb)
			       (gb16 b i lb-1 bsb)
			       carry))
		 (setq carry (if (minusp diff) 1 0))
		 (%sp-typed-v-store 4 z (truncate i 2) diff)))))))


;;;  Bignum-negate returns the negation of any bignum.
;;; VM:T.

(defun bignum-negate (X)
  (let* ((size (bignum-length x))
	 (xsign (bignum-sign x))
	 (neg-size (if (zerop xsign) size (1+ size)))
	 (neg (cons-a-bignum neg-size)))
;;; If x is negative, neg must be one byte longer just in case
;;; the negation overflows.
    (do ((byte 0 (+ byte 2))
		 (size-1 (1- size))
		 (carry 1))
		((>= byte size-1)
		 (when (= byte size-1)
		       (%sp-v-store neg byte
				    (+ (- #2r11111111
					  (%sp-v-access x byte))
				       carry))))
	      (setq carry 
		    (%sp-logldb 1 16
				(%sp-typed-v-store
				 4 neg (truncate byte 2)
				 (+ (- #2r1111111111111111
				       (%sp-typed-v-access 4 x (truncate byte 2)))
				    carry)))))
	      (integerize neg neg-size)))



;;;
;;;		4. Ratio operations

;;; Build-ratio takes two integer arguments and builds
;;; the rational number which is thier quotient.
;;; When reduction is implemented, it will go in here.
;;; VM:T

(defun build-ratio (x y)
 (multiple-value-bind (q r) (truncate x y)
   (if (zerop r) q
       (let ((gcd (gcd x y)))
	 (unless (= gcd 1)
	   (setq x (/ x gcd)
		 y (/ y gcd)))
	 (cond ((minusp y) (%primitive make-ratio (- x) (- y)))
	       (t (%primitive make-ratio x y)))))))

;;; Rational produces a rational number for any numeric argument.
;;; Rational assumed that the floating point is completely accurate.

(defun rational (x)
  (typecase x
    (float (multiple-value-bind (f e) (decode-float x)
	     (let* ((precision (float-precision f))
		    (f (truncate (scale-float f precision))))
	       (if (minusp e)
		   (build-ratio f (ash 1 (+ precision (abs e))))
		   (build-ratio (ash f e) (ash 1 precision))))))
    (rational x)
    (otherwise (error "Argument not a non complex number, ~A."
		      x))))



;;; Rationalize does a rational, but it assumes that floats
;;; are only accurate to their precision, and generates a good
;;; rational aproximation of them.
(eval-when (compile)
  (remprop 'long-float-epsilon '%constant))
;;;
(defun rationalize (x)
  (typecase x
    (rational x)
    (short-float (rationalize-float x short-float-epsilon))
    (long-float (rationalize-float x long-float-epsilon))
    (otherwise (error "Argument not a non complex number, ~A."
		      x))))


;;; Thanks to Kim Fateman, who stole this function rationalize-float
;;; from macsyma's rational. Macsyma'a rationalize was written
;;; by the legendary Gosper (rwg). Gosper is now working for xerox
;;; at parc. Guy Steele said about Gosper, "He has been called the
;;; only living 17th century mathematician and is also the best
;;; pdp-10 hacker I know." So, if you can understand or debug this
;;; code you win big.

(defun rationalize-float (x &optional (eps long-float-epsilon))
  (cond ((minusp x) (- (rationalize (- x))))
	((zerop x) 0)
	(t (let ((y ())
		 (a ()))
	     (do ((xx x (setq y (/ 1.0 (- xx (float a x)))))
		  (num (setq a (truncate x))
		       (+ (* (setq a (truncate y)) num) onum))
		  (den 1 (+ (* a den) oden))
		  (onum 1 num)
		  (oden 0 den))
		 ((and (not (zerop den))
		       (not (> (abs (/ (- x (/ (float num x)
					       (float den x)))
				       x))
			       eps)))
		  (/ num den)))))))

;;; %sp-ratio* does a ratio to ratio multiplication.
;;; VM:T


(defun %sp-ratio* (x y)
  (let ((numx (%primitive numerator x))
	(numy (%primitive numerator y))
	(denx (%primitive denominator x))
	(deny (%primitive denominator y)))
    (let ((gcdnumx (gcd numx deny))
	  (gcdnumy (gcd numy denx)))
      (let ((num (* (/ numx gcdnumx) (/ numy gcdnumy)))
	    (den (* (/ denx gcdnumy) (/ deny gcdnumx))))
	(if (= 1 den) num
	    (%primitive make-ratio num den))))))

;;; %sp-ratio/ does a ratio to ratio division.
;;; vm:T


(defun %sp-ratio/ (x y)
  (%sp-ratio* x (%sp-ratio-inverse y)))




;;; %sp-ratio-negate negates the numerator of the ratio.
;;; VM:T

(defun %sp-ratio-negate (x)
  (%primitive make-ratio (- (%primitive numerator x)) (%primitive denominator x)))


;;; %sp-ratio+ does a ratio to ratio addition.
;;;  VM:T

(defun %sp-ratio+ (x y)
  (let ((denx (%primitive denominator x))
	(deny (%primitive denominator y)))
    (build-ratio (+ (* (%primitive numerator x) deny)
		    (* (%primitive numerator y) denx))
	       (* denx deny))))

;;; %sp-ratio- does a ratio to ratio subtraction.
;;; VM:T

(defun %sp-ratio- (x y)
  (let ((denx (%primitive denominator x))
	(deny (%primitive denominator y)))
    (build-ratio (- (* (%primitive numerator x) deny)
		    (* (%primitive numerator y) denx))
	       (* denx deny))))


;;; %sp-ratio-inverse inverts a ratio.
;;; VM:T


(defun %sp-ratio-inverse (x)
  (let ((numx (%primitive numerator x))
	(denx (%primitive denominator x)))
    (when (zerop numx)
	  (error "%sp-ratio-inverse : the inverse of zero is not defined."))
    (cond ((= 1 numx) denx)
	  ((= -1 numx) (- denx))
	  ((minusp denx)
	   (%primitive make-ratio (- denx) (- numx)))
	  (t (%primitive make-ratio denx numx)))))


;;;	
;;;		 5. Integer and mixed mode operations
;;;



;;; %sp-integer/ does a division of two integers.
;;; VM:T


(defun %sp-integer/ (x y)
  (when (zerop y)
	(error  "~A / ~A" x y))
  (multiple-value-bind (zi zr) (truncate x y)
    (if (zerop zr) zi
	(build-ratio x y))))

;;; %sp-ratio-integer* multiplies a ratio by an integer.
;;; vm:t

(defun %sp-ratio-integer* (x y)
  (let* ((den (%primitive denominator x))
	 (gcd (gcd den y)))
    (build-ratio (* (%primitive numerator x) (/ y gcd)) (/ den gcd))))


;;; %sp-ratio-integer/ divides a ratio by an integer.
;;; VM:T

(defun %sp-ratio-integer/ (x y)
  (when (zerop y)
	(error "Attempt to divide ~A by zero." x))
  (let* ((num (%primitive numerator x))
	 (gcd (gcd num y)))
    (%primitive make-ratio (/ num gcd) (* (%primitive denominator x) (/ y gcd)))))

;;; %sp-ratio-integer+ adds an integer to a ratio.
;;; VM:T

(defun %sp-ratio-integer+ (x y)
  (let ((denx (%primitive denominator x)))
    (build-ratio
     (+ (%primitive numerator x) (* denx y))
     denx)))

;;; %sp-ratio-integer- subtracts an integer from a ratio.

(defun %sp-ratio-integer- (x y)
  (let ((denx (%primitive denominator x)))
    (build-ratio
     (- (%primitive numerator x) (* denx y))
     denx)))




;;; gfb8 and gfb16 get a sign extended fixnum byte of 8 or 16 bits.
;;; The index, i, is an 8 bit index, and s is a byte of the sign of f.

(defun gfb8 (f i s)
  (case i
    ((0 1 2) (%sp-logldb 8 (* i 8) f))
    (3 (%sp-logdpb s 4 4 (%sp-logldb 4 24 f)))
    (t (%sp-logldb 8 0 s)))) 

;;; I is an 8 bit index.

(defun gfb16 (f i s)
  (case i
    (0 (%sp-logldb 16 0 f))
    (2 (%sp-logdpb s 4 12 (%sp-logldb 12 16 f)))
    (t (%sp-logldb 16 0 s))))



;;; %sp-bignum-fixnum* multiplies a bignum by a fixnum.
;;; VM:T

(defun %sp-bignum-fixnum* (x y)
  (let* ((xs (bignum-sign x))
	 (ys (fixnum-sign y))
	 (zs (eq xs ys))
	 (x (if (zerop xs) x (- x)))
	 (y (if (zerop ys) y (- y))))
    (cond ((or (not (bignump x)) (not (fixnump y)))
	   (if zs (* x y) (- (* x y))))
	  (t (let* ((lenx (bignum-length x))
		    (bit-length (integer-length y))
		    (byte-len (1+ (truncate bit-length 8)))
		    (c (cons-a-bignum (+ byte-len lenx))))
	       (do ((y←index 0 (1+ y←index))
		    (byte-in-y 0))
		   ((= y←index byte-len) (let ((c (integerize
						   c (+ byte-len lenx))))
					   (if zs c (- c))))
		 (setq byte-in-y (gfb8 y y←index 0))
		 (unless (zerop byte-in-y)
			 (do ((carry 0)
			      (x←index 0 (1+ x←index))
			      (byte-in-x 0)
			      (x+y←index y←index (1+ x+y←index)))
			 ((= x←index lenx)
			  (unless (zerop carry)
				  (add-in-carry c (+ lenx x←index)))) 
		       (setq byte-in-x (%sp-v-access x x←index))
		       (setq carry (add-in-bytes byte-in-x byte-in-y
						     c x+y←index
						     carry))))))))))



;;; %sp-bignum-fixnum+ adds a bignum to a fixnum, using twos
;;; complement addition.

(defun %sp-bignum-fixnum+ (b f)
  (let* ((bs (bignum-sign b))
	 (bsign-byte (- bs))
	 (bl (bignum-length b))
	 (1-bl (1- bl))
	 (fs (fixnum-sign f))
	 (fsign-byte (- fs))
	 (carry 0))
    (cond ((eq bs fs) ; if they have the same sign, 
	   (let* ((z (cons-a-bignum (1+ bl)))); make a larger xnum.
	     (do ((i 0 (+ i 2))
		  (sum 0))
		 ((>= i bl)
		  (cond ((= bs (%sp-logldb 1 15 sum))
			 (%sp-shrink-vector z bl))
			(t (unless (zerop bs)
				   (%sp-v-store z bl bsign-byte))
			   z)))
	       (setq sum (+ (gb16 b i 1-bl bsign-byte)
			    (gfb16 f i fsign-byte)
			    carry))
	       (setq carry (%sp-logldb 1 16 sum))
	       (%sp-typed-v-store 4 z (truncate i 2) sum))))
	  (t (let* ((z (cons-a-bignum bl))
		    (carry 0))
	       (do ((i 0 (+ i 2))
		    (sum 0))
		   ((>= i 1-bl)
		    (when (= i 1-bl)
			  (setq sum (+ (gb8 b i 1-bl bsign-byte)
				       (gfb8 f i fsign-byte)
				       carry))
			  (%sp-v-store z 1-bl sum))
		    (integerize z bl))
		 (setq sum (+ (gb16 b i 1-bl bsign-byte)
			      (gfb16 f i fsign-byte)
			      carry))
		 (setq carry (%sp-logldb 1 16 sum))
		 (%sp-typed-v-store 4 z (truncate i 2) sum)))))))



;;; %sp-fixnum-bignum- subtracts a bignum from a fixnum.

(defun %sp-fixnum-bignum- (f b)
  (let* ((fs (fixnum-sign f))
	 (fsign-byte (- fs))
	 (bs (bignum-sign b))
	 (bsign-byte (- bs))
	 (bl (bignum-length b))
	 (1-bl (1- bl)))
    (cond ((eq fs bs)
	   (let* ((z (cons-a-bignum bl)) 
		  (carry 0))
	     (do ((i 0 (+ i 2))
		  (sum 0))
		 ((>= i 1-bl)
		  (when (= i 1-bl)
		    (%sp-v-store z 1-bl
				 (- (%sp-logldb 8 0 fsign-byte)
				    (gb8 b i 1-bl bsign-byte)
				    carry)))
		  (integerize z bl))
	       (setq sum (- (gfb16 f i fsign-byte)
			    (gb16 b i 1-bl bsign-byte)
			    carry))
	       (setq carry (if (minusp sum) 1 0))
	       (%sp-typed-v-store 4 z (truncate i 2) sum))))
	  (t (let ((z (cons-a-bignum (1+ bl)))
		   (carry 0))
	       (do ((i 0 (+ i 2))
		    (sum 0))
		   ((>= i bl)
		    (unless (zerop fs)
		      (%sp-v-store z bl 255))
		    (integerize z (1+ bl)))
		 (setq sum (- (gfb16 f i fsign-byte)
			      (gb16 b i 1-bl bsign-byte)
			      carry))
		 (setq carry (if (minusp sum) 1 0))
		 (%sp-typed-v-store 4 z (truncate i 2) sum)))))))
  

;;;
;;; Spicelisp Gcd.
;;;	Speed is very a important factor in the calculation of
;;; Gcd, because the implementation of rational numbers uses Gcd
;;; very heavily.

;;; Gcd of two nonnegative integers, no type checking.
;;; Based on the Lehman multiple precision algorithm in Knuth vol. 2.

(defun gcd2 (u v)
  (cond ((zerop u) v)
	((zerop v) u)
	((= 1 u) (if (zerop v) 0 1))		;For inverses.
	((= 1 v) (if (zerop u) 0 1))
	(t (let* ((k (do ((k 0 (1+ k)))
			 ((or (oddp u) (oddp v)) k)
		       (setq u (ash u -1))
		       (setq v (ash v -1))))
		  (tee 0))
	     (cond ((oddp u)
		    (setq tee (- v)))	     ; falls into b4.
		   (t (setq tee (ash u -1)))) ; Setq tee to U/2 and then B4.
	     (do ()			     ; B4 is the test, B3 the body.
		 ((oddp tee))
	       (Setq tee (ash tee -1)))
	     (if (> tee 0)
		 (setq u tee)
		 (setq v (- tee)))
	     (setq tee (- u v))		     ;B6
	     (do ()
		 ((zerop tee) (ash u k))
	       (setq tee (ash tee -1))	;B3 again.
	       (do ()
		   ((oddp tee))
		 (setq tee (ash tee -1))) ;B3.
	       (if (> tee 0)
		   (setq u tee)
		   (setq v (- tee)))
	       (setq tee (- u v)))))))
      

(defun gcd (&rest args)
  "Returns the greatest common divisor of the arguments, which must be
integers.  Gcd with no arguments is defined to be 0."
  (do ((args args (cdr args))
       (gcd 0 (gcd2 gcd (abs (car args)))))
      ((null args) gcd)
    (if (not (integerp (car args)))
	(error "Gcd - argument not an integer: ~A"
		(car args)))))

;;; Lcm.
;;; Lcm2 is defined this way so that operations won't unnecessarily bignumify.

(defun lcm2 (n m)
  "Least common multiple of two nonzero integers.  No type checking."
  (* (/ (max n m) (gcd n m)) (min n m)))


(defun lcm (&rest args)
  "Returns the least common multiple of one or more integers."
  (do ((args args (cdr args))
       (lcm 1 (lcm2 lcm (car args))))
      ((null args) lcm)
    (cond ((not (integerp (car args)))
	   (error "Lcm: argument not an integer, ~A"
		   (car args)))
	  ((zerop (car args))				;Result is zero.
	   (dolist (arg (cdr args))
	     (if (not (integerp arg))
		 (error "Lcm: argument not an integer, ~A" arg)))
	   (return 0)))))



;;; %sp-Trunc-escape and its associated division and truncation
;;; routines.

;;; Fixnum-fixnum-divide is passed only positive fixnums, so
;;; it can call the microcode for its answers.
(eval-when (compile)

(defmacro fixnum-fixnum-divide (x y)
  `(truncate ,x ,y))


;;;	Converts the integer-length of the number to the byte length.

(defmacro bit-to-8-bit-byte (bits)
  `(multiple-value-bind (q r) (truncate ,bits 8)
     (if (zerop r) q (1+ q))))

)


;;;	Bignum Division
;;;
;;;	

;;;	Integer-divide takes two integers and returns thier quotient and
;;; remainder.

;;;	Divide-with-sign uses the function to divide u by v and checks
;;; usign and vsign to return the correctly signed q and r.

(eval-when (compile)
  (defmacro divide-with-sign (function)
    `(multiple-value-bind (q r) (,function u v)
       (if usign
	   (if vsign (values q (- r))
	        (values (- q) (- r)))
	   (if vsign (values (- q) r)
	       (values q r))))))

(defun integer-divide (u v)
  (typecase u
    (fixnum (typecase v
	      (fixnum  (truncate u v))
	      (bignum (values 0 u))		;WHM 10-18-83
	      (otherwise
	       (error "~a must be an integer, truncate." v))))
    (otherwise
     (let* ((usign (minusp u))
	    (u (abs u))
	    (vsign (minusp v))
	    (v (abs v)))
       (typecase u
	 (fixnum (typecase v
		   (fixnum (divide-with-sign truncate))
		   (bignum (if vsign  (values 0 (- v))
			       (values 0 v)))))
	 (bignum (typecase v
		   (fixnum (divide-with-sign bignum-fixnum-divide))
		   (bignum (divide-with-sign bignum-divide)))))))))




;;; Implement Knuth's indexing scheme.

(defmacro knuth-u (bignum index &optional (length 'ulen))
  `(let ((index (- ,length ,index 1)))
     (if (minusp index) 0 (aref ,bignum index))))
(defsetf knuth-u (bignum index &optional (length 'ulen)) (new)
  `(let ((index (- ,length ,index 1))
	 (new ,new))
     (if (minusp index) new
	 (setf (aref ,bignum index) new))))
    
(defmacro knuth-v (bignum index &optional (length 'vlen))
  `(aref ,bignum (- ,length ,index))))

;;; Part of the Knuth algorithm for division.  Does step D4, p.258 (vol 2).
;;; Returns the borrow (-1 if there was one).
;;; May not work if u and v are the same byte size.

(defun new-multiply-and-subtract (u j v qhat)
  (let ((vlen (bignum-length v))
	(ulen (bignum-length u)))
    (do ((i vlen (1- i))
	 (multiply-carry 0)  ; Nonnegative.
	 (subtract-carry 0)) ; Nonpositive.
	((zerop i)
	 (ash (incf (knuth-u u j ulen)
		    (- subtract-carry multiply-carry))
	      -8))
	(let* ((subtrahend (+ multiply-carry (* qhat (knuth-v v i))))
	       (difference (- (+ subtract-carry (knuth-u u (+ j i)))
			      (ldb (byte 8 0) subtrahend))))
	  (setq multiply-carry (ash subtrahend -8)
		subtract-carry (ash difference -8))
	  (setf (knuth-u u (+ j i) ulen) difference)))))


;;; Does step D6, except for decrementing qhat.
(defun new-add-back (u j v)
  (let ((n (bignum-length v))
	(ulen (bignum-length u))
	(carry 0))
    (do ((i n (1- i)))
	((< i j))
      (Setq carry (ash (incf (knuth-u u (+ j i) ulen)
			     (+ carry (knuth-v v i n)))
		       -8)))))


;;; Destructively modifies the new u and v.
;;; U and V are normalized, and d is what we shift the remainder by.

;(proclaim (inline do-bignum-division))
(defun do-bignum-division (u v d)
  ;; First, really normalize u and v so that indexing works.  This may make them
  ;; look negative.
  (let ((ulen (bignum-length u))
	(vlen (bignum-length v)))
;    (if (zerop (aref v (1- vlen)))
;	(%primitive shrink-vector v (decf vlen)))
;( (((let ((new-u (%primitive alloc-bignum (1+ ulen) 3)))
;		 (dotimes (i ulen) (setf (aref new-u i) (aref u i)))
;		 (setq u new-u ulen (1+ ulen)))))
;	  ((= vlen (1- ulen)))
;	  (t (%primitive shrink-vector u (decf ulen))))
    (if (= ulen vlen)
	(let ((new-u (%primitive alloc-bignum (1+ ulen) 3)))
	  (dotimes (i ulen) (setf (aref new-u i) (aref u i)))
	  (setq u new-u ulen (1+ ulen))))
    (let ((v1 (knuth-v v 1))
	  (v2 (knuth-v v 2))
	  (m (- ulen vlen)))
      (do ((j 0 (1+ j))
	   (q (%primitive alloc-bignum (max 4 (1+ m)) 3))) ; For hackish system.
	  ((> j m)
	   (values (integerize q (1+ m))
		   (ash (integerize u ulen) (- d))))
	(let* ((qhat (do ((qhat (if (= (knuth-u u j) v1) 255
				    (truncate (+ (ash (knuth-u u j) 8)
						 (knuth-u u (1+ j)))
					      v1))
				(1- qhat)))
			 ((<= (* qhat v2)
			      (+ (ash (+ (ash (knuth-u u j) 8)
					 (knuth-u u (1+ j))
					 (- (* qhat v1)))
				      8)
				 (knuth-u u (+ 2 j))))
			  qhat)))
	       (borrow (new-multiply-and-subtract u j v qhat)))
	  (cond ((not (zerop borrow))
		 (setf (knuth-u q j m) (1- qhat))
		 (new-add-back u j v))
		(t (setf (knuth-u q j m) qhat))))))))

(defun bignum-fixnum-divide (u v)
  (let ((us (bignum-sign u))
	(vs (if (plusp v) 0 1)))
    (if (not (zerop us)) (setq u (- u)))
    (if (not (zerop vs)) (setq v (- v)))
    (let ((d (- 31 (integer-length v))))
      (multiple-value-bind (div rem)
			   (do-bignum-division (ash u d) (ash v d) d)
	(values (if (zerop (logxor us vs)) div (- div))
		(if (zerop us) rem (- rem)))))))

(defun bignum-divide (u v)
  (if (< u v)
      (values 0 u)
      (let ((us (bignum-sign u))
	    (vs (bignum-sign v)))
	(if (not (zerop us)) (setq u (- u)))
	(if (not (zerop vs)) (setq v (- v)))
	(multiple-value-bind (bytes bits) (truncate (integer-length v) 8)
	  (let ((d (mod (- 8 bits) 8)))
	    (multiple-value-bind (div rem)
				 (do-bignum-division (ash u d) (ash v d) d)
	      (values (if (zerop (logxor us vs)) div (- div))
		      (if (zerop us) rem (- rem)))))))))

;;; Bignum-divide as above does not work.  So here's the Milnes code.


;;;	bignum-divide takes two positive bignums u,v of significant length
;;; ul and vl and returns thier quotient, q, of ul - vl bytes and the 
;;; remainder, r, of vl bytes.

;;;	multiply and subtract subtracts qhat*v from the middle of
;;; u and returns t iff the subtraction took u negative.

(defun multiply-and-subtract (u qhat v j ul vl)
  (let* ((qhat*v (* qhat v))
	 (qhat*vlength (bignum-length qhat*v)))
    (do ((i 0 (1+ i))
	 (carry ()))
	((= i qhat*vlength) 
	 (if (and carry (<= (+ (- ul j vl) i) ul)
		  (not (zerop (%sp-v-access u (+ (- ul j vl) i)))))
	     (setq carry (minusp
			  (%sp-v-store u (+ (- ul j vl) i)
				 (- (%sp-v-access u (+ (- ul j vl) i)) 1)))))
	 carry)
      (setq carry (minusp 
		   (%sp-v-store u (+ (- ul j vl) i)
				(if carry
				    (- (%sp-v-access u (+ (- ul j vl) i))
				       (%sp-v-access qhat*v i) 1)
				    (- (%sp-v-access u (+ (- ul j vl) i))
				       (%sp-v-access qhat*v i)))))))))

;;;	add-back adds v back into u if the quotient guess was too big.

(defun add-back (u v j ul vl qhat)
  (setq qhat (1- qhat))
  (do ((i 0 (1+ i))
       (carry ()))
      ((= i vl)
       (%sp-v-store u (- ul j) (1+ (%sp-v-access u (- ul j)))) u)
    (setq carry
	  (%sp-logldb 1 8
		      (%sp-v-store u (+ (- ul j vl) i)
				   (if carry
				       (+ (%sp-v-access u (+ (- ul j vl) i))
					  (%sp-v-access v i) 1)
				       (+ (%sp-v-access u (+ (- ul j vl) i))
					  (%sp-v-access v i))))))))


(defun bignum-divide (u v)
  (let* ((ulength (bignum-length u))
	 (vlength (bignum-length v))
	 (ul (if (zerop (%sp-v-access u (1- ulength))) (1- ulength) ulength))
	 (vl (if (zerop (%sp-v-access v (1- vlength))) (1- vlength) vlength))
	 (ql (1+ (- ul vl))))
    (if (not (plusp ql)) (values 0 u)
	(let* ((q (cons-a-bignum (max ql 4)))
	       (d (- 8 (integer-length (%sp-v-access v (1- vl)))))
;;	d is the number of bits needed to shift v such that v's
;; highest significant bit is in position 7 of its byte.
	       (u (let* ((u* (ash u d))
			 (u*length (bignum-length u*)))
		    (if (not (zerop (%sp-v-access u* (1- u*length))))
			(let ((u** (cons-a-bignum (1+ u*length))))
			  (do ((i 0 (1+ i)))
			      ((= i u*length) u**)
			    (%sp-v-store u** i (%sp-v-access u* i))))
			u*)))
	       (v (ash v d)))
	  (do* ((j 0 (1+ j))
		(qhat 0)
		(v1 (%sp-v-access v (1- vl)))
		(v2 (%sp-v-access v (- vl 2))))
	      ((= j ql) (values (integerize q ql)
				(ash (integerize u (1+ ul)) (- d))))
	    (let ((uj (%sp-v-access u (- ul j)))
		  (uj+1 (%sp-v-access u (- ul j 1)))
		  (uj+2 (%sp-v-access u (- ul j 2))))
;;;	multiply and subtract subtracts qhat*v from the middle of u
;;; and returns t if and only if qhat*v was larger than the byte chunk
;;; being subtracted from, so that add-back can add back in 1*v to
;;; to the middle of u.
	      (setq qhat 
		    (do ((qhat
			  (if (= uj v1) 255 (truncate (+ (ash uj 8) uj+1) v1))
			  (1- qhat)))
			((<= (* v2 qhat)
			     (+ (ash (- (+ (ash uj 8) uj+1) (* qhat v1)) 8)
				uj+2))
			 qhat)))
	      (unless (zerop qhat)
		(if (multiply-and-subtract u qhat v j ul vl)
		    (add-back u v j ul vl qhat)))
	      (%sp-v-store q (- ql j 1) qhat)))))))




;;; Ratio-trunc returns the integer quotient and the ratio remainder.
;;; 

(defun ratio-trunc (x)
  (multiple-value-bind (q r)
		      (truncate (numerator x) (denominator x))
   (values q
	   (if (zerop r) 0
	       (build-ratio r (denominator x))))))



;;;
;;;		VII. LOGICAL OPERATIONS
;;;


;;;
;;;	Integer-length returns the number of significant bits in the
;;; representation of the integer.

(defun %sp-integerlength-escape (x)
  (typecase x
    (bignum (let ((sign (bignum-sign x))
		  (length (bignum-length x)))
	      (cond ((= sign 0)
		     (do* ((byte (1- length) (1- byte))
			   (the-byte (%sp-v-access x byte)
				     (%sp-v-access x byte)))
			  ((or (not (zerop the-byte)) (zerop byte))
			   (%sp-escape-return
			    (+ (integer-length the-byte) (* 8 byte))))))
		    (t (do* ((byte (1- length) (1- byte))
			     (the-byte (%sp-v-access x byte)
				       (%sp-v-access x byte)))
			    ((or (not (= 255 the-byte)) (zerop byte))
			     (%sp-escape-return
			      (+ (integer-length (- 255 the-byte))
				 (* 8 byte)))))))))
    (otherwise (error "~A, argument not integer, integer-length" x))))




;;; Put-in-big takes an 8 bit byte and puts it into a bignum
;;; at rightmost bit position rb.
;;; Used by fix and big lsh.

(defun put-in-big (x rb big)
  (multiple-value-bind (byte0 hmin1) (truncate rb 8)
;	
    (cond ((and (not (zerop hmin1))
		(= byte0 (1- (bignum-length big))))
	 (let ((b0 (%sp-v-access big byte0)))
	   
	 (%sp-v-store big byte0
				(%sp-logdpb x (- 8 hmin1) hmin1 b0))
	 big))
	 ((zerop hmin1)
	 (%sp-v-store big byte0 x)
	 big)
	 (t (let ((b0 (%sp-v-access big byte0))
		  (b1 (%sp-v-access big (1+ byte0))))
	      
	      (%sp-v-store big byte0
		      (%sp-logdpb x (- 8 hmin1) hmin1 b0))
	 (%sp-v-store big (1+ byte0)
				 (%sp-logdpb (%sp-logldb hmin1 (- 8
hmin1) x)
					 hmin1 0 b1))
	 big)))))

;;; Get-from-big gets a byte of 8 bits starting at rb from X.  
;;; Sign extendes if rb + 8 is greater than the highest bit.  
;;; Length is the number of bytes in x.
;;; Used only by BigAsh.  
;;; VM:T

(defun get-from-big (rb x length)
  (multiple-value-bind (byte0 hmin1) (truncate rb 8)
    (cond ((< byte0 length)  ;; Byte0 is in vector not off the end.
	   (cond ((zerop hmin1) (%sp-v-access x byte0))
		 ((< (1+ byte0) length) ;; Byte1 is in vector.
		  (%sp-logdpb
		   (%sp-logldb hmin1 0 (%sp-v-access x (1+ byte0)))
		   hmin1 (- 8 hmin1)
		   (%sp-lsh (%sp-v-access x byte0) (- hmin1))))
		 (t (%sp-logdpb
		     (%sp-logldb hmin1 0 (- (bignum-sign x)))
		     hmin1 (- 8 hmin1)
		     (%sp-lsh (%sp-v-access x byte0) (- hmin1))))))
	  (t (%sp-logldb 8 0 (- (bignum-sign x)))))))


;;; 	FixAsh arithmetically shifts a fixnum, N, left by B bits.  
;;; It uses lsh when possible, otherwise it uses put-in-big.  
;;; vm:T

(defun fixash (n b)
  (if (not (fixnump b))
      (error "Ash, shift argument is not a fixnum, ~A" b))
  (let ((tch (integer-length n)))
    (cond ((<= (+ tch b) 0) (- (%sp-logldb 1 (1- %fixnum-length) n)))
	  ((minusp b) (%sp-logdpb (%sp-lsh n b) (+ %fixnum-length b) 0
				 (- (%sp-logldb 1 (1- %fixnum-length) n))))
	  ((<= (+ tch b) (1- %fixnum-length)) (%sp-lsh n b))
	  (t (let* ((byte-length (1+ (truncate (+ tch b) 8)))
		    (s (%sp-logldb 1 (1- %fixnum-length) n))
		    (sign-byte (- s))
		    (ans (cons-a-bignum byte-length)))
	       (do ((i 0 (+ i 8)))
		   ((> i tch))
		 (put-in-big (gfb8 n (truncate i 8) sign-byte) (+ i b)
			     ans))
	       (integerize ans byte-length))))))


;;; Shifts a bignum into a bignum.

(defun big-to-bigash (n b nl)
  (let* ((byte-length (1+ (truncate (+ nl b) 8)))
	 (ans (cons-a-bignum byte-length))
	 (bl (bignum-length n)))
    (do ((i (1- byte-length) (1- i)))
	((minusp i) (integerize ans byte-length))
      (put-in-big (get-from-big (+ (- b) (* 8 i)) n bl)
		  (* i 8) ans))))

;;; Shifts a bignum into a fixnum.
;;;

(defun big-to-fixash (n b nl)
  
  (let* ((size (bignum-length n)) 
	 (fix (%sp-logdpb (get-from-big (- nl 7) n size) 8 20
			  (%sp-logdpb
			   (get-from-big (- nl 15) n size) 8 12
			   (%sp-logdpb 
			    (get-from-big (- nl 23) n size) 8 4
			    (%sp-logdpb (get-from-big (- nl 27) n size) 4 0
					(- (bignum-sign n))))))))
    
    (fixash fix (+ nl b -27))))


;;; BigAsh arithmetically shifts the bignum N left by B bits.
;;; VM:T

(defun nzero-bytes (n c)
  "Detructively nullifies the bottom c bytes of the xnum n."
  (do ((i 0 (1+ i))
       (end (min c (bignum-length n))))
      ((= i end) n)
    (%sp-v-store n i 0)))

(defun byte-ash (n c)
  "Shifts the bignum n by c bytes, by playing with the internal vector."
  
  (let* ((length (bignum-length n))
	 (newlen (max 0 (+ length c))))
    (if (zerop newlen) (- (bignum-sign n))
	(let* ((start (max 0 (- c)))
	       (dststart (max 0 c))
	       (res (nzero-bytes (create-bignum newlen (bignum-sign n))
				 dststart)))	;At least 4, and 0 shifted in
	  (%sp-byte-blt n start res dststart newlen)
	  (integerize1 res)))))

;;; Similar to gb16.  c must be valid.
(defun Load-two-bytes (n c 1-length sign-byte)
  
  "Loads two bytes at position c in bignum n, extending with byte."
  (%sp-logdpb (if (= c 1-length) sign-byte (%sp-v-access n (1+ c)))
	      8 8 (%sp-v-access n c)))
  

;;; Bigash first byte-ashes for coarse adjustments, then ashes each byte the
;;; remaining amount.  This is always downward, so we can get 16 bits
;;; and still have the 8 we want to store.
(defun bigash (n c)
  (multiple-value-bind (bytes bits) (ceiling c 8)
    (let ((res (byte-ash n bytes)))
      (cond ((fixnump res)
	     (ash res bits))
	    (t (let ((length (bignum-length res))
		     (sign-byte (- (bignum-sign res))))
		 (do ((i 0 (1+ i))
		      (1-len (1- length)))
		     ((= i length) (integerize1 res))
		   (%sp-v-store res i
				(ash (load-two-bytes res i 1-len sign-byte)
				     bits)))))))))





;;; %sp-ash-escape is an arithmetic shift function for any integer.
;;; N is the integer, and B is the number of bits to shift
;;; left.
;;; VM:T

(defun %sp-ash-escape (n b)
  (unless (fixnump b)
    (error "Ash, cannot shift by a non fixnum value, ~A." b))
  (%sp-escape-return
   (cond ((zerop b) n)
	 (t (typecase n
	      (fixnum (%sp-escape-return (fixash n b)))
	      (bignum (%sp-escape-return (bigash n b)))
	      (t (error "Ash, object to shift is not an integer, ~A." n)))))))


;;; Count-bits counts the number of bits equal to s from
;;; bit p to 0.
;
(defun count-bits (s p x)
  
  (do ((sum (if (= s (%sp-logldb 1 p x)) 1 0)
	    (+ sum (if (= s (%sp-logldb 1 i x)) 1 0)))
       (i (1- p) (1- i)))
      ((minusp i) sum)
    ))

;;; Logcount returns the number of bits that are the complement of 
;;; the sign in the integer argument x.

(defun logcount (x)
  (typecase x
	    (fixnum (do* ((s (complement (fixnum-sign x)))
			  (i 0 (1+ i))
			  (sum (if (= s (%sp-logldb 1 i x)) 1 0)
			       (if (= s (%sp-logldb 1 i x)) (1+ sum) sum)))
			((= i (1- %fixnum-length)) sum)
			))
	    (bignum (let* ((sum 0)
			   (l (bignum-length x))
			   (l-1 (1- l))
			   (s (complement (bignum-sign x))))
		      
		      (do ((b 0 (+ b 2)))
			  ((>= b l-1)
			   (cond ((= b l-1)
				  (+ sum
				     (count-bits s 7
						 (%sp-v-access x l-1)
						 )))
				 (t sum)))
			
			(setq sum
			      (+ sum
				 (count-bits s 16 (%sp-typed-v-access
						   4 x (truncate b 2))))))))
	    (otherwise (error "Argument not integer, ~A." x))))


;;; get-from-big-no-extend does a get-from-big with no sign
;;; extend.
;;; Gets a byte of 8 bits, starting at rb, from x.
;;; Does not sign extend, fills with zeros.

(defun get-from-big-no-extend (rb x length)
  (let* ((byte0 (truncate rb 8)) ; The right hand byte's position.
	 (hmin1 (- rb (* byte0 8)))); How many bit's in byte1.
    (cond ((zerop hmin1)
	   (%sp-v-access x byte0))
	  ((> (+ rb 8) (* length 8))
	   (let ((b0 (%sp-lsh (%sp-v-access x byte0)
			      (- hmin1))))
	     (%sp-logdpb 0 hmin1 (- 8 hmin1) b0)))
	  (t (%sp-logdpb (%sp-v-access x (1+ byte0))
			 hmin1 (- 8 hmin1)
			 (%sp-lsh (%sp-v-access x byte0)
				  (- hmin1)))))))



;;; Ldb-a-fixnum, loads a fixnum chunck of bits from either a fixnum or a 
;;; bignum.


(defun sign-extend-tva (v vl i sign-byte)
  (cond ((>= i vl) sign-byte)
	 (t (%sp-v-access v i))))

;
(defun ldb-a-fixnum (s p n)
  
  (typecase n
	    (fixnum
	     ;; A fixnum sized load can be broken down into two fields:
	     ;; The significant field and the sign field.
	     (let* ((significant-size (if (> p (1- %fixnum-length))
					  0 (min s (- %fixnum-length p))))
		    (sign-field-size (- s significant-size)))
	       
	       (cond ((zerop significant-size) ;; No significant field.
		      (case (fixnum-sign n)
			(0 0)
			(1 (%sp-logldb sign-field-size 0 -1))))
		     ((zerop sign-field-size) ;; No sign field.
		      (%sp-logldb significant-size p n))
		     (t (case (fixnum-sign n) ;; Both fields.
			  (0 (%sp-logldb significant-size p n))
			  (1 (%sp-logdpb
			      -1 sign-field-size significant-size
			      (%sp-logldb significant-size p n))))))))
	    (bignum (let* ((nl (bignum-length n))
			   (sign-byte (- (bignum-sign n)))
;;; The loading of a fixnum from a bignum can be divided into 3 distinct
;;; parts, the lowest byte, the middle 8 bit bytes and the highest byte.
;;; The lowest byte can be up to 8 bits long; starts at Lbp and is lbs
;;; bits long. There are from 0 to 3 middle 8 bit bytes, and the number
;;; of the highest byte is -1 if it does not exist.
			   (Lb (truncate p 8))
			   (Lbp (- p (* lb 8)))
			   (Lbs (min s (- 8 lbp)))
			   (num-middle-bytes (truncate (- s lbs) 8))
			   (Hb (if (zerop (- s lbs (* 8 num-middle-bytes)))
				   -1 (+ 1 Lb num-middle-bytes)))
			   (fix 0))
		      
;;; Now I deposit the fields into their places in fix.
;;; Lb field
		      (setq fix (%sp-logdpb
				 (%sp-logldb
				  Lbs Lbp
				  (sign-extend-tva n nl Lb sign-byte))
				 Lbs 0 fix))
;;; Middle byte fields.
		      (do ((i 1 (1+ i)))
			  ((> i num-middle-bytes) t)
			
			(setq fix 
			      (%sp-logdpb
				  (sign-extend-tva n nl (+ Lb i) sign-byte)
				  8 (+ (* 8 (1- i)) Lbs) fix)))
;;; High Byte.
			(unless (= -1 hb)
				(let* ((bits-in (+ Lbs (* 8 num-middle-bytes)))
				       (hbs (- s bits-in)))
				  
				  (setq fix
					(%sp-logdpb
					 (sign-extend-tva
					  n nl hb sign-byte)
					 hbs bits-in fix))))
			fix))))

;;; get-from-fix gets a byte of size 8, whose rightmost bit is rb,
;;; from n, where n's signbyte is s.

;
(defun get-from-fix (rb n sign)
  
  (cond ((> rb (1- %fixnum-length)) (%sp-logldb 8 0 sign))
	((< (+ rb 8) %fixnum-length) (%sp-logldb 8 rb n))
	(t (let* ((rbs (max 0 (min (- %fixnum-length rb) 8)))
		  (rb (if (plusp rbs) (%sp-logldb rbs rb n) 0))
		  (lbs (- 8 rbs))
		  (lb (%sp-logldb lbs 0 sign)))
	     
	     (cond ((plusp lbs)
		    (%sp-logdpb lb lbs rbs rb))
		   (t rb))))))

;;; ldb-a-bignum loads a bignum sized byte from a fixnum or a bignum.

; 
(defun ldb-a-bignum (s p n)
  
  (typecase n
	;; The load of a bignum, from a fixnum or a bignum, can
	;; be seperated into the loading, and depositing, of
	;; the right hand 8 bit bytes which compose the load,
	;; and the final byte, of which less than 8 bits are used.
	    (fixnum (let* ((l (bit-to-8-bit-byte s))
			   (ansl (if (and (zerop (rem s 8))
					  (not (zerop (logbitp
						       (1- (+ s p)) n))))
				     (1+ l) l))
			   (ans (cons-a-bignum ansl))
			   (sign (- (fixnum-sign n))))
		      
		      (multiple-value-bind (num-whole-bytes
					    size-of-partial-byte)
					   (truncate s 8)
;			
			(do ((i 0 (1+ i))
			     (pcount p (+ pcount 8)))
			    ((= i num-whole-bytes))
			  
			  (%sp-v-store ans i (get-from-fix pcount n sign)))
			(unless (zerop size-of-partial-byte)
				(%sp-v-store ans num-whole-bytes
				 (%sp-logldb size-of-partial-byte 0
					     (get-from-fix
					      (+ p (* num-whole-bytes 8))
					      n sign))))
			(integerize ans ansl))))
	    (bignum (let* ((l (bignum-length n))
			  (byte-length 
			   (multiple-value-bind (b r) (truncate s 8)
			     (cond ((zerop r)
				    (if (logbitp (1- (+ p s)) n) (1+ b) b))
				   (t (1+ b)))))
			  (bytes (truncate s 8))
			  (ans (cons-a-bignum byte-length)))
		      
		      (do ((bc p (+ bc 8))
			   (bytec 0 (1+ bytec)))
			  ((>= bytec bytes)
			   (let ((rem (- s (* bytec 8))))
			     (when (plusp rem)
				   (%sp-v-store ans bytec 
				    (%sp-logldb rem 0 (get-from-big bc n l)))))
			   (integerize ans byte-length))
			
			(%sp-v-store ans bytec
					   (get-from-big bc n l)))))))

;;; %sp-ldb-escape (S P N). S is the size of the byte field,
;;; P is the position it starts at and N is the number to get 
;;; the byte field from.

(defun %sp-ldb-escape (S P N)
  (unless (and (fixnump s) (>= s 0))
    (error "Size argument must be a positive fixnum, ~A." s))
  (unless (and (fixnump p) (>= p 0))
    (error "Position argument must be a positive fixnum, ~A." p))
  (unless (integerp n)
    (error "Ldb: source must be an integer, ~A." n))
  (%sp-escape-return
   (cond ((zerop s) 0)
	 ((or (< s %fixnum-length)
	      (and (= s %fixnum-length)
		   (not (logbitp (1- (+ p s)) n))))
	  (ldb-a-fixnum s p n))
	 (t (ldb-a-bignum s p n)))))





;;; Fixnum-mask-field does a mask-field when n is a fixnum.

(defun fixnum-mask-field (s p n)
  
  (when (and (>= n 0)
	     (< (1- %fixnum-length) (+ s p)))
	(setq s (max 0 (- (1- %fixnum-length) p))))
  (cond ((zerop s) 0) ;; Getting sign bits from positive fixnum.
	((< (+ s p) %fixnum-length) (%sp-lsh (%sp-logldb s p n) p))
;; The sign is being masked out, so the result is fixnum.
;; For the next clause to be chosen, n must be negative,
;; and the result will be a bignum.
	(t (let* ((bit-length (+ 1 p s)) ;; 1+ for the sign.
		  (byte-length
		   (multiple-value-bind (q r) (truncate bit-length 8)
		     (if (zerop r) q (1+ q)))))
	     
	     (multiple-value-bind (Low LowPos) (truncate p 8)
	       (let* ((LowSize (min s (- 8 LowPos)))
		      (num-middle-bytes (truncate (- s lowsize) 8))
		      (HighSize (- s lowsize (* 8 Num-middle-bytes)))
		      (ans (cons-a-bignum byte-length)))
		 
		 (%sp-v-store ans low
				   (%sp-lsh (ldb (byte LowSize p) n) lowpos))
		 (do ((anscount (1+ Low) (1+ anscount))
		      (num-middle-bytes+low (+ num-middle-bytes low)))
		     ((> anscount num-middle-bytes+low)
		      (unless (zerop HighSize)
			      (%sp-v-store ans anscount
			       (%sp-logldb HighSize 0
					   (gfb8 n anscount #b11111111))))
		      ans)
		   
		   (%sp-v-store ans anscount
				      (gfb8 n anscount #b11111111)))))))))

;;; Bignum-mask-field does a mask-field when n is a bignum.

(defun bignum-mask-field (s p n)
  
  (let* ((nl (bignum-length n))
	 (nl-1 (1- nl))
	 (sign-start (- (* 8 nl) (- 8 (integer-length nl)))))
    
    (when (and (plusp n)		;; Reduce size if possible to
	       (< sign-start (+ s p)))  ;; avoid consing up uneeded bignums.
	  (setq s (max 0 (- sign-start p))))
    (cond ((zerop s) 0) ;; Positive bignum, with only sign not masked.
	  (t (let* ((bit-length (+ s p)))
	       
	       (cond ((or (< bit-length %fixnum-length)
			  (and (= bit-length %fixnum-length)
			       (not (logbitp (1- bit-length) n))))
		      (%sp-lsh (ldb (byte s p) n) p))
		     (t (let ((byte-length
			       (multiple-value-bind (q r) (truncate bit-length 8)
				 (if (or (and (zerop r)
					      (logbitp (1- bit-length) n))
					 (not (zerop r))) (1+ q) q))))
			  
			  (multiple-value-bind (low lowpos) (truncate p 8)
			    (let ((LowSize (min s (- 8 LowPos))))
			      (multiple-value-bind (num-middle-bytes Highsize)
						   (truncate (- s lowsize) 8)
				(let ((sign-byte
				       (%sp-logldb
					8 0 (- (bignum-sign n))))
				      (ans (cons-a-bignum byte-length)))
				  (%sp-v-store ans low
				      (%sp-lsh (ldb (byte LowSize p) n)
					       lowpos))
				  (do ((bc (1+ low) (+ bc 1))
				       (bytecount 0 (1+ bytecount)))
				      ((= bytecount num-middle-bytes)
				       (unless (zerop HighSize)
					       (%sp-v-store
						ans bc
						(%sp-logldb
						 HighSize 0
						 (gb8 n bc nl-1 sign-byte))))
				       ans)
				    (%sp-v-store
				     ans bc (gb8 n bc nl-1
						   sign-byte)))))))))))))))


;;; 	Mask-field always returns a positive integer. Bignum arguments
;;; can yield bignum or fixnum arguments but only negative fixnum
;;; arguments can return a bignum.
;;; 

(defun %sp-maskfield-escape (s p n)
  (unless (and (fixnump s) (not (minusp s)))
	  (error "Size argument must be a non negative fixnum, ~A." s))
  (unless (and (fixnump p) (not (minusp p)))
	(error "Position argument must be a non negative fixnum, ~A." p))
  (%sp-escape-return
   (typecase n
     (fixnum (fixnum-mask-field s p n))
     (bignum (bignum-mask-field s p n))
     (t (error "Argument must be an integer, ~A." n)))))



;;;
;;; Fixnum-dpb is cased by result type : bignum or fixnum,
;;; but called only when n is a fixnum. 

(defun fixnum-dpb (v s p n)
  (let ((size (+ s p)))
    (cond ((<= size 27)
	   (dpb (ldb (byte s 0) v) (byte s p) n))

	  (t (multiple-value-bind (high-byte high-bits-in) (floor size 8)
	       (multiple-value-bind (low-byte low-bits-out) (floor p 8)
		 (let* ((bytes (ceiling (1+ size) 8))
			(res (sizify n bytes))
			(workspace-high (- high-byte low-byte))
			(workspace (nsizify (ash v low-bits-out)
					    (1+ workspace-high))))
		   (%sp-v-store workspace 0
				(%sp-logdpb (%sp-v-access res low-byte)
					    low-bits-out 0
					    (%sp-v-access workspace 0)))
		   (%sp-v-store workspace workspace-high
				(%sp-logdpb (%sp-v-access workspace
							  workspace-high)
					    high-bits-in 0
					    (%sp-v-access res high-byte)))
		   (bash-bytes workspace
			       (1+ workspace-high)
			       low-byte res)
		   (integerize1 res))))))))


;;; Bignum-dpb handles dpb when n is a bignum.


(defun bignum-dpb (v s p n)
  
  (let* ((nlength (bignum-length n))
	 (nlength-1 (1- nlength))
	 (nsign (bignum-sign n))
	 (nsign-byte (- nsign))
	 (bit-size (cond ((>= (+ s p) (* 8 nlength))
			  (if (and (logbitp (1- s) v) (= 1 nsign))
			      (+ s p)
			      (+ 1 s p)))
			 (t (* 8 nlength))))
	 (byte-size
	  (multiple-value-bind (q r) (truncate bit-size 8)
	    (if (zerop r) q (1+ q))))
	 (answer (cons-a-bignum byte-size)))
    (multiple-value-bind (low lowpos) (truncate p 8)
      (multiple-value-bind (high highpos) (truncate (+ p s -1) 8)
	(let* ((lowsize (min s (- 8 lowpos)))
	       (highsize (1+ highpos))) ;; This assumes that low /= high.
	  (cond ((= low high)
		 (do ((i 0 (1+ i)))
		     ((= i low))
		   (%sp-v-store answer i
				      (gb8 n i nlength-1 nsign-byte)))
		 (%sp-v-store
		  answer low
		  (%sp-logdpb (if (bignump v) (ldb (byte s 0) v) v)
			      lowsize lowpos
			      (gb8 n low nlength-1 nsign-byte)))
		 (do ((i (1+ low) (1+ i)))
		     ((>= i byte-size))
		   (%sp-v-store answer i
				      (gb8 n i nlength-1 nsign-byte)))
		 (integerize answer byte-size))
		(t (do ((i 0 (1+ i)))
		       ((= i low))
		   (%sp-v-store answer i
				      (gb8 n i nlength-1 nsign-byte)))
		   (%sp-v-store
		     answer low
		    (%sp-logdpb (if (bignump v)
				    (ldb (byte lowsize 0) v) v)
				lowsize lowpos
				(gb8 n low nlength-1 nsign-byte)))
		   (do ((i (1+ low) (1+ i))
			(pc lowsize (+ pc 8)))
		       ((>= i high))
		     (%sp-v-store answer i
					(ldb (byte 8 pc) v)))
		   (%sp-v-store answer high
		    (%sp-logdpb
		     (ldb (byte highsize (- s highsize)) v)
		     highsize 0 (gb8 n high nlength-1 nsign-byte)))
		   (do ((i (1+ high) (1+ i)))
		       ((>= i byte-size))
		     (%sp-v-store answer i
					(gb8 n i nlength-1 nsign-byte)))
		   (integerize answer byte-size))))))))



;;; %sp-dpb-escape is the bugout for dpb, but it can handle all cases.
;;; VM:T

(defun %sp-dpb-escape (v s p n)
  (unless (integerp v)
	  (error "Value argument must be integer, ~A."  v))
  (unless (integerp n)
	  (error "Argument must be integer, ~A." n))
  (unless (and (integerp s) (not (minusp s)))
	  (error "Size argument must be non-negative, ~A." S))
  (unless (and (integerp s) (not (minusp s)))
	  (error "Position argument must be non-negative, ~A." s))
  (if (zerop s)  (%sp-escape-return n)
      (typecase n
		(fixnum (%sp-escape-return (fixnum-dpb v s p n)))
		(bignum  (%sp-escape-return (bignum-dpb v s p n))))))

 


;;;
;;; Fixnum-deposit-field handles the case where n is a fixnum.

(defun fixnum-deposit-field (v s p n)
  
  (let* ((nsign (not (zerop (%sp-logldb 1 (1- %fixnum-length) n))))
	 (bit-size (cond ((> (+ s p) (1- %fixnum-length))
			  (if (eq (logbitp (1- s) v) nsign)
			      (+ s p)
			      (+ 1 s p)))
			 (t %fixnum-length))))
    
    (cond ((<= bit-size %fixnum-length)
	   (%sp-logdpb (ldb (byte s p) v) s p n))
	  (t (let* ((nsign-byte (if nsign -1 0))
		    (byte-size
		     (multiple-value-bind (q r) (truncate bit-size 8)
		       (if (zerop r) q (1+ q))))
		    (answer (cons-a-bignum byte-size)))
	       
	       (multiple-value-bind (low lowpos) (truncate p 8)
;		 
		 (multiple-value-bind (high highpos) (truncate (+ p s -1) 8)
;		   
		   (let* ((lowsize (min s (- 8 lowpos)))
			  (highsize (1+ highpos)))
		     
;; If low is the same byte as high, I use 2 do loops. From 0 to low,
;; then insert the broken byte, and then a do from low+1 to byte-size.
		     (cond ((= low high)
			    (do ((i 0 (1+ i)))
				((= i low))
			      
			      (%sp-v-store answer i
						 (gfb8 n i nsign-byte)))
;; Put in broken byte.
			    (%sp-v-store answer low
			     (%sp-logdpb (ldb (byte s p) v)
					 Lowsize Lowpos
					 (gfb8 n low nsign-byte)))
			    (do ((i (1+ low) (1+ i)))
				((>= i byte-size))
			      
			      (%sp-v-store answer i
						 (gfb8 n i nsign-byte)))
			    (integerize answer byte-size))
;; If low is not = to high, then I use 3 do loops, one from 0 to low,
;; then I put in the low byte, one from low + 1 to high -1 , then
;; I put in the high byte and then one from high + 1 to byte size.
			   (t (do ((i 0 (1+ i)))
				  ((= i low))
				
				(%sp-v-store answer i
						   (gfb8 n i nsign-byte)))
;; Put in low byte.
			      (%sp-v-store answer low
			       (%sp-logdpb 
				(ldb (byte lowsize p) v)
				lowsize lowpos
				(gfb8 n low nsign-byte)))
			      (do ((i (1+ low) (1+ i))
				   (pc (* 8 (1+ low)) (+ 8 pc)))
				  ((>= i high)
;; Put in high byte.
				   (%sp-v-store answer high
				    (%sp-logdpb 
				     (ldb (byte highsize pc) v)
				     highsize 0
				     (gfb8 n i nsign-byte))))
				
				(%sp-v-store answer i
				 (ldb (byte 8 pc) v)))
			      (do ((i (1+ high) (1+ i)))
				  ((>= i byte-size))
				
				(%sp-v-store
				 answer i
				 (gfb8 n i nsign-byte)))
			      (integerize answer byte-size)))))))))))


;;; Bignum-deposit-field handles dpb when n is a bignum.

(defun bignum-deposit-field (v s p n)
  (let* ((nlength (bignum-length n))
	 (nlength-1 (1- nlength))
	 (nsign (bignum-sign n))
	 (nsign-byte (- nsign))
	 (bit-size (cond ((>= (+ s p) (* 8 nlength))
			  (if (and (logbitp (1- s) v) (= 1 nsign))
			      (+ s p)
			      (+ 1 s p)))
			 (t (* 8 nlength))))
	 (byte-size
	  (multiple-value-bind (q r) (truncate bit-size 8)
	    (if (zerop r) q (1+ q))))
	 (answer (cons-a-bignum byte-size)))
    (multiple-value-bind (low lowpos) (truncate p 8)
      (multiple-value-bind (high highpos) (truncate (+ p s -1) 8)
	(let* ((lowsize (min s (- 8 lowpos)))
	       (highsize (1+ highpos))) ;; This assumes that low /= high.
	  (cond ((= low high)
		 (do ((i 0 (1+ i)))
		     ((= i low))
		   (%sp-v-store answer i
				      (gb8 n i nlength-1 nsign-byte)))
		 (%sp-v-store
		  answer low
		  (%sp-logdpb  (ldb (byte s p) v)
			       lowsize lowpos
			       (gb8 n low nlength-1 nsign-byte)))
		 (do ((i (1+ low) (1+ i)))
		     ((>= i byte-size))
		   (%sp-v-store answer i
				      (gb8 n i nlength-1 nsign-byte)))
		 (integerize answer byte-size))
		(t (do ((i 0 (1+ i)))
		       ((= i low))
		     (%sp-v-store answer i
					(gb8 n i nlength-1 nsign-byte)))
		   (%sp-v-store
		     answer low
		    (%sp-logdpb (ldb (byte lowsize p) v)
				lowsize lowpos
				(gb8 n low nlength-1 nsign-byte)))
		   (do ((i (1+ low) (1+ i))
			(pc lowsize (+ pc 8)))
		       ((>= i high)
			(%sp-v-store
			  answer high
			 (%sp-logdpb
			  (ldb (byte highsize pc) v)
			  highsize 0 (gb8 n high nlength-1 nsign-byte))))
		     (%sp-v-store answer i
					(ldb (byte 8 pc) v)))
		   (do ((i (1+ high) (1+ i)))
		       ((>= i byte-size))
		     (%sp-v-store answer i
					(gb8 n i nlength-1 nsign-byte)))
		   (integerize answer byte-size))))))))

;;; %sp-depositfield-escape is the bugout for deposit-field.
;;; VM:T

(defun %sp-depositfield-escape (v s p n)
  (unless (integerp v)
	  (error "Value argument must be integer, ~A."
v))
  (unless (integerp n)
	  (error "Argument must be integer, ~A." n))
  (unless (and (integerp s) (not (minusp s)))
	  (error "Size argument must be non-negative, ~A." S))
  (unless (and (integerp s) (not (minusp s)))
	  (error "Position argument must be non-negative, ~A." s))
  (if (zerop s) (%sp-escape-return n)
      (typecase n
		(fixnum  (%sp-escape-return (fixnum-deposit-field v s p n)))
		(bignum  (%sp-escape-return (bignum-deposit-field v s p n))))))



;;;
;;;
;;;			VIII. Exported routines
;;;

;;; Part 12.5 of the laser edition



(defun float-sign (float1 &optional (float2 (float 1 float1)))
  (if (eq (minusp float1) (minusp float2))
      float2
      (- float2)))





(defun float-precision (f)
  (typecase f
    (short-float 20)
    (long-float 53)
    (t (error "Float-precision: ~A not a float" f))))

(defun integer-decode-float (x)
  (let ((precision (float-precision x)))
    (multiple-value-bind (f e s) (decode-float x)
      (values (truncate (scale-float f precision))
	      (- e precision)
	      s))))

(defun decode-float (f)
  (%sp-decode-float f))
(defun scale-float (f e) (%sp-scale-float f e))


;;; The two macros, bignum-bignum-bitfun and bignum-fixnum-bitfun
;;; are used to implement all of the one logical function bit funs.
;;; The first argument, fun is the micro-instruction name used 
;;; (e.g. one of %sp-bit-and, %sp-bit-or or %sp-bit-xor),
;;; the second and third arguments are the numbers.

(defmacro bignum-bignum-bitfun (fun b1 b2)
  `(let* ((b1l (%sp-get-vector-length ,b1))
	  (b2l (%sp-get-vector-length ,b2))
	  (b1s (%sp-get-vector-subtype ,b1))
	  (b2s (%sp-get-vector-subtype ,b2))
	  (max-length (max b1l b2l))
	  (r (%sp-alloc-xnum max-length (%sp-logldb 1 0 (,fun b1s b2s))))
	  (b1sign-byte (- b1s))
	  (b2sign-byte (- b2s)))
     (do ((i 0 (+ i 2))
	  (1-b1l (1- b1l))
	  (1-b2l (1- b2l))
	  (1-max-length (1- max-length)))
	 ((>= i 1-max-length)
	  (when (= i 1-max-length)
		(%sp-typed-v-store
		 3 r 1-max-length
		 (,fun (gb8 ,b1 i 1-b1l b1sign-byte)
		       (gb8 ,b2 i 1-b2l b2sign-byte))))
	  (integerize r max-length))
       (%sp-typed-v-store 4 r (truncate i 2)
			  (,fun (gb16 ,b1 i 1-b1l b1sign-byte)
				(gb16 ,b2 i 1-b2l b2sign-byte))))))

(defmacro bignum-fixnum-bitfun (fun b f)
 `(let* ((bl (bignum-length ,b))
	 (bs (bignum-sign ,b))
	 (fs (%sp-logldb 1 27 ,f))
	 (r (cons-a-bignum bl))
	 (bsign-byte (- bs))
	 (fsign-byte (- fs)))
    (do ((i 0 (+ i 2))
	 (1-bl (1- bl)))
	((>= i 1-bl)
	 (when (= i 1-bl)
	       (%sp-typed-v-store 3 r 1-bl
				  (,fun (gb8 ,b i 1-bl bsign-byte)
					(gfb8 ,f i fsign-byte))))
	 (integerize r bl))
      (%sp-typed-v-store 4 r (truncate i 2)
			 (,fun (gb16 ,b i 1-bl bsign-byte)
			       (gfb16 ,f i fsign-byte))))))


(defun %sp-bitand-escape (a b)
  (cond ((and (bignump a) (bignump b))
	 (%sp-escape-return (bignum-bignum-bitfun logand a b)))
	((fixnump a)
	 (%sp-escape-return (bignum-fixnum-bitfun logand b a)))
	(t (%sp-escape-return (bignum-fixnum-bitfun logand a b)))))


(defun %sp-bitxor-escape (a b)
  (cond ((and (bignump a) (bignump b))
	 (%sp-escape-return (bignum-bignum-bitfun logxor a b)))
	((fixnump a)
	 (%sp-escape-return (bignum-fixnum-bitfun logxor b a)))
	(t (%sp-escape-return (bignum-fixnum-bitfun logxor a b)))))


(defun %sp-bitor-escape (a b)
  (cond ((and (bignump a) (bignump b))
	 (%sp-escape-return (bignum-bignum-bitfun logior a b)))
	((fixnump a)
	 (%sp-escape-return (bignum-fixnum-bitfun logior b a)))
	(t (%sp-escape-return (bignum-fixnum-bitfun logior a b)))))