use thermite::{
element::FloatElementWithBits,
mask::GenericMask,
math::{
CoreMathWithPolicy as _, FloatConsts, TranscendentalMathWithPolicy as _,
policy::{Policy, PrecisionPolicy},
},
register::FloatElement,
vector::FloatVectorWithBits,
};
const fn recurrence_threshold(n: usize, mantissa_bits: u32) -> f64 {
if n <= 1 {
return f64::MAX;
}
const fn const_powi_f64(mut x: f64, mut n: u32) -> f64 {
let mut r = 1.0f64;
while n > 0 {
if n & 1 == 1 {
r *= x;
}
n >>= 1;
if n > 0 {
x *= x;
}
}
r
}
let k = (n - 1) as u32;
let target: f64 = {
let mut f = (1u64 << mantissa_bits) as f64;
let mut i = 2usize;
while i < n {
f *= i as f64;
i += 1;
}
f
};
let mut lo = 0.0f64;
let mut hi = if k == 1 {
target
} else {
(1u64 << (mantissa_bits / 2 + 2)) as f64
};
let mut i = 0;
while i < 64 {
let mid = (lo + hi) * 0.5;
if const_powi_f64(mid, k) < target {
lo = mid;
} else {
hi = mid;
}
i += 1;
}
let result = (lo + hi) * 0.5;
if result < n as f64 { n as f64 } else { result }
}
pub trait ExpIntConsts<const N: usize>: FloatConsts + Sized {
const SMALL_N: [Self; 6];
const SMALL_D: [Self; 6];
const LARGE_N: [Self; 11];
const LARGE_D: [Self; 12];
const ASYMPTOTIC_CONST: Self;
const RECURRENCE_THRESHOLD: Self;
const ONE_OVER_N_MINUS_1: Self;
const FACTORS: [Self; N]; const RECIPROCALS: [Self; N]; }
macro_rules! impl_expint_consts {
(
SMALL_N [ $($sn_value:literal),* $(,)? ],
SMALL_D [ $($sd_value:literal),* $(,)? ],
LARGE_N [ $($ln_value:literal),* $(,)? ],
LARGE_D [ $($ld_value:literal),* $(,)? ]
) => {
impl<const N: usize> ExpIntConsts<N> for f32 {
const SMALL_N: [Self; 6] = [$($sn_value),*];
const SMALL_D: [Self; 6] = [$($sd_value),*];
const LARGE_N: [Self; 11] = [$($ln_value),*];
const LARGE_D: [Self; 12] = [$($ld_value),*];
const ASYMPTOTIC_CONST: Self = 0.66373538970947265625;
const RECURRENCE_THRESHOLD: Self = const { recurrence_threshold(N, Self::MANTISSA_BITS) as f32 };
const ONE_OVER_N_MINUS_1: Self = if N > 1 { 1.0 / (N as Self - 1.0) } else { Self::INFINITY };
const FACTORS: [Self; N] = {
let mut facts = [0.0; N]; let mut i = 0;
while i < N { facts[i] = (2 + i) as Self; i += 1; }
facts
};
const RECIPROCALS: [Self; N] = {
let mut r = Self::FACTORS; let mut i = 0;
while i < N { r[i] = 1.0 / r[i]; i += 1; }
r
};
}
impl<const N: usize> ExpIntConsts<N> for f64 {
const SMALL_N: [Self; 6] = [$($sn_value),*];
const SMALL_D: [Self; 6] = [$($sd_value),*];
const LARGE_N: [Self; 11] = [$($ln_value),*];
const LARGE_D: [Self; 12] = [$($ld_value),*];
const ASYMPTOTIC_CONST: Self = 0.66373538970947265625;
const RECURRENCE_THRESHOLD: Self = const { recurrence_threshold(N, Self::MANTISSA_BITS) };
const ONE_OVER_N_MINUS_1: Self = if N > 1 { 1.0 / (N as Self - 1.0) } else { Self::INFINITY };
const FACTORS: [Self; N] = {
let mut facts = [0.0; N]; let mut i = 0;
while i < N { facts[i] = (2 + i) as Self; i += 1; }
facts
};
const RECIPROCALS: [Self; N] = {
let mut r = Self::FACTORS; let mut i = 0;
while i < N { r[i] = 1.0 / r[i]; i += 1; }
r
};
}
};
}
impl_expint_consts! {
SMALL_N [
-0.000111507792921197858394,
-0.00399167106081113256961,
-0.0368031736257943745142,
-0.245088216639761496153,
0.0320913665303559189999,
0.0865197248079397976498,
],
SMALL_D [
0.528611029520217142048e-6,
0.000131049900798434683324,
0.00427347600017103698101,
0.056770677104207528384,
0.37091387659397013215,
1.0,
],
LARGE_N [
-1185.45720315201027667,
-14751.4895786128450662,
-54844.4587226402067411,
-86273.1567711649528784,
-66598.2652345418633509,
-27182.6254466733970467,
-6046.8250112711035463,
-724.581482791462469795,
-43.3058660811817946037,
-0.999999999999998811143,
-0.121013190657725568138e-18,
],
LARGE_D [
-0.776491285282330997549,
1229.20784182403048905,
18455.4124737722049515,
86722.3403467334749201,
180329.498380501819718,
192104.047790227984431,
113057.05869159631492,
38129.5594484818471461,
7417.37624454689546708,
809.193214954550328455,
45.3058660811801465927,
1.0,
]
}
#[inline(always)]
pub fn expint_double<P: Policy, E, V, const N: usize>(x: V) -> V
where
E: FloatElementWithBits + ExpIntConsts<N>,
V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
{
expint_double_primal::<P, E, V, N>(x).0
}
pub fn expint_double_primal<P: Policy, E, V, const N: usize>(x: V) -> (V, V)
where
E: FloatElementWithBits + ExpIntConsts<N>,
V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
{
let exp_neg_x = (-x).exp_p::<P>();
let x_ex = exp_neg_x / x;
if const { N == 0 } {
let mut result = x_ex;
let mut prev = x_ex * (V::ONE + x.reciprocal_p::<P>());
if const { P::POLICY.check_overflow } {
let x_is_zero = x.is_zero();
result = x_is_zero.select(V::INFINITY, result);
prev = x_is_zero.select(V::INFINITY, prev);
let bad = x.cmp_lt(V::ZERO) | x.is_nan();
result = bad.select(V::NAN, result);
prev = bad.select(V::NAN, prev);
}
return (result, prev);
}
let is_large = x.cmp_gt(V::ONE);
let inv_x = x.reciprocal_p::<P>();
let mut e_n = x
.poly_rev_p::<P, _>(&E::SMALL_N)
.approx_div_p::<P>(x.poly_rev_p::<P, _>(&E::SMALL_D));
let large_e1 = inv_x
.poly_rev_p::<P, _>(&E::LARGE_N)
.approx_div_p::<P>(inv_x.poly_rev_p::<P, _>(&E::LARGE_D));
e_n += x - x.ln_p::<P>() - V::splat(E::ASYMPTOTIC_CONST);
e_n = is_large.select((V::ONE + large_e1) * x_ex, e_n);
let mut e_prev = x_ex;
if const { N > 1 } {
e_prev = e_n;
e_n = x.nmul_adde(e_n, exp_neg_x);
if const { N > 2 } {
if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
let mut n = 0;
while n < (N - 2) {
e_prev = e_n;
e_n = x.nmul_adde(e_n, exp_neg_x) / V::splat(E::FACTORS[n]);
n += 1;
}
} else {
use crunchy::unroll;
macro_rules! unroll_recurrence {
($($len:tt),*) => {
$( if const { N == ($len + 2) } {
unroll! { for n in 0..$len {
e_prev = e_n;
e_n = x.nmul_adde(e_n, exp_neg_x)
.scale(const { if n < N { E::RECIPROCALS[n] } else { E::ONE } });
}}
} else )* {
let mut n = 0;
while n < const { if N > 2 { N - 2 } else { 0 } } {
e_prev = e_n;
e_n = x.nmul_adde(e_n, exp_neg_x).scale(E::RECIPROCALS[n]);
n += 1;
}
}
};
}
unroll_recurrence!(1, 2, 3, 4, 5, 6); }
}
}
let is_very_large = x.cmp_ge(V::splat(E::RECURRENCE_THRESHOLD));
if const { N > 1 } && thermite::unlikely(is_very_large.any()) {
let mut term = V::ONE;
let mut partial_sum = V::ONE;
let mut prev_abs = V::INFINITY;
let mut k = 0u32;
while k < 2 * N as u32 + 8 {
term *= inv_x.scale(FloatElement::from_int(
const { -(N as thermite::LargeInt) } - k as thermite::LargeInt,
));
let abs_term = term.abs();
let still_shrinking = abs_term.cmp_le(prev_abs);
if k % 4 == 3 && still_shrinking.none() {
break;
}
partial_sum = partial_sum.add_c(still_shrinking, term);
prev_abs = still_shrinking.select(abs_term, prev_abs);
k += 1;
}
e_n = is_very_large.select(x_ex * partial_sum.abs(), e_n);
let back = (exp_neg_x - e_n.scale(E::from_int(const { N as thermite::LargeInt - 1 }))) / x;
e_prev = is_very_large.select(back, e_prev);
}
if const { P::POLICY.check_overflow } {
let x_is_zero = x.is_zero();
if const { N == 1 } {
e_n = x_is_zero.select(V::INFINITY, e_n);
} else if const { N > 1 } {
e_n = x_is_zero.select(V::splat(E::ONE_OVER_N_MINUS_1), e_n);
}
if const { N <= 2 } {
e_prev = x_is_zero.select(V::INFINITY, e_prev);
} else {
e_prev = x_is_zero.select(
V::splat(E::ONE / E::from_int(const { N as thermite::LargeInt - 2 })),
e_prev,
);
}
let bad = x.cmp_lt(V::ZERO) | x.is_nan();
e_n = bad.select(V::NAN, e_n);
e_prev = bad.select(V::NAN, e_prev);
}
(e_n, e_prev)
}