Skip to main content

fixed_bigint/heapless/
multiple.rs

1//! `const_num_traits::MultipleOf` / `NextMultipleOf` for `HeaplessBigInt<_, Nct>`.
2//!
3//! Division-based, so Nct-only. `MultipleOf::is_multiple_of` returns `false`
4//! for a zero divisor (the const-num-traits convention — distinct from
5//! `num_integer::Integer::is_multiple_of`, which returns `self.is_zero()`).
6
7use super::HeaplessBigInt;
8use crate::MachineWord;
9use const_num_traits::{CarryingMul, CheckedAdd, MultipleOf, Nct, NextMultipleOf, Zero};
10
11impl<T, const CAP: usize> MultipleOf for HeaplessBigInt<T, CAP, Nct>
12where
13    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
14{
15    fn is_multiple_of(self, rhs: Self) -> bool {
16        if <Self as Zero>::is_zero(&rhs) {
17            false
18        } else {
19            <Self as Zero>::is_zero(&(self % rhs))
20        }
21    }
22}
23
24impl<T, const CAP: usize> NextMultipleOf for HeaplessBigInt<T, CAP, Nct>
25where
26    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
27{
28    type Output = Self;
29
30    fn next_multiple_of(self, rhs: Self) -> Self {
31        match self.checked_next_multiple_of(rhs) {
32            Some(v) => v,
33            None => panic!("HeaplessBigInt::next_multiple_of: rhs is zero or the result overflows"),
34        }
35    }
36
37    fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
38        if <Self as Zero>::is_zero(&rhs) {
39            return None;
40        }
41        let rem = self % rhs;
42        if <Self as Zero>::is_zero(&rem) {
43            // Already a multiple, but the result width is max(self.len, rhs.len)
44            // (the non-zero path resolves there via `%`/`+`); widen so this
45            // early return doesn't narrow below the contract.
46            Some(self.widened(core::cmp::max(self.len(), rhs.len())))
47        } else {
48            // self + (rhs - rem), None on overflow.
49            CheckedAdd::checked_add(self, rhs - rem)
50        }
51    }
52}
53
54// `&Self` mirrors so `(&h).is_multiple_of(&g)` resolves without an explicit copy.
55impl<T, const CAP: usize> MultipleOf for &HeaplessBigInt<T, CAP, Nct>
56where
57    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
58{
59    fn is_multiple_of(self, rhs: Self) -> bool {
60        <HeaplessBigInt<T, CAP, Nct> as MultipleOf>::is_multiple_of(*self, *rhs)
61    }
62}
63
64impl<T, const CAP: usize> NextMultipleOf for &HeaplessBigInt<T, CAP, Nct>
65where
66    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
67{
68    type Output = HeaplessBigInt<T, CAP, Nct>;
69
70    fn next_multiple_of(self, rhs: Self) -> Self::Output {
71        <HeaplessBigInt<T, CAP, Nct> as NextMultipleOf>::next_multiple_of(*self, *rhs)
72    }
73
74    fn checked_next_multiple_of(self, rhs: Self) -> Option<Self::Output> {
75        <HeaplessBigInt<T, CAP, Nct> as NextMultipleOf>::checked_next_multiple_of(*self, *rhs)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::HeaplessBigInt;
82    use const_num_traits::NextMultipleOf;
83
84    type H = HeaplessBigInt<u8, 8>;
85
86    // The already-a-multiple early return must carry max(self.len, rhs.len),
87    // not the narrow self width. The value-based shared harness can't see this
88    // (its operands are all one width), so pin it here.
89    #[test]
90    fn already_multiple_preserves_wider_rhs_width() {
91        let self_narrow = H::from(10u8); // len 1
92        let rhs_wide = H::from(5u8).widened(8); // len 8
93        assert_eq!(self_narrow.len(), 1);
94        let out = NextMultipleOf::checked_next_multiple_of(self_narrow, rhs_wide).unwrap();
95        assert_eq!(out, H::from(10u8));
96        assert_eq!(out.len(), 8, "result must widen to max(self.len, rhs.len)");
97    }
98
99    #[test]
100    fn rhs_larger_than_self_is_rhs() {
101        // 3's next multiple of 10 is 10 itself (the `self + (rhs - rem)` path).
102        let out = NextMultipleOf::next_multiple_of(H::from(3u8), H::from(10u8));
103        assert_eq!(out, H::from(10u8));
104    }
105
106    #[test]
107    fn byref_matches_value() {
108        use const_num_traits::MultipleOf;
109        let a = H::from(12u8);
110        let b = H::from(5u8);
111        assert_eq!(
112            MultipleOf::is_multiple_of(&a, &b),
113            MultipleOf::is_multiple_of(a, b)
114        );
115        assert_eq!(
116            NextMultipleOf::next_multiple_of(&a, &b),
117            NextMultipleOf::next_multiple_of(a, b)
118        );
119        assert_eq!(
120            NextMultipleOf::checked_next_multiple_of(&a, &b),
121            NextMultipleOf::checked_next_multiple_of(a, b)
122        );
123    }
124}