; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.


; Integer-only primitive operations

; These predicates are used to characterize the numeric representations that
; are implemented in the VM.

(define (unary-lose x)
  (raise-exception wrong-type-argument 0 x))

(define (binary-lose x y)
  (raise-exception wrong-type-argument 0 x y))

; They're all numbers, even if we can't handle them.

(define-primitive number? (any->)
  (lambda (x)
    (or (fixnum? x)
	(bignum? x)
	(ratnum? x)
	(double? x)
	(extended-number? x)))
  return-boolean)
  
(define (vm-integer? n)
  (cond ((or (fixnum? n)
	     (bignum? n))
	 (goto return-boolean #t))
	((extended-number? n)
	 (unary-lose n))
	(else
	 (goto return-boolean #f))))

; These assume that ratnums and doubles aren't being used.

(define-primitive integer?  (any->) vm-integer?)
(define-primitive rational? (any->) vm-integer?)
(define-primitive real?     (any->) vm-integer?)
(define-primitive complex?  (any->) vm-integer?)

;----------------
; A macro for defining primitives that only operate on fixnums.

(define-syntax define-integer-only
  (syntax-rules ()
    ((define-integer-only (opcode arg) value)
     (define-primitive opcode (vm-integer->)
       (lambda (arg)
	 (goto return value))))
    ((define-integer-only (opcode arg0 arg1) value)
     (define-primitive opcode (vm-integer-> vm-integer->)
       (lambda (arg0 arg1)
	 (goto return value))))))

; These primitives have a simple answer in the case of fixnums; for all others
; they punt to the run-time system.

(define-integer-only (exact?      n) true)
(define-integer-only (real-part   n) n)
(define-integer-only (imag-part   n) (enter-fixnum 0))
(define-integer-only (floor       n) n)
(define-integer-only (numerator   n) n)
(define-integer-only (denominator n) (enter-fixnum 1))

(define-primitive angle (vm-integer->)
  (lambda (n)
    (if (if (fixnum? n)
	    (fx>= n (enter-fixnum 0))
	    (bignum-nonnegative? n))
	(goto return (enter-fixnum 0))
	(unary-lose n))))

(define-primitive magnitude (vm-integer->) integer-abs)

; These all just raise an exception and let the run-time system do the work.

(define-syntax define-punter
  (syntax-rules ()
    ((define-punter opcode)
     (define-primitive opcode (any->) unary-lose))))

(define-punter exact->inexact)
(define-punter inexact->exact)
(define-punter exp)
(define-punter log)
(define-punter sin)
(define-punter cos)
(define-punter tan)
(define-punter asin)
(define-punter acos)
(define-punter sqrt)

(define-syntax define-punter2
  (syntax-rules ()
    ((define-punter2 opcode)
     (define-primitive opcode (any-> any->) binary-lose))))

(define-punter2 atan)
(define-punter2 make-polar)
(define-punter2 make-rectangular)

(define-syntax define-binop
  (syntax-rules ()
    ((define-binop opcode careful-op integer-op)
     (define-primitive opcode (any-> any->)
       (lambda (x y)
	 (cond ((and (fixnum? x)
		     (fixnum? y))
		(goto careful-op
		      x
		      y
		      return
		      (lambda (x y)
			(goto return (integer-op x y)))))
	       ((and (vm-integer? x)
		     (vm-integer? y))
		(goto return (integer-op x y)))
	       (else
		(binary-lose x y))))))))

(define-binop + add-carefully integer-add)
(define-binop - subtract-carefully integer-subtract)
(define-binop * multiply-carefully integer-multiply)
(define-binop / divide-carefully integer-divide)
(define-binop quotient         quotient-carefully integer-quotient)
(define-binop remainder        remainder-carefully integer-remainder)
;(define-binop arithmetic-shift shift-carefully)

(define-syntax define-comparison
  (syntax-rules ()
    ((define-comparison opcode fixnum-op integer-op)
     (define-primitive opcode (any-> any->)
       (lambda (x y)
	 (cond ((and (fixnum? x)
		     (fixnum? y))
		(goto careful-op
		      x
		      y
		      return
		      (lambda (x y)
			(goto return (integer-op x y)))))
	       ((and (vm-integer? x)
		     (vm-integer? y))
		(goto return (integer-op x y)))
	       (else
		(binary-lose x y))))))))

(define-primitive = (any-> any->)
  (lambda (x y)
    (cond ((fixnum? x)
	   (if (fixnum? y)
	       (goto return-boolean (fixnum= x y))
	       (if (bignum? y)
		   (goto return-boolean #f)
		   (binary-lose x y))))
	  ((fixnum? y)
	   (if (bignum? x)
	       (goto return-boolean #f)
	       (binary-lose x y)))
	  ((and (bignum? x)
		(bignum? y))
	   (goto return-boolean (bignum= x y)))
	  (else
	   (binary-lose x y)))))

(define-primitive < (any-> any->)
  (lambda (x y)
    (cond ((fixnum? x)
	   (if (fixnum? y)
	       (goto return-boolean (fixnum< x y))
	       (if (bignum? y)
		   (goto return-boolean (bignum-positive? y))
		   (binary-lose x y))))
	  ((fixnum? y)
	   (if (bignum? x)
	       (goto return-boolean (bignum-negative? x))
	       (binary-lose x y)))
	  ((and (bignum? x)
		(bignum? y))
	   (goto return-boolean (bignum< x y)))
	  (else
	   (binary-lose x y)))))

(define-integer-only (<  x y) (enter-boolean (fixnum<  x y)))
(define-integer-only (>  x y) (enter-boolean (fixnum>  x y)))
(define-integer-only (<= x y) (enter-boolean (fixnum<= x y)))
(define-integer-only (>= x y) (enter-boolean (fixnum>= x y)))

(define-integer-only (bitwise-not x)   (fixnum-bitwise-not x))
(define-integer-only (bitwise-and x y) (fixnum-bitwise-and x y))
(define-integer-only (bitwise-ior x y) (fixnum-bitwise-ior x y))
(define-integer-only (bitwise-xor x y) (fixnum-bitwise-xor x y))