use crate::{error::MathError, integer::Z, integer_mod_q::Zq, traits::Pow};
use flint_sys::fmpz_mod::fmpz_mod_pow_fmpz;
impl<Integer: Into<Z>> Pow<Integer> for Zq {
type Output = Zq;
fn pow(&self, exp: Integer) -> Result<Self::Output, MathError> {
let exp = exp.into();
let mut out = self.clone();
if 0 == unsafe {
fmpz_mod_pow_fmpz(
&mut out.value.value,
&self.value.value,
&exp.value,
self.modulus.get_fmpz_mod_ctx_struct(),
)
} {
return Err(MathError::InvalidExponent(format!(
"The negative exponent {exp} was used for a non-invertible base {self}"
)));
}
Ok(out)
}
}
#[cfg(test)]
mod test_pow {
use super::*;
#[test]
fn small() {
let base = Zq::from((2, 9));
let exp_0 = Z::from(4);
let exp_1 = Z::ZERO;
let exp_2 = Z::from(-2);
let res_0 = base.pow(&exp_0).unwrap();
let res_1 = base.pow(&exp_1).unwrap();
let res_2 = base.pow(&exp_2).unwrap();
assert_eq!(Zq::from((7, 9)), res_0);
assert_eq!(Zq::from((1, 9)), res_1);
assert_eq!(Zq::from((7, 9)), res_2);
}
#[test]
fn large() {
let base = Zq::from((i64::MAX, u64::MAX));
let exp_0 = Z::from(4);
let exp_1 = Z::ZERO;
let exp_2 = Z::from(-1);
let cmp_0 = Zq::from((1152921504606846976_i64, u64::MAX));
let cmp_1 = Zq::from((1, u64::MAX));
let cmp_2 = Zq::from((18446744073709551613_u64, u64::MAX));
let res_0 = base.pow(&exp_0).unwrap();
let res_1 = base.pow(&exp_1).unwrap();
let res_2 = base.pow(&exp_2).unwrap();
assert_eq!(cmp_0, res_0);
assert_eq!(cmp_1, res_1);
assert_eq!(cmp_2, res_2);
}
#[test]
fn availability() {
let base = Zq::from((i64::MAX, u64::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 = Zq::from((2, 4));
assert!(base.pow(-1).is_err());
}
}