Skip to main content

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}