rustyqlib/core/interpolation/
linear.rs1pub fn lerp(a: f64, b: f64, w: f64) -> f64 {
6 a + w * (b - a)
7}
8
9pub fn bracket(xs: &[f64], x: f64) -> (usize, f64) {
14 assert!(xs.len() >= 2, "need at least two pillars");
15 let n = xs.len();
16 if x <= xs[0] {
17 return (1, 0.0);
18 }
19 if x >= xs[n - 1] {
20 return (n - 1, 1.0);
21 }
22 let idx = xs.partition_point(|&xi| xi < x);
23 let (x0, x1) = (xs[idx - 1], xs[idx]);
24 (idx, (x - x0) / (x1 - x0))
25}
26
27pub fn linear_interp(xs: &[f64], ys: &[f64], x: f64) -> f64 {
30 assert_eq!(xs.len(), ys.len());
31 let (idx, w) = bracket(xs, x);
32 lerp(ys[idx - 1], ys[idx], w)
33}
34
35pub fn interp_pairs(points: &[(f64, f64)], x: f64) -> f64 {
38 assert!(points.len() >= 2, "need at least two points");
39 let n = points.len();
40 if x <= points[0].0 {
41 return points[0].1;
42 }
43 if x >= points[n - 1].0 {
44 return points[n - 1].1;
45 }
46 let idx = points.partition_point(|&(xi, _)| xi < x);
47 let (x0, y0) = points[idx - 1];
48 let (x1, y1) = points[idx];
49 lerp(y0, y1, (x - x0) / (x1 - x0))
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn interpolates_and_extrapolates_flat() {
58 let xs = [1.0, 2.0, 4.0];
59 let ys = [10.0, 20.0, 40.0];
60 assert_eq!(linear_interp(&xs, &ys, 1.5), 15.0);
61 assert_eq!(linear_interp(&xs, &ys, 3.0), 30.0);
62 assert_eq!(linear_interp(&xs, &ys, 0.0), 10.0); assert_eq!(linear_interp(&xs, &ys, 9.0), 40.0); assert_eq!(interp_pairs(&[(1.0, 10.0), (2.0, 20.0)], 1.25), 12.5);
65 }
66
67 #[test]
68 fn bracket_clamps_to_the_grid() {
69 let xs = [0.0, 1.0, 3.0];
70 assert_eq!(bracket(&xs, -5.0), (1, 0.0));
71 assert_eq!(bracket(&xs, 5.0), (2, 1.0));
72 let (idx, w) = bracket(&xs, 2.0);
73 assert_eq!(idx, 2);
74 assert!((w - 0.5).abs() < 1e-15);
75 }
76}