1use num_bigint::BigInt;
4use num_integer::Integer;
5use num_rational::BigRational;
6use num_traits::{One, ToPrimitive, Zero};
7
8pub trait BigRationalExt {
10 fn from_u64(value: u64) -> Self;
12
13 fn from_frac_u64(numerator: u64, denominator: u64) -> Self;
19
20 fn from_u128(value: u128) -> Self;
22
23 fn from_frac_u128(numerator: u128, denominator: u128) -> Self;
29
30 fn ceil_to_u128(&self) -> Option<u128>;
32
33 fn from_usize(value: usize) -> Self;
35
36 fn from_frac_usize(numerator: usize, denominator: usize) -> Self;
42
43 fn log2_ceil(&self, binary_digits: usize) -> BigRational;
65}
66
67impl BigRationalExt for BigRational {
68 fn from_u64(value: u64) -> Self {
69 Self::from_integer(BigInt::from(value))
70 }
71
72 fn from_frac_u64(numerator: u64, denominator: u64) -> Self {
73 Self::new(BigInt::from(numerator), BigInt::from(denominator))
74 }
75
76 fn from_u128(value: u128) -> Self {
77 Self::from_integer(BigInt::from(value))
78 }
79
80 fn from_frac_u128(numerator: u128, denominator: u128) -> Self {
81 Self::new(BigInt::from(numerator), BigInt::from(denominator))
82 }
83
84 fn ceil_to_u128(&self) -> Option<u128> {
85 if self < &Self::zero() {
86 return Some(0);
87 }
88
89 let den = self.denom();
90 if den.is_zero() {
91 return None;
92 }
93
94 let (quot, rem) = self.numer().div_rem(den);
95 let mut result = quot.to_u128().unwrap_or(u128::MAX);
96 if !rem.is_zero() {
97 result = result.saturating_add(1);
98 }
99 Some(result)
100 }
101
102 fn from_usize(value: usize) -> Self {
103 Self::from_integer(BigInt::from(value))
104 }
105
106 fn from_frac_usize(numerator: usize, denominator: usize) -> Self {
107 Self::new(BigInt::from(numerator), BigInt::from(denominator))
108 }
109
110 fn log2_ceil(&self, binary_digits: usize) -> BigRational {
111 if self <= &Self::zero() {
112 panic!("log2 undefined for non-positive numbers");
113 }
114
115 let numer = self.numer().to_biguint().expect("positive");
117 let denom = self.denom().to_biguint().expect("positive");
118
119 let numer_bits = numer.bits();
123 let denom_bits = denom.bits();
124 let mut integer_part = numer_bits as i128 - denom_bits as i128;
125
126 let mut normalized_numer = numer;
130 if denom_bits > numer_bits {
131 normalized_numer <<= denom_bits - numer_bits;
132 }
133 let mut normalized_denom = denom;
134 if numer_bits > denom_bits {
135 normalized_denom <<= numer_bits - denom_bits;
136 }
137
138 if normalized_numer < normalized_denom {
140 normalized_numer <<= 1;
141 integer_part -= 1;
142 }
143 assert!(
144 normalized_numer >= normalized_denom && normalized_numer < (&normalized_denom << 1)
145 );
146
147 if normalized_numer == normalized_denom {
150 let numerator = BigInt::from(integer_part) << binary_digits;
151 let denominator = BigInt::one() << binary_digits;
152 return Self::new(numerator, denominator);
153 }
154
155 let mut fractional_bits = BigInt::zero();
161 let one = BigInt::one();
162
163 for _ in 0..binary_digits {
164 let numer_squared = &normalized_numer * &normalized_numer;
166 let denom_squared = &normalized_denom * &normalized_denom;
167
168 fractional_bits <<= 1;
170
171 let two_denom_squared = &denom_squared << 1;
174 if numer_squared >= two_denom_squared {
175 fractional_bits |= &one;
176 normalized_numer = numer_squared;
177 normalized_denom = two_denom_squared;
178 } else {
179 normalized_numer = numer_squared;
180 normalized_denom = denom_squared;
181 }
182 }
183
184 let mut numerator = (BigInt::from(integer_part) << binary_digits) + fractional_bits;
190
191 if normalized_numer > normalized_denom {
194 numerator += &one;
195 }
196
197 let denominator = one << binary_digits;
198 Self::new(numerator, denominator)
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::BigRationalExt;
205 use num_bigint::BigInt;
206 use num_rational::BigRational;
207
208 #[test]
209 fn converts_from_u64() {
210 let rational = BigRational::from_u64(42);
211 assert_eq!(rational.numer(), &BigInt::from(42u64));
212 assert_eq!(rational.denom(), &BigInt::from(1u32));
213 }
214
215 #[test]
216 fn converts_from_frac_u64() {
217 let rational = BigRational::from_frac_u64(6, 8);
218 assert_eq!(rational.numer(), &BigInt::from(3u32));
219 assert_eq!(rational.denom(), &BigInt::from(4u32));
220 }
221
222 #[test]
223 fn converts_from_u128() {
224 let value = (u64::MAX as u128) + 10;
225 let rational = BigRational::from_u128(value);
226 assert_eq!(rational.numer(), &BigInt::from(value));
227 assert_eq!(rational.denom(), &BigInt::from(1u32));
228 }
229
230 #[test]
231 fn converts_from_frac_u128() {
232 let rational = BigRational::from_frac_u128(10, 4);
233 assert_eq!(rational.numer(), &BigInt::from(5u32));
234 assert_eq!(rational.denom(), &BigInt::from(2u32));
235 }
236
237 #[test]
238 fn converts_from_usize() {
239 let value = usize::MAX;
240 let rational = BigRational::from_usize(value);
241 assert_eq!(rational.numer(), &BigInt::from(value));
242 assert_eq!(rational.denom(), &BigInt::from(1u32));
243 }
244
245 #[test]
246 fn converts_from_frac_usize() {
247 let rational = BigRational::from_frac_usize(48, 18);
248 assert_eq!(rational.numer(), &BigInt::from(8u32));
249 assert_eq!(rational.denom(), &BigInt::from(3u32));
250 }
251
252 #[test]
253 fn ceiling_handles_positive_fraction() {
254 let value = BigRational::new(BigInt::from(5u32), BigInt::from(2u32));
255 assert_eq!(value.ceil_to_u128(), Some(3));
256 }
257
258 #[test]
259 fn ceiling_handles_negative() {
260 let value = BigRational::new(BigInt::from(-3i32), BigInt::from(2u32));
261 assert_eq!(value.ceil_to_u128(), Some(0));
262 }
263
264 #[test]
265 fn ceiling_handles_large_values() {
266 let value = BigRational::from_u128(u128::MAX - 1);
267 assert_eq!(value.ceil_to_u128(), Some(u128::MAX - 1));
268 }
269
270 #[test]
271 #[should_panic(expected = "log2 undefined for non-positive numbers")]
272 fn log2_ceil_negative_panics() {
273 <BigRational as num_traits::FromPrimitive>::from_i64(-1)
274 .unwrap()
275 .log2_ceil(8);
276 }
277
278 #[test]
279 fn log2_ceil_exact_powers_of_two() {
280 let value = BigRational::from_u64(1); assert_eq!(value.log2_ceil(4), BigRational::from_u64(0));
283
284 let value = BigRational::from_u64(2); assert_eq!(value.log2_ceil(4), BigRational::from_u64(1));
286
287 let value = BigRational::from_u64(8); assert_eq!(value.log2_ceil(4), BigRational::from_u64(3));
289
290 let value = BigRational::from_u64(1024); assert_eq!(value.log2_ceil(4), BigRational::from_u64(10));
292 }
293
294 #[test]
295 fn log2_ceil_fractional_powers_of_two() {
296 let value = BigRational::from_frac_u64(1, 2); let result = value.log2_ceil(4);
299 assert_eq!(result, BigRational::from_integer(BigInt::from(-1)));
300
301 let value = BigRational::from_frac_u64(1, 4); let result = value.log2_ceil(4);
303 assert_eq!(result, BigRational::from_integer(BigInt::from(-2)));
304
305 let value = BigRational::from_frac_u64(3, 8); let result = value.log2_ceil(4);
307 assert_eq!(result, BigRational::new(BigInt::from(-11), BigInt::from(8)));
308 }
309
310 #[test]
311 fn log2_ceil_simple_values() {
312 let value = BigRational::from_u64(3);
314 let result = value.log2_ceil(0);
315 assert_eq!(result, BigRational::from_u64(2));
316
317 let value = BigRational::from_u64(5);
319 let result = value.log2_ceil(0);
320 assert_eq!(result, BigRational::from_u64(3));
321
322 let value = BigRational::from_u64(3);
324 let result = value.log2_ceil(4);
325 assert_eq!(result, BigRational::from_frac_u64(13, 8));
326 }
327
328 #[test]
329 fn log2_ceil_rational_values() {
330 let value = BigRational::from_frac_u64(3, 2);
332 let result = value.log2_ceil(4);
333 assert_eq!(result, BigRational::from_frac_u64(5, 8));
334
335 let value = BigRational::from_frac_u64(7, 4);
336 let result = value.log2_ceil(4);
337 assert_eq!(result, BigRational::from_frac_u64(13, 16));
338 }
339
340 #[test]
341 fn log2_ceil_different_precisions() {
342 let value = BigRational::from_u64(3);
343
344 let result0 = value.log2_ceil(0);
346 let result1 = value.log2_ceil(1);
347 let result4 = value.log2_ceil(4);
348 let result8 = value.log2_ceil(8);
349
350 assert_eq!(result0, BigRational::from_u64(2));
351 assert_eq!(result1, BigRational::from_u64(2));
352 assert_eq!(result4, BigRational::from_frac_u64(13, 8));
353 assert_eq!(
354 result8,
355 BigRational::new(BigInt::from(203), BigInt::from(128))
356 );
357 }
358
359 #[test]
360 fn log2_ceil_large_values() {
361 let value = BigRational::from_u64(1000);
363 let result = value.log2_ceil(4);
364 assert_eq!(result, BigRational::from_u64(10));
365 }
366
367 #[test]
368 fn log2_ceil_very_small_values() {
369 let value = BigRational::from_frac_u64(1, 1000);
371 let result = value.log2_ceil(4);
372 assert_eq!(
373 result,
374 BigRational::new(BigInt::from(-159), BigInt::from(16))
375 );
376 }
377
378 #[test]
379 fn log2_ceil_edge_cases() {
380 let x = BigRational::from_frac_u64(17, 16);
383 assert_eq!(x.log2_ceil(8), BigRational::from_frac_u64(23, 256));
384
385 let x = BigRational::from_frac_u64(129, 128);
387 assert_eq!(x.log2_ceil(8), BigRational::from_frac_u64(3, 256));
388
389 let x = BigRational::from_frac_u64(33, 32);
391 assert_eq!(x.log2_ceil(10), BigRational::from_frac_u64(46, 1024));
392
393 let x = BigRational::from_frac_u64(255, 256);
396 assert_eq!(x.log2_ceil(8), BigRational::new((-1).into(), 256u32.into()));
397
398 let x = BigRational::from_frac_u64(1023, 1024);
400 assert_eq!(x.log2_ceil(9), BigRational::new(0.into(), 512u32.into()));
401
402 let x = BigRational::from_frac_u64(3, 2);
405 assert_eq!(x.log2_ceil(0), BigRational::from_integer(1.into()));
406
407 let x = BigRational::from_frac_u64(3, 4);
409 assert_eq!(x.log2_ceil(0), BigRational::from_integer(0.into()));
410
411 let x = BigRational::from_frac_u64(3, 4);
414 assert_eq!(x.log2_ceil(4), BigRational::new((-6).into(), 16u32.into()));
415
416 let x = BigRational::from_frac_u64(257, 256);
420 assert_eq!(x.log2_ceil(8), BigRational::new(2.into(), 256u32.into()));
421 assert_eq!(x.log2_ceil(9), BigRational::new(3.into(), 512u32.into()));
422
423 let num = BigInt::from(17u32) << 30;
426 let den = BigInt::from(16u32) << 30;
427 let x = BigRational::new(num, den);
428 assert_eq!(x.log2_ceil(8), BigRational::from_frac_u64(23, 256));
429 }
430}