use nalgebra::DVector;
use std::f64;
#[allow(dead_code)]
fn lagrange_interpolate(x: f64, x_vals: &DVector<f64>, y_vals: &DVector<f64>) -> f64 {
let n = x_vals.len();
let mut result = 0.0;
for i in 0..n {
let mut term = y_vals[i];
for j in 0..n {
if i != j {
term *= (x - x_vals[j]) / (x_vals[i] - x_vals[j]);
}
}
result += term;
}
result
}
#[allow(dead_code)]
fn newton_divided_differences(x_vals: &DVector<f64>, y_vals: &DVector<f64>) -> DVector<f64> {
let n = x_vals.len();
let mut coef = y_vals.clone();
for j in 1..n {
for i in (j..n).rev() {
coef[i] = (coef[i] - coef[i - 1]) / (x_vals[i] - x_vals[i - j]);
}
}
coef
}
#[allow(dead_code)]
fn newton_interpolate(x: f64, x_vals: &DVector<f64>, coef: &DVector<f64>) -> f64 {
let n = coef.len();
let mut result = coef[n - 1];
for i in (0..n - 1).rev() {
result = result * (x - x_vals[i]) + coef[i];
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_lagrange_linear_interpolation() {
let x_vals = DVector::from_vec(vec![0.0, 1.0]);
let y_vals = DVector::from_vec(vec![1.0, 3.0]);
assert_relative_eq!(
lagrange_interpolate(0.0, &x_vals, &y_vals),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(1.0, &x_vals, &y_vals),
3.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(0.5, &x_vals, &y_vals),
2.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(2.0, &x_vals, &y_vals),
5.0,
epsilon = 1e-10
);
}
#[test]
fn test_lagrange_quadratic_interpolation() {
let x_vals = DVector::from_vec(vec![0.0, 1.0, 2.0]);
let y_vals = DVector::from_vec(vec![1.0, 0.0, 1.0]);
assert_relative_eq!(
lagrange_interpolate(0.0, &x_vals, &y_vals),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(1.0, &x_vals, &y_vals),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(2.0, &x_vals, &y_vals),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(0.5, &x_vals, &y_vals),
0.25,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(1.5, &x_vals, &y_vals),
0.25,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(3.0, &x_vals, &y_vals),
4.0,
epsilon = 1e-10
);
}
#[test]
fn test_lagrange_cubic_interpolation() {
let x_vals = DVector::from_vec(vec![0.0, 1.0, 2.0, 3.0]);
let y_vals = DVector::from_vec(vec![-6.0, 0.0, 0.0, 0.0]);
assert_relative_eq!(
lagrange_interpolate(0.0, &x_vals, &y_vals),
-6.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(1.0, &x_vals, &y_vals),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(2.0, &x_vals, &y_vals),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(3.0, &x_vals, &y_vals),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(1.5, &x_vals, &y_vals),
0.375,
epsilon = 1e-10
);
}
#[test]
fn test_newton_divided_differences_linear() {
let x_vals = DVector::from_vec(vec![0.0, 1.0]);
let y_vals = DVector::from_vec(vec![1.0, 3.0]);
let coef = newton_divided_differences(&x_vals, &y_vals);
assert_relative_eq!(coef[0], 1.0, epsilon = 1e-10);
assert_relative_eq!(coef[1], 2.0, epsilon = 1e-10);
}
#[test]
fn test_newton_divided_differences_quadratic() {
let x_vals = DVector::from_vec(vec![0.0, 1.0, 2.0]);
let y_vals = DVector::from_vec(vec![0.0, 1.0, 4.0]);
let coef = newton_divided_differences(&x_vals, &y_vals);
assert_relative_eq!(coef[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(coef[1], 1.0, epsilon = 1e-10);
assert_relative_eq!(coef[2], 1.0, epsilon = 1e-10);
}
#[test]
fn test_newton_interpolate_linear() {
let x_vals = DVector::from_vec(vec![0.0, 1.0]);
let y_vals = DVector::from_vec(vec![1.0, 3.0]);
let coef = newton_divided_differences(&x_vals, &y_vals);
assert_relative_eq!(
newton_interpolate(0.0, &x_vals, &coef),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(1.0, &x_vals, &coef),
3.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(0.5, &x_vals, &coef),
2.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(2.0, &x_vals, &coef),
5.0,
epsilon = 1e-10
);
}
#[test]
fn test_newton_interpolate_quadratic() {
let x_vals = DVector::from_vec(vec![0.0, 1.0, 2.0]);
let y_vals = DVector::from_vec(vec![1.0, 0.0, 1.0]);
let coef = newton_divided_differences(&x_vals, &y_vals);
assert_relative_eq!(
newton_interpolate(0.0, &x_vals, &coef),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(1.0, &x_vals, &coef),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(2.0, &x_vals, &coef),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(0.5, &x_vals, &coef),
0.25,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(1.5, &x_vals, &coef),
0.25,
epsilon = 1e-10
);
}
#[test]
fn test_newton_vs_lagrange_equivalence() {
let x_vals = DVector::from_vec(vec![0.0, 1.0, 2.0, 3.0]);
let y_vals = DVector::from_vec(vec![1.0, 4.0, 9.0, 16.0]);
let coef = newton_divided_differences(&x_vals, &y_vals);
let test_points = vec![-1.0, 0.5, 1.5, 2.5, 4.0];
for &x in &test_points {
let lagrange_result = lagrange_interpolate(x, &x_vals, &y_vals);
let newton_result = newton_interpolate(x, &x_vals, &coef);
assert_relative_eq!(lagrange_result, newton_result, epsilon = 1e-10,)
}
}
#[test]
fn test_interpolation_with_uneven_spacing() {
let x_vals = DVector::from_vec(vec![-2.0, -0.5, 1.0, 3.5]);
let y_vals = DVector::from_vec(vec![4.0, 0.25, 1.0, 12.25]);
let coef = newton_divided_differences(&x_vals, &y_vals);
for i in 0..x_vals.len() {
assert_relative_eq!(
lagrange_interpolate(x_vals[i], &x_vals, &y_vals),
y_vals[i],
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(x_vals[i], &x_vals, &coef),
y_vals[i],
epsilon = 1e-10
);
}
let test_x = 0.0;
let expected_y = 0.0;
assert_relative_eq!(
lagrange_interpolate(test_x, &x_vals, &y_vals),
expected_y,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(test_x, &x_vals, &coef),
expected_y,
epsilon = 1e-10
);
}
#[test]
fn test_single_point_interpolation() {
let x_vals = DVector::from_vec(vec![1.0]);
let y_vals = DVector::from_vec(vec![5.0]);
let coef = newton_divided_differences(&x_vals, &y_vals);
assert_relative_eq!(
lagrange_interpolate(0.0, &x_vals, &y_vals),
5.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(1.0, &x_vals, &y_vals),
5.0,
epsilon = 1e-10
);
assert_relative_eq!(
lagrange_interpolate(10.0, &x_vals, &y_vals),
5.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(0.0, &x_vals, &coef),
5.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(1.0, &x_vals, &coef),
5.0,
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(10.0, &x_vals, &coef),
5.0,
epsilon = 1e-10
);
}
#[test]
fn test_interpolation_with_sine_function() {
use std::f64::consts::PI;
let x_vals = DVector::from_vec(vec![0.0, PI / 6.0, PI / 4.0, PI / 3.0, PI / 2.0]);
let y_vals = DVector::from_vec(vec![
0.0, 0.5, 2_f64.sqrt() / 2.0, 3_f64.sqrt() / 2.0, 1.0, ]);
let coef = newton_divided_differences(&x_vals, &y_vals);
for i in 0..x_vals.len() {
assert_relative_eq!(
lagrange_interpolate(x_vals[i], &x_vals, &y_vals),
y_vals[i],
epsilon = 1e-10
);
assert_relative_eq!(
newton_interpolate(x_vals[i], &x_vals, &coef),
y_vals[i],
epsilon = 1e-10
)
}
}
}