Skip to main content

fixed_bigint/fixeduint/
mul_div_impl.rs

1use super::{
2    FixedUInt, MachineWord, PanicReason, const_ct_select, const_div_rem, const_mul, maybe_panic_if,
3};
4use crate::machineword::ConstMachineWord;
5use const_num_traits::{
6    Bounded, CheckedDiv, CheckedMul, CheckedRem, OverflowingMul, SaturatingMul, WrappingMul, Zero,
7};
8use const_num_traits::{Nct, Personality, PersonalityTag};
9
10#[cfg(feature = "num-traits")]
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 OverflowingMul>::overflowing_mul(self, other)
16    }
17}
18
19c0nst::c0nst! {
20    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> OverflowingMul for FixedUInt<T, N, P> {
21        type Output = FixedUInt<T, N, P>;
22        fn overflowing_mul(self, other: Self) -> (Self, bool) {
23            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::WORD_BITS);
24            (Self::from_array(array), overflow)
25        }
26    }
27
28    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> WrappingMul for FixedUInt<T, N, P> {
29        type Output = FixedUInt<T, N, P>;
30        fn wrapping_mul(self, other: Self) -> Self {
31            let (array, _) = const_mul::<T, N, false, P>(&self.array, &other.array, Self::WORD_BITS);
32            Self::from_array(array)
33        }
34    }
35
36    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CheckedMul for FixedUInt<T, N, P> {
37        type Output = FixedUInt<T, N, P>;
38        fn checked_mul(self, other: Self) -> Option<Self> {
39            let (res, overflow) = <Self as OverflowingMul>::overflowing_mul(self, other);
40            if overflow { None } else { Some(res) }
41        }
42    }
43
44    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> SaturatingMul for FixedUInt<T, N, P> {
45        type Output = FixedUInt<T, N, P>;
46        fn saturating_mul(self, other: Self) -> Self {
47            let (res, overflow) = <Self as OverflowingMul>::overflowing_mul(self, other);
48            match P::TAG {
49                PersonalityTag::Nct => if overflow { <Self as Bounded>::max_value() } else { res },
50                PersonalityTag::Ct => const_ct_select(res, <Self as Bounded>::max_value(), overflow as u8),
51            }
52        }
53    }
54
55    // --- Reference-receiver mul impls -------------------------------------
56    //
57    // Read-through-ref primitives. See add_sub_impl.rs for the CT rationale
58    // — bodies must NOT deref-copy the operand `FixedUInt` onto the stack
59    // (that defeats the whole reason `&T` is used, e.g. `Zeroizing<T>` on
60    // ed25519's CT sign path). `const_mul` reads `&[T; N]` slices and
61    // returns a fresh `[T; N]`. The `Checked` and `Saturating` mirrors
62    // delegate to the primitives above so they inherit the read-through
63    // behaviour.
64
65    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> OverflowingMul for &FixedUInt<T, N, P> {
66        type Output = FixedUInt<T, N, P>;
67        fn overflowing_mul(self, other: Self) -> (FixedUInt<T, N, P>, bool) {
68            let (array, overflow) = const_mul::<T, N, true, P>(
69                &self.array,
70                &other.array,
71                FixedUInt::<T, N, P>::WORD_BITS,
72            );
73            (FixedUInt::from_array(array), overflow)
74        }
75    }
76
77    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> WrappingMul for &FixedUInt<T, N, P> {
78        type Output = FixedUInt<T, N, P>;
79        fn wrapping_mul(self, other: Self) -> FixedUInt<T, N, P> {
80            let (array, _overflow) = const_mul::<T, N, false, P>(
81                &self.array,
82                &other.array,
83                FixedUInt::<T, N, P>::WORD_BITS,
84            );
85            FixedUInt::from_array(array)
86        }
87    }
88
89    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CheckedMul for &FixedUInt<T, N, P> {
90        type Output = FixedUInt<T, N, P>;
91        fn checked_mul(self, other: Self) -> Option<FixedUInt<T, N, P>> {
92            let (res, overflow) = <Self as OverflowingMul>::overflowing_mul(self, other);
93            if overflow { None } else { Some(res) }
94        }
95    }
96
97    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> SaturatingMul for &FixedUInt<T, N, P> {
98        type Output = FixedUInt<T, N, P>;
99        fn saturating_mul(self, other: Self) -> FixedUInt<T, N, P> {
100            let (res, overflow) = <Self as OverflowingMul>::overflowing_mul(self, other);
101            if overflow {
102                <FixedUInt<T, N, P> as Bounded>::max_value()
103            } else {
104                res
105            }
106        }
107    }
108}
109
110c0nst::c0nst! {
111    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Mul for FixedUInt<T, N, P> {
112        type Output = Self;
113        fn mul(self, other: Self) -> Self::Output {
114            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::WORD_BITS);
115            maybe_panic_if::<P>(overflow, PanicReason::Mul);
116            Self::from_array(array)
117        }
118    }
119
120    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Mul<&FixedUInt<T, N, P>> for FixedUInt<T, N, P> {
121        type Output = Self;
122        fn mul(self, other: &FixedUInt<T, N, P>) -> Self::Output {
123            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::WORD_BITS);
124            maybe_panic_if::<P>(overflow, PanicReason::Mul);
125            Self::from_array(array)
126        }
127    }
128
129    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Mul<FixedUInt<T, N, P>> for &FixedUInt<T, N, P> {
130        type Output = FixedUInt<T, N, P>;
131        fn mul(self, other: FixedUInt<T, N, P>) -> Self::Output {
132            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::Output::WORD_BITS);
133            maybe_panic_if::<P>(overflow, PanicReason::Mul);
134            FixedUInt::from_array(array)
135        }
136    }
137
138    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Mul<&FixedUInt<T, N, P>> for &FixedUInt<T, N, P> {
139        type Output = FixedUInt<T, N, P>;
140        fn mul(self, other: &FixedUInt<T, N, P>) -> Self::Output {
141            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::Output::WORD_BITS);
142            maybe_panic_if::<P>(overflow, PanicReason::Mul);
143            FixedUInt::from_array(array)
144        }
145    }
146
147    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Mul<&&FixedUInt<T, N, P>> for &FixedUInt<T, N, P> {
148        type Output = FixedUInt<T, N, P>;
149        fn mul(self, other: &&FixedUInt<T, N, P>) -> Self::Output {
150            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::Output::WORD_BITS);
151            maybe_panic_if::<P>(overflow, PanicReason::Mul);
152            FixedUInt::from_array(array)
153        }
154    }
155
156    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::MulAssign for FixedUInt<T, N, P> {
157        fn mul_assign(&mut self, other: Self) {
158            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::WORD_BITS);
159            maybe_panic_if::<P>(overflow, PanicReason::Mul);
160            *self = Self::from_array(array);
161        }
162    }
163
164    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::MulAssign<&FixedUInt<T, N, P>> for FixedUInt<T, N, P> {
165        fn mul_assign(&mut self, other: &FixedUInt<T, N, P>) {
166            let (array, overflow) = const_mul::<T, N, true, P>(&self.array, &other.array, Self::WORD_BITS);
167            maybe_panic_if::<P>(overflow, PanicReason::Mul);
168            *self = Self::from_array(array);
169        }
170    }
171}
172
173// num_traits wrappers - delegate to const versions
174#[cfg(feature = "num-traits")]
175impl<T: MachineWord, const N: usize, P: Personality> num_traits::WrappingMul
176    for FixedUInt<T, N, P>
177{
178    fn wrapping_mul(&self, other: &Self) -> Self {
179        <&Self as WrappingMul>::wrapping_mul(self, other)
180    }
181}
182
183#[cfg(feature = "num-traits")]
184impl<T: MachineWord, const N: usize, P: Personality> num_traits::CheckedMul for FixedUInt<T, N, P> {
185    fn checked_mul(&self, other: &Self) -> Option<Self> {
186        <&Self as CheckedMul>::checked_mul(self, other)
187    }
188}
189
190#[cfg(feature = "num-traits")]
191impl<T: MachineWord, const N: usize, P: Personality> num_traits::ops::saturating::SaturatingMul
192    for FixedUInt<T, N, P>
193{
194    fn saturating_mul(&self, other: &Self) -> Self {
195        <&Self as SaturatingMul>::saturating_mul(self, other)
196    }
197}
198
199c0nst::c0nst! {
200    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Div for FixedUInt<T, N, Nct> {
201        type Output = Self;
202        fn div(self, other: Self) -> Self::Output {
203            Self::from_array(const_div_rem(&self.array, &other.array).0)
204        }
205    }
206
207    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Div<&FixedUInt<T, N, Nct>> for FixedUInt<T, N, Nct> {
208        type Output = Self;
209        fn div(self, other: &FixedUInt<T, N, Nct>) -> Self::Output {
210            Self::from_array(const_div_rem(&self.array, &other.array).0)
211        }
212    }
213
214    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Div<FixedUInt<T, N, Nct>> for &FixedUInt<T, N, Nct> {
215        type Output = FixedUInt<T, N, Nct>;
216        fn div(self, other: FixedUInt<T, N, Nct>) -> Self::Output {
217            Self::Output::from_array(const_div_rem(&self.array, &other.array).0)
218        }
219    }
220
221    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Div<&FixedUInt<T, N, Nct>> for &FixedUInt<T, N, Nct> {
222        type Output = FixedUInt<T, N, Nct>;
223        fn div(self, other: &FixedUInt<T, N, Nct>) -> Self::Output {
224            Self::Output::from_array(const_div_rem(&self.array, &other.array).0)
225        }
226    }
227
228    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::DivAssign for FixedUInt<T, N, Nct> {
229        fn div_assign(&mut self, other: Self) {
230            self.array = const_div_rem(&self.array, &other.array).0;
231        }
232    }
233
234    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::DivAssign<&FixedUInt<T, N, Nct>> for FixedUInt<T, N, Nct> {
235        fn div_assign(&mut self, other: &FixedUInt<T, N, Nct>) {
236            self.array = const_div_rem(&self.array, &other.array).0;
237        }
238    }
239}
240
241c0nst::c0nst! {
242    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> CheckedDiv for FixedUInt<T, N, Nct> {
243        type Output = FixedUInt<T, N, Nct>;
244        fn checked_div(self, other: Self) -> Option<Self> {
245            if <Self as Zero>::is_zero(&other) {
246                None
247            } else {
248                Some(self / other)
249            }
250        }
251    }
252
253    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> CheckedDiv for &FixedUInt<T, N, Nct> {
254        type Output = FixedUInt<T, N, Nct>;
255        fn checked_div(self, other: Self) -> Option<FixedUInt<T, N, Nct>> {
256            <FixedUInt<T, N, Nct> as CheckedDiv>::checked_div(FixedUInt::from_array(self.array), FixedUInt::from_array(other.array))
257        }
258    }
259}
260
261#[cfg(feature = "num-traits")]
262impl<T: MachineWord, const N: usize> num_traits::CheckedDiv for FixedUInt<T, N, Nct> {
263    fn checked_div(&self, other: &Self) -> Option<Self> {
264        <&Self as CheckedDiv>::checked_div(self, other)
265    }
266}
267
268c0nst::c0nst! {
269    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Rem for FixedUInt<T, N, Nct> {
270        type Output = Self;
271        fn rem(self, other: Self) -> Self::Output {
272            Self::from_array(const_div_rem(&self.array, &other.array).1)
273        }
274    }
275
276    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Rem<&FixedUInt<T, N, Nct>> for FixedUInt<T, N, Nct> {
277        type Output = Self;
278        fn rem(self, other: &FixedUInt<T, N, Nct>) -> Self::Output {
279            Self::from_array(const_div_rem(&self.array, &other.array).1)
280        }
281    }
282
283    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Rem<FixedUInt<T, N, Nct>> for &FixedUInt<T, N, Nct> {
284        type Output = FixedUInt<T, N, Nct>;
285        fn rem(self, other: FixedUInt<T, N, Nct>) -> Self::Output {
286            Self::Output::from_array(const_div_rem(&self.array, &other.array).1)
287        }
288    }
289
290    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::Rem<&FixedUInt<T, N, Nct>> for &FixedUInt<T, N, Nct> {
291        type Output = FixedUInt<T, N, Nct>;
292        fn rem(self, other: &FixedUInt<T, N, Nct>) -> Self::Output {
293            Self::Output::from_array(const_div_rem(&self.array, &other.array).1)
294        }
295    }
296
297    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::RemAssign for FixedUInt<T, N, Nct> {
298        fn rem_assign(&mut self, other: Self) {
299            self.array = const_div_rem(&self.array, &other.array).1;
300        }
301    }
302
303    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> core::ops::RemAssign<&FixedUInt<T, N, Nct>> for FixedUInt<T, N, Nct> {
304        fn rem_assign(&mut self, other: &FixedUInt<T, N, Nct>) {
305            self.array = const_div_rem(&self.array, &other.array).1;
306        }
307    }
308}
309
310c0nst::c0nst! {
311    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> CheckedRem for FixedUInt<T, N, Nct> {
312        type Output = FixedUInt<T, N, Nct>;
313        fn checked_rem(self, other: Self) -> Option<Self> {
314            if <Self as Zero>::is_zero(&other) {
315                None
316            } else {
317                Some(self % other)
318            }
319        }
320    }
321
322    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> CheckedRem for &FixedUInt<T, N, Nct> {
323        type Output = FixedUInt<T, N, Nct>;
324        fn checked_rem(self, other: Self) -> Option<FixedUInt<T, N, Nct>> {
325            <FixedUInt<T, N, Nct> as CheckedRem>::checked_rem(FixedUInt::from_array(self.array), FixedUInt::from_array(other.array))
326        }
327    }
328}
329
330#[cfg(feature = "num-traits")]
331impl<T: MachineWord, const N: usize> num_traits::CheckedRem for FixedUInt<T, N, Nct> {
332    fn checked_rem(&self, other: &Self) -> Option<Self> {
333        <&Self as CheckedRem>::checked_rem(self, other)
334    }
335}
336
337// Test wrappers for const mul traits
338#[cfg(test)]
339c0nst::c0nst! {
340    pub c0nst fn const_wrapping_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
341        a: &FixedUInt<T, N, P>,
342        b: &FixedUInt<T, N, P>
343    ) -> FixedUInt<T, N, P> {
344        <FixedUInt<T, N, P> as WrappingMul>::wrapping_mul(*a, *b)
345    }
346
347    pub c0nst fn const_checked_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
348        a: &FixedUInt<T, N, P>,
349        b: &FixedUInt<T, N, P>
350    ) -> Option<FixedUInt<T, N, P>> {
351        <FixedUInt<T, N, P> as CheckedMul>::checked_mul(*a, *b)
352    }
353
354    pub c0nst fn const_saturating_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
355        a: &FixedUInt<T, N, P>,
356        b: &FixedUInt<T, N, P>
357    ) -> FixedUInt<T, N, P> {
358        <FixedUInt<T, N, P> as SaturatingMul>::saturating_mul(*a, *b)
359    }
360
361    pub c0nst fn const_overflowing_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
362        a: &FixedUInt<T, N, P>,
363        b: &FixedUInt<T, N, P>
364    ) -> (FixedUInt<T, N, P>, bool) {
365        <FixedUInt<T, N, P> as OverflowingMul>::overflowing_mul(*a, *b)
366    }
367
368    pub c0nst fn const_checked_div<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
369        a: &FixedUInt<T, N, Nct>,
370        b: &FixedUInt<T, N, Nct>
371    ) -> Option<FixedUInt<T, N, Nct>> {
372        <FixedUInt<T, N, Nct> as CheckedDiv>::checked_div(*a, *b)
373    }
374
375    pub c0nst fn const_checked_rem<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
376        a: &FixedUInt<T, N, Nct>,
377        b: &FixedUInt<T, N, Nct>
378    ) -> Option<FixedUInt<T, N, Nct>> {
379        <FixedUInt<T, N, Nct> as CheckedRem>::checked_rem(*a, *b)
380    }
381}
382
383// ── CtCheckedMul (masked-return checked mul) ─────────────────────────
384//
385// Same shape as CtCheckedAdd/Sub: delegate to `OverflowingMul`, wrap
386// the (Self, bool) overflow flag into CtOption via Choice. Uniform
387// across both personalities — the underlying `const_mul`'s `true`
388// generic arm computes the overflow flag branchlessly.
389impl<T, const N: usize, P: Personality> const_num_traits::ops::ct::CtCheckedMul
390    for FixedUInt<T, N, P>
391where
392    T: MachineWord,
393{
394    fn ct_checked_mul(&self, v: &Self) -> subtle::CtOption<Self> {
395        let (val, overflow) = <&Self as OverflowingMul>::overflowing_mul(self, v);
396        subtle::CtOption::new(val, subtle::Choice::from(!overflow as u8))
397    }
398}
399
400#[cfg(test)]
401// Coverage tests deliberately exercise every ref/value combination of
402// `Mul`/`Div` (see `test_*_combinations`).
403#[allow(clippy::op_ref)]
404mod tests {
405    use super::*;
406
407    #[test]
408    fn test_basic_mul() {
409        let a = FixedUInt::<u8, 2>::from(123u8);
410        let b = FixedUInt::<u8, 2>::from(234u8);
411        let c = a * b;
412        assert_eq!(c, FixedUInt::<u8, 2>::from(28782u16));
413    }
414
415    #[test]
416    fn test_mul_combinations() {
417        let a = FixedUInt::<u8, 2>::from(12u8);
418        let b = FixedUInt::<u8, 2>::from(3u8);
419        let expected = FixedUInt::<u8, 2>::from(36u8);
420
421        // value * value
422        assert_eq!(a * b, expected);
423        // value * ref
424        assert_eq!(a * &b, expected);
425        // ref * value
426        assert_eq!(&a * b, expected);
427        // ref * ref
428        assert_eq!(&a * &b, expected);
429    }
430
431    #[test]
432    fn test_div_combinations() {
433        let a = FixedUInt::<u8, 2>::from(36u8);
434        let b = FixedUInt::<u8, 2>::from(3u8);
435        let expected = FixedUInt::<u8, 2>::from(12u8);
436
437        // value / value
438        assert_eq!(a / b, expected);
439        // value / ref
440        assert_eq!(a / &b, expected);
441        // ref / value
442        assert_eq!(&a / b, expected);
443        // ref / ref
444        assert_eq!(&a / &b, expected);
445    }
446
447    #[test]
448    fn test_rem_combinations() {
449        let a = FixedUInt::<u8, 2>::from(37u8);
450        let b = FixedUInt::<u8, 2>::from(3u8);
451        let expected = FixedUInt::<u8, 2>::from(1u8); // 37 % 3 = 1
452
453        // value % value
454        assert_eq!(a % b, expected);
455        // value % ref
456        assert_eq!(a % &b, expected);
457        // ref % value
458        assert_eq!(&a % b, expected);
459        // ref % ref
460        assert_eq!(&a % &b, expected);
461    }
462
463    #[test]
464    fn test_const_mul_traits() {
465        let a = FixedUInt::<u8, 2>::from(12u8);
466        let b = FixedUInt::<u8, 2>::from(3u8);
467        let expected = FixedUInt::<u8, 2>::from(36u8);
468
469        // WrappingMul
470        assert_eq!(const_wrapping_mul(&a, &b), expected);
471
472        // CheckedMul - no overflow
473        assert_eq!(const_checked_mul(&a, &b), Some(expected));
474
475        // SaturatingMul - no overflow
476        assert_eq!(const_saturating_mul(&a, &b), expected);
477
478        // OverflowingMul - no overflow
479        let (result, overflow) = const_overflowing_mul(&a, &b);
480        assert_eq!(result, expected);
481        assert!(!overflow);
482
483        // Test overflow cases: 256 * 256 = 65536 which overflows 16 bits
484        let large = FixedUInt::<u8, 2>::from(256u16); // 0x100
485
486        // CheckedMul - with overflow
487        assert_eq!(const_checked_mul(&large, &large), None);
488
489        // SaturatingMul - with overflow saturates to max
490        let saturated = const_saturating_mul(&large, &large);
491        assert_eq!(saturated, FixedUInt::<u8, 2>::from(0xFFFFu16));
492
493        // OverflowingMul - with overflow
494        let (_, overflow) = const_overflowing_mul(&large, &large);
495        assert!(overflow);
496
497        #[cfg(feature = "nightly")]
498        {
499            const A: FixedUInt<u8, 2> = FixedUInt::from_array([12, 0]);
500            const B: FixedUInt<u8, 2> = FixedUInt::from_array([3, 0]);
501
502            const WRAPPING_RESULT: FixedUInt<u8, 2> = const_wrapping_mul(&A, &B);
503            assert_eq!(WRAPPING_RESULT.array, [36, 0]);
504
505            const CHECKED_RESULT: Option<FixedUInt<u8, 2>> = const_checked_mul(&A, &B);
506            assert!(CHECKED_RESULT.is_some());
507
508            const SATURATING_RESULT: FixedUInt<u8, 2> = const_saturating_mul(&A, &B);
509            assert_eq!(SATURATING_RESULT.array, [36, 0]);
510
511            const OVERFLOWING_RESULT: (FixedUInt<u8, 2>, bool) = const_overflowing_mul(&A, &B);
512            assert_eq!(OVERFLOWING_RESULT.0.array, [36, 0]);
513            assert!(!OVERFLOWING_RESULT.1);
514        }
515    }
516
517    #[test]
518    fn test_const_checked_div_rem() {
519        let a = FixedUInt::<u8, 2>::from(36u8);
520        let b = FixedUInt::<u8, 2>::from(5u8);
521        let zero = FixedUInt::<u8, 2>::from(0u8);
522
523        // CheckedDiv - valid division
524        assert_eq!(
525            const_checked_div(&a, &b),
526            Some(FixedUInt::<u8, 2>::from(7u8))
527        ); // 36 / 5 = 7
528
529        // CheckedDiv - divide by zero
530        assert_eq!(const_checked_div(&a, &zero), None);
531
532        // CheckedRem - valid remainder
533        assert_eq!(
534            const_checked_rem(&a, &b),
535            Some(FixedUInt::<u8, 2>::from(1u8))
536        ); // 36 % 5 = 1
537
538        // CheckedRem - remainder by zero
539        assert_eq!(const_checked_rem(&a, &zero), None);
540
541        #[cfg(feature = "nightly")]
542        {
543            const A: FixedUInt<u8, 2> = FixedUInt::from_array([36, 0]);
544            const B: FixedUInt<u8, 2> = FixedUInt::from_array([5, 0]);
545            const ZERO: FixedUInt<u8, 2> = FixedUInt::from_array([0, 0]);
546
547            const CHECKED_DIV_OK: Option<FixedUInt<u8, 2>> = const_checked_div(&A, &B);
548            const CHECKED_DIV_ZERO: Option<FixedUInt<u8, 2>> = const_checked_div(&A, &ZERO);
549            const CHECKED_REM_OK: Option<FixedUInt<u8, 2>> = const_checked_rem(&A, &B);
550            const CHECKED_REM_ZERO: Option<FixedUInt<u8, 2>> = const_checked_rem(&A, &ZERO);
551
552            assert_eq!(CHECKED_DIV_OK, Some(FixedUInt::from_array([7, 0])));
553            assert_eq!(CHECKED_DIV_ZERO, None);
554            assert_eq!(CHECKED_REM_OK, Some(FixedUInt::from_array([1, 0])));
555            assert_eq!(CHECKED_REM_ZERO, None);
556        }
557    }
558
559    #[test]
560    fn ct_checked_mul_masks_overflow() {
561        use const_num_traits::ops::ct::CtCheckedMul;
562        type U16 = FixedUInt<u8, 2>;
563        let ok = U16::from(100u8).ct_checked_mul(&U16::from(50u8));
564        assert!(bool::from(ok.is_some()));
565        assert_eq!(ok.unwrap(), U16::from(5000u16));
566        // Overflow: 1000 * 1000 = 1_000_000 > U16::MAX
567        let bad = U16::from(1000u16).ct_checked_mul(&U16::from(1000u16));
568        assert!(!bool::from(bad.is_some()));
569    }
570}