use crate::errors::MathError;
use super::{InterpolationError, MAX_SAMPLES};
pub fn hermite_eval(
xs: &[f64],
ys: &[f64],
ydots: &[f64],
x_eval: f64,
) -> Result<(f64, f64), InterpolationError> {
if xs.len() != ys.len() || xs.len() != ydots.len() {
return Err(InterpolationError::CorruptedData {
what: "lengths of abscissas (xs), ordinates (ys), and first derivatives (ydots) differ",
});
} else if xs.is_empty() {
return Err(InterpolationError::CorruptedData {
what: "list of abscissas (xs) is empty",
});
} else if xs.len() > MAX_SAMPLES {
return Err(InterpolationError::CorruptedData {
what: "list of abscissas (xs) contains more items than MAX_SAMPLES (32)",
});
}
let work: &mut [f64] = &mut [0.0; 4 * MAX_SAMPLES];
let n: usize = xs.len();
for i in 0..n {
work[2 * i] = ys[i];
work[2 * i + 1] = ydots[i];
}
for i in 1..=n - 1 {
let c1 = xs[i] - x_eval;
let c2 = x_eval - xs[i - 1];
let denom = xs[i] - xs[i - 1];
if denom.abs() < f64::EPSILON {
return Err(InterpolationError::InterpMath {
source: MathError::DivisionByZero {
action:
"hermite data contains likely duplicate abcissa, remove duplicate states",
},
});
}
let prev = 2 * i - 1;
let curr = 2 * i;
work[prev + 2 * n - 1] = work[prev];
work[prev + 2 * n] = (work[curr] - work[prev - 1]) / denom;
let temp = work[prev] * (x_eval - xs[i - 1]) + work[prev - 1];
work[prev] = (c1 * work[prev - 1] + c2 * work[curr]) / denom;
work[prev - 1] = temp;
}
work[4 * n - 2] = work[(2 * n) - 1];
work[2 * (n - 1)] += work[(2 * n) - 1] * (x_eval - xs[n - 1]);
for j in 2..=(2 * n) - 1 {
for i in 1..=(2 * n) - j {
let xi = i.div_ceil(2);
let xij = (i + j).div_ceil(2);
let c1 = xs[xij - 1] - x_eval;
let c2 = x_eval - xs[xi - 1];
let denom = xs[xij - 1] - xs[xi - 1];
if denom.abs() < f64::EPSILON {
return Err(InterpolationError::InterpMath {
source: MathError::DivisionByZero {
action: "hermite data contains duplicate states",
},
});
}
work[i + 2 * n - 1] =
(c1 * work[i + 2 * n - 1] + c2 * work[i + 2 * n] + (work[i] - work[i - 1])) / denom;
work[i - 1] = (c1 * work[i - 1] + c2 * work[i]) / denom;
}
}
let f = work[0];
let df = work[2 * n];
Ok((f, df))
}
#[test]
fn hermite_spice_docs_example() {
let ts = [-1.0, 0.0, 3.0, 5.0];
let yvals = [6.0, 5.0, 2210.0, 78180.0];
let ydotvals = [3.0, 0.0, 5115.0, 109395.0];
for (i, t) in ts.iter().enumerate() {
let (eval, deriv) = hermite_eval(&ts, &yvals, &ydotvals, *t).unwrap();
let eval_err = (eval - yvals[i]).abs();
assert!(eval_err < f64::EPSILON, "f(x) error is {eval_err:e}");
let deriv_err = (deriv - ydotvals[i]).abs();
assert!(deriv_err < f64::EPSILON, "f'(x) error is {deriv_err:e}");
}
let (x, vx) = hermite_eval(&ts, &yvals, &ydotvals, 2.0).unwrap();
assert!((x - 141.0).abs() < f64::EPSILON, "X error");
assert!((vx - 456.0).abs() < f64::EPSILON, "VX error");
}