use crate::core::{nodes, ChebyScalar, ChebySeries, ChebySeriesOn, Domain, NodeKind};
#[inline]
pub fn fit_coeffs<T: ChebyScalar, const N: usize>(values: &[T; N]) -> [T; N] {
let mut coeffs = [T::zero(); N];
if N == 0 {
return coeffs;
}
let xs = nodes::<N>(NodeKind::Roots);
for (k, &v) in values.iter().enumerate() {
let x = xs[k];
coeffs[0] = coeffs[0] + v;
if N > 1 {
coeffs[1] = coeffs[1] + v * x;
let two_x = 2.0 * x;
let mut tkm1 = 1.0_f64;
let mut tk = x;
for coeff in coeffs.iter_mut().take(N).skip(2) {
let tkp1 = two_x * tk - tkm1;
*coeff = *coeff + v * tkp1;
tkm1 = tk;
tk = tkp1;
}
}
}
let nf = N as f64;
coeffs[0] = coeffs[0] / nf;
let scale = 2.0 / nf;
for coeff in coeffs.iter_mut().skip(1) {
*coeff = *coeff * scale;
}
coeffs
}
#[inline]
pub fn fit_from_fn<T: ChebyScalar, const N: usize>(f: impl Fn(f64) -> T) -> ChebySeries<T, N> {
let xs = nodes::<N>(NodeKind::Roots);
let values = core::array::from_fn(|k| f(xs[k]));
ChebySeries::new(fit_coeffs(&values))
}
#[inline]
pub fn fit_from_fn_on<T, X, const N: usize>(
domain: Domain<X>,
f: impl Fn(X) -> T,
) -> ChebySeriesOn<T, X, N>
where
T: ChebyScalar,
X: crate::core::ChebyTime,
{
let xs = crate::core::nodes::nodes_mapped::<X, N>(domain, NodeKind::Roots);
let values = core::array::from_fn(|k| f(xs[k]));
ChebySeriesOn::new(domain, ChebySeries::new(fit_coeffs(&values)))
}