Skip to main content

chebyshev_series

Function chebyshev_series 

Source
pub fn chebyshev_series<P, E, V, const K: usize, const N: usize, const REINSCH: bool>(
    x: V,
    coeffs: &[E],
) -> V
where P: Policy, E: FloatElement, V: FloatVector<Element = E>,
Expand 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.

Last built: 2026-09-08 21:35:55 UTC