1#[inline]
27pub fn abs_i32(value: i32) -> i32 {
28 if value == i32::MIN {
29 i32::MAX
30 } else if value < 0 {
31 -value
32 } else {
33 value
34 }
35}
36
37#[inline]
39pub fn abs_i16(value: i16) -> i16 {
40 if value == i16::MIN {
41 i16::MAX
42 } else if value < 0 {
43 -value
44 } else {
45 value
46 }
47}
48
49#[inline]
51pub fn clamp_i16(value: i32) -> i16 {
52 value.clamp(i16::MIN as i32, i16::MAX as i32) as i16
53}
54
55#[inline]
58pub fn square_i32_saturating(value: i32) -> i32 {
59 let abs = abs_i32(value);
60 if abs > 46_340 {
61 return i32::MAX;
62 }
63 abs * abs
64}
65
66#[inline]
72pub fn isqrt_i32(value: i32) -> i32 {
73 if value <= 0 {
74 return 0;
75 }
76 isqrt_u32(value as u32) as i32
77}
78
79#[inline]
81pub fn isqrt_u32(mut value: u32) -> u32 {
82 let mut root = 0u32;
83 let mut bit = 1u32 << 30;
84 while bit > value {
85 bit >>= 2;
86 }
87 while bit != 0 {
88 if value >= root + bit {
89 value -= root + bit;
90 root = (root >> 1) + bit;
91 } else {
92 root >>= 1;
93 }
94 bit >>= 2;
95 }
96 root
97}
98
99#[cfg(test)]
101const ROOT8: [u8; 256] = {
102 let mut out = [0u8; 256];
103 let mut n = 0;
104 while n < 256 {
105 let mut r = 0;
106 while (r + 1) * (r + 1) <= n {
107 r += 1;
108 }
109 out[n] = r as u8;
110 n += 1;
111 }
112 out
113};
114
115#[cfg(test)]
130#[inline(always)]
131const fn isqrt_below_2_48(high: u32, low: u32) -> u32 {
132 debug_assert!(high >> 16 == 0);
133 let mut root = if high < 256 {
134 ROOT8[high as usize] as u32
135 } else {
136 let mut r = ROOT8[(high >> 8) as usize] as u32;
137 let mut remainder = (high >> 8) - r * r;
138 let mut shift = 8;
139 while shift != 0 {
140 shift -= 2;
141 remainder = (remainder << 2) | ((high >> shift) & 3);
142 let trial = (r << 2) | 1;
143 let take = (remainder >= trial) as u32;
144 remainder -= trial & take.wrapping_neg();
145 r = (r << 1) | take;
146 }
147 r
148 };
149 let mut remainder = high - root * root;
150 let mut low = low;
151 let mut step = 0;
152 while step < 8 {
153 step += 1;
154 remainder = (remainder << 4) | (low >> 28);
155 low <<= 4;
156 let mid = (root << 4) | 4;
157 let upper = (remainder.wrapping_sub(mid) >> 31) ^ 1;
158 remainder -= mid & upper.wrapping_neg();
159 root = (root << 2) | (upper << 1);
160 let trial = (root << 1) | 1;
161 let lower = (remainder >= trial) as u32;
162 remainder -= trial & lower.wrapping_neg();
163 root |= lower;
164 }
165 root
166}
167
168const RSQRT_KNOTS: [u32; 193] = {
171 let mut out = [0u32; 193];
172 let mut i = 0;
173 while i < 193 {
174 let target = (1u128 << 68) / (i as u128 + 64);
177 let (mut lo, mut hi) = (0u128, 1u128 << 35);
179 while lo < hi {
180 let mid = (lo + hi).div_ceil(2);
181 if mid * mid <= target {
182 lo = mid;
183 } else {
184 hi = mid - 1;
185 }
186 }
187 out[i] = if lo >= 1 << 31 {
189 (1 << 31) - 1
190 } else {
191 lo as u32
192 };
193 i += 1;
194 }
195 out
196};
197
198#[inline(always)]
200const fn mul32(a: u32, b: u32) -> u64 {
202 (a as u64) * (b as u64)
204}
205
206#[inline]
220pub const fn isqrt_u64(value: u64) -> u32 {
222 if value == 0 {
223 return 0;
224 }
225 let high = (value >> 32) as u32;
228 let (mut top, mut low, mut shift) = if high != 0 {
229 (high, value as u32, 0)
230 } else {
231 (value as u32, 0, 32)
232 };
233 let mut step = 16;
234 while step >= 2 {
235 if top >> (32 - step) == 0 {
236 top = (top << step) | (low >> (32 - step));
237 low <<= step;
238 shift += step;
239 }
240 step /= 2;
241 }
242 let knot = (top >> 24) as usize - 64;
245 let (left, right) = (RSQRT_KNOTS[knot], RSQRT_KNOTS[knot + 1]);
246 let mut y = left - (((left - right) >> 8) * ((top >> 16) & 0xff));
247 let square = (mul32(y, y) >> 30) as u32;
249 let scaled = (mul32(top, square) >> 32) as u32;
250 y = (mul32(y, (3u32 << 30).wrapping_sub(scaled)) >> 31) as u32;
251 let mut root = (mul32(top, y) >> (30 + shift / 2)) as u32;
252 let mut square = mul32(root, root);
255 while square > value {
256 root -= 1;
257 square -= 2 * root as u64 + 1;
259 }
260 while root != u32::MAX && square + 2 * (root as u64) < value {
262 square += 2 * root as u64 + 1;
264 root += 1;
265 }
266 root
267}
268
269#[inline]
278pub fn mul_q12_i32(value: i32, q12: i32) -> i32 {
279 const Q12_SHIFT: i32 = 12;
280 const Q12_ONE: i32 = 1 << Q12_SHIFT;
281 let whole = (value >> Q12_SHIFT).saturating_mul(q12);
282 let frac = ((value & (Q12_ONE - 1)).saturating_mul(q12)) >> Q12_SHIFT;
283 whole.saturating_add(frac)
284}
285
286#[inline(always)]
296pub fn mul_q12_i32_wide(value: i32, q12: i32) -> i32 {
297 ((i64::from(value) * i64::from(q12)) >> 12) as i32
299}
300
301#[inline(always)]
311const fn divide_digit(remainder: u32, next: u32, divisor: u32) -> (u32, u32) {
312 const RADIX: u32 = 1 << 16;
313 let upper = divisor >> 16;
314 let lower = divisor & 0xFFFF;
315 let mut q = remainder / upper;
316 let mut r = remainder % upper;
317 if q >= RADIX || q * lower > (r << 16) + next {
318 q -= 1;
319 r += upper;
320 if r < RADIX && (q >= RADIX || q * lower > (r << 16) + next) {
321 q -= 1;
322 }
323 }
324 let rem = (remainder << 16)
326 .wrapping_add(next)
327 .wrapping_sub(q.wrapping_mul(divisor));
328 (q, rem)
329}
330
331#[inline]
346pub const fn div_u64_by_u32(hi: u32, lo: u32, divisor: u32) -> u32 {
347 debug_assert!(divisor != 0 && hi < divisor);
348 let shift = divisor.leading_zeros();
349 let normalized = divisor << shift;
350 let top = if shift == 0 {
352 hi
353 } else {
354 (hi << shift) | (lo >> (32 - shift))
355 };
356 let tail = lo << shift;
357 let (upper, remainder) = divide_digit(top, tail >> 16, normalized);
358 let (lower, _) = divide_digit(remainder, tail & 0xFFFF, normalized);
359 (upper << 16) | lower
360}
361
362#[inline]
368pub fn mul_div_u32(a: u32, b: u32, divisor: u32) -> u32 {
369 let product = u64::from(a) * u64::from(b);
371 let hi = (product >> 32) as u32;
372 let lo = product as u32;
373 debug_assert!(divisor != 0 && hi < divisor);
374 if hi == 0 {
375 lo / divisor
376 } else {
377 div_u64_by_u32(hi, lo, divisor)
378 }
379}
380
381#[inline]
401pub fn mul_div_i32(a: i32, b: i32, c: i32) -> i32 {
402 let negative = (a < 0) ^ (b < 0) ^ (c < 0);
403 let divisor = c.unsigned_abs();
404 let product = u64::from(a.unsigned_abs()) * u64::from(b.unsigned_abs());
406 let hi = (product >> 32) as u32;
407 let lo = product as u32;
408 let magnitude = if hi == 0 {
409 lo / divisor
410 } else if hi < divisor {
411 div_u64_by_u32(hi, lo, divisor)
412 } else {
413 div_u64_by_u32(hi % divisor, lo, divisor)
417 };
418 if negative {
419 magnitude.wrapping_neg() as i32
420 } else {
421 magnitude as i32
422 }
423}
424
425#[inline(always)]
434pub fn mul_shr_i32(a: i32, b: i32, shift: u32) -> i32 {
435 debug_assert!(shift < 32);
436 ((i64::from(a) * i64::from(b)) >> shift) as i32
438}
439
440#[inline(always)]
450pub fn mul_shr_trunc_i32(a: i32, b: i32, shift: u32) -> i32 {
451 debug_assert!(shift < 32);
452 let product = i64::from(a) * i64::from(b);
454 let bias = if product < 0 { (1i64 << shift) - 1 } else { 0 };
455 ((product + bias) >> shift) as i32
456}
457
458#[inline]
461pub fn scroll_q8_wrapped(speed: i16, phase: u8, period: u16, tick: u32, hz: u16) -> u8 {
462 debug_assert!(period > 0 && period <= 256);
463 let period = u32::from(period);
464 if speed == 0 || hz == 0 {
465 return (u32::from(phase) % period) as u8;
466 }
467 let divisor = u32::from(hz) << 8;
472 let wrap_ticks = divisor * period;
473 let tick = if tick >= wrap_ticks {
474 tick % wrap_ticks
475 } else {
476 tick
477 };
478 let distance = mul_div_u32(u32::from(speed.unsigned_abs()), tick, divisor) as i32;
480 let distance = if speed < 0 { -distance } else { distance };
481 (distance + i32::from(phase)).rem_euclid(period as i32) as u8
482}
483
484#[derive(Clone, Copy, Debug, PartialEq, Eq)]
495pub struct InvariantDivisor31 {
496 magic: u32,
497 shift: u32,
500}
501
502impl InvariantDivisor31 {
503 #[inline]
507 pub const fn new(divisor: u32) -> Self {
508 debug_assert!(divisor != 0);
509 if divisor <= 1 {
510 return Self {
511 magic: 0,
512 shift: u32::MAX,
513 };
514 }
515 let l = 32 - (divisor - 1).leading_zeros();
518 let magic = div_u64_by_u32(1 << (l - 1), 0, divisor).wrapping_add(1);
519 Self {
520 magic,
521 shift: l - 1,
522 }
523 }
524
525 #[inline(always)]
527 pub fn divide(self, n: u32) -> u32 {
528 debug_assert!(n < 1 << 31);
529 if self.shift == u32::MAX {
530 return n;
531 }
532 let high = ((u64::from(self.magic) * u64::from(n)) >> 32) as u32;
534 high >> self.shift
535 }
536}
537
538#[cfg(test)]
539mod tests {
540 use super::*;
541
542 #[test]
543 #[cfg_attr(
544 miri,
545 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
546 )]
547 fn unsigned_square_root_full_domain_boundaries() {
548 let check = |n: u64| {
549 let r = u64::from(isqrt_u64(n));
550 assert!(r * r <= n);
551 assert!(r == u64::from(u32::MAX) || (r + 1) * (r + 1) > n);
552 };
553 for root in [0u64, 1, 2, 255, 65535, 65536, 1 << 31, u64::from(u32::MAX)] {
554 let square = root * root;
555 for n in [
556 square.saturating_sub(1),
557 square,
558 square.saturating_add(1),
559 u64::MAX,
560 ] {
561 check(n);
562 }
563 }
564 for n in 0..100_000 {
565 check(n);
566 }
567 }
568
569 #[test]
570 #[cfg_attr(
571 miri,
572 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
573 )]
574 fn mul_div_u32_preserves_full_width_animation_phases() {
575 let check = |a: u32, b: u32, d: u32| {
576 let expected = u64::from(a) * u64::from(b) / u64::from(d);
577 assert!(expected <= u64::from(u32::MAX));
578 assert_eq!(u64::from(mul_div_u32(a, b, d)), expected, "{a} * {b} / {d}");
579 };
580 let edges = [1, 2, 59, 60, 65535, 65536, 65537, 1 << 31, u32::MAX];
581 for d in edges {
582 for b in edges {
583 for a in [0, 1, d / 2, d - 1, d] {
584 check(a, b, d);
585 }
586 }
587 }
588 check(u32::MAX, 1, 1);
589 check(65536, 65536, 2);
590 let mut seed = 0x5817_9513u32;
591 let mut next = || {
592 seed ^= seed << 13;
593 seed ^= seed >> 17;
594 seed ^= seed << 5;
595 seed
596 };
597 for _ in 0..100_000 {
598 let d = next().max(1);
599 check(next() % d, next(), d);
600 }
601 }
602
603 #[test]
604 #[cfg_attr(
605 miri,
606 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
607 )]
608 fn wrapped_scroll_matches_wide_oracle_at_wrap_and_tick_boundaries() {
609 let check = |speed: i16, phase: u8, period: u16, tick: u32, hz: u16| {
610 let distance = if hz == 0 {
611 0
612 } else {
613 i64::from(speed) * i64::from(tick) / i64::from(hz) / 256
614 };
615 let expected = (distance + i64::from(phase)).rem_euclid(i64::from(period)) as u8;
616 assert_eq!(
617 scroll_q8_wrapped(speed, phase, period, tick, hz),
618 expected,
619 "speed={speed} phase={phase} period={period} tick={tick} hz={hz}"
620 );
621 };
622 for speed in i16::MIN..=i16::MAX {
624 for tick in [0, 1, 59, 60, 15359, 15360, u32::MAX] {
625 check(speed, 255, 63, tick, 60);
626 }
627 }
628 for period in 1..=256 {
630 for hz in [1u16, 50, 60, 256, 512, 65535] {
631 let cycle = (u32::from(hz) << 8) * u32::from(period);
632 for tick in [0, 1, cycle - 1, cycle, cycle + 1, u32::MAX - 1, u32::MAX] {
633 for speed in [i16::MIN, -32767, -257, -1, 0, 1, 257, i16::MAX] {
634 for phase in [0, 127, 255] {
635 check(speed, phase, period, tick, hz);
636 }
637 }
638 }
639 }
640 }
641 for phase in [0, 127, 255] {
642 check(i16::MIN, phase, 63, u32::MAX, 0);
643 }
644 let mut seed = 0x3211_8a53u32;
645 let mut next = || {
646 seed ^= seed << 13;
647 seed ^= seed >> 17;
648 seed ^= seed << 5;
649 seed
650 };
651 for _ in 0..100_000 {
652 check(
653 next() as i16,
654 next() as u8,
655 (next() % 256 + 1) as u16,
656 next(),
657 next() as u16,
658 );
659 }
660 }
661
662 #[test]
663 #[cfg_attr(
664 miri,
665 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
666 )]
667 fn invariant_divisor_31_is_exact() {
668 let mut state = 0x9e37_79b9_7f4a_7c15u64;
669 let mut next = || {
670 state ^= state << 13;
671 state ^= state >> 7;
672 state ^= state << 17;
673 state
674 };
675 let check = |n: u32, d: u32| {
676 assert_eq!(InvariantDivisor31::new(d).divide(n), n / d, "n={n} d={d}");
677 };
678 for d in 1..=70u32 {
679 for n in 0..2048u32 {
680 check(n, d);
681 }
682 let top = (1u32 << 31) - 1;
683 for n in [top, top - 1, top - d, d, d - 1, d + 1, d * 3 - 1, d * 3] {
684 check(n, d);
685 }
686 }
687 for shift in 1..31u32 {
688 let d = 1u32 << shift;
689 for n in [0, 1, d - 1, d, d + 1, (1u32 << 31) - 1] {
690 check(n, d);
691 check(n, d - 1);
692 check(n, d + 1);
693 }
694 }
695 for _ in 0..200_000 {
696 let d = (next() as u32 >> (next() % 32) as u32).max(1);
697 let n = next() as u32 >> 1;
698 check(n, d);
699 check(n % d, d);
700 check(n - n % d, d);
701 }
702 }
703
704 #[test]
705 fn div_u64_by_u32_matches_the_wide_reference() {
706 let mut state = 0x2545_f491_4f6c_dd1du64;
707 let mut next = || {
708 state ^= state << 13;
709 state ^= state >> 7;
710 state ^= state << 17;
711 state
712 };
713 for _ in 0..20_000 {
714 let divisor = ((next() as u32) | 1).max(2);
715 let hi = (next() as u32) % divisor;
716 let lo = next() as u32;
717 let wide = ((u64::from(hi) << 32) | u64::from(lo)) / u64::from(divisor);
718 assert_eq!(u64::from(div_u64_by_u32(hi, lo, divisor)), wide);
719 }
720 assert_eq!(div_u64_by_u32(0, 0, 1), 0);
721 assert_eq!(div_u64_by_u32(u32::MAX - 1, u32::MAX, u32::MAX), u32::MAX);
722 assert_eq!(div_u64_by_u32(0, 4096 << 12, 4096), 4096);
723 }
724
725 #[test]
726 #[cfg_attr(
727 miri,
728 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
729 )]
730 fn mul_q12_i32_matches_wide_where_exact() {
731 let mut state = 0x9e37_79b9_7f4a_7c15u64;
735 let mut next = || {
736 state ^= state << 13;
737 state ^= state >> 7;
738 state ^= state << 17;
739 state
740 };
741 let mut checked = 0u32;
742 for _ in 0..200_000 {
743 let r = next();
744 let value = ((r as i32) >> 7).clamp(-(1 << 24), 1 << 24);
745 let q12 = (((r >> 32) as i32) >> 19).clamp(-4096, 4096);
746 let exact = (i64::from(value) * i64::from(q12)) >> 12;
747 if exact > i64::from(i32::MAX) || exact < i64::from(i32::MIN) {
748 continue;
749 }
750 assert_eq!(
751 mul_q12_i32(value, q12),
752 mul_q12_i32_wide(value, q12),
753 "{value} {q12}"
754 );
755 assert_eq!(mul_q12_i32_wide(value, q12), exact as i32);
756 checked += 1;
757 }
758 assert!(checked > 190_000, "{checked}");
759 for &(value, q12) in &[
760 (0, 0),
761 (-1, 4096),
762 (-4095, 4096),
763 (4095, -4096),
764 (1 << 24, 4096),
765 (-(1 << 24), 4096),
766 (i32::MAX >> 12, 4096),
767 (i32::MIN >> 12, 4096),
768 (7, -1),
769 (-7, 1),
770 ] {
771 assert_eq!(
772 mul_q12_i32(value, q12),
773 mul_q12_i32_wide(value, q12),
774 "{value} {q12}"
775 );
776 }
777 }
778
779 fn xorshift(mut state: u64) -> impl FnMut() -> u64 {
780 move || {
781 state ^= state << 13;
782 state ^= state >> 7;
783 state ^= state << 17;
784 state
785 }
786 }
787
788 fn assert_floor_root(n: u64) {
790 let r = u128::from(isqrt_u64(n));
791 let n128 = u128::from(n);
792 assert!(
793 r * r <= n128 && (r + 1) * (r + 1) > n128,
794 "isqrt_u64({n}) = {r}"
795 );
796 }
797
798 #[test]
800 #[cfg_attr(
801 miri,
802 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
803 )]
804 fn isqrt_u64_matches_the_nibble_root_below_2_48() {
805 let mut next = xorshift(0x9e37_79b9_7f4a_7c15);
806 for i in 0..4_000_000u64 {
807 let n = (next() >> 16) >> (i % 48);
808 let old = isqrt_below_2_48((n >> 32) as u32, n as u32);
809 assert_eq!(isqrt_u64(n), old, "{n}");
810 }
811 for n in 0..1u64 << 22 {
812 assert_eq!(isqrt_u64(n), isqrt_below_2_48(0, n as u32), "{n}");
813 }
814 }
815
816 #[test]
817 #[cfg_attr(
818 miri,
819 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
820 )]
821 fn isqrt_u64_is_exact_at_every_path_boundary() {
822 let roots = (0u64..300)
825 .chain((1 << 16) - 300..(1 << 16) + 300)
826 .chain((1 << 24) - 300..(1 << 24) + 300)
827 .chain((1 << 32) - 70_000..1 << 32);
828 for root in roots {
829 let square = root * root;
830 for n in [square.saturating_sub(1), square, square.saturating_add(1)] {
831 assert_floor_root(n);
832 }
833 assert_floor_root(square.saturating_add(2 * root));
834 }
835 for n in ((1u64 << 48) - 100_000..(1 << 48) + 100_000).chain(u64::MAX - 100_000..=u64::MAX)
836 {
837 assert_floor_root(n);
838 }
839 for n in 0..1 << 20 {
840 assert_floor_root(n);
841 }
842 }
843
844 #[test]
845 #[cfg_attr(
846 miri,
847 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
848 )]
849 fn isqrt_u64_matches_definition_at_every_magnitude() {
850 let mut next = xorshift(0x2545_f491_4f6c_dd1d);
851 for i in 0..2_000_000u64 {
852 let n = next() >> (i % 64);
853 assert_floor_root(n);
854 let r = u64::from(isqrt_u64(n));
856 assert_floor_root((r * r).wrapping_sub(1));
857 }
858 }
859
860 #[test]
861 #[cfg_attr(
862 miri,
863 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
864 )]
865 fn div_u64_by_u32_every_short_divisor_and_digit_boundary() {
866 let check = |n: u64, d: u32| {
867 assert_eq!(
868 u64::from(div_u64_by_u32((n >> 32) as u32, n as u32, d)),
869 n / u64::from(d),
870 "{n} / {d}"
871 );
872 };
873 let wide = [
874 65537,
875 0x7fff_ffff,
876 0x8000_0000,
877 0x8000_ffff,
878 0xffff_0001,
879 u32::MAX,
880 ];
881 for d in (1u32..=65536).chain(wide) {
882 for q in [
883 0u32,
884 1,
885 65534,
886 65535,
887 65536,
888 65537,
889 0x7fff_ffff,
890 0xffff_fffe,
891 u32::MAX,
892 ] {
893 for rem in [0, d / 2, d - 1] {
894 check(u64::from(q) * u64::from(d) + u64::from(rem), d);
895 }
896 }
897 }
898 let mut next = xorshift(0x9ab4_3f87_6546_bcad);
899 for i in 0..2_000_000u32 {
900 let seed = next();
901 let d = ((seed as u32) >> (i % 32)).max(1);
902 let hi = ((seed >> 32) as u32) % d;
903 check((u64::from(hi) << 32) | u64::from(next() as u32), d);
904 }
905 }
906
907 fn mul_div_wide(a: i32, b: i32, c: i32) -> i32 {
909 (i64::from(a) * i64::from(b) / i64::from(c)) as i32
910 }
911
912 const SIGNED_EDGES: [i32; 23] = [
913 i32::MIN,
914 i32::MIN + 1,
915 -(1 << 30) - 1,
916 -65537,
917 -65536,
918 -65535,
919 -4096,
920 -7,
921 -2,
922 -1,
923 0,
924 1,
925 2,
926 3,
927 7,
928 4096,
929 46341,
930 65535,
931 65536,
932 65537,
933 1 << 30,
934 i32::MAX - 1,
935 i32::MAX,
936 ];
937
938 #[test]
939 fn mul_div_i32_matches_i64_on_every_edge_triple() {
940 for a in SIGNED_EDGES {
941 for b in SIGNED_EDGES {
942 for c in SIGNED_EDGES.into_iter().filter(|&c| c != 0) {
943 assert_eq!(
944 mul_div_i32(a, b, c),
945 mul_div_wide(a, b, c),
946 "{a} * {b} / {c}"
947 );
948 }
949 }
950 }
951 assert_eq!(mul_div_i32(-7, 1, 2), -3);
953 assert_eq!(mul_div_i32(7, -1, 2), -3);
954 assert_eq!(mul_div_i32(-7, -1, -2), -3);
955 assert_eq!(mul_div_i32(i32::MIN, 1, -1), i32::MIN);
956 assert_eq!(mul_div_i32(i32::MIN, i32::MIN, 1), 0);
957 assert_eq!(mul_div_i32(i32::MAX, i32::MAX, 1), 1);
958 assert_eq!(mul_div_i32(123_456, 789, 1), 123_456 * 789);
959 }
960
961 #[test]
962 #[cfg_attr(
963 miri,
964 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
965 )]
966 fn mul_div_i32_matches_i64_on_random_inputs_at_every_magnitude() {
967 let mut next = xorshift(0x5817_9513_0bad_cafe);
968 for i in 0..2_000_000u32 {
969 let r = next();
970 let a = (r as i32) >> (i % 32);
971 let b = ((r >> 32) as i32) >> ((i / 32) % 32);
972 let c = ((next() as i32) >> (i % 31)) | 1;
973 assert_eq!(
974 mul_div_i32(a, b, c),
975 mul_div_wide(a, b, c),
976 "{a} * {b} / {c}"
977 );
978 assert_eq!(mul_div_i32(a, b, 65536), mul_div_wide(a, b, 65536));
980 assert_eq!(mul_div_i32(a, 65536, c), mul_div_wide(a, 65536, c));
981 }
982 }
983
984 #[test]
985 #[should_panic]
986 fn mul_div_i32_panics_on_zero_divisor_with_a_small_product() {
987 mul_div_i32(3, 4, core::hint::black_box(0));
988 }
989
990 #[test]
991 #[should_panic]
992 fn mul_div_i32_panics_on_zero_divisor_with_a_wide_product() {
993 mul_div_i32(i32::MAX, i32::MAX, core::hint::black_box(0));
994 }
995
996 #[test]
997 #[cfg_attr(
998 miri,
999 ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
1000 )]
1001 fn shift_multiplies_match_i64_for_every_shift() {
1002 let check = |a: i32, b: i32, shift: u32| {
1003 let product = i64::from(a) * i64::from(b);
1004 assert_eq!(
1005 mul_shr_i32(a, b, shift),
1006 (product >> shift) as i32,
1007 "{a} {b} {shift}"
1008 );
1009 assert_eq!(
1010 mul_shr_trunc_i32(a, b, shift),
1011 (product / (1i64 << shift)) as i32,
1012 "{a} {b} {shift}"
1013 );
1014 if shift < 31 {
1015 assert_eq!(
1016 mul_shr_trunc_i32(a, b, shift),
1017 mul_div_i32(a, b, 1 << shift)
1018 );
1019 }
1020 };
1021 for shift in 0..32 {
1022 for a in SIGNED_EDGES {
1023 for b in SIGNED_EDGES {
1024 check(a, b, shift);
1025 }
1026 }
1027 }
1028 let mut next = xorshift(0x3211_8a53_dead_beef);
1029 for i in 0..1_000_000u32 {
1030 let r = next();
1031 check(
1032 r as i32 >> (i % 32),
1033 (r >> 32) as i32 >> ((i / 7) % 32),
1034 i % 32,
1035 );
1036 }
1037 assert_eq!(mul_shr_i32(-1, 1, 16), -1);
1038 assert_eq!(mul_shr_trunc_i32(-1, 1, 16), 0);
1039 assert_eq!(mul_shr_i32(-65537, 1, 16), -2);
1040 assert_eq!(mul_shr_trunc_i32(-65537, 1, 16), -1);
1041 }
1042
1043 #[test]
1044 fn abs_saturates_at_min() {
1045 assert_eq!(abs_i32(i32::MIN), i32::MAX);
1046 assert_eq!(abs_i32(-5), 5);
1047 assert_eq!(abs_i32(7), 7);
1048 assert_eq!(abs_i16(i16::MIN), i16::MAX);
1049 assert_eq!(abs_i16(-5), 5);
1050 }
1051
1052 #[test]
1053 fn clamp_i16_covers_both_ends() {
1054 assert_eq!(clamp_i16(40_000), i16::MAX);
1055 assert_eq!(clamp_i16(-40_000), i16::MIN);
1056 assert_eq!(clamp_i16(123), 123);
1057 }
1058
1059 #[test]
1060 fn square_saturates_past_46340() {
1061 assert_eq!(square_i32_saturating(46_340), 46_340 * 46_340);
1062 assert_eq!(square_i32_saturating(46_341), i32::MAX);
1063 assert_eq!(square_i32_saturating(i32::MIN), i32::MAX);
1064 assert_eq!(square_i32_saturating(-3), 9);
1065 }
1066
1067 #[test]
1068 fn isqrt_matches_floor_sqrt() {
1069 assert_eq!(isqrt_i32(0), 0);
1070 assert_eq!(isqrt_i32(-9), 0);
1071 assert_eq!(isqrt_i32(1), 1);
1072 assert_eq!(isqrt_i32(15), 3);
1073 assert_eq!(isqrt_i32(16), 4);
1074 assert_eq!(isqrt_i32(i32::MAX), 46_340);
1075 }
1076
1077 #[test]
1078 fn mul_q12_handles_sign_and_scale() {
1079 assert_eq!(mul_q12_i32(1000, 4096), 1000);
1080 assert_eq!(mul_q12_i32(1000, 2048), 500);
1081 assert_eq!(mul_q12_i32(-1000, 2048), -500);
1082 assert_eq!(mul_q12_i32(0, 4096), 0);
1083 }
1084}