Skip to main content

crypto_bigint/uint/
sub.rs

1//! [`Uint`] subtraction operations.
2
3use super::Uint;
4use crate::{
5    Checked, CheckedSub, Choice, CtOption, Limb, Sub, SubAssign, Wrapping, WrappingSub, word,
6};
7
8impl<const LIMBS: usize> Uint<LIMBS> {
9    /// Computes `self - (rhs + borrow)`, returning the result along with the new borrow.
10    #[deprecated(since = "0.7.0", note = "please use `borrowing_sub` instead")]
11    #[must_use]
12    pub const fn sbb(&self, rhs: &Self, borrow: Limb) -> (Self, Limb) {
13        self.borrowing_sub(rhs, borrow)
14    }
15
16    /// Computes `self - (rhs + borrow)`, returning the result along with the new borrow.
17    #[inline(always)]
18    #[must_use]
19    pub const fn borrowing_sub(&self, rhs: &Self, mut borrow: Limb) -> (Self, Limb) {
20        let mut limbs = [Limb::ZERO; LIMBS];
21        let mut i = 0;
22
23        while i < LIMBS {
24            let (w, b) = self.limbs[i].borrowing_sub(rhs.limbs[i], borrow);
25            limbs[i] = w;
26            borrow = b;
27            i += 1;
28        }
29
30        (Self { limbs }, borrow)
31    }
32
33    /// Perform wrapping subtraction, returning the truthy value as the second element of
34    /// the tuple if an underflow has occurred.
35    #[inline]
36    #[must_use]
37    pub(crate) const fn conditional_borrowing_sub(
38        &self,
39        rhs: &Self,
40        choice: Choice,
41    ) -> (Self, Choice) {
42        let mut limbs = [Limb::ZERO; LIMBS];
43        let mask = Limb::select(Limb::ZERO, Limb::MAX, choice);
44        let mut borrow = Limb::ZERO;
45
46        let mut i = 0;
47        while i < LIMBS {
48            let masked_rhs = rhs.limbs[i].bitand(mask);
49            (limbs[i], borrow) = self.limbs[i].borrowing_sub(masked_rhs, borrow);
50            i += 1;
51        }
52
53        (Self { limbs }, borrow.lsb_to_choice())
54    }
55
56    /// Perform saturating subtraction, returning `ZERO` on underflow.
57    #[must_use]
58    pub const fn saturating_sub(&self, rhs: &Self) -> Self {
59        let (res, underflow) = self.borrowing_sub(rhs, Limb::ZERO);
60        Self::select(&res, &Self::ZERO, word::choice_from_mask(underflow.0))
61    }
62
63    /// Perform wrapping subtraction, discarding underflow and wrapping around
64    /// the boundary of the type.
65    #[inline]
66    #[must_use]
67    pub const fn wrapping_sub(&self, rhs: &Self) -> Self {
68        self.borrowing_sub(rhs, Limb::ZERO).0
69    }
70}
71
72impl<const LIMBS: usize> CheckedSub for Uint<LIMBS> {
73    fn checked_sub(&self, rhs: &Self) -> CtOption<Self> {
74        let (result, underflow) = self.borrowing_sub(rhs, Limb::ZERO);
75        CtOption::new(result, underflow.is_zero())
76    }
77}
78
79impl<const LIMBS: usize> Sub for Uint<LIMBS> {
80    type Output = Self;
81
82    fn sub(self, rhs: Self) -> Self {
83        self.sub(&rhs)
84    }
85}
86
87impl<const LIMBS: usize> Sub<&Uint<LIMBS>> for Uint<LIMBS> {
88    type Output = Self;
89
90    fn sub(self, rhs: &Self) -> Self {
91        self.checked_sub(rhs)
92            .expect("attempted to subtract with underflow")
93    }
94}
95
96impl<const LIMBS: usize> SubAssign<Uint<LIMBS>> for Uint<LIMBS> {
97    fn sub_assign(&mut self, rhs: Uint<LIMBS>) {
98        *self = self.sub(&rhs);
99    }
100}
101
102impl<const LIMBS: usize> SubAssign<&Uint<LIMBS>> for Uint<LIMBS> {
103    fn sub_assign(&mut self, rhs: &Uint<LIMBS>) {
104        *self = self.sub(rhs);
105    }
106}
107
108impl<const LIMBS: usize> SubAssign for Wrapping<Uint<LIMBS>> {
109    fn sub_assign(&mut self, other: Self) {
110        *self = *self - other;
111    }
112}
113
114impl<const LIMBS: usize> SubAssign<&Wrapping<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
115    fn sub_assign(&mut self, other: &Self) {
116        *self = *self - other;
117    }
118}
119
120impl<const LIMBS: usize> SubAssign for Checked<Uint<LIMBS>> {
121    fn sub_assign(&mut self, other: Self) {
122        *self = *self - other;
123    }
124}
125
126impl<const LIMBS: usize> SubAssign<&Checked<Uint<LIMBS>>> for Checked<Uint<LIMBS>> {
127    fn sub_assign(&mut self, other: &Self) {
128        *self = *self - other;
129    }
130}
131
132impl<const LIMBS: usize> WrappingSub for Uint<LIMBS> {
133    fn wrapping_sub(&self, v: &Self) -> Self {
134        self.wrapping_sub(v)
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use ctutils::Choice;
141
142    use crate::{CheckedSub, Limb, U128};
143
144    #[test]
145    fn borrowing_sub_no_borrow() {
146        let (res, borrow) = U128::ONE.borrowing_sub(&U128::ONE, Limb::ZERO);
147        assert_eq!(res, U128::ZERO);
148        assert_eq!(borrow, Limb::ZERO);
149    }
150
151    #[test]
152    fn borrowing_sub_with_borrow() {
153        let (res, borrow) = U128::ZERO.borrowing_sub(&U128::ONE, Limb::ZERO);
154
155        assert_eq!(res, U128::MAX);
156        assert_eq!(borrow, Limb::MAX);
157    }
158
159    #[test]
160    fn conditional_borrowing_sub_no_sub() {
161        let (res, borrow) = U128::ONE.conditional_borrowing_sub(&U128::ONE, Choice::FALSE);
162        assert_eq!(res, U128::ONE);
163        assert!(!borrow.to_bool_vartime());
164    }
165
166    #[test]
167    fn conditional_borrowing_sub_no_borrow() {
168        let (res, borrow) = U128::ONE.conditional_borrowing_sub(&U128::ZERO, Choice::TRUE);
169        assert_eq!(res, U128::ONE);
170        assert!(!borrow.to_bool_vartime());
171    }
172
173    #[test]
174    fn conditional_borrowing_sub_borrow() {
175        let (res, borrow) = U128::ZERO.conditional_borrowing_sub(&U128::ONE, Choice::TRUE);
176        assert_eq!(res, U128::MAX);
177        assert!(borrow.to_bool_vartime());
178    }
179
180    #[test]
181    fn saturating_sub_no_borrow() {
182        assert_eq!(
183            U128::from(5u64).saturating_sub(&U128::ONE),
184            U128::from(4u64)
185        );
186    }
187
188    #[test]
189    fn saturating_sub_with_borrow() {
190        assert_eq!(
191            U128::from(4u64).saturating_sub(&U128::from(5u64)),
192            U128::ZERO
193        );
194    }
195
196    #[test]
197    fn wrapping_sub_no_borrow() {
198        assert_eq!(U128::ONE.wrapping_sub(&U128::ONE), U128::ZERO);
199    }
200
201    #[test]
202    fn wrapping_sub_with_borrow() {
203        assert_eq!(U128::ZERO.wrapping_sub(&U128::ONE), U128::MAX);
204    }
205
206    #[test]
207    fn checked_sub_ok() {
208        let result = U128::ONE.checked_sub(&U128::ONE);
209        assert_eq!(result.unwrap(), U128::ZERO);
210    }
211
212    #[test]
213    fn checked_sub_overflow() {
214        let result = U128::ZERO.checked_sub(&U128::ONE);
215        assert!(!bool::from(result.is_some()));
216    }
217}