1#[cfg(all(
19 target_arch = "aarch64",
20 target_endian = "little",
21 target_pointer_width = "64",
22 any(
23 target_vendor = "apple",
24 target_os = "linux",
25 target_os = "android",
26 target_os = "windows",
27 target_os = "freebsd",
28 target_os = "netbsd",
29 target_os = "openbsd",
30 target_os = "fuchsia"
31 ),
32 not(miri)
33))]
34#[path = "pow/aarch64.rs"]
35mod kernels;
36#[cfg(not(all(
37 target_arch = "aarch64",
38 target_endian = "little",
39 target_pointer_width = "64",
40 any(
41 target_vendor = "apple",
42 target_os = "linux",
43 target_os = "android",
44 target_os = "windows",
45 target_os = "freebsd",
46 target_os = "netbsd",
47 target_os = "openbsd",
48 target_os = "fuchsia"
49 ),
50 not(miri)
51)))]
52#[path = "pow/portable.rs"]
53mod kernels;
54pub(super) mod low;
55pub(crate) mod montgomery;
56pub(super) mod window;
57
58use super::add::{SMALL, read_small, write_small};
59use super::div::by_zero;
60use super::div::limbs::{bdiv_q, binvert_limb, rshift};
61use super::mul::limbs::{add_1, add_n, mul, mul_itch, sqr, sqr_itch, sub_1, sub_n};
62use super::mul::{Operand, Product, STACK_LIMBS, on_heap, on_stack, to_limbs};
63use super::{ByteOrder, Number};
64use core::mem::MaybeUninit;
65use core::ptr;
66use low::{Low, Low2, LowN, mullo, mullo_itch};
67use montgomery::{Mont1, Mont2, MontN, Montgomery, even, lshift, rem, rem_itch};
68use window::{power, power_in_registers, window};
69
70impl Number {
71 pub fn pow(&self, exp: u32) -> Number {
98 let order = self.byte_order;
99 if exp == 0 {
100 return small_number(order, 1, false);
101 }
102 let negative = self.negative && exp & 1 == 1;
103 if self.value.len() <= SMALL {
104 let x = unsafe { read_small(&self.value, order) };
106 if x == 0 {
107 return small_number(order, 0, false);
108 }
109 if let Some(p) = small_power(x, exp) {
110 return small_number(order, p, negative);
111 }
112 let z = x.trailing_zeros();
113 let o = x >> z;
114 if o == 1 || u64::from(128 - o.leading_zeros()) * u64::from(exp) <= 512 {
115 return register_power(
116 [o as u64, (o >> 64) as u64],
117 z as usize,
118 exp,
119 negative,
120 order,
121 );
122 }
123 }
124 let x = Operand::of(self);
125 if x.significant == 0 {
126 return small_number(order, 0, false);
127 }
128 power_of(x, exp, negative, order)
129 }
130
131 #[track_caller]
167 pub fn pow_mod(&self, exp: &Number, modulus: &Number) -> Number {
168 let m = Operand::of(modulus);
169 if m.significant == 0 {
170 by_zero::<true>();
171 }
172 let e = Operand::of(exp);
173 if exp.negative && e.significant != 0 {
174 negative_exponent();
175 }
176 let order = self.byte_order;
177 if self.value.len() <= SMALL && exp.value.len() <= SMALL && modulus.value.len() <= SMALL {
178 let (b, e, m) = unsafe {
180 (
181 read_small(&self.value, order),
182 read_small(&exp.value, exp.byte_order),
183 read_small(&modulus.value, modulus.byte_order),
184 )
185 };
186 let mut r = small_pow_mod(b, e, m);
187 if self.negative && e & 1 == 1 && r != 0 {
188 r = m - r;
189 }
190 return small_number(order, r, false);
191 }
192 let b = Operand::of(self);
193 pow_mod_of(b, self.negative, e, m, order)
194 }
195}
196
197#[inline(never)]
200fn small_pow_mod(b: u128, e: u128, m: u128) -> u128 {
201 if m == 1 {
202 return 0;
203 }
204 if e == 0 {
205 return 1;
206 }
207 if b == 0 {
208 return 0;
209 }
210 let el = [e as u64, (e >> 64) as u64];
211 let ebits = 128 - e.leading_zeros() as usize;
212 let z = m.trailing_zeros();
213 let odd = m >> z;
214 let bl = [b as u64, (b >> 64) as u64];
215 let bn = if bl[1] != 0 { 2 } else { 1 };
216 let r1 = if odd == 1 {
218 0
219 } else {
220 let ol = [odd as u64, (odd >> 64) as u64];
221 let mut s = [0u64; 16];
222 unsafe {
226 if ol[1] == 0 {
227 let ring = Mont1::new(ol[0]);
228 let mut x = 0;
229 rem(&mut x, bl.as_ptr(), bn, 1, ol.as_ptr(), 1, s.as_mut_ptr());
230 let y = power_in_registers(&ring, x, el.as_ptr(), ebits);
231 u128::from(ring.plain(y))
232 } else {
233 let ring = Mont2::new(ol);
234 let mut x = [0; 2];
235 rem(
236 x.as_mut_ptr(),
237 bl.as_ptr(),
238 bn,
239 2,
240 ol.as_ptr(),
241 2,
242 s.as_mut_ptr(),
243 );
244 let y = power_in_registers(&ring, x, el.as_ptr(), ebits);
246 let y = ring.plain(y);
247 u128::from(y[0]) | (u128::from(y[1]) << 64)
248 }
249 }
250 };
251 if z == 0 {
252 return r1;
253 }
254 let mask = (1u128 << z) - 1;
256 let v = b.trailing_zeros();
257 let r2 = if v > 0
258 && u128::from(v)
259 .checked_mul(e)
260 .is_none_or(|p| p >= u128::from(z))
261 {
262 0
263 } else {
264 unsafe { power_in_registers(&Low2, b, el.as_ptr(), ebits) & mask }
266 };
267 if odd == 1 {
268 return r2;
269 }
270 let mut inv = u128::from(binvert_limb(odd as u64));
272 inv = inv.wrapping_mul(2u128.wrapping_sub(odd.wrapping_mul(inv)));
273 let x = r2.wrapping_sub(r1).wrapping_mul(inv) & mask;
274 r1 + odd * x
275}
276
277#[cold]
279#[inline(never)]
280#[track_caller]
281fn negative_exponent() -> ! {
282 panic!("negative exponent")
283}
284
285#[inline]
290fn small_power(x: u128, exp: u32) -> Option<u128> {
291 let z = x.trailing_zeros();
292 let o = x >> z;
293 let bits = 128 - o.leading_zeros();
294 if u64::from(bits + z) * u64::from(exp) > 128 {
295 return None;
296 }
297 let mut p = o;
298 for i in (0..31 - exp.leading_zeros()).rev() {
299 p *= p;
300 if exp >> i & 1 == 1 {
301 p *= o;
302 }
303 }
304 Some(p << (z * exp))
305}
306
307fn small_number(order: ByteOrder, p: u128, negative: bool) -> Number {
310 let len = 16 - (p | 1).leading_zeros() as usize / 8;
311 let mut v = Vec::with_capacity(len);
312 unsafe {
315 write_small(v.as_mut_ptr(), order, p as u64, (p >> 64) as u64, len);
316 v.set_len(len);
317 }
318 Number {
319 value: v,
320 negative: negative && p != 0,
321 byte_order: order,
322 }
323}
324
325unsafe fn zeros_and_bits(x: Operand) -> (usize, usize) {
334 let l = x.significant;
335 unsafe {
338 let byte = |i: usize| {
339 if x.big_endian {
340 *x.ptr.add(x.len - 1 - i)
341 } else {
342 *x.ptr.add(i)
343 }
344 };
345 let mut i = 0;
346 while byte(i) == 0 {
347 i += 1;
348 }
349 let zeros = 8 * i + byte(i).trailing_zeros() as usize;
350 (zeros, 8 * l - byte(l - 1).leading_zeros() as usize)
351 }
352}
353
354fn power_limbs(bits: usize, exp: u32) -> usize {
361 let limbs = (bits as u128 * u128::from(exp)).div_ceil(64) + 1;
362 match usize::try_from(limbs) {
363 Ok(limbs) if limbs <= isize::MAX as usize / 32 => limbs,
364 _ => too_long(),
365 }
366}
367
368#[cold]
370#[inline(never)]
371fn too_long() -> ! {
372 panic!("the power is too long to allocate")
373}
374
375#[inline(never)]
377fn power_of(x: Operand, exp: u32, negative: bool, order: ByteOrder) -> Number {
378 let xn = x.significant.div_ceil(8);
379 let (z, bits) = unsafe { zeros_and_bits(x) };
381 let odd_bits = bits - z;
382 if odd_bits == 1 || odd_bits <= 128 && odd_bits as u64 * u64::from(exp) <= 512 {
385 return unsafe { short_power(x, z, exp, negative, order) };
387 }
388 let on = odd_bits.div_ceil(64);
389 let rn = power_limbs(odd_bits, exp);
390 if rn > STACK_LIMBS {
391 let mut v = Vec::<Pair>::new();
394 if v.try_reserve_exact(rn + 1).is_err() {
395 too_long();
396 }
397 }
398 let mut scratch = 0;
405 if rn >= 112 || on >= 24 {
406 let mut len = on;
407 for i in (0..31 - exp.leading_zeros()).rev() {
408 scratch = scratch.max(sqr_itch(len));
409 len = (2 * len).min(rn);
410 if exp >> i & 1 == 1 {
411 scratch = scratch.max(mul_itch(len.max(on), len.min(on)));
412 len = (len + on).min(rn);
413 }
414 }
415 }
416 let need = even(xn) + even(on) + 2 * even(rn + 1) + scratch;
418 let compute = |s: *mut MaybeUninit<u64>| {
419 unsafe { power_in(s.cast(), x, xn, z, on, exp, rn, scratch, negative, order) }
421 };
422 if need <= SHORT_LIMBS {
423 let mut s = [PAIR; SHORT_LIMBS / 2];
424 compute(s.as_mut_ptr().cast())
425 } else if need <= STACK_LIMBS {
426 on_stack(compute)
427 } else {
428 on_heap_or_panic(need, compute)
429 }
430}
431
432const SHORT_LIMBS: usize = 96;
436
437#[derive(Clone, Copy)]
439#[repr(C, align(16))]
440struct Pair([MaybeUninit<u64>; 2]);
441
442const PAIR: Pair = Pair([MaybeUninit::uninit(); 2]);
444
445#[cold]
449#[inline(never)]
450fn on_heap_or_panic<R>(need: usize, f: impl FnOnce(*mut MaybeUninit<u64>) -> R) -> R {
451 let mut s = Vec::<Pair>::new();
452 if s.try_reserve_exact(need.div_ceil(2)).is_err() {
453 too_long();
454 }
455 f(s.as_mut_ptr().cast())
456}
457
458#[inline(never)]
466unsafe fn short_power(x: Operand, z: usize, exp: u32, negative: bool, order: ByteOrder) -> Number {
467 let (zl, zb) = (z / 64, (z % 64) as u32);
469 let l = |i: usize| unsafe { limb_at(x, zl + i) };
471 let (l0, l1, l2) = (l(0), l(1), l(2));
472 let o = if zb == 0 {
473 [l0, l1]
474 } else {
475 [
476 (l0 >> zb) | (l1 << (64 - zb)),
477 (l1 >> zb) | (l2 << (64 - zb)),
478 ]
479 };
480 register_power(o, z, exp, negative, order)
481}
482
483#[inline(never)]
486fn register_power(o: [u64; 2], z: usize, exp: u32, negative: bool, order: ByteOrder) -> Number {
487 let bits = 128
488 - if o[1] != 0 {
489 o[1].leading_zeros()
490 } else {
491 64 + o[0].leading_zeros()
492 };
493 let shift = z as u128 * u128::from(exp);
494 unsafe {
496 if bits == 1 || bits as u64 * u64::from(exp) <= 256 {
497 let mut r = odd_power::<4, 5>(o, exp);
498 shifted_number(r.as_mut_ptr(), len_of(&r[..4]), shift, negative, order)
499 } else {
500 let mut r = odd_power::<8, 9>(o, exp);
501 shifted_number(r.as_mut_ptr(), len_of(&r[..8]), shift, negative, order)
502 }
503 }
504}
505
506#[inline(always)]
508fn len_of(r: &[u64]) -> usize {
509 r.len() - r.iter().rev().take_while(|&&l| l == 0).count()
510}
511
512#[inline(always)]
517fn odd_power<const N: usize, const M: usize>(o: [u64; 2], exp: u32) -> [u64; M] {
518 let ol = if o[1] != 0 { 2 } else { 1 };
519 let mut p = [0u64; N];
520 p[0] = o[0];
521 p[1] = o[1];
522 let mut len = ol;
523 for i in (0..31 - exp.leading_zeros()).rev() {
524 let mut r = [0u64; N];
526 for a in 0..len {
527 let mut carry = 0u64;
528 for b in 0..len {
529 let t =
530 u128::from(p[a]) * u128::from(p[b]) + u128::from(r[a + b]) + u128::from(carry);
531 r[a + b] = t as u64;
532 carry = (t >> 64) as u64;
533 }
534 if a + len < N {
535 r[a + len] = carry;
536 }
537 }
538 len = (2 * len).min(N);
539 if r[len - 1] == 0 {
540 len -= 1;
541 }
542 p = r;
543 if exp >> i & 1 == 1 {
544 let mut r = [0u64; N];
545 for (a, &oa) in o.iter().enumerate().take(ol) {
546 let mut carry = 0u64;
547 for b in 0..len.min(N - a) {
548 let t = u128::from(oa) * u128::from(p[b])
549 + u128::from(r[a + b])
550 + u128::from(carry);
551 r[a + b] = t as u64;
552 carry = (t >> 64) as u64;
553 }
554 if a + len < N {
555 r[a + len] = carry;
556 }
557 }
558 len = (len + ol).min(N);
559 if r[len - 1] == 0 {
560 len -= 1;
561 }
562 p = r;
563 }
564 }
565 let mut r = [0u64; M];
566 r[..N].copy_from_slice(&p);
567 r
568}
569
570#[inline(always)]
576unsafe fn limb_at(x: Operand, i: usize) -> u64 {
577 let mut l = 0;
578 for k in (0..8).rev() {
579 let p = 8 * i + k;
580 let byte = if p >= x.significant {
581 0
582 } else if x.big_endian {
583 unsafe { *x.ptr.add(x.len - 1 - p) }
585 } else {
586 unsafe { *x.ptr.add(p) }
588 };
589 l = (l << 8) | u64::from(byte);
590 }
591 l
592}
593
594unsafe fn shifted_number(
601 r: *mut u64,
602 mut len: usize,
603 shift: u128,
604 negative: bool,
605 order: ByteOrder,
606) -> Number {
607 unsafe {
609 let bytes = usize::try_from(shift / 8).unwrap_or_else(|_| too_long());
612 *r.add(len) = lshift(r, r, len, (shift % 8) as u32);
613 if *r.add(len) != 0 {
614 len += 1;
615 }
616 let odd_len = 8 * len - (*r.add(len - 1)).leading_zeros() as usize / 8;
617 let total = odd_len
618 .checked_add(bytes)
619 .filter(|&t| t <= isize::MAX as usize)
620 .unwrap_or_else(|| too_long());
621 let mut v: Vec<u8> = Vec::new();
622 if v.try_reserve_exact(total).is_err() {
623 too_long();
624 }
625 let dst = v.as_mut_ptr();
626 let product = Product {
627 r,
628 len: odd_len,
629 negative,
630 };
631 if order == ByteOrder::BigEndian {
632 product.write(dst, order);
633 ptr::write_bytes(dst.add(odd_len), 0, bytes);
634 } else {
635 ptr::write_bytes(dst, 0, bytes);
636 product.write(dst.add(bytes), order);
637 }
638 v.set_len(total);
639 Number {
640 value: v,
641 negative,
642 byte_order: order,
643 }
644 }
645}
646
647#[allow(clippy::too_many_arguments)]
656unsafe fn power_in(
657 s: *mut u64,
658 x: Operand,
659 xn: usize,
660 z: usize,
661 on: usize,
662 exp: u32,
663 rn: usize,
664 room: usize,
665 negative: bool,
666 order: ByteOrder,
667) -> Number {
668 unsafe {
671 let a = s;
672 let o = a.add(even(xn));
673 let p = o.add(even(on));
674 let q = p.add(even(rn + 1));
675 let t = q.add(even(rn + 1));
676 to_limbs(x, a);
677 let (zl, zb) = (z / 64, (z % 64) as u32);
679 if zb != 0 {
680 rshift(a.add(zl), xn - zl, zb);
681 }
682 ptr::copy_nonoverlapping(a.add(zl), o, on);
683 let (mut r, mut w) = (p, q);
686 ptr::copy_nonoverlapping(o, r, on);
687 let mut len = on;
688 for i in (0..31 - exp.leading_zeros()).rev() {
689 with_room(t, room, sqr_itch(len), |t| sqr(w, r, len, t));
690 len *= 2;
691 if *w.add(len - 1) == 0 {
692 len -= 1;
693 }
694 (r, w) = (w, r);
695 if exp >> i & 1 == 1 {
696 let itch = mul_itch(len.max(on), len.min(on));
697 with_room(t, room, itch, |t| {
698 if len >= on {
699 mul(w, r, len, o, on, t);
700 } else {
701 mul(w, o, on, r, len, t);
702 }
703 });
704 len += on;
705 if *w.add(len - 1) == 0 {
706 len -= 1;
707 }
708 (r, w) = (w, r);
709 }
710 }
711 shifted_number(r, len, z as u128 * u128::from(exp), negative, order)
712 }
713}
714
715unsafe fn with_room<R>(t: *mut u64, room: usize, need: usize, f: impl FnOnce(*mut u64) -> R) -> R {
722 if need <= room {
723 f(t)
724 } else {
725 more_room(need, f)
726 }
727}
728
729#[cold]
731#[inline(never)]
732fn more_room<R>(need: usize, f: impl FnOnce(*mut u64) -> R) -> R {
733 let mut s = Vec::<Pair>::with_capacity(need.div_ceil(2));
734 f(s.as_mut_ptr().cast())
735}
736
737#[derive(Clone, Copy)]
741struct Shape {
742 mn: usize,
744 z: usize,
746 nodd: usize,
748 kn: usize,
750}
751
752impl Shape {
753 unsafe fn of(m: Operand) -> Self {
759 let (z, bits) = unsafe { zeros_and_bits(m) };
761 Shape {
762 mn: m.significant.div_ceil(8),
763 z,
764 nodd: (bits - z).div_ceil(64),
765 kn: z.div_ceil(64),
766 }
767 }
768
769 fn power_of_two(&self, bits: usize) -> bool {
771 bits == self.z + 1
772 }
773}
774
775#[inline(never)]
778fn pow_mod_of(b: Operand, negative_b: bool, e: Operand, m: Operand, order: ByteOrder) -> Number {
779 let (bn, en) = (b.significant.div_ceil(8), e.significant.div_ceil(8));
780 let shape = unsafe { Shape::of(m) };
782 let ebits = 8 * e.significant;
785 let need = pow_mod_itch(bn, en, ebits, shape);
786 let compute = |s: *mut MaybeUninit<u64>| {
787 unsafe { pow_mod_in(s.cast(), b, negative_b, e, m, shape, order) }
789 };
790 if need <= STACK_LIMBS {
791 on_stack(compute)
792 } else {
793 on_heap(need, compute)
794 }
795}
796
797fn pow_mod_itch(bn: usize, en: usize, ebits: usize, shape: Shape) -> usize {
800 let Shape { mn, nodd, kn, .. } = shape;
801 let inputs = even(bn) + even(en) + 3 * even(mn + 1);
802 let mut work = rem_itch(bn, mn);
803 if kn == 0 {
804 work = work.max(powm_odd_itch(bn, ebits, nodd));
805 } else {
806 let crt = 4 * even(kn) + even(nodd + kn + 1);
809 let products = mullo_itch(kn).max(mul_itch(nodd.max(kn), nodd.min(kn)));
810 let rest = powm_odd_itch(bn, ebits, nodd)
811 .max(powlo_itch(ebits, kn))
812 .max(products);
813 work = work.max(crt + rest);
814 }
815 inputs + work
816}
817
818unsafe fn pow_mod_in(
825 s: *mut u64,
826 b: Operand,
827 negative_b: bool,
828 e: Operand,
829 m: Operand,
830 shape: Shape,
831 order: ByteOrder,
832) -> Number {
833 let (bn, en) = (b.significant.div_ceil(8), e.significant.div_ceil(8));
834 let Shape { mn, z, nodd, kn } = shape;
835 unsafe {
838 let bp = s;
839 let ep = bp.add(even(bn));
840 let mp = ep.add(even(en));
841 let odd = mp.add(even(mn + 1));
842 let r = odd.add(even(mn + 1));
843 let t = r.add(even(mn + 1));
844 to_limbs(m, mp);
845 let one = mn == 1 && *mp == 1;
846 if en == 0 {
847 return small_number(order, u128::from(!one), false);
849 }
850 if bn == 0 || one {
851 return small_number(order, 0, false);
852 }
853 to_limbs(e, ep);
854 to_limbs(b, bp);
855 let ebits = 64 * en - (*ep.add(en - 1)).leading_zeros() as usize;
856 let mbits = 64 * mn - (*mp.add(mn - 1)).leading_zeros() as usize;
857 if ebits == 1 {
858 rem(r, bp, bn, 0, mp, mn, t);
860 } else if z == 0 {
861 powm_odd(r, bp, bn, ep, ebits, mp, mn, t);
862 } else if shape.power_of_two(mbits) {
863 powlo(r, bp, bn, ep, en, ebits, z, t);
864 ptr::write_bytes(r.add(kn), 0, mn - kn);
865 } else {
866 let (zl, zb) = (z / 64, (z % 64) as u32);
869 ptr::copy_nonoverlapping(mp.add(zl), odd, mn - zl);
870 if zb != 0 {
871 rshift(odd, mn - zl, zb);
872 }
873 let r2 = t;
874 let inv = r2.add(even(kn));
875 let num = inv.add(even(kn));
876 let d = num.add(even(kn));
877 let y = d.add(even(kn));
878 let rest = y.add(even(nodd + kn + 1));
879 powm_odd(r, bp, bn, ep, ebits, odd, nodd, rest);
880 powlo(r2, bp, bn, ep, en, ebits, z, rest);
881 ptr::write_bytes(num, 0, kn);
882 *num = 1;
883 bdiv_q(inv, num, kn, odd, nodd, binvert_limb(*odd));
884 if nodd >= kn {
886 sub_n(d, r2, r, kn);
887 } else {
888 let borrow = sub_n(d, r2, r, nodd);
889 ptr::copy_nonoverlapping(r2.add(nodd), d.add(nodd), kn - nodd);
890 sub_1(d.add(nodd), kn - nodd, u64::from(borrow));
891 }
892 let x = num;
893 mullo(x, d, inv, kn, rest);
894 if zb != 0 {
895 *x.add(kn - 1) &= (1 << zb) - 1;
896 }
897 if nodd >= kn {
898 mul(y, odd, nodd, x, kn, rest);
899 } else {
900 mul(y, x, kn, odd, nodd, rest);
901 }
902 let carry = add_n(y, y, r, nodd);
904 add_1(y.add(nodd), kn, u64::from(carry));
905 ptr::copy_nonoverlapping(y, r, mn);
906 }
907 let mut top = mn;
909 while top > 0 && *r.add(top - 1) == 0 {
910 top -= 1;
911 }
912 if negative_b && *ep & 1 == 1 && top != 0 {
913 sub_n(r, mp, r, mn);
914 top = mn;
915 while top > 0 && *r.add(top - 1) == 0 {
916 top -= 1;
917 }
918 }
919 number_of(r, top, order)
920 }
921}
922
923unsafe fn number_of(r: *const u64, n: usize, order: ByteOrder) -> Number {
930 unsafe {
932 if n == 0 {
933 return small_number(order, 0, false);
934 }
935 let len = 8 * n - (*r.add(n - 1)).leading_zeros() as usize / 8;
936 let mut v = Vec::with_capacity(len);
937 Product {
938 r,
939 len,
940 negative: false,
941 }
942 .write(v.as_mut_ptr(), order);
943 v.set_len(len);
944 Number {
945 value: v,
946 negative: false,
947 byte_order: order,
948 }
949 }
950}
951
952fn powm_odd_itch(bn: usize, ebits: usize, n: usize) -> usize {
955 if n <= 2 {
956 rem_itch(bn + n, n)
957 } else {
958 let table = (1 << (window(ebits) - 1)) * even(n);
959 Montgomery::new_itch(n) + table + Montgomery::itch(n).max(rem_itch(bn + n, n))
960 }
961}
962
963#[allow(clippy::too_many_arguments)]
972unsafe fn powm_odd(
973 r: *mut u64,
974 b: *const u64,
975 bn: usize,
976 e: *const u64,
977 ebits: usize,
978 m: *const u64,
979 n: usize,
980 s: *mut u64,
981) {
982 unsafe {
985 match n {
986 1 => {
987 let ring = Mont1::new(*m);
988 let mut x = 0;
989 rem(&mut x, b, bn, 1, m, 1, s);
990 let y = power_in_registers(&ring, x, e, ebits);
991 *r = ring.plain(y);
992 }
993 2 => {
994 let ring = Mont2::new([*m, *m.add(1)]);
995 let mut x = [0; 2];
996 rem(x.as_mut_ptr(), b, bn, 2, m, 2, s);
997 let y = power_in_registers(&ring, x, e, ebits);
998 let z = ring.plain(y);
999 (*r, *r.add(1)) = (z[0], z[1]);
1000 }
1001 3 => powm_registers::<3>(r, b, bn, e, ebits, m, s),
1005 4 => powm_registers::<4>(r, b, bn, e, ebits, m, s),
1006 5 => powm_registers::<5>(r, b, bn, e, ebits, m, s),
1007 6 => powm_registers::<6>(r, b, bn, e, ebits, m, s),
1008 7 => powm_registers::<7>(r, b, bn, e, ebits, m, s),
1009 _ => {
1010 let ring = Montgomery::new(m, n, s);
1011 let table = s.add(Montgomery::new_itch(n));
1012 let stride = even(n);
1013 let t = table.add((1 << (window(ebits) - 1)) * stride);
1014 rem(table, b, bn, n, m, n, t);
1015 power(&ring, r, e, ebits, table, stride, t);
1016 ring.plain(r, r, t);
1017 }
1018 }
1019 }
1020}
1021
1022#[inline(never)]
1028unsafe fn powm_registers<const N: usize>(
1029 r: *mut u64,
1030 b: *const u64,
1031 bn: usize,
1032 e: *const u64,
1033 ebits: usize,
1034 m: *const u64,
1035 s: *mut u64,
1036) {
1037 unsafe {
1039 let mut mm = [0; N];
1040 ptr::copy_nonoverlapping(m, mm.as_mut_ptr(), N);
1041 let ring = MontN::new(mm);
1042 let mut x = [0; N];
1043 rem(x.as_mut_ptr(), b, bn, N, m, N, s);
1044 let y = power_in_registers(&ring, x, e, ebits);
1045 let z = ring.plain(y);
1046 ptr::copy_nonoverlapping(z.as_ptr(), r, N);
1047 }
1048}
1049
1050fn powlo_itch(ebits: usize, n: usize) -> usize {
1053 (1 << (window(ebits) - 1)) * even(n) + Low::itch(n)
1054}
1055
1056#[allow(clippy::too_many_arguments)]
1067unsafe fn powlo(
1068 r: *mut u64,
1069 b: *const u64,
1070 bn: usize,
1071 e: *const u64,
1072 en: usize,
1073 ebits: usize,
1074 z: usize,
1075 s: *mut u64,
1076) {
1077 let kn = z.div_ceil(64);
1078 unsafe {
1080 let mut v = 0;
1081 while *b.add(v / 64) == 0 {
1082 v += 64;
1083 }
1084 v += (*b.add(v / 64)).trailing_zeros() as usize;
1085 if v > 0 && (en > 1 || u128::from(*e) * v as u128 >= z as u128) {
1086 ptr::write_bytes(r, 0, kn);
1087 return;
1088 }
1089 if kn <= 2 {
1090 let x = u128::from(*b)
1091 | if bn > 1 {
1092 u128::from(*b.add(1)) << 64
1093 } else {
1094 0
1095 };
1096 let y = power_in_registers(&Low2, x, e, ebits);
1097 let y = if z < 128 { y & ((1 << z) - 1) } else { y };
1098 *r = y as u64;
1099 if kn == 2 {
1100 *r.add(1) = (y >> 64) as u64;
1101 }
1102 return;
1103 }
1104 match kn {
1105 3 => return low_registers::<3>(r, b, bn, e, ebits, z),
1106 4 => return low_registers::<4>(r, b, bn, e, ebits, z),
1107 5 => return low_registers::<5>(r, b, bn, e, ebits, z),
1108 6 => return low_registers::<6>(r, b, bn, e, ebits, z),
1109 7 => return low_registers::<7>(r, b, bn, e, ebits, z),
1110 8 => return low_registers::<8>(r, b, bn, e, ebits, z),
1111 _ => {}
1112 }
1113 let table = s;
1114 let stride = even(kn);
1115 let t = table.add((1 << (window(ebits) - 1)) * stride);
1116 let have = bn.min(kn);
1117 ptr::copy_nonoverlapping(b, table, have);
1118 ptr::write_bytes(table.add(have), 0, kn - have);
1119 power(&Low { n: kn }, r, e, ebits, table, stride, t);
1120 if !z.is_multiple_of(64) {
1121 *r.add(kn - 1) &= (1 << (z % 64)) - 1;
1122 }
1123 }
1124}
1125
1126#[inline(never)]
1132unsafe fn low_registers<const N: usize>(
1133 r: *mut u64,
1134 b: *const u64,
1135 bn: usize,
1136 e: *const u64,
1137 ebits: usize,
1138 z: usize,
1139) {
1140 let mut x = [0u64; N];
1141 unsafe {
1144 ptr::copy_nonoverlapping(b, x.as_mut_ptr(), bn.min(N));
1145 let mut y = power_in_registers(&LowN::<N>, x, e, ebits);
1146 if !z.is_multiple_of(64) {
1147 y[N - 1] &= (1 << (z % 64)) - 1;
1148 }
1149 ptr::copy_nonoverlapping(y.as_ptr(), r, N);
1150 }
1151}