Skip to main content

malachite_base/num/factorization/
remove_power.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::num::arithmetic::traits::{Parity, UnsignedAbs};
10use crate::num::basic::signeds::PrimitiveSigned;
11use crate::num::basic::unsigneds::PrimitiveUnsigned;
12use crate::num::conversion::traits::WrappingFrom;
13use crate::num::factorization::traits::{RemovePower, RemovePowerAssign};
14
15fn remove_power_unsigned<T: PrimitiveUnsigned>(mut x: T, y: T) -> (T, u64) {
16    assert!(y > T::ONE, "Cannot remove powers of {y}");
17    if x == T::ZERO {
18        // every power of `y` divides zero, so, as GMP does, leave it alone
19        return (x, 0);
20    }
21    if y == T::TWO {
22        // the exponent is just the number of trailing zeros, which beats dividing repeatedly; GMP
23        // special-cases a factor of 2 the same way
24        let k = x.trailing_zeros();
25        return (x >> k, k);
26    }
27    let mut k = 0;
28    loop {
29        let (q, r) = x.div_mod(y);
30        if r != T::ZERO {
31            return (x, k);
32        }
33        x = q;
34        k += 1;
35    }
36}
37
38fn remove_power_signed<T: PrimitiveSigned + WrappingFrom<<T as UnsignedAbs>::Output>>(
39    x: T,
40    y: T,
41) -> (T, u64)
42where
43    <T as UnsignedAbs>::Output: PrimitiveUnsigned,
44{
45    assert!(
46        y > T::ONE || y < T::NEGATIVE_ONE,
47        "Cannot remove powers of {y}"
48    );
49    let (abs, k) = remove_power_unsigned(x.unsigned_abs(), y.unsigned_abs());
50    // The quotient is the exact division by the signed power: negative when the value is, and
51    // negated again when the factor is negative and the power is odd. Only the negative case can
52    // reach the magnitude of `T::MIN`, and there it is representable, so the wrapping conversion is
53    // exact.
54    let q = T::wrapping_from(abs);
55    (
56        if (x < T::ZERO) == (y < T::ZERO && k.odd()) {
57            q
58        } else {
59            q.wrapping_neg()
60        },
61        k,
62    )
63}
64
65macro_rules! impl_remove_power {
66    ($t:ident, $f:ident) => {
67        impl RemovePower<$t> for $t {
68            type Output = $t;
69
70            /// Removes the largest power of a factor from a number, returning the reduced number
71            /// together with the exponent of that power.
72            ///
73            /// If $f^k$ is the largest power of `other` that divides `self`, this returns
74            /// $(\text{self}/f^k, k)$. The factor need not be prime. Zero is left alone, with an
75            /// exponent of 0, since every power of the factor divides it.
76            ///
77            /// For signed types the quotient is the exact division by the signed power, so a
78            /// negative factor raised to an odd power flips its sign.
79            ///
80            /// # Worst-case complexity
81            /// $T(n) = O(n)$
82            ///
83            /// $M(n) = O(1)$
84            ///
85            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`:
86            /// each division by `other`, which is at least 2, removes at least one bit.
87            ///
88            /// # Panics
89            /// Panics if `other` is 0 or 1, or, for signed types, -1: no largest power exists in
90            /// those cases.
91            ///
92            /// # Examples
93            /// See [here](super::remove_power#remove_power).
94            #[inline]
95            fn remove_power(self, other: $t) -> ($t, u64) {
96                $f(self, other)
97            }
98        }
99
100        impl RemovePowerAssign<$t> for $t {
101            /// Divides a number by the largest power of a factor that divides it, in place,
102            /// returning the exponent of that power.
103            ///
104            /// The factor need not be prime. Zero is left alone, with an exponent of 0.
105            ///
106            /// # Worst-case complexity
107            /// $T(n) = O(n)$
108            ///
109            /// $M(n) = O(1)$
110            ///
111            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`:
112            /// each division by `other`, which is at least 2, removes at least one bit.
113            ///
114            /// # Panics
115            /// Panics if `other` is 0 or 1, or, for signed types, -1.
116            ///
117            /// # Examples
118            /// See [here](super::remove_power#remove_power_assign).
119            #[inline]
120            fn remove_power_assign(&mut self, other: $t) -> u64 {
121                let (q, k) = $f(*self, other);
122                *self = q;
123                k
124            }
125        }
126    };
127}
128macro_rules! impl_remove_power_unsigned {
129    ($t:ident) => {
130        impl_remove_power!($t, remove_power_unsigned);
131    };
132}
133macro_rules! impl_remove_power_signed {
134    ($t:ident) => {
135        impl_remove_power!($t, remove_power_signed);
136    };
137}
138apply_to_unsigneds!(impl_remove_power_unsigned);
139apply_to_signeds!(impl_remove_power_signed);