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