pub const LAPLACE_LOG_MINP: u32 = 0;
pub const LAPLACE_MINP: u32 = 1 << LAPLACE_LOG_MINP;
pub const LAPLACE_NMIN: u32 = 16;
pub const LAPLACE_TOTAL: u32 = 1 << 15;
pub const LAPLACE_DECAY_UNIT: u32 = 1 << 14;
#[inline]
#[must_use]
pub const fn prob_to_fs(prob: u8) -> u32 {
(prob as u32) << 7
}
#[inline]
#[must_use]
pub const fn decay_byte_to_q14(decay: u8) -> u32 {
(decay as u32) << 6
}
#[inline]
#[must_use]
fn laplace_get_freq1(fs: u32, decay: u32) -> u32 {
let ft = LAPLACE_TOTAL
.saturating_sub(LAPLACE_MINP * (2 * LAPLACE_NMIN))
.saturating_sub(fs);
let prod = (ft as u64) * ((LAPLACE_DECAY_UNIT - decay) as u64);
(prod >> 15) as u32
}
pub fn ec_laplace_decode(
rd: &mut crate::range_decoder::RangeDecoder<'_>,
mut fs: u32,
decay: u32,
) -> i32 {
let fm = rd.decode_bin(15);
let mut val: i32 = 0;
let mut fl: u32 = 0;
if fm >= fs {
val = 1;
fl = fs;
fs = laplace_get_freq1(fs, decay) + LAPLACE_MINP;
while fs > LAPLACE_MINP && fm >= fl + 2 * fs {
fs *= 2;
fl += fs;
fs = ((fs - 2 * LAPLACE_MINP) * decay) >> 15;
fs += LAPLACE_MINP;
val += 1;
}
if fs <= LAPLACE_MINP {
let di = (fm - fl) >> (LAPLACE_LOG_MINP + 1);
val += di as i32;
fl += 2 * di * LAPLACE_MINP;
}
if fm < fl + fs {
val = -val;
} else {
fl += fs;
}
}
let fh = (fl + fs).min(LAPLACE_TOTAL);
rd.ec_dec_update(fl, fh, LAPLACE_TOTAL);
val
}
#[cfg(test)]
mod tests {
use super::*;
use crate::range_decoder::RangeDecoder;
#[test]
fn constants_match_table() {
assert_eq!(LAPLACE_LOG_MINP, 0);
assert_eq!(LAPLACE_MINP, 1);
assert_eq!(LAPLACE_NMIN, 16);
assert_eq!(LAPLACE_TOTAL, 32768);
assert_eq!(LAPLACE_DECAY_UNIT, 16384);
}
#[test]
fn scaling_shifts() {
assert_eq!(prob_to_fs(72), 72 << 7);
assert_eq!(prob_to_fs(72), 9216);
assert_eq!(decay_byte_to_q14(127), 127 << 6);
assert_eq!(decay_byte_to_q14(127), 8128);
}
#[test]
fn get_freq1_worked_example() {
let fs0 = prob_to_fs(72);
let decay = decay_byte_to_q14(127);
assert_eq!(23520u64 * 8256, 194_181_120);
assert_eq!(laplace_get_freq1(fs0, decay), 5925);
}
#[test]
fn body_step_decay_ratio() {
let decay = 6000u32;
let fs = 5000u32;
let doubled = fs * 2; let shrunk = ((doubled - 2 * LAPLACE_MINP) * decay) >> 15;
assert_eq!(shrunk, 1830);
assert_eq!(shrunk + LAPLACE_MINP, 1831);
}
#[test]
fn zero_symbol_for_central_draw() {
let buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let mut rd = RangeDecoder::new(&buf);
let qi = ec_laplace_decode(&mut rd, prob_to_fs(72), decay_byte_to_q14(127));
assert_eq!(qi, 0);
assert!(!rd.has_error());
}
#[test]
fn body_and_tail_branches_run_clean() {
for byte in [0xffu8, 0x80, 0x40, 0x01] {
let buf = [byte, byte, byte, byte, byte, byte, byte, byte];
let mut rd = RangeDecoder::new(&buf);
let _ = ec_laplace_decode(&mut rd, prob_to_fs(1), decay_byte_to_q14(8));
assert!(!rd.has_error(), "byte {byte:#x} latched an error");
}
}
}