use crate::bit_writer::BitWriter;
use crate::error::Result;
pub fn move_to_front_transform(input: &[u8]) -> Vec<u8> {
if input.is_empty() {
return Vec::new();
}
let max_value = *input.iter().max().unwrap_or(&0);
let mut mtf: Vec<u8> = (0..=max_value).collect();
let mut result = Vec::with_capacity(input.len());
for &value in input {
let index = mtf.iter().position(|&x| x == value).unwrap();
result.push(index as u8);
if index > 0 {
let val = mtf.remove(index);
mtf.insert(0, val);
}
}
result
}
pub fn inverse_move_to_front_transform(input: &[u8], max_symbol: u8) -> Vec<u8> {
if input.is_empty() {
return Vec::new();
}
let mut mtf: Vec<u8> = (0..=max_symbol).collect();
let mut result = Vec::with_capacity(input.len());
for &index in input {
let idx = index as usize;
if idx >= mtf.len() {
result.push(0);
continue;
}
let value = mtf[idx];
result.push(value);
if idx > 0 {
mtf.remove(idx);
mtf.insert(0, value);
}
}
result
}
pub fn encode_context_map(
context_map: &[u8],
num_histograms: usize,
writer: &mut BitWriter,
) -> Result<()> {
if num_histograms == 1 {
writer.write(1, 1)?; writer.write(2, 0)?; return Ok(());
}
let entry_bits = ceil_log2_nonzero(num_histograms);
if entry_bits > 3 {
crate::trace::debug_eprintln!(
"WARNING: context_map requires {} bits but simple mode max is 3 bits. \
Using 3 bits, which may cause decoding errors if num_histograms > 8.",
entry_bits
);
}
let effective_bits = entry_bits.min(3);
writer.write(1, 1)?; writer.write(2, effective_bits as u64)?; for &entry in context_map {
let masked_entry = entry & ((1 << effective_bits) - 1);
writer.write(effective_bits, masked_entry as u64)?;
}
Ok(())
}
#[inline]
fn ceil_log2_nonzero(n: usize) -> usize {
if n <= 1 {
0
} else {
(usize::BITS - (n - 1).leading_zeros()) as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mtf_simple() {
let input = vec![1, 2, 1, 2, 1, 2];
let transformed = move_to_front_transform(&input);
assert_eq!(transformed, vec![1, 2, 1, 1, 1, 1]);
}
#[test]
fn test_mtf_repeated() {
let input = vec![5, 5, 5, 5];
let transformed = move_to_front_transform(&input);
assert_eq!(transformed, vec![5, 0, 0, 0]);
}
#[test]
fn test_mtf_empty() {
let input: Vec<u8> = vec![];
let transformed = move_to_front_transform(&input);
assert!(transformed.is_empty());
}
#[test]
fn test_mtf_roundtrip() {
let original = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
let max_symbol = *original.iter().max().unwrap();
let transformed = move_to_front_transform(&original);
let recovered = inverse_move_to_front_transform(&transformed, max_symbol);
assert_eq!(original, recovered);
}
#[test]
fn test_mtf_roundtrip_sequential() {
let original: Vec<u8> = (0..10).collect();
let max_symbol = *original.iter().max().unwrap();
let transformed = move_to_front_transform(&original);
let recovered = inverse_move_to_front_transform(&transformed, max_symbol);
assert_eq!(original, recovered);
}
#[test]
fn test_encode_context_map_single() {
let context_map: Vec<u8> = vec![0, 0, 0, 0];
let mut writer = BitWriter::new();
encode_context_map(&context_map, 1, &mut writer).unwrap();
let bytes = writer.finish_with_padding();
assert!(!bytes.is_empty());
}
#[test]
fn test_encode_context_map_two_histograms() {
let context_map: Vec<u8> = vec![0, 1, 0, 1];
let mut writer = BitWriter::new();
encode_context_map(&context_map, 2, &mut writer).unwrap();
let bytes = writer.finish_with_padding();
assert!(!bytes.is_empty());
}
#[test]
fn test_ceil_log2() {
assert_eq!(ceil_log2_nonzero(1), 0);
assert_eq!(ceil_log2_nonzero(2), 1);
assert_eq!(ceil_log2_nonzero(3), 2);
assert_eq!(ceil_log2_nonzero(4), 2);
assert_eq!(ceil_log2_nonzero(5), 3);
assert_eq!(ceil_log2_nonzero(8), 3);
assert_eq!(ceil_log2_nonzero(9), 4);
assert_eq!(ceil_log2_nonzero(256), 8);
}
}