use crate::Error;
const EC_PROB_SHIFT: u32 = 6;
const EC_MIN_PROB: u32 = 4;
fn floor_log2(x: u32) -> u32 {
if x == 0 {
0
} else {
31 - x.leading_zeros()
}
}
fn log2_frac256(x: u32) -> u32 {
debug_assert!((1 << 15..1 << 16).contains(&x));
let mut v: u64 = u64::from(x) << 17;
let mut frac: u32 = 0;
for _ in 0..8 {
v = ((u128::from(v) * u128::from(v)) >> 32) as u64;
frac <<= 1;
if v >= 2u64 << 32 {
frac |= 1;
v >>= 1;
}
}
frac
}
pub(crate) fn recenter(r: i64, v: i64) -> i64 {
if v > 2 * r {
v
} else if v >= r {
2 * (v - r)
} else {
2 * (r - v) - 1
}
}
#[derive(Debug, Clone)]
pub struct SymbolWriter {
low_bits: Vec<bool>,
range: u32,
disable_cdf_update: bool,
count_only: bool,
renorm_bits: u64,
initial_log2_frac256: u32,
}
impl SymbolWriter {
pub fn new(disable_cdf_update: bool) -> Self {
Self {
low_bits: vec![false; 15],
range: 1 << 15,
disable_cdf_update,
count_only: false,
renorm_bits: 0,
initial_log2_frac256: 0,
}
}
pub fn new_counting(disable_cdf_update: bool, range: u32) -> Self {
debug_assert!(
(1 << 15..1 << 16).contains(&range),
"counting writer seeded outside the §8.2.6 range window"
);
Self {
low_bits: Vec::new(),
range,
disable_cdf_update,
count_only: true,
renorm_bits: 0,
initial_log2_frac256: log2_frac256(range),
}
}
#[must_use]
pub fn range(&self) -> u32 {
self.range
}
#[must_use]
pub fn disable_cdf_update(&self) -> bool {
self.disable_cdf_update
}
#[must_use]
pub fn cost_bits256(&self) -> u64 {
let cur = i64::from(log2_frac256(self.range));
let init = i64::from(self.initial_log2_frac256);
let total = self.renorm_bits as i64 * 256 + init - cur;
debug_assert!(total >= 0, "symbol-sequence cost cannot be negative");
total.max(0) as u64
}
pub fn write_symbol(&mut self, symbol: u32, cdf: &mut [u16]) -> Result<(), Error> {
let n = cdf.len() as u32 - 1;
debug_assert!(n >= 1, "§8.2.6: N must be greater than 1");
debug_assert!(symbol < n, "symbol {symbol} out of range for N={n}");
let cur = self.cur_for_symbol(cdf, symbol, n);
let prev = if symbol == 0 {
self.range
} else {
self.cur_for_symbol(cdf, symbol - 1, n)
};
let offset = self.range - prev;
self.range = prev - cur;
debug_assert!(self.range >= 1, "§8.2.6 produced a zero-width sub-interval");
if !self.count_only {
self.add_to_low(offset);
}
let bits = 15 - floor_log2(self.range);
self.range <<= bits;
self.renorm_bits += u64::from(bits);
if !self.count_only {
for _ in 0..bits {
self.low_bits.push(false);
}
}
Self::check_range_invariant(self.range)?;
if !self.disable_cdf_update {
crate::symbol_decoder::update_cdf_for_encoder(cdf, symbol, n);
}
Ok(())
}
fn check_range_invariant(range: u32) -> Result<(), Error> {
const RANGE_MIN: u32 = 1 << 15; const RANGE_MAX: u32 = (1 << 16) - 1; if !(RANGE_MIN..=RANGE_MAX).contains(&range) {
return Err(Error::SymbolStateInvariantBroken);
}
Ok(())
}
pub fn write_bool(&mut self, bit: u32) -> Result<(), Error> {
let mut cdf: [u16; 3] = [1 << 14, 1 << 15, 0];
self.write_symbol(bit, &mut cdf)
}
pub fn write_literal(&mut self, n: u32, value: u32) -> Result<(), Error> {
debug_assert!(n <= 32, "§8.2.5 read_literal only consumes up to 32 bits");
debug_assert!(
n == 32 || value < (1u32 << n),
"value {value} does not fit in {n} bits"
);
for i in (0..n).rev() {
let bit = (value >> i) & 0x1;
self.write_bool(bit)?;
}
Ok(())
}
pub fn write_ns(&mut self, n: u32, value: u32) -> Result<(), Error> {
debug_assert!(n >= 1, "§4.10.10 NS(n) requires n >= 1");
debug_assert!(value < n, "NS({n}) value {value} out of range");
let w = floor_log2(n) + 1;
let m = (1u32 << w) - n;
if value < m {
self.write_literal(w - 1, value)
} else {
let coded = value + m;
self.write_literal(w, coded)
}
}
pub fn write_subexp_bool(&mut self, num_syms: u32, k: u32, value: u32) -> Result<(), Error> {
debug_assert!(num_syms >= 1, "§5.9.28 subexp requires numSyms >= 1");
debug_assert!(value < num_syms, "subexp value {value} out of range");
let mut i: u32 = 0;
let mut mk: u32 = 0;
loop {
let b2 = if i != 0 { k + i - 1 } else { k };
let a = 1u32 << b2;
if num_syms <= mk + 3 * a {
return self.write_ns(num_syms - mk, value - mk);
}
if value >= mk + a {
self.write_literal(1, 1)?;
i += 1;
mk += a;
} else {
self.write_literal(1, 0)?;
return self.write_literal(b2, value - mk);
}
}
}
pub fn write_unsigned_subexp_with_ref_bool(
&mut self,
mx: i64,
k: u32,
r: i64,
value: i64,
) -> Result<(), Error> {
let v = if (r << 1) <= mx {
recenter(r, value)
} else {
recenter(mx - 1 - r, mx - 1 - value)
};
debug_assert!(v >= 0 && v < mx, "recentred subexp value in 0..mx");
self.write_subexp_bool(mx as u32, k, v as u32)
}
pub fn write_signed_subexp_with_ref_bool(
&mut self,
low: i64,
high: i64,
k: u32,
r: i64,
value: i64,
) -> Result<(), Error> {
self.write_unsigned_subexp_with_ref_bool(high - low, k, r - low, value - low)
}
fn cur_for_symbol(&self, cdf: &[u16], s: u32, n: u32) -> u32 {
let f = (1u32 << 15) - u32::from(cdf[s as usize]);
let mut cur = ((self.range >> 8) * (f >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT);
cur += EC_MIN_PROB * (n - s - 1);
cur
}
fn add_to_low(&mut self, offset: u32) {
let mut carry: u64 = u64::from(offset);
for i in (0..self.low_bits.len()).rev() {
if carry == 0 {
break;
}
let bit_val: u64 = if self.low_bits[i] { 1 } else { 0 };
let sum = bit_val + (carry & 1);
self.low_bits[i] = (sum & 1) != 0;
carry = (carry >> 1) + (sum >> 1);
}
debug_assert!(
carry == 0,
"`low + offset` overflowed the §8.2.6 interval invariant"
);
}
pub fn finish(mut self) -> Vec<u8> {
debug_assert!(
!self.count_only,
"a counting (rate-twin) writer has no bytestream to flush"
);
let n = self.low_bits.len();
debug_assert!(n >= 15, "init seeds 15 bits; finish sees at least those");
if n >= 15 {
let mut r: u32 = 0;
for &b in &self.low_bits[n - 15..] {
r = (r << 1) | u32::from(b);
}
let offset = ((1u32 << 14).wrapping_sub(r)) & 0x7FFF;
debug_assert!(offset < self.range, "termination offset exceeds range");
if offset != 0 {
self.add_to_low(offset);
}
debug_assert!(
self.low_bits[n - 15] && self.low_bits[n - 14..].iter().all(|&b| !b),
"post-adjust low must end in the §8.2.4 trailing pattern"
);
self.low_bits.truncate(n - 14);
}
let mut out = Vec::with_capacity(self.low_bits.len().div_ceil(8));
let mut cur: u8 = 0;
let mut bits_in_cur: u32 = 0;
for b in self.low_bits {
cur |= (b as u8) << (7 - bits_in_cur);
bits_in_cur += 1;
if bits_in_cur == 8 {
out.push(cur);
cur = 0;
bits_in_cur = 0;
}
}
if bits_in_cur != 0 {
out.push(cur);
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol_decoder::SymbolDecoder;
#[test]
fn finish_terminates_with_trailing_one_bit_pattern() {
for pattern_len in [1usize, 3, 17, 64, 200] {
let mut w = SymbolWriter::new(false);
let mut cdf_w: [u16; 4] = [12000, 20000, 1 << 15, 0];
for i in 0..pattern_len {
w.write_symbol((i % 3) as u32, &mut cdf_w).unwrap();
}
let bytes = w.finish();
let mut bit_idx = bytes.len() * 8;
let bit_at = |bytes: &[u8], i: usize| (bytes[i / 8] >> (7 - (i % 8))) & 1;
while bit_idx > 0 && bit_at(&bytes, bit_idx - 1) == 0 {
bit_idx -= 1;
}
assert!(bit_idx > 0, "len {pattern_len}: no trailing one-bit");
assert!(
bytes.len() * 8 - bit_idx < 8,
"len {pattern_len}: emission is not minimal"
);
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let mut cdf_r: [u16; 4] = [12000, 20000, 1 << 15, 0];
for i in 0..pattern_len {
assert_eq!(
d.read_symbol(&mut cdf_r).unwrap(),
(i % 3) as u32,
"len {pattern_len}: symbol {i} diverged"
);
}
}
}
#[test]
fn bool_round_trip_single_zero() {
let mut w = SymbolWriter::new(true);
w.write_bool(0).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(d.read_bool().unwrap(), 0);
}
#[test]
fn bool_round_trip_single_one() {
let mut w = SymbolWriter::new(true);
w.write_bool(1).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(d.read_bool().unwrap(), 1);
}
#[test]
fn bool_round_trip_pattern() {
let pattern = [0u32, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1];
let mut w = SymbolWriter::new(true);
for &b in &pattern {
w.write_bool(b).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
for &expected in &pattern {
assert_eq!(d.read_bool().unwrap(), expected, "bit stream mismatch");
}
}
#[test]
fn multi_symbol_round_trip_adapting_cdf() {
let symbols = [0u32, 2, 1, 3, 0, 1, 2, 3, 0, 0, 1, 2, 3, 1, 2, 0, 3, 1];
let start_cdf: [u16; 5] = [8192, 16384, 24576, 32768, 0];
let mut w = SymbolWriter::new(false);
let mut enc_cdf = start_cdf;
for &s in &symbols {
w.write_symbol(s, &mut enc_cdf).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let mut dec_cdf = start_cdf;
for &expected in &symbols {
let got = d.read_symbol(&mut dec_cdf).unwrap();
assert_eq!(got, expected, "symbol mismatch");
}
assert_eq!(enc_cdf, dec_cdf, "encoder/decoder CDFs diverged");
}
#[test]
fn skewed_cdf_round_trip_disabled_update() {
let cdf_template: [u16; 3] = [28672, 32768, 0];
let symbols = [0u32; 32];
let mut w = SymbolWriter::new(true);
let mut wc = cdf_template;
for &s in &symbols {
w.write_symbol(s, &mut wc).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
let mut dc = cdf_template;
for &expected in &symbols {
assert_eq!(d.read_symbol(&mut dc).unwrap(), expected);
}
}
#[test]
fn rare_symbol_round_trip() {
let cdf_template: [u16; 3] = [4096, 32768, 0];
let symbols = [1u32; 20];
let mut w = SymbolWriter::new(true);
let mut wc = cdf_template;
for &s in &symbols {
w.write_symbol(s, &mut wc).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
let mut dc = cdf_template;
for &expected in &symbols {
assert_eq!(d.read_symbol(&mut dc).unwrap(), expected);
}
}
#[test]
fn long_sequence_round_trip_with_adaptation() {
let start_cdf: [u16; 9] = [3000, 7000, 12000, 16000, 20000, 24000, 28000, 32768, 0];
let mut x: u32 = 0xDEAD_BEEF;
let mut symbols = Vec::with_capacity(200);
for _ in 0..200 {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
symbols.push(x % 8);
}
let mut w = SymbolWriter::new(false);
let mut enc_cdf = start_cdf;
for &s in &symbols {
w.write_symbol(s, &mut enc_cdf).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let mut dec_cdf = start_cdf;
for &expected in &symbols {
assert_eq!(d.read_symbol(&mut dec_cdf).unwrap(), expected);
}
assert_eq!(enc_cdf, dec_cdf);
}
#[test]
fn mixed_bool_and_symbol_round_trip() {
let bools = [1u32, 0, 1, 1];
let multi: [u32; 4] = [2, 0, 3, 1];
let start_cdf: [u16; 5] = [6000, 14000, 22000, 32768, 0];
let mut w = SymbolWriter::new(true);
for &b in &bools {
w.write_bool(b).unwrap();
}
let mut wc = start_cdf;
for &s in &multi {
w.write_symbol(s, &mut wc).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
for &expected in &bools {
assert_eq!(d.read_bool().unwrap(), expected);
}
let mut dc = start_cdf;
for &expected in &multi {
assert_eq!(d.read_symbol(&mut dc).unwrap(), expected);
}
}
#[test]
fn empty_payload_finish_does_not_panic() {
let w = SymbolWriter::new(true);
let bytes = w.finish();
let _ = SymbolDecoder::init_symbol(&bytes, bytes.len(), true);
}
#[test]
fn all_ones_bool_stream_round_trip() {
let n = 64;
let mut w = SymbolWriter::new(true);
for _ in 0..n {
w.write_bool(1).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
for _ in 0..n {
assert_eq!(d.read_bool().unwrap(), 1);
}
}
#[test]
fn all_zeros_bool_stream_round_trip() {
let n = 64;
let mut w = SymbolWriter::new(true);
for _ in 0..n {
w.write_bool(0).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
for _ in 0..n {
assert_eq!(d.read_bool().unwrap(), 0);
}
}
#[test]
fn write_literal_round_trip_widths_1_to_16() {
let cases: &[(u32, u32)] = &[
(1, 0),
(1, 1),
(3, 0),
(3, 5),
(3, 7),
(4, 0xA),
(8, 0xCD),
(12, 0xABC),
(16, 0xDEAD),
(16, 0xBEEF),
(16, 0),
];
for &(n, v) in cases {
let mut w = SymbolWriter::new(true);
w.write_literal(n, v).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(
d.read_literal(n).unwrap(),
v,
"L({n}) round-trip mismatch for {v}"
);
}
}
#[test]
fn write_literal_n_zero_emits_no_bits() {
let mut w = SymbolWriter::new(true);
w.write_literal(0, 0).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(d.read_literal(0).unwrap(), 0);
}
#[test]
fn write_literal_concatenated_round_trip() {
let mut w = SymbolWriter::new(true);
w.write_literal(3, 5).unwrap();
w.write_literal(5, 17).unwrap();
w.write_literal(8, 0xAB).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(d.read_literal(3).unwrap(), 5);
assert_eq!(d.read_literal(5).unwrap(), 17);
assert_eq!(d.read_literal(8).unwrap(), 0xAB);
}
#[test]
fn write_ns_round_trip_against_read_ns() {
for n in [1u32, 2, 3, 4, 5, 7, 8, 9, 13, 16, 17] {
for v in 0..n {
let mut w = SymbolWriter::new(true);
w.write_ns(n, v).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(
d.read_ns(n).unwrap(),
v,
"NS({n}) round-trip mismatch for {v}"
);
}
}
}
#[test]
fn write_ns_n_equals_one_no_bits() {
let mut w = SymbolWriter::new(true);
w.write_ns(1, 0).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(d.read_ns(1).unwrap(), 0);
}
#[test]
fn write_subexp_bool_round_trip_grid() {
let cases: &[(u32, u32, &[u32])] = &[
(2, 0, &[0, 1]),
(3, 1, &[0, 1, 2]),
(32, 1, &[0, 1, 2, 3, 6, 7, 8, 15, 16, 31]),
(64, 2, &[0, 5, 11, 12, 24, 32, 47, 63]),
(50, 0, &[0, 1, 2, 10, 25, 40, 49]),
];
for &(num_syms, k, values) in cases {
for &v in values {
let mut w = SymbolWriter::new(true);
w.write_subexp_bool(num_syms, k, v).unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(
d.decode_subexp_bool(num_syms, k).unwrap(),
v,
"subexp_bool(num_syms={num_syms}, k={k}) round-trip mismatch for {v}"
);
}
}
}
#[test]
fn write_subexp_bool_concatenated_round_trip() {
let triples: &[(u32, u32, u32)] = &[(16, 1, 5), (8, 0, 3), (32, 2, 17), (64, 1, 0)];
let mut w = SymbolWriter::new(true);
for &(num_syms, k, v) in triples {
w.write_subexp_bool(num_syms, k, v).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
for &(num_syms, k, v) in triples {
assert_eq!(d.decode_subexp_bool(num_syms, k).unwrap(), v);
}
}
#[test]
fn recenter_inverts_inverse_recenter() {
fn inverse_recenter(r: i64, v: i64) -> i64 {
if v > 2 * r {
v
} else if v & 1 != 0 {
r - ((v + 1) >> 1)
} else {
r + (v >> 1)
}
}
for r in 0..40i64 {
for v in 0..(2 * r + 8) {
let code = recenter(r, v);
assert!(code >= 0, "recenter({r},{v}) = {code} negative");
assert_eq!(
inverse_recenter(r, code),
v,
"recenter/inverse_recenter mismatch r={r} v={v} code={code}"
);
}
}
}
#[test]
fn signed_subexp_with_ref_bool_round_trip() {
let cases: &[(i64, i64, u32, i64, &[i64])] = &[
(-5, 11, 1, 3, &[-5, -2, 0, 3, 7, 10]),
(-23, 9, 2, -7, &[-23, -10, -7, 0, 8]),
(-17, 47, 3, 15, &[-17, 0, 15, 30, 46]),
(-96, 32, 4, -32, &[-96, -50, -32, 0, 31]),
(-32, 96, 4, 31, &[-32, 0, 31, 60, 95]),
];
for &(low, high, k, r, values) in cases {
for &v in values {
let mut w = SymbolWriter::new(true);
w.write_signed_subexp_with_ref_bool(low, high, k, r, v)
.unwrap();
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
assert_eq!(
d.decode_signed_subexp_with_ref_bool(low, high, k, r)
.unwrap(),
v,
"signed_subexp(low={low},high={high},k={k},r={r}) mismatch for {v}"
);
}
}
}
#[test]
fn encoder_decoder_state_stays_in_top_half_window() {
let mut w = SymbolWriter::new(true);
let mut cdf_for_write: [u16; 5] = [8192, 16384, 24576, 32768, 0];
for s in 0..5u32 {
w.write_symbol(s % 4, &mut cdf_for_write).unwrap();
}
for b in [0u32, 1, 1, 0, 1, 0].iter() {
w.write_bool(*b).unwrap();
}
let mut cdf_for_write2: [u16; 5] = [4096, 12288, 22000, 32768, 0];
for s in [3u32, 0, 2, 1].iter() {
w.write_symbol(*s, &mut cdf_for_write2).unwrap();
}
let bytes = w.finish();
let mut d = SymbolDecoder::init_symbol(&bytes, bytes.len(), true).unwrap();
let mut cdf_for_read: [u16; 5] = [8192, 16384, 24576, 32768, 0];
for _ in 0..5 {
d.read_symbol(&mut cdf_for_read).unwrap();
let r = d.symbol_range();
let v = d.symbol_value();
assert!(
(1u32 << 15..=u32::from(u16::MAX)).contains(&r),
"invariant #1: SymbolRange = {r}"
);
assert!(v < r, "invariant #2: SymbolValue={v} SymbolRange={r}");
}
for _ in 0..6 {
d.read_bool().unwrap();
let r = d.symbol_range();
let v = d.symbol_value();
assert!(
(1u32 << 15..=u32::from(u16::MAX)).contains(&r),
"invariant #1: SymbolRange = {r}"
);
assert!(v < r, "invariant #2: SymbolValue={v} SymbolRange={r}");
}
let mut cdf_for_read2: [u16; 5] = [4096, 12288, 22000, 32768, 0];
for _ in 0..4 {
d.read_symbol(&mut cdf_for_read2).unwrap();
let r = d.symbol_range();
let v = d.symbol_value();
assert!(
(1u32 << 15..=u32::from(u16::MAX)).contains(&r),
"invariant #1: SymbolRange = {r}"
);
assert!(v < r, "invariant #2: SymbolValue={v} SymbolRange={r}");
}
}
#[test]
fn log2_frac256_anchor_points() {
assert_eq!(log2_frac256(1 << 15), 0);
assert_eq!(log2_frac256((1 << 16) - 1), 255);
assert_eq!(log2_frac256(46341), 128);
let mut prev = 0;
for x in (1u32 << 15)..(1 << 16) {
let f = log2_frac256(x);
assert!(f >= prev, "log2_frac256 not monotone at {x}");
prev = f;
}
}
#[test]
fn counting_writer_tracks_emitting_writer() {
let start_cdf: [u16; 9] = [3000, 7000, 12000, 16000, 20000, 24000, 28000, 32768, 0];
let mut x: u32 = 0x1234_5678;
let mut emit = SymbolWriter::new(false);
let mut count = SymbolWriter::new_counting(false, emit.range());
let mut cdf_e = start_cdf;
let mut cdf_c = start_cdf;
for _ in 0..500 {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
let s = x % 8;
emit.write_symbol(s, &mut cdf_e).unwrap();
count.write_symbol(s, &mut cdf_c).unwrap();
assert_eq!(emit.range(), count.range(), "range trajectory diverged");
}
assert_eq!(cdf_e, cdf_c, "§8.3 adaptation diverged");
let cost = count.cost_bits256();
let renorm = emit.renorm_bits * 256;
let diff = cost.abs_diff(renorm);
assert!(
diff <= 256,
"cost {cost} vs renorm-bit count {renorm}: off by more than one bit"
);
assert_eq!(emit.low_bits.len() as u64, 15 + emit.renorm_bits);
}
#[test]
fn counting_writer_seeded_mid_stream_prices_suffix() {
let start_cdf: [u16; 5] = [6000, 14000, 22000, 32768, 0];
let mut emit = SymbolWriter::new(false);
let mut cdf_e = start_cdf;
for s in [0u32, 2, 1, 3, 0, 0, 1] {
emit.write_symbol(s, &mut cdf_e).unwrap();
}
let renorm_before = emit.renorm_bits;
let mut count = SymbolWriter::new_counting(false, emit.range());
let mut cdf_c = cdf_e;
for s in [3u32, 3, 2, 0, 1, 2, 3, 1, 0, 2] {
emit.write_symbol(s, &mut cdf_e).unwrap();
count.write_symbol(s, &mut cdf_c).unwrap();
}
assert_eq!(emit.range(), count.range());
assert_eq!(cdf_e, cdf_c);
let suffix_renorm = (emit.renorm_bits - renorm_before) * 256;
assert!(count.cost_bits256().abs_diff(suffix_renorm) <= 256);
}
#[test]
fn counting_writer_resolves_sub_bit_costs() {
let mut w = SymbolWriter::new_counting(true, 1 << 15);
for _ in 0..64 {
let mut cdf: [u16; 3] = [4096, 32768, 0];
w.write_symbol(1, &mut cdf).unwrap();
}
let per_symbol = w.cost_bits256() as f64 / 64.0 / 256.0;
assert!(
per_symbol > 0.05 && per_symbol < 0.5,
"likely-symbol cost {per_symbol} bits: outside the sub-bit window"
);
let mut w = SymbolWriter::new_counting(true, 1 << 15);
for _ in 0..64 {
let mut cdf: [u16; 3] = [4096, 32768, 0];
w.write_symbol(0, &mut cdf).unwrap();
}
let per_symbol = w.cost_bits256() as f64 / 64.0 / 256.0;
assert!(
per_symbol > 2.0 && per_symbol < 4.0,
"rare-symbol cost {per_symbol} bits: outside the expected window"
);
}
#[test]
fn encoder_range_invariant_check_distinguishes_boundaries() {
assert!(SymbolWriter::check_range_invariant(1 << 15).is_ok());
assert!(SymbolWriter::check_range_invariant((1 << 16) - 1).is_ok());
assert!(SymbolWriter::check_range_invariant(40000).is_ok());
assert_eq!(
SymbolWriter::check_range_invariant((1 << 15) - 1).unwrap_err(),
Error::SymbolStateInvariantBroken
);
assert_eq!(
SymbolWriter::check_range_invariant(0).unwrap_err(),
Error::SymbolStateInvariantBroken
);
assert_eq!(
SymbolWriter::check_range_invariant(1 << 16).unwrap_err(),
Error::SymbolStateInvariantBroken
);
assert_eq!(
SymbolWriter::check_range_invariant(u32::MAX).unwrap_err(),
Error::SymbolStateInvariantBroken
);
}
}