use super::n_choose_m::NChooseM;
use super::ratio128::Ratio128;
#[derive(Clone, Debug)]
pub(crate) struct RationalCoefficients {
pub coefficients: Vec<Ratio128>,
}
impl RationalCoefficients {
pub fn configure_adams_corrector(nelem: usize) -> Self {
let mut coefficients = vec![Ratio128::ZERO; nelem];
coefficients[0] = Ratio128::ONE;
for nn in 1..nelem {
let mut sum = Ratio128::ZERO;
for (ii, coeff) in coefficients.iter().enumerate().take(nn) {
sum -= *coeff / Ratio128::from((nn + 1 - ii) as i64);
}
coefficients[nn] = sum;
}
Self { coefficients }
}
pub fn construct_stormer_cowell_corrector(&self) -> Self {
let nelem = self.coefficients.len();
let mut result = vec![Ratio128::ZERO; nelem];
for (ii, res) in result.iter_mut().enumerate().take(nelem) {
let mut sum = Ratio128::ZERO;
for kk in 0..=ii {
sum += self.coefficients[kk] * self.coefficients[ii - kk];
}
*res = sum;
}
Self {
coefficients: result,
}
}
pub fn construct_predictor(&self) -> Self {
let nelem = self.coefficients.len();
let mut result = vec![Ratio128::ZERO; nelem];
let mut sum = Ratio128::ZERO;
for (res, coeff) in result.iter_mut().zip(self.coefficients.iter()).take(nelem) {
sum += *coeff;
*res = sum;
}
Self {
coefficients: result,
}
}
pub fn convert_to_ordinate_form(&self, n_choose_m: &mut NChooseM, result: &mut [f64]) {
let nelem = self.coefficients.len();
let mut rational_coefs = vec![Ratio128::ZERO; nelem];
for mm in 0..nelem {
let mut sum = Ratio128::ZERO;
for ii in mm..nelem {
sum += Ratio128::from(n_choose_m.get(ii, mm) as i64) * self.coefficients[ii];
}
if mm % 2 != 0 {
sum = -sum;
}
rational_coefs[nelem - 1 - mm] = sum;
}
Ratio128::slice_to_f64(&rational_coefs, result);
}
pub fn discard_extra_terms(&mut self, nfront: usize, nback: usize) {
assert_eq!(nfront + nback, 2);
self.coefficients.drain(..nfront);
let new_len = self.coefficients.len() - nback;
self.coefficients.truncate(new_len);
}
pub fn displace_back(&mut self) {
let nelem = self.coefficients.len();
let mut new_coefficients = vec![Ratio128::ZERO; nelem];
new_coefficients[0] = self.coefficients[0];
for (nc, w) in new_coefficients
.iter_mut()
.skip(1)
.zip(self.coefficients.windows(2))
{
*nc = w[1] - w[0];
}
self.coefficients = new_coefficients;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adams_corrector_order3() {
let ac = RationalCoefficients::configure_adams_corrector(6);
assert_eq!(ac.coefficients[0].to_f64(), 1.0);
assert!((ac.coefficients[1].to_f64() - (-0.5)).abs() < 1e-15);
assert!((ac.coefficients[2].to_f64() - (-1.0 / 12.0)).abs() < 1e-15);
assert!((ac.coefficients[3].to_f64() - (-1.0 / 24.0)).abs() < 1e-15);
}
#[test]
fn test_stormer_cowell_is_convolution() {
let ac = RationalCoefficients::configure_adams_corrector(6);
let sc = ac.construct_stormer_cowell_corrector();
assert_eq!(sc.coefficients[0].to_f64(), 1.0);
assert!((sc.coefficients[1].to_f64() - (-1.0)).abs() < 1e-15);
}
#[test]
fn test_predictor_is_cumulative_sum() {
let ac = RationalCoefficients::configure_adams_corrector(4);
let ap = ac.construct_predictor();
assert_eq!(ap.coefficients[0].to_f64(), 1.0);
assert!((ap.coefficients[1].to_f64() - 0.5).abs() < 1e-15);
}
}