Skip to main content

fixed_bigint/fixeduint/
add_sub_impl.rs

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