use crate::lossy::constants::{
COEFFS_PROBA_0, COEFFS_UPDATE_PROBA, CoeffProbas, CoeffStats, CoeffUpdateFlags, ENTROPY_COST,
NUM_BANDS, NUM_CTX, NUM_PROBAS, NUM_TYPES,
};
use crate::lossy::work::work;
pub(crate) fn bit_cost(bit: bool, prob: u8) -> u32 {
let idx = if bit {
256 - usize::from(prob)
} else {
usize::from(prob)
};
u32::from(ENTROPY_COST[idx])
}
fn optimize_node(c0: u64, c1: u64, default_p: u8, update_proba: u8) -> (u8, bool) {
let total = c0 + c1;
if total == 0 {
return (default_p, false);
}
let ratio = ((c0 << 8) + (total >> 1)) / total;
let new_p = ratio.clamp(1, 255) as u8;
let cost_default =
c0 * u64::from(bit_cost(false, default_p)) + c1 * u64::from(bit_cost(true, default_p));
let cost_new = c0 * u64::from(bit_cost(false, new_p)) + c1 * u64::from(bit_cost(true, new_p));
let cost_keep = u64::from(bit_cost(false, update_proba));
let cost_update = u64::from(bit_cost(true, update_proba)) + 8 * 256;
if cost_new + cost_update < cost_default + cost_keep {
(new_p, true)
} else {
(default_p, false)
}
}
pub(crate) fn optimize_probas(stats: &CoeffStats) -> (CoeffProbas, CoeffUpdateFlags) {
let mut probas = COEFFS_PROBA_0;
let mut updated = CoeffUpdateFlags::default();
for t in 0..NUM_TYPES {
for b in 0..NUM_BANDS {
for c in 0..NUM_CTX {
for p in 0..NUM_PROBAS {
work!(ProbaOptNode);
let [c0, c1] = stats[t][b][c][p];
let (prob, upd) = optimize_node(
c0,
c1,
COEFFS_PROBA_0[t][b][c][p],
COEFFS_UPDATE_PROBA[t][b][c][p],
);
probas[t][b][c][p] = prob;
updated[t][b][c][p] = upd;
}
}
}
}
(probas, updated)
}
#[cfg(test)]
mod tests {
use super::{optimize_node, optimize_probas};
use crate::lossy::constants::{
COEFFS_PROBA_0, CoeffStats, NUM_BANDS, NUM_CTX, NUM_PROBAS, NUM_TYPES,
};
#[test]
fn optimize_node_pins_rounded_probability_and_update() {
assert_eq!(optimize_node(3000, 1000, 128, 128), (192, true));
}
#[test]
fn optimize_node_keeps_default_until_saving_beats_overhead() {
assert_eq!(optimize_node(1000, 1000, 128, 128), (128, false));
}
#[test]
fn optimize_node_update_boundary_is_strict() {
assert_eq!(optimize_node(256, 0, 250, 128), (250, false));
}
#[test]
fn all_zero_stats_keep_every_default() {
let stats = Box::<CoeffStats>::default();
let (probas, updated) = optimize_probas(&stats);
assert_eq!(probas, COEFFS_PROBA_0, "empty stats must keep the defaults");
for plane in &updated {
for band in plane {
for ctx in band {
for &u in ctx {
assert!(!u, "no node may be updated on empty stats");
}
}
}
}
}
#[test]
fn a_skewed_node_is_updated_toward_its_empirical_probability() {
let mut stats = Box::<CoeffStats>::default();
stats[0][1][0][0] = [10_000, 5];
let (probas, updated) = optimize_probas(&stats);
assert!(updated[0][1][0][0], "the skewed node must be updated");
assert!(
probas[0][1][0][0] >= 250,
"empirical prob of bit0 near 255, got {}",
probas[0][1][0][0]
);
for t in 0..NUM_TYPES {
for b in 0..NUM_BANDS {
for c in 0..NUM_CTX {
for p in 0..NUM_PROBAS {
if (t, b, c, p) != (0, 1, 0, 0) {
assert_eq!(
probas[t][b][c][p], COEFFS_PROBA_0[t][b][c][p],
"untouched node ({t},{b},{c},{p}) changed"
);
assert!(!updated[t][b][c][p], "node ({t},{b},{c},{p}) updated");
}
}
}
}
}
}
}