crypto_bigint/uint/boxed/
div.rs

1//! [`BoxedUint`] division operations.
2
3use crate::{
4    uint::{
5        boxed,
6        div_limb::{div2by1, div3by2},
7    },
8    BoxedUint, CheckedDiv, ConstChoice, ConstantTimeSelect, DivRemLimb, Limb, NonZero, Reciprocal,
9    RemLimb, RemMixed, Wrapping,
10};
11use core::ops::{Div, DivAssign, Rem, RemAssign};
12use subtle::CtOption;
13
14impl BoxedUint {
15    /// Computes `self / rhs` using a pre-made reciprocal,
16    /// returns the quotient (q) and remainder (r).
17    pub fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) {
18        boxed::div_limb::div_rem_limb_with_reciprocal(self, reciprocal)
19    }
20
21    /// Computes `self / rhs`, returns the quotient (q) and remainder (r).
22    pub fn div_rem_limb(&self, rhs: NonZero<Limb>) -> (Self, Limb) {
23        boxed::div_limb::div_rem_limb_with_reciprocal(self, &Reciprocal::new(rhs))
24    }
25
26    /// Computes `self % rhs` using a pre-made reciprocal.
27    #[inline(always)]
28    pub fn rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> Limb {
29        boxed::div_limb::rem_limb_with_reciprocal(self, reciprocal)
30    }
31
32    /// Computes `self % rhs`.
33    #[inline(always)]
34    pub fn rem_limb(&self, rhs: NonZero<Limb>) -> Limb {
35        boxed::div_limb::rem_limb_with_reciprocal(self, &Reciprocal::new(rhs))
36    }
37
38    /// Computes self / rhs, returns the quotient, remainder.
39    pub fn div_rem(&self, rhs: &NonZero<Self>) -> (Self, Self) {
40        // Since `rhs` is nonzero, this should always hold.
41        self.div_rem_unchecked(rhs.as_ref())
42    }
43
44    /// Computes self % rhs, returns the remainder.
45    pub fn rem(&self, rhs: &NonZero<Self>) -> Self {
46        self.div_rem(rhs).1
47    }
48
49    /// Computes self / rhs, returns the quotient, remainder.
50    ///
51    /// Variable-time with respect to `rhs`
52    pub fn div_rem_vartime(&self, rhs: &NonZero<Self>) -> (Self, Self) {
53        let yc = rhs.0.bits_vartime().div_ceil(Limb::BITS) as usize;
54
55        match yc {
56            0 => panic!("zero divisor"),
57            1 => {
58                // Perform limb division
59                let (quo, rem_limb) =
60                    self.div_rem_limb(rhs.0.limbs[0].to_nz().expect("zero divisor"));
61                let mut rem = Self::zero_with_precision(rhs.bits_precision());
62                rem.limbs[0] = rem_limb;
63                (quo, rem)
64            }
65            _ => {
66                let mut quo = self.clone();
67                let mut rem = rhs.0.clone();
68                div_rem_vartime_in_place(&mut quo.limbs, &mut rem.limbs[..yc]);
69                (quo, rem)
70            }
71        }
72    }
73
74    /// Computes self % rhs, returns the remainder.
75    ///
76    /// Variable-time with respect to `rhs`.
77    pub fn rem_vartime(&self, rhs: &NonZero<Self>) -> Self {
78        let yc = rhs.0.bits_vartime().div_ceil(Limb::BITS) as usize;
79
80        match yc {
81            0 => panic!("zero divisor"),
82            1 => {
83                // Perform limb division
84                let rem_limb = self.rem_limb(rhs.0.limbs[0].to_nz().expect("zero divisor"));
85                let mut rem = Self::zero_with_precision(rhs.bits_precision());
86                rem.limbs[0] = rem_limb;
87                rem
88            }
89            _ if yc > self.limbs.len() => {
90                let mut rem = Self::zero_with_precision(rhs.bits_precision());
91                rem.limbs[..self.limbs.len()].copy_from_slice(&self.limbs);
92                rem
93            }
94            _ => {
95                let mut quo = self.clone();
96                let mut rem = rhs.0.clone();
97                div_rem_vartime_in_place(&mut quo.limbs, &mut rem.limbs[..yc]);
98                rem
99            }
100        }
101    }
102
103    /// Wrapped division is just normal division i.e. `self` / `rhs`
104    /// There’s no way wrapping could ever happen.
105    ///
106    /// This function exists, so that all operations are accounted for in the wrapping operations.
107    ///
108    /// Panics if `rhs == 0`.
109    pub fn wrapping_div(&self, rhs: &NonZero<Self>) -> Self {
110        self.div_rem(rhs).0
111    }
112
113    /// Wrapped division is just normal division i.e. `self` / `rhs`
114    ///
115    /// There’s no way wrapping could ever happen.
116    /// This function exists, so that all operations are accounted for in the wrapping operations
117    pub fn wrapping_div_vartime(&self, rhs: &NonZero<Self>) -> Self {
118        self.div_rem_vartime(rhs).0
119    }
120
121    /// Perform checked division, returning a [`CtOption`] which `is_some`
122    /// only if the rhs != 0
123    pub fn checked_div(&self, rhs: &Self) -> CtOption<Self> {
124        let is_nz = rhs.is_nonzero();
125        let nz = NonZero(Self::ct_select(
126            &Self::one_with_precision(self.bits_precision()),
127            rhs,
128            is_nz,
129        ));
130        let q = self.div_rem_unchecked(&nz).0;
131        CtOption::new(q, is_nz)
132    }
133
134    fn div_rem_unchecked(&self, rhs: &Self) -> (Self, Self) {
135        // Based on Section 4.3.1, of The Art of Computer Programming, Volume 2, by Donald E. Knuth.
136        // Further explanation at https://janmr.com/blog/2014/04/basic-multiple-precision-long-division/
137
138        let size = self.limbs.len();
139        assert_eq!(
140            size,
141            rhs.limbs.len(),
142            "the precision of the divisor must match the dividend"
143        );
144
145        // Short circuit for single-word precision
146        if size == 1 {
147            let (quo, rem_limb) = self.div_rem_limb(rhs.limbs[0].to_nz().expect("zero divisor"));
148            let mut rem = Self::zero_with_precision(self.bits_precision());
149            rem.limbs[0] = rem_limb;
150            return (quo, rem);
151        }
152
153        let dbits = rhs.bits();
154        assert!(dbits > 0, "zero divisor");
155        let dwords = dbits.div_ceil(Limb::BITS);
156        let lshift = (Limb::BITS - (dbits % Limb::BITS)) % Limb::BITS;
157
158        // Shift entire divisor such that the high bit is set
159        let mut y = rhs.shl((size as u32) * Limb::BITS - dbits).to_limbs();
160        // Shift the dividend to align the words
161        let (x, mut x_hi) = self.shl_limb(lshift);
162        let mut x = x.to_limbs();
163        let mut xi = size - 1;
164        let mut x_lo = x[size - 1];
165        let mut i;
166        let mut carry;
167
168        let reciprocal = Reciprocal::new(y[size - 1].to_nz().expect("zero divisor"));
169
170        while xi > 0 {
171            // Divide high dividend words by the high divisor word to estimate the quotient word
172            let mut quo = div3by2(x_hi.0, x_lo.0, x[xi - 1].0, &reciprocal, y[size - 2].0);
173
174            // This loop is a no-op once xi is smaller than the number of words in the divisor
175            let done = ConstChoice::from_u32_lt(xi as u32, dwords - 1);
176            quo = done.select_word(quo, 0);
177
178            // Subtract q*divisor from the dividend
179            carry = Limb::ZERO;
180            let mut borrow = Limb::ZERO;
181            let mut tmp;
182            i = 0;
183            while i <= xi {
184                (tmp, carry) = Limb::ZERO.mac(y[size - xi + i - 1], Limb(quo), carry);
185                (x[i], borrow) = x[i].sbb(tmp, borrow);
186                i += 1;
187            }
188            (_, borrow) = x_hi.sbb(carry, borrow);
189
190            // If the subtraction borrowed, then decrement q and add back the divisor
191            // The probability of this being needed is very low, about 2/(Limb::MAX+1)
192            let ct_borrow = ConstChoice::from_word_mask(borrow.0);
193            carry = Limb::ZERO;
194            i = 0;
195            while i <= xi {
196                (x[i], carry) = x[i].adc(
197                    Limb::select(Limb::ZERO, y[size - xi + i - 1], ct_borrow),
198                    carry,
199                );
200                i += 1;
201            }
202            quo = ct_borrow.select_word(quo, quo.saturating_sub(1));
203
204            // Store the quotient within dividend and set x_hi to the current highest word
205            x_hi = Limb::select(x[xi], x_hi, done);
206            x[xi] = Limb::select(Limb(quo), x[xi], done);
207            x_lo = Limb::select(x[xi - 1], x_lo, done);
208            xi -= 1;
209        }
210
211        let limb_div = ConstChoice::from_u32_eq(1, dwords);
212
213        // Calculate quotient and remainder for the case where the divisor is a single word
214        // Note that `div2by1()` will panic if `x_hi >= reciprocal.divisor_normalized`,
215        // but this can only be the case if `limb_div` is falsy,
216        // in which case we discard the result anyway,
217        // so we conditionally set `x_hi` to zero for this branch.
218        let x_hi_adjusted = Limb::select(Limb::ZERO, x_hi, limb_div);
219        let (quo2, rem2) = div2by1(x_hi_adjusted.0, x_lo.0, &reciprocal);
220
221        // Adjust the quotient for single limb division
222        x[0] = Limb::select(x[0], Limb(quo2), limb_div);
223
224        // Copy out the remainder
225        y[0] = Limb::select(x[0], Limb(rem2), limb_div);
226        i = 1;
227        while i < size {
228            y[i] = Limb::select(Limb::ZERO, x[i], ConstChoice::from_u32_lt(i as u32, dwords));
229            y[i] = Limb::select(y[i], x_hi, ConstChoice::from_u32_eq(i as u32, dwords - 1));
230            i += 1;
231        }
232
233        (
234            Self { limbs: x }.shr((dwords - 1) * Limb::BITS),
235            Self { limbs: y }.shr(lshift),
236        )
237    }
238}
239
240impl CheckedDiv for BoxedUint {
241    fn checked_div(&self, rhs: &BoxedUint) -> CtOption<Self> {
242        self.checked_div(rhs)
243    }
244}
245
246impl Div<&NonZero<BoxedUint>> for &BoxedUint {
247    type Output = BoxedUint;
248
249    fn div(self, rhs: &NonZero<BoxedUint>) -> Self::Output {
250        self.wrapping_div(rhs)
251    }
252}
253
254impl Div<&NonZero<BoxedUint>> for BoxedUint {
255    type Output = BoxedUint;
256
257    fn div(self, rhs: &NonZero<BoxedUint>) -> Self::Output {
258        self.wrapping_div(rhs)
259    }
260}
261
262impl Div<NonZero<BoxedUint>> for &BoxedUint {
263    type Output = BoxedUint;
264
265    fn div(self, rhs: NonZero<BoxedUint>) -> Self::Output {
266        self.wrapping_div(&rhs)
267    }
268}
269
270impl Div<NonZero<BoxedUint>> for BoxedUint {
271    type Output = BoxedUint;
272
273    fn div(self, rhs: NonZero<BoxedUint>) -> Self::Output {
274        self.div_rem(&rhs).0
275    }
276}
277
278impl DivAssign<&NonZero<BoxedUint>> for BoxedUint {
279    fn div_assign(&mut self, rhs: &NonZero<BoxedUint>) {
280        *self = self.wrapping_div(rhs);
281    }
282}
283
284impl DivAssign<NonZero<BoxedUint>> for BoxedUint {
285    fn div_assign(&mut self, rhs: NonZero<BoxedUint>) {
286        *self = self.wrapping_div(&rhs);
287    }
288}
289
290impl Div<NonZero<BoxedUint>> for Wrapping<BoxedUint> {
291    type Output = Wrapping<BoxedUint>;
292
293    fn div(self, rhs: NonZero<BoxedUint>) -> Self::Output {
294        Wrapping(self.0 / rhs)
295    }
296}
297
298impl Div<NonZero<BoxedUint>> for &Wrapping<BoxedUint> {
299    type Output = Wrapping<BoxedUint>;
300
301    fn div(self, rhs: NonZero<BoxedUint>) -> Self::Output {
302        Wrapping(self.0.wrapping_div(&rhs))
303    }
304}
305
306impl Div<&NonZero<BoxedUint>> for &Wrapping<BoxedUint> {
307    type Output = Wrapping<BoxedUint>;
308
309    fn div(self, rhs: &NonZero<BoxedUint>) -> Self::Output {
310        Wrapping(self.0.wrapping_div(rhs))
311    }
312}
313
314impl Div<&NonZero<BoxedUint>> for Wrapping<BoxedUint> {
315    type Output = Wrapping<BoxedUint>;
316
317    fn div(self, rhs: &NonZero<BoxedUint>) -> Self::Output {
318        Wrapping(self.0.wrapping_div(rhs))
319    }
320}
321
322impl DivAssign<&NonZero<BoxedUint>> for Wrapping<BoxedUint> {
323    fn div_assign(&mut self, rhs: &NonZero<BoxedUint>) {
324        *self = Wrapping(&self.0 / rhs);
325    }
326}
327
328impl DivAssign<NonZero<BoxedUint>> for Wrapping<BoxedUint> {
329    fn div_assign(&mut self, rhs: NonZero<BoxedUint>) {
330        *self /= &rhs;
331    }
332}
333
334impl Rem<&NonZero<BoxedUint>> for &BoxedUint {
335    type Output = BoxedUint;
336
337    #[inline]
338    fn rem(self, rhs: &NonZero<BoxedUint>) -> Self::Output {
339        self.rem(rhs)
340    }
341}
342
343impl Rem<&NonZero<BoxedUint>> for BoxedUint {
344    type Output = BoxedUint;
345
346    #[inline]
347    fn rem(self, rhs: &NonZero<BoxedUint>) -> Self::Output {
348        Self::rem(&self, rhs)
349    }
350}
351
352impl Rem<NonZero<BoxedUint>> for &BoxedUint {
353    type Output = BoxedUint;
354
355    #[inline]
356    fn rem(self, rhs: NonZero<BoxedUint>) -> Self::Output {
357        self.rem(&rhs)
358    }
359}
360
361impl Rem<NonZero<BoxedUint>> for BoxedUint {
362    type Output = BoxedUint;
363
364    #[inline]
365    fn rem(self, rhs: NonZero<BoxedUint>) -> Self::Output {
366        self.rem(&rhs)
367    }
368}
369
370impl RemAssign<&NonZero<BoxedUint>> for BoxedUint {
371    fn rem_assign(&mut self, rhs: &NonZero<BoxedUint>) {
372        *self = Self::rem(self, rhs)
373    }
374}
375
376impl RemAssign<NonZero<BoxedUint>> for BoxedUint {
377    fn rem_assign(&mut self, rhs: NonZero<BoxedUint>) {
378        *self = Self::rem(self, &rhs)
379    }
380}
381
382impl DivRemLimb for BoxedUint {
383    fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) {
384        Self::div_rem_limb_with_reciprocal(self, reciprocal)
385    }
386}
387
388impl RemLimb for BoxedUint {
389    fn rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> Limb {
390        Self::rem_limb_with_reciprocal(self, reciprocal)
391    }
392}
393
394impl RemMixed<BoxedUint> for BoxedUint {
395    fn rem_mixed(&self, reductor: &NonZero<BoxedUint>) -> BoxedUint {
396        Self::div_rem_vartime(self, reductor).1
397    }
398}
399
400/// Computes `limbs << shift` inplace, where `0 <= shift < Limb::BITS`, returning the carry.
401fn shl_limb_vartime(limbs: &mut [Limb], shift: u32) -> Limb {
402    if shift == 0 {
403        return Limb::ZERO;
404    }
405
406    let lshift = shift;
407    let rshift = Limb::BITS - shift;
408    let limbs_num = limbs.len();
409
410    let carry = limbs[limbs_num - 1] >> rshift;
411    for i in (1..limbs_num).rev() {
412        limbs[i] = (limbs[i] << lshift) | (limbs[i - 1] >> rshift);
413    }
414    limbs[0] <<= lshift;
415
416    carry
417}
418
419/// Computes `limbs >> shift` inplace, where `0 <= shift < Limb::BITS`.
420fn shr_limb_vartime(limbs: &mut [Limb], shift: u32) {
421    if shift == 0 {
422        return;
423    }
424
425    let lshift = Limb::BITS - shift;
426    let rshift = shift;
427
428    let limbs_num = limbs.len();
429
430    for i in 0..limbs_num - 1 {
431        limbs[i] = (limbs[i] >> rshift) | (limbs[i + 1] << lshift);
432    }
433    limbs[limbs_num - 1] >>= rshift;
434}
435
436/// Computes `x` / `y`, returning the quotient in `x` and the remainder in `y`.
437///
438/// This function operates in variable-time. It will panic if the divisor is zero
439/// or the leading word of the divisor is zero.
440pub(crate) fn div_rem_vartime_in_place(x: &mut [Limb], y: &mut [Limb]) {
441    let xc = x.len();
442    let yc = y.len();
443    assert!(
444        yc > 0 && y[yc - 1].0 != 0,
445        "divisor must have a non-zero leading word"
446    );
447
448    if xc == 0 {
449        // If the quotient is empty, set the remainder to zero and return.
450        y.fill(Limb::ZERO);
451        return;
452    } else if yc > xc {
453        // Divisor is greater than dividend. Return zero and the dividend as the
454        // quotient and remainder
455        y[..xc].copy_from_slice(&x[..xc]);
456        y[xc..].fill(Limb::ZERO);
457        x.fill(Limb::ZERO);
458        return;
459    }
460
461    let lshift = y[yc - 1].leading_zeros();
462
463    // Shift divisor such that it has no leading zeros
464    // This means that div2by1 requires no extra shifts, and ensures that the high word >= b/2
465    shl_limb_vartime(y, lshift);
466
467    // Shift the dividend to match
468    let mut x_hi = shl_limb_vartime(x, lshift);
469
470    let reciprocal = Reciprocal::new(y[yc - 1].to_nz().expect("zero divisor"));
471
472    for xi in (yc - 1..xc).rev() {
473        // Divide high dividend words by the high divisor word to estimate the quotient word
474        let mut quo = div3by2(x_hi.0, x[xi].0, x[xi - 1].0, &reciprocal, y[yc - 2].0);
475
476        // Subtract q*divisor from the dividend
477        let borrow = {
478            let mut carry = Limb::ZERO;
479            let mut borrow = Limb::ZERO;
480            let mut tmp;
481            for i in 0..yc {
482                (tmp, carry) = Limb::ZERO.mac(y[i], Limb(quo), carry);
483                (x[xi + i + 1 - yc], borrow) = x[xi + i + 1 - yc].sbb(tmp, borrow);
484            }
485            (_, borrow) = x_hi.sbb(carry, borrow);
486            borrow
487        };
488
489        // If the subtraction borrowed, then decrement q and add back the divisor
490        // The probability of this being needed is very low, about 2/(Limb::MAX+1)
491        quo = {
492            let ct_borrow = ConstChoice::from_word_mask(borrow.0);
493            let mut carry = Limb::ZERO;
494            for i in 0..yc {
495                (x[xi + i + 1 - yc], carry) =
496                    x[xi + i + 1 - yc].adc(Limb::select(Limb::ZERO, y[i], ct_borrow), carry);
497            }
498            ct_borrow.select_word(quo, quo.wrapping_sub(1))
499        };
500
501        // Store the quotient within dividend and set x_hi to the current highest word
502        x_hi = x[xi];
503        x[xi] = Limb(quo);
504    }
505
506    // Copy the remainder to divisor
507    y[..yc - 1].copy_from_slice(&x[..yc - 1]);
508    y[yc - 1] = x_hi;
509
510    // Unshift the remainder from the earlier adjustment
511    shr_limb_vartime(y, lshift);
512
513    // Shift the quotient to the low limbs within dividend
514    // let x_size = xc - yc + 1;
515    x.copy_within(yc - 1..xc, 0);
516    x[xc - yc + 1..].fill(Limb::ZERO);
517}
518
519#[cfg(test)]
520mod tests {
521    use super::{BoxedUint, Limb, NonZero};
522
523    #[test]
524    fn rem() {
525        let n = BoxedUint::from(0xFFEECCBBAA99887766u128);
526        let p = NonZero::new(BoxedUint::from(997u128)).unwrap();
527        assert_eq!(BoxedUint::from(648u128), n.rem(&p));
528    }
529
530    #[test]
531    fn rem_vartime() {
532        let n = BoxedUint::from(0xFFEECCBBAA99887766u128);
533        let p = NonZero::new(BoxedUint::from(997u128)).unwrap();
534        assert_eq!(BoxedUint::from(648u128), n.rem_vartime(&p));
535    }
536
537    #[test]
538    fn rem_limb() {
539        let n = BoxedUint::from(0xFFEECCBBAA99887766u128);
540        let pl = NonZero::new(Limb(997)).unwrap();
541        let p = NonZero::new(BoxedUint::from(997u128)).unwrap();
542        assert_eq!(n.rem(&p).limbs[0], n.rem_limb(pl));
543    }
544}