SxRational type in OCaml (Rational of int * int, stored reduced, denom>0) and JS (SxRational class with _rational marker). n/d reader syntax in spec/parser.sx. Arithmetic contagion: int op rational → rational, rational op float → float. JS keeps int/int → float for CSS backward compatibility. OCaml as_number + safe_eq extended for cross-type rational equality so (= 2.5 5/2) → true. 62 tests in test-rationals.sx, all pass. JS: 2232 passed. OCaml: 4532 passed (+11 vs pre-fix baseline). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
136 lines
5.6 KiB
Plaintext
136 lines
5.6 KiB
Plaintext
;; ==========================================================================
|
|
;; test-rationals.sx — Rational number type: literals, arithmetic, tower
|
|
;;
|
|
;; Note: in the JS host, (/ int int) returns float (backward-compatible).
|
|
;; Use rational literals (1/3, 3/4) or make-rational for exact rationals.
|
|
;; ==========================================================================
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Literals and type
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"rationals:literals"
|
|
(deftest "1/3 is rational" (assert (rational? 1/3)))
|
|
(deftest "1/2 is rational" (assert (rational? 1/2)))
|
|
(deftest "2/3 is rational" (assert (rational? 2/3)))
|
|
(deftest "literal numerator 1/3" (assert= (numerator 1/3) 1))
|
|
(deftest "literal denominator 1/3" (assert= (denominator 1/3) 3))
|
|
(deftest "literal numerator 2/3" (assert= (numerator 2/3) 2))
|
|
(deftest "auto-reduce 2/4 = 1/2" (assert= 2/4 1/2))
|
|
(deftest "auto-reduce 6/9 = 2/3" (assert= 6/9 2/3))
|
|
(deftest "negative literal" (assert= (numerator -1/3) -1)))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Constructor and predicates
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"rationals:constructor"
|
|
(deftest
|
|
"make-rational basic"
|
|
(assert (rational? (make-rational 1 3))))
|
|
(deftest
|
|
"make-rational reduces"
|
|
(assert= (make-rational 2 4) 1/2))
|
|
(deftest
|
|
"make-rational exact int"
|
|
(assert (integer? (make-rational 6 3))))
|
|
(deftest
|
|
"make-rational 6/3 = 2"
|
|
(assert= (make-rational 6 3) 2))
|
|
(deftest
|
|
"make-rational negative"
|
|
(assert= (numerator (make-rational -1 3)) -1))
|
|
(deftest
|
|
"make-rational neg denom"
|
|
(assert= (numerator (make-rational 1 -3)) -1))
|
|
(deftest "rational? on int" (assert (not (rational? 5))))
|
|
(deftest "rational? on float" (assert (not (rational? 1.5))))
|
|
(deftest "rational? on string" (assert (not (rational? "1/2"))))
|
|
(deftest "number? on rational" (assert (number? 1/3)))
|
|
(deftest "exact? on rational" (assert (exact? 1/3)))
|
|
(deftest "inexact? on rational" (assert (not (inexact? 1/3))))
|
|
(deftest "integer? on rational" (assert (not (integer? 1/3))))
|
|
(deftest "dict? on rational" (assert (not (dict? 1/3)))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Accessors
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"rationals:accessors"
|
|
(deftest "numerator 1/3" (assert= (numerator 1/3) 1))
|
|
(deftest "denominator 1/3" (assert= (denominator 1/3) 3))
|
|
(deftest "numerator 3/4" (assert= (numerator 3/4) 3))
|
|
(deftest "denominator 3/4" (assert= (denominator 3/4) 4))
|
|
(deftest "numerator of int" (assert= (numerator 5) 5))
|
|
(deftest
|
|
"denominator of int"
|
|
(assert= (denominator 5) 1)))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Arithmetic
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"rationals:arithmetic"
|
|
(deftest "add two rationals" (assert= (+ 1/3 1/3) 2/3))
|
|
(deftest "add to integer" (assert= (+ 1 1/2) 3/2))
|
|
(deftest "add integer to rational" (assert= (+ 1/2 1) 3/2))
|
|
(deftest "add reduces" (assert= (+ 1/6 1/6) 1/3))
|
|
(deftest "add to whole number" (assert (integer? (+ 1/2 1/2))))
|
|
(deftest "add whole = 1" (assert= (+ 1/2 1/2) 1))
|
|
(deftest "subtract rationals" (assert= (- 3/4 1/4) 1/2))
|
|
(deftest "subtract int from rational" (assert= (- 3/2 1) 1/2))
|
|
(deftest "negate rational" (assert= (- 1/3) -1/3))
|
|
(deftest "multiply rationals" (assert= (* 2/3 3/4) 1/2))
|
|
(deftest "multiply int and rational" (assert= (* 2 1/3) 2/3))
|
|
(deftest "multiply reduces to int" (assert (integer? (* 3 1/3))))
|
|
(deftest "divide rational by int" (assert= (/ 2/3 2) 1/3))
|
|
(deftest "divide rational by rational" (assert= (/ 1/2 1/4) 2))
|
|
(deftest
|
|
"divide rational gives int when exact"
|
|
(assert (integer? (/ 1/2 1/2)))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Float contagion
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"rationals:float-contagion"
|
|
(deftest "rational + float = float" (assert (float? (+ 1/3 0.5))))
|
|
(deftest "float + rational = float" (assert (float? (+ 0.5 1/3))))
|
|
(deftest "rational * float = float" (assert (float? (* 1/2 2))))
|
|
(deftest "rational - float = float" (assert (float? (- 1/2 0.1)))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Comparison
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"rationals:comparison"
|
|
(deftest "equal rationals" (assert (= 1/2 1/2)))
|
|
(deftest "equal reduced" (assert (= 2/4 1/2)))
|
|
(deftest "not equal" (assert (not (= 1/3 1/2))))
|
|
(deftest "less than" (assert (< 1/3 1/2)))
|
|
(deftest "less than int" (assert (< 1/3 1)))
|
|
(deftest "greater than" (assert (> 2/3 1/2)))
|
|
(deftest "less equal" (assert (<= 1/3 1/3)))
|
|
(deftest "greater equal" (assert (>= 2/3 2/3)))
|
|
(deftest "rational less than float" (assert (< 1/3 0.5))))
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Coercion
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(defsuite
|
|
"rationals:coercion"
|
|
(deftest "exact->inexact 1/2" (assert= (exact->inexact 1/2) 0.5))
|
|
(deftest "exact->inexact 1/4" (assert= (exact->inexact 1/4) 0.25))
|
|
(deftest
|
|
"exact->inexact 1/3 is float"
|
|
(assert (float? (exact->inexact 1/3))))
|
|
(deftest "number->string 1/2" (assert= (number->string 1/2) "1/2"))
|
|
(deftest "number->string 3/4" (assert= (number->string 3/4) "3/4")))
|