pub fn chebyshev_series<P, E, V, const K: usize, const N: usize, const REINSCH: bool>(
x: V,
coeffs: &[E],
) -> VExpand description
Shared Chebyshev series summation for all element types, all four kinds, and both the compile-time and runtime coefficient counts.
Evaluates $\sum_{k=0}^{N-1} c_k P_k(x)$ where P_k is T_k, U_k, V_k, or W_k
for K of 1, 2, 3, or 4. All four share the recurrence
$P_{k+1}(x) = 2x P_k(x) - P_{k-1}(x)$ with P_0 = 1, differing only in P_1, so
the b_k loop below is common to every kind.
REINSCH says whether the arithmetic admits the endpoint form (see below): it needs
a real copysign and a meaningful nearest endpoint, so real vectors pass true and
Complex and the composites pass false. It is a capability, not a request: the
form is taken only when the policy also asks for Best precision or better.
§The N parameter
N is the coefficient count when the caller knows it and 0 when it does not, the
same sentinel fast_polynomial::poly_f_internal uses. At a nonzero N the length is
handed to LLVM as an assert_unchecked, so the n == 1/n == 2 shortcuts fold away
and the loop unrolls exactly as it did when the bound was the const generic itself. At
N = 0 every one of those becomes an ordinary runtime branch.
This replaces a hand-ported chebyshev_series_slice that duplicated the whole
recurrence, Reinsch arm included, under a doc comment reading “both forms must be edited
together”. A series is still not a reduction, since it carries k-dependent state and
cannot be folded over chunks the way a norm can, so sharing the body is the only way
to share anything here, and it is what removes the drift.
Chebyshev is the merge’s safe case on purpose: the only per-step quantity is
coeffs[k], so nothing here depends on k becoming a literal. The Legendre, Hermite
and Laguerre series do (a division or a square root per step folds away only if the
loop unrolls), which is why they have not been merged.
§Safety
N != 0 promises coeffs.len() == N. The const-length entry point is the only caller
that passes a nonzero N, and it takes a &[E; N], so the promise is the array’s.
The empty series is 0. N = 0 is therefore both “unknown length” and “empty”, which
agree: an empty slice returns V::ZERO down the runtime path. The rejection of an empty
const count lives on the chebyshev_n entry point, where it is still a compile error.