primal_check/perfect_power.rs
1use num_integer::Integer;
2
3fn wrapping_pow(mut base: u64, mut exp: u32) -> u64 {
4 let mut acc: u64 = 1;
5 while exp > 0 {
6 if exp % 2 == 1 {
7 acc = acc.wrapping_mul(base)
8 }
9 base = base.wrapping_mul(base);
10 exp /= 2;
11 }
12 acc
13}
14
15/// Returns integers `(y, k)` such that `x = y^k` with `k` maximised
16/// (other than for `x = 0, 1`, in which case `y = x`, `k = 1`).
17///
18/// # Examples
19///
20/// ```rust
21/// # use primal_check as primal;
22/// assert_eq!(primal::as_perfect_power(2), (2, 1));
23/// assert_eq!(primal::as_perfect_power(4), (2, 2));
24/// assert_eq!(primal::as_perfect_power(8), (2, 3));
25/// assert_eq!(primal::as_perfect_power(1024), (2, 10));
26///
27/// assert_eq!(primal::as_perfect_power(1000), (10, 3));
28///
29/// assert_eq!(primal::as_perfect_power(15), (15, 1));
30/// ```
31pub fn as_perfect_power(x: u64) -> (u64, u8) {
32 if x == 0 || x == 1 {
33 return (x, 1)
34 }
35
36 let floor_log_2 = 64 - x.leading_zeros() as u32 - 1;
37
38 let x_ = x as f64;
39 let mut last = (x, 1);
40 // TODO: we could be smarter about this: we know all the possible
41 // primes that can divide the exponent (since we have a list up to
42 // 251 >= 64), so we really only need to check them.
43 let mut expn: u32 = 2;
44 let mut step = 1;
45 while expn <= floor_log_2 {
46 let factor = x_.powf(1.0/expn as f64).round() as u64;
47 // the only case this will wrap is if x is close to 2^64 and
48 // the round() rounds up, pushing this calculation over the
49 // edge, however, the overflow will be well away from x, so we
50 // still correctly don't take this branch. (x can't be a
51 // perfect power if the result rounds away.)
52 if wrapping_pow(factor, expn) == x {
53 last = (factor, expn as u8);
54 // if x is a 2nd and 5th power, it's going to be a 10th
55 // power too, meaning we can search faster.
56 // TODO: check if this is actually saving work
57 step = step.lcm(&expn);
58 }
59
60 expn += step;
61 }
62 last
63}
64
65/// Return `Some((p, k))` if `x = p^k` for some prime `p` and `k >= 1`
66/// (that is, including when `x` is itself a prime).
67///
68/// Returns `None` if `x` not a perfect power.
69///
70/// # Examples
71///
72/// ```rust
73/// # use primal_check as primal;
74/// assert_eq!(primal::as_prime_power(2), Some((2, 1)));
75/// assert_eq!(primal::as_prime_power(4), Some((2, 2)));
76/// assert_eq!(primal::as_prime_power(8), Some((2, 3)));
77/// assert_eq!(primal::as_prime_power(1024), Some((2, 10)));
78///
79/// assert_eq!(primal::as_prime_power(1000), None);
80///
81/// assert_eq!(primal::as_prime_power(15), None);
82/// ```
83pub fn as_prime_power(x: u64) -> Option<(u64, u8)> {
84 let (y, k) = as_perfect_power(x);
85 if crate::miller_rabin(y) {
86 Some((y, k))
87 } else {
88 None
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use primal::Sieve;
95
96 use super::{as_perfect_power, as_prime_power};
97
98 #[test]
99 fn perfect_and_prime_power() {
100 let tests = [
101 (0, (0, 1), false),
102 (1, (1, 1), false),
103 (2, (2, 1), true),
104 (3, (3, 1), true),
105 (4, (2, 2), true),
106 (5, (5, 1), true),
107 (6, (6, 1), false),
108 (8, (2, 3), true),
109 (9, (3, 2), true),
110 (16, (2, 4), true),
111 (25, (5, 2), true),
112 (32, (2, 5), true),
113 (36, (6, 2), false),
114 (100, (10, 2), false),
115 (1000, (10, 3), false),
116 ];
117
118 for &(x, expected, is_prime) in tests.iter() {
119 assert_eq!(as_perfect_power(x), expected);
120 assert_eq!(as_prime_power(x),
121 if is_prime { Some(expected)} else { None })
122 }
123
124 let sieve = Sieve::new(200);
125 let mut primes = sieve.primes_from(0);
126 const MAX: f64 = 0xFFFF_FFFF_FFFF_FFFFu64 as f64;
127 // test a whole pile of (semi)primes
128 loop {
129 let p = match primes.next() {
130 Some(p) => p as u64,
131 None => break
132 };
133
134 let subprimes = primes.clone().map(|x| (x, false));
135 // include 1 to test p itself.
136 for (q, is_prime) in Some((1, true)).into_iter().chain(subprimes) {
137 let pq = p * q as u64;
138 for n in 1..(MAX.log(pq as f64) as u32) {
139 let x = pq.pow(n);
140
141 let expected = (pq, n as u8);
142 assert_eq!(as_perfect_power(x), expected);
143 assert_eq!(as_prime_power(x),
144 if is_prime { Some(expected) } else { None });
145 }
146 }
147 }
148 }
149}