Skip to main content

fixed_bigint/fixeduint/
mul_div_impl.rs

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