;;;============================================================================

;;; File: "_num.scm"

;;; Copyright (c) 1994-2018 by Marc Feeley, All Rights Reserved.
;;; Copyright (c) 2004-2017 by Brad Lucier, All Rights Reserved.

;;;============================================================================

(macro-case-target
 ((C)
  (c-declare "#include \"mem.h\"")
  (##define-macro (use-fast-bignum-algorithms) #t))

 ((js)
  (##define-macro (use-fast-bignum-algorithms) #t))

 (else
  (##define-macro (use-fast-bignum-algorithms) #f)))

(define-prim (##use-fast-bignum-algorithms?)
  (use-fast-bignum-algorithms))

;;;============================================================================

;;; Implementation of exceptions.

(implement-library-type-range-exception)

(define-prim (##raise-range-exception arg-num proc . args)
  (##extract-procedure-and-arguments
   proc
   args
   arg-num
   #f
   #f
   (lambda (procedure arguments arg-num dummy1 dummy2)
     (macro-raise
      (macro-make-range-exception procedure arguments arg-num)))))

(implement-library-type-divide-by-zero-exception)

(define-prim (##raise-divide-by-zero-exception proc . args)
  (##extract-procedure-and-arguments
   proc
   args
   #f
   #f
   #f
   (lambda (procedure arguments dummy1 dummy2 dummy3)
     (macro-raise
      (macro-make-divide-by-zero-exception procedure arguments)))))

(implement-library-type-fixnum-overflow-exception)

(define-prim (##raise-fixnum-overflow-exception proc . args)
  (##extract-procedure-and-arguments
   proc
   args
   #f
   #f
   #f
   (lambda (procedure arguments dummy1 dummy2 dummy3)
     (macro-raise
      (macro-make-fixnum-overflow-exception procedure arguments)))))

;;;----------------------------------------------------------------------------

;;; Define type checking procedures.

(define-fail-check-type exact-signed-int8 'exact-signed-int8)
(define-fail-check-type exact-signed-int8-list 'exact-signed-int8-list)
(define-fail-check-type exact-unsigned-int8 'exact-unsigned-int8)
(define-fail-check-type exact-unsigned-int8-list 'exact-unsigned-int8-list)
(define-fail-check-type exact-signed-int16 'exact-signed-int16)
(define-fail-check-type exact-signed-int16-list 'exact-signed-int16-list)
(define-fail-check-type exact-unsigned-int16 'exact-unsigned-int16)
(define-fail-check-type exact-unsigned-int16-list 'exact-unsigned-int16-list)
(define-fail-check-type exact-signed-int32 'exact-signed-int32)
(define-fail-check-type exact-signed-int32-list 'exact-signed-int32-list)
(define-fail-check-type exact-unsigned-int32 'exact-unsigned-int32)
(define-fail-check-type exact-unsigned-int32-list 'exact-unsigned-int32-list)
(define-fail-check-type exact-signed-int64 'exact-signed-int64)
(define-fail-check-type exact-signed-int64-list 'exact-signed-int64-list)
(define-fail-check-type exact-unsigned-int64 'exact-unsigned-int64)
(define-fail-check-type exact-unsigned-int64-list 'exact-unsigned-int64-list)
(define-fail-check-type inexact-real 'inexact-real)
(define-fail-check-type inexact-real-list 'inexact-real-list)
(define-fail-check-type number 'number)
(define-fail-check-type real 'real)
(define-fail-check-type finite-real 'finite-real)
(define-fail-check-type rational 'rational)
(define-fail-check-type integer 'integer)
(define-fail-check-type exact-integer 'exact-integer)
(define-fail-check-type fixnum 'fixnum)
(define-fail-check-type flonum 'flonum)

;;;----------------------------------------------------------------------------

;;; Numerical type predicates.

(define-prim (##number? x)
  (##complex? x))

(define-prim (##complex? x)
  (macro-number-dispatch x #f
    #t ;; x = fixnum
    #t ;; x = bignum
    #t ;; x = ratnum
    #t ;; x = flonum
    #t)) ;; x = cpxnum

(define-prim (number? x)
  (macro-force-vars (x)
    (##number? x)))

(define-prim (complex? x)
  (macro-force-vars (x)
    (##complex? x)))

(define-prim (##real? x)
  (macro-number-dispatch x #f
    #t ;; x = fixnum
    #t ;; x = bignum
    #t ;; x = ratnum
    #t ;; x = flonum
    (macro-cpxnum-real? x))) ;; x = cpxnum

(define-prim (real? x)
  (macro-force-vars (x)
    (##real? x)))

(define-prim (##rational? x)
  (macro-number-dispatch x #f
    #t ;; x = fixnum
    #t ;; x = bignum
    #t ;; x = ratnum
    (macro-flonum-rational? x) ;; x = flonum
    (macro-cpxnum-rational? x))) ;; x = cpxnum

(define-prim (rational? x)
  (macro-force-vars (x)
    (##rational? x)))

(define-prim (##integer? x)
  (macro-number-dispatch x #f
    #t ;; x = fixnum
    #t ;; x = bignum
    #f ;; x = ratnum
    (macro-flonum-int? x) ;; x = flonum
    (macro-cpxnum-int? x))) ;; x = cpxnum

(define-prim (integer? x)
  (macro-force-vars (x)
    (##integer? x)))

(define-prim (##exact-integer? x)
  (macro-exact-int? x))

(define-prim (exact-integer? x)
  (macro-force-vars (x)
    (macro-exact-int? x)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; Exactness predicates.

(define-prim (##exact? x)

  (define (type-error) #f)

  (macro-number-dispatch x (type-error)
    #t ;; x = fixnum
    #t ;; x = bignum
    #t ;; x = ratnum
    #f ;; x = flonum
    (and (##not (##flonum? (macro-cpxnum-real x))) ;; x = cpxnum
         (##not (##flonum? (macro-cpxnum-imag x))))))

(define-prim (exact? x)
  (macro-force-vars (x)
    (let ()

      (define (type-error)
        (##fail-check-number 1 exact? x))

      (macro-number-dispatch x (type-error)
        #t ;; x = fixnum
        #t ;; x = bignum
        #t ;; x = ratnum
        #f ;; x = flonum
        (and (##not (##flonum? (macro-cpxnum-real x))) ;; x = cpxnum
             (##not (##flonum? (macro-cpxnum-imag x))))))))

(define-prim (##inexact? x)

  (define (type-error) #f)

  (macro-number-dispatch x (type-error)
    #f ;; x = fixnum
    #f ;; x = bignum
    #f ;; x = ratnum
    #t ;; x = flonum
    (or (##flonum? (macro-cpxnum-real x)) ;; x = cpxnum
        (##flonum? (macro-cpxnum-imag x)))))

(define-prim (inexact? x)
  (macro-force-vars (x)
    (let ()

      (define (type-error)
        (##fail-check-number 1 inexact? x))

      (macro-number-dispatch x (type-error)
        #f ;; x = fixnum
        #f ;; x = bignum
        #f ;; x = ratnum
        #t ;; x = flonum
        (or (##flonum? (macro-cpxnum-real x)) ;; x = cpxnum
            (##flonum? (macro-cpxnum-imag x)))))))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; Comparison predicates.

(define-prim (##= x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (macro-number-dispatch x (type-error-on-x)

    (macro-number-dispatch y (type-error-on-y) ;; x = fixnum
      (##fx= x y)
      #f
      #f
      (macro-if-ratnum
       (if (##fixnum->flonum-exact? x)
           (##fl= (##fixnum->flonum x) y)
           (and (##flfinite? y)
                (##ratnum.= (macro-exact-int->ratnum x) (##flonum->ratnum y))))
       (##fl= (##fixnum->flonum x) y))
      (##cpxnum.= (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = bignum
      #f
      (or (##eq? x y)
          (##exact-int.= x y))
      #f
      (macro-if-ratnum
       (and (##flfinite? y)
            (##ratnum.= (macro-exact-int->ratnum x) (##flonum->ratnum y)))
       (##fl= (##exact-int->flonum x) y))
      (##cpxnum.= (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = ratnum
      #f
      #f
      (or (##eq? x y)
          (##ratnum.= x y))
      (and (##flfinite? y)
           (##ratnum.= x (##flonum->ratnum y)))
      (##cpxnum.= (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = flonum
      (macro-if-ratnum
       (if (##fixnum->flonum-exact? y)
           (##fl= x (##fixnum->flonum y))
           (and (##flfinite? x)
                (##ratnum.= (##flonum->ratnum x) (macro-exact-int->ratnum y))))
       (##fl= x (##fixnum->flonum y)))
      (macro-if-ratnum
       (and (##flfinite? x)
            (##ratnum.= (##flonum->ratnum x) (macro-exact-int->ratnum y)))
       (##fl= x (##exact-int->flonum y)))
      (and (##flfinite? x)
           (##ratnum.= (##flonum->ratnum x) y))
      (##fl= x y)
      (##cpxnum.= (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = cpxnum
      (##cpxnum.= x (macro-noncpxnum->cpxnum y))
      (##cpxnum.= x (macro-noncpxnum->cpxnum y))
      (##cpxnum.= x (macro-noncpxnum->cpxnum y))
      (##cpxnum.= x (macro-noncpxnum->cpxnum y))
      (##cpxnum.= x y))))

(define-prim-nary-bool (= x y)
  #t
  (if (##number? x) #t '(1))
  (##= x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-number))

(define-prim (##< x y #!optional (nan-result #f))

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (macro-number-dispatch x (type-error-on-x)

    (macro-number-dispatch y (type-error-on-y) ;; x = fixnum
      (##fx< x y)
      (##not (##bignum.negative? y))
      (##ratnum.< (macro-exact-int->ratnum x) y)
      (cond ((##flfinite? y)
             (macro-if-ratnum
              (if (##fixnum->flonum-exact? x)
                  (##fl< (##fixnum->flonum x) y)
                  (##ratnum.< (macro-exact-int->ratnum x) (##flonum->ratnum y)))
              (##fl< (##fixnum->flonum x) y)))
            ((##flnan? y)
             nan-result)
            (else
             (##flpositive? y)))
      (if (macro-cpxnum-real? y)
          (##< x (macro-cpxnum-real y) nan-result)
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = bignum
      (##bignum.negative? x)
      (##exact-int.< x y)
      (##ratnum.< (macro-exact-int->ratnum x) y)
      (cond ((##flfinite? y)
             (macro-if-ratnum
              (##ratnum.< (macro-exact-int->ratnum x) (##flonum->ratnum y))
              (##fl< (##exact-int->flonum x) y)))
            ((##flnan? y)
             nan-result)
            (else
             (##flpositive? y)))
      (if (macro-cpxnum-real? y)
          (##< x (macro-cpxnum-real y) nan-result)
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = ratnum
      (##ratnum.< x (macro-exact-int->ratnum y))
      (##ratnum.< x (macro-exact-int->ratnum y))
      (##ratnum.< x y)
      (cond ((##flfinite? y)
             (##ratnum.< x (##flonum->ratnum y)))
            ((##flnan? y)
             nan-result)
            (else
             (##flpositive? y)))
      (if (macro-cpxnum-real? y)
          (##< x (macro-cpxnum-real y) nan-result)
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = flonum
      (cond ((##flfinite? x)
             (macro-if-ratnum
              (if (##fixnum->flonum-exact? y)
                  (##fl< x (##fixnum->flonum y))
                  (##ratnum.< (##flonum->ratnum x) (macro-exact-int->ratnum y)))
              (##fl< x (##fixnum->flonum y))))
            ((##flnan? x)
             nan-result)
            (else
             (##flnegative? x)))
      (cond ((##flfinite? x)
             (macro-if-ratnum
              (##ratnum.< (##flonum->ratnum x) (macro-exact-int->ratnum y))
              (##fl< x (##exact-int->flonum y))))
            ((##flnan? x)
             nan-result)
            (else
             (##flnegative? x)))
      (cond ((##flfinite? x)
             (##ratnum.< (##flonum->ratnum x) y))
            ((##flnan? x)
             nan-result)
            (else
             (##flnegative? x)))
      (if (or (##flnan? x) (##flnan? y))
          nan-result
          (##fl< x y))
      (if (macro-cpxnum-real? y)
          (##< x (macro-cpxnum-real y) nan-result)
          (type-error-on-y)))

    (if (macro-cpxnum-real? x) ;; x = cpxnum
        (macro-number-dispatch y (type-error-on-y)
          (##< (macro-cpxnum-real x) y nan-result)
          (##< (macro-cpxnum-real x) y nan-result)
          (##< (macro-cpxnum-real x) y nan-result)
          (##< (macro-cpxnum-real x) y nan-result)
          (if (macro-cpxnum-real? y)
              (##< (macro-cpxnum-real x) (macro-cpxnum-real y) nan-result)
              (type-error-on-y)))
        (type-error-on-x))))

(define-prim-nary-bool (< x y)
  #t
  (if (##real? x) #t '(1))
  (##< x y #f)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-real))

(define-prim-nary-bool (> x y)
  #t
  (if (##real? x) #t '(1))
  (##< y x #f)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-real))

(define-prim-nary-bool (<= x y)
  #t
  (if (##real? x) #t '(1))
  (##not (##< y x #t))
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-real))

(define-prim-nary-bool (>= x y)
  #t
  (if (##real? x) #t '(1))
  (##not (##< x y #t))
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-real))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; Numerical property predicates.

(define-prim (##zero? x)

  (define (type-error)
    (##fail-check-number 1 zero? x))

  (macro-number-dispatch x (type-error)
    (##fxzero? x)
    #f
    #f
    (##flzero? x)
    (and (let ((imag (macro-cpxnum-imag x)))
           (and (##flonum? imag) (##flzero? imag)))
         (let ((real (macro-cpxnum-real x)))
           (if (##flonum? real)
               (##flzero? real)
               (##eqv? real 0))))))

(define-prim (zero? x)
  (macro-force-vars (x)
    (##zero? x)))

(define-prim (##positive? x)

  (define (type-error)
    (##fail-check-real 1 positive? x))

  (macro-number-dispatch x (type-error)
    (##fxpositive? x)
    (##not (##bignum.negative? x))
    (##positive? (macro-ratnum-numerator x))
    (##flpositive? x)
    (if (macro-cpxnum-real? x)
        (##positive? (macro-cpxnum-real x))
        (type-error))))

(define-prim (positive? x)
  (macro-force-vars (x)
    (##positive? x)))

(define-prim (##negative? x)

  (define (type-error)
    (##fail-check-real 1 negative? x))

  (macro-number-dispatch x (type-error)
    (##fxnegative? x)
    (##bignum.negative? x)
    (##negative? (macro-ratnum-numerator x))
    (##flnegative? x)
    (if (macro-cpxnum-real? x)
        (##negative? (macro-cpxnum-real x))
        (type-error))))

(define-prim (negative? x)
  (macro-force-vars (x)
    (##negative? x)))

(define-prim (##odd? x)

  (define (type-error)
    (##fail-check-integer 1 odd? x))

  (macro-number-dispatch x (type-error)
    (##fxodd? x)
    (macro-bignum-odd? x)
    (type-error)
    (if (macro-flonum-int? x)
        (##odd? (##flonum->exact-int x))
        (type-error))
    (if (macro-cpxnum-int? x)
        (##odd? (##inexact->exact (macro-cpxnum-real x)))
        (type-error))))

(define-prim (odd? x)
  (macro-force-vars (x)
    (##odd? x)))

(define-prim (##even? x)

  (define (type-error)
    (##fail-check-integer 1 even? x))

  (macro-number-dispatch x (type-error)
    (##not (##fxodd? x))
    (##not (macro-bignum-odd? x))
    (type-error)
    (if (macro-flonum-int? x)
        (##even? (##flonum->exact-int x))
        (type-error))
    (if (macro-cpxnum-int? x)
        (##even? (##inexact->exact (macro-cpxnum-real x)))
        (type-error))))

(define-prim (even? x)
  (macro-force-vars (x)
    (##even? x)))

(define-prim (##finite? x)

  (define (type-error)
    (##fail-check-number 1 finite? x))

  (macro-number-dispatch x (type-error)
    #t
    #t
    #t
    (##flfinite? x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (and (or (##not (##flonum? real))
               (##flfinite? real))
           (or (##not (##flonum? imag))
               (##flfinite? imag))))))

(define-prim (finite? x)
  (macro-force-vars (x)
    (##finite? x)))

(define-prim (##infinite? x)

  (define (type-error)
    (##fail-check-number 1 infinite? x))

  (macro-number-dispatch x (type-error)
    #f
    #f
    #f
    (##flinfinite? x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (##flonum? real)
          (if (##flnan? real)
              #f
              (if (##flinfinite? real)
                  (##not (and (##flonum? imag)
                              (##flnan? imag)))
                  (and (##flonum? imag)
                       (##flinfinite? imag))))
          (and (##flonum? imag)
               (##flinfinite? imag))))))

(define-prim (infinite? x)
  (macro-force-vars (x)
    (##infinite? x)))

(define-prim (##nan? x)

  (define (type-error)
    (##fail-check-number 1 nan? x))

  (macro-number-dispatch x (type-error)
    #f
    #f
    #f
    (##flnan? x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (or (and (##flonum? real)
               (##flnan? real))
          (and (##flonum? imag)
               (##flnan? imag))))))

(define-prim (nan? x)
  (macro-force-vars (x)
    (##nan? x)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; Max and min.

(define-prim (##max x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (macro-number-dispatch x (type-error-on-x)

    (macro-number-dispatch y (type-error-on-y) ;; x = fixnum
      (##fxmax x y)
      (if (##< x y) y x)
      (if (##< x y) y x)
      (##flmax (##fixnum->flonum x) y)
      (if (macro-cpxnum-real? y)
          (##max x (macro-cpxnum-real y))
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = bignum
      (if (##< x y) y x)
      (if (##< x y) y x)
      (if (##< x y) y x)
      (##flmax (##exact-int->flonum x) y)
      (if (macro-cpxnum-real? y)
          (##max x (macro-cpxnum-real y))
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = ratnum
      (if (##< x y) y x)
      (if (##< x y) y x)
      (if (##< x y) y x)
      (##flmax (##ratnum->flonum x) y)
      (if (macro-cpxnum-real? y)
          (##max x (macro-cpxnum-real y))
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = flonum
      (##flmax x (##fixnum->flonum y))
      (##flmax x (##exact-int->flonum y))
      (##flmax x (##ratnum->flonum y))
      (##flmax x y)
      (if (macro-cpxnum-real? y)
          (##max x (macro-cpxnum-real y))
          (type-error-on-y)))

    (if (macro-cpxnum-real? x) ;; x = cpxnum
        (macro-number-dispatch y (type-error-on-y)
          (##max (macro-cpxnum-real x) y)
          (##max (macro-cpxnum-real x) y)
          (##max (macro-cpxnum-real x) y)
          (##max (macro-cpxnum-real x) y)
          (if (macro-cpxnum-real? y)
              (##max (macro-cpxnum-real x) (macro-cpxnum-real y))
              (type-error-on-y)))
        (type-error-on-x))))

(define-prim-nary (max x y)
  ()
  (if (##real? x) x '(1))
  (##max x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-real))

(define-prim (##min x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (macro-number-dispatch x (type-error-on-x)

    (macro-number-dispatch y (type-error-on-y) ;; x = fixnum
      (##fxmin x y)
      (if (##< x y) x y)
      (if (##< x y) x y)
      (##flmin (##fixnum->flonum x) y)
      (if (macro-cpxnum-real? y)
          (##min x (macro-cpxnum-real y))
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = bignum
      (if (##< x y) x y)
      (if (##< x y) x y)
      (if (##< x y) x y)
      (##flmin (##exact-int->flonum x) y)
      (if (macro-cpxnum-real? y)
          (##min x (macro-cpxnum-real y))
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = ratnum
      (if (##< x y) x y)
      (if (##< x y) x y)
      (if (##< x y) x y)
      (##flmin (##ratnum->flonum x) y)
      (if (macro-cpxnum-real? y)
          (##min x (macro-cpxnum-real y))
          (type-error-on-y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = flonum
      (##flmin x (##fixnum->flonum y))
      (##flmin x (##exact-int->flonum y))
      (##flmin x (##ratnum->flonum y))
      (##flmin x y)
      (if (macro-cpxnum-real? y)
          (##min x (macro-cpxnum-real y))
          (type-error-on-y)))

    (if (macro-cpxnum-real? x) ;; x = cpxnum
        (macro-number-dispatch y (type-error-on-y)
          (##min (macro-cpxnum-real x) y)
          (##min (macro-cpxnum-real x) y)
          (##min (macro-cpxnum-real x) y)
          (##min (macro-cpxnum-real x) y)
          (if (macro-cpxnum-real? y)
              (##min (macro-cpxnum-real x) (macro-cpxnum-real y))
              (type-error-on-y)))
        (type-error-on-x))))

(define-prim-nary (min x y)
  ()
  (if (##real? x) x '(1))
  (##min x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-real))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; +, *, -, /

(define-prim (##+ x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))
  (##define-macro (fixnum-overflow) `#f)

  (macro-number-dispatch x (type-error-on-x)

    (macro-number-dispatch y (type-error-on-y) ;; x = fixnum
      (or (##fx+? x y)
          (macro-if-bignum
           (##bignum.+ (##fixnum->bignum x) (##fixnum->bignum y))
           (fixnum-overflow)))
      (if (##fxzero? x)
          y
          (##bignum.+ (##fixnum->bignum x) y))
      (if (##fxzero? x)
          y
          (##ratnum.+ (macro-exact-int->ratnum x) y))
      (if (and (macro-special-case-exact-zero?) (##fxzero? x))
          y
          (##fl+ (##fixnum->flonum x) y))
      (if (and (macro-special-case-exact-zero?) (##fxzero? x))
          y
          (##cpxnum.+ (macro-noncpxnum->cpxnum x) y)))

    (macro-number-dispatch y (type-error-on-y) ;; x = bignum
      (if (##fxzero? y)
          x
          (##bignum.+ x (##fixnum->bignum y)))
      (##bignum.+ x y)
      (##ratnum.+ (macro-exact-int->ratnum x) y)
      (##fl+ (##exact-int->flonum x) y)
      (##cpxnum.+ (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = ratnum
      (if (##fxzero? y)
          x
          (##ratnum.+ x (macro-exact-int->ratnum y)))
      (##ratnum.+ x (macro-exact-int->ratnum y))
      (##ratnum.+ x y)
      (##fl+ (##ratnum->flonum x) y)
      (##cpxnum.+ (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = flonum
      (if (and (macro-special-case-exact-zero?) (##fxzero? y))
          x
          (##fl+ x (##fixnum->flonum y)))
      (##fl+ x (##exact-int->flonum y))
      (##fl+ x (##ratnum->flonum y))
      (##fl+ x y)
      (##cpxnum.+ (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = cpxnum
      (if (and (macro-special-case-exact-zero?) (##fxzero? y))
          x
          (##cpxnum.+ x (macro-noncpxnum->cpxnum y)))
      (##cpxnum.+ x (macro-noncpxnum->cpxnum y))
      (##cpxnum.+ x (macro-noncpxnum->cpxnum y))
      (##cpxnum.+ x (macro-noncpxnum->cpxnum y))
      (##cpxnum.+ x y))))

(define-prim-nary (+ x y)
  0
  (if (##number? x) x '(1))
  (##+ x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-number)
  (##not ##raise-fixnum-overflow-exception))

(define-prim (##* x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))
  (##define-macro (fixnum-overflow) `#f)

  (macro-number-dispatch x (type-error-on-x)

    (macro-number-dispatch y (type-error-on-y) ;; x = fixnum
      (cond ((##fx= y 0)
             0)
            ((if (##fx= y -1)
                 (##fx-? x)
                 (##fx*? x y))
             => (lambda (result) result))
            (else
             (macro-if-bignum
              (##bignum.* (##fixnum->bignum x) (##fixnum->bignum y))
              (fixnum-overflow))))
      (cond ((##fxzero? x)
             0)
            ((##fx= x 1)
             y)
            ((##fx= x -1)
             (##negate y))
            (else
             (##bignum.* (##fixnum->bignum x) y)))
      (cond ((##fxzero? x)
             0)
            ((##fx= x 1)
             y)
            ((##fx= x -1)
             (##negate y))
            (else
             (##ratnum.* (macro-exact-int->ratnum x) y)))
      (cond ((and (macro-special-case-exact-zero?)
                  (##fxzero? x))
             0)
            ((##fx= x 1)
             y)
            (else
             (##fl* (##fixnum->flonum x) y)))
      (cond ((and (macro-special-case-exact-zero?)
                  (##fxzero? x))
             0)
            ((##fx= x 1)
             y)
            (else
             (##cpxnum.* (macro-noncpxnum->cpxnum x) y))))

    (macro-number-dispatch y (type-error-on-y) ;; x = bignum
      (cond ((##fx= y 0)
             0)
            ((##fx= y 1)
             x)
            ((##fx= y -1)
             (##negate x))
            (else
             (##bignum.* x (##fixnum->bignum y))))
      (##bignum.* x y)
      (##ratnum.* (macro-exact-int->ratnum x) y)
      (##fl* (##exact-int->flonum x) y)
      (##cpxnum.* (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = ratnum
      (cond ((##fxzero? y)
             0)
            ((##fx= y 1)
             x)
            ((##fx= y -1)
             (##negate x))
            (else
             (##ratnum.* x (macro-exact-int->ratnum y))))
      (##ratnum.* x (macro-exact-int->ratnum y))
      (##ratnum.* x y)
      (##fl* (##ratnum->flonum x) y)
      (##cpxnum.* (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = flonum
      (cond ((and (macro-special-case-exact-zero?) (##fxzero? y))
             0)
            ((##fx= y 1)
             x)
            (else
             (##fl* x (##fixnum->flonum y))))
      (##fl* x (##exact-int->flonum y))
      (##fl* x (##ratnum->flonum y))
      (##fl* x y)
      (##cpxnum.* (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = cpxnum
      (cond ((and (macro-special-case-exact-zero?) (##fxzero? y))
             0)
            ((##fx= y 1)
             x)
            (else
             (##cpxnum.* x (macro-noncpxnum->cpxnum y))))
      (##cpxnum.* x (macro-noncpxnum->cpxnum y))
      (##cpxnum.* x (macro-noncpxnum->cpxnum y))
      (##cpxnum.* x (macro-noncpxnum->cpxnum y))
      (##cpxnum.* x y))))

(define-prim-nary (* x y)
  1
  (if (##number? x) x '(1))
  (##* x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-number)
  (##not ##raise-fixnum-overflow-exception))

(define-prim (##square x)

  (define (type-error)
    (##fail-check-number 1 square x))

  (define (fixnum-overflow)
    (##raise-fixnum-overflow-exception square x))

  (macro-number-dispatch x (type-error)
    (or (##fxsquare? x)
        (macro-if-bignum
         (let ((x (##fixnum->bignum x)))
           (##bignum.* x x))
         (fixnum-overflow)))
    (##bignum.* x x)
    (##ratnum.* x x)
    (##fl* x x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (or (##eqv? real 0)
              (##exact? x))
          (##make-rectangular (##* (##- real imag) (##+ real imag))
                              (##* 2 (##* real imag)))
          (##csquare (##exact->inexact x))))))

(define-prim (square x)
  (macro-force-vars (x)
    (##square x)))

(define-prim (##negate x)

  (##define-macro (type-error) `'(1))
  (##define-macro (fixnum-overflow) `#f)

  (macro-number-dispatch x (type-error)
    (or (##fx-? x)
        (macro-if-bignum
         (##bignum.- (##fixnum->bignum 0) (##fixnum->bignum ##min-fixnum))
         (fixnum-overflow)))
    (##bignum.- (##fixnum->bignum 0) x)
    (macro-ratnum-make (##negate (macro-ratnum-numerator x))
                       (macro-ratnum-denominator x))
    (##fl- x)
    (##make-rectangular (##negate (macro-cpxnum-real x))
                        (##negate (macro-cpxnum-imag x)))))

(define-prim (##- x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))
  (##define-macro (fixnum-overflow) `#f)

  (macro-number-dispatch x (type-error-on-x)

    (macro-number-dispatch y (type-error-on-y) ;; x = fixnum
      (or (##fx-? x y)
          (macro-if-bignum
           (##bignum.- (##fixnum->bignum x) (##fixnum->bignum y))
           (fixnum-overflow)))
      (##bignum.- (##fixnum->bignum x) y)
      (if (##fxzero? x)
          (##negate y)
          (##ratnum.- (macro-exact-int->ratnum x) y))
      (if (and (macro-special-case-exact-zero?) (##fxzero? x))
          (##fl- y)
          (##fl- (##fixnum->flonum x) y))
      (##cpxnum.- (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = bignum
      (if (##fxzero? y)
          x
          (##bignum.- x (##fixnum->bignum y)))
      (##bignum.- x y)
      (##ratnum.- (macro-exact-int->ratnum x) y)
      (##fl- (##exact-int->flonum x) y)
      (##cpxnum.- (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = ratnum
      (if (##fxzero? y)
          x
          (##ratnum.- x (macro-exact-int->ratnum y)))
      (##ratnum.- x (macro-exact-int->ratnum y))
      (##ratnum.- x y)
      (##fl- (##ratnum->flonum x) y)
      (##cpxnum.- (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = flonum
      (if (and (macro-special-case-exact-zero?) (##fxzero? y))
          x
          (##fl- x (##fixnum->flonum y)))
      (##fl- x (##exact-int->flonum y))
      (##fl- x (##ratnum->flonum y))
      (##fl- x y)
      (##cpxnum.- (macro-noncpxnum->cpxnum x) y))

    (macro-number-dispatch y (type-error-on-y) ;; x = cpxnum
      (if (and (macro-special-case-exact-zero?) (##fxzero? y))
          x
          (##cpxnum.- x (macro-noncpxnum->cpxnum y)))
      (##cpxnum.- x (macro-noncpxnum->cpxnum y))
      (##cpxnum.- x (macro-noncpxnum->cpxnum y))
      (##cpxnum.- x (macro-noncpxnum->cpxnum y))
      (##cpxnum.- x y))))

(define-prim-nary (- x y)
  ()
  (##negate x)
  (##- x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-number)
  (##not ##raise-fixnum-overflow-exception))

(define-prim (##inverse x)

  (##define-macro (type-error) `'(1))

  (define (divide-by-zero-error) #f)

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        (divide-by-zero-error)
        (macro-if-ratnum
         (if (##fxnegative? x)
             (if (##fx= x -1)
                 x
                 (macro-ratnum-make -1 (##negate x)))
             (if (##fx= x 1)
                 x
                 (macro-ratnum-make 1 x)))
         (##fl/ (macro-inexact-+1) (##fixnum->flonum x))))
    (if (##bignum.negative? x)
        (macro-ratnum-make -1 (##negate x))
        (macro-ratnum-make 1 x))
    (let ((num (macro-ratnum-numerator x))
          (den (macro-ratnum-denominator x)))
      (cond ((##fx= num 1)
             den)
            ((##fx= num -1)
             (##negate den))
            (else
             (if (##negative? num)
                 (macro-ratnum-make (##negate den) (##negate num))
                 (macro-ratnum-make den num)))))
    (##fl/ (macro-inexact-+1) x)
    (##cpxnum./ (macro-noncpxnum->cpxnum 1) x)))

(##define-macro (macro-make-qr q r) `(##values ,q ,r))
(##define-macro (macro-qr-q qr)     `(##values-ref ,qr 0))
(##define-macro (macro-qr-r qr)     `(##values-ref ,qr 1))

(define-prim (##/ x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (define (divide-by-zero-error) #f)

  (define (divide-exact-ints)
    (macro-if-ratnum
     (##ratnum.normalize x y)
     (let ((qr (##exact-int.div x y)))
       (if (##eqv? (macro-qr-r qr) 0)
           (macro-qr-q qr)
           (##fl/ (##exact-int->flonum x) (##exact-int->flonum y))))))

  (macro-number-dispatch y (type-error-on-y)

    (macro-number-dispatch x (type-error-on-x) ;; y = fixnum
      (cond ((##fxzero? y)
             (divide-by-zero-error))
            ((##fx= y 1)
             x)
            ((##fx= y -1)
             (##negate x))
            ((##fxzero? x)
             0)
            ((##fx= x 1)
             (##inverse y))
            (else
             (divide-exact-ints)))
      (cond ((##fxzero? y)
             (divide-by-zero-error))
            ((##fx= y 1)
             x)
            ((##fx= y -1)
             (##negate x))
            (else
             (divide-exact-ints)))
      (cond ((##fxzero? y)
             (divide-by-zero-error))
            ((##fx= y 1)
             x)
            ((##fx= y -1)
             (##negate x))
            (else
             (##ratnum./ x (macro-exact-int->ratnum y))))
      (cond ((##fxzero? y)
             (divide-by-zero-error))
            ((##fx= y 1)
             x)
            (else
             (##fl/ x (##fixnum->flonum y))))
      (cond ((##fxzero? y)
             (divide-by-zero-error))
            ((##fx= y 1)
             x)
            (else
             (##cpxnum./ x (macro-noncpxnum->cpxnum y)))))

    (macro-number-dispatch x (type-error-on-x) ;; y = bignum
      (cond ((##fxzero? x)
             0)
            ((##fx= x 1)
             (##inverse y))
            (else
             (divide-exact-ints)))
      (divide-exact-ints)
      (##ratnum./ x (macro-exact-int->ratnum y))
      (##fl/ x (##exact-int->flonum y))
      (##cpxnum./ x (macro-noncpxnum->cpxnum y)))

    (macro-number-dispatch x (type-error-on-x) ;; y = ratnum
      (cond ((##fxzero? x)
             0)
            ((##fx= x 1)
             (##inverse y))
            (else
             (##ratnum./ (macro-exact-int->ratnum x) y)))
      (##ratnum./ (macro-exact-int->ratnum x) y)
      (##ratnum./ x y)
      (##fl/ x (##ratnum->flonum y))
      (##cpxnum./ x (macro-noncpxnum->cpxnum y)))

    (macro-number-dispatch x (type-error-on-x) ;; y = flonum, no error possible
      (##fl/ (##fixnum->flonum x) y)
      (##fl/ (##exact-int->flonum x) y)
      (##fl/ (##ratnum->flonum x) y)
      (##fl/ x y)
      (##cpxnum./ x (macro-noncpxnum->cpxnum y)))

    (macro-number-dispatch x (type-error-on-x) ;; y = cpxnum
      (##cpxnum./ (macro-noncpxnum->cpxnum x) y)
      (##cpxnum./ (macro-noncpxnum->cpxnum x) y)
      (##cpxnum./ (macro-noncpxnum->cpxnum x) y)
      (##cpxnum./ (macro-noncpxnum->cpxnum x) y)
      (##cpxnum./ x y))))

(define-prim-nary (/ x y)
  ()
  (##inverse x)
  (##/ x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-number)
  (##not ##raise-divide-by-zero-exception))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; abs

(define-prim (##exact-int.negative? x)
  (macro-if-bignum
   (if (##fixnum? x)
       (##fxnegative? x)
       (##bignum.negative? x))
   (##fxnegative? x)))

(define-prim (##abs x)

  (define (type-error)
    (##fail-check-real 1 abs x))

  (macro-number-dispatch x (type-error)
    (if (##fxnegative? x) (##negate x) x)
    (if (##bignum.negative? x) (##negate x) x)
    (if (##exact-int.negative? (macro-ratnum-numerator x))
        (macro-ratnum-make (##negate (macro-ratnum-numerator x))
                           (macro-ratnum-denominator x))
        x)
    (##flabs x)
    (if (macro-cpxnum-real? x)
        (##make-rectangular (##abs (macro-cpxnum-real x))
                            (##abs (macro-cpxnum-imag x)))
        (type-error))))

(define-prim (abs x)
  (macro-force-vars (x)
    (##abs x)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; quotient, remainder, modulo

(define-prim (##divide x y kind)

  ;; kind = 0  : floor-remainder
  ;; kind = 1  : floor-quotient
  ;; kind = 2  : floor/
  ;; kind = 4  : truncate-remainder
  ;; kind = 5  : truncate-quotient
  ;; kind = 6  : truncate/
  ;; kind = 8  : modulo
  ;; kind = 12 : remainder
  ;; kind = 13 : quotient

  (define (prim)
    (cond ((##fx= kind 0)  floor-remainder)
          ((##fx= kind 1)  floor-quotient)
          ((##fx= kind 2)  floor/)
          ((##fx= kind 4)  truncate-remainder)
          ((##fx= kind 5)  truncate-quotient)
          ((##fx= kind 6)  truncate/)
          ((##fx= kind 12) remainder)
          ((##fx= kind 13) quotient)
          (else            modulo))) ;; kind = 8

  (define (type-error-on-x)
    (##fail-check-integer 1 (prim) x y))

  (define (type-error-on-y)
    (##fail-check-integer 2 (prim) x y))

  (define (divide-by-zero-error)
    (##raise-divide-by-zero-exception (prim) x y))

  (define (fixnum-fixnum-case x y)
    (if (##fx= y -1) ;; needed because (quotient ##min-fixnum -1) is a bignum

        (let ((k (##fxand kind 3)))
          (if (##fx= k 0)
              0 ;; floor-remainder and truncate-remainder
              (if (##fx= k 1)
                  (##negate x) ;; floor-quotient and truncate-quotient
                  (##values (##negate x) 0)))) ;; floor/ and truncate/

        (if (##fx= (##fxand kind 4) 0)

            ;; floor variants
            (let ((k (##fxand kind 3)))
              (if (##fx= k 0)
                  (##fxmodulo x y) ;; floor-remainder
                  (let* ((q (##fxquotient x y))
                         (m (##fxmodulo x y)))
                    (if (or (##eqv? m 0)
                            (##eq? (##fxnegative? x) (##fxnegative? y)))
                        (if (##fx= k 1)
                            q ;; floor-quotient
                            (##values q ;; floor/
                                      m))
                        (if (##fx= k 1)
                            (##fx- q 1) ;; floor-quotient
                            (##values (##fx- q 1) ;; floor/
                                      m))))))

            ;; truncate variants
            (let ((k (##fxand kind 3)))
              (if (##fx= k 0)
                  (##fxremainder x y) ;; truncate-remainder
                  (if (##fx= k 1)
                      (##fxquotient x y) ;; truncate-quotient
                      (##values (##fxquotient x y) ;; truncate/
                                (##fxremainder x y))))))))

  (define (exact-case x y convert-to-inexact?)

    (define (conv n)
      (if convert-to-inexact? (##exact->inexact n) n))

    (let* ((qr (##exact-int.div x
                                y
                                (##fx> (##fxand kind 3) 0) ;; need-quotient?
                                #t                         ;; keep-dividend?
                                ))
           (q (macro-qr-q qr))
           (r (macro-qr-r qr)))

      (if (and (##fx= (##fxand kind 4) 0)
               (##not (##eqv? r 0))
               (##not (##eq? (##negative? x) (##negative? y))))

          ;; floor variants
          (let ((k (##fxand kind 3)))
            (if (##fx= k 0)
                (conv (##+ r y)) ;; floor-remainder
                (if (##fx= k 1)
                    (conv (##- q 1)) ;; floor-quotient
                    (##values (conv (##- q 1)) ;; floor/
                              (conv (##+ r y))))))

          ;; truncate variants
          (let ((k (##fxand kind 3)))
            (if (##fx= k 0)
                (conv r) ;; truncate-remainder
                (if (##fx= k 1)
                    (conv q) ;; truncate-quotient
                    (##values (conv q) ;; truncate/
                              (conv r))))))))

  (define (inexact-case x y)
    (let ((exact-y (##inexact->exact y)))
      (if (##eqv? exact-y 0)
          (divide-by-zero-error)
          (exact-case (##inexact->exact x) exact-y #t))))

  (macro-number-dispatch y (type-error-on-y)

    (macro-number-dispatch x (type-error-on-x) ;; y = fixnum
      (cond ((##fx= y 0)
             (divide-by-zero-error))
            (else
             (fixnum-fixnum-case x y)))
      (cond ((##fx= y 0)
             (divide-by-zero-error))
            (else
             (exact-case x y #f)))
      (type-error-on-x)
      (if (macro-flonum-int? x)
          (inexact-case x y)
          (type-error-on-x))
      (if (macro-cpxnum-int? x)
          (inexact-case x y)
          (type-error-on-x)))

    (macro-number-dispatch x (type-error-on-x) ;; y = bignum
      (exact-case x y #f)
      (exact-case x y #f)
      (type-error-on-x)
      (if (macro-flonum-int? x)
          (inexact-case x y)
          (type-error-on-x))
      (if (macro-cpxnum-int? x)
          (inexact-case x y)
          (type-error-on-x)))

    (type-error-on-y) ;; y = ratnum

    (macro-number-dispatch x (type-error-on-x) ;; y = flonum
      (if (macro-flonum-int? y)
          (inexact-case x y)
          (type-error-on-y))
      (if (macro-flonum-int? y)
          (inexact-case x y)
          (type-error-on-y))
      (type-error-on-x)
      (if (macro-flonum-int? x)
          (if (macro-flonum-int? y)
              (inexact-case x y)
              (type-error-on-y))
          (type-error-on-x))
      (if (macro-cpxnum-int? x)
          (if (macro-flonum-int? y)
              (inexact-case x y)
              (type-error-on-y))
          (type-error-on-x)))

    (if (macro-cpxnum-int? y) ;; y = cpxnum
        (macro-number-dispatch x (type-error-on-x)
          (inexact-case x y)
          (inexact-case x y)
          (type-error-on-x)
          (if (macro-flonum-int? x)
              (inexact-case x y)
              (type-error-on-x))
          (if (macro-cpxnum-int? x)
              (inexact-case x y)
              (type-error-on-x)))
        (type-error-on-y))))

(define-prim (floor-remainder x y)
  (macro-force-vars (x y)
    (##divide x y 0)))

(define-prim (floor-quotient x y)
  (macro-force-vars (x y)
    (##divide x y 1)))

(define-prim (floor/ x y)
  (macro-force-vars (x y)
    (##divide x y 2)))

(define-prim (truncate-remainder x y)
  (macro-force-vars (x y)
    (##divide x y 4)))

(define-prim (truncate-quotient x y)
  (macro-force-vars (x y)
    (##divide x y 5)))

(define-prim (truncate/ x y)
  (macro-force-vars (x y)
    (##divide x y 6)))

(define-prim (##remainder x y)
  (##divide x y 12))

(define-prim (remainder x y)
  (macro-force-vars (x y)
    (##divide x y 12)))

(define-prim (##quotient x y)
  (##divide x y 13))

(define-prim (quotient x y)
  (macro-force-vars (x y)
    (##divide x y 13)))

(define-prim (##modulo x y)
  (##divide x y 8))

(define-prim (modulo x y)
  (macro-force-vars (x y)
    (##divide x y 8)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; gcd, lcm

(define-prim (##gcd x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (define (##fast-gcd u v)

    ;; See the paper "Fast Reduction and Composition of Binary
    ;; Quadratic Forms" by Arnold Schoenhage.  His algorithm and proof
    ;; are derived from, and basically the same for, his Controlled
    ;; Euclidean Descent algorithm for gcd, which he has never
    ;; published.  This algorithm has complexity log N times a
    ;; constant times the complexity of a multiplication of the same
    ;; size.  We don't use it until we get to about 6800 bits.  Note
    ;; that this is the same place that we start using FFT
    ;; multiplication and fast division with Newton's method for
    ;; finding inverses.

    ;; Niels Mo"ller has written two papers about an improved version
    ;; of this algorithm.

    ;; assumes u and v are nonnegative exact ints

    (define (make-gcd-matrix A_11 A_12
                             A_21 A_22)
      (##vector A_11 A_12
                A_21 A_22))

    (define (gcd-matrix_11 A)
      (##vector-ref A 0))

    (define (gcd-matrix_12 A)
      (##vector-ref A 1))

    (define (gcd-matrix_21 A)
      (##vector-ref A 2))

    (define (gcd-matrix_22 A)
      (##vector-ref A 3))

    (define (make-gcd-vector v_1 v_2)
      (##vector v_1 v_2))

    (define (gcd-vector_1 v)
      (##vector-ref v 0))

    (define (gcd-vector_2 v)
      (##vector-ref v 1))

    (define gcd-matrix-identity '#(1 0
                                   0 1))

    (define (gcd-matrix-multiply A B)
      (cond ((##eq? A gcd-matrix-identity)
             B)
            ((##eq? B gcd-matrix-identity)
             A)
            (else
             (let ((A_11 (gcd-matrix_11 A)) (A_12 (gcd-matrix_12 A))
                   (A_21 (gcd-matrix_21 A)) (A_22 (gcd-matrix_22 A))
                   (B_11 (gcd-matrix_11 B)) (B_12 (gcd-matrix_12 B))
                   (B_21 (gcd-matrix_21 B)) (B_22 (gcd-matrix_22 B)))
               (make-gcd-matrix (##+ (##* A_11 B_11)
                                     (##* A_12 B_21))
                                (##+ (##* A_11 B_12)
                                     (##* A_12 B_22))
                                (##+ (##* A_21 B_11)
                                     (##* A_22 B_21))
                                (##+ (##* A_21 B_12)
                                     (##* A_22 B_22)))))))

    (define (gcd-matrix-multiply-strassen A B)
      ;; from http://mathworld.wolfram.com/StrassenFormulas.html
      (cond ((##eq? A gcd-matrix-identity)
             B)
            ((##eq? B gcd-matrix-identity)
             A)
            (else
             (let ((A_11 (gcd-matrix_11 A)) (A_12 (gcd-matrix_12 A))
                   (A_21 (gcd-matrix_21 A)) (A_22 (gcd-matrix_22 A))
                   (B_11 (gcd-matrix_11 B)) (B_12 (gcd-matrix_12 B))
                   (B_21 (gcd-matrix_21 B)) (B_22 (gcd-matrix_22 B)))
               (let ((Q_1 (##* (##+ A_11 A_22) (##+ B_11 B_22)))
                     (Q_2 (##* (##+ A_21 A_22) B_11))
                     (Q_3 (##* A_11 (##- B_12 B_22)))
                     (Q_4 (##* A_22 (##- B_21 B_11)))
                     (Q_5 (##* (##+ A_11 A_12) B_22))
                     (Q_6 (##* (##- A_21 A_11) (##+ B_11 B_12)))
                     (Q_7 (##* (##- A_12 A_22) (##+ B_21 B_22))))
                 (make-gcd-matrix (##+ (##+ Q_1 Q_4) (##- Q_7 Q_5))
                                  (##+ Q_3 Q_5)
                                  (##+ Q_2 Q_4)
                                  (##+ (##+ Q_1 Q_3) (##- Q_6 Q_2))))))))

    (define (gcd-matrix-solve A y)
      (let ((y_1 (gcd-vector_1 y))
            (y_2 (gcd-vector_2 y)))
        (make-gcd-vector (##- (##* y_1 (gcd-matrix_22 A))
                              (##* y_2 (gcd-matrix_12 A)))
                         (##- (##* y_2 (gcd-matrix_11 A))
                              (##* y_1 (gcd-matrix_21 A))))))

    (define (x>=2^n x n)
      (##fx< n (##integer-length x)))

    (define (determined-minimal? u v s)
      ;; assumes  2^s <= u , v; s>= 0 fixnum
      ;; returns #t if we can determine that |u-v|<2^s
      ;; at least one of u and v is a bignum
      (let ((u (if (##fixnum? u) (##fixnum->bignum u) u))
            (v (if (##fixnum? v) (##fixnum->bignum v) v)))
        (let ((u-length (##bignum.mdigit-length u)))
          (and (##fx= u-length (##bignum.mdigit-length v))
               (let loop ((i (##fx- u-length 1)))
                 (let ((v-digit (##bignum.mdigit-ref v i))
                       (u-digit (##bignum.mdigit-ref u i)))
                   (if (and (##fxzero? u-digit)
                            (##fxzero? v-digit))
                       (loop (##fx- i 1))
                       (and (##fx= (##fxquotient s ##bignum.mdigit-width)
                                   i)
                            (##fx< (##fxmax (##fx- u-digit v-digit)
                                            (##fx- v-digit u-digit))
                                   (##fxarithmetic-shift-left
                                    1
                                    (##fxremainder s ##bignum.mdigit-width)))))))))))

    (define (gcd-small-step cont M u v s)
      ;;  u, v >= 2^s
      ;; M is the matrix product of the partial sums of
      ;; the continued fraction representation of a/b so far
      ;; returns updated M, u, v, and a truth value
      ;;  u, v >= 2^s and
      ;; if last return value is #t, we know that
      ;; (- (max u v) (min u v)) < 2^s, i.e, u, v are minimal above 2^s

      (define (gcd-matrix-multiply-low M q)
        (let ((M_11 (gcd-matrix_11 M))
              (M_12 (gcd-matrix_12 M))
              (M_21 (gcd-matrix_21 M))
              (M_22 (gcd-matrix_22 M)))
          (make-gcd-matrix (##+ M_11 (##* q M_12))  M_12
                           (##+ M_21 (##* q M_22))  M_22)))

      (define (gcd-matrix-multiply-high M q)
        (let ((M_11 (gcd-matrix_11 M))
              (M_12 (gcd-matrix_12 M))
              (M_21 (gcd-matrix_21 M))
              (M_22 (gcd-matrix_22 M)))
          (make-gcd-matrix M_11  (##+ (##* q M_11) M_12)
                           M_21  (##+ (##* q M_21) M_22))))

      (if (or (##bignum? u)
              (##bignum? v))

          ;; if u and v are nearly equal bignums, the two ##<
          ;; following this condition could take O(N) time to compute.
          ;; When this happens, however, it will be likely that
          ;; determined-minimal? will return true.

          (cond ((determined-minimal? u v s)
                 (cont M
                       u
                       v
                       #t))
                ((##< u v)
                 (let* ((qr (##exact-int.div v u))
                        (q (macro-qr-q qr))
                        (r (macro-qr-r qr)))
                   (cond ((x>=2^n r s)
                          (cont (gcd-matrix-multiply-low M q)
                                u
                                r
                                #f))
                         ((##eqv? q 1)
                          (cont M
                                u
                                v
                                #t))
                         (else
                          (cont (gcd-matrix-multiply-low M (##- q 1))
                                u
                                (##+ r u)
                                #t)))))
                ((##< v u)
                 (let* ((qr (##exact-int.div u v))
                        (q (macro-qr-q qr))
                        (r (macro-qr-r qr)))
                   (cond ((x>=2^n r s)
                          (cont (gcd-matrix-multiply-high M q)
                                r
                                v
                                #f))
                         ((##eqv? q 1)
                          (cont M
                                u
                                v
                                #t))
                         (else
                          (cont (gcd-matrix-multiply-high M (##- q 1))
                                (##+ r v)
                                v
                                #t)))))
                (else
                 (cont M
                       u
                       v
                       #t)))
          ;; here u and v are fixnums, so 2^s, which is <= u and v, is
          ;; also a fixnum
          (let ((two^s (##fxarithmetic-shift-left 1 s)))
            (if (##fx< u v)
                (if (##fx< (##fx- v u) two^s)
                    (cont M
                          u
                          v
                          #t)
                    (let ((r (##fxremainder v u))
                          (q (##fxquotient  v u)))
                      (if (##fx>= r two^s)
                          (cont (gcd-matrix-multiply-low M q)
                                u
                                r
                                #f)
                          ;; the case when q is one and the remainder is < two^s
                          ;; is covered in the first test
                          (cont (gcd-matrix-multiply-low M (##fx- q 1))
                                u
                                (##fx+ r u)
                                #t))))
                ;; here u >= v, but the case u = v is covered by the first test
                (if (##fx< (##fx- u v) two^s)
                    (cont M
                          u
                          v
                          #t)
                    (let ((r (##fxremainder u v))
                          (q (##fxquotient  u v)))
                      (if (##fx>= r two^s)
                          (cont (gcd-matrix-multiply-high M q)
                                r
                                v
                                #f)
                          ;; the case when q is one and the remainder is < two^s
                          ;; is covered in the first test
                          (cont (gcd-matrix-multiply-high M (##fx- q 1))
                                (##fx+ r v)
                                v
                                #t))))))))

    (define (gcd-middle-step cont a b h m-prime cont-needs-M?)
      ((lambda (cont)
         (if (and (x>=2^n a h)
                  (x>=2^n b h))
             (MR cont a b h cont-needs-M?)
             (cont gcd-matrix-identity a b)))
       (lambda (M x y)
         (let loop ((M M)
                    (x x)
                    (y y))
           (if (or (x>=2^n x h)
                   (x>=2^n y h))
               ((lambda (cont) (gcd-small-step cont M x y m-prime))
                (lambda (M x y minimal?)
                  (if minimal?
                      (cont M x y)
                      (loop M x y))))
               ((lambda (cont) (MR cont x y m-prime cont-needs-M?))
                (lambda (M-prime alpha beta)
                  (cont (if cont-needs-M?
                            (if (##fx> (##fx- h m-prime) 1024)
                                ;; here we trade off 1 multiplication
                                ;; for 21 additions
                                (gcd-matrix-multiply-strassen M M-prime)
                                (gcd-matrix-multiply          M M-prime))
                            gcd-matrix-identity)
                        alpha
                        beta))))))))

    (define (MR cont a b m cont-needs-M?)
      ((lambda (cont)
         (if (and (x>=2^n a (##fx+ m 2))
                  (x>=2^n b (##fx+ m 2)))
             (let ((n (##fx- (##fxmax (##integer-length a)
                                      (##integer-length b))
                             m)))
               ((lambda (cont)
                  (if (##fx<= m n)
                      (cont m 0)
                      (cont n (##fx- (##fx+ m 1) n))))
                (lambda (m-prime p)
                  (let ((h (##fx+ m-prime (##fxquotient n 2))))
                    (if (##fx< 0 p)
                        (let ((a   (##arithmetic-shift a (##fx- p)))
                              (b   (##arithmetic-shift b (##fx- p)))
                              (a_0 (##extract-bit-field p 0 a))
                              (b_0 (##extract-bit-field p 0 b)))
                          ((lambda (cont)
                             (gcd-middle-step cont a b h m-prime #t))
                           (lambda (M alpha beta)
                             (let ((M-inverse-v_0 (gcd-matrix-solve M (make-gcd-vector a_0 b_0))))
                               (cont (if cont-needs-M? M gcd-matrix-identity)
                                     (##+ (##arithmetic-shift alpha p)
                                          (gcd-vector_1 M-inverse-v_0))
                                     (##+ (##arithmetic-shift beta p)
                                          (gcd-vector_2 M-inverse-v_0)))))))
                        (gcd-middle-step cont a b h m-prime cont-needs-M?))))))
             (cont gcd-matrix-identity
                   a
                   b)))
       (lambda (M alpha beta)
         (let loop ((M M)
                    (alpha alpha)
                    (beta beta)
                    (minimal? #f))
           (if minimal?
               (cont M alpha beta)
               (gcd-small-step loop M alpha beta m))))))

    ((lambda (cont)
       (if (and (use-fast-bignum-algorithms)
                (##bignum? u)
                (##bignum? v)
                (x>=2^n u ##bignum.fast-gcd-size)
                (x>=2^n v ##bignum.fast-gcd-size))
           (MR cont u v ##bignum.fast-gcd-size #f)
           (cont 0 u v)))
     (lambda (M a b)
       (general-base a b))))

  (define (general-base a b)
    (if (##eqv? b 0)
        a
        (let ((r (macro-qr-r (##exact-int.div a  ;; calculate (remainder a b)
                                              b
                                              #f ;; need-quotient?
                                              #f ;; keep-dividend?
                                              ))))
          (if (##fixnum? b)
              (fixnum-base b r)
              (general-base b r)))))

  (define (fixnum-base a b)
    (##declare (not interrupts-enabled))
    (if (##eqv? b 0)
        a
        (let ((a b)
              (b (##fxremainder a b)))
          (if (##eqv? b 0)
              a
              (fixnum-base b (##fxremainder a b))))))

  (define (exact-gcd x y)
    ;; always returns an exact result, even with inexact arguments.
    (let ((x (cond ((##inexact? x)
                    (##inexact->exact (##flabs x)))
                   ((##negative? x)
                    (##negate x))
                   ((##bignum? x)
                    (##bignum.copy x))
                   (else ;; nonnegative fixnum
                    x)))
          (y (cond ((##inexact? y)
                    (##inexact->exact (##flabs y)))
                   ((##negative? y)
                    (##negate y))
                   ((##bignum? y)
                    (##bignum.copy y))
                   (else ;; nonnegative fixnum
                    y))))
      ;; now x and y are newly allocated, so we can overwrite them if
      ;; necessary in general-base
      (cond ((##eqv? x 0)
             y)
            ((##eqv? y 0)
             x)
            ((##fixnum? x)
             (if (##fixnum? y)
                 (fixnum-base x y)
                 (fixnum-base x (##remainder y x))))
            ((##fixnum? y)
             (fixnum-base y (##remainder x y)))
            (else
             (let* ((first-x-bit
                     (##first-bit-set x))
                    (first-y-bit
                     (##first-bit-set y))
                    (shift-x?
                     (##fx> (##fx* 2 first-x-bit) (##integer-length x)))
                    (shift-y?
                     (##fx> (##fx* 2 first-y-bit) (##integer-length y)))
                    (x
                     (if shift-x?
                         (##arithmetic-shift x (##fx- first-x-bit))
                         x))
                    (y
                     (if shift-y?
                         (##arithmetic-shift y (##fx- first-y-bit))
                         y)))
               (if (or shift-x? shift-y?)
                   ;; we've shifted out all the powers of two in at
                   ;; least one argument, so we need to put them back
                   ;; in the gcd.
                   (##arithmetic-shift (##gcd x y)
                                       (##fxmin first-x-bit first-y-bit))
                   (##fast-gcd x y)))))))

  (cond ((##not (##integer? x))
         (type-error-on-x))
        ((##not (##integer? y))
         (type-error-on-y))
        ((##eq? x y)
         (##abs x))
        (else
         (if (and (##exact? x) (##exact? y))
             (exact-gcd x y)
             (##exact->inexact (exact-gcd x y))))))

(define-prim-nary (gcd x y)
  0
  (if (##integer? x) (##abs x) '(1))
  (##gcd x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-integer))

(define-prim (##lcm x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (define (exact-lcm x y)
    (if (or (##eqv? x 0) (##eqv? y 0))
        0
        (##abs (##* (##quotient x (##gcd x y))
                    y))))

  (define (inexact-lcm x y)
    (##exact->inexact
     (exact-lcm (##inexact->exact x)
                (##inexact->exact y))))

  (cond ((##not (##integer? x))
         (type-error-on-x))
        ((##not (##integer? y))
         (type-error-on-y))
        (else
         (if (and (##exact? x) (##exact? y))
             (exact-lcm x y)
             (inexact-lcm x y)))))

(define-prim-nary (lcm x y)
  1
  (if (##integer? x) (##abs x) '(1))
  (##lcm x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-integer))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; numerator, denominator

(define-prim (##numerator x)

  (define (type-error)
    (##fail-check-rational 1 numerator x))

  (macro-number-dispatch x (type-error)
    x
    x
    (macro-ratnum-numerator x)
    (cond ((##flzero? x)
           x)
          ((macro-flonum-rational? x)
           (##exact->inexact (##numerator (##flonum->exact x))))
          (else
           (type-error)))
    (if (macro-cpxnum-rational? x)
        (##numerator (macro-cpxnum-real x))
        (type-error))))

(define-prim (numerator x)
  (macro-force-vars (x)
    (##numerator x)))

(define-prim (##denominator x)

  (define (type-error)
    (##fail-check-rational 1 denominator x))

  (macro-number-dispatch x (type-error)
    1
    1
    (macro-ratnum-denominator x)
    (if (macro-flonum-rational? x)
        (##exact->inexact (##denominator (##flonum->exact x)))
        (type-error))
    (if (macro-cpxnum-rational? x)
        (##denominator (macro-cpxnum-real x))
        (type-error))))

(define-prim (denominator x)
  (macro-force-vars (x)
    (##denominator x)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; floor, ceiling, truncate, round

(define-prim (##floor x)

  (define (type-error)
    (##fail-check-finite-real 1 floor x))

  (macro-number-dispatch x (type-error)
    x
    x
    (let ((num (macro-ratnum-numerator x))
          (den (macro-ratnum-denominator x)))
      (if (##negative? num)
          (##quotient (##- num (##- den 1)) den)
          (##quotient num den)))
    (if (##flfinite? x)
        (##flfloor x)
        (type-error))
    (if (macro-cpxnum-real? x)
        (##floor (macro-cpxnum-real x))
        (type-error))))

(define-prim (floor x)
  (macro-force-vars (x)
    (##floor x)))

(define-prim (##ceiling x)

  (define (type-error)
    (##fail-check-finite-real 1 ceiling x))

  (macro-number-dispatch x (type-error)
    x
    x
    (let ((num (macro-ratnum-numerator x))
          (den (macro-ratnum-denominator x)))
      (if (##negative? num)
          (##quotient num den)
          (##quotient (##+ num (##- den 1)) den)))
    (if (##flfinite? x)
        (##flceiling x)
        (type-error))
    (if (macro-cpxnum-real? x)
        (##ceiling (macro-cpxnum-real x))
        (type-error))))

(define-prim (ceiling x)
  (macro-force-vars (x)
    (##ceiling x)))

(define-prim (##truncate x)

  (define (type-error)
    (##fail-check-finite-real 1 truncate x))

  (macro-number-dispatch x (type-error)
    x
    x
    (##quotient (macro-ratnum-numerator x)
                (macro-ratnum-denominator x))
    (if (##flfinite? x)
        (##fltruncate x)
        (type-error))
    (if (macro-cpxnum-real? x)
        (##truncate (macro-cpxnum-real x))
        (type-error))))

(define-prim (truncate x)
  (macro-force-vars (x)
    (##truncate x)))

(define-prim (##round x)

  (define (type-error)
    (##fail-check-finite-real 1 round x))

  (macro-number-dispatch x (type-error)
    x
    x
    (##ratnum.round x)
    (if (##flfinite? x)
        (##flround x)
        (type-error))
    (if (macro-cpxnum-real? x)
        (##round (macro-cpxnum-real x))
        (type-error))))

(define-prim (round x)
  (macro-force-vars (x)
    (##round x)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; rationalize

(define-prim (##rationalize x y)

  (define (simplest-rational1 x y)
    (if (##< y x)
        (simplest-rational2 y x)
        (simplest-rational2 x y)))

  (define (simplest-rational2 x y)
    (cond ((##not (##< x y))
           x)
          ((##positive? x)
           (simplest-rational3 x y))
          ((##negative? y)
           (##negate (simplest-rational3 (##negate y) (##negate x))))
          (else
           0)))

  (define (simplest-rational3 x y)
    (let ((fx (##floor x))
          (fy (##floor y)))
      (cond ((##not (##< fx x))
             fx)
            ((##= fx fy)
             (##+ fx
                  (##inverse
                   (simplest-rational3
                    (##inverse (##- y fy))
                    (##inverse (##- x fx))))))
            (else
             (##+ fx 1)))))

  (cond ((##not (##rational? x))
         (##fail-check-finite-real 1 rationalize x y))
        ((and (##flonum? y)
              (##fl= y (macro-inexact-+inf)))
         (macro-inexact-+0))
        ((##not (##rational? y))
         (##fail-check-real 2 rationalize x y))
        ((##negative? y)
         (##raise-range-exception 2 rationalize x y))
        ((and (##exact? x) (##exact? y))
         (simplest-rational1 (##- x y) (##+ x y)))
        (else
         (let ((exact-x (##inexact->exact x))
               (exact-y (##inexact->exact y)))
           (##exact->inexact
            (simplest-rational1 (##- exact-x exact-y)
                                (##+ exact-x exact-y)))))))

(define-prim (rationalize x y)
  (macro-force-vars (x y)
    (##rationalize x y)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; trigonometry and complex numbers

#|

The next functions are from

 Functions from
 Branch Cuts for Complex Elementary Functions
 or
 Much Ado About Nothing's Sign Bit
 by W. Kahan

 Full reference:

 Kahan, W: Branch cuts for complex elementary functions; or, Much ado about nothing’s sign bit. In Iserles, A., and Powell, M. (eds.), The state of the art in numerical analysis. Clarendon Press (1987) pp 165-211.

Note that Kahan's paper contains two treatments of branch cuts---Section 4, which deals with arithmetic with signed zeros (like IEEE arithmetic) and Section 5, which deals with arithmetic with only unsigned zeros.  The codes in the paper are only for IEEE-style arithmetic.

Gambit Scheme is in a funny position, as it allows mixed-exactness complex numbers.  We'll consider inexact real zeros (+0., -0.) as signed (of course), but we'll interpret exact zero (0) as unsigned.

The branch cuts of all the functions considered here lie on the exact real axis or the exact imaginary axis.

All of the inverse functions are defined in terms of log and sqrt, and the side of the continuity at the branch cuts is determined by the sides of continuity of those two functions.

I believe that this is the same as the continuity rules that the CLHS gives for atan, asin, acos, etc., along branch cuts.

Thanks to Raymond Toy for email discussions and for the code for cmucl, which gets this stuff right in the Common Lisp context.

See

http://140.177.205.23/InverseHyperbolicFunctions.html

for a discussion of branch cuts.

|#

(macro-if-cpxnum (begin

(define-prim (##cabs z)

  ;; As far as I can tell, this is just magic.  It works, and I'm not
  ;; going to touch it.

  #|
  Code to compute the constants using my computable reals package.

  (load "exact-reals")
  (define r2-exact
    (computable-sqrt (exact->computable 2)))
  (define r2p1-exact
    (computable-+ r2-exact (exact->computable 1)))
  (define r2p1
    (computable->inexact r2p1-exact))
  (define t2p1-exact
    (computable-- r2p1-exact (exact->computable (inexact->exact r2p1))))
  (define r2
    (computable->inexact r2-exact))
  (define t2p1
    (computable->inexact t2p1-exact))
  (for-each pretty-print
            `((define r2 ,r2) (define r2p1 ,r2p1) (define t2p1 ,t2p1)))
  |#
  (define r2 1.4142135623730951)
  (define r2p1 2.414213562373095)
  (define t2p1 1.2537167179050217e-16)

  (let ((x (##flabs (macro-cpxnum-real z)))
        (y (##flabs (macro-cpxnum-imag z))))

    (define (continue x y)
      (let* ((x (if (##flinfinite? y) y x))
             (t (##fl- x y)))
        (if (and (##not (##fl= x +inf.0))
                 (##not (##fl= t x)))
            (if (##fl> t y)
                (let* ((s (##fl/ x y))
                       (s (##fl+ s (##flsqrt (##fl+ (macro-inexact-+1) (##fl* s s))))))
                  (##fl+ x (##fl/ y s)))
                (let* ((s (##fl/ t y))
                       (t (##fl* (##fl+ (macro-inexact-+2) s) s))
                       (s (##fl+ r2p1
                                 (##fl+ s
                                        (##fl+ t2p1
                                               (##fl/ t
                                                      (##fl+ r2 (##flsqrt (##fl+ (macro-inexact-+2) t)))))))))
                  (##fl+ x (##fl/ y s))))
            x)))

    (if (##fl< x y)
        (continue y x)
        (continue x y))))

(define-prim (##carg z)
  (##angle z))

(define-prim (##csquare xi+ieta)
  (let ((xi  (macro-cpxnum-real xi+ieta))
        (eta (macro-cpxnum-imag xi+ieta)))
    (let ((x (##fl* (##fl- xi eta) (##fl+ xi eta)))
          (y (##fl* (macro-inexact-+2) xi eta)))
      (cond ((##flnan? x)
             (cond ((##flinfinite? y)
                    (macro-cpxnum-make (##flcopysign (macro-inexact-+0) xi) y))
                   ((##flinfinite? eta)
                    (macro-cpxnum-make (macro-inexact--inf) y))
                   ((##flinfinite? xi)
                    (macro-cpxnum-make (macro-inexact-+inf) y))
                   (else
                    (macro-cpxnum-make x y))))
            ((and (##flnan? y)
                  (##flinfinite? x))
             (macro-cpxnum-make x (##flcopysign (macro-inexact-+0) y)))
            (else
             (macro-cpxnum-make x y))))))

(define-prim (##cssqs x+iy)
  (let ((x (macro-cpxnum-real x+iy))
        (y (macro-cpxnum-imag x+iy)))
    (cond ((or (##flinfinite? x)
               (##flinfinite? y))
           (##cons (macro-inexact-+inf) 0))
          ((and (##flzero? x)
                (##flzero? y))
           (##cons (macro-inexact-+0) 0))
          (else
           ;; from now on, neither x nor y are infinite, and one is non-zero
           (let* ((x^2 (##flsquare x))
                  (y^2 (##flsquare y))
                  (rho (##fl+ x^2 y^2)))
             (if (or (##flinfinite? rho)                    ;; if rho is NaN, this is false
                     (and (or (##fl< x^2 (macro-inexact-lambda))    ;; poor man's way to see whether underflow flag was set
                              (##fl< y^2 (macro-inexact-lambda)))
                          (##fl< rho (##fl/ (macro-inexact-lambda) (macro-inexact-epsilon))))) ;; if rho is NaN, this is false
                 ;; rho is not NaN, so x and y are not NaN, and x and y are not infinite.  Whew.
                 (let ((k (##flilogb (##flmax (##flabs x) (##flabs y)))))
                   (##cons (##fl+ (##flsquare (##flscalbn x (##fx- k)))
                                  (##flsquare (##flscalbn y (##fx- k))))
                           k))
                 (##cons rho 0)))))))

(define-prim (##csqrt x+iy)
  (let* ((x (macro-cpxnum-real x+iy))
         (y (macro-cpxnum-imag x+iy))
         (rho+ik (##cssqs x+iy))
         (rho (##car rho+ik))
         (k   (##cdr rho+ik))
         (rho (if (##flnan? x)
                  rho
                  (##fl+ (##flscalbn (##flabs x) (##fx- k))
                         (##flsqrt rho))))
         (rho (if (##fxodd? k)
                  (##flscalbn (##flsqrt rho) (##fxquotient (##fx- k 1) 2))
                  (##flscalbn (##flsqrt (##fl* (macro-inexact-+2) rho)) (##fx- (##fxquotient k 2) 1))))
         (xi rho)
         (eta y))
    (if (##not (##fl= rho (macro-inexact-+0)))
        (let ((eta (if (##not (##flinfinite? (##flabs eta)))
                       (##fl/ (##fl/ eta rho) (macro-inexact-+2))
                       eta)))
          (if (##flnegative? x)
              (macro-cpxnum-make (##flabs eta) (##flcopysign rho y))
              (macro-cpxnum-make xi eta)))
        (macro-cpxnum-make xi eta))))

(define-prim (##cacos z)
  (##- (macro-inexact-+pi/2) (##casin z)))

(define-prim (##cacosh z)
  (let ((sqrt-z-1 (##sqrt (##- z 1)))
        (sqrt-z+1 (##sqrt (##+ z 1))))

    ;; if z is real and > 1, then the imaginary part of the next expression can be
    ;; inexact 0, but that's OK because this routine is not called in this case.

    (##make-rectangular (##asinh (##real-part (##* (##conjugate sqrt-z-1) sqrt-z+1)))
                        (##* 2 (##atan2 (##imag-part sqrt-z-1) (##real-part sqrt-z+1))))))

(define-prim (##casin z)

  ;; if (##real-part z) is exact zero, then there is a correlation of errors in sqrt-1-z and sqrt-1+z that
  ;; allows the next substitution

  (let ((x (##real-part z)))
    (if (##eqv? x 0)
        (##make-rectangular 0 (##asinh (##imag-part z)))
        (let ((sqrt-1-z (##sqrt (##- 1 z)))
              (sqrt-1+z (##sqrt (##+ 1 z))))
          (##make-rectangular (##atan2 x (##real-part (##* sqrt-1-z sqrt-1+z)))
                              (##asinh (##imag-part (##* (##conjugate sqrt-1-z) sqrt-1+z))))))))

(define-prim (##casinh z)
  (##* -i (##casin (##* +i z))))

(define-prim (##catanh x+iy)
  (define (x/x^2+y^2 x y)
    (if (##fl< (##flabs y) (##flabs x))
        (##fl/ (macro-inexact-+1) (##fl+ x (##fl* (##fl/ y x) y)))
        (let ((x/y (##fl/ x y)))
          (##fl/ x/y (##fl+ (##fl* x x/y) y)))))

  (define (##->exact-sign x)
    ;; returns an exact number with the same sign as x, returns 1 if x is exact zero
    (if (##flonum? x)
        (##inexact->exact (##flcopysign (macro-inexact-+1) x))
        (if (##negative? x) -1 1)))

  (let* ((pi/2 (##* 2 (##atan 1)))
         (theta (##fl/ (##flsqrt (macro-inexact-omega)) (macro-inexact-+4)))
         (rho (##fl/ theta))
         (beta (##->exact-sign (##real-part x+iy))) ;; beta is exact
         (x+iy (##* beta (##conjugate x+iy)))
         (x (##real-part x+iy))
         (y (##imag-part x+iy))
         (inexact-x (##exact->inexact x))
         (inexact-y (##exact->inexact y))
         (abs-y (##flabs inexact-y))
         (zeta (cond ((or (##fl< theta inexact-x)
                          (##fl< theta abs-y))
                      (macro-cpxnum-make (##exact->inexact (x/x^2+y^2 inexact-x inexact-y))
                                         (##flcopysign pi/2 inexact-y)))
                     ((##fl= inexact-x (macro-inexact-+1))
                      (macro-cpxnum-make (##fllog (##fl/ (##flsqrt (##flsqrt (##fl+ (macro-inexact-+4) (##flsquare abs-y))))
                                                         (##flsqrt (##fl+ abs-y rho))))
                                         (##fl/ (##flcopysign (##fl+ pi/2 (##flatan (##fl/ (##fl+ abs-y rho) (macro-inexact-+2))))
                                                              inexact-y)
                                                (macro-inexact-+2))))
                     (else
                      (macro-cpxnum-make (if (##eqv? x 0)
                                             ;; if rho and abs-y were exact in the next expression (no matter their values)
                                             ;; then the argument to fllog1p would be exact 0, so the result would be exact 0.
                                             0
                                             (##fl/ (##fllog1p (##fl/ (##fl* (macro-inexact-+4) inexact-x)                 ;; was (##* 4 x) originally
                                                                      (##fl+ (##flsquare (##fl- (macro-inexact-+1) inexact-x))
                                                                             (##flsquare (##fl+ abs-y rho)))))
                                                    (macro-inexact-+4)))
                                         (##fl/ (##carg (macro-cpxnum-make (##fl- (##fl* (##fl- (macro-inexact-+1) inexact-x)
                                                                                         (##fl+ (macro-inexact-+1) inexact-x))
                                                                                  (##flsquare (##fl+ abs-y rho)))
                                                                           (##fl* (macro-inexact-+2) inexact-y)))
                                                (macro-inexact-+2)))))))
    (##* beta (##conjugate zeta))))

(define-prim (##ctanh xi+ieta)
  ;; we assume that neither xi nor eta can be exact 0
  (let* ((xi  (macro-cpxnum-real xi+ieta))
         (eta (macro-cpxnum-imag xi+ieta)))
    (if (##< (##fl/ (##flasinh (macro-inexact-omega)) (macro-inexact-+4))
             (##abs xi))
        (macro-cpxnum-make (##flcopysign (macro-inexact-+1) (##exact->inexact xi))     ;; xi cannot be exact 0
                           (##flcopysign (macro-inexact-+0) (##exact->inexact eta)))   ;; eta cannot be exact 0
        (let* ((t (##tan eta))                                    ;; sin(eta)/cos(eta) can't be exact 0, so can't be exact
               (beta (##fl+ (macro-inexact-+1) (##flsquare t)))                   ;; 1/cos^2(eta), can't be exact
               (s (##sinh xi))                                    ;; sinh(xi), can't be exact zero, so can't be exact
               (rho (##flsqrt (##fl+ (macro-inexact-+1) (##flsquare s)))))        ;; cosh(xi), can't be exact
          (if (##infinite? t)                                     ;; if sin(eta)/cos(eta) = infinity (how, I don't know)
              (macro-cpxnum-make (##fl/ rho s)
                                 (##fl/ t))
              (let ((one+beta*s^2 (##fl+ (macro-inexact-+1) (##fl* beta (##flsquare s)))))
                (macro-cpxnum-make (##fl/ (##fl* beta (##fl* rho s))
                                          one+beta*s^2)
                                   (##fl/ t
                                          one+beta*s^2))))))))
))

;;; End of Kahan's functions

(define-prim (##conjugate x)

  (define (type-error)
    (##fail-check-number 1 conjugate x))

  (macro-number-dispatch x (type-error)
    x x x x (macro-cpxnum-make (macro-cpxnum-real x)
                               (##negate (macro-cpxnum-imag x)))))

(define-prim (conjugate x)
  (macro-force-vars (x)
    (##conjugate x)))

(define-prim (##exp x)

  (define (type-error)
    (##fail-check-number 1 exp x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        1
        (##flexp (##fixnum->flonum x)))
    (##flexp (##exact-int->flonum x))
    (##flexp (##ratnum->flonum x))
    (##flexp x)
    (##make-polar (##exp (macro-cpxnum-real x))
                  (macro-cpxnum-imag x))))

(define-prim (exp x)
  (macro-force-vars (x)
    (##exp x)))

(define-prim (##flonum-full-precision? x)
  (let ((y (##flabs x)))
    (and (##fl< y (macro-inexact-+inf))
         (##fl<= (macro-flonum-min-normal) y))))

(define-prim (##log x)

  (define (type-error)
    (##fail-check-number 1 log x))

  (define (range-error)
    (##raise-range-exception 1 log x))

  (define (negative-log)
    (macro-if-cpxnum
     (##make-rectangular (##log (##negate x)) (macro-inexact-+pi))
     (range-error)))

  (define (exact-log x)

    ;; x is positive, x is not 1.

    ;; There are three places where just converting to a flonum and
    ;; taking the flonum logarithm doesn't work well.
    ;; 1. Overflow in the conversion
    ;; 2. Underflow in the conversion (or even loss of precision
    ;;    because of a denormalized conversion result)
    ;; 3. When the number is close to 1.

    (let ((float-x (##exact->inexact x)))
      (cond ((##= x float-x)
             (##fllog float-x)) ;; first, we trust the builtin flonum log

            ((##not (##flonum-full-precision? float-x))

             ;; direct conversion to flonum could incur massive relative
             ;; rounding errors, or would just lead to an infinite result
             ;; so we tolerate more than one rounding error in the calculation

             (let* ((wn (##integer-length (##numerator x)))
                    (wd (##integer-length (##denominator x)))
                    (p  (##fx- wn wd))
                    (float-p (##fixnum->flonum p))
                    (partial-result (##fllog
                                     (##exact->inexact
                                      (##* x (##expt 2 (##fx- p)))))))
               (##fl+ (##fl* float-p
                             (macro-inexact-log-2))
                      partial-result)))

            ((or (##fl< (macro-inexact-exp-+1/2) float-x)
                 (##fl< float-x (macro-inexact-exp--1/2)))

             ;; here the absolute value of the logarithm is at least 0.5,
             ;; so there is less rounding error in the final result.

             (##fllog float-x))

            (else

             ;; use ln1p for arguments near one.

             (##fllog1p (##exact->inexact (##- x 1)))))))

  (define (complex-log-magnitude x)

    (define (log-mag a b)
      ;; both are finite, 0 < a <= b
      (let* ((c (##/ a b))
             (approx-mag (##* b (##sqrt (##+ 1 (##* c c))))))
        (if (or (##exact? approx-mag)
                (and (##flonum-full-precision? approx-mag)
                     (or (##fl< (macro-inexact-exp-+1/2) approx-mag)
                         (##fl< approx-mag (macro-inexact-exp--1/2)))))
            ;; log composed with magnitude will compute a relatively accurate answer
            (##log approx-mag)
            (let ((a (##inexact->exact a))
                  (b (##inexact->exact b)))
              (##* 1/2 (exact-log (##+ (##* a a) (##* b b))))))))

    (let ((abs-r (##abs (##real-part x)))
          (abs-i (##abs (##imag-part x))))

      ;; abs-i is not exact 0
      (cond ((or (and (##flonum? abs-r)
                      (##fl= abs-r (macro-inexact-+inf)))
                 (and (##flonum? abs-i)
                      (##fl= abs-i (macro-inexact-+inf))))
             (macro-inexact-+inf))
            ;; neither abs-r or abs-i is infinite
            ((and (##flonum? abs-r)
                  (##flnan? abs-r))
             abs-r)
            ;; abs-r is not a NaN
            ((and (##flonum? abs-i)
                  (##flnan? abs-i))
             abs-i)
            ;; abs-i is not a NaN
            ((##zero? abs-r)
             (let ((temp (##log abs-i)))
               (if (##eqv? abs-r 0)
                   temp
                   (##exact->inexact temp))))
            ;; abs-r is not zero
            (else
             (if (##< abs-r abs-i)
                 (log-mag abs-r abs-i)
                 (log-mag abs-i abs-r))))))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        (range-error)
        (if (##fxnegative? x)
            (negative-log)
            (if (##eqv? x 1)
                0
                (exact-log x))))
    (if (##bignum.negative? x)
        (negative-log)
        (exact-log x))
    (if (##negative? (macro-ratnum-numerator x))
        (negative-log)
        (exact-log x))
    (if (or (##flnan? x)
            (##not (##flnegative?
                    (##flcopysign (macro-inexact-+1) x))))
        (##fllog x)
        (negative-log))
    (##make-rectangular (complex-log-magnitude x) (##angle x))))

(define-prim (##log2 x y)

  (define (general-case x y)
    (##/ (##log x) (##log y)))

  (if (and (macro-exact-int? x)
           (macro-exact-int? y)
           (##< 0 x)
           (##< 1 y))
      (let ((floor-log2-x (##fx- (##integer-length x) 1)))
        (if (##fx= floor-log2-x ;; is n a power of 2?
                   (##first-bit-set x))
            (let ((floor-log2-y (##fx- (##integer-length y) 1)))
              (if (##fx= floor-log2-y ;; is y a power of 2?
                         (##first-bit-set y))
                  (##/ floor-log2-x floor-log2-y)
                  (general-case x y)))
            (general-case x y)))
      (general-case x y)))

(define-prim (log x #!optional (y (macro-absent-obj)))
  (macro-force-vars (x)
    (if (##eq? y (macro-absent-obj))
        (##log x)
        (macro-force-vars (y)
          (cond ((##not (##number? x))
                 (##fail-check-number 1 log x y))
                ((##not (##number? y))
                 (##fail-check-number 2 log x y))
                ((##eqv? x 0)
                 (##raise-range-exception 1 log x y))
                ((##eqv? y 0)
                 (##raise-range-exception 2 log x y))
                ((##eqv? y 1)
                 (##raise-range-exception 2 log x y))
                (else
                 (##log2 x y)))))))

(define-prim (##sin x)

  (define (type-error)
    (##fail-check-number 1 sin x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        0
        (##flsin (##fixnum->flonum x)))
    (##flsin (##exact-int->flonum x))
    (##flsin (##ratnum->flonum x))
    (##flsin x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (and (##flonum? real)
               (##flonum? imag))
          ;; fast path for flonums case
          (macro-cpxnum-make  (##fl* (##flsin real) (##flcosh imag))
                              (##fl* (##flcos real) (##flsinh imag)))
          (##make-rectangular (##* (##sin real) (##cosh imag))
                              (##* (##cos real) (##sinh imag)))))))

(define-prim (sin x)
  (macro-force-vars (x)
    (##sin x)))

(define-prim (##cos x)

  (define (type-error)
    (##fail-check-number 1 cos x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        1
        (##flcos (##fixnum->flonum x)))
    (##flcos (##exact-int->flonum x))
    (##flcos (##ratnum->flonum x))
    (##flcos x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (and (##flonum? real)
               (##flonum? imag))
          ;; fast path for flonums case
          (macro-cpxnum-make        (##fl* (##flcos real) (##flcosh imag))
                             (##fl- (##fl* (##flsin real) (##flsinh imag))))
          (##make-rectangular           (##* (##cos real) (##cosh imag))
                              (##negate (##* (##sin real) (##sinh imag))))))))

(define-prim (cos x)
  (macro-force-vars (x)
    (##cos x)))

(define-prim (##tan x)

  (define (type-error)
    (##fail-check-number 1 tan x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        0
        (##fltan (##fixnum->flonum x)))
    (##fltan (##exact-int->flonum x))
    (##fltan (##ratnum->flonum x))
    (##fltan x)
    ;; ##tanh is the basic function here.
    ;; Note that multiplying a purely imaginary argument
    ;; x by +i gives a purely real result, so we apply
    ;; ##tanh to (##* +i x), not ##ctanh.
    (##* -i (##tanh (##* +i x)))))

(define-prim (tan x)
  (macro-force-vars (x)
    (##tan x)))

(define-prim (##asin x)

  (define (type-error)
    (##fail-check-number 1 asin x))

  (define (range-error)
    (##raise-range-exception 1 asin x))

  (define (real-case x)
    (if (or (##< 1 x)
            (##< x -1))
        (macro-if-cpxnum
         (##casin (macro-cpxnum-make x 0))
         (range-error))
        (##flasin (##exact->inexact x))))

  (macro-number-dispatch x (type-error)
    (if (##eqv? x 0)
        0
        (real-case x))
    (real-case x)
    (real-case x)
    (real-case x)
    (##casin x)))

(define-prim (asin x)
  (macro-force-vars (x)
    (##asin x)))

(define-prim (##acos x)

  (define (type-error)
    (##fail-check-number 1 acos x))

  (define (range-error)
    (##raise-range-exception 1 acos x))

  (define (real-case x)
    (if (or (##< 1 x)
            (##< x -1))
        (macro-if-cpxnum
         (##cacos (macro-cpxnum-make x 0))
         (range-error))
        (##flacos (##exact->inexact x))))

  (macro-number-dispatch x (type-error)
    (if (##eqv? x 1)
        0
        (real-case x))
    (real-case x)
    (real-case x)
    (real-case x)
    (##cacos x)))

(define-prim (acos x)
  (macro-force-vars (x)
    (##acos x)))

(define-prim (##atan x)

  (define (type-error)
    (##fail-check-number 1 atan x))

  (define (range-error)
    (##raise-range-exception 1 atan x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        0
        (##flatan (##fixnum->flonum x)))
    (##flatan (##exact-int->flonum x))
    (##flatan (##ratnum->flonum x))
    (##flatan x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (and (##eqv? real 0)
               (or (##eqv? imag 1)
                   (##eqv? imag -1)))
          (range-error)
          (##* -i (##atanh (##* +i x)))))))

(define-prim (##atan2 y x)
  (cond ((or (and (##flonum? x) (##flnan? x))
             (and (##flonum? y) (##flnan? y)))
         +nan.0)
        ((##eqv? 0 y)
         (if (##exact? x)
             (if (##negative? x)
                 (macro-inexact-+pi)
                 0)
             (if (##negative? (##flcopysign (macro-inexact-+1) x))
                 (macro-inexact-+pi)
                 (macro-inexact-+0))))
        ((and (##not (##finite? x))
              (##not (##finite? y)))
         (if (##positive? x)
             (##flcopysign (macro-inexact-+pi/4) y)
             (##flcopysign (macro-inexact-+3pi/4) y)))
        (else
         (let ((inexact-x (##exact->inexact x))
               (inexact-y (##exact->inexact y)))
           (if (and (or (##flonum? x)
                        (##flonum-full-precision? inexact-x)
                        (##= x inexact-x))
                    (or (##flonum? y)
                        (##flonum-full-precision? inexact-y)
                        (##= y inexact-y)))
               (##flatan inexact-y inexact-x)
               ;; at least one of x or y is nonzero
               ;; and at least one of them is not a flonum
               (let* ((exact-x (##inexact->exact x))
                      (exact-y (##inexact->exact y))
                      (max-arg (##max (##abs exact-x)
                                      (##abs exact-y)))
                      (normalizer (##expt 2 (##- (##integer-length (##denominator max-arg))
                                                 (##integer-length (##numerator   max-arg))))))
                 ;; now the largest argument will be about 1.
                 (##flatan (##exact->inexact (##* normalizer exact-y))
                           (##exact->inexact (##* normalizer exact-x)))))))))

(define-prim (atan x #!optional (y (macro-absent-obj)))
  (macro-force-vars (x)
    (if (##eq? y (macro-absent-obj))
        (##atan x)
        (macro-force-vars (y)
          (cond ((##not (##real? x))
                 (##fail-check-real 1 atan x y))
                ((##not (##real? y))
                 (##fail-check-real 2 atan x y))
                (else
                 (##atan2 x y)))))))

;;; Hyperbolic functions

(define-prim (##sinh x)

  (define (type-error)
    (##fail-check-number 1 sinh x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        0
        (##flsinh (##fixnum->flonum x)))
    (##flsinh (##exact-int->flonum x))
    (##flsinh (##ratnum->flonum x))
    (##flsinh x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (and (##flonum? real) (##flonum? imag))
          ;; fast path for flonum case
          (macro-cpxnum-make (##fl* (##flsinh real) (##flcos imag))
                             (##fl* (##flcosh real) (##flsin imag)))
          (macro-cpxnum-make (##* (##sinh real) (##cos imag))
                             (##* (##cosh real) (##sin imag)))))))

(define-prim (sinh x)
  (macro-force-vars (x)
    (##sinh x)))

(define-prim (##cosh x)

  (define (type-error)
    (##fail-check-number 1 cosh x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        1
        (##flcosh (##fixnum->flonum x)))
    (##flcosh (##exact-int->flonum x))
    (##flcosh (##ratnum->flonum x))
    (##flcosh x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (and (##flonum? real) (##flonum? imag))
          ;; fast path for flonum case
          (macro-cpxnum-make (##fl* (##flcosh real) (##flcos imag))
                             (##fl* (##flsinh real) (##flsin imag)))
          (macro-cpxnum-make (##* (##cosh real) (##cos imag))
                             (##* (##sinh real) (##sin imag)))))))

(define-prim (cosh x)
  (macro-force-vars (x)
    (##cosh x)))

(define-prim (##tanh x)

  (define (type-error)
    (##fail-check-number 1 tanh x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        0
        (##fltanh (##fixnum->flonum x)))
    (##fltanh (##exact-int->flonum x))
    (##fltanh (##ratnum->flonum x))
    (##fltanh x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (##eqv? real 0)
          ;; the argument of the next ##tan is real
          ;; (##* +i (##tan (##* -i x)))
          (macro-cpxnum-make 0 (##tan imag))
          (##ctanh x)))))

(define-prim (tanh x)
  (macro-force-vars (x)
    (##tanh x)))

;;; Inverse hyperbolic functions

(define-prim (##asinh x)

  (define (type-error)
    (##fail-check-number 1 asinh x))

  (macro-number-dispatch x (type-error)
    (if (##fxzero? x)
        0
        (##flasinh (##fixnum->flonum x)))
    (##flasinh (##exact-int->flonum x))
    (##flasinh (##ratnum->flonum x))
    (##flasinh x)
    (##casinh x)))

(define-prim (asinh x)
  (macro-force-vars (x)
    (##asinh x)))

(define-prim (##acosh x)

  (define (type-error)
    (##fail-check-number 1 acosh x))

  (define (range-error)
    (##raise-range-exception 1 acosh x))

  (define (real-case x)
    (if (##< x 1)
        (macro-if-cpxnum
         (##cacosh (macro-cpxnum-make x 0))
         (range-error))
        (##flacosh (##exact->inexact x))))

  (macro-number-dispatch x (type-error)
    (if (##fx= x 1)
        0
        (real-case x))
    (real-case x)
    (real-case x)
    (real-case x)
    (##cacosh x)))

(define-prim (acosh x)
  (macro-force-vars (x)
    (##acosh x)))

(define-prim (##atanh x)

  (define (type-error)
    (##fail-check-number 1 atanh x))

  (define (range-error)
    (##raise-range-exception 1 atanh x))

  (define (real-case x)
    (cond ((##< 1 x) ;; TODO: avoid calling real-case in non-tail position
           (##negate (real-case (##negate x))))
          ((##< x -1)
           (macro-if-cpxnum
            (##make-rectangular
             (##fl/ (##fllog1p (##exact->inexact (##/ (##* 4 x)
                                                      (##square (##- x 1)))))
                    (macro-inexact-+4))
             (macro-inexact-+pi/2))
            (range-error)))
          (else
           (##flatanh (##exact->inexact x)))))

  (macro-number-dispatch x (type-error)
    (case x
      ((0)
       0)
      ((-1 1)
       (range-error))
      (else
       (real-case x)))
    (real-case x)
    (real-case x)
    (real-case x)
    (##catanh x)))

(define-prim (atanh x)
  (macro-force-vars (x)
    (##atanh x)))

(##define-macro (macro-make-sr s r) `(##values ,s ,r))
(##define-macro (macro-sr-s sr)     `(##values-ref ,sr 0))
(##define-macro (macro-sr-r sr)     `(##values-ref ,sr 1))

(define-prim (##sqrt x)

  (define (type-error)
    (##fail-check-number 1 sqrt x))

  (define (range-error)
    (##raise-range-exception 1 sqrt x))

  (define (exact-int-sqrt x)
    (if (##negative? x)
        (macro-if-cpxnum
         (##make-rectangular 0 (nonneg-exact-int-sqrt (##negate x)))
         (range-error))
        (nonneg-exact-int-sqrt x)))

  (define (nonneg-exact-int-sqrt x)
    (let ((y (##exact-int.sqrt x)))
      (cond ((##eqv? (macro-sr-r y) 0)
             (macro-sr-s y))
            ((##not (##exact-int.< (macro-flonum-+m-max-plus-1) x))
             ;; 0 <= x <= (macro-flonum-+m-max-plus-1), can be
             ;; converted to flonum exactly so avoids double
             ;; rounding in next expression. This has a relatively
             ;; fast path for small integers.
             (##flsqrt (##exact-int->flonum x)))
            ((##not (##< (macro-sr-s y) (macro-flonum-+m-max-plus-1)))
             ;; ##exact-int->flonum uses second argument correctly
             (##exact-int->flonum (macro-sr-s y) #t))
            (else
             ;; The integer part of y does not have enough bits accuracy
             ;; to round it correctly to a flonum, so to
             ;; make sure (macro-sr-s y) is big enough in the next call we
             ;; multiply by (expt 2 (macro-flonum-m-bits-plus-1*2)),
             ;; which is somewhat extravagant;
             ;; (expt 2 (+ 1 (macro-flonum-m-bits-plus-1))) should
             ;; work fine.
             (##fl* (macro-flonum-inverse-+m-max-plus-1-inexact)
                    (exact-int-sqrt
                     (##arithmetic-shift
                      x
                      (macro-flonum-m-bits-plus-1*2))))))))

  (define (ratnum-sqrt x)
    (if (##negative? x)
        (macro-if-cpxnum
         (##make-rectangular 0 (nonneg-ratnum-sqrt (##negate x)))
         (range-error))
        (nonneg-ratnum-sqrt x)))

  (define (nonneg-ratnum-sqrt x)
    (let ((p (macro-ratnum-numerator x))
          (q (macro-ratnum-denominator x)))
      (let ((sqrt-p (##exact-int.sqrt p))
            (sqrt-q (##exact-int.sqrt q)))
        (if (and (##zero? (macro-sr-r sqrt-p))
                 (##zero? (macro-sr-r sqrt-q)))
            ;; both (abs p) and q are perfect squares and
            ;; their square roots do not have any common factors
            (macro-ratnum-make (macro-sr-s sqrt-p)
                               (macro-sr-s sqrt-q))
            (let ((wp (##integer-length p))
                  (wq (##integer-length q)))

              ;; for IEEE 754 double precision, we need at least
              ;; 53 or 54 (I can't seem to work it out) of the
              ;; leading bits of (sqrt (/ p q)).  Here we get
              ;; about 64 leading bits.  We just shift p (either
              ;; right or left) until it is about 128 bits longer
              ;; than q (shift must be even), then take the
              ;; integer square root of the result.

              (let* ((shift
                      (##fxarithmetic-shift-left
                       (##fxarithmetic-shift-right
                        (##fx- 128 (##fx- wp wq))
                        1)
                       1))
                     (leading-bits
                      (macro-sr-s
                       (##exact-int.sqrt
                        (##quotient
                         (##arithmetic-shift p shift)
                         q))))
                     (pre-rounded-result
                      (if (##fxnegative? shift)
                          (##arithmetic-shift
                           leading-bits
                           (##fx-
                            (##fxarithmetic-shift-right
                             shift
                             1)))
                          (##ratnum.normalize
                           leading-bits
                           (##arithmetic-shift
                            1
                            (##fxarithmetic-shift-right
                             shift
                             1))))))
                (if (##ratnum? pre-rounded-result)
                    (##ratnum->flonum pre-rounded-result #t)
                    (##exact-int->flonum  pre-rounded-result #t))))))))

  (macro-number-dispatch x (type-error)
    (exact-int-sqrt x)
    (exact-int-sqrt x)
    (ratnum-sqrt x)
    (if (##flnegative? x)
        (macro-if-cpxnum
         (##make-rectangular 0 (##flsqrt (##fl- x)))
         (range-error))
        (##flsqrt x))
    (let ((real (##real-part x))
          (imag (##imag-part x)))
      (or (and (##exact? real)
               (##exact? imag)
               (let ((discriminant (##sqrt (##+ (##* real real)
                                                (##* imag imag)))))
                 (and (##exact? discriminant)
                      (let ((result-real (##sqrt (##/ (##+ real discriminant) 2))))
                        (and (##exact? result-real)
                             (##make-rectangular result-real (##/ imag (##* 2 result-real))))))))
          (##csqrt (##exact->inexact x))))))

(define-prim (sqrt x)
  (macro-force-vars (x)
    (##sqrt x)))

(define-prim (##expt x y)

  (define (type-error-on-x)
    (##fail-check-number 1 expt x y))

  (define (type-error-on-y)
    (##fail-check-number 2 expt x y))

  (define (range-error)
    (##raise-range-exception 1 expt x y))

  (define (exact-int-expt x y)

    (define (positive-int-expt x y)

      ;; x is an exact number and y is a positive exact integer

      (define (expt-aux x y)

        ;; x is an exact integer (not 0 or 1) and y is a nonzero exact integer

        (if (##eqv? y 1)
            x
            (let ((temp (##square (expt-aux x (##arithmetic-shift y -1)))))
              (if (##even? y)
                  temp
                  (##* x temp)))))

      (cond ((or (##eqv? x 0)
                 (##eqv? x 1))
             x)
            ((eqv? x -1)
             (if (##odd? y)
                 -1
                 1))
            ((##ratnum? x)
             (macro-ratnum-make
              (exact-int-expt (macro-ratnum-numerator   x) y)
              (exact-int-expt (macro-ratnum-denominator x) y)))
            (else
             (expt-aux x y))))

    (define (invert z)
      ;; z is exact
      (or (##inverse z)
          (range-error)))

    (if (##negative? y)
        (invert (positive-int-expt x (##negate y)))
        (positive-int-expt x y)))

  (define (complex-expt x y)
    (macro-if-cpxnum
     (##exp (##* (##log x) y))
     (range-error)))

  (define (ratnum-expt x y)
    ;; x is exact-int or ratnum
    (cond ((##eqv? x 0)
           (if (##negative? y)
               (range-error)
               0))
          ((##eqv? x 1)
           1)
          ((##negative? x)
           (macro-if-cpxnum
            ;; We'll do some nice multiples of angles of pi carefully
            (case (macro-ratnum-denominator y)
              ((2)
               (##* (##expt (##negate x) y)
                    (case (##modulo (macro-ratnum-numerator y) 4)
                      ((1)
                       (macro-cpxnum-+i))
                      (else ;; (3)
                       (macro-cpxnum--i)))))
              ((3)
               (##* (##expt (##negate x) y)
                    (case (##modulo (macro-ratnum-numerator y) 6)
                      ((1)
                       (macro-cpxnum-+1/2+sqrt3/2i))
                      ((2)
                       (macro-cpxnum--1/2+sqrt3/2i))
                      ((4)
                       (macro-cpxnum--1/2-sqrt3/2i))
                      (else ;; (5)
                       (macro-cpxnum-+1/2-sqrt3/2i)))))
              ((6)
               (##* (##expt (##negate x) y)
                    (case (##modulo (macro-ratnum-numerator y) 12)
                      ((1)
                       (macro-cpxnum-+sqrt3/2+1/2i))
                      ((5)
                       (macro-cpxnum--sqrt3/2+1/2i))
                      ((7)
                       (macro-cpxnum--sqrt3/2-1/2i))
                      (else ;; (11)
                       (macro-cpxnum-+sqrt3/2-1/2i)))))
              ;; otherwise, we punt
              (else
               (ratnum-complex-expt x y)))
            (range-error)))
          ((macro-exact-int? x)
           (let* ((y-den (macro-ratnum-denominator y))
                  (temp (##exact-int.nth-root x y-den)))
             (if (##= x (exact-int-expt temp y-den))
                 (exact-int-expt temp (macro-ratnum-numerator y))
                 (##flexpt (##exact-int->flonum x)
                           (##ratnum->flonum y)))))
          (else
           ;; x is a ratnum
           (let ((x-num (macro-ratnum-numerator   x))
                 (x-den (macro-ratnum-denominator x))
                 (y-num (macro-ratnum-numerator   y))
                 (y-den (macro-ratnum-denominator y)))
             (let ((temp-num (##exact-int.nth-root x-num y-den)))
               (if (##= (exact-int-expt temp-num y-den) x-num)
                   (let ((temp-den (##exact-int.nth-root x-den y-den)))
                     (if (##= (exact-int-expt temp-den y-den) x-den)
                         (exact-int-expt (macro-ratnum-make temp-num temp-den)
                                         y-num)
                         (##flexpt (##ratnum->flonum x)
                                   (##ratnum->flonum y))))
                   (##flexpt (##ratnum->flonum x)
                             (##ratnum->flonum y))))))))

  (define (ratnum-complex-expt x y)

    ;; y is ratnum, x is negative exact or complex

    (define (power-of-two? n)
      (if (##fixnum? n)
          (##fxzero? (##fxand n (##fx- n 1)))
          (##fx= (##fx+ (##first-bit-set n) 1)
                 (##integer-length n))))

    (define (exact-dyadic-root? x n)
      (and (##exact? x)
           (or (and (##fxzero? n) x)
               (exact-dyadic-root? (##sqrt x)
                                   (##fx- n 1)))))

    (or (and (power-of-two? (macro-ratnum-denominator y))
             (or (and (##eqv? (macro-ratnum-denominator y) 2)
                      (##eqv? (macro-ratnum-numerator y) 1)
                      (##sqrt x))
                 (let ((root? (exact-dyadic-root? x (##first-bit-set (macro-ratnum-denominator y)))))
                   (and root?
                        (##expt root? (macro-ratnum-numerator y))))))
        (complex-expt x y)))

  (macro-number-dispatch y (type-error-on-y)

    (macro-number-dispatch x (type-error-on-x) ;; y a fixnum
      (if (##fx= y 0)
          1
          (exact-int-expt x y))
      (if (##fx= y 0)
          1
          (exact-int-expt x y))
      (if (##fx= y 0)
          1
          (exact-int-expt x y))
      (cond ((##fx= y 0)
             1)
            ((##flnan? x)
             x)
            ((##flnegative? x)
             ;; we do this because (##fixnum->flonum y) is always
             ;; even for large enough y on 64-bit machines
             (let ((abs-result
                    (##flexpt (##fl- x) (##fixnum->flonum y))))
               (if (##fxodd? y)
                   (##fl- abs-result)
                   abs-result)))
            (else
             (##flexpt x (##fixnum->flonum y))))
      (cond ((##fx= y 0)
             1)
            ((##fx= y 1)
             x)
            ((##exact? x)
             (exact-int-expt x y))
            (else
             (complex-expt x y))))

    (macro-number-dispatch x (type-error-on-x) ;; y a bignum
      (exact-int-expt x y)
      (exact-int-expt x y)
      (exact-int-expt x y)
      (cond ((##flnan? x)
             x)
            ((##flnegative? x)
             ;; we do this because (##exact-int->flonum y) is always
             ;; even for large enough y
             (let ((abs-result
                    (##flexpt (##fl- x) (##exact-int->flonum y))))
               (if (##odd? y)
                   (##fl- abs-result)
                   abs-result)))
            (else
             (##flexpt x (##exact-int->flonum y))))
      (if (##exact? x)
          (exact-int-expt x y)
          (complex-expt x y)))

    (macro-number-dispatch x (type-error-on-x) ;; y a ratnum
      (ratnum-expt x y)
      (ratnum-expt x y)
      (ratnum-expt x y)
      (cond ((##flnan? x)
             x)
            ((##flnegative? x)
             (macro-if-cpxnum
              (if (##eqv? 2 (macro-ratnum-denominator y))
                  (let ((magnitude (##flexpt (##fl- x) (##ratnum->flonum y))))
                    (if (##eqv? 1 (##modulo (macro-ratnum-numerator y) 4))
                        ;; multiple of i
                        (macro-cpxnum-make 0 magnitude)
                        ;; multiple of -i
                        (macro-cpxnum-make 0 (##fl- magnitude))))
                  (complex-expt x y))
              (range-error)))
            (else
             (##flexpt x (##ratnum->flonum y))))
      (ratnum-complex-expt x y))

    (macro-number-dispatch x (type-error-on-x) ;; y a flonum
      (cond ((##flnan? y)
             y)
            ((##eqv? x 0)
             (if (##flnegative? y)
                 (range-error)
                 (macro-inexact-+0)))
            ((or (##fxpositive? x)
                 (macro-flonum-int? y))
             (##flexpt (##fixnum->flonum x) y))
            (else
             (complex-expt x y)))
      (cond ((##flnan? y)
             y)
            ((or (##positive? x)
                 (macro-flonum-int? y))
             (##flexpt (##exact-int->flonum x) y))
            (else
             (complex-expt x y)))
      (cond ((##flnan? y)
             y)
            ((or (##positive? x)
                 (macro-flonum-int? y))
             (##flexpt (##ratnum->flonum x) y))
            (else
             (complex-expt x y)))
      (cond ((##flnan? x)
             x)
            ((##flnan? y)
             y)
            ((or (##flpositive? x)
                 (macro-flonum-int? y))
             (##flexpt x y))
            (else
             (complex-expt x y)))
      (cond ((##flnan? y)
             y)
            (else
             (complex-expt x y))))

    (macro-number-dispatch x (type-error-on-x)  ;; y a cpxnum
      (if (##eqv? x 0)
          (let ((real (##real-part y)))
            (if (##positive? real)
                0
                ;; If we call (complex-expt 0 y),
                ;; we'll try to take (##log 0) in complex-expt,
                ;; so we raise the exception here.
                (range-error)))
          (complex-expt x y))
      (complex-expt x y)
      (complex-expt x y)
      (complex-expt x y)
      (complex-expt x y))))

(define-prim (expt x y)
  (macro-force-vars (x y)
    (##expt x y)))

(define-prim (##make-rectangular x y)
  (cond ((##not (##real? x))
         (##fail-check-real 1 make-rectangular x y))
        ((##not (##real? y))
         (##fail-check-real 2 make-rectangular x y))
        (else
         (let ((real (##real-part x))
               (imag (##real-part y)))
           (if (##eqv? imag 0)
               real
               (macro-if-cpxnum
                (macro-cpxnum-make real imag)
                (##raise-range-exception 2 make-rectangular x y)))))))

(define-prim (make-rectangular x y)
  (macro-force-vars (x y)
    (##make-rectangular x y)))

(define-prim (##make-polar x y)
  (cond ((##not (##real? x))
         (##fail-check-real 1 make-polar x y))
        ((##not (##real? y))
         (##fail-check-real 2 make-polar x y))
        (else
         (let ((real-x (##real-part x))
               (real-y (##real-part y)))
           (##make-rectangular (##* real-x (##cos real-y))
                               (##* real-x (##sin real-y)))))))

(define-prim (make-polar x y)
  (macro-force-vars (x y)
    (##make-polar x y)))

(define-prim (##real-part x)
  (macro-number-dispatch x x
    x x x x (macro-cpxnum-real x)))

(define-prim (real-part x)

  (define (type-error)
    (##fail-check-number 1 real-part x))

  (macro-force-vars (x)
    (macro-number-dispatch x (type-error)
      x x x x (macro-cpxnum-real x))))

(define-prim (##imag-part x)
  (macro-number-dispatch x 0
    0 0 0 0 (macro-cpxnum-imag x)))

(define-prim (imag-part x)

  (define (type-error)
    (##fail-check-number 1 imag-part x))

  (macro-force-vars (x)
    (macro-number-dispatch x (type-error)
      0 0 0 0 (macro-cpxnum-imag x))))

(define-prim (##magnitude x)

  (define (type-error)
    (##fail-check-number 1 magnitude x))

  (macro-number-dispatch x (type-error)
    (if (##fxnegative? x) (##negate x) x)
    (if (##bignum.negative? x) (##negate x) x)
    (if (##exact-int.negative? (macro-ratnum-numerator x))
        (macro-ratnum-make (##negate (macro-ratnum-numerator x))
                           (macro-ratnum-denominator x))
        x)
    (##flabs x)
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (cond ((and (##flonum? real) (##flonum? imag))
             (##cabs x))
            ;; at least one of real or imag is exact
            ((and (##exact? real) (##exact? imag))
             (##sqrt (##+ (##square real) (##square imag))))
            ;; one is exact, other is inexact
            ((and (##finite? real) (##finite? imag))
             (##exact->inexact (##sqrt (##+ (##square (##inexact->exact real))
                                            (##square (##inexact->exact imag))))))
            ;; one is exact, other is not finite inexact
            (else
             (##cabs (macro-cpxnum-make (##exact->inexact real)
                                        (##exact->inexact imag))))))))

(define-prim (magnitude x)
  (macro-force-vars (x)
    (##magnitude x)))

(define-prim (##angle x)

  (define (type-error)
    (##fail-check-number 1 angle x))

  (macro-number-dispatch x (type-error)
    (if (##fxnegative? x)
        (macro-inexact-+pi)
        0)
    (if (##bignum.negative? x)
        (macro-inexact-+pi)
        0)
    (if (##negative? (macro-ratnum-numerator x))
        (macro-inexact-+pi)
        0)
    (if (##flnegative? (##flcopysign (macro-inexact-+1) x))
        (macro-inexact-+pi)
        (macro-inexact-+0))
    (##atan2 (macro-cpxnum-imag x) (macro-cpxnum-real x))))

(define-prim (angle x)
  (macro-force-vars (x)
    (##angle x)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; exact->inexact, inexact->exact

(define-prim (##exact->inexact x)

  (define (type-error)
    (##fail-check-number 1 exact->inexact x))

  (macro-number-dispatch x (type-error)
    (##fixnum->flonum x)
    (##exact-int->flonum x)
    (##ratnum->flonum x)
    x
    (##make-rectangular (##exact->inexact (macro-cpxnum-real x))
                        (##exact->inexact (macro-cpxnum-imag x)))))

(define-prim (exact->inexact x)
  (macro-force-vars (x)
    (##exact->inexact x)))

(define-prim (##inexact x)

  (define (type-error)
    (##fail-check-number 1 inexact x))

  (macro-number-dispatch x (type-error)
    (##fixnum->flonum x)
    (##exact-int->flonum x)
    (##ratnum->flonum x)
    x
    (##make-rectangular (##inexact (macro-cpxnum-real x))
                        (##inexact (macro-cpxnum-imag x)))))

(define-prim (inexact x)
  (macro-force-vars (x)
    (##inexact x)))

(define-prim (##inexact->exact x)

  (define (type-error)
    (##fail-check-number 1 inexact->exact x))

  (define (range-error)
    (##raise-range-exception 1 inexact->exact x))

  (macro-number-dispatch x (type-error)
    x
    x
    x
    (if (macro-flonum-rational? x)
        (##flonum->exact x)
        (range-error))
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (and (macro-noncpxnum-rational? real)
               (macro-noncpxnum-rational? imag))
          (##make-rectangular (##inexact->exact real)
                              (##inexact->exact imag))
          (range-error)))))

(define-prim (inexact->exact x)
  (macro-force-vars (x)
    (##inexact->exact x)))

(define-prim (##exact x)

  (define (type-error)
    (##fail-check-number 1 exact x))

  (define (range-error)
    (##raise-range-exception 1 exact x))

  (macro-number-dispatch x (type-error)
    x
    x
    x
    (if (macro-flonum-rational? x)
        (##flonum->exact x)
        (range-error))
    (let ((real (macro-cpxnum-real x))
          (imag (macro-cpxnum-imag x)))
      (if (and (macro-noncpxnum-rational? real)
               (macro-noncpxnum-rational? imag))
          (##make-rectangular (##exact real)
                              (##exact imag))
          (range-error)))))

(define-prim (exact x)
  (macro-force-vars (x)
    (##exact x)))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; number->string, string->number

(define-prim (##exact-int->string x rad force-sign?)

  (##define-macro (macro-make-block-size)
    (let* ((max-rad 16)
           (t (make-vector (+ max-rad 1) 0)))

      (define max-fixnum 536870911) ;; OK to be conservative

      (define (block-size-for rad)
        (let loop ((i 0) (rad^i 1))
          (let ((new-rad^i (* rad^i rad)))
            (if (<= new-rad^i max-fixnum)
                (loop (+ i 1) new-rad^i)
                i))))

      (let loop ((i max-rad))
        (if (< 1 i)
            (begin
              (vector-set! t i (block-size-for i))
              (loop (- i 1)))))

      `',t))

  (define block-size (macro-make-block-size))

  (##define-macro (macro-make-rad^block-size)
    (let* ((max-rad 16)
           (t (make-vector (+ max-rad 1) 0)))

      (define max-fixnum 536870911) ;; OK to be conservative

      (define (rad^block-size-for rad)
        (let loop ((i 0) (rad^i 1))
          (let ((new-rad^i (* rad^i rad)))
            (if (<= new-rad^i max-fixnum)
                (loop (+ i 1) new-rad^i)
                rad^i))))

      (let loop ((i max-rad))
        (if (< 1 i)
            (begin
              (vector-set! t i (rad^block-size-for i))
              (loop (- i 1)))))

      `',t))

  (define rad^block-size (macro-make-rad^block-size))

  (define (make-string-from-last-fixnum rad x len pos)
    (let loop ((x x) (len len) (pos pos))
      (if (##fx= x 0)
          (##make-string len)
          (let* ((new-pos
                  (##fx+ pos 1))
                 (s
                  (loop (##fxquotient x rad)
                        (##fx+ len 1)
                        new-pos)))
            (##string-set!
             s
             (##fx- (##string-length s) new-pos)
             (##string-ref ##digit-to-char-table
                           (##fx- (##fxremainder x rad))))
            s))))

  (define (convert-non-last-fixnum s rad x pos)
    (let loop ((x x)
               (size (##vector-ref block-size rad))
               (i (##fx- (##string-length s) pos)))
      (if (##fx< 0 size)
          (let ((new-i (##fx- i 1)))
            (##string-set!
             s
             new-i
             (##string-ref ##digit-to-char-table
                           (##fxremainder x rad)))
            (loop (##fxquotient x rad)
                  (##fx- size 1)
                  new-i)))))

  (define (make-string-from-fixnums rad lst len pos)
    (let loop ((lst lst) (pos pos))
      (let ((new-lst (##cdr lst)))
        (if (##null? new-lst)
            (make-string-from-last-fixnum
             rad
             (##fx- (##car lst))
             (##fx+ len pos)
             pos)
            (let* ((size
                    (##vector-ref block-size rad))
                   (new-pos
                    (##fx+ pos size))
                   (s
                    (loop new-lst new-pos)))
              (convert-non-last-fixnum s rad (##car lst) pos)
              s)))))

  (define (uinteger->fixnums level sqs x lst)
    (cond ((and (##null? lst) (##eqv? x 0))
           lst)
          ((##fx= level 0)
           (##cons x lst))
          (else
           (let* ((qr (##exact-int.div x (##car sqs)))
                  (new-level (##fx- level 1))
                  (new-sqs (##cdr sqs))
                  (q (macro-qr-q qr))
                  (r (macro-qr-r qr)))
             (uinteger->fixnums
              new-level
              new-sqs
              r
              (uinteger->fixnums new-level new-sqs q lst))))))

  (define (uinteger->string x rad len)
    (make-string-from-fixnums
     rad
     (let ((rad^size
            (##vector-ref rad^block-size rad))
           (x-length
            (##integer-length x)))
       (let loop ((level 0)
                  (sqs '())
                  (rad^size^2^level rad^size))
         (let ((new-level
                (##fx+ level 1))
               (new-sqs
                (##cons rad^size^2^level sqs)))
           (if (##fx< x-length
                      (##fx-
                       (##fx* (##integer-length rad^size^2^level) 2)
                       1))
               (uinteger->fixnums new-level new-sqs x '())
               (let ((new-rad^size^2^level
                      (##exact-int.square rad^size^2^level)))
                 (if (##< x new-rad^size^2^level)
                     (uinteger->fixnums new-level new-sqs x '())
                     (loop new-level
                           new-sqs
                           new-rad^size^2^level)))))))
     len
     0))

  (macro-exact-int-dispatch-no-error x

    (cond ((##fxnegative? x)
           (let ((s (make-string-from-last-fixnum rad x 1 0)))
             (##string-set! s 0 #\-)
             s))
          ((##fxzero? x)
           (if force-sign?
               (##string #\+ #\0)
               (##string #\0)))
          (else
           (if force-sign?
               (let ((s (make-string-from-last-fixnum rad (##fx- x) 1 0)))
                 (##string-set! s 0 #\+)
                 s)
               (make-string-from-last-fixnum rad (##fx- x) 0 0))))

    (cond ((##bignum.negative? x)
           (let ((s (uinteger->string (##negate x) rad 1)))
             (##string-set! s 0 #\-)
             s))
          (else
           (if force-sign?
               (let ((s (uinteger->string x rad 1)))
                 (##string-set! s 0 #\+)
                 s)
               (uinteger->string x rad 0))))))

(define ##digit-to-char-table "0123456789abcdefghijklmnopqrstuvwxyz")

(define-prim (##ratnum->string x rad force-sign?)
  (##string-append
   (##exact-int->string (macro-ratnum-numerator x) rad force-sign?)
   "/"
   (##exact-int->string (macro-ratnum-denominator x) rad #f)))

(##define-macro (macro-r6rs-fp-syntax) #t)
(##define-macro (macro-chez-fp-syntax) #f)

(##define-macro (macro-make-10^constants)
  (define n 326)
  (let ((v (make-vector n)))
    (let loop ((i 0) (x 1))
      (if (< i n)
          (begin
            (vector-set! v i x)
            (loop (+ i 1) (* x 10)))))
    `',v))

(define ##10^-constants
  (if (use-fast-bignum-algorithms)
      (macro-make-10^constants)
      #f))

(define-prim (##flonum-printout v sign-prefix)

  ;; This algorithm is derived from the paper "Printing Floating-Point
  ;; Numbers Quickly and Accurately" by Robert G. Burger and R. Kent Dybvig,
  ;; SIGPLAN'96 Conference on Programming Language Design an Implementation.

  ;; v is a flonum
  ;; f is an exact integer (fixnum or bignum)
  ;; e is an exact integer (fixnum only)

  (define (10^ n) ;; 0 <= n < 326
    (if (use-fast-bignum-algorithms)
        (##vector-ref ##10^-constants n)
        (##expt 10 n)))

  (define (base-10-log x)
    (##define-macro (1/log10) `',(/ (log 10)))
    (##fl* (##fllog x) (1/log10)))

  (##define-macro (epsilon)
    1e-10)

  (define (scale r s m+ m- round? v)

    ;; r is an exact integer (fixnum or bignum)
    ;; s is an exact integer (fixnum or bignum)
    ;; m+ is an exact integer (fixnum or bignum)
    ;; m- is an exact integer (fixnum or bignum)
    ;; round? is a boolean
    ;; v is a flonum

    (let ((est
           (##flonum->fixnum
            (##flceiling (##fl- (base-10-log v) (epsilon))))))
      (if (##fxnegative? est)
          (let ((factor (10^ (##fx- est))))
            (fixup (##* r factor)
                   s
                   (##* m+ factor)
                   (##* m- factor)
                   est
                   round?))
          (let ((factor (10^ est)))
            (fixup r
                   (##* s factor)
                   m+
                   m-
                   est
                   round?)))))

  (define (fixup r s m+ m- k round?)
    (if (if round?
            (##not (##< (##+ r m+) s))
            (##< s (##+ r m+)))
        (##cons (##fx+ k 1)
                (generate r
                          s
                          m+
                          m-
                          round?
                          0))
        (##cons k
                (generate (##* r 10)
                          s
                          (##* m+ 10)
                          (##* m- 10)
                          round?
                          0))))

  (define (generate r s m+ m- round? n)
    (let* ((dr (##exact-int.div r s))
           (d (macro-qr-q dr))
           (r (macro-qr-r dr))
           (tc (if round?
                   (##not (##< (##+ r m+) s))
                   (##< s (##+ r m+)))))
      (if (if round? (##not (##< m- r)) (##< r m-))
          (let* ((last-digit
                  (if tc
                      (let ((r*2 (##arithmetic-shift r 1)))
                        (if (or (and (##fxeven? d)
                                     (##= r*2 s)) ;; tie, round d to even
                                (##< r*2 s))
                            d
                            (##fx+ d 1)))
                      d))
                 (str
                  (##make-string (##fx+ n 1))))
            (##string-set!
             str
             n
             (##string-ref ##digit-to-char-table last-digit))
            str)
          (if tc
              (let ((str
                     (##make-string (##fx+ n 1))))
                (##string-set!
                 str
                 n
                 (##string-ref ##digit-to-char-table (##fx+ d 1)))
                str)
              (let ((str
                     (generate (##* r 10)
                               s
                               (##* m+ 10)
                               (##* m- 10)
                               round?
                               (##fx+ n 1))))
                (##string-set!
                 str
                 n
                 (##string-ref ##digit-to-char-table d))
                str)))))

  (define (flonum->exponent-and-digits v)
    (macro-if-bignum
     (flonum->exponent-and-digits-accurately v)
     (flonum->exponent-and-digits-host v)))

  (define (flonum->exponent-and-digits-accurately v)
    (let* ((x (##flonum->exact-exponential-format v))
           (f (##vector-ref x 0))
           (e (##vector-ref x 1))
           (round? (##not (##odd? f))))
      (if (##fxnegative? e)
          (if (and (##not (##fx= e (macro-flonum-e-min)))
                   (##= f (macro-flonum-+m-min)))
              (scale (##arithmetic-shift f 2)
                     (##arithmetic-shift 1 (##fx- 2 e))
                     2
                     1
                     round?
                     v)
              (scale (##arithmetic-shift f 1)
                     (##arithmetic-shift 1 (##fx- 1 e))
                     1
                     1
                     round?
                     v))
          (let ((2^e (##arithmetic-shift 1 e)))
            (if (##= f (macro-flonum-+m-min))
                (scale (##arithmetic-shift f (##fx+ e 2))
                       4
                       (##arithmetic-shift 1 (##fx+ e 1))
                       2^e
                       round?
                       v)
                (scale (##arithmetic-shift f (##fx+ e 1))
                       2
                       2^e
                       2^e
                       round?
                       v))))))

  (define (flonum->exponent-and-digits-host v)
    (string->exponent-and-digits (##flonum->string-host v)))

  (define (string->exponent-and-digits str)
    (let loop1 ((i 0)
                (j 0)
                (last-nz #f)
                (decimals #f))

      (define (err)
        (##cons 1 "0error"))

      (define (done e)
        (if last-nz

            (let ((len (##fx+ last-nz 1)))
              (##string-shrink! str len)
              (let ((decimals (##fx- (or decimals 0) (##fx- j len))))
                (##cons (##fx+ e (##fx- len decimals)) str)))

            (##cons 1 "0")))

      (if (##fx< i (##string-length str))
          (let* ((c (##string-ref str i))
                 (ic (##char->integer c))
                 (d
                  (if (##fx< ic 128)
                      (##u8vector-ref
                       ##char-to-digit-table
                       ic)
                      99)))
            (cond ((##fx= d 0)
                   (loop1 (##fx+ i 1)
                          (if last-nz
                              (begin
                                (##string-set! str j c)
                                (##fx+ j 1))
                              j)
                          last-nz
                          (and decimals (##fx+ decimals 1))))
                  ((##fx< d 10)
                   (##string-set! str j c)
                   (loop1 (##fx+ i 1)
                          (##fx+ j 1)
                          j
                          (and decimals (##fx+ decimals 1))))
                  ((##char=? c #\.)
                   (if decimals
                       (err)
                       (loop1 (##fx+ i 1)
                              j
                              last-nz
                              0)))
                  ((or (##char=? c #\e)
                       (##char=? c #\E))
                   (let ((i (##fx+ i 1)))
                     (if (##fx< i (##string-length str))
                         (let ((sign (##string-ref str i)))
                           (let ((i (if (or (##char=? sign #\-)
                                            (##char=? sign #\+))
                                        (##fx+ i 1)
                                        i)))
                             (if (##not (##fx< i (##string-length str)))
                                 (err)
                                 (let loop2 ((i i)
                                             (e 0))
                                   (if (##fx< i (##string-length str))
                                       (let* ((c (##string-ref str i))
                                              (ic (##char->integer c))
                                              (d
                                               (if (##fx< ic 128)
                                                   (##u8vector-ref
                                                    ##char-to-digit-table
                                                    ic)
                                                   99)))
                                         (if (and (##fx< d 10)
                                                  (##fx< e 100))
                                             (loop2 (##fx+ i 1)
                                                    (##fx+ (##fx* e 10) d))
                                             (err)))
                                       (done (if (##char=? sign #\-)
                                                 (##fx- e)
                                                 e)))))))
                         (err))))
                  ((##fx< i (##string-length str))
                   (err))
                  (else
                   (done 0))))
          (done 0))))

  (let* ((x (flonum->exponent-and-digits v))
         (e (##car x))
         (d (##cdr x))            ;; d = digits
         (n (##string-length d))) ;; n = number of digits

    (cond ((and (##not (##fx< e 0)) ;; 0<=e<=10
                (##not (##fx< 10 e)))

           (cond ((##fx= e 0) ;; e=0

                  ;; Format 1: .DDD    (0.DDD in chez-fp-syntax)

                  (##string-append sign-prefix
                                   (if (macro-chez-fp-syntax) "0." ".")
                                   d))

                 ((##fx< e n) ;; e<n

                  ;; Format 2: D.DDD up to DDD.D

                  (##string-append sign-prefix
                                   (##substring d 0 e)
                                   "."
                                   (##substring d e n)))

                 ((##fx= e n) ;; e=n

                  ;; Format 3: DDD.    (DDD.0 in chez-fp-syntax)

                  (##string-append sign-prefix
                                   d
                                   (if (macro-chez-fp-syntax) ".0" ".")))

                 (else ;; e>n

                  ;; Format 4: DDD000000.    (DDD000000.0 in chez-fp-syntax)

                  (##string-append sign-prefix
                                   d
                                   (##make-string (##fx- e n) #\0)
                                   (if (macro-chez-fp-syntax) ".0" ".")))))

          ((and (##not (##fx< e -2)) ;; -2<=e<=-1
                (##not (##fx< -1 e)))

           ;; Format 5: .0DDD or .00DDD    (0.0DDD or 0.00DDD in chez-fp-syntax)

           (##string-append sign-prefix
                            (if (macro-chez-fp-syntax) "0." ".")
                            (##make-string (##fx- e) #\0)
                            d))

          (else

           ;; Format 6: D.DDDeEEE
           ;;
           ;; This is the most general format.  We insert a period after
           ;; the first digit (unless there is only one digit) and add
           ;; an exponent.

           (##string-append sign-prefix
                            (##substring d 0 1)
                            (if (##fx= n 1) "" ".")
                            (##substring d 1 n)
                            "e"
                            (##exact-int->string (##fx- e 1) 10 #f))))))

(define-prim (##flonum->string x rad force-sign?)

  (define (non-neg-num->str x rad sign-prefix)
    (if (##flzero? x)
        (##string-append sign-prefix (if (macro-chez-fp-syntax) "0.0" "0."))
        (##flonum-printout x sign-prefix)))

  (cond ((##flnan? x)
         (##string-copy (if (or (macro-r6rs-fp-syntax)
                                (macro-chez-fp-syntax))
                            "+nan.0"
                            "+nan.")))
        ((##flnegative? (##flcopysign (macro-inexact-+1) x))
         (let ((abs-x (##flcopysign x (macro-inexact-+1))))
           (cond ((##fl= abs-x (macro-inexact-+inf))
                  (##string-copy (if (or (macro-r6rs-fp-syntax)
                                         (macro-chez-fp-syntax))
                                     "-inf.0"
                                     "-inf.")))
                 (else
                  (non-neg-num->str abs-x rad "-")))))
        (else
         (cond ((##fl= x (macro-inexact-+inf))
                (##string-copy (if (or (macro-r6rs-fp-syntax)
                                       (macro-chez-fp-syntax))
                                   "+inf.0"
                                   "+inf.")))
               (force-sign?
                (non-neg-num->str x rad "+"))
               (else
                (non-neg-num->str x rad ""))))))

(define-prim (##flonum->string-host x)
  (##flonum->string x 10 #f)) ;; TODO: remove after bootstrap

(define-prim (##cpxnum->string x rad force-sign?)
  (let* ((real
          (macro-cpxnum-real x))
         (real-str
          (if (##eqv? real 0) "" (##number->string real rad force-sign?))))
    (let ((imag (macro-cpxnum-imag x)))
      (cond ((##eqv? imag 1)
             (##string-append real-str "+i"))
            ((##eqv? imag -1)
             (##string-append real-str "-i"))
            (else
             (##string-append real-str
                              (##number->string imag rad #t)
                              "i"))))))

(define-prim (##number->string x #!optional (rad 10) (force-sign? #f))
  (macro-number-dispatch x '()
    (##exact-int->string x rad force-sign?)
    (let ((normalized-x (or (##bignum->fixnum? x) x))) ;; allow conversion of unnormalized bignums
      (##exact-int->string normalized-x rad force-sign?))
    (##ratnum->string x rad force-sign?)
    (##flonum->string x rad force-sign?)
    (##cpxnum->string x rad force-sign?)))

(define-prim (number->string n #!optional (r (macro-absent-obj)))
  (macro-force-vars (n r)
    (let ((rad (if (##eq? r (macro-absent-obj)) 10 r)))
      (if (macro-exact-int? rad)
          (if (or (##eqv? rad 2)
                  (##eqv? rad 8)
                  (##eqv? rad 10)
                  (##eqv? rad 16))
              (let ((result (##number->string n rad #f)))
                (if (##null? result)
                    (##fail-check-number 1 number->string n r)
                    result))
              (##raise-range-exception 2 number->string n r))
          (##fail-check-exact-integer 2 number->string n r)))))

(##define-macro (macro-make-char-to-digit-table)
  (let ((t (make-vector 128 99)))
    (vector-set! t (char->integer #\#) 0) ;; #\# counts as 0
    (let loop1 ((i 9))
      (if (not (< i 0))
          (begin
            (vector-set! t (+ (char->integer #\0) i) i)
            (loop1 (- i 1)))))
    (let loop2 ((i 25))
      (if (not (< i 0))
          (begin
            (vector-set! t (+ (char->integer #\A) i) (+ i 10))
            (vector-set! t (+ (char->integer #\a) i) (+ i 10))
            (loop2 (- i 1)))))
    `',(list->u8vector (vector->list t))))

(define ##char-to-digit-table (macro-make-char-to-digit-table))

(define-prim (##string->number str #!optional (rad 10) (check-only? #f))

  ;; The number grammar parsed by this procedure is:
  ;;
  ;; <num R E> : <prefix R E> <complex R E>
  ;;
  ;; <complex R E> : <real R E>
  ;;               | <real R E> @ <real R E>
  ;;               | <real R E> <sign> <ureal R> i
  ;;               | <real R E> <sign-inf-nan R E> i
  ;;               | <real R E> <sign> i
  ;;               | <sign> <ureal R> i
  ;;               | <sign-inf-nan R E> i
  ;;               | <sign> i
  ;;
  ;; <real R E> : <ureal R>
  ;;            | <sign> <ureal R>
  ;;            | <sign-inf-nan R E>
  ;;
  ;; <sign-inf-nan R i> : +inf.0
  ;;                    | -inf.0
  ;;                    | +nan.0
  ;; <sign-inf-nan R empty> : <sign-inf-nan R i>
  ;;
  ;; <ureal R> : <uinteger R>
  ;;           | <uinteger R> / <uinteger R>
  ;;           | <decimal R>
  ;;
  ;; <decimal 10> : <uinteger 10> <suffix>
  ;;              | . <digit 10>+ #* <suffix>
  ;;              | <digit 10>+ . <digit 10>* #* <suffix>
  ;;              | <digit 10>+ #+ . #* <suffix>
  ;;
  ;; <uinteger R> : <digit R>+ #*
  ;;
  ;; <prefix R E> : <radix R E> <exactness E>
  ;;              | <exactness E> <radix R E>
  ;;
  ;; <suffix> : <empty>
  ;;          | <exponent marker> <digit 10>+
  ;;          | <exponent marker> <sign> <digit 10>+
  ;;
  ;; <exponent marker> : e | s | f | d | l
  ;; <sign> : + | -
  ;; <exactness empty> : <empty>
  ;; <exactness i> : #i
  ;; <exactness e> : #e
  ;; <radix 2> : #b
  ;; <radix 8> : #o
  ;; <radix 10> : <empty> | #d
  ;; <radix 16> : #x
  ;; <digit 2> : 0 | 1
  ;; <digit 8> : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
  ;; <digit 10> : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
  ;; <digit 16> : <digit 10> | a | b | c | d | e | f

  (##define-macro (macro-make-exact-10^n-table)

    (define max-exact-power-of-10 22) ;; (floor (inexact->exact (/ (log (expt 2 (macro-flonum-m-bits-plus-1))) (log 5))))

    (let ((t (make-vector (+ max-exact-power-of-10 1))))

      (let loop ((i max-exact-power-of-10))
        (if (not (< i 0))
            (begin
              (vector-set! t i (exact->inexact (expt 10 i)))
              (loop (- i 1)))))

      `',(list->f64vector (vector->list t))))

  (define exact-10^n-table (macro-make-exact-10^n-table))

  (##define-macro (macro-make-block-size)
    (let* ((max-rad 16)
           (t (make-vector (+ max-rad 1) 0)))

      (define max-fixnum 536870911) ;; OK to be conservative

      (define (block-size-for rad)
        (let loop ((i 0) (rad^i 1))
          (let ((new-rad^i (* rad^i rad)))
            (if (<= new-rad^i max-fixnum)
                (loop (+ i 1) new-rad^i)
                i))))

      (let loop ((i max-rad))
        (if (< 1 i)
            (begin
              (vector-set! t i (block-size-for i))
              (loop (- i 1)))))

      `',t))

  (define block-size (macro-make-block-size))

  (##define-macro (macro-make-rad^block-size)
    (let* ((max-rad 16)
           (t (make-vector (+ max-rad 1) 0)))

      (define max-fixnum 536870911) ;; OK to be conservative

      (define (rad^block-size-for rad)
        (let loop ((i 0) (rad^i 1))
          (let ((new-rad^i (* rad^i rad)))
            (if (<= new-rad^i max-fixnum)
                (loop (+ i 1) new-rad^i)
                rad^i))))

      (let loop ((i max-rad))
        (if (< 1 i)
            (begin
              (vector-set! t i (rad^block-size-for i))
              (loop (- i 1)))))

      `',t))

  (define rad^block-size (macro-make-rad^block-size))

  (define (substring->uinteger-fixnum str rad i j)

    ;; Simple case: result is known to fit in a fixnum.

    (let loop ((i i) (n 0))
      (if (##fx< i j)
          (let* ((c (##string-ref str i))
                 (ic (##char->integer c)))
            (loop (##fx+ i 1)
                  (##fx+ (##fx* n rad)
                         (if (##fx< ic 128)
                             (##u8vector-ref ##char-to-digit-table ic)
                             0))))
          n)))

  (define (substring->uinteger-aux sqs width str rad i j)

    ;; Divide-and-conquer algorithm (fast for large bignums if bignum
    ;; multiplication is fast).

    (if (##null? sqs)
        (substring->uinteger-fixnum str rad i j)
        (let* ((new-sqs (##cdr sqs))
               (new-width (##fxquotient width 2))
               (mid (##fx- j new-width)))
          (if (##fx< i mid)
              (let* ((a (substring->uinteger-aux new-sqs new-width str rad i mid))
                     (b (substring->uinteger-aux new-sqs new-width str rad mid j)))
                (##+ (##* a (##car sqs)) b))
              (substring->uinteger-aux new-sqs new-width str rad i j)))))

  (define (squares rad n)
    (let loop ((rad rad) (n n) (lst '()))
      (if (##fx= n 1)
          (##cons rad lst)
          (loop (##exact-int.square rad)
                (##fx- n 1)
                (##cons rad lst)))))

  (define (substring->uinteger str rad i j)

    ;; Converts a substring into an unsigned integer.  Selects a fast
    ;; conversion algorithm when result fits in a fixnum.

    (let ((len (##fx- j i))
          (size (##vector-ref block-size rad)))
      (if (##fx< size len)
          (let ((levels
                 (##integer-length (##fxquotient (##fx- len 1) size))))
            (substring->uinteger-aux
             (squares (##vector-ref rad^block-size rad) levels)
             (##fxarithmetic-shift-left size levels)
             str
             rad
             i
             j))
          (substring->uinteger-fixnum str rad i j))))

  (define (float-substring->uinteger str i j)

    ;; Converts a substring containing the decimals of a floating-point
    ;; number into an unsigned integer (any period is simply skipped).

    (let loop1 ((i i) (n 0))
      (if (##not (##fx< i j))
          n
          (let ((c (##string-ref str i)))
            (if (##char=? c #\.)
                (loop1 (##fx+ i 1) n)
                (let* ((ic
                        (##char->integer c))
                       (new-n
                        (##fx+ (##fx* n 10)
                               (if (##fx< ic 128)
                                   (##u8vector-ref ##char-to-digit-table ic)
                                   0))))
                  (if (##fx< new-n (macro-max-fixnum32-div-10))
                      (loop1 (##fx+ i 1) new-n)
                      (let loop2 ((i i) (n n))
                        (if (##not (##fx< i j))
                            n
                            (let ((c (##string-ref str i)))
                              (if (##char=? c #\.)
                                  (loop2 (##fx+ i 1) n)
                                  (let* ((ic
                                          (##char->integer c))
                                         (new-n
                                          (##+ (##* n 10)
                                               (if (##fx< ic 128)
                                                   (##u8vector-ref ##char-to-digit-table ic)
                                                   0))))
                                    (loop2 (##fx+ i 1) new-n)))))))))))))

  (define (uinteger str rad i)
    (and (##fx< i (##string-length str))
         (let* ((c (##string-ref str i))
                (ic (##char->integer c)))
           (and (##fx< ic 128)
                (##not (##char=? c #\#))
                (##fx< (##u8vector-ref ##char-to-digit-table ic) rad)
                (digits-and-sharps str rad (##fx+ i 1))))))

  (define (digits-and-sharps str rad i)
    (let loop ((i i))
      (if (##fx< i (##string-length str))
          (let* ((c (##string-ref str i))
                 (ic (##char->integer c)))
            (if (##fx< ic 128)
                (if (##char=? c #\#)
                    (sharps str (##fx+ i 1))
                    (if (##fx< (##u8vector-ref ##char-to-digit-table ic) rad)
                        (loop (##fx+ i 1))
                        i))
                i))
          i)))

  (define (sharps str i)
    (let loop ((i i))
      (if (##fx< i (##string-length str))
          (if (##char=? (##string-ref str i) #\#)
              (loop (##fx+ i 1))
              i)
          i)))

  (define (suffix str i1)
    (if (##fx< (##fx+ i1 1) (##string-length str))
        (let ((c1 (##string-ref str i1)))
          (if (or (##char=? c1 #\e) (##char=? c1 #\E)
                  (##char=? c1 #\s) (##char=? c1 #\S)
                  (##char=? c1 #\f) (##char=? c1 #\F)
                  (##char=? c1 #\d) (##char=? c1 #\D)
                  (##char=? c1 #\l) (##char=? c1 #\L))
              (let ((c2 (##string-ref str (##fx+ i1 1))))
                (let ((i2
                       (if (or (##char=? c2 #\+) (##char=? c2 #\-))
                           (uinteger str 10 (##fx+ i1 2))
                           (uinteger str 10 (##fx+ i1 1)))))
                  (if (and i2
                           (##not (##char=? (##string-ref str (##fx- i2 1))
                                            #\#)))
                      i2
                      i1)))
              i1))
        i1))

  (define (ureal str rad e i1)
    (let ((i2 (uinteger str rad i1)))
      (if i2
          (if (##fx< i2 (##string-length str))
              (let ((c (##string-ref str i2)))
                (cond ((##char=? c #\/)
                       (let ((i3 (uinteger str rad (##fx+ i2 1))))
                         (and i3
                              (let ((inexact-num?
                                     (or (##eq? e 'i)
                                         (and (##not e)
                                              (or (##char=? (##string-ref
                                                             str
                                                             (##fx- i2 1))
                                                            #\#)
                                                  (##char=? (##string-ref
                                                             str
                                                             (##fx- i3 1))
                                                            #\#))))))
                                (if (and (##not inexact-num?)
                                         (##eqv? (substring->uinteger
                                                  str
                                                  rad
                                                  (##fx+ i2 1)
                                                  i3)
                                                 0))
                                    #f
                                    (##vector i3 i2))))))
                      ((##fx= rad 10)
                       (if (##char=? c #\.)
                           (let ((i3
                                  (if (##char=? (##string-ref str (##fx- i2 1))
                                                #\#)
                                      (sharps str (##fx+ i2 1))
                                      (digits-and-sharps str 10 (##fx+ i2 1)))))
                             (and i3
                                  (let ((i4 (suffix str i3)))
                                    (##vector i4 i3 i2))))
                           (let ((i3 (suffix str i2)))
                             (if (##fx= i2 i3)
                                 i2
                                 (##vector i3 i2 i2)))))
                      (else
                       i2)))
              i2)
          (and (##fx= rad 10)
               (##fx< i1 (##string-length str))
               (##char=? (##string-ref str i1) #\.)
               (let ((i3 (uinteger str rad (##fx+ i1 1))))
                 (and i3
                      (let ((i4 (suffix str i3)))
                        (##vector i4 i3 i1))))))))

  (define (inf-nan str sign i e)
    (and (##not (##eq? e 'e))
         (if (##fx< (##fx+ i (if (or (macro-r6rs-fp-syntax)
                                     (macro-chez-fp-syntax))
                                 4
                                 3))
                    (##string-length str))
             (and (##char=? (##string-ref str (##fx+ i 3)) #\.)
                  (if (or (macro-r6rs-fp-syntax)
                          (macro-chez-fp-syntax))
                      (##char=? (##string-ref str (##fx+ i 4)) #\0)
                      #t)
                  (or (and (let ((c (##string-ref str i)))
                             (or (##char=? c #\i) (##char=? c #\I)))
                           (let ((c (##string-ref str (##fx+ i 1))))
                             (or (##char=? c #\n) (##char=? c #\N)))
                           (let ((c (##string-ref str (##fx+ i 2))))
                             (or (##char=? c #\f) (##char=? c #\F))))
                      (and (##not (##char=? sign #\-))
                           (let ((c (##string-ref str i)))
                             (or (##char=? c #\n) (##char=? c #\N)))
                           (let ((c (##string-ref str (##fx+ i 1))))
                             (or (##char=? c #\a) (##char=? c #\A)))
                           (let ((c (##string-ref str (##fx+ i 2))))
                             (or (##char=? c #\n) (##char=? c #\N)))))
                  (##vector (##fx+ i (if (or (macro-r6rs-fp-syntax)
                                             (macro-chez-fp-syntax))
                                         5
                                         4))))
             #f)))

  (define (make-rec x y)
    (##make-rectangular x y))

  (define (make-pol x y e)
    (let ((n (##make-polar x y)))
      (if (##eq? e 'e)
          (##inexact->exact n)
          n)))

  (define (make-inexact-real sign uinteger exponent)
    (let ((n
           (if (and (##fixnum? uinteger)
                    (##fixnum->flonum-exact? uinteger)
                    (##fixnum? exponent)
                    (##fx< (##fx- exponent)
                           (##f64vector-length exact-10^n-table))
                    (##fx< exponent
                           (##f64vector-length exact-10^n-table)))
               (if (##fx< exponent 0)
                   (##fl/ (##fixnum->flonum uinteger)
                          (##f64vector-ref exact-10^n-table
                                           (##fx- exponent)))
                   (##fl* (##fixnum->flonum uinteger)
                          (##f64vector-ref exact-10^n-table
                                           exponent)))
               (##exact->inexact
                (##* uinteger (##expt 10 exponent))))))
      (if (##char=? sign #\-)
          (##flcopysign n (macro-inexact--1))
          n)))

  (define (get-zero e)
    (if (##eq? e 'i)
        (macro-inexact-+0)
        0))

  (define (get-one sign e)
    (if (##eq? e 'i)
        (if (##char=? sign #\-) (macro-inexact--1) (macro-inexact-+1))
        (if (##char=? sign #\-) -1 1)))

  (define (get-real start sign str rad e i)
    (if (##fixnum? i)
        (let* ((abs-n
                (substring->uinteger str rad start i))
               (n
                (if (##char=? sign #\-)
                    (##negate abs-n)
                    abs-n)))
          (if (or (##eq? e 'i)
                  (and (##not e)
                       (##char=? (##string-ref str (##fx- i 1)) #\#)))
              (##exact->inexact n)
              n))
        (let ((j (##vector-ref i 0))
              (len (##vector-length i)))
          (cond ((##fx= len 3) ;; xxx.yyyEzzz
                 (let* ((after-frac-part
                         (##vector-ref i 1))
                        (unadjusted-exponent
                         (if (##fx= after-frac-part j) ;; no exponent part?
                             0
                             (let* ((c
                                     (##string-ref
                                      str
                                      (##fx+ after-frac-part 1)))
                                    (n
                                     (substring->uinteger
                                      str
                                      10
                                      (if (or (##char=? c #\+) (##char=? c #\-))
                                          (##fx+ after-frac-part 2)
                                          (##fx+ after-frac-part 1))
                                      j)))
                               (if (##char=? c #\-)
                                   (##negate n)
                                   n))))
                        (c
                         (##string-ref str start))
                        (uinteger
                         (float-substring->uinteger str start after-frac-part))
                        (decimals-after-point
                         (##fx-
                          (##fx- after-frac-part (##vector-ref i 2))
                          1))
                        (exponent
                         (if (##fx< 0 decimals-after-point)
                             (if (and (##fixnum? unadjusted-exponent)
                                      (##fx< (##fx- unadjusted-exponent
                                                    decimals-after-point)
                                             unadjusted-exponent))
                                 (##fx- unadjusted-exponent
                                        decimals-after-point)
                                 (##- unadjusted-exponent
                                      decimals-after-point))
                             unadjusted-exponent)))
                   (if (##eq? e 'e)
                       (##*
                        (if (##char=? sign #\-)
                            (##negate uinteger)
                            uinteger)
                        (##expt 10 exponent))
                       (make-inexact-real sign uinteger exponent))))
                ((##fx= len 2) ;; xxx/yyy
                 (let* ((after-num
                         (##vector-ref i 1))
                        (inexact-num?
                         (or (##eq? e 'i)
                             (and (##not e)
                                  (or (##char=? (##string-ref
                                                 str
                                                 (##fx- after-num 1))
                                                #\#)
                                      (##char=? (##string-ref
                                                 str
                                                 (##fx- j 1))
                                                #\#)))))
                        (abs-num
                         (substring->uinteger str rad start after-num))
                        (den
                         (substring->uinteger str
                                              rad
                                              (##fx+ after-num 1)
                                              j)))

                   (define (num-div-den)
                     (##/ (if (##char=? sign #\-)
                              (##negate abs-num)
                              abs-num)
                          den))

                   (if inexact-num?
                       (if (##eqv? den 0)
                           (let ((n
                                  (if (##eqv? abs-num 0)
                                      (macro-inexact-+nan)
                                      (macro-inexact-+inf))))
                             (if (##char=? sign #\-)
                                 (##flcopysign n (macro-inexact--1))
                                 n))
                           (##exact->inexact (num-div-den)))
                       (num-div-den))))
                (else ;; (##fx= len 1) ;; inf or nan
                 (let* ((c
                         (##string-ref str start))
                        (n
                         (if (or (##char=? c #\i) (##char=? c #\I))
                             (macro-inexact-+inf)
                             (macro-inexact-+nan))))
                   (if (##char=? sign #\-)
                       (##flcopysign n (macro-inexact--1))
                       n)))))))

  (define (i-end str i)
    (and (##fx= (##fx+ i 1) (##string-length str))
         (let ((c (##string-ref str i)))
           (or (##char=? c #\i) (##char=? c #\I)))))

  (define (complex start sign str rad e i)
    (let ((j (if (##fixnum? i) i (##vector-ref i 0))))
      (let ((c (##string-ref str j)))
        (cond ((##char=? c #\@)
               (let ((j+1 (##fx+ j 1)))
                 (if (##fx< j+1 (##string-length str))
                     (let* ((sign2
                             (##string-ref str j+1))
                            (start2
                             (if (or (##char=? sign2 #\+) (##char=? sign2 #\-))
                                 (##fx+ j+1 1)
                                 j+1))
                            (k
                             (or (ureal str rad e start2)
                                 (and (##fx< j+1 start2)
                                      (inf-nan str sign2 start2 e)))))
                       (and k
                            (let ((l (if (##fixnum? k) k (##vector-ref k 0))))
                              (and (##fx= l (##string-length str))
                                   (or check-only?
                                       (make-pol
                                        (get-real start sign str rad e i)
                                        (get-real start2 sign2 str rad e k)
                                        e))))))
                     #f)))
              ((or (##char=? c #\+) (##char=? c #\-))
               (let* ((start2
                       (##fx+ j 1))
                      (k
                       (or (ureal str rad e start2)
                           (inf-nan str c start2 e))))
                 (if (##not k)
                     (if (i-end str start2)
                         (or check-only?
                             (make-rec
                              (get-real start sign str rad e i)
                              (get-one c e)))
                         #f)
                     (let ((l (if (##fixnum? k) k (##vector-ref k 0))))
                       (and (i-end str l)
                            (or check-only?
                                (make-rec
                                 (get-real start sign str rad e i)
                                 (get-real start2 c str rad e k))))))))
              (else
               #f)))))

  (define (after-prefix start str rad e)

    ;; invariant: start = 0, 2 or 4, (string-length str) > start

    (let ((c (##string-ref str start)))
      (if (or (##char=? c #\+) (##char=? c #\-))
          (let ((i (or (ureal str rad e (##fx+ start 1))
                       (inf-nan str c (##fx+ start 1) e))))
            (if (##not i)
                (if (i-end str (##fx+ start 1))
                    (or check-only?
                        (make-rec
                         (get-zero e)
                         (get-one c e)))
                    #f)
                (let ((j (if (##fixnum? i) i (##vector-ref i 0))))
                  (cond ((##fx= j (##string-length str))
                         (or check-only?
                             (get-real (##fx+ start 1) c str rad e i)))
                        ((i-end str j)
                         (or check-only?
                             (make-rec
                              (get-zero e)
                              (get-real (##fx+ start 1) c str rad e i))))
                        (else
                         (complex (##fx+ start 1) c str rad e i))))))
          (let ((i (ureal str rad e start)))
            (if (##not i)
                #f
                (let ((j (if (##fixnum? i) i (##vector-ref i 0))))
                  (cond ((##fx= j (##string-length str))
                         (or check-only?
                             (get-real start #\+ str rad e i)))
                        (else
                         (complex start #\+ str rad e i)))))))))

  (define (radix-prefix c)
    (cond ((or (##char=? c #\b) (##char=? c #\B))  2)
          ((or (##char=? c #\o) (##char=? c #\O))  8)
          ((or (##char=? c #\d) (##char=? c #\D)) 10)
          ((or (##char=? c #\x) (##char=? c #\X)) 16)
          (else                                   #f)))

  (define (exactness-prefix c)
    (cond ((or (##char=? c #\i) (##char=? c #\I)) 'i)
          ((or (##char=? c #\e) (##char=? c #\E)) 'e)
          (else                                   #f)))

  (cond ((##fx< 2 (##string-length str)) ;; >= 3 chars
         (if (##char=? (##string-ref str 0) #\#)
             (let ((rad1 (radix-prefix (##string-ref str 1))))
               (if rad1
                   (if (and (##fx< 4 (##string-length str)) ;; >= 5 chars
                            (##char=? (##string-ref str 2) #\#))
                       (let ((e1 (exactness-prefix (##string-ref str 3))))
                         (if e1
                             (after-prefix 4 str rad1 e1)
                             #f))
                       (after-prefix 2 str rad1 #f))
                   (let ((e2 (exactness-prefix (##string-ref str 1))))
                     (if e2
                         (if (and (##fx< 4 (##string-length str)) ;; >= 5 chars
                                  (##char=? (##string-ref str 2) #\#))
                             (let ((rad2 (radix-prefix (##string-ref str 3))))
                               (if rad2
                                   (after-prefix 4 str rad2 e2)
                                   #f))
                             (after-prefix 2 str rad e2))
                         #f))))
             (after-prefix 0 str rad #f)))
        ((##fx< 0 (##string-length str)) ;; >= 1 char
         (after-prefix 0 str rad #f))
        (else
         #f)))

(define-prim (string->number str #!optional (r (macro-absent-obj)))
  (macro-force-vars (str r)
    (macro-check-string str 1 (string->number str r)
      (let ((rad (if (##eq? r (macro-absent-obj)) 10 r)))
        (if (macro-exact-int? rad)
            (if (or (##eqv? rad 2)
                    (##eqv? rad 8)
                    (##eqv? rad 10)
                    (##eqv? rad 16))
                (##string->number str rad #f)
                (##raise-range-exception 2 string->number str r))
            (##fail-check-exact-integer 2 string->number str r))))))

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; Bitwise operations.

(define-prim (##bitwise-ior x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (define (bignum-bitwise-ior-loop x result n)
    (##declare (not interrupts-enabled))
    (let loop ((i (##fx- n 1)))
      (if (##fx< i 0)
          (##bignum.normalize! result)
          (begin
            (##bignum.adigit-bitwise-ior! result i x i)
            (loop (##fx- i 1))))))

  (define (bignum-bitwise-ior x x-length y y-length)
    ;; x-length <= y-length
    (if (##bignum.negative? x)
        (bignum-bitwise-ior-loop y (##bignum.copy x) x-length)
        (bignum-bitwise-ior-loop x (##bignum.copy y) x-length)))

  (macro-exact-int-dispatch x (type-error-on-x)

    (macro-exact-int-dispatch y (type-error-on-y) ;; x = fixnum
      (##fxior x y)
      (let* ((x-bignum (##fixnum->bignum x))
             (x-length (##bignum.adigit-length x-bignum))
             (y-length (##bignum.adigit-length y)))
        (bignum-bitwise-ior x-bignum x-length y y-length)))

    (let ((x-length (##bignum.adigit-length x))) ;; x = bignum
      (macro-exact-int-dispatch y (type-error-on-y)
        (let* ((y-bignum (##fixnum->bignum y))
               (y-length (##bignum.adigit-length y-bignum)))
          (bignum-bitwise-ior y-bignum y-length x x-length))
        (let ((y-length (##bignum.adigit-length y)))
          (if (##fx< x-length y-length)
              (bignum-bitwise-ior x x-length y y-length)
              (bignum-bitwise-ior y y-length x x-length)))))))

(define-prim-nary (bitwise-ior x y)
  0
  (if (macro-exact-int? x) x '(1))
  (##bitwise-ior x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-exact-integer))

(define-prim (##bitwise-xor x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (define (bignum-bitwise-xor x x-length y y-length)
    (let ((result (##bignum.copy y)))
      (##declare (not interrupts-enabled))
      (let loop1 ((i 0))
        (if (##fx< i x-length)
            (begin
              (##bignum.adigit-bitwise-xor! result i x i)
              (loop1 (##fx+ i 1)))
            (if (##bignum.negative? x)
                (let loop2 ((i i))
                  (if (##fx< i y-length)
                      (begin
                        (##bignum.adigit-bitwise-not! result i)
                        (loop2 (##fx+ i 1)))
                      (##bignum.normalize! result)))
                (##bignum.normalize! result))))))

  (macro-exact-int-dispatch x (type-error-on-x)

    (macro-exact-int-dispatch y (type-error-on-y) ;; x = fixnum
      (##fxxor x y)
      (let* ((x-bignum (##fixnum->bignum x))
             (x-length (##bignum.adigit-length x-bignum))
             (y-length (##bignum.adigit-length y)))
        (bignum-bitwise-xor x-bignum x-length y y-length)))

    (let ((x-length (##bignum.adigit-length x))) ;; x = bignum
      (macro-exact-int-dispatch y (type-error-on-y)
        (let* ((y-bignum (##fixnum->bignum y))
               (y-length (##bignum.adigit-length y-bignum)))
          (bignum-bitwise-xor y-bignum y-length x x-length))
        (let ((y-length (##bignum.adigit-length y)))
          (if (##fx< x-length y-length)
              (bignum-bitwise-xor x x-length y y-length)
              (bignum-bitwise-xor y y-length x x-length)))))))

(define-prim-nary (bitwise-xor x y)
  0
  (if (macro-exact-int? x) x '(1))
  (##bitwise-xor x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-exact-integer))

(define-prim (##bitwise-and x y)

  (##define-macro (type-error-on-x) `'(1))
  (##define-macro (type-error-on-y) `'(2))

  (define (bignum-bitwise-and-loop x result n)
    (##declare (not interrupts-enabled))
    (let loop ((i (##fx- n 1)))
      (if (##fx< i 0)
          (##bignum.normalize! result)
          (begin
            (##bignum.adigit-bitwise-and! result i x i)
            (loop (##fx- i 1))))))

  (define (bignum-bitwise-and x x-length y y-length)
    ;; x-length <= y-length
    (if (##bignum.negative? x)
        (bignum-bitwise-and-loop x (##bignum.copy y) x-length)
        (bignum-bitwise-and-loop y (##bignum.copy x) x-length)))

  (macro-exact-int-dispatch x (type-error-on-x)

    (macro-exact-int-dispatch y (type-error-on-y) ;; x = fixnum
      (##fxand x y)
      (let* ((x-bignum (##fixnum->bignum x))
             (x-length (##bignum.adigit-length x-bignum))
             (y-length (##bignum.adigit-length y)))
        (bignum-bitwise-and x-bignum x-length y y-length)))

    (let ((x-length (##bignum.adigit-length x))) ;; x = bignum
      (macro-exact-int-dispatch y (type-error-on-y)
        (let* ((y-bignum (##fixnum->bignum y))
               (y-length (##bignum.adigit-length y-bignum)))
          (bignum-bitwise-and y-bignum y-length x x-length))
        (let ((y-length (##bignum.adigit-length y)))
          (if (##fx< x-length y-length)
              (bignum-bitwise-and x x-length y y-length)
              (bignum-bitwise-and y y-length x x-length)))))))

(define-prim-nary (bitwise-and x y)
  -1
  (if (macro-exact-int? x) x '(1))
  (##bitwise-and x y)
  macro-force-vars
  macro-no-check
  (##pair? ##fail-check-exact-integer))

(define-prim (##bitwise-not x)

  (define (type-error)
    (##fail-check-exact-integer 1 bitwise-not x))

  (macro-exact-int-dispatch x (type-error)
    (##fxnot x)
    (##bignum.make (##bignum.adigit-length x) x #t)))  ;; don't copy, bitwise invert

(define-prim (bitwise-not x)
  (macro-force-vars (x)
    (##bitwise-not x)))

(define-prim (##arithmetic-shift x y)

  (define (type-error-on-x)
    (##fail-check-exact-integer 1 arithmetic-shift x y))

  (define (type-error-on-y)
    (##fail-check-exact-integer 2 arithmetic-shift x y))

  (define (fixnum-overflow)
    (##raise-fixnum-overflow-exception arithmetic-shift x y))

  (define (overflow)
    (##raise-heap-overflow-exception)
    (##arithmetic-shift x y))

  (define (general-fixnum-fixnum-case)
    (macro-if-bignum
     (##bignum.arithmetic-shift (##fixnum->bignum x) y)
     (fixnum-overflow)))

  (macro-exact-int-dispatch x (type-error-on-x)

    (macro-exact-int-dispatch y (type-error-on-y) ;; x = fixnum
      (cond ((##fxzero? y)
             x)
            ((##fxnegative? y) ;; right shift
             (if (##fx< (##fx- ##fixnum-width) y)
                 (##fxarithmetic-shift-right x (##fx- y))
                 (if (##fxnegative? x)
                     -1
                     0)))
            (else ;; left shift
             (or (and (##fx< y ##fixnum-width)
                      (##fxarithmetic-shift-left? x y))
                 (general-fixnum-fixnum-case))))
      (cond ((##fxzero? x)
             0)
            ((##bignum.negative? y)
             (if (##fxnegative? x)
                 -1
                 0))
            (else
             (overflow))))

    (macro-exact-int-dispatch y (type-error-on-y) ;; x = bignum
      (cond ((##fxzero? y)
             x)
            (else
             (##bignum.arithmetic-shift x y)))
      (cond ((##bignum.negative? y)
             (if (##bignum.negative? x)
                 -1
                 0))
            (else
             (overflow))))))

(define-prim (arithmetic-shift x y)
  (macro-force-vars (x y)
    (##arithmetic-shift x y)))

(define-prim (##bit-count x)

  (define (type-error)
    (##fail-check-exact-integer 1 bit-count x))

  (macro-exact-int-dispatch x (type-error)
    (##fxbit-count x)
    (let ((x-length (##bignum.mdigit-length x)))
      (let loop ((i (##fx- x-length 1))
                 (n 0))
        (if (##fx< i 0)
            (if (##bignum.negative? x)
                (##fx- (##fx* x-length ##bignum.mdigit-width) n)
                n)
            (loop (##fx- i 1)
                  (##fx+ n (##fxbit-count (##bignum.mdigit-ref x i)))))))))

(define-prim (bit-count x)
  (macro-force-vars (x)
    (##bit-count x)))

(define-prim (##integer-length x)

  (define (type-error)
    (##fail-check-exact-integer 1 integer-length x))

  (macro-exact-int-dispatch x (type-error)
    (##fxlength x)
    (let ((x-length (##bignum.mdigit-length x)))
      (if (##bignum.negative? x)
          (let loop1 ((i (##fx- x-length 1)))
            (let ((mdigit (##bignum.mdigit-ref x i)))
              (if (##fx= mdigit ##bignum.mdigit-base-minus-1)
                  (loop1 (##fx- i 1))
                  (##fx+
                   (##fxlength (##fx- ##bignum.mdigit-base-minus-1 mdigit))
                   (##fx* i ##bignum.mdigit-width)))))
          (let loop2 ((i (##fx- x-length 1)))
            (let ((mdigit (##bignum.mdigit-ref x i)))
              (if (##fx= mdigit 0)
                  (loop2 (##fx- i 1))
                  (##fx+
                   (##fxlength mdigit)
                   (##fx* i ##bignum.mdigit-width)))))))))

(define-prim (integer-length x)
  (macro-force-vars (x)
    (##integer-length x)))

(define-prim (##bitwise-merge x y z)
  (##bitwise-ior (##bitwise-and (##bitwise-not x) y)
                 (##bitwise-and x z)))

(define-prim (bitwise-merge x y z)
  (macro-force-vars (x y z)
    (cond ((##not (macro-exact-int? x))
           (##fail-check-exact-integer 1 bitwise-merge x y z))
          ((##not (macro-exact-int? y))
           (##fail-check-exact-integer 2 bitwise-merge x y z))
          ((##not (macro-exact-int? z))
           (##fail-check-exact-integer 3 bitwise-merge x y z))
          (else
           (##bitwise-merge x y z)))))

(define-prim (##bit-set? x y)

  (define (type-error-on-x)
    (##fail-check-exact-integer 1 bit-set? x y))

  (define (type-error-on-y)
    (##fail-check-exact-integer 2 bit-set? x y))

  (define (range-error)
    (##raise-range-exception 1 bit-set? x y))

  (macro-exact-int-dispatch x (type-error-on-x)

    (macro-exact-int-dispatch y (type-error-on-y) ;; x = fixnum
      (if (##fxnegative? x)
          (range-error)
          (if (##fx< x ##fixnum-width)
              (##fxodd? (##fxarithmetic-shift-right y x))
              (##fxnegative? y)))
      (if (##fxnegative? x)
          (range-error)
          (let ((i (##fxquotient x ##bignum.mdigit-width)))
            (if (##fx< i (##bignum.mdigit-length y))
                (##fxodd?
                 (##fxarithmetic-shift-right
                  (##bignum.mdigit-ref y i)
                  (##fxmodulo x ##bignum.mdigit-width)))
                (##bignum.negative? y)))))

    (macro-exact-int-dispatch y (type-error-on-y) ;; x = bignum
      (if (##bignum.negative? x)
          (range-error)
          (##fxnegative? y))
      (if (##bignum.negative? x)
          (range-error)
          (##bignum.negative? y)))))

(define-prim (bit-set? x y)
  (macro-force-vars (x y)
    (##bit-set? x y)))

(define-prim (##any-bits-set? x y)
  (##not (##eqv? (##bitwise-and x y) 0)))

(define-prim (any-bits-set? x y)
  (macro-force-vars (x y)
    (cond ((##not (macro-exact-int? x))
           (##fail-check-exact-integer 1 any-bits-set? x y))
          ((##not (macro-exact-int? y))
           (##fail-check-exact-integer 2 any-bits-set? x y))
          (else
           (##any-bits-set? x y)))))

(define-prim (##all-bits-set? x y)
  (##= x (##bitwise-and x y)))

(define-prim (all-bits-set? x y)
  (macro-force-vars (x y)
    (cond ((##not (macro-exact-int? x))
           (##fail-check-exact-integer 1 all-bits-set? x y))
          ((##not (macro-exact-int? y))
           (##fail-check-exact-integer 2 all-bits-set? x y))
          (else
           (##all-bits-set? x y)))))

(define-prim (##first-bit-set x)

  (define (type-error)
    (##fail-check-exact-integer 1 first-bit-set x))

  (macro-exact-int-dispatch x (type-error)
    (##fxfirst-bit-set x)
    (let ((x-length (##bignum.mdigit-length x)))
      (let loop ((i 0))
        (let ((mdigit (##bignum.mdigit-ref x i)))
          (if (##fx= mdigit 0)
              (loop (##fx+ i 1))
              (##fx+
               (##fxfirst-bit-set mdigit)
               (##fx* i ##bignum.mdigit-width))))))))

(define-prim (first-bit-set x)
  (macro-force-vars (x)
    (##first-bit-set x)))

(define-prim (##extract-bit-field size position n)

  ;; I've decided to be brutally simple and not optimize
  ;; for special cases, fixnums, etc.

  (let* ((result-length
          (##fxceiling-ratio (##fx+ 1 size) ##bignum.adigit-width))  ;;  top bit is always 0
         (bignum-n
          (if (##bignum? n) n (##fixnum->bignum n)))
         (result
          (##bignum.arithmetic-shift-into!
           bignum-n (##fx- position) (##bignum.make result-length #f #f))))

    ;; zero top bits of result and normalize

    (let ((size-words (##fxquotient  size ##bignum.mdigit-width))
          (size-bits  (##fxremainder size ##bignum.mdigit-width)))
      (##declare (not interrupts-enabled))
      (let loop ((i (##fx- (##bignum.mdigit-length result) 1)))
        (if (##fx< size-words i)
            (begin
              (##bignum.mdigit-set! result i 0)
              (loop (##fx- i 1)))
            (##bignum.mdigit-set!
             result i
             (##fxand
              (##bignum.mdigit-ref result i)
              (##fxnot (##fxarithmetic-shift-left -1 size-bits)))))
        (##bignum.normalize! result)))))

(define-prim (extract-bit-field size position n)
  (macro-force-vars (size position n)
    (macro-check-index
      size
      1
      (extract-bit-field size position n)
      (macro-check-index
        position
        2
        (extract-bit-field size position n)
        (if (##not (macro-exact-int? n))
            (##fail-check-exact-integer 3 extract-bit-field size position n)
            (##extract-bit-field size position n))))))

(define-prim (##test-bit-field? size position n)
  (##not (##eqv? (##extract-bit-field size position n)
                 0)))

(define-prim (test-bit-field? size position n)
  (macro-force-vars (size position n)
    (macro-check-index
      size
      1
      (test-bit-field? size position n)
      (macro-check-index
        position
        2
        (test-bit-field? size position n)
        (if (##not (macro-exact-int? n))
            (##fail-check-exact-integer 3 test-bit-field? size position n)
            (##test-bit-field? size position n))))))

(define-prim (##clear-bit-field size position n)
  (##replace-bit-field size position 0 n))

(define-prim (clear-bit-field size position n)
  (macro-force-vars (size position n)
    (macro-check-index
      size
      1
      (clear-bit-field size position n)
      (macro-check-index
        position
        2
        (clear-bit-field size position n)
        (if (##not (macro-exact-int? n))
            (##fail-check-exact-integer 3 clear-bit-field size position n)
            (##clear-bit-field size position n))))))

(define-prim (##replace-bit-field size position newfield n)
  (let ((m (##bit-mask size)))
    (##bitwise-ior
     (##bitwise-and n (##bitwise-not (##arithmetic-shift m position)))
     (##arithmetic-shift (##bitwise-and newfield m) position))))

(define-prim (replace-bit-field size position newfield n)
  (macro-force-vars (size position newfield n)
    (macro-check-index
      size
      1
      (replace-bit-field size position newfield n)
      (macro-check-index
        position
        2
        (replace-bit-field size position newfield n)
        (cond ((##not (macro-exact-int? newfield))
               (##fail-check-exact-integer 3 replace-bit-field size position newfield n))
              ((##not (macro-exact-int? n))
               (##fail-check-exact-integer 4 replace-bit-field size position newfield n))
              (else
               (##replace-bit-field size position newfield n)))))))

(define-prim (##copy-bit-field size position from to)
  (##bitwise-merge
   (##arithmetic-shift (##bit-mask size) position)
   to
   from))

(define-prim (copy-bit-field size position from to)
  (macro-force-vars (size position from to)
    (macro-check-index
      size
      1
      (copy-bit-field size position from to)
      (macro-check-index
        position
        2
        (copy-bit-field size position from to)
        (cond ((##not (macro-exact-int? from))
               (##fail-check-exact-integer 3 copy-bit-field size position from to))
              ((##not (macro-exact-int? to))
               (##fail-check-exact-integer 4 copy-bit-field size position from to))
              (else
               (##copy-bit-field size position from to)))))))

(define-prim (##bit-mask size)
  (##bitwise-not (##arithmetic-shift -1 size)))

;;;----------------------------------------------------------------------------

;;; Fixnum operations
;;; -----------------

(##define-macro (define-prim-fixnum form . special-body)
  (let ((body (if (null? special-body) form `(begin ,@special-body))))
    (cond ((= 1 (length (cdr form)))
           (let* ((name-fn (car form))
                  (name-param1 (cadr form)))
             `(define-prim ,form
                (macro-force-vars (,name-param1)
                  (macro-check-fixnum
                    ,name-param1
                    1
                    ,form
                    ,body)))))
          ((= 2 (length (cdr form)))
           (let* ((name-fn (car form))
                  (name-param1 (cadr form))
                  (name-param2 (caddr form)))
             `(define-prim ,form
                (macro-force-vars (,name-param1 ,name-param2)
                  (macro-check-fixnum
                    ,name-param1
                    1
                    ,form
                    (macro-check-fixnum
                      ,name-param2
                      2
                      ,form
                      ,body))))))
          (else
           (error "define-prim-fixnum supports only 1 or 2 parameter procedures")))))

(define-prim (fixnum? obj)
  (macro-force-vars (obj)
    (##fixnum? obj)))

(define-prim-nary-bool (##fx= x y)
  #t
  #t
  (##fx= x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fx= x y)
  #t
  #t
  (##fx= x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary-bool (##fx< x y)
  #t
  #t
  (##fx< x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fx< x y)
  #t
  #t
  (##fx< x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary-bool (##fx> x y)
  #t
  #t
  (##fx> x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fx> x y)
  #t
  #t
  (##fx> x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary-bool (##fx<= x y)
  #t
  #t
  (##fx<= x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fx<= x y)
  #t
  #t
  (##fx<= x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary-bool (##fx>= x y)
  #t
  #t
  (##fx>= x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fx>= x y)
  #t
  #t
  (##fx>= x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim (##fxzero? x))

(define-prim-fixnum (fxzero? x)
  (##fxzero? x))

(define-prim (##fxpositive? x))

(define-prim-fixnum (fxpositive? x)
  (##fxpositive? x))

(define-prim (##fxnegative? x))

(define-prim-fixnum (fxnegative? x)
  (##fxnegative? x))

(define-prim (##fxodd? x))

(define-prim-fixnum (fxodd? x)
  (##fxodd? x))

(define-prim (##fxeven? x))

(define-prim-fixnum (fxeven? x)
  (##fxeven? x))

(define-prim-nary (##fxmax x y)
  ()
  x
  (##fxmax x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxmax x y)
  ()
  x
  (##fxmax x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary (##fxmin x y)
  ()
  x
  (##fxmin x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxmin x y)
  ()
  x
  (##fxmin x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary (##fxwrap+ x y)
  0
  x
  (##fxwrap+ x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxwrap+ x y)
  0
  x
  (##fxwrap+ x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary (##fx+ x y)
  0
  x
  (##fx+ x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fx+ x y)
  0
  x
  (##fx+? x y)
  macro-force-vars
  macro-check-fixnum
  (##not ##raise-fixnum-overflow-exception))

(define-prim (##fx+? x y))

(define-prim-nary (##fxwrap* x y)
  1
  x
  (##fxwrap* x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxwrap* x y)
  1
  x
  (##fxwrap* x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary (##fx* x y)
  1
  x
  (##fx* x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fx* x y)
  1
  x
  ((lambda (x y)
     (cond ((##fx= y 0)
            0)
           ((##fx= y -1)
            (##fx-? x))
           (else
            (##fx*? x y))))
   x
   y)
  macro-force-vars
  macro-check-fixnum
  (##not ##raise-fixnum-overflow-exception))

(define-prim (##fx*? x y))

(define-prim-nary (##fxwrap- x y)
  ()
  (##fxwrap- x)
  (##fxwrap- x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxwrap- x y)
  ()
  (##fxwrap- x)
  (##fxwrap- x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary (##fx- x y)
  ()
  (##fx- x)
  (##fx- x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fx- x y)
  ()
  (##fx-? x)
  (##fx-? x y)
  macro-force-vars
  macro-check-fixnum
  (##not ##raise-fixnum-overflow-exception))

(define-prim (##fx-? x #!optional (y (macro-absent-obj)))
  (if (##eq? y (macro-absent-obj))
      (##fx-? x)
      (##fx-? x y)))

(define-prim (##fxwrapquotient x y))

(define-prim-fixnum (fxwrapquotient x y)
  (if (##fx= y 0)
      (##raise-divide-by-zero-exception fxwrapquotient x y)
      (##fxwrapquotient x y)))

(define-prim (##fxquotient x y))

(define-prim-fixnum (fxquotient x y)
  (if (##fx= y 0)
      (##raise-divide-by-zero-exception fxquotient x y)
      (if (##fx= y -1)
          (or (##fx-? x)
              (##raise-fixnum-overflow-exception fxquotient x y))
          (##fxquotient x y))))

(define-prim (##fxremainder x y))

(define-prim-fixnum (fxremainder x y)
  (if (##fx= y 0)
      (##raise-divide-by-zero-exception fxremainder x y)
      (##fxremainder x y)))

(define-prim (##fxmodulo x y))

(define-prim-fixnum (fxmodulo x y)
  (if (##fx= y 0)
      (##raise-divide-by-zero-exception fxmodulo x y)
      (##fxmodulo x y)))

(define-prim (##fxnot x)
  (##fx- -1 x))

(define-prim-fixnum (fxnot x)
  (##fxnot x))

(define-prim-nary (##fxand x y)
  -1
  x
  (##fxand x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxand x y)
  -1
  x
  (##fxand x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary (##fxior x y)
  0
  x
  (##fxior x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxior x y)
  0
  x
  (##fxior x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim-nary (##fxxor x y)
  0
  x
  (##fxxor x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fxxor x y)
  0
  x
  (##fxxor x y)
  macro-force-vars
  macro-check-fixnum)

(define-prim (##fxif x y z))

(define-prim (fxif x y z)
  (macro-force-vars (x y z)
    (macro-check-fixnum
      x
      1
      (fxif x y z)
      (macro-check-fixnum
        y
        2
        (fxif x y z)
        (macro-check-fixnum
          z
          3
          (fxif x y z)
          (##fxif x y z))))))

(define-prim (##fxbit-count x))

(define-prim (fxbit-count x)
  (macro-force-vars (x)
    (macro-check-fixnum
      x
      1
      (fxbit-count x)
      (##fxbit-count x))))

(define-prim (##fxlength x))

(define-prim (fxlength x)
  (macro-force-vars (x)
    (macro-check-fixnum
      x
      1
      (fxlength x)
      (##fxlength x))))

(define-prim (##fxfirst-bit-set x))

(define-prim (fxfirst-bit-set x)
  (macro-force-vars (x)
    (macro-check-fixnum
      x
      1
      (fxfirst-bit-set x)
      (##fxfirst-bit-set x))))

(define-prim (##fxbit-set? x y))

(define-prim (fxbit-set? x y)
  (macro-force-vars (x y)
    (macro-check-fixnum-range-incl
      x
      1
      0
      ##fixnum-width
      (fxbit-set? x y)
      (macro-check-fixnum
        y
        2
        (fxbit-set? x y)
        (##fxbit-set? x y)))))

(define-prim (##fxwraparithmetic-shift x y))

(define-prim (fxwraparithmetic-shift x y)
  (macro-force-vars (x y)
    (macro-check-fixnum
      x
      1
      (fxwraparithmetic-shift x y)
      (macro-check-fixnum-range-incl
        y
        2
        ##fixnum-width-neg
        ##fixnum-width
        (fxwraparithmetic-shift x y)
        (##fxwraparithmetic-shift x y)))))

(define-prim (##fxwraparithmetic-shift? x y))

(define-prim (##fxarithmetic-shift x y))

(define-prim-fixnum (fxarithmetic-shift x y)
  (or (##fxarithmetic-shift? x y)
      (##raise-fixnum-overflow-exception fxarithmetic-shift x y)))

(define-prim (##fxarithmetic-shift? x y))

(define-prim (##fxwraparithmetic-shift-left x y))

(define-prim (fxwraparithmetic-shift-left x y)
  (macro-force-vars (x y)
    (macro-check-fixnum
      x
      1
      (fxwraparithmetic-shift-left x y)
      (macro-check-fixnum-range-incl
        y
        2
        0
        ##fixnum-width
        (fxwraparithmetic-shift-left x y)
        (##fxwraparithmetic-shift-left x y)))))

(define-prim (##fxwraparithmetic-shift-left? x y))

(define-prim (##fxarithmetic-shift-left x y))

(define-prim-fixnum (fxarithmetic-shift-left x y)
  (or (##fxarithmetic-shift-left? x y)
      (if (##fx< y 0)
          (##raise-range-exception 2 fxarithmetic-shift-left x y)
          (##raise-fixnum-overflow-exception fxarithmetic-shift-left x y))))

(define-prim (##fxarithmetic-shift-left? x y))

(define-prim (##fxarithmetic-shift-right x y))

(define-prim-fixnum (fxarithmetic-shift-right x y)
  (or (##fxarithmetic-shift-right? x y)
      (##raise-range-exception 2 fxarithmetic-shift-right x y)))

(define-prim (##fxarithmetic-shift-right? x y))

(define-prim (##fxwraplogical-shift-right x y))

(define-prim-fixnum (fxwraplogical-shift-right x y)
  (or (##fxwraplogical-shift-right? x y)
      (##raise-range-exception 2 fxwraplogical-shift-right x y)))

(define-prim (##fxwraplogical-shift-right? x y))

(define-prim (##fxwrapabs x))

(define-prim-fixnum (fxwrapabs x)
  (##fxwrapabs x))

(define-prim (##fxabs x))

(define-prim-fixnum (fxabs x)
  (or (##fxabs? x)
      (##raise-fixnum-overflow-exception fxabs x)))

(define-prim (##fxabs? x))

(define-prim (##fxwrapsquare x))

(define-prim-fixnum (fxwrapsquare x)
  (##fxwrapsquare x))

(define-prim (##fxsquare x))

(define-prim-fixnum (fxsquare x)
  (or (##fxsquare? x)
      (##raise-fixnum-overflow-exception fxsquare x)))

(define-prim (##fxsquare? x))

(define-prim (##integer->char x))
(define-prim (##char->integer x))

;;; ------------------------------------------------------------------------------
;;; Bignum Operations
;;; ------------------------------------------------------------------------------
;;;
;;; The bignum operations were implemented mostly by Brad Lucier
;;; (http://www.math.purdue.edu/~lucier) with some coding guidance from
;;; Marc Feeley.
;;;
;;; The low-level representation of bignums and the low-level operations on
;;; bignums are inspired by the paper
;;;
;;; Reconfigurable, retargetable bignums:
;;; a case study in efficient, portable Lisp system building
;;; Jon L White
;;; Conference on LISP and Functional Programming
;;; Proceedings of the 1986 ACM conference on LISP and functional programming
;;; Cambridge, Massachusetts, United States
;;; Pages: 174 - 191
;;; Year of Publication: 1986
;;; ISBN:0-89791-200-4
;;;
;;; We describe here the representation for the C back end.  See _univlib.scm for
;;; other back ends.
;;;
;;; Bignums are represented as vectors of "adigit"s.  Each element is an unsigned
;;; integer containing ##bignum.adigit-width bits, which is 64 bits if a 64-bit
;;; type is available (either as long or as long long).  Logically, the 0th adigit
;;; of a bignum contains its least-significant bits; bignums are little-endian,
;;; and the top bit of the last adigit is interpreted as the sign bit of the bignum.
;;; Before being returned to the user, bignums must be normalized so that they have
;;; no redundant all-zero or all-one high-order adigits.  Adigits are so called
;;; because they're used in addition (among other operations).  For the purpose of
;;; documentation we'll use "adigit-base" to represent (expt 2 ##bignum.adigit-width).
;;;
;;; The bits of a bignum can be accessed as a vector of "mdigit"s, which are
;;; unsigned integers containing ##bignum.mdigit-width bits, which is 16 bits
;;; on a 32-bit Gambit or 32 bits on a 64-bit Gambit (so an mdigit always fits
;;; in a fixnum).  Mdigits are so called because they're used in multiplciation
;;; (among other operations). For the purpose of documentation we'll use "mdigit-base"
;;; to represent (expt 2 ##bignum.mdigit-width).
;;;
;;; Finally, the bits of a bignum can be accessed as a vector of "fdigit"s,
;;; which are unsigned integers containing ##bignum.fdigit-width bits, which
;;; is currently 8 on all architectures.  Fdigits are so called because a
;;; bignum is represented as fdigits before the Fast Fourier Transforms used
;;; in large bignum multiplications are performed.  Some comments indicate that for
;;; bignums of larger than half a billion bits, four-bit fdigits may be useful, but that
;;; isn't implemented.
;;;
;;; The global variables ##bignum.adigit-width, ##bignum.mdigit-width, and
;;; ##bignum.fdigit-width are defined in _kernel.scm.
;;;
;;; All issues of big-endian or little-endian accesses are taken care of in the
;;; C macros implementing the low-level operations, so we can program as if we're
;;; on a little-endian machine.
;;;
;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(macro-if-bignum (begin

;;; Bignum related constants.

(define ##bignum.adigit-ones (##fixnum->bignum -1))           ;; the 0th adigit is all ones
(define ##bignum.adigit-zeros (##fixnum->bignum 0))           ;; the 0th adigit is all zeros

(macro-case-target
 ((C)
  (define ##bignum.fdigit-base
    (##fxarithmetic-shift-left 1 ##bignum.fdigit-width)))

 ((js)
  (define ##bignum.fdigit-width 7)
  (define ##bignum.fdigit-base
    (##fxarithmetic-shift-left 1 ##bignum.fdigit-width)))
 )

(define ##bignum.mdigit-base
  (##fxarithmetic-shift-left 1 ##bignum.mdigit-width))

(define ##bignum.inexact-mdigit-base
  (##fixnum->flonum ##bignum.mdigit-base))

(define ##bignum.mdigit-base-minus-1
  (##fx- ##bignum.mdigit-base 1))

(define ##bignum.minus-mdigit-base
  (##fx- ##bignum.mdigit-base))

(define ##bignum.max-fixnum-div-mdigit-base
  (##fxquotient ##max-fixnum ##bignum.mdigit-base))

(define ##bignum.min-fixnum-div-mdigit-base
  (##fxquotient ##min-fixnum ##bignum.mdigit-base))

(define ##bignum.2*min-fixnum
  (if (##fixnum? -1073741824)
      -4611686018427387904 ;; (- (expt 2 62))
      -1073741824))        ;; (- (expt 2 30))

;;; The following global variables control when each of the three
;;; multiplication algorithms are used.
;;;
;;; Naive (grade-school) multiplication is used as long as one of
;;; the arguments has fewer than ##bignum.naive-mul-max-width
;;; bits.
;;;
;;; Karatsuba multiplication is used if the smaller of the two
;;; multiplication arguments has fewer than ##bignum.fft-mul-min-width
;;; or the larger of the two arguments has more than
;;; ##bignum.fft-mul-max-width bits
;;;
;;; For other sizes of the arguments use FFT multiplication.
;;;
;;; A "fast", reciprocal-based division is used if the divisor has
;;; more than ##bignum.fft-mul-min-width bits and the difference in
;;; size of the divident and divisor is more than
;;; ##bignum.fft-mul-min-width bits.
;;;
;;; Note that these global variables are not constants that are
;;; inlined, so one can change them if you like.

(define ##bignum.naive-mul-max-width 1400)

(define-prim (##bignum.naive-mul-max-width-set! x)
  (set! ##bignum.naive-mul-max-width x))

(define ##bignum.fft-mul-min-width 20000)

(define-prim (##bignum.fft-mul-min-width-set! x)
  (set! ##bignum.fft-mul-min-width x))

(define ##bignum.fft-mul-max-width
  (if (##fixnum? -1073741824) ;; #t iff using 64-bit fixnums
      536870912
       ;; to avoid creating f64vectors that are too long
      4194304))

(define-prim (##bignum.fft-mul-max-width-set! x)
  (set! ##bignum.fft-mul-max-width x))

;;; An O(N(\log N)^2) algorithm for GCD is used if both arguments have more
;;; than ##bignum.fast-gcd-size bits

(define ##bignum.fast-gcd-size ##bignum.naive-mul-max-width)  ;; must be >= 64

(define-prim (##bignum.fast-gcd-size-set! x)
  (set! ##bignum.fast-gcd-size x))

;;; -------------------------------------------------------------------------------

;;; These are the low-level operations on adigits, mdigits, and fdigits.
;;; Two-argument functions are generally destructive, and overwrite part of
;;; their first argument.  These operations are supported in the Gambit Virtual
;;; Machine (GVM) and the Gambit Scheme Compiler (gsc).

;;; Returns #t if x is negative, #f otherwise
(define-prim (##bignum.negative? x))

;;; Returns the number of adigits in x (always a fixnum)
(define-prim (##bignum.adigit-length x))

;;; Increments the i'th adigit of x by 1 and returns 1 if the result
;;; overflowed, and 0 otherwise.
(define-prim (##bignum.adigit-inc! x i))

;;; Decrements the i'th digit of x by 1 and returns 1 if the result
;;; underflowed and 0 otherwise.
(define-prim (##bignum.adigit-dec! x i))

;;; Calculate
;;; sum = x[i] + y[j] + carry (accessing x and y as adigits)
;;; Sets x[i] = sum modulo adigit-base; returns 1 if overflow occured,
;;; 0 otherwise.
(define-prim (##bignum.adigit-add! x i y j carry))

;;; Calculate
;;; difference = x[i] - y[j] - borrow (accessing x and y as adigits)
;;; Sets x[i] = difference modulo adigit-base; returns 1 if underflow occured,
;;; 0 otherwise.
(define-prim (##bignum.adigit-sub! x i y j borrow))

;;; Returns the number of mdigits in x (always a fixnum)
(define-prim (##bignum.mdigit-length x))

;;; Returns the i'th mdigit of x
(define-prim (##bignum.mdigit-ref x i))

;;; Sets the i'th mdigit of x to mdigit
(define-prim (##bignum.mdigit-set! x i mdigit))

;;; Calculate
;;; z = x[i] + y[j] * multiplier + carry (accessing x and y as mdigits)
;;; Sets x[i] = z modulo mdigit-base; returns (quotient z mdigit-base)
(define-prim (##bignum.mdigit-mul! x i y j multiplier carry))

;;; Calculate
;;; z = x[i] - y[j] * quotient + borrow
;;; Sets x[i] to z modulo mdigit-base; returns
;;; (quotient (z - x[i]) mdigit-base)
(define-prim (##bignum.mdigit-div! x i y j quotient borrow))

;;; Returns
;;; (u[j] * mdigit-base + u[j-1]) / v
;;; (accessing u as mdigits)
(define-prim (##bignum.mdigit-quotient u j v_n-1))

;;; Returns
;;; (u[j] * mdigit-base + u[j-1] - v_n-1 * q-hat)
;;; (accessing u as mdigits)
(define-prim (##bignum.mdigit-remainder u j v_n-1 q-hat))

;;; Returns #t if
;;; q-hat * v_n-2 > (r-hat * mdigit-base + u_j-2)
;;; and #f otherwise
(define-prim (##bignum.mdigit-test? q-hat v_n-2 r-hat u_j-2))

;;; Returns #t if x[i] (accessed as adigits) is all ones, and
;;; #f otherwise
(define-prim (##bignum.adigit-ones? x i))

;;; Returns #t if x[i] (accessed as adigits) is all zeros, and
;;; #f otherwise
(define-prim (##bignum.adigit-zero? x i))

;;; Returns #t if the high-order bit of x[i] (accessing x as
;;; adigits) is 1, and #f otherwise
(define-prim (##bignum.adigit-negative? x i))

;;; Returns #t if x[i]=y[i] (accessing x and y as adigits) and
;;; #f otherwise
(define-prim (##bignum.adigit-= x y i))

;;; Returns #t if x[i]<y[i] (accessing x and y as adigits) and
;;; #f otherwise
(define-prim (##bignum.adigit-< x y i))

;;; Convert the fixnum x to an (unnormalized) bignum with one adigit
(define-prim (##fixnum->bignum x))

;;; Sets the number of adigits in the bignum x to n; must not increase
;;; the number of adigits in x
(define-prim (##bignum.adigit-shrink! x n))

;;; Sets x[i] to y[j] (accessing x and y as adigits)
(define-prim (##bignum.adigit-copy! x i y j))

;;; Calculate
;;; z = hi[j] << divider | lo[k] >> (##bignum.adigit-width - divider)
;;; (accessing hi and lo as adigits)
;;; Sets x[i] to z modulo adigit-base
(define-prim (##bignum.adigit-cat! x i hi j lo k divider))

;;; Sets x[i] to x[i] & y[j] (accessing x and y as adigits)
(define-prim (##bignum.adigit-bitwise-and! x i y j))

;;; Sets x[i] to x[i] | y[j] (accessing x and y as adigits)
(define-prim (##bignum.adigit-bitwise-ior! x i y j))

;;; Sets x[i] to x[i] ^ y[j] (accessing x and y as adigits)
(define-prim (##bignum.adigit-bitwise-xor! x i y j))

;;; Sets x[i] to !x[i] (accessing x as adigits)
(define-prim (##bignum.adigit-bitwise-not! x i))

(macro-case-target
 ((C)
  ;; Returns the number of fdigits in x
  (define-prim (##bignum.fdigit-length x))

  ;; Returns x[i] (accessing x as fdigits)
  (define-prim (##bignum.fdigit-ref x i))

  ;; Sets x[i] to fdigit (accessing x as fdigits)
  (define-prim (##bignum.fdigit-set! x i fdigit)))

 ((js)

  ;; assumes that mdigits are 14 bits wide and fdigits are
  ;; halves of mdigits

  (define ##bignum.fdigit-mask
    (##fx- ##bignum.fdigit-base 1))

  ;; Returns the number of fdigits in x

  (define-prim (##bignum.fdigit-length x)
    (##fx* (##bignum.adigit-length x) 2))

  ;; Returns x[i] (accessing x as fdigits)
  (define-prim (##bignum.fdigit-ref x i)
    (let ((mdigit (##bignum.mdigit-ref x (##fxquotient i 2))))
      (if (##fxeven? i)
          (##fxand mdigit ##bignum.fdigit-mask)
          (##fxarithmetic-shift-right mdigit ##bignum.fdigit-width))))

  ;; Sets x[i] to fdigit (accessing x as fdigits)
  (define-prim (##bignum.fdigit-set! x i fdigit)
    (let* ((i/2         (##fxquotient i 2))
           (mdigit      (##bignum.mdigit-ref x i/2))
           (new-mdigit
            (if (##fxeven? i)
                (##fxior (##fxand (##fxnot ##bignum.fdigit-mask)
                                  mdigit)
                         fdigit)
                (##fxior (##fxarithmetic-shift-left fdigit ##bignum.fdigit-width)
                         (##fxand mdigit ##bignum.fdigit-mask)))))
      (##bignum.mdigit-set! x i/2 new-mdigit))))
  )

;;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;;; Operations where arguments are in bignum format

(define-prim (##bignum.make k x complement?)
  (##declare (not interrupts-enabled))
  (let ((v (##c-code #<<end-of-code
___SCMOBJ k;
___SCMOBJ x;
___SCMOBJ complement;
___SIZE_TS i;
___SIZE_TS n;
___SCMOBJ result;
___POP_ARGS3(k,x,complement);
___ps->saved[0] = k;
___ps->saved[1] = x;
___ps->saved[2] = complement;
n = ___INT(k);

if (n > ___CAST(___WORD, ___LMASK>>___LF)/(___BIG_ABASE_WIDTH/8))
  result = ___FIX(___HEAP_OVERFLOW_ERR); /* requested object is too big! */
else
  {
    ___SIZE_TS words = ___WORDS((n*(___BIG_ABASE_WIDTH/8))) + ___SUBTYPED_BODY;

#if ___BIG_ABASE_WIDTH == 64 && ___WS == 4
    words++;
#endif

    if (words > ___MSECTION_BIGGEST)
      {
        ___FRAME_STORE_RA(___R0)
        ___W_ALL
#if ___BIG_ABASE_WIDTH == 32
        result = ___EXT(___alloc_scmobj) (___ps, ___sBIGNUM, n<<2);
#else
        result = ___EXT(___alloc_scmobj) (___ps, ___sBIGNUM, n<<3);
#endif
        ___R_ALL
        ___SET_R0(___FRAME_FETCH_RA)
        if (!___FIXNUMP(result))
          ___still_obj_refcount_dec (result);
      }
    else
      {
        ___BOOL overflow = 0;
        ___hp += words;
        if (___hp > ___ps->heap_limit)
          {
            ___FRAME_STORE_RA(___R0)
            ___W_ALL
            overflow = ___heap_limit (___PSPNC) && ___garbage_collect (___PSP 0);
            ___R_ALL
            ___SET_R0(___FRAME_FETCH_RA)
          }
        else
          ___hp -= words;
        if (overflow)
          result = ___FIX(___HEAP_OVERFLOW_ERR);
        else
          {
#if ___BIG_ABASE_WIDTH == 64 && ___WS == 4
            result = ___SUBTYPED_FROM_BODY(___CAST(___WORD,___hp+___SUBTYPED_BODY+1)&~7);
#else
            result = ___SUBTYPED_FROM_START(___hp);
#endif
#if ___BIG_ABASE_WIDTH == 32
            ___SUBTYPED_HEADER_SET(result, ___MAKE_HD_BYTES((n<<2), ___sBIGNUM));
#else
            ___SUBTYPED_HEADER_SET(result, ___MAKE_HD_BYTES((n<<3), ___sBIGNUM));
#endif
            ___hp += words;
          }
      }
  }
k = ___ps->saved[0];
x = ___ps->saved[1];
complement = ___ps->saved[2];
___ps->saved[0] = ___VOID;
___ps->saved[1] = ___VOID;
___ps->saved[2] = ___VOID;
if (!___FIXNUMP(result))
  {
    ___SCMOBJ len;
    if (x == ___FAL)
      len = 0;
    else
      {
        len = ___INT(___BIGALENGTH(x));
        if (len > n)
          len = n;
      }
#if ___BIG_ABASE_WIDTH == 32
    if (complement == ___FAL)
      {
        for (i=0; i<len; i++)
          ___STORE_U32(___BODY_AS(result,___tSUBTYPED),i,
                       ___FETCH_U32(___BODY_AS(x,___tSUBTYPED),i));
        if (x != ___FAL &&
            ___FETCH_S32(___BODY_AS(x,___tSUBTYPED),(i-1)) < 0)
          for (; i<n; i++)
            ___STORE_U32(___BODY_AS(result,___tSUBTYPED),i,___BIG_ABASE_MIN_1);
        else
          for (; i<n; i++)
            ___STORE_U32(___BODY_AS(result,___tSUBTYPED),i,0);
      }
    else
      {
        for (i=0; i<len; i++)
          ___STORE_U32(___BODY_AS(result,___tSUBTYPED),i,
                       ~___FETCH_U32(___BODY_AS(x,___tSUBTYPED),i));
        if (x != ___FAL &&
            ___FETCH_S32(___BODY_AS(x,___tSUBTYPED),(i-1)) < 0)
          for (; i<n; i++)
            ___STORE_U32(___BODY_AS(result,___tSUBTYPED),i,0);
        else
          for (; i<n; i++)
            ___STORE_U32(___BODY_AS(result,___tSUBTYPED),i,___BIG_ABASE_MIN_1);
      }
#else
    if (complement == ___FAL)
      {
        for (i=0; i<len; i++)
          ___STORE_U64(___BODY_AS(result,___tSUBTYPED),i,
                       ___FETCH_U64(___BODY_AS(x,___tSUBTYPED),i));
        if (x != ___FAL &&
            ___FETCH_S64(___BODY_AS(x,___tSUBTYPED),(i-1)) < 0)
          for (; i<n; i++)
            ___STORE_U64(___BODY_AS(result,___tSUBTYPED),i,___BIG_ABASE_MIN_1);
        else
          for (; i<n; i++)
            ___STORE_U64(___BODY_AS(result,___tSUBTYPED),i,0);
      }
    else
      {
        for (i=0; i<len; i++)
          ___STORE_U64(___BODY_AS(result,___tSUBTYPED),i,
                       ~___FETCH_U64(___BODY_AS(x,___tSUBTYPED),i));
        if (x != ___FAL &&
            ___FETCH_S64(___BODY_AS(x,___tSUBTYPED),(i-1)) < 0)
          for (; i<n; i++)
            ___STORE_U64(___BODY_AS(result,___tSUBTYPED),i,0);
        else
          for (; i<n; i++)
            ___STORE_U64(___BODY_AS(result,___tSUBTYPED),i,___BIG_ABASE_MIN_1);
      }
#endif
  }
___RESULT = result;
___PUSH_ARGS3(k,x,complement);

end-of-code
)))
    (if (##fixnum? v)
        (begin
          (##raise-heap-overflow-exception)
          (##bignum.make k x complement?))
        v)))

(define-prim (##bignum.copy x)
  (##bignum.make (##bignum.adigit-length x) x #f))

;;; Bignum addition and subtraction.

(define-prim (##bignum.+ x y)

  ;; x is an unnormalized bignum, y is an unnormalized bignum

  (define (add x x-length y y-length)
    (let* ((result-length
            (##fx+ y-length
                   (if (##eq? (##bignum.negative? x)
                              (##bignum.negative? y))
                       1
                       0)))
           (result
            (##bignum.make result-length y #f)))

      (##declare (not interrupts-enabled))

      (let loop ((i 0)
                 (carry 0))
        (if (##fx< i x-length)
            (loop (##fx+ i 1)
                  (##bignum.adigit-add! result i x i carry))
            (##bignum.propagate-carry-and-normalize!
             result
             result-length
             x-length
             (##bignum.negative? x)
             (##fxzero? carry))))))

  (let ((x-length (##bignum.adigit-length x))
        (y-length (##bignum.adigit-length y)))
    (if (##fx< x-length y-length)
        (add x x-length y y-length)
        (add y y-length x x-length))))

(define-prim (##bignum.- x y)

  ;; x is an unnormalized bignum, y is an unnormalized bignum

  (let ((x-length (##bignum.adigit-length x))
        (y-length (##bignum.adigit-length y)))
    (if (##fx< x-length y-length)

        (let* ((result-length
                (##fx+ y-length
                       (if (##eq? (##bignum.negative? x)
                                  (##bignum.negative? y))
                           0
                           1)))
               (result
                (##bignum.make result-length y #t)))

          (##declare (not interrupts-enabled))

          (let loop1 ((i 0)
                      (carry 1))
            (if (##fx< i x-length)
                (loop1 (##fx+ i 1)
                       (##bignum.adigit-add! result i x i carry))
                (##bignum.propagate-carry-and-normalize!
                 result
                 result-length
                 x-length
                 (##bignum.negative? x)
                 (##fxzero? carry)))))

        (let* ((result-length
                (##fx+ x-length
                       (if (##eq? (##bignum.negative? x)
                                  (##bignum.negative? y))
                           0
                           1)))
               (result
                (##bignum.make result-length x #f)))

          (##declare (not interrupts-enabled))

          (let loop2 ((i 0)
                      (borrow 0))
            (if (##fx< i y-length)
                (loop2 (##fx+ i 1)
                       (##bignum.adigit-sub! result i y i borrow))
                (##bignum.propagate-carry-and-normalize!
                 result
                 result-length
                 y-length
                 (##not (##bignum.negative? y))
                 (##not (##fxzero? borrow)))))))))

(define-prim (##bignum.propagate-carry-and-normalize!
              result
              result-length
              i
              borrow?
              propagate?)

  (##declare (not interrupts-enabled))

  (if (##eq? borrow? propagate?)
      (if borrow?

          (let loop1 ((i i)
                      (borrow 1))
            (if (and (##not (##fxzero? borrow))
                     (##fx< i result-length))
                (loop1 (##fx+ i 1)
                       (##bignum.adigit-dec! result i))
                (##bignum.normalize! result)))

          (let loop2 ((i i)
                      (carry 1))
            (if (and (##not (##fxzero? carry))
                     (##fx< i result-length))
                (loop2 (##fx+ i 1)
                       (##bignum.adigit-inc! result i))
                (##bignum.normalize! result))))

      (##bignum.normalize! result)))

(define-prim (##bignum->fixnum? bn)
  (let* ((i
          (##fx- (##bignum.mdigit-length bn) 1))
         (n
          (##bignum.mdigit-ref bn i))
         (bias
          (if (##fx< (##fx* 2 n) ##bignum.mdigit-base)
              0
              ##bignum.mdigit-base-minus-1)))
    (let loop ((n (##fx- n bias))
               (i (##fx- i 1)))
      (if (##fx< i 0)
          (if (##fx= 0 bias)
              n
              (##fx+? n -1))
          (let ((n1 (##fx*? n ##bignum.mdigit-base)))
            (and n1
                 (let ((n2 (##fx+? n1 (##fx- (##bignum.mdigit-ref bn i) bias))))
                   (and n2
                        (loop n2
                              (##fx- i 1))))))))))

(define-prim (##bignum.normalize! result)

  (##declare (not interrupts-enabled))

  (or (##bignum->fixnum? result)

      (let ((n (##fx- (##bignum.adigit-length result) 1)))

        (cond ((##bignum.adigit-zero? result n)
               (let loop1 ((i (##fx- n 1)))
                 (cond ((##fx< i 0)
                        0)
                       ((##bignum.adigit-zero? result i)
                        (loop1 (##fx- i 1)))
                       ((##bignum.adigit-negative? result i)
                        (##bignum.adigit-shrink! result (##fx+ i 2)))
                       (else
                        (##bignum.adigit-shrink! result (##fx+ i 1))))))

              ((##bignum.adigit-ones? result n)
               (let loop2 ((i (##fx- n 1)))
                 (cond ((##fx< i 0)
                        -1)
                       ((##bignum.adigit-ones? result i)
                        (loop2 (##fx- i 1)))
                       ((##not (##bignum.adigit-negative? result i))
                        (##bignum.adigit-shrink! result (##fx+ i 2)))
                       (else
                        (##bignum.adigit-shrink! result (##fx+ i 1))))))

              (else
               result)))))

;;; Bignum multiplication.

(define-prim (##bignum.* x y)

  (define (fft-mul x y)

    ;; Marc, the results of make-w should be cached, since bigger
    ;; tables can be used for any smaller size FFT.

    ;; This code works for x and y up to 536,870,912 bits, with
    ;; results up to 1Gb; numbers of this size require 8Gb, or 1GB, of
    ;; intermediate storage. It is always faster than the old code,
    ;; and it is mathematically correct.  (Whether it is
    ;; programmatically correct is, of course, another matter, but I
    ;; have tested it extensively.)

    ;; This is an experiment.

    ;; This code implements bignum multiplication based on
    ;; double-precision FFT computations rather than on
    ;; number-theoretic FFTs and the Chinese remainder theorem.

    ;; The theory is in the article

    ;; Rapid multiplication modulo the sum and difference of highly
    ;; composite numbers, by Colin Percival

    ;; The complex roots of unity ("twiddle factors") need to be
    ;; computed in such a way that there is a known bound on the
    ;; error.  I did this with a "computable reals" package I wrote.
    ;; I did not know a bound for the roots of unity in Ooura's FFT,
    ;; which is what we previously used..

    ;; If you use a different complex FFT, then you need to ensure
    ;; that the same operations are done as in this FFT (perhaps in a
    ;; somewhat different order), or that you prove the corresponding
    ;; theorem for your FFT that Percival proved in his paper.  On a
    ;; 2GHz PPC 970, my complex FFT seems to be about half as fast as
    ;; FFTW's complex FFT, so that doesn't seem too bad for one
    ;; written by hand in Scheme.

    ;; After years of fiddling around, I finally understook the
    ;; weighted FT transform and the so-called right-angle
    ;; convolution.  See section 8.3.2 the book "Algorithms for
    ;; Programmers" by Jo"rg Arndt, currently available at
    ;; www.jjj.de/fxt/fxtbook.pdf for a description of the right-angle
    ;; transform.  It's also covered in "Prime Numbers" by Crandall
    ;; and Pomerance, and was originally introduced in 1994 by
    ;; Crandall and Fagin.

    ;; The basic reference for the fft codes is
    ;; {\it Inside the FFT Black Box,} by Eleanor Chu and Alan George,
    ;; CRC Press, New York, 2000.  In the end, I should say that these
    ;; codes are just motivated by this book.

    ;; One of the biggest problem in translating their notation is
    ;; that they work with complex numbers, and we're working with
    ;; pairs of reals.  Let us assume that all complex numbers are
    ;; stored with adjacent real and imaginary parts, real first.

    #|

    The strategy in the next function is to calculate a 2^n'th
    root of unity by multiplying entries from up to three look-up
    tables, each of which has lut-table-size complex entries, stored
    as pairs of f64s.  Each of the tables contains correctly-rounded
    complex roots of unity, as computed by my computable-reals code.

    The j'th entry of the first table is

    exp(\pi/2 * i * (bit-reverse j log-lut-table-size)/lut-table-size),  j = 0,...,lut-table-size - 1.

    where (bit-reverse j k) reverses the bits of j when
    considered as a bit string of length k.

    The j'th entry of the second table is

    exp(\pi/2 * i * j/lut-table-size^2), j = 0,...,lut-table-size-1

    and the j'th entry in the third table is

    exp(\pi/2 * i * j/lut-table-size^3), j = 0,...,lut-table-size-1

    From these three tables we construct a lut w in bit-reverse
    order of size 2^log-n.

    Any table we construct is also usable for ffts of a smaller size.

    The errors in the tables are as follows.

    When log-lut-table-size=10, we have the error in the first
    table is bounded by

    7.241394152931137e-17

    Theoretically, it should be bounded by

    > (* (sqrt 1/2) (expt 2. -53))
    7.850462293418875e-17

    The maximum error in the product of the first two tables is bounded by

    2.5438950740364204e-16

    The error in the general product of two correctly-rounded
    complex floating-point numbers of magnitude one is bounded by

    > (* (+ (sqrt 5) (sqrt 1/2) (sqrt 1/2)) (expt 2. -53))
    4.052626611931048e-16

    but what we're seeing here is that the entries of the second
    table have real part close to 1 and imaginary part <
    pi/2*2^{-10}, so (a) the error in the entries of the second table
    is much closer to 1/2 epsilon rather than (sqrt 1/2) epsilon,
    and (2) we might expect an error of about sqrt(2)epsilon in the
    complex product instead of the general result of sqrt(5)epsilon,
    or

    > (* (+ (sqrt 2) (sqrt 1/2) 1/2) (expt 2. -53))
    2.910250200338241e-16

    The maximum error in the product of entries from all three tables is

    4.158491068379826e-16

    which we can plug into the error bounds.  Using the above
    heuristics, we would expect it to be <

    > (* (+ (sqrt 2) (sqrt 2) (sqrt 1/2) 1/2 1/2) (expt 2. -53))
    5.035454171334594e-16

    and we have the general bound of

    > (* (+ (* 2 (sqrt 5)) (* 3 (sqrt 1/2))) (expt 2. -53))
    7.320206994520208e-16

    so I'm glad I measured it.

    And, yes, I waited six days to compute the difference between
    the computed roots of unity and the exact roots of unity for all
    2^{30} products from the three tables.

    When log-lut-table-size=9, the corresponding maximum errors are

    7.113686303921851e-17

    for entries in the first table,

    2.4506454051660923e-16

    for products of entries in the first two tables, and

    4.164343159519809e-16

    for products from all three tables.

    Added later:

    We could try a different strategy here.

    If it's necessary to multiply entries of all three tables to populate
    the result, we multiply the entries from the last two tables first to
    get a multiplier.  Because the real parts of the second and third table
    entries are nearly one and the imaginary parts are < 2^{-9} or so, the
    rounding error in each entry is about 1/2 epsilon instead of (sqrt 1/2)
    epsilon, and the biggest error in the product is 1/2 epsilon in the
    product of the real parts and then another 1/2 epsilon when subtracting
    the product of the imaginary parts.  So the total error is about

    (* (+ 1/2 1/2 1/2 1/2) (expt 2 -53))

    or 2.220446049250313e-16.

    The final product adds further error of (sqrt 1/2) epsilon in the
    entries in the first table and then (sqrt 2) epsilon in the product.
    So my guess is that the total error in the product of three entries
    from the table will be bounded by

    (* (+ 1/2 1/2 1/2 1/2 (sqrt 1/2) (sqrt 2)) (expt 2 -53))

    or 4.575584737275976e-16.

    |#

    (define lut-table-size 512)
    (define lut-table-size^2 262144)
    (define lut-table-size^3 134217728)
    (define log-lut-table-size 9)

    (define low-lut
    '#f64(1. 0.
       .7071067811865476 .7071067811865476
       .9238795325112867 .3826834323650898
       .3826834323650898 .9238795325112867
       .9807852804032304 .19509032201612828
       .5555702330196022 .8314696123025452
       .8314696123025452 .5555702330196022
       .19509032201612828 .9807852804032304
       .9951847266721969 .0980171403295606
       .6343932841636455 .773010453362737
       .881921264348355 .47139673682599764
       .2902846772544624 .9569403357322088
       .9569403357322088 .2902846772544624
       .47139673682599764 .881921264348355
       .773010453362737 .6343932841636455
       .0980171403295606 .9951847266721969
       .9987954562051724 .049067674327418015
       .6715589548470184 .7409511253549591
       .9039892931234433 .4275550934302821
       .33688985339222005 .9415440651830208
       .970031253194544 .2429801799032639
       .5141027441932218 .8577286100002721
       .8032075314806449 .5956993044924334
       .14673047445536175 .989176509964781
       .989176509964781 .14673047445536175
       .5956993044924334 .8032075314806449
       .8577286100002721 .5141027441932218
       .2429801799032639 .970031253194544
       .9415440651830208 .33688985339222005
       .4275550934302821 .9039892931234433
       .7409511253549591 .6715589548470184
       .049067674327418015 .9987954562051724
       .9996988186962042 .024541228522912288
       .6895405447370669 .7242470829514669
       .9142097557035307 .40524131400498986
       .35989503653498817 .9329927988347388
       .9757021300385286 .2191012401568698
       .5349976198870973 .8448535652497071
       .8175848131515837 .5758081914178453
       .17096188876030122 .9852776423889412
       .99247953459871 .1224106751992162
       .6152315905806268 .7883464276266062
       .8700869911087115 .49289819222978404
       .26671275747489837 .9637760657954398
       .9495281805930367 .31368174039889146
       .4496113296546066 .8932243011955153
       .7572088465064846 .6531728429537768
       .07356456359966743 .9972904566786902
       .9972904566786902 .07356456359966743
       .6531728429537768 .7572088465064846
       .8932243011955153 .4496113296546066
       .31368174039889146 .9495281805930367
       .9637760657954398 .26671275747489837
       .49289819222978404 .8700869911087115
       .7883464276266062 .6152315905806268
       .1224106751992162 .99247953459871
       .9852776423889412 .17096188876030122
       .5758081914178453 .8175848131515837
       .8448535652497071 .5349976198870973
       .2191012401568698 .9757021300385286
       .9329927988347388 .35989503653498817
       .40524131400498986 .9142097557035307
       .7242470829514669 .6895405447370669
       .024541228522912288 .9996988186962042
       .9999247018391445 .012271538285719925
       .6983762494089728 .7157308252838187
       .9191138516900578 .3939920400610481
       .37131719395183754 .9285060804732156
       .9783173707196277 .20711137619221856
       .5453249884220465 .8382247055548381
       .8245893027850253 .5657318107836132
       .18303988795514095 .9831054874312163
       .9939069700023561 .11022220729388306
       .6248594881423863 .7807372285720945
       .8760700941954066 .4821837720791228
       .2785196893850531 .9604305194155658
       .9533060403541939 .3020059493192281
       .46053871095824 .8876396204028539
       .765167265622459 .6438315428897915
       .0857973123444399 .996312612182778
       .9981181129001492 .06132073630220858
       .6624157775901718 .7491363945234594
       .8986744656939538 .43861623853852766
       .3253102921622629 .9456073253805213
       .9669764710448521 .25486565960451457
       .5035383837257176 .8639728561215867
       .7958369046088836 .6055110414043255
       .1345807085071262 .99090263542778
       .9873014181578584 .15885814333386145
       .5857978574564389 .8104571982525948
       .8513551931052652 .524589682678469
       .2310581082806711 .9729399522055602
       .937339011912575 .34841868024943456
       .4164295600976372 .9091679830905224
       .7326542716724128 .680600997795453
       .03680722294135883 .9993223845883495
       .9993223845883495 .03680722294135883
       .680600997795453 .7326542716724128
       .9091679830905224 .4164295600976372
       .34841868024943456 .937339011912575
       .9729399522055602 .2310581082806711
       .524589682678469 .8513551931052652
       .8104571982525948 .5857978574564389
       .15885814333386145 .9873014181578584
       .99090263542778 .1345807085071262
       .6055110414043255 .7958369046088836
       .8639728561215867 .5035383837257176
       .25486565960451457 .9669764710448521
       .9456073253805213 .3253102921622629
       .43861623853852766 .8986744656939538
       .7491363945234594 .6624157775901718
       .06132073630220858 .9981181129001492
       .996312612182778 .0857973123444399
       .6438315428897915 .765167265622459
       .8876396204028539 .46053871095824
       .3020059493192281 .9533060403541939
       .9604305194155658 .2785196893850531
       .4821837720791228 .8760700941954066
       .7807372285720945 .6248594881423863
       .11022220729388306 .9939069700023561
       .9831054874312163 .18303988795514095
       .5657318107836132 .8245893027850253
       .8382247055548381 .5453249884220465
       .20711137619221856 .9783173707196277
       .9285060804732156 .37131719395183754
       .3939920400610481 .9191138516900578
       .7157308252838187 .6983762494089728
       .012271538285719925 .9999247018391445
       .9999811752826011 .006135884649154475
       .7027547444572253 .7114321957452164
       .9215140393420419 .3883450466988263
       .37700741021641826 .9262102421383114
       .9795697656854405 .2011046348420919
       .5504579729366048 .83486287498638
       .8280450452577558 .560661576197336
       .18906866414980622 .9819638691095552
       .9945645707342554 .10412163387205457
       .629638238914927 .7768884656732324
       .8790122264286335 .47679923006332214
       .2844075372112718 .9587034748958716
       .9551411683057707 .29615088824362384
       .4659764957679662 .8847970984309378
       .7691033376455796 .6391244448637757
       .09190895649713272 .9957674144676598
       .9984755805732948 .05519524434968994
       .6669999223036375 .745057785441466
       .901348847046022 .43309381885315196
       .33110630575987643 .9435934581619604
       .9685220942744173 .24892760574572018
       .508830142543107 .8608669386377673
       .799537269107905 .600616479383869
       .14065823933284924 .9900582102622971
       .9882575677307495 .15279718525844344
       .5907597018588743 .8068475535437992
       .8545579883654005 .5193559901655896
       .2370236059943672 .9715038909862518
       .9394592236021899 .3426607173119944
       .4220002707997997 .9065957045149153
       .7368165688773699 .6760927035753159
       .04293825693494082 .9990777277526454
       .9995294175010931 .030674803176636626
       .6850836677727004 .7284643904482252
       .9117060320054299 .41084317105790397
       .3541635254204904 .9351835099389476
       .9743393827855759 .22508391135979283
       .5298036246862947 .8481203448032972
       .8140363297059484 .5808139580957645
       .16491312048996992 .9863080972445987
       .9917097536690995 .12849811079379317
       .6103828062763095 .7921065773002124
       .8670462455156926 .49822766697278187
       .2607941179152755 .9653944416976894
       .9475855910177411 .3195020308160157
       .44412214457042926 .8959662497561851
       .7531867990436125 .6578066932970786
       .06744391956366406 .9977230666441916
       .9968202992911657 .07968243797143013
       .6485144010221124 .7612023854842618
       .8904487232447579 .45508358712634384
       .30784964004153487 .9514350209690083
       .9621214042690416 .272621355449949
       .48755016014843594 .8730949784182901
       .7845565971555752 .6200572117632892
       .11631863091190477 .9932119492347945
       .984210092386929 .17700422041214875
       .5707807458869673 .8211025149911046
       .8415549774368984 .5401714727298929
       .21311031991609136 .9770281426577544
       .9307669610789837 .36561299780477385
       .39962419984564684 .9166790599210427
       .7200025079613817 .693971460889654
       .01840672990580482 .9998305817958234
       .9998305817958234 .01840672990580482
       .693971460889654 .7200025079613817
       .9166790599210427 .39962419984564684
       .36561299780477385 .9307669610789837
       .9770281426577544 .21311031991609136
       .5401714727298929 .8415549774368984
       .8211025149911046 .5707807458869673
       .17700422041214875 .984210092386929
       .9932119492347945 .11631863091190477
       .6200572117632892 .7845565971555752
       .8730949784182901 .48755016014843594
       .272621355449949 .9621214042690416
       .9514350209690083 .30784964004153487
       .45508358712634384 .8904487232447579
       .7612023854842618 .6485144010221124
       .07968243797143013 .9968202992911657
       .9977230666441916 .06744391956366406
       .6578066932970786 .7531867990436125
       .8959662497561851 .44412214457042926
       .3195020308160157 .9475855910177411
       .9653944416976894 .2607941179152755
       .49822766697278187 .8670462455156926
       .7921065773002124 .6103828062763095
       .12849811079379317 .9917097536690995
       .9863080972445987 .16491312048996992
       .5808139580957645 .8140363297059484
       .8481203448032972 .5298036246862947
       .22508391135979283 .9743393827855759
       .9351835099389476 .3541635254204904
       .41084317105790397 .9117060320054299
       .7284643904482252 .6850836677727004
       .030674803176636626 .9995294175010931
       .9990777277526454 .04293825693494082
       .6760927035753159 .7368165688773699
       .9065957045149153 .4220002707997997
       .3426607173119944 .9394592236021899
       .9715038909862518 .2370236059943672
       .5193559901655896 .8545579883654005
       .8068475535437992 .5907597018588743
       .15279718525844344 .9882575677307495
       .9900582102622971 .14065823933284924
       .600616479383869 .799537269107905
       .8608669386377673 .508830142543107
       .24892760574572018 .9685220942744173
       .9435934581619604 .33110630575987643
       .43309381885315196 .901348847046022
       .745057785441466 .6669999223036375
       .05519524434968994 .9984755805732948
       .9957674144676598 .09190895649713272
       .6391244448637757 .7691033376455796
       .8847970984309378 .4659764957679662
       .29615088824362384 .9551411683057707
       .9587034748958716 .2844075372112718
       .47679923006332214 .8790122264286335
       .7768884656732324 .629638238914927
       .10412163387205457 .9945645707342554
       .9819638691095552 .18906866414980622
       .560661576197336 .8280450452577558
       .83486287498638 .5504579729366048
       .2011046348420919 .9795697656854405
       .9262102421383114 .37700741021641826
       .3883450466988263 .9215140393420419
       .7114321957452164 .7027547444572253
       .006135884649154475 .9999811752826011
       .9999952938095762 .003067956762965976
       .7049340803759049 .7092728264388657
       .9227011283338785 .38551605384391885
       .37984720892405116 .9250492407826776
       .9801821359681174 .1980984107179536
       .5530167055800276 .8331701647019132
       .829761233794523 .5581185312205561
       .19208039704989244 .9813791933137546
       .9948793307948056 .10106986275482782
       .6320187359398091 .7749531065948739
       .8804708890521608 .47410021465055
       .2873474595447295 .9578264130275329
       .9560452513499964 .29321916269425863
       .46868882203582796 .8833633386657316
       .7710605242618138 .6367618612362842
       .094963495329639 .9954807554919269
       .9986402181802653 .052131704680283324
       .6692825883466361 .7430079521351217
       .9026733182372588 .4303264813400826
       .3339996514420094 .9425731976014469
       .9692812353565485 .24595505033579462
       .5114688504379704 .8593018183570084
       .8013761717231402 .5981607069963423
       .14369503315029444 .9896220174632009
       .9887216919603238 .1497645346773215
       .5932322950397998 .8050313311429635
       .8561473283751945 .5167317990176499
       .2400030224487415 .9707721407289504
       .9405060705932683 .33977688440682685
       .4247796812091088 .9052967593181188
       .7388873244606151 .673829000378756
       .04600318213091463 .9989412931868569
       .9996188224951786 .027608145778965743
       .6873153408917592 .726359155084346
       .9129621904283982 .4080441628649787
       .35703096123343003 .9340925504042589
       .9750253450669941 .22209362097320354
       .532403127877198 .8464909387740521
       .8158144108067338 .5783137964116556
       .16793829497473117 .9857975091675675
       .9920993131421918 .12545498341154623
       .6128100824294097 .79023022143731
       .8685707059713409 .49556526182577254
       .2637546789748314 .9645897932898128
       .9485613499157303 .31659337555616585
       .4468688401623742 .8945994856313827
       .7552013768965365 .6554928529996153
       .07050457338961387 .9975114561403035
       .997060070339483 .07662386139203149
       .6508466849963809 .7592091889783881
       .8918407093923427 .4523495872337709
       .3107671527496115 .9504860739494817
       .9629532668736839 .2696683255729151
       .49022648328829116 .8715950866559511
       .7864552135990858 .617647307937804
       .11936521481099137 .9928504144598651
       .9847485018019042 .17398387338746382
       .5732971666980422 .819347520076797
       .8432082396418454 .5375870762956455
       .21610679707621952 .9763697313300211
       .9318842655816681 .3627557243673972
       .40243465085941843 .9154487160882678
       .7221281939292153 .6917592583641577
       .021474080275469508 .9997694053512153
       .9998823474542126 .015339206284988102
       .696177131491463 .7178700450557317
       .9179007756213905 .3968099874167103
       .3684668299533723 .9296408958431812
       .9776773578245099 .2101118368804696
       .5427507848645159 .8398937941959995
       .8228497813758263 .5682589526701316
       .18002290140569951 .9836624192117303
       .9935641355205953 .11327095217756435
       .62246127937415 .7826505961665757
       .8745866522781761 .4848692480007911
       .27557181931095814 .9612804858113206
       .9523750127197659 .30492922973540243
       .45781330359887723 .8890483558546646
       .7631884172633813 .6461760129833164
       .08274026454937569 .9965711457905548
       .997925286198596 .06438263092985747
       .6601143420674205 .7511651319096864
       .8973245807054183 .44137126873171667
       .32240767880106985 .9466009130832835
       .9661900034454125 .257831102162159
       .5008853826112408 .8655136240905691
       .7939754775543372 .6079497849677736
       .13154002870288312 .9913108598461154
       .9868094018141855 .16188639378011183
       .5833086529376983 .8122505865852039
       .8497417680008524 .5271991347819014
       .22807208317088573 .973644249650812
       .9362656671702783 .35129275608556715
       .41363831223843456 .9104412922580672
       .7305627692278276 .6828455463852481
       .03374117185137759 .9994306045554617
       .9992047586183639 .03987292758773981
       .6783500431298615 .7347388780959635
       .9078861164876663 .41921688836322396
       .34554132496398904 .9384035340631081
       .9722264970789363 .23404195858354343
       .5219752929371544 .8529606049303636
       .808656181588175 .5882815482226453
       .15582839765426523 .9877841416445722
       .9904850842564571 .13762012158648604
       .6030665985403482 .7976908409433912
       .8624239561110405 .5061866453451553
       .25189781815421697 .9677538370934755
       .9446048372614803 .32820984357909255
       .4358570799222555 .9000158920161603
       .7471006059801801 .6647109782033449
       .05825826450043576 .9983015449338929
       .996044700901252 .0888535525825246
       .6414810128085832 .7671389119358204
       .8862225301488806 .4632597835518602
       .2990798263080405 .9542280951091057
       .9595715130819845 .281464937925758
       .479493757660153 .8775452902072612
       .778816512381476 .6272518154951441
       .10717242495680884 .9942404494531879
       .9825393022874412 .18605515166344666
       .5631993440138341 .8263210628456635
       .836547727223512 .5478940591731002
       .20410896609281687 .9789481753190622
       .9273625256504011 .374164062971458
       .39117038430225387 .9203182767091106
       .7135848687807936 .7005687939432483
       .00920375478205982 .9999576445519639
       .9999576445519639 .00920375478205982
       .7005687939432483 .7135848687807936
       .9203182767091106 .39117038430225387
       .374164062971458 .9273625256504011
       .9789481753190622 .20410896609281687
       .5478940591731002 .836547727223512
       .8263210628456635 .5631993440138341
       .18605515166344666 .9825393022874412
       .9942404494531879 .10717242495680884
       .6272518154951441 .778816512381476
       .8775452902072612 .479493757660153
       .281464937925758 .9595715130819845
       .9542280951091057 .2990798263080405
       .4632597835518602 .8862225301488806
       .7671389119358204 .6414810128085832
       .0888535525825246 .996044700901252
       .9983015449338929 .05825826450043576
       .6647109782033449 .7471006059801801
       .9000158920161603 .4358570799222555
       .32820984357909255 .9446048372614803
       .9677538370934755 .25189781815421697
       .5061866453451553 .8624239561110405
       .7976908409433912 .6030665985403482
       .13762012158648604 .9904850842564571
       .9877841416445722 .15582839765426523
       .5882815482226453 .808656181588175
       .8529606049303636 .5219752929371544
       .23404195858354343 .9722264970789363
       .9384035340631081 .34554132496398904
       .41921688836322396 .9078861164876663
       .7347388780959635 .6783500431298615
       .03987292758773981 .9992047586183639
       .9994306045554617 .03374117185137759
       .6828455463852481 .7305627692278276
       .9104412922580672 .41363831223843456
       .35129275608556715 .9362656671702783
       .973644249650812 .22807208317088573
       .5271991347819014 .8497417680008524
       .8122505865852039 .5833086529376983
       .16188639378011183 .9868094018141855
       .9913108598461154 .13154002870288312
       .6079497849677736 .7939754775543372
       .8655136240905691 .5008853826112408
       .257831102162159 .9661900034454125
       .9466009130832835 .32240767880106985
       .44137126873171667 .8973245807054183
       .7511651319096864 .6601143420674205
       .06438263092985747 .997925286198596
       .9965711457905548 .08274026454937569
       .6461760129833164 .7631884172633813
       .8890483558546646 .45781330359887723
       .30492922973540243 .9523750127197659
       .9612804858113206 .27557181931095814
       .4848692480007911 .8745866522781761
       .7826505961665757 .62246127937415
       .11327095217756435 .9935641355205953
       .9836624192117303 .18002290140569951
       .5682589526701316 .8228497813758263
       .8398937941959995 .5427507848645159
       .2101118368804696 .9776773578245099
       .9296408958431812 .3684668299533723
       .3968099874167103 .9179007756213905
       .7178700450557317 .696177131491463
       .015339206284988102 .9998823474542126
       .9997694053512153 .021474080275469508
       .6917592583641577 .7221281939292153
       .9154487160882678 .40243465085941843
       .3627557243673972 .9318842655816681
       .9763697313300211 .21610679707621952
       .5375870762956455 .8432082396418454
       .819347520076797 .5732971666980422
       .17398387338746382 .9847485018019042
       .9928504144598651 .11936521481099137
       .617647307937804 .7864552135990858
       .8715950866559511 .49022648328829116
       .2696683255729151 .9629532668736839
       .9504860739494817 .3107671527496115
       .4523495872337709 .8918407093923427
       .7592091889783881 .6508466849963809
       .07662386139203149 .997060070339483
       .9975114561403035 .07050457338961387
       .6554928529996153 .7552013768965365
       .8945994856313827 .4468688401623742
       .31659337555616585 .9485613499157303
       .9645897932898128 .2637546789748314
       .49556526182577254 .8685707059713409
       .79023022143731 .6128100824294097
       .12545498341154623 .9920993131421918
       .9857975091675675 .16793829497473117
       .5783137964116556 .8158144108067338
       .8464909387740521 .532403127877198
       .22209362097320354 .9750253450669941
       .9340925504042589 .35703096123343003
       .4080441628649787 .9129621904283982
       .726359155084346 .6873153408917592
       .027608145778965743 .9996188224951786
       .9989412931868569 .04600318213091463
       .673829000378756 .7388873244606151
       .9052967593181188 .4247796812091088
       .33977688440682685 .9405060705932683
       .9707721407289504 .2400030224487415
       .5167317990176499 .8561473283751945
       .8050313311429635 .5932322950397998
       .1497645346773215 .9887216919603238
       .9896220174632009 .14369503315029444
       .5981607069963423 .8013761717231402
       .8593018183570084 .5114688504379704
       .24595505033579462 .9692812353565485
       .9425731976014469 .3339996514420094
       .4303264813400826 .9026733182372588
       .7430079521351217 .6692825883466361
       .052131704680283324 .9986402181802653
       .9954807554919269 .094963495329639
       .6367618612362842 .7710605242618138
       .8833633386657316 .46868882203582796
       .29321916269425863 .9560452513499964
       .9578264130275329 .2873474595447295
       .47410021465055 .8804708890521608
       .7749531065948739 .6320187359398091
       .10106986275482782 .9948793307948056
       .9813791933137546 .19208039704989244
       .5581185312205561 .829761233794523
       .8331701647019132 .5530167055800276
       .1980984107179536 .9801821359681174
       .9250492407826776 .37984720892405116
       .38551605384391885 .9227011283338785
       .7092728264388657 .7049340803759049
       .003067956762965976 .9999952938095762
       ))

    (define med-lut
    '#f64(1. 0.
       .9999999999820472 5.9921124526424275e-6
       .9999999999281892 1.1984224905069707e-5
       .9999999998384257 1.7976337357066685e-5
       .9999999997127567 2.396844980841822e-5
       .9999999995511824 2.9960562258909154e-5
       .9999999993537025 3.5952674708324344e-5
       .9999999991203175 4.1944787156448635e-5
       .9999999988510269 4.793689960306688e-5
       .9999999985458309 5.3929012047963936e-5
       .9999999982047294 5.992112449092465e-5
       .9999999978277226 6.591323693173387e-5
       .9999999974148104 7.190534937017645e-5
       .9999999969659927 7.789746180603723e-5
       .9999999964812697 8.388957423910108e-5
       .9999999959606412 8.988168666915283e-5
       .9999999954041073 9.587379909597734e-5
       .999999994811668 1.0186591151935948e-4
       .9999999941833233 1.0785802393908407e-4
       .9999999935190732 1.1385013635493597e-4
       .9999999928189177 1.1984224876670004e-4
       .9999999920828567 1.2583436117416112e-4
       .9999999913108903 1.3182647357710405e-4
       .9999999905030187 1.3781858597531374e-4
       .9999999896592414 1.4381069836857496e-4
       .9999999887795589 1.498028107566726e-4
       .9999999878639709 1.5579492313939151e-4
       .9999999869124775 1.6178703551651655e-4
       .9999999859250787 1.6777914788783258e-4
       .9999999849017744 1.737712602531244e-4
       .9999999838425648 1.797633726121769e-4
       .9999999827474497 1.8575548496477492e-4
       .9999999816164293 1.9174759731070332e-4
       .9999999804495034 1.9773970964974692e-4
       .9999999792466722 2.037318219816906e-4
       .9999999780079355 2.0972393430631923e-4
       .9999999767332933 2.1571604662341763e-4
       .9999999754227459 2.2170815893277063e-4
       .9999999740762929 2.2770027123416315e-4
       .9999999726939346 2.3369238352737996e-4
       .9999999712756709 2.3968449581220595e-4
       .9999999698215016 2.45676608088426e-4
       .9999999683314271 2.5166872035582493e-4
       .9999999668054471 2.5766083261418755e-4
       .9999999652435617 2.636529448632988e-4
       .9999999636457709 2.696450571029434e-4
       .9999999620120748 2.756371693329064e-4
       .9999999603424731 2.8162928155297243e-4
       .9999999586369661 2.876213937629265e-4
       .9999999568955537 2.936135059625534e-4
       .9999999551182358 2.99605618151638e-4
       .9999999533050126 3.055977303299651e-4
       .9999999514558838 3.115898424973196e-4
       .9999999495708498 3.1758195465348636e-4
       .9999999476499103 3.235740667982502e-4
       .9999999456930654 3.2956617893139595e-4
       .9999999437003151 3.3555829105270853e-4
       .9999999416716594 3.4155040316197275e-4
       .9999999396070982 3.475425152589734e-4
       .9999999375066316 3.535346273434955e-4
       .9999999353702598 3.595267394153237e-4
       .9999999331979824 3.6551885147424295e-4
       .9999999309897996 3.7151096352003814e-4
       .9999999287457114 3.7750307555249406e-4
       .9999999264657179 3.8349518757139556e-4
       .9999999241498189 3.8948729957652753e-4
       .9999999217980144 3.954794115676748e-4
       .9999999194103046 4.0147152354462224e-4
       .9999999169866894 4.0746363550715466e-4
       .9999999145271687 4.134557474550569e-4
       .9999999120317428 4.194478593881139e-4
       .9999999095004113 4.2543997130611036e-4
       .9999999069331744 4.314320832088313e-4
       .9999999043300322 4.3742419509606144e-4
       .9999999016909845 4.4341630696758576e-4
       .9999998990160315 4.4940841882318896e-4
       .9999998963051729 4.55400530662656e-4
       .999999893558409 4.613926424857717e-4
       .9999998907757398 4.673847542923209e-4
       .9999998879571651 4.7337686608208844e-4
       .9999998851026849 4.793689778548592e-4
       .9999998822122994 4.8536108961041806e-4
       .9999998792860085 4.913532013485497e-4
       .9999998763238122 4.973453130690393e-4
       .9999998733257104 5.033374247716714e-4
       .9999998702917032 5.09329536456231e-4
       .9999998672217907 5.153216481225028e-4
       .9999998641159727 5.213137597702719e-4
       .9999998609742493 5.27305871399323e-4
       .9999998577966206 5.332979830094408e-4
       .9999998545830864 5.392900946004105e-4
       .9999998513336468 5.452822061720168e-4
       .9999998480483018 5.512743177240444e-4
       .9999998447270514 5.572664292562783e-4
       .9999998413698955 5.632585407685033e-4
       .9999998379768343 5.692506522605043e-4
       .9999998345478677 5.752427637320661e-4
       .9999998310829956 5.812348751829735e-4
       .9999998275822183 5.872269866130116e-4
       .9999998240455354 5.93219098021965e-4
       .9999998204729471 5.992112094096185e-4
       .9999998168644535 6.052033207757572e-4
       .9999998132200545 6.111954321201659e-4
       .99999980953975 6.171875434426292e-4
       .9999998058235401 6.231796547429323e-4
       .9999998020714248 6.291717660208597e-4
       .9999997982834041 6.351638772761965e-4
       .9999997944594781 6.411559885087275e-4
       .9999997905996466 6.471480997182375e-4
       .9999997867039097 6.531402109045114e-4
       .9999997827722674 6.591323220673341e-4
       .9999997788047197 6.651244332064902e-4
       .9999997748012666 6.711165443217649e-4
       .9999997707619082 6.771086554129428e-4
       .9999997666866443 6.83100766479809e-4
       .9999997625754748 6.89092877522148e-4
       .9999997584284002 6.950849885397449e-4
       .9999997542454201 7.010770995323844e-4
       .9999997500265345 7.070692104998515e-4
       .9999997457717437 7.130613214419311e-4
       .9999997414810473 7.190534323584079e-4
       .9999997371544456 7.250455432490666e-4
       .9999997327919384 7.310376541136925e-4
       .9999997283935259 7.3702976495207e-4
       .999999723959208 7.430218757639842e-4
       .9999997194889846 7.490139865492199e-4
       .9999997149828559 7.55006097307562e-4
       .9999997104408218 7.609982080387952e-4
       .9999997058628822 7.669903187427045e-4
       .9999997012490373 7.729824294190747e-4
       .9999996965992869 7.789745400676906e-4
       .9999996919136313 7.849666506883372e-4
       .99999968719207 7.909587612807992e-4
       .9999996824346035 7.969508718448614e-4
       .9999996776412315 8.029429823803089e-4
       .9999996728119542 8.089350928869263e-4
       .9999996679467715 8.149272033644986e-4
       .9999996630456833 8.209193138128106e-4
       .9999996581086897 8.269114242316472e-4
       .9999996531357909 8.329035346207931e-4
       .9999996481269865 8.388956449800333e-4
       .9999996430822767 8.448877553091527e-4
       .9999996380016616 8.508798656079359e-4
       .999999632885141 8.56871975876168e-4
       .9999996277327151 8.628640861136338e-4
       .9999996225443838 8.68856196320118e-4
       .9999996173201471 8.748483064954056e-4
       .999999612060005 8.808404166392814e-4
       .9999996067639574 8.868325267515304e-4
       .9999996014320045 8.928246368319371e-4
       .9999995960641462 8.988167468802867e-4
       .9999995906603825 9.048088568963639e-4
       .9999995852207133 9.108009668799535e-4
       .9999995797451389 9.167930768308405e-4
       .9999995742336589 9.227851867488095e-4
       .9999995686862736 9.287772966336457e-4
       .9999995631029829 9.347694064851338e-4
       .9999995574837868 9.407615163030585e-4
       .9999995518286853 9.467536260872047e-4
       .9999995461376784 9.527457358373575e-4
       .9999995404107661 9.587378455533015e-4
       .9999995346479484 9.647299552348216e-4
       .9999995288492254 9.707220648817027e-4
       .9999995230145969 9.767141744937296e-4
       .9999995171440631 9.827062840706872e-4
       .9999995112376238 9.886983936123602e-4
       .9999995052952791 9.946905031185337e-4
       .9999994993170291 .0010006826125889925
       .9999994933028736 .0010066747220235214
       .9999994872528128 .001012666831421905
       .9999994811668466 .0010186589407839286
       .999999475044975 .0010246510501093766
       .9999994688871979 .0010306431593980344
       .9999994626935156 .0010366352686496862
       .9999994564639277 .0010426273778641173
       .9999994501984345 .0010486194870411127
       .999999443897036 .0010546115961804568
       .999999437559732 .0010606037052819344
       .9999994311865227 .0010665958143453308
       .9999994247774079 .0010725879233704307
       .9999994183323877 .0010785800323570187
       .9999994118514622 .0010845721413048801
       .9999994053346313 .0010905642502137994
       .9999993987818949 .0010965563590835613
       .9999993921932533 .0011025484679139511
       .9999993855687062 .0011085405767047535
       .9999993789082536 .0011145326854557532
       .9999993722118957 .001120524794166735
       .9999993654796325 .0011265169028374842
       .9999993587114638 .0011325090114677853
       .9999993519073898 .001138501120057423
       .9999993450674104 .0011444932286061825
       .9999993381915255 .0011504853371138485
       .9999993312797354 .0011564774455802057
       .9999993243320398 .0011624695540050393
       .9999993173484387 .001168461662388134
       .9999993103289324 .0011744537707292742
       .9999993032735206 .0011804458790282454
       .9999992961822035 .0011864379872848323
       .9999992890549809 .0011924300954988195
       .999999281891853 .001198422203669992
       .9999992746928197 .0012044143117981348
       .999999267457881 .0012104064198830327
       .999999260187037 .0012163985279244702
       .9999992528802875 .0012223906359222325
       .9999992455376326 .0012283827438761045
       .9999992381590724 .0012343748517858707
       .9999992307446068 .0012403669596513162
       .9999992232942359 .001246359067472226
       .9999992158079595 .0012523511752483847
       .9999992082857777 .001258343282979577
       .9999992007276906 .001264335390665588
       .999999193133698 .0012703274983062026
       .9999991855038001 .0012763196059012057
       .9999991778379967 .001282311713450382
       .9999991701362881 .0012883038209535163
       .999999162398674 .0012942959284103935
       .9999991546251547 .0013002880358207985
       .9999991468157298 .001306280143184516
       .9999991389703996 .001312272250501331
       .999999131089164 .0013182643577710285
       .999999123172023 .0013242564649933932
       .9999991152189767 .0013302485721682098
       .9999991072300249 .001336240679295263
       .9999990992051678 .0013422327863743383
       .9999990911444054 .0013482248934052201
       .9999990830477375 .0013542170003876934
       .9999990749151643 .001360209107321543
       .9999990667466857 .0013662012142065536
       .9999990585423016 .0013721933210425101
       .9999990503020123 .0013781854278291975
       .9999990420258176 .0013841775345664006
       .9999990337137175 .0013901696412539043
       .999999025365712 .0013961617478914935
       .999999016981801 .0014021538544789526
       .9999990085619848 .001408145961016067
       .9999990001062631 .0014141380675026214
       .9999989916146361 .0014201301739384005
       .9999989830871038 .0014261222803231893
       .9999989745236659 .0014321143866567725
       .9999989659243228 .001438106492938935
       .9999989572890743 .0014440985991694619
       .9999989486179204 .0014500907053481378
       .9999989399108612 .0014560828114747475
       .9999989311678965 .0014620749175490758
       .9999989223890265 .001468067023570908
       .9999989135742512 .0014740591295400284
       .9999989047235704 .0014800512354562223
       .9999988958369843 .0014860433413192743
       .9999988869144928 .0014920354471289693
       .9999988779560959 .0014980275528850922
       .9999988689617937 .0015040196585874275
       .9999988599315861 .0015100117642357607
       .999998850865473 .0015160038698298762
       .9999988417634548 .001521995975369559
       .999998832625531 .0015279880808545937
       .9999988234517019 .0015339801862847657
       .9999988142419675 .0015399722916598592
       .9999988049963277 .0015459643969796596
       .9999987957147825 .0015519565022439512
       .9999987863973319 .0015579486074525195
       .9999987770439759 .001563940712605149
       .9999987676547146 .0015699328177016243
       .999998758229548 .0015759249227417307
       .9999987487684759 .0015819170277252528
       .9999987392714985 .0015879091326519755
       .9999987297386157 .0015939012375216837
       .9999987201698276 .0015998933423341623
       .9999987105651341 .001605885447089196
       .9999987009245352 .0016118775517865696
       .999998691248031 .0016178696564260683
       .9999986815356214 .0016238617610074765
       .9999986717873064 .0016298538655305794
       .9999986620030861 .0016358459699951618
       .9999986521829605 .0016418380744010084
       .9999986423269294 .0016478301787479041
       .999998632434993 .0016538222830356339
       .9999986225071512 .0016598143872639823
       .999998612543404 .0016658064914327345
       .9999986025437515 .0016717985955416754
       .9999985925081937 .0016777906995905894
       .9999985824367305 .0016837828035792617
       .9999985723293618 .0016897749075074774
       .999998562186088 .0016957670113750207
       .9999985520069086 .0017017591151816769
       .9999985417918239 .0017077512189272307
       .999998531540834 .001713743322611467
       .9999985212539385 .0017197354262341706
       .9999985109311378 .0017257275297951264
       .9999985005724317 .0017317196332941192
       .9999984901778203 .0017377117367309341
       .9999984797473034 .0017437038401053556
       .9999984692808812 .0017496959434171687
       .9999984587785538 .0017556880466661582
       .9999984482403208 .001761680149852109
       .9999984376661826 .0017676722529748061
       .999998427056139 .0017736643560340342
       .99999841641019 .001779656459029578
       .9999984057283358 .0017856485619612225
       .9999983950105761 .0017916406648287528
       .999998384256911 .0017976327676319532
       .9999983734673407 .001803624870370609
       .9999983626418649 .0018096169730445048
       .9999983517804839 .0018156090756534257
       .9999983408831975 .0018216011781971562
       .9999983299500057 .0018275932806754815
       .9999983189809085 .0018335853830881864
       .999998307975906 .0018395774854350557
       .9999982969349982 .001845569587715874
       .9999982858581851 .0018515616899304264
       .9999982747454665 .001857553792078498
       .9999982635968426 .001863545894159873
       .9999982524123134 .0018695379961743367
       .9999982411918789 .001875530098121674
       .9999982299355389 .0018815222000016696
       .9999982186432936 .0018875143018141083
       .999998207315143 .0018935064035587748
       .999998195951087 .0018994985052354545
       .9999981845511257 .0019054906068439318
       .9999981731152591 .0019114827083839918
       .999998161643487 .001917474809855419
       .9999981501358096 .0019234669112579987
       .999998138592227 .0019294590125915154
       .9999981270127389 .0019354511138557542
       .9999981153973455 .0019414432150504997
       .9999981037460468 .0019474353161755369
       .9999980920588427 .001953427417230651
       .9999980803357332 .001959419518215626
       .9999980685767185 .0019654116191302473
       .9999980567817984 .0019714037199743
       .9999980449509729 .0019773958207475683
       .9999980330842422 .0019833879214498375
       .999998021181606 .001989380022080892
       .9999980092430646 .0019953721226405176
       .9999979972686177 .002001364223128498
       .9999979852582656 .002007356323544619
       .9999979732120081 .002013348423888665
       .9999979611298453 .002019340524160421
       .9999979490117771 .0020253326243596715
       .9999979368578036 .0020313247244862017
       .9999979246679247 .002037316824539796
       .9999979124421405 .00204330892452024
       .999997900180451 .002049301024427318
       .9999978878828562 .0020552931242608153
       .9999978755493559 .002061285224020516
       .9999978631799504 .0020672773237062057
       .9999978507746395 .002073269423317669
       .9999978383334234 .0020792615228546903
       .9999978258563018 .002085253622317055
       .999997813343275 .0020912457217045484
       .9999978007943428 .002097237821016954
       .9999977882095052 .0021032299202540577
       .9999977755887623 .0021092220194156444
       .9999977629321142 .0021152141185014984
       .9999977502395607 .0021212062175114043
       .9999977375111019 .002127198316445148
       .9999977247467376 .0021331904153025134
       .9999977119464681 .002139182514083286
       .9999976991102932 .0021451746127872503
       .9999976862382131 .002151166711414191
       .9999976733302276 .0021571588099638934
       .9999976603863368 .0021631509084361423
       .9999976474065406 .002169143006830722
       .9999976343908391 .002175135105147418
       .9999976213392323 .0021811272033860148
       .9999976082517201 .002187119301546297
       .9999975951283027 .00219311139962805
       .9999975819689799 .0021991034976310588
       .9999975687737518 .0022050955955551076
       .9999975555426184 .0022110876933999816
       .9999975422755796 .0022170797911654654
       .9999975289726355 .002223071888851344
       .9999975156337861 .0022290639864574026
       .9999975022590314 .0022350560839834253
       .9999974888483714 .002241048181429198
       .999997475401806 .0022470402787945045
       .9999974619193353 .00225303237607913
       .9999974484009593 .0022590244732828596
       .9999974348466779 .0022650165704054784
       .9999974212564913 .0022710086674467703
       .9999974076303992 .002277000764406521
       .9999973939684019 .002282992861284515
       .9999973802704993 .0022889849580805368
       .9999973665366915 .0022949770547943723
       .9999973527669782 .0023009691514258054
       .9999973389613596 .002306961247974621
       .9999973251198357 .0023129533444406045
       .9999973112424065 .0023189454408235406
       .999997297329072 .0023249375371232135
       .9999972833798322 .002330929633339409
       .999997269394687 .0023369217294719113
       .9999972553736366 .0023429138255205055
       .9999972413166809 .0023489059214849765
       .9999972272238198 .002354898017365109
       .9999972130950534 .0023608901131606883
       .9999971989303816 .0023668822088714985
       .9999971847298047 .0023728743044973246
       .9999971704933224 .0023788664000379523
       .9999971562209347 .0023848584954931653
       .9999971419126418 .0023908505908627493
       .9999971275684435 .0023968426861464883
       .99999711318834 .002402834781344168
       .9999970987723311 .0024088268764555732
       .9999970843204169 .002414818971480488
       .9999970698325974 .002420811066418698
       .9999970553088726 .0024268031612699878
       .9999970407492426 .002432795256034142
       .9999970261537071 .002438787350710946
       .9999970115222664 .002444779445300184
       .9999969968549204 .0024507715398016418
       .9999969821516691 .002456763634215103
       .9999969674125124 .002462755728540353
       .9999969526374506 .0024687478227771774
       .9999969378264834 .00247473991692536
       .9999969229796108 .002480732010984686
       .999996908096833 .0024867241049549406
       .9999968931781499 .002492716198835908
       .9999968782235614 .0024987082926273734
       .9999968632330677 .002504700386329122
       .9999968482066687 .002510692479940938
       .9999968331443644 .0025166845734626068
       .9999968180461547 .0025226766668939127
       .9999968029120399 .002528668760234641
       .9999967877420196 .002534660853484576
       .9999967725360941 .0025406529466435036
       .9999967572942633 .002546645039711208
       .9999967420165272 .002552637132687474
       .9999967267028858 .002558629225572086
       .9999967113533391 .0025646213183648297
       .9999966959678871 .0025706134110654896
       .9999966805465298 .002576605503673851
       .9999966650892672 .0025825975961896977
       .9999966495960994 .0025885896886128153
       .9999966340670262 .0025945817809429885
       .9999966185020478 .0026005738731800024
       .9999966029011641 .0026065659653236417
       .999996587264375 .002612558057373691
       .9999965715916808 .002618550149329935
       .9999965558830811 .0026245422411921592
       .9999965401385762 .002630534332960148
       .9999965243581661 .002636526424633687
       .9999965085418506 .0026425185162125596
       .9999964926896299 .0026485106076965517
       .9999964768015038 .0026545026990854484
       .9999964608774725 .0026604947903790337
       .9999964449175359 .0026664868815770926
       .999996428921694 .0026724789726794104
       .9999964128899468 .002678471063685772
       .9999963968222944 .0026844631545959617
       .9999963807187366 .002690455245409765
       .9999963645792737 .002696447336126966
       .9999963484039053 .00270243942674735
       .9999963321926317 .002708431517270702
       .9999963159454529 .0027144236076968066
       .9999962996623687 .0027204156980254485
       .9999962833433793 .002726407788256413
       .9999962669884847 .002732399878389485
       .9999962505976846 .0027383919684244484
       .9999962341709794 .002744384058361089
       .9999962177083689 .0027503761481991913
       .999996201209853 .0027563682379385403
       .9999961846754319 .0027623603275789207
       .9999961681051056 .0027683524171201175
       .999996151498874 .002774344506561915
       .9999961348567371 .002780336595904099
       .9999961181786949 .0027863286851464537
       .9999961014647475 .0027923207742887642
       .9999960847148948 .0027983128633308155
       .9999960679291368 .002804304952272392
       .9999960511074735 .002810297041113279
       .9999960342499049 .0028162891298532606
       .9999960173564312 .0028222812184921227
       .9999960004270521 .002828273307029649
       .9999959834617678 .002834265395465626
       .9999959664605781 .0028402574837998367
       .9999959494234832 .002846249572032067
       .9999959323504831 .0028522416601621014
       .9999959152415777 .002858233748189725
       .999995898096767 .002864225836114723
       .9999958809160512 .0028702179239368793
       .9999958636994299 .0028762100116559793
       .9999958464469034 .0028822020992718077
       .9999958291584717 .0028881941867841495
       .9999958118341348 .0028941862741927895
       .9999957944738925 .0029001783614975127
       .999995777077745 .002906170448698104
       .9999957596456922 .0029121625357943475
       .9999957421777342 .002918154622786029
       .999995724673871 .0029241467096729327
       .9999957071341024 .002930138796454844
       .9999956895584287 .0029361308831315474
       .9999956719468496 .0029421229697028273
       .9999956542993652 .0029481150561684695
       .9999956366159757 .0029541071425282584
       .9999956188966809 .002960099228781979
       .9999956011414808 .002966091314929416
       .9999955833503754 .002972083400970354
       .9999955655233649 .0029780754869045785
       .9999955476604491 .0029840675727318736
       .999995529761628 .002990059658452025
       .9999955118269016 .0029960517440648163
       .99999549385627 .0030020438295700336
       .9999954758497331 .0030080359149674612
       .999995457807291 .003014028000256884
       .9999954397289438 .003020020085438087
       .9999954216146911 .0030260121705108552
       .9999954034645333 .003032004255474973
       .9999953852784702 .003037996340330225
       .9999953670565019 .003043988425076397
       .9999953487986284 .003049980509713273
       .9999953305048496 .0030559725942406386
       .9999953121751655 .003061964678658278
       ))

    (define high-lut
    '#f64(1. 0.
       .9999999999999999 1.1703344634137277e-8
       .9999999999999998 2.3406689268274554e-8
       .9999999999999993 3.5110033902411824e-8
       .9999999999999989 4.6813378536549095e-8
       .9999999999999983 5.851672317068635e-8
       .9999999999999976 7.022006780482361e-8
       .9999999999999967 8.192341243896085e-8
       .9999999999999957 9.362675707309808e-8
       .9999999999999944 1.0533010170723531e-7
       .9999999999999931 1.170334463413725e-7
       .9999999999999917 1.287367909755097e-7
       .9999999999999901 1.4044013560964687e-7
       .9999999999999885 1.5214348024378403e-7
       .9999999999999866 1.6384682487792116e-7
       .9999999999999846 1.7555016951205827e-7
       .9999999999999825 1.8725351414619535e-7
       .9999999999999802 1.989568587803324e-7
       .9999999999999778 2.1066020341446942e-7
       .9999999999999752 2.2236354804860645e-7
       .9999999999999726 2.3406689268274342e-7
       .9999999999999698 2.4577023731688034e-7
       .9999999999999668 2.5747358195101726e-7
       .9999999999999638 2.6917692658515413e-7
       .9999999999999606 2.8088027121929094e-7
       .9999999999999571 2.9258361585342776e-7
       .9999999999999537 3.042869604875645e-7
       .99999999999995 3.159903051217012e-7
       .9999999999999463 3.276936497558379e-7
       .9999999999999424 3.3939699438997453e-7
       .9999999999999384 3.5110033902411114e-7
       .9999999999999342 3.6280368365824763e-7
       .9999999999999298 3.7450702829238413e-7
       .9999999999999254 3.8621037292652057e-7
       .9999999999999208 3.979137175606569e-7
       .9999999999999161 4.0961706219479325e-7
       .9999999999999113 4.2132040682892953e-7
       .9999999999999063 4.330237514630657e-7
       .9999999999999011 4.447270960972019e-7
       .9999999999998959 4.5643044073133796e-7
       .9999999999998904 4.68133785365474e-7
       .9999999999998849 4.7983712999961e-7
       .9999999999998792 4.915404746337459e-7
       .9999999999998733 5.032438192678817e-7
       .9999999999998674 5.149471639020175e-7
       .9999999999998613 5.266505085361531e-7
       .9999999999998551 5.383538531702888e-7
       .9999999999998487 5.500571978044243e-7
       .9999999999998422 5.617605424385598e-7
       .9999999999998356 5.734638870726952e-7
       .9999999999998288 5.851672317068305e-7
       .9999999999998219 5.968705763409657e-7
       .9999999999998148 6.085739209751009e-7
       .9999999999998076 6.202772656092359e-7
       .9999999999998003 6.319806102433709e-7
       .9999999999997928 6.436839548775058e-7
       .9999999999997853 6.553872995116406e-7
       .9999999999997775 6.670906441457753e-7
       .9999999999997696 6.7879398877991e-7
       .9999999999997616 6.904973334140445e-7
       .9999999999997534 7.02200678048179e-7
       .9999999999997452 7.139040226823132e-7
       .9999999999997368 7.256073673164475e-7
       .9999999999997282 7.373107119505817e-7
       .9999999999997194 7.490140565847157e-7
       .9999999999997107 7.607174012188497e-7
       .9999999999997017 7.724207458529835e-7
       .9999999999996926 7.841240904871172e-7
       .9999999999996834 7.958274351212508e-7
       .9999999999996739 8.075307797553844e-7
       .9999999999996644 8.192341243895178e-7
       .9999999999996547 8.309374690236511e-7
       .999999999999645 8.426408136577842e-7
       .9999999999996351 8.543441582919173e-7
       .999999999999625 8.660475029260503e-7
       .9999999999996148 8.777508475601831e-7
       .9999999999996044 8.894541921943158e-7
       .999999999999594 9.011575368284484e-7
       .9999999999995833 9.128608814625808e-7
       .9999999999995726 9.245642260967132e-7
       .9999999999995617 9.362675707308454e-7
       .9999999999995507 9.479709153649775e-7
       .9999999999995395 9.596742599991095e-7
       .9999999999995283 9.713776046332412e-7
       .9999999999995168 9.83080949267373e-7
       .9999999999995052 9.947842939015044e-7
       .9999999999994935 1.006487638535636e-6
       .9999999999994816 1.0181909831697673e-6
       .9999999999994696 1.0298943278038984e-6
       .9999999999994575 1.0415976724380293e-6
       .9999999999994453 1.0533010170721601e-6
       .9999999999994329 1.065004361706291e-6
       .9999999999994204 1.0767077063404215e-6
       .9999999999994077 1.088411050974552e-6
       .9999999999993949 1.1001143956086822e-6
       .9999999999993819 1.1118177402428122e-6
       .9999999999993688 1.1235210848769423e-6
       .9999999999993556 1.135224429511072e-6
       .9999999999993423 1.1469277741452017e-6
       .9999999999993288 1.1586311187793313e-6
       .9999999999993151 1.1703344634134605e-6
       .9999999999993014 1.1820378080475897e-6
       .9999999999992875 1.1937411526817187e-6
       .9999999999992735 1.2054444973158477e-6
       .9999999999992593 1.2171478419499764e-6
       .9999999999992449 1.2288511865841048e-6
       .9999999999992305 1.2405545312182331e-6
       .999999999999216 1.2522578758523615e-6
       .9999999999992012 1.2639612204864894e-6
       .9999999999991863 1.2756645651206173e-6
       .9999999999991713 1.287367909754745e-6
       .9999999999991562 1.2990712543888725e-6
       .9999999999991409 1.3107745990229998e-6
       .9999999999991255 1.3224779436571269e-6
       .9999999999991099 1.3341812882912537e-6
       .9999999999990943 1.3458846329253806e-6
       .9999999999990785 1.3575879775595072e-6
       .9999999999990625 1.3692913221936337e-6
       .9999999999990464 1.3809946668277597e-6
       .9999999999990302 1.3926980114618857e-6
       .9999999999990138 1.4044013560960117e-6
       .9999999999989974 1.4161047007301373e-6
       .9999999999989807 1.4278080453642627e-6
       .9999999999989639 1.439511389998388e-6
       .999999999998947 1.451214734632513e-6
       .99999999999893 1.462918079266638e-6
       .9999999999989128 1.4746214239007625e-6
       .9999999999988954 1.486324768534887e-6
       .999999999998878 1.4980281131690111e-6
       .9999999999988604 1.5097314578031353e-6
       .9999999999988426 1.5214348024372591e-6
       .9999999999988247 1.5331381470713828e-6
       .9999999999988067 1.544841491705506e-6
       .9999999999987886 1.5565448363396294e-6
       .9999999999987703 1.5682481809737524e-6
       .9999999999987519 1.579951525607875e-6
       .9999999999987333 1.5916548702419977e-6
       .9999999999987146 1.60335821487612e-6
       .9999999999986958 1.615061559510242e-6
       .9999999999986768 1.626764904144364e-6
       .9999999999986577 1.6384682487784858e-6
       .9999999999986384 1.6501715934126072e-6
       .9999999999986191 1.6618749380467283e-6
       .9999999999985996 1.6735782826808495e-6
       .9999999999985799 1.6852816273149702e-6
       .9999999999985602 1.6969849719490907e-6
       .9999999999985402 1.708688316583211e-6
       .9999999999985201 1.720391661217331e-6
       .9999999999985 1.732095005851451e-6
       .9999999999984795 1.7437983504855706e-6
       .9999999999984591 1.7555016951196899e-6
       .9999999999984385 1.767205039753809e-6
       .9999999999984177 1.778908384387928e-6
       .9999999999983968 1.7906117290220465e-6
       .9999999999983759 1.802315073656165e-6
       .9999999999983546 1.814018418290283e-6
       .9999999999983333 1.825721762924401e-6
       .9999999999983119 1.8374251075585186e-6
       .9999999999982904 1.8491284521926361e-6
       .9999999999982686 1.8608317968267533e-6
       .9999999999982468 1.8725351414608702e-6
       .9999999999982249 1.8842384860949866e-6
       .9999999999982027 1.8959418307291031e-6
       .9999999999981805 1.9076451753632194e-6
       .999999999998158 1.919348519997335e-6
       .9999999999981355 1.9310518646314507e-6
       .9999999999981128 1.942755209265566e-6
       .9999999999980901 1.954458553899681e-6
       .9999999999980671 1.966161898533796e-6
       .999999999998044 1.9778652431679103e-6
       .9999999999980208 1.9895685878020246e-6
       .9999999999979975 2.0012719324361386e-6
       .999999999997974 2.012975277070252e-6
       .9999999999979503 2.0246786217043656e-6
       .9999999999979265 2.0363819663384787e-6
       .9999999999979027 2.048085310972592e-6
       .9999999999978786 2.0597886556067045e-6
       .9999999999978545 2.0714920002408167e-6
       .9999999999978302 2.0831953448749286e-6
       .9999999999978058 2.0948986895090404e-6
       .9999999999977811 2.106602034143152e-6
       .9999999999977564 2.118305378777263e-6
       .9999999999977315 2.1300087234113738e-6
       .9999999999977065 2.1417120680454843e-6
       .9999999999976814 2.153415412679595e-6
       .9999999999976561 2.1651187573137046e-6
       .9999999999976307 2.1768221019478143e-6
       .9999999999976051 2.188525446581924e-6
       .9999999999975795 2.200228791216033e-6
       .9999999999975536 2.2119321358501417e-6
       .9999999999975278 2.22363548048425e-6
       .9999999999975017 2.2353388251183586e-6
       .9999999999974754 2.247042169752466e-6
       .999999999997449 2.2587455143865738e-6
       .9999999999974225 2.2704488590206814e-6
       .9999999999973959 2.282152203654788e-6
       .9999999999973691 2.293855548288895e-6
       .9999999999973422 2.305558892923001e-6
       .9999999999973151 2.317262237557107e-6
       .999999999997288 2.328965582191213e-6
       .9999999999972606 2.340668926825318e-6
       .9999999999972332 2.352372271459423e-6
       .9999999999972056 2.364075616093528e-6
       .9999999999971778 2.3757789607276323e-6
       .99999999999715 2.3874823053617365e-6
       .999999999997122 2.3991856499958403e-6
       .9999999999970938 2.4108889946299437e-6
       .9999999999970656 2.4225923392640466e-6
       .9999999999970371 2.4342956838981495e-6
       .9999999999970085 2.445999028532252e-6
       .9999999999969799 2.457702373166354e-6
       .999999999996951 2.4694057178004558e-6
       .999999999996922 2.4811090624345574e-6
       .9999999999968929 2.4928124070686583e-6
       .9999999999968637 2.504515751702759e-6
       .9999999999968343 2.5162190963368595e-6
       .9999999999968048 2.5279224409709594e-6
       .9999999999967751 2.5396257856050594e-6
       .9999999999967454 2.5513291302391585e-6
       .9999999999967154 2.5630324748732576e-6
       .9999999999966853 2.5747358195073563e-6
       .9999999999966551 2.5864391641414546e-6
       .9999999999966248 2.5981425087755525e-6
       .9999999999965944 2.6098458534096503e-6
       .9999999999965637 2.6215491980437473e-6
       .999999999996533 2.6332525426778443e-6
       .9999999999965021 2.644955887311941e-6
       .999999999996471 2.656659231946037e-6
       .99999999999644 2.6683625765801328e-6
       .9999999999964087 2.680065921214228e-6
       .9999999999963772 2.6917692658483234e-6
       .9999999999963456 2.703472610482418e-6
       .999999999996314 2.7151759551165123e-6
       .9999999999962821 2.7268792997506064e-6
       .9999999999962501 2.7385826443846996e-6
       .9999999999962179 2.750285989018793e-6
       .9999999999961857 2.761989333652886e-6
       .9999999999961533 2.7736926782869783e-6
       .9999999999961208 2.78539602292107e-6
       .9999999999960881 2.797099367555162e-6
       .9999999999960553 2.808802712189253e-6
       .9999999999960224 2.8205060568233443e-6
       .9999999999959893 2.832209401457435e-6
       .9999999999959561 2.8439127460915247e-6
       .9999999999959227 2.8556160907256145e-6
       .9999999999958893 2.867319435359704e-6
       .9999999999958556 2.879022779993793e-6
       .9999999999958219 2.8907261246278814e-6
       .9999999999957879 2.90242946926197e-6
       .999999999995754 2.9141328138960576e-6
       .9999999999957198 2.925836158530145e-6
       .9999999999956855 2.9375395031642317e-6
       .999999999995651 2.9492428477983186e-6
       .9999999999956164 2.9609461924324046e-6
       .9999999999955816 2.9726495370664905e-6
       .9999999999955468 2.9843528817005757e-6
       .9999999999955118 2.996056226334661e-6
       .9999999999954767 3.007759570968745e-6
       .9999999999954414 3.0194629156028294e-6
       .999999999995406 3.0311662602369133e-6
       .9999999999953705 3.0428696048709963e-6
       .9999999999953348 3.0545729495050794e-6
       .999999999995299 3.066276294139162e-6
       .999999999995263 3.0779796387732437e-6
       .9999999999952269 3.0896829834073255e-6
       .9999999999951907 3.101386328041407e-6
       .9999999999951543 3.1130896726754873e-6
       .9999999999951178 3.1247930173095678e-6
       .9999999999950812 3.136496361943648e-6
       .9999999999950444 3.148199706577727e-6
       .9999999999950075 3.1599030512118063e-6
       .9999999999949705 3.171606395845885e-6
       .9999999999949333 3.183309740479963e-6
       .999999999994896 3.195013085114041e-6
       .9999999999948584 3.206716429748118e-6
       .9999999999948209 3.218419774382195e-6
       .9999999999947832 3.2301231190162714e-6
       .9999999999947453 3.2418264636503477e-6
       .9999999999947072 3.253529808284423e-6
       .9999999999946692 3.265233152918498e-6
       .9999999999946309 3.276936497552573e-6
       .9999999999945924 3.288639842186647e-6
       .9999999999945539 3.300343186820721e-6
       .9999999999945152 3.312046531454794e-6
       .9999999999944763 3.323749876088867e-6
       .9999999999944373 3.3354532207229395e-6
       .9999999999943983 3.3471565653570115e-6
       .9999999999943591 3.358859909991083e-6
       .9999999999943197 3.370563254625154e-6
       .9999999999942801 3.3822665992592245e-6
       .9999999999942405 3.3939699438932944e-6
       .9999999999942008 3.4056732885273643e-6
       .9999999999941608 3.4173766331614334e-6
       .9999999999941207 3.429079977795502e-6
       .9999999999940805 3.4407833224295702e-6
       .9999999999940402 3.452486667063638e-6
       .9999999999939997 3.4641900116977054e-6
       .999999999993959 3.4758933563317723e-6
       .9999999999939183 3.4875967009658384e-6
       .9999999999938775 3.4993000455999045e-6
       .9999999999938364 3.5110033902339697e-6
       .9999999999937953 3.5227067348680345e-6
       .999999999993754 3.534410079502099e-6
       .9999999999937126 3.546113424136163e-6
       .999999999993671 3.5578167687702264e-6
       .9999999999936293 3.5695201134042896e-6
       .9999999999935875 3.581223458038352e-6
       .9999999999935454 3.592926802672414e-6
       .9999999999935033 3.6046301473064755e-6
       .9999999999934611 3.6163334919405365e-6
       .9999999999934187 3.628036836574597e-6
       .9999999999933762 3.639740181208657e-6
       .9999999999933334 3.6514435258427166e-6
       .9999999999932907 3.6631468704767755e-6
       .9999999999932477 3.674850215110834e-6
       .9999999999932047 3.686553559744892e-6
       .9999999999931615 3.6982569043789496e-6
       .9999999999931181 3.7099602490130064e-6
       .9999999999930747 3.7216635936470627e-6
       .999999999993031 3.733366938281119e-6
       .9999999999929873 3.745070282915174e-6
       .9999999999929433 3.756773627549229e-6
       .9999999999928992 3.768476972183284e-6
       .9999999999928552 3.7801803168173377e-6
       .9999999999928109 3.791883661451391e-6
       .9999999999927663 3.803587006085444e-6
       .9999999999927218 3.8152903507194965e-6
       .9999999999926771 3.826993695353548e-6
       .9999999999926322 3.838697039987599e-6
       .9999999999925873 3.85040038462165e-6
       .9999999999925421 3.862103729255701e-6
       .9999999999924968 3.87380707388975e-6
       .9999999999924514 3.885510418523799e-6
       .9999999999924059 3.897213763157848e-6
       .9999999999923602 3.9089171077918965e-6
       .9999999999923144 3.9206204524259435e-6
       .9999999999922684 3.9323237970599905e-6
       .9999999999922223 3.9440271416940376e-6
       .9999999999921761 3.955730486328084e-6
       .9999999999921297 3.967433830962129e-6
       .9999999999920832 3.9791371755961736e-6
       .9999999999920366 3.990840520230218e-6
       .9999999999919899 4.002543864864262e-6
       .9999999999919429 4.014247209498305e-6
       .9999999999918958 4.025950554132348e-6
       .9999999999918486 4.03765389876639e-6
       .9999999999918013 4.049357243400431e-6
       .9999999999917539 4.061060588034472e-6
       .9999999999917063 4.072763932668513e-6
       .9999999999916586 4.084467277302553e-6
       .9999999999916107 4.096170621936592e-6
       .9999999999915626 4.107873966570632e-6
       .9999999999915146 4.119577311204669e-6
       .9999999999914663 4.131280655838707e-6
       .9999999999914179 4.142984000472745e-6
       .9999999999913692 4.154687345106781e-6
       .9999999999913206 4.166390689740817e-6
       .9999999999912718 4.178094034374852e-6
       .9999999999912228 4.189797379008887e-6
       .9999999999911737 4.201500723642921e-6
       .9999999999911244 4.213204068276955e-6
       .999999999991075 4.224907412910988e-6
       .9999999999910255 4.236610757545021e-6
       .9999999999909759 4.248314102179053e-6
       .9999999999909261 4.260017446813084e-6
       .9999999999908762 4.271720791447115e-6
       .9999999999908261 4.283424136081145e-6
       .9999999999907759 4.295127480715175e-6
       .9999999999907256 4.306830825349204e-6
       .9999999999906751 4.3185341699832325e-6
       .9999999999906245 4.33023751461726e-6
       .9999999999905738 4.3419408592512875e-6
       .9999999999905229 4.353644203885314e-6
       .9999999999904718 4.36534754851934e-6
       .9999999999904207 4.377050893153365e-6
       .9999999999903694 4.38875423778739e-6
       .999999999990318 4.400457582421414e-6
       .9999999999902665 4.4121609270554384e-6
       .9999999999902147 4.423864271689461e-6
       .9999999999901629 4.435567616323483e-6
       .9999999999901109 4.447270960957506e-6
       .9999999999900587 4.458974305591527e-6
       .9999999999900065 4.470677650225547e-6
       .9999999999899541 4.482380994859567e-6
       .9999999999899016 4.494084339493587e-6
       .9999999999898489 4.5057876841276054e-6
       .9999999999897962 4.517491028761624e-6
       .9999999999897432 4.529194373395641e-6
       .9999999999896901 4.5408977180296584e-6
       .999999999989637 4.552601062663675e-6
       .9999999999895836 4.564304407297691e-6
       .99999999998953 4.5760077519317055e-6
       .9999999999894764 4.5877110965657195e-6
       .9999999999894227 4.5994144411997335e-6
       .9999999999893688 4.611117785833747e-6
       .9999999999893148 4.622821130467759e-6
       .9999999999892606 4.634524475101771e-6
       .9999999999892063 4.646227819735783e-6
       .9999999999891518 4.657931164369793e-6
       .9999999999890973 4.669634509003803e-6
       .9999999999890425 4.681337853637813e-6
       .9999999999889877 4.693041198271821e-6
       .9999999999889327 4.704744542905829e-6
       .9999999999888776 4.716447887539837e-6
       .9999999999888223 4.728151232173843e-6
       .9999999999887669 4.73985457680785e-6
       .9999999999887114 4.751557921441855e-6
       .9999999999886556 4.76326126607586e-6
       .9999999999885999 4.774964610709864e-6
       .9999999999885439 4.786667955343868e-6
       .9999999999884878 4.798371299977871e-6
       .9999999999884316 4.810074644611873e-6
       .9999999999883752 4.821777989245874e-6
       .9999999999883187 4.833481333879875e-6
       .9999999999882621 4.845184678513876e-6
       .9999999999882053 4.856888023147875e-6
       .9999999999881484 4.868591367781874e-6
       .9999999999880914 4.880294712415872e-6
       .9999999999880341 4.89199805704987e-6
       .9999999999879768 4.903701401683867e-6
       .9999999999879194 4.915404746317863e-6
       .9999999999878618 4.9271080909518585e-6
       .9999999999878041 4.938811435585853e-6
       .9999999999877462 4.9505147802198475e-6
       .9999999999876882 4.962218124853841e-6
       .99999999998763 4.973921469487834e-6
       .9999999999875717 4.985624814121826e-6
       .9999999999875133 4.997328158755817e-6
       .9999999999874548 5.009031503389808e-6
       .9999999999873961 5.0207348480237985e-6
       .9999999999873372 5.032438192657788e-6
       .9999999999872783 5.0441415372917765e-6
       .9999999999872192 5.055844881925764e-6
       .9999999999871599 5.067548226559752e-6
       .9999999999871007 5.079251571193739e-6
       .9999999999870411 5.090954915827725e-6
       .9999999999869814 5.10265826046171e-6
       .9999999999869217 5.1143616050956945e-6
       .9999999999868617 5.126064949729678e-6
       .9999999999868017 5.1377682943636615e-6
       .9999999999867415 5.149471638997644e-6
       .9999999999866811 5.161174983631626e-6
       .9999999999866207 5.172878328265607e-6
       .9999999999865601 5.184581672899587e-6
       .9999999999864994 5.196285017533567e-6
       .9999999999864384 5.2079883621675455e-6
       .9999999999863775 5.219691706801524e-6
       .9999999999863163 5.2313950514355015e-6
       .999999999986255 5.243098396069478e-6
       .9999999999861935 5.254801740703454e-6
       .999999999986132 5.266505085337429e-6
       .9999999999860703 5.278208429971404e-6
       .9999999999860084 5.289911774605378e-6
       .9999999999859465 5.301615119239351e-6
       .9999999999858843 5.313318463873323e-6
       .9999999999858221 5.325021808507295e-6
       .9999999999857597 5.336725153141267e-6
       .9999999999856971 5.3484284977752366e-6
       .9999999999856345 5.360131842409206e-6
       .9999999999855717 5.371835187043175e-6
       .9999999999855087 5.383538531677143e-6
       .9999999999854456 5.3952418763111104e-6
       .9999999999853825 5.406945220945077e-6
       .9999999999853191 5.418648565579043e-6
       .9999999999852557 5.4303519102130076e-6
       .9999999999851921 5.4420552548469724e-6
       .9999999999851282 5.453758599480936e-6
       .9999999999850644 5.465461944114899e-6
       .9999999999850003 5.47716528874886e-6
       .9999999999849362 5.488868633382822e-6
       .9999999999848719 5.500571978016782e-6
       .9999999999848074 5.512275322650742e-6
       .9999999999847429 5.523978667284702e-6
       .9999999999846781 5.53568201191866e-6
       .9999999999846133 5.547385356552617e-6
       .9999999999845482 5.5590887011865745e-6
       .9999999999844832 5.57079204582053e-6
       .9999999999844179 5.582495390454486e-6
       .9999999999843525 5.59419873508844e-6
       .9999999999842869 5.605902079722394e-6
       .9999999999842213 5.617605424356347e-6
       .9999999999841555 5.629308768990299e-6
       .9999999999840895 5.641012113624251e-6
       .9999999999840234 5.652715458258201e-6
       .9999999999839572 5.664418802892152e-6
       .9999999999838908 5.6761221475261e-6
       .9999999999838243 5.687825492160048e-6
       .9999999999837577 5.699528836793996e-6
       .9999999999836909 5.711232181427943e-6
       .999999999983624 5.722935526061889e-6
       .9999999999835569 5.734638870695834e-6
       .9999999999834898 5.746342215329779e-6
       .9999999999834225 5.758045559963722e-6
       .999999999983355 5.769748904597665e-6
       .9999999999832874 5.781452249231607e-6
       .9999999999832196 5.793155593865548e-6
       .9999999999831518 5.804858938499489e-6
       .9999999999830838 5.816562283133429e-6
       .9999999999830157 5.8282656277673675e-6
       .9999999999829474 5.839968972401306e-6
       .9999999999828789 5.851672317035243e-6
       .9999999999828104 5.86337566166918e-6
       .9999999999827417 5.875079006303115e-6
       .9999999999826729 5.88678235093705e-6
       .9999999999826039 5.898485695570985e-6
       .9999999999825349 5.910189040204917e-6
       .9999999999824656 5.92189238483885e-6
       .9999999999823962 5.933595729472782e-6
       .9999999999823267 5.945299074106713e-6
       .9999999999822571 5.957002418740643e-6
       .9999999999821872 5.9687057633745715e-6
       .9999999999821173 5.9804091080085e-6
       ))

    (define low-lut-rac
    '#f64(1. 0.
       .9999952938095762 .003067956762965976
       .9999811752826011 .006135884649154475
       .9999576445519639 .00920375478205982
       .9999247018391445 .012271538285719925
       .9998823474542126 .015339206284988102
       .9998305817958234 .01840672990580482
       .9997694053512153 .021474080275469508
       .9996988186962042 .024541228522912288
       .9996188224951786 .027608145778965743
       .9995294175010931 .030674803176636626
       .9994306045554617 .03374117185137759
       .9993223845883495 .03680722294135883
       .9992047586183639 .03987292758773981
       .9990777277526454 .04293825693494082
       .9989412931868569 .04600318213091463
       .9987954562051724 .049067674327418015
       .9986402181802653 .052131704680283324
       .9984755805732948 .05519524434968994
       .9983015449338929 .05825826450043576
       .9981181129001492 .06132073630220858
       .997925286198596 .06438263092985747
       .9977230666441916 .06744391956366406
       .9975114561403035 .07050457338961387
       .9972904566786902 .07356456359966743
       .997060070339483 .07662386139203149
       .9968202992911657 .07968243797143013
       .9965711457905548 .08274026454937569
       .996312612182778 .0857973123444399
       .996044700901252 .0888535525825246
       .9957674144676598 .09190895649713272
       .9954807554919269 .094963495329639
       .9951847266721969 .0980171403295606
       .9948793307948056 .10106986275482782
       .9945645707342554 .10412163387205457
       .9942404494531879 .10717242495680884
       .9939069700023561 .11022220729388306
       .9935641355205953 .11327095217756435
       .9932119492347945 .11631863091190477
       .9928504144598651 .11936521481099137
       .99247953459871 .1224106751992162
       .9920993131421918 .12545498341154623
       .9917097536690995 .12849811079379317
       .9913108598461154 .13154002870288312
       .99090263542778 .1345807085071262
       .9904850842564571 .13762012158648604
       .9900582102622971 .14065823933284924
       .9896220174632009 .14369503315029444
       .989176509964781 .14673047445536175
       .9887216919603238 .1497645346773215
       .9882575677307495 .15279718525844344
       .9877841416445722 .15582839765426523
       .9873014181578584 .15885814333386145
       .9868094018141855 .16188639378011183
       .9863080972445987 .16491312048996992
       .9857975091675675 .16793829497473117
       .9852776423889412 .17096188876030122
       .9847485018019042 .17398387338746382
       .984210092386929 .17700422041214875
       .9836624192117303 .18002290140569951
       .9831054874312163 .18303988795514095
       .9825393022874412 .18605515166344666
       .9819638691095552 .18906866414980622
       .9813791933137546 .19208039704989244
       .9807852804032304 .19509032201612828
       .9801821359681174 .1980984107179536
       .9795697656854405 .2011046348420919
       .9789481753190622 .20410896609281687
       .9783173707196277 .20711137619221856
       .9776773578245099 .2101118368804696
       .9770281426577544 .21311031991609136
       .9763697313300211 .21610679707621952
       .9757021300385286 .2191012401568698
       .9750253450669941 .22209362097320354
       .9743393827855759 .22508391135979283
       .973644249650812 .22807208317088573
       .9729399522055602 .2310581082806711
       .9722264970789363 .23404195858354343
       .9715038909862518 .2370236059943672
       .9707721407289504 .2400030224487415
       .970031253194544 .2429801799032639
       .9692812353565485 .24595505033579462
       .9685220942744173 .24892760574572018
       .9677538370934755 .25189781815421697
       .9669764710448521 .25486565960451457
       .9661900034454125 .257831102162159
       .9653944416976894 .2607941179152755
       .9645897932898128 .2637546789748314
       .9637760657954398 .26671275747489837
       .9629532668736839 .2696683255729151
       .9621214042690416 .272621355449949
       .9612804858113206 .27557181931095814
       .9604305194155658 .2785196893850531
       .9595715130819845 .281464937925758
       .9587034748958716 .2844075372112718
       .9578264130275329 .2873474595447295
       .9569403357322088 .2902846772544624
       .9560452513499964 .29321916269425863
       .9551411683057707 .29615088824362384
       .9542280951091057 .2990798263080405
       .9533060403541939 .3020059493192281
       .9523750127197659 .30492922973540243
       .9514350209690083 .30784964004153487
       .9504860739494817 .3107671527496115
       .9495281805930367 .31368174039889146
       .9485613499157303 .31659337555616585
       .9475855910177411 .3195020308160157
       .9466009130832835 .32240767880106985
       .9456073253805213 .3253102921622629
       .9446048372614803 .32820984357909255
       .9435934581619604 .33110630575987643
       .9425731976014469 .3339996514420094
       .9415440651830208 .33688985339222005
       .9405060705932683 .33977688440682685
       .9394592236021899 .3426607173119944
       .9384035340631081 .34554132496398904
       .937339011912575 .34841868024943456
       .9362656671702783 .35129275608556715
       .9351835099389476 .3541635254204904
       .9340925504042589 .35703096123343003
       .9329927988347388 .35989503653498817
       .9318842655816681 .3627557243673972
       .9307669610789837 .36561299780477385
       .9296408958431812 .3684668299533723
       .9285060804732156 .37131719395183754
       .9273625256504011 .374164062971458
       .9262102421383114 .37700741021641826
       .9250492407826776 .37984720892405116
       .9238795325112867 .3826834323650898
       .9227011283338785 .38551605384391885
       .9215140393420419 .3883450466988263
       .9203182767091106 .39117038430225387
       .9191138516900578 .3939920400610481
       .9179007756213905 .3968099874167103
       .9166790599210427 .39962419984564684
       .9154487160882678 .40243465085941843
       .9142097557035307 .40524131400498986
       .9129621904283982 .4080441628649787
       .9117060320054299 .41084317105790397
       .9104412922580672 .41363831223843456
       .9091679830905224 .4164295600976372
       .9078861164876663 .41921688836322396
       .9065957045149153 .4220002707997997
       .9052967593181188 .4247796812091088
       .9039892931234433 .4275550934302821
       .9026733182372588 .4303264813400826
       .901348847046022 .43309381885315196
       .9000158920161603 .4358570799222555
       .8986744656939538 .43861623853852766
       .8973245807054183 .44137126873171667
       .8959662497561851 .44412214457042926
       .8945994856313827 .4468688401623742
       .8932243011955153 .4496113296546066
       .8918407093923427 .4523495872337709
       .8904487232447579 .45508358712634384
       .8890483558546646 .45781330359887723
       .8876396204028539 .46053871095824
       .8862225301488806 .4632597835518602
       .8847970984309378 .4659764957679662
       .8833633386657316 .46868882203582796
       .881921264348355 .47139673682599764
       .8804708890521608 .47410021465055
       .8790122264286335 .47679923006332214
       .8775452902072612 .479493757660153
       .8760700941954066 .4821837720791228
       .8745866522781761 .4848692480007911
       .8730949784182901 .48755016014843594
       .8715950866559511 .49022648328829116
       .8700869911087115 .49289819222978404
       .8685707059713409 .49556526182577254
       .8670462455156926 .49822766697278187
       .8655136240905691 .5008853826112408
       .8639728561215867 .5035383837257176
       .8624239561110405 .5061866453451553
       .8608669386377673 .508830142543107
       .8593018183570084 .5114688504379704
       .8577286100002721 .5141027441932218
       .8561473283751945 .5167317990176499
       .8545579883654005 .5193559901655896
       .8529606049303636 .5219752929371544
       .8513551931052652 .524589682678469
       .8497417680008524 .5271991347819014
       .8481203448032972 .5298036246862947
       .8464909387740521 .532403127877198
       .8448535652497071 .5349976198870973
       .8432082396418454 .5375870762956455
       .8415549774368984 .5401714727298929
       .8398937941959995 .5427507848645159
       .8382247055548381 .5453249884220465
       .836547727223512 .5478940591731002
       .83486287498638 .5504579729366048
       .8331701647019132 .5530167055800276
       .8314696123025452 .5555702330196022
       .829761233794523 .5581185312205561
       .8280450452577558 .560661576197336
       .8263210628456635 .5631993440138341
       .8245893027850253 .5657318107836132
       .8228497813758263 .5682589526701316
       .8211025149911046 .5707807458869673
       .819347520076797 .5732971666980422
       .8175848131515837 .5758081914178453
       .8158144108067338 .5783137964116556
       .8140363297059484 .5808139580957645
       .8122505865852039 .5833086529376983
       .8104571982525948 .5857978574564389
       .808656181588175 .5882815482226453
       .8068475535437992 .5907597018588743
       .8050313311429635 .5932322950397998
       .8032075314806449 .5956993044924334
       .8013761717231402 .5981607069963423
       .799537269107905 .600616479383869
       .7976908409433912 .6030665985403482
       .7958369046088836 .6055110414043255
       .7939754775543372 .6079497849677736
       .7921065773002124 .6103828062763095
       .79023022143731 .6128100824294097
       .7883464276266062 .6152315905806268
       .7864552135990858 .617647307937804
       .7845565971555752 .6200572117632892
       .7826505961665757 .62246127937415
       .7807372285720945 .6248594881423863
       .778816512381476 .6272518154951441
       .7768884656732324 .629638238914927
       .7749531065948739 .6320187359398091
       .773010453362737 .6343932841636455
       .7710605242618138 .6367618612362842
       .7691033376455796 .6391244448637757
       .7671389119358204 .6414810128085832
       .765167265622459 .6438315428897915
       .7631884172633813 .6461760129833164
       .7612023854842618 .6485144010221124
       .7592091889783881 .6508466849963809
       .7572088465064846 .6531728429537768
       .7552013768965365 .6554928529996153
       .7531867990436125 .6578066932970786
       .7511651319096864 .6601143420674205
       .7491363945234594 .6624157775901718
       .7471006059801801 .6647109782033449
       .745057785441466 .6669999223036375
       .7430079521351217 .6692825883466361
       .7409511253549591 .6715589548470184
       .7388873244606151 .673829000378756
       .7368165688773699 .6760927035753159
       .7347388780959635 .6783500431298615
       .7326542716724128 .680600997795453
       .7305627692278276 .6828455463852481
       .7284643904482252 .6850836677727004
       .726359155084346 .6873153408917592
       .7242470829514669 .6895405447370669
       .7221281939292153 .6917592583641577
       .7200025079613817 .693971460889654
       .7178700450557317 .696177131491463
       .7157308252838187 .6983762494089728
       .7135848687807936 .7005687939432483
       .7114321957452164 .7027547444572253
       .7092728264388657 .7049340803759049
       ))

    (define (make-w log-n)
      (let* ((n (##expt 2 log-n))  ;; number of complexes
             (result (##make-f64vector (##fx* 2 n))))

        (define (copy-low-lut)
          (##declare (not interrupts-enabled))
          (do ((i 0 (##fx+ i 1)))
              ((##fx= i lut-table-size))
            (let ((index (##fx* i 2)))
              (##f64vector-set!
               result
               index
               (##f64vector-ref low-lut index))
              (##f64vector-set!
               result
               (##fx+ index 1)
               (##f64vector-ref low-lut (##fx+ index 1))))))

        (define (extend-lut multiplier-lut bit-reverse-size bit-reverse-multiplier start end)

          (define (bit-reverse x n)
            (declare (not interrupts-enabled))
            (do ((i 0 (##fx+ i 1))
                 (x x (##fxarithmetic-shift-right x 1))
                 (result 0 (##fx+ (##fx* result 2)
                                       (##fxand x 1))))
                ((##fx= i n) result)))

          (let loop ((i start)
                     (j 1))
            (if (##fx< i end)
                (let* ((multiplier-index
                        (##fx* 2
                                    (bit-reverse j bit-reverse-size)
                                    bit-reverse-multiplier))
                       (multiplier-real
                        (##f64vector-ref multiplier-lut multiplier-index))
                       (multiplier-imag
                        (##f64vector-ref multiplier-lut (##fx+ multiplier-index 1))))
                  (let inner ((i i)
                              (k 0))
                    (declare (not interrupts-enabled))
                    ;; we copy complex multiples of all entries below
                    ;; start to entries starting at start
                    (if (##fx< k start)
                        (let* ((index
                                (##fx* k 2))
                               (real
                                (##f64vector-ref result index))
                               (imag
                                (##f64vector-ref result (##fx+ index 1)))
                               (result-real
                                (##fl- (##fl* multiplier-real real)
                                            (##fl* multiplier-imag imag)))
                               (result-imag
                                (##fl+ (##fl* multiplier-real imag)
                                            (##fl* multiplier-imag real)))
                               (result-index (##fx* i 2)))
                          (##f64vector-set! result result-index result-real)
                          (##f64vector-set! result (##fx+ result-index 1) result-imag)
                          (inner (##fx+ i 1)
                                 (##fx+ k 1)))
                        (loop i
                              (##fx+ j 1)))))
                result)))

        (cond ((##fx<= n lut-table-size)
               low-lut)
              ((##fx<= n lut-table-size^2)
               (copy-low-lut)
               (extend-lut med-lut
                           (##fx- log-n log-lut-table-size)
                           (##fxarithmetic-shift-left 1 (##fx- (##fx* 2 log-lut-table-size) log-n))
                           lut-table-size
                           n))
              (else ;; (##fx<= n lut-table-size^3)
               (copy-low-lut)
               (extend-lut med-lut
                           log-lut-table-size
                           1
                           lut-table-size
                           lut-table-size^2)
               (extend-lut high-lut
                           (##fx- log-n (##fx* 2 log-lut-table-size))
                           (##fxarithmetic-shift-left 1 (##fx- (##fx* 3 log-lut-table-size) log-n))
                           lut-table-size^2
                           n)))))

    (define (two^p>=m m)
      ;; returns smallest p, assumes fixnum m >= 0
      (##fxlength (##fx- m)))

    ;; The next two routines are so-called radix-4 ffts, which seems
    ;; to mean that they combine two passes, each of which works on
    ;; pairs of complex numbers (hence radix-2?), so if you combine
    ;; two passes in one, you work on two pairs of complex numbers at
    ;; a time and make half as many passes through the f64vector a.

    (define (direct-fft-recursive-4 a W-table)

      ;; This is a direcct complex fft, using a decimation-in-time
      ;; algorithm with inputs in natural order and outputs in
      ;; bit-reversed order.  The table of "twiddle" factors is in
      ;; bit-reversed order.

      ;; this is from page 66 of Chu and George, except that we have
      ;; combined passes in pairs to cut the number of passes through
      ;; the vector a

      (let ((W (##f64vector (macro-inexact-+0)
                            (macro-inexact-+0)
                            (macro-inexact-+0)
                            (macro-inexact-+0))))

        (define (main-loop M N K SizeOfGroup)

          (##declare (not interrupts-enabled))

          (let inner-loop ((K K)
                           (JFirst M))

            (if (##fx< JFirst N)

                (let* ((JLast  (##fx+ JFirst SizeOfGroup)))

                  (if (##fxeven? K)
                      (begin
                        (##f64vector-set! W 0 (##f64vector-ref W-table K))
                        (##f64vector-set! W 1 (##f64vector-ref W-table (##fx+ K 1))))
                      (begin
                        (##f64vector-set! W 0 (##fl- (##f64vector-ref W-table K)))
                        (##f64vector-set! W 1 (##f64vector-ref W-table (##fx- K 1)))))

                  ;; we know the that the next two complex roots of
                  ;; unity have index 2K and 2K+1 so that the 2K+1
                  ;; index root can be gotten from the 2K index root
                  ;; in the same way that we get W_0 and W_1 from the
                  ;; table depending on whether K is even or not

                  (##f64vector-set! W 2 (##f64vector-ref W-table (##fx* K 2)))
                  (##f64vector-set! W 3 (##f64vector-ref W-table (##fx+ (##fx* K 2) 1)))

                  (let J-loop ((J0 JFirst))
                    (if (##fx< J0 JLast)

                        (let* ((J0 J0)
                               (J1 (##fx+ J0 1))
                               (J2 (##fx+ J0 SizeOfGroup))
                               (J3 (##fx+ J2 1))
                               (J4 (##fx+ J2 SizeOfGroup))
                               (J5 (##fx+ J4 1))
                               (J6 (##fx+ J4 SizeOfGroup))
                               (J7 (##fx+ J6 1)))

                          (let ((W_0  (##f64vector-ref W 0))
                                (W_1  (##f64vector-ref W 1))
                                (W_2  (##f64vector-ref W 2))
                                (W_3  (##f64vector-ref W 3))
                                (a_J0 (##f64vector-ref a J0))
                                (a_J1 (##f64vector-ref a J1))
                                (a_J2 (##f64vector-ref a J2))
                                (a_J3 (##f64vector-ref a J3))
                                (a_J4 (##f64vector-ref a J4))
                                (a_J5 (##f64vector-ref a J5))
                                (a_J6 (##f64vector-ref a J6))
                                (a_J7 (##f64vector-ref a J7)))

                            ;; first we do the (overlapping) pairs of
                            ;; butterflies with entries 2*SizeOfGroup
                            ;; apart.

                            (let ((Temp_0 (##fl- (##fl* W_0 a_J4)
                                                      (##fl* W_1 a_J5)))
                                  (Temp_1 (##fl+ (##fl* W_0 a_J5)
                                                      (##fl* W_1 a_J4)))
                                  (Temp_2 (##fl- (##fl* W_0 a_J6)
                                                      (##fl* W_1 a_J7)))
                                  (Temp_3 (##fl+ (##fl* W_0 a_J7)
                                                      (##fl* W_1 a_J6))))

                              (let ((a_J0 (##fl+ a_J0 Temp_0))
                                    (a_J1 (##fl+ a_J1 Temp_1))
                                    (a_J2 (##fl+ a_J2 Temp_2))
                                    (a_J3 (##fl+ a_J3 Temp_3))
                                    (a_J4 (##fl- a_J0 Temp_0))
                                    (a_J5 (##fl- a_J1 Temp_1))
                                    (a_J6 (##fl- a_J2 Temp_2))
                                    (a_J7 (##fl- a_J3 Temp_3)))

                                ;; now we do the two (disjoint) pairs
                                ;; of butterflies distance SizeOfGroup
                                ;; apart, the first pair with W2+W3i,
                                ;; the second with -W3+W2i

                                ;; we rewrite the multipliers so I
                                ;; don't hurt my head too much when
                                ;; thinking about them.

                                (let ((W_0 W_2)
                                      (W_1 W_3)
                                      (W_2 (##fl- W_3))
                                      (W_3 W_2))

                                  (let ((Temp_0
                                         (##fl- (##fl* W_0 a_J2)
                                                     (##fl* W_1 a_J3)))
                                        (Temp_1
                                         (##fl+ (##fl* W_0 a_J3)
                                                     (##fl* W_1 a_J2)))
                                        (Temp_2
                                         (##fl- (##fl* W_2 a_J6)
                                                     (##fl* W_3 a_J7)))
                                        (Temp_3
                                         (##fl+ (##fl* W_2 a_J7)
                                                     (##fl* W_3 a_J6))))

                                    (let ((a_J0 (##fl+ a_J0 Temp_0))
                                          (a_J1 (##fl+ a_J1 Temp_1))
                                          (a_J2 (##fl- a_J0 Temp_0))
                                          (a_J3 (##fl- a_J1 Temp_1))
                                          (a_J4 (##fl+ a_J4 Temp_2))
                                          (a_J5 (##fl+ a_J5 Temp_3))
                                          (a_J6 (##fl- a_J4 Temp_2))
                                          (a_J7 (##fl- a_J5 Temp_3)))

                                      (##f64vector-set! a J0 a_J0)
                                      (##f64vector-set! a J1 a_J1)
                                      (##f64vector-set! a J2 a_J2)
                                      (##f64vector-set! a J3 a_J3)
                                      (##f64vector-set! a J4 a_J4)
                                      (##f64vector-set! a J5 a_J5)
                                      (##f64vector-set! a J6 a_J6)
                                      (##f64vector-set! a J7 a_J7)

                                      (J-loop (##fx+ J0 2)))))))))
                        (inner-loop (##fx+ K 1)
                                    (##fx+ JFirst (##fx* SizeOfGroup 4)))))))))

        (define (recursive-bit M N K SizeOfGroup)
          (if (##fx<= 2 SizeOfGroup)
              (begin
                (main-loop M N K SizeOfGroup)
                (if (##fx< 2048 (##fx- N M))
                    (let ((new-size (##fxarithmetic-shift-right (##fx- N M) 2)))
                      (recursive-bit M
                                     (##fx+ M new-size)
                                     (##fx* K 4)
                                     (##fxarithmetic-shift-right SizeOfGroup 2))
                      (recursive-bit (##fx+ M new-size)
                                     (##fx+ M (##fx* new-size 2))
                                     (##fx+ (##fx* K 4) 1)
                                     (##fxarithmetic-shift-right SizeOfGroup 2))
                      (recursive-bit (##fx+ M (##fx* new-size 2))
                                     (##fx+ M (##fx* new-size 3))
                                     (##fx+ (##fx* K 4) 2)
                                     (##fxarithmetic-shift-right SizeOfGroup 2))
                      (recursive-bit (##fx+ M (##fx* new-size 3))
                                     N
                                     (##fx+ (##fx* K 4) 3)
                                     (##fxarithmetic-shift-right SizeOfGroup 2)))
                    (recursive-bit M
                                   N
                                   (##fx* K 4)
                                   (##fxarithmetic-shift-right SizeOfGroup 2))))))

        (define (radix-2-pass a)

          ;; If we're here, the size of our (conceptually complex)
          ;; array is not a power of 4, so we need to do a basic radix
          ;; two pass with w=1 (so W[0]=1.0 and W[1] = 0.)  and then
          ;; call recursive-bit appropriately on the two half arrays.

          (declare (not interrupts-enabled))

          (let ((SizeOfGroup
                 (##fxarithmetic-shift-right (##f64vector-length a) 1)))
            (let loop ((J0 0))
              (if (##fx< J0 SizeOfGroup)
                  (let ((J0 J0)
                        (J2 (##fx+ J0 SizeOfGroup)))
                    (let ((J1 (##fx+ J0 1))
                          (J3 (##fx+ J2 1)))
                      (let ((a_J0 (##f64vector-ref a J0))
                            (a_J1 (##f64vector-ref a J1))
                            (a_J2 (##f64vector-ref a J2))
                            (a_J3 (##f64vector-ref a J3)))
                        (let ((a_J0 (##fl+ a_J0 a_J2))
                              (a_J1 (##fl+ a_J1 a_J3))
                              (a_J2 (##fl- a_J0 a_J2))
                              (a_J3 (##fl- a_J1 a_J3)))
                          (##f64vector-set! a J0 a_J0)
                          (##f64vector-set! a J1 a_J1)
                          (##f64vector-set! a J2 a_J2)
                          (##f64vector-set! a J3 a_J3)
                          (loop (##fx+ J0 2))))))))))

        (let* ((n (##f64vector-length a))
               (log_n (two^p>=m n)))

          ;; there are n/2 complex entries in a; if n/2 is not a power
          ;; of 4, then do a single radix-2 pass and do the rest of
          ;; the passes as radix-4 passes

          (if (##fxodd? log_n)
              (recursive-bit 0 n 0 (##fxarithmetic-shift-right n 2))
              (let ((n/2 (##fxarithmetic-shift-right n 1))
                    (n/8 (##fxarithmetic-shift-right n 3)))
                (radix-2-pass a)
                (recursive-bit 0 n/2 0 n/8)
                (recursive-bit n/2 n 1 n/8))))))

    ;; The following routine simply reverses the operations of the
    ;; previous routine.

    (define (inverse-fft-recursive-4 a W-table)

      ;; This is an complex fft, using a decimation-in-frequency algorithm
      ;; with inputs in bit-reversed order and outputs in natural order.

      ;; The organization of the algorithm has little to do with the the
      ;; associated algorithm on page 41 of Chu and George,
      ;; I just reversed the operations of the direct algorithm given
      ;; above (without dividing by 2 each time, so that this has to
      ;; be "normalized" by dividing by N/2 at the end.

      ;; The table of "twiddle" factors is in bit-reversed order.

      (let ((W (##f64vector (macro-inexact-+0)
                            (macro-inexact-+0)
                            (macro-inexact-+0)
                            (macro-inexact-+0))))

        (define (main-loop M N K SizeOfGroup)
          (##declare (not interrupts-enabled))
          (let inner-loop ((K K)
                           (JFirst M))
            (if (##fx< JFirst N)
                (let* ((JLast  (##fx+ JFirst SizeOfGroup)))
                  (if (##fxeven? K)
                      (begin
                        (##f64vector-set! W 0 (##f64vector-ref W-table K))
                        (##f64vector-set! W 1 (##f64vector-ref W-table (##fx+ K 1))))
                      (begin
                        (##f64vector-set! W 0 (##fl- (##f64vector-ref W-table K)))
                        (##f64vector-set! W 1 (##f64vector-ref W-table (##fx- K 1)))))
                  (##f64vector-set! W 2 (##f64vector-ref W-table (##fx* K 2)))
                  (##f64vector-set! W 3 (##f64vector-ref W-table (##fx+ (##fx* K 2) 1)))
                  (let J-loop ((J0 JFirst))
                    (if (##fx< J0 JLast)
                        (let* ((J0 J0)
                               (J1 (##fx+ J0 1))
                               (J2 (##fx+ J0 SizeOfGroup))
                               (J3 (##fx+ J2 1))
                               (J4 (##fx+ J2 SizeOfGroup))
                               (J5 (##fx+ J4 1))
                               (J6 (##fx+ J4 SizeOfGroup))
                               (J7 (##fx+ J6 1)))
                          (let ((W_0  (##f64vector-ref W 0))
                                (W_1  (##f64vector-ref W 1))
                                (W_2  (##f64vector-ref W 2))
                                (W_3  (##f64vector-ref W 3))
                                (a_J0 (##f64vector-ref a J0))
                                (a_J1 (##f64vector-ref a J1))
                                (a_J2 (##f64vector-ref a J2))
                                (a_J3 (##f64vector-ref a J3))
                                (a_J4 (##f64vector-ref a J4))
                                (a_J5 (##f64vector-ref a J5))
                                (a_J6 (##f64vector-ref a J6))
                                (a_J7 (##f64vector-ref a J7)))
                            (let ((W_00 W_2)
                                  (W_01 W_3)
                                  (W_02 (##fl- W_3))
                                  (W_03 W_2))
                              (let ((Temp_0 (##fl- a_J0 a_J2))
                                    (Temp_1 (##fl- a_J1 a_J3))
                                    (Temp_2 (##fl- a_J4 a_J6))
                                    (Temp_3 (##fl- a_J5 a_J7)))
                                (let ((a_J0 (##fl+ a_J0 a_J2))
                                      (a_J1 (##fl+ a_J1 a_J3))
                                      (a_J4 (##fl+ a_J4 a_J6))
                                      (a_J5 (##fl+ a_J5 a_J7))
                                      (a_J2 (##fl+ (##fl* W_00 Temp_0)
                                                        (##fl* W_01 Temp_1)))
                                      (a_J3 (##fl- (##fl* W_00 Temp_1)
                                                        (##fl* W_01 Temp_0)))
                                      (a_J6 (##fl+ (##fl* W_02 Temp_2)
                                                        (##fl* W_03 Temp_3)))
                                      (a_J7 (##fl- (##fl* W_02 Temp_3)
                                                        (##fl* W_03 Temp_2))))
                                  (let ((Temp_0 (##fl- a_J0 a_J4))
                                        (Temp_1 (##fl- a_J1 a_J5))
                                        (Temp_2 (##fl- a_J2 a_J6))
                                        (Temp_3 (##fl- a_J3 a_J7)))
                                    (let ((a_J0 (##fl+ a_J0 a_J4))
                                          (a_J1 (##fl+ a_J1 a_J5))
                                          (a_J2 (##fl+ a_J2 a_J6))
                                          (a_J3 (##fl+ a_J3 a_J7))
                                          (a_J4 (##fl+ (##fl* W_0 Temp_0)
                                                            (##fl* W_1 Temp_1)))
                                          (a_J5 (##fl- (##fl* W_0 Temp_1)
                                                            (##fl* W_1 Temp_0)))
                                          (a_J6 (##fl+ (##fl* W_0 Temp_2)
                                                            (##fl* W_1 Temp_3)))
                                          (a_J7 (##fl- (##fl* W_0 Temp_3)
                                                            (##fl* W_1 Temp_2))))
                                      (##f64vector-set! a J0 a_J0)
                                      (##f64vector-set! a J1 a_J1)
                                      (##f64vector-set! a J2 a_J2)
                                      (##f64vector-set! a J3 a_J3)
                                      (##f64vector-set! a J4 a_J4)
                                      (##f64vector-set! a J5 a_J5)
                                      (##f64vector-set! a J6 a_J6)
                                      (##f64vector-set! a J7 a_J7)
                                      (J-loop (##fx+ J0 2)))))))))
                        (inner-loop (##fx+ K 1)
                                    (##fx+ JFirst (##fx* SizeOfGroup 4)))))))))

        (define (recursive-bit M N K SizeOfGroup)
          (if (##fx<= 2 SizeOfGroup)
              (begin
                (if (##fx< 2048 (##fx- N M))
                    (let ((new-size (##fxarithmetic-shift-right (##fx- N M) 2)))
                      (recursive-bit M
                                     (##fx+ M new-size)
                                     (##fx* K 4)
                                     (##fxarithmetic-shift-right SizeOfGroup 2))
                      (recursive-bit (##fx+ M new-size)
                                     (##fx+ M (##fx* new-size 2))
                                     (##fx+ (##fx* K 4) 1)
                                     (##fxarithmetic-shift-right SizeOfGroup 2))
                      (recursive-bit (##fx+ M (##fx* new-size 2))
                                     (##fx+ M (##fx* new-size 3))
                                     (##fx+ (##fx* K 4) 2)
                                     (##fxarithmetic-shift-right SizeOfGroup 2))
                      (recursive-bit (##fx+ M (##fx* new-size 3))
                                     N
                                     (##fx+ (##fx* K 4) 3)
                                     (##fxarithmetic-shift-right SizeOfGroup 2)))
                    (recursive-bit M
                                   N
                                   (##fx* K 4)
                                   (##fxarithmetic-shift-right SizeOfGroup 2)))
                (main-loop M N K SizeOfGroup))))

        (define (radix-2-pass a)
          (declare (not interrupts-enabled))
          (let ((SizeOfGroup
                 (##fxarithmetic-shift-right (##f64vector-length a) 1)))
            (let loop ((J0 0))
              (if (##fx< J0 SizeOfGroup)
                  (let ((J0 J0)
                        (J2 (##fx+ J0 SizeOfGroup)))
                    (let ((J1 (##fx+ J0 1))
                          (J3 (##fx+ J2 1)))
                      (let ((a_J0 (##f64vector-ref a J0))
                            (a_J1 (##f64vector-ref a J1))
                            (a_J2 (##f64vector-ref a J2))
                            (a_J3 (##f64vector-ref a J3)))
                        (let ((a_J0 (##fl+ a_J0 a_J2))
                              (a_J1 (##fl+ a_J1 a_J3))
                              (a_J2 (##fl- a_J0 a_J2))
                              (a_J3 (##fl- a_J1 a_J3)))
                          (##f64vector-set! a J0 a_J0)
                          (##f64vector-set! a J1 a_J1)
                          (##f64vector-set! a J2 a_J2)
                          (##f64vector-set! a J3 a_J3)
                          (loop (##fx+ J0 2))))))))))

        (let* ((n (##f64vector-length a))
               (log_n (two^p>=m n)))
          (if (##fxodd? log_n)
              (recursive-bit 0 n 0 (##fxarithmetic-shift-right n 2))
              (let ((n/2 (##fxarithmetic-shift-right n 1))
                    (n/8 (##fxarithmetic-shift-right n 3)))
                (recursive-bit 0 n/2 0 n/8)
                (recursive-bit n/2 n 1 n/8)
                (radix-2-pass a))))))

    #|

    See the wonderful paper
    Rapid multiplication modulo the sum and difference of highly
    composite numbers, by Colin Percival, electronically published
    by Mathematics of Computation, number S 0025-5718(02)01419-9, URL
    http://www.ams.org/journal-getitem?pii=S0025-5718-02-01419-9
    that gives these very nice error bounds.  This should be published
    in the paper journal sometime after March 2002.

    What we're going to do is:

    Take x and y, each with <= 2^n 8-bit fdigits.
    Put the fdigits of x and y into the real parts of the
    first 2^n complex entries of a vector of length 2^{n+1}.
    Do ffts of length 2^{n+1}.
    Multiply the complex fft coefficients of x and y.
    do an inverse fft of length 2^{n+1}.
    Extract the digits of x*y from the real parts of the inverse fft.

    From theorem 5.1 we get the following error bound:

    (define epsilon (expt 2. -53))
    (define bigepsilon (* epsilon (sqrt 5)))
    (define n 26)
    (define beta 4.158491068379826e-16)      ;; accuracy of trigonometric inputs (check) error in product of three entries from the tables
    (define norm-x (sqrt (* (expt 2 n) (* 255 255))))
    (define norm-y norm-x)
    (define error (* norm-x
                     norm-y
                     ;; the following three lines use the slight overestimate that
                     ;; ln(1+epsilon) = epsilon, etc.
                     ;; there are more accurate ways to calculate this, but we
                     ;; don't really need them.
                     (- (exp (+ (* 3 (+ n 1) epsilon)
                                (* (+ (* 3 (+ n 1)) 1) bigepsilon)
                                (* 3 (+ n 1) beta)))
                        1)))
    (pp error)

    Error bound is .27518123388290405  < 1/2

    So if x and y have fewer than 2^{26}\times 8=536,870,912 bits, this computes the product exactly.

    It appears that we need tables only of size 2^9 complex entries rather than 2^10 if we do this.  That would
    cut down on memory.

    Let's look at what happens when you have 4-bit fft words:

    (define epsilon (expt 2. -53))
    (define bigepsilon (* epsilon (sqrt 5)))
    (define n 34)
    (define beta 4.158491068379826e-16)      ;; accuracy of trigonometric inputs
    (define l 4)
    (define norm-x (sqrt (* (expt 2 n) (* 15 15))))
    (define norm-y norm-x)
    (define error (* norm-x
                     norm-y
                     (- (exp (+ (* 3 (+ n 1) epsilon)
                                (* (+ (* 3 (+ n 1)) 1) bigepsilon)
                                (* 3 (+ n 1) beta)))
                        1)))
    (pp error)

    Error bound is .31585693359375 < 1/2

    So if x and y have fewer than 2^{34}\times 4=68,719,476,736 bits, this
    computes the product exactly.

    But then I would have to increase the size of the tables to 2^{11}
    complex entries each, so we'd have tables of 4 times the size.

    I think I won't add a four-bit fft word option for now.

    Because the fft algorithm as written requires temporary storage at least
    sixteen times the size of the final result, people working with large
    integers but barely enough memory on 64-bit machines may wish to
    set! ##bignum.fft-mul-max-width to a slightly smaller value so that
    karatsuba will kick in earlier for the largest integers.

    COMMENTS FOR THE RAC (Right-Angle Convolution) VERSION

    What we're going to do is:

    Take x and y, which together have a total of  <= 2^{n+1} 8-bit fdigits.
    We take the fdigits of f and put them into the real parts of a complex
    vector of length 2^n; if there are any left over, place the rest in the
    imaginary parts of the complex vector, starting over at the 0 entry.
    We do the same for y.
    We componentwise multiply x_j by e^{\pi/2 i j/2^n}; similarly for y_j.
    (This is the "right-angle" part of the right-angle transform.)
    The maximum possible product of |x|_2 and |y|_2 are when they both
    have 2^n eight-bit digits.
    Do ffts of length 2^n.
    Multiply the complex fft coefficients of x and y.
    do an inverse fft of length 2^n.
    We componentwise multiply the result by e^{-\pi/2 i j/2^n}, i.e.,
    the inverse of the entries of the weights applied to x and y

    Extract the digits of x*y from the real parts and then the imaginary
    parts of the weighted inverse fft.

    From Theorem 6.1 and the following displayed equation we have

    (define epsilon (expt 2. -53))
    (define bigepsilon (* epsilon (sqrt 5)))
    (define n 26)
    (define beta 4.164343159519809e-16)      ;; accuracy of trigonometric inputs (check) error in product of three entries from the tables
    (define norm-x (sqrt (* (expt 2 n) (* 255 255))))
    (define norm-y norm-x)
    (define error (* norm-x
                     norm-y
                     ;; the following three lines use the slight overestimate that
                     ;; ln(1+epsilon) = epsilon, etc.
                     ;; there are more accurate ways to calculate this, but we
                     ;; don't really need them.
                     (- (exp (+ (* 3 n epsilon)
                                (* (+ (* 3 n) 4) bigepsilon)
                                (* (+ (* 3 n) 3) beta)))
                        1)))
    (pp error)

    The error bound is .2742122858762741, so we're cool.

    |#

    #|

    Let n = 2^{\log n}; the following routine calculates

    e^{\pi/2 i (j/n)} j=0,\ldots, n/2-1

    It uses the tables med-lut and high-lut (both described above) and
    low-lut-rac, which contains in fftluts-9.scm

    e^{\pi/2 i (j/2^9)}, j=0,\ldots, 2^8-1

    It uses the same general strategy as make-w, except, because the
    final result is in normal order rather than bit-reversed order, we
    start with the highest table and work our way to the lowest.  As
    noted above, this should result in slightly smaller error than from make-w.

    Instead of always building a new table, one could reuse a bigger one with
    a stride (do the math).  I don't want to do this, however; I'd rather build
    a new, compact table and hope that this will result in fewer cache/TLB/page
    misses.

    |#

    (define (make-w-rac log-n)
      (let* ((n (##expt 2 log-n))
             (result (##make-f64vector n)))   ;; contains n/2 complexes

        (define (copy-lut lut stride)

          ;; copies the (conceptually complex) entries
          ;; lut[0], lut[(stride/2)], lut[2*(stride/2)], ...
          ;; to the first entries of result.  We stop when we hit
          ;; the end of lut.

          (##declare (not interrupts-enabled))
          (let ((lut-size (##f64vector-length lut)))
            (do ((i 0 (##fx+ i 2))
                 (j 0 (##fx+ j stride)))
                ((##fx= j lut-size) result)
              (##f64vector-set! result             i    (##f64vector-ref lut             j   ))
              (##f64vector-set! result (##fx+ i 1) (##f64vector-ref lut (##fx+ j 1))))))

        (define (extend-lut multiplier-lut start)

          ;; we multiply the table from 0 to start-1 (in pairs of reals
          ;; as complexes) by all the multipliers in multiplier-lut
          ;; starting at 2 (again in pairs of reals)

          (let ((end (##f64vector-length multiplier-lut)))
            (let loop ((i start)
                       (j 2))
              (if (##fx< j end)
                  (let* ((multiplier-real (##f64vector-ref multiplier-lut j))
                         (multiplier-imag (##f64vector-ref multiplier-lut (##fx+ j 1))))
                    (let inner ((i i)
                                (k 0))
                      (declare (not interrupts-enabled))
                      (if (##fx< k start)
                          (let* ((real  (##f64vector-ref result k))
                                 (imag  (##f64vector-ref result (##fx+ k 1)))
                                 (result-real (##fl- (##fl* multiplier-real real)
                                                          (##fl* multiplier-imag imag)))
                                 (result-imag (##fl+ (##fl* multiplier-real imag)
                                                          (##fl* multiplier-imag real))))
                            (##f64vector-set! result i result-real)
                            (##f64vector-set! result (##fx+ i 1) result-imag)
                            (inner (##fx+ i 2)
                                   (##fx+ k 2)))
                          (loop i
                                (##fx+ j 2)))))
                  result))))

        (cond ((##fx= n lut-table-size)
               low-lut-rac)

              ((##fx< n lut-table-size)
               (let ((stride (##fxquotient (##fx* lut-table-size 2) n))) ;; = 2 when n = lut-table-size, etc.
                 (copy-lut low-lut-rac stride)))

              ((##fx<= n lut-table-size^2)
               (let* ((stride (##fxquotient (##fx* lut-table-size^2 2) n))
                      (start  (##fxquotient (##fx* lut-table-size 4) stride))) ;; = 2 lut-table-size when n=lut-table-size^2
                 (copy-lut med-lut stride)
                 (extend-lut low-lut-rac (##fxarithmetic-shift-right n (##fx- log-lut-table-size 1)))))

              (else ;; (##fx<= n lut-table-size^3)
               (let* ((stride (##fxquotient (##fx* lut-table-size^3 2) n))
                      (start  (##fxquotient (##fx* lut-table-size 4) stride)))
                 (copy-lut high-lut stride)
                 (extend-lut med-lut start)
                 (extend-lut low-lut-rac (##fx* start lut-table-size)))))))

    (define (bignum->f64vector-rac x a)

      ;; Copies the first (##f64vector-length a)/2 fdigits of x into the
      ;; even components of a, which represent the real parts of complex
      ;; elements, and then the rest of the fdigits of x into the odd
      ;; components of a, starting over at 1.

      (let ((two^n (##f64vector-length a))
            (x-length (##bignum.fdigit-length x)))

        (if (##fx<= (##fx* x-length 2)
                         two^n)
            ;; all imaginary parts are 0.
            (let loop1 ((i 0)
                        (j 0))
              (##declare (not interrupts-enabled))
              (if (##fx< i x-length)
                  (let ((digit-real   (##fixnum->flonum (##bignum.fdigit-ref x i))))
                    (##f64vector-set! a             j    digit-real)
                    (##f64vector-set! a (##fx+ j 1) (macro-inexact-+0))
                    (loop1 (##fx+ i 1)
                           (##fx+ j 2)))
                                        ;; all parts are zero
                  (let loop2 ((j j))
                    (if (##fx< j two^n)
                        (begin
                          (##f64vector-set! a j (macro-inexact-+0))
                          (##f64vector-set! a (##fx+ j 1) (macro-inexact-+0))
                          (loop2 (##fx+ j 2)))))))

            (let ((offset (##fxarithmetic-shift-right two^n 1)))
              (let loop1 ((i 0)
                          (j 0))
                (##declare (not interrupts-enabled))
                (if (##fx< (##fx+ i offset) x-length)
                    (let ((digit-real (##fixnum->flonum (##bignum.fdigit-ref x             i        )))
                          (digit-imag (##fixnum->flonum (##bignum.fdigit-ref x (##fx+ i offset)))))
                      (##f64vector-set! a             j    digit-real)
                      (##f64vector-set! a (##fx+ j 1) digit-imag)
                      (loop1 (##fx+ i 1)
                             (##fx+ j 2)))
                    ;; all imaginary parts are 0.
                    (let loop2 ((i i)
                                (j j))
                      (if (##fx< j two^n)
                          (let ((digit-real   (##fixnum->flonum (##bignum.fdigit-ref x i))))
                            (##f64vector-set! a             j    digit-real)
                            (##f64vector-set! a (##fx+ j 1) (macro-inexact-+0))
                            (loop2 (##fx+ i 1)
                                   (##fx+ j 2)))))))))))

    (define (componentwise-rac-multiply a table)

      ;; the (conceptually complex) entries of table are
      ;; e^{\pi/2 i (j/2^n)}, j=0,...,2^{n-1}-1.
      ;; We multiply a_i componentwise by table_i, using symmetry when i\geq 2^{n-1}

      (let ((table-size (##f64vector-length table))
            (a-size (##f64vector-length a)))
        (declare (not interrupts-enabled))   ;; note that this means we have to be careful not to cons.
        (let loop ((i 2)
                   (j 2))
          (if (##fx< i table-size)
              (let ((multiplier-real (##f64vector-ref table i))
                    (multiplier-imag (##f64vector-ref table (##fx+ i 1))))
                (let ((a_j-real   (##f64vector-ref a             j   ))
                      (a_j-imag   (##f64vector-ref a (##fx+ j 1)))
                      (a_N-j-real (##f64vector-ref a (##fx- a-size j   )))
                      (a_N-j-imag (##f64vector-ref a (##fx- a-size j -1))))
                  (let ((result_j-real (##fl- (##fl* a_j-real multiplier-real)
                                                   (##fl* a_j-imag multiplier-imag)))
                        (result_j-imag (##fl+ (##fl* a_j-imag multiplier-real)
                                                   (##fl* a_j-real multiplier-imag)))
                        ;; if multipler_j=(make-rectangular r i) then multiplier_{N-j}=(make-rectangular i r)
                        (result_N-j-real (##fl- (##fl* a_N-j-real multiplier-imag)
                                                     (##fl* a_N-j-imag multiplier-real)))
                        (result_N-j-imag (##fl+ (##fl* a_N-j-imag multiplier-imag)
                                                     (##fl* a_N-j-real multiplier-real))))
                    (##f64vector-set! a             j    result_j-real)
                    (##f64vector-set! a (##fx+ j 1) result_j-imag)
                    (##f64vector-set! a (##fx- a-size j   ) result_N-j-real)
                    (##f64vector-set! a (##fx- a-size j -1) result_N-j-imag)
                    (loop (##fx+ i 2)
                          (##fx+ j 2)))))
              (let ((multiplier-real .7071067811865476)  ;; here the multiplier is always (sqrt i)
                    (multiplier-imag .7071067811865476)
                    (a_j-real (##f64vector-ref a j))
                    (a_j-imag (##f64vector-ref a (##fx+ j 1))))
                (let ((result_j-real (##fl- (##fl* a_j-real multiplier-real)
                                                 (##fl* a_j-imag multiplier-imag)))
                      (result_j-imag (##fl+ (##fl* a_j-imag multiplier-real)
                                                 (##fl* a_j-real multiplier-imag))))
                  (##f64vector-set! a             j    result_j-real)
                  (##f64vector-set! a (##fx+ j 1) result_j-imag)))))))

    (define (componentwise-rac-multiply-conjugate a table)
      ;; the (conceptually complex) entries of table are
      ;; e^{\pi/2 i (j/2^n)}, j=0,...,2^{n-1}-1.
      ;; We multiply a_i componentwise by the conjugate/inverse of table_i, using symmetry when i\geq 2^{n-1}

      (let ((table-size (##f64vector-length table))
            (a-size (##f64vector-length a)))
        (declare (not interrupts-enabled))   ;; note that this means we have to be careful not to cons.
        (let loop ((i 2)
                   (j 2))
          (if (##fx< i table-size)
              (let ((multiplier-real (##f64vector-ref table i))
                    (multiplier-imag (##f64vector-ref table (##fx+ i 1))))
                (let ((a_j-real   (##f64vector-ref a             j   ))
                      (a_j-imag   (##f64vector-ref a (##fx+ j 1)))
                      (a_N-j-real (##f64vector-ref a (##fx- a-size j   )))
                      (a_N-j-imag (##f64vector-ref a (##fx- a-size j -1))))

                  (let ((result_j-real (##fl+ (##fl* a_j-real multiplier-real)
                                                   (##fl* a_j-imag multiplier-imag)))
                        (result_j-imag (##fl- (##fl* a_j-imag multiplier-real)
                                                   (##fl* a_j-real multiplier-imag)))
                        ;; if multipler_j=(make-rectangular r i) then multiplier_{N-j}=(make-rectangular i r)
                        (result_N-j-real (##fl+ (##fl* a_N-j-real multiplier-imag)
                                                     (##fl* a_N-j-imag multiplier-real)))
                        (result_N-j-imag (##fl- (##fl* a_N-j-imag multiplier-imag)
                                                     (##fl* a_N-j-real multiplier-real))))
                    (##f64vector-set! a             j    result_j-real)
                    (##f64vector-set! a (##fx+ j 1) result_j-imag)
                    (##f64vector-set! a (##fx- a-size j   ) result_N-j-real)
                    (##f64vector-set! a (##fx- a-size j -1) result_N-j-imag)
                    (loop (##fx+ i 2)
                          (##fx+ j 2)))))
              (let ((multiplier-real .7071067811865476)  ;; here the multiplier is always (sqrt i)
                    (multiplier-imag .7071067811865476)
                    (a_j-real (##f64vector-ref a j))
                    (a_j-imag (##f64vector-ref a (##fx+ j 1))))
                (let ((result_j-real (##fl+ (##fl* a_j-real multiplier-real)
                                                 (##fl* a_j-imag multiplier-imag)))
                      (result_j-imag (##fl- (##fl* a_j-imag multiplier-real)
                                                 (##fl* a_j-real multiplier-imag))))
                  (##f64vector-set! a             j    result_j-real)
                  (##f64vector-set! a (##fx+ j 1) result_j-imag)))))))

    (define (componentwise-complex-multiply a b)
      (let ((two^n (##f64vector-length a)))
        (let loop ((j 0))
          (##declare (not interrupts-enabled))
          (if (##fx< j two^n)
              (let ((aj (##f64vector-ref a j))
                    (aj+1 (##f64vector-ref a (##fx+ j 1)))
                    (bj (##f64vector-ref b j))
                    (bj+1 (##f64vector-ref b (##fx+ j 1))))
                (##f64vector-set! a j
                                  (##fl- (##fl* bj aj)   (##fl* aj+1 bj+1)))
                (##f64vector-set! a (##fx+ j 1)
                                  (##fl+ (##fl* bj aj+1) (##fl* aj   bj+1)))
                (loop (##fx+ j 2)))))))

    (define (f64vector-rac->bignum a result result-length)

      ;; result-length is > the number of complex entries in a, because
      ;; otherwise the length of a would be cut in half.

      (let ((normalizer (##fl/ (##fixnum->flonum (##fxarithmetic-shift-right (##f64vector-length a) 1)))))
        (if (and (##fx> ##fixnum-width 32)
                 (##fx= ##bignum.fdigit-base 256))
            ;; Here we have faster code for the case of
            ;; (1) 64-bit fixnums and
            ;; (2) 8-bit fdigits
            ;; So each rounded entry of a can fit into a fixnum, and
            ;; we know how many bits to mask out and shift for fdigits.
            (let loop ((i 0)
                       (j 0)
                       (carry 0)
                       (limit (##fxarithmetic-shift-right (##f64vector-length a) 1)))
              (##declare (not interrupts-enabled))
              (if (##fx< i limit)
                  (let* ((t (##fx+ carry
                                   (##flonum->fixnum (##fl+ (macro-inexact-+1/2)
                                                            (##fl* (##f64vector-ref a j)
                                                                   normalizer))))))
                    (##bignum.fdigit-set! result i (##fxand t 255))
                    (loop (##fx+ i 1)
                          (##fx+ j 2)
                          (##fxarithmetic-shift-right t 8)
                          limit))
                  (if (##fxeven? j)
                      (loop i
                            1
                            carry
                            result-length))))
            (let* ((fbase (##fixnum->flonum ##bignum.fdigit-base))
                   (fbase-inverse (##fl/ fbase)))
              (let ((loop-carry (##f64vector (macro-inexact-+0))))
                (let loop ((i 0)
                           (j 0)
                           (limit (##fxarithmetic-shift-right (##f64vector-length a) 1)))
                  (##declare (not interrupts-enabled))
                  (if (##fx< i limit)
                      (let* ((t
                              (##fl+ (##fl+ (##f64vector-ref loop-carry 0)
                                            (macro-inexact-+1/2))
                                     (##fl* (##f64vector-ref a j)
                                            normalizer)))
                             (carry
                              (##flfloor (##fl* t fbase-inverse)))
                             (digit
                              (##fl- t (##fl* carry fbase))))
                        (##bignum.fdigit-set! result i (##flonum->fixnum digit))
                        (##f64vector-set! loop-carry 0 carry)
                        (loop (##fx+ i 1)
                              (##fx+ j 2)
                              limit))
                      (if (##fxeven? j)
                          (loop i
                                1
                                result-length)))))))))

    ;; this is the right-angle convolution method of section 6 in Percival's paper

    (let* ((x-length (##bignum.fdigit-length x))
           (y-length (##bignum.fdigit-length y))
           (result-length (##fx+ x-length y-length))
           (result (##bignum.make
                    (##fxquotient
                     result-length
                     (##fxquotient ##bignum.adigit-width
                                   ##bignum.fdigit-width))
                    #f
                    #f))
           ;; minimum power of 2 >= x-length + y-length, half # of complex elements in fft vectors
           (log-two^n (##fx- (two^p>=m (##fx+ x-length y-length)) 1))
           (two^n (##fxarithmetic-shift-left 1 log-two^n)))

      (let ((a         (##make-f64vector (##fx* two^n 2)))
            (table     (make-w     (##fx- log-two^n 1)))
            (rac-table (make-w-rac log-two^n)))

        (bignum->f64vector-rac x a)
        (componentwise-rac-multiply a rac-table)
        (direct-fft-recursive-4 a table)
        (if (##eq? x y)
            (componentwise-complex-multiply a a)
            (let ((b (##make-f64vector (##fx* two^n 2))))
              (bignum->f64vector-rac y b)
              (componentwise-rac-multiply b rac-table)
              (direct-fft-recursive-4 b table)
              (componentwise-complex-multiply a b)))
        (inverse-fft-recursive-4 a table)
        (componentwise-rac-multiply-conjugate a rac-table)
        (f64vector-rac->bignum a result result-length)
        (cleanup x y result))))


  (define (naive-mul x x-length y y-length)  ;; multiplies x by each digit of y
    (let ((result
           (##bignum.make
            (##fx+ (##bignum.adigit-length x) (##bignum.adigit-length y))
            #f
            #f)))
      (##declare (not interrupts-enabled))
      (let loop1 ((k 0))
        (if (##fx< k y-length)
            (let ((multiplier (##bignum.mdigit-ref y k)))
              (if (##fx= multiplier 0)
                (loop1 (##fx+ k 1))
                (let loop2 ((i 0)
                            (j k)
                            (carry 0))
                  (if (##fx< i x-length)
                    (loop2 (##fx+ i 1)
                           (##fx+ j 1)
                           (##bignum.mdigit-mul! result
                                                 j
                                                 x
                                                 i
                                                 multiplier
                                                 carry))
                    (begin
                      (##bignum.mdigit-set! result j carry)
                      (loop1 (##fx+ k 1)))))))
            (cleanup x y result)))))

  (define (cleanup x y result)

    ;; Both naive-mul and fft-mul do unsigned multiplies, fix that here.

    (define (fix x y result)

      (##declare (not interrupts-enabled))

      (if (##bignum.negative? y)
        (let ((x-length (##bignum.adigit-length x)))
          (let loop ((i 0)
                     (j (##bignum.adigit-length y))
                     (borrow 0))
            (if (##fx< i x-length)
              (loop (##fx+ i 1)
                    (##fx+ j 1)
                    (##bignum.adigit-sub! result j x i borrow)))))))

    (fix x y result)
    (fix y x result)
    (##bignum.normalize! result))

  (define (karatsuba-mul x y)
    (let* ((x-length
            (##bignum.adigit-length x))
           (y-length
            (##bignum.adigit-length y))
           (shift-digits
            (##fxarithmetic-shift-right y-length 1))
           (shift-bits
            (##fx* shift-digits ##bignum.adigit-width))
           (y-high
            (##bignum.arithmetic-shift y (##fx- shift-bits)))
           (y-low
            (##extract-bit-field shift-bits 0 y)))
      (if (##eq? x y)
          (let ((high-term
                 (##* y-high y-high))
                (low-term
                 (##* y-low y-low))
                (mid-term
                 (let ((arg (##- y-high y-low)))
                   (##* arg arg))))
            (##+ (##arithmetic-shift high-term (##fx* shift-bits 2))
                 (##+ (##arithmetic-shift
                       (##+ high-term
                            (##- low-term mid-term))
                       shift-bits)
                      low-term)))
          (let ((x-high
                 (##bignum.arithmetic-shift x (##fx- shift-bits)))
                (x-low
                 (##extract-bit-field shift-bits 0 x)))
            (let ((high-term
                   (##* x-high y-high))
                  (low-term
                   (##* x-low y-low))
                  (mid-term
                   (##* (##- x-high x-low)
                        (##- y-high y-low))))
              (##+ (##arithmetic-shift high-term (##fx* shift-bits 2))
                   (##+ (##arithmetic-shift
                         (##+ high-term
                              (##- low-term mid-term))
                         shift-bits)
                        low-term)))))))

  (define (mul x x-length y y-length) ;; x-length <= y-length
    (let ((x-width (##fx* x-length ##bignum.mdigit-width)))
      (cond ((##fx< x-width ##bignum.naive-mul-max-width)
             (naive-mul y y-length x x-length))
            ((or (##fx< x-width ##bignum.fft-mul-min-width)
                 (##fx< ##bignum.fft-mul-max-width
                             (##fx* y-length ##bignum.mdigit-width)))
             (karatsuba-mul x y))
            (else
             (fft-mul x y)))))

  ;; Certain decisions must be made for multiplication.
  ;; First, if both bignums are small, just do naive mul to avoid
  ;; further overhead.
  ;; This is done in the main body of ##bignum.*.
  ;; Second, if it would help to shift out low-order zeros of an
  ;; argument, do so.  That's done in the main body of ##bignum.*.
  ;; Finally, one must decide whether one is using naive mul, karatsuba, or fft.
  ;; This is done in mul.

  (define (low-bits-to-shift x)
    (let ((size (##integer-length x))
          (low-bits (##first-bit-set x)))
      (if (##fx< size (##fx+ low-bits low-bits))
          low-bits
          0)))

  (define (possibly-unnormalized-bignum-arithmetic-shift x bits)
    (if (##fx= bits 0)
        (if (##fx= (##bignum.adigit-length x) 1)
            (##bignum.normalize! x)
            x)
        (##arithmetic-shift x bits)))

  (let ((x-length (##bignum.mdigit-length x))
        (y-length (##bignum.mdigit-length y)))
    (cond ((or (##not (use-fast-bignum-algorithms))
               (and (##fx< x-length 50)
                    (##fx< y-length 50)))
           (if (##fx< x-length y-length)
               (naive-mul y y-length x x-length)
               (naive-mul x x-length y y-length)))
          ((##eq? x y)
           (let ((low-bits (low-bits-to-shift x)))
             (if (##fx= low-bits 0)
                 (mul x x-length x x-length)
                 (##arithmetic-shift
                  (##exact-int.square (##arithmetic-shift x (##fx- low-bits)))
                  (##fx+ low-bits low-bits)))))
          (else
           (let ((x-low-bits (low-bits-to-shift x))
                 (y-low-bits (low-bits-to-shift y)))
             (if (##fx= (##fx+ x-low-bits y-low-bits) 0)
                 (if (##fx< x-length y-length)
                     (mul x x-length y y-length)
                     (mul y y-length x x-length))
                 (##arithmetic-shift
                  (##* (possibly-unnormalized-bignum-arithmetic-shift x (##fx- x-low-bits))
                       (possibly-unnormalized-bignum-arithmetic-shift y (##fx- y-low-bits)))
                  (##fx+ x-low-bits y-low-bits))))))))

(define-prim (##bignum.arithmetic-shift x shift)
  (let* ((bit-shift
          (##fxmodulo shift ##bignum.adigit-width))
         (digit-shift
          (##fxquotient (##fx- shift bit-shift) ##bignum.adigit-width))
         (x-length
          (##bignum.adigit-length x))
         (result-length
          (##fx+ (##fx+ x-length digit-shift)
                 (if (##fxzero? bit-shift) 0 1))))
    (cond ((##fx< 0 result-length)
           (##bignum.normalize!
            (##bignum.arithmetic-shift-into! x shift (##bignum.make result-length #f #f))))
          ((##bignum.negative? x)
            -1)
          (else
           0))))

(define-prim (##bignum.arithmetic-shift-into! x shift result)

  #|
  Shifts x by shift bits into result.

  Left pads by sign bit as necessary, right pads by zeros as necessary.
  Makes *no* error checks.
  |#

  ;; allocates nothing
  (declare (not interrupts-enabled))

  (let* ((bit-shift
          (##fxmodulo shift ##bignum.adigit-width))
         (digit-shift
          (##fxquotient (##fx- shift bit-shift)
                        ##bignum.adigit-width))
         (x-length
          (##bignum.adigit-length x))
         (result-length
          (##bignum.adigit-length result))
         (zeros
          ##bignum.adigit-zeros)
         (left-fill
          (if (##bignum.negative? x)
              ##bignum.adigit-ones
              ##bignum.adigit-zeros)))
    (if (##fxzero? bit-shift)
        ;; Copy left-fill into leftmost digits of result as needed.
        (let loop1 ((i (##fx- result-length 1))              ;; index for adigit in result
                    (j (##fx- result-length 1 digit-shift))) ;; index for adigit in x
          (if (and (##fx>= i 0) (##fx>= j x-length))
              (begin (##bignum.adigit-copy! result i left-fill 0)
                     (loop1 (##fx- i 1) (##fx- j 1)))
              ;; Copy the digits from x into result as needed.
              (let loop2 ((i i)
                          (j j))
                (if (and (##fx>= i 0) (##fx>= j 0))
                    (begin (##bignum.adigit-copy! result i x j)
                           (loop2 (##fx- i 1) (##fx- j 1)))
                    ;; copy zero into digits of result as needed.
                    (let loop3 ((i i))
                      (if (##fx>= i 0)
                          (begin (##bignum.adigit-copy! result i zeros 0)
                                 (loop3 (##fx- i 1)))))))))
        (let ()
          ;; copy left-fill into leftmost digits of result as needed,
          ;; then concatenate left-fill with leftmost digit of x if needed.
          (define (loop4 i j)
            (if (and (##fx>= i 0) (##fx>= j x-length))
                (begin (##bignum.adigit-copy! result i left-fill 0)
                       (loop4 (##fx- i 1) (##fx- j 1)))
                (if (##fx>= i 0)
                    (if (##fx= (##fx+ j 1) x-length)
                        (begin (##bignum.adigit-cat! result i left-fill 0 x j bit-shift)
                               (loop5 (##fx- i 1) (##fx- j 1)))
                        (loop5 i j)))))
          ;; concatenate adjacent digits of x into result as needed,
          ;; then concatenate rightmost digit of x with 0 if needed.
          (define (loop5 i j)
            (if (and (##fx>= i 0) (##fx>= j 0))
                (begin (##bignum.adigit-cat! result i x (##fx+ j 1) x j bit-shift)
                       (loop5 (##fx- i 1) (##fx- j 1)))
                (if (##fx>= i 0)
                    (if (##fx= (##fx+ j 1) 0)
                        (begin (##bignum.adigit-cat! result i x 0 zeros 0 bit-shift)
                               (loop6 (##fx- i 1)))
                        (loop6 i)))))
          ;; copy 0 into rightmost digits of x as needed.
          (define (loop6 i)
            (if (##fx>= i 0)
                (begin (##bignum.adigit-copy! result i zeros 0)
                       (loop6 (##fx- i 1)))))

          (loop4 (##fx- result-length 1)                  ;; index for adigit in result
                 (##fx- result-length digit-shift 2))))   ;; index for adigit in x
    ;; return something useful
    result))

;;; Bignum division.

(define ##reciprocal-cache (##make-table 0 #f #t #f ##eq?))

(define ##bignum.mdigit-width/2
  (##fxquotient ##bignum.mdigit-width 2))

(define ##bignum.mdigit-base*16
  (##fx* ##bignum.mdigit-base 16))

(define (##fxceiling-ratio a b)
  ;; computes (ceiling (/ a b)) with a b fixnums
  (##fxquotient (##fx+ a b -1) b))

(define-prim (##bignum.div u v #!optional (need-quotient? #t) (keep-dividend? #t))

  ;; u is an unnormalized bignum, v is a normalized exact-int
  ;; 0 < v <= u

  (define (##exact-int.reciprocal v bits)

    ;; returns an approximation to the reciprocal of
    ;; .v1 v2 v3 ...
    ;; where v1 is the highest set bit of v; result is of the form
    ;; xx . xxxxxxxxxxxxxxxxxxx where there are bits + 1 bits to the
    ;; right of the binary point. The result is always <= 2.
    ;; See Knuth, volume 2, Algorithm R in Section 4.3.3

    (let ((cached-value (##table-ref ##reciprocal-cache v #f)))
      (if (and cached-value
               (##not (##fx< (##cdr cached-value) bits)))
          cached-value
          (let ((v-length (##integer-length v)))

            (define (recip v bits)
              (cond ((and cached-value
                          (##not (##fx< (##cdr cached-value) bits)))
                     cached-value)
                    ((##fx<= bits ##bignum.mdigit-width/2)
                     (macro-make-qr
                      (##fxquotient
                       ##bignum.mdigit-base*16
                       (##arithmetic-shift
                        v
                        (##fx- ##bignum.mdigit-width/2 -3 v-length)))
                      ##bignum.mdigit-width/2))
                    (else
                     (let* ((high-bits
                             (##fxarithmetic-shift-right
                              (##fx+ bits 1)
                              1))
                            (z-bits      ;; >= high-bits + 1 to right of point
                             (recip v high-bits))
                            (z           ;; high-bits + 1 to right of point
                             (##arithmetic-shift
                              (macro-qr-q z-bits)
                              (##fx- high-bits (macro-qr-r z-bits))))
                            (v-bits      ;; bits + 3 to right of point
                             (##arithmetic-shift
                              v
                              (##fx- (##fx+ bits 3)
                                     v-length)))
                            (v*z*z       ;; 2 * high-bits + bits + 5 to right
                             (##* v-bits (##exact-int.square z)))
                            (two-z       ;; 2 * high-bits + bits + 5 to right
                             (##arithmetic-shift
                              z
                              (##fx+ high-bits (##fx+ bits 5))))
                            (temp
                             (##- two-z v*z*z))
                            (bits-to-shift
                             (##fx+ 4 (##fx+ high-bits high-bits)))
                            (shifted-temp
                             (##arithmetic-shift
                              temp
                              (##fx- bits-to-shift))))
                       (if (##fx< (##first-bit-set temp) bits-to-shift)
                           (macro-make-qr (##+ shifted-temp 1) bits)
                           (macro-make-qr shifted-temp bits))))))

            (let ((result (recip v bits)))
              (##table-set! ##reciprocal-cache v result)
              result)))))

  (define (naive-div u v)

    ;; u is a normalized bignum, v is a possibly unnormalized bignum
    ;; u >= v >= ##bignum.mdigit-base
    ;; Based on Algorithm D of Knuth, Volume 2, Section 4.3.1

    (define (estimate-q-hat top-bits-of-u v_n-1 v_n-2)
      (let ((q-hat
             (##bignum.mdigit-quotient top-bits-of-u 2 v_n-1))
            (u_n+j-2
             (##bignum.mdigit-ref top-bits-of-u 0 )))
        (let ((r-hat
               (##bignum.mdigit-remainder top-bits-of-u 2 v_n-1 q-hat)))
          (if (or (##fx= q-hat ##bignum.mdigit-base)
                  (##bignum.mdigit-test? q-hat v_n-2 r-hat u_n+j-2))
              (let ((q-hat
                     (##fx- q-hat 1))
                    (r-hat
                     (##fx+ r-hat v_n-1)))
                (if (and (##fx< r-hat ##bignum.mdigit-base)
                         (or (##fx= q-hat ##bignum.mdigit-base)
                             (##bignum.mdigit-test? q-hat v_n-2 r-hat u_n+j-2)))
                    (##fx- q-hat 1)
                    q-hat))
              q-hat))))

    (define (subtract-multiple-of-v u v q-hat n j)
      ;; subtracts q-hat * v from u, starting at mdigit j, returns final q-hat
      (##declare (not interrupts-enabled))
      (if (##fx= q-hat 0)
          q-hat
          (let loop4 ((i j)
                      (k 0)
                      (borrow 0))
            (if (##fx< k n)
                (loop4 (##fx+ i 1)
                       (##fx+ k 1)
                       (##bignum.mdigit-div! u i v k q-hat borrow))
                (let* ((borrow  (if (or (##fxzero? borrow)
                                        (##fx= i (##bignum.mdigit-length u))
                                        (##fx= (##bignum.mdigit-ref u i) 0))
                                    borrow
                                    (##bignum.mdigit-div! u i ##bignum.adigit-zeros 0 q-hat borrow))))
                  (if (##fx< borrow 0)
                      (let loop5 ((i j)
                                  (l 0)
                                  (carry 0))
                        (if (##fx< l n)
                            (loop5 (##fx+ i 1)
                                   (##fx+ l 1)
                                   (##bignum.mdigit-mul! u i v l 1 carry))
                            (begin
                              (if (and (##fx= carry 1)
                                       (not (or (##fx= i (##bignum.mdigit-length u))
                                                (##fx= (##bignum.mdigit-ref u i) 0))))
                                  (##bignum.mdigit-mul! u i ##bignum.adigit-zeros 0 1 carry))
                              (##fx- q-hat 1))))
                      q-hat))))))


    (let ((u-bits
           (##integer-length u))
          (v-bits
           (##integer-length v)))
      (let* ((n
              (##fxceiling-ratio v-bits ##bignum.mdigit-width))
             (temp
              ;; need three mdigits for top-bits-of-u
              (##bignum.make (##fxceiling-ratio (##fx* 3 ##bignum.mdigit-width)
                                                ##bignum.adigit-width)
                             #f
                             #f))
             (top-2*mdigit-width-bits-of-v
              (##bignum.arithmetic-shift-into! v (##fx- (##fx* ##bignum.mdigit-width 2) v-bits) temp))
             (v_n-1
              (##bignum.mdigit-ref top-2*mdigit-width-bits-of-v 1))
             (v_n-2
              (##bignum.mdigit-ref top-2*mdigit-width-bits-of-v 0)))

        ;; Knuth says to simplify things by shifting u and v so that
        ;; the top nonzero mdigit of v is >= mdigit-base/2

        ;; We're not going to do the shift, but we're going to use that idea.

        ;; This strategy does a bit more work, but generates less garbage.

        (let* ((q-bits                             ;; maximum number of possible bits in q
                (##fx+ (##fx- u-bits v-bits) 2))   ;; 1 is not always sufficient...
               (q-adigits
                (##fxceiling-ratio q-bits ##bignum.adigit-width))
               (q-mdigits
                (##fxceiling-ratio q-bits ##bignum.mdigit-width))
               (q
                (and need-quotient?
                     (##fx> q-mdigits 1) ;; result might be bignum
                     (##bignum.make q-adigits #f #f)))
               (u
                (if keep-dividend?
                    ;; copy u
                    (##bignum.copy u)
                    ;; overwrite u with remainder
                    u)))
          (if (##fx= q-mdigits 1)
              ;; final result can't be bignum
              (let* ((top-bits-of-u
                      (##bignum.arithmetic-shift-into! u (##fx- (##fx* 2 ##bignum.mdigit-width) v-bits) temp))
                     (q-hat-estimate
                      (estimate-q-hat top-bits-of-u v_n-1 v_n-2))
                     (q-hat
                      (subtract-multiple-of-v u v q-hat-estimate n 0)))
                (macro-make-qr (and need-quotient? q-hat)
                               (##bignum.normalize! u)))
              ;; final result may be bignum
              (let loop3 ((j (##fx- q-mdigits 1)))
                (if (##not (##fx< j 0))
                    (let* ((top-bits-of-u
                            (##bignum.arithmetic-shift-into! u (##fx- (##fx* (##fx- 2 j) ##bignum.mdigit-width) v-bits) temp))
                           (q-hat-estimate
                            (estimate-q-hat top-bits-of-u v_n-1 v_n-2))
                           (q-hat
                            (subtract-multiple-of-v u v q-hat-estimate n j)))
                      (and need-quotient? (##bignum.mdigit-set! q j q-hat))
                      (loop3 (##fx- j 1)))
                    (macro-make-qr (and need-quotient? (##bignum.normalize! q))
                                   (##bignum.normalize! u)))))))))

  (define (div-one u v)
    (let ((m
           (let loop6 ((i (##fx- (##bignum.mdigit-length u) 1)))
             (if (##fx< 0 (##bignum.mdigit-ref u i))
                 (##fx+ i 1)
                 (loop6 (##fx- i 1))))))
      (let ((work-u (##bignum.make
                     (##fxceiling-ratio (##fx* 2 ##bignum.mdigit-width)
                                        ##bignum.adigit-width)
                     #f
                     #f))
            (q (and need-quotient?
                    (##bignum.make (##bignum.adigit-length u) #f #f))))

        (##declare (not interrupts-enabled))

        (let loop7 ((i m)
                    (r-hat 0))
          (##bignum.mdigit-set!
           work-u
           1
           r-hat)
          (##bignum.mdigit-set!
           work-u
           0
           (##bignum.mdigit-ref u (##fx- i 1)))
          (let ((q-hat (##bignum.mdigit-quotient work-u 1 v)))
            (let ((r-hat (##bignum.mdigit-remainder work-u 1 v q-hat)))
              (and need-quotient? (##bignum.mdigit-set! q (##fx- i 1) q-hat))
              (if (##fx< 1 i)
                  (loop7 (##fx- i 1)
                         r-hat)
                  (let ()
                    (##declare (interrupts-enabled))
                    (macro-make-qr (and need-quotient? (##bignum.normalize! q))
                                   r-hat)))))))))

  (define (big-divide u v)

    ;; u and v are positive bignums

    (let ((v-length (##integer-length v))
          (v-first-bit-set (##first-bit-set v)))
      ;; first we check whether it may be beneficial to shift out
      ;; low-order zero bits of v
      (if (##fx>= v-first-bit-set
                  (##fxarithmetic-shift-right v-length 1))
          (let ((reduced-quotient
                 (##exact-int.div
                  (##bignum.arithmetic-shift u (##fx- v-first-bit-set))
                  (##bignum.arithmetic-shift v (##fx- v-first-bit-set))
                  #t          ;; need-quotient?
                  #f          ;; keep-dividend?
                  ))
                (extra-remainder
                 (##extract-bit-field v-first-bit-set 0 u)))
            (macro-make-qr (macro-qr-q reduced-quotient)
                           (##+ (##arithmetic-shift
                                 (macro-qr-r reduced-quotient)
                                 v-first-bit-set)
                                extra-remainder)))
          (if (##fx< v-length ##bignum.fft-mul-min-width)
              (naive-div u v)
              (let* ((u-length (##integer-length u))
                     (length-difference (##fx- u-length v-length)))
                (if (##fx< length-difference ##bignum.fft-mul-min-width)
                    (naive-div u v)
                    (let* ((z-bits (##exact-int.reciprocal v length-difference))
                           (z (macro-qr-q z-bits))
                           (bits (macro-qr-r z-bits)))
                      (let ((test-quotient
                             (##bignum.arithmetic-shift
                              (##* (##bignum.arithmetic-shift
                                    u
                                    (##fx- length-difference
                                           (##fx- u-length 2)))
                                   (##bignum.arithmetic-shift
                                    z
                                    (##fx- length-difference bits)))
                              (##fx- -3 length-difference))))
                        (let ((rem (##- u (##* test-quotient v))))
                          ;; I believe, and I haven't found any counterexamples in my tests
                          ;; to disprove it, that test-quotient can be off by at most +-1.
                          ;; I can't prove this, however, so we put in the following loops.

                          ;; Especially note that our reciprocal does not satisfy the
                          ;; error bounds in Knuth's volume 2 in perhaps a vain effort to
                          ;; save some computations. perhaps this should be fixed.  blah.

                          (cond ((##negative? rem)
                                 (let loop ((test-quotient test-quotient)
                                            (rem rem))
                                   (let ((test-quotient (##- test-quotient 1))
                                         (rem (##+ rem v)))
                                     (if (##negative? rem)
                                         (loop test-quotient rem)
                                         (macro-make-qr test-quotient rem)))))
                                ((##< rem v)
                                 (macro-make-qr test-quotient
                                                rem))
                                (else
                                 (let loop ((test-quotient test-quotient)
                                            (rem rem))
                                   (let ((test-quotient (##+ test-quotient 1))
                                         (rem (##- rem v)))
                                     (if (##< rem v)
                                         (macro-make-qr test-quotient rem)
                                         (loop test-quotient rem)))))))))))))))

  (macro-exact-int-dispatch-no-error v
    (if (##fx< v ##bignum.mdigit-base)
        (div-one u v)
        (begin
          ;; here it's probably not worth the extra cycles to check whether
          ;; a subtraction would be sufficient, i.e., we don't call
          ;; short-divisor-or-quotient-divide
          (naive-div u (##fixnum->bignum v))))
    (if (use-fast-bignum-algorithms)
        (big-divide u v)
        (naive-div u v))))

))

;;;----------------------------------------------------------------------------

;;; Exact integer operations
;;; ------------------------

(define (##exact-int.= x y)
  (##fx= 0 (##exact-int.compare x y)))

(define (##exact-int.< x y)
  (##fx= -1 (##exact-int.compare x y)))

(define (##exact-int.compare x y)

  ;; returns -1 if x < y, 0 if x = y, or 1 if x > y

  (define (compare x y x-smaller)
    (##declare (not interrupts-enabled))
    (let ((x-digits (##bignum.adigit-length x))
          (y-digits (##bignum.adigit-length y)))
      (cond ((##fx< x-digits y-digits) x-smaller)
            ((##fx< y-digits x-digits) (##fx- x-smaller))
            (else
             (let loop ((i (##fx- x-digits 1)))
               (cond ((##fx< i 0) 0)
                     ((##bignum.adigit-< x y i) -1)
                     ((##bignum.adigit-< y x i) 1)
                     (else
                      (loop (##fx- i 1)))))))))
  (macro-exact-int-dispatch-no-error x

    (macro-exact-int-dispatch-no-error y ;; x = fixnum
      (cond ((##fx< x y) -1)
            ((##fx= x y)  0)
            (else         1))
      (if (##bignum.negative? y) 1 -1))

    (macro-exact-int-dispatch-no-error y ;; x = bignum
      (if (##bignum.negative? x) -1 1)
      (if (##bignum.negative? x)
          (if (##bignum.negative? y) (compare x y 1) -1)
          (if (##bignum.negative? y) 1 (compare x y -1))))))

(define-prim (##exact-int.*-expt2 x y)
  (if (##fxnegative? y)
      (##/ x (##arithmetic-shift 1 (##fx- y)))
      (##arithmetic-shift x y)))

(define-prim (##exact-int.div x y #!optional (need-quotient? #t) (keep-dividend? #t))

  (define (big-quotient x y)
    (let* ((x-negative? (##negative? x))
           (abs-x (if x-negative?
                      (##negate x)
                      x))
           (y-negative? (##negative? y))
           (abs-y (if y-negative?
                      (begin
                        (set! keep-dividend? #f)
                        (##negate y))
                      y)))
      (if (##< abs-x abs-y)
          (macro-make-qr 0 x)

          ;; at least one of x and y is a bignum, so
          ;; here abs-x must be a bignum

          (let ((result (##bignum.div abs-x abs-y need-quotient? keep-dividend?)))

            (if (and need-quotient?
                     (##not (##eq? x-negative? y-negative?)))
                (macro-make-qr (##negate (macro-qr-q result))
                               (if x-negative?
                                   (##negate (macro-qr-r result))
                                   (macro-qr-r result)))
                (if x-negative?
                    (macro-make-qr (macro-qr-q result)
                                   (##negate (macro-qr-r result)))
                    result))))))

  (cond ((##eqv? y 1)
         (macro-make-qr x 0))
        ((##eqv? y -1)
         (macro-make-qr (##negate x) 0))
        ((##eq? x y)              ;; can come up in rational arithmetic
         (macro-make-qr 1 0))
        (else
         (macro-exact-int-dispatch-no-error x

           (macro-exact-int-dispatch-no-error y ;; x = fixnum
             (macro-make-qr (##fxquotient x y) ;; note: y can't be -1
                            (##fxremainder x y))
             (if (##fx< 1 (##bignum.adigit-length y))
                 ;; y has at least two adigits, so
                 ;; (abs y) > (abs x)
                 (macro-make-qr 0 x)
                 (big-quotient x y)))

           (macro-exact-int-dispatch-no-error y ;; x = bignum
             (big-quotient x y)
             (if (##fx< 1 (##fx- (##bignum.adigit-length y)
                                 (##bignum.adigit-length x)))
                 ;; x and y are bignums, and y has at least two more adigits
                 ;; than x, so (abs y) > (abs x)
                 (macro-make-qr 0 x)
                 (big-quotient x y)))))))

(define-prim (##exact-int.nth-root x y)
  (cond ((##eqv? x 0)
         0)
        ((##eqv? x 1)
         1)
        ((##eqv? y 1)
         x)
        ((##eqv? y 2)
         (macro-sr-s (##exact-int.sqrt x)))
        (else
         (macro-exact-int-dispatch-no-error y
           (let ((length (##integer-length x)))
             ;; (expt 2 (- length l 1)) <= x < (expt 2 length)
             (cond ((##fx<= length y)
                    1)
                   ;; result is >= 2
                   ((##fx<= length (##fx* 2 y))
                    ;; result is < 4
                    (if (##< x (##expt 3 y))
                        2
                        3))
                   ((##fxeven? y)
                    (##exact-int.nth-root
                     (macro-sr-s (##exact-int.sqrt x))
                     (##fxarithmetic-shift-right y 1)))
                   (else
                    (let* ((length/y/2 ;; length/y/2 >= 1 because (< (* 2 y) length)
                            (##fxarithmetic-shift-right
                             (##fxquotient
                              (##fx- length 1)
                              y)
                             1)))
                      (let ((init-g
                             (let* ((top-bits
                                     (##arithmetic-shift
                                      x
                                      (##fx- (##fx* length/y/2 y))))
                                    (nth-root-top-bits
                                     (##exact-int.nth-root top-bits y)))
                               (##arithmetic-shift
                                (##+ nth-root-top-bits 1)
                                length/y/2))))
                        (let loop ((g init-g))
                          (let* ((a (##expt g (##fx- y 1)))
                                 (b (##* a y))
                                 (c (##* a (##fx- y 1)))
                                 (d (##quotient (##+ x (##* g c)) b)))
                            (let ((diff (##- d g)))
                              (cond ((##not (##negative? diff))
                                     g)
                                    ((##< diff -1)
                                     (loop d))
                                    (else
                                     ;; once the difference is one, it's more
                                     ;; efficient to just decrement until g^y <= x
                                     (let loop ((g d))
                                       (if (##not (##< x (##expt g y)))
                                           g
                                           (loop (##- g 1))))))))))))))
           1))))

(define-prim (##integer-nth-root x y)

  (define (type-error-on-x)
    (##fail-check-exact-integer 1 integer-nth-root x y))

  (define (type-error-on-y)
    (##fail-check-exact-integer 2 integer-nth-root x y))

  (define (range-error-on-x)
    (##raise-range-exception 1 integer-nth-root x y))

  (define (range-error-on-y)
    (##raise-range-exception 2 integer-nth-root x y))

  (if (macro-exact-int? x)
      (if (macro-exact-int? y)
          (cond ((##negative? x)
                 (range-error-on-x))
                ((##positive? y)
                 (##exact-int.nth-root x y))
                (else
                 (range-error-on-y)))
          (type-error-on-y))
      (type-error-on-x)))

(define-prim (integer-nth-root x y)
  (macro-force-vars (x y)
    (##integer-nth-root x y)))

(define-prim (##exact-int.sqrt x)

  ;; Derived from the paper "Karatsuba Square Root" by Paul Zimmermann,
  ;; INRIA technical report RR-3805, 1999.  (Used in gmp 4.*)

  ;; Note that the statement of the theorem requires that
  ;; b/4 <= high-order digit of x < b which can be impossible when b is a
  ;; power of 2; the paper later notes that it is the lower bound that is
  ;; necessary, which we preserve.

  (macro-exact-int-dispatch-no-error x

    ;; Here we assume that we have at least as much precision as
    ;; IEEE double precision, that we round to nearest, and that
    ;; fixnums have no more than 64 bits.

    ;; Here's why this works.

    ;; The three functions ##fixnum->flonum, ##flsqrt, and
    ;; ##flonum->fixnum are all monotone, in that if the
    ;; argument is increased, the result does not decrease.
    ;; So for each fixnum i such that (* i i) and (+ (* i i) (* 2 i))
    ;; are fixnums, check that

    ;; (##exact-int.sqrt (* i i)) => i and
    ;; (##exact-int.sqrt (+ (* i i) (* 2 i))) => i

    ;; and this will be true of all fixnums in between.
    ;; And that is all fixnums.  So we ran a code to check this.

    (let* ((s (##flonum->fixnum (##flsqrt (##fixnum->flonum x))))
           (r (##fx- x (##fx* s s))))
      ;; s can be too big, so we check for that here.
      (if (##fxnegative? r)
          (macro-make-sr (##fx- s 1)
                         (##fx+ r (##fx* s 2) -1))
          (macro-make-sr s r)))

    (let ((length/4
           (##fxarithmetic-shift-right
            (##fx+ (##integer-length x) 1)
            2)))
      (let* ((s-prime&r-prime
              (##exact-int.sqrt
               (##arithmetic-shift
                x
                (##fx- (##fxarithmetic-shift-left length/4 1)))))
             (s-prime
              (macro-sr-s s-prime&r-prime))
             (r-prime
              (macro-sr-r s-prime&r-prime)))
        (let* ((qu
                (##exact-int.div
                 (##+ (##arithmetic-shift r-prime length/4)
                      (##extract-bit-field length/4 length/4 x))
                 (##arithmetic-shift s-prime 1)
                 #t           ;; need-quotient?
                 #f))         ;; keep-dividend?
               (q
                (macro-qr-q qu))
               (u
                (macro-qr-r qu)))
          (let ((s
                 (##+ (##arithmetic-shift s-prime length/4) q))
                (r
                 (##- (##+ (##arithmetic-shift u length/4)
                           (##extract-bit-field length/4 0 x))
                      (##* q q))))
            (if (##negative? r)
                (macro-make-sr (##- s 1)
                               (##+ r
                                    (##- (##arithmetic-shift s 1) 1)))
                (macro-make-sr s
                               r))))))))

(define-prim (##integer-sqrt x)

  (define (type-error)
    (##fail-check-exact-integer 1 integer-sqrt x))

  (define (range-error)
    (##raise-range-exception 1 integer-sqrt x))

  (if (macro-exact-int? x)
      (if (##negative? x)
          (range-error)
          (macro-sr-s (##exact-int.sqrt x)))
      (type-error)))

(define-prim (integer-sqrt x)
  (macro-force-vars (x)
    (##integer-sqrt x)))

(define-prim (##exact-integer-sqrt x)

  (define (type-error)
    (##fail-check-exact-integer 1 exact-integer-sqrt x))

  (define (range-error)
    (##raise-range-exception 1 exact-integer-sqrt x))

  (if (macro-exact-int? x)
      (if (##negative? x)
          (range-error)
          (##exact-int.sqrt x))
      (type-error)))

(define-prim (exact-integer-sqrt x)
  (macro-force-vars (x)
    (##exact-integer-sqrt x)))

(define-prim (##exact-int.square n)
  (##* n n))

;;;----------------------------------------------------------------------------

;;; Ratnum operations
;;; -----------------

;;; In the following ratnum routines we sometimes use generic numeric
;;; operators with a mostly-fixnum declaration.  The compiler expands
;;; these operations into inline fixnum arithmetic if the arguments
;;; and results are fixnums.  In the common case where all
;;; numerators and denominators are fixnums this speeds up
;;; these routines noticeably.

(macro-if-ratnum (begin

(define-prim (##ratnum-make num den)
  (macro-ratnum-make num den))

(define-prim (##ratnum-numerator x)
  (macro-ratnum-numerator x))

(define-prim (##ratnum-denominator x)
  (macro-ratnum-denominator x))

(define-prim (##ratnum.= x y)
  (##declare (mostly-fixnum)
             (standard-bindings))
  (and (= (macro-ratnum-numerator x)
          (macro-ratnum-numerator y))
       (= (macro-ratnum-denominator x)
          (macro-ratnum-denominator y))))

(define-prim (##ratnum.< x y)
  (##declare (mostly-fixnum)
             (standard-bindings))
  (< (* (macro-ratnum-numerator x)
        (macro-ratnum-denominator y))
     (* (macro-ratnum-denominator x)
        (macro-ratnum-numerator y))))

(define-prim (##ratnum.+ x y)
  (##declare (mostly-fixnum)
             (standard-bindings))
  (let ((p (macro-ratnum-numerator x))
        (q (macro-ratnum-denominator x))
        (r (macro-ratnum-numerator y))
        (s (macro-ratnum-denominator y)))
    (let ((d1 (##gcd q s)))
      (if (eqv? d1 1)
          (macro-ratnum-make (+ (* p s)
                                (* r q))
                             (* q s))
          (let* ((s-prime (quotient s d1))
                 (t (+ (* p s-prime)
                       (* r (quotient q d1))))
                 (d2 (##gcd d1 t))
                 (num (quotient t d2))
                 (den (* (quotient q d2)
                         s-prime)))
            (if (eqv? den 1)
                num
                (macro-ratnum-make num den)))))))

(define-prim (##ratnum.- x y)
  (##declare (mostly-fixnum)
             (standard-bindings))
  (let ((p (macro-ratnum-numerator x))
        (q (macro-ratnum-denominator x))
        (r (macro-ratnum-numerator y))
        (s (macro-ratnum-denominator y)))
    (let ((d1 (##gcd q s)))
      (if (eqv? d1 1)
          (macro-ratnum-make (- (* p s)
                                (* r q))
                             (* q s))
          (let* ((s-prime (quotient s d1))
                 (t (- (* p s-prime)
                       (* r (quotient q d1))))
                 (d2 (##gcd d1 t))
                 (num (quotient t d2))
                 (den (* (quotient q d2)
                         s-prime)))
            (if (eqv? den 1)
                num
                (macro-ratnum-make num den)))))))

(define-prim (##ratnum.* x y)
  (##declare (mostly-fixnum)
             (standard-bindings))
  (let ((p (macro-ratnum-numerator x))
        (q (macro-ratnum-denominator x))
        (r (macro-ratnum-numerator y))
        (s (macro-ratnum-denominator y)))
    (if (eq? x y)
        (macro-ratnum-make (square p) (square q))     ;; already in lowest form
        (let* ((gcd-ps (##gcd p s))
               (gcd-rq (##gcd r q))
               (num (* (quotient p gcd-ps) (quotient r gcd-rq)))
               (den (* (quotient q gcd-rq) (quotient s gcd-ps))))
          (if (eqv? den 1)
              num
              (macro-ratnum-make num den))))))

(define-prim (##ratnum./ x y)
  (##declare (mostly-fixnum)
             (standard-bindings))
  (let ((p (macro-ratnum-numerator x))
        (q (macro-ratnum-denominator x))
        (r (macro-ratnum-denominator y))
        (s (macro-ratnum-numerator y)))
    (if (eq? x y)
        1
        (let* ((gcd-ps (##gcd p s))
               (gcd-rq (##gcd r q))
               (num (* (quotient p gcd-ps) (quotient r gcd-rq)))
               (den (* (quotient q gcd-rq) (quotient s gcd-ps))))
          (if (negative? den)
              (if (eqv? den -1)
                  (- num)
                  (macro-ratnum-make (- num) (- den)))
              (if (eqv? den 1)
                  num
                  (macro-ratnum-make num den)))))))

(define-prim (##ratnum.normalize num den)
  (##declare (mostly-fixnum)
             (standard-bindings))
  (let* ((x (##gcd num den))
         (y (if (negative? den) (- x) x))
         (num (quotient num y))
         (den (quotient den y)))
    (if (eqv? den 1)
        num
        (macro-ratnum-make num den))))

(define-prim (##ratnum.round x #!optional (round-half-away-from-zero? #f))
  (let ((num (macro-ratnum-numerator x))
        (den (macro-ratnum-denominator x)))
    (if (##eqv? den 2)
        (if round-half-away-from-zero?
            (##arithmetic-shift (##+ num (if (##positive? num) 1 -1)) -1)
            (##arithmetic-shift (##arithmetic-shift (##+ num 1) -2) 1))
        ;; here the ratnum cannot have fractional part = 1/2
        (##floor
         (##ratnum.normalize
          (##+ (##arithmetic-shift num 1) den)
          (##arithmetic-shift den 1))))))

))

;;;----------------------------------------------------------------------------

;;; Flonum operations
;;; -----------------

(##define-macro (define-prim-flonum form . special-body)
  (let ((body (if (null? special-body) form `(begin ,@special-body))))
    (cond ((= 1 (length (cdr form)))
           (let* ((name-fn (car form))
                  (name-param1 (cadr form)))
             `(define-prim ,form
                (macro-force-vars (,name-param1)
                  (macro-check-flonum
                    ,name-param1
                    1
                    ,form
                    ,body)))))
          ((= 2 (length (cdr form)))
           (let* ((name-fn (car form))
                  (name-param1 (cadr form))
                  (name-param2 (caddr form)))
             `(define-prim ,form
                (macro-force-vars (,name-param1 ,name-param2)
                  (macro-check-flonum
                    ,name-param1
                    1
                    ,form
                    (macro-check-flonum
                      ,name-param2
                      2
                      ,form
                      ,body))))))
          (else
           (error "define-prim-flonum supports only 1 or 2 parameter procedures")))))

(define-prim (flonum? obj)
  (macro-force-vars (obj)
    (##flonum? obj)))

(define-prim (##fleqv? x y))

(define-prim-nary-bool (##fl= x y)
  #t
  #t
  (##fl= x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fl= x y)
  #t
  #t
  (##fl= x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary-bool (##fl< x y)
  #t
  #t
  (##fl< x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fl< x y)
  #t
  #t
  (##fl< x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary-bool (##fl> x y)
  #t
  #t
  (##fl> x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fl> x y)
  #t
  #t
  (##fl> x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary-bool (##fl<= x y)
  #t
  #t
  (##fl<= x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fl<= x y)
  #t
  #t
  (##fl<= x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary-bool (##fl>= x y)
  #t
  #t
  (##fl>= x y)
  macro-no-force
  macro-no-check)

(define-prim-nary-bool (fl>= x y)
  #t
  #t
  (##fl>= x y)
  macro-force-vars
  macro-check-flonum)

(define-prim (##flinteger? x))

(define-prim-flonum (flinteger? x)
  (##flinteger? x))

(define-prim (##flzero? x))

(define-prim-flonum (flzero? x)
  (##flzero? x))

(define-prim (##flpositive? x))

(define-prim-flonum (flpositive? x)
  (##flpositive? x))

(define-prim (##flnegative? x))

(define-prim-flonum (flnegative? x)
  (##flnegative? x))

(define-prim (##flodd? x))

(define-prim-flonum (flodd? x)
  (##flodd? x))

(define-prim (##fleven? x))

(define-prim-flonum (fleven? x)
  (##fleven? x))

(define-prim (##flfinite? x))

(define-prim-flonum (flfinite? x)
  (##flfinite? x))

(define-prim (##flinfinite? x))

(define-prim-flonum (flinfinite? x)
  (##flinfinite? x))

(define-prim (##flnan? x))

(define-prim-flonum (flnan? x)
  (##flnan? x))

(define-prim-nary (##flmax x y)
  ()
  x
  (##flmax x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (flmax x y)
  ()
  x
  (##flmax x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary (##flmin x y)
  ()
  x
  (##flmin x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (flmin x y)
  ()
  x
  (##flmin x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary (##fl+ x y)
  (macro-inexact-+0)
  x
  (##fl+ x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fl+ x y)
  (macro-inexact-+0)
  x
  (##fl+ x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary (##fl* x y)
  (macro-inexact-+1)
  x
  (##fl* x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fl* x y)
  (macro-inexact-+1)
  x
  (##fl* x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary (##fl- x y)
  ()
  (##fl- x)
  (##fl- x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fl- x y)
  ()
  (##fl- x)
  (##fl- x y)
  macro-force-vars
  macro-check-flonum)

(define-prim-nary (##fl/ x y)
  ()
  (##fl/ x)
  (##fl/ x y)
  macro-no-force
  macro-no-check)

(define-prim-nary (fl/ x y)
  ()
  (##fl/ x)
  (##fl/ x y)
  macro-force-vars
  macro-check-flonum)

(define-prim (##flabs x))

(define-prim-flonum (flabs x)
  (##flabs x))

(define-prim-flonum (flnumerator x)
  (cond ((##flzero? x)
         x)
        ((macro-flonum-rational? x)
         (##exact->inexact (##numerator (##flonum->exact x))))
        (else
         (##fail-check-rational 1 flnumerator x))))

(define-prim-flonum (fldenominator x)
  (if (macro-flonum-rational? x)
      (##exact->inexact (##denominator (##flonum->exact x)))
      (##fail-check-rational 1 fldenominator x)))

(define-prim (##flfloor x))

(define-prim-flonum (flfloor x)
  (##flfloor x))

(define-prim (##flceiling x))

(define-prim-flonum (flceiling x)
  (##flceiling x))

(define-prim (##fltruncate x))

(define-prim-flonum (fltruncate x)
  (##fltruncate x))

(define-prim (##flround x))

(define-prim-flonum (flround x)
  (##flround x))

(define-prim (##flscalbn x n))

(define-prim (flscalbn x n)
  (macro-force-vars (x n)
    (macro-check-flonum x 1 (flscalbn x n)
      (macro-check-fixnum n 2 (flscalbn x n)
        (##flscalbn x n)))))

(define-prim (##flilogb x))

(define-prim-flonum (flilogb x)
  (##flilogb x))

(define-prim (##flexp x))

(define-prim-flonum (flexp x)
  (##flexp x))

(define-prim (##flexpm1 x))

(define-prim-flonum (flexpm1 x)
  (##flexpm1 x))

(define-prim (##fllog x))

(define-prim-flonum (fllog x)
  (##fllog x))

(define-prim (##fllog1p x))

(define-prim-flonum (fllog1p x)
  (##fllog1p x))

(define-prim (##flsin x))

(define-prim-flonum (flsin x)
  (##flsin x))

(define-prim (##flcos x))

(define-prim-flonum (flcos x)
  (##flcos x))

(define-prim (##fltan x))

(define-prim-flonum (fltan x)
  (##fltan x))

(define-prim (##flasin x))

(define-prim-flonum (flasin x)
  (##flasin x))

(define-prim (##flacos x))

(define-prim-flonum (flacos x)
  (##flacos x))

(define-prim (##flatan x #!optional (y (macro-absent-obj)))
  (if (##eq? y (macro-absent-obj))
      (##flatan x)
      (##flatan x y)))

(define-prim (flatan x #!optional (y (macro-absent-obj)))
  (macro-force-vars (x y)
    (macro-check-flonum x 1 (flatan x y)
      (if (##eq? y (macro-absent-obj))
          (##flatan x)
          (macro-check-flonum y 2 (flatan x y)
            (##flatan x y))))))

(define-prim (##flsinh x))

(define-prim-flonum (flsinh x)
  (##flsinh x))

(define-prim (##flcosh x))

(define-prim-flonum (flcosh x)
  (##flcosh x))

(define-prim (##fltanh x))

(define-prim-flonum (fltanh x)
  (##fltanh x))

(define-prim (##flasinh x))

(define-prim-flonum (flasinh x)
  (##flasinh x))

(define-prim (##flacosh x))

(define-prim-flonum (flacosh x)
  (##flacosh x))

(define-prim (##flatanh x))

(define-prim-flonum (flatanh x)
  (##flatanh x))

(define-prim (##flexpt x y))

(define-prim-flonum (flexpt x y)
  (##flexpt x y))

(define-prim (##flsqrt x))

(define-prim-flonum (flsqrt x)
  (##flsqrt x))

(define-prim (##flsquare x))

(define-prim-flonum (flsquare x)
  (##flsquare x))

(define-prim-fixnum (fixnum->flonum x)
  (##fixnum->flonum x))

(define-prim (##flcopysign x y))

(define-prim (##flonum->fixnum x))
(define-prim (##fixnum->flonum x))
(define-prim (##fixnum->flonum-exact? x))

(macro-if-ratnum

(define-prim (##ratnum->flonum x #!optional (nonzero-fractional-part? #f))
  (let* ((num (macro-ratnum-numerator x))
         (n (##abs num))
         (d (macro-ratnum-denominator x))
         (wn (##integer-length n)) ;; 2^(wn-1) <= n < 2^wn
         (wd (##integer-length d)) ;; 2^(wd-1) <= d < 2^wd
         (p (##fx- wn wd)))

    (define (f1 sn sd)
      (if (##< sn sd) ;; n/(d*2^p) < 1 ?
          (f2 (##arithmetic-shift sn 1) sd (##fx- p 1))
          (f2 sn sd p)))

    (define (f2 a b p)
      ;; 1 <= a/b < 2  and  n/d = (2^p*a)/b  and  n/d < 2^(p+1)
      (let* ((shift
              (##fxmin (macro-flonum-m-bits)
                       (##fx- p (macro-flonum-e-min))))
             (normalized-result
              (##ratnum.normalize
               (##arithmetic-shift a shift)
               b))
             (abs-result
              (##fl*
               (##exact-int->flonum
                (if (##ratnum? normalized-result)
                    (##ratnum.round
                     normalized-result
                     nonzero-fractional-part?)
                    normalized-result))
               (##flonum-expt2 (##fx- p shift)))))
        (if (##negative? num)
            (##flcopysign abs-result (macro-inexact--1))
            abs-result)))

    ;; 2^(p-1) <= n/d < 2^(p+1)
    ;; 1/2 <= n/(d*2^p) < 2  or equivalently  1/2 <= (n*2^-p)/d < 2

    (if (##fxnegative? p)
        (f1 (##arithmetic-shift n (##fx- p)) d)
        (f1 n (##arithmetic-shift d p)))))

)

(define-prim (##exact-int->flonum x #!optional (nonzero-fractional-part? #f))

  (define (f1 x)
    (let* ((w ;; 2^(w-1) <= x < 2^w
            (##integer-length x))
           (p ;; 2^52 <= x/2^p < 2^53
            (##fx- w (macro-flonum-m-bits-plus-1))))
      (if (##fx< p 1)
          ;; it really should be an error here if
          ;; positive-fractional-part? is true because we can't
          ;; determine the value of the first discarded bit
          (f2 x)
          (let* ((q (##arithmetic-shift x (##fx- p)))
                 (next-bit-index (##fx- p 1)))
            (##fl*
             (##flonum-expt2 p)
             (f2 (if (and (##bit-set? next-bit-index x)
                          (or nonzero-fractional-part?
                              (##odd? q)
                              (##fx< (##first-bit-set x)
                                     next-bit-index)))
                     (##+ q 1)
                     q)))))))

  (define (f2 x) ;; 0 <= x < 2^53
    (macro-exact-int-dispatch-no-error x
      (##fixnum->flonum x)
      (let ((n (##bignum.mdigit-length x)))
        (let loop ((i (##fx- n 1))
                   (result (macro-inexact-+0)))
          (if (##fx< i 0)
              result
              (let ((mdigit (##bignum.mdigit-ref x i)))
                (loop (##fx- i 1)
                      (##fl+ (##fl* result
                                    ##bignum.inexact-mdigit-base)
                             (##fixnum->flonum mdigit)))))))))

  (macro-exact-int-dispatch-no-error x
    (##fixnum->flonum x)
    (if (##negative? x)
        (##flcopysign (f1 (##negate x)) (macro-inexact--1))
        (f1 x))))

(define-prim (##flonum-expt2 n)
  (cond ((##fxzero? n)
         (macro-inexact-+1))
        ((##fxnegative? n)
         (##expt (macro-inexact-+1/2) (##fx- n)))
        (else
         (##expt (macro-inexact-+2) n))))

(define-prim (##flonum->exact-int x)
  (let loop1 ((z (##flabs x))
              (n 1))
    (if (##fl< ##bignum.inexact-mdigit-base z)
        (loop1 (##fl/ z ##bignum.inexact-mdigit-base)
               (##fx+ n 1))
        (let loop2 ((result 0)
                    (z z)
                    (i n))
          (if (##fx< 0 i)
              (let* ((inexact-floor-z
                      (##flfloor z))
                     (floor-z
                      (##flonum->fixnum inexact-floor-z))
                     (new-z
                      (##fl* (##fl- z inexact-floor-z)
                             ##bignum.inexact-mdigit-base)))
                (loop2 (##+ floor-z
                            (##arithmetic-shift result ##bignum.mdigit-width))
                       new-z
                       (##fx- i 1)))
              (if (##flnegative? x)
                  (##negate result)
                  result))))))

(define-prim (##flonum->inexact-exponential-format x)

  (define (exp-form-pos x y i)
    (let ((i*2 (##fx+ i i)))
      (let ((z (if (and (##not (##fx< (macro-flonum-e-bias) i*2))
                        (##not (##fl< x y)))
                   (exp-form-pos x (##fl* y y) i*2)
                   (##vector x 0 1))))
        (let ((a (##vector-ref z 0)) (b (##vector-ref z 1)))
          (let ((i+b (##fx+ i b)))
            (if (and (##not (##fx< (macro-flonum-e-bias) i+b))
                     (##not (##fl< a y)))
                (begin
                  (##vector-set! z 0 (##fl/ a y))
                  (##vector-set! z 1 i+b)))
            z)))))

  (define (exp-form-neg x y i)
    (let ((i*2 (##fx+ i i)))
      (let ((z (if (and (##fx< i*2 (macro-flonum-e-bias-minus-1))
                        (##fl< x y))
                   (exp-form-neg x (##fl* y y) i*2)
                   (##vector x 0 1))))
        (let ((a (##vector-ref z 0)) (b (##vector-ref z 1)))
          (let ((i+b (##fx+ i b)))
            (if (and (##fx< i+b (macro-flonum-e-bias-minus-1))
                     (##fl< a y))
                (begin
                  (##vector-set! z 0 (##fl/ a y))
                  (##vector-set! z 1 i+b)))
            z)))))

  (define (exp-form x)
    (if (##fl< x (macro-inexact-+1))
        (let ((z (exp-form-neg x (macro-inexact-+1/2) 1)))
          (##vector-set! z 0 (##fl* (macro-inexact-+2) (##vector-ref z 0)))
          (##vector-set! z 1 (##fx- -1 (##vector-ref z 1)))
          z)
        (exp-form-pos x (macro-inexact-+2) 1)))

  (if (##flnegative? (##flcopysign (macro-inexact-+1) x))
      (let ((z (exp-form (##flcopysign x (macro-inexact-+1)))))
        (##vector-set! z 2 -1)
        z)
      (exp-form x)))

(define-prim (##flonum->exact-exponential-format x)
  (let ((z (##flonum->inexact-exponential-format x)))
    (let ((y (##vector-ref z 0)))
      (if (##not (##fl< y (macro-inexact-+2))) ;; +inf.0 or +nan.0?
          (begin
            (if (##fl< (macro-inexact-+0) y)
                (##vector-set! z 0 (macro-flonum-+m-min))  ;; +inf.0
                (##vector-set! z 0 (macro-flonum-+m-max))) ;; +nan.0
            (##vector-set! z 1 (macro-flonum-e-bias-plus-1)))
          (##vector-set! z 0
                         (##flonum->exact-int
                          (##fl* (##vector-ref z 0) (macro-flonum-m-min)))))
      (##vector-set! z 1 (##fx- (##vector-ref z 1) (macro-flonum-m-bits)))
      z)))

(define-prim (##flonum->exact x)
  (let ((y (##flonum->exact-exponential-format x)))
    (##exact-int.*-expt2
     (if (##fxnegative? (##vector-ref y 2))
         (##negate (##vector-ref y 0))
         (##vector-ref y 0))
     (##vector-ref y 1))))

(macro-if-ratnum

(define-prim (##flonum->ratnum x)
  (let ((y (##flonum->exact x)))
    (if (macro-exact-int? y)
        (macro-exact-int->ratnum y)
        y)))

)

(define-prim (##flonum->ieee754-32 x)
  (##u32vector-ref (##f32vector x) 0))

(define-prim (##ieee754-32->flonum n)
  (let ((x (##u32vector n)))
    (##f32vector-ref x 0)))

(define-prim (##flonum->ieee754-64 x)
  (##u64vector-ref x 0))

(define-prim (##ieee754-64->flonum n)
  (let ((x (##u64vector n)))
    (##subtype-set! x (macro-subtype-flonum))
    x))

;;;----------------------------------------------------------------------------

;;; Cpxnum operations
;;; -----------------

(macro-if-cpxnum (begin

(define-prim (##cpxnum-make real imag)
  (macro-cpxnum-make real imag))

(define-prim (##cpxnum-real x)
  (macro-cpxnum-real x))

(define-prim (##cpxnum-imag x)
  (macro-cpxnum-imag x))

(define-prim (##cpxnum.= x y)
  (and (##= (macro-cpxnum-real x) (macro-cpxnum-real y))
       (##= (macro-cpxnum-imag x) (macro-cpxnum-imag y))))

(define-prim (##cpxnum.+ x y)
  (let ((a (macro-cpxnum-real x)) (b (macro-cpxnum-imag x))
        (c (macro-cpxnum-real y)) (d (macro-cpxnum-imag y)))
    (if (and (##flonum? a) (##flonum? b)
             (##flonum? c) (##flonum? d))
        (##make-rectangular (##fl+ a c) (##fl+ b d))
        (let ()
          (##declare (standard-bindings)
                     (mostly-fixnum))
          (##make-rectangular (+ a c) (+ b d))))))

(define-prim (##cpxnum.* x y)
  (let ((a (macro-cpxnum-real x)) (b (macro-cpxnum-imag x))
        (c (macro-cpxnum-real y)) (d (macro-cpxnum-imag y)))
    (if (and (##flonum? a) (##flonum? b)
             (##flonum? c) (##flonum? d))
        (##make-rectangular (##fl- (##fl* a c) (##fl* b d))
                            (##fl+ (##fl* a d) (##fl* b c)))
        (let ()
          (##declare (standard-bindings)
                     (mostly-fixnum))
          (##make-rectangular (- (* a c) (* b d))
                              (+ (* a d) (* b c)))))))
(define-prim (##cpxnum.- x y)
  (let ((a (macro-cpxnum-real x)) (b (macro-cpxnum-imag x))
        (c (macro-cpxnum-real y)) (d (macro-cpxnum-imag y)))
    (if (and (##flonum? a) (##flonum? b)
             (##flonum? c) (##flonum? d))
        (##make-rectangular (##fl- a c) (##fl- b d))
        (let ()
          (##declare (standard-bindings)
                     (mostly-fixnum))
          (##make-rectangular (- a c) (- b d))))))


(define-prim (##cpxnum./ x y)

  (define (basic/ a b c d q)
    (##make-rectangular (##/ (##+ (##* a c) (##* b d)) q)
                        (##/ (##- (##* b c) (##* a d)) q)))

  (let ((a (macro-cpxnum-real x)) (b (macro-cpxnum-imag x))
        (c (macro-cpxnum-real y)) (d (macro-cpxnum-imag y)))
    (cond ((##eqv? d 0)
           ;; A normalized cpxnum can't have an imaginary part that is
           ;; exact 0 but it is possible that ##cpxnum./ receives a
           ;; nonnormalized cpxnum as x or y when it is called from ##/.
           (##make-rectangular (##/ a c)
                               (##/ b c)))
          ((##eqv? c 0)
           (##make-rectangular (##/ b d)
                               (##negate (##/ a d))))
          ((and (##exact? c) (##exact? d))
           (basic/ a b c d (##+ (##* c c) (##* d d))))
          (else
           ;; just coerce everything to inexact and move on
           (let ((inexact-c (##exact->inexact c))
                 (inexact-d (##exact->inexact d)))
             (if (and (##flfinite? inexact-c)
                      (##flfinite? inexact-d))
                 (let ((q
                        (##fl+ (##fl* inexact-c inexact-c)
                               (##fl* inexact-d inexact-d))))
                   (cond ((##not (##flfinite? q))
                          (let ((a
                                 (if (##flonum? a)
                                     (##fl* a (macro-inexact-scale-down))
                                     (##* a (macro-scale-down))))
                                (b
                                 (if (##flonum? b)
                                     (##fl* b (macro-inexact-scale-down))
                                     (##* b (macro-scale-down))))
                                (inexact-c
                                 (##fl* inexact-c
                                        (macro-inexact-scale-down)))
                                (inexact-d
                                 (##fl* inexact-d
                                        (macro-inexact-scale-down))))
                            (basic/ a
                                    b
                                    inexact-c
                                    inexact-d
                                    (##fl+
                                     (##fl* inexact-c inexact-c)
                                     (##fl* inexact-d inexact-d)))))
                         ((##fl< q (macro-flonum-min-normal))
                          (let ((a
                                 (if (##flonum? a)
                                     (##fl* a (macro-inexact-scale-up))
                                     (##* a (macro-scale-up))))
                                (b
                                 (if (##flonum? b)
                                     (##fl* b (macro-inexact-scale-up))
                                     (##* b (macro-scale-up))))
                                (inexact-c
                                 (##fl* inexact-c
                                        (macro-inexact-scale-up)))
                                (inexact-d
                                 (##fl* inexact-d
                                        (macro-inexact-scale-up))))
                            (basic/ a
                                    b
                                    inexact-c
                                    inexact-d
                                    (##fl+
                                     (##fl* inexact-c inexact-c)
                                     (##fl* inexact-d inexact-d)))))
                         (else
                          (basic/ a b inexact-c inexact-d q))))
                 (cond ((##fl= inexact-c (macro-inexact-+inf))
                        (basic/ a
                                b
                                (macro-inexact-+0)
                                (if (##flnan? inexact-d)
                                    inexact-d
                                    (##flcopysign (macro-inexact-+0)
                                                  inexact-d))
                                (macro-inexact-+1)))
                       ((##fl= inexact-c (macro-inexact--inf))
                        (basic/ a
                                b
                                (macro-inexact--0)
                                (if (##flnan? inexact-d)
                                    inexact-d
                                    (##flcopysign (macro-inexact-+0)
                                                  inexact-d))
                                (macro-inexact-+1)))
                       ((##flnan? inexact-c)
                        (cond ((##fl= inexact-d (macro-inexact-+inf))
                               (basic/ a
                                       b
                                       inexact-c
                                       (macro-inexact-+0)
                                       (macro-inexact-+1)))
                              ((##fl= inexact-d (macro-inexact--inf))
                               (basic/ a
                                       b
                                       inexact-c
                                       (macro-inexact--0)
                                       (macro-inexact-+1)))
                              ((##flnan? inexact-d)
                               (basic/ a
                                       b
                                       inexact-c
                                       inexact-d
                                       (macro-inexact-+1)))
                              (else
                               (basic/ a
                                       b
                                       inexact-c
                                       (macro-inexact-+nan)
                                       (macro-inexact-+1)))))
                       (else
                        ;; finite inexact-c
                        (cond ((##flnan? inexact-d)
                               (basic/ a
                                       b
                                       (macro-inexact-+nan)
                                       inexact-d
                                       (macro-inexact-+1)))
                              (else
                               ;; inexact-d is +inf.0 or -inf.0
                               (basic/ a
                                       b
                                       (##flcopysign (macro-inexact-+0)
                                                     inexact-c)
                                       (##flcopysign (macro-inexact-+0)
                                                     inexact-d)
                                       (macro-inexact-+1))))))))))))

))

;;;----------------------------------------------------------------------------

;;; Pseudo-random number generation, compatible with srfi-27.

;;; This code is based on Pierre Lecuyer's MRG32K3A generator.

(define-type random-source
  id: 1b002758-f900-4e96-be5e-fa407e331fc0
  implementer: implement-type-random-source
  constructor: macro-make-random-source
  type-exhibitor: macro-type-random-source
  macros:
  prefix: macro-
  opaque:

  (state-ref         unprintable: read-only:)
  (state-set!        unprintable: read-only:)
  (randomize!        unprintable: read-only:)
  (pseudo-randomize! unprintable: read-only:)
  (make-integers     unprintable: read-only:)
  (make-reals        unprintable: read-only:)
  (make-u8vectors    unprintable: read-only:)
  (make-f64vectors   unprintable: read-only:)
  )

(define-check-type random-source
  (macro-type-random-source)
  macro-random-source?)

(implement-type-random-source)
(implement-check-type-random-source)

(define-prim (##make-random-source-mrg32k3a)

  (##define-macro (macro-w)
    65536)

  (##define-macro (macro-w^2-mod-m1)
    209)

  (##define-macro (macro-w^2-mod-m2)
    22853)

  (##define-macro (macro-m1)
    4294967087) ;; (- (expt (macro-w) 2) (macro-w^2-mod-m1))

  (##define-macro (macro-m1-inexact)
    4294967087.0) ;; (exact->inexact (macro-m1))

  (##define-macro (macro-m1-plus-1-inexact)
    4294967088.0) ;; (exact->inexact (+ (macro-m1) 1))

  (##define-macro (macro-inv-m1-plus-1-inexact)
    2.328306549295728e-10) ;; (exact->inexact (/ (+ (macro-m1) 1)))

  (##define-macro (macro-m1-minus-1)
    4294967086) ;; (- (macro-m1) 1)

  (##define-macro (macro-k)
    28)

  (##define-macro (macro-2^k)
    268435456) ;; (expt 2 (macro-k))

  (##define-macro (macro-2^k-inexact)
    268435456.0) ;; (exact->inexact (expt 2 (macro-k)))

  (##define-macro (macro-inv-2^k-inexact)
    3.725290298461914e-9) ;; (exact->inexact (/ (expt 2 (macro-k))))

  (##define-macro (macro-2^53-k-inexact)
    33554432.0) ;; (exact->inexact (expt 2 (- 53 (macro-k))))

  (##define-macro (macro-m1-div-2^k-inexact)
    15.0) ;; (exact->inexact (quotient (macro-m1) (expt 2 (macro-k))))

  (##define-macro (macro-m1-div-2^k-times-2^k-inexact)
    4026531840.0) ;; (exact->inexact (* (quotient (macro-m1) (expt 2 (macro-k))) (expt 2 (macro-k))))

  (##define-macro (macro-m2)
    4294944443) ;; (- (expt (macro-w) 2) (macro-w^2-mod-m2))

  (##define-macro (macro-m2-inexact)
    4294944443.0) ;; (exact->inexact (macro-m2))

  (##define-macro (macro-m2-minus-1)
    4294944442) ;; (- (macro-m2) 1)

  (define (pack-state a b c d e f)
    (##f64vector
     (##exact-int->flonum a)
     (##exact-int->flonum b)
     (##exact-int->flonum c)
     (##exact-int->flonum d)
     (##exact-int->flonum e)
     (##exact-int->flonum f)
     (macro-inexact-+0) ;; where the result of advance-state! is put
     (macro-inexact-+0) ;; q in rand-fixnum32
     (macro-inexact-+0) ;; qn in rand-fixnum32
     ))

  (define (unpack-state state)
    (##vector
     (##flonum->exact-int (##f64vector-ref state 0))
     (##flonum->exact-int (##f64vector-ref state 1))
     (##flonum->exact-int (##f64vector-ref state 2))
     (##flonum->exact-int (##f64vector-ref state 3))
     (##flonum->exact-int (##f64vector-ref state 4))
     (##flonum->exact-int (##f64vector-ref state 5))))

  (let ((state ;; initial state is 0 3 6 9 12 15 of A^(2^4), see below
         (pack-state
          1062452522
          2961816100
          342112271
          2854655037
          3321940838
          3542344109)))

    (define (state-ref)
      (unpack-state state))

    (define (state-set! rs new-state)

      (define (integer-in-range? x m)
        (and (macro-exact-int? x)
             (##not (##negative? x))
             (##< x m)))

      (or (and (##vector? new-state)
               (##fx= (##vector-length new-state) 6)
               (let ((a (##vector-ref new-state 0))
                     (b (##vector-ref new-state 1))
                     (c (##vector-ref new-state 2))
                     (d (##vector-ref new-state 3))
                     (e (##vector-ref new-state 4))
                     (f (##vector-ref new-state 5)))
                 (and (integer-in-range? a (macro-m1))
                      (integer-in-range? b (macro-m1))
                      (integer-in-range? c (macro-m1))
                      (integer-in-range? d (macro-m2))
                      (integer-in-range? e (macro-m2))
                      (integer-in-range? f (macro-m2))
                      (not (and (eqv? a 0) (eqv? b 0) (eqv? c 0)))
                      (not (and (eqv? d 0) (eqv? e 0) (eqv? f 0)))
                      (begin
                        (set! state
                              (pack-state a b c d e f))
                        (void)))))
          (##raise-type-exception
           2
           'random-source-state
           random-source-state-set!
           (list rs new-state))))

    (define (randomize!)

      (define (random-fixnum-from-time)
        (let ((v (##f64vector (macro-inexact-+0))))
          (##get-current-time! v 0)
          (let ((x (##f64vector-ref v 0)))
            (##flonum->fixnum
             (##fl* 536870912.0 ;; (expt 2.0 29)
                    (##fl- x (##flfloor x)))))))

      (define seed16
        (random-fixnum-from-time))

      (define (simple-random16)
        (let ((r (##bitwise-and seed16 65535)))
          (set! seed16
                (##+ (##* 30903 r)
                     (##arithmetic-shift seed16 -16)))
          r))

      (define (simple-random32)
        (##+ (##arithmetic-shift (simple-random16) 16)
             (simple-random16)))

      ;; perturb the state randomly

      (let ((s (unpack-state state)))
        (set! state
              (pack-state
               (##+ 1
                    (##modulo (##+ (##vector-ref s 0)
                                   (simple-random32))
                              (macro-m1-minus-1)))
               (##modulo (##+ (##vector-ref s 1)
                              (simple-random32))
                         (macro-m1))
               (##modulo (##+ (##vector-ref s 2)
                              (simple-random32))
                         (macro-m1))
               (##+ 1
                    (##modulo (##+ (##vector-ref s 3)
                                   (simple-random32))
                              (macro-m2-minus-1)))
               (##modulo (##+ (##vector-ref s 4)
                              (simple-random32))
                         (macro-m2))
               (##modulo (##+ (##vector-ref s 5)
                              (simple-random32))
                         (macro-m2))))
        (void)))

    (define (pseudo-randomize! i j)

      (define (mult A B) ;; A*B

        (define (lc i0 i1 i2 j0 j1 j2 m)
          (##modulo (##+ (##* (##vector-ref A i0)
                              (##vector-ref B j0))
                         (##+ (##* (##vector-ref A i1)
                                   (##vector-ref B j1))
                              (##* (##vector-ref A i2)
                                   (##vector-ref B j2))))
                    m))

        (##vector
         (lc  0  1  2   0  3  6  (macro-m1))
         (lc  0  1  2   1  4  7  (macro-m1))
         (lc  0  1  2   2  5  8  (macro-m1))
         (lc  3  4  5   0  3  6  (macro-m1))
         (lc  3  4  5   1  4  7  (macro-m1))
         (lc  3  4  5   2  5  8  (macro-m1))
         (lc  6  7  8   0  3  6  (macro-m1))
         (lc  6  7  8   1  4  7  (macro-m1))
         (lc  6  7  8   2  5  8  (macro-m1))
         (lc  9 10 11   9 12 15  (macro-m2))
         (lc  9 10 11  10 13 16  (macro-m2))
         (lc  9 10 11  11 14 17  (macro-m2))
         (lc 12 13 14   9 12 15  (macro-m2))
         (lc 12 13 14  10 13 16  (macro-m2))
         (lc 12 13 14  11 14 17  (macro-m2))
         (lc 15 16 17   9 12 15  (macro-m2))
         (lc 15 16 17  10 13 16  (macro-m2))
         (lc 15 16 17  11 14 17  (macro-m2))))

      (define (power A e) ;; A^e
        (cond ((##eq? e 0)
               identity)
              ((##eq? e 1)
               A)
              ((##even? e)
               (power (mult A A) (##arithmetic-shift e -1)))
              (else
               (mult (power A (##- e 1)) A))))

      (define identity
        '#(         1           0           0
                    0           1           0
                    0           0           1
                    1           0           0
                    0           1           0
                    0           0           1))

      (define A ;; primary MRG32k3a equations
        '#(         0     1403580  4294156359
                    1           0           0
                    0           1           0
               527612           0  4293573854
                    1           0           0
                    0           1           0))

      (define A^2^127 ;; A^(2^127)
        '#(1230515664   986791581  1988835001
           3580155704  1230515664   226153695
            949770784  3580155704  2427906178
           2093834863    32183930  2824425944
           1022607788  1464411153    32183930
           1610723613   277697599  1464411153))

      (define A^2^76 ;; A^(2^76)
        '#(  69195019  3528743235  3672091415
           1871391091    69195019  3672831523
           4127413238  1871391091    82758667
           3708466080  4292754251  3859662829
           3889917532  1511326704  4292754251
           1610795712  3759209742  1511326704))

      (define A^2^4 ;; A^(2^4)
        '#(1062452522   340793741  2955879160
           2961816100  1062452522   387300998
            342112271  2961816100   736416029
           2854655037  1817134745  3493477402
           3321940838   818368950  1817134745
           3542344109  3790774567   818368950))

      (let ((M ;; M = A^(2^4 + i*2^127 + j*2^76)
             (mult A^2^4
                   (mult (power A^2^127 i)
                         (power A^2^76 j)))))
        (set! state
              (pack-state
               (##vector-ref M 0)
               (##vector-ref M 3)
               (##vector-ref M 6)
               (##vector-ref M 9)
               (##vector-ref M 12)
               (##vector-ref M 15)))
        (void)))

    (define (advance-state!)
      (##declare (not interrupts-enabled))
      (let* ((state state)
             (x10
              (##fl- (##fl* 1403580.0 (##f64vector-ref state 1))
                     (##fl*  810728.0 (##f64vector-ref state 2))))
             (y10
              (##fl- x10
                     (##fl* (##flfloor (##fl/ x10 (macro-m1-inexact)))
                            (macro-m1-inexact))))
             (x20
              (##fl- (##fl*  527612.0 (##f64vector-ref state 3))
                     (##fl* 1370589.0 (##f64vector-ref state 5))))
             (y20
              (##fl- x20
                     (##fl* (##flfloor (##fl/ x20 (macro-m2-inexact)))
                            (macro-m2-inexact)))))
        (##f64vector-set! state 5 (##f64vector-ref state 4))
        (##f64vector-set! state 4 (##f64vector-ref state 3))
        (##f64vector-set! state 3 y20)
        (##f64vector-set! state 2 (##f64vector-ref state 1))
        (##f64vector-set! state 1 (##f64vector-ref state 0))
        (##f64vector-set! state 0 y10)
        (if (##fl< y10 y20)
            (##f64vector-set! state 6 (##fl+ (macro-m1-inexact)
                                             (##fl- (##f64vector-ref state 0)
                                                    (##f64vector-ref state 3))))
            (##f64vector-set! state 6 (##fl- (##f64vector-ref state 0)
                                             (##f64vector-ref state 3))))))

    (define (make-integers)

      (define (random-integer range)

        (define (type-error)
          (##fail-check-exact-integer 1 random-integer range))

        (define (range-error)
          (##raise-range-exception 1 random-integer range))

        (macro-force-vars (range)
          (macro-exact-int-dispatch range (type-error)
            (if (##fxpositive? range)
                (if (##fx< (macro-max-fixnum32) range) ;;TODO: check if this should be fx<=
                    (rand-integer range)
                    (rand-fixnum32 range))
                (range-error))
            (if (##bignum.negative? range)
                (range-error)
                (rand-integer range)))))

      random-integer)

    (define (rand-integer range)

      ;; constants for computing fixnum approximation of inverse of range

      (define size 14)
      (define 2^2*size 268435456)

      (let ((len (integer-length range)))
        (if (##fx= (##fx- len 1) ;; check if range is a power of 2
                   (##first-bit-set range))
            (rand-integer-2^ (##fx- len 1))
            (let* ((inv
                    (##fxquotient
                     2^2*size
                     (##fx+ 1
                            (##extract-bit-field size (##fx- len size) range))))
                   (range2
                    (##* range inv)))
              (let loop ()
                (let ((r (rand-integer-2^ (##fx+ len size))))
                  (if (##< r range2)
                      (##quotient r inv)
                      (loop))))))))

    (define (rand-integer-2^ w)

      (define (rand w s)
        (cond ((##fx< w (macro-k))
               (##fxand (rand-fixnum32-2^k)
                        (##fx- (##fxarithmetic-shift-left 1 w) 1)))
              ((##fx= w (macro-k))
               (rand-fixnum32-2^k))
              (else
               (let ((s/2 (##fxarithmetic-shift-right s 1)))
                 (if (##fx< s w)
                     (##+ (rand s s/2)
                          (##arithmetic-shift (rand (##fx- w s) s/2) s))
                     (rand w s/2))))))

      (define (split w s)
        (let ((s*2 (##fx* 2 s)))
          (if (##fx< s*2 w)
              (split w s*2)
              s)))

      (rand w (split w (macro-k))))

    (define (rand-fixnum32-2^k)
      (##declare (not interrupts-enabled))
      (let loop ()
        (advance-state!)
        (if (##fl< (##f64vector-ref state 6)
                   (macro-m1-div-2^k-times-2^k-inexact))
            (##flonum->fixnum
             (##fl/ (##f64vector-ref state 6)
                    (macro-m1-div-2^k-inexact)))
            (loop))))

    (define (rand-fixnum32 range) ;; range is a fixnum32
      (##declare (not interrupts-enabled))
      (let* ((a (##fixnum->flonum range))
             (b (##flfloor (##fl/ (macro-m1-inexact) a))))
        (##f64vector-set! state 7 b)
        (##f64vector-set! state 8 (##fl* a b)))
      (let loop ()
        (advance-state!)
        (if (##fl< (##f64vector-ref state 6)
                   (##f64vector-ref state 8))
            (##flonum->fixnum
             (##fl/ (##f64vector-ref state 6)
                    (##f64vector-ref state 7)))
            (loop))))

    (define (make-reals precision)
      (if (##fl< precision (macro-inv-m1-plus-1-inexact))
          (lambda ()
            (let loop ((r (##fixnum->flonum (rand-fixnum32-2^k)))
                       (d (macro-inv-2^k-inexact)))
              (if (##fl< r (macro-flonum-+m-max-plus-1-inexact))
                  (loop (##fl+ (##fl* r (macro-2^k-inexact))
                               (##fixnum->flonum (rand-fixnum32-2^k)))
                        (##fl* d (macro-inv-2^k-inexact)))
                  (##fl* r d))))
          (lambda ()
            (##declare (not interrupts-enabled))
            (advance-state!)
            (##fl* (##fl+ (macro-inexact-+1) (##f64vector-ref state 6))
                   (macro-inv-m1-plus-1-inexact)))))

    (define (make-u8vectors)
      (lambda (len)
        (macro-force-vars (len)
          (macro-check-index len 1 (random-u8vector len)
            (let ((u8vect (##make-u8vector len 0)))
              (let loop ((i (##fx- len 1)))
                (if (##fx< i 0)
                    u8vect
                    (begin
                      (##u8vector-set! u8vect i (rand-fixnum32 256))
                      (loop (##fx- i 1))))))))))

    (define (make-f64vectors precision)
      (if (##fl< precision (macro-inv-m1-plus-1-inexact))
          (let ((make-real (make-reals precision)))
            (lambda (len)
              (macro-force-vars (len)
                (macro-check-index len 1 (random-f64vector len)
                  (let ((f64vect (##make-f64vector len (macro-inexact-+0))))
                    (let loop ((i (##fx- len 1)))
                      (if (##fx< i 0)
                          f64vect
                          (begin
                            (##f64vector-set! f64vect i (make-real))
                            (loop (##fx- i 1))))))))))
          (lambda (len)
            (macro-force-vars (len)
              (macro-check-index len 1 (random-f64vector len)
                (let ((f64vect (##make-f64vector len (macro-inexact-+0))))
                  (let loop ((i (##fx- len 1)))
                    (if (##fx< i 0)
                        f64vect
                        (let ()
                          (##declare (not interrupts-enabled))
                          (advance-state!)
                          (##f64vector-set! f64vect i (##fl* (##fl+ (macro-inexact-+1)
                                                                (##f64vector-ref state 6))
                                                           (macro-inv-m1-plus-1-inexact)))
                          (loop (##fx- i 1)))))))))))

    (macro-make-random-source
     state-ref
     state-set!
     randomize!
     pseudo-randomize!
     make-integers
     make-reals
     make-u8vectors
     make-f64vectors)))

(define-prim (make-random-source)
  (##make-random-source-mrg32k3a))

(define-prim (random-source? obj)
  (macro-force-vars (obj)
    (macro-random-source? obj)))

(define-prim (##random-source-state-ref rs)
  ((macro-random-source-state-ref rs)))

(define-prim (random-source-state-ref rs)
  (macro-force-vars (rs)
    (macro-check-random-source rs 1 (random-source-state-ref rs)
      (##random-source-state-ref rs))))

(define-prim (##random-source-state-set! rs new-state)
  ((macro-random-source-state-set! rs) rs new-state))

(define-prim (random-source-state-set! rs new-state)
  (macro-force-vars (rs new-state)
    (macro-check-random-source rs 1 (random-source-state-set! rs new-state)
      (##random-source-state-set! rs new-state))))

(define-prim (##random-source-randomize! rs)
  ((macro-random-source-randomize! rs)))

(define-prim (random-source-randomize! rs)
  (macro-force-vars (rs)
    (macro-check-random-source rs 1 (random-source-randomize! rs)
      (##random-source-randomize! rs))))

(define-prim (##random-source-pseudo-randomize! rs i j)
  ((macro-random-source-pseudo-randomize! rs) i j))

(define-prim (random-source-pseudo-randomize! rs i j)
  (macro-force-vars (rs i j)
    (macro-check-random-source rs 1 (random-source-pseudo-randomize! rs i j)
      (if (not (macro-exact-int? i))
          (##fail-check-exact-integer 2 random-source-pseudo-randomize! rs i j)
          (if (not (macro-exact-int? j))
              (##fail-check-exact-integer 3 random-source-pseudo-randomize! rs i j)
              (if (##negative? i)
                  (##raise-range-exception 2 random-source-pseudo-randomize! rs i j)
                  (if (##negative? j)
                      (##raise-range-exception 3 random-source-pseudo-randomize! rs i j)
                      (##random-source-pseudo-randomize! rs i j))))))))

(define-prim (##random-source-make-integers rs)
  ((macro-random-source-make-integers rs)))

(define-prim (random-source-make-integers rs)
  (macro-force-vars (rs)
    (macro-check-random-source rs 1 (random-source-make-integers rs)
      (##random-source-make-integers rs))))

(define-prim (##random-source-make-reals rs #!optional (p (macro-absent-obj)))
  ((macro-random-source-make-reals rs)
   (if (eq? p (macro-absent-obj))
       (macro-inexact-+1)
       p)))

(define-prim (random-source-make-reals rs #!optional (p (macro-absent-obj)))
  (macro-force-vars (rs p)
    (macro-check-random-source rs 1 (random-source-make-reals rs p)
      (if (eq? p (macro-absent-obj))
          (##random-source-make-reals rs)
          (if (rational? p)
              (let ((precision (macro-real->inexact p)))
                (if (and (##fl< (macro-inexact-+0) precision)
                         (##fl< precision (macro-inexact-+1)))
                    (##random-source-make-reals rs precision)
                    (##raise-range-exception 2 random-source-make-reals rs p)))
              (##fail-check-finite-real 2 random-source-make-reals rs p))))))

(define-prim (##random-source-make-f64vectors rs #!optional (p (macro-absent-obj)))
  ((macro-random-source-make-f64vectors rs)
   (if (eq? p (macro-absent-obj))
       (macro-inexact-+1)
       p)))

(define-prim (random-source-make-f64vectors rs #!optional (p (macro-absent-obj)))
  (macro-force-vars (rs p)
    (macro-check-random-source rs 1 (random-source-make-f64vectors rs p)
      (if (eq? p (macro-absent-obj))
          (##random-source-make-f64vectors rs)
          (if (rational? p)
              (let ((precision (macro-real->inexact p)))
                (if (and (##fl< (macro-inexact-+0) precision)
                         (##fl< precision (macro-inexact-+1)))
                    (##random-source-make-f64vectors rs precision)
                    (##raise-range-exception 2 random-source-make-f64vectors rs p)))
              (##fail-check-finite-real 2 random-source-make-f64vectors rs p))))))

(define-prim (##random-source-make-u8vectors rs)
  ((macro-random-source-make-u8vectors rs)))

(define-prim (random-source-make-u8vectors rs)
  (macro-force-vars (rs)
    (macro-check-random-source rs 1 (random-source-make-u8vectors rs)
      (##random-source-make-u8vectors rs))))

(define ##default-random-source (##make-random-source-mrg32k3a))

(define default-random-source ##default-random-source)

(define random-integer
  (##random-source-make-integers ##default-random-source))

(define random-real
  (##random-source-make-reals ##default-random-source))

(define random-u8vector
  (##random-source-make-u8vectors ##default-random-source))

(define random-f64vector
  (##random-source-make-f64vectors ##default-random-source))

;;;============================================================================
