; Copyright (c) 1993, 1994 by Richard Kelsey and Jonathan Rees.
; Copyright (c) 1996 by NEC Research Institute, Inc.    See file COPYING.

; Bitwise logical operators on bignums.


(define-opcode-extension bitwise-not &bitwise-not)
(define-opcode-extension bitwise-and &bitwise-and)
(define-opcode-extension bitwise-ior &bitwise-ior)
(define-opcode-extension bitwise-xor &bitwise-xor)
(define-opcode-extension arithmetic-shift &arithmetic-shift)


(define (integer-bitwise-not m)
  ;; (integer+ (integer-negate m) -1)
  (integer- -1 m))

(define (integer-bitwise-and m n)
  (if (or (integer= 0 m) (integer= 0 n))
      0
      (integer-bitwise-op bitwise-and m n)))

(define (integer-bitwise-ior m n)
  (cond ((integer= 0 m) n)
	((integer= 0 n) m)
	(else
	 (integer-bitwise-op bitwise-ior m n))))

(define (integer-bitwise-xor m n)
  (cond ((integer= 0 m) n)
	((integer= 0 n) m)
	(else
	 (integer-bitwise-op bitwise-xor m n))))

(define (integer-bitwise-op op m n)
  (let ((m (integer->bignum m))
	(n (integer->bignum n)))
    (let ((finish (lambda (sign-bit mag-op)
		    (let ((mag (mag-op op
				       (bignum-magnitude m)
				       (bignum-magnitude n))))
		      (make-integer (if (= 0 sign-bit) 1 -1)
				    (if (= 0 sign-bit)
					mag
					(negate-magnitude mag)))))))
      (if (>= (bignum-sign m) 0)
	  (if (>= (bignum-sign n) 0)
	      (finish (op 0 0) magnitude-bitwise-binop-pos-pos)
	      (finish (op 0 1) magnitude-bitwise-binop-pos-neg))
	  (if (>= (bignum-sign n) 0)
	      (finish (op 0 1) magnitude-bitwise-binop-neg-pos)
	      (finish (op 1 1) magnitude-bitwise-binop-neg-neg))))))

(define radix-mask (- radix 1))

(define (magnitude-bitwise-binop-pos-pos op m n)
  (let recur ((m m) (n n))
    (if (and (zero-magnitude? m) (zero-magnitude? n))
	m
	(adjoin-digit (bitwise-and (op (low-digit m) (low-digit n)) radix-mask)
		      (recur (high-digits m) (high-digits n))))))

; Same as the above, except that one magnitude is that of a negative number.

(define (magnitude-bitwise-binop-neg-pos op m n)
  (magnitude-bitwise-binop-pos-neg op n m))

(define (magnitude-bitwise-binop-pos-neg op m n)
  (let recur ((m m) (n n) (carry 1))
    (if (and (zero-magnitude? n) (zero-magnitude? m))
	(integer->magnitude (op 0 carry))
	(call-with-values
	 (lambda ()
	   (negate-low-digit n carry))
	 (lambda (n-digit carry)
	   (adjoin-digit (op (low-digit m) n-digit)
			 (recur (high-digits m)
				(high-digits n)
				carry)))))))

; Now both M and N are magnitudes of negative numbers.

(define (magnitude-bitwise-binop-neg-neg op m n)
  (let recur ((m m) (n n) (m-carry 1) (n-carry 1))
    (if (and (zero-magnitude? n) (zero-magnitude? m))
	(integer->magnitude (op m-carry n-carry))
	(call-with-values
	 (lambda ()
	   (negate-low-digit m m-carry))
	 (lambda (m-digit m-carry)
	   (call-with-values
	    (lambda ()
	      (negate-low-digit n n-carry))
	    (lambda (n-digit n-carry)
	      (adjoin-digit (op m-digit n-digit)
			    (recur (high-digits m)
				   (high-digits n)
				   m-carry
				   n-carry)))))))))

(define (negate-low-digit m carry)
  (let ((m (+ (bitwise-and (bitwise-not (low-digit m))
			   radix-mask)
	      carry)))
    (if (>= m radix)
	(values (- m radix) 1)
	(values m 0))))

(define (negate-magnitude m)
  (let recur ((m m) (carry 1))
    (if (zero-magnitude? m)
	(integer->magnitude carry)
	(call-with-values
	 (lambda ()
	   (negate-low-digit m carry))
	 (lambda (next carry)
	   (adjoin-digit next
			 (recur (high-digits m) carry)))))))

; arithmetic-shift

(define (integer-arithmetic-shift m n)
  (let ((m (integer->bignum m)))
    (make-integer (bignum-sign m)
		  (cond ((> n 0)
			 (shift-left-magnitude (bignum-magnitude m) n))
			((= 1 (bignum-sign m))
			 (shift-right-pos-magnitude (bignum-magnitude m) n))
			(else
			 (shift-right-neg-magnitude (bignum-magnitude m) n))))))

(define (shift-left-magnitude mag n)
  (if (< n log-radix)
      (let ((mask (- (arithmetic-shift 1 (- log-radix n)) 1)))
	(let recur ((mag mag)
		    (low 0))
	  (if (zero-magnitude? mag)
	      (adjoin-digit low zero-magnitude)
	      ;; Split the low digit into left and right parts, and shift
	      (let ((left (arithmetic-shift (low-digit mag)
					    (- n log-radix))) ;shift right
		    (right (arithmetic-shift (bitwise-and (low-digit mag) mask)
					     n)))
		(adjoin-digit (bitwise-ior low right)
			      (recur (high-digits mag)
				     left))))))
      (adjoin-digit 0 (shift-left-magnitude mag (- n log-radix)))))

(define (shift-right-pos-magnitude mag n)
  (if (> n (- 0 log-radix))
      (let ((mask (- (arithmetic-shift 1 (- 0 n)) 1)))
	(let recur ((mag mag))
	  (let ((low (low-digit mag))
		(high (high-digits mag)))
	    (adjoin-digit
	     (bitwise-ior (arithmetic-shift low n)
			  (arithmetic-shift (bitwise-and mask (low-digit high))
					    (+ n log-radix)))
	     (if (zero-magnitude? high)
		 zero-magnitude
		 (recur high))))))
      (shift-right-pos-magnitude (high-digits mag) (+ n log-radix))))
      
(define (shift-right-neg-magnitude mag n)
  (negate-magnitude
   (let digit-recur ((mag mag) (n n) (carry 1))
     (call-with-values
      (lambda ()
	(negate-low-digit mag carry))
      (lambda (digits carry)
	(if (<= n (- 0 log-radix))
	    (digit-recur (high-digits mag) (+ n log-radix) carry)
	    (let ((mask (- (arithmetic-shift 1 (- 0 n)) 1)))
	      (let recur ((mag mag) (low digits) (carry carry))
		(let ((high-digits (high-digits mag)))
		  (call-with-values
		   (lambda ()
		     (negate-low-digit high-digits carry))
		   (lambda (high carry)
		     (adjoin-digit
		      (bitwise-ior (arithmetic-shift low n)
				   (arithmetic-shift (bitwise-and mask high)
						     (+ n log-radix)))
		      (if (zero-magnitude? high-digits)
			  (integer->magnitude carry)
			  (recur high-digits high carry))))))))))))))

;(define (tst)
;  (let* ((m (random))
;         (n (bitwise-and m 63))
;         (m1 (integer-arithmetic-shift
;              (integer-arithmetic-shift m n)
;              (- 0 n))))
;    (list n m m1 (= m m1))))
;(define random (make-random 17))


(define-method &bitwise-not ((n :integer)) (integer-bitwise-not n))

(define-method &bitwise-and ((n1 :exact-integer) (n2 :exact-integer))
  (integer-bitwise-and n1 n2))
(define-method &bitwise-ior ((n1 :exact-integer) (n2 :exact-integer))
  (integer-bitwise-ior n1 n2))
(define-method &bitwise-xor ((n1 :exact-integer) (n2 :exact-integer))
  (integer-bitwise-xor n1 n2))

(define-method &arithmetic-shift ((n1 :exact-integer) (n2 :exact-integer))
  (integer-arithmetic-shift n1 n2))
