#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BakedLut<const N: usize> {
pub knots: [u16; N],
pub max_err_lsb: u16,
}
pub const LUT_LENGTHS: [usize; 4] = [17, 33, 65, 257];
#[must_use]
pub fn bake_lut<const N: usize>(f: impl Fn(f64) -> f64) -> BakedLut<N> {
assert!(LUT_LENGTHS.contains(&N), "N must be 17, 33, 65, or 257");
let mut knots = [0u16; N];
let den = (N - 1) as f64;
for (i, slot) in knots.iter_mut().enumerate() {
let x = i as f64 / den;
*slot = u16_from_unit(f(x));
}
let max_err_lsb = measure_err::<N>(&knots, &f);
BakedLut { knots, max_err_lsb }
}
pub(crate) fn u16_from_unit(v: f64) -> u16 {
let v = v.clamp(0.0, 1.0);
let scaled = v * f64::from(u16::MAX);
let rounded = if scaled >= 0.0 {
(scaled + 0.5).floor()
} else {
(scaled - 0.5).ceil()
};
rounded as u16
}
pub(crate) fn lerp_u16(a: u16, b: u16, t: u16) -> u16 {
if t == 0 {
return a;
}
if t == u16::MAX {
return b;
}
let den = i64::from(u16::MAX);
let num = i64::from(a) * (den - i64::from(t)) + i64::from(b) * i64::from(t);
let quot = num / den;
let rem = num % den;
let out = if rem.saturating_mul(2) >= den {
quot + 1
} else {
quot
};
out.clamp(0, i64::from(u16::MAX)) as u16
}
pub(crate) fn lookup<const N: usize>(knots: &[u16; N], x: u16) -> u16 {
let segs = (N - 1) as u64;
let den = u64::from(u16::MAX);
let pos = u64::from(x) * segs;
let idx = (pos / den) as usize;
if idx >= N - 1 {
return knots[N - 1];
}
let rem = (pos % den) as u16;
lerp_u16(knots[idx], knots[idx + 1], rem)
}
fn measure_err<const N: usize>(knots: &[u16; N], f: &impl Fn(f64) -> f64) -> u16 {
let mut max_err = 0u16;
for x in 0..=u16::MAX {
let baked = lookup(knots, x);
let reference = u16_from_unit(f(f64::from(x) / f64::from(u16::MAX)));
let err = baked.abs_diff(reference);
if err > max_err {
max_err = err;
}
}
max_err
}
#[must_use]
pub fn identity(x: f64) -> f64 {
x
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identity_lut_has_finite_error() {
let baked = bake_lut::<17>(identity);
assert_eq!(baked.knots[0], 0);
assert_eq!(baked.knots[16], 65535);
let _ = baked.max_err_lsb;
}
}