crypto_bigint/int/
sub.rs

1//! [`Int`] subtraction operations.
2
3use core::ops::{Sub, SubAssign};
4
5use num_traits::WrappingSub;
6use subtle::{Choice, ConstantTimeEq, CtOption};
7
8use crate::{Checked, CheckedSub, Int, Wrapping};
9
10impl<const LIMBS: usize> CheckedSub for Int<LIMBS> {
11    fn checked_sub(&self, rhs: &Self) -> CtOption<Self> {
12        // Step 1. subtract operands
13        let res = Self(self.0.wrapping_sub(&rhs.0));
14
15        // Step 2. check whether underflow happened.
16        // Note:
17        // - underflow can only happen when the inputs have opposing signs, and then
18        // - underflow occurs if and only if the result and the lhs have opposing signs.
19        //
20        // We can thus express the overflow flag as: (self.msb != rhs.msb) & (self.msb != res.msb)
21        let self_msb: Choice = self.is_negative().into();
22        let underflow =
23            self_msb.ct_ne(&rhs.is_negative().into()) & self_msb.ct_ne(&res.is_negative().into());
24
25        // Step 3. Construct result
26        CtOption::new(res, !underflow)
27    }
28}
29
30impl<const LIMBS: usize> Sub for Int<LIMBS> {
31    type Output = Self;
32
33    fn sub(self, rhs: Self) -> Self {
34        self.sub(&rhs)
35    }
36}
37
38impl<const LIMBS: usize> Sub<&Int<LIMBS>> for Int<LIMBS> {
39    type Output = Self;
40
41    fn sub(self, rhs: &Self) -> Self {
42        self.checked_sub(rhs)
43            .expect("attempted to subtract with underflow")
44    }
45}
46
47impl<const LIMBS: usize> SubAssign for Wrapping<Int<LIMBS>> {
48    fn sub_assign(&mut self, other: Self) {
49        *self = *self - other;
50    }
51}
52
53impl<const LIMBS: usize> SubAssign<&Wrapping<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
54    fn sub_assign(&mut self, other: &Self) {
55        *self = *self - other;
56    }
57}
58
59impl<const LIMBS: usize> SubAssign for Checked<Int<LIMBS>> {
60    fn sub_assign(&mut self, other: Self) {
61        *self = *self - other;
62    }
63}
64
65impl<const LIMBS: usize> SubAssign<&Checked<Int<LIMBS>>> for Checked<Int<LIMBS>> {
66    fn sub_assign(&mut self, other: &Self) {
67        *self = *self - other;
68    }
69}
70
71impl<const LIMBS: usize> WrappingSub for Int<LIMBS> {
72    fn wrapping_sub(&self, v: &Self) -> Self {
73        Self(self.0.wrapping_sub(&v.0))
74    }
75}
76
77#[cfg(test)]
78mod tests {
79
80    #[cfg(test)]
81    mod tests {
82        use num_traits::WrappingSub;
83
84        use crate::{CheckedSub, Int, I128, U128};
85
86        #[test]
87        fn checked_sub() {
88            let min_plus_one = Int {
89                0: I128::MIN.0.wrapping_add(&I128::ONE.0),
90            };
91            let max_minus_one = Int {
92                0: I128::MAX.0.wrapping_sub(&I128::ONE.0),
93            };
94            let two = Int {
95                0: U128::from(2u32),
96            };
97            let min_plus_two = Int {
98                0: I128::MIN.0.wrapping_add(&two.0),
99            };
100
101            // lhs = MIN
102
103            let result = I128::MIN.checked_sub(&I128::MIN);
104            assert_eq!(result.unwrap(), I128::ZERO);
105
106            let result = I128::MIN.checked_sub(&I128::MINUS_ONE);
107            assert_eq!(result.unwrap(), min_plus_one);
108
109            let result = I128::MIN.checked_sub(&I128::ZERO);
110            assert_eq!(result.unwrap(), I128::MIN);
111
112            let result = I128::MIN.checked_sub(&I128::ONE);
113            assert!(bool::from(result.is_none()));
114
115            let result = I128::MIN.checked_sub(&I128::MAX);
116            assert!(bool::from(result.is_none()));
117
118            // lhs = -1
119
120            let result = I128::MINUS_ONE.checked_sub(&I128::MIN);
121            assert_eq!(result.unwrap(), I128::MAX);
122
123            let result = I128::MINUS_ONE.checked_sub(&I128::MINUS_ONE);
124            assert_eq!(result.unwrap(), I128::ZERO);
125
126            let result = I128::MINUS_ONE.checked_sub(&I128::ZERO);
127            assert_eq!(result.unwrap(), I128::MINUS_ONE);
128
129            let result = I128::MINUS_ONE.checked_sub(&I128::ONE);
130            assert_eq!(result.unwrap(), two.wrapping_neg());
131
132            let result = I128::MINUS_ONE.checked_sub(&I128::MAX);
133            assert_eq!(result.unwrap(), I128::MIN);
134
135            // lhs = 0
136
137            let result = I128::ZERO.checked_sub(&I128::MIN);
138            assert!(bool::from(result.is_none()));
139
140            let result = I128::ZERO.checked_sub(&I128::MINUS_ONE);
141            assert_eq!(result.unwrap(), I128::ONE);
142
143            let result = I128::ZERO.checked_sub(&I128::ZERO);
144            assert_eq!(result.unwrap(), I128::ZERO);
145
146            let result = I128::ZERO.checked_sub(&I128::ONE);
147            assert_eq!(result.unwrap(), I128::MINUS_ONE);
148
149            let result = I128::ZERO.checked_sub(&I128::MAX);
150            assert_eq!(result.unwrap(), min_plus_one);
151
152            // lhs = 1
153
154            let result = I128::ONE.checked_sub(&I128::MIN);
155            assert!(bool::from(result.is_none()));
156
157            let result = I128::ONE.checked_sub(&I128::MINUS_ONE);
158            assert_eq!(result.unwrap(), two);
159
160            let result = I128::ONE.checked_sub(&I128::ZERO);
161            assert_eq!(result.unwrap(), I128::ONE);
162
163            let result = I128::ONE.checked_sub(&I128::ONE);
164            assert_eq!(result.unwrap(), I128::ZERO);
165
166            let result = I128::ONE.checked_sub(&I128::MAX);
167            assert_eq!(result.unwrap(), min_plus_two);
168
169            // lhs = MAX
170
171            let result = I128::MAX.checked_sub(&I128::MIN);
172            assert!(bool::from(result.is_none()));
173
174            let result = I128::MAX.checked_sub(&I128::MINUS_ONE);
175            assert!(bool::from(result.is_none()));
176
177            let result = I128::MAX.checked_sub(&I128::ZERO);
178            assert_eq!(result.unwrap(), I128::MAX);
179
180            let result = I128::MAX.checked_sub(&I128::ONE);
181            assert_eq!(result.unwrap(), max_minus_one);
182
183            let result = I128::MAX.checked_sub(&I128::MAX);
184            assert_eq!(result.unwrap(), I128::ZERO);
185        }
186
187        #[test]
188        fn wrapping_sub() {
189            let min_plus_one = Int {
190                0: I128::MIN.0.wrapping_add(&I128::ONE.0),
191            };
192            let two = Int {
193                0: U128::from(2u32),
194            };
195            let max_minus_one = Int {
196                0: I128::MAX.0.wrapping_sub(&I128::ONE.0),
197            };
198
199            // + sub -
200            let result = I128::ONE.wrapping_sub(&I128::MIN);
201            assert_eq!(result, min_plus_one);
202
203            // 0 sub -
204            let result = I128::ZERO.wrapping_sub(&I128::MIN);
205            assert_eq!(result, I128::MIN);
206
207            // - sub +
208            let result = I128::MIN.wrapping_sub(&two);
209            assert_eq!(result, max_minus_one);
210        }
211    }
212}