use crate::common::{f_fmla, is_integer, is_odd_integer};
use crate::double_double::DoubleDouble;
use crate::dyadic_float::{DyadicFloat128, DyadicSign};
use crate::logs::{log1p_f64_dyadic, log1p_fast_dd};
use crate::pow_exec::{exp_dyadic, pow_exp_dd};
use crate::triple_double::TripleDouble;
pub fn f_compound(x: f64, y: f64) -> f64 {
let x_sign = x.is_sign_negative();
let y_sign = y.is_sign_negative();
let x_abs = x.to_bits() & 0x7fff_ffff_ffff_ffff;
let y_abs = y.to_bits() & 0x7fff_ffff_ffff_ffff;
const MANTISSA_MASK: u64 = (1u64 << 52) - 1;
let y_mant = y.to_bits() & MANTISSA_MASK;
let x_u = x.to_bits();
let x_a = x_abs;
let y_a = y_abs;
if x.is_nan() || y.is_nan() {
return f64::NAN;
}
let mut s = 1.0;
let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
let ay = y.to_bits() & 0x7fff_ffff_ffff_ffff;
if y_mant == 0
|| y_a > 0x43d7_4910_d52d_3052
|| x_u == 1f64.to_bits()
|| x_u >= f64::INFINITY.to_bits()
|| x_u < f64::MIN.to_bits()
{
if y == 0.0 {
return 1.0;
}
if x.is_nan() {
if y != 0. {
return x;
} return 1.0;
}
if x == 0.0 {
return 1.0;
}
if x.is_infinite() && x > 0.0 {
return if y > 0. { x } else { 0.0 };
}
if x < -1.0 {
return f64::NAN;
}
if x == -1.0 {
return if y < 0. { f64::INFINITY } else { 0.0 };
}
match y_a {
0x3fe0_0000_0000_0000 => {
if x == 0.0 {
return 1.0;
}
let z = DoubleDouble::from_full_exact_add(x, 1.0).sqrt();
return if y_sign {
z.recip().to_f64()
} else {
z.to_f64()
};
}
0x3ff0_0000_0000_0000 => {
return if y_sign {
const ONES: DyadicFloat128 = DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
};
let z = DyadicFloat128::new_from_f64(x) + ONES;
z.reciprocal().fast_as_f64()
} else {
DoubleDouble::from_full_exact_add(x, 1.0).to_f64()
};
}
0x4000_0000_0000_0000 => {
let z0 = DoubleDouble::from_full_exact_add(x, 1.0);
let z = DoubleDouble::quick_mult(z0, z0);
return if y_sign {
z.recip().to_f64()
} else {
f64::copysign(z.to_f64(), x)
};
}
_ => {}
}
if y_a >= 0x7ff0_0000_0000_0000 {
if y_mant != 0 {
return if x_u == 1f64.to_bits() { 1.0 } else { y };
}
if f64::from_bits(x_abs).is_nan() {
return x;
}
if x == 0.0 && y_sign {
return f64::INFINITY;
}
return if (x_a < 1f64.to_bits()) == y_sign {
f64::INFINITY
} else {
0.0
};
}
if x == 0.0 {
let out_is_neg = x_sign && is_odd_integer(y);
if y_sign {
return if out_is_neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
return if out_is_neg { -0.0 } else { 0.0 };
}
if x_a == f64::INFINITY.to_bits() {
let out_is_neg = x_sign && is_odd_integer(y);
if y_sign {
return if out_is_neg { -0.0 } else { 0.0 };
}
return if out_is_neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if x_a > f64::INFINITY.to_bits() {
return x;
}
if x_sign {
if is_integer(y) {
if is_odd_integer(y) {
static CS: [f64; 2] = [1.0, -1.0];
let y_parity = if (y.abs()) >= f64::from_bits(0x4340000000000000) {
0usize
} else {
(y as i64 & 0x1) as usize
};
s = CS[y_parity];
}
} else {
return f64::NAN;
}
}
if x_u == 1f64.to_bits() {
return 2.0;
}
if x == 0.0 {
let out_is_neg = x_sign && is_odd_integer(y);
if y_sign {
return if out_is_neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
return if out_is_neg { -0.0 } else { 0.0 };
}
if x_a == f64::INFINITY.to_bits() {
let out_is_neg = x_sign && is_odd_integer(y);
if y_sign {
return if out_is_neg { -0.0 } else { 0.0 };
}
return if out_is_neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if x_a > f64::INFINITY.to_bits() {
return x;
}
let min_abs = f64::min(f64::from_bits(ax), f64::from_bits(ay)).to_bits();
let max_abs = f64::max(f64::from_bits(ax), f64::from_bits(ay)).to_bits();
let min_exp = min_abs.wrapping_shr(52);
let max_exp = max_abs.wrapping_shr(52);
if max_exp > 0x7ffu64 - 128u64 || min_exp < 128u64 {
let scale_up = min_exp < 128u64;
let scale_down = max_exp > 0x7ffu64 - 128u64;
if scale_up || scale_down {
return compound_accurate(x, y, s);
}
}
}
#[cfg(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
))]
let straight_path_precondition: bool = true;
#[cfg(not(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
)))]
let straight_path_precondition: bool = y.is_sign_positive();
if is_integer(y)
&& y_a <= 0x4059800000000000u64
&& x_a <= 0x4090000000000000u64
&& x_a > 0x3cc0_0000_0000_0000
&& straight_path_precondition
{
let mut s = DoubleDouble::from_full_exact_add(1.0, x);
let mut iter_count = unsafe { y.abs().to_int_unchecked::<usize>() };
let mut acc = if iter_count % 2 != 0 {
s
} else {
DoubleDouble::new(0., 1.)
};
while {
iter_count >>= 1;
iter_count
} != 0
{
s = DoubleDouble::mult(s, s);
if iter_count % 2 != 0 {
acc = DoubleDouble::mult(acc, s);
}
}
let dz = if y.is_sign_negative() {
acc.recip()
} else {
acc
};
let ub = dz.hi + f_fmla(f64::from_bits(0x3c40000000000000), -dz.hi, dz.lo); let lb = dz.hi + f_fmla(f64::from_bits(0x3c40000000000000), dz.hi, dz.lo); if ub == lb {
return dz.to_f64();
}
return mul_fixed_power_hard(x, y);
}
let l = log1p_fast_dd(x);
let ey = ((y.to_bits() >> 52) & 0x7ff) as i32;
if ey < 0x36 || ey >= 0x7f5 {
return compound_accurate(x, y, s);
}
let r = DoubleDouble::quick_mult_f64(l, y);
let res = pow_exp_dd(r, s);
let res_min = res.hi + f_fmla(f64::from_bits(0x3bf0000000000000), -res.hi, res.lo);
let res_max = res.hi + f_fmla(f64::from_bits(0x3bf0000000000000), res.hi, res.lo);
if res_min == res_max {
return res_max;
}
compound_accurate(x, y, s)
}
#[cold]
fn compound_accurate(x: f64, y: f64, s: f64) -> f64 {
let f_y = DyadicFloat128::new_from_f64(y);
let r = log1p_f64_dyadic(x) * f_y;
let mut result = exp_dyadic(r);
if result.exponent < -1075 {
return 0.5 * (s * f64::from_bits(0x0000000000000001));
}
if result.exponent >= 1025 {
return 1.0;
}
result.sign = if s == -1.0 {
DyadicSign::Neg
} else {
DyadicSign::Pos
};
result.fast_as_f64()
}
#[cold]
#[inline(never)]
fn mul_fixed_power_hard(x: f64, y: f64) -> f64 {
let mut s = TripleDouble::from_full_exact_add(1.0, x);
let mut iter_count = unsafe { y.abs().to_int_unchecked::<usize>() };
let mut acc = if iter_count % 2 != 0 {
s
} else {
TripleDouble::new(0., 0., 1.)
};
while {
iter_count >>= 1;
iter_count
} != 0
{
s = TripleDouble::quick_mult(s, s);
if iter_count % 2 != 0 {
acc = TripleDouble::quick_mult(acc, s);
}
}
if y.is_sign_negative() {
acc.recip().to_f64()
} else {
acc.to_f64()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compound() {
assert_eq!(f_compound(4831835136., -13.),0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012780345669344118 );
assert_eq!(
f_compound(11468322278342656., 2.9995136260713475),
1481455956234813000000000000000000000000000000000.
);
assert_eq!(f_compound(0.9999999999999999, 3.), 7.999999999999999);
assert_eq!(
f_compound(1.0039215087890625, 10.000000000349134),
1044.2562119607103
);
assert_eq!(f_compound(10., 18.0), 5559917313492231000.0);
assert_eq!(
f_compound(131071.65137729312, 2.000001423060894),
17180328027.532265
);
assert_eq!(f_compound(2., 5.), 243.);
assert_eq!(f_compound(126.4324324, 126.4324324), 1.4985383310514043e266);
assert_eq!(f_compound(0.4324324, 126.4324324), 5.40545942023447e19);
assert!(f_compound(-0.4324324, 126.4324324).is_nan());
assert_eq!(f_compound(0.0, 0.0), 1.0);
assert_eq!(f_compound(0.0, -1. / 2.), 1.0);
assert_eq!(f_compound(-1., -1. / 2.), f64::INFINITY);
assert_eq!(f_compound(f64::INFINITY, -1. / 2.), 0.0);
assert_eq!(f_compound(f64::INFINITY, 1. / 2.), f64::INFINITY);
assert_eq!(f_compound(46.3828125, 46.3828125), 5.248159634773675e77);
}
#[test]
fn test_compound_exotic_cases() {
assert_eq!(f_compound(0.9999999850987819, -1.), 0.5000000037253046);
assert_eq!(
f_compound(22427285907987670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.,
-1.),
0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004458854290718438
);
assert_eq!(f_compound(0.786438105629145, 607.999512419221),
1616461095392737200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
assert_eq!(f_compound( 1.0000002381857613, 960.8218657970428),
17228671476562465000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
assert_eq!(f_compound(1., 1.0000000000000284), 2.);
assert_eq!(f_compound(1., f64::INFINITY), f64::INFINITY);
assert_eq!(
f_compound(10.000000000000007, -8.),
0.00000000466507380209731
);
}
}