use crate::{error::MathError, integer::Z, traits::Pow};
use flint_sys::fmpz::fmpz_pow_fmpz;
impl<Integer: Into<Z>> Pow<Integer> for Z {
type Output = Z;
fn pow(&self, exp: Integer) -> Result<Self::Output, MathError> {
let exp = exp.into();
let mut out = Z::ZERO;
if exp < Z::ZERO {
return Err(MathError::InvalidExponent(format!(
"A negative exponent {exp} was used for the integer value {self}.
If you want to get the inverse as a rational object in return use `.inverse().pow({})`",
-1 * &exp
)));
}
unsafe { fmpz_pow_fmpz(&mut out.value, &self.value, &exp.value) };
Ok(out)
}
}
#[cfg(test)]
mod test_pow {
use super::*;
#[test]
fn small() {
let base = Z::from(2);
let exp_pos = Z::from(4);
let zero = Z::ZERO;
let res_0 = base.pow(&exp_pos).unwrap();
let res_1 = base.pow(&zero).unwrap();
let res_2 = zero.pow(&zero).unwrap();
let res_3 = zero.pow(&exp_pos).unwrap();
assert_eq!(Z::from(16), res_0);
assert_eq!(Z::from(1), res_1);
assert_eq!(Z::ONE, res_2);
assert_eq!(Z::ZERO, res_3);
}
#[test]
fn large() {
let base = Z::from(i64::MIN);
let exp_pos = Z::from(3);
let zero = Z::ZERO;
let cmp = &base * &base * &base;
let res_0 = base.pow(&exp_pos).unwrap();
let res_1 = base.pow(&zero).unwrap();
assert_eq!(cmp, res_0);
assert_eq!(Z::ONE, res_1);
}
#[test]
fn availability() {
let base = Z::from(i64::MAX);
let exp = Z::from(4);
let _ = base.pow(exp);
let _ = base.pow(2_i8);
let _ = base.pow(2_i16);
let _ = base.pow(2_i32);
let _ = base.pow(2_i64);
let _ = base.pow(2_u8);
let _ = base.pow(2_u16);
let _ = base.pow(2_u32);
let _ = base.pow(2_u64);
}
#[test]
fn non_invertible_detection() {
let base_0 = Z::from(4);
let base_1 = Z::from(u64::MAX);
assert!(base_0.pow(-1).is_err());
assert!(base_1.pow(-1).is_err());
}
}