use crate::{float::Native, ln::ln_parts};
#[inline]
pub(crate) fn sinh<R: Native, const FUSED: bool>(x: R) -> R { sinh_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x) }
#[inline]
pub(crate) fn cosh<R: Native, const FUSED: bool>(x: R) -> R { cosh_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x) }
#[inline]
pub(crate) fn tanh<R: Native, const FUSED: bool>(x: R) -> R { tanh_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x) }
#[inline]
pub(crate) fn sinh_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
let one = o.number(F::one());
let h = crate::exp::minus_one_formula(o, o.magnitude(x), o.negate(one));
let result = o.multiply(h, o.add(one, o.divide(one, o.add(o.add(h, h), one))));
o.select(o.less(x, o.number(F::zero())), o.negate(result), result)
}
#[inline]
pub(crate) fn cosh_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
let e = crate::exp::scaled_formula(o, o.magnitude(x), o.number(-F::one()));
o.add(e, o.divide(o.number(F::narrow(0.25)), e))
}
#[inline]
pub(crate) fn tanh_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
let top = o.number(F::narrow(if o.width() == crate::buffer::FloatWidth::F64 { 20.0 } else { 9.0 }));
let a = o.magnitude(x);
let a = o.select(o.less(top, a), top, a);
let e = crate::exp::minus_one_formula(o, o.add(a, a), o.number(F::zero()));
let result = o.divide(e, o.add(e, o.number(F::narrow(2.0))));
o.select(o.less(x, o.number(F::zero())), o.negate(result), result)
}
#[inline]
fn large<R: Native>() -> R { R::narrow(4503599627370496.0) }
#[inline]
fn ln_of<R: Native, const FUSED: bool>(v: R, far: bool, near: (R, R)) -> R {
let (m, e) = v.split();
let e = if far { e + R::one() } else { e };
let (num, den) = if e == R::zero() { near } else { (m - R::one(), m + R::one()) };
let y = ln_parts::<R, FUSED>(num / den, e);
if v.is_nan() | (v == R::infinity()) { v } else { y }
}
#[inline]
pub(crate) fn asinh<R: Native, const FUSED: bool>(x: R) -> R {
let a = x.abs();
let far = a > large();
let r = a.mla::<FUSED>(a, R::one()).sqrt();
let (q, a2) = (R::one() + r, a * a);
let near = (a.mla::<FUSED>(q, a2), (a + R::narrow(2.0)).mla::<FUSED>(q, a2));
ln_of::<R, FUSED>(if far { a } else { a + r }, far, near).copysign(x)
}
#[inline]
pub(crate) fn acosh<R: Native, const FUSED: bool>(x: R) -> R {
let far = x > large();
let d = x - R::one();
let t = d + d.mla::<FUSED>(d, d + d).sqrt();
let y = ln_of::<R, FUSED>(if far { x } else { R::one() + t }, far, (t, t + R::narrow(2.0)));
if x < R::one() { R::nan() } else { y }
}
#[inline]
pub(crate) fn atanh<R: Native, const FUSED: bool>(x: R) -> R {
let a = x.abs();
let y = ln_of::<R, FUSED>((R::one() + a) / (R::one() - a), false, (a, R::one())) * R::narrow(0.5);
(if a <= R::one() { y } else { R::nan() }).copysign(x)
}
#[derive(Clone, Copy, Debug)]
pub struct RealAcosh;
#[derive(Clone, Copy, Debug)]
pub struct RealAtanh;