crypto_bigint/uint/
add.rs

1//! [`Uint`] addition operations.
2
3use crate::{Checked, CheckedAdd, CtChoice, Limb, Uint, Wrapping, Zero};
4use core::ops::{Add, AddAssign};
5use subtle::CtOption;
6
7impl<const LIMBS: usize> Uint<LIMBS> {
8    /// Computes `a + b + carry`, returning the result along with the new carry.
9    #[inline(always)]
10    pub const fn adc(&self, rhs: &Self, mut carry: Limb) -> (Self, Limb) {
11        let mut limbs = [Limb::ZERO; LIMBS];
12        let mut i = 0;
13
14        while i < LIMBS {
15            let (w, c) = self.limbs[i].adc(rhs.limbs[i], carry);
16            limbs[i] = w;
17            carry = c;
18            i += 1;
19        }
20
21        (Self { limbs }, carry)
22    }
23
24    /// Perform saturating addition, returning `MAX` on overflow.
25    pub const fn saturating_add(&self, rhs: &Self) -> Self {
26        let (res, overflow) = self.adc(rhs, Limb::ZERO);
27        Self::ct_select(&res, &Self::MAX, CtChoice::from_lsb(overflow.0))
28    }
29
30    /// Perform wrapping addition, discarding overflow.
31    pub const fn wrapping_add(&self, rhs: &Self) -> Self {
32        self.adc(rhs, Limb::ZERO).0
33    }
34
35    /// Perform wrapping addition, returning the truthy value as the second element of the tuple
36    /// if an overflow has occurred.
37    pub(crate) const fn conditional_wrapping_add(
38        &self,
39        rhs: &Self,
40        choice: CtChoice,
41    ) -> (Self, CtChoice) {
42        let actual_rhs = Uint::ct_select(&Uint::ZERO, rhs, choice);
43        let (sum, carry) = self.adc(&actual_rhs, Limb::ZERO);
44        (sum, CtChoice::from_lsb(carry.0))
45    }
46}
47
48impl<const LIMBS: usize> CheckedAdd<&Uint<LIMBS>> for Uint<LIMBS> {
49    type Output = Self;
50
51    fn checked_add(&self, rhs: &Self) -> CtOption<Self> {
52        let (result, carry) = self.adc(rhs, Limb::ZERO);
53        CtOption::new(result, carry.is_zero())
54    }
55}
56
57impl<const LIMBS: usize> Add for Wrapping<Uint<LIMBS>> {
58    type Output = Self;
59
60    fn add(self, rhs: Self) -> Wrapping<Uint<LIMBS>> {
61        Wrapping(self.0.wrapping_add(&rhs.0))
62    }
63}
64
65impl<const LIMBS: usize> Add<&Wrapping<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
66    type Output = Wrapping<Uint<LIMBS>>;
67
68    fn add(self, rhs: &Wrapping<Uint<LIMBS>>) -> Wrapping<Uint<LIMBS>> {
69        Wrapping(self.0.wrapping_add(&rhs.0))
70    }
71}
72
73impl<const LIMBS: usize> Add<Wrapping<Uint<LIMBS>>> for &Wrapping<Uint<LIMBS>> {
74    type Output = Wrapping<Uint<LIMBS>>;
75
76    fn add(self, rhs: Wrapping<Uint<LIMBS>>) -> Wrapping<Uint<LIMBS>> {
77        Wrapping(self.0.wrapping_add(&rhs.0))
78    }
79}
80
81impl<const LIMBS: usize> Add<&Wrapping<Uint<LIMBS>>> for &Wrapping<Uint<LIMBS>> {
82    type Output = Wrapping<Uint<LIMBS>>;
83
84    fn add(self, rhs: &Wrapping<Uint<LIMBS>>) -> Wrapping<Uint<LIMBS>> {
85        Wrapping(self.0.wrapping_add(&rhs.0))
86    }
87}
88
89impl<const LIMBS: usize> AddAssign for Wrapping<Uint<LIMBS>> {
90    fn add_assign(&mut self, other: Self) {
91        *self = *self + other;
92    }
93}
94
95impl<const LIMBS: usize> AddAssign<&Wrapping<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
96    fn add_assign(&mut self, other: &Self) {
97        *self = *self + other;
98    }
99}
100
101impl<const LIMBS: usize> Add for Checked<Uint<LIMBS>> {
102    type Output = Self;
103
104    fn add(self, rhs: Self) -> Checked<Uint<LIMBS>> {
105        Checked(
106            self.0
107                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
108        )
109    }
110}
111
112impl<const LIMBS: usize> Add<&Checked<Uint<LIMBS>>> for Checked<Uint<LIMBS>> {
113    type Output = Checked<Uint<LIMBS>>;
114
115    fn add(self, rhs: &Checked<Uint<LIMBS>>) -> Checked<Uint<LIMBS>> {
116        Checked(
117            self.0
118                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
119        )
120    }
121}
122
123impl<const LIMBS: usize> Add<Checked<Uint<LIMBS>>> for &Checked<Uint<LIMBS>> {
124    type Output = Checked<Uint<LIMBS>>;
125
126    fn add(self, rhs: Checked<Uint<LIMBS>>) -> Checked<Uint<LIMBS>> {
127        Checked(
128            self.0
129                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
130        )
131    }
132}
133
134impl<const LIMBS: usize> Add<&Checked<Uint<LIMBS>>> for &Checked<Uint<LIMBS>> {
135    type Output = Checked<Uint<LIMBS>>;
136
137    fn add(self, rhs: &Checked<Uint<LIMBS>>) -> Checked<Uint<LIMBS>> {
138        Checked(
139            self.0
140                .and_then(|lhs| rhs.0.and_then(|rhs| lhs.checked_add(&rhs))),
141        )
142    }
143}
144
145impl<const LIMBS: usize> AddAssign for Checked<Uint<LIMBS>> {
146    fn add_assign(&mut self, other: Self) {
147        *self = *self + other;
148    }
149}
150
151impl<const LIMBS: usize> AddAssign<&Checked<Uint<LIMBS>>> for Checked<Uint<LIMBS>> {
152    fn add_assign(&mut self, other: &Self) {
153        *self = *self + other;
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use crate::{CheckedAdd, Limb, U128};
160
161    #[test]
162    fn adc_no_carry() {
163        let (res, carry) = U128::ZERO.adc(&U128::ONE, Limb::ZERO);
164        assert_eq!(res, U128::ONE);
165        assert_eq!(carry, Limb::ZERO);
166    }
167
168    #[test]
169    fn adc_with_carry() {
170        let (res, carry) = U128::MAX.adc(&U128::ONE, Limb::ZERO);
171        assert_eq!(res, U128::ZERO);
172        assert_eq!(carry, Limb::ONE);
173    }
174
175    #[test]
176    fn saturating_add_no_carry() {
177        assert_eq!(U128::ZERO.saturating_add(&U128::ONE), U128::ONE);
178    }
179
180    #[test]
181    fn saturating_add_with_carry() {
182        assert_eq!(U128::MAX.saturating_add(&U128::ONE), U128::MAX);
183    }
184
185    #[test]
186    fn wrapping_add_no_carry() {
187        assert_eq!(U128::ZERO.wrapping_add(&U128::ONE), U128::ONE);
188    }
189
190    #[test]
191    fn wrapping_add_with_carry() {
192        assert_eq!(U128::MAX.wrapping_add(&U128::ONE), U128::ZERO);
193    }
194
195    #[test]
196    fn checked_add_ok() {
197        let result = U128::ZERO.checked_add(&U128::ONE);
198        assert_eq!(result.unwrap(), U128::ONE);
199    }
200
201    #[test]
202    fn checked_add_overflow() {
203        let result = U128::MAX.checked_add(&U128::ONE);
204        assert!(!bool::from(result.is_some()));
205    }
206}