1use zr::static_assert;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum Exact {
9 No,
10 Yes,
11}
12
13pub struct Round;
20impl Round {
21 pub const DOWN: u8 = 0;
22 pub const UP: u8 = 1;
23 pub const TOWARDS_ZERO: u8 = 2;
24 pub const AWAY_FROM_ZERO: u8 = 3;
25}
26
27#[repr(C)]
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct Ratio {
30 numerator: u32,
31 denominator: u32,
32}
33
34static_assert!(core::mem::size_of::<Ratio>() == 8);
35static_assert!(core::mem::align_of::<Ratio>() == 4);
36
37impl Default for Ratio {
38 fn default() -> Self {
39 Ratio { numerator: 1, denominator: 1 }
40 }
41}
42
43impl Ratio {
44 pub const OVERFLOW: i64 = i64::MAX;
45 pub const UNDERFLOW: i64 = i64::MIN;
46
47 pub fn new(numerator: u32, denominator: u32) -> Self {
48 debug_assert!(denominator != 0);
49 Ratio { numerator, denominator }
50 }
51
52 pub fn numerator(&self) -> u32 {
53 self.numerator
54 }
55
56 pub fn denominator(&self) -> u32 {
57 self.denominator
58 }
59
60 pub fn invertible(&self) -> bool {
61 self.numerator != 0
62 }
63
64 pub fn inverse(&self) -> Self {
65 debug_assert!(self.invertible());
66 Ratio { numerator: self.denominator, denominator: self.numerator }
67 }
68
69 pub fn reduce_u32(numerator: &mut u32, denominator: &mut u32) {
71 assert!(*denominator != 0);
72 if *numerator == 0 {
73 *denominator = 1;
74 return;
75 }
76 let gcd = binary_gcd(*numerator as u64, *denominator as u64) as u32;
77 *numerator /= gcd;
78 *denominator /= gcd;
79 }
80
81 pub fn reduce_u64(numerator: &mut u64, denominator: &mut u64) {
83 assert!(*denominator != 0);
84 if *numerator == 0 {
85 *denominator = 1;
86 return;
87 }
88 let gcd = binary_gcd(*numerator, *denominator);
89 *numerator /= gcd;
90 *denominator /= gcd;
91 }
92
93 pub fn reduce(&mut self) {
95 Self::reduce_u32(&mut self.numerator, &mut self.denominator);
96 }
97
98 pub fn product_raw(
103 a_numerator: u32,
104 a_denominator: u32,
105 b_numerator: u32,
106 b_denominator: u32,
107 exact: Exact,
108 ) -> (u32, u32) {
109 let mut numerator = a_numerator as u64 * b_numerator as u64;
110 let mut denominator = a_denominator as u64 * b_denominator as u64;
111
112 Self::reduce_u64(&mut numerator, &mut denominator);
113
114 if numerator > u32::MAX as u64 || denominator > u32::MAX as u64 {
115 assert!(exact == Exact::No, "Precision loss in exact Ratio::product");
116
117 for i in 1..=32 {
132 let rounded_numerator = (numerator + (1u64 << (i - 1))) >> i;
136 let rounded_denominator = (denominator + (1u64 << (i - 1))) >> i;
137
138 if rounded_denominator == 0 {
139 return (u32::MAX, 1);
142 }
143
144 if rounded_numerator == 0 {
145 return (0, 1);
147 }
148
149 let mut rn = rounded_numerator;
150 let mut rd = rounded_denominator;
151 Self::reduce_u64(&mut rn, &mut rd);
152 if rn <= u32::MAX as u64 && rd <= u32::MAX as u64 {
153 return (rn as u32, rd as u32);
154 }
155 }
156 return (numerator as u32, denominator as u32);
158 }
159
160 (numerator as u32, denominator as u32)
161 }
162
163 pub fn product(a: Ratio, b: Ratio, exact: Exact) -> Ratio {
164 let (n, d) =
165 Self::product_raw(a.numerator, a.denominator, b.numerator, b.denominator, exact);
166 Ratio { numerator: n, denominator: d }
167 }
168
169 pub fn scale_with_round<const ROUND: u8>(value: i64, numerator: u32, denominator: u32) -> i64 {
174 assert!(denominator != 0);
175
176 if value >= 0 {
177 let limit = i64::MAX as u64;
179 let round_up = match ROUND {
180 Round::UP | Round::AWAY_FROM_ZERO => true,
181 _ => false,
182 };
183 let scaled = scale_unsigned(value as u64, numerator, denominator, round_up, limit);
184 scaled as i64
185 } else {
186 let limit = 0x8000000000000000u64; let round_up = match ROUND {
202 Round::DOWN | Round::AWAY_FROM_ZERO => true,
203 _ => false,
204 };
205 let scaled =
206 scale_unsigned(value.unsigned_abs(), numerator, denominator, round_up, limit);
207 if scaled == 0x8000000000000000 { i64::MIN } else { -(scaled as i64) }
208 }
209 }
210
211 pub fn scale<const ROUND: u8>(&self, value: i64) -> i64 {
216 Self::scale_with_round::<ROUND>(value, self.numerator, self.denominator)
217 }
218}
219
220fn binary_gcd(mut a: u64, mut b: u64) -> u64 {
222 debug_assert!(a != 0 && b != 0);
223
224 let mut twos = 0;
226 while ((a | b) & 1) == 0 {
227 a >>= 1;
228 b >>= 1;
229 twos += 1;
230 }
231
232 while (a & 1) == 0 {
235 a >>= 1;
236 }
237
238 loop {
239 while (b & 1) == 0 {
242 b >>= 1;
243 }
244
245 if a > b {
247 core::mem::swap(&mut a, &mut b);
248 }
249
250 b -= a;
251 if b == 0 {
252 break;
253 }
254 }
255
256 a << twos
258}
259
260fn scale_unsigned(value: u64, numerator: u32, denominator: u32, round_up: bool, limit: u64) -> u64 {
263 let prod = (value as u128) * (numerator as u128);
264 let q = prod / (denominator as u128);
265 let r = prod % (denominator as u128);
266
267 if q >= (limit as u128) {
268 return limit;
269 }
270
271 let mut result = q as u64;
272 if round_up && r != 0 {
273 result += 1;
274 if result >= limit {
275 return limit;
276 }
277 }
278 result
279}
280
281impl core::ops::Mul for Ratio {
284 type Output = Self;
285 fn mul(self, rhs: Self) -> Self::Output {
286 Ratio::product(self, rhs, Exact::Yes)
287 }
288}
289
290impl core::ops::Div for Ratio {
291 type Output = Self;
292 fn div(self, rhs: Self) -> Self::Output {
293 self * rhs.inverse()
294 }
295}
296
297impl core::ops::Mul<i64> for Ratio {
298 type Output = i64;
299 fn mul(self, rhs: i64) -> Self::Output {
300 self.scale::<{ Round::DOWN }>(rhs)
301 }
302}
303
304impl core::ops::Mul<Ratio> for i64 {
305 type Output = i64;
306 fn mul(self, rhs: Ratio) -> Self::Output {
307 rhs.scale::<{ Round::DOWN }>(self)
308 }
309}
310
311impl core::ops::Div<Ratio> for i64 {
312 type Output = i64;
313 fn div(self, rhs: Ratio) -> Self::Output {
314 rhs.inverse().scale::<{ Round::DOWN }>(self)
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 #[test]
323 fn test_construction() {
324 let valid_vectors = [(0, 1), (1, 1), (23, 41)];
325 for &(n, d) in &valid_vectors {
326 let r = Ratio::new(n, d);
327 assert_eq!(r.numerator(), n);
328 assert_eq!(r.denominator(), d);
329 }
330
331 let r = Ratio::default();
333 assert_eq!(r.numerator(), 1);
334 assert_eq!(r.denominator(), 1);
335
336 let r = Ratio::new(9, 21);
338 assert_eq!(r.numerator(), 9);
339 assert_eq!(r.denominator(), 21);
340 }
341
342 #[test]
343 fn test_reduction_32() {
344 let mut vectors = [
345 (1, 1, 1, 1),
346 (10, 10, 1, 1),
347 (10, 2, 5, 1),
348 (0, 1, 0, 1),
349 (0, 500, 0, 1),
350 (48000, 44100, 160, 147),
351 (44100, 48000, 147, 160),
352 (1000007, 1000000, 1000007, 1000000),
353 ];
354
355 for v in &mut vectors {
356 let mut n = v.0;
357 let mut d = v.1;
358 Ratio::reduce_u32(&mut n, &mut d);
359 assert_eq!((n, d), (v.2, v.3));
360
361 let mut r = Ratio::new(v.0, v.1);
362 r.reduce();
363 assert_eq!((r.numerator(), r.denominator()), (v.2, v.3));
364 }
365 }
366
367 #[test]
368 fn test_reduction_64() {
369 let mut vectors = [
370 (1, 1, 1, 1),
371 (10, 10, 1, 1),
372 (10, 2, 5, 1),
373 (0, 1, 0, 1),
374 (0, 500, 0, 1),
375 (48000, 44100, 160, 147),
376 (44100, 48000, 147, 160),
377 (1000007, 1000000, 1000007, 1000000),
378 (48000336000, 44100000000, 1000007, 918750),
379 ];
380
381 for v in &mut vectors {
382 let mut n = v.0;
383 let mut d = v.1;
384 Ratio::reduce_u64(&mut n, &mut d);
385 assert_eq!((n, d), (v.2, v.3));
386 }
387 }
388
389 #[test]
390 fn test_product() {
391 struct TestVector {
392 a_n: u32,
393 a_d: u32,
394 b_n: u32,
395 b_d: u32,
396 expected_n: u32,
397 expected_d: u32,
398 exact: Exact,
399 }
400
401 let test_vectors = [
402 TestVector {
403 a_n: 1,
404 a_d: 1,
405 b_n: 1,
406 b_d: 1,
407 expected_n: 1,
408 expected_d: 1,
409 exact: Exact::Yes,
410 },
411 TestVector {
412 a_n: 0,
413 a_d: 1,
414 b_n: 1,
415 b_d: 1,
416 expected_n: 0,
417 expected_d: 1,
418 exact: Exact::Yes,
419 },
420 TestVector {
421 a_n: 0,
422 a_d: 500,
423 b_n: 1,
424 b_d: 1,
425 expected_n: 0,
426 expected_d: 1,
427 exact: Exact::Yes,
428 },
429 TestVector {
430 a_n: 3,
431 a_d: 4,
432 b_n: 5,
433 b_d: 9,
434 expected_n: 5,
435 expected_d: 12,
436 exact: Exact::Yes,
437 },
438 TestVector {
439 a_n: 48000,
440 a_d: 44100,
441 b_n: 1000007,
442 b_d: 1000000,
443 expected_n: 1000007,
444 expected_d: 918750,
445 exact: Exact::Yes,
446 },
447 TestVector {
448 a_n: 3465653567,
449 a_d: 2327655023,
450 b_n: 1291540343,
451 b_d: 3698423317,
452 expected_n: 317609835,
453 expected_d: 610852072,
454 exact: Exact::No,
455 },
456 TestVector {
457 a_n: 0xFFFFFFFF,
458 a_d: 1,
459 b_n: 0xFFFFFFFF,
460 b_d: 1,
461 expected_n: 0xFFFFFFFF,
462 expected_d: 1,
463 exact: Exact::No,
464 },
465 TestVector {
466 a_n: 1,
467 a_d: 0xFFFFFFFF,
468 b_n: 1,
469 b_d: 0xFFFFFFFF,
470 expected_n: 0,
471 expected_d: 1,
472 exact: Exact::No,
473 },
474 ];
475
476 for v in &test_vectors {
477 let a = Ratio::new(v.a_n, v.a_d);
478 let b = Ratio::new(v.b_n, v.b_d);
479
480 let res = Ratio::product(a, b, v.exact);
481 assert_eq!(
482 (res.numerator(), res.denominator()),
483 (v.expected_n, v.expected_d),
484 "Expected {}/{} * {}/{} to produce {}/{}; got {}/{} instead (static)",
485 v.a_n,
486 v.a_d,
487 v.b_n,
488 v.b_d,
489 v.expected_n,
490 v.expected_d,
491 res.numerator(),
492 res.denominator()
493 );
494
495 let res = Ratio::product(b, a, v.exact);
496 assert_eq!(
497 (res.numerator(), res.denominator()),
498 (v.expected_n, v.expected_d),
499 "Expected {}/{} * {}/{} to produce {}/{}; got {}/{} instead (commutative static)",
500 v.b_n,
501 v.b_d,
502 v.a_n,
503 v.a_d,
504 v.expected_n,
505 v.expected_d,
506 res.numerator(),
507 res.denominator()
508 );
509
510 if v.exact == Exact::Yes {
511 let res = a * b;
512 assert_eq!((res.numerator(), res.denominator()), (v.expected_n, v.expected_d));
513
514 let res = b * a;
515 assert_eq!((res.numerator(), res.denominator()), (v.expected_n, v.expected_d));
516
517 if b.invertible() {
518 let res = a / b.inverse();
519 assert_eq!((res.numerator(), res.denominator()), (v.expected_n, v.expected_d));
520 }
521
522 if a.invertible() {
523 let res = b / a.inverse();
524 assert_eq!((res.numerator(), res.denominator()), (v.expected_n, v.expected_d));
525 }
526 }
527 }
528 }
529
530 #[test]
531 fn test_product_raw() {
532 struct TestVector {
533 a_n: u32,
534 a_d: u32,
535 b_n: u32,
536 b_d: u32,
537 expected_n: u32,
538 expected_d: u32,
539 exact: Exact,
540 }
541
542 let test_vectors = [
543 TestVector {
544 a_n: 1,
545 a_d: 1,
546 b_n: 1,
547 b_d: 1,
548 expected_n: 1,
549 expected_d: 1,
550 exact: Exact::Yes,
551 },
552 TestVector {
553 a_n: 0,
554 a_d: 1,
555 b_n: 1,
556 b_d: 1,
557 expected_n: 0,
558 expected_d: 1,
559 exact: Exact::Yes,
560 },
561 TestVector {
562 a_n: 0,
563 a_d: 500,
564 b_n: 1,
565 b_d: 1,
566 expected_n: 0,
567 expected_d: 1,
568 exact: Exact::Yes,
569 },
570 TestVector {
571 a_n: 3,
572 a_d: 4,
573 b_n: 5,
574 b_d: 9,
575 expected_n: 5,
576 expected_d: 12,
577 exact: Exact::Yes,
578 },
579 TestVector {
580 a_n: 48000,
581 a_d: 44100,
582 b_n: 1000007,
583 b_d: 1000000,
584 expected_n: 1000007,
585 expected_d: 918750,
586 exact: Exact::Yes,
587 },
588 TestVector {
589 a_n: 3465653567,
590 a_d: 2327655023,
591 b_n: 1291540343,
592 b_d: 3698423317,
593 expected_n: 317609835,
594 expected_d: 610852072,
595 exact: Exact::No,
596 },
597 TestVector {
598 a_n: 0xFFFFFFFF,
599 a_d: 1,
600 b_n: 0xFFFFFFFF,
601 b_d: 1,
602 expected_n: 0xFFFFFFFF,
603 expected_d: 1,
604 exact: Exact::No,
605 },
606 TestVector {
607 a_n: 1,
608 a_d: 0xFFFFFFFF,
609 b_n: 1,
610 b_d: 0xFFFFFFFF,
611 expected_n: 0,
612 expected_d: 1,
613 exact: Exact::No,
614 },
615 ];
616
617 for v in &test_vectors {
618 let res = Ratio::product_raw(v.a_n, v.a_d, v.b_n, v.b_d, v.exact);
619 assert_eq!(
620 res,
621 (v.expected_n, v.expected_d),
622 "Expected {}/{} * {}/{} to produce {}/{}; got {}/{} instead",
623 v.a_n,
624 v.a_d,
625 v.b_n,
626 v.b_d,
627 v.expected_n,
628 v.expected_d,
629 res.0,
630 res.1
631 );
632 }
633 }
634
635 fn test_scale_helper<const ROUND: u8>() {
636 struct TestVector {
637 val: i64,
638 n: u32,
639 d: u32,
640 expected: i64,
641 fractional_result: bool,
642 }
643
644 let test_vectors = [
645 TestVector { val: 0, n: 0, d: 1, expected: 0, fractional_result: false },
646 TestVector { val: 1234567890, n: 0, d: 1, expected: 0, fractional_result: false },
647 TestVector { val: 0, n: 1, d: 1, expected: 0, fractional_result: false },
648 TestVector {
649 val: 1234567890,
650 n: 1,
651 d: 1,
652 expected: 1234567890,
653 fractional_result: false,
654 },
655 TestVector { val: 198, n: 48000, d: 44100, expected: 215, fractional_result: true },
656 TestVector { val: -198, n: 48000, d: 44100, expected: -216, fractional_result: true },
657 TestVector {
658 val: 49 * 198,
659 n: 48000,
660 d: 44100,
661 expected: 10560,
662 fractional_result: false,
663 },
664 TestVector {
665 val: -(49 * 198),
666 n: 48000,
667 d: 44100,
668 expected: -10560,
669 fractional_result: false,
670 },
671 TestVector {
672 val: (49 * 198) + 1,
673 n: 48000,
674 d: 44100,
675 expected: 10561,
676 fractional_result: true,
677 },
678 TestVector {
679 val: -((49 * 198) + 1),
680 n: 48000,
681 d: 44100,
682 expected: -10562,
683 fractional_result: true,
684 },
685 TestVector {
686 val: 0x1517ffffeae80,
687 n: 0xbebc200,
688 d: 0x33333333,
689 expected: 0x4e94914f0000,
690 fractional_result: false,
691 },
692 TestVector {
693 val: -0x1517ffffeae80,
694 n: 0xbebc200,
695 d: 0x33333333,
696 expected: -0x4e94914f0000,
697 fractional_result: false,
698 },
699 TestVector {
700 val: i64::MAX,
701 n: 1000001,
702 d: 1000000,
703 expected: Ratio::OVERFLOW,
704 fractional_result: false,
705 },
706 TestVector {
707 val: i64::MIN,
708 n: 1000001,
709 d: 1000000,
710 expected: Ratio::UNDERFLOW,
711 fractional_result: false,
712 },
713 TestVector {
714 val: -0x2000000000000001,
715 n: 4,
716 d: 1,
717 expected: Ratio::UNDERFLOW,
718 fractional_result: false,
719 },
720 ];
721
722 for v in &test_vectors {
723 let res_static = Ratio::scale_with_round::<ROUND>(v.val, v.n, v.d);
724 let r = Ratio::new(v.n, v.d);
725 let res_inst = r.scale::<ROUND>(v.val);
726
727 let adjusted_expected = if !v.fractional_result || ROUND == Round::DOWN {
728 v.expected
729 } else if v.val >= 0 {
730 if ROUND == Round::TOWARDS_ZERO { v.expected } else { v.expected + 1 }
731 } else {
732 if ROUND == Round::AWAY_FROM_ZERO { v.expected } else { v.expected + 1 }
733 };
734
735 assert_eq!(
736 res_static, adjusted_expected,
737 "Static: Expected {} * {}/{} to produce {}; got {}",
738 v.val, v.n, v.d, adjusted_expected, res_static
739 );
740 assert_eq!(
741 res_inst, adjusted_expected,
742 "Instanced: Expected {} * {}/{} to produce {}; got {}",
743 v.val, v.n, v.d, adjusted_expected, res_inst
744 );
745
746 if ROUND == Round::DOWN {
747 let res_op1 = r * v.val;
748 let res_op2 = v.val * r;
749 assert_eq!(res_op1, adjusted_expected);
750 assert_eq!(res_op2, adjusted_expected);
751
752 if r.invertible() {
753 let res_op3 = v.val / r.inverse();
754 assert_eq!(res_op3, adjusted_expected);
755 }
756 }
757 }
758 }
759
760 #[test]
761 fn test_scale_round_down() {
762 test_scale_helper::<{ Round::DOWN }>();
763 }
764 #[test]
765 fn test_scale_round_up() {
766 test_scale_helper::<{ Round::UP }>();
767 }
768 #[test]
769 fn test_scale_round_towards_zero() {
770 test_scale_helper::<{ Round::TOWARDS_ZERO }>();
771 }
772 #[test]
773 fn test_scale_round_away_from_zero() {
774 test_scale_helper::<{ Round::AWAY_FROM_ZERO }>();
775 }
776
777 #[test]
778 fn test_inverse() {
779 let test_vectors = [(1, 1), (123456, 987654)];
780 for &(n, d) in &test_vectors {
781 let r = Ratio::new(n, d);
782 let inv = r.inverse();
783 assert_eq!(inv.numerator(), d);
784 assert_eq!(inv.denominator(), n);
785 }
786
787 let r = Ratio::new(0, 1);
788 assert!(!r.invertible());
789 }
790}