#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_pointer_width = "64",
any(
target_vendor = "apple",
target_os = "linux",
target_os = "android",
target_os = "windows",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia"
),
not(miri)
))]
#[path = "pow/aarch64.rs"]
mod kernels;
#[cfg(not(all(
target_arch = "aarch64",
target_endian = "little",
target_pointer_width = "64",
any(
target_vendor = "apple",
target_os = "linux",
target_os = "android",
target_os = "windows",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia"
),
not(miri)
)))]
#[path = "pow/portable.rs"]
mod kernels;
pub(super) mod low;
pub(super) mod montgomery;
pub(super) mod window;
use super::add::{SMALL, read_small, write_small};
use super::div::by_zero;
use super::div::limbs::{bdiv_q, binvert_limb, rshift};
use super::mul::limbs::{add_1, add_n, mul, mul_itch, sqr, sqr_itch, sub_1, sub_n};
use super::mul::{Operand, Product, STACK_LIMBS, on_heap, on_stack, to_limbs};
use super::{ByteOrder, Number};
use core::mem::MaybeUninit;
use core::ptr;
use low::{Low, Low2, LowN, mullo, mullo_itch};
use montgomery::{Mont1, Mont2, MontN, Montgomery, even, lshift, rem, rem_itch};
use window::{power, power_in_registers, window};
impl Number {
pub fn pow(&self, exp: u32) -> Number {
let order = self.byte_order;
if exp == 0 {
return small_number(order, 1, false);
}
let negative = self.negative && exp & 1 == 1;
if self.value.len() <= SMALL {
let x = unsafe { read_small(&self.value, order) };
if x == 0 {
return small_number(order, 0, false);
}
if let Some(p) = small_power(x, exp) {
return small_number(order, p, negative);
}
let z = x.trailing_zeros();
let o = x >> z;
if o == 1 || u64::from(128 - o.leading_zeros()) * u64::from(exp) <= 512 {
return register_power(
[o as u64, (o >> 64) as u64],
z as usize,
exp,
negative,
order,
);
}
}
let x = Operand::of(self);
if x.significant == 0 {
return small_number(order, 0, false);
}
power_of(x, exp, negative, order)
}
#[track_caller]
pub fn pow_mod(&self, exp: &Number, modulus: &Number) -> Number {
let m = Operand::of(modulus);
if m.significant == 0 {
by_zero::<true>();
}
let e = Operand::of(exp);
if exp.negative && e.significant != 0 {
negative_exponent();
}
let order = self.byte_order;
if self.value.len() <= SMALL && exp.value.len() <= SMALL && modulus.value.len() <= SMALL {
let (b, e, m) = unsafe {
(
read_small(&self.value, order),
read_small(&exp.value, exp.byte_order),
read_small(&modulus.value, modulus.byte_order),
)
};
let mut r = small_pow_mod(b, e, m);
if self.negative && e & 1 == 1 && r != 0 {
r = m - r;
}
return small_number(order, r, false);
}
let b = Operand::of(self);
pow_mod_of(b, self.negative, e, m, order)
}
}
#[inline(never)]
fn small_pow_mod(b: u128, e: u128, m: u128) -> u128 {
if m == 1 {
return 0;
}
if e == 0 {
return 1;
}
if b == 0 {
return 0;
}
let el = [e as u64, (e >> 64) as u64];
let ebits = 128 - e.leading_zeros() as usize;
let z = m.trailing_zeros();
let odd = m >> z;
let bl = [b as u64, (b >> 64) as u64];
let bn = if bl[1] != 0 { 2 } else { 1 };
let r1 = if odd == 1 {
0
} else {
let ol = [odd as u64, (odd >> 64) as u64];
let mut s = [0u64; 16];
unsafe {
if ol[1] == 0 {
let ring = Mont1::new(ol[0]);
let mut x = 0;
rem(&mut x, bl.as_ptr(), bn, 1, ol.as_ptr(), 1, s.as_mut_ptr());
let y = power_in_registers(&ring, x, el.as_ptr(), ebits);
u128::from(ring.plain(y))
} else {
let ring = Mont2::new(ol);
let mut x = [0; 2];
rem(
x.as_mut_ptr(),
bl.as_ptr(),
bn,
2,
ol.as_ptr(),
2,
s.as_mut_ptr(),
);
let y = power_in_registers(&ring, x, el.as_ptr(), ebits);
let y = ring.plain(y);
u128::from(y[0]) | (u128::from(y[1]) << 64)
}
}
};
if z == 0 {
return r1;
}
let mask = (1u128 << z) - 1;
let v = b.trailing_zeros();
let r2 = if v > 0
&& u128::from(v)
.checked_mul(e)
.is_none_or(|p| p >= u128::from(z))
{
0
} else {
unsafe { power_in_registers(&Low2, b, el.as_ptr(), ebits) & mask }
};
if odd == 1 {
return r2;
}
let mut inv = u128::from(binvert_limb(odd as u64));
inv = inv.wrapping_mul(2u128.wrapping_sub(odd.wrapping_mul(inv)));
let x = r2.wrapping_sub(r1).wrapping_mul(inv) & mask;
r1 + odd * x
}
#[cold]
#[inline(never)]
#[track_caller]
fn negative_exponent() -> ! {
panic!("negative exponent")
}
#[inline]
fn small_power(x: u128, exp: u32) -> Option<u128> {
let z = x.trailing_zeros();
let o = x >> z;
let bits = 128 - o.leading_zeros();
if u64::from(bits + z) * u64::from(exp) > 128 {
return None;
}
let mut p = o;
for i in (0..31 - exp.leading_zeros()).rev() {
p *= p;
if exp >> i & 1 == 1 {
p *= o;
}
}
Some(p << (z * exp))
}
fn small_number(order: ByteOrder, p: u128, negative: bool) -> Number {
let len = 16 - (p | 1).leading_zeros() as usize / 8;
let mut v = Vec::with_capacity(len);
unsafe {
write_small(v.as_mut_ptr(), order, p as u64, (p >> 64) as u64, len);
v.set_len(len);
}
Number {
value: v,
negative: negative && p != 0,
byte_order: order,
}
}
unsafe fn zeros_and_bits(x: Operand) -> (usize, usize) {
let l = x.significant;
unsafe {
let byte = |i: usize| {
if x.big_endian {
*x.ptr.add(x.len - 1 - i)
} else {
*x.ptr.add(i)
}
};
let mut i = 0;
while byte(i) == 0 {
i += 1;
}
let zeros = 8 * i + byte(i).trailing_zeros() as usize;
(zeros, 8 * l - byte(l - 1).leading_zeros() as usize)
}
}
fn power_limbs(bits: usize, exp: u32) -> usize {
let limbs = (bits as u128 * u128::from(exp)).div_ceil(64) + 1;
match usize::try_from(limbs) {
Ok(limbs) if limbs <= isize::MAX as usize / 32 => limbs,
_ => too_long(),
}
}
#[cold]
#[inline(never)]
fn too_long() -> ! {
panic!("the power is too long to allocate")
}
#[inline(never)]
fn power_of(x: Operand, exp: u32, negative: bool, order: ByteOrder) -> Number {
let xn = x.significant.div_ceil(8);
let (z, bits) = unsafe { zeros_and_bits(x) };
let odd_bits = bits - z;
if odd_bits == 1 || odd_bits <= 128 && odd_bits as u64 * u64::from(exp) <= 512 {
return unsafe { short_power(x, z, exp, negative, order) };
}
let on = odd_bits.div_ceil(64);
let rn = power_limbs(odd_bits, exp);
if rn > STACK_LIMBS {
let mut v = Vec::<Pair>::new();
if v.try_reserve_exact(rn + 1).is_err() {
too_long();
}
}
let mut scratch = 0;
if rn >= 112 || on >= 24 {
let mut len = on;
for i in (0..31 - exp.leading_zeros()).rev() {
scratch = scratch.max(sqr_itch(len));
len = (2 * len).min(rn);
if exp >> i & 1 == 1 {
scratch = scratch.max(mul_itch(len.max(on), len.min(on)));
len = (len + on).min(rn);
}
}
}
let need = even(xn) + even(on) + 2 * even(rn + 1) + scratch;
let compute = |s: *mut MaybeUninit<u64>| {
unsafe { power_in(s.cast(), x, xn, z, on, exp, rn, scratch, negative, order) }
};
if need <= SHORT_LIMBS {
let mut s = [PAIR; SHORT_LIMBS / 2];
compute(s.as_mut_ptr().cast())
} else if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap_or_panic(need, compute)
}
}
const SHORT_LIMBS: usize = 96;
#[derive(Clone, Copy)]
#[repr(C, align(16))]
struct Pair([MaybeUninit<u64>; 2]);
const PAIR: Pair = Pair([MaybeUninit::uninit(); 2]);
#[cold]
#[inline(never)]
fn on_heap_or_panic<R>(need: usize, f: impl FnOnce(*mut MaybeUninit<u64>) -> R) -> R {
let mut s = Vec::<Pair>::new();
if s.try_reserve_exact(need.div_ceil(2)).is_err() {
too_long();
}
f(s.as_mut_ptr().cast())
}
#[inline(never)]
unsafe fn short_power(x: Operand, z: usize, exp: u32, negative: bool, order: ByteOrder) -> Number {
let (zl, zb) = (z / 64, (z % 64) as u32);
let l = |i: usize| unsafe { limb_at(x, zl + i) };
let (l0, l1, l2) = (l(0), l(1), l(2));
let o = if zb == 0 {
[l0, l1]
} else {
[
(l0 >> zb) | (l1 << (64 - zb)),
(l1 >> zb) | (l2 << (64 - zb)),
]
};
register_power(o, z, exp, negative, order)
}
#[inline(never)]
fn register_power(o: [u64; 2], z: usize, exp: u32, negative: bool, order: ByteOrder) -> Number {
let bits = 128
- if o[1] != 0 {
o[1].leading_zeros()
} else {
64 + o[0].leading_zeros()
};
let shift = z as u128 * u128::from(exp);
unsafe {
if bits == 1 || bits as u64 * u64::from(exp) <= 256 {
let mut r = odd_power::<4, 5>(o, exp);
shifted_number(r.as_mut_ptr(), len_of(&r[..4]), shift, negative, order)
} else {
let mut r = odd_power::<8, 9>(o, exp);
shifted_number(r.as_mut_ptr(), len_of(&r[..8]), shift, negative, order)
}
}
}
#[inline(always)]
fn len_of(r: &[u64]) -> usize {
r.len() - r.iter().rev().take_while(|&&l| l == 0).count()
}
#[inline(always)]
fn odd_power<const N: usize, const M: usize>(o: [u64; 2], exp: u32) -> [u64; M] {
let ol = if o[1] != 0 { 2 } else { 1 };
let mut p = [0u64; N];
p[0] = o[0];
p[1] = o[1];
let mut len = ol;
for i in (0..31 - exp.leading_zeros()).rev() {
let mut r = [0u64; N];
for a in 0..len {
let mut carry = 0u64;
for b in 0..len {
let t =
u128::from(p[a]) * u128::from(p[b]) + u128::from(r[a + b]) + u128::from(carry);
r[a + b] = t as u64;
carry = (t >> 64) as u64;
}
if a + len < N {
r[a + len] = carry;
}
}
len = (2 * len).min(N);
if r[len - 1] == 0 {
len -= 1;
}
p = r;
if exp >> i & 1 == 1 {
let mut r = [0u64; N];
for (a, &oa) in o.iter().enumerate().take(ol) {
let mut carry = 0u64;
for b in 0..len.min(N - a) {
let t = u128::from(oa) * u128::from(p[b])
+ u128::from(r[a + b])
+ u128::from(carry);
r[a + b] = t as u64;
carry = (t >> 64) as u64;
}
if a + len < N {
r[a + len] = carry;
}
}
len = (len + ol).min(N);
if r[len - 1] == 0 {
len -= 1;
}
p = r;
}
}
let mut r = [0u64; M];
r[..N].copy_from_slice(&p);
r
}
#[inline(always)]
unsafe fn limb_at(x: Operand, i: usize) -> u64 {
let mut l = 0;
for k in (0..8).rev() {
let p = 8 * i + k;
let byte = if p >= x.significant {
0
} else if x.big_endian {
unsafe { *x.ptr.add(x.len - 1 - p) }
} else {
unsafe { *x.ptr.add(p) }
};
l = (l << 8) | u64::from(byte);
}
l
}
unsafe fn shifted_number(
r: *mut u64,
mut len: usize,
shift: u128,
negative: bool,
order: ByteOrder,
) -> Number {
unsafe {
let bytes = usize::try_from(shift / 8).unwrap_or_else(|_| too_long());
*r.add(len) = lshift(r, r, len, (shift % 8) as u32);
if *r.add(len) != 0 {
len += 1;
}
let odd_len = 8 * len - (*r.add(len - 1)).leading_zeros() as usize / 8;
let total = odd_len
.checked_add(bytes)
.filter(|&t| t <= isize::MAX as usize)
.unwrap_or_else(|| too_long());
let mut v: Vec<u8> = Vec::new();
if v.try_reserve_exact(total).is_err() {
too_long();
}
let dst = v.as_mut_ptr();
let product = Product {
r,
len: odd_len,
negative,
};
if order == ByteOrder::BigEndian {
product.write(dst, order);
ptr::write_bytes(dst.add(odd_len), 0, bytes);
} else {
ptr::write_bytes(dst, 0, bytes);
product.write(dst.add(bytes), order);
}
v.set_len(total);
Number {
value: v,
negative,
byte_order: order,
}
}
}
#[allow(clippy::too_many_arguments)]
unsafe fn power_in(
s: *mut u64,
x: Operand,
xn: usize,
z: usize,
on: usize,
exp: u32,
rn: usize,
room: usize,
negative: bool,
order: ByteOrder,
) -> Number {
unsafe {
let a = s;
let o = a.add(even(xn));
let p = o.add(even(on));
let q = p.add(even(rn + 1));
let t = q.add(even(rn + 1));
to_limbs(x, a);
let (zl, zb) = (z / 64, (z % 64) as u32);
if zb != 0 {
rshift(a.add(zl), xn - zl, zb);
}
ptr::copy_nonoverlapping(a.add(zl), o, on);
let (mut r, mut w) = (p, q);
ptr::copy_nonoverlapping(o, r, on);
let mut len = on;
for i in (0..31 - exp.leading_zeros()).rev() {
with_room(t, room, sqr_itch(len), |t| sqr(w, r, len, t));
len *= 2;
if *w.add(len - 1) == 0 {
len -= 1;
}
(r, w) = (w, r);
if exp >> i & 1 == 1 {
let itch = mul_itch(len.max(on), len.min(on));
with_room(t, room, itch, |t| {
if len >= on {
mul(w, r, len, o, on, t);
} else {
mul(w, o, on, r, len, t);
}
});
len += on;
if *w.add(len - 1) == 0 {
len -= 1;
}
(r, w) = (w, r);
}
}
shifted_number(r, len, z as u128 * u128::from(exp), negative, order)
}
}
unsafe fn with_room<R>(t: *mut u64, room: usize, need: usize, f: impl FnOnce(*mut u64) -> R) -> R {
if need <= room {
f(t)
} else {
more_room(need, f)
}
}
#[cold]
#[inline(never)]
fn more_room<R>(need: usize, f: impl FnOnce(*mut u64) -> R) -> R {
let mut s = Vec::<Pair>::with_capacity(need.div_ceil(2));
f(s.as_mut_ptr().cast())
}
#[derive(Clone, Copy)]
struct Shape {
mn: usize,
z: usize,
nodd: usize,
kn: usize,
}
impl Shape {
unsafe fn of(m: Operand) -> Self {
let (z, bits) = unsafe { zeros_and_bits(m) };
Shape {
mn: m.significant.div_ceil(8),
z,
nodd: (bits - z).div_ceil(64),
kn: z.div_ceil(64),
}
}
fn power_of_two(&self, bits: usize) -> bool {
bits == self.z + 1
}
}
#[inline(never)]
fn pow_mod_of(b: Operand, negative_b: bool, e: Operand, m: Operand, order: ByteOrder) -> Number {
let (bn, en) = (b.significant.div_ceil(8), e.significant.div_ceil(8));
let shape = unsafe { Shape::of(m) };
let ebits = 8 * e.significant;
let need = pow_mod_itch(bn, en, ebits, shape);
let compute = |s: *mut MaybeUninit<u64>| {
unsafe { pow_mod_in(s.cast(), b, negative_b, e, m, shape, order) }
};
if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(need, compute)
}
}
fn pow_mod_itch(bn: usize, en: usize, ebits: usize, shape: Shape) -> usize {
let Shape { mn, nodd, kn, .. } = shape;
let inputs = even(bn) + even(en) + 3 * even(mn + 1);
let mut work = rem_itch(bn, mn);
if kn == 0 {
work = work.max(powm_odd_itch(bn, ebits, nodd));
} else {
let crt = 4 * even(kn) + even(nodd + kn + 1);
let products = mullo_itch(kn).max(mul_itch(nodd.max(kn), nodd.min(kn)));
let rest = powm_odd_itch(bn, ebits, nodd)
.max(powlo_itch(ebits, kn))
.max(products);
work = work.max(crt + rest);
}
inputs + work
}
unsafe fn pow_mod_in(
s: *mut u64,
b: Operand,
negative_b: bool,
e: Operand,
m: Operand,
shape: Shape,
order: ByteOrder,
) -> Number {
let (bn, en) = (b.significant.div_ceil(8), e.significant.div_ceil(8));
let Shape { mn, z, nodd, kn } = shape;
unsafe {
let bp = s;
let ep = bp.add(even(bn));
let mp = ep.add(even(en));
let odd = mp.add(even(mn + 1));
let r = odd.add(even(mn + 1));
let t = r.add(even(mn + 1));
to_limbs(m, mp);
let one = mn == 1 && *mp == 1;
if en == 0 {
return small_number(order, u128::from(!one), false);
}
if bn == 0 || one {
return small_number(order, 0, false);
}
to_limbs(e, ep);
to_limbs(b, bp);
let ebits = 64 * en - (*ep.add(en - 1)).leading_zeros() as usize;
let mbits = 64 * mn - (*mp.add(mn - 1)).leading_zeros() as usize;
if ebits == 1 {
rem(r, bp, bn, 0, mp, mn, t);
} else if z == 0 {
powm_odd(r, bp, bn, ep, ebits, mp, mn, t);
} else if shape.power_of_two(mbits) {
powlo(r, bp, bn, ep, en, ebits, z, t);
ptr::write_bytes(r.add(kn), 0, mn - kn);
} else {
let (zl, zb) = (z / 64, (z % 64) as u32);
ptr::copy_nonoverlapping(mp.add(zl), odd, mn - zl);
if zb != 0 {
rshift(odd, mn - zl, zb);
}
let r2 = t;
let inv = r2.add(even(kn));
let num = inv.add(even(kn));
let d = num.add(even(kn));
let y = d.add(even(kn));
let rest = y.add(even(nodd + kn + 1));
powm_odd(r, bp, bn, ep, ebits, odd, nodd, rest);
powlo(r2, bp, bn, ep, en, ebits, z, rest);
ptr::write_bytes(num, 0, kn);
*num = 1;
bdiv_q(inv, num, kn, odd, nodd, binvert_limb(*odd));
if nodd >= kn {
sub_n(d, r2, r, kn);
} else {
let borrow = sub_n(d, r2, r, nodd);
ptr::copy_nonoverlapping(r2.add(nodd), d.add(nodd), kn - nodd);
sub_1(d.add(nodd), kn - nodd, u64::from(borrow));
}
let x = num;
mullo(x, d, inv, kn, rest);
if zb != 0 {
*x.add(kn - 1) &= (1 << zb) - 1;
}
if nodd >= kn {
mul(y, odd, nodd, x, kn, rest);
} else {
mul(y, x, kn, odd, nodd, rest);
}
let carry = add_n(y, y, r, nodd);
add_1(y.add(nodd), kn, u64::from(carry));
ptr::copy_nonoverlapping(y, r, mn);
}
let mut top = mn;
while top > 0 && *r.add(top - 1) == 0 {
top -= 1;
}
if negative_b && *ep & 1 == 1 && top != 0 {
sub_n(r, mp, r, mn);
top = mn;
while top > 0 && *r.add(top - 1) == 0 {
top -= 1;
}
}
number_of(r, top, order)
}
}
unsafe fn number_of(r: *const u64, n: usize, order: ByteOrder) -> Number {
unsafe {
if n == 0 {
return small_number(order, 0, false);
}
let len = 8 * n - (*r.add(n - 1)).leading_zeros() as usize / 8;
let mut v = Vec::with_capacity(len);
Product {
r,
len,
negative: false,
}
.write(v.as_mut_ptr(), order);
v.set_len(len);
Number {
value: v,
negative: false,
byte_order: order,
}
}
}
fn powm_odd_itch(bn: usize, ebits: usize, n: usize) -> usize {
if n <= 2 {
rem_itch(bn + n, n)
} else {
let table = (1 << (window(ebits) - 1)) * even(n);
Montgomery::new_itch(n) + table + Montgomery::itch(n).max(rem_itch(bn + n, n))
}
}
#[allow(clippy::too_many_arguments)]
unsafe fn powm_odd(
r: *mut u64,
b: *const u64,
bn: usize,
e: *const u64,
ebits: usize,
m: *const u64,
n: usize,
s: *mut u64,
) {
unsafe {
match n {
1 => {
let ring = Mont1::new(*m);
let mut x = 0;
rem(&mut x, b, bn, 1, m, 1, s);
let y = power_in_registers(&ring, x, e, ebits);
*r = ring.plain(y);
}
2 => {
let ring = Mont2::new([*m, *m.add(1)]);
let mut x = [0; 2];
rem(x.as_mut_ptr(), b, bn, 2, m, 2, s);
let y = power_in_registers(&ring, x, e, ebits);
let z = ring.plain(y);
(*r, *r.add(1)) = (z[0], z[1]);
}
3 => powm_registers::<3>(r, b, bn, e, ebits, m, s),
4 => powm_registers::<4>(r, b, bn, e, ebits, m, s),
5 => powm_registers::<5>(r, b, bn, e, ebits, m, s),
6 => powm_registers::<6>(r, b, bn, e, ebits, m, s),
7 => powm_registers::<7>(r, b, bn, e, ebits, m, s),
_ => {
let ring = Montgomery::new(m, n, s);
let table = s.add(Montgomery::new_itch(n));
let stride = even(n);
let t = table.add((1 << (window(ebits) - 1)) * stride);
rem(table, b, bn, n, m, n, t);
power(&ring, r, e, ebits, table, stride, t);
ring.plain(r, r, t);
}
}
}
}
#[inline(never)]
unsafe fn powm_registers<const N: usize>(
r: *mut u64,
b: *const u64,
bn: usize,
e: *const u64,
ebits: usize,
m: *const u64,
s: *mut u64,
) {
unsafe {
let mut mm = [0; N];
ptr::copy_nonoverlapping(m, mm.as_mut_ptr(), N);
let ring = MontN::new(mm);
let mut x = [0; N];
rem(x.as_mut_ptr(), b, bn, N, m, N, s);
let y = power_in_registers(&ring, x, e, ebits);
let z = ring.plain(y);
ptr::copy_nonoverlapping(z.as_ptr(), r, N);
}
}
fn powlo_itch(ebits: usize, n: usize) -> usize {
(1 << (window(ebits) - 1)) * even(n) + Low::itch(n)
}
#[allow(clippy::too_many_arguments)]
unsafe fn powlo(
r: *mut u64,
b: *const u64,
bn: usize,
e: *const u64,
en: usize,
ebits: usize,
z: usize,
s: *mut u64,
) {
let kn = z.div_ceil(64);
unsafe {
let mut v = 0;
while *b.add(v / 64) == 0 {
v += 64;
}
v += (*b.add(v / 64)).trailing_zeros() as usize;
if v > 0 && (en > 1 || u128::from(*e) * v as u128 >= z as u128) {
ptr::write_bytes(r, 0, kn);
return;
}
if kn <= 2 {
let x = u128::from(*b)
| if bn > 1 {
u128::from(*b.add(1)) << 64
} else {
0
};
let y = power_in_registers(&Low2, x, e, ebits);
let y = if z < 128 { y & ((1 << z) - 1) } else { y };
*r = y as u64;
if kn == 2 {
*r.add(1) = (y >> 64) as u64;
}
return;
}
match kn {
3 => return low_registers::<3>(r, b, bn, e, ebits, z),
4 => return low_registers::<4>(r, b, bn, e, ebits, z),
5 => return low_registers::<5>(r, b, bn, e, ebits, z),
6 => return low_registers::<6>(r, b, bn, e, ebits, z),
7 => return low_registers::<7>(r, b, bn, e, ebits, z),
8 => return low_registers::<8>(r, b, bn, e, ebits, z),
_ => {}
}
let table = s;
let stride = even(kn);
let t = table.add((1 << (window(ebits) - 1)) * stride);
let have = bn.min(kn);
ptr::copy_nonoverlapping(b, table, have);
ptr::write_bytes(table.add(have), 0, kn - have);
power(&Low { n: kn }, r, e, ebits, table, stride, t);
if !z.is_multiple_of(64) {
*r.add(kn - 1) &= (1 << (z % 64)) - 1;
}
}
}
#[inline(never)]
unsafe fn low_registers<const N: usize>(
r: *mut u64,
b: *const u64,
bn: usize,
e: *const u64,
ebits: usize,
z: usize,
) {
let mut x = [0u64; N];
unsafe {
ptr::copy_nonoverlapping(b, x.as_mut_ptr(), bn.min(N));
let mut y = power_in_registers(&LowN::<N>, x, e, ebits);
if !z.is_multiple_of(64) {
y[N - 1] &= (1 << (z % 64)) - 1;
}
ptr::copy_nonoverlapping(y.as_ptr(), r, N);
}
}