use crate::common::*;
use crate::double_double::DoubleDouble;
use crate::dyadic_float::{DyadicFloat128, DyadicSign};
use crate::logs::log1p_fast_dd;
use crate::pow_exec::pow_expm1_1;
pub fn f_compound_m1(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;
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 0.0;
}
if x.is_nan() {
if y != 0. {
return x;
} return 0.0;
}
if x == 0.0 {
return 0.0;
}
if x.is_infinite() && x > 0.0 {
return if y > 0. { x } else { -1.0 };
}
if x < -1.0 {
return f64::NAN;
}
if x == -1.0 {
return if y < 0. { f64::INFINITY } else { -1.0 };
}
match y_a {
0x3ff0_0000_0000_0000 => {
return if y_sign {
let z = DyadicFloat128::new_from_f64(x);
const ONES: DyadicFloat128 = DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
};
const M_ONES: DyadicFloat128 = DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
};
let p = (z + ONES).reciprocal() + M_ONES;
p.fast_as_f64()
} else {
x
};
}
0x4000_0000_0000_0000 => {
const ONES: DyadicFloat128 = DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
};
let z0 = DyadicFloat128::new_from_f64(x) + ONES;
let z = z0 * z0;
const M_ONES: DyadicFloat128 = DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
};
return if y_sign {
(z.reciprocal() + M_ONES).fast_as_f64()
} else {
f64::copysign((z + M_ONES).fast_as_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_a == 0x3ff0_0000_0000_0000 {
return 0.0;
}
if x == 0.0 && y_sign {
return f64::INFINITY;
}
return if (x_a < 1f64.to_bits()) == y_sign {
f64::INFINITY
} else {
-1.0
};
}
if x_u == 1f64.to_bits() {
return 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 -1.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 { -1.0 } else { 1.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 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 -1.;
}
return if out_is_neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if x_a > f64::INFINITY.to_bits() {
return x;
}
}
#[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 mut dz = if y.is_sign_negative() {
acc.recip()
} else {
acc
};
dz = DoubleDouble::full_add_f64(dz, -1.);
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 0.;
}
let r = DoubleDouble::quick_mult_f64(l, y);
let res = pow_expm1_1(r, s);
res.to_f64()
}
#[cold]
#[inline(never)]
fn mul_fixed_power_hard(x: f64, y: f64) -> f64 {
const ONE: DyadicFloat128 = DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
};
const M_ONE: DyadicFloat128 = DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
};
let mut s = DyadicFloat128::new_from_f64(x) + ONE;
let mut iter_count = unsafe { y.abs().to_int_unchecked::<usize>() };
let mut acc = if iter_count % 2 != 0 { s } else { ONE };
while {
iter_count >>= 1;
iter_count
} != 0
{
s = s * s;
if iter_count % 2 != 0 {
acc = acc * s;
}
}
if y.is_sign_negative() {
(acc.reciprocal() + M_ONE).fast_as_f64()
} else {
(acc + M_ONE).fast_as_f64()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compound_exotic() {
assert_eq!(
f_compound_m1(0.000152587890625, -8.484374999999998),
-0.0012936766014690006
);
assert_eq!(
f_compound_m1(
0.00000000000000799360578102344,
-0.000000000000000000000001654361225106131
),
-0.000000000000000000000000000000000000013224311452909338
);
assert_eq!(
f_compound_m1( 4.517647064592699, 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055329046628180653),
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009449932890153435
);
assert_eq!(f_compound_m1(
11944758478933760000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.,
-1242262631503757300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.,
), -1.);
}
#[test]
fn test_compound_m1() {
assert_eq!(
f_compound_m1(0.0000000000000009991998751296936, -4.),
-0.000000000000003996799500518764
);
assert_eq!(f_compound_m1(-0.003173828125, 25.), -0.0763960132649781);
assert_eq!(f_compound_m1(3., 2.8927001953125), 54.154259038961406);
assert_eq!(
f_compound_m1(-0.43750000000000044, 19.),
-0.9999821216263793
);
assert_eq!(
f_compound_m1(127712., -2.0000000000143525),
-0.9999999999386903
);
assert_eq!(
f_compound_m1(-0.11718749767214207, 2893226081485815000000000000000.),
-1.
);
assert_eq!(
f_compound_m1(2418441935074801400000000., 512.),
f64::INFINITY
);
assert_eq!(
f_compound_m1(32.50198364245834, 128000.00000000093),
f64::INFINITY
);
assert_eq!(
f_compound_m1(1.584716796877785, 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004168916810703412),
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003958869879428553
);
assert_eq!(
f_compound_m1(
-0.000000000000000000000000000000001997076793037533,
366577337071337140000000000000000f64
),
-0.5190938261758579
);
assert_eq!(f_compound_m1(2.1075630259863374, 0.5), 00.7628281328553664);
assert_eq!(f_compound_m1(2.1078916412661783, 0.5), 0.7629213372315222);
assert_eq!(f_compound_m1(3.0000000000001115, -0.5), -0.500000000000007);
assert_eq!(
f_compound_m1(0.0004873839215895903, 3.),
0.0014628645098045245
);
assert_eq!(f_compound_m1(-0.483765364602732, 3.), -0.862424399516842);
assert_eq!(f_compound_m1(3.0000001192092896, -2.), -0.9375000037252902);
assert_eq!(f_compound_m1(29.38323424607434, -1.), -0.9670871115332561);
assert_eq!(f_compound_m1(-0.4375, 4.), -0.8998870849609375);
assert_eq!(
f_compound_m1(-0.0039033182037826464, 3.),
-0.011664306402886494
);
assert_eq!(
f_compound_m1(0.000000000000000000000000000000000000007715336350455947,
-262034087537726030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.),
-1.,
);
assert_eq!(f_compound_m1(10.000000059604645, 10.), 25937426005.44638);
assert_eq!(f_compound_m1(10., -308.25471555814863), -1.0);
assert_eq!(
f_compound_m1(5.4172231599824623E-312, 9.4591068440831498E+164),
5.124209266851586e-147
);
assert_eq!(
f_compound_m1(5.8776567263633397E-39, 3.4223548116804511E-310),
0.0
);
assert_eq!(
f_compound_m1(5.8639503496997932E-148, -7.1936801558778956E+305),
0.0
);
assert_eq!(
f_compound_m1(0.9908447265624999,
-19032028850336152000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.),
-1.
);
assert_eq!(
f_compound_m1(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006952247559980936,
5069789834563405000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.),
3.524643400695958e-163
);
assert_eq!(
f_compound_m1(1.000000000000341,
-69261261804788370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.),
-1.
);
assert_eq!(
f_compound_m1(
0.0000000000000001053438024827798,
0.0000000000000001053438024827798
),
0.000000000000000000000000000000011097316721530923
);
assert_eq!(
f_compound_m1(
0.00000000000000010755285551056508,
0.00000000000000010755285551056508
),
0.00000000000000000000000000000001156761672847649
);
assert_eq!(f_compound_m1(2.4324324, 1.4324324), 4.850778380908823);
assert_eq!(f_compound_m1(2., 5.), 242.);
assert_eq!(f_compound_m1(0.4324324, 126.4324324), 5.40545942023447e19);
assert!(f_compound_m1(-0.4324324, 126.4324324).is_nan());
assert_eq!(f_compound_m1(0.0, 0.0), 0.0);
assert_eq!(f_compound_m1(0.0, -1. / 2.), 0.0);
assert_eq!(f_compound_m1(-1., -1. / 2.), f64::INFINITY);
assert_eq!(f_compound_m1(f64::INFINITY, -1. / 2.), -1.0);
assert_eq!(f_compound_m1(f64::INFINITY, 1. / 2.), f64::INFINITY);
assert_eq!(f_compound_m1(46.3828125, 46.3828125), 5.248159634773675e77);
}
}