tur/rational
exact Rational arithmetic over an int64 num/den pair.
Since: Phase N1 (numeric-tower-rational-complex-plan)
ArithError
(defdata ArithError :copy (DivByZero) (Overflow))
the failure modes of exact integer/rational arithmetic.
Since: Phase N1
Rational
(defstruct Rational :copy [num : int den : int])
an exact num/den pair over int64.
Since: Phase N1
int/abs
(int/abs [x : int] :)
absolute value of an int.
| x | the integer |
|x|. `int/abs` of the int64 minimum is not representable and wraps back to itself, matching two's-complement negation.
(int/abs -7) ; => 7
Since: Phase N1
int/gcd
(int/gcd [a : int b : int] :)
greatest common divisor of two non-negative ints (Euclid).
| a | first value, >= 0 | |
| b | second value, >= 0 |
gcd(a, b); gcd(0, 0) is 0.
(int/gcd 6 8) ; => 2
Since: Phase N1
int/max-value
(int/max-value :)
the largest representable int (2^63 - 1).
9223372036854775807.
(int/max-value) ; => 9223372036854775807
Since: Phase N1
int/min-value
(int/min-value :)
the smallest representable int (-2^63).
-9223372036854775808. Written as `-(2^63 - 1) - 1` because the literal itself is one past the positive range.
(int/min-value) ; => -9223372036854775808
Since: Phase N1
int/add-overflows?
(int/add-overflows? [a : int b : int] :)
would `(+ a b)` leave the int64 range?
| a | left addend | |
| b | right addend |
true when the exact sum is outside [int/min-value, int/max-value]. The test itself never overflows: each comparison subtracts b from a bound, which stays in range for the sign of b being tested.
(int/add-overflows? (int/max-value) 1) ; => true
Since: Phase N1
int/sub-overflows?
(int/sub-overflows? [a : int b : int] :)
would `(- a b)` leave the int64 range?
| a | minuend | |
| b | subtrahend |
true when the exact difference is outside the int64 range.
(int/sub-overflows? (int/min-value) 1) ; => true
Since: Phase N1
int/mul-overflows?
(int/mul-overflows? [a : int b : int] :)
would `(* a b)` leave the int64 range?
| a | left factor | |
| b | right factor |
true when the exact product is outside the int64 range. The `-1` cases are peeled off first so the remaining tests can divide a bound by a factor without tripping the one division that overflows (`int/min-value / -1`).
(int/mul-overflows? 4294967296 4294967296) ; => true
Since: Phase N1
rat/normalize
(rat/normalize [n : int d : int] :)
build a Rational in lowest terms with a positive denominator.
| n | numerator | |
| d | denominator; must be non-zero (callers check first) |
The normalized Rational n/d.
(rat/num (rat/normalize 6 -8)) ; => -3
Since: Phase N1
rat/of
(rat/of [n : int d : int] :)
construct a Rational, reporting a zero denominator as an error.
| n | numerator | |
| d | denominator |
`(ok r)` with r normalized, or `(err (DivByZero))` when d is 0.
(rat/of 6 8) ; => ok 3/4 (rat/of 1 0) ; => err DivByZero
Since: Phase N1
rat/of!
(rat/of! [n : int d : int] :)
construct a Rational, panicking on a zero denominator.
| n | numerator | |
| d | denominator; must not be 0 |
The normalized Rational n/d. Panics when d is 0 -- this is the form `#rat{n/d}` expands to, and the reader rejects a literal zero denominator at read time, so the panic is reachable only from a computed denominator.
(rat/num (rat/of! 6 8)) ; => 3
Since: Phase N1
rat/from-int
(rat/from-int [n : int] :)
the Rational n/1.
| n | the integer |
n as an exact Rational.
(rat/den (rat/from-int 5)) ; => 1
Since: Phase N1
rat/zero
(rat/zero :)
the Rational 0/1.
0 as an exact Rational.
(rat/num (rat/zero)) ; => 0
Since: Phase N1
rat/one
(rat/one :)
the Rational 1/1.
1 as an exact Rational.
(rat/num (rat/one)) ; => 1
Since: Phase N1
rat/num
(rat/num [r : Rational] :)
the numerator (carries the sign).
| r | a Rational |
The numerator.
(rat/num (rat/of! -6 8)) ; => -3
Since: Phase N1
rat/den
(rat/den [r : Rational] :)
the denominator (always positive).
| r | a Rational |
The denominator.
(rat/den (rat/of! -6 8)) ; => 4
Since: Phase N1
rat/zero?
(rat/zero? [r : Rational] :)
is this the rational 0?
| r | a Rational |
true when the numerator is 0.
(rat/zero? (rat/of! 0 5)) ; => true
Since: Phase N1
rat/integer?
(rat/integer? [r : Rational] :)
does this rational denote a whole number?
| r | a Rational |
true when the (normalized) denominator is 1.
(rat/integer? (rat/of! 6 3)) ; => true
Since: Phase N1
rat/add
(rat/add [x : Rational y : Rational] :)
exact sum of two rationals.
| x | left addend | |
| y | right addend |
The normalized sum. Cross-cancels by `g = gcd(b, d)` first and computes `(a*(d/g) + c*(b/g)) / (b*(d/g))`, which keeps the common case in int64 range far longer than the naive `(a*d + c*b) / (b*d)`. Wraps on int64 overflow; use `rat/try-add` to detect it.
(rat/den (rat/add (rat/of! 1 3) (rat/of! 1 6))) ; => 2
Since: Phase N1
rat/neg
(rat/neg [x : Rational] :)
negate a rational.
| x | a Rational |
-x, still normalized (the denominator is untouched).
(rat/num (rat/neg (rat/of! 3 4))) ; => -3
Since: Phase N1
rat/sub
(rat/sub [x : Rational y : Rational] :)
exact difference of two rationals.
| x | minuend | |
| y | subtrahend |
The normalized difference. Wraps on int64 overflow.
(rat/num (rat/sub (rat/of! 1 2) (rat/of! 1 6))) ; => 1
Since: Phase N1
rat/mul
(rat/mul [x : Rational y : Rational] :)
exact product of two rationals.
| x | left factor | |
| y | right factor |
The normalized product. Cross-cancels each numerator against the OTHER denominator before multiplying, so the intermediates stay small. Wraps on int64 overflow; use `rat/try-mul` to detect it.
(rat/num (rat/mul (rat/of! 2 3) (rat/of! 3 4))) ; => 1
Since: Phase N1
rat/div
(rat/div [x : Rational y : Rational] :)
exact quotient of two rationals; panics on a zero divisor.
| x | dividend | |
| y | divisor; must not be the zero rational |
The normalized quotient. Panics when y is 0 -- use `rat/try-div` for the checked form. Wraps on int64 overflow.
(rat/num (rat/div (rat/of! 1 2) (rat/of! 3 4))) ; => 2
Since: Phase N1
rat/recip
(rat/recip [x : Rational] :)
the reciprocal; panics on the zero rational.
| x | a non-zero Rational |
1/x, normalized (the sign moves back to the numerator).
(rat/num (rat/recip (rat/of! -3 4))) ; => -4
Since: Phase N1
rat/try-add
(rat/try-add [x : Rational y : Rational] :)
checked sum; `Err Overflow` instead of wrapping.
| x | left addend | |
| y | right addend |
`(ok (rat/add x y))`, or `(err (Overflow))` when any intermediate of the cross-cancelled formula would leave the int64 range.
(rat/try-add (rat/of! 1 3) (rat/of! 1 6)) ; => ok 1/2
Since: Phase N1
rat/try-sub
(rat/try-sub [x : Rational y : Rational] :)
checked difference; `Err Overflow` instead of wrapping.
| x | minuend | |
| y | subtrahend |
`(ok (rat/sub x y))`, or `(err (Overflow))`. Negating the subtrahend is itself checked: the int64 minimum has no positive counterpart.
(rat/try-sub (rat/of! 1 2) (rat/of! 1 6)) ; => ok 1/3
Since: Phase N1
rat/try-mul
(rat/try-mul [x : Rational y : Rational] :)
checked product; `Err Overflow` instead of wrapping.
| x | left factor | |
| y | right factor |
`(ok (rat/mul x y))`, or `(err (Overflow))` when either cross-cancelled product would leave the int64 range.
(rat/try-mul (rat/of! 2 3) (rat/of! 3 4)) ; => ok 1/2
Since: Phase N1
rat/try-div
(rat/try-div [x : Rational y : Rational] :)
checked quotient; `Err DivByZero` / `Err Overflow`.
| x | dividend | |
| y | divisor |
`(err (DivByZero))` when y is the zero rational, `(err (Overflow))` when the product would leave the int64 range, else `(ok (rat/div x y))`.
(rat/try-div (rat/of! 1 2) (rat/of! 0 1)) ; => err DivByZero
Since: Phase N1
rat/eq?
(rat/eq? [x : Rational y : Rational] :)
exact equality of two rationals.
| x | left value | |
| y | right value |
true when the two denote the same number. Both are normalized, so this is plain structural equality.
(rat/eq? (rat/of! 2 4) (rat/of! 1 2)) ; => true
Since: Phase N1
rat/cmp
(rat/cmp [x : Rational y : Rational] :)
three-way comparison of two rationals.
| x | left value | |
| y | right value |
-1 when x < y, 0 when equal, 1 when x > y. Compares `a*(d/g)` against `c*(b/g)` for `g = gcd(b, d)`, so the cross-products stay small; both denominators are positive, so the comparison direction is preserved.
(rat/cmp (rat/of! 1 3) (rat/of! 1 2)) ; => -1
Since: Phase N1
rat/lt?
(rat/lt? [x : Rational y : Rational] :)
is x strictly less than y?
| x | left value | |
| y | right value |
true when x < y.
(rat/lt? (rat/of! 1 3) (rat/of! 1 2)) ; => true
Since: Phase N1
rat/lte?
(rat/lte? [x : Rational y : Rational] :)
is x less than or equal to y?
| x | left value | |
| y | right value |
true when x <= y.
(rat/lte? (rat/of! 1 2) (rat/of! 1 2)) ; => true
Since: Phase N1
rat/gt?
(rat/gt? [x : Rational y : Rational] :)
is x strictly greater than y?
| x | left value | |
| y | right value |
true when x > y.
(rat/gt? (rat/of! 1 2) (rat/of! 1 3)) ; => true
Since: Phase N1
rat/gte?
(rat/gte? [x : Rational y : Rational] :)
is x greater than or equal to y?
| x | left value | |
| y | right value |
true when x >= y.
(rat/gte? (rat/of! 1 2) (rat/of! 1 2)) ; => true
Since: Phase N1
rat->float
(rat->float [r : Rational] :)
the nearest float to this rational.
| r | a Rational |
`num / den` as a double. Inexact by construction -- this is the lossy direction out of the exact tower.
(rat->float (rat/of! 1 4)) ; => 0.25
Since: Phase N1
rat->string
(rat->string [r : Rational] :)
render a Rational as "3/4", "-3/4", or "5".
| r | a Rational |
A fresh owned String. A whole number prints without a denominator; every other value round-trips through the `#rat{...}` literal.
(rat->string (rat/of! 6 8)) ; => String "3/4"
Since: Phase N1
Num[Rational]
(definstance Num [Rational])
exact rational arithmetic behind + - * /.
Eq[Rational]
(definstance Eq [Rational])
structural equality, which normalization makes exact.
Ord[Rational]
(definstance Ord [Rational])
total order via the cross-cancelled cross-product.
Hash[Rational]
(definstance Hash [Rational])
mixes the normalized numerator and denominator.
Show[Rational]
(definstance Show [Rational])
"3/4" / "-3/4" / "5"; round-trips through #rat{...}.