#![warn(missing_docs)]
#![allow(unknown_lints)]
#![warn(clippy::all, clippy::pedantic, clippy::cargo)]
#![allow(clippy::many_single_char_names)]
use num::Num;
use itertools::{izip, Itertools};
#[inline]
fn deltas<T>(p: &[T]) -> Vec<T>
where
T: Num + Copy,
{
p.iter().tuple_windows().map(|(&p1, &p2)| p2 - p1).collect()
}
#[inline]
fn slopes<T>(dx: &[T], dy: &[T]) -> Vec<T>
where
T: Num + Copy,
{
izip!(dx, dy).map(|(&dx, &dy)| dy / dx).collect()
}
#[inline]
fn intercepts<T>(x: &[T], y: &[T], m: &[T]) -> Vec<T>
where
T: Num + Copy,
{
izip!(x, y, m).map(|(&x, &y, &m)| y - x * m).collect()
}
#[inline]
fn prev_index<T>(x: &[T], xp: T) -> usize
where
T: Num + PartialOrd + Copy,
{
x.iter()
.take_while(|&&x| x < xp)
.enumerate()
.last()
.map_or(0, |(i, _)| i)
}
pub fn interp<T>(x: &[T], y: &[T], xp: T) -> T
where
T: Num + PartialOrd + Copy,
{
let min_len = std::cmp::min(x.len(), y.len());
if min_len == 0 {
T::zero()
} else if min_len == 1 {
y[0]
} else {
let dx = deltas(&x[..min_len]);
let dy = deltas(&y[..min_len]);
let m = slopes(&dx, &dy);
let c = intercepts(x, y, &m);
let i = prev_index(x, xp).min(min_len - 2);
m[i] * xp + c[i]
}
}
pub fn interp_slice<T>(x: &[T], y: &[T], xp: &[T]) -> Vec<T>
where
T: Num + PartialOrd + Copy,
{
let min_len = std::cmp::min(x.len(), y.len());
if min_len == 0 {
vec![T::zero(); xp.len()]
} else if min_len == 1 {
vec![y[0]; xp.len()]
} else {
let dx = deltas(&x[..min_len]);
let dy = deltas(&y[..min_len]);
let m = slopes(&dx, &dy);
let c = intercepts(x, y, &m);
xp.iter()
.map(|&xp| {
let i = prev_index(x, xp).min(min_len - 2);
m[i] * xp + c[i]
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deltas() {
let p = vec![0.0, 1.0, 3.5, 2.5, 6.0];
let delta = vec![1.0, 2.5, -1.0, 3.5];
let result = deltas(&p);
assert_eq!(result.len(), delta.len());
for (r, d) in izip!(result, delta) {
assert_eq!(r, d);
}
}
#[test]
fn test_slopes() {
let dx = vec![1.0, 2.5, 2.0, 1.0];
let dy = vec![1.0, 5.0, -1.0, 3.5];
let slope = vec![1.0, 2.0, -0.5, 3.5];
let result = slopes(&dx, &dy);
assert_eq!(result.len(), slope.len());
for (r, m) in izip!(result, slope) {
assert_eq!(r, m);
}
}
#[test]
fn test_intercepts() {
let x = vec![0.0, 1.0, 3.5, 2.5, 6.0];
let y = vec![0.0, 1.0, 6.0, 5.0, 8.5];
let slope = vec![1.0, 2.0, -0.5, 3.5, 1.0];
let intercept = vec![0.0, -1.0, 7.75, -3.75, 2.5];
let result = intercepts(&x, &y, &slope);
assert_eq!(result.len(), intercept.len());
for (r, c) in izip!(result, intercept) {
assert_eq!(r, c);
}
}
#[test]
fn test_prev_index() {
let x = vec![0.0, 1.0, 3.5, 2.5, 6.0];
let result = prev_index(&x, 3.0);
assert_eq!(result, 1);
let result = prev_index(&x, 4.0);
assert_eq!(result, 3);
}
#[test]
fn test_interp() {
assert_eq!(interp(&[], &[], 2.0), 0.0);
assert_eq!(interp(&[1.0], &[2.0], 2.0), 2.0);
let x = vec![0.0, 1.0, 2.0, 3.0, 4.5];
let y = vec![0.0, 2.0, 5.0, 3.0, 2.0];
assert_eq!(interp(&x, &y, 2.5), 4.0);
assert_eq!(interp(&x, &y, -1.0), -2.0);
assert_eq!(interp(&x, &y, 7.5), 0.0);
}
#[test]
fn test_interp_slice() {
assert_eq!(interp_slice(&[], &[], &[2.0]), vec![0.0]);
assert_eq!(interp_slice(&[1.0], &[2.0], &[2.0]), vec![2.0]);
let x = vec![0.0, 1.0, 2.0, 3.0, 4.5];
let y = vec![0.0, 2.0, 5.0, 3.0, 2.0];
assert_eq!(interp_slice(&x, &y, &[]), vec![]);
let xp = vec![2.5, -1.0, 7.5];
let result = vec![4.0, -2.0, 0.0];
assert_eq!(interp_slice(&x, &y, &xp), result);
}
}