use super::coefficients_pair::CoefficientsPair;
use super::n_choose_m::NChooseM;
use super::rational_coeffs::RationalCoefficients;
#[derive(Debug, Clone)]
pub(crate) struct GaussJacksonCoeffs {
pub predictor: CoefficientsPair,
pub corrector: Vec<CoefficientsPair>,
pub max_order: usize,
pub order: usize,
}
impl GaussJacksonCoeffs {
pub fn configure(max_order: usize) -> Self {
let predictor = CoefficientsPair::configure(max_order);
let corrector = (0..=max_order)
.map(|_| CoefficientsPair::configure(max_order))
.collect();
Self {
predictor,
corrector,
max_order,
order: 0,
}
}
pub fn compute_coeffs(&mut self, order_in: usize) {
assert!(order_in <= self.max_order);
self.order = order_in;
let mut n_choose_m = NChooseM::new();
let mut ac = RationalCoefficients::configure_adams_corrector(order_in + 3);
let mut sc = ac.construct_stormer_cowell_corrector();
let mut ap = ac.construct_predictor();
let mut sp = sc.construct_predictor();
ac.discard_extra_terms(1, 1);
ap.discard_extra_terms(1, 1);
sc.discard_extra_terms(2, 0);
sp.discard_extra_terms(2, 0);
ap.convert_to_ordinate_form(&mut n_choose_m, &mut self.predictor.sa_coefs);
sp.convert_to_ordinate_form(&mut n_choose_m, &mut self.predictor.gj_coefs);
ac.convert_to_ordinate_form(&mut n_choose_m, &mut self.corrector[order_in].sa_coefs);
sc.convert_to_ordinate_form(&mut n_choose_m, &mut self.corrector[order_in].gj_coefs);
for ii in 1..=order_in {
ac.displace_back();
ac.convert_to_ordinate_form(
&mut n_choose_m,
&mut self.corrector[order_in - ii].sa_coefs,
);
sc.displace_back();
sc.convert_to_ordinate_form(
&mut n_choose_m,
&mut self.corrector[order_in - ii].gj_coefs,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_coeffs_order4() {
let mut coeffs = GaussJacksonCoeffs::configure(14);
coeffs.compute_coeffs(4);
assert!(
coeffs.predictor.sa_coefs[..5]
.iter()
.any(|c| c.abs() > 1e-15),
"Predictor SA coefficients should be non-zero"
);
assert!(
coeffs.corrector[4].sa_coefs[..5]
.iter()
.any(|c| c.abs() > 1e-15),
"Corrector[4] SA coefficients should be non-zero"
);
assert!(
coeffs.corrector[0].sa_coefs[..5]
.iter()
.any(|c| c.abs() > 1e-15),
"Corrector[0] SA coefficients should be non-zero"
);
}
#[test]
fn test_compute_coeffs_order8() {
let mut coeffs = GaussJacksonCoeffs::configure(14);
coeffs.compute_coeffs(8);
let sum: f64 = coeffs.predictor.gj_coefs[..9].iter().sum();
assert!(sum.abs() > 1e-10, "GJ predictor coefs should sum non-zero");
}
#[test]
fn test_compute_coeffs_order12() {
let mut coeffs = GaussJacksonCoeffs::configure(14);
coeffs.compute_coeffs(12);
for ii in 0..=12 {
let sa_sum: f64 = coeffs.corrector[ii].sa_coefs[..13]
.iter()
.map(|c| c.abs())
.sum();
assert!(sa_sum > 1e-15, "corrector[{ii}] SA should be non-zero");
}
}
}