(export * + - / < <= = > >= abs ceiling denominator even? exact-integer-sqrt expt floor floor-quotient floor-remainder floor/ gcd integer? lcm max min modulo negative? number? numerator odd? positive? quotient rational? remainder round square truncate truncate-quotient truncate-remainder truncate/ zero?) (import (only (csc builtins) call-builtin)) (begin ;; Integers (define-record-type (make-boxed-int positive? digits) boxed-int? (positive? boxed-int-positive?) (digits boxed-int-digits)) (define (small-int? obj) (call-builtin eq 1 (call-builtin typeof obj))) (define (integer? obj) (or (small-int? obj) (boxed-int? obj))) (define (int=? n1 n2) (cond ((and (small-int? n1) (small-int? n2)) (call-builtin eq n1 n2)) ((and (boxed-int? n1) (boxed-int? n2)) (let ((n1-digits (boxed-int-digits n1)) (n2-digits (boxed-int-digits n2))) (and (boolean=? (boxed-int-positive? n1) (boxed-int-positive? n2)) (call-builtin eq (vector-length n1-digits) (vector-length n2-digits)) (let loop ((i 0)) (if (call-builtin lt i (vector-length n1-digits)) (and (call-builtin eq (vector-ref n1-digits i) (vector-ref n2-digits i)) (loop (+ 1 i))) #t))))) (else #f))) (define (int-positive? n) (or (and (small-int? n) (call-builtin lt 0 n)) (and (boxed-int? n) (boxed-int-positive? n)))) (define (int-negative? n) (or (and (small-int? n) (call-builtin lt n 0)) (and (boxed-int? n) (not (boxed-int-positive? n))))) (define (int n2 (define (int>? n1 n2) (int=? n1 n2) (or (int=? n1 n2) (int>? n1 n2))) (define (zero? obj) (call-builtin eq 0 obj)) (define (odd? n) (cond ((small-int? n) (int=? 1 (call-builtin mod n 2))) (else (odd? (vector-ref (boxed-int-digits n) 0))))) (define (even? n) (cond ((small-int? n) (int=? 0 (call-builtin mod n 2))) (else (even? (vector-ref (boxed-int-digits n) 0))))) (define small-int-max #x3FFFFFFFFFFFFFFF) ; 2^62 - 1 (define small-int-min (- #x4000000000000000)) ; -(2^62) (define (digits n) (if (small-int? n) (vector n) (boxed-int-digits n))) ; adds two small positive ints. (define (add2 x y) (define z (call-builtin add x y)) (if (negative? z) (values 1 (call-builtin add 1 (call-builtin add z small-int-max))) (values 0 z))) (define (big-int+ x y) (cond ((and (int-negative? x) (int-negative? y)) (- (int+ (- x) (- y)))) ((int-negative? x) (int- y (- x))) ((int-negative? y) (int- x (- y))) (else (let* ((x-digits (digits x)) (y-digits (digits y)) (x-ndigits (vector-length x-digits)) (y-ndigits (vector-length y-digits)) (out-digits (make-vector (max x-ndigits y-ndigits)))) (let loop ((i 0) (carry 0)) (cond ((and (int=? i 0) (zero? (vector-ref digits i))) (loop (int- i 1) (int+ 1 n)) n))) (make-boxed-int (boxed-int-positive? n) (vector-copy digits 0 (int- (vector-length digits) leading-zeros)))) (define (normalize n) (set! n (remove-leading-zeros n)) (define digits (boxed-int-digits n)) (define len (vector-length digits)) (cond ((call-builtin eq 0 len) 0) ((call-builtin eq 1 len) (vector-ref digits 0)) (else n))) (define (big-int- n1 n2) (cond ((and (int-negative? n1) (int-negative? n2)) (- (big-int- (- n1) (- n2)))) ((and (int-positive? n1) (int-negative? n2)) (big-int+ n1 (- n2))) ((and (int-negative? n1) (int-positive? n2)) (- (big-int+ (- n1) n2))) ((int? lo hi) (error "binary search is hard")) ((negative? check) ; guess was too big (loop lo (- guess 1))) ((int (make-quotient numerator denominator) quotient? (numerator quotient-numerator) (denominator quotient-denominator)) (define (rational? obj) (or (integer? obj) (quotient? obj))) (define number? rational?) ; only rational numbers for now. (define (numerator q) (cond ((integer? q) q) (else (quotient-numerator q)))) (define (denominator q) (cond ((integer? q) 1) (else (quotient-denominator q)))) (define (= z1 z2 . zs) (cond ((integer? z1) (let loop ((zs (cons z2 zs))) (or (null? zs) (and (int=? z1 (car zs)) (loop (cdr zs)))))) (else (let ((n (numerator z1)) (d (denominator z1))) (let loop ((zs (cons z2 zs))) (or (null? zs) (and (int=? n (numerator (car zs))) (int=? d (denominator (car zs))) (loop (cdr zs))))))))) (define (<2 x1 x2) (if (and (integer? x1) (integer? x2)) (int (gcd d1 d2))) (int)) (* (numerator x2) (quotient d1 >)))))) (define < (case-lambda ((x1 x2) (<2 x1 x2)) ((x1 x2 . xs) (and (<2 x1 x2) (apply < x2 xs))))) (define (> x1 x2 . xs) (let loop ((x1 x1) (xs (cons x2 xs))) (or (null? xs) (and (<2 (car xs) x1) (loop (car xs) (cdr xs)))))) (define (<= x1 x2 . xs) (let loop ((x1 x1) (xs (cons x2 xs))) (or (null? xs) (and (or (= x1 (car xs)) (< x1 (car xs))) (loop (car xs) (cdr xs)))))) (define (>= x1 x2 . xs) (let loop ((x1 x1) (xs (cons x2 xs))) (or (null? xs) (and (or (= x1 (car xs)) (> x1 (car xs))) (loop (car xs) (cdr xs)))))) (define (positive? x) (> x 0)) (define (negative? x) (< x 0)) (define (max x1 . xs) (let loop ((xs xs) (m x1)) (if (null? xs) m (loop (cdr xs) (if (> (car xs) m) (car xs) m))))) (define (min x1 . xs) (let loop ((xs xs) (m x1)) (if (null? xs) m (loop (cdr xs) (if (< (car xs) m) (car xs) m))))) (define + (case-lambda ((x1 x2) (cond ((and (integer? x1) (integer? x2)) (int+ x1 x2)) (else (let* ((d1 (denominator x1)) (d2 (denominator x2)) (> (gcd d1 d2)) (s1 (quotient d2 >)) (s2 (quotient d1 >))) (/ (+ (* s1 (numerator x1)) (* s2 (numerator x2))) (* d1 s1)))))) (xs (let loop ((xs xs) (s 0)) (if (null? xs) s (loop (cdr xs) (+ s (car xs)))))))) (define * (case-lambda ((x1 x2) (cond ((and (integer? x1) (integer? x2)) (int* x1 x2)) (else (/ (* (numerator x1) (numerator x2)) (* (denominator x1) (denominator x2)))))) (xs (let loop ((xs xs) (p 1)) (if (null? xs) p (loop (cdr xs) (* p (car xs)))))))) (define - (case-lambda ((z) (cond ((int=? z small-int-min) #x4000000000000000) ((small-int? z) (call-builtin sub 0 z)) ((boxed-int? z) (make-boxed-int (not (positive? z)) (boxed-int-digits z))) (else (/ (- (numerator z)) (denominator z))))) ((z1 z2) (cond ((and (integer? z1) (integer? z2)) (int- z1 z2)) (else (+ z1 (- z2))))) ((z1 . zs) (let loop ((zs zs) (d z1)) (if (null? zs) d (loop (cdr zs) (- d (car zs)))))))) (define (normalize-quotient q) (when (negative? q) (set! q (make-quotient (- (numerator q)) (- (denominator q))))) (let* ((n (numerator q)) (d (denominator q)) (> (gcd n d))) (make-quotient (quotient n >) (quotient d >)))) (define / (case-lambda ((z) (/ 1 z)) ((z1 z2) (cond ((and (integer? z1) (integer? z2)) (when (negative? z2) (set! z1 (- z1)) (set! z2 (- z2))) (let ((g (gcd z1 z2))) (if (= g z2) (quotient z1 g) (make-quotient (quotient z1 g) (quotient z2 g))))) (else (* z1 (/ (denominator z2) (numerator z2)))))) ((z1 . zs) (let loop ((zs zs) (q z1)) (if (null? zs) q (loop (cdr zs) (/ q (car zs)))))))) (define (abs x) (if (negative? x) (- x) x)) (define (floor x) (if (integer? x) x (floor-quotient (numerator x) (denominator x)))) (define (ceiling x) (if (integer? x) x (+ 1 (floor x)))) (define (truncate x) (if (integer? x) x (truncate-quotient (numerator x) (denominator x)))) (define (round x) (define d (denominator x)) (define-values (q r) (floor-quotient (numerator x) d)) (cond ((< (* 2 r) d) q) ((= (* 2 r) d) ; round to even (if (even? q) q (+ 1 q))) (else (+ 1 q)))) (define (square z) (* z z)) (define (exact-integer-sqrt k) (if (zero? k) (values 0 0) (let loop ((x (quotient k 2))) ; initial estimate (define y (quotient (+ x (quotient k x)) 2)) (if (>= y x) (values x (- k (square x))) (loop y))))) (define (expt x y) (unless (integer? y) (error "only integer exponents are supported for now")) (cond ((zero? y) 1) ((even? y) (expt (square x) (quotient y 2))) (else (* x (expt x (- y 1)))))))