malachite_nz/natural/factorization/is_power.rs
1// Copyright © 2026 William Youmans
2//
3// Uses code adopted from the FLINT Library.
4//
5// This file is part of Malachite.
6//
7// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
8// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
9// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
10
11use crate::natural::InnerNatural::{Large, Small};
12use crate::natural::Natural;
13use malachite_base::num::arithmetic::traits::{CheckedRoot, DivAssignMod, DivMod, GcdAssign};
14use malachite_base::num::basic::traits::One;
15use malachite_base::num::factorization::traits::{
16 ExpressAsPower, Factor, IsPower, IsPrime, Primes,
17};
18use malachite_base::num::logic::traits::SignificantBits;
19
20const PRIMES: [u32; 168] = [
21 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
22 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,
23 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
24 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421,
25 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547,
26 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
27 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
28 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929,
29 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,
30];
31
32// Find ONE perfect power representation for a Natural (not necessarily the smallest base).
33//
34// This function does NOT recurse - it just checks if n can be expressed as some base^exp.
35fn get_perfect_power_natural(n: &Natural) -> Option<(Natural, u64)> {
36 // Find largest power of 2 dividing n
37 let mut pow_2 = n.trailing_zeros().unwrap();
38 // Two divides exactly once - not a perfect power
39 if pow_2 == 1 {
40 return None;
41 }
42 // If pow_2 is prime, just check if n is a perfect pow_2-th power
43 if pow_2.is_prime() {
44 return n.checked_root(pow_2).map(|root| (root, pow_2));
45 }
46 // Divide out 2^pow_2 to get the odd part
47 let mut q = n >> pow_2;
48 // Factor out powers of small primes
49 for &prime in PRIMES.iter().skip(1) {
50 let prime = Natural::from(prime);
51 let (new_q, r) = (&q).div_mod(&prime);
52 if r == 0u32 {
53 q = new_q;
54 if q.div_assign_mod(&prime) != 0u32 {
55 return None; // prime divides exactly once, reject
56 }
57 let mut pow_p = 2u64;
58 loop {
59 let (new_q, r) = (&q).div_mod(&prime);
60 if r == 0 {
61 q = new_q;
62 pow_p += 1;
63 } else {
64 break;
65 }
66 }
67 pow_2.gcd_assign(pow_p);
68 if pow_2 == 1 {
69 return None; // we have multiplicity 1 of some factor
70 }
71 // As soon as pow_2 becomes prime, stop factoring
72 if q == 1u32 || pow_2.is_prime() {
73 return n.checked_root(pow_2).map(|root| (root, pow_2));
74 }
75 }
76 }
77 // After factoring, check remaining cases
78 if pow_2 == 0 {
79 // No factors found above; exhaustively check all prime exponents
80 let bits = n.significant_bits();
81 for nth in u64::primes() {
82 // Terminate if exponent exceeds bit length (n ^ (1 / nth) < 2 for nth > bits)
83 if nth > bits {
84 return None;
85 }
86 if let Some(root) = n.checked_root(nth) {
87 return Some((root, nth));
88 }
89 }
90 } else {
91 // Found some factors; only check prime divisors of pow_2
92 for (nth, _) in pow_2.factor() {
93 if let Some(root) = n.checked_root(nth) {
94 return Some((root, nth));
95 }
96 }
97 }
98 None
99}
100
101// Boolean check: is n a perfect power?
102//
103// Note: This function computes roots the same number of times as get_perfect_power_natural, but
104// discards the root values, only returning whether a perfect power representation exists.
105//
106// Left here in case we can find a way to optimize out root computations later.
107fn get_perfect_power_natural_bool(n: &Natural) -> bool {
108 // Find largest power of 2 dividing n
109 let mut pow_2 = n.trailing_zeros().unwrap();
110 // Two divides exactly once - not a perfect power
111 if pow_2 == 1 {
112 return false;
113 }
114 // If pow_2 is prime, check if n is a perfect pow_2-th power
115 if pow_2.is_prime() {
116 return n.checked_root(pow_2).is_some();
117 }
118 // Divide out 2^pow_2 to get the odd part
119 let mut q = n >> pow_2;
120 // Factor out powers of small primes
121 for &prime in PRIMES.iter().skip(1) {
122 let prime = Natural::from(prime);
123 let (new_q, r) = (&q).div_mod(&prime);
124 if r == 0 {
125 q = new_q;
126 if q.div_assign_mod(&prime) != 0u32 {
127 return false; // prime divides exactly once, reject
128 }
129 let mut pow_p = 2u64;
130 loop {
131 let (new_q, r) = (&q).div_mod(&prime);
132 if r == 0 {
133 q = new_q;
134 pow_p += 1;
135 } else {
136 break;
137 }
138 }
139 pow_2.gcd_assign(pow_p);
140 if pow_2 == 1 {
141 return false; // we have multiplicity 1 of some factor
142 }
143 // As soon as pow_2 becomes prime, stop factoring
144 if q == Natural::ONE || pow_2.is_prime() {
145 return n.checked_root(pow_2).is_some();
146 }
147 }
148 }
149 // After factoring, check remaining cases
150 if pow_2 == 0 {
151 // No factors found above; exhaustively check all prime exponents
152 let bits = n.significant_bits();
153 for nth in u64::primes() {
154 // Terminate if exponent exceeds bit length (n ^ (1 / nth) < 2 for nth > bits)
155 if nth > bits {
156 return false;
157 }
158 if n.checked_root(nth).is_some() {
159 return true;
160 }
161 }
162 } else {
163 // Found some factors; only check prime divisors of pow_2
164 for (nth, _) in pow_2.factor() {
165 if n.checked_root(nth).is_some() {
166 return true;
167 }
168 }
169 }
170 false
171}
172
173// Express Natural as a power with the smallest possible base
174//
175// Note: This function is only called for multi-limb numbers (Large variant).
176fn express_as_power_natural(n: &Natural) -> Option<(Natural, u64)> {
177 // Get initial representation
178 let (mut base, mut exp) = get_perfect_power_natural(n)?;
179 // Continue until we have the smallest possible base
180 while base > 3u32 {
181 match get_perfect_power_natural(&base) {
182 Some((base2, exp2)) => {
183 base = base2;
184 exp *= exp2;
185 }
186 None => break,
187 }
188 }
189 Some((base, exp))
190}
191
192// Is Natural a perfect power?
193//
194// Note: This function is only called for multi-limb numbers
195#[inline]
196fn is_power_natural(n: &Natural) -> bool {
197 get_perfect_power_natural_bool(n)
198}
199
200impl ExpressAsPower for Natural {
201 /// Expresses a [`Natural`] as a perfect power if possible.
202 ///
203 /// Returns `Some((root, exponent))` where `root ^ exponent = self` and `exponent > 1`, or
204 /// `None` if the number cannot be expressed as a perfect power.
205 ///
206 /// # Examples
207 /// ```
208 /// use malachite_base::num::factorization::traits::ExpressAsPower;
209 /// use malachite_nz::natural::Natural;
210 ///
211 /// assert_eq!(
212 /// Natural::from(8u32).express_as_power(),
213 /// Some((Natural::from(2u32), 3))
214 /// );
215 /// assert_eq!(
216 /// Natural::from(16u32).express_as_power(),
217 /// Some((Natural::from(2u32), 4))
218 /// );
219 /// assert_eq!(Natural::from(6u32).express_as_power(), None);
220 /// ```
221 fn express_as_power(&self) -> Option<(Self, u64)> {
222 match self {
223 // use the single-limb express_as_power impl for primitive integers
224 Self(Small(small)) => small
225 .express_as_power()
226 .map(|(root, exp)| (Self::from(root), exp)),
227 Self(Large(_)) => express_as_power_natural(self),
228 }
229 }
230}
231
232impl IsPower for Natural {
233 /// Determines whether a [`Natural`] is a perfect power.
234 ///
235 /// A perfect power is any number of the form $a^x$ where $x > 1$, with $a$ and $x$ both
236 /// integers. In particular, 0 and 1 are considered perfect powers.
237 ///
238 /// # Examples
239 /// ```
240 /// use malachite_base::num::factorization::traits::IsPower;
241 /// use malachite_nz::natural::Natural;
242 ///
243 /// assert_eq!(Natural::from(0u32).is_power(), true);
244 /// assert_eq!(Natural::from(1u32).is_power(), true);
245 /// assert_eq!(Natural::from(4u32).is_power(), true);
246 /// assert_eq!(Natural::from(6u32).is_power(), false);
247 /// assert_eq!(Natural::from(8u32).is_power(), true);
248 /// ```
249 fn is_power(&self) -> bool {
250 match self {
251 // use the single-limb is_power impl for primitive integers
252 Self(Small(small)) => small.is_power(),
253 Self(Large(_)) => is_power_natural(self),
254 }
255 }
256}