81 lines
2.3 KiB
Rust
81 lines
2.3 KiB
Rust
//! 二次插值函数。
|
||
//!
|
||
//! 重构自 TLUSTY `yint.f`
|
||
|
||
/// 二次插值。
|
||
///
|
||
/// 给定三个点 (xl[0],yl[0]), (xl[1],yl[1]), (xl[2],yl[2]),
|
||
/// 计算 xl0 处的插值结果。
|
||
///
|
||
/// # 参数
|
||
/// * `xl` - x 坐标数组(长度为 3)
|
||
/// * `yl` - y 坐标数组(长度为 3)
|
||
/// * `xl0` - 需要插值的位置
|
||
///
|
||
/// # 返回值
|
||
/// 返回插值结果
|
||
///
|
||
/// # 注意
|
||
/// 调用者需确保 xl[0], xl[1], xl[2] 互不相等
|
||
pub fn yint(xl: &[f64], yl: &[f64], xl0: f64) -> f64 {
|
||
// Fortran 索引转换:XL(1) -> xl[0], XL(2) -> xl[1], XL(3) -> xl[2]
|
||
let a0 = (xl[1] - xl[0]) * (xl[2] - xl[1]) * (xl[2] - xl[0]);
|
||
let a1 = (xl0 - xl[1]) * (xl0 - xl[2]) * (xl[2] - xl[1]);
|
||
let a2 = (xl0 - xl[0]) * (xl[2] - xl0) * (xl[2] - xl[0]);
|
||
let a3 = (xl0 - xl[0]) * (xl0 - xl[1]) * (xl[1] - xl[0]);
|
||
|
||
(yl[0] * a1 + yl[1] * a2 + yl[2] * a3) / a0
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use approx::assert_relative_eq;
|
||
|
||
#[test]
|
||
fn test_yint_at_points() {
|
||
let xl = [0.0, 1.0, 2.0];
|
||
let yl = [1.0, 2.0, 5.0];
|
||
|
||
// 测试在插值节点上的值
|
||
assert_relative_eq!(yint(&xl, &yl, 0.0), 1.0, epsilon = 1e-15);
|
||
assert_relative_eq!(yint(&xl, &yl, 1.0), 2.0, epsilon = 1e-15);
|
||
assert_relative_eq!(yint(&xl, &yl, 2.0), 5.0, epsilon = 1e-15);
|
||
}
|
||
|
||
#[test]
|
||
fn test_yint_quadratic() {
|
||
// y = x^2
|
||
let xl = [0.0, 1.0, 2.0];
|
||
let yl = [0.0, 1.0, 4.0];
|
||
|
||
assert_relative_eq!(yint(&xl, &yl, 0.5), 0.25, epsilon = 1e-15);
|
||
assert_relative_eq!(yint(&xl, &yl, 1.5), 2.25, epsilon = 1e-15);
|
||
}
|
||
|
||
#[test]
|
||
fn test_yint_extrapolation() {
|
||
let xl = [0.0, 1.0, 2.0];
|
||
let yl = [0.0, 1.0, 4.0];
|
||
|
||
// 外推: y = x^2
|
||
assert_relative_eq!(yint(&xl, &yl, -1.0), 1.0, epsilon = 1e-15);
|
||
assert_relative_eq!(yint(&xl, &yl, 3.0), 9.0, epsilon = 1e-15);
|
||
}
|
||
|
||
#[test]
|
||
fn test_yint_vs_lagran() {
|
||
// yint 和 lagran 应该给出相同的结果
|
||
use crate::tlusty::math::lagran;
|
||
|
||
let xl = [0.0, 1.0, 2.0];
|
||
let yl = [1.0, 3.0, 2.0];
|
||
|
||
for x in [0.0, 0.5, 1.0, 1.5, 2.0, 3.0].iter() {
|
||
let y1 = yint(&xl, &yl, *x);
|
||
let y2 = lagran(xl[0], xl[1], xl[2], yl[0], yl[1], yl[2], *x);
|
||
assert_relative_eq!(y1, y2, epsilon = 1e-15);
|
||
}
|
||
}
|
||
}
|