use crate::{dd::Double, float::{Native, horner, sealed::Core}};
const EXP_64: [f64; 10] = [
2.749197269771299e-07, 2.7639186521124947e-06, 2.4801900316905035e-05, 0.00019841173089486715, 0.0013888888529025865, 0.008333333382940285,
0.04166666666820299, 0.16666666666564642, 0.4999999999999798, 1.0000000000000056,
];
const EXP_32: [f64; 5] = [0.008247390389442444, 0.04189976677298546, 0.16668395698070526, 0.4999912679195404, 0.9999994039535522];
const EXP_NARROW: [f64; 6] = [0.0013941108433972674, 0.008375126398153335, 0.04166635289677515, 0.16666415514653277, 0.5000000047117757, 1.000000037716214];
const H_64: [f64; 11] = [
2.5052099500253996e-08, 2.762626280199822e-07, 2.7557414780064832e-06, 2.4801504602137864e-05, 0.00019841269707477827, 0.001388888893225886,
0.008333333333397829, 0.041666666666573905, 0.16666666666666563, 0.5000000000000006, 1.0,
];
const H_32: [f64; 6] = [0.0013888871762901545, 0.00836906861513853, 0.041667137295007706, 0.16666506230831146, 0.4999999701976776, 1.0];
#[inline]
pub(crate) fn clamp<R: Native>(x: R, l: R) -> R { if x < -l { -l } else if x > l { l } else { x } }
#[inline]
fn reduce<R: Native, const FUSED: bool>(x: R) -> (R, R) { reduce_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x) }
#[inline]
fn exp_parts<R: Native, const FUSED: bool>(x: R, lo: R, k: R) -> R { parts_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x, lo, k) }
#[inline]
pub(crate) fn exp<R: Native, const FUSED: bool>(x: R) -> R { exp_parts::<R, FUSED>(x, R::neg_zero(), R::neg_zero()) }
#[derive(Clone, Copy, Debug)]
pub struct Exp;
#[inline]
pub(crate) fn exp_times<R: Native, const FUSED: bool>(x: R, k: R) -> R { exp_parts::<R, FUSED>(x, R::neg_zero(), k) }
#[inline]
pub(crate) fn expm1<R: Native, const FUSED: bool>(x: R) -> R { expm1_times::<R, FUSED>(x, R::zero()) }
#[derive(Clone, Copy, Debug)]
pub struct Expm1;
#[inline]
pub(crate) fn expm1_times<R: Native, const FUSED: bool>(x: R, k: R) -> R { minus_one_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x, k) }
#[inline]
pub(crate) fn exp_plus<R: Native, const FUSED: bool>(x: Double<R>) -> R { let x = Double::sum(x.hi, x.lo); exp_parts::<R, FUSED>(x.hi, x.lo, R::neg_zero()) }
#[inline]
pub(crate) fn exp_narrow<const FUSED: bool>(x: f64) -> f64 {
let (n, r) = reduce::<f64, FUSED>(clamp(x, 110.0));
r.mla::<FUSED>(horner::<f64, FUSED, 6>(r, EXP_NARROW), 1.0) * f64::pow2(n + f64::SHIFT)
}
#[inline(always)]
fn reduce_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> (O::V, O::V) {
let shift = crate::recipe::rounding_shift(o);
let n = o.subtract(o.multiply_add(x, o.number(F::narrow(std::f64::consts::LOG2_E)), shift), shift);
let (hi, lo) = crate::recipe::ln2(o);
(n, o.multiply_add(n, o.negate(lo), o.multiply_add(n, o.negate(hi), x)))
}
#[inline(always)]
fn reduced_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, r: O::V) -> O::V {
let h = crate::recipe::horner(o, r, if o.width() == crate::buffer::FloatWidth::F64 { &EXP_64 } else { &EXP_32 });
o.multiply_add(r, h, o.number(F::one()))
}
#[inline(always)]
fn clamp_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
let limit = o.number(F::narrow(if o.width() == crate::buffer::FloatWidth::F64 { 1100.0 } else { 150.0 }));
let low = o.negate(limit);
o.select(o.less(x, low), low, o.select(o.less(limit, x), limit, x))
}
#[inline(always)]
fn parts_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, lo: O::V, k: O::V) -> O::V {
let c = clamp_with(o, x);
let (n, r) = reduce_with(o, c);
let r = o.select(o.equal(c, x), o.add(r, lo), r);
crate::recipe::ldexp(o, reduced_with(o, r), o.add(n, k))
}
#[inline(always)]
pub(crate) fn formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
let zero = o.number(-F::zero());
parts_with(o, x, zero, zero)
}
#[inline(always)]
fn minus_one_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, r: O::V, scale: O::V) -> O::V {
let h = crate::recipe::horner(o, r, if o.width() == crate::buffer::FloatWidth::F64 { &H_64 } else { &H_32 });
o.multiply_add(o.multiply(r, h), scale, o.subtract(scale, o.number(F::one())))
}
#[inline]
pub(crate) fn scaled_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, scale: O::V) -> O::V {
parts_with(o, x, o.number(-F::zero()), scale)
}
#[inline]
pub(crate) fn minus_one_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, scale: O::V) -> O::V {
let (low, top) = if o.width() == crate::buffer::FloatWidth::F64 { (-40.0, 1023.0) } else { (-18.0, 127.0) };
let low = o.number(F::narrow(low));
let (n, r) = reduce_with(o, clamp_with(o, o.select(o.less(x, low), low, x)));
let top = o.number(F::narrow(top));
let m = o.select(o.less(top, n), top, n);
let shift = crate::recipe::rounding_shift(o);
let first = minus_one_with(o, r, crate::recipe::pow2(o, o.add(m, shift)));
o.multiply(first, crate::recipe::pow2(o, o.add(o.add(o.subtract(n, m), scale), shift)))
}