use crate::math::UnitValue;
pub trait Curve<I, V> {
fn eval(&self, u: I) -> V;
}
pub trait MonotonicCurve<I, V>: Curve<I, V> {
fn inv(&self, w: V) -> I;
}
#[derive(Copy, Clone, Debug)]
pub struct CurveLut<I: UnitValue, V: UnitValue, const N: usize, const M: usize = N> {
pub(crate) fwd: &'static [V; N],
pub(crate) inv: Option<&'static [I; M]>,
}
pub type CurveLut256 = CurveLut<u8, u8, 256>;
#[cfg(not(target_pointer_width = "16"))]
pub type CurveLut65536 = CurveLut<u16, u16, 65536>;
impl<I: UnitValue, V: UnitValue, const N: usize, const M: usize> CurveLut<I, V, N, M> {
pub const fn new(fwd: &'static [V; N], inv: Option<&'static [I; M]>) -> Self {
Self { fwd, inv }
}
#[inline(always)]
pub const fn fwd_lut(&self) -> &'static [V; N] {
self.fwd
}
#[inline(always)]
pub const fn inv_lut(&self) -> Option<&'static [I; M]> {
self.inv
}
#[inline(always)]
pub const fn monotonic(self) -> Option<MonotonicCurveLut<I, V, N, M>> {
match self.inv {
Some(inv) => Some(MonotonicCurveLut { fwd: self.fwd, inv }),
None => None,
}
}
}
impl<I: UnitValue, V: UnitValue, const N: usize, const M: usize> Curve<I, V>
for CurveLut<I, V, N, M>
{
#[inline(always)]
fn eval(&self, u: I) -> V {
let index = u.to_index();
assert!(index < N, "CurveLut forward LUT does not cover input index");
self.fwd[index]
}
}
#[derive(Copy, Clone, Debug)]
pub struct MonotonicCurveLut<I: UnitValue, V: UnitValue, const N: usize, const M: usize = N> {
fwd: &'static [V; N],
inv: &'static [I; M],
}
pub type MonotonicCurveLut256 = MonotonicCurveLut<u8, u8, 256>;
#[cfg(not(target_pointer_width = "16"))]
pub type MonotonicCurveLut65536 = MonotonicCurveLut<u16, u16, 65536>;
impl<I: UnitValue, V: UnitValue, const N: usize, const M: usize> MonotonicCurveLut<I, V, N, M> {
pub const fn new(fwd: &'static [V; N], inv: &'static [I; M]) -> Self {
Self { fwd, inv }
}
#[inline(always)]
pub const fn fwd_lut(&self) -> &'static [V; N] {
self.fwd
}
#[inline(always)]
pub const fn inv_lut(&self) -> &'static [I; M] {
self.inv
}
}
impl<I: UnitValue, V: UnitValue, const N: usize, const M: usize> Curve<I, V>
for MonotonicCurveLut<I, V, N, M>
{
#[inline(always)]
fn eval(&self, u: I) -> V {
let index = u.to_index();
assert!(
index < N,
"MonotonicCurveLut forward LUT does not cover input index"
);
self.fwd[index]
}
}
impl<I: UnitValue, V: UnitValue, const N: usize, const M: usize> MonotonicCurve<I, V>
for MonotonicCurveLut<I, V, N, M>
{
#[inline(always)]
fn inv(&self, w: V) -> I {
let index = w.to_index();
assert!(
index < M,
"MonotonicCurveLut inverse LUT does not cover value index"
);
self.inv[index]
}
}
#[cfg(test)]
mod tests {
use super::*;
static SHORT: [u8; 255] = [0; 255];
static FULL: [u8; 256] = [0; 256];
const SHORT_CURVE: CurveLut<u8, u8, 255> = CurveLut::new(&SHORT, None);
const SHORT_MONOTONIC_FORWARD: MonotonicCurveLut<u8, u8, 255, 256> =
MonotonicCurveLut::new(&SHORT, &FULL);
const SHORT_MONOTONIC_INVERSE: MonotonicCurveLut<u8, u8, 256, 255> =
MonotonicCurveLut::new(&FULL, &SHORT);
#[test]
#[should_panic(expected = "CurveLut forward LUT does not cover input index")]
fn undersized_curve_lut_has_an_explicit_eval_panic() {
let _ = SHORT_CURVE.eval(u8::MAX);
}
#[test]
#[should_panic(expected = "MonotonicCurveLut forward LUT does not cover input index")]
fn undersized_monotonic_forward_lut_has_an_explicit_eval_panic() {
let _ = SHORT_MONOTONIC_FORWARD.eval(u8::MAX);
}
#[test]
#[should_panic(expected = "MonotonicCurveLut inverse LUT does not cover value index")]
fn undersized_monotonic_inverse_lut_has_an_explicit_inv_panic() {
let _ = SHORT_MONOTONIC_INVERSE.inv(u8::MAX);
}
}