1use 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
20pub type Ext = BigInt;
22
23pub const MAX_BITS: u64 = 1 << 26;
27
28#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct Rat {
32 num: BigInt,
33 den: BigInt,
34}
35
36impl Rat {
37 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 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 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 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 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 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 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 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 (&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
215impl 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
226pub 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 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
263pub 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
271const CT_BITS: usize = 44;
273
274pub 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
309pub 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
337pub 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
348pub 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
363pub 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
376pub 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
387pub fn ext_binomial(x: &BigInt, y: &BigInt) -> Option<BigInt> {
391 if y.sign() == Sign::Minus {
392 return None;
394 }
395 if x.sign() == Sign::Minus || x > y {
396 return Some(BigInt::zero());
397 }
398 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
409pub 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
418pub 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
427pub 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
436pub 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 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}