use crate::core::{ChebyError, ChebyScalar, ChebySeries, ChebySeriesOn, ChebyTime, Domain};
pub use crate::core::IntegrateWith;
#[derive(Debug, Clone, PartialEq)]
pub struct DefiniteIntegralFromStart<T, X, const N: usize>
where
T: ChebyScalar,
X: ChebyTime,
{
domain: Domain<X>,
coeffs: [T; N],
coeffs_tail: T,
}
impl<T, X, const N: usize> DefiniteIntegralFromStart<T, X, N>
where
T: ChebyScalar + IntegrateWith<X>,
X: ChebyTime,
{
#[inline]
pub fn domain(&self) -> Domain<X> {
self.domain
}
pub fn evaluate(&self, x: X) -> Result<<T as IntegrateWith<X>>::Integral, ChebyError> {
if !self.domain.contains(x) {
return Err(ChebyError::EvaluationOutOfDomain);
}
let tau = self.domain.normalize(x);
let normalized = clenshaw_with_tail::<T, N>(&self.coeffs, self.coeffs_tail, tau);
Ok(normalized.scale_integral(self.domain.half_width()))
}
}
#[inline]
fn clenshaw_with_tail<T: ChebyScalar, const N: usize>(coeffs: &[T; N], tail: T, x: f64) -> T {
let two_x = 2.0 * x;
let mut b1 = T::zero();
let mut b2 = T::zero();
let mut b0 = b1 * two_x - b2 + tail;
b2 = b1;
b1 = b0;
for k in (1..N).rev() {
b0 = b1 * two_x - b2 + coeffs[k];
b2 = b1;
b1 = b0;
}
if N == 0 {
return b1;
}
coeffs[0] + b1 * x - b2
}
impl<T, X, const N: usize> ChebySeriesOn<T, X, N>
where
T: ChebyScalar + IntegrateWith<X>,
X: ChebyTime,
{
pub fn evaluate_integral_from_start(
&self,
x: X,
) -> Result<<T as IntegrateWith<X>>::Integral, ChebyError> {
self.definite_integral_from_start().evaluate(x)
}
pub fn definite_integral_from_start(&self) -> DefiniteIntegralFromStart<T, X, N> {
let domain = self.domain();
let coeffs_in = self.series().coeffs();
let mut out = [T::zero(); N];
let mut tail = T::zero();
if N >= 1 {
if N >= 2 {
out[1] = out[1] + coeffs_in[0];
} else {
tail = tail + coeffs_in[0];
}
}
if N >= 2 {
if N >= 3 {
out[2] = out[2] + coeffs_in[1] / 4.0;
} else {
tail = tail + coeffs_in[1] / 4.0;
}
}
for k in 2..N {
let plus = coeffs_in[k] / (2.0 * (k + 1) as f64);
let minus = coeffs_in[k] / (2.0 * (k - 1) as f64);
if k + 1 < N {
out[k + 1] = out[k + 1] + plus;
} else {
tail = tail + plus;
}
out[k - 1] = out[k - 1] - minus;
}
let mut shift = T::zero();
let mut sign = 1.0_f64;
for &b in out.iter() {
shift = shift + b * sign;
sign = -sign;
}
let tail_sign = if N.is_multiple_of(2) { 1.0 } else { -1.0 };
shift = shift + tail * tail_sign;
if N >= 1 {
out[0] = out[0] - shift;
} else {
tail = tail - shift;
}
DefiniteIntegralFromStart {
domain,
coeffs: out,
coeffs_tail: tail,
}
}
}
#[allow(dead_code)]
fn _force_chebyseries_use<T: ChebyScalar, const N: usize>(_: &ChebySeries<T, N>) {}