use {
crate::Approx,
sigma_types::{Finite, Zero as _, usize::LessThan},
};
#[cfg(feature = "error")]
use {crate::constants, sigma_types::NonNegative};
#[inline]
#[must_use]
pub fn eval<const N_COEFFICIENTS: usize>(
coefficients: &[Finite<f64>; N_COEFFICIENTS],
x: Finite<f64>,
#[cfg(feature = "precision")] order: LessThan<{ N_COEFFICIENTS }>,
) -> Approx {
#![expect(
clippy::arithmetic_side_effects,
reason = "property-based testing ensures this never happens"
)]
debug_assert!(
N_COEFFICIENTS > 0,
"Chebyshev series without any coefficients",
);
let two_x: Finite<f64> = Finite::new(2_f64) * x;
#[cfg(feature = "error")]
let mut e = NonNegative::<Finite<f64>>::ZERO;
let mut d = Finite::<f64>::ZERO;
let mut dd = Finite::<f64>::ZERO;
{
let mut j: LessThan<{ N_COEFFICIENTS }> = {
#[cfg(feature = "precision")]
{
order
}
#[cfg(not(feature = "precision"))]
{
LessThan::new(const { N_COEFFICIENTS - 1 })
}
};
while *j >= 1 {
let coefficient = *unsafe { coefficients.get_unchecked(*j) };
let tmp = d;
d = ((two_x * d) - dd) + coefficient;
#[cfg(feature = "error")]
{
e += NonNegative::<Finite<f64>>::new((two_x * tmp).map(f64::abs))
+ NonNegative::<Finite<f64>>::new(dd.map(f64::abs))
+ NonNegative::<Finite<f64>>::new(coefficient.map(f64::abs));
}
dd = tmp;
j.map_mut(|u| *u -= 1);
}
}
{
#[cfg(feature = "error")]
let tmp = d;
let coefficient = *unsafe { coefficients.get_unchecked(0) };
let half_coefficient = coefficient.map(|c| 0.5_f64 * c);
d = x * d - dd + half_coefficient;
#[cfg(feature = "error")]
{
e += NonNegative::<Finite<f64>>::new((x * tmp).map(f64::abs))
+ NonNegative::<Finite<f64>>::new(dd.map(f64::abs))
+ NonNegative::<Finite<f64>>::new(half_coefficient.map(f64::abs));
}
}
#[cfg(feature = "error")]
let last_coefficient = *unsafe { coefficients.get_unchecked(const { N_COEFFICIENTS - 1 }) };
Approx {
value: d,
#[cfg(feature = "error")]
error: NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON)) * e
+ NonNegative::new(last_coefficient.map(f64::abs)),
}
}
#[inline]
#[cfg_attr(not(test), expect(dead_code, reason = "TODO: REMOVE"))]
#[cfg_attr(test, expect(clippy::single_call_fn, reason = "TODO: REMOVE"))]
pub(crate) const fn min(a: usize, b: usize) -> usize {
if a.checked_sub(b).is_some() { b } else { a }
}