;;; mathprims.ss
;;; Copyright 1984-2017 Cisco Systems, Inc.
;;; 
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;; 
;;; http://www.apache.org/licenses/LICENSE-2.0
;;; 
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

(begin
(eval-when (compile)

  (define-syntax define-relop
    (syntax-rules ()
      [(_ name pred? err not-nan?)
       (set! name
          (case-lambda
             [(x1 x2) (#2%name x1 x2)]
             [(x1 x2 x3) (if (#2%name x1 x2) (#2%name x2 x3) (begin (#2%name x2 x3) #f))]
             [(x1 x2 . rest)
              (let loop ([x1 x1] [x2 x2] [rest rest])
                 (if (#2%name x1 x2)
                     (or (null? rest) (loop x2 (car rest) (cdr rest)))
                     (let loop ([rest rest])
                        (cond
                           [(null? rest) #f]
                           [(pred? (car rest)) (loop (cdr rest))]
                           [else (err 'name (car rest))]))))]
             [(x1)
              (unless (pred? x1) (err 'name x1))
              (#3%not-nan? x1)]))]))

  (define-syntax define-r6rs-relop ; requires 2+ arguments
    (syntax-rules ()
      [(_ name pred? err)
       (set! name
          (case-lambda
             [(x1 x2) (#2%name x1 x2)]
             [(x1 x2 x3) (if (#2%name x1 x2) (#2%name x2 x3) (begin (#2%name x2 x3) #f))]
             [(x1 x2 . rest)
              (let loop ([x1 x1] [x2 x2] [rest rest])
                 (if (#2%name x1 x2)
                     (or (null? rest) (loop x2 (car rest) (cdr rest)))
                     (let loop ([rest rest])
                        (cond
                           [(null? rest) #f]
                           [(pred? (car rest)) (loop (cdr rest))]
                           [else (err 'name (car rest))]))))]))]))

  (define-syntax define-addop
    (syntax-rules ()
      [(_ name)
       (set! name
          (case-lambda
             [(x1 x2) (#2%name x1 x2)]
             [(x1 x2 x3) (#2%name (#2%name x1 x2) x3)]
             [(x1 x2 . rest)
              (let loop ([x1 x1] [x2 x2] [rest rest])
                 (let ([x (#2%name x1 x2)])
                    (if (null? rest) x (loop x (car rest) (cdr rest)))))]
             [(x1) (#2%name x1)]
             [() (#2%name)]))]))

  (define-syntax define-subop
    (syntax-rules ()
      [(_ name pred? err)
       (set! name
          (case-lambda
             [(x1 x2) (#2%name x1 x2)]
             [(x1 x2 x3) (#2%name (#2%name x1 x2) x3)]
             [(x1) (#2%name x1)]
             [(x0 x1 . rest)
              (unless (pred? x0) (err 'name x0))
              (let loop ([x0 x0] [x1 x1] [rest rest])
                (unless (pred? x1) (err 'name x1))
                (if (null? rest)
                    (#3%name x0 x1)
                    (loop (#3%name x0 x1) (car rest) (cdr rest))))]))]))

  (define-syntax define-generic-subop
    (syntax-rules ()
      [(_ name)
       (set! name
          (case-lambda
             [(x1 x2) (#2%name x1 x2)]
             [(x1 x2 x3) (#2%name (#2%name x1 x2) x3)]
             [(x1) (#2%name x1)]
             [(x0 x1 . rest)
              (let loop ([x0 x0] [x1 x1] [rest rest])
                (if (null? rest)
                    (#2%name x0 x1)
                    (loop (#2%name x0 x1) (car rest) (cdr rest))))]))]))

  (define-syntax define-cfl-relop
    (syntax-rules ()
      [(_ name pred? err not-nan?)
       (set! name
          (case-lambda
             [(x1 x2)
              (unless (pred? x1) (err 'name x1))
              (unless (pred? x2) (err 'name x2))
              (#3%name x1 x2)]
             [(x1 x2 x3)
              (unless (pred? x1) (err 'name x1))
              (unless (pred? x2) (err 'name x2))
              (unless (pred? x3) (err 'name x3))
              (and (#3%name x1 x2) (#3%name x2 x3))]
             [(x1 x2 . rest)
              (unless (pred? x1) (err 'name x1))
              (let loop ([x1 x1] [x2 x2] [rest rest])
                 (unless (pred? x2) (err 'name x2))
                 (if (#3%name x1 x2)
                     (or (null? rest) (loop x2 (car rest) (cdr rest)))
                     (let loop ([rest rest])
                        (cond
                           [(null? rest) #f]
                           [(pred? (car rest)) (loop (cdr rest))]
                           [else (err 'name (car rest))]))))]
             [(x1)
              (unless (pred? x1) (err 'name x1))
              (not-nan? x1)]))]))

  (define-syntax define-cfl-addop
    (syntax-rules ()
      [(_ name pred? err)
       (set! name
          (case-lambda
             [(x1 x2)
              (unless (pred? x1) (err 'name x1))
              (unless (pred? x2) (err 'name x2))
              (#3%name x1 x2)]
             [(x1 x2 x3)
              (unless (pred? x1) (err 'name x1))
              (unless (pred? x2) (err 'name x2))
              (unless (pred? x3) (err 'name x3))
              (#3%name (#3%name x1 x2) x3)]
             [(x1 x2 . rest)
              (unless (pred? x1) (err 'name x1))
              (let loop ([x1 x1] [x2 x2] [rest rest])
                 (unless (pred? x2) (err 'name x2))
                 (let ([x (#3%name x1 x2)])
                    (if (null? rest) x (loop x (car rest) (cdr rest)))))]
             [(x1)
              (unless (pred? x1) (err 'name x1))
              (#3%name x1)]
             [() (name)]))]))

  (define-syntax define-cfl-subop
    (syntax-rules ()
      [(_ name pred? err)
       (set! name
          (case-lambda
             [(x1 x2)
              (unless (pred? x1) (err 'name x1))
              (unless (pred? x2) (err 'name x2))
              (#3%name x1 x2)]
             [(x1 x2 x3)
              (unless (pred? x1) (err 'name x1))
              (unless (pred? x2) (err 'name x2))
              (unless (pred? x3) (err 'name x3))
              (#3%name (#3%name x1 x2) x3)]
             [(x1)
              (unless (pred? x1) (err 'name x1))
              (#3%name x1)]
             [(x0 x1 . rest)
              (unless (pred? x0) (err 'name x0))
              (let loop ([x0 x0] [x1 x1] [rest rest])
                (unless (pred? x1) (err 'name x1))
                (if (null? rest)
                    (#3%name x0 x1)
                    (loop (#3%name x0 x1) (car rest) (cdr rest))))]))]))
)

(define 1- (lambda (x) (#2%1- x)))

(define 1+ (lambda (x) (#2%1+ x)))

(define sub1 (lambda (x) (#2%sub1 x)))

(define -1+ (lambda (x) (#2%-1+ x)))

(define add1 (lambda (x) (#2%add1 x)))

(define-addop +)
(define-generic-subop -)
(define-addop *)
(define-generic-subop /)

(define-addop logand)
(define-addop bitwise-and)
(define-addop logior)
(define-addop bitwise-ior)
(define-addop logor)
(define-addop logxor)
(define-addop bitwise-xor)

(define (lognot x) (#2%lognot x))
(define (bitwise-not x) (#2%bitwise-not x))

(define (logbit? x y) (#2%logbit? x y))
(define (bitwise-bit-set? x y) (#2%bitwise-bit-set? x y))
(define (logbit0 x y) (#2%logbit0 x y))
(define (logbit1 x y) (#2%logbit1 x y))
(define (logtest x y) (#2%logtest x y))

(eval-when (compile)
  (define-syntax define-number-relop
    (syntax-rules ()
      [(_ name)
       (define name
          (case-lambda
             [(x1 x2) (#2%name x1 x2)]
             [(x1 x2 x3) (if (#2%name x1 x2) (#2%name x2 x3) (begin (#2%name x2 x3) #f))]
             [(x1) (begin (#2%name x1 0) #t)]
             [(x1 x2 . rest)
              (let loop ([x1 x1] [x2 x2] [ls rest])
                 (if (or (null? ls) (loop x2 (car ls) (cdr ls)))
                     (#2%name x1 x2)
                     (begin (#2%name x1 x2) #f)))]))])))

(define-number-relop =)
(define-number-relop <)
(define-number-relop >)
(define-number-relop <=)
(define-number-relop >=)

(eval-when (compile)
  (define-syntax define-r6rs-number-relop ; requires 2+ argument
    (syntax-rules ()
      [(_ r6rs:name name)
       (define-who #(r6rs: name)
          (case-lambda
             [(x1 x2) (#2%r6rs:name x1 x2)]
             [(x1 x2 x3) (if (#2%r6rs:name x1 x2)
                             (#2%r6rs:name x2 x3)
                             (begin (#2%r6rs:name x2 x3) #f))]
             [(x1 x2 . rest)
              (let loop ([x1 x1] [x2 x2] [ls rest])
                 (if (or (null? ls) (loop x2 (car ls) (cdr ls)))
                     (#2%r6rs:name x1 x2)
                     (begin (#2%r6rs:name x1 x2) #f)))]))])))

(define-r6rs-number-relop r6rs:= =)
(define-r6rs-number-relop r6rs:< <)
(define-r6rs-number-relop r6rs:> >)
(define-r6rs-number-relop r6rs:<= <=)
(define-r6rs-number-relop r6rs:>= >=)

(eval-when (compile) (optimize-level 3))

(let ()
   (define flargerr
      (lambda (who x)
         ($oops who "~s is not a flonum" x)))

   (set! fl-make-rectangular
      (lambda (x y)
         (unless (flonum? x) (flargerr 'fl-make-rectangular x))
         (unless (flonum? y) (flargerr 'fl-make-rectangular y))
         (#3%fl-make-rectangular x y)))

   (define-addop fl+)
   (define-subop fl- flonum? flargerr)
   (define-addop fl*)
   (define-subop fl/ flonum? flargerr)

   (set! flabs
      (lambda (x)
         (#2%flabs x)))

   (set! flround
      (lambda (x)
         (unless (flonum? x) (flargerr 'flround x))
         (#3%flround x)))

   (set! flsingle
         (lambda (x)
           (unless (flonum? x) (flargerr 'flsingle x))
           (#3%flsingle x)))

   (set! fllp
      (lambda (x)
         (unless (flonum? x) (flargerr 'fllp x))
         (#3%fllp x)))

   (define-relop fl= flonum? flargerr fl=)
   (define-relop fl< flonum? flargerr fl=)
   (define-relop fl> flonum? flargerr fl=)
   (define-relop fl<= flonum? flargerr fl=)
   (define-relop fl>= flonum? flargerr fl=)
   (define-r6rs-relop fl=? flonum? flargerr)
   (define-r6rs-relop fl<? flonum? flargerr)
   (define-r6rs-relop fl>? flonum? flargerr)
   (define-r6rs-relop fl<=? flonum? flargerr)
   (define-r6rs-relop fl>=? flonum? flargerr)

   (set-who! $fleqv?
     (lambda (x y)
       (unless (flonum? x) (flargerr who x))
       (unless (flonum? y) (flargerr who y))
       (#3%$fleqv? x y)))

   (set-who! $flhash
     (lambda (x)
       (unless (flonum? x) (flargerr who x))
       (#3%$flhash x)))

   (set-who! $flonum-exponent ; requires optimize-level 3
      (lambda (x)
         (unless (flonum? x) (flargerr who x))
         ($flonum-exponent x)))

   (set-who! $flonum-sign ; requires optimize-level 3
      (lambda (x)
         (unless (flonum? x) (flargerr who x))
         ($flonum-sign x)))

   (set-who! flonum->fixnum
     (lambda (x)
       (#2%flonum->fixnum x)))
)

(let ()
   (define fxargerr
      (lambda (who x)
         ($oops who "~s is not a fixnum" x)))

   (define /zeroerr
      (lambda (who)
         ($oops who "attempt to divide by zero")))

   (define fxanserr
      (lambda (who . args)
         ($impoops who "fixnum overflow computing ~s" (cons who args))))

   (define-addop fx+)
   (define-subop fx- fixnum? fxargerr)

   (set-who! #(r6rs: fx+) (lambda (x y) (#2%r6rs:fx+ x y)))
   (set-who! #(r6rs: fx-)
     (case-lambda
       [(x) (#2%r6rs:fx- x)]
       [(x y) (#2%r6rs:fx- x y)]))

   (set-who! fx+/wraparound
     (lambda (x1 x2)
       (#2%fx+/wraparound x1 x2)))

   (set-who! fx-/wraparound
     (case-lambda
      [(x)
       (#2%fx-/wraparound x)]
      [(x1 x2)
       (#2%fx-/wraparound x1 x2)]))

   (set! fx1-
      (lambda (x)
         (#2%fx1- x)))

   (set! fx1+
      (lambda (x)
         (#2%fx1+ x)))

   (set! fxzero?
      (lambda (x)
         (#2%fxzero? x)))

   (set! fx*
     (rec fx*
       (case-lambda
         [(x1 x2)
          (if (fixnum? x1)
              (if (fixnum? x2)
                  ; should handle fixnums (avoiding overflow)
                  (let ([n (* x1 x2)])
                     (if (fixnum? n) n (fxanserr 'fx* x1 x2)))
                  (fxargerr 'fx* x2))
              (fxargerr 'fx* x1))]
         [(x1 x2 x3)
          (if (fixnum? x1)
              (if (fixnum? x2)
                  (if (fixnum? x3)
                      ; should handle fixnums (avoiding overflow)
                      (let ([n (* x1 x2)])
                        (if (fixnum? n)
                            ; should handle fixnums (avoiding overflow)
                            (let ([n (* n x3)])
                              (if (fixnum? n) n (fxanserr 'fx* x1 x2 x3)))
                            (fxanserr 'fx* x1 x2 x3)))
                      (fxargerr 'fx* x3))
                  (fxargerr 'fx* x2))
              (fxargerr 'fx* x1))]
         [(x1) (if (fixnum? x1) x1 (fxargerr 'fx* x1))]
         [() 1]
         [(x1 . rest)
          (let loop ([a x1] [ls rest])
             (if (null? ls)
                 a
                 (loop (fx* a (car ls)) (cdr ls))))])))

   (set-who! #(r6rs: fx*)
     (lambda (x1 x2)
       (if (fixnum? x1)
           (if (fixnum? x2)
               ; should handle fixnums (avoiding overflow)
               (let ([n (* x1 x2)])
                 (if (fixnum? n) n (fxanserr who x1 x2)))
               (fxargerr who x2))
           (fxargerr who x1))))

   (set-who! fx*/wraparound
     (lambda (x1 x2)
       (#2%fx*/wraparound x1 x2)))

   (set! fxquotient
     (rec fxquotient
       (case-lambda
         [(x1 x2)
          (if (fixnum? x1)
              (if (fixnum? x2)
                  (begin
                    (when (fx= x2 0) (/zeroerr 'fxquotient))
                    (if (and (fx= x2 -1) (fx= x1 (most-negative-fixnum)))
                        (fxanserr 'fxquotient x1 x2)
                        (#3%fxquotient x1 x2)))
                  (fxargerr 'fxquotient x2))
              (fxargerr 'fxquotient x1))]
         [(x1 x2 x3)
          (if (fixnum? x1)
              (if (fixnum? x2)
                  (if (fixnum? x3)
                      (begin
                        (when (fx= x2 0) (/zeroerr 'fxquotient))
                        (if (and (fx= x2 -1) (fx= x1 (most-negative-fixnum)))
                            (fxanserr 'fxquotient x1 x2 x3)
                            (let ([n (#3%fxquotient x1 x2)])
                              (when (fx= x3 0) (/zeroerr 'fxquotient))
                              (if (and (fx= x3 -1) (fx= n (most-negative-fixnum)))
                                  (fxanserr 'fxquotient x1 x2 x3)
                                  (#3%fxquotient n x3)))))
                      (fxargerr 'fxquotient x3))
                  (fxargerr 'fxquotient x2))
              (fxargerr 'fxquotient x1))]
         [(x1)
          (if (fixnum? x1)
              (if (fx= x1 0)
                  (/zeroerr 'fxquotient)
                  (#3%fxquotient 1 x1))
              (fxargerr 'fxquotient x1))]
         [(x1 . rest)
          (let loop ([a x1] [ls rest])
            (if (null? ls)
                a
                (loop (fxquotient a (car ls)) (cdr ls))))])))

   (set! fx/
     (rec fx/ ;; same as fxquotient---should it be?
       (case-lambda
         [(x1 x2)
          (if (fixnum? x1)
              (if (fixnum? x2)
                  (begin
                    (when (fx= x2 0) (/zeroerr 'fx/))
                    (if (and (fx= x2 -1) (fx= x1 (most-negative-fixnum)))
                        (fxanserr 'fx/ x1 x2)
                        (#3%fx/ x1 x2)))
                  (fxargerr 'fx/ x2))
              (fxargerr 'fx/ x1))]
         [(x1 x2 x3)
          (if (fixnum? x1)
              (if (fixnum? x2)
                  (if (fixnum? x3)
                      (begin
                        (when (fx= x2 0) (/zeroerr 'fx/))
                        (if (and (fx= x2 -1) (fx= x1 (most-negative-fixnum)))
                            (fxanserr 'fx/ x1 x2 x3)
                            (let ([n (#3%fx/ x1 x2)])
                              (when (fx= x3 0) (/zeroerr 'fx/))
                              (if (and (fx= x3 -1) (fx= n (most-negative-fixnum)))
                                  (fxanserr 'fx/ x1 x2 x3)
                                  (#3%fx/ n x3)))))
                      (fxargerr 'fx/ x3))
                  (fxargerr 'fx/ x2))
              (fxargerr 'fx/ x1))]
         [(x1)
          (if (fixnum? x1)
              (if (fx= x1 0)
                  (/zeroerr 'fx/)
                  (#3%fx/ 1 x1))
              (fxargerr 'fx/ x1))]
         [(x1 . rest)
          (let loop ([a x1] [ls rest])
            (if (null? ls)
                a
                (loop (fx/ a (car ls)) (cdr ls))))])))

   (set! fxabs
      (lambda (x)
         (unless (fixnum? x) (fxargerr 'fxabs x))
         (when (fx= x (most-negative-fixnum)) (fxanserr 'fxabs x))
         (#3%fxabs x)))

   (define-relop fx= fixnum? fxargerr fx=)
   (define-relop fx< fixnum? fxargerr fx=)
   (define-relop fx> fixnum? fxargerr fx=)
   (define-relop fx<= fixnum? fxargerr fx=)
   (define-relop fx>= fixnum? fxargerr fx=)
   (define-r6rs-relop fx=? fixnum? fxargerr)
   (define-r6rs-relop fx<? fixnum? fxargerr)
   (define-r6rs-relop fx>? fixnum? fxargerr)
   (define-r6rs-relop fx<=? fixnum? fxargerr)
   (define-r6rs-relop fx>=? fixnum? fxargerr)

   (set! $fxu<
     (lambda (x y)
       (unless (fixnum? x) (fxargerr '$fxu< x))
       (unless (fixnum? y) (fxargerr '$fxu< y))
       (#3%$fxu< x y)))

   (set! $fxx+
     (case-lambda
       [(x1 x2)
        (unless (fixnum? x1) (fxargerr '$fxx+ x1))
        (unless (fixnum? x2) (fxargerr '$fxx+ x2))
        (#3%$fxx+ x1 x2)]
       [(x1 x2 x3)
        (unless (fixnum? x1) (fxargerr '$fxx+ x1))
        (unless (fixnum? x2) (fxargerr '$fxx+ x2))
        (unless (fixnum? x3) (fxargerr '$fxx+ x3))
         (#3%+ (#3%$fxx+ x1 x2) x3)]
       [(x1 x2 x3 . rest)
        (unless (fixnum? x1) (fxargerr '$fxx+ x1))
        (unless (fixnum? x2) (fxargerr '$fxx+ x2))
        (let loop ([x1 (#3%$fxx+ x1 x2)] [x3 x3] [rest rest])
          (unless (fixnum? x3) (fxargerr '$fxx+ x3))
            (let ([x (#3%+ x1 x3)])
               (if (null? rest) x (loop x (car rest) (cdr rest)))))]
       [(x1)
        (unless (fixnum? x1) (fxargerr '$fxx+ x1))
        x1]
       [() 0]))

   (set! $fxx-
     (case-lambda
       [(x1 x2)
        (unless (fixnum? x1) (fxargerr '$fxx- x1))
        (unless (fixnum? x2) (fxargerr '$fxx- x2))
        (#3%$fxx- x1 x2)]
       [(x1 x2 x3)
        (unless (fixnum? x1) (fxargerr '$fxx- x1))
        (unless (fixnum? x2) (fxargerr '$fxx- x2))
        (unless (fixnum? x3) (fxargerr '$fxx- x3))
         (#3%- (#3%$fxx- x1 x2) x3)]
       [(x1 x2 x3 . rest)
        (unless (fixnum? x1) (fxargerr '$fxx- x1))
        (unless (fixnum? x2) (fxargerr '$fxx- x2))
        (let loop ([x1 (#3%$fxx- x1 x2)] [x3 x3] [rest rest])
          (unless (fixnum? x3) (fxargerr '$fxx- x3))
            (let ([x (#3%- x1 x3)])
               (if (null? rest) x (loop x (car rest) (cdr rest)))))]
       [(x1)
        (unless (fixnum? x1) (fxargerr '$fxx- x1))
        (#3%$fxx- x1)]))

   (define-addop fxlogand)
   (define-addop fxlogior)
   (define-addop fxlogor)
   (define-addop fxlogxor)
   (define-addop fxand)
   (define-addop fxior)
   (define-addop fxxor)

   (set! fxsll
      (lambda (x y)
        (#2%fxsll x y)))

   (set-who! fxsll/wraparound
     (lambda (x1 x2)
       (#2%fxsll/wraparound x1 x2)))

   (set! fxsrl
      (lambda (x y)
         (#2%fxsrl x y)))

   (set! fxarithmetic-shift-left
     (lambda (x y)
       (#2%fxarithmetic-shift-left x y)))

   (set! fxsra
      (lambda (x y)
         (#2%fxsra x y)))

   (set! fxarithmetic-shift-right
     (lambda (x y)
       (#2%fxarithmetic-shift-right x y)))

   (set! fxarithmetic-shift
     (lambda (x y)
       (#2%fxarithmetic-shift x y)))

   (set! fxlognot
      (lambda (x)
         (#2%fxlognot x)))

   (set! fxnot
      (lambda (x)
         (#2%fxnot x)))

   (set! fxlogtest
      (lambda (x y)
         (#2%fxlogtest x y)))

   (set! fxlogbit?
      (lambda (x y)
         (#2%fxlogbit? x y)))

   (set! fxbit-set?
      (lambda (x y)
         (#2%fxbit-set? x y)))

   (set! fxlogbit0
      (lambda (x y)
         (#2%fxlogbit0 x y)))

   (set! fxlogbit1
      (lambda (x y)
         (#2%fxlogbit1 x y)))

   (set-who! fxcopy-bit
     (lambda (n k b)
      ; optimize-level 2 handler doesn't kick in unless b=0 or b=1
       (unless (fixnum? n) (fxargerr who n))
       (unless (fixnum? k) (fxargerr who k))
       (unless ($fxu< k (fx- (fixnum-width) 1))
         ($oops who "invalid bit index ~s" k))
       (case b
         [(0) (#3%fxlogbit0 k n)]
         [(1) (#3%fxlogbit1 k n)]
         [else ($oops who "invalid bit value ~s" b)])))

   (set! fxpopcount
      (lambda (x)
         (#2%fxpopcount x)))

   (set! fxpopcount32
      (lambda (x)
         (#2%fxpopcount32 x)))

   (set! fxpopcount16
      (lambda (x)
         (#2%fxpopcount16 x)))

   (set! fxeven?
      (lambda (x)
         (#2%fxeven? x)))

   (set! fxodd?
      (lambda (x)
         (#2%fxodd? x)))

   (set! fxremainder
      (lambda (x y)
         (unless (fixnum? x) (fxargerr 'fxremainder x))
         (unless (fixnum? y) (fxargerr 'fxremainder y))
         (when (fx= y 0) (/zeroerr 'fxremainder))
         (#3%fxremainder x y)))

   (set! fxmodulo
      (lambda (x y)
         (unless (fixnum? x) (fxargerr 'fxmodulo x))
         (unless (fixnum? y) (fxargerr 'fxmodulo y))
         (when (fx= y 0) (/zeroerr 'fxmodulo))
         (let ([r (fxremainder x y)])
            (if (if (fxnegative? y) (fxpositive? r) (fxnegative? r))
                (fx+ r y)
                r))))

   (set! fxmin
      (case-lambda
         [(x y)
          (unless (fixnum? x) (fxargerr 'fxmin x))
          (unless (fixnum? y) (fxargerr 'fxmin y))
          (if (fx< y x) y x)]
         [(x y z)
          (unless (fixnum? x) (fxargerr 'fxmin x))
          (unless (fixnum? y) (fxargerr 'fxmin y))
          (unless (fixnum? z) (fxargerr 'fxmin z))
          (if (fx< y x)
              (if (fx< z y) z y)
              (if (fx< z x) z x))]
         [(x . y)
          (unless (fixnum? x) (fxargerr 'fxmin x))
          (let f ([x x] [y y])
             (if (null? y)
                 x
                 (f (let ([z (car y)])
                       (unless (fixnum? z) (fxargerr 'fxmin z))
                       (if (fx< z x) z x))
                    (cdr y))))]))

   (set! fxmax
      (case-lambda
         [(x y)
          (unless (fixnum? x) (fxargerr 'fxmax x))
          (unless (fixnum? y) (fxargerr 'fxmax y))
          (if (fx> y x) y x)]
         [(x y z)
          (unless (fixnum? x) (fxargerr 'fxmax x))
          (unless (fixnum? y) (fxargerr 'fxmax y))
          (unless (fixnum? z) (fxargerr 'fxmax z))
          (if (fx> y x)
              (if (fx> z y) z y)
              (if (fx> z x) z x))]
         [(x . y)
          (unless (fixnum? x) (fxargerr 'fxmax x))
          (let f ([x x] [y y])
             (if (null? y)
                 x
                 (f (let ([z (car y)])
                       (unless (fixnum? z) (fxargerr 'fxmax z))
                       (if (fx> z x) z x))
                    (cdr y))))]))

   (set! fxnegative?
      (lambda (x)
         (#2%fxnegative? x)))

   (set! fxpositive?
      (lambda (x)
         (#2%fxpositive? x)))

   (set! fxnonnegative?
      (lambda (x)
         (#2%fxnonnegative? x)))

   (set! fxnonpositive?
      (lambda (x)
         (#2%fxnonpositive? x)))

   (set! fixnum->flonum
      (lambda (x)
         (#2%fixnum->flonum x)))

   (set-who! fxlength
     (lambda (x)
       (if (fixnum? x)
           (#3%fxlength x)
           (fxargerr who x))))

   (set-who! fxfirst-bit-set
     (lambda (x)
       (if (fixnum? x)
           (#3%fxfirst-bit-set x)
           (fxargerr who x))))

   (set-who! fxif
     (lambda (x y z)
       (if (fixnum? x)
           (if (fixnum? y)
               (if (fixnum? z)
                   (#3%fxif x y z)
                   (fxargerr who z))
               (fxargerr who y))
           (fxargerr who x))))

   (set-who! fxbit-field
     (lambda (n start end)
       (if (fixnum? n)
           (if (and (fixnum? start) ($fxu< start (fixnum-width)))
               (if (and (fixnum? end) ($fxu< end (fixnum-width)))
                   (if (fx<= start end)
                       (fxsra (fxand n (fxnot (fxsll -1 end))) start)
                       ($oops who "start index ~s is greater than end index ~s" start end))
                   ($oops who "~s is not a valid end index" end))
               ($oops who "~s is not a valid start index" start))
           (fxargerr who n))))

   (set-who! fxcopy-bit-field
     (lambda (n start end m)
       (if (fixnum? n)
           (if (and (fixnum? start) ($fxu< start (fixnum-width)))
               (if (and (fixnum? end) ($fxu< end (fixnum-width)))
                   (if (fx<= start end)
                       (if (fixnum? m)
                           (let ([mask (fx- (fxsll 1 (fx- end start)) 1)])
                             (fxior
                               (fxand n (fxnot (fxsll mask start)))
                               (fxsll (fxand m mask) start)))
                           (fxargerr who m))
                       ($oops who "start index ~s is greater than end index ~s" start end))
                   ($oops who "~s is not a valid end index" end))
               ($oops who "~s is not a valid start index" start))
           (fxargerr who n))))
)

;;; The "cfl" operations could be done at level 0 by expanding them out.
;;; They might be more efficient that way since they wouldn't have to
;;; do double flonum checking.

(define cflonum?
   (lambda (x)
      (cflonum? x)))

(let ()

(define noncflonum-error
   (lambda (who x)
      ($oops who "~s is not a cflonum" x)))

(set! cfl-real-part
   (lambda (z)
      (type-case z
          [($inexactnum?) ($inexactnum-real-part z)]
          [(flonum?) z]
          [else (noncflonum-error 'cfl-real-part z)])))

(set! cfl-imag-part
   (lambda (z)
      (type-case z
          [($inexactnum?) ($inexactnum-imag-part z)]
          [(flonum?) 0.0]
          [else (noncflonum-error 'cfl-imag-part z)])))

(define-cfl-addop cfl+ cflonum? noncflonum-error)
(define-cfl-addop cfl* cflonum? noncflonum-error)
(define-cfl-subop cfl- cflonum? noncflonum-error)
(define-cfl-subop cfl/ cflonum? noncflonum-error)
(define-cfl-relop cfl= cflonum? noncflonum-error cfl=)

(set! cfl-conjugate
   (lambda (x)
      (type-case x
         [(cflonum?) (#3%cfl-conjugate x)]
         [else (noncflonum-error 'cfl-conjugate x)])))

)
)
