clock_bigint/
div.rs

1//! Division and modulo operations for BigInt.
2//!
3//! Implements constant-time binary long division algorithm.
4
5use crate::error::{BigIntError, Result};
6use crate::limbs::{limb_add_carry, limb_mul_wide, limb_sub_borrow};
7use crate::types::BigIntCore;
8
9#[cfg(feature = "alloc")]
10use crate::types::BigInt;
11#[cfg(feature = "alloc")]
12use alloc::vec;
13
14/// Divide two BigInt values, returning quotient and remainder.
15///
16/// Computes: a = q × b + r where 0 ≤ |r| < |b|
17///
18/// Uses binary long division algorithm with constant-time operations.
19#[cfg(feature = "alloc")]
20pub fn div_rem(a: &BigInt, b: &BigInt) -> Result<(BigInt, BigInt)> {
21    // Check for division by zero
22    if b.is_zero() {
23        return Err(BigIntError::DivisionByZero);
24    }
25
26    // Handle zero dividend
27    if a.is_zero() {
28        let max_limbs = a.max_limbs().max(b.max_limbs());
29        let zero = BigInt::new(max_limbs);
30        return Ok((zero.clone(), zero));
31    }
32
33    // Handle signs: division of signed numbers
34    let result_sign = a.sign() != b.sign();
35    let a_abs = if a.sign() {
36        crate::sub::sub(&BigInt::new(a.max_limbs()), a)?
37    } else {
38        a.clone()
39    };
40    let b_abs = if b.sign() {
41        crate::sub::sub(&BigInt::new(b.max_limbs()), b)?
42    } else {
43        b.clone()
44    };
45
46    // Perform unsigned division
47    let (mut quotient, mut remainder) = div_rem_unsigned(&a_abs, &b_abs)?;
48
49    // Apply correct signs
50    if result_sign {
51        quotient.set_sign(true);
52    }
53    if a.sign() {
54        remainder.set_sign(true);
55    }
56
57    quotient.canonicalize()?;
58    remainder.canonicalize()?;
59
60    Ok((quotient, remainder))
61}
62
63/// Unsigned division: divides two positive BigInt values using Knuth Algorithm D.
64fn div_rem_unsigned(a: &BigInt, b: &BigInt) -> Result<(BigInt, BigInt)> {
65    let a_limbs = a.limbs();
66    let b_limbs = b.limbs();
67
68    // If |a| < |b|, quotient is 0, remainder is a
69    if a_limbs.len() < b_limbs.len()
70        || (a_limbs.len() == b_limbs.len()
71            && a_limbs[a_limbs.len() - 1] < b_limbs[a_limbs.len() - 1])
72    {
73        let max_limbs = a.max_limbs().max(b.max_limbs());
74        let zero = BigInt::new(max_limbs);
75        return Ok((zero, a.clone()));
76    }
77
78    // For single limb cases, use built-in division
79    if a_limbs.len() == 1 && b_limbs.len() == 1 {
80        let max_limbs = a.max_limbs().max(b.max_limbs());
81        let mut quotient = BigInt::new(max_limbs);
82        let mut remainder = BigInt::new(max_limbs);
83
84        let a_val = a_limbs[0];
85        let b_val = b_limbs[0];
86        let q = a_val / b_val;
87        let r = a_val % b_val;
88
89        quotient.limbs_mut()[0] = q;
90        remainder.limbs_mut()[0] = r;
91
92        quotient.canonicalize()?;
93        remainder.canonicalize()?;
94        return Ok((quotient, remainder));
95    }
96
97    // Knuth Algorithm D implementation
98    let max_limbs = a.max_limbs().max(b.max_limbs());
99
100    // Step D1: Normalize
101    // Find d such that d * v_{n-1} >= 2^{63}
102    let v_n1 = b_limbs[b_limbs.len() - 1];
103    let d_shift = 63 - (63 - v_n1.leading_zeros());
104    let d = if d_shift < 64 {
105        1u64 << (63 - v_n1.leading_zeros())
106    } else {
107        1
108    };
109
110    // Normalize u and v by multiplying by d
111    let mut u_normalized = vec![0u64; a_limbs.len() + 1]; // Extra space for normalization
112    let mut v_normalized = vec![0u64; b_limbs.len()];
113
114    // Multiply a by d
115    let mut carry = 0u64;
116    for i in 0..a_limbs.len() {
117        let prod = limb_mul_wide(a_limbs[i], d);
118        let (sum, carry1) = limb_add_carry(prod.lo, carry, 0);
119        u_normalized[i] = sum;
120        carry = prod.hi + carry1;
121    }
122    if carry > 0 {
123        u_normalized[a_limbs.len()] = carry;
124    }
125
126    // Multiply b by d
127    carry = 0;
128    for i in 0..b_limbs.len() {
129        let prod = limb_mul_wide(b_limbs[i], d);
130        let (sum, carry1) = limb_add_carry(prod.lo, carry, 0);
131        v_normalized[i] = sum;
132        carry = prod.hi + carry1;
133    }
134
135    // Ensure v_normalized has leading bit set (for proper quotient estimation)
136    if v_normalized.len() > 0 && (v_normalized[v_normalized.len() - 1] & (1u64 << 63)) == 0 {
137        // Shift everything left by 1
138        let mut carry = 0u64;
139        for i in 0..u_normalized.len() {
140            let new_carry = (u_normalized[i] >> 63) & 1;
141            u_normalized[i] = (u_normalized[i] << 1) | carry;
142            carry = new_carry;
143        }
144        carry = 0;
145        for i in 0..v_normalized.len() {
146            let new_carry = (v_normalized[i] >> 63) & 1;
147            v_normalized[i] = (v_normalized[i] << 1) | carry;
148            carry = new_carry;
149        }
150    }
151
152    let m = u_normalized.len() - v_normalized.len();
153    let n = v_normalized.len();
154
155    // Create quotient
156    let mut quotient = BigInt::new(max_limbs);
157    let q_limbs = quotient.limbs_mut();
158
159    // Step D2: Initialize j
160    for j in (0..=m).rev() {
161        // Step D3: Calculate q̂
162        // q̂ = (u[j+n] * 2^64 + u[j+n-1]) / v[n-1]
163        let u_jn = if j + n < u_normalized.len() {
164            u_normalized[j + n]
165        } else {
166            0
167        };
168        let u_jn1 = if j + n - 1 < u_normalized.len() {
169            u_normalized[j + n - 1]
170        } else {
171            0
172        };
173        let v_nm1 = v_normalized[n - 1];
174
175        // Estimate quotient digit
176        let mut q_hat = if v_nm1 == 0 {
177            u64::MAX
178        } else {
179            // (u[j+n] * 2^64 + u[j+n-1]) / v[n-1]
180            let num = (u_jn as u128) << 64 | (u_jn1 as u128);
181            (num / (v_nm1 as u128)) as u64
182        };
183
184        // Adjust q̂ if necessary (Knuth's correction)
185        if q_hat > 0 {
186            let mut prod_carry = 0u64;
187            for i in 0..n {
188                let prod = limb_mul_wide(q_hat, v_normalized[i]);
189                let (sum, carry1) = limb_add_carry(prod.lo, prod_carry, 0);
190                prod_carry = prod.hi + carry1;
191                if i + j < u_normalized.len() && sum > u_normalized[i + j] {
192                    q_hat -= 1;
193                    break;
194                } else if i + j < u_normalized.len() && sum < u_normalized[i + j] {
195                    break;
196                }
197            }
198        }
199
200        // Step D4: Multiply and subtract
201        let mut borrow = 0u64;
202        for i in 0..n {
203            let idx = j + i;
204            if idx >= u_normalized.len() {
205                continue;
206            }
207
208            let prod = limb_mul_wide(q_hat, v_normalized[i]);
209            let (diff, borrow_out) = limb_sub_borrow(u_normalized[idx], prod.lo, borrow);
210            u_normalized[idx] = diff;
211            borrow = borrow_out + prod.hi;
212        }
213
214        // Handle borrow from next limb
215        let mut idx = j + n;
216        while borrow > 0 && idx < u_normalized.len() {
217            let (diff, borrow_out) = limb_sub_borrow(u_normalized[idx], 0, borrow);
218            u_normalized[idx] = diff;
219            borrow = borrow_out;
220            idx += 1;
221        }
222
223        // Step D5: Test remainder
224        if borrow > 0 {
225            // Remainder is negative, add back v
226            q_hat -= 1;
227            let mut carry = 0u64;
228            for i in 0..n {
229                let idx = j + i;
230                if idx >= u_normalized.len() {
231                    continue;
232                }
233                let (sum, carry_out) = limb_add_carry(u_normalized[idx], v_normalized[i], carry);
234                u_normalized[idx] = sum;
235                carry = carry_out;
236            }
237        }
238
239        // Step D6: Store quotient digit
240        let q_idx = j;
241        let limb_idx = q_idx / 64;
242        let bit_idx = q_idx % 64;
243        if limb_idx < q_limbs.len() {
244            q_limbs[limb_idx] |= q_hat << bit_idx;
245        }
246    }
247
248    // Step D8: Unnormalize remainder
249    let mut remainder = BigInt::new(max_limbs);
250    let r_limbs = remainder.limbs_mut();
251
252    // Copy remainder and divide by d
253    for i in 0..b_limbs.len() {
254        if i < u_normalized.len() {
255            r_limbs[i] = u_normalized[i];
256        }
257    }
258
259    // Divide remainder by d (unnormalize)
260    if d > 1 {
261        let mut rem = 0u64;
262        for i in (0..r_limbs.len()).rev() {
263            let num = (rem as u128) << 64 | (r_limbs[i] as u128);
264            r_limbs[i] = (num / (d as u128)) as u64;
265            rem = (num % (d as u128)) as u64;
266        }
267    }
268
269    quotient.canonicalize()?;
270    remainder.canonicalize()?;
271
272    Ok((quotient, remainder))
273}
274
275/// Divide two BigInt values, returning only the quotient.
276#[cfg(feature = "alloc")]
277pub fn div(a: &BigInt, b: &BigInt) -> Result<BigInt> {
278    let (q, _) = div_rem(a, b)?;
279    Ok(q)
280}
281
282/// Compute the remainder of dividing two BigInt values.
283#[cfg(feature = "alloc")]
284pub fn rem(a: &BigInt, b: &BigInt) -> Result<BigInt> {
285    let (_, remainder) = div_rem(a, b)?;
286    Ok(remainder)
287}
288
289/// In-place division for dynamic BigInt.
290#[cfg(feature = "alloc")]
291pub fn div_assign(a: &mut BigInt, b: &BigInt) -> Result<()> {
292    let result = div(a, b)?;
293    *a = result;
294    Ok(())
295}
296
297/// In-place remainder for dynamic BigInt.
298#[cfg(feature = "alloc")]
299pub fn rem_assign(a: &mut BigInt, b: &BigInt) -> Result<()> {
300    let result = rem(a, b)?;
301    *a = result;
302    Ok(())
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    fn test_div_by_zero() {
311        let a = BigInt::from_u64(10, 10);
312        let b = BigInt::from_u64(0, 10);
313        assert!(div(&a, &b).is_err());
314    }
315
316    #[test]
317    fn test_div_zero_dividend() {
318        let a = BigInt::from_u64(0, 10);
319        let b = BigInt::from_u64(10, 10);
320        let (q, r) = div_rem(&a, &b).unwrap();
321        assert!(q.is_zero());
322        assert!(r.is_zero());
323    }
324
325    #[test]
326    fn test_div_simple() {
327        let a = BigInt::from_u64(100, 10);
328        let b = BigInt::from_u64(10, 10);
329        let (q, r) = div_rem(&a, &b).unwrap();
330        assert_eq!(q.limbs()[0], 10);
331        assert_eq!(r.limbs()[0], 0);
332    }
333
334    #[test]
335    fn test_div_with_remainder() {
336        let a = BigInt::from_u64(123, 10);
337        let b = BigInt::from_u64(10, 10);
338        let (q, r) = div_rem(&a, &b).unwrap();
339        assert_eq!(q.limbs()[0], 12);
340        assert_eq!(r.limbs()[0], 3);
341    }
342}