use crate::core::{ChebyScalar, ChebyTime, Domain, IntegrateWith, NodeKind};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ClenshawCurtisRule<const N: usize> {
nodes: [f64; N],
weights: [f64; N],
}
impl<const N: usize> Default for ClenshawCurtisRule<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> ClenshawCurtisRule<N> {
#[inline]
pub fn new() -> Self {
Self {
nodes: crate::core::nodes::nodes::<N>(NodeKind::GaussLobatto),
weights: clenshaw_curtis_weights::<N>(),
}
}
#[inline]
pub fn nodes(&self) -> &[f64; N] {
&self.nodes
}
#[inline]
pub fn weights(&self) -> &[f64; N] {
&self.weights
}
#[inline]
pub fn integrate<T, X>(
&self,
domain: Domain<X>,
f: impl Fn(X) -> T,
) -> <T as IntegrateWith<X>>::Integral
where
T: ChebyScalar + IntegrateWith<X>,
X: ChebyTime,
{
let mut sum = T::zero();
for k in 0..N {
sum = sum + f(domain.denormalize(self.nodes[k])) * self.weights[k];
}
sum.scale_integral(domain.half_width())
}
}
pub fn clenshaw_curtis_weights<const N: usize>() -> [f64; N] {
let mut weights = [0.0; N];
if N == 0 {
return weights;
}
if N == 1 {
weights[0] = 2.0;
return weights;
}
if N == 2 {
weights[0] = 1.0;
weights[1] = 1.0;
return weights;
}
let m = N - 1; let theta: [f64; N] = core::array::from_fn(|k| core::f64::consts::PI * k as f64 / m as f64);
if m.is_multiple_of(2) {
weights[0] = 1.0 / ((m as f64) * (m as f64) - 1.0);
weights[N - 1] = weights[0];
} else {
weights[0] = 1.0 / (m as f64 * m as f64);
weights[N - 1] = weights[0];
}
for (k, w) in weights.iter_mut().enumerate().take(N - 1).skip(1) {
let mut v = 1.0_f64;
let half = m / 2;
let upper = if m.is_multiple_of(2) { half - 1 } else { half };
for j in 1..=upper {
v -= 2.0 * (2.0 * j as f64 * theta[k]).cos() / (4.0 * (j * j) as f64 - 1.0);
}
if m.is_multiple_of(2) {
v -= (m as f64 * theta[k]).cos() / ((m as f64) * (m as f64) - 1.0);
}
*w = 2.0 * v / m as f64;
}
weights
}
pub fn integrate<T, X, const N: usize>(
domain: Domain<X>,
f: impl Fn(X) -> T,
) -> <T as IntegrateWith<X>>::Integral
where
T: ChebyScalar + IntegrateWith<X>,
X: ChebyTime,
{
ClenshawCurtisRule::<N>::new().integrate(domain, f)
}