use crate::common::{is_integer, is_odd_integer};
use crate::double_double::DoubleDouble;
use crate::exponents::{EXPM1_T0, EXPM1_T1, ldexp};
use crate::pow_exec::pow_log_1;
use crate::rounding::CpuRoundTiesEven;
pub fn f_powm1(x: f64, y: f64) -> f64 {
let ax: u64 = x.to_bits().wrapping_shl(1);
let ay: u64 = y.to_bits().wrapping_shl(1);
if ax == 0 || ax >= 0x7ffu64 << 53 || ay == 0 || ay >= 0x7ff64 << 53 {
if x.is_nan() || y.is_nan() {
return f64::NAN;
}
if x.is_infinite() {
return if x.is_sign_positive() {
if y.is_infinite() {
return f64::INFINITY;
} else if y > 0.0 {
f64::INFINITY } else if y < 0.0 {
-1.0 } else {
f64::NAN }
} else {
if y.is_infinite() {
return -1.0;
}
if is_integer(y) {
let pow = if y as i32 % 2 == 0 {
f64::INFINITY
} else {
f64::NEG_INFINITY
};
pow - 1.0
} else {
f64::NAN }
};
}
if y.is_infinite() {
return if x.abs() > 1.0 {
if y.is_sign_positive() {
f64::INFINITY
} else {
-1.0
}
} else if x.abs() < 1.0 {
if y.is_sign_positive() {
-1.0
} else {
f64::INFINITY
}
} else {
f64::NAN };
}
if x == 0.0 {
return if y > 0.0 {
-1.0 } else if y < 0.0 {
f64::INFINITY } else {
0.0 };
}
}
let y_integer = is_integer(y);
let mut negative_parity: bool = false;
let mut x = x;
if x < 0.0 {
if !y_integer {
return f64::NAN; }
x = x.abs();
if is_odd_integer(y) {
negative_parity = true;
}
}
let (mut l, _) = pow_log_1(x);
l = DoubleDouble::from_exact_add(l.hi, l.lo);
let r = DoubleDouble::quick_mult_f64(l, y);
if r.hi < -37.42994775023705 {
return -1.;
}
let res = powm1_expm1_1(r);
if negative_parity {
DoubleDouble::full_add_f64(-res, -2.).to_f64()
} else {
res.to_f64()
}
}
#[inline]
pub(crate) fn powm1_expm1_1(r: DoubleDouble) -> DoubleDouble {
let ax = r.hi.to_bits() & 0x7fffffffffffffffu64;
const LOG2H: f64 = f64::from_bits(0x3f262e42fefa39ef);
const LOG2L: f64 = f64::from_bits(0x3bbabc9e3b39803f);
if ax <= 0x3f80000000000000 {
if ax < 0x3970000000000000 {
return r;
}
let d = crate::pow_exec::expm1_poly_dd_tiny(r);
return d;
}
const INVLOG2: f64 = f64::from_bits(0x40b71547652b82fe);
let k = (r.hi * INVLOG2).cpu_round_ties_even();
let z = DoubleDouble::mul_f64_add(DoubleDouble::new(LOG2L, LOG2H), -k, r);
let bk = k as i64;
let mk = (bk >> 12) + 0x3ff;
let i2 = (bk >> 6) & 0x3f;
let i1 = bk & 0x3f;
let t0 = DoubleDouble::from_bit_pair(EXPM1_T0[i2 as usize]);
let t1 = DoubleDouble::from_bit_pair(EXPM1_T1[i1 as usize]);
let tbh = DoubleDouble::quick_mult(t1, t0);
let mut de = tbh;
let q = crate::pow_exec::expm1_poly_fast(z);
de = DoubleDouble::quick_mult(de, q);
de = DoubleDouble::add(tbh, de);
let ie = mk - 0x3ff;
let off: f64 = f64::from_bits((2048i64 + 1023i64).wrapping_sub(ie).wrapping_shl(52) as u64);
let e: f64;
if ie < 53 {
let fhz = DoubleDouble::from_exact_add(off, de.hi);
de.hi = fhz.hi;
e = fhz.lo;
} else if ie < 104 {
let fhz = DoubleDouble::from_exact_add(de.hi, off);
de.hi = fhz.hi;
e = fhz.lo;
} else {
e = 0.;
}
de.lo += e;
de.hi = ldexp(de.to_f64(), ie as i32);
de.lo = 0.;
de
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_powm1() {
assert_eq!(f_powm1(f64::INFINITY, f64::INFINITY), f64::INFINITY);
assert_eq!(f_powm1(50850368932909610000000000., 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023201985303960773), 1.3733470789307166e-303);
assert_eq!(f_powm1(-3.375, -9671689000000000000000000.), -1.);
assert_eq!(f_powm1(1.83329e-40, 2.4645883e-32), -2.255031542428047e-30);
assert_eq!(f_powm1(3., 2.), 8.);
assert_eq!(f_powm1(3., 3.), 26.);
assert_eq!(f_powm1(5., 2.), 24.);
assert_eq!(f_powm1(5., -2.), 1. / 25. - 1.);
assert_eq!(f_powm1(-5., 2.), 24.);
assert_eq!(f_powm1(-5., 3.), -126.);
assert_eq!(
f_powm1(196560., 0.000000000000000000000000000000000000001193773),
1.4550568430468268e-38
);
}
}