Skip to main content

fixed_bigint/fixeduint/
add_sub_impl.rs

1use super::{add_impl, maybe_panic, sub_impl, FixedUInt, MachineWord, PanicReason};
2use crate::const_numtraits::{
3    ConstBounded, ConstCheckedAdd, ConstCheckedSub, ConstOverflowingAdd, ConstOverflowingSub,
4    ConstSaturatingAdd, ConstSaturatingSub, ConstWrappingAdd, ConstWrappingSub, ConstZero,
5};
6use crate::machineword::ConstMachineWord;
7
8c0nst::c0nst! {
9    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst crate::const_numtraits::ConstOverflowingAdd for FixedUInt<T, N> {
10        fn overflowing_add(&self, other: &Self) -> (Self, bool) {
11            let mut ret = *self;
12            let overflow = add_impl(&mut ret.array, &other.array);
13            (ret, overflow)
14        }
15    }
16
17    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst crate::const_numtraits::ConstOverflowingSub for FixedUInt<T, N> {
18        fn overflowing_sub(&self, other: &Self) -> (Self, bool) {
19            let mut ret = *self;
20            let overflow = sub_impl(&mut ret.array, &other.array);
21            (ret, overflow)
22        }
23    }
24
25    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstWrappingAdd for FixedUInt<T, N> {
26        fn wrapping_add(&self, other: &Self) -> Self {
27            <Self as ConstOverflowingAdd>::overflowing_add(self, other).0
28        }
29    }
30
31    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstWrappingSub for FixedUInt<T, N> {
32        fn wrapping_sub(&self, other: &Self) -> Self {
33            <Self as ConstOverflowingSub>::overflowing_sub(self, other).0
34        }
35    }
36
37    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstCheckedAdd for FixedUInt<T, N> {
38        fn checked_add(&self, other: &Self) -> Option<Self> {
39            let (res, overflow) = <Self as ConstOverflowingAdd>::overflowing_add(self, other);
40            if overflow { None } else { Some(res) }
41        }
42    }
43
44    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstCheckedSub for FixedUInt<T, N> {
45        fn checked_sub(&self, other: &Self) -> Option<Self> {
46            let (res, overflow) = <Self as ConstOverflowingSub>::overflowing_sub(self, other);
47            if overflow { None } else { Some(res) }
48        }
49    }
50
51    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstSaturatingAdd for FixedUInt<T, N> {
52        fn saturating_add(&self, other: &Self) -> Self {
53            let (res, overflow) = <Self as ConstOverflowingAdd>::overflowing_add(self, other);
54            if overflow { <Self as ConstBounded>::max_value() } else { res }
55        }
56    }
57
58    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstSaturatingSub for FixedUInt<T, N> {
59        fn saturating_sub(&self, other: &Self) -> Self {
60            let (res, overflow) = <Self as ConstOverflowingSub>::overflowing_sub(self, other);
61            if overflow { <Self as ConstZero>::zero() } else { res }
62        }
63    }
64
65    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Add for FixedUInt<T, N> {
66        type Output = Self;
67        fn add(self, other: Self) -> Self {
68            let (res, overflow) = <Self as crate::const_numtraits::ConstOverflowingAdd>::overflowing_add(&self, &other);
69            if overflow {
70                maybe_panic(PanicReason::Add);
71            }
72            res
73        }
74    }
75
76    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Sub for FixedUInt<T, N> {
77        type Output = Self;
78        fn sub(self, other: Self) -> Self {
79            let (res, overflow) = <Self as crate::const_numtraits::ConstOverflowingSub>::overflowing_sub(&self, &other);
80            if overflow {
81                maybe_panic(PanicReason::Sub);
82            }
83            res
84        }
85    }
86
87    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Add<&'_ Self> for FixedUInt<T, N> {
88        type Output = Self;
89        fn add(self, other: &Self) -> Self {
90            let (res, overflow) = <Self as crate::const_numtraits::ConstOverflowingAdd>::overflowing_add(&self, other);
91            if overflow {
92                maybe_panic(PanicReason::Add);
93            }
94            res
95        }
96    }
97
98    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Add<FixedUInt<T, N>> for &FixedUInt<T, N> {
99        type Output = FixedUInt<T, N>;
100        fn add(self, other: FixedUInt<T, N>) -> Self::Output {
101            let (res, overflow) = <FixedUInt<T, N> as crate::const_numtraits::ConstOverflowingAdd>::overflowing_add(self, &other);
102            if overflow {
103                maybe_panic(PanicReason::Add);
104            }
105            res
106        }
107    }
108
109    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Add<Self> for &FixedUInt<T, N> {
110        type Output = FixedUInt<T, N>;
111        fn add(self, other: Self) -> Self::Output {
112            let (res, overflow) = <FixedUInt<T, N> as crate::const_numtraits::ConstOverflowingAdd>::overflowing_add(self, other);
113            if overflow {
114                maybe_panic(PanicReason::Add);
115            }
116            res
117        }
118    }
119
120    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Sub<&'_ Self> for FixedUInt<T, N> {
121        type Output = Self;
122        fn sub(self, other: &Self) -> Self {
123            let (res, overflow) = <Self as crate::const_numtraits::ConstOverflowingSub>::overflowing_sub(&self, other);
124            if overflow {
125                maybe_panic(PanicReason::Sub);
126            }
127            res
128        }
129    }
130
131    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Sub<FixedUInt<T, N>> for &FixedUInt<T, N> {
132        type Output = FixedUInt<T, N>;
133        fn sub(self, other: FixedUInt<T, N>) -> Self::Output {
134            let (res, overflow) = <FixedUInt<T, N> as crate::const_numtraits::ConstOverflowingSub>::overflowing_sub(self, &other);
135            if overflow {
136                maybe_panic(PanicReason::Sub);
137            }
138            res
139        }
140    }
141
142    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Sub<Self> for &FixedUInt<T, N> {
143        type Output = FixedUInt<T, N>;
144        fn sub(self, other: Self) -> Self::Output {
145            let (res, overflow) = <FixedUInt<T, N> as crate::const_numtraits::ConstOverflowingSub>::overflowing_sub(self, other);
146            if overflow {
147                maybe_panic(PanicReason::Sub);
148            }
149            res
150        }
151    }
152
153    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::AddAssign<Self> for FixedUInt<T, N> {
154        fn add_assign(&mut self, other: Self) {
155            if add_impl(&mut self.array, &other.array) {
156                maybe_panic(PanicReason::Add);
157            }
158        }
159    }
160
161    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::AddAssign<&'_ Self> for FixedUInt<T, N> {
162        fn add_assign(&mut self, other: &Self) {
163            if add_impl(&mut self.array, &other.array) {
164                maybe_panic(PanicReason::Add);
165            }
166        }
167    }
168
169    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::SubAssign<Self> for FixedUInt<T, N> {
170        fn sub_assign(&mut self, other: Self) {
171            if sub_impl(&mut self.array, &other.array) {
172                maybe_panic(PanicReason::Sub);
173            }
174        }
175    }
176
177    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::SubAssign<&'_ Self> for FixedUInt<T, N> {
178        fn sub_assign(&mut self, other: &Self) {
179            if sub_impl(&mut self.array, &other.array) {
180                maybe_panic(PanicReason::Sub);
181            }
182        }
183    }
184}
185
186impl<T: MachineWord, const N: usize> num_traits::ops::overflowing::OverflowingAdd
187    for FixedUInt<T, N>
188{
189    fn overflowing_add(&self, other: &Self) -> (Self, bool) {
190        <Self as crate::const_numtraits::ConstOverflowingAdd>::overflowing_add(self, other)
191    }
192}
193
194impl<T: MachineWord, const N: usize> num_traits::WrappingAdd for FixedUInt<T, N> {
195    fn wrapping_add(&self, other: &Self) -> Self {
196        <Self as ConstWrappingAdd>::wrapping_add(self, other)
197    }
198}
199
200impl<T: MachineWord, const N: usize> num_traits::CheckedAdd for FixedUInt<T, N> {
201    fn checked_add(&self, other: &Self) -> Option<Self> {
202        <Self as ConstCheckedAdd>::checked_add(self, other)
203    }
204}
205
206impl<T: MachineWord, const N: usize> num_traits::ops::saturating::SaturatingAdd
207    for FixedUInt<T, N>
208{
209    fn saturating_add(&self, other: &Self) -> Self {
210        <Self as ConstSaturatingAdd>::saturating_add(self, other)
211    }
212}
213
214impl<T: MachineWord, const N: usize> num_traits::ops::overflowing::OverflowingSub
215    for FixedUInt<T, N>
216{
217    fn overflowing_sub(&self, other: &Self) -> (Self, bool) {
218        <Self as crate::const_numtraits::ConstOverflowingSub>::overflowing_sub(self, other)
219    }
220}
221
222impl<T: MachineWord, const N: usize> num_traits::WrappingSub for FixedUInt<T, N> {
223    fn wrapping_sub(&self, other: &Self) -> Self {
224        <Self as ConstWrappingSub>::wrapping_sub(self, other)
225    }
226}
227
228impl<T: MachineWord, const N: usize> num_traits::CheckedSub for FixedUInt<T, N> {
229    fn checked_sub(&self, other: &Self) -> Option<Self> {
230        <Self as ConstCheckedSub>::checked_sub(self, other)
231    }
232}
233
234impl<T: MachineWord, const N: usize> num_traits::ops::saturating::SaturatingSub
235    for FixedUInt<T, N>
236{
237    fn saturating_sub(&self, other: &Self) -> Self {
238        <Self as ConstSaturatingSub>::saturating_sub(self, other)
239    }
240}
241
242/// Note: This is marked deprecated, but still used by PrimInt
243impl<T: MachineWord, const N: usize> num_traits::Saturating for FixedUInt<T, N> {
244    fn saturating_add(self, other: Self) -> Self {
245        <Self as ConstSaturatingAdd>::saturating_add(&self, &other)
246    }
247
248    fn saturating_sub(self, other: Self) -> Self {
249        <Self as ConstSaturatingSub>::saturating_sub(&self, &other)
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256    use crate::const_numtraits::{
257        ConstCheckedAdd, ConstCheckedSub, ConstOverflowingAdd, ConstOverflowingSub,
258        ConstWrappingAdd, ConstWrappingSub,
259    };
260    use crate::machineword::ConstMachineWord;
261    use num_traits::Bounded;
262
263    c0nst::c0nst! {
264        /// Test wrapper for ConstOverflowingAdd
265        pub c0nst fn const_overflowing_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
266            a: &FixedUInt<T, N>,
267            b: &FixedUInt<T, N>
268        ) -> (FixedUInt<T, N>, bool) {
269            <FixedUInt<T, N> as ConstOverflowingAdd>::overflowing_add(a, b)
270        }
271
272        /// Test wrapper for ConstOverflowingSub
273        pub c0nst fn const_overflowing_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
274            a: &FixedUInt<T, N>,
275            b: &FixedUInt<T, N>
276        ) -> (FixedUInt<T, N>, bool) {
277            <FixedUInt<T, N> as ConstOverflowingSub>::overflowing_sub(a, b)
278        }
279
280        /// Test wrapper for const Add
281        pub c0nst fn const_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
282            a: FixedUInt<T, N>,
283            b: FixedUInt<T, N>
284        ) -> FixedUInt<T, N> {
285            a + b
286        }
287
288        /// Test wrapper for const Sub
289        pub c0nst fn const_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
290            a: FixedUInt<T, N>,
291            b: FixedUInt<T, N>
292        ) -> FixedUInt<T, N> {
293            a - b
294        }
295
296        /// Test wrapper for ConstWrappingAdd
297        pub c0nst fn const_wrapping_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
298            a: &FixedUInt<T, N>,
299            b: &FixedUInt<T, N>
300        ) -> FixedUInt<T, N> {
301            <FixedUInt<T, N> as ConstWrappingAdd>::wrapping_add(a, b)
302        }
303
304        /// Test wrapper for ConstWrappingSub
305        pub c0nst fn const_wrapping_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
306            a: &FixedUInt<T, N>,
307            b: &FixedUInt<T, N>
308        ) -> FixedUInt<T, N> {
309            <FixedUInt<T, N> as ConstWrappingSub>::wrapping_sub(a, b)
310        }
311
312        /// Test wrapper for ConstCheckedAdd
313        pub c0nst fn const_checked_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
314            a: &FixedUInt<T, N>,
315            b: &FixedUInt<T, N>
316        ) -> Option<FixedUInt<T, N>> {
317            <FixedUInt<T, N> as ConstCheckedAdd>::checked_add(a, b)
318        }
319
320        /// Test wrapper for ConstCheckedSub
321        pub c0nst fn const_checked_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
322            a: &FixedUInt<T, N>,
323            b: &FixedUInt<T, N>
324        ) -> Option<FixedUInt<T, N>> {
325            <FixedUInt<T, N> as ConstCheckedSub>::checked_sub(a, b)
326        }
327    }
328
329    #[test]
330    fn test_add_combinations() {
331        let a = FixedUInt::<u8, 2>::from(12u8);
332        let b = FixedUInt::<u8, 2>::from(3u8);
333        let expected = FixedUInt::<u8, 2>::from(15u8);
334
335        // value + value
336        assert_eq!(a + b, expected);
337        // value + ref
338        assert_eq!(a + &b, expected);
339        // ref + value
340        assert_eq!(&a + b, expected);
341        // ref + ref
342        assert_eq!(&a + &b, expected);
343    }
344
345    #[test]
346    fn test_sub_combinations() {
347        let a = FixedUInt::<u8, 2>::from(15u8);
348        let b = FixedUInt::<u8, 2>::from(3u8);
349        let expected = FixedUInt::<u8, 2>::from(12u8);
350
351        // value - value
352        assert_eq!(a - b, expected);
353        // value - ref
354        assert_eq!(a - &b, expected);
355        // ref - value
356        assert_eq!(&a - b, expected);
357        // ref - ref
358        assert_eq!(&a - &b, expected);
359    }
360
361    #[test]
362    fn test_const_overflowing_add() {
363        // No overflow
364        let a = FixedUInt::<u8, 2>::from(12u8);
365        let b = FixedUInt::<u8, 2>::from(3u8);
366        let (result, overflow) = const_overflowing_add(&a, &b);
367        assert_eq!(result, FixedUInt::<u8, 2>::from(15u8));
368        assert!(!overflow);
369
370        // With overflow: max + max wraps to max-1 with overflow
371        let a = <FixedUInt<u8, 2> as Bounded>::max_value();
372        let b = <FixedUInt<u8, 2> as Bounded>::max_value();
373        let (result, overflow) = const_overflowing_add(&a, &b);
374        // 0xFFFF + 0xFFFF = 0x1FFFE, which wraps to 0xFFFE with overflow
375        assert_eq!(result, FixedUInt::<u8, 2>::from(u16::MAX - 1));
376        assert!(overflow);
377
378        // Max value overflow
379        let max = <FixedUInt<u8, 2> as Bounded>::max_value();
380        let one = FixedUInt::<u8, 2>::from(1u8);
381        let (_, overflow) = const_overflowing_add(&max, &one);
382        assert!(overflow);
383
384        #[cfg(feature = "nightly")]
385        {
386            const A: FixedUInt<u8, 2> = FixedUInt { array: [12, 0] };
387            const B: FixedUInt<u8, 2> = FixedUInt { array: [3, 0] };
388            const RESULT: (FixedUInt<u8, 2>, bool) = const_overflowing_add(&A, &B);
389            assert_eq!(RESULT.0.array, [15, 0]);
390            assert!(!RESULT.1);
391        }
392    }
393
394    #[test]
395    fn test_const_overflowing_sub() {
396        // No overflow
397        let a = FixedUInt::<u8, 2>::from(15u8);
398        let b = FixedUInt::<u8, 2>::from(3u8);
399        let (result, overflow) = const_overflowing_sub(&a, &b);
400        assert_eq!(result, FixedUInt::<u8, 2>::from(12u8));
401        assert!(!overflow);
402
403        // With underflow
404        let a = FixedUInt::<u8, 2>::from(0u8);
405        let b = FixedUInt::<u8, 2>::from(1u8);
406        let (_, overflow) = const_overflowing_sub(&a, &b);
407        assert!(overflow);
408
409        #[cfg(feature = "nightly")]
410        {
411            const A: FixedUInt<u8, 2> = FixedUInt { array: [15, 0] };
412            const B: FixedUInt<u8, 2> = FixedUInt { array: [3, 0] };
413            const RESULT: (FixedUInt<u8, 2>, bool) = const_overflowing_sub(&A, &B);
414            assert_eq!(RESULT.0.array, [12, 0]);
415            assert!(!RESULT.1);
416        }
417    }
418
419    #[test]
420    fn test_const_add_op() {
421        let a = FixedUInt::<u8, 2>::from(12u8);
422        let b = FixedUInt::<u8, 2>::from(3u8);
423        let result = const_add(a, b);
424        assert_eq!(result, FixedUInt::<u8, 2>::from(15u8));
425
426        // Test with u32 word type
427        let a = FixedUInt::<u32, 2>::from(100u32);
428        let b = FixedUInt::<u32, 2>::from(200u32);
429        let result = const_add(a, b);
430        assert_eq!(result, FixedUInt::<u32, 2>::from(300u32));
431
432        #[cfg(feature = "nightly")]
433        {
434            const A: FixedUInt<u8, 2> = FixedUInt { array: [12, 0] };
435            const B: FixedUInt<u8, 2> = FixedUInt { array: [3, 0] };
436            const RESULT: FixedUInt<u8, 2> = const_add(A, B);
437            assert_eq!(RESULT.array, [15, 0]);
438        }
439    }
440
441    #[test]
442    fn test_const_sub_op() {
443        let a = FixedUInt::<u8, 2>::from(15u8);
444        let b = FixedUInt::<u8, 2>::from(3u8);
445        let result = const_sub(a, b);
446        assert_eq!(result, FixedUInt::<u8, 2>::from(12u8));
447
448        // Test with u32 word type
449        let a = FixedUInt::<u32, 2>::from(300u32);
450        let b = FixedUInt::<u32, 2>::from(100u32);
451        let result = const_sub(a, b);
452        assert_eq!(result, FixedUInt::<u32, 2>::from(200u32));
453
454        #[cfg(feature = "nightly")]
455        {
456            const A: FixedUInt<u8, 2> = FixedUInt { array: [15, 0] };
457            const B: FixedUInt<u8, 2> = FixedUInt { array: [3, 0] };
458            const RESULT: FixedUInt<u8, 2> = const_sub(A, B);
459            assert_eq!(RESULT.array, [12, 0]);
460        }
461    }
462
463    #[test]
464    fn test_const_wrapping_checked() {
465        // Test wrapping_add without overflow
466        let a = FixedUInt::<u8, 2>::from(100u8);
467        let b = FixedUInt::<u8, 2>::from(50u8);
468        let result = const_wrapping_add(&a, &b);
469        assert_eq!(result, FixedUInt::<u8, 2>::from(150u8));
470
471        // Test wrapping_add with overflow
472        let max = <FixedUInt<u8, 2> as Bounded>::max_value();
473        let one = FixedUInt::<u8, 2>::from(1u8);
474        let result = const_wrapping_add(&max, &one);
475        assert_eq!(result, FixedUInt::<u8, 2>::from(0u8));
476
477        // Test wrapping_sub without overflow
478        let a = FixedUInt::<u8, 2>::from(100u8);
479        let b = FixedUInt::<u8, 2>::from(50u8);
480        let result = const_wrapping_sub(&a, &b);
481        assert_eq!(result, FixedUInt::<u8, 2>::from(50u8));
482
483        // Test wrapping_sub with underflow
484        let zero = FixedUInt::<u8, 2>::from(0u8);
485        let one = FixedUInt::<u8, 2>::from(1u8);
486        let result = const_wrapping_sub(&zero, &one);
487        assert_eq!(result, <FixedUInt::<u8, 2> as Bounded>::max_value());
488
489        // Test checked_add without overflow
490        let a = FixedUInt::<u8, 2>::from(100u8);
491        let b = FixedUInt::<u8, 2>::from(50u8);
492        let result = const_checked_add(&a, &b);
493        assert_eq!(result, Some(FixedUInt::<u8, 2>::from(150u8)));
494
495        // Test checked_add with overflow
496        let max = <FixedUInt<u8, 2> as Bounded>::max_value();
497        let one = FixedUInt::<u8, 2>::from(1u8);
498        let result = const_checked_add(&max, &one);
499        assert_eq!(result, None);
500
501        // Test checked_sub without overflow
502        let a = FixedUInt::<u8, 2>::from(100u8);
503        let b = FixedUInt::<u8, 2>::from(50u8);
504        let result = const_checked_sub(&a, &b);
505        assert_eq!(result, Some(FixedUInt::<u8, 2>::from(50u8)));
506
507        // Test checked_sub with underflow
508        let zero = FixedUInt::<u8, 2>::from(0u8);
509        let one = FixedUInt::<u8, 2>::from(1u8);
510        let result = const_checked_sub(&zero, &one);
511        assert_eq!(result, None);
512
513        #[cfg(feature = "nightly")]
514        {
515            const A: FixedUInt<u8, 2> = FixedUInt { array: [100, 0] };
516            const B: FixedUInt<u8, 2> = FixedUInt { array: [50, 0] };
517
518            const WRAP_ADD: FixedUInt<u8, 2> = const_wrapping_add(&A, &B);
519            const WRAP_SUB: FixedUInt<u8, 2> = const_wrapping_sub(&A, &B);
520            const CHECK_ADD: Option<FixedUInt<u8, 2>> = const_checked_add(&A, &B);
521            const CHECK_SUB: Option<FixedUInt<u8, 2>> = const_checked_sub(&A, &B);
522
523            assert_eq!(WRAP_ADD.array, [150, 0]);
524            assert_eq!(WRAP_SUB.array, [50, 0]);
525            assert!(CHECK_ADD.is_some());
526            assert!(CHECK_SUB.is_some());
527
528            const MAX: FixedUInt<u8, 2> = FixedUInt { array: [255, 255] };
529            const ONE: FixedUInt<u8, 2> = FixedUInt { array: [1, 0] };
530            const CHECK_ADD_OVERFLOW: Option<FixedUInt<u8, 2>> = const_checked_add(&MAX, &ONE);
531            assert!(CHECK_ADD_OVERFLOW.is_none());
532        }
533    }
534}