use crate::num::arithmetic::traits::{Parity, UnsignedAbs};
use crate::num::basic::signeds::PrimitiveSigned;
use crate::num::basic::unsigneds::PrimitiveUnsigned;
use crate::num::conversion::traits::WrappingFrom;
use crate::num::factorization::traits::{RemovePower, RemovePowerAssign};
fn remove_power_unsigned<T: PrimitiveUnsigned>(mut x: T, y: T) -> (T, u64) {
assert!(y > T::ONE, "Cannot remove powers of {y}");
if x == T::ZERO {
return (x, 0);
}
if y == T::TWO {
let k = x.trailing_zeros();
return (x >> k, k);
}
let mut k = 0;
loop {
let (q, r) = x.div_mod(y);
if r != T::ZERO {
return (x, k);
}
x = q;
k += 1;
}
}
fn remove_power_signed<T: PrimitiveSigned + WrappingFrom<<T as UnsignedAbs>::Output>>(
x: T,
y: T,
) -> (T, u64)
where
<T as UnsignedAbs>::Output: PrimitiveUnsigned,
{
assert!(
y > T::ONE || y < T::NEGATIVE_ONE,
"Cannot remove powers of {y}"
);
let (abs, k) = remove_power_unsigned(x.unsigned_abs(), y.unsigned_abs());
let q = T::wrapping_from(abs);
(
if (x < T::ZERO) == (y < T::ZERO && k.odd()) {
q
} else {
q.wrapping_neg()
},
k,
)
}
macro_rules! impl_remove_power {
($t:ident, $f:ident) => {
impl RemovePower<$t> for $t {
type Output = $t;
#[inline]
fn remove_power(self, other: $t) -> ($t, u64) {
$f(self, other)
}
}
impl RemovePowerAssign<$t> for $t {
#[inline]
fn remove_power_assign(&mut self, other: $t) -> u64 {
let (q, k) = $f(*self, other);
*self = q;
k
}
}
};
}
macro_rules! impl_remove_power_unsigned {
($t:ident) => {
impl_remove_power!($t, remove_power_unsigned);
};
}
macro_rules! impl_remove_power_signed {
($t:ident) => {
impl_remove_power!($t, remove_power_signed);
};
}
apply_to_unsigneds!(impl_remove_power_unsigned);
apply_to_signeds!(impl_remove_power_signed);