use super::super::div::limbs::{div_2by1, invert_limb};
use super::super::mul::limbs::{add_n, cmp_n, sub_n};
use super::super::pow::montgomery::{Mont1, Mont2, MontN, Montgomery, even, rem, rem_itch};
use super::super::pow::window::{Registers, Ring};
use core::cmp::Ordering;
use core::ptr;
pub(super) trait Field: Registers {
fn zero(&self) -> Self::V;
fn one(&self) -> Self::V;
fn add(&self, a: Self::V, b: Self::V) -> Self::V;
fn sub(&self, a: Self::V, b: Self::V) -> Self::V;
fn half(&self, a: Self::V) -> Self::V;
fn same(&self, a: Self::V, b: Self::V) -> bool;
fn residue(&self, c: u64) -> Self::V;
}
impl Field for Mont1 {
#[inline(always)]
fn zero(&self) -> u64 {
0
}
#[inline(always)]
fn one(&self) -> u64 {
self.m.wrapping_neg() % self.m
}
#[inline(always)]
fn add(&self, a: u64, b: u64) -> u64 {
let (s, carry) = a.overflowing_add(b);
if carry || s >= self.m {
s.wrapping_sub(self.m)
} else {
s
}
}
#[inline(always)]
fn sub(&self, a: u64, b: u64) -> u64 {
let (d, borrow) = a.overflowing_sub(b);
if borrow { d.wrapping_add(self.m) } else { d }
}
#[inline(always)]
fn half(&self, a: u64) -> u64 {
if a & 1 == 1 {
(a >> 1) + (self.m >> 1) + 1
} else {
a >> 1
}
}
#[inline(always)]
fn same(&self, a: u64, b: u64) -> bool {
a == b
}
#[inline]
fn residue(&self, c: u64) -> u64 {
((u128::from(c) << 64) % u128::from(self.m)) as u64
}
}
#[inline(always)]
fn add_mod<const N: usize>(a: [u64; N], b: [u64; N], m: &[u64; N]) -> [u64; N] {
let mut s = [0; N];
let mut carry = false;
for j in 0..N {
let (x, c1) = a[j].overflowing_add(b[j]);
let (x, c2) = x.overflowing_add(u64::from(carry));
s[j] = x;
carry = c1 | c2;
}
let mut d = [0; N];
let mut borrow = false;
for j in 0..N {
let (x, b1) = s[j].overflowing_sub(m[j]);
let (x, b2) = x.overflowing_sub(u64::from(borrow));
d[j] = x;
borrow = b1 | b2;
}
if carry || !borrow { d } else { s }
}
#[inline(always)]
fn sub_mod<const N: usize>(a: [u64; N], b: [u64; N], m: &[u64; N]) -> [u64; N] {
let mut d = [0; N];
let mut borrow = false;
for j in 0..N {
let (x, b1) = a[j].overflowing_sub(b[j]);
let (x, b2) = x.overflowing_sub(u64::from(borrow));
d[j] = x;
borrow = b1 | b2;
}
if borrow {
let mut carry = false;
for j in 0..N {
let (x, c1) = d[j].overflowing_add(m[j]);
let (x, c2) = x.overflowing_add(u64::from(carry));
d[j] = x;
carry = c1 | c2;
}
}
d
}
#[inline(always)]
fn half_mod<const N: usize>(a: [u64; N], m: &[u64; N]) -> [u64; N] {
let mut s = a;
let mut carry = false;
if a[0] & 1 == 1 {
for j in 0..N {
let (x, c1) = a[j].overflowing_add(m[j]);
let (x, c2) = x.overflowing_add(u64::from(carry));
s[j] = x;
carry = c1 | c2;
}
}
let mut h = [0; N];
for j in 0..N {
let above = if j + 1 < N {
s[j + 1]
} else {
u64::from(carry)
};
h[j] = (s[j] >> 1) | (above << 63);
}
h
}
#[inline]
fn residue_of<const N: usize>(c: u64, m: &[u64; N]) -> [u64; N] {
let mut r = [0; N];
let mut s = [0u64; 64];
unsafe { rem(r.as_mut_ptr(), &c, 1, N, m.as_ptr(), N, s.as_mut_ptr()) };
r
}
impl Field for Mont2 {
#[inline(always)]
fn zero(&self) -> [u64; 2] {
[0; 2]
}
#[inline(always)]
fn one(&self) -> [u64; 2] {
residue_of(1, &self.m)
}
#[inline(always)]
fn add(&self, a: [u64; 2], b: [u64; 2]) -> [u64; 2] {
add_mod(a, b, &self.m)
}
#[inline(always)]
fn sub(&self, a: [u64; 2], b: [u64; 2]) -> [u64; 2] {
sub_mod(a, b, &self.m)
}
#[inline(always)]
fn half(&self, a: [u64; 2]) -> [u64; 2] {
half_mod(a, &self.m)
}
#[inline(always)]
fn same(&self, a: [u64; 2], b: [u64; 2]) -> bool {
a == b
}
#[inline]
fn residue(&self, c: u64) -> [u64; 2] {
residue_of(c, &self.m)
}
}
impl<const N: usize> Field for MontN<N> {
#[inline(always)]
fn zero(&self) -> [u64; N] {
[0; N]
}
#[inline(always)]
fn one(&self) -> [u64; N] {
residue_of(1, &self.m)
}
#[inline(always)]
fn add(&self, a: [u64; N], b: [u64; N]) -> [u64; N] {
add_mod(a, b, &self.m)
}
#[inline(always)]
fn sub(&self, a: [u64; N], b: [u64; N]) -> [u64; N] {
sub_mod(a, b, &self.m)
}
#[inline(always)]
fn half(&self, a: [u64; N]) -> [u64; N] {
half_mod(a, &self.m)
}
#[inline(always)]
fn same(&self, a: [u64; N], b: [u64; N]) -> bool {
a == b
}
#[inline]
fn residue(&self, c: u64) -> [u64; N] {
residue_of(c, &self.m)
}
}
#[derive(Clone, Copy)]
pub(super) struct Bits {
pub(super) n: *const u64,
pub(super) len: usize,
}
impl Bits {
#[inline(always)]
unsafe fn get(&self, i: usize) -> bool {
unsafe { (*self.n.add(i / 64) >> (i % 64)) & 1 == 1 }
}
#[inline]
unsafe fn zeros_below_minus_one(&self) -> usize {
let mut i = 0;
unsafe {
loop {
let l = *self.n.add(i) & if i == 0 { !1 } else { !0 };
if l != 0 {
return 64 * i + l.trailing_zeros() as usize;
}
i += 1;
}
}
}
#[inline]
pub(super) unsafe fn ones_at_bottom(&self) -> usize {
let mut i = 0;
unsafe {
loop {
let l = !*self.n.add(i);
if l != 0 || 64 * (i + 1) >= self.len {
return (64 * i + l.trailing_zeros() as usize).min(self.len);
}
i += 1;
}
}
}
}
#[inline(always)]
fn times_q<F: Field>(f: &F, x: F::V, big_q: i64, q: F::V) -> F::V {
let y = match big_q.unsigned_abs() {
1 => x,
2 => f.add(x, x),
3 => f.add(f.add(x, x), x),
4 => {
let y = f.add(x, x);
f.add(y, y)
}
_ => f.mul(x, q),
};
if big_q < 0 { f.sub(f.zero(), y) } else { y }
}
#[inline(always)]
pub(super) unsafe fn strong_base_2<F: Field>(f: &F, bits: Bits) -> bool {
unsafe {
let s = bits.zeros_below_minus_one();
let one = f.one();
let minus_one = f.sub(f.zero(), one);
let mut x = f.add(one, one);
let mut i = bits.len - 1;
while i > s {
i -= 1;
x = f.sqr(x);
if bits.get(i) {
x = f.add(x, x);
}
}
if f.same(x, one) || f.same(x, minus_one) {
return true;
}
for _ in 1..s {
x = f.sqr(x);
if f.same(x, minus_one) {
return true;
}
if f.same(x, one) {
return false;
}
}
false
}
}
#[inline(always)]
pub(super) unsafe fn strong_lucas<F: Field>(f: &F, bits: Bits, big_d: i64) -> bool {
if big_d == 5 {
return unsafe { strong_lucas_5(f, bits) };
}
let big_q = (1 - big_d) / 4;
unsafe {
let b0 = bits.ones_at_bottom();
let q = f.residue(big_q.unsigned_abs());
let one = f.one();
let (mut v, mut qk);
if bits.len - 2 < b0 {
v = one;
qk = if big_q < 0 { f.sub(f.zero(), q) } else { q };
} else {
let (mut u0, mut u1) = (one, one);
let mut i = bits.len - 2;
loop {
let t1 = f.sqr(u1);
let t2 = f.sqr(f.sub(u1, u0));
let t3 = f.sqr(u0);
let even = f.sub(t1, t2);
let odd = f.sub(t1, times_q(f, t3, big_q, q));
if bits.get(i) {
(u0, u1) = (odd, f.sub(odd, times_q(f, even, big_q, q)));
} else {
(u0, u1) = (even, odd);
}
if i == b0 {
break;
}
i -= 1;
}
if f.same(u1, f.zero()) {
return true;
}
let w = times_q(f, u0, big_q, q);
v = f.sub(u1, f.add(w, w));
if f.same(v, f.zero()) {
return true;
}
if b0 == 1 {
return false;
}
let du2 = f.mul(f.sqr(u1), f.residue(big_d.unsigned_abs()));
let du2 = if big_d < 0 { f.sub(f.zero(), du2) } else { du2 };
qk = f.half(f.half(f.sub(f.sqr(v), du2)));
}
for r in 1..b0 {
v = f.sub(f.sqr(v), f.add(qk, qk));
if f.same(v, f.zero()) {
return true;
}
if r + 1 < b0 {
qk = f.sqr(qk);
}
}
false
}
}
#[inline(always)]
unsafe fn strong_lucas_5<F: Field>(f: &F, bits: Bits) -> bool {
unsafe {
let b0 = bits.ones_at_bottom();
let one = f.one();
let two = f.add(one, one);
let (mut v0, mut v1) = (one, f.add(two, one));
let mut odd = true;
if bits.len > b0 {
let mut i = bits.len - 1;
loop {
let bit = if i > b0 + 1 {
i -= 1;
bits.get(i)
} else {
i = 0;
true
};
let m = f.mul(v0, v1);
let m = if odd { f.add(m, one) } else { f.sub(m, one) };
if bit {
let s = f.sqr(v1);
(v0, v1) = (m, if odd { f.sub(s, two) } else { f.add(s, two) });
} else {
let s = f.sqr(v0);
(v0, v1) = (if odd { f.add(s, two) } else { f.sub(s, two) }, m);
}
odd = bit;
if i == 0 {
break;
}
}
}
if f.same(f.add(v1, v1), v0) || f.same(v0, f.zero()) {
return true;
}
for r in 1..b0 {
let s = f.sqr(v0);
v0 = if r == 1 { f.add(s, two) } else { f.sub(s, two) };
if f.same(v0, f.zero()) {
return true;
}
}
false
}
}
#[inline(always)]
pub(super) unsafe fn bpsw<F: Field>(
f: &F,
bits: Bits,
select: impl FnOnce() -> Option<i64>,
) -> bool {
unsafe {
if !strong_base_2(f, bits) {
return false;
}
match select() {
Some(d) => strong_lucas(f, bits, d),
None => false,
}
}
}
pub(super) struct LimbField {
mont: Montgomery,
t: *mut u64,
}
impl LimbField {
pub(super) fn itch(nn: usize) -> usize {
Montgomery::new_itch(nn) + Montgomery::itch(nn).max(rem_itch(nn + 1, nn))
}
pub(super) unsafe fn new(n: *const u64, nn: usize, s: *mut u64) -> Self {
unsafe {
let mont = Montgomery::new(n, nn, s);
LimbField {
mont,
t: s.add(Montgomery::new_itch(nn)),
}
}
}
#[inline(always)]
fn nn(&self) -> usize {
self.mont.n
}
#[inline(always)]
pub(super) unsafe fn sqr(&self, r: *mut u64, a: *const u64) {
unsafe {
self.mont.sqr(r, a, self.t);
self.mont.reduce(r);
}
}
#[inline(always)]
pub(super) unsafe fn mul(&self, r: *mut u64, a: *const u64, b: *const u64) {
unsafe {
self.mont.mul(r, a, b, self.t);
self.mont.reduce(r);
}
}
#[inline(always)]
pub(super) unsafe fn add(&self, r: *mut u64, a: *const u64, b: *const u64) {
let nn = self.nn();
unsafe {
if add_n(r, a, b, nn) || cmp_n(r, self.mont.m, nn) != Ordering::Less {
sub_n(r, r, self.mont.m, nn);
}
}
}
#[inline(always)]
pub(super) unsafe fn sub(&self, r: *mut u64, a: *const u64, b: *const u64) {
unsafe {
if sub_n(r, a, b, self.nn()) {
add_n(r, r, self.mont.m, self.nn());
}
}
}
#[inline(always)]
unsafe fn neg(&self, r: *mut u64, a: *const u64) {
unsafe {
if is_zero(a, self.nn()) {
ptr::copy(a, r, self.nn());
} else {
sub_n(r, self.mont.m, a, self.nn());
}
}
}
unsafe fn half(&self, r: *mut u64, a: *const u64) {
let nn = self.nn();
unsafe {
let carry = if *a & 1 == 1 {
add_n(r, a, self.mont.m, nn)
} else {
ptr::copy(a, r, nn);
false
};
for j in 0..nn {
let above = if j + 1 < nn {
*r.add(j + 1)
} else {
u64::from(carry)
};
*r.add(j) = (*r.add(j) >> 1) | (above << 63);
}
}
}
unsafe fn residue(&self, r: *mut u64, c: u64) {
unsafe { rem(r, &c, 1, self.nn(), self.mont.m, self.nn(), self.t) }
}
unsafe fn times_q(&self, r: *mut u64, x: *const u64, big_q: i64, q: *const u64) {
unsafe {
match big_q.unsigned_abs() {
1 => ptr::copy_nonoverlapping(x, r, self.nn()),
2 => self.add(r, x, x),
3 => {
self.add(r, x, x);
self.add(r, r, x);
}
4 => {
self.add(r, x, x);
self.add(r, r, r);
}
_ => self.mul(r, x, q),
}
if big_q < 0 {
self.neg(r, r);
}
}
}
}
#[inline(always)]
unsafe fn is_zero(a: *const u64, n: usize) -> bool {
unsafe { (0..n).all(|i| *a.add(i) == 0) }
}
#[inline(always)]
unsafe fn equal(a: *const u64, b: *const u64, n: usize) -> bool {
unsafe { cmp_n(a, b, n) == Ordering::Equal }
}
pub(super) fn bpsw_limbs_itch(nn: usize) -> usize {
LimbField::itch(nn) + 10 * even(nn)
}
pub(super) unsafe fn bpsw_limbs(
f: &LimbField,
bits: Bits,
s: *mut u64,
select: impl FnOnce() -> Option<i64>,
) -> bool {
let nn = f.nn();
let stride = even(nn);
unsafe {
let slot = |i: usize| s.add(i * stride);
let (one, minus_one, x) = (slot(0), slot(1), slot(2));
f.residue(one, 1);
f.neg(minus_one, one);
let r = bits.zeros_below_minus_one();
f.add(x, one, one);
let mut i = bits.len - 1;
while i > r {
i -= 1;
f.sqr(x, x);
if bits.get(i) {
f.add(x, x, x);
}
}
if !(equal(x, one, nn) || equal(x, minus_one, nn)) {
let mut passed = false;
for _ in 1..r {
f.sqr(x, x);
if equal(x, minus_one, nn) {
passed = true;
break;
}
if equal(x, one, nn) {
return false;
}
}
if !passed {
return false;
}
}
let Some(big_d) = select() else {
return false;
};
let b0 = bits.ones_at_bottom();
if big_d == 5 {
let (two, v0, v1, m, t) = (slot(1), slot(2), slot(3), slot(4), slot(5));
f.add(two, one, one);
ptr::copy_nonoverlapping(one, v0, nn);
f.add(v1, two, one);
let mut odd = true;
if bits.len > b0 {
let mut i = bits.len - 1;
loop {
let bit = if i > b0 + 1 {
i -= 1;
bits.get(i)
} else {
i = 0;
true
};
f.mul(m, v0, v1);
if odd {
f.add(m, m, one);
} else {
f.sub(m, m, one);
}
if bit {
f.sqr(t, v1);
ptr::copy_nonoverlapping(m, v0, nn);
if odd {
f.sub(v1, t, two);
} else {
f.add(v1, t, two);
}
} else {
f.sqr(t, v0);
ptr::copy_nonoverlapping(m, v1, nn);
if odd {
f.add(v0, t, two);
} else {
f.sub(v0, t, two);
}
}
odd = bit;
if i == 0 {
break;
}
}
}
f.add(t, v1, v1);
if equal(t, v0, nn) || is_zero(v0, nn) {
return true;
}
for r in 1..b0 {
f.sqr(v0, v0);
if r == 1 {
f.add(v0, v0, two);
} else {
f.sub(v0, v0, two);
}
if is_zero(v0, nn) {
return true;
}
}
return false;
}
let big_q = (1 - big_d) / 4;
let (q, u0, u1, t1, t2, t3, v, qk) = (
slot(1),
slot(2),
slot(3),
slot(4),
slot(5),
slot(6),
slot(7),
slot(8),
);
f.residue(q, big_q.unsigned_abs());
if bits.len - 2 < b0 {
ptr::copy_nonoverlapping(one, v, nn);
ptr::copy_nonoverlapping(q, qk, nn);
if big_q < 0 {
f.neg(qk, qk);
}
} else {
ptr::copy_nonoverlapping(one, u0, nn);
ptr::copy_nonoverlapping(one, u1, nn);
let mut i = bits.len - 2;
loop {
f.sqr(t1, u1);
f.sub(t2, u1, u0);
f.sqr(t2, t2);
f.sqr(t3, u0);
f.sub(t2, t1, t2);
f.times_q(u0, t3, big_q, q);
f.sub(u1, t1, u0);
if bits.get(i) {
ptr::copy_nonoverlapping(u1, u0, nn);
f.times_q(t3, t2, big_q, q);
f.sub(u1, u1, t3);
} else {
ptr::copy_nonoverlapping(t2, u0, nn);
}
if i == b0 {
break;
}
i -= 1;
}
if is_zero(u1, nn) {
return true;
}
f.times_q(t1, u0, big_q, q);
f.add(t1, t1, t1);
f.sub(v, u1, t1);
if is_zero(v, nn) {
return true;
}
if b0 == 1 {
return false;
}
f.sqr(t1, u1);
f.residue(t2, big_d.unsigned_abs());
f.mul(t1, t1, t2);
if big_d < 0 {
f.neg(t1, t1);
}
f.sqr(t2, v);
f.sub(qk, t2, t1);
f.half(qk, qk);
f.half(qk, qk);
}
for r in 1..b0 {
f.sqr(v, v);
f.add(t1, qk, qk);
f.sub(v, v, t1);
if is_zero(v, nn) {
return true;
}
if r + 1 < b0 {
f.sqr(qk, qk);
}
}
false
}
}
#[inline]
pub(super) unsafe fn rem_by(n: *const u64, nn: usize, d: u64, v: u64) -> u64 {
let mut r = 0;
unsafe {
for i in (0..nn).rev() {
r = div_2by1(r, *n.add(i), d, v).1;
}
}
r
}
#[inline]
pub(super) fn reciprocal(d: u64) -> u64 {
invert_limb(d)
}