use std::{collections::HashMap, convert::TryInto};
use tiny_bitstream::{BitDstream, BitEstream, BitReader, BitWriter};
pub fn build_cumulative_symbol_frequency(normalized_counter: &[usize]) -> Vec<usize> {
let mut cs = Vec::with_capacity(normalized_counter.len() + 1);
let cumul_fn = |acc, frequency| {
cs.push(acc);
acc + frequency
};
let sum = normalized_counter.iter().fold(0, cumul_fn);
cs.push(sum);
cs
}
pub fn compress(state: usize, table_log: usize, frequency: usize, cumul: usize) -> usize {
#[cfg(feature = "checks")]
if frequency == 0 {
panic!("attemp division by zero because of an unexpected null frequency")
}
((state / frequency) << table_log) + (state % frequency) + cumul
}
pub fn simple_normalization(histogram: &mut [usize], cumul: &mut [usize], table_log: usize) {
let mut previous = 0;
let max_cumul = *cumul.last().unwrap();
let target_range = 1 << table_log; let actual_range = max_cumul;
cumul.iter_mut().enumerate().skip(1).for_each(|(i, c)| {
*c = (target_range * (*c)) / actual_range;
if *c <= previous {
panic!("table log too low");
}
histogram[i - 1] = *c - previous;
previous = *c;
});
}
pub fn encode(
hist: &mut [usize],
symbol_index: &HashMap<u16, usize>,
table_log: usize, src: &[u16],
) -> (usize, Vec<u32>, Vec<u8>) {
let mut cs = build_cumulative_symbol_frequency(hist);
simple_normalization(hist, &mut cs, table_log);
let mut state = 0;
let d = 32 - table_log;
let msk = 2usize.pow(16) - 1;
let mut estream = BitEstream::new();
let mut nb_bits_table = vec![];
src.iter().for_each(|symbol| {
let index = *symbol_index.get(symbol).unwrap();
let fs = *hist.get(index).unwrap();
if state >= (fs << d) {
let bits = state & msk;
let nb_bits = u64::BITS - bits.leading_zeros();
estream.unchecked_write(bits, nb_bits.try_into().unwrap());
nb_bits_table.push(nb_bits);
state >>= 16;
};
state = compress(state, table_log, fs, *cs.get(index).unwrap());
});
(state, nb_bits_table, estream.try_into().unwrap())
}
pub fn encode_u8(
hist: &mut [usize],
table_log: usize, src: &[u8],
) -> (usize, Vec<u32>, Vec<u8>) {
let mut cs = build_cumulative_symbol_frequency(hist);
simple_normalization(hist, &mut cs, table_log);
let mut state = 0;
let d = 32 - table_log;
let msk = 2usize.pow(16) - 1;
let mut estream = BitEstream::new();
let mut nb_bits_table = vec![];
src.iter().for_each(|symbol| {
let index = *symbol as usize;
let fs = hist[index];
if state >= (fs << d) {
let bits = state & msk;
let nb_bits = u64::BITS - bits.leading_zeros();
estream.unchecked_write(bits, nb_bits.try_into().unwrap());
nb_bits_table.push(nb_bits);
state >>= 16;
};
state = compress(state, table_log, fs, cs[index]);
});
(state, nb_bits_table, estream.try_into().unwrap())
}
pub fn find_s(state: usize, cs: &[usize]) -> usize {
for (i, &c) in cs.iter().enumerate() {
if c == state {
return i;
}
if c > state {
return i - 1;
}
}
0
}
pub fn decompress(state: usize, frequency: usize, table_log: usize, cumul: usize) -> usize {
let mask = 2usize.pow(table_log as u32) - 1;
(frequency * (state >> table_log)) + (state & mask) - cumul
}
pub fn decode(
mut state: usize,
mut bits: Vec<u32>,
str: Vec<u8>,
normalized_counter: &[usize],
symbols: &[u16],
table_log: usize,
) -> Vec<u16> {
let mask = 2usize.pow(table_log as u32) - 1;
let mut dstream: BitDstream = str.try_into().unwrap();
dstream.read(1).unwrap();
let cs = build_cumulative_symbol_frequency(normalized_counter);
let mut ret = vec![];
while state > 0 {
let symbol_index = find_s(state & mask, &cs);
ret.push(*symbols.get(symbol_index).expect("symbol not found"));
state = decompress(
state,
*normalized_counter
.get(symbol_index)
.expect("symbol frequency not found"),
table_log,
*cs.get(symbol_index).expect("symbol cumul not found"),
);
if state < 2usize.pow(16) {
if let Some(nb_bits) = bits.pop() {
state = (state << 16) + dstream.read(nb_bits as u8).unwrap() as usize;
}
}
}
ret.reverse();
ret
}
pub fn decode_u8(
mut state: usize,
mut bits: Vec<u32>,
str: Vec<u8>,
normalized_counter: &[usize],
table_log: usize,
) -> Vec<u8> {
let mask = 2usize.pow(table_log as u32) - 1;
let mut dstream: BitDstream = str.try_into().unwrap();
dstream.read(1).unwrap();
let cs = build_cumulative_symbol_frequency(normalized_counter);
let mut ret = vec![];
while state > 0 {
let symbol_index = find_s(state & mask, &cs);
ret.push(symbol_index.try_into().expect("symbol overflow"));
state = decompress(
state,
*normalized_counter
.get(symbol_index)
.expect("symbol frequency not found"),
table_log,
*cs.get(symbol_index).expect("symbol cumul not found"),
);
if state < 2usize.pow(16) {
if let Some(nb_bits) = bits.pop() {
state = (state << 16) + dstream.read(nb_bits as u8).unwrap() as usize;
}
}
}
ret.reverse();
ret
}