1use num_bigint::{BigInt, BigUint};
4use num_integer::Integer;
5use num_rational::BigRational;
6use num_traits::{One, ToPrimitive, Zero};
7
8fn log2(numer: BigUint, denom: BigUint, binary_digits: usize) -> (BigInt, bool) {
13 let numer_bits = numer.bits();
17 let denom_bits = denom.bits();
18 let mut integer_part = numer_bits as i128 - denom_bits as i128;
19
20 let mut normalized_numer = numer;
24 if denom_bits > numer_bits {
25 normalized_numer <<= denom_bits - numer_bits;
26 }
27 let mut normalized_denom = denom;
28 if numer_bits > denom_bits {
29 normalized_denom <<= numer_bits - denom_bits;
30 }
31
32 if normalized_numer < normalized_denom {
34 normalized_numer <<= 1;
35 integer_part -= 1;
36 }
37
38 if normalized_numer == normalized_denom {
41 return (BigInt::from(integer_part) << binary_digits, false);
42 }
43
44 let mut fractional_bits = BigInt::zero();
50 let one = BigInt::one();
51
52 for _ in 0..binary_digits {
53 let numer_squared = &normalized_numer * &normalized_numer;
55 let denom_squared = &normalized_denom * &normalized_denom;
56
57 fractional_bits <<= 1;
59
60 let two_denom_squared = &denom_squared << 1;
63 if numer_squared >= two_denom_squared {
64 fractional_bits |= &one;
65 normalized_numer = numer_squared;
66 normalized_denom = two_denom_squared;
67 } else {
68 normalized_numer = numer_squared;
69 normalized_denom = denom_squared;
70 }
71 }
72
73 let numerator = (BigInt::from(integer_part) << binary_digits) + fractional_bits;
79
80 let has_remainder = normalized_numer > normalized_denom;
83 (numerator, has_remainder)
84}
85
86pub trait BigRationalExt {
88 fn from_u64(value: u64) -> Self;
90
91 fn from_frac_u64(numerator: u64, denominator: u64) -> Self;
97
98 fn from_u128(value: u128) -> Self;
100
101 fn from_frac_u128(numerator: u128, denominator: u128) -> Self;
107
108 fn ceil_to_u128(&self) -> Option<u128>;
110
111 fn from_usize(value: usize) -> Self;
113
114 fn from_frac_usize(numerator: usize, denominator: usize) -> Self;
120
121 fn log2_ceil(&self, binary_digits: usize) -> BigRational;
143
144 fn log2_floor(&self, binary_digits: usize) -> BigRational;
166}
167
168impl BigRationalExt for BigRational {
169 fn from_u64(value: u64) -> Self {
170 Self::from_integer(BigInt::from(value))
171 }
172
173 fn from_frac_u64(numerator: u64, denominator: u64) -> Self {
174 Self::new(BigInt::from(numerator), BigInt::from(denominator))
175 }
176
177 fn from_u128(value: u128) -> Self {
178 Self::from_integer(BigInt::from(value))
179 }
180
181 fn from_frac_u128(numerator: u128, denominator: u128) -> Self {
182 Self::new(BigInt::from(numerator), BigInt::from(denominator))
183 }
184
185 fn ceil_to_u128(&self) -> Option<u128> {
186 if self < &Self::zero() {
187 return Some(0);
188 }
189
190 let den = self.denom();
191 if den.is_zero() {
192 return None;
193 }
194
195 let (quot, rem) = self.numer().div_rem(den);
196 let mut result = quot.to_u128().unwrap_or(u128::MAX);
197 if !rem.is_zero() {
198 result = result.saturating_add(1);
199 }
200 Some(result)
201 }
202
203 fn from_usize(value: usize) -> Self {
204 Self::from_integer(BigInt::from(value))
205 }
206
207 fn from_frac_usize(numerator: usize, denominator: usize) -> Self {
208 Self::new(BigInt::from(numerator), BigInt::from(denominator))
209 }
210
211 fn log2_ceil(&self, binary_digits: usize) -> BigRational {
212 if self <= &Self::zero() {
213 panic!("log2 undefined for non-positive numbers");
214 }
215
216 let numer = self.numer().to_biguint().expect("positive");
217 let denom = self.denom().to_biguint().expect("positive");
218 let (mut numerator, has_remainder) = log2(numer, denom, binary_digits);
219 if has_remainder {
220 numerator += 1;
221 }
222 let denominator = BigInt::one() << binary_digits;
223 Self::new(numerator, denominator)
224 }
225
226 fn log2_floor(&self, binary_digits: usize) -> BigRational {
227 if self <= &Self::zero() {
228 panic!("log2 undefined for non-positive numbers");
229 }
230
231 let numer = self.numer().to_biguint().expect("positive");
232 let denom = self.denom().to_biguint().expect("positive");
233 let (numerator, _) = log2(numer, denom, binary_digits);
234 let denominator = BigInt::one() << binary_digits;
235 Self::new(numerator, denominator)
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::BigRationalExt;
242 use num_bigint::BigInt;
243 use num_rational::BigRational;
244
245 #[test]
246 fn converts_from_u64() {
247 let rational = BigRational::from_u64(42);
248 assert_eq!(rational.numer(), &BigInt::from(42u64));
249 assert_eq!(rational.denom(), &BigInt::from(1u32));
250 }
251
252 #[test]
253 fn converts_from_frac_u64() {
254 let rational = BigRational::from_frac_u64(6, 8);
255 assert_eq!(rational.numer(), &BigInt::from(3u32));
256 assert_eq!(rational.denom(), &BigInt::from(4u32));
257 }
258
259 #[test]
260 fn converts_from_u128() {
261 let value = (u64::MAX as u128) + 10;
262 let rational = BigRational::from_u128(value);
263 assert_eq!(rational.numer(), &BigInt::from(value));
264 assert_eq!(rational.denom(), &BigInt::from(1u32));
265 }
266
267 #[test]
268 fn converts_from_frac_u128() {
269 let rational = BigRational::from_frac_u128(10, 4);
270 assert_eq!(rational.numer(), &BigInt::from(5u32));
271 assert_eq!(rational.denom(), &BigInt::from(2u32));
272 }
273
274 #[test]
275 fn converts_from_usize() {
276 let value = usize::MAX;
277 let rational = BigRational::from_usize(value);
278 assert_eq!(rational.numer(), &BigInt::from(value));
279 assert_eq!(rational.denom(), &BigInt::from(1u32));
280 }
281
282 #[test]
283 fn converts_from_frac_usize() {
284 let rational = BigRational::from_frac_usize(48, 18);
285 assert_eq!(rational.numer(), &BigInt::from(8u32));
286 assert_eq!(rational.denom(), &BigInt::from(3u32));
287 }
288
289 #[test]
290 fn ceiling_handles_positive_fraction() {
291 let value = BigRational::new(BigInt::from(5u32), BigInt::from(2u32));
292 assert_eq!(value.ceil_to_u128(), Some(3));
293 }
294
295 #[test]
296 fn ceiling_handles_negative() {
297 let value = BigRational::new(BigInt::from(-3i32), BigInt::from(2u32));
298 assert_eq!(value.ceil_to_u128(), Some(0));
299 }
300
301 #[test]
302 fn ceiling_handles_large_values() {
303 let value = BigRational::from_u128(u128::MAX - 1);
304 assert_eq!(value.ceil_to_u128(), Some(u128::MAX - 1));
305 }
306
307 #[test]
308 #[should_panic(expected = "log2 undefined for non-positive numbers")]
309 fn log2_ceil_negative_panics() {
310 <BigRational as num_traits::FromPrimitive>::from_i64(-1)
311 .unwrap()
312 .log2_ceil(8);
313 }
314
315 #[test]
316 fn log2_ceil_exact_powers_of_two() {
317 let value = BigRational::from_u64(1); assert_eq!(value.log2_ceil(4), BigRational::from_u64(0));
320
321 let value = BigRational::from_u64(2); assert_eq!(value.log2_ceil(4), BigRational::from_u64(1));
323
324 let value = BigRational::from_u64(8); assert_eq!(value.log2_ceil(4), BigRational::from_u64(3));
326
327 let value = BigRational::from_u64(1024); assert_eq!(value.log2_ceil(4), BigRational::from_u64(10));
329 }
330
331 #[test]
332 fn log2_ceil_fractional_powers_of_two() {
333 let value = BigRational::from_frac_u64(1, 2); let result = value.log2_ceil(4);
336 assert_eq!(result, BigRational::from_integer(BigInt::from(-1)));
337
338 let value = BigRational::from_frac_u64(1, 4); let result = value.log2_ceil(4);
340 assert_eq!(result, BigRational::from_integer(BigInt::from(-2)));
341
342 let value = BigRational::from_frac_u64(3, 8); let result = value.log2_ceil(4);
344 assert_eq!(result, BigRational::new(BigInt::from(-11), BigInt::from(8)));
345 }
346
347 #[test]
348 fn log2_ceil_simple_values() {
349 let value = BigRational::from_u64(3);
351 let result = value.log2_ceil(0);
352 assert_eq!(result, BigRational::from_u64(2));
353
354 let value = BigRational::from_u64(5);
356 let result = value.log2_ceil(0);
357 assert_eq!(result, BigRational::from_u64(3));
358
359 let value = BigRational::from_u64(3);
361 let result = value.log2_ceil(4);
362 assert_eq!(result, BigRational::from_frac_u64(13, 8));
363 }
364
365 #[test]
366 fn log2_ceil_rational_values() {
367 let value = BigRational::from_frac_u64(3, 2);
369 let result = value.log2_ceil(4);
370 assert_eq!(result, BigRational::from_frac_u64(5, 8));
371
372 let value = BigRational::from_frac_u64(7, 4);
373 let result = value.log2_ceil(4);
374 assert_eq!(result, BigRational::from_frac_u64(13, 16));
375 }
376
377 #[test]
378 fn log2_ceil_different_precisions() {
379 let value = BigRational::from_u64(3);
380
381 let result0 = value.log2_ceil(0);
383 let result1 = value.log2_ceil(1);
384 let result4 = value.log2_ceil(4);
385 let result8 = value.log2_ceil(8);
386
387 assert_eq!(result0, BigRational::from_u64(2));
388 assert_eq!(result1, BigRational::from_u64(2));
389 assert_eq!(result4, BigRational::from_frac_u64(13, 8));
390 assert_eq!(
391 result8,
392 BigRational::new(BigInt::from(203), BigInt::from(128))
393 );
394 }
395
396 #[test]
397 fn log2_ceil_large_values() {
398 let value = BigRational::from_u64(1000);
400 let result = value.log2_ceil(4);
401 assert_eq!(result, BigRational::from_u64(10));
402 }
403
404 #[test]
405 fn log2_ceil_very_small_values() {
406 let value = BigRational::from_frac_u64(1, 1000);
408 let result = value.log2_ceil(4);
409 assert_eq!(
410 result,
411 BigRational::new(BigInt::from(-159), BigInt::from(16))
412 );
413 }
414
415 #[test]
416 fn log2_ceil_edge_cases() {
417 let x = BigRational::from_frac_u64(17, 16);
420 assert_eq!(x.log2_ceil(8), BigRational::from_frac_u64(23, 256));
421
422 let x = BigRational::from_frac_u64(129, 128);
424 assert_eq!(x.log2_ceil(8), BigRational::from_frac_u64(3, 256));
425
426 let x = BigRational::from_frac_u64(33, 32);
428 assert_eq!(x.log2_ceil(10), BigRational::from_frac_u64(46, 1024));
429
430 let x = BigRational::from_frac_u64(255, 256);
433 assert_eq!(x.log2_ceil(8), BigRational::new((-1).into(), 256u32.into()));
434
435 let x = BigRational::from_frac_u64(1023, 1024);
437 assert_eq!(x.log2_ceil(9), BigRational::new(0.into(), 512u32.into()));
438
439 let x = BigRational::from_frac_u64(3, 2);
442 assert_eq!(x.log2_ceil(0), BigRational::from_integer(1.into()));
443
444 let x = BigRational::from_frac_u64(3, 4);
446 assert_eq!(x.log2_ceil(0), BigRational::from_integer(0.into()));
447
448 let x = BigRational::from_frac_u64(3, 4);
451 assert_eq!(x.log2_ceil(4), BigRational::new((-6).into(), 16u32.into()));
452
453 let x = BigRational::from_frac_u64(257, 256);
457 assert_eq!(x.log2_ceil(8), BigRational::new(2.into(), 256u32.into()));
458 assert_eq!(x.log2_ceil(9), BigRational::new(3.into(), 512u32.into()));
459
460 let num = BigInt::from(17u32) << 30;
463 let den = BigInt::from(16u32) << 30;
464 let x = BigRational::new(num, den);
465 assert_eq!(x.log2_ceil(8), BigRational::from_frac_u64(23, 256));
466 }
467
468 #[test]
469 #[should_panic(expected = "log2 undefined for non-positive numbers")]
470 fn log2_floor_negative_panics() {
471 <BigRational as num_traits::FromPrimitive>::from_i64(-1)
472 .unwrap()
473 .log2_floor(8);
474 }
475
476 #[test]
477 fn log2_floor_exact_powers_of_two() {
478 let value = BigRational::from_u64(1); assert_eq!(value.log2_floor(4), BigRational::from_u64(0));
481
482 let value = BigRational::from_u64(2); assert_eq!(value.log2_floor(4), BigRational::from_u64(1));
484
485 let value = BigRational::from_u64(8); assert_eq!(value.log2_floor(4), BigRational::from_u64(3));
487
488 let value = BigRational::from_u64(1024); assert_eq!(value.log2_floor(4), BigRational::from_u64(10));
490 }
491
492 #[test]
493 fn log2_floor_fractional_powers_of_two() {
494 let value = BigRational::from_frac_u64(1, 2); let result = value.log2_floor(4);
497 assert_eq!(result, BigRational::from_integer(BigInt::from(-1)));
498
499 let value = BigRational::from_frac_u64(1, 4); let result = value.log2_floor(4);
501 assert_eq!(result, BigRational::from_integer(BigInt::from(-2)));
502
503 let value = BigRational::from_frac_u64(3, 8);
505 let result = value.log2_floor(4);
506 assert_eq!(
507 result,
508 BigRational::new(BigInt::from(-23), BigInt::from(16))
509 );
510 }
511
512 #[test]
513 fn log2_floor_simple_values() {
514 let value = BigRational::from_u64(3);
516 let result = value.log2_floor(0);
517 assert_eq!(result, BigRational::from_u64(1));
518
519 let value = BigRational::from_u64(5);
521 let result = value.log2_floor(0);
522 assert_eq!(result, BigRational::from_u64(2));
523
524 let value = BigRational::from_u64(3);
527 let result = value.log2_floor(4);
528 assert_eq!(result, BigRational::from_frac_u64(25, 16));
529 }
530
531 #[test]
532 fn log2_floor_rational_values() {
533 let value = BigRational::from_frac_u64(3, 2);
535 let result = value.log2_floor(4);
536 assert_eq!(result, BigRational::from_frac_u64(9, 16));
537
538 let value = BigRational::from_frac_u64(7, 4);
540 let result = value.log2_floor(4);
541 assert_eq!(result, BigRational::from_frac_u64(12, 16));
542 }
543
544 #[test]
545 fn log2_floor_different_precisions() {
546 let value = BigRational::from_u64(3);
547
548 let result0 = value.log2_floor(0);
550 let result1 = value.log2_floor(1);
551 let result4 = value.log2_floor(4);
552 let result8 = value.log2_floor(8);
553
554 assert_eq!(result0, BigRational::from_u64(1));
555 assert_eq!(result1, BigRational::from_frac_u64(3, 2));
556 assert_eq!(result4, BigRational::from_frac_u64(25, 16));
557 assert_eq!(
558 result8,
559 BigRational::new(BigInt::from(405), BigInt::from(256))
560 );
561 }
562
563 #[test]
564 fn log2_floor_large_values() {
565 let value = BigRational::from_u64(1000);
567 let result = value.log2_floor(4);
568 assert_eq!(result, BigRational::from_frac_u64(159, 16));
569 }
570
571 #[test]
572 fn log2_floor_very_small_values() {
573 let value = BigRational::from_frac_u64(1, 1000);
575 let result = value.log2_floor(4);
576 assert_eq!(
577 result,
578 BigRational::new(BigInt::from(-160), BigInt::from(16))
579 );
580 }
581
582 #[test]
583 fn log2_floor_edge_cases() {
584 let x = BigRational::from_frac_u64(17, 16);
587 assert_eq!(x.log2_floor(8), BigRational::from_frac_u64(22, 256));
588
589 let x = BigRational::from_frac_u64(129, 128);
591 assert_eq!(x.log2_floor(8), BigRational::from_frac_u64(2, 256));
592
593 let x = BigRational::from_frac_u64(33, 32);
595 assert_eq!(x.log2_floor(10), BigRational::from_frac_u64(45, 1024));
596
597 let x = BigRational::from_frac_u64(255, 256);
600 assert_eq!(
601 x.log2_floor(8),
602 BigRational::new((-2).into(), 256u32.into())
603 );
604
605 let x = BigRational::from_frac_u64(1023, 1024);
607 assert_eq!(
608 x.log2_floor(9),
609 BigRational::new((-1).into(), 512u32.into())
610 );
611
612 let x = BigRational::from_frac_u64(3, 2);
615 assert_eq!(x.log2_floor(0), BigRational::from_integer(0.into()));
616
617 let x = BigRational::from_frac_u64(3, 4);
619 assert_eq!(x.log2_floor(0), BigRational::from_integer((-1).into()));
620
621 let x = BigRational::from_frac_u64(3, 4);
624 assert_eq!(x.log2_floor(4), BigRational::new((-7).into(), 16u32.into()));
625
626 let x = BigRational::from_frac_u64(257, 256);
630 assert_eq!(x.log2_floor(8), BigRational::new(1.into(), 256u32.into()));
631 assert_eq!(x.log2_floor(9), BigRational::new(2.into(), 512u32.into()));
632
633 let num = BigInt::from(17u32) << 30;
636 let den = BigInt::from(16u32) << 30;
637 let x = BigRational::new(num, den);
638 assert_eq!(x.log2_floor(8), BigRational::from_frac_u64(22, 256));
639 }
640
641 #[test]
642 fn log2_floor_ceil_relationship() {
643 let test_values = [
646 BigRational::from_u64(3),
647 BigRational::from_u64(5),
648 BigRational::from_frac_u64(3, 2),
649 BigRational::from_frac_u64(7, 8),
650 BigRational::from_frac_u64(1, 3),
651 ];
652
653 for value in test_values {
654 let floor = value.log2_floor(8);
655 let ceil = value.log2_ceil(8);
656 assert!(
657 floor < ceil,
658 "floor should be less than ceil for non-power-of-2"
659 );
660 }
661
662 let powers_of_two = [
664 BigRational::from_u64(1),
665 BigRational::from_u64(2),
666 BigRational::from_u64(4),
667 BigRational::from_frac_u64(1, 2),
668 BigRational::from_frac_u64(1, 4),
669 ];
670
671 for value in powers_of_two {
672 let floor = value.log2_floor(8);
673 let ceil = value.log2_ceil(8);
674 assert_eq!(floor, ceil, "floor should equal ceil for power of 2");
675 }
676 }
677}