#![forbid(unsafe_code)]
use crate::core::representation::RansCodec;
pub const MIN_SCALE_BITS: u8 = 8;
pub const MAX_SCALE_BITS: u8 = 15;
pub const DEFAULT_SCALE_BITS: u8 = 14;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RansModel {
pub scale_bits: u8,
pub codec: RansCodec,
pub freqs: [u16; 256],
}
impl RansModel {
pub fn total(&self) -> u32 {
1u32 << self.scale_bits
}
pub fn entropy_bits_per_symbol(&self) -> f64 {
let total = self.total() as f64;
let mut h = 0.0f64;
for &f in self.freqs.iter() {
if f > 0 {
let p = f as f64 / total;
h -= p * p.log2();
}
}
h
}
pub fn expected_encoded_len(&self, n: u64) -> Option<u64> {
let h = self.entropy_bits_per_symbol();
if h <= 0.001 {
return None;
}
Some((n as f64 * h / 8.0).ceil() as u64)
}
pub fn distinct_symbols(&self) -> usize {
self.freqs.iter().filter(|&&f| f > 0).count()
}
pub fn build_enc_symbols(
&self,
) -> Result<Vec<Option<ryg_rans_rs::byte::RansByteEncSymbol>>, ryg_rans_rs::byte::ModelError>
{
let mut start: u32 = 0;
let mut out = vec![None; 256];
for (i, &f) in self.freqs.iter().enumerate() {
if f == 0 {
continue;
}
let sym =
ryg_rans_rs::byte::RansByteEncSymbol::new(start, f as u32, self.scale_bits as u32)?;
start += f as u32;
out[i] = Some(sym);
}
Ok(out)
}
pub fn build_dec_symbols(
&self,
) -> Result<Vec<Option<ryg_rans_rs::byte::RansByteDecSymbol>>, ryg_rans_rs::byte::ModelError>
{
let mut start: u32 = 0;
let mut out = vec![None; 256];
for (i, &f) in self.freqs.iter().enumerate() {
if f == 0 {
continue;
}
let sym = ryg_rans_rs::byte::RansByteDecSymbol::new(start, f as u32)?;
start += f as u32;
out[i] = Some(sym);
}
Ok(out)
}
pub fn validate(&self) -> Result<(), ModelValidationError> {
if !(MIN_SCALE_BITS..=MAX_SCALE_BITS).contains(&self.scale_bits) {
return Err(ModelValidationError::BadScaleBits);
}
let total = self.total() as u64;
let mut sum: u64 = 0;
let mut any = false;
for &f in self.freqs.iter() {
if f == 0 {
continue;
}
any = true;
if f > 32768 {
return Err(ModelValidationError::FrequencyTooLarge);
}
sum += f as u64;
}
if !any {
return Err(ModelValidationError::EmptyModel);
}
if sum != total {
return Err(ModelValidationError::TotalMismatch { sum, total });
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModelValidationError {
BadScaleBits,
FrequencyTooLarge,
EmptyModel,
TotalMismatch {
sum: u64,
total: u64,
},
}
impl std::fmt::Display for ModelValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ModelValidationError {}
pub fn normalize_histogram(
hist: &[u32; 256],
scale_bits: u8,
codec: RansCodec,
) -> Option<RansModel> {
if !(MIN_SCALE_BITS..=MAX_SCALE_BITS).contains(&scale_bits) {
return None;
}
let total: u64 = hist.iter().map(|&h| h as u64).sum();
if total == 0 {
return None;
}
let distinct = hist.iter().filter(|&&h| h > 0).count();
if distinct <= 1 {
return None;
}
let scale: u64 = 1u64 << scale_bits;
let mut f = [0u64; 256];
let mut error = [0u64; 256];
for i in 0..256 {
f[i] = (hist[i] as u64 * scale) / total;
error[i] = (hist[i] as u64 * scale) % total;
}
let mut rem = scale - f.iter().sum::<u64>();
for i in 0..256 {
if hist[i] > 0 && f[i] == 0 {
if rem > 0 {
f[i] += 1;
rem -= 1;
} else {
let target = (0..256)
.filter(|&j| f[j] > 1)
.max_by_key(|&j| (f[j], std::cmp::Reverse(j)))?;
f[target] -= 1;
f[i] = 1;
}
}
}
if rem > 0 {
let mut order: Vec<usize> = (0..256).filter(|&i| hist[i] > 0).collect();
order.sort_by(|&a, &b| error[b].cmp(&error[a]).then(a.cmp(&b)));
for &i in order.iter().take(rem as usize) {
f[i] += 1;
}
}
let mut freqs = [0u16; 256];
for i in 0..256 {
debug_assert!(f[i] <= 32768, "frequency exceeds u16-safe range");
freqs[i] = f[i] as u16;
}
let model = RansModel {
scale_bits,
codec,
freqs,
};
debug_assert_eq!(model.validate().map(|_| ()), Ok(()));
Some(model)
}
#[cfg(test)]
mod tests {
use super::*;
fn hist_of(data: &[u8]) -> [u32; 256] {
let mut h = [0u32; 256];
for &b in data {
h[b as usize] += 1;
}
h
}
#[test]
fn normalization_sums_to_scale() {
for scale_bits in MIN_SCALE_BITS..=MAX_SCALE_BITS {
let data: Vec<u8> = (0..20000u32).map(|i| (i % 37) as u8).collect();
let m =
normalize_histogram(&hist_of(&data), scale_bits, RansCodec::Interleaved2).unwrap();
m.validate().unwrap();
assert_eq!(m.total(), 1u32 << scale_bits);
}
}
#[test]
fn normalization_deterministic() {
let data: Vec<u8> = (0..50000u32).map(|i| ((i * 7) % 256) as u8).collect();
let h = hist_of(&data);
let a = normalize_histogram(&h, 14, RansCodec::Single).unwrap();
let b = normalize_histogram(&h, 14, RansCodec::Single).unwrap();
assert_eq!(a, b);
}
#[test]
fn every_present_symbol_has_frequency() {
let mut data = vec![0u8; 65536];
let mut present = [false; 256];
for i in 0..65536 {
data[i] = if i % 1000 == 0 {
present[i / 1000] = true;
(i / 1000) as u8
} else {
present[7] = true;
7
};
}
let m = normalize_histogram(&hist_of(&data), 8, RansCodec::Single).unwrap();
for (i, &p) in present.iter().enumerate() {
if p {
assert!(m.freqs[i] > 0, "symbol {i} present but zero frequency");
}
}
m.validate().unwrap();
}
#[test]
fn degenerate_models_rejected() {
assert!(normalize_histogram(&hist_of(&[0u8; 100]), 14, RansCodec::Single).is_none());
assert!(normalize_histogram(&[0u32; 256], 14, RansCodec::Single).is_none());
assert!(normalize_histogram(&hist_of(&[1u8; 10]), 20, RansCodec::Single).is_none());
}
#[test]
fn entropy_of_uniform_is_8() {
let data: Vec<u8> = (0..65536u32).map(|i| (i % 256) as u8).collect();
let m = normalize_histogram(&hist_of(&data), 14, RansCodec::Single).unwrap();
let h = m.entropy_bits_per_symbol();
assert!((h - 8.0).abs() < 0.01, "h = {h}");
}
#[test]
fn expected_len_low_for_skewed() {
let mut data = vec![0u8; 65536];
for (i, slot) in data.iter_mut().enumerate() {
*slot = if i % 10 == 0 { 1 } else { 0 };
}
let m = normalize_histogram(&hist_of(&data), 14, RansCodec::Single).unwrap();
let expected = m.expected_encoded_len(65536).unwrap();
assert!(expected < 65536, "expected {expected}");
assert!(expected < 10_000);
}
#[test]
fn symbols_rebuild() {
let data: Vec<u8> = (0..20000u32).map(|i| (i % 53) as u8).collect();
let m = normalize_histogram(&hist_of(&data), 14, RansCodec::Single).unwrap();
let esyms = m.build_enc_symbols().unwrap();
let dsyms = m.build_dec_symbols().unwrap();
assert_eq!(esyms.len(), 256);
assert_eq!(dsyms.len(), 256);
}
}