extern crate alloc;
use alloc::vec::Vec;
pub(crate) fn mtf_forward_reduced(input: &[u8], alphabet: &[u8]) -> Vec<u8> {
debug_assert!(alphabet.len() <= 256);
let mut list: [u8; 256] = [0u8; 256];
let n = alphabet.len();
list[..n].copy_from_slice(alphabet);
let mut out = Vec::with_capacity(input.len());
for &b in input {
let mut pos = 0;
while pos < n && list[pos] != b {
pos += 1;
}
debug_assert!(pos < n, "byte {} not in MTF alphabet", b);
out.push(pos as u8);
if pos > 0 {
list.copy_within(0..pos, 1);
list[0] = b;
}
}
out
}
pub(crate) fn mtf_inverse_reduced(indices: &[u8], alphabet: &[u8]) -> Vec<u8> {
debug_assert!(alphabet.len() <= 256);
let mut list: [u8; 256] = [0u8; 256];
let n = alphabet.len();
list[..n].copy_from_slice(alphabet);
let mut out = Vec::with_capacity(indices.len());
for &idx in indices {
let pos = idx as usize;
debug_assert!(pos < n);
let b = list[pos];
out.push(b);
if pos > 0 {
list.copy_within(0..pos, 1);
list[0] = b;
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn forward_then_inverse_roundtrip() {
let input = b"banana banana banana";
let mut alpha: Vec<u8> = input.to_vec();
alpha.sort_unstable();
alpha.dedup();
let f = mtf_forward_reduced(input, &alpha);
let inv = mtf_inverse_reduced(&f, &alpha);
assert_eq!(inv, input);
}
#[test]
fn long_run_becomes_zeros() {
let input = vec![b'a'; 100];
let alpha = vec![b'a'];
let f = mtf_forward_reduced(&input, &alpha);
assert!(f.iter().all(|&x| x == 0));
}
}