mod bilinear;
mod hermite;
mod lagrange;
mod linear;
mod spline;
#[cfg(test)]
mod tests;
pub use bilinear::BilinearInterp;
pub use hermite::HermiteInterp;
pub use lagrange::LagrangeInterp;
pub use linear::LinearInterp;
pub use spline::CubicSpline;
#[cfg(feature = "alloc")]
pub use bilinear::DynBilinearInterp;
#[cfg(feature = "alloc")]
pub use hermite::DynHermiteInterp;
#[cfg(feature = "alloc")]
pub use lagrange::DynLagrangeInterp;
#[cfg(feature = "alloc")]
pub use linear::DynLinearInterp;
#[cfg(feature = "alloc")]
pub use spline::DynCubicSpline;
use crate::traits::FloatScalar;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InterpError {
TooFewPoints,
NotSorted,
LengthMismatch,
IllConditioned,
}
impl core::fmt::Display for InterpError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
InterpError::TooFewPoints => write!(f, "not enough data points for interpolation"),
InterpError::NotSorted => write!(f, "x values must be strictly increasing"),
InterpError::LengthMismatch => write!(f, "xs and ys must have the same length"),
InterpError::IllConditioned => write!(f, "system is too ill-conditioned to solve"),
}
}
}
fn validate_sorted<T: FloatScalar>(xs: &[T]) -> Result<(), InterpError> {
let scale = if xs.is_empty() {
T::one()
} else {
let first = xs[0].abs();
let last = xs[xs.len() - 1].abs();
if first > last { first } else { last }.max(T::one())
};
let min_spacing = T::epsilon().sqrt() * scale;
for i in 1..xs.len() {
if xs[i] <= xs[i - 1] {
return Err(InterpError::NotSorted);
}
if xs[i] - xs[i - 1] < min_spacing {
return Err(InterpError::IllConditioned);
}
}
Ok(())
}
fn find_interval<T: FloatScalar>(xs: &[T], x: T) -> usize {
debug_assert!(xs.len() >= 2);
let n = xs.len();
if x <= xs[0] {
return 0;
}
if x >= xs[n - 1] {
return n - 2;
}
let mut lo = 0;
let mut hi = n - 1;
while hi - lo > 1 {
let mid = lo + (hi - lo) / 2;
if x < xs[mid] {
hi = mid;
} else {
lo = mid;
}
}
lo
}