use crate::math::{AsType, Integer};
pub trait IntRoot
where
Self: Integer + AsType<f64>,
f64: AsType<Self>,
{
fn is_perfect_pow(&self, k: usize) -> bool {
assert!(k >= 1);
self.root(k).is_some()
}
fn root(&self, k: usize) -> Option<Self> {
let x = self.root_floor(k);
if x.pow(k as u32) == *self {
Some(x)
} else {
None
}
}
fn sqrt(&self) -> Option<Self> {
assert!(
self >= &Self::zero(),
"Cannot compute square root of a negative number: {self}",
);
self.root(2)
}
fn cbrt(&self) -> Option<Self> {
self.root(3)
}
#[must_use]
fn root_floor(&self, k: usize) -> Self;
#[must_use]
fn root_ceil(&self, k: usize) -> Self {
let n = *self;
assert!(n >= Self::zero());
let x = self.root_floor(k);
if x.pow(k as u32) == n {
x
} else {
x + Self::one()
}
}
}
#[macro_export]
macro_rules! impl_int_root_unsigned {
($($t: ident)+) => {$(
impl $crate::math::root::IntRoot for $t {
fn root_floor(&self, k: usize) -> Self {
let n = *self;
let mut x = ((n as f64).powf(1.0 / k as f64).floor()) as $t;
while x.pow(k as u32) > n {
x -= 1;
}
x
}
}
)+};
}
#[macro_export]
macro_rules! impl_int_root_signed {
($($t: ident $u: ident),+) => {$(
impl $crate::math::root::IntRoot for $t {
fn root_floor(&self, k: usize) -> Self {
assert!(k >= 1);
let n = *self;
return if n >= 0 {
(n as $u).root_floor(k) as $t
} else {
assert!(k.is_odd(), "Cannot compute even root of a negative number: {}", n);
-((n.wrapping_neg() as $u).root_floor(k) as $t)
}
}
}
)+};
}
impl_int_root_unsigned!(u16 u32 u64 usize);
impl_int_root_signed!(i16 u16, i32 u32, i64 u64, isize usize);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_root() {
assert_eq!((-8_i64).root(3), Some(-2));
assert_eq!(27_i64.root(3), Some(3));
assert_eq!(28_i64.root(3), None);
assert_eq!(28_i64.root_floor(3), 3);
assert_eq!(28_i64.root_ceil(3), 4);
assert_eq!(29_i64.root_ceil(3), 4);
assert_eq!(0_i64.root(2), Some(0));
assert_eq!(1_i64.root(2), Some(1));
assert_eq!(2_i64.root(2), None);
assert_eq!(3_i64.root(2), None);
assert_eq!(4_i64.root(2), Some(2));
assert_eq!(12_i64.root(2), None);
assert_eq!(12_i64.root_floor(2), 3);
assert_eq!(12_i64.root_ceil(2), 4);
let x: i32 = 12345;
assert_eq!(x.root(1), Some(x));
assert_eq!(x.root(2), x.sqrt());
assert_eq!(x.root(3), x.cbrt());
assert_eq!(x.root(4), None);
assert_eq!(x.root_floor(4), 10);
assert_eq!(x.root_floor(13), 2);
assert_eq!(x.root_floor(14), 1);
assert_eq!(x.root_floor(std::usize::MAX), 1);
assert_eq!(std::i32::MAX.root_floor(30), 2);
assert_eq!(std::i32::MAX.root_floor(31), 1);
assert_eq!(std::i32::MIN.root_floor(31), -2);
assert_eq!((std::i32::MIN + 1).root_floor(31), -1);
assert_eq!(std::u32::MAX.root_floor(31), 2);
assert_eq!(std::u32::MAX.root_floor(32), 1);
}
}