#[inline]
pub fn interpolate(a: f64, b: f64, fraction: f64) -> f64 {
a * (1.0 - fraction) + b * fraction
}
#[derive(Default, Clone, PartialEq, PartialOrd, Debug, Serialize, Deserialize)]
pub struct InterpolationTable<F: Fn(f64) -> f64> {
inner: Vec<f64>,
func: F,
offset: usize,
min_x: f64,
max_x: f64,
shift: f64,
}
impl<F: Fn(f64) -> f64> InterpolationTable<F> {
pub fn new(min_x: f64, max_x: f64, frac_digits: i32, func: F) -> Self {
let shift = 10.0_f64.powi(frac_digits);
let offset = (min_x * shift) as usize;
let mut table = InterpolationTable {
inner: Vec::new(),
func,
min_x,
max_x,
shift,
offset,
};
for i in table.index(min_x)..table.index(max_x) {
let x = i as f64 / shift;
table.inner.push((table.func)(x));
}
table
}
#[inline]
fn index(&self, x: f64) -> usize {
(x * self.shift) as usize - self.offset
}
pub fn get(&self, x: f64) -> f64 {
if x < self.min_x || x >= self.max_x {
(self.func)(x)
} else {
let i = self.index(x);
let fraction = (x * self.shift - i as f64) / self.shift;
interpolate(self.inner[i], self.inner[i + 1], fraction)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interpolation_table() {
let table = InterpolationTable::new(0.0, 10.0, 5, |x| x.ln_1p());
for &x in &[0.02, 0.04, 0.45678686, 0.23875, 1.45345e-6] {
assert_relative_eq!(table.get(x), x.ln_1p(), epsilon = 0.00001);
}
}
}