1use num_bigint::BigInt;
23use num_integer::Integer;
24use num_rational::BigRational;
25use num_traits::{One, Signed, Zero};
26use std::cmp::Ordering;
27use std::fmt;
28use std::hash::{Hash, Hasher};
29use std::ops::{
30 Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
31};
32
33#[derive(Clone, Debug)]
37pub enum FastRational {
38 Small {
40 num: i64,
42 den: i64,
44 },
45 Big(Box<BigRational>),
47}
48
49#[inline]
57fn gcd_i64(a: i64, b: i64) -> i64 {
58 let mut a = a.unsigned_abs();
60 let mut b = b.unsigned_abs();
61 if a == 0 {
62 return b as i64;
63 }
64 if b == 0 {
65 return a as i64;
66 }
67 let shift = (a | b).trailing_zeros();
69 a >>= a.trailing_zeros();
70 loop {
71 b >>= b.trailing_zeros();
72 if a > b {
73 std::mem::swap(&mut a, &mut b);
74 }
75 b -= a;
76 if b == 0 {
77 break;
78 }
79 }
80 (a << shift) as i64
81}
82
83impl FastRational {
88 #[inline]
95 pub fn new_small(num: i64, den: i64) -> Self {
96 debug_assert!(den != 0, "FastRational: denominator must not be zero");
97 if num == 0 {
98 return FastRational::Small { num: 0, den: 1 };
99 }
100 let (n, d) = if den < 0 {
101 match (num.checked_neg(), den.checked_neg()) {
104 (Some(nn), Some(dd)) => (nn, dd),
105 _ => {
106 let big = BigRational::new(BigInt::from(num), BigInt::from(den));
108 return FastRational::Big(Box::new(big));
109 }
110 }
111 } else {
112 (num, den)
113 };
114 let g = gcd_i64(n.saturating_abs(), d);
115 if g == 0 {
116 return FastRational::Small { num: 0, den: 1 };
117 }
118 FastRational::Small {
119 num: n / g,
120 den: d / g,
121 }
122 }
123
124 pub fn from_big(br: BigRational) -> Self {
126 let n_opt: Option<i64> = br.numer().try_into().ok();
128 let d_opt: Option<i64> = br.denom().try_into().ok();
129 match (n_opt, d_opt) {
130 (Some(n), Some(d)) if d != 0 => FastRational::new_small(n, d),
131 _ => FastRational::Big(Box::new(br)),
132 }
133 }
134
135 pub fn to_big_rational(&self) -> BigRational {
137 match self {
138 FastRational::Small { num, den } => {
139 BigRational::new(BigInt::from(*num), BigInt::from(*den))
140 }
141 FastRational::Big(b) => (**b).clone(),
142 }
143 }
144
145 #[inline]
147 pub fn to_f64(&self) -> f64 {
148 match self {
149 FastRational::Small { num, den } => *num as f64 / *den as f64,
150 FastRational::Big(b) => {
151 use num_traits::ToPrimitive;
152 b.numer().to_f64().unwrap_or(f64::NAN) / b.denom().to_f64().unwrap_or(f64::NAN)
153 }
154 }
155 }
156
157 pub fn recip(&self) -> Option<Self> {
159 match self {
160 FastRational::Small { num, den } => {
161 if *num == 0 {
162 None
163 } else {
164 Some(FastRational::new_small(*den, *num))
165 }
166 }
167 FastRational::Big(b) => {
168 if b.is_zero() {
169 None
170 } else {
171 Some(FastRational::from_big(b.recip()))
172 }
173 }
174 }
175 }
176
177 pub fn floor(&self) -> BigInt {
179 match self {
180 FastRational::Small { num, den } => {
181 if *den == 1 {
182 BigInt::from(*num)
183 } else {
184 let q = num.div_floor(den);
185 BigInt::from(q)
186 }
187 }
188 FastRational::Big(b) => crate::rational::floor(b),
189 }
190 }
191
192 pub fn ceil(&self) -> BigInt {
194 match self {
195 FastRational::Small { num, den } => {
196 if *den == 1 {
197 BigInt::from(*num)
198 } else {
199 let q = if *num >= 0 {
204 (*num + *den - 1) / *den
205 } else {
206 *num / *den
209 };
210 BigInt::from(q)
211 }
212 }
213 FastRational::Big(b) => crate::rational::ceil(b),
214 }
215 }
216
217 pub fn abs(&self) -> Self {
219 match self {
220 FastRational::Small { num, den } => {
221 match num.checked_abs() {
222 Some(n) => FastRational::Small { num: n, den: *den },
223 None => {
224 let big = BigRational::new(BigInt::from(*num).abs(), BigInt::from(*den));
226 FastRational::from_big(big)
227 }
228 }
229 }
230 FastRational::Big(b) => FastRational::from_big(b.abs()),
231 }
232 }
233
234 pub fn numer(&self) -> BigInt {
236 match self {
237 FastRational::Small { num, .. } => BigInt::from(*num),
238 FastRational::Big(b) => b.numer().clone(),
239 }
240 }
241
242 pub fn denom(&self) -> BigInt {
244 match self {
245 FastRational::Small { den, .. } => BigInt::from(*den),
246 FastRational::Big(b) => b.denom().clone(),
247 }
248 }
249
250 #[inline]
252 pub fn is_integer(&self) -> bool {
253 match self {
254 FastRational::Small { den, .. } => *den == 1,
255 FastRational::Big(b) => b.is_integer(),
256 }
257 }
258
259 pub fn from_big_ref(br: &BigRational) -> Self {
261 let n_opt: Option<i64> = br.numer().try_into().ok();
262 let d_opt: Option<i64> = br.denom().try_into().ok();
263 match (n_opt, d_opt) {
264 (Some(n), Some(d)) if d != 0 => FastRational::new_small(n, d),
265 _ => FastRational::Big(Box::new(br.clone())),
266 }
267 }
268
269 pub fn from_bigint(bi: &BigInt) -> Self {
271 let n_opt: Option<i64> = bi.try_into().ok();
272 match n_opt {
273 Some(n) => FastRational::Small { num: n, den: 1 },
274 None => FastRational::Big(Box::new(BigRational::from_integer(bi.clone()))),
275 }
276 }
277}
278
279#[inline]
285fn add_small(a_num: i64, a_den: i64, b_num: i64, b_den: i64) -> FastRational {
286 if let (Some(ad), Some(cb), Some(bd)) = (
288 a_num.checked_mul(b_den),
289 b_num.checked_mul(a_den),
290 a_den.checked_mul(b_den),
291 ) && let Some(num) = ad.checked_add(cb)
292 {
293 return FastRational::new_small(num, bd);
294 }
295 let a = BigRational::new(BigInt::from(a_num), BigInt::from(a_den));
297 let b = BigRational::new(BigInt::from(b_num), BigInt::from(b_den));
298 FastRational::from_big(a + b)
299}
300
301#[inline]
303fn sub_small(a_num: i64, a_den: i64, b_num: i64, b_den: i64) -> FastRational {
304 if let (Some(ad), Some(cb), Some(bd)) = (
306 a_num.checked_mul(b_den),
307 b_num.checked_mul(a_den),
308 a_den.checked_mul(b_den),
309 ) && let Some(num) = ad.checked_sub(cb)
310 {
311 return FastRational::new_small(num, bd);
312 }
313 let a = BigRational::new(BigInt::from(a_num), BigInt::from(a_den));
314 let b = BigRational::new(BigInt::from(b_num), BigInt::from(b_den));
315 FastRational::from_big(a - b)
316}
317
318#[inline]
320fn mul_small(a_num: i64, a_den: i64, b_num: i64, b_den: i64) -> FastRational {
321 let g1 = gcd_i64(a_num.saturating_abs(), b_den);
324 let g2 = gcd_i64(b_num.saturating_abs(), a_den);
325 let an = if g1 != 0 { a_num / g1 } else { a_num };
326 let bd = if g1 != 0 { b_den / g1 } else { b_den };
327 let bn = if g2 != 0 { b_num / g2 } else { b_num };
328 let ad = if g2 != 0 { a_den / g2 } else { a_den };
329
330 if let (Some(num), Some(den)) = (an.checked_mul(bn), ad.checked_mul(bd)) {
331 return FastRational::new_small(num, den);
332 }
333 let a = BigRational::new(BigInt::from(a_num), BigInt::from(a_den));
334 let b = BigRational::new(BigInt::from(b_num), BigInt::from(b_den));
335 FastRational::from_big(a * b)
336}
337
338#[inline]
341fn div_small(a_num: i64, a_den: i64, b_num: i64, b_den: i64) -> Option<FastRational> {
342 if b_num == 0 {
343 return None;
344 }
345 if let (Some(num), Some(den)) = (a_num.checked_mul(b_den), a_den.checked_mul(b_num)) {
349 Some(FastRational::new_small(num, den))
350 } else {
351 let a = BigRational::new(BigInt::from(a_num), BigInt::from(a_den));
352 let b = BigRational::new(BigInt::from(b_num), BigInt::from(b_den));
353 Some(FastRational::from_big(a / b))
354 }
355}
356
357impl From<i64> for FastRational {
362 #[inline]
363 fn from(n: i64) -> Self {
364 FastRational::Small { num: n, den: 1 }
365 }
366}
367
368impl From<(i64, i64)> for FastRational {
369 fn from((n, d): (i64, i64)) -> Self {
370 if d == 0 {
371 FastRational::Small { num: 0, den: 1 }
373 } else {
374 FastRational::new_small(n, d)
375 }
376 }
377}
378
379impl From<BigRational> for FastRational {
380 fn from(br: BigRational) -> Self {
381 FastRational::from_big(br)
382 }
383}
384
385impl From<BigInt> for FastRational {
386 fn from(bi: BigInt) -> Self {
387 FastRational::from_bigint(&bi)
388 }
389}
390
391impl From<&BigRational> for FastRational {
392 fn from(br: &BigRational) -> Self {
393 FastRational::from_big_ref(br)
394 }
395}
396
397impl Zero for FastRational {
402 #[inline]
403 fn zero() -> Self {
404 FastRational::Small { num: 0, den: 1 }
405 }
406
407 #[inline]
408 fn is_zero(&self) -> bool {
409 match self {
410 FastRational::Small { num, .. } => *num == 0,
411 FastRational::Big(b) => b.is_zero(),
412 }
413 }
414}
415
416impl One for FastRational {
417 #[inline]
418 fn one() -> Self {
419 FastRational::Small { num: 1, den: 1 }
420 }
421
422 #[inline]
423 fn is_one(&self) -> bool {
424 match self {
425 FastRational::Small { num, den } => *num == 1 && *den == 1,
426 FastRational::Big(b) => b.is_one(),
427 }
428 }
429}
430
431impl Signed for FastRational {
436 fn abs(&self) -> Self {
437 FastRational::abs(self)
438 }
439
440 fn abs_sub(&self, other: &Self) -> Self {
441 let diff = self - other;
442 if diff.is_positive() {
443 diff
444 } else {
445 FastRational::zero()
446 }
447 }
448
449 fn signum(&self) -> Self {
450 if self.is_zero() {
451 FastRational::zero()
452 } else if self.is_positive() {
453 FastRational::one()
454 } else {
455 FastRational::Small { num: -1, den: 1 }
456 }
457 }
458
459 fn is_positive(&self) -> bool {
460 match self {
461 FastRational::Small { num, .. } => *num > 0,
462 FastRational::Big(b) => b.is_positive(),
463 }
464 }
465
466 fn is_negative(&self) -> bool {
467 match self {
468 FastRational::Small { num, .. } => *num < 0,
469 FastRational::Big(b) => b.is_negative(),
470 }
471 }
472}
473
474impl num_traits::Num for FastRational {
475 type FromStrRadixErr = String;
476
477 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
478 if let Some((num_str, den_str)) = str.split_once('/') {
480 let num = BigInt::from_str_radix(num_str.trim(), radix)
481 .map_err(|e| format!("invalid numerator: {}", e))?;
482 let den = BigInt::from_str_radix(den_str.trim(), radix)
483 .map_err(|e| format!("invalid denominator: {}", e))?;
484 if den.is_zero() {
485 return Err("denominator is zero".to_string());
486 }
487 Ok(FastRational::from_big(BigRational::new(num, den)))
488 } else {
489 let num = BigInt::from_str_radix(str.trim(), radix)
490 .map_err(|e| format!("invalid integer: {}", e))?;
491 Ok(FastRational::from_bigint(&num))
492 }
493 }
494}
495
496impl Neg for FastRational {
501 type Output = FastRational;
502
503 #[inline]
504 fn neg(self) -> Self::Output {
505 match self {
506 FastRational::Small { num, den } => match num.checked_neg() {
507 Some(n) => FastRational::Small { num: n, den },
508 None => {
509 let big = BigRational::new(-BigInt::from(num), BigInt::from(den));
510 FastRational::Big(Box::new(big))
511 }
512 },
513 FastRational::Big(b) => FastRational::from_big(-*b),
514 }
515 }
516}
517
518impl Neg for &FastRational {
519 type Output = FastRational;
520
521 #[inline]
522 fn neg(self) -> Self::Output {
523 match self {
524 FastRational::Small { num, den } => match num.checked_neg() {
525 Some(n) => FastRational::Small { num: n, den: *den },
526 None => {
527 let big = BigRational::new(-BigInt::from(*num), BigInt::from(*den));
528 FastRational::Big(Box::new(big))
529 }
530 },
531 FastRational::Big(b) => FastRational::from_big(-((**b).clone())),
532 }
533 }
534}
535
536impl Add for FastRational {
541 type Output = FastRational;
542
543 #[inline]
544 fn add(self, rhs: FastRational) -> Self::Output {
545 (&self).add(&rhs)
546 }
547}
548
549impl Add<&FastRational> for FastRational {
550 type Output = FastRational;
551
552 #[inline]
553 fn add(self, rhs: &FastRational) -> Self::Output {
554 (&self).add(rhs)
555 }
556}
557
558impl Add<FastRational> for &FastRational {
559 type Output = FastRational;
560
561 #[inline]
562 fn add(self, rhs: FastRational) -> Self::Output {
563 self.add(&rhs)
564 }
565}
566
567impl Add<&FastRational> for &FastRational {
568 type Output = FastRational;
569
570 #[inline]
571 fn add(self, rhs: &FastRational) -> Self::Output {
572 match (self, rhs) {
573 (
574 FastRational::Small { num: an, den: ad },
575 FastRational::Small { num: bn, den: bd },
576 ) => add_small(*an, *ad, *bn, *bd),
577 (a, b) => {
578 let big_a = a.to_big_rational();
579 let big_b = b.to_big_rational();
580 FastRational::from_big(big_a + big_b)
581 }
582 }
583 }
584}
585
586impl AddAssign for FastRational {
587 #[inline]
588 fn add_assign(&mut self, rhs: FastRational) {
589 *self = (&*self) + &rhs;
590 }
591}
592
593impl AddAssign<&FastRational> for FastRational {
594 #[inline]
595 fn add_assign(&mut self, rhs: &FastRational) {
596 *self = (&*self) + rhs;
597 }
598}
599
600impl Sub for FastRational {
605 type Output = FastRational;
606
607 #[inline]
608 fn sub(self, rhs: FastRational) -> Self::Output {
609 (&self).sub(&rhs)
610 }
611}
612
613impl Sub<&FastRational> for FastRational {
614 type Output = FastRational;
615
616 #[inline]
617 fn sub(self, rhs: &FastRational) -> Self::Output {
618 (&self).sub(rhs)
619 }
620}
621
622impl Sub<FastRational> for &FastRational {
623 type Output = FastRational;
624
625 #[inline]
626 fn sub(self, rhs: FastRational) -> Self::Output {
627 self.sub(&rhs)
628 }
629}
630
631impl Sub<&FastRational> for &FastRational {
632 type Output = FastRational;
633
634 #[inline]
635 fn sub(self, rhs: &FastRational) -> Self::Output {
636 match (self, rhs) {
637 (
638 FastRational::Small { num: an, den: ad },
639 FastRational::Small { num: bn, den: bd },
640 ) => sub_small(*an, *ad, *bn, *bd),
641 (a, b) => {
642 let big_a = a.to_big_rational();
643 let big_b = b.to_big_rational();
644 FastRational::from_big(big_a - big_b)
645 }
646 }
647 }
648}
649
650impl SubAssign for FastRational {
651 #[inline]
652 fn sub_assign(&mut self, rhs: FastRational) {
653 *self = (&*self) - &rhs;
654 }
655}
656
657impl SubAssign<&FastRational> for FastRational {
658 #[inline]
659 fn sub_assign(&mut self, rhs: &FastRational) {
660 *self = (&*self) - rhs;
661 }
662}
663
664impl Mul for FastRational {
669 type Output = FastRational;
670
671 #[inline]
672 fn mul(self, rhs: FastRational) -> Self::Output {
673 (&self).mul(&rhs)
674 }
675}
676
677impl Mul<&FastRational> for FastRational {
678 type Output = FastRational;
679
680 #[inline]
681 fn mul(self, rhs: &FastRational) -> Self::Output {
682 (&self).mul(rhs)
683 }
684}
685
686impl Mul<FastRational> for &FastRational {
687 type Output = FastRational;
688
689 #[inline]
690 fn mul(self, rhs: FastRational) -> Self::Output {
691 self.mul(&rhs)
692 }
693}
694
695impl Mul<&FastRational> for &FastRational {
696 type Output = FastRational;
697
698 #[inline]
699 fn mul(self, rhs: &FastRational) -> Self::Output {
700 match (self, rhs) {
701 (
702 FastRational::Small { num: an, den: ad },
703 FastRational::Small { num: bn, den: bd },
704 ) => mul_small(*an, *ad, *bn, *bd),
705 (a, b) => {
706 let big_a = a.to_big_rational();
707 let big_b = b.to_big_rational();
708 FastRational::from_big(big_a * big_b)
709 }
710 }
711 }
712}
713
714impl MulAssign for FastRational {
715 #[inline]
716 fn mul_assign(&mut self, rhs: FastRational) {
717 *self = (&*self) * &rhs;
718 }
719}
720
721impl MulAssign<&FastRational> for FastRational {
722 #[inline]
723 fn mul_assign(&mut self, rhs: &FastRational) {
724 *self = (&*self) * rhs;
725 }
726}
727
728impl Div for FastRational {
733 type Output = FastRational;
734
735 #[inline]
741 fn div(self, rhs: FastRational) -> Self::Output {
742 (&self).div(&rhs)
743 }
744}
745
746impl Div<&FastRational> for FastRational {
747 type Output = FastRational;
748
749 #[inline]
750 fn div(self, rhs: &FastRational) -> Self::Output {
751 (&self).div(rhs)
752 }
753}
754
755impl Div<FastRational> for &FastRational {
756 type Output = FastRational;
757
758 #[inline]
759 fn div(self, rhs: FastRational) -> Self::Output {
760 self.div(&rhs)
761 }
762}
763
764impl Div<&FastRational> for &FastRational {
765 type Output = FastRational;
766
767 #[inline]
768 fn div(self, rhs: &FastRational) -> Self::Output {
769 match (self, rhs) {
770 (
771 FastRational::Small { num: an, den: ad },
772 FastRational::Small { num: bn, den: bd },
773 ) => match div_small(*an, *ad, *bn, *bd) {
774 Some(r) => r,
775 None => {
776 debug_assert!(false, "FastRational: division by zero");
777 FastRational::zero()
778 }
779 },
780 (a, b) => {
781 if b.is_zero() {
782 debug_assert!(false, "FastRational: division by zero");
783 return FastRational::zero();
784 }
785 let big_a = a.to_big_rational();
786 let big_b = b.to_big_rational();
787 FastRational::from_big(big_a / big_b)
788 }
789 }
790 }
791}
792
793impl DivAssign for FastRational {
794 #[inline]
795 fn div_assign(&mut self, rhs: FastRational) {
796 *self = (&*self) / &rhs;
797 }
798}
799
800impl DivAssign<&FastRational> for FastRational {
801 #[inline]
802 fn div_assign(&mut self, rhs: &FastRational) {
803 *self = (&*self) / rhs;
804 }
805}
806
807impl Rem for FastRational {
812 type Output = FastRational;
813
814 #[inline]
818 fn rem(self, rhs: FastRational) -> Self::Output {
819 (&self).rem(&rhs)
820 }
821}
822
823impl Rem<&FastRational> for &FastRational {
824 type Output = FastRational;
825
826 #[inline]
827 fn rem(self, rhs: &FastRational) -> Self::Output {
828 if rhs.is_zero() {
829 return FastRational::zero();
830 }
831 let quotient = self / rhs;
832 let floor_q = FastRational::from_integer(quotient.floor());
833 self - &(rhs * &floor_q)
834 }
835}
836
837impl RemAssign for FastRational {
838 #[inline]
839 fn rem_assign(&mut self, rhs: FastRational) {
840 *self = (&*self).rem(&rhs);
841 }
842}
843
844impl PartialEq for FastRational {
849 fn eq(&self, other: &Self) -> bool {
850 match (self, other) {
851 (
852 FastRational::Small { num: an, den: ad },
853 FastRational::Small { num: bn, den: bd },
854 ) => {
855 an == bn && ad == bd
857 }
858 (a, b) => {
859 a.to_big_rational() == b.to_big_rational()
861 }
862 }
863 }
864}
865
866impl Eq for FastRational {}
867
868impl PartialOrd for FastRational {
873 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
874 Some(self.cmp(other))
875 }
876}
877
878impl Ord for FastRational {
879 fn cmp(&self, other: &Self) -> Ordering {
880 match (self, other) {
881 (
882 FastRational::Small { num: an, den: ad },
883 FastRational::Small { num: bn, den: bd },
884 ) => {
885 if let (Some(lhs), Some(rhs)) = (an.checked_mul(*bd), bn.checked_mul(*ad)) {
889 return lhs.cmp(&rhs);
890 }
891 let big_a = BigRational::new(BigInt::from(*an), BigInt::from(*ad));
893 let big_b = BigRational::new(BigInt::from(*bn), BigInt::from(*bd));
894 big_a.cmp(&big_b)
895 }
896 (a, b) => {
897 let big_a = a.to_big_rational();
898 let big_b = b.to_big_rational();
899 big_a.cmp(&big_b)
900 }
901 }
902 }
903}
904
905impl Hash for FastRational {
910 fn hash<H: Hasher>(&self, state: &mut H) {
911 match self {
915 FastRational::Small { num, den } => {
916 num.hash(state);
917 den.hash(state);
918 }
919 FastRational::Big(b) => {
920 let reduced = b.reduced();
922 let n_opt: Option<i64> = reduced.numer().try_into().ok();
923 let d_opt: Option<i64> = reduced.denom().try_into().ok();
924 match (n_opt, d_opt) {
925 (Some(n), Some(d)) if d > 0 => {
926 n.hash(state);
927 d.hash(state);
928 }
929 (Some(n), Some(d)) if d < 0 => {
930 (-n).hash(state);
932 (-d).hash(state);
933 }
934 _ => {
935 let (n, d) = if reduced.denom().is_negative() {
938 (-reduced.numer(), -reduced.denom())
939 } else {
940 (reduced.numer().clone(), reduced.denom().clone())
941 };
942 n.hash(state);
943 d.hash(state);
944 }
945 }
946 }
947 }
948 }
949}
950
951impl fmt::Display for FastRational {
956 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
957 match self {
958 FastRational::Small { num, den } => {
959 if *den == 1 {
960 write!(f, "{}", num)
961 } else {
962 write!(f, "{}/{}", num, den)
963 }
964 }
965 FastRational::Big(b) => write!(f, "{}", b),
966 }
967 }
968}
969
970impl FastRational {
975 #[inline]
977 pub fn from_integer(n: BigInt) -> Self {
978 FastRational::from_bigint(&n)
979 }
980}
981
982impl FastRational {
987 #[inline]
989 pub fn max(self, other: Self) -> Self {
990 if self >= other { self } else { other }
991 }
992
993 #[inline]
995 pub fn min(self, other: Self) -> Self {
996 if self <= other { self } else { other }
997 }
998}
999
1000#[cfg(test)]
1005mod tests {
1006 use super::*;
1007 use std::collections::hash_map::DefaultHasher;
1008
1009 fn small(n: i64, d: i64) -> FastRational {
1010 FastRational::new_small(n, d)
1011 }
1012
1013 fn fr(n: i64) -> FastRational {
1014 FastRational::from(n)
1015 }
1016
1017 fn hash_of(val: &FastRational) -> u64 {
1018 let mut h = DefaultHasher::new();
1019 val.hash(&mut h);
1020 h.finish()
1021 }
1022
1023 #[test]
1026 fn test_new_small_normalization() {
1027 let r = small(2, 4);
1029 assert_eq!(r, small(1, 2));
1030 }
1031
1032 #[test]
1033 fn test_new_small_negative_den() {
1034 let r = small(3, -5);
1036 assert_eq!(r, small(-3, 5));
1037 }
1038
1039 #[test]
1040 fn test_new_small_zero() {
1041 let r = small(0, 42);
1042 assert_eq!(r, FastRational::zero());
1043 }
1044
1045 #[test]
1046 fn test_from_i64() {
1047 let r = FastRational::from(7i64);
1048 assert_eq!(r, small(7, 1));
1049 }
1050
1051 #[test]
1052 fn test_from_tuple() {
1053 let r = FastRational::from((6i64, 4i64));
1054 assert_eq!(r, small(3, 2));
1055 }
1056
1057 #[test]
1058 fn test_from_bigrational() {
1059 let br = BigRational::new(BigInt::from(10), BigInt::from(4));
1060 let r = FastRational::from(br);
1061 assert_eq!(r, small(5, 2));
1062 }
1063
1064 #[test]
1067 fn test_add() {
1068 let a = small(1, 2);
1070 let b = small(1, 3);
1071 assert_eq!(&a + &b, small(5, 6));
1072 }
1073
1074 #[test]
1075 fn test_sub() {
1076 let a = small(3, 4);
1078 let b = small(1, 4);
1079 assert_eq!(&a - &b, small(1, 2));
1080 }
1081
1082 #[test]
1083 fn test_mul() {
1084 let a = small(2, 3);
1086 let b = small(3, 4);
1087 assert_eq!(&a * &b, small(1, 2));
1088 }
1089
1090 #[test]
1091 fn test_div() {
1092 let a = small(2, 3);
1094 let b = small(4, 5);
1095 assert_eq!(&a / &b, small(5, 6));
1096 }
1097
1098 #[test]
1099 fn test_neg() {
1100 let r = small(3, 5);
1101 assert_eq!(-r, small(-3, 5));
1102 }
1103
1104 #[test]
1105 fn test_add_assign() {
1106 let mut a = small(1, 2);
1107 a += small(1, 3);
1108 assert_eq!(a, small(5, 6));
1109 }
1110
1111 #[test]
1112 fn test_mul_assign() {
1113 let mut a = small(2, 3);
1114 a *= small(3, 4);
1115 assert_eq!(a, small(1, 2));
1116 }
1117
1118 #[test]
1121 fn test_overflow_to_big() {
1122 let big_val = i64::MAX;
1123 let a = fr(big_val);
1124 let b = fr(big_val);
1125 let result = &a + &b;
1126 let expected = BigRational::from_integer(BigInt::from(big_val))
1128 + BigRational::from_integer(BigInt::from(big_val));
1129 assert_eq!(result.to_big_rational(), expected);
1130 }
1131
1132 #[test]
1135 fn test_ord() {
1136 assert!(small(1, 3) < small(1, 2));
1137 assert!(small(-1, 2) < small(1, 2));
1138 assert!(small(0, 1) == FastRational::zero());
1139 }
1140
1141 #[test]
1142 fn test_eq_cross_representation() {
1143 let s = small(1, 2);
1145 let b = FastRational::Big(Box::new(BigRational::new(BigInt::from(1), BigInt::from(2))));
1146 assert_eq!(s, b);
1147 }
1148
1149 #[test]
1152 fn test_hash_consistency() {
1153 let s = small(1, 2);
1154 let b = FastRational::Big(Box::new(BigRational::new(BigInt::from(1), BigInt::from(2))));
1155 assert_eq!(hash_of(&s), hash_of(&b));
1156 }
1157
1158 #[test]
1161 fn test_zero_one() {
1162 assert!(FastRational::zero().is_zero());
1163 assert!(FastRational::one().is_one());
1164 assert!(!FastRational::zero().is_one());
1165 assert!(!FastRational::one().is_zero());
1166 }
1167
1168 #[test]
1169 fn test_signed() {
1170 assert!(small(3, 5).is_positive());
1171 assert!(small(-3, 5).is_negative());
1172 assert!(!FastRational::zero().is_positive());
1173 assert!(!FastRational::zero().is_negative());
1174 }
1175
1176 #[test]
1177 fn test_abs() {
1178 assert_eq!(small(-3, 5).abs(), small(3, 5));
1179 assert_eq!(small(3, 5).abs(), small(3, 5));
1180 }
1181
1182 #[test]
1185 fn test_recip() {
1186 assert_eq!(small(3, 5).recip(), Some(small(5, 3)));
1187 assert_eq!(FastRational::zero().recip(), None);
1188 }
1189
1190 #[test]
1191 fn test_floor_ceil() {
1192 let r = small(7, 3);
1194 assert_eq!(r.floor(), BigInt::from(2));
1195 assert_eq!(r.ceil(), BigInt::from(3));
1196
1197 let r = small(-7, 3);
1199 assert_eq!(r.floor(), BigInt::from(-3));
1200 assert_eq!(r.ceil(), BigInt::from(-2));
1201 }
1202
1203 #[test]
1204 fn test_to_f64() {
1205 let r = small(1, 2);
1206 assert!((r.to_f64() - 0.5).abs() < 1e-10);
1207 }
1208
1209 #[test]
1210 fn test_to_big_rational_roundtrip() {
1211 let r = small(7, 13);
1212 let big = r.to_big_rational();
1213 let back = FastRational::from_big(big);
1214 assert_eq!(r, back);
1215 }
1216
1217 #[test]
1218 fn test_numer_denom() {
1219 let r = small(3, 7);
1220 assert_eq!(r.numer(), BigInt::from(3));
1221 assert_eq!(r.denom(), BigInt::from(7));
1222 }
1223
1224 #[test]
1225 fn test_is_integer() {
1226 assert!(fr(5).is_integer());
1227 assert!(!small(5, 3).is_integer());
1228 }
1229
1230 #[test]
1231 fn test_display() {
1232 assert_eq!(format!("{}", fr(5)), "5");
1233 assert_eq!(format!("{}", small(3, 7)), "3/7");
1234 assert_eq!(format!("{}", small(-1, 2)), "-1/2");
1235 }
1236
1237 #[test]
1238 fn test_max_min() {
1239 let a = small(1, 3);
1240 let b = small(1, 2);
1241 assert_eq!(a.clone().max(b.clone()), b);
1242 assert_eq!(a.clone().min(b.clone()), a);
1243 }
1244
1245 #[test]
1246 fn test_from_str_radix() {
1247 use num_traits::Num;
1248 let r = FastRational::from_str_radix("3/7", 10);
1249 assert!(r.is_ok());
1250 assert_eq!(r.ok(), Some(small(3, 7)));
1251
1252 let r2 = FastRational::from_str_radix("42", 10);
1253 assert!(r2.is_ok());
1254 assert_eq!(r2.ok(), Some(fr(42)));
1255 }
1256}