use num_integer::Integer;
fn wrapping_pow(mut base: u64, mut exp: u32) -> u64 {
let mut acc: u64 = 1;
while exp > 0 {
if exp % 2 == 1 {
acc = acc.wrapping_mul(base)
}
base = base.wrapping_mul(base);
exp /= 2;
}
acc
}
pub fn as_perfect_power(x: u64) -> (u64, u8) {
if x == 0 || x == 1 {
return (x, 1)
}
let floor_log_2 = 64 - x.leading_zeros() as u32 - 1;
let x_ = x as f64;
let mut last = (x, 1);
let mut expn: u32 = 2;
let mut step = 1;
while expn <= floor_log_2 {
let factor = x_.powf(1.0/expn as f64).round() as u64;
if wrapping_pow(factor, expn) == x {
last = (factor, expn as u8);
step = step.lcm(&expn);
}
expn += step;
}
last
}
pub fn as_prime_power(x: u64) -> Option<(u64, u8)> {
let (y, k) = as_perfect_power(x);
if crate::miller_rabin(y) {
Some((y, k))
} else {
None
}
}
#[cfg(test)]
mod tests {
use primal::Sieve;
use super::{as_perfect_power, as_prime_power};
#[test]
fn perfect_and_prime_power() {
let tests = [
(0, (0, 1), false),
(1, (1, 1), false),
(2, (2, 1), true),
(3, (3, 1), true),
(4, (2, 2), true),
(5, (5, 1), true),
(6, (6, 1), false),
(8, (2, 3), true),
(9, (3, 2), true),
(16, (2, 4), true),
(25, (5, 2), true),
(32, (2, 5), true),
(36, (6, 2), false),
(100, (10, 2), false),
(1000, (10, 3), false),
];
for &(x, expected, is_prime) in tests.iter() {
assert_eq!(as_perfect_power(x), expected);
assert_eq!(as_prime_power(x),
if is_prime { Some(expected)} else { None })
}
let sieve = Sieve::new(200);
let mut primes = sieve.primes_from(0);
const MAX: f64 = 0xFFFF_FFFF_FFFF_FFFFu64 as f64;
loop {
let p = match primes.next() {
Some(p) => p as u64,
None => break
};
let subprimes = primes.clone().map(|x| (x, false));
for (q, is_prime) in Some((1, true)).into_iter().chain(subprimes) {
let pq = p * q as u64;
for n in 1..(MAX.log(pq as f64) as u32) {
let x = pq.pow(n);
let expected = (pq, n as u8);
assert_eq!(as_perfect_power(x), expected);
assert_eq!(as_prime_power(x),
if is_prime { Some(expected) } else { None });
}
}
}
}
}