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}