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