Skip to main content

crypto_bigint/int/
sub.rs

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