#![allow(clippy::many_single_char_names)]
use std::hash::Hash;
use rand::{rngs::StdRng, Rng, SeedableRng};
use num_bigint::{BigInt, BigUint, Sign};
use num_integer::Integer;
use num_traits::*;
lazy_static::lazy_static! {
static ref BIG_1: BigUint = BigUint::from(1u32);
static ref BIG_2: BigUint = BigUint::from(2u32);
static ref BIG_3: BigUint = BigUint::from(3u32);
static ref BIG_5: BigUint = BigUint::from(5u32);
static ref BIG_7: BigUint = BigUint::from(7u32);
static ref BIG_64: BigUint = BigUint::from(64u32);
}
const NUMBER_OF_PRIMES: u64 = 127;
const PRIME_GAP: [u64; 167] = [
2, 2, 4, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 14, 4, 6,
2, 10, 2, 6, 6, 4, 6, 6, 2, 10, 2, 4, 2, 12, 12, 4, 2, 4, 6, 2, 10, 6, 6, 6, 2, 6, 4, 2, 10,
14, 4, 2, 4, 14, 6, 10, 2, 4, 6, 8, 6, 6, 4, 6, 8, 4, 8, 10, 2, 10, 2, 6, 4, 6, 8, 4, 2, 4, 12,
8, 4, 8, 4, 6, 12, 2, 18, 6, 10, 6, 6, 2, 6, 10, 6, 6, 2, 6, 6, 4, 2, 12, 10, 2, 4, 6, 6, 2,
12, 4, 6, 8, 10, 8, 10, 8, 6, 6, 4, 8, 6, 4, 8, 4, 14, 10, 12, 2, 10, 2, 4, 2, 10, 14, 4, 2, 4,
14, 4, 2, 4, 20, 4, 8, 10, 8, 4, 6, 6, 14, 4, 6, 6, 8, 6, 12,
];
const INCR_LIMIT: usize = 0x10000;
const PRIME_BIT_MASK: u64 = 1 << 2
| 1 << 3
| 1 << 5
| 1 << 7
| 1 << 11
| 1 << 13
| 1 << 17
| 1 << 19
| 1 << 23
| 1 << 29
| 1 << 31
| 1 << 37
| 1 << 41
| 1 << 43
| 1 << 47
| 1 << 53
| 1 << 59
| 1 << 61;
const PRIMES_A: u64 = 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 37;
const PRIMES_B: u64 = 29 * 31 * 41 * 43 * 47 * 53;
pub fn probably_prime(x: &BigUint, n: usize) -> bool {
if x.is_zero() {
return false;
}
if x < &*BIG_64 {
return (PRIME_BIT_MASK & (1 << x.to_u64().unwrap())) != 0;
}
if x.is_even() {
return false;
}
let r_a = &(x % PRIMES_A);
let r_b = &(x % PRIMES_B);
if (r_a % 3u32).is_zero()
|| (r_a % 5u32).is_zero()
|| (r_a % 7u32).is_zero()
|| (r_a % 11u32).is_zero()
|| (r_a % 13u32).is_zero()
|| (r_a % 17u32).is_zero()
|| (r_a % 19u32).is_zero()
|| (r_a % 23u32).is_zero()
|| (r_a % 37u32).is_zero()
|| (r_b % 29u32).is_zero()
|| (r_b % 31u32).is_zero()
|| (r_b % 41u32).is_zero()
|| (r_b % 43u32).is_zero()
|| (r_b % 47u32).is_zero()
|| (r_b % 53u32).is_zero()
{
return false;
}
probably_prime_miller_rabin(x, n + 1, true) && probably_prime_lucas(x)
}
pub fn probably_prime_miller_rabin(n: &BigUint, reps: usize, force2: bool) -> bool {
let nm1 = n - &*BIG_1;
let k = nm1.trailing_zeros().unwrap() as usize;
let q = &nm1 >> k;
let nm3 = n - &*BIG_3;
struct Hasher([u8; 32]);
impl std::hash::Hasher for Hasher {
fn finish(&self) -> u64 {
unreachable!("we do not call this method")
}
fn write(&mut self, bytes: &[u8]) {
for (i, chunk) in bytes.chunks(16).enumerate() {
let i = if i & 1 == 1 { 16 } else { 0 };
let mut a = [0u8; 16];
a.copy_from_slice(&self.0[i..i + 16]);
let mut b = [0u8; 16];
(&mut b[..chunk.len()]).copy_from_slice(chunk);
let c = (u128::from_ne_bytes(a) ^ u128::from_ne_bytes(b)).to_ne_bytes();
(&mut self.0[i..i + 16]).copy_from_slice(&c[..]);
}
}
}
let mut hasher = Hasher([0; 32]);
n.hash(&mut hasher);
let seed = hasher.0;
let mut rng = StdRng::from_seed(seed);
'nextrandom: for i in 0..reps {
let x = if i == reps - 1 && force2 {
BIG_2.clone()
} else {
gen_biguint_below(&mut rng, &nm3) + &*BIG_2
};
let mut y = x.modpow(&q, n);
if y.is_one() || y == nm1 {
continue;
}
for _ in 1..k {
y = y.modpow(&*BIG_2, n);
if y == nm1 {
break 'nextrandom;
}
if y.is_one() {
return false;
}
}
return false;
}
true
}
pub fn probably_prime_lucas(n: &BigUint) -> bool {
if n.is_zero() || n.is_one() {
return false;
}
if n.to_u64() == Some(2) {
return false;
}
let mut p = 3u64;
let n_int = BigInt::from_biguint(Sign::Plus, n.clone());
loop {
if p > 10000 {
panic!("internal error: cannot find (D/n) = -1 for {:?}", n)
}
let j = jacobi(&BigInt::from(p * p - 4), &n_int);
if j == -1 {
break;
}
if j == 0 {
return n_int.to_i64() == Some(p as i64 + 2);
}
let t1 = &n_int * &n_int;
if p == 40 && t1.sqrt() == n_int {
return false;
}
p += 1;
}
let mut s = n + &*BIG_1;
let r = s.trailing_zeros().unwrap() as usize;
s = &s >> r;
let nm2 = n - &*BIG_2;
let mut vk = BIG_2.clone();
let mut vk1 = BigUint::from_u64(p).unwrap();
for i in (0..s.bits() as usize).rev() {
if is_bit_set(&s, i) {
let t1 = (&vk * &vk1) + n - p;
vk = &t1 % n;
let t1 = (&vk1 * &vk1) + &nm2;
vk1 = &t1 % n;
} else {
let t1 = (&vk * &vk1) + n - p;
vk1 = &t1 % n;
let t1 = (&vk * &vk) + &nm2;
vk = &t1 % n;
}
}
if vk.to_u64() == Some(2) || vk == nm2 {
let mut t1 = &vk * p;
let mut t2 = &vk1 << 1;
if t1 < t2 {
core::mem::swap(&mut t1, &mut t2);
}
t1 -= t2;
if (t1 % n).is_zero() {
return true;
}
}
for _ in 0..r - 1 {
if vk.is_zero() {
return true;
}
if vk.to_u64() == Some(2) {
return false;
}
let t1 = (&vk * &vk) - &*BIG_2;
vk = &t1 % n;
}
false
}
pub fn next_prime(n: &BigUint) -> BigUint {
if n < &*BIG_2 {
return BIG_2.clone();
}
let mut res = n + &*BIG_1;
res |= &*BIG_1;
if let Some(val) = res.to_u64() {
if val < 7 {
return res;
}
}
let nbits = res.bits();
let prime_limit = if nbits / 2 >= NUMBER_OF_PRIMES {
NUMBER_OF_PRIMES - 1
} else {
nbits / 2
} as usize;
let mut moduli = vec![BigUint::zero(); prime_limit];
'outer: loop {
let mut prime = 3;
for i in 0..prime_limit {
moduli[i] = &res / prime;
prime += PRIME_GAP[i];
}
let mut difference: usize = 0;
for incr in (0..INCR_LIMIT as u64).step_by(2) {
let mut prime: u64 = 3;
let mut cancel = false;
for i in 0..prime_limit {
let r = (&moduli[i] + incr) % prime;
prime += PRIME_GAP[i];
if r.is_zero() {
cancel = true;
break;
}
}
if !cancel {
res += difference;
difference = 0;
if probably_prime(&res, 20) {
break 'outer;
}
}
difference += 2;
}
res += difference;
}
res
}
pub fn jacobi(x: &BigInt, y: &BigInt) -> isize {
if !y.is_odd() {
panic!(
"invalid arguments, y must be an odd integer,but got {:?}",
y
);
}
let mut a = x.clone();
let mut b = y.clone();
let mut j = 1;
if b.is_negative() {
if a.is_negative() {
j = -1;
}
b = -b;
}
loop {
if b.is_one() {
return j;
}
if a.is_zero() {
return 0;
}
a = a.mod_floor(&b);
if a.is_zero() {
return 0;
}
let s = a.trailing_zeros().unwrap();
if s & 1 != 0 {
let bmod8 = (&b & BigInt::from(7)).to_u64().unwrap();
if bmod8 == 3 || bmod8 == 5 {
j = -j;
}
}
let c = &a >> s;
if &b & BigInt::from(3) == BigInt::from(3) && &c & BigInt::from(3) == BigInt::from(3) {
j = -j
}
a = b;
b = c;
}
}
fn is_bit_set(x: &BigUint, i: usize) -> bool {
((x >> i) & &*BIG_1) == *BIG_1
}
fn gen_biguint_below<R: Rng>(r: &mut R, upper: &BigUint) -> BigUint {
loop {
let bits = upper.bits();
let bytes = bits.div_ceil(&8);
let mut buf = vec![0u8; bytes as usize];
r.fill_bytes(&mut buf);
let mask = 0xff_u8 >> (bytes * 8 - bits);
buf[0] &= mask;
let n = BigUint::from_bytes_be(&buf);
if &n < upper {
break n;
}
}
}