Skip to main content

fixed_bigint/fixeduint/
mul_div_impl.rs

1use super::{const_div_rem, const_mul, maybe_panic, FixedUInt, MachineWord, PanicReason};
2use crate::const_numtraits::{
3    ConstBounded, ConstCheckedDiv, ConstCheckedMul, ConstCheckedRem, ConstOverflowingMul,
4    ConstSaturatingMul, ConstWrappingMul, ConstZero,
5};
6use crate::machineword::ConstMachineWord;
7
8impl<T: MachineWord, const N: usize> num_traits::ops::overflowing::OverflowingMul
9    for FixedUInt<T, N>
10{
11    fn overflowing_mul(&self, other: &Self) -> (Self, bool) {
12        <Self as ConstOverflowingMul>::overflowing_mul(self, other)
13    }
14}
15
16c0nst::c0nst! {
17    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstOverflowingMul for FixedUInt<T, N> {
18        fn overflowing_mul(&self, other: &Self) -> (Self, bool) {
19            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::WORD_BITS);
20            (Self { array }, overflow)
21        }
22    }
23
24    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstWrappingMul for FixedUInt<T, N> {
25        fn wrapping_mul(&self, other: &Self) -> Self {
26            let (array, _) = const_mul::<T, N, false>(&self.array, &other.array, Self::WORD_BITS);
27            Self { array }
28        }
29    }
30
31    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstCheckedMul for FixedUInt<T, N> {
32        fn checked_mul(&self, other: &Self) -> Option<Self> {
33            let (res, overflow) = <Self as ConstOverflowingMul>::overflowing_mul(self, other);
34            if overflow { None } else { Some(res) }
35        }
36    }
37
38    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstSaturatingMul for FixedUInt<T, N> {
39        fn saturating_mul(&self, other: &Self) -> Self {
40            let (res, overflow) = <Self as ConstOverflowingMul>::overflowing_mul(self, other);
41            if overflow { <Self as ConstBounded>::max_value() } else { res }
42        }
43    }
44}
45
46c0nst::c0nst! {
47    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Mul for FixedUInt<T, N> {
48        type Output = Self;
49        fn mul(self, other: Self) -> Self::Output {
50            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::WORD_BITS);
51            if overflow {
52                maybe_panic(PanicReason::Mul);
53            }
54            Self { array }
55        }
56    }
57
58    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Mul<&FixedUInt<T, N>> for FixedUInt<T, N> {
59        type Output = Self;
60        fn mul(self, other: &FixedUInt<T, N>) -> Self::Output {
61            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::WORD_BITS);
62            if overflow {
63                maybe_panic(PanicReason::Mul);
64            }
65            Self { array }
66        }
67    }
68
69    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Mul<FixedUInt<T, N>> for &FixedUInt<T, N> {
70        type Output = FixedUInt<T, N>;
71        fn mul(self, other: FixedUInt<T, N>) -> Self::Output {
72            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::Output::WORD_BITS);
73            if overflow {
74                maybe_panic(PanicReason::Mul);
75            }
76            FixedUInt { array }
77        }
78    }
79
80    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Mul<&FixedUInt<T, N>> for &FixedUInt<T, N> {
81        type Output = FixedUInt<T, N>;
82        fn mul(self, other: &FixedUInt<T, N>) -> Self::Output {
83            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::Output::WORD_BITS);
84            if overflow {
85                maybe_panic(PanicReason::Mul);
86            }
87            FixedUInt { array }
88        }
89    }
90
91    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Mul<&&FixedUInt<T, N>> for &FixedUInt<T, N> {
92        type Output = FixedUInt<T, N>;
93        fn mul(self, other: &&FixedUInt<T, N>) -> Self::Output {
94            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::Output::WORD_BITS);
95            if overflow {
96                maybe_panic(PanicReason::Mul);
97            }
98            FixedUInt { array }
99        }
100    }
101
102    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::MulAssign for FixedUInt<T, N> {
103        fn mul_assign(&mut self, other: Self) {
104            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::WORD_BITS);
105            if overflow {
106                maybe_panic(PanicReason::Mul);
107            }
108            *self = Self { array };
109        }
110    }
111
112    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::MulAssign<&FixedUInt<T, N>> for FixedUInt<T, N> {
113        fn mul_assign(&mut self, other: &FixedUInt<T, N>) {
114            let (array, overflow) = const_mul::<T, N, true>(&self.array, &other.array, Self::WORD_BITS);
115            if overflow {
116                maybe_panic(PanicReason::Mul);
117            }
118            *self = Self { array };
119        }
120    }
121}
122
123// num_traits wrappers - delegate to const versions
124impl<T: MachineWord, const N: usize> num_traits::WrappingMul for FixedUInt<T, N> {
125    fn wrapping_mul(&self, other: &Self) -> Self {
126        <Self as ConstWrappingMul>::wrapping_mul(self, other)
127    }
128}
129
130impl<T: MachineWord, const N: usize> num_traits::CheckedMul for FixedUInt<T, N> {
131    fn checked_mul(&self, other: &Self) -> Option<Self> {
132        <Self as ConstCheckedMul>::checked_mul(self, other)
133    }
134}
135
136impl<T: MachineWord, const N: usize> num_traits::ops::saturating::SaturatingMul
137    for FixedUInt<T, N>
138{
139    fn saturating_mul(&self, other: &Self) -> Self {
140        <Self as ConstSaturatingMul>::saturating_mul(self, other)
141    }
142}
143
144c0nst::c0nst! {
145    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Div for FixedUInt<T, N> {
146        type Output = Self;
147        fn div(self, other: Self) -> Self::Output {
148            Self { array: const_div_rem(&self.array, &other.array).0 }
149        }
150    }
151
152    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Div<&FixedUInt<T, N>> for FixedUInt<T, N> {
153        type Output = Self;
154        fn div(self, other: &FixedUInt<T, N>) -> Self::Output {
155            Self { array: const_div_rem(&self.array, &other.array).0 }
156        }
157    }
158
159    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Div<FixedUInt<T, N>> for &FixedUInt<T, N> {
160        type Output = FixedUInt<T, N>;
161        fn div(self, other: FixedUInt<T, N>) -> Self::Output {
162            Self::Output { array: const_div_rem(&self.array, &other.array).0 }
163        }
164    }
165
166    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Div<&FixedUInt<T, N>> for &FixedUInt<T, N> {
167        type Output = FixedUInt<T, N>;
168        fn div(self, other: &FixedUInt<T, N>) -> Self::Output {
169            Self::Output { array: const_div_rem(&self.array, &other.array).0 }
170        }
171    }
172
173    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::DivAssign for FixedUInt<T, N> {
174        fn div_assign(&mut self, other: Self) {
175            self.array = const_div_rem(&self.array, &other.array).0;
176        }
177    }
178
179    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::DivAssign<&FixedUInt<T, N>> for FixedUInt<T, N> {
180        fn div_assign(&mut self, other: &FixedUInt<T, N>) {
181            self.array = const_div_rem(&self.array, &other.array).0;
182        }
183    }
184}
185
186c0nst::c0nst! {
187    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstCheckedDiv for FixedUInt<T, N> {
188        fn checked_div(&self, other: &Self) -> Option<Self> {
189            if <Self as ConstZero>::is_zero(other) {
190                None
191            } else {
192                Some(*self / *other)
193            }
194        }
195    }
196}
197
198impl<T: MachineWord, const N: usize> num_traits::CheckedDiv for FixedUInt<T, N> {
199    fn checked_div(&self, other: &Self) -> Option<Self> {
200        <Self as ConstCheckedDiv>::checked_div(self, other)
201    }
202}
203
204c0nst::c0nst! {
205    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Rem for FixedUInt<T, N> {
206        type Output = Self;
207        fn rem(self, other: Self) -> Self::Output {
208            Self { array: const_div_rem(&self.array, &other.array).1 }
209        }
210    }
211
212    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Rem<&FixedUInt<T, N>> for FixedUInt<T, N> {
213        type Output = Self;
214        fn rem(self, other: &FixedUInt<T, N>) -> Self::Output {
215            Self { array: const_div_rem(&self.array, &other.array).1 }
216        }
217    }
218
219    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Rem<FixedUInt<T, N>> for &FixedUInt<T, N> {
220        type Output = FixedUInt<T, N>;
221        fn rem(self, other: FixedUInt<T, N>) -> Self::Output {
222            Self::Output { array: const_div_rem(&self.array, &other.array).1 }
223        }
224    }
225
226    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Rem<&FixedUInt<T, N>> for &FixedUInt<T, N> {
227        type Output = FixedUInt<T, N>;
228        fn rem(self, other: &FixedUInt<T, N>) -> Self::Output {
229            Self::Output { array: const_div_rem(&self.array, &other.array).1 }
230        }
231    }
232
233    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::RemAssign for FixedUInt<T, N> {
234        fn rem_assign(&mut self, other: Self) {
235            self.array = const_div_rem(&self.array, &other.array).1;
236        }
237    }
238
239    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::RemAssign<&FixedUInt<T, N>> for FixedUInt<T, N> {
240        fn rem_assign(&mut self, other: &FixedUInt<T, N>) {
241            self.array = const_div_rem(&self.array, &other.array).1;
242        }
243    }
244}
245
246c0nst::c0nst! {
247    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstCheckedRem for FixedUInt<T, N> {
248        fn checked_rem(&self, other: &Self) -> Option<Self> {
249            if <Self as ConstZero>::is_zero(other) {
250                None
251            } else {
252                Some(*self % *other)
253            }
254        }
255    }
256}
257
258impl<T: MachineWord, const N: usize> num_traits::CheckedRem for FixedUInt<T, N> {
259    fn checked_rem(&self, other: &Self) -> Option<Self> {
260        <Self as ConstCheckedRem>::checked_rem(self, other)
261    }
262}
263
264// Test wrappers for const mul traits
265#[cfg(test)]
266c0nst::c0nst! {
267    pub c0nst fn const_wrapping_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
268        a: &FixedUInt<T, N>,
269        b: &FixedUInt<T, N>
270    ) -> FixedUInt<T, N> {
271        <FixedUInt<T, N> as ConstWrappingMul>::wrapping_mul(a, b)
272    }
273
274    pub c0nst fn const_checked_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
275        a: &FixedUInt<T, N>,
276        b: &FixedUInt<T, N>
277    ) -> Option<FixedUInt<T, N>> {
278        <FixedUInt<T, N> as ConstCheckedMul>::checked_mul(a, b)
279    }
280
281    pub c0nst fn const_saturating_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
282        a: &FixedUInt<T, N>,
283        b: &FixedUInt<T, N>
284    ) -> FixedUInt<T, N> {
285        <FixedUInt<T, N> as ConstSaturatingMul>::saturating_mul(a, b)
286    }
287
288    pub c0nst fn const_overflowing_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
289        a: &FixedUInt<T, N>,
290        b: &FixedUInt<T, N>
291    ) -> (FixedUInt<T, N>, bool) {
292        <FixedUInt<T, N> as ConstOverflowingMul>::overflowing_mul(a, b)
293    }
294
295    pub c0nst fn const_checked_div<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
296        a: &FixedUInt<T, N>,
297        b: &FixedUInt<T, N>
298    ) -> Option<FixedUInt<T, N>> {
299        <FixedUInt<T, N> as ConstCheckedDiv>::checked_div(a, b)
300    }
301
302    pub c0nst fn const_checked_rem<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
303        a: &FixedUInt<T, N>,
304        b: &FixedUInt<T, N>
305    ) -> Option<FixedUInt<T, N>> {
306        <FixedUInt<T, N> as ConstCheckedRem>::checked_rem(a, b)
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use super::*;
313
314    #[test]
315    fn test_basic_mul() {
316        let a = FixedUInt::<u8, 2>::from(123u8);
317        let b = FixedUInt::<u8, 2>::from(234u8);
318        let c = a * b;
319        assert_eq!(c, FixedUInt::<u8, 2>::from(28782u16));
320    }
321
322    #[test]
323    fn test_mul_combinations() {
324        let a = FixedUInt::<u8, 2>::from(12u8);
325        let b = FixedUInt::<u8, 2>::from(3u8);
326        let expected = FixedUInt::<u8, 2>::from(36u8);
327
328        // value * value
329        assert_eq!(a * b, expected);
330        // value * ref
331        assert_eq!(a * &b, expected);
332        // ref * value
333        assert_eq!(&a * b, expected);
334        // ref * ref
335        assert_eq!(&a * &b, expected);
336    }
337
338    #[test]
339    fn test_div_combinations() {
340        let a = FixedUInt::<u8, 2>::from(36u8);
341        let b = FixedUInt::<u8, 2>::from(3u8);
342        let expected = FixedUInt::<u8, 2>::from(12u8);
343
344        // value / value
345        assert_eq!(a / b, expected);
346        // value / ref
347        assert_eq!(a / &b, expected);
348        // ref / value
349        assert_eq!(&a / b, expected);
350        // ref / ref
351        assert_eq!(&a / &b, expected);
352    }
353
354    #[test]
355    fn test_rem_combinations() {
356        let a = FixedUInt::<u8, 2>::from(37u8);
357        let b = FixedUInt::<u8, 2>::from(3u8);
358        let expected = FixedUInt::<u8, 2>::from(1u8); // 37 % 3 = 1
359
360        // value % value
361        assert_eq!(a % b, expected);
362        // value % ref
363        assert_eq!(a % &b, expected);
364        // ref % value
365        assert_eq!(&a % b, expected);
366        // ref % ref
367        assert_eq!(&a % &b, expected);
368    }
369
370    #[test]
371    fn test_const_mul_traits() {
372        let a = FixedUInt::<u8, 2>::from(12u8);
373        let b = FixedUInt::<u8, 2>::from(3u8);
374        let expected = FixedUInt::<u8, 2>::from(36u8);
375
376        // ConstWrappingMul
377        assert_eq!(const_wrapping_mul(&a, &b), expected);
378
379        // ConstCheckedMul - no overflow
380        assert_eq!(const_checked_mul(&a, &b), Some(expected));
381
382        // ConstSaturatingMul - no overflow
383        assert_eq!(const_saturating_mul(&a, &b), expected);
384
385        // ConstOverflowingMul - no overflow
386        let (result, overflow) = const_overflowing_mul(&a, &b);
387        assert_eq!(result, expected);
388        assert!(!overflow);
389
390        // Test overflow cases: 256 * 256 = 65536 which overflows 16 bits
391        let large = FixedUInt::<u8, 2>::from(256u16); // 0x100
392
393        // ConstCheckedMul - with overflow
394        assert_eq!(const_checked_mul(&large, &large), None);
395
396        // ConstSaturatingMul - with overflow saturates to max
397        let saturated = const_saturating_mul(&large, &large);
398        assert_eq!(saturated, FixedUInt::<u8, 2>::from(0xFFFFu16));
399
400        // ConstOverflowingMul - with overflow
401        let (_, overflow) = const_overflowing_mul(&large, &large);
402        assert!(overflow);
403
404        #[cfg(feature = "nightly")]
405        {
406            const A: FixedUInt<u8, 2> = FixedUInt { array: [12, 0] };
407            const B: FixedUInt<u8, 2> = FixedUInt { array: [3, 0] };
408
409            const WRAPPING_RESULT: FixedUInt<u8, 2> = const_wrapping_mul(&A, &B);
410            assert_eq!(WRAPPING_RESULT.array, [36, 0]);
411
412            const CHECKED_RESULT: Option<FixedUInt<u8, 2>> = const_checked_mul(&A, &B);
413            assert!(CHECKED_RESULT.is_some());
414
415            const SATURATING_RESULT: FixedUInt<u8, 2> = const_saturating_mul(&A, &B);
416            assert_eq!(SATURATING_RESULT.array, [36, 0]);
417
418            const OVERFLOWING_RESULT: (FixedUInt<u8, 2>, bool) = const_overflowing_mul(&A, &B);
419            assert_eq!(OVERFLOWING_RESULT.0.array, [36, 0]);
420            assert!(!OVERFLOWING_RESULT.1);
421        }
422    }
423
424    #[test]
425    fn test_const_checked_div_rem() {
426        let a = FixedUInt::<u8, 2>::from(36u8);
427        let b = FixedUInt::<u8, 2>::from(5u8);
428        let zero = FixedUInt::<u8, 2>::from(0u8);
429
430        // ConstCheckedDiv - valid division
431        assert_eq!(
432            const_checked_div(&a, &b),
433            Some(FixedUInt::<u8, 2>::from(7u8))
434        ); // 36 / 5 = 7
435
436        // ConstCheckedDiv - divide by zero
437        assert_eq!(const_checked_div(&a, &zero), None);
438
439        // ConstCheckedRem - valid remainder
440        assert_eq!(
441            const_checked_rem(&a, &b),
442            Some(FixedUInt::<u8, 2>::from(1u8))
443        ); // 36 % 5 = 1
444
445        // ConstCheckedRem - remainder by zero
446        assert_eq!(const_checked_rem(&a, &zero), None);
447
448        #[cfg(feature = "nightly")]
449        {
450            const A: FixedUInt<u8, 2> = FixedUInt { array: [36, 0] };
451            const B: FixedUInt<u8, 2> = FixedUInt { array: [5, 0] };
452            const ZERO: FixedUInt<u8, 2> = FixedUInt { array: [0, 0] };
453
454            const CHECKED_DIV_OK: Option<FixedUInt<u8, 2>> = const_checked_div(&A, &B);
455            const CHECKED_DIV_ZERO: Option<FixedUInt<u8, 2>> = const_checked_div(&A, &ZERO);
456            const CHECKED_REM_OK: Option<FixedUInt<u8, 2>> = const_checked_rem(&A, &B);
457            const CHECKED_REM_ZERO: Option<FixedUInt<u8, 2>> = const_checked_rem(&A, &ZERO);
458
459            assert_eq!(CHECKED_DIV_OK, Some(FixedUInt { array: [7, 0] }));
460            assert_eq!(CHECKED_DIV_ZERO, None);
461            assert_eq!(CHECKED_REM_OK, Some(FixedUInt { array: [1, 0] }));
462            assert_eq!(CHECKED_REM_ZERO, None);
463        }
464    }
465}