commonware_utils/
rational.rs1use num_bigint::BigInt;
4use num_integer::Integer;
5use num_rational::BigRational;
6use num_traits::{ToPrimitive, Zero};
7
8pub trait BigRationalExt {
10 fn from_u64(value: u64) -> Self;
12
13 fn from_frac_u64(numerator: u64, denominator: u64) -> Self;
15
16 fn from_u128(value: u128) -> Self;
18
19 fn from_frac_u128(numerator: u128, denominator: u128) -> Self;
21
22 fn ceil_to_u128(&self) -> Option<u128>;
24
25 fn from_usize(value: usize) -> Self;
27
28 fn from_frac_usize(numerator: usize, denominator: usize) -> Self;
30}
31
32impl BigRationalExt for BigRational {
33 fn from_u64(value: u64) -> Self {
34 BigRational::from_integer(BigInt::from(value))
35 }
36
37 fn from_frac_u64(numerator: u64, denominator: u64) -> Self {
38 BigRational::new(BigInt::from(numerator), BigInt::from(denominator))
39 }
40
41 fn from_u128(value: u128) -> Self {
42 BigRational::from_integer(BigInt::from(value))
43 }
44
45 fn from_frac_u128(numerator: u128, denominator: u128) -> Self {
46 BigRational::new(BigInt::from(numerator), BigInt::from(denominator))
47 }
48
49 fn ceil_to_u128(&self) -> Option<u128> {
50 if self < &BigRational::zero() {
51 return Some(0);
52 }
53
54 let den = self.denom();
55 if den.is_zero() {
56 return None;
57 }
58
59 let (quot, rem) = self.numer().div_rem(den);
60 let mut result = quot.to_u128().unwrap_or(u128::MAX);
61 if !rem.is_zero() {
62 result = result.saturating_add(1);
63 }
64 Some(result)
65 }
66
67 fn from_usize(value: usize) -> Self {
68 BigRational::from_integer(BigInt::from(value))
69 }
70
71 fn from_frac_usize(numerator: usize, denominator: usize) -> Self {
72 BigRational::new(BigInt::from(numerator), BigInt::from(denominator))
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::BigRationalExt;
79 use num_bigint::BigInt;
80 use num_rational::BigRational;
81
82 #[test]
83 fn converts_from_u64() {
84 let rational = BigRational::from_u64(42);
85 assert_eq!(rational.numer(), &BigInt::from(42u64));
86 assert_eq!(rational.denom(), &BigInt::from(1u32));
87 }
88
89 #[test]
90 fn converts_from_frac_u64() {
91 let rational = BigRational::from_frac_u64(6, 8);
92 assert_eq!(rational.numer(), &BigInt::from(3u32));
93 assert_eq!(rational.denom(), &BigInt::from(4u32));
94 }
95
96 #[test]
97 fn converts_from_u128() {
98 let value = (u64::MAX as u128) + 10;
99 let rational = BigRational::from_u128(value);
100 assert_eq!(rational.numer(), &BigInt::from(value));
101 assert_eq!(rational.denom(), &BigInt::from(1u32));
102 }
103
104 #[test]
105 fn converts_from_frac_u128() {
106 let rational = BigRational::from_frac_u128(10, 4);
107 assert_eq!(rational.numer(), &BigInt::from(5u32));
108 assert_eq!(rational.denom(), &BigInt::from(2u32));
109 }
110
111 #[test]
112 fn converts_from_usize() {
113 let value = usize::MAX;
114 let rational = BigRational::from_usize(value);
115 assert_eq!(rational.numer(), &BigInt::from(value));
116 assert_eq!(rational.denom(), &BigInt::from(1u32));
117 }
118
119 #[test]
120 fn converts_from_frac_usize() {
121 let rational = BigRational::from_frac_usize(48, 18);
122 assert_eq!(rational.numer(), &BigInt::from(8u32));
123 assert_eq!(rational.denom(), &BigInt::from(3u32));
124 }
125
126 #[test]
127 fn ceiling_handles_positive_fraction() {
128 let value = BigRational::new(BigInt::from(5u32), BigInt::from(2u32));
129 assert_eq!(value.ceil_to_u128(), Some(3));
130 }
131
132 #[test]
133 fn ceiling_handles_negative() {
134 let value = BigRational::new(BigInt::from(-3i32), BigInt::from(2u32));
135 assert_eq!(value.ceil_to_u128(), Some(0));
136 }
137
138 #[test]
139 fn ceiling_handles_large_values() {
140 let value = BigRational::from_u128(u128::MAX - 1);
141 assert_eq!(value.ceil_to_u128(), Some(u128::MAX - 1));
142 }
143}