use num_bigint::BigUint;
use num_traits::One;
use num_traits::Zero;
use crate::operations::{ pow, pow_mod };
use crate::operations::utils::get_trailing_zeros;
pub fn miller_rabin(num: &BigUint) -> bool {
if *num <= BigUint::one() {
return false;
}
if *num == BigUint::from(2u32) {
return true;
}
let one_minus_num: BigUint = num - BigUint::one();
let s: &BigUint = &get_trailing_zeros(&one_minus_num);
let d: &BigUint = &(one_minus_num / pow(&BigUint::from(2u32), s));
let switnesses = vec![2, 3, 5, 7, 11];
let witnesses: Vec<BigUint> = switnesses
.iter()
.map(|x| BigUint::from(*x as u32))
.collect();
for a in witnesses {
if a >= *num {
continue;
}
if pow_mod(&a, &d, num) == BigUint::one() {
continue;
}
let mut found = false;
let mut r = BigUint::zero();
while r < *s {
let a_power = d * pow(&BigUint::from(2u32), &r);
if (pow_mod(&a, &a_power, num) + BigUint::one()) % num == BigUint::zero() {
found = true;
break;
}
r = r + BigUint::one();
}
if !found {
println!("Miller Rabin test failed for {}, witness {}", num, a);
return false;
}
}
return true;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::generators::get_max_primes;
#[test]
fn edge_cases() {
assert_eq!(miller_rabin(&BigUint::zero()), false);
assert_eq!(miller_rabin(&BigUint::one()), false);
assert_eq!(miller_rabin(&BigUint::from(2u32)), true);
assert_eq!(miller_rabin(&BigUint::from(3u32)), true);
assert_eq!(miller_rabin(&BigUint::from(4u32)), false);
}
#[test]
fn large_primes() {
let primes = [
"871603259",
"98762051",
"1000000007",
"123575321",
"193818613",
"444444443",
"999999937",
"1000000000039",
"9999999929",
];
for prime in primes {
let prime = BigUint::parse_bytes(prime.as_bytes(), 10).unwrap();
assert_eq!(miller_rabin(&prime), true);
}
}
#[test]
fn continuous_test() {
let primes = get_max_primes(100000);
for prime in primes {
assert_eq!(miller_rabin(&prime), true);
}
}
#[test]
fn large_composites() {
let primes = [
"871603259",
"98762051",
"1000000007",
"123575321",
"193818613",
"444444443",
"999999937",
"1000000000039",
"9999999929",
];
for i in 0..primes.len() {
for j in 0..primes.len() {
if i == j {
continue;
}
let composite =
BigUint::parse_bytes(primes[i].as_bytes(), 10).unwrap() *
BigUint::parse_bytes(primes[j].as_bytes(), 10).unwrap();
assert_eq!(miller_rabin(&composite), false);
}
}
}
#[test]
fn counter_example() {
let counter_example = BigUint::parse_bytes(b"2152302898747", 10).unwrap();
assert_eq!(miller_rabin(&counter_example), true);
}
}