use crate::uint::{u256, u512, U256};
mod constants;
use constants::{D_VALUES, SMALL_PRIMES};
pub fn is_prob_prime(n: &U256) -> bool {
for &p in SMALL_PRIMES.iter() {
if n.is_divisible_u(p) {
return *n == p;
}
}
passes_miller_rabin_base_2(&n) && passes_lucas(&n)
}
pub fn passes_miller_rabin_base_2(n: &U256) -> bool {
let (d, r) = (n - 1).remove_factor(u256(2));
let mut x = u256(2).pow_mod(d, n);
if x == 1 || x == n - 1 {
return true;
}
for _ in 1..r {
x = x * x % n;
if x == 1 {
return false;
}
if x == n - 1 {
return true;
}
}
false
}
pub fn passes_lucas(n: &U256) -> bool {
let d_ = choose_d(n);
if d_.is_err() {
return false;
}
let d = d_.unwrap();
let q = (1 - d) / 4;
let (u_delta, v_delta, q_delta_over_2) =
compute_lucas_sequences(*n + 1, n, u256(1), u256(1), q, d);
u_delta == 0
&& v_delta.is_congruent(2 * q, n)
&& q_delta_over_2.is_congruent(q * U256::jacobi(q, n), n)
}
#[derive(Debug)]
struct IsPerfectSquare();
fn choose_d(n: &U256) -> Result<i32, IsPerfectSquare> {
if n.is_perfect_square() {
return Err(IsPerfectSquare());
}
for &d in D_VALUES.iter() {
if U256::jacobi(d, n) == -1 {
return Ok(d);
}
}
panic!("n is not square but we still couldn't find a d value!")
}
#[allow(clippy::cast_sign_loss)]
fn compute_lucas_sequences(
k_target: U256,
n: &U256,
mut u: U256,
mut v: U256,
q0: i32,
d: i32,
) -> (U256, U256, U256) {
let i_mod_n = |x: i32| {
if x < 0 {
*n - (u256(x.abs() as u64) % n)
} else {
u256(x as u64) % n
}
};
let q0 = i_mod_n(q0);
let d = i_mod_n(d);
let mut q = q0;
let mut q_k_over_2 = q0;
let half = |x: U256| {
if x.is_odd() {
(x >> 1) + (*n >> 1) + 1
} else {
x >> 1
}
};
let sub_mod_n = |a, b| {
if a > b {
(a - b) % n
} else {
*n - (b - a) % n
}
};
let mut k_target_bits = [0; 257];
let len = k_target.write_binary(&mut k_target_bits);
for &bit in k_target_bits[..len].iter().skip(1) {
u = u * v % n;
v = sub_mod_n(v * v, u512(q) << 1);
q_k_over_2 = q;
q = q * q % n;
if bit == 1 {
let u_old = u;
u = half((u512(u) + u512(v)) % n);
v = half((d * u_old + u512(v)) % n);
q = q * q0 % n;
}
}
(u, v, q_k_over_2)
}
#[cfg(test)]
mod tests {
use self::constants::*;
use super::*;
#[test]
fn test_miller_rabin() {
assert!(passes_miller_rabin_base_2(&u256(13)));
assert!(!passes_miller_rabin_base_2(&u256(65)));
for &p in LARGE_PRIMES.iter() {
assert!(passes_miller_rabin_base_2(&u256(p)));
assert!(!passes_miller_rabin_base_2(
&(u256(p) * u256(106_957)).low_u256()
));
}
for &n in STRONG_BASE_2_PSEUDOPRIMES.iter() {
assert!(passes_miller_rabin_base_2(&u256(n)));
}
}
#[test]
fn test_lucas() {
assert!(passes_lucas(&u256(5)));
for &sp in SMALL_PRIMES[1..].iter() {
assert!(passes_lucas(&u256(sp)));
assert!(!passes_lucas(&(u256(sp) * u256(2047)).low_u256()));
}
for &mp in MED_PRIMES.iter() {
assert!(passes_lucas(&u256(mp)));
assert!(!passes_lucas(&(u256(mp) * u256(5)).low_u256()));
}
for &lp in LARGE_PRIMES.iter() {
assert!(passes_lucas(&u256(lp)));
assert!(!passes_lucas(&(u256(lp) * u256(7)).low_u256()));
}
}
#[test]
fn test_is_prob_prime() {
assert!(is_prob_prime(&u256(2)));
assert!(is_prob_prime(&u256(5)));
assert!(is_prob_prime(&u256(7)));
assert!(is_prob_prime(&u256(241)));
assert!(is_prob_prime(&u256(7919)));
assert!(is_prob_prime(&u256(48131)));
assert!(is_prob_prime(&u256(76463)));
assert!(is_prob_prime(&u256(115_547)));
for &p in MED_PRIMES.iter() {
assert!(is_prob_prime(&u256(p)));
}
for &p in LARGE_PRIMES.iter() {
assert!(is_prob_prime(&u256(p)));
}
for &p in LARGE_PRIMES.iter() {
for &q in LARGE_PRIMES.iter() {
assert!(!is_prob_prime(&(u256(p) * u256(q)).low_u256()));
}
}
}
}