Skip to main content

jay/
exact.rs

1//! Exact numbers: arbitrary-precision integers and rationals.
2//!
3//! These are the two element types that never round. An [`Ext`] is J's
4//! "extended" integer, a `num-bigint` `BigInt`; a [`Rat`] is a pair of them
5//! in lowest terms with a positive denominator.
6//!
7//! Functions here are the arithmetic only, plus the exactness tests the
8//! languages' type rules turn on — "is this square exact", "is this power a
9//! whole number". Where an operation has no exact answer the function says
10//! so (`None`) and the caller widens to float; the type rules and the
11//! diagnostics live in `verb.rs`.
12
13use std::cmp::Ordering;
14use std::fmt;
15
16use num_bigint::{BigInt, Sign};
17use num_integer::Integer;
18use num_traits::{FromPrimitive, One, Signed, ToPrimitive, Zero};
19
20/// An extended-precision integer (J `x`).
21pub type Ext = BigInt;
22
23/// The largest magnitude a power is allowed to reach, in bits. A bignum
24/// power grows without warning — `2 ^ 10000000x` is a gigabyte — so the
25/// arithmetic refuses rather than exhausts the machine.
26pub const MAX_BITS: u64 = 1 << 26;
27
28/// A rational number in lowest terms; the denominator is always positive
29/// and never zero.
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct Rat {
32    num: BigInt,
33    den: BigInt,
34}
35
36impl Rat {
37    /// `num / den`, reduced. None when the denominator is zero: an infinite
38    /// rational is not a rational, and the caller answers in floats.
39    pub fn new(num: BigInt, den: BigInt) -> Option<Rat> {
40        if den.is_zero() {
41            return None;
42        }
43        let mut r = Rat { num, den };
44        r.normalize();
45        Some(r)
46    }
47
48    fn normalize(&mut self) {
49        if self.den.sign() == Sign::Minus {
50            self.num = -std::mem::take(&mut self.num);
51            self.den = -std::mem::take(&mut self.den);
52        }
53        let g = self.num.gcd(&self.den);
54        if !g.is_one() && !g.is_zero() {
55            self.num /= &g;
56            self.den /= &g;
57        }
58    }
59
60    pub fn from_int(v: BigInt) -> Rat {
61        Rat { num: v, den: BigInt::one() }
62    }
63
64    pub fn zero() -> Rat {
65        Rat::from_int(BigInt::zero())
66    }
67
68    pub fn one() -> Rat {
69        Rat::from_int(BigInt::one())
70    }
71
72    pub fn numer(&self) -> &BigInt {
73        &self.num
74    }
75
76    pub fn denom(&self) -> &BigInt {
77        &self.den
78    }
79
80    pub fn is_zero(&self) -> bool {
81        self.num.is_zero()
82    }
83
84    pub fn is_integer(&self) -> bool {
85        self.den.is_one()
86    }
87
88    /// The value as a whole number, when it is one.
89    pub fn to_int(&self) -> Option<BigInt> {
90        self.is_integer().then(|| self.num.clone())
91    }
92
93    pub fn to_f64(&self) -> f64 {
94        ratio_to_f64(&self.num, &self.den)
95    }
96
97    pub fn neg(&self) -> Rat {
98        Rat { num: -self.num.clone(), den: self.den.clone() }
99    }
100
101    pub fn abs(&self) -> Rat {
102        Rat { num: self.num.abs(), den: self.den.clone() }
103    }
104
105    /// -1, 0 or 1 as a whole number, which is the type J answers `*` with.
106    pub fn signum(&self) -> BigInt {
107        match self.num.sign() {
108            Sign::Minus => -BigInt::one(),
109            Sign::NoSign => BigInt::zero(),
110            Sign::Plus => BigInt::one(),
111        }
112    }
113
114    /// True when both values are whole, which is the common case an
115    /// extended-integer computation carries all the way through.
116    fn both_whole(&self, other: &Rat) -> bool {
117        self.den.is_one() && other.den.is_one()
118    }
119
120    pub fn add(&self, other: &Rat) -> Rat {
121        if self.both_whole(other) {
122            return Rat::from_int(&self.num + &other.num);
123        }
124        let mut r = Rat {
125            num: &self.num * &other.den + &other.num * &self.den,
126            den: &self.den * &other.den,
127        };
128        r.normalize();
129        r
130    }
131
132    pub fn sub(&self, other: &Rat) -> Rat {
133        if self.both_whole(other) {
134            return Rat::from_int(&self.num - &other.num);
135        }
136        self.add(&other.neg())
137    }
138
139    pub fn mul(&self, other: &Rat) -> Rat {
140        if self.both_whole(other) {
141            return Rat::from_int(&self.num * &other.num);
142        }
143        let mut r = Rat { num: &self.num * &other.num, den: &self.den * &other.den };
144        r.normalize();
145        r
146    }
147
148    /// Division; None when the divisor is zero.
149    pub fn div(&self, other: &Rat) -> Option<Rat> {
150        Rat::new(&self.num * &other.den, &self.den * &other.num)
151    }
152
153    pub fn recip(&self) -> Option<Rat> {
154        Rat::new(self.den.clone(), self.num.clone())
155    }
156
157    /// The greatest integer at or below the value.
158    pub fn floor(&self) -> BigInt {
159        self.num.div_floor(&self.den)
160    }
161
162    pub fn ceil(&self) -> BigInt {
163        -(-self.num.clone()).div_floor(&self.den)
164    }
165
166    /// `self ^ n` for a whole exponent. None when the value is zero and the
167    /// exponent negative.
168    pub fn pow(&self, n: i64) -> Option<Rat> {
169        if n == 0 {
170            return Some(Rat::one());
171        }
172        let k = n.unsigned_abs();
173        if k > u32::MAX as u64 {
174            return None;
175        }
176        let k = k as u32;
177        let (a, b) = (self.num.magnitude().bits(), self.den.magnitude().bits());
178        if a.max(b).saturating_mul(u64::from(k)) > MAX_BITS {
179            return None;
180        }
181        let p = Rat { num: self.num.pow(k), den: self.den.pow(k) };
182        if n > 0 { Some(p) } else { p.recip() }
183    }
184
185    /// The exact square root, when both halves have one.
186    pub fn sqrt(&self) -> Option<Rat> {
187        if self.num.sign() == Sign::Minus {
188            return None;
189        }
190        Some(Rat { num: exact_sqrt(&self.num)?, den: exact_sqrt(&self.den)? })
191    }
192}
193
194impl Ord for Rat {
195    fn cmp(&self, other: &Rat) -> Ordering {
196        // Both denominators are positive, so cross-multiplying keeps the
197        // sense of the comparison.
198        (&self.num * &other.den).cmp(&(&other.num * &self.den))
199    }
200}
201
202impl PartialOrd for Rat {
203    fn partial_cmp(&self, other: &Rat) -> Option<Ordering> {
204        Some(self.cmp(other))
205    }
206}
207
208impl std::hash::Hash for Rat {
209    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
210        self.num.hash(state);
211        self.den.hash(state);
212    }
213}
214
215/// The J spelling, with `-` where the language's own negative sign goes:
216/// `3r4`, and a whole value as the integer alone.
217impl fmt::Display for Rat {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        if self.is_integer() {
220            return write!(f, "{}", self.num);
221        }
222        write!(f, "{}r{}", self.num, self.den)
223    }
224}
225
226// ------------------------------------------------------------ conversions
227
228/// `num / den` as the nearest double. Dividing the two `to_f64`s overflows
229/// as soon as either half leaves the double range, so the ratio is scaled
230/// by its own bit lengths first.
231pub fn ratio_to_f64(num: &BigInt, den: &BigInt) -> f64 {
232    if den.is_zero() {
233        return match num.sign() {
234            Sign::Minus => f64::NEG_INFINITY,
235            Sign::NoSign => 0.0,
236            Sign::Plus => f64::INFINITY,
237        };
238    }
239    if let (Some(a), Some(b)) = (num.to_f64(), den.to_f64()) && a.is_finite() && b.is_finite() {
240        return a / b;
241    }
242    // Shift both halves down until they fit, keeping the quotient's value:
243    // the same shift on both leaves the ratio unchanged.
244    let bits = num.magnitude().bits().max(den.magnitude().bits());
245    let shift = bits.saturating_sub(900);
246    let scale = |v: &BigInt| (v >> shift as usize).to_f64().unwrap_or(f64::NAN);
247    scale(num) / scale(den)
248}
249
250pub fn ext_to_f64(v: &BigInt) -> f64 {
251    v.to_f64().unwrap_or(match v.sign() {
252        Sign::Minus => f64::NEG_INFINITY,
253        _ => f64::INFINITY,
254    })
255}
256
257pub fn ext_to_i64(v: &BigInt) -> Option<i64> {
258    v.to_i64()
259}
260
261/// A double as an exact integer, when it is one.
262pub fn f64_to_ext(x: f64) -> Option<BigInt> {
263    if !x.is_finite() || x.fract() != 0.0 {
264        return None;
265    }
266    BigInt::from_f64(x)
267}
268
269/// J's comparison tolerance, `2^-44` — the one `x:` reads a float through.
270const CT_BITS: usize = 44;
271
272/// A double as the simplest rational within J's comparison tolerance of it:
273/// the first continued-fraction convergent that is close enough. An integral
274/// value is exact, so `x: 1e30` keeps every digit the double really holds.
275pub fn f64_to_rat(x: f64) -> Option<Rat> {
276    if !x.is_finite() {
277        return None;
278    }
279    let exact = f64_exact(x)?;
280    if exact.is_integer() || x == 0.0 {
281        return Some(exact);
282    }
283    let target = exact.abs();
284    let (mut h0, mut h1) = (BigInt::zero(), BigInt::one());
285    let (mut k0, mut k1) = (BigInt::one(), BigInt::zero());
286    let mut a = target.clone();
287    let tolerance = Rat::new(BigInt::one(), BigInt::one() << CT_BITS)?;
288    let limit = target.mul(&tolerance);
289    loop {
290        let n = a.floor();
291        let (nh, nk) = (&n * &h1 + &h0, &n * &k1 + &k0);
292        h0 = std::mem::replace(&mut h1, nh);
293        k0 = std::mem::replace(&mut k1, nk);
294        let cand = Rat::new(h1.clone(), k1.clone())?;
295        let err = cand.sub(&target).abs();
296        if err <= limit {
297            return Some(if x < 0.0 { cand.neg() } else { cand });
298        }
299        let frac = a.sub(&Rat::from_int(n));
300        if frac.is_zero() {
301            return Some(if x < 0.0 { cand.neg() } else { cand });
302        }
303        a = frac.recip()?;
304    }
305}
306
307/// A double as the rational it exactly is: mantissa over a power of two.
308pub fn f64_exact(x: f64) -> Option<Rat> {
309    if !x.is_finite() {
310        return None;
311    }
312    if x == 0.0 {
313        return Some(Rat::zero());
314    }
315    let bits = x.to_bits();
316    let sign = if bits >> 63 == 1 { -1i8 } else { 1 };
317    let raw_exp = ((bits >> 52) & 0x7ff) as i64;
318    let frac = bits & 0x000f_ffff_ffff_ffff;
319    let (mantissa, exp) = if raw_exp == 0 {
320        (frac, -1074i64)
321    } else {
322        (frac | 0x0010_0000_0000_0000, raw_exp - 1075)
323    };
324    let mut num = BigInt::from(mantissa);
325    if sign < 0 {
326        num = -num;
327    }
328    if exp >= 0 {
329        Some(Rat::from_int(num << exp as usize))
330    } else {
331        Rat::new(num, BigInt::one() << (-exp) as usize)
332    }
333}
334
335// -------------------------------------------------------- exact arithmetic
336
337/// The exact square root, when the argument has one.
338pub fn exact_sqrt(v: &BigInt) -> Option<BigInt> {
339    if v.sign() == Sign::Minus {
340        return None;
341    }
342    let r = v.sqrt();
343    (&r * &r == *v).then_some(r)
344}
345
346/// The exact `n`-th root, when the argument has one. `n` must be positive.
347pub fn exact_root(n: u32, v: &BigInt) -> Option<BigInt> {
348    if n == 0 {
349        return None;
350    }
351    if n == 1 {
352        return Some(v.clone());
353    }
354    if v.sign() == Sign::Minus && n % 2 == 0 {
355        return None;
356    }
357    let r = v.nth_root(n);
358    (r.pow(n) == *v).then_some(r)
359}
360
361/// `base ^ exp` for a whole nonnegative exponent, refusing a result too big
362/// to hold.
363pub fn ext_pow(base: &BigInt, exp: u64) -> Option<BigInt> {
364    if exp > u32::MAX as u64 {
365        return None;
366    }
367    let bits = base.magnitude().bits();
368    if bits.saturating_mul(exp) > MAX_BITS {
369        return None;
370    }
371    Some(base.pow(exp as u32))
372}
373
374/// `! y` on a whole number: the exact factorial. None for a negative
375/// argument (a pole) or one large enough to exhaust the machine.
376pub fn ext_factorial(v: &BigInt) -> Option<BigInt> {
377    let n = v.to_u64().filter(|&n| n <= 200_000)?;
378    let mut acc = BigInt::one();
379    for k in 2..=n {
380        acc *= k;
381    }
382    Some(acc)
383}
384
385/// `x ! y` on whole numbers: the number of ways to choose x things from y.
386/// Follows J for the degenerate cases — a negative or oversized x gives 0,
387/// and `0 ! y` is 1.
388pub fn ext_binomial(x: &BigInt, y: &BigInt) -> Option<BigInt> {
389    if y.sign() == Sign::Minus {
390        // The gamma-function extension takes over below zero.
391        return None;
392    }
393    if x.sign() == Sign::Minus || x > y {
394        return Some(BigInt::zero());
395    }
396    // Choose the smaller of the two factors, so `2 ! 1000000x` is cheap.
397    let rest = y - x;
398    let k = if *x <= rest { x } else { &rest };
399    let k = k.to_u64().filter(|&k| k <= 1_000_000)?;
400    let mut acc = BigInt::one();
401    for i in 0..k {
402        acc = acc * (y - BigInt::from(i)) / BigInt::from(i + 1);
403    }
404    Some(acc)
405}
406
407/// `x | y`: y reduced modulo x, the residue taking x's sign; `0 | y` is y.
408pub fn ext_residue(x: &BigInt, y: &BigInt) -> BigInt {
409    if x.is_zero() {
410        return y.clone();
411    }
412    let r = y.mod_floor(&x.abs());
413    if x.sign() == Sign::Minus && !r.is_zero() { r - x.abs() } else { r }
414}
415
416/// `x | y` on rationals: `y - x * <. y % x`, as on the reals.
417pub fn rat_residue(x: &Rat, y: &Rat) -> Rat {
418    if x.is_zero() {
419        return y.clone();
420    }
421    let q = y.div(x).expect("x is not zero");
422    y.sub(&x.mul(&Rat::from_int(q.floor())))
423}
424
425/// The greatest common divisor of two rationals: `gcd(numerators) over
426/// lcm(denominators)`, which is the largest rational dividing both a whole
427/// number of times.
428pub fn rat_gcd(a: &Rat, b: &Rat) -> Rat {
429    let num = a.num.gcd(&b.num);
430    let den = a.den.lcm(&b.den);
431    Rat::new(num, den).expect("denominators are positive")
432}
433
434/// The least common multiple of two rationals, `lcm(numerators)` over
435/// `gcd(denominators)`; zero when either is zero.
436pub fn rat_lcm(a: &Rat, b: &Rat) -> Rat {
437    if a.is_zero() || b.is_zero() {
438        return Rat::zero();
439    }
440    let num = a.num.lcm(&b.num);
441    let den = a.den.gcd(&b.den);
442    Rat::new(num, den).expect("denominators are positive")
443}
444
445#[cfg(test)]
446mod tests {
447    use super::*;
448
449    fn r(n: i64, d: i64) -> Rat {
450        Rat::new(BigInt::from(n), BigInt::from(d)).expect("nonzero denominator")
451    }
452
453    #[test]
454    fn rationals_are_kept_in_lowest_terms_with_a_positive_denominator() {
455        assert_eq!(r(2, 6), r(1, 3));
456        assert_eq!(r(1, -2), r(-1, 2));
457        assert_eq!(r(6, 3).to_string(), "2");
458        assert_eq!(r(-1, 2).to_string(), "-1r2");
459        assert!(Rat::new(BigInt::one(), BigInt::zero()).is_none());
460    }
461
462    #[test]
463    fn rational_arithmetic_is_exact() {
464        assert_eq!(r(1, 2).add(&r(1, 3)), r(5, 6));
465        assert_eq!(r(1, 2).mul(&r(2, 3)), r(1, 3));
466        assert_eq!(r(1, 2).div(&r(1, 3)), Some(r(3, 2)));
467        assert_eq!(r(1, 2).pow(-2), Some(r(4, 1)));
468        assert_eq!(r(1, 2).sub(&r(1, 2)), Rat::zero());
469        assert_eq!(r(1, 4).sqrt(), Some(r(1, 2)));
470        assert_eq!(r(1, 2).sqrt(), None);
471    }
472
473    #[test]
474    fn rationals_order_by_value_however_they_are_spelled() {
475        assert!(r(1, 3) < r(1, 2));
476        assert!(r(2, 4) == r(1, 2));
477        assert_eq!(r(7, 2).floor(), BigInt::from(3));
478        assert_eq!(r(7, 2).ceil(), BigInt::from(4));
479        assert_eq!(r(-7, 2).floor(), BigInt::from(-4));
480    }
481
482    #[test]
483    fn residue_takes_the_sign_of_its_left_argument() {
484        let e = |v: i64| BigInt::from(v);
485        assert_eq!(ext_residue(&e(2), &e(-7)), e(1));
486        assert_eq!(ext_residue(&e(-2), &e(7)), e(-1));
487        assert_eq!(ext_residue(&e(0), &e(7)), e(7));
488        assert_eq!(ext_residue(&e(3), &e(10)), e(1));
489    }
490
491    #[test]
492    fn exact_roots_are_found_only_where_they_exist() {
493        assert_eq!(exact_sqrt(&BigInt::from(9)), Some(BigInt::from(3)));
494        assert_eq!(exact_sqrt(&BigInt::from(8)), None);
495        assert_eq!(exact_root(5, &BigInt::from(32)), Some(BigInt::from(2)));
496        assert_eq!(exact_root(2, &BigInt::from(8)), None);
497    }
498
499    #[test]
500    fn a_float_becomes_the_simplest_rational_near_it() {
501        assert_eq!(f64_to_rat(0.1), Some(r(1, 10)));
502        assert_eq!(f64_to_rat(1.5), Some(r(3, 2)));
503        assert_eq!(f64_to_rat(-0.5), Some(r(-1, 2)));
504        assert_eq!(f64_to_rat(2.0), Some(r(2, 1)));
505        // An integral double keeps every digit it really holds.
506        assert_eq!(f64_to_rat(1e30).map(|v| v.to_string()), Some("1000000000000000019884624838656".to_string()));
507    }
508
509    #[test]
510    fn gcd_and_lcm_of_rationals() {
511        assert_eq!(rat_gcd(&r(1, 2), &r(1, 3)), r(1, 6));
512        assert_eq!(rat_lcm(&r(1, 2), &r(1, 3)), r(1, 1));
513        assert_eq!(rat_gcd(&r(5, 1), &r(15, 1)), r(5, 1));
514    }
515
516    #[test]
517    fn factorials_and_binomials_stay_whole() {
518        assert_eq!(
519            ext_factorial(&BigInt::from(30)).map(|v| v.to_string()),
520            Some("265252859812191058636308480000000".to_string())
521        );
522        assert_eq!(ext_binomial(&BigInt::from(2), &BigInt::from(5)), Some(BigInt::from(10)));
523        assert_eq!(ext_binomial(&BigInt::from(6), &BigInt::from(5)), Some(BigInt::zero()));
524        assert_eq!(ext_binomial(&BigInt::from(0), &BigInt::from(5)), Some(BigInt::one()));
525    }
526
527    #[test]
528    fn a_power_that_would_exhaust_the_machine_is_refused() {
529        assert!(ext_pow(&BigInt::from(2), 1000).is_some());
530        assert!(ext_pow(&BigInt::from(2), 1 << 30).is_none());
531    }
532
533    #[test]
534    fn huge_ratios_reach_a_float_without_overflowing() {
535        let big = BigInt::from(10).pow(400);
536        assert!((ratio_to_f64(&(&big * 3), &big) - 3.0).abs() < 1e-12);
537    }
538}