Skip to main content

fixed_bigint/fixeduint/
add_sub_impl.rs

1use super::{
2    FixedUInt, MachineWord, PanicReason, add_impl, const_ct_select, maybe_panic_if, sub_impl,
3};
4use crate::machineword::ConstMachineWord;
5use const_num_traits::ops::ct::{CtCheckedAdd, CtCheckedSub};
6use const_num_traits::{
7    Bounded, CheckedAdd, CheckedSub, ConstZero, OverflowingAdd, OverflowingSub, Personality,
8    PersonalityTag, SaturatingAdd, SaturatingSub, WrappingAdd, WrappingSub,
9};
10
11c0nst::c0nst! {
12    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> OverflowingAdd for FixedUInt<T, N, P> {
13        fn overflowing_add(self, other: Self) -> (Self, bool) {
14            let mut ret = self;
15            let overflow = add_impl(&mut ret.array, &other.array);
16            (ret, overflow)
17        }
18    }
19
20    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> OverflowingSub for FixedUInt<T, N, P> {
21        fn overflowing_sub(self, other: Self) -> (Self, bool) {
22            let mut ret = self;
23            let overflow = sub_impl(&mut ret.array, &other.array);
24            (ret, overflow)
25        }
26    }
27
28    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> WrappingAdd for FixedUInt<T, N, P> {
29        fn wrapping_add(self, other: Self) -> Self {
30            <Self as OverflowingAdd>::overflowing_add(self, other).0
31        }
32    }
33
34    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> WrappingSub for FixedUInt<T, N, P> {
35        fn wrapping_sub(self, other: Self) -> Self {
36            <Self as OverflowingSub>::overflowing_sub(self, other).0
37        }
38    }
39
40    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CheckedAdd for FixedUInt<T, N, P> {
41        fn checked_add(self, other: Self) -> Option<Self> {
42            let (res, overflow) = <Self as OverflowingAdd>::overflowing_add(self, other);
43            if overflow { None } else { Some(res) }
44        }
45    }
46
47    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CheckedSub for FixedUInt<T, N, P> {
48        fn checked_sub(self, other: Self) -> Option<Self> {
49            let (res, overflow) = <Self as OverflowingSub>::overflowing_sub(self, other);
50            if overflow { None } else { Some(res) }
51        }
52    }
53
54    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> SaturatingAdd for FixedUInt<T, N, P> {
55        fn saturating_add(self, other: Self) -> Self {
56            let (res, overflow) = <Self as OverflowingAdd>::overflowing_add(self, other);
57            match P::TAG {
58                PersonalityTag::Nct => if overflow { <Self as Bounded>::max_value() } else { res },
59                PersonalityTag::Ct => const_ct_select(res, <Self as Bounded>::max_value(), overflow as u8),
60            }
61        }
62    }
63
64    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> SaturatingSub for FixedUInt<T, N, P> {
65        fn saturating_sub(self, other: Self) -> Self {
66            let (res, overflow) = <Self as OverflowingSub>::overflowing_sub(self, other);
67            match P::TAG {
68                PersonalityTag::Nct => if overflow { <Self as ConstZero>::ZERO } else { res },
69                PersonalityTag::Ct => const_ct_select(res, <Self as ConstZero>::ZERO, overflow as u8),
70            }
71        }
72    }
73
74    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Add for FixedUInt<T, N, P> {
75        type Output = Self;
76        fn add(self, other: Self) -> Self {
77            let (res, overflow) = <Self as OverflowingAdd>::overflowing_add(self, other);
78            maybe_panic_if::<P>(overflow, PanicReason::Add);
79            res
80        }
81    }
82
83    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Sub for FixedUInt<T, N, P> {
84        type Output = Self;
85        fn sub(self, other: Self) -> Self {
86            let (res, overflow) = <Self as OverflowingSub>::overflowing_sub(self, other);
87            maybe_panic_if::<P>(overflow, PanicReason::Sub);
88            res
89        }
90    }
91
92    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Add<&'_ Self> for FixedUInt<T, N, P> {
93        type Output = Self;
94        fn add(self, other: &Self) -> Self {
95            let (res, overflow) = <Self as OverflowingAdd>::overflowing_add(self, *other);
96            maybe_panic_if::<P>(overflow, PanicReason::Add);
97            res
98        }
99    }
100
101    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Add<FixedUInt<T, N, P>> for &FixedUInt<T, N, P> {
102        type Output = FixedUInt<T, N, P>;
103        fn add(self, other: FixedUInt<T, N, P>) -> Self::Output {
104            let (res, overflow) = <FixedUInt<T, N, P> as OverflowingAdd>::overflowing_add(*self, other);
105            maybe_panic_if::<P>(overflow, PanicReason::Add);
106            res
107        }
108    }
109
110    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Add<Self> for &FixedUInt<T, N, P> {
111        type Output = FixedUInt<T, N, P>;
112        fn add(self, other: Self) -> Self::Output {
113            let (res, overflow) = <FixedUInt<T, N, P> as OverflowingAdd>::overflowing_add(*self, *other);
114            maybe_panic_if::<P>(overflow, PanicReason::Add);
115            res
116        }
117    }
118
119    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Sub<&'_ Self> for FixedUInt<T, N, P> {
120        type Output = Self;
121        fn sub(self, other: &Self) -> Self {
122            let (res, overflow) = <Self as OverflowingSub>::overflowing_sub(self, *other);
123            maybe_panic_if::<P>(overflow, PanicReason::Sub);
124            res
125        }
126    }
127
128    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Sub<FixedUInt<T, N, P>> for &FixedUInt<T, N, P> {
129        type Output = FixedUInt<T, N, P>;
130        fn sub(self, other: FixedUInt<T, N, P>) -> Self::Output {
131            let (res, overflow) = <FixedUInt<T, N, P> as OverflowingSub>::overflowing_sub(*self, other);
132            maybe_panic_if::<P>(overflow, PanicReason::Sub);
133            res
134        }
135    }
136
137    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::Sub<Self> for &FixedUInt<T, N, P> {
138        type Output = FixedUInt<T, N, P>;
139        fn sub(self, other: Self) -> Self::Output {
140            let (res, overflow) = <FixedUInt<T, N, P> as OverflowingSub>::overflowing_sub(*self, *other);
141            maybe_panic_if::<P>(overflow, PanicReason::Sub);
142            res
143        }
144    }
145
146    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::AddAssign<Self> for FixedUInt<T, N, P> {
147        fn add_assign(&mut self, other: Self) {
148            let mut array = self.array;
149            let overflow = add_impl(&mut array, &other.array);
150            maybe_panic_if::<P>(overflow, PanicReason::Add);
151            self.array = array;
152        }
153    }
154
155    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::AddAssign<&'_ Self> for FixedUInt<T, N, P> {
156        fn add_assign(&mut self, other: &Self) {
157            let mut array = self.array;
158            let overflow = add_impl(&mut array, &other.array);
159            maybe_panic_if::<P>(overflow, PanicReason::Add);
160            self.array = array;
161        }
162    }
163
164    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::SubAssign<Self> for FixedUInt<T, N, P> {
165        fn sub_assign(&mut self, other: Self) {
166            let mut array = self.array;
167            let overflow = sub_impl(&mut array, &other.array);
168            maybe_panic_if::<P>(overflow, PanicReason::Sub);
169            self.array = array;
170        }
171    }
172
173    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::ops::SubAssign<&'_ Self> for FixedUInt<T, N, P> {
174        fn sub_assign(&mut self, other: &Self) {
175            let mut array = self.array;
176            let overflow = sub_impl(&mut array, &other.array);
177            maybe_panic_if::<P>(overflow, PanicReason::Sub);
178            self.array = array;
179        }
180    }
181
182    // --- Reference-receiver impls ------------------------------------------
183    //
184    // Mirror the by-value trait surface for `&Self` via the trait's
185    // `type Output` — for Copy carriers like FixedUInt the body just
186    // dereferences and delegates. Call sites can write
187    // `(&x).checked_add(&y)` (or use a `T: CheckedAdd` bound where T is
188    // `&FixedUInt`) without sprinkling derefs. The `Add<&FixedUInt> for
189    // &FixedUInt` impls above supply `Output = FixedUInt<T,N,P>`.
190
191    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> OverflowingAdd for &FixedUInt<T, N, P> {
192        fn overflowing_add(self, other: Self) -> (FixedUInt<T, N, P>, bool) {
193            <FixedUInt<T, N, P> as OverflowingAdd>::overflowing_add(*self, *other)
194        }
195    }
196
197    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> OverflowingSub for &FixedUInt<T, N, P> {
198        fn overflowing_sub(self, other: Self) -> (FixedUInt<T, N, P>, bool) {
199            <FixedUInt<T, N, P> as OverflowingSub>::overflowing_sub(*self, *other)
200        }
201    }
202
203    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> WrappingAdd for &FixedUInt<T, N, P> {
204        fn wrapping_add(self, other: Self) -> FixedUInt<T, N, P> {
205            <FixedUInt<T, N, P> as WrappingAdd>::wrapping_add(*self, *other)
206        }
207    }
208
209    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> WrappingSub for &FixedUInt<T, N, P> {
210        fn wrapping_sub(self, other: Self) -> FixedUInt<T, N, P> {
211            <FixedUInt<T, N, P> as WrappingSub>::wrapping_sub(*self, *other)
212        }
213    }
214
215    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CheckedAdd for &FixedUInt<T, N, P> {
216        fn checked_add(self, other: Self) -> Option<FixedUInt<T, N, P>> {
217            <FixedUInt<T, N, P> as CheckedAdd>::checked_add(*self, *other)
218        }
219    }
220
221    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> CheckedSub for &FixedUInt<T, N, P> {
222        fn checked_sub(self, other: Self) -> Option<FixedUInt<T, N, P>> {
223            <FixedUInt<T, N, P> as CheckedSub>::checked_sub(*self, *other)
224        }
225    }
226
227    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> SaturatingAdd for &FixedUInt<T, N, P> {
228        fn saturating_add(self, other: Self) -> FixedUInt<T, N, P> {
229            <FixedUInt<T, N, P> as SaturatingAdd>::saturating_add(*self, *other)
230        }
231    }
232
233    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> SaturatingSub for &FixedUInt<T, N, P> {
234        fn saturating_sub(self, other: Self) -> FixedUInt<T, N, P> {
235            <FixedUInt<T, N, P> as SaturatingSub>::saturating_sub(*self, *other)
236        }
237    }
238}
239
240#[cfg(feature = "num-traits")]
241impl<T: MachineWord, const N: usize, P: Personality> num_traits::ops::overflowing::OverflowingAdd
242    for FixedUInt<T, N, P>
243{
244    fn overflowing_add(&self, other: &Self) -> (Self, bool) {
245        <Self as OverflowingAdd>::overflowing_add(*self, *other)
246    }
247}
248
249#[cfg(feature = "num-traits")]
250impl<T: MachineWord, const N: usize, P: Personality> num_traits::WrappingAdd
251    for FixedUInt<T, N, P>
252{
253    fn wrapping_add(&self, other: &Self) -> Self {
254        <Self as WrappingAdd>::wrapping_add(*self, *other)
255    }
256}
257
258#[cfg(feature = "num-traits")]
259impl<T: MachineWord, const N: usize, P: Personality> num_traits::CheckedAdd for FixedUInt<T, N, P> {
260    fn checked_add(&self, other: &Self) -> Option<Self> {
261        <Self as CheckedAdd>::checked_add(*self, *other)
262    }
263}
264
265#[cfg(feature = "num-traits")]
266impl<T: MachineWord, const N: usize, P: Personality> num_traits::ops::saturating::SaturatingAdd
267    for FixedUInt<T, N, P>
268{
269    fn saturating_add(&self, other: &Self) -> Self {
270        <Self as SaturatingAdd>::saturating_add(*self, *other)
271    }
272}
273
274#[cfg(feature = "num-traits")]
275impl<T: MachineWord, const N: usize, P: Personality> num_traits::ops::overflowing::OverflowingSub
276    for FixedUInt<T, N, P>
277{
278    fn overflowing_sub(&self, other: &Self) -> (Self, bool) {
279        <Self as OverflowingSub>::overflowing_sub(*self, *other)
280    }
281}
282
283#[cfg(feature = "num-traits")]
284impl<T: MachineWord, const N: usize, P: Personality> num_traits::WrappingSub
285    for FixedUInt<T, N, P>
286{
287    fn wrapping_sub(&self, other: &Self) -> Self {
288        <Self as WrappingSub>::wrapping_sub(*self, *other)
289    }
290}
291
292#[cfg(feature = "num-traits")]
293impl<T: MachineWord, const N: usize, P: Personality> num_traits::CheckedSub for FixedUInt<T, N, P> {
294    fn checked_sub(&self, other: &Self) -> Option<Self> {
295        <Self as CheckedSub>::checked_sub(*self, *other)
296    }
297}
298
299#[cfg(feature = "num-traits")]
300impl<T: MachineWord, const N: usize, P: Personality> num_traits::ops::saturating::SaturatingSub
301    for FixedUInt<T, N, P>
302{
303    fn saturating_sub(&self, other: &Self) -> Self {
304        <Self as SaturatingSub>::saturating_sub(*self, *other)
305    }
306}
307
308/// Note: This is marked deprecated, but still used by PrimInt
309#[cfg(feature = "num-traits")]
310impl<T: MachineWord, const N: usize, P: Personality> num_traits::Saturating for FixedUInt<T, N, P> {
311    fn saturating_add(self, other: Self) -> Self {
312        <Self as SaturatingAdd>::saturating_add(self, other)
313    }
314
315    fn saturating_sub(self, other: Self) -> Self {
316        <Self as SaturatingSub>::saturating_sub(self, other)
317    }
318}
319
320// ── CtCheckedAdd / CtCheckedSub (masked-return checked arithmetic) ───
321//
322// Delegate to the existing `OverflowingAdd`/`OverflowingSub` impls (which
323// already cover both personalities branchlessly via `add_impl`/`sub_impl`),
324// then wrap the `(Self, bool)` into a `CtOption<Self>` by converting the
325// overflow flag to a `Choice`. Same shape upstream uses for the primitives.
326impl<T: MachineWord, const N: usize, P: Personality> CtCheckedAdd for FixedUInt<T, N, P> {
327    fn ct_checked_add(&self, v: &Self) -> subtle::CtOption<Self> {
328        let (val, overflow) = <Self as OverflowingAdd>::overflowing_add(*self, *v);
329        subtle::CtOption::new(val, subtle::Choice::from(!overflow as u8))
330    }
331}
332
333impl<T: MachineWord, const N: usize, P: Personality> CtCheckedSub for FixedUInt<T, N, P> {
334    fn ct_checked_sub(&self, v: &Self) -> subtle::CtOption<Self> {
335        let (val, overflow) = <Self as OverflowingSub>::overflowing_sub(*self, *v);
336        subtle::CtOption::new(val, subtle::Choice::from(!overflow as u8))
337    }
338}
339
340#[cfg(test)]
341// Coverage tests deliberately exercise every ref/value combination of
342// `Add`/`Sub` (see `test_add_combinations`, `test_sub_combinations`).
343#[allow(clippy::op_ref)]
344mod tests {
345    use super::*;
346    use crate::machineword::ConstMachineWord;
347    use const_num_traits::Bounded;
348    use const_num_traits::{
349        CheckedAdd, CheckedSub, OverflowingAdd, OverflowingSub, WrappingAdd, WrappingSub,
350    };
351
352    c0nst::c0nst! {
353        /// Test wrapper for OverflowingAdd
354        pub c0nst fn const_overflowing_add<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>, bool) {
358            <FixedUInt<T, N, P> as OverflowingAdd>::overflowing_add(*a, *b)
359        }
360
361        /// Test wrapper for OverflowingSub
362        pub c0nst fn const_overflowing_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
363            a: &FixedUInt<T, N, P>,
364            b: &FixedUInt<T, N, P>
365        ) -> (FixedUInt<T, N, P>, bool) {
366            <FixedUInt<T, N, P> as OverflowingSub>::overflowing_sub(*a, *b)
367        }
368
369        /// Test wrapper for const Add
370        pub c0nst fn const_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
371            a: FixedUInt<T, N, P>,
372            b: FixedUInt<T, N, P>
373        ) -> FixedUInt<T, N, P> {
374            a + b
375        }
376
377        /// Test wrapper for const Sub
378        pub c0nst fn const_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
379            a: FixedUInt<T, N, P>,
380            b: FixedUInt<T, N, P>
381        ) -> FixedUInt<T, N, P> {
382            a - b
383        }
384
385        /// Test wrapper for WrappingAdd
386        pub c0nst fn const_wrapping_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
387            a: &FixedUInt<T, N, P>,
388            b: &FixedUInt<T, N, P>
389        ) -> FixedUInt<T, N, P> {
390            <FixedUInt<T, N, P> as WrappingAdd>::wrapping_add(*a, *b)
391        }
392
393        /// Test wrapper for WrappingSub
394        pub c0nst fn const_wrapping_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
395            a: &FixedUInt<T, N, P>,
396            b: &FixedUInt<T, N, P>
397        ) -> FixedUInt<T, N, P> {
398            <FixedUInt<T, N, P> as WrappingSub>::wrapping_sub(*a, *b)
399        }
400
401        /// Test wrapper for CheckedAdd
402        pub c0nst fn const_checked_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
403            a: &FixedUInt<T, N, P>,
404            b: &FixedUInt<T, N, P>
405        ) -> Option<FixedUInt<T, N, P>> {
406            <FixedUInt<T, N, P> as CheckedAdd>::checked_add(*a, *b)
407        }
408
409        /// Test wrapper for CheckedSub
410        pub c0nst fn const_checked_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
411            a: &FixedUInt<T, N, P>,
412            b: &FixedUInt<T, N, P>
413        ) -> Option<FixedUInt<T, N, P>> {
414            <FixedUInt<T, N, P> as CheckedSub>::checked_sub(*a, *b)
415        }
416    }
417
418    #[test]
419    fn test_add_combinations() {
420        let a = FixedUInt::<u8, 2>::from(12u8);
421        let b = FixedUInt::<u8, 2>::from(3u8);
422        let expected = FixedUInt::<u8, 2>::from(15u8);
423
424        // value + value
425        assert_eq!(a + b, expected);
426        // value + ref
427        assert_eq!(a + &b, expected);
428        // ref + value
429        assert_eq!(&a + b, expected);
430        // ref + ref
431        assert_eq!(&a + &b, expected);
432    }
433
434    #[test]
435    fn test_sub_combinations() {
436        let a = FixedUInt::<u8, 2>::from(15u8);
437        let b = FixedUInt::<u8, 2>::from(3u8);
438        let expected = FixedUInt::<u8, 2>::from(12u8);
439
440        // value - value
441        assert_eq!(a - b, expected);
442        // value - ref
443        assert_eq!(a - &b, expected);
444        // ref - value
445        assert_eq!(&a - b, expected);
446        // ref - ref
447        assert_eq!(&a - &b, expected);
448    }
449
450    #[test]
451    fn test_const_overflowing_add() {
452        // No overflow
453        let a = FixedUInt::<u8, 2>::from(12u8);
454        let b = FixedUInt::<u8, 2>::from(3u8);
455        let (result, overflow) = const_overflowing_add(&a, &b);
456        assert_eq!(result, FixedUInt::<u8, 2>::from(15u8));
457        assert!(!overflow);
458
459        // With overflow: max + max wraps to max-1 with overflow
460        let a = <FixedUInt<u8, 2> as Bounded>::max_value();
461        let b = <FixedUInt<u8, 2> as Bounded>::max_value();
462        let (result, overflow) = const_overflowing_add(&a, &b);
463        // 0xFFFF + 0xFFFF = 0x1FFFE, which wraps to 0xFFFE with overflow
464        assert_eq!(result, FixedUInt::<u8, 2>::from(u16::MAX - 1));
465        assert!(overflow);
466
467        // Max value overflow
468        let max = <FixedUInt<u8, 2> as Bounded>::max_value();
469        let one = FixedUInt::<u8, 2>::from(1u8);
470        let (_, overflow) = const_overflowing_add(&max, &one);
471        assert!(overflow);
472
473        #[cfg(feature = "nightly")]
474        {
475            const A: FixedUInt<u8, 2> = FixedUInt::from_array([12, 0]);
476            const B: FixedUInt<u8, 2> = FixedUInt::from_array([3, 0]);
477            const RESULT: (FixedUInt<u8, 2>, bool) = const_overflowing_add(&A, &B);
478            assert_eq!(RESULT.0.array, [15, 0]);
479            assert!(!RESULT.1);
480        }
481    }
482
483    #[test]
484    fn test_const_overflowing_sub() {
485        // No overflow
486        let a = FixedUInt::<u8, 2>::from(15u8);
487        let b = FixedUInt::<u8, 2>::from(3u8);
488        let (result, overflow) = const_overflowing_sub(&a, &b);
489        assert_eq!(result, FixedUInt::<u8, 2>::from(12u8));
490        assert!(!overflow);
491
492        // With underflow
493        let a = FixedUInt::<u8, 2>::from(0u8);
494        let b = FixedUInt::<u8, 2>::from(1u8);
495        let (_, overflow) = const_overflowing_sub(&a, &b);
496        assert!(overflow);
497
498        #[cfg(feature = "nightly")]
499        {
500            const A: FixedUInt<u8, 2> = FixedUInt::from_array([15, 0]);
501            const B: FixedUInt<u8, 2> = FixedUInt::from_array([3, 0]);
502            const RESULT: (FixedUInt<u8, 2>, bool) = const_overflowing_sub(&A, &B);
503            assert_eq!(RESULT.0.array, [12, 0]);
504            assert!(!RESULT.1);
505        }
506    }
507
508    #[test]
509    fn test_const_add_op() {
510        let a = FixedUInt::<u8, 2>::from(12u8);
511        let b = FixedUInt::<u8, 2>::from(3u8);
512        let result = const_add(a, b);
513        assert_eq!(result, FixedUInt::<u8, 2>::from(15u8));
514
515        // Test with u32 word type
516        let a = FixedUInt::<u32, 2>::from(100u32);
517        let b = FixedUInt::<u32, 2>::from(200u32);
518        let result = const_add(a, b);
519        assert_eq!(result, FixedUInt::<u32, 2>::from(300u32));
520
521        #[cfg(feature = "nightly")]
522        {
523            const A: FixedUInt<u8, 2> = FixedUInt::from_array([12, 0]);
524            const B: FixedUInt<u8, 2> = FixedUInt::from_array([3, 0]);
525            const RESULT: FixedUInt<u8, 2> = const_add(A, B);
526            assert_eq!(RESULT.array, [15, 0]);
527        }
528    }
529
530    #[test]
531    fn test_const_sub_op() {
532        let a = FixedUInt::<u8, 2>::from(15u8);
533        let b = FixedUInt::<u8, 2>::from(3u8);
534        let result = const_sub(a, b);
535        assert_eq!(result, FixedUInt::<u8, 2>::from(12u8));
536
537        // Test with u32 word type
538        let a = FixedUInt::<u32, 2>::from(300u32);
539        let b = FixedUInt::<u32, 2>::from(100u32);
540        let result = const_sub(a, b);
541        assert_eq!(result, FixedUInt::<u32, 2>::from(200u32));
542
543        #[cfg(feature = "nightly")]
544        {
545            const A: FixedUInt<u8, 2> = FixedUInt::from_array([15, 0]);
546            const B: FixedUInt<u8, 2> = FixedUInt::from_array([3, 0]);
547            const RESULT: FixedUInt<u8, 2> = const_sub(A, B);
548            assert_eq!(RESULT.array, [12, 0]);
549        }
550    }
551
552    #[test]
553    fn test_const_wrapping_checked() {
554        // Test wrapping_add without overflow
555        let a = FixedUInt::<u8, 2>::from(100u8);
556        let b = FixedUInt::<u8, 2>::from(50u8);
557        let result = const_wrapping_add(&a, &b);
558        assert_eq!(result, FixedUInt::<u8, 2>::from(150u8));
559
560        // Test wrapping_add with overflow
561        let max = <FixedUInt<u8, 2> as Bounded>::max_value();
562        let one = FixedUInt::<u8, 2>::from(1u8);
563        let result = const_wrapping_add(&max, &one);
564        assert_eq!(result, FixedUInt::<u8, 2>::from(0u8));
565
566        // Test wrapping_sub without overflow
567        let a = FixedUInt::<u8, 2>::from(100u8);
568        let b = FixedUInt::<u8, 2>::from(50u8);
569        let result = const_wrapping_sub(&a, &b);
570        assert_eq!(result, FixedUInt::<u8, 2>::from(50u8));
571
572        // Test wrapping_sub with underflow
573        let zero = FixedUInt::<u8, 2>::from(0u8);
574        let one = FixedUInt::<u8, 2>::from(1u8);
575        let result = const_wrapping_sub(&zero, &one);
576        assert_eq!(result, <FixedUInt::<u8, 2> as Bounded>::max_value());
577
578        // Test checked_add without overflow
579        let a = FixedUInt::<u8, 2>::from(100u8);
580        let b = FixedUInt::<u8, 2>::from(50u8);
581        let result = const_checked_add(&a, &b);
582        assert_eq!(result, Some(FixedUInt::<u8, 2>::from(150u8)));
583
584        // Test checked_add with overflow
585        let max = <FixedUInt<u8, 2> as Bounded>::max_value();
586        let one = FixedUInt::<u8, 2>::from(1u8);
587        let result = const_checked_add(&max, &one);
588        assert_eq!(result, None);
589
590        // Test checked_sub without overflow
591        let a = FixedUInt::<u8, 2>::from(100u8);
592        let b = FixedUInt::<u8, 2>::from(50u8);
593        let result = const_checked_sub(&a, &b);
594        assert_eq!(result, Some(FixedUInt::<u8, 2>::from(50u8)));
595
596        // Test checked_sub with underflow
597        let zero = FixedUInt::<u8, 2>::from(0u8);
598        let one = FixedUInt::<u8, 2>::from(1u8);
599        let result = const_checked_sub(&zero, &one);
600        assert_eq!(result, None);
601
602        #[cfg(feature = "nightly")]
603        {
604            const A: FixedUInt<u8, 2> = FixedUInt::from_array([100, 0]);
605            const B: FixedUInt<u8, 2> = FixedUInt::from_array([50, 0]);
606
607            const WRAP_ADD: FixedUInt<u8, 2> = const_wrapping_add(&A, &B);
608            const WRAP_SUB: FixedUInt<u8, 2> = const_wrapping_sub(&A, &B);
609            const CHECK_ADD: Option<FixedUInt<u8, 2>> = const_checked_add(&A, &B);
610            const CHECK_SUB: Option<FixedUInt<u8, 2>> = const_checked_sub(&A, &B);
611
612            assert_eq!(WRAP_ADD.array, [150, 0]);
613            assert_eq!(WRAP_SUB.array, [50, 0]);
614            assert!(CHECK_ADD.is_some());
615            assert!(CHECK_SUB.is_some());
616
617            const MAX: FixedUInt<u8, 2> = FixedUInt::from_array([255, 255]);
618            const ONE: FixedUInt<u8, 2> = FixedUInt::from_array([1, 0]);
619            const CHECK_ADD_OVERFLOW: Option<FixedUInt<u8, 2>> = const_checked_add(&MAX, &ONE);
620            assert!(CHECK_ADD_OVERFLOW.is_none());
621        }
622    }
623
624    #[test]
625    fn ct_checked_add_masks_overflow() {
626        use CtCheckedAdd;
627        type U16 = FixedUInt<u8, 2>;
628        let a = U16::from(100u8);
629        let b = U16::from(50u8);
630        let ok = a.ct_checked_add(&b);
631        assert!(bool::from(ok.is_some()));
632        assert_eq!(ok.unwrap(), U16::from(150u8));
633        // Overflow case: MAX + 1
634        let max = <U16 as const_num_traits::Bounded>::max_value();
635        let one = U16::from(1u8);
636        let bad = max.ct_checked_add(&one);
637        assert!(!bool::from(bad.is_some()));
638    }
639
640    #[test]
641    fn ct_checked_sub_masks_underflow() {
642        use CtCheckedSub;
643        type U16 = FixedUInt<u8, 2>;
644        let ok = U16::from(100u8).ct_checked_sub(&U16::from(50u8));
645        assert!(bool::from(ok.is_some()));
646        assert_eq!(ok.unwrap(), U16::from(50u8));
647        // Underflow: 0 - 1
648        let bad = U16::from(0u8).ct_checked_sub(&U16::from(1u8));
649        assert!(!bool::from(bad.is_some()));
650    }
651}