crypto_bigint/uint/boxed/
sub.rs1use crate::{BoxedUint, CheckedSub, Limb, Uint, Wrapping, WrappingSub, Zero, U128, U64};
4use core::ops::{Sub, SubAssign};
5use subtle::{Choice, ConditionallySelectable, CtOption};
6
7impl BoxedUint {
8 #[inline(always)]
10 pub fn sbb(&self, rhs: &Self, borrow: Limb) -> (Self, Limb) {
11 Self::fold_limbs(self, rhs, borrow, |a, b, c| a.sbb(b, c))
12 }
13
14 #[inline(always)]
18 pub fn sbb_assign(&mut self, rhs: impl AsRef<[Limb]>, mut borrow: Limb) -> Limb {
19 debug_assert!(self.bits_precision() <= (rhs.as_ref().len() as u32 * Limb::BITS));
20
21 for i in 0..self.nlimbs() {
22 let (limb, b) = self.limbs[i].sbb(*rhs.as_ref().get(i).unwrap_or(&Limb::ZERO), borrow);
23 self.limbs[i] = limb;
24 borrow = b;
25 }
26
27 borrow
28 }
29
30 pub fn wrapping_sub(&self, rhs: &Self) -> Self {
32 self.sbb(rhs, Limb::ZERO).0
33 }
34
35 pub(crate) fn conditional_sbb_assign(&mut self, rhs: &Self, choice: Choice) -> Choice {
38 debug_assert!(self.bits_precision() <= rhs.bits_precision());
39 let mask = Limb::conditional_select(&Limb::ZERO, &Limb::MAX, choice);
40 let mut borrow = Limb::ZERO;
41
42 for i in 0..self.nlimbs() {
43 let masked_rhs = *rhs.limbs.get(i).unwrap_or(&Limb::ZERO) & mask;
44 let (limb, b) = self.limbs[i].sbb(masked_rhs, borrow);
45 self.limbs[i] = limb;
46 borrow = b;
47 }
48
49 Choice::from((borrow.0 & 1) as u8)
50 }
51}
52
53impl CheckedSub for BoxedUint {
54 fn checked_sub(&self, rhs: &Self) -> CtOption<Self> {
55 let (result, carry) = self.sbb(rhs, Limb::ZERO);
56 CtOption::new(result, carry.is_zero())
57 }
58}
59
60impl Sub for BoxedUint {
61 type Output = Self;
62
63 fn sub(self, rhs: Self) -> Self {
64 self.sub(&rhs)
65 }
66}
67
68impl Sub<&BoxedUint> for BoxedUint {
69 type Output = Self;
70
71 fn sub(self, rhs: &Self) -> Self {
72 Sub::sub(&self, rhs)
73 }
74}
75
76impl Sub<BoxedUint> for &BoxedUint {
77 type Output = BoxedUint;
78
79 fn sub(self, rhs: BoxedUint) -> BoxedUint {
80 Sub::sub(self, &rhs)
81 }
82}
83
84impl Sub<&BoxedUint> for &BoxedUint {
85 type Output = BoxedUint;
86
87 fn sub(self, rhs: &BoxedUint) -> BoxedUint {
88 self.checked_sub(rhs)
89 .expect("attempted to subtract with underflow")
90 }
91}
92
93impl SubAssign<BoxedUint> for BoxedUint {
94 fn sub_assign(&mut self, rhs: BoxedUint) {
95 self.sub_assign(&rhs)
96 }
97}
98
99impl SubAssign<&BoxedUint> for BoxedUint {
100 fn sub_assign(&mut self, rhs: &BoxedUint) {
101 let carry = self.sbb_assign(rhs, Limb::ZERO);
102 assert_eq!(
103 carry.is_zero().unwrap_u8(),
104 1,
105 "attempted to subtract with underflow"
106 );
107 }
108}
109
110impl<const LIMBS: usize> Sub<Uint<LIMBS>> for BoxedUint {
111 type Output = BoxedUint;
112
113 fn sub(self, rhs: Uint<LIMBS>) -> BoxedUint {
114 self.sub(&rhs)
115 }
116}
117
118impl<const LIMBS: usize> Sub<&Uint<LIMBS>> for BoxedUint {
119 type Output = BoxedUint;
120
121 fn sub(mut self, rhs: &Uint<LIMBS>) -> BoxedUint {
122 self -= rhs;
123 self
124 }
125}
126
127impl<const LIMBS: usize> Sub<Uint<LIMBS>> for &BoxedUint {
128 type Output = BoxedUint;
129
130 fn sub(self, rhs: Uint<LIMBS>) -> BoxedUint {
131 self.clone().sub(rhs)
132 }
133}
134
135impl<const LIMBS: usize> Sub<&Uint<LIMBS>> for &BoxedUint {
136 type Output = BoxedUint;
137
138 fn sub(self, rhs: &Uint<LIMBS>) -> BoxedUint {
139 self.clone().sub(rhs)
140 }
141}
142
143impl<const LIMBS: usize> SubAssign<Uint<LIMBS>> for BoxedUint {
144 fn sub_assign(&mut self, rhs: Uint<LIMBS>) {
145 *self -= &rhs;
146 }
147}
148
149impl<const LIMBS: usize> SubAssign<&Uint<LIMBS>> for BoxedUint {
150 fn sub_assign(&mut self, rhs: &Uint<LIMBS>) {
151 let carry = self.sbb_assign(rhs.as_limbs(), Limb::ZERO);
152 assert_eq!(carry.0, 0, "attempted to sub with overflow");
153 }
154}
155
156impl SubAssign<Wrapping<BoxedUint>> for Wrapping<BoxedUint> {
157 fn sub_assign(&mut self, other: Wrapping<BoxedUint>) {
158 self.0.sbb_assign(&other.0, Limb::ZERO);
159 }
160}
161
162impl SubAssign<&Wrapping<BoxedUint>> for Wrapping<BoxedUint> {
163 fn sub_assign(&mut self, other: &Wrapping<BoxedUint>) {
164 self.0.sbb_assign(&other.0, Limb::ZERO);
165 }
166}
167
168impl WrappingSub for BoxedUint {
169 fn wrapping_sub(&self, v: &Self) -> Self {
170 self.wrapping_sub(v)
171 }
172}
173
174macro_rules! impl_sub_primitive {
175 ($($primitive:ty),+) => {
176 $(
177 impl Sub<$primitive> for BoxedUint {
178 type Output = BoxedUint;
179
180 #[inline]
181 fn sub(self, rhs: $primitive) -> BoxedUint {
182 self - U64::from(rhs)
183 }
184 }
185
186 impl Sub<$primitive> for &BoxedUint {
187 type Output = BoxedUint;
188
189 #[inline]
190 fn sub(self, rhs: $primitive) -> BoxedUint {
191 self - U64::from(rhs)
192 }
193 }
194
195 impl SubAssign<$primitive> for BoxedUint {
196 fn sub_assign(&mut self, rhs: $primitive) {
197 *self -= U64::from(rhs);
198 }
199 }
200 )+
201 };
202}
203
204impl_sub_primitive!(u8, u16, u32, u64);
205
206impl Sub<u128> for BoxedUint {
207 type Output = BoxedUint;
208
209 #[inline]
210 fn sub(self, rhs: u128) -> BoxedUint {
211 self - U128::from(rhs)
212 }
213}
214
215impl Sub<u128> for &BoxedUint {
216 type Output = BoxedUint;
217
218 #[inline]
219 fn sub(self, rhs: u128) -> BoxedUint {
220 self - U128::from(rhs)
221 }
222}
223
224impl SubAssign<u128> for BoxedUint {
225 fn sub_assign(&mut self, rhs: u128) {
226 *self -= U128::from(rhs);
227 }
228}
229
230#[cfg(test)]
231#[allow(clippy::unwrap_used)]
232mod tests {
233 use super::{BoxedUint, CheckedSub, Limb};
234
235 #[test]
236 fn sbb_no_borrow() {
237 let (res, carry) = BoxedUint::one().sbb(&BoxedUint::one(), Limb::ZERO);
238 assert_eq!(res, BoxedUint::zero());
239 assert_eq!(carry, Limb::ZERO);
240
241 let (res, carry) = BoxedUint::one().sbb(&BoxedUint::zero(), Limb::ZERO);
242 assert_eq!(res, BoxedUint::one());
243 assert_eq!(carry, Limb::ZERO);
244 }
245
246 #[test]
247 fn sbb_with_borrow() {
248 let (res, borrow) = BoxedUint::zero().sbb(&BoxedUint::one(), Limb::ZERO);
249 assert_eq!(res, BoxedUint::max(Limb::BITS));
250 assert_eq!(borrow, Limb::MAX);
251 }
252
253 #[test]
254 fn sub_with_u32_rhs() {
255 let a = BoxedUint::from(0x100000000u64);
256 let b = a - u32::MAX;
257 assert_eq!(b, BoxedUint::one());
258 }
259
260 #[test]
261 fn checked_sub_ok() {
262 let result = BoxedUint::one().checked_sub(&BoxedUint::one());
263 assert_eq!(result.unwrap(), BoxedUint::zero());
264 }
265
266 #[test]
267 fn checked_sub_overflow() {
268 let result = BoxedUint::zero().checked_sub(&BoxedUint::one());
269 assert!(!bool::from(result.is_some()));
270 }
271}