use super::atanh_data::{CH, CL};
use super::{asdouble, copysign, fasttwosum, fma_internal};
use super::{log::ln, log1p};
const TINY_BITS: u64 = 0x3e4d_12ed_0af1_a27f; const SMALL_BITS: u64 = 0x3fd0_0000_0000_0000; const ONE_BITS: u64 = 0x3ff0_0000_0000_0000;
const INF_BITS: u64 = 0x7ff0_0000_0000_0000;
const LOG1P_BOUND: f64 = 0.5;
const SMALL_C: [f64; 9] = [
f64::from_bits(0x3fc9_9999_9999_999a),
f64::from_bits(0x3fc2_4924_9249_2244),
f64::from_bits(0x3fbc_71c7_1c79_715f),
f64::from_bits(0x3fb7_45d1_6f77_7723),
f64::from_bits(0x3fb3_b13c_a417_4634),
f64::from_bits(0x3fb1_10c9_7249_89bd),
f64::from_bits(0x3fae_2d17_608a_5b2e),
f64::from_bits(0x3faa_0b56_308c_ba0b),
f64::from_bits(0x3faf_b634_1208_ad2e),
];
const SMALL_T: f64 = f64::from_bits(0x3c75_5555_5555_5555); const SMALL_BASE: f64 = f64::from_bits(0x3fd5_5555_5555_5555); const EPS_X4: f64 = f64::from_bits(0x3cad_0000_0000_0000); const EPS_TINY: f64 = f64::from_bits(0x3980_0000_0000_0000);
#[inline(always)]
fn muldd_acc(xh: f64, xl: f64, ch: f64, cl: f64, l: &mut f64) -> f64 {
let ahlh = ch * xl;
let alhh = cl * xh;
let ahhh = ch * xh;
let mut ahhl = fma_internal(ch, xh, -ahhh);
ahhl += alhh + ahlh;
let chh = ahhh + ahhl;
*l = (ahhh - chh) + ahhl;
chh
}
#[inline(always)]
fn muldd_acc2(xh: f64, xl: f64, ch: f64, cl: f64, l: &mut f64) -> f64 {
let ahlh = ch * xl;
let alhh = cl * xh;
let ahhh = ch * xh;
let mut ahhl = fma_internal(ch, xh, -ahhh);
ahhl += alhh + ahlh;
fasttwosum(ahhh, ahhl, l)
}
#[inline(always)]
fn mulddd3(xh: f64, xl: f64, ch: f64, l: &mut f64) -> f64 {
let hh = xh * ch;
*l = fma_internal(ch, xh, -hh) + xl * ch;
hh
}
#[inline(always)]
fn polydd3(xh: f64, xl: f64, n: usize, c: &[[f64; 2]], l: &mut f64) -> f64 {
let mut i = n - 1;
let mut cl = 0.0;
let mut ch = fasttwosum(c[i][0], *l, &mut cl);
cl += c[i][1];
while i > 0 {
i -= 1;
ch = muldd_acc2(xh, xl, ch, cl, &mut cl);
let mut tl = 0.0;
let th = fasttwosum(c[i][0], ch, &mut tl);
ch = th;
cl += tl + c[i][1];
}
*l = cl;
ch
}
#[inline(always)]
fn as_atanh_zero(x: f64) -> f64 {
let x2 = x * x;
let x2l = fma_internal(x, x, -x2);
let y2 = x2 * (CL[0] + x2 * (CL[1] + x2 * (CL[2] + x2 * (CL[3] + x2 * (CL[4])))));
let mut y2v = y2;
let mut y1 = polydd3(x2, x2l, CH.len(), CH, &mut y2v);
y1 = mulddd3(y1, y2v, x, &mut y2v);
y1 = muldd_acc2(x2, x2l, y1, y2v, &mut y2v);
let mut y1e = 0.0;
let y0 = fasttwosum(x, y1, &mut y1e);
let mut y2e = 0.0;
let mut y1h = fasttwosum(y1e, y2v, &mut y2e);
let mut t = (y1h).to_bits();
if (t & (!0u64 >> 12)) == 0 {
let w = (y2e).to_bits();
if ((w ^ t) >> 63) != 0 {
t = t.wrapping_sub(1);
} else {
t = t.wrapping_add(1);
}
y1h = asdouble(t);
}
y0 + y1h
}
#[inline(always)]
pub fn atanh(x: f64) -> f64 {
let ax = x.abs();
let aix = (ax).to_bits();
if aix == 0 {
return x;
}
if aix >= ONE_BITS {
if aix == ONE_BITS {
return if x.is_sign_negative() {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if aix > INF_BITS {
return x + x;
}
return f64::NAN;
}
if aix < SMALL_BITS {
if aix < TINY_BITS {
return fma_internal(x, f64::from_bits(0x3c80_0000_0000_0000), x);
}
let x2 = x * x;
let dx2 = fma_internal(x, x, -x2);
let x4 = x2 * x2;
let x3 = x2 * x;
let x8 = x4 * x4;
let dx3 = fma_internal(x2, x, -x3) + dx2 * x;
let p = (SMALL_C[0] + x2 * SMALL_C[1])
+ x4 * (SMALL_C[2] + x2 * SMALL_C[3])
+ x8 * ((SMALL_C[4] + x2 * SMALL_C[5])
+ x4 * (SMALL_C[6] + x2 * SMALL_C[7])
+ x8 * SMALL_C[8]);
let t = fma_internal(x2, p, SMALL_T);
let mut pl = 0.0;
let mut ph = fasttwosum(SMALL_BASE, t, &mut pl);
ph = muldd_acc(ph, pl, x3, dx3, &mut pl);
let mut tl = 0.0;
ph = fasttwosum(x, ph, &mut tl);
pl += tl;
let eps = x * (x4 * EPS_X4 + EPS_TINY);
let lb = ph + (pl - eps);
let ub = ph + (pl + eps);
if lb == ub {
return lb;
}
return as_atanh_zero(x);
}
if ax <= LOG1P_BOUND {
let y = 0.5 * (log1p(ax) - log1p(-ax));
return copysign(y, x);
}
let ratio = (1.0 + ax) / (1.0 - ax);
let y = 0.5 * ln(ratio);
copysign(y, x)
}