use super::exp::exp_split;
use super::log::hi_lo_log_inner;
use super::{scalbn, Exp, Log};
use crate::double::{DenormDouble, SemiDouble};
use crate::traits::{CastInto as _, Int as _};
pub(crate) fn powi<F: Log + Exp>(x: F, y: i32) -> F {
let (nx, xedelta) = x.normalize_arg();
let xexp = nx.raw_exp();
if y == 0 || nx == F::one() {
F::one()
} else if xexp == F::MAX_RAW_EXP && nx.raw_mant() != F::Raw::ZERO {
F::NAN
} else if xexp == F::RawExp::ZERO {
if (y & 1) != 0 {
if y < 0 {
F::INFINITY.copysign(nx)
} else {
nx
}
} else {
if y < 0 {
F::INFINITY
} else {
F::ZERO
}
}
} else if xexp == F::MAX_RAW_EXP {
if nx.sign() {
if (y & 1) != 0 {
if y < 0 {
-F::ZERO
} else {
F::neg_infinity()
}
} else {
if y < 0 {
F::ZERO
} else {
F::INFINITY
}
}
} else {
if y < 0 {
F::ZERO
} else {
F::INFINITY
}
}
} else {
let logx = hi_lo_log_inner(nx.abs(), xedelta).to_semi();
let mut k_total = 0;
let mut z = SemiDouble::one();
let absy = y.unsigned_abs();
let mut yshift = 0;
while yshift < 32 {
let mask: u32 = ((F::MANT_MASK << 1) | F::Raw::ONE).cast_into();
let yf: F = ((absy >> yshift) & mask).cast_into();
let yf = (yf * F::exp2i_fast(yshift.cast_into())).set_sign(y < 0);
yshift += F::MANT_BITS + 1;
let ylx = (logx * yf).to_norm();
if ylx.hi() >= F::exp_hi_th() {
if (absy & 1) == 0 || !nx.sign() {
return F::INFINITY;
} else {
return F::neg_infinity();
}
} else if ylx.hi() <= F::exp_lo_th() {
if (absy & 1) == 0 || !nx.sign() {
return F::ZERO;
} else {
return -F::ZERO;
}
} else {
let (k, r_hi, r_lo) = exp_split(ylx.hi());
let r = DenormDouble::new(r_hi, r_lo + ylx.lo());
let t = hi_lo_exp_inner(r).to_semi();
k_total += k;
z = (z * t).to_semi();
}
}
let absz = scalbn(z.to_single(), k_total);
if nx.sign() && (y & 1) != 0 {
-absz
} else {
absz
}
}
}
fn hi_lo_exp_inner<F: Exp>(r: DenormDouble<F>) -> DenormDouble<F> {
let r_single = r.to_single();
let r2 = r_single * r_single;
let t1 = r_single + F::exp_special_poly(r2);
let t2 = r_single * t1 / (F::two() - t1);
let t3 = r.qadd1(t2);
t3.qradd1(F::one())
}
#[cfg(test)]
mod tests {
use crate::traits::Float;
use crate::FloatMath;
fn test<F: Float + FloatMath>() {
use crate::powi;
let f = F::parse;
assert_is_nan!(powi(F::NAN, 1));
assert_total_eq!(powi(F::ZERO, -33), F::INFINITY);
assert_total_eq!(powi(-F::ZERO, -33), F::neg_infinity());
assert_total_eq!(powi(F::ZERO, -34), F::INFINITY);
assert_total_eq!(powi(-F::ZERO, -34), F::INFINITY);
assert_total_eq!(powi(F::ZERO, 33), F::ZERO);
assert_total_eq!(powi(-F::ZERO, 33), -F::ZERO);
assert_total_eq!(powi(F::ZERO, 34), F::ZERO);
assert_total_eq!(powi(-F::ZERO, 34), F::ZERO);
assert_total_eq!(powi(F::one(), 0), F::one());
assert_total_eq!(powi(F::one(), 33), F::one());
assert_total_eq!(powi(F::one(), -33), F::one());
assert_total_eq!(powi(F::one(), 34), F::one());
assert_total_eq!(powi(F::one(), -34), F::one());
assert_total_eq!(powi(F::INFINITY, 0), F::one());
assert_total_eq!(powi(F::INFINITY, 33), F::INFINITY);
assert_total_eq!(powi(F::INFINITY, -33), F::ZERO);
assert_total_eq!(powi(F::INFINITY, 34), F::INFINITY);
assert_total_eq!(powi(F::INFINITY, -34), F::ZERO);
assert_total_eq!(powi(F::neg_infinity(), 0), F::one());
assert_total_eq!(powi(F::neg_infinity(), -0), F::one());
assert_total_eq!(powi(F::neg_infinity(), 33), F::neg_infinity());
assert_total_eq!(powi(F::neg_infinity(), -33), -F::ZERO);
assert_total_eq!(powi(F::neg_infinity(), 34), F::INFINITY);
assert_total_eq!(powi(F::neg_infinity(), -34), F::ZERO);
assert_total_eq!(powi(F::two(), 2), f("4"));
assert_total_eq!(powi(F::two(), -2), f("0.25"));
assert_total_eq!(powi(-F::two(), 3), f("-8"));
assert_total_eq!(powi(-F::two(), -3), f("-0.125"));
assert_total_eq!(powi(f("3.5"), 3), f("42.875"));
assert_total_eq!(powi(f("10"), 4), f("10000"));
}
#[test]
fn test_f32() {
test::<f32>();
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f32() {
test::<crate::SoftF32>();
}
#[test]
fn test_f64() {
test::<f64>();
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f64() {
test::<crate::SoftF64>();
}
}