clock_bigint/
add.rs

1//! Addition operations for BigInt.
2//!
3//! Implements constant-time addition using the `adc` (add with carry) algorithm
4//! as specified in the Arithmetic Algorithm Specification.
5//!
6//! ## Constant-Time Behavior
7//!
8//! All addition operations execute in constant time regardless of input values,
9//! making them suitable for cryptographic applications where timing attacks
10//! must be prevented.
11//!
12//! ## Error Handling
13//!
14//! Operations may return `BigIntError::Overflow` if the result would exceed
15//! the configured maximum limb capacity of the BigInt.
16
17use crate::error::Result;
18use crate::limbs::limb_add_carry;
19use crate::sub;
20use crate::types::BigIntCore;
21
22#[cfg(feature = "alloc")]
23use crate::types::BigInt;
24
25/// Add two BigInt magnitudes (unsigned addition).
26///
27/// This function performs addition of the absolute values of two BigInts,
28/// ignoring their signs. The result is stored in the provided mutable BigInt.
29/// Both inputs must have their magnitudes added together.
30///
31/// # Arguments
32///
33/// * `a` - First BigInt operand
34/// * `b` - Second BigInt operand
35/// * `result` - Mutable BigInt to store the result (must have sufficient capacity)
36///
37/// # Returns
38///
39/// Returns `Ok(())` on success, or `BigIntError::Overflow` if the result
40/// would exceed the maximum limb capacity.
41///
42/// # Examples
43///
44/// ```rust
45/// use clock_bigint::{BigInt, BigIntCore, add_magnitude};
46///
47/// let a = BigInt::from_u64(10, 10);
48/// let b = BigInt::from_u64(5, 10);
49/// let mut result = BigInt::new(10);
50///
51/// add_magnitude(&a, &b, &mut result).unwrap();
52/// assert_eq!(result.limbs()[0], 15);
53/// ```
54///
55/// # Performance
56///
57/// Executes in O(n) time where n is the maximum limb count of the inputs.
58/// The algorithm processes limbs from least significant to most significant
59/// using constant-time carry propagation.
60#[cfg(feature = "alloc")]
61pub fn add_magnitude(a: &BigInt, b: &BigInt, result: &mut BigInt) -> Result<()> {
62    let n = a.limb_count().max(b.limb_count());
63
64    // Ensure result has sufficient capacity
65    result.ensure_capacity(n + 1)?; // +1 for potential carry
66
67    let a_limbs = a.limbs();
68    let b_limbs = b.limbs();
69    let result_limbs = result.limbs_mut();
70
71    let mut carry = 0u64;
72
73    // Add limbs from least significant to most significant
74    // Execute full loop for constant-time behavior
75    for i in 0..n {
76        let a_val = if i < a_limbs.len() { a_limbs[i] } else { 0 };
77        let b_val = if i < b_limbs.len() { b_limbs[i] } else { 0 };
78
79        let (sum, carry_out) = limb_add_carry(a_val, b_val, carry);
80        if i < result_limbs.len() {
81            result_limbs[i] = sum;
82        }
83        carry = carry_out;
84    }
85
86    // Handle final carry
87    if carry != 0 {
88        if n < result_limbs.len() {
89            result_limbs[n] = carry;
90        } else {
91            // Need to ensure capacity for the additional limb
92            result.ensure_capacity(n + 1)?;
93            result.limbs_mut()[n] = carry;
94        }
95    }
96
97    result.canonicalize()?;
98    Ok(())
99}
100
101/// Add two BigInt values with proper sign handling.
102///
103/// Performs algebraic addition of two BigInts, correctly handling all sign combinations:
104///
105/// - **Same sign**: Adds the magnitudes and keeps the common sign
106/// - **Opposite signs**: Performs subtraction of the smaller magnitude from the larger,
107///   with the result taking the sign of the larger operand
108/// - **Zero handling**: Returns the non-zero operand when one input is zero
109///
110/// # Arguments
111///
112/// * `a` - First BigInt operand
113/// * `b` - Second BigInt operand
114///
115/// # Returns
116///
117/// Returns `Ok(result)` containing the sum, or `BigIntError::Overflow` if the
118/// result would exceed the maximum limb capacity of either input.
119///
120/// # Examples
121///
122/// ```rust
123/// use clock_bigint::{BigInt, BigIntCore};
124///
125/// // Positive + positive
126/// let a = BigInt::from_u64(10, 10);
127/// let b = BigInt::from_u64(5, 10);
128/// let sum = clock_bigint::add(&a, &b).unwrap();
129/// assert_eq!(sum.limbs()[0], 15);
130/// assert!(!sum.sign());
131///
132/// // Positive + negative (becomes subtraction)
133/// let mut neg_b = BigInt::from_u64(3, 10);
134/// neg_b.set_sign(true);
135/// let diff = clock_bigint::add(&a, &neg_b).unwrap();
136/// assert_eq!(diff.limbs()[0], 7);
137/// assert!(!diff.sign());
138///
139/// // Zero handling
140/// let zero = BigInt::from_u64(0, 10);
141/// let same = clock_bigint::add(&a, &zero).unwrap();
142/// assert_eq!(same.limbs()[0], 10);
143/// ```
144///
145/// # Algorithm
146///
147/// For same-sign operands, delegates to `add_magnitude` for efficient limb-by-limb
148/// addition with carry propagation.
149///
150/// For opposite-sign operands, uses subtraction: `a + b = a - (-b)` when signs differ,
151/// optimized to avoid temporary allocations.
152///
153/// # Performance
154///
155/// Same-sign addition: O(n) where n is the maximum limb count
156/// Opposite-sign addition: O(n) via subtraction algorithm
157/// Zero handling: O(1)
158#[cfg(feature = "alloc")]
159pub fn add(a: &BigInt, b: &BigInt) -> Result<BigInt> {
160    // Handle zero cases
161    if a.is_zero() {
162        return Ok(b.clone());
163    }
164    if b.is_zero() {
165        return Ok(a.clone());
166    }
167
168    // Same sign: add magnitudes
169    if a.sign() == b.sign() {
170        let max_limbs = a.max_limbs().max(b.max_limbs());
171        let mut result = BigInt::new(max_limbs + 1); // +1 for potential carry
172        add_magnitude(a, b, &mut result)?;
173        result.set_sign(a.sign());
174        return Ok(result);
175    }
176
177    // Opposite sign: delegate to subtraction
178    // If signs differ: a + b = a - (-b)
179    // But we can optimize: if a >= 0 and b < 0, then a + b = a - |b|
180    // If a < 0 and b >= 0, then a + b = b - |a|
181    if a.sign() == false && b.sign() == true {
182        // a >= 0, b < 0: a + b = a - |b|
183        sub::sub(a, b)
184    } else {
185        // a < 0, b >= 0: a + b = b - |a|
186        sub::sub(b, a)
187    }
188}
189
190/// In-place addition for BigInt.
191///
192/// Adds the second operand to the first operand in-place, modifying the first operand.
193/// This is more efficient than `add` when you don't need to preserve the original value.
194///
195/// # Arguments
196///
197/// * `a` - Mutable BigInt to be modified (left-hand side)
198/// * `b` - BigInt to add (right-hand side)
199///
200/// # Returns
201///
202/// Returns `Ok(())` on success, or `BigIntError::Overflow` if the result would
203/// exceed the maximum limb capacity.
204///
205/// # Examples
206///
207/// ```rust
208/// use clock_bigint::{BigInt, BigIntCore, add_assign};
209///
210/// let mut a = BigInt::from_u64(10, 10);
211/// let b = BigInt::from_u64(5, 10);
212///
213/// add_assign(&mut a, &b).unwrap();
214/// assert_eq!(a.limbs()[0], 15);
215/// ```
216///
217/// # Performance
218///
219/// Same performance characteristics as `add`, but avoids allocating a new BigInt
220/// for the result.
221#[cfg(feature = "alloc")]
222pub fn add_assign(a: &mut BigInt, b: &BigInt) -> Result<()> {
223    let result = add(a, b)?;
224    *a = result;
225    Ok(())
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231    use crate::types::BigInt;
232
233    #[test]
234    fn test_add_same_sign() {
235        let a = BigInt::from_u64(10, 10);
236        let b = BigInt::from_u64(20, 10);
237        let result = add(&a, &b).unwrap();
238        assert_eq!(result.limbs()[0], 30);
239        assert!(!result.sign());
240    }
241
242    #[test]
243    fn test_add_positive_negative() {
244        // 10 + (-5) = 5
245        let a = BigInt::from_u64(10, 10);
246        let mut neg_b = BigInt::from_u64(5, 10);
247        neg_b.set_sign(true);
248        let result = add(&a, &neg_b).unwrap();
249        assert_eq!(result.limbs()[0], 5);
250        assert!(!result.sign());
251    }
252
253    #[test]
254    fn test_add_negative_positive() {
255        // -10 + 5 = -5
256        let mut neg_a = BigInt::from_u64(10, 10);
257        neg_a.set_sign(true);
258        let b = BigInt::from_u64(5, 10);
259        let result = add(&neg_a, &b).unwrap();
260        assert_eq!(result.limbs()[0], 5);
261        assert!(result.sign());
262    }
263
264    #[test]
265    fn test_add_negative_negative() {
266        // -10 + (-5) = -15
267        let mut neg_a = BigInt::from_u64(10, 10);
268        neg_a.set_sign(true);
269        let mut neg_b = BigInt::from_u64(5, 10);
270        neg_b.set_sign(true);
271        let result = add(&neg_a, &neg_b).unwrap();
272        assert_eq!(result.limbs()[0], 15);
273        assert!(result.sign());
274    }
275
276    #[test]
277    fn test_add_opposite_signs_result_zero() {
278        // 10 + (-10) = 0
279        let a = BigInt::from_u64(10, 10);
280        let mut neg_b = BigInt::from_u64(10, 10);
281        neg_b.set_sign(true);
282        let result = add(&a, &neg_b).unwrap();
283        assert!(result.is_zero());
284    }
285
286    #[test]
287    fn test_add_opposite_signs_result_negative() {
288        // 5 + (-10) = -5
289        let a = BigInt::from_u64(5, 10);
290        let mut neg_b = BigInt::from_u64(10, 10);
291        neg_b.set_sign(true);
292        let result = add(&a, &neg_b).unwrap();
293        assert_eq!(result.limbs()[0], 5);
294        assert!(result.sign());
295    }
296
297    #[test]
298    fn test_add_with_carry() {
299        let a = BigInt::from_u64(u64::MAX, 10);
300        let b = BigInt::from_u64(1, 10);
301        let result = add(&a, &b).unwrap();
302        assert_eq!(result.limbs()[0], 0);
303        assert_eq!(result.limbs()[1], 1);
304        assert!(!result.sign());
305    }
306
307    #[test]
308    fn test_add_zero() {
309        let a = BigInt::from_u64(42, 10);
310        let b = BigInt::from_u64(0, 10);
311        let result = add(&a, &b).unwrap();
312        assert_eq!(result.limbs()[0], 42);
313    }
314
315    #[test]
316    fn test_add_both_zero() {
317        let a = BigInt::from_u64(0, 10);
318        let b = BigInt::from_u64(0, 10);
319        let result = add(&a, &b).unwrap();
320        assert!(result.is_zero());
321    }
322
323    #[test]
324    fn test_add_large_numbers() {
325        // Test with multi-limb numbers: (2^128 - 1) + 1 = 2^128
326        let a = BigInt::from_limbs(&[u64::MAX, u64::MAX], 10).unwrap();
327        let b = BigInt::from_limbs(&[1, 0], 10).unwrap();
328        let result = add(&a, &b).unwrap();
329
330        // Result should be [0, 0, 1] representing 2^128
331        assert_eq!(result.limb_count(), 3);
332        assert_eq!(result.limbs()[0], 0);
333        assert_eq!(result.limbs()[1], 0);
334        assert_eq!(result.limbs()[2], 1);
335        assert!(!result.sign());
336    }
337
338    #[test]
339    fn test_add_assign() {
340        let mut a = BigInt::from_u64(10, 10);
341        let b = BigInt::from_u64(5, 10);
342        add_assign(&mut a, &b).unwrap();
343        assert_eq!(a.limbs()[0], 15);
344        assert!(!a.sign());
345    }
346
347    #[test]
348    fn test_add_assign_opposite_signs() {
349        let mut a = BigInt::from_u64(10, 10);
350        let mut neg_b = BigInt::from_u64(15, 10);
351        neg_b.set_sign(true);
352        add_assign(&mut a, &neg_b).unwrap();
353        assert_eq!(a.limbs()[0], 5);
354        assert!(a.sign());
355    }
356
357    #[test]
358    fn test_add_magnitude_basic() {
359        let a = BigInt::from_u64(10, 10);
360        let b = BigInt::from_u64(5, 10);
361        let mut result = BigInt::new(10);
362        add_magnitude(&a, &b, &mut result).unwrap();
363        assert_eq!(result.limbs()[0], 15);
364        assert!(!result.sign());
365    }
366
367    #[test]
368    fn test_add_magnitude_with_carry() {
369        let a = BigInt::from_u64(u64::MAX, 10);
370        let b = BigInt::from_u64(1, 10);
371        let mut result = BigInt::new(10);
372        add_magnitude(&a, &b, &mut result).unwrap();
373        assert_eq!(result.limbs()[0], 0);
374        assert_eq!(result.limbs()[1], 1);
375        assert!(!result.sign());
376    }
377
378    #[test]
379    fn test_add_magnitude_different_sizes() {
380        let a = BigInt::from_limbs(&[1, 2, 3], 10).unwrap();
381        let b = BigInt::from_limbs(&[4], 10).unwrap();
382        let mut result = BigInt::new(10);
383        add_magnitude(&a, &b, &mut result).unwrap();
384        assert_eq!(result.limbs()[0], 5);
385        assert_eq!(result.limbs()[1], 2);
386        assert_eq!(result.limbs()[2], 3);
387        assert!(!result.sign());
388    }
389}