use std::sync::OnceLock;
pub(crate) const MAX_LEN: usize = 24;
pub(crate) const K: usize = 4;
pub(crate) struct ChebTable {
data: Vec<f32>,
offsets: [usize; MAX_LEN + 1],
}
impl ChebTable {
fn build() -> Self {
let total = K * MAX_LEN * (MAX_LEN + 1) / 2;
let mut data = Vec::with_capacity(total);
let mut offsets = [0usize; MAX_LEN + 1];
for (n, off) in offsets.iter_mut().enumerate().take(MAX_LEN + 1).skip(1) {
*off = data.len();
let n_f = n as f32;
for i in 0..n {
let x = (2.0 * (i as f32) + 1.0) / n_f - 1.0;
let mut t_prev = 1.0f32;
let mut t_curr = x;
data.push(t_prev);
if K > 1 {
data.push(t_curr);
}
for _ in 2..K {
let t_next = 2.0 * x * t_curr - t_prev;
data.push(t_next);
t_prev = t_curr;
t_curr = t_next;
}
}
}
Self { data, offsets }
}
#[inline]
pub(crate) fn row(&self, n: usize, i: usize) -> &[f32] {
debug_assert!(n > 0 && n <= MAX_LEN);
debug_assert!(i < n);
let base = self.offsets[n] + i * K;
&self.data[base..base + K]
}
}
static CHEB_TABLE: OnceLock<ChebTable> = OnceLock::new();
#[inline]
pub(crate) fn table() -> &'static ChebTable {
CHEB_TABLE.get_or_init(ChebTable::build)
}
#[rustfmt::skip]
pub(crate) const LENGTH_BUCKETS: [u8; 14] = [
0, 1, 2, 3, 4, 4, 5, 5, 5, 6, 6, 6, 6, 6,
];
#[inline]
pub(crate) fn length_bucket(n: usize) -> u8 {
if n < LENGTH_BUCKETS.len() {
LENGTH_BUCKETS[n]
} else {
7
}
}