Skip to main content

crypto_bigint/uint/
sub.rs

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