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()) {
240        if a.is_finite() && b.is_finite() {
241            return a / b;
242        }
243    }
244    // Shift both halves down until they fit, keeping the quotient's value:
245    // the same shift on both leaves the ratio unchanged.
246    let bits = num.magnitude().bits().max(den.magnitude().bits());
247    let shift = bits.saturating_sub(900);
248    let scale = |v: &BigInt| (v >> shift as usize).to_f64().unwrap_or(f64::NAN);
249    scale(num) / scale(den)
250}
251
252pub fn ext_to_f64(v: &BigInt) -> f64 {
253    v.to_f64().unwrap_or(match v.sign() {
254        Sign::Minus => f64::NEG_INFINITY,
255        _ => f64::INFINITY,
256    })
257}
258
259pub fn ext_to_i64(v: &BigInt) -> Option<i64> {
260    v.to_i64()
261}
262
263/// A double as an exact integer, when it is one.
264pub fn f64_to_ext(x: f64) -> Option<BigInt> {
265    if !x.is_finite() || x.fract() != 0.0 {
266        return None;
267    }
268    BigInt::from_f64(x)
269}
270
271/// J's comparison tolerance, `2^-44` — the one `x:` reads a float through.
272const CT_BITS: usize = 44;
273
274/// A double as the simplest rational within J's comparison tolerance of it:
275/// the first continued-fraction convergent that is close enough. An integral
276/// value is exact, so `x: 1e30` keeps every digit the double really holds.
277pub fn f64_to_rat(x: f64) -> Option<Rat> {
278    if !x.is_finite() {
279        return None;
280    }
281    let exact = f64_exact(x)?;
282    if exact.is_integer() || x == 0.0 {
283        return Some(exact);
284    }
285    let target = exact.abs();
286    let (mut h0, mut h1) = (BigInt::zero(), BigInt::one());
287    let (mut k0, mut k1) = (BigInt::one(), BigInt::zero());
288    let mut a = target.clone();
289    let tolerance = Rat::new(BigInt::one(), BigInt::one() << CT_BITS)?;
290    let limit = target.mul(&tolerance);
291    loop {
292        let n = a.floor();
293        let (nh, nk) = (&n * &h1 + &h0, &n * &k1 + &k0);
294        h0 = std::mem::replace(&mut h1, nh);
295        k0 = std::mem::replace(&mut k1, nk);
296        let cand = Rat::new(h1.clone(), k1.clone())?;
297        let err = cand.sub(&target).abs();
298        if err <= limit {
299            return Some(if x < 0.0 { cand.neg() } else { cand });
300        }
301        let frac = a.sub(&Rat::from_int(n));
302        if frac.is_zero() {
303            return Some(if x < 0.0 { cand.neg() } else { cand });
304        }
305        a = frac.recip()?;
306    }
307}
308
309/// A double as the rational it exactly is: mantissa over a power of two.
310pub fn f64_exact(x: f64) -> Option<Rat> {
311    if !x.is_finite() {
312        return None;
313    }
314    if x == 0.0 {
315        return Some(Rat::zero());
316    }
317    let bits = x.to_bits();
318    let sign = if bits >> 63 == 1 { -1i8 } else { 1 };
319    let raw_exp = ((bits >> 52) & 0x7ff) as i64;
320    let frac = bits & 0x000f_ffff_ffff_ffff;
321    let (mantissa, exp) = if raw_exp == 0 {
322        (frac, -1074i64)
323    } else {
324        (frac | 0x0010_0000_0000_0000, raw_exp - 1075)
325    };
326    let mut num = BigInt::from(mantissa);
327    if sign < 0 {
328        num = -num;
329    }
330    if exp >= 0 {
331        Some(Rat::from_int(num << exp as usize))
332    } else {
333        Rat::new(num, BigInt::one() << (-exp) as usize)
334    }
335}
336
337// -------------------------------------------------------- exact arithmetic
338
339/// The exact square root, when the argument has one.
340pub fn exact_sqrt(v: &BigInt) -> Option<BigInt> {
341    if v.sign() == Sign::Minus {
342        return None;
343    }
344    let r = v.sqrt();
345    (&r * &r == *v).then_some(r)
346}
347
348/// The exact `n`-th root, when the argument has one. `n` must be positive.
349pub fn exact_root(n: u32, v: &BigInt) -> Option<BigInt> {
350    if n == 0 {
351        return None;
352    }
353    if n == 1 {
354        return Some(v.clone());
355    }
356    if v.sign() == Sign::Minus && n % 2 == 0 {
357        return None;
358    }
359    let r = v.nth_root(n);
360    (r.pow(n) == *v).then_some(r)
361}
362
363/// `base ^ exp` for a whole nonnegative exponent, refusing a result too big
364/// to hold.
365pub fn ext_pow(base: &BigInt, exp: u64) -> Option<BigInt> {
366    if exp > u32::MAX as u64 {
367        return None;
368    }
369    let bits = base.magnitude().bits();
370    if bits.saturating_mul(exp) > MAX_BITS {
371        return None;
372    }
373    Some(base.pow(exp as u32))
374}
375
376/// `! y` on a whole number: the exact factorial. None for a negative
377/// argument (a pole) or one large enough to exhaust the machine.
378pub fn ext_factorial(v: &BigInt) -> Option<BigInt> {
379    let n = v.to_u64().filter(|&n| n <= 200_000)?;
380    let mut acc = BigInt::one();
381    for k in 2..=n {
382        acc *= k;
383    }
384    Some(acc)
385}
386
387/// `x ! y` on whole numbers: the number of ways to choose x things from y.
388/// Follows J for the degenerate cases — a negative or oversized x gives 0,
389/// and `0 ! y` is 1.
390pub fn ext_binomial(x: &BigInt, y: &BigInt) -> Option<BigInt> {
391    if y.sign() == Sign::Minus {
392        // The gamma-function extension takes over below zero.
393        return None;
394    }
395    if x.sign() == Sign::Minus || x > y {
396        return Some(BigInt::zero());
397    }
398    // Choose the smaller of the two factors, so `2 ! 1000000x` is cheap.
399    let rest = y - x;
400    let k = if *x <= rest { x } else { &rest };
401    let k = k.to_u64().filter(|&k| k <= 1_000_000)?;
402    let mut acc = BigInt::one();
403    for i in 0..k {
404        acc = acc * (y - BigInt::from(i)) / BigInt::from(i + 1);
405    }
406    Some(acc)
407}
408
409/// `x | y`: y reduced modulo x, the residue taking x's sign; `0 | y` is y.
410pub fn ext_residue(x: &BigInt, y: &BigInt) -> BigInt {
411    if x.is_zero() {
412        return y.clone();
413    }
414    let r = y.mod_floor(&x.abs());
415    if x.sign() == Sign::Minus && !r.is_zero() { r - x.abs() } else { r }
416}
417
418/// `x | y` on rationals: `y - x * <. y % x`, as on the reals.
419pub fn rat_residue(x: &Rat, y: &Rat) -> Rat {
420    if x.is_zero() {
421        return y.clone();
422    }
423    let q = y.div(x).expect("x is not zero");
424    y.sub(&x.mul(&Rat::from_int(q.floor())))
425}
426
427/// The greatest common divisor of two rationals: `gcd(numerators) over
428/// lcm(denominators)`, which is the largest rational dividing both a whole
429/// number of times.
430pub fn rat_gcd(a: &Rat, b: &Rat) -> Rat {
431    let num = a.num.gcd(&b.num);
432    let den = a.den.lcm(&b.den);
433    Rat::new(num, den).expect("denominators are positive")
434}
435
436/// The least common multiple of two rationals, `lcm(numerators)` over
437/// `gcd(denominators)`; zero when either is zero.
438pub fn rat_lcm(a: &Rat, b: &Rat) -> Rat {
439    if a.is_zero() || b.is_zero() {
440        return Rat::zero();
441    }
442    let num = a.num.lcm(&b.num);
443    let den = a.den.gcd(&b.den);
444    Rat::new(num, den).expect("denominators are positive")
445}
446
447#[cfg(test)]
448mod tests {
449    use super::*;
450
451    fn r(n: i64, d: i64) -> Rat {
452        Rat::new(BigInt::from(n), BigInt::from(d)).expect("nonzero denominator")
453    }
454
455    #[test]
456    fn rationals_are_kept_in_lowest_terms_with_a_positive_denominator() {
457        assert_eq!(r(2, 6), r(1, 3));
458        assert_eq!(r(1, -2), r(-1, 2));
459        assert_eq!(r(6, 3).to_string(), "2");
460        assert_eq!(r(-1, 2).to_string(), "-1r2");
461        assert!(Rat::new(BigInt::one(), BigInt::zero()).is_none());
462    }
463
464    #[test]
465    fn rational_arithmetic_is_exact() {
466        assert_eq!(r(1, 2).add(&r(1, 3)), r(5, 6));
467        assert_eq!(r(1, 2).mul(&r(2, 3)), r(1, 3));
468        assert_eq!(r(1, 2).div(&r(1, 3)), Some(r(3, 2)));
469        assert_eq!(r(1, 2).pow(-2), Some(r(4, 1)));
470        assert_eq!(r(1, 2).sub(&r(1, 2)), Rat::zero());
471        assert_eq!(r(1, 4).sqrt(), Some(r(1, 2)));
472        assert_eq!(r(1, 2).sqrt(), None);
473    }
474
475    #[test]
476    fn rationals_order_by_value_however_they_are_spelled() {
477        assert!(r(1, 3) < r(1, 2));
478        assert!(r(2, 4) == r(1, 2));
479        assert_eq!(r(7, 2).floor(), BigInt::from(3));
480        assert_eq!(r(7, 2).ceil(), BigInt::from(4));
481        assert_eq!(r(-7, 2).floor(), BigInt::from(-4));
482    }
483
484    #[test]
485    fn residue_takes_the_sign_of_its_left_argument() {
486        let e = |v: i64| BigInt::from(v);
487        assert_eq!(ext_residue(&e(2), &e(-7)), e(1));
488        assert_eq!(ext_residue(&e(-2), &e(7)), e(-1));
489        assert_eq!(ext_residue(&e(0), &e(7)), e(7));
490        assert_eq!(ext_residue(&e(3), &e(10)), e(1));
491    }
492
493    #[test]
494    fn exact_roots_are_found_only_where_they_exist() {
495        assert_eq!(exact_sqrt(&BigInt::from(9)), Some(BigInt::from(3)));
496        assert_eq!(exact_sqrt(&BigInt::from(8)), None);
497        assert_eq!(exact_root(5, &BigInt::from(32)), Some(BigInt::from(2)));
498        assert_eq!(exact_root(2, &BigInt::from(8)), None);
499    }
500
501    #[test]
502    fn a_float_becomes_the_simplest_rational_near_it() {
503        assert_eq!(f64_to_rat(0.1), Some(r(1, 10)));
504        assert_eq!(f64_to_rat(1.5), Some(r(3, 2)));
505        assert_eq!(f64_to_rat(-0.5), Some(r(-1, 2)));
506        assert_eq!(f64_to_rat(2.0), Some(r(2, 1)));
507        // An integral double keeps every digit it really holds.
508        assert_eq!(f64_to_rat(1e30).map(|v| v.to_string()), Some("1000000000000000019884624838656".to_string()));
509    }
510
511    #[test]
512    fn gcd_and_lcm_of_rationals() {
513        assert_eq!(rat_gcd(&r(1, 2), &r(1, 3)), r(1, 6));
514        assert_eq!(rat_lcm(&r(1, 2), &r(1, 3)), r(1, 1));
515        assert_eq!(rat_gcd(&r(5, 1), &r(15, 1)), r(5, 1));
516    }
517
518    #[test]
519    fn factorials_and_binomials_stay_whole() {
520        assert_eq!(
521            ext_factorial(&BigInt::from(30)).map(|v| v.to_string()),
522            Some("265252859812191058636308480000000".to_string())
523        );
524        assert_eq!(ext_binomial(&BigInt::from(2), &BigInt::from(5)), Some(BigInt::from(10)));
525        assert_eq!(ext_binomial(&BigInt::from(6), &BigInt::from(5)), Some(BigInt::zero()));
526        assert_eq!(ext_binomial(&BigInt::from(0), &BigInt::from(5)), Some(BigInt::one()));
527    }
528
529    #[test]
530    fn a_power_that_would_exhaust_the_machine_is_refused() {
531        assert!(ext_pow(&BigInt::from(2), 1000).is_some());
532        assert!(ext_pow(&BigInt::from(2), 1 << 30).is_none());
533    }
534
535    #[test]
536    fn huge_ratios_reach_a_float_without_overflowing() {
537        let big = BigInt::from(10).pow(400);
538        assert!((ratio_to_f64(&(&big * 3), &big) - 3.0).abs() < 1e-12);
539    }
540}