1use super::UintRef;
7use crate::{
8 Choice, Limb, NonZero,
9 div_limb::{Reciprocal, div2by1, div3by2},
10 primitives::u32_min,
11 word,
12};
13
14impl UintRef {
16 #[inline(always)]
21 #[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
22 pub(crate) const fn div_rem(&mut self, rhs: &mut Self) {
23 let (x, y) = (self, rhs);
24
25 if y.nlimbs() == 1 {
27 y.limbs[0] = x.div_rem_limb(y.limbs[0].to_nz().expect_copied("zero divisor"));
28 return;
29 }
30
31 let ybits = y.bits();
33 assert!(ybits > 0, "zero divisor");
34 let ywords = ybits.div_ceil(Limb::BITS);
35
36 let yz = y.bits_precision() - ybits;
38 y.unbounded_shl_assign(yz);
39
40 let lshift = yz % Limb::BITS;
42 let x_hi = x.shl_assign_limb(lshift);
43
44 Self::div_rem_shifted(x, x_hi, y, ywords);
45
46 x.unbounded_shr_assign_by_limbs(ywords - 1);
47 y.shr_assign_limb(lshift);
48 }
49
50 #[allow(clippy::cast_possible_truncation)]
58 pub(crate) const fn div_rem_vartime(&mut self, rhs: &mut Self) {
59 let (x, y) = (self, rhs);
60 let xsize = x.nlimbs();
61 let ywords = y.bits_vartime().div_ceil(Limb::BITS) as usize;
62
63 match (xsize, ywords) {
64 (_, 0) => panic!("zero divisor"),
65 (0, _) => {
66 y.fill(Limb::ZERO);
68 return;
69 }
70 (_, 1) => {
71 let rem_limb = x.div_rem_limb(y.limbs[0].to_nz().expect_copied("zero divisor"));
73 y.fill(Limb::ZERO);
74 y.limbs[0] = rem_limb;
75 return;
76 }
77 _ if ywords > xsize => {
78 y.leading_mut(xsize).copy_from(x.leading(xsize));
81 y.trailing_mut(xsize).fill(Limb::ZERO);
82 x.fill(Limb::ZERO);
83 return;
84 }
85 _ => (),
86 }
87
88 x.div_rem_large_vartime(y.leading_mut(ywords));
89
90 x.unbounded_shr_assign_by_limbs_vartime((ywords - 1) as u32);
92 }
93
94 #[inline(always)]
102 #[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
103 pub(crate) const fn rem_wide(x_lower_upper: (&mut Self, &mut Self), rhs: &mut Self) {
104 let (x_lo, x) = x_lower_upper;
105 let y = rhs;
106
107 if y.nlimbs() == 1 {
109 let reciprocal = Reciprocal::new(y.limbs[0].to_nz().expect_copied("zero divisor"));
110 let carry = x.rem_limb_with_reciprocal(&reciprocal, Limb::ZERO);
111 y.limbs[0] = x_lo.rem_limb_with_reciprocal(&reciprocal, carry);
112 return;
113 }
114
115 let ybits = y.bits();
117 assert!(ybits > 0, "zero divisor");
118 let ywords = ybits.div_ceil(Limb::BITS);
119
120 let yz = y.bits_precision() - ybits;
122 y.unbounded_shl_assign(yz);
123
124 let lshift = yz % Limb::BITS;
126 let x_lo_carry = x_lo.shl_assign_limb(lshift);
127 let x_hi = x.shl_assign_limb(lshift);
128 x.limbs[0] = x.limbs[0].bitor(x_lo_carry);
129
130 Self::rem_wide_shifted((x_lo, x), x_hi, y, ywords);
132
133 y.shr_assign_limb(lshift);
135 }
136
137 #[inline(always)]
148 #[allow(clippy::cast_possible_truncation)]
149 pub(crate) const fn rem_wide_vartime(x_lower_upper: (&mut Self, &mut Self), rhs: &mut Self) {
150 let (x_lo, x) = x_lower_upper;
151 let xsize = x.nlimbs();
152 let ysize = rhs.bits_vartime().div_ceil(Limb::BITS) as usize;
153 let y = rhs.leading_mut(ysize);
154
155 match (xsize, ysize) {
156 (_, 0) => panic!("zero divisor"),
157 (0, _) => {
158 y.fill(Limb::ZERO);
160 return;
161 }
162 (_, 1) => {
163 let reciprocal = Reciprocal::new(y.limbs[0].to_nz().expect_copied("zero divisor"));
165 y.fill(Limb::ZERO);
166 let carry = x.rem_limb_with_reciprocal(&reciprocal, Limb::ZERO);
167 y.limbs[0] = x_lo.rem_limb_with_reciprocal(&reciprocal, carry);
168 return;
169 }
170 _ if ysize > xsize => {
171 panic!("divisor too large");
172 }
173 _ => (),
174 }
175
176 let lshift = y.limbs[ysize - 1].leading_zeros();
177
178 y.shl_assign_limb_vartime(lshift);
181
182 let x_lo_carry = x_lo.shl_assign_limb_vartime(lshift);
184 let mut x_hi = x.shl_assign_limb_vartime(lshift);
185 x.limbs[0] = x.limbs[0].bitor(x_lo_carry);
186
187 let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
189
190 x_hi = Self::rem_wide_large_shifted(
192 (x_lo, x),
193 x_hi,
194 y,
195 ysize as u32,
196 reciprocal,
197 Choice::TRUE,
198 );
199
200 y.leading_mut(ysize - 1).copy_from(x.leading(ysize - 1));
202 y.limbs[ysize - 1] = x_hi;
203
204 y.shr_assign_limb_vartime(lshift);
206 }
207
208 #[inline(always)]
216 #[allow(clippy::cast_possible_truncation)]
217 pub(crate) const fn div_rem_shifted(&mut self, mut x_hi: Limb, y: &mut Self, ywords: u32) {
218 let x = self;
219
220 let reciprocal = Reciprocal::new(
222 y.limbs[y.nlimbs() - 1]
223 .to_nz()
224 .expect_copied("zero divisor"),
225 );
226 debug_assert!(reciprocal.shift() == 0);
227
228 x_hi = Self::div_rem_large_shifted(x, x_hi, y, ywords, reciprocal, Choice::FALSE);
230
231 let limb_div = Choice::from_u32_eq(1, ywords);
233 let x_hi_adjusted = Limb::select(Limb::ZERO, x_hi, limb_div);
237 let (quo2, rem2) = div2by1(x_hi_adjusted.0, x.limbs[0].0, &reciprocal);
238
239 x.limbs[0] = Limb::select(x.limbs[0], Limb(quo2), limb_div);
241 y.limbs[0] = Limb::select(x.limbs[0], Limb(rem2), limb_div);
243
244 let min = if x.nlimbs() < y.nlimbs() {
248 x.nlimbs()
249 } else {
250 y.nlimbs()
251 };
252 let hi_pos = u32_min(x.nlimbs() as u32, ywords - 1);
253 let mut i = 1;
254 while i < min {
255 y.limbs[i] = Limb::select(
256 Limb::ZERO,
257 x.limbs[i],
258 Choice::from_u32_lt(i as u32, ywords),
259 );
260 y.limbs[i] = Limb::select(y.limbs[i], x_hi, Choice::from_u32_eq(i as u32, hi_pos));
261 i += 1;
262 }
263 while i < y.nlimbs() {
264 y.limbs[i] = Limb::select(Limb::ZERO, x_hi, Choice::from_u32_eq(i as u32, hi_pos));
265 i += 1;
266 }
267 }
268
269 #[inline(always)]
278 #[allow(clippy::cast_possible_truncation)]
279 pub(crate) const fn div_rem_large_shifted(
280 &mut self,
281 mut x_hi: Limb,
282 y: &Self,
283 ywords: u32,
284 reciprocal: Reciprocal,
285 vartime: Choice,
286 ) -> Limb {
287 let x = self;
288 let xsize = x.nlimbs();
289 let ysize = y.nlimbs();
290
291 let mut xi = xsize - 1;
292 let mut x_xi = x.limbs[xi];
293 let mut i;
294 let mut carry;
295
296 while xi > 0 {
297 let mut quo = div3by2(
299 x_hi.0,
300 x_xi.0,
301 x.limbs[xi - 1].0,
302 &reciprocal,
303 y.limbs[ysize - 2].0,
304 );
305
306 let done = Choice::from_u32_lt(xi as u32, ywords - 1);
308 if vartime.and(done).to_bool_vartime() {
309 break;
310 }
311 quo = word::select(quo, 0, done);
312
313 let borrow = {
315 carry = Limb::ZERO;
316 let mut borrow = Limb::ZERO;
317 let mut tmp;
318 i = (xi + 1).saturating_sub(ysize);
319 while i <= xi {
320 (tmp, carry) =
321 y.limbs[ysize + i - xi - 1].carrying_mul_add(Limb(quo), carry, Limb::ZERO);
322 (x.limbs[i], borrow) = x.limbs[i].borrowing_sub(tmp, borrow);
323 i += 1;
324 }
325 (_, borrow) = x_hi.borrowing_sub(carry, borrow);
326 borrow
327 };
328
329 quo = {
332 let ct_borrow = word::choice_from_mask(borrow.0);
333 carry = Limb::ZERO;
334 i = (xi + 1).saturating_sub(ysize);
335 while i <= xi {
336 (x.limbs[i], carry) = x.limbs[i].carrying_add(
337 Limb::select(Limb::ZERO, y.limbs[ysize + i - xi - 1], ct_borrow),
338 carry,
339 );
340 i += 1;
341 }
342 word::select(quo, quo.saturating_sub(1), ct_borrow)
343 };
344
345 x_hi = Limb::select(x.limbs[xi], x_hi, done);
347 x.limbs[xi] = Limb::select(Limb(quo), x.limbs[xi], done);
348 x_xi = Limb::select(x.limbs[xi - 1], x_xi, done);
349 xi -= 1;
350 }
351
352 x_hi
353 }
354
355 #[inline(always)]
358 #[allow(clippy::cast_possible_truncation)]
359 pub(crate) const fn div_rem_large_vartime(&mut self, rhs: &mut Self) {
360 let (x, y) = (self, rhs);
361 let ysize = y.nlimbs();
362 debug_assert!(ysize > 1);
363 let lshift = y.limbs[ysize - 1].leading_zeros();
364
365 y.shl_assign_limb_vartime(lshift);
368
369 let mut x_hi = x.shl_assign_limb_vartime(lshift);
371
372 let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
374
375 x_hi = Self::div_rem_large_shifted(x, x_hi, y, ysize as u32, reciprocal, Choice::TRUE);
377
378 y.leading_mut(ysize - 1).copy_from(x.leading(ysize - 1));
380 y.limbs[ysize - 1] = x_hi;
381
382 y.shr_assign_limb_vartime(lshift);
384 }
385
386 #[inline(always)]
395 #[allow(clippy::cast_possible_truncation)]
396 const fn rem_wide_shifted(
397 x: (&mut Self, &mut Self),
398 mut x_hi: Limb,
399 y: &mut Self,
400 ywords: u32,
401 ) {
402 let (x_lo, x) = x;
403 let ysize = y.nlimbs();
404
405 let reciprocal = Reciprocal::new(y.limbs[ysize - 1].to_nz().expect_copied("zero divisor"));
407 debug_assert!(reciprocal.shift() == 0);
408
409 x_hi = Self::rem_wide_large_shifted((x_lo, x), x_hi, y, ywords, reciprocal, Choice::FALSE);
411
412 let limb_div = Choice::from_u32_eq(1, ywords);
414 let x_hi_adjusted = Limb::select(Limb::ZERO, x_hi, limb_div);
418 let (_, rem2) = div2by1(x_hi_adjusted.0, x.limbs[0].0, &reciprocal);
419
420 y.limbs[0] = Limb::select(x.limbs[0], Limb(rem2), limb_div);
422
423 let mut i = 1;
425 while i < ysize {
426 y.limbs[i] = Limb::select(
427 Limb::ZERO,
428 x.limbs[i],
429 Choice::from_u32_lt(i as u32, ywords),
430 );
431 y.limbs[i] = Limb::select(y.limbs[i], x_hi, Choice::from_u32_eq(i as u32, ywords - 1));
432 i += 1;
433 }
434 }
435
436 #[inline(always)]
444 #[allow(clippy::cast_possible_truncation)]
445 const fn rem_wide_large_shifted(
446 x: (&Self, &mut Self),
447 mut x_hi: Limb,
448 y: &Self,
449 ywords: u32,
450 reciprocal: Reciprocal,
451 vartime: Choice,
452 ) -> Limb {
453 assert!(
454 y.nlimbs() <= x.1.nlimbs(),
455 "invalid input sizes for rem_wide_large_shifted"
456 );
457
458 let (x_lo, x) = x;
459 let xsize = x.nlimbs();
460 let ysize = y.nlimbs();
461 let mut extra_limbs = x_lo.nlimbs();
462
463 let mut xi = xsize - 1;
464 let mut x_xi = x.limbs[xi];
465 let mut i;
466 let mut carry;
467
468 while xi > 0 {
472 let mut quo = div3by2(
474 x_hi.0,
475 x_xi.0,
476 x.limbs[xi - 1].0,
477 &reciprocal,
478 y.limbs[ysize - 2].0,
479 );
480
481 let done = Choice::from_u32_lt(xi as u32, ywords - 1);
483 if vartime.and(done).to_bool_vartime() {
484 break;
485 }
486 quo = word::select(quo, 0, done);
487
488 let borrow = {
490 carry = Limb::ZERO;
491 let mut borrow = Limb::ZERO;
492 let mut tmp;
493 i = (xi + 1).saturating_sub(ysize);
494 while i <= xi {
495 (tmp, carry) =
496 y.limbs[ysize + i - xi - 1].carrying_mul_add(Limb(quo), carry, Limb::ZERO);
497 (x.limbs[i], borrow) = x.limbs[i].borrowing_sub(tmp, borrow);
498 i += 1;
499 }
500 (_, borrow) = x_hi.borrowing_sub(carry, borrow);
501 borrow
502 };
503
504 {
507 let ct_borrow = word::choice_from_mask(borrow.0);
508 carry = Limb::ZERO;
509 i = (xi + 1).saturating_sub(ysize);
510 while i <= xi {
511 (x.limbs[i], carry) = x.limbs[i].carrying_add(
512 Limb::select(Limb::ZERO, y.limbs[ysize + i - xi - 1], ct_borrow),
513 carry,
514 );
515 i += 1;
516 }
517 }
518
519 if extra_limbs > 0 {
521 x_hi = x.limbs[xi];
522 x_xi = x.limbs[xi - 1];
523 extra_limbs -= 1;
524 i = xi;
525 while i > 0 {
526 x.limbs[i] = x.limbs[i - 1];
527 i -= 1;
528 }
529 x.limbs[0] = x_lo.limbs[extra_limbs];
530 } else {
531 x_hi = Limb::select(x.limbs[xi], x_hi, done);
532 x_xi = Limb::select(x.limbs[xi - 1], x_xi, done);
533 xi -= 1;
534 }
535 }
536
537 x_hi
538 }
539
540 #[inline(always)]
543 pub(crate) const fn div_rem_limb(&mut self, rhs: NonZero<Limb>) -> Limb {
544 self.div_rem_limb_with_reciprocal(&Reciprocal::new(rhs))
545 }
546
547 #[inline(always)]
550 pub(crate) const fn div_rem_limb_with_reciprocal(&mut self, reciprocal: &Reciprocal) -> Limb {
551 let hi = self.shl_assign_limb(reciprocal.shift());
552 self.div_rem_limb_with_reciprocal_shifted(hi, reciprocal)
553 }
554
555 #[inline(always)]
558 pub(crate) const fn div_rem_limb_with_reciprocal_shifted(
559 &mut self,
560 mut hi: Limb,
561 reciprocal: &Reciprocal,
562 ) -> Limb {
563 let mut j = self.limbs.len();
564 while j > 0 {
565 j -= 1;
566 (self.limbs[j].0, hi.0) = div2by1(hi.0, self.limbs[j].0, reciprocal);
567 }
568 hi.shr(reciprocal.shift())
569 }
570
571 #[inline(always)]
573 pub(crate) const fn rem_limb(&self, rhs: NonZero<Limb>) -> Limb {
574 self.rem_limb_with_reciprocal(&Reciprocal::new(rhs), Limb::ZERO)
575 }
576
577 pub(crate) const fn rem_limb_with_reciprocal(
579 &self,
580 reciprocal: &Reciprocal,
581 carry: Limb,
582 ) -> Limb {
583 let nlimbs = self.nlimbs();
584 if nlimbs == 0 {
585 return carry;
586 }
587 let lshift = reciprocal.shift();
588 let nz = Choice::from_u32_nz(lshift);
589 let rshift = nz.select_u32(0, Limb::BITS - lshift);
590 let mut hi = (carry.0 << lshift) | word::select(0, self.limbs[nlimbs - 1].0 >> rshift, nz);
591 let mut lo;
592 let mut j = nlimbs;
593 while j > 1 {
594 j -= 1;
595 lo = self.limbs[j].0 << lshift;
596 lo |= word::select(0, self.limbs[j - 1].0 >> rshift, nz);
597 (_, hi) = div2by1(hi, lo, reciprocal);
598 }
599 (_, hi) = div2by1(hi, self.limbs[0].0 << lshift, reciprocal);
600 Limb(hi >> lshift)
601 }
602}