(library (tests bignums)
  (export test-bignums test-bignum-conversion test-bitwise-bit-count 
          test-bignum-length)
  (import (ikarus) (tests framework))

  (define (test-bignum-conversion)
    (define (test x) 
      (define (test1 x prefix radix)
        (let ([s (string-append prefix 
                   (number->string x radix))])
          (assert (equal? x (read (open-string-input-port s))))))
      (test1 x "#x" 16)
      (test1 x "#o" 8)
      (test1 x "#b" 2))
    (test #b11111111111111111111111111111111111111111111111111)
    (test #b1111111111111111111111111111111111111111)
    (test 39487932748923498234)
    (test #b-11111111111111111111111111111111111111111111111111)
    (test #b-1111111111111111111111111111111111111111)
    (test -39487932748923498234))

  (define (test-bignum-length)
    (define (ref ei)
      (do ((result 0 (+ result 1)) 
           (bits (if (negative? ei) 
                     (bitwise-not ei) 
                   ei) 
                 (bitwise-arithmetic-shift bits -1))) 
        ((zero? bits)
         result)))
    (define (test n) 
      (let ([n0 (bitwise-length n)]
            [n1 (ref n)])
        (unless (= n0 n1)
          (error 'test-bignum-length "mismatch" 
                 (format "#x~x" n) n0 n1))))
    (test #xF)
    (test #xFF)
    (test #xFFF)
    (test #xFFFF)
    (test #xFFFFF)
    (test #xFFFFFF)
    (test #xFFFFFFF)
    (test #xFFFFFFFF)
    (test #xFFFFFFFFF)
    (test #xFFFFFFFFFF)
    (test #xFFFFFFFFFFF)
    (test #xFFFFFFFFFFFF)
    (test #xFFFFFFFFFFFFF)
    (test #xFFFFFFFFFFFFFF)
    (test #xFFFFFFFFFFFFFFF)
    (test #xFFFFFFFFFFFFFFFF)
    (test #x-F)
    (test #x-FF)
    (test #x-FFF)
    (test #x-FFFF)
    (test #x-FFFFF)
    (test #x-FFFFFF)
    (test #x-FFFFFFF)
    (test #x-FFFFFFFF)
    (test #x-FFFFFFFFF)
    (test #x-FFFFFFFFFF)
    (test #x-FFFFFFFFFFF)
    (test #x-FFFFFFFFFFFF)
    (test #x-FFFFFFFFFFFFF)
    (test #x-FFFFFFFFFFFFFF)
    (test #x-FFFFFFFFFFFFFFF)
    (test #x-FFFFFFFFFFFFFFFF)

    (test #xE)
    (test #xFE)
    (test #xFFE)
    (test #xFFFE)
    (test #xFFFFE)
    (test #xFFFFFE)
    (test #xFFFFFFE)
    (test #xFFFFFFFE)
    (test #xFFFFFFFFE)
    (test #xFFFFFFFFFE)
    (test #xFFFFFFFFFFE)
    (test #xFFFFFFFFFFFE)
    (test #xFFFFFFFFFFFFE)
    (test #xFFFFFFFFFFFFFE)
    (test #xFFFFFFFFFFFFFFE)
    (test #xFFFFFFFFFFFFFFFE)
    (test #x-E)
    (test #x-FE)
    (test #x-FFE)
    (test #x-FFFE)
    (test #x-FFFFE)
    (test #x-FFFFFE)
    (test #x-FFFFFFE)
    (test #x-FFFFFFFE)
    (test #x-FFFFFFFFE)
    (test #x-FFFFFFFFFE)
    (test #x-FFFFFFFFFFE)
    (test #x-FFFFFFFFFFFE)
    (test #x-FFFFFFFFFFFFE)
    (test #x-FFFFFFFFFFFFFE)
    (test #x-FFFFFFFFFFFFFFE)
    (test #x-FFFFFFFFFFFFFFFE)

    (test #x1)
    (test #x1F)
    (test #x1FF)
    (test #x1FFF)
    (test #x1FFFF)
    (test #x1FFFFF)
    (test #x1FFFFFF)
    (test #x1FFFFFFF)
    (test #x1FFFFFFFF)
    (test #x1FFFFFFFFF)
    (test #x1FFFFFFFFFF)
    (test #x1FFFFFFFFFFF)
    (test #x1FFFFFFFFFFFF)
    (test #x1FFFFFFFFFFFFF)
    (test #x1FFFFFFFFFFFFFF)
    (test #x1FFFFFFFFFFFFFFF)
    (test #x-1)
    (test #x-1F)
    (test #x-1FF)
    (test #x-1FFF)
    (test #x-1FFFF)
    (test #x-1FFFFF)
    (test #x-1FFFFFF)
    (test #x-1FFFFFFF)
    (test #x-1FFFFFFFF)
    (test #x-1FFFFFFFFF)
    (test #x-1FFFFFFFFFF)
    (test #x-1FFFFFFFFFFF)
    (test #x-1FFFFFFFFFFFF)
    (test #x-1FFFFFFFFFFFFF)
    (test #x-1FFFFFFFFFFFFFF)
    (test #x-1FFFFFFFFFFFFFFF)

    (test #x1)
    (test #x10)
    (test #x100)
    (test #x1000)
    (test #x10000)
    (test #x100000)
    (test #x1000000)
    (test #x10000000)
    (test #x100000000)
    (test #x1000000000)
    (test #x10000000000)
    (test #x100000000000)
    (test #x1000000000000)
    (test #x10000000000000)
    (test #x100000000000000)
    (test #x1000000000000000)
    (test #x-1)
    (test #x-10)
    (test #x-100)
    (test #x-1000)
    (test #x-10000)
    (test #x-100000)
    (test #x-1000000)
    (test #x-10000000)
    (test #x-100000000)
    (test #x-1000000000)
    (test #x-10000000000)
    (test #x-100000000000)
    (test #x-1000000000000)
    (test #x-10000000000000)
    (test #x-100000000000000)
    (test #x-1000000000000000)

    (test #x1)
    (test #x11)
    (test #x101)
    (test #x1001)
    (test #x10001)
    (test #x100001)
    (test #x1000001)
    (test #x10000001)
    (test #x100000001)
    (test #x1000000001)
    (test #x10000000001)
    (test #x100000000001)
    (test #x1000000000001)
    (test #x10000000000001)
    (test #x100000000000001)
    (test #x1000000000000001)
    (test #x-1)
    (test #x-11)
    (test #x-101)
    (test #x-1001)
    (test #x-10001)
    (test #x-100001)
    (test #x-1000001)
    (test #x-10000001)
    (test #x-100000001)
    (test #x-1000000001)
    (test #x-10000000001)
    (test #x-100000000001)
    (test #x-1000000000001)
    (test #x-10000000000001)
    (test #x-100000000000001)
    (test #x-1000000000000001))






  (define (test-bitwise-bit-count)
    (define (test n)
      (define (pos-count-bits n)
        (if (zero? n) 
            0
            (let ([c (count-bits (bitwise-arithmetic-shift-right n 1))])
              (if (even? n) c (+ c 1)))))
      (define (count-bits n)
        (if (>= n 0)
            (pos-count-bits n)
            (bitwise-not (pos-count-bits (bitwise-not n)))))
      (let ([bc0 (bitwise-bit-count n)]
            [bc1 (count-bits n)])
        (unless (= bc0 bc1)
          (error 'test-bitcount "failed/expected/got" n bc1 bc0))))
    (define (test-fx n)
      (when (fixnum? n) 
        (when (zero? (fxlogand n #x7FFFFFF))
          (printf "bitwise-bit-count ~s\n" n))
        (test n)
        (test-fx (+ n 512))))
    (test-fx (least-fixnum))
    (test 28472347823493290482390849023840928390482309480923840923840983)
    (test -847234234903290482390849023840928390482309480923840923840983))

  (define-tests test-bignums
    ; first, some simple quotients
    [(lambda (x) (= x 101))  (quotient 348972 3434)]
    [(lambda (x) (= x -101)) (quotient -348972 3434)]
    [(lambda (x) (= x -101)) (quotient 348972 -3434)]
    [(lambda (x) (= x 101))  (quotient -348972 -3434)]
    ; then bump first argument to a small bignum:
    [(lambda (x) (= x 2255760))  (quotient 536870912 238)]
    [(lambda (x) (= x -2255760)) (quotient -536870912 238)]
    [(lambda (x) (= x -2255760)) (quotient 536870912 -238)]
    [(lambda (x) (= x 2255760))  (quotient -536870912 -238)]
    ; then bump first argument to a big bignum:
    [(lambda (x) (= x 1652556267336712615))
     (quotient 536870912238479837489374 324873)]
    [(lambda (x) (= x -1652556267336712615))
     (quotient -536870912238479837489374 324873)]
    [(lambda (x) (= x -1652556267336712615))
     (quotient 536870912238479837489374 -324873)]
    [(lambda (x) (= x 1652556267336712615))
     (quotient -536870912238479837489374 -324873)]
    ; then make both arguments bignums, but result fixnum:
    [(lambda (x) (= x 165)) 
     (quotient 536870912238479837489374 3248732398479823749283)]
    [(lambda (x) (= x -165)) 
     (quotient -536870912238479837489374 3248732398479823749283)]
    [(lambda (x) (= x -165)) 
     (quotient 536870912238479837489374 -3248732398479823749283)]
    [(lambda (x) (= x 165)) 
     (quotient -536870912238479837489374 -3248732398479823749283)]
    ; then both arguments and result are all bignums:
    [(lambda (x) (= x 1652555047284588078))
     (quotient 5368709122384798374893743894798327498234 3248732398479823749283)]
    [(lambda (x) (= x -1652555047284588078))
     (quotient -5368709122384798374893743894798327498234 3248732398479823749283)]
    [(lambda (x) (= x -1652555047284588078))
     (quotient 5368709122384798374893743894798327498234 -3248732398479823749283)]
    [(lambda (x) (= x 1652555047284588078))
     (quotient -5368709122384798374893743894798327498234 -3248732398479823749283)]




    [(lambda (x) (= x 23))            (remainder 23 349839489348)]
    [(lambda (x) (= x -23))           (remainder -23 349839489348)]
    [(lambda (x) (= x 23))            (remainder 23 -349839489348)]
    [(lambda (x) (= x -23))           (remainder -23 -349839489348)]
    

    ;;; Next, modulo
    ; first, some simple arguments
    [(lambda (x) (= x 2138))  (modulo 348972 3434)]
    [(lambda (x) (= x 1296))  (modulo -348972 3434)]
    [(lambda (x) (= x -1296)) (modulo 348972 -3434)]
    [(lambda (x) (= x -2138)) (modulo -348972 -3434)]
    ; then bignum second argument: can be done with +/-
    [(lambda (x) (= x 349839489325))  (modulo -23 349839489348)]
    [(lambda (x) (= x -23))           (modulo -23 -349839489348)]
    [(lambda (x) (= x 23))            (modulo 23 349839489348)]
    [(lambda (x) (= x -349839489325)) (modulo 23 -349839489348)]

    ; then bump first argument to a small bignum:
    [(lambda (x) (= x 32))  (remainder 536870912 238)]
    [(lambda (x) (= x -32)) (remainder -536870912 238)]
    [(lambda (x) (= x 32))  (remainder 536870912 -238)]
    [(lambda (x) (= x -32)) (remainder -536870912 -238)]

    [(lambda (x) (= x 32))   (modulo 536870912 238)]
    [(lambda (x) (= x 206))  (modulo -536870912 238)]
    [(lambda (x) (= x -206)) (modulo 536870912 -238)]
    [(lambda (x) (= x -32))  (modulo -536870912 -238)]
    ; then bump first argument to a big bignum:
    [(lambda (x) (= x 116479))
     (modulo 536870912238479837489374 324873)]
    [(lambda (x) (= x 208394))
     (modulo -536870912238479837489374 324873)]
    [(lambda (x) (= x -208394))
     (modulo 536870912238479837489374 -324873)]
    [(lambda (x) (= x -116479))
     (modulo -536870912238479837489374 -324873)]
    ; then make both arguments bignums
    [(lambda (x) (= x 830066489308918857679)) 
     (modulo 536870912238479837489374 3248732398479823749283)]
    [(lambda (x) (= x -2418665909170904891604)) 
     (modulo 536870912238479837489374 -3248732398479823749283)]
    [(lambda (x) (= x 2418665909170904891604)) 
     (modulo -536870912238479837489374 3248732398479823749283)]
    [(lambda (x) (= x -830066489308918857679))
     (modulo -536870912238479837489374 -3248732398479823749283)]




    [(lambda (x) (= x -13)) (bitwise-not 12)]
    [(lambda (x) (= x 11)) (bitwise-not -12)]
    [(lambda (x) (= x 0)) (bitwise-not -1)]
    [(lambda (x) (= x -1)) (bitwise-not 0)]
    [(lambda (x) (= x (least-fixnum))) (bitwise-not (greatest-fixnum))]
    [(lambda (x) (= x (greatest-fixnum))) (bitwise-not (least-fixnum))]

    [(lambda (x) (= x -38947389478348937489375)) 
     (bitwise-not 38947389478348937489374)]
    [(lambda (x) (= x -22300745198530623141535718272648361505980416))
     (bitwise-not #xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)]
    [(lambda (x) (= x 38947389478348937489374)) 
     (bitwise-not -38947389478348937489375)]
    [(lambda (x) (= x 22300745198530623141535718272648361505980414))
     (bitwise-not #x-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)]
    [(lambda (x) (= x -340282366920938463463374607431768211456))
     (bitwise-not #xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)]
    [(lambda (x) (= x 340282366920938463463374607431768211454))
     (bitwise-not #x-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)]
    [(lambda (x) (= x -79228162514264337593543950337))
     (bitwise-not #x1000000000000000000000000)]
    [(lambda (x) (= x 79228162514264337593543950335))
     (bitwise-not #x-1000000000000000000000000)]


    ))