use super::types::Number;
use crate::error::MathError;
use num_bigint::BigInt;
impl Number {
pub fn pow(&self, exponent: &Number) -> Result<Number, MathError> {
match (self, exponent) {
(Number::Integer(base), Number::Integer(exp)) if *exp >= 0 => {
if *exp > u32::MAX as i64 {
return Err(MathError::NumericOverflow {
operation: "exponent too large for integer power".to_owned(),
});
}
let exp_u32 = *exp as u32;
if let Some(result) = Self::checked_pow_i64(*base, exp_u32) {
Ok(Number::Integer(result))
} else {
let base_bigint = BigInt::from(*base);
Ok(Number::BigInteger(Box::new(num_traits::Pow::pow(
base_bigint,
exp_u32,
))))
}
}
(Number::BigInteger(base), Number::Integer(exp)) if *exp >= 0 => {
if *exp > u32::MAX as i64 {
return Err(MathError::NumericOverflow {
operation: "exponent too large for BigInteger power".to_owned(),
});
}
Ok(Number::BigInteger(Box::new(num_traits::Pow::pow(
base.as_ref().clone(),
*exp as u32,
))))
}
_ => {
let base_float = self.to_float()?;
let exp_float = exponent.to_float()?;
let result = base_float.powf(exp_float);
if result.is_infinite() || result.is_nan() {
Err(MathError::NumericOverflow {
operation: "float power".to_owned(),
})
} else {
Ok(Number::Float(result))
}
}
}
}
fn checked_pow_i64(base: i64, exp: u32) -> Option<i64> {
if exp == 0 {
return Some(1);
}
let mut result = 1i64;
let mut base = base;
let mut exp = exp;
while exp > 0 {
if exp % 2 == 1 {
result = result.checked_mul(base)?;
}
base = base.checked_mul(base)?;
exp /= 2;
}
Some(result)
}
}