Skip to main content

crypto_bigint/uint/ref_type/
div.rs

1//! In-place integer division
2//!
3//! Based on Section 4.3.1, of The Art of Computer Programming, Volume 2, by Donald E. Knuth.
4//! Further explanation at <https://janmr.com/blog/2014/04/basic-multiple-precision-long-division/>
5
6use super::UintRef;
7use crate::{
8    Choice, Limb, NonZero, Odd, bitlen,
9    div_limb::{Reciprocal, div2by1, div3by2},
10    primitives::{u32_min, usize_lt},
11    word,
12};
13
14// TODO(tarcieri): make `rhs` into `NonZeroUintRef` then make currently panicking functions `pub`
15impl UintRef {
16    /// Computes `self` / `rhs`, returning the quotient in `self` and the remainder in `rhs`.
17    ///
18    /// # Panics
19    /// If the divisor is zero.
20    #[inline(always)]
21    pub(crate) const fn div_rem(&mut self, rhs: &mut Self) {
22        let (x, y) = (self, rhs);
23
24        // Short circuit for single-word divisor
25        if y.nlimbs() == 1 {
26            y.limbs[0] = x.div_rem_limb(y.limbs[0].to_nz().expect_copied("zero divisor"));
27            return;
28        }
29
30        // Compute the size of the divisor
31        let ybits = y.bits();
32        assert!(ybits > 0, "zero divisor");
33        let ywords = bitlen::to_limbs_u32(ybits);
34
35        // Shift the entire divisor such that the high bit is set
36        let yz = y.bits_precision() - ybits;
37        y.unbounded_shl_assign(yz);
38
39        // Shift the dividend to align the words
40        let lshift = yz & (Limb::BITS - 1);
41        let x_hi = x.shl_assign_limb(lshift);
42
43        Self::div_rem_shifted(x, x_hi, y, ywords);
44
45        x.unbounded_shr_assign_by_limbs(ywords - 1);
46        y.shr_assign_limb(lshift);
47    }
48
49    /// Computes `self` / `rhs`, returning the quotient in `self` and the remainder in `rhs`.
50    ///
51    /// This function operates in variable-time with respect to `rhs`. For a fixed divisor,
52    /// it operates in constant-time
53    ///
54    /// # Panics
55    /// If the divisor is zero.
56    pub(crate) const fn div_rem_vartime(&mut self, rhs: &mut Self) {
57        let (x, y) = (self, rhs);
58        let xsize = x.nlimbs();
59        let ywords = bitlen::to_limbs(y.bits_vartime());
60
61        match (xsize, ywords) {
62            (_, 0) => panic!("zero divisor"),
63            (0, _) => {
64                // If the quotient is empty, set the remainder to zero and return.
65                y.fill(Limb::ZERO);
66                return;
67            }
68            (_, 1) => {
69                // Perform limb division
70                let rem_limb = x.div_rem_limb(y.limbs[0].to_nz().expect_copied("zero divisor"));
71                y.fill(Limb::ZERO);
72                y.limbs[0] = rem_limb;
73                return;
74            }
75            _ if ywords > xsize => {
76                // Divisor is greater than dividend. Return zero and the dividend as the
77                // quotient and remainder
78                y.leading_mut(xsize).copy_from(x.leading(xsize));
79                y.trailing_mut(xsize).fill(Limb::ZERO);
80                x.fill(Limb::ZERO);
81                return;
82            }
83            _ => (),
84        }
85
86        x.div_rem_large_vartime(y.leading_mut(ywords));
87
88        // Shift the quotient to the low limbs within dividend
89        #[allow(clippy::cast_possible_truncation, reason = "TODO")]
90        x.unbounded_shr_assign_by_limbs_vartime((ywords - 1) as u32);
91    }
92
93    /// Computes `x_lower_upper` % `rhs`, returning the remainder in `rhs`.
94    ///
95    /// The `x_lower_upper` tuple represents a wide integer. The size of `x_lower_upper.1` must be
96    /// at least as large as `rhs`. `x_lower_upper` is left in an indeterminate state.
97    ///
98    /// # Panics
99    /// If the divisor is zero.
100    #[inline(always)]
101    pub(crate) const fn rem_wide(x_lower_upper: (&mut Self, &mut Self), rhs: &mut Self) {
102        let (x_lo, x) = x_lower_upper;
103        let y = rhs;
104
105        // Short circuit for single-word divisor
106        if y.nlimbs() == 1 {
107            let reciprocal = Reciprocal::new(y.limbs[0].to_nz().expect_copied("zero divisor"));
108            let carry = x.rem_limb_with_reciprocal(&reciprocal, Limb::ZERO);
109            y.limbs[0] = x_lo.rem_limb_with_reciprocal(&reciprocal, carry);
110            return;
111        }
112
113        // Compute the size of the divisor
114        let ybits = y.bits();
115        assert!(ybits > 0, "zero divisor");
116        let ywords = bitlen::to_limbs_u32(ybits);
117
118        // Shift the entire divisor such that the high bit is set
119        let yz = y.bits_precision() - ybits;
120        y.unbounded_shl_assign(yz);
121
122        // Shift the dividend to align the words
123        let lshift = yz & (Limb::BITS - 1);
124        let x_lo_carry = x_lo.shl_assign_limb(lshift);
125        let x_hi = x.shl_assign_limb(lshift);
126        x.limbs[0] = x.limbs[0].bitor(x_lo_carry);
127
128        // Perform the core division algorithm
129        Self::rem_wide_shifted((x_lo, x), x_hi, y, ywords);
130
131        // Unshift the remainder from the earlier adjustment
132        y.shr_assign_limb(lshift);
133    }
134
135    /// Computes `x_lower_upper` % `rhs`, returning the remainder in `rhs`.
136    ///
137    /// This function operates in variable-time with respect to `rhs`. For a fixed divisor,
138    /// it operates in constant-time.
139    ///
140    /// The `x_lower_upper` tuple represents a wide integer. The size of `x_lower_upper.1`
141    /// must be at least as large as `rhs`. `x_lower_upper` is left in an indeterminate state.
142    ///
143    /// # Panics
144    /// If the divisor is zero.
145    #[inline(always)]
146    pub(crate) const fn rem_wide_vartime(x_lower_upper: (&mut Self, &mut Self), rhs: &mut Self) {
147        let (x_lo, x) = x_lower_upper;
148        let xsize = x.nlimbs();
149        let ysize = bitlen::to_limbs(rhs.bits_vartime());
150        let y = rhs.leading_mut(ysize);
151
152        match (xsize, ysize) {
153            (_, 0) => panic!("zero divisor"),
154            (0, _) => {
155                // If the quotient is empty, set the remainder to zero and return.
156                y.fill(Limb::ZERO);
157                return;
158            }
159            (_, 1) => {
160                // Short circuit for single-word divisor
161                let reciprocal = Reciprocal::new(y.limbs[0].to_nz().expect_copied("zero divisor"));
162                y.fill(Limb::ZERO);
163                let carry = x.rem_limb_with_reciprocal(&reciprocal, Limb::ZERO);
164                y.limbs[0] = x_lo.rem_limb_with_reciprocal(&reciprocal, carry);
165                return;
166            }
167            _ if ysize > xsize => {
168                panic!("divisor too large");
169            }
170            _ => (),
171        }
172
173        let lshift = y.limbs[ysize - 1].leading_zeros();
174
175        // Shift divisor such that it has no leading zeros
176        // This means that div2by1 requires no extra shifts, and ensures that the high word >= b/2
177        y.shl_assign_limb_vartime(lshift);
178
179        // Shift the dividend to align the words
180        let x_lo_carry = x_lo.shl_assign_limb_vartime(lshift);
181        let mut x_hi = x.shl_assign_limb_vartime(lshift);
182        x.limbs[0] = x.limbs[0].bitor(x_lo_carry);
183
184        // Calculate a reciprocal from the highest word of the divisor
185        let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
186
187        // Perform the core division algorithm
188        x_hi = Self::rem_wide_large_shifted::<true>(
189            (x_lo, x),
190            x_hi,
191            y,
192            #[allow(clippy::cast_possible_truncation, reason = "TODO")]
193            {
194                ysize as u32
195            },
196            reciprocal,
197        );
198
199        // Copy the remainder to the divisor
200        y.leading_mut(ysize - 1).copy_from(x.leading(ysize - 1));
201        y.limbs[ysize - 1] = x_hi;
202
203        // Unshift the remainder from the earlier adjustment
204        y.shr_assign_limb_vartime(lshift);
205    }
206
207    /// Perform in-place division (`self` / `y`) for a pre-shifted dividend and divisor.
208    ///
209    /// The dividend and divisor must be left-shifted such that the high bit of the divisor
210    /// is set, and `x_hi` holds the top bits of the dividend.
211    ///
212    /// The quotient is returned in `self` and the remainder in `y`, but these values require
213    /// additional correction. This is left to the caller for performance reasons.
214    #[inline(always)]
215    #[allow(clippy::cast_possible_truncation)]
216    pub(crate) const fn div_rem_shifted(&mut self, mut x_hi: Limb, y: &mut Self, ywords: u32) {
217        let x = self;
218
219        // Calculate a reciprocal from the highest word of the divisor
220        let reciprocal = Reciprocal::new(
221            y.limbs[y.nlimbs() - 1]
222                .to_nz()
223                .expect_copied("zero divisor"),
224        );
225        debug_assert!(reciprocal.shift() == 0);
226
227        // Perform the core division algorithm
228        x_hi = Self::div_rem_large_shifted::<false>(x, x_hi, y, ywords, reciprocal);
229
230        // Calculate quotient and remainder for the case where the divisor is a single word.
231        let limb_div = Choice::from_u32_eq(1, ywords);
232        // Note that `div2by1()` will panic if `x_hi >= reciprocal.divisor_normalized`,
233        // but this can only be the case if `limb_div` is falsy, in which case we discard
234        // the result anyway, so we conditionally set `x_hi` to zero for this branch.
235        let x_hi_adjusted = Limb::select(Limb::ZERO, x_hi, limb_div);
236        let (quo2, rem2) = div2by1(x.limbs[0].0, x_hi_adjusted.0, &reciprocal);
237
238        // Adjust the quotient for single limb division
239        x.limbs[0] = Limb::select(x.limbs[0], Limb(quo2), limb_div);
240        // Copy out the low limb of the remainder
241        y.limbs[0] = Limb::select(x.limbs[0], Limb(rem2), limb_div);
242
243        // Adjust the remainder, copying `x_hi` to the appropriate position and clearing
244        // any extra limbs.
245        // Note: branching only based on the size of the operands, which is not secret.
246        let min = if x.nlimbs() < y.nlimbs() {
247            x.nlimbs()
248        } else {
249            y.nlimbs()
250        };
251        let hi_pos = u32_min(x.nlimbs() as u32, ywords - 1);
252        let mut i = 1;
253        while i < min {
254            y.limbs[i] = Limb::select(
255                Limb::ZERO,
256                x.limbs[i],
257                Choice::from_u32_lt(i as u32, ywords),
258            );
259            y.limbs[i] = Limb::select(y.limbs[i], x_hi, Choice::from_u32_eq(i as u32, hi_pos));
260            i += 1;
261        }
262        while i < y.nlimbs() {
263            y.limbs[i] = Limb::select(Limb::ZERO, x_hi, Choice::from_u32_eq(i as u32, hi_pos));
264            i += 1;
265        }
266    }
267
268    /// Computes `self` / `y` for a "large" divisor (>1 limbs), returning the quotient and
269    /// the remainder in `self`.
270    ///
271    /// While the divisor may only be a single limb, additional corrections to the result are
272    /// required in this case.
273    ///
274    /// The dividend and divisor must be left-shifted such that the high bit of the divisor
275    /// is set, and `x_hi` holds the top bits of the dividend.
276    #[inline(always)]
277    #[allow(clippy::cast_possible_truncation)]
278    pub(crate) const fn div_rem_large_shifted<const VARTIME: bool>(
279        &mut self,
280        mut x_hi: Limb,
281        y: &Self,
282        ywords: u32,
283        reciprocal: Reciprocal,
284    ) -> Limb {
285        let x = self;
286        let xsize = x.nlimbs();
287        let ysize = y.nlimbs();
288
289        let mut xi = xsize - 1;
290        let mut x_xi = x.limbs[xi];
291        let mut i;
292        let mut carry;
293
294        // Compute the adjusted reciprocal
295        let v = reciprocal.reciprocal_3by2(y.limbs[ysize - 2].0, y.limbs[ysize - 1].0);
296
297        while xi > 0 {
298            // Divide high dividend words by the high divisor word to estimate the quotient word
299            let (mut quo, _) = div3by2(
300                (x.limbs[xi - 1].0, x_xi.0, x_hi.0),
301                (y.limbs[ysize - 2].0, y.limbs[ysize - 1].0),
302                v,
303            );
304
305            // This loop is a no-op once xi is smaller than the number of words in the divisor
306            let done = Choice::from_u32_lt(xi as u32, ywords - 1);
307            if VARTIME && done.to_bool() {
308                break;
309            }
310            quo = word::select(quo, 0, done);
311
312            // Subtract q*divisor from the dividend
313            let borrow = {
314                carry = Limb::ZERO;
315                let mut borrow = Limb::ZERO;
316                let mut tmp;
317                i = (xi + 1).saturating_sub(ysize);
318                while i <= xi {
319                    (tmp, carry) =
320                        y.limbs[ysize + i - xi - 1].carrying_mul_add(Limb(quo), carry, Limb::ZERO);
321                    (x.limbs[i], borrow) = x.limbs[i].borrowing_sub(tmp, borrow);
322                    i += 1;
323                }
324                (_, borrow) = x_hi.borrowing_sub(carry, borrow);
325                borrow
326            };
327
328            // If the subtraction borrowed, then decrement quo and add back the divisor
329            // The probability of this being needed is very low, about 2/(Limb::MAX+1)
330            quo = {
331                carry = Limb::ZERO;
332                i = (xi + 1).saturating_sub(ysize);
333                while i <= xi {
334                    (x.limbs[i], carry) =
335                        x.limbs[i].carrying_add(y.limbs[ysize + i - xi - 1].bitand(borrow), carry);
336                    i += 1;
337                }
338                quo.wrapping_sub(borrow.0 & word::choice_to_mask(word::choice_from_nz(quo)) & 1)
339            };
340
341            // Store the quotient within dividend and set x_hi to the current highest word
342            x_hi = Limb::select(x.limbs[xi], x_hi, done);
343            x.limbs[xi] = Limb::select(Limb(quo), x.limbs[xi], done);
344            x_xi = Limb::select(x.limbs[xi - 1], x_xi, done);
345            xi -= 1;
346        }
347
348        x_hi
349    }
350
351    /// Perform in-place variable-time division for a "large" divisor (>1 limbs). The
352    /// quotient is returned in `self` and the remainder in `rhs`.
353    #[inline(always)]
354    #[allow(clippy::cast_possible_truncation)]
355    pub(crate) const fn div_rem_large_vartime(&mut self, rhs: &mut Self) {
356        let (x, y) = (self, rhs);
357        let ysize = y.nlimbs();
358        debug_assert!(ysize > 1);
359        let lshift = y.limbs[ysize - 1].leading_zeros();
360
361        // Shift divisor such that it has no leading zeros
362        // This means that div2by1 requires no extra shifts, and ensures that the high word >= b/2
363        y.shl_assign_limb_vartime(lshift);
364
365        // Shift the dividend to match
366        let mut x_hi = x.shl_assign_limb_vartime(lshift);
367
368        // Calculate a reciprocal from the highest word of the divisor
369        let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
370
371        // Perform the core division algorithm
372        x_hi = Self::div_rem_large_shifted::<true>(x, x_hi, y, ysize as u32, reciprocal);
373
374        // Copy the remainder to divisor
375        y.leading_mut(ysize - 1).copy_from(x.leading(ysize - 1));
376        y.limbs[ysize - 1] = x_hi;
377
378        // Unshift the remainder from the earlier adjustment
379        y.shr_assign_limb_vartime(lshift);
380    }
381
382    /// Perform in-place division (`x` / `y`) for a pre-shifted dividend and divisor,
383    /// tracking only the remainder.
384    ///
385    /// The dividend and divisor must be left-shifted such that the high bit of the divisor
386    /// is set, and `x_hi` holds the top bits of the dividend.
387    ///
388    /// The shifted remainder is returned in `y`, and must be unshifted by the caller.
389    /// `x` is left in an indeterminate state.
390    #[inline(always)]
391    #[allow(clippy::cast_possible_truncation)]
392    const fn rem_wide_shifted(
393        x: (&mut Self, &mut Self),
394        mut x_hi: Limb,
395        y: &mut Self,
396        ywords: u32,
397    ) {
398        let (x_lo, x) = x;
399        let ysize = y.nlimbs();
400
401        // Calculate a reciprocal from the highest word of the divisor
402        let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
403        debug_assert!(reciprocal.shift() == 0);
404
405        // Perform the core division algorithm
406        x_hi = Self::rem_wide_large_shifted::<false>((x_lo, x), x_hi, y, ywords, reciprocal);
407
408        // Calculate remainder for the case where the divisor is a single word.
409        let limb_div = Choice::from_u32_eq(1, ywords);
410        // Note that `div2by1()` will panic if `x_hi >= reciprocal.divisor_normalized`,
411        // but this can only be the case if `limb_div` is falsy, in which case we discard
412        // the result anyway, so we conditionally set `x_hi` to zero for this branch.
413        let x_hi_adjusted = Limb::select(Limb::ZERO, x_hi, limb_div);
414        let (_, rem2) = div2by1(x.limbs[0].0, x_hi_adjusted.0, &reciprocal);
415
416        // Copy out the low limb of the remainder
417        y.limbs[0] = Limb::select(x.limbs[0], Limb(rem2), limb_div);
418
419        // Copy the remainder to divisor
420        let mut i = 1;
421        while i < ysize {
422            y.limbs[i] = Limb::select(
423                Limb::ZERO,
424                x.limbs[i],
425                Choice::from_u32_lt(i as u32, ywords),
426            );
427            y.limbs[i] = Limb::select(y.limbs[i], x_hi, Choice::from_u32_eq(i as u32, ywords - 1));
428            i += 1;
429        }
430    }
431
432    /// Computes `x % y` for a "large" divisor (>1 limbs), returning the remainder in `x.1`.
433    ///
434    /// While the divisor may only be a single limb, additional corrections to the result are
435    /// required in this case.
436    ///
437    /// The dividend and divisor must be left-shifted such that the high bit of the divisor
438    /// is set, and `x_hi` holds the top bits of the dividend.
439    #[inline(always)]
440    #[allow(clippy::cast_possible_truncation)]
441    const fn rem_wide_large_shifted<const VARTIME: bool>(
442        x: (&Self, &mut Self),
443        mut x_hi: Limb,
444        y: &Self,
445        ywords: u32,
446        reciprocal: Reciprocal,
447    ) -> Limb {
448        assert!(
449            y.nlimbs() <= x.1.nlimbs(),
450            "invalid input sizes for rem_wide_large_shifted"
451        );
452
453        let (x_lo, x) = x;
454        let xsize = x.nlimbs();
455        let ysize = y.nlimbs();
456        let mut extra_limbs = x_lo.nlimbs();
457
458        let mut xi = xsize - 1;
459        let mut x_xi = x.limbs[xi];
460        let mut i;
461        let mut carry;
462
463        // Compute the adjusted reciprocal
464        let v = reciprocal.reciprocal_3by2(y.limbs[ysize - 2].0, y.limbs[ysize - 1].0);
465
466        // We proceed similarly to `div_rem_large_shifted()` applied to the high half of
467        // the dividend, fetching the limbs from the lower part as we go.
468
469        while xi > 0 {
470            // Divide high dividend words by the high divisor word to estimate the quotient word
471            let (mut quo, _) = div3by2(
472                (x.limbs[xi - 1].0, x_xi.0, x_hi.0),
473                (y.limbs[ysize - 2].0, y.limbs[ysize - 1].0),
474                v,
475            );
476
477            // This loop is a no-op once xi is smaller than the number of words in the divisor
478            let done = Choice::from_u32_lt(xi as u32, ywords - 1);
479            if VARTIME && done.to_bool() {
480                break;
481            }
482            quo = word::select(quo, 0, done);
483
484            // Subtract q*divisor from the dividend
485            let borrow = {
486                carry = Limb::ZERO;
487                let mut borrow = Limb::ZERO;
488                let mut tmp;
489                i = (xi + 1).saturating_sub(ysize);
490                while i <= xi {
491                    (tmp, carry) =
492                        y.limbs[ysize + i - xi - 1].carrying_mul_add(Limb(quo), carry, Limb::ZERO);
493                    (x.limbs[i], borrow) = x.limbs[i].borrowing_sub(tmp, borrow);
494                    i += 1;
495                }
496                (_, borrow) = x_hi.borrowing_sub(carry, borrow);
497                borrow
498            };
499
500            // If the subtraction borrowed, then add back the divisor
501            // The probability of this being needed is very low, about 2/(Limb::MAX+1)
502            {
503                carry = Limb::ZERO;
504                i = (xi + 1).saturating_sub(ysize);
505                while i <= xi {
506                    (x.limbs[i], carry) =
507                        x.limbs[i].carrying_add(y.limbs[ysize + i - xi - 1].bitand(borrow), carry);
508                    i += 1;
509                }
510            }
511
512            // If we have lower limbs remaining, shift the dividend words one word left
513            if extra_limbs > 0 {
514                x_hi = x.limbs[xi];
515                x_xi = x.limbs[xi - 1];
516                extra_limbs -= 1;
517                i = xi;
518                while i > 0 {
519                    x.limbs[i] = x.limbs[i - 1];
520                    i -= 1;
521                }
522                x.limbs[0] = x_lo.limbs[extra_limbs];
523            } else {
524                x_hi = Limb::select(x.limbs[xi], x_hi, done);
525                x_xi = Limb::select(x.limbs[xi - 1], x_xi, done);
526                xi -= 1;
527            }
528        }
529
530        x_hi
531    }
532
533    /// Divides `self` by the divisor encoded in the `reciprocal`, setting `self`
534    /// to the quotient and returning the remainder.
535    #[inline(always)]
536    pub(crate) const fn div_rem_limb(&mut self, rhs: NonZero<Limb>) -> Limb {
537        self.div_rem_limb_with_reciprocal(&Reciprocal::new(rhs))
538    }
539
540    /// Divides `self` by the divisor encoded in the `reciprocal`, setting `self`
541    /// to the quotient and returning the remainder.
542    #[inline(always)]
543    pub(crate) const fn div_rem_limb_with_reciprocal(&mut self, reciprocal: &Reciprocal) -> Limb {
544        let hi = self.shl_assign_limb(reciprocal.shift());
545        self.div_rem_limb_with_reciprocal_shifted(hi, reciprocal)
546    }
547
548    /// Divides `self` by the divisor encoded in the `reciprocal`, setting `self`
549    /// to the quotient and returning the remainder.
550    #[inline(always)]
551    pub(crate) const fn div_rem_limb_with_reciprocal_shifted(
552        &mut self,
553        mut hi: Limb,
554        reciprocal: &Reciprocal,
555    ) -> Limb {
556        let mut j = self.limbs.len();
557        while j > 0 {
558            j -= 1;
559            (self.limbs[j].0, hi.0) = div2by1(self.limbs[j].0, hi.0, reciprocal);
560        }
561        hi.shr(reciprocal.shift())
562    }
563
564    /// Divides `self` by the divisor encoded in the `reciprocal`, returning the remainder.
565    #[inline(always)]
566    pub(crate) const fn rem_limb(&self, rhs: NonZero<Limb>) -> Limb {
567        self.rem_limb_with_reciprocal(&Reciprocal::new(rhs), Limb::ZERO)
568    }
569
570    /// Divides `self` by the divisor encoded in the `reciprocal`, and returns the remainder.
571    pub(crate) const fn rem_limb_with_reciprocal(
572        &self,
573        reciprocal: &Reciprocal,
574        carry: Limb,
575    ) -> Limb {
576        let nlimbs = self.nlimbs();
577        if nlimbs == 0 {
578            return carry;
579        }
580        let lshift = reciprocal.shift();
581        let lshift_nz = Choice::from_u32_nz(lshift);
582        let lo_mask = Limb(word::choice_to_mask(lshift_nz));
583        let rshift = lshift_nz.select_u32(0, Limb::BITS - lshift);
584        let mut hi = carry
585            .shl(lshift)
586            .bitor(self.limbs[nlimbs - 1].bitand(lo_mask).shr(rshift));
587        let mut lo;
588        let mut j = nlimbs;
589        while j > 1 {
590            j -= 1;
591            lo = self.limbs[j]
592                .shl(lshift)
593                .bitor(self.limbs[j - 1].bitand(lo_mask).shr(rshift));
594            (_, hi.0) = div2by1(lo.0, hi.0, reciprocal);
595        }
596        (_, hi.0) = div2by1(self.limbs[0].shl(lshift).0, hi.0, reciprocal);
597        hi.shr(lshift)
598    }
599
600    /// Exactly divides `self` by `rhs`, returning `Choice::FALSE` if `self` is not divisible by `rhs`.
601    ///
602    /// If the division is not exact then `self` is left in an indeterminate state.
603    /// The divisor `rhs` is right-shifted to produce an odd integer.
604    ///
605    /// # Panics
606    /// If the divisor is zero.
607    #[inline(always)]
608    pub(crate) const fn div_exact(&mut self, rhs: &mut UintRef) -> Choice {
609        let y_bits = rhs.bits();
610        assert!(y_bits > 0, "zero divisor");
611
612        let x_tz = self.trailing_zeros();
613        let x_zero = Choice::from_u32_eq(x_tz, self.bits_precision());
614        let y_tz = rhs.trailing_zeros();
615
616        // Shift the divisor such that it is odd
617        rhs.shr_assign(y_tz);
618
619        // Check that the dividend evenly divides by 2^y+tz, and shift it to match the divisor
620        let div2s_exact = Choice::from_u32_le(y_tz, x_tz).or(x_zero);
621        self.unbounded_shr_assign(y_tz);
622
623        let y = Odd::new_ref_unchecked(rhs);
624        let y_inv = y.invert_mod_limb();
625        let ywords = bitlen::to_limbs(y_bits - y_tz);
626        let is_exact =
627            Self::div_exact_odd_with_inverse::<false>(self, y, y_inv, ywords).and(div2s_exact);
628
629        // Restore the divisor
630        rhs.shl_assign(y_tz);
631
632        is_exact
633    }
634
635    /// Exactly divides `self` by `rhs`, returning `Choice::FALSE` if `self` is not divisible by `rhs`.
636    ///
637    /// The quotient is left in `self`, but is not guaranteed to be in a usable state unless
638    /// the return value is `Choice::TRUE`.
639    ///
640    /// # Panics
641    /// If the divisor is zero.
642    #[inline(always)]
643    pub(crate) const fn div_exact_vartime(&mut self, rhs: &mut UintRef) -> Choice {
644        let y_bits = rhs.bits_vartime();
645        assert!(y_bits > 0, "zero divisor");
646
647        let x_tz = self.trailing_zeros();
648        let x_zero = Choice::from_u32_eq(x_tz, self.bits_precision());
649        let y_tz = rhs.trailing_zeros_vartime();
650
651        // Shift the divisor to be odd
652        rhs.unbounded_shr_assign_vartime(y_tz);
653
654        // Check that the dividend evenly divides by 2^y_tz, and shift it to match the divisor
655        let div2s_exact = Choice::from_u32_le(y_tz, x_tz).or(x_zero);
656        self.unbounded_shr_assign_vartime(y_tz);
657
658        let ywords = bitlen::to_limbs(y_bits - y_tz);
659        let y = Odd::new_ref_unchecked(rhs.leading_mut(ywords));
660        let y_inv = y.invert_mod_limb();
661        let is_exact =
662            Self::div_exact_odd_with_inverse::<true>(self, y, y_inv, ywords).and(div2s_exact);
663
664        // Restore the divisor
665        rhs.unbounded_shl_assign_vartime(y_tz);
666
667        is_exact
668    }
669
670    /// Exactly divides `x` by `y`, returning `Choice::FALSE` if `x` is not divisible by `y`.
671    ///
672    /// The quotient is left in `self`, but is not guaranteed to be in a usable state unless
673    /// the return value is `Choice::TRUE`.
674    ///
675    /// This method performs a Hensel division, subtracting from the least significant bits
676    /// and calculating an LSB quotient and remainder `(q,r) s.t. x = qy + r•2^N`. When the remainder
677    /// is zero, the calculated quotient corresponds to the traditional division quotient.
678    ///
679    /// For constant-time operation, this acts as if the divisor `y` is as small as one limb,
680    /// performing loops without updates to the quotient for larger divisors when vartime operation
681    /// is not specified.
682    #[inline(always)]
683    const fn div_exact_odd_with_inverse<const VARTIME: bool>(
684        x: &mut UintRef,
685        y: &Odd<UintRef>,
686        y_inv: Limb,
687        ywords: usize,
688    ) -> Choice {
689        let xc = x.nlimbs();
690        let y = y.as_ref();
691        let yc = y.nlimbs();
692        let mut meta_carry = Limb::ZERO;
693        let mut zero_hi = Choice::TRUE;
694        let mut xi = 0;
695
696        while xi < xc {
697            let y_remain = if yc < (xc - xi) { yc } else { xc - xi };
698            // This loop is a no-op once there are fewer words remaining than the size of the divisor
699            let done = usize_lt(y_remain, ywords);
700            if VARTIME && done.to_bool() {
701                zero_hi = x.trailing(xi).is_zero();
702                break;
703            }
704
705            // Compute the quotient limb that will clear the low dividend limb
706            let quo = Limb::select(x.limbs[xi].wrapping_mul(y_inv), x.limbs[xi], done);
707            x.limbs[xi] = quo;
708
709            if !VARTIME {
710                zero_hi = zero_hi.and(Limb::select(Limb::ZERO, quo, done).is_zero());
711            }
712
713            let (_, mut carry) = quo.widening_mul(y.limbs[0]);
714            let mut sub;
715            let mut borrow = Limb::ZERO;
716            let mut yi = 1;
717
718            while yi < y_remain {
719                (sub, carry) = quo.carrying_mul_add(y.limbs[yi], Limb::ZERO, carry);
720                (x.limbs[xi + yi], borrow) = x.limbs[xi + yi].borrowing_sub(sub, borrow);
721                yi += 1;
722            }
723
724            meta_carry = meta_carry.wrapping_add(carry);
725
726            if xi + yi < xc {
727                (x.limbs[xi + yi], borrow) = x.limbs[xi + yi].borrowing_sub(meta_carry, borrow);
728                meta_carry = borrow.wrapping_neg();
729            } else {
730                meta_carry = meta_carry.wrapping_sub(borrow);
731            }
732
733            xi += 1;
734        }
735
736        meta_carry.is_zero().and(zero_hi)
737    }
738}
739
740#[cfg(test)]
741mod tests {
742    use crate::{U64, U128, U192, Uint};
743
744    #[test]
745    fn div_exact_inexact() {
746        fn check<const L: usize, const R: usize>(lhs: Uint<L>, rhs: Uint<R>, exact: bool) {
747            let (mut q, mut r) = (lhs, rhs);
748            let actual = q.as_mut_uint_ref().div_exact(r.as_mut_uint_ref()).to_bool();
749            assert_eq!(actual, exact, "{lhs} / {rhs}: exact={actual}");
750            let (mut q, mut r) = (lhs, rhs);
751            let actual = q
752                .as_mut_uint_ref()
753                .div_exact_vartime(r.as_mut_uint_ref())
754                .to_bool();
755            assert_eq!(actual, exact, "{lhs} / {rhs}: exact={actual}");
756        }
757
758        check(
759            U64::from_be_hex("0000000000000010"),
760            U64::from_be_hex("0000000000000010"),
761            true,
762        );
763        check(
764            U64::from_be_hex("0000000000000010"),
765            U128::from_be_hex("00000000000000000000000000000001"),
766            true,
767        );
768        check(
769            U64::from_be_hex("0000000000000000"),
770            U128::from_be_hex("10000000000000000000000000000000"),
771            true,
772        );
773        check(
774            U64::from_be_hex("0000000000000001"),
775            U64::from_be_hex("0000000000000010"),
776            false,
777        );
778        check(
779            U64::from_be_hex("0000000000000001"),
780            U128::from_be_hex("00000000000000100000000000000000"),
781            false,
782        );
783        check(
784            U192::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000"),
785            U192::from_be_hex("000000000000000100000000000000003ACEDC010F13471D"),
786            false,
787        );
788    }
789}