1pub mod ratio_trait;
74
75pub use crate::ratio_trait::{RatioInteger, WideInteger};
76use core::cmp::Ordering;
77
78#[derive(Clone, Debug)]
104pub struct Ratio<T: RatioInteger> {
105 pub numer: T,
107 pub denom: T,
109 pub negative: bool,
111}
112
113impl<T: RatioInteger> Ratio<T> {
114 #[inline(always)]
134 pub fn new_raw(numer: T, denom: T, negative: bool) -> Self {
135 Self {
136 numer,
137 denom,
138 negative,
139 }
140 }
141
142 pub fn new(numer: T, denom: T) -> Self {
158 if numer.is_zero_bool() {
159 return Self::zero();
160 }
161 let g = T::gcd(numer.clone(), denom.clone());
162 Self {
163 numer: numer.wrapping_div(&g),
164 denom: denom.wrapping_div(&g),
165 negative: false,
166 }
167 }
168
169 #[inline]
182 pub fn from_u64(numer: u64, denom: u64) -> Self {
183 let g = gcd_u64(numer, denom);
184 Self {
185 numer: T::from_u64(numer / g),
186 denom: T::from_u64(denom / g),
187 negative: false,
188 }
189 }
190
191 #[inline(always)]
193 pub fn one() -> Self {
194 Self {
195 numer: T::ONE,
196 denom: T::ONE,
197 negative: false,
198 }
199 }
200
201 #[inline(always)]
203 pub fn zero() -> Self {
204 Self {
205 numer: T::ZERO,
206 denom: T::ONE,
207 negative: false,
208 }
209 }
210
211 #[allow(clippy::should_implement_trait)] #[inline(always)]
218 pub fn neg(mut self) -> Self {
219 if !self.numer.is_zero_bool() {
220 self.negative = !self.negative;
221 }
222 self
223 }
224
225 #[inline(always)]
227 pub fn abs(&self) -> Self {
228 Self {
229 numer: self.numer.clone(),
230 denom: self.denom.clone(),
231 negative: false,
232 }
233 }
234
235 #[inline(always)]
237 pub fn is_zero(&self) -> bool {
238 self.numer.is_zero_bool()
239 }
240
241 #[inline]
243 pub fn is_positive(&self) -> bool {
244 !self.negative && !self.numer.is_zero_bool()
245 }
246
247 #[inline]
249 pub fn is_negative(&self) -> bool {
250 self.negative && !self.numer.is_zero_bool()
251 }
252
253 #[inline]
255 pub fn is_integer(&self) -> bool {
256 self.denom == T::ONE
257 }
258
259 #[inline(always)]
268 pub fn needs_reduction(&self) -> bool {
269 self.numer.bits_u32() > T::REDUCTION_THRESHOLD
270 || self.denom.bits_u32() > T::REDUCTION_THRESHOLD
271 }
272
273 #[inline]
277 pub fn normalize_if_needed(&mut self) {
278 if self.needs_reduction() {
279 self.normalize();
280 }
281 }
282
283 #[inline]
305 #[allow(clippy::should_implement_trait)]
306 pub fn normalize(&mut self) {
307 if self.numer.is_zero_bool() {
308 self.denom = T::ONE;
309 self.negative = false;
310 return;
311 }
312
313 let g = T::gcd(self.numer.clone(), self.denom.clone());
314 if g == T::ONE {
315 return;
316 }
317
318 let trailing = g.trailing_zeros_u32();
319 let bits = g.bits_u32();
320
321 if trailing > 0 && bits == trailing + 1 {
322 self.numer = self.numer.shr_vartime_u32(trailing);
323 self.denom = self.denom.shr_vartime_u32(trailing);
324 } else {
325 self.numer = self.numer.wrapping_div(&g);
326 self.denom = self.denom.wrapping_div(&g);
327 }
328 }
329
330 pub fn from_float(f: f64) -> Option<Self> {
348 if !f.is_finite() {
349 return None;
350 }
351 let negative = f.is_sign_negative();
352 let abs_f = f.abs();
353
354 const SCALE_POWER: u32 = 52;
355 let scale = 1u64 << SCALE_POWER;
356 let scaled = (abs_f * scale as f64) as u128;
357
358 if scaled == 0 {
359 return Some(Self::zero());
360 }
361
362 if scaled <= u64::MAX as u128 {
363 let n_u64 = scaled as u64;
364 let g = gcd_u64(n_u64, scale);
365 return Some(Self {
366 numer: T::from_u64(n_u64 / g),
367 denom: T::from_u64(scale / g),
368 negative,
369 });
370 }
371
372 let n = T::from_u128(scaled);
373 let d = T::from_u64(scale);
374 let g = T::gcd(n.clone(), d.clone());
375
376 Some(Self {
377 numer: n.wrapping_div(&g),
378 denom: d.wrapping_div(&g),
379 negative,
380 })
381 }
382
383 #[inline]
396 pub fn to_f64_approx(&self) -> f64 {
397 let n_bits = self.numer.bits_u32();
398 let d_bits = self.denom.bits_u32();
399
400 if n_bits == 0 {
401 return 0.0;
402 }
403
404 let n_shift = n_bits.saturating_sub(64);
405 let d_shift = d_bits.saturating_sub(64);
406
407 let n_approx = self.numer.shr_vartime_u32(n_shift).first_word() as f64;
408 let d_approx = self.denom.shr_vartime_u32(d_shift).first_word() as f64;
409
410 let exp_diff = (n_shift as i32) - (d_shift as i32);
411 let val = n_approx / d_approx * 2f64.powi(exp_diff);
412
413 if self.negative {
414 -val
415 } else {
416 val
417 }
418 }
419
420 #[inline]
439 pub fn add(&self, other: &Self) -> Self {
440 if self.denom == other.denom {
441 let (n, neg) = self.add_sub_numer(other);
442 return Self {
443 numer: n,
444 denom: self.denom.clone(),
445 negative: neg,
446 };
447 }
448
449 let (ad, ad_hi) = self.numer.mul_wide(&other.denom);
450 let (bc, bc_hi) = other.numer.mul_wide(&self.denom);
451 let (bd, bd_hi) = self.denom.mul_wide(&other.denom);
452
453 if ad_hi.is_zero_bool() && bc_hi.is_zero_bool() && bd_hi.is_zero_bool() {
454 let (numer, negative) = match (self.negative, other.negative) {
455 (false, false) => (ad.wrapping_add(&bc), false),
456 (true, true) => (ad.wrapping_add(&bc), true),
457 (false, true) if ad >= bc => (ad.wrapping_sub(&bc), false),
458 (false, true) => (bc.wrapping_sub(&ad), true),
459 (true, false) if bc >= ad => (bc.wrapping_sub(&ad), false),
460 (true, false) => (ad.wrapping_sub(&bc), true),
461 };
462
463 return Self {
464 numer,
465 denom: bd,
466 negative,
467 };
468 }
469
470 self.add_wide(other)
471 }
472
473 #[inline(always)]
474 fn add_sub_numer(&self, other: &Self) -> (T, bool) {
475 match (self.negative, other.negative) {
476 (false, false) => (self.numer.wrapping_add(&other.numer), false),
477 (true, true) => (self.numer.wrapping_add(&other.numer), true),
478 (false, true) if self.numer >= other.numer => {
479 (self.numer.wrapping_sub(&other.numer), false)
480 }
481 (false, true) => (other.numer.wrapping_sub(&self.numer), true),
482 (true, false) if other.numer >= self.numer => {
483 (other.numer.wrapping_sub(&self.numer), false)
484 }
485 (true, false) => (self.numer.wrapping_sub(&other.numer), true),
486 }
487 }
488
489 #[cold]
490 #[inline(never)]
491 fn add_wide(&self, other: &Self) -> Self {
492 let a = self.numer.to_wide();
493 let b = self.denom.to_wide();
494 let c = other.numer.to_wide();
495 let d = other.denom.to_wide();
496
497 let ad = a.wrapping_mul(&d);
498 let bc = b.wrapping_mul(&c);
499 let bd = b.wrapping_mul(&d);
500
501 let (numer_wide, negative) = match (self.negative, other.negative) {
502 (false, false) => (ad.wrapping_add(&bc), false),
503 (true, true) => (ad.wrapping_add(&bc), true),
504 (false, true) => {
505 if ad >= bc {
506 (ad.wrapping_sub(&bc), false)
507 } else {
508 (bc.wrapping_sub(&ad), true)
509 }
510 }
511 (true, false) => {
512 if bc >= ad {
513 (bc.wrapping_sub(&ad), false)
514 } else {
515 (ad.wrapping_sub(&bc), true)
516 }
517 }
518 };
519
520 let g = T::Wide::gcd(numer_wide.clone(), bd.clone());
521 let normalized_numer = numer_wide.wrapping_div(&g);
522 let normalized_denom = bd.wrapping_div(&g);
523
524 let numer =
525 T::from_wide_checked(&normalized_numer).expect("numerator overflow after reduction");
526 let denom =
527 T::from_wide_checked(&normalized_denom).expect("denominator overflow after reduction");
528
529 Self {
530 numer,
531 denom,
532 negative,
533 }
534 }
535
536 #[inline]
556 pub fn mul(&self, other: &Self) -> Self {
557 let negative = self.negative ^ other.negative;
558
559 let self_max_bits = self.numer.bits_u32().max(self.denom.bits_u32());
560 let other_max_bits = other.numer.bits_u32().max(other.denom.bits_u32());
561 let large_threshold = (T::BITS * 6) / 10;
562
563 let (self_work, other_work) =
564 if self_max_bits > large_threshold || other_max_bits > large_threshold {
565 let mut s = self.clone();
566 let mut o = other.clone();
567 if self_max_bits > large_threshold {
568 s.normalize();
569 }
570 if other_max_bits > large_threshold {
571 o.normalize();
572 }
573 (s, o)
574 } else {
575 (self.clone(), other.clone())
576 };
577
578 let (ac, ac_hi) = self_work.numer.mul_wide(&other_work.numer);
579 let (bd, bd_hi) = self_work.denom.mul_wide(&other_work.denom);
580
581 if ac_hi.is_zero_bool() && bd_hi.is_zero_bool() {
582 return Self {
583 numer: ac,
584 denom: bd,
585 negative,
586 };
587 }
588
589 let g_ad = T::gcd(self_work.numer.clone(), other_work.denom.clone());
590 let g_bc = T::gcd(self_work.denom.clone(), other_work.numer.clone());
591
592 let a = self_work.numer.wrapping_div(&g_ad);
593 let c = other_work.numer.wrapping_div(&g_bc);
594 let b = self_work.denom.wrapping_div(&g_bc);
595 let d = other_work.denom.wrapping_div(&g_ad);
596
597 let (ac, ac_hi) = a.mul_wide(&c);
598 let (bd, bd_hi) = b.mul_wide(&d);
599
600 if !ac_hi.is_zero_bool() || !bd_hi.is_zero_bool() {
601 panic!("multiplication overflow after cross-cancellation");
602 }
603
604 Self {
605 numer: ac,
606 denom: bd,
607 negative,
608 }
609 }
610
611 #[inline]
629 pub fn mul_reduced(&self, other: &Self) -> Self {
630 let self_max_bits = self.numer.bits_u32().max(self.denom.bits_u32());
631 let other_max_bits = other.numer.bits_u32().max(other.denom.bits_u32());
632 let large_threshold = (T::BITS * 6) / 10;
633
634 let (self_work, other_work) =
635 if self_max_bits > large_threshold || other_max_bits > large_threshold {
636 let mut s = self.clone();
637 let mut o = other.clone();
638 if self_max_bits > large_threshold {
639 s.normalize();
640 }
641 if other_max_bits > large_threshold {
642 o.normalize();
643 }
644 (s, o)
645 } else {
646 (self.clone(), other.clone())
647 };
648
649 let negative = self.negative ^ other.negative;
650
651 let (ac, ac_hi) = self_work.numer.mul_wide(&other_work.numer);
652 let (bd, bd_hi) = self_work.denom.mul_wide(&other_work.denom);
653
654 if ac_hi.is_zero_bool() && bd_hi.is_zero_bool() {
655 let mut numer = ac;
656 let mut denom = bd;
657
658 let trailing_n = numer.trailing_zeros_u32();
659 let trailing_d = denom.trailing_zeros_u32();
660 let common_trailing = trailing_n.min(trailing_d);
661
662 if common_trailing > 0 {
663 numer = numer.shr_vartime_u32(common_trailing);
664 denom = denom.shr_vartime_u32(common_trailing);
665 }
666
667 let numer_bits = numer.bits_u32();
668 let denom_bits = denom.bits_u32();
669
670 if numer_bits <= 64 && denom_bits <= 64 {
671 if let (Some(n_u64), Some(d_u64)) = (numer.try_to_u64(), denom.try_to_u64()) {
672 let g = gcd_u64(n_u64, d_u64);
673 if g > 1 {
674 return Self {
675 numer: T::from_u64(n_u64 / g),
676 denom: T::from_u64(d_u64 / g),
677 negative,
678 };
679 }
680 return Self {
681 numer,
682 denom,
683 negative,
684 };
685 }
686 }
687
688 if numer_bits <= 128 && denom_bits <= 128 {
689 let g = T::gcd(numer.clone(), denom.clone());
690 if g != T::ONE {
691 return Self {
692 numer: numer.wrapping_div(&g),
693 denom: denom.wrapping_div(&g),
694 negative,
695 };
696 }
697 }
698
699 return Self {
700 numer,
701 denom,
702 negative,
703 };
704 }
705
706 let g_ad = T::gcd(self_work.numer.clone(), other_work.denom.clone());
707 let g_bc = T::gcd(self_work.denom.clone(), other_work.numer.clone());
708
709 let a = self_work.numer.wrapping_div(&g_ad);
710 let c = other_work.numer.wrapping_div(&g_bc);
711 let b = self_work.denom.wrapping_div(&g_bc);
712 let d = other_work.denom.wrapping_div(&g_ad);
713
714 let (ac, ac_hi) = a.mul_wide(&c);
715 let (bd, bd_hi) = b.mul_wide(&d);
716
717 if !ac_hi.is_zero_bool() || !bd_hi.is_zero_bool() {
718 panic!("multiplication overflow after cross-cancellation");
719 }
720
721 Self {
722 numer: ac,
723 denom: bd,
724 negative,
725 }
726 }
727
728 #[inline]
732 pub fn div_by_uint(&self, divisor: &T) -> Self {
733 let (bd, bd_hi) = self.denom.mul_wide(divisor);
734
735 if !bd_hi.is_zero_bool() {
736 panic!("denominator overflow in div_by_uint");
737 }
738
739 Self {
740 numer: self.numer.clone(),
741 denom: bd,
742 negative: self.negative,
743 }
744 }
745
746 #[inline]
750 pub fn sub(&self, other: &Self) -> Self {
751 self.add(&other.clone().neg())
752 }
753
754 #[inline]
760 pub fn div(&self, other: &Self) -> Self {
761 if other.numer.is_zero_bool() {
762 panic!("division by zero");
763 }
764
765 let reciprocal = Self {
766 numer: other.denom.clone(),
767 denom: other.numer.clone(),
768 negative: other.negative,
769 };
770 self.mul(&reciprocal)
771 }
772
773 #[inline]
779 pub fn recip(&self) -> Self {
780 if self.numer.is_zero_bool() {
781 panic!("reciprocal of zero");
782 }
783 Self {
784 numer: self.denom.clone(),
785 denom: self.numer.clone(),
786 negative: self.negative,
787 }
788 }
789
790 #[inline]
801 pub fn lt(&self, other: &Self) -> bool {
802 if self.negative != other.negative {
803 return self.negative;
804 }
805
806 let self_numer_bits = self.numer.bits_u32();
807 let self_denom_bits = self.denom.bits_u32();
808 let other_numer_bits = other.numer.bits_u32();
809 let other_denom_bits = other.denom.bits_u32();
810
811 let self_mag = self_numer_bits as i32 - self_denom_bits as i32;
812 let other_mag = other_numer_bits as i32 - other_denom_bits as i32;
813 let mag_diff = self_mag - other_mag;
814
815 if mag_diff < -2 {
816 return !self.negative;
817 }
818 if mag_diff > 2 {
819 return self.negative;
820 }
821
822 if self_numer_bits <= 64
823 && self_denom_bits <= 64
824 && other_numer_bits <= 64
825 && other_denom_bits <= 64
826 {
827 if let (Some(self_n), Some(self_d), Some(other_n), Some(other_d)) = (
828 self.numer.try_to_u64(),
829 self.denom.try_to_u64(),
830 other.numer.try_to_u64(),
831 other.denom.try_to_u64(),
832 ) {
833 let ad = (self_n as u128) * (other_d as u128);
834 let bc = (other_n as u128) * (self_d as u128);
835 return if self.negative { ad > bc } else { ad < bc };
836 }
837 }
838
839 let (ad, ad_hi) = self.numer.mul_wide(&other.denom);
840 let (bc, bc_hi) = self.denom.mul_wide(&other.numer);
841
842 match (ad_hi.cmp(&bc_hi), ad.cmp(&bc)) {
843 (Ordering::Less, _) => !self.negative,
844 (Ordering::Greater, _) => self.negative,
845 (Ordering::Equal, ord) => {
846 if self.negative {
847 ord == Ordering::Greater
848 } else {
849 ord == Ordering::Less
850 }
851 }
852 }
853 }
854
855 #[inline]
862 pub fn gt(&self, other: &Self) -> bool {
863 if self.negative != other.negative {
864 return !self.negative && other.negative;
865 }
866
867 let self_numer_bits = self.numer.bits_u32();
868 let self_denom_bits = self.denom.bits_u32();
869 let other_numer_bits = other.numer.bits_u32();
870 let other_denom_bits = other.denom.bits_u32();
871
872 let self_mag = self_numer_bits as i32 - self_denom_bits as i32;
873 let other_mag = other_numer_bits as i32 - other_denom_bits as i32;
874 let mag_diff = self_mag - other_mag;
875
876 if mag_diff > 2 {
877 return !self.negative;
878 }
879 if mag_diff < -2 {
880 return self.negative;
881 }
882
883 if self_numer_bits <= 64
884 && self_denom_bits <= 64
885 && other_numer_bits <= 64
886 && other_denom_bits <= 64
887 {
888 if let (Some(self_n), Some(self_d), Some(other_n), Some(other_d)) = (
889 self.numer.try_to_u64(),
890 self.denom.try_to_u64(),
891 other.numer.try_to_u64(),
892 other.denom.try_to_u64(),
893 ) {
894 let ad = (self_n as u128) * (other_d as u128);
895 let bc = (other_n as u128) * (self_d as u128);
896 return if self.negative { ad < bc } else { ad > bc };
897 }
898 }
899
900 let (ad, ad_hi) = self.numer.mul_wide(&other.denom);
901 let (bc, bc_hi) = self.denom.mul_wide(&other.numer);
902
903 match (ad_hi.cmp(&bc_hi), ad.cmp(&bc)) {
904 (Ordering::Greater, _) => !self.negative,
905 (Ordering::Less, _) => self.negative,
906 (Ordering::Equal, ord) => {
907 if self.negative {
908 ord == Ordering::Less
909 } else {
910 ord == Ordering::Greater
911 }
912 }
913 }
914 }
915}
916
917#[inline]
923fn gcd_u64(mut a: u64, mut b: u64) -> u64 {
924 while b != 0 {
925 let temp = b;
926 b = a % b;
927 a = temp;
928 }
929 a
930}
931
932use core::ops::{Add, Div, Mul, Neg, Sub};
937
938impl<T: RatioInteger> Add for Ratio<T> {
939 type Output = Self;
940 #[inline(always)]
941 fn add(self, other: Self) -> Self {
942 Ratio::add(&self, &other)
943 }
944}
945
946impl<T: RatioInteger> Add for &Ratio<T> {
947 type Output = Ratio<T>;
948 #[inline(always)]
949 fn add(self, other: Self) -> Ratio<T> {
950 Ratio::add(self, other)
951 }
952}
953
954impl<T: RatioInteger> Mul for Ratio<T> {
955 type Output = Self;
956 #[inline(always)]
957 fn mul(self, other: Self) -> Self {
958 Ratio::mul(&self, &other)
959 }
960}
961
962impl<T: RatioInteger> Mul for &Ratio<T> {
963 type Output = Ratio<T>;
964 #[inline(always)]
965 fn mul(self, other: Self) -> Ratio<T> {
966 Ratio::mul(self, other)
967 }
968}
969
970impl<T: RatioInteger> Neg for Ratio<T> {
971 type Output = Self;
972 #[inline(always)]
973 fn neg(self) -> Self {
974 Ratio::neg(self)
975 }
976}
977
978impl<T: RatioInteger> Sub for Ratio<T> {
979 type Output = Self;
980 fn sub(self, other: Self) -> Self {
981 Ratio::sub(&self, &other)
982 }
983}
984
985impl<T: RatioInteger> Sub for &Ratio<T> {
986 type Output = Ratio<T>;
987 fn sub(self, other: Self) -> Ratio<T> {
988 Ratio::sub(self, other)
989 }
990}
991
992impl<T: RatioInteger> Div for Ratio<T> {
993 type Output = Self;
994 fn div(self, other: Self) -> Self {
995 Ratio::div(&self, &other)
996 }
997}
998
999impl<T: RatioInteger> Div for &Ratio<T> {
1000 type Output = Ratio<T>;
1001 fn div(self, other: Self) -> Ratio<T> {
1002 Ratio::div(self, other)
1003 }
1004}
1005
1006impl<T: RatioInteger> PartialEq for Ratio<T> {
1007 fn eq(&self, other: &Self) -> bool {
1008 if self.numer.is_zero_bool() && other.numer.is_zero_bool() {
1009 return true;
1010 }
1011 if self.negative != other.negative {
1012 return false;
1013 }
1014
1015 let (ad, ad_hi) = self.numer.mul_wide(&other.denom);
1016 let (bc, bc_hi) = other.numer.mul_wide(&self.denom);
1017
1018 ad == bc && ad_hi == bc_hi
1019 }
1020}
1021
1022impl<T: RatioInteger> Eq for Ratio<T> {}
1023
1024impl<T: RatioInteger> PartialOrd for Ratio<T> {
1025 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1026 Some(self.cmp(other))
1027 }
1028}
1029
1030impl<T: RatioInteger> Ord for Ratio<T> {
1031 fn cmp(&self, other: &Self) -> Ordering {
1032 if self.gt(other) {
1033 Ordering::Greater
1034 } else if self.lt(other) {
1035 Ordering::Less
1036 } else {
1037 Ordering::Equal
1038 }
1039 }
1040}
1041
1042pub type RatioU64 = Ratio<crypto_bigint::U64>;
1048
1049pub type RatioU128 = Ratio<crypto_bigint::U128>;
1051
1052pub type RatioU256 = Ratio<crypto_bigint::U256>;
1054
1055pub type RatioU512 = Ratio<crypto_bigint::U512>;
1057
1058pub type RatioU1024 = Ratio<crypto_bigint::U1024>;
1060
1061pub type RatioU2048 = Ratio<crypto_bigint::U2048>;
1063
1064pub type RatioU4096 = Ratio<crypto_bigint::U4096>;
1066
1067pub type RatioU8192 = Ratio<crypto_bigint::U8192>;
1069
1070pub type RatioU16384 = Ratio<crypto_bigint::U16384>;
1072
1073#[cfg(test)]
1074mod tests {
1075 use super::*;
1076 use crypto_bigint::*;
1077
1078 #[test]
1079 fn test_basic_u512() {
1080 let r = Ratio::<U512>::from_u64(3, 4);
1081 assert_eq!(r.numer, U512::from_u64(3));
1082 assert_eq!(r.denom, U512::from_u64(4));
1083 }
1084
1085 #[test]
1086 fn test_basic_u256() {
1087 let r = Ratio::<U256>::from_u64(5, 10);
1088 assert_eq!(r.numer, U256::from_u64(1));
1089 assert_eq!(r.denom, U256::from_u64(2));
1090 }
1091
1092 #[test]
1093 fn test_add_same_denom() {
1094 let r1 = Ratio::<U512>::from_u64(1, 4);
1095 let r2 = Ratio::<U512>::from_u64(1, 4);
1096 let sum = &r1 + &r2;
1097 assert_eq!(sum.numer, U512::from_u64(2));
1098 assert_eq!(sum.denom, U512::from_u64(4));
1099 }
1100
1101 #[test]
1102 fn test_mul_unreduced() {
1103 let r1 = Ratio::<U256>::from_u64(2, 3);
1104 let r2 = Ratio::<U256>::from_u64(3, 4);
1105 let prod = &r1 * &r2;
1106
1107 assert_eq!(prod.numer, U256::from_u64(6));
1108 assert_eq!(prod.denom, U256::from_u64(12));
1109 }
1110
1111 #[test]
1112 fn test_mul_reduced() {
1113 let r1 = Ratio::<U256>::from_u64(2, 3);
1114 let r2 = Ratio::<U256>::from_u64(3, 4);
1115 let prod = r1.mul_reduced(&r2);
1116
1117 assert_eq!(prod.numer, U256::from_u64(1));
1118 assert_eq!(prod.denom, U256::from_u64(2));
1119 }
1120
1121 #[test]
1122 fn test_mul_reduced_power_of_2() {
1123 let r1 = Ratio::<U256>::from_u64(4, 8);
1124 let r2 = Ratio::<U256>::from_u64(2, 6);
1125 let prod = r1.mul_reduced(&r2);
1126
1127 assert_eq!(prod.numer, U256::from_u64(1));
1128 assert_eq!(prod.denom, U256::from_u64(6));
1129 }
1130
1131 #[test]
1132 fn test_flexible_wide_types() {
1133 let r576 = Ratio::<U576>::from_u64(1, 2);
1134 assert_eq!(r576.numer, U576::from_u64(1));
1135
1136 let r640 = Ratio::<U640>::from_u64(3, 4);
1137 assert_eq!(r640.numer, U640::from_u64(3));
1138 }
1139}