1use 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
14impl UintRef {
16 #[inline(always)]
21 pub(crate) const fn div_rem(&mut self, rhs: &mut Self) {
22 let (x, y) = (self, rhs);
23
24 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 let ybits = y.bits();
32 assert!(ybits > 0, "zero divisor");
33 let ywords = ybits.div_ceil(Limb::BITS);
34
35 let yz = y.bits_precision() - ybits;
37 y.unbounded_shl_assign(yz);
38
39 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 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 y.fill(Limb::ZERO);
66 return;
67 }
68 (_, 1) => {
69 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 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 #[allow(clippy::cast_possible_truncation, reason = "TODO")]
90 x.unbounded_shr_assign_by_limbs_vartime((ywords - 1) as u32);
91 }
92
93 #[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 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 let ybits = y.bits();
115 assert!(ybits > 0, "zero divisor");
116 let ywords = ybits.div_ceil(Limb::BITS);
117
118 let yz = y.bits_precision() - ybits;
120 y.unbounded_shl_assign(yz);
121
122 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 Self::rem_wide_shifted((x_lo, x), x_hi, y, ywords);
130
131 y.shr_assign_limb(lshift);
133 }
134
135 #[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 y.fill(Limb::ZERO);
157 return;
158 }
159 (_, 1) => {
160 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 y.shl_assign_limb_vartime(lshift);
178
179 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 let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
186
187 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 y.leading_mut(ysize - 1).copy_from(x.leading(ysize - 1));
201 y.limbs[ysize - 1] = x_hi;
202
203 y.shr_assign_limb_vartime(lshift);
205 }
206
207 #[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 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 x_hi = Self::div_rem_large_shifted::<false>(x, x_hi, y, ywords, reciprocal);
229
230 let limb_div = Choice::from_u32_eq(1, ywords);
232 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 x.limbs[0] = Limb::select(x.limbs[0], Limb(quo2), limb_div);
240 y.limbs[0] = Limb::select(x.limbs[0], Limb(rem2), limb_div);
242
243 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 #[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 let v = reciprocal.reciprocal_3by2(y.limbs[ysize - 2].0, y.limbs[ysize - 1].0);
296
297 while xi > 0 {
298 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 let done = Choice::from_u32_lt(xi as u32, ywords - 1);
307 if VARTIME && done.to_bool_vartime() {
308 break;
309 }
310 quo = word::select(quo, 0, done);
311
312 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 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.saturating_sub(borrow.0 & 1)
339 };
340
341 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 #[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 y.shl_assign_limb_vartime(lshift);
364
365 let mut x_hi = x.shl_assign_limb_vartime(lshift);
367
368 let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
370
371 x_hi = Self::div_rem_large_shifted::<true>(x, x_hi, y, ysize as u32, reciprocal);
373
374 y.leading_mut(ysize - 1).copy_from(x.leading(ysize - 1));
376 y.limbs[ysize - 1] = x_hi;
377
378 y.shr_assign_limb_vartime(lshift);
380 }
381
382 #[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 let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
403 debug_assert!(reciprocal.shift() == 0);
404
405 x_hi = Self::rem_wide_large_shifted::<false>((x_lo, x), x_hi, y, ywords, reciprocal);
407
408 let limb_div = Choice::from_u32_eq(1, ywords);
410 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 y.limbs[0] = Limb::select(x.limbs[0], Limb(rem2), limb_div);
418
419 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 #[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 let v = reciprocal.reciprocal_3by2(y.limbs[ysize - 2].0, y.limbs[ysize - 1].0);
465
466 while xi > 0 {
470 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 let done = Choice::from_u32_lt(xi as u32, ywords - 1);
479 if VARTIME && done.to_bool_vartime() {
480 break;
481 }
482 quo = word::select(quo, 0, done);
483
484 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 {
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 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 #[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 #[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 #[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 #[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 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 #[inline(always)]
608 pub(crate) const fn div_exact(&mut self, rhs: &mut UintRef) -> Choice {
609 let x_prec = self.bits_precision();
610 let y_bits = rhs.bits();
611 assert!(y_bits > 0, "zero divisor");
612
613 let tz = rhs.trailing_zeros();
614 let excess_z = Choice::from_u32_lt(x_prec, tz);
616 let tz = excess_z.select_u32(tz, x_prec);
617
618 rhs.shr_assign(tz);
620
621 let div2s_exact = self.ensure_trailing_zeros(tz).and(excess_z.not());
623 self.unbounded_shr_assign(tz);
624
625 let y = Odd::new_ref_unchecked(rhs);
626 let y_inv = y.invert_mod_limb();
627 let ywords = bitlen::to_limbs(y_bits - tz);
628 let is_exact =
629 Self::div_exact_odd_with_inverse::<false>(self, y, y_inv, ywords).and(div2s_exact);
630
631 rhs.shl_assign(tz);
633
634 is_exact
635 }
636
637 #[inline(always)]
645 pub(crate) const fn div_exact_vartime(&mut self, rhs: &mut UintRef) -> Choice {
646 let x_prec = self.bits_precision();
647 let y_bits = rhs.bits_vartime();
648 assert!(y_bits > 0, "zero divisor");
649
650 let tz = rhs.trailing_zeros_vartime();
651 if tz > x_prec {
652 return Choice::FALSE;
655 }
656 let rhs = rhs.leading_mut(bitlen::to_limbs(y_bits));
658 rhs.unbounded_shr_assign_vartime(tz);
659
660 let div2s_exact = self.ensure_trailing_zeros(tz);
662 self.unbounded_shr_assign_vartime(tz);
663
664 let ywords = bitlen::to_limbs(y_bits - tz);
665 let y = Odd::new_ref_unchecked(rhs.leading(ywords));
666 let y_inv = y.invert_mod_limb();
667 let is_exact =
668 Self::div_exact_odd_with_inverse::<true>(self, y, y_inv, ywords).and(div2s_exact);
669
670 rhs.unbounded_shl_assign_vartime(tz);
672
673 is_exact
674 }
675
676 #[inline(always)]
689 const fn div_exact_odd_with_inverse<const VARTIME: bool>(
690 x: &mut UintRef,
691 y: &Odd<UintRef>,
692 y_inv: Limb,
693 ywords: usize,
694 ) -> Choice {
695 let xc = x.nlimbs();
696 let y = y.as_ref();
697 let yc = y.nlimbs();
698 let mut meta_carry = Limb::ZERO;
699 let mut xi = 0;
700
701 while xi < xc {
702 let y_remain = if yc < (xc - xi) { yc } else { xc - xi };
703 let done = usize_lt(y_remain, ywords);
705 if VARTIME && done.to_bool_vartime() {
706 x.trailing_mut(xi).fill(Limb::ZERO);
708 break;
709 }
710
711 let quo = Limb::select(x.limbs[xi].wrapping_mul(y_inv), Limb::ZERO, done);
713 x.limbs[xi] = quo;
714
715 let (_, mut carry) = quo.widening_mul(y.limbs[0]);
716 let mut sub;
717 let mut borrow = Limb::ZERO;
718 let mut yi = 1;
719
720 while yi < y_remain {
721 (sub, carry) = quo.carrying_mul_add(y.limbs[yi], Limb::ZERO, carry);
722 (x.limbs[xi + yi], borrow) = x.limbs[xi + yi].borrowing_sub(sub, borrow);
723 yi += 1;
724 }
725
726 meta_carry = meta_carry.wrapping_add(carry);
727
728 if xi + yi < xc {
729 (x.limbs[xi + yi], borrow) = x.limbs[xi + yi].borrowing_sub(meta_carry, borrow);
730 meta_carry = borrow.wrapping_neg();
731 } else {
732 meta_carry = meta_carry.wrapping_sub(borrow);
733 }
734
735 xi += 1;
736 }
737
738 meta_carry.is_zero()
739 }
740
741 #[inline(always)]
743 const fn ensure_trailing_zeros(&self, zs: u32) -> Choice {
744 let z_words = (zs >> Limb::LOG2_BITS) as usize;
745 let z_bits = zs & (Limb::BITS - 1);
746 if z_words >= self.nlimbs() {
747 self.is_zero()
748 } else {
749 self.leading(z_words)
750 .is_zero()
751 .and(self.limbs[z_words].restrict_bits(z_bits).is_zero())
752 }
753 }
754}