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()) && a.is_finite() && b.is_finite() {
240 return a / b;
241 }
242 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
261pub 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
269const CT_BITS: usize = 44;
271
272pub 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
307pub 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
335pub 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
346pub 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
361pub 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
374pub 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
385pub fn ext_binomial(x: &BigInt, y: &BigInt) -> Option<BigInt> {
389 if y.sign() == Sign::Minus {
390 return None;
392 }
393 if x.sign() == Sign::Minus || x > y {
394 return Some(BigInt::zero());
395 }
396 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
407pub 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
416pub 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
425pub 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
434pub 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 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}