use crate::range::{RangeDecoder, RangeEncoder};
const LAPLACE_LOG_MINP: u32 = 0;
const LAPLACE_MINP: u32 = 1 << LAPLACE_LOG_MINP;
const LAPLACE_NMIN: u32 = 16;
const FTB: u32 = 15;
const FT: u32 = 1 << FTB;
fn freq1(fs0: u32, decay: u32) -> u32 {
let ft = FT - LAPLACE_MINP * (2 * LAPLACE_NMIN) - fs0;
(ft * (16384 - decay)) >> 15
}
pub fn ec_laplace_encode(enc: &mut RangeEncoder, value: i32, fs: u32, decay: u32) -> i32 {
let mut fl = 0u32;
let mut fs = fs;
let mut value = value;
if value != 0 {
let s = -i32::from(value < 0);
let val = (value + s) ^ s;
fl = fs;
fs = freq1(fs, decay);
let mut i = 1;
while fs > 0 && i < val {
fs *= 2;
fl += fs + 2 * LAPLACE_MINP;
fs = (fs * decay) >> 15;
i += 1;
}
if fs == 0 {
let ndi_max = ((FT - fl + LAPLACE_MINP - 1) >> LAPLACE_LOG_MINP) as i32;
let ndi_max = (ndi_max - s) >> 1;
let di = (val - i).min(ndi_max - 1);
fl = (fl as i32 + (2 * di + 1 + s) * LAPLACE_MINP as i32) as u32;
fs = LAPLACE_MINP.min(FT - fl);
value = (i + di + s) ^ s;
} else {
fs += LAPLACE_MINP;
if s == 0 {
fl += fs;
}
}
debug_assert!(fl + fs <= FT);
debug_assert!(fs > 0);
}
enc.encode_bin(fl, fl + fs, FTB);
value
}
pub fn ec_laplace_decode(dec: &mut RangeDecoder, fs: u32, decay: u32) -> i32 {
let mut val = 0i32;
let mut fl = 0u32;
let mut fs = fs;
let fm = dec.decode_bin(FTB);
if fm >= fs {
val += 1;
fl = fs;
fs = 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;
}
}
debug_assert!(fl < FT);
debug_assert!(fs > 0);
debug_assert!(fl <= fm);
debug_assert!(fm < (fl + fs).min(FT));
dec.update(fl, (fl + fs).min(FT), FT);
val
}
#[cfg(test)]
mod tests {
use super::*;
use crate::range::{RangeDecoder, RangeEncoder};
const MODELS: [(u32, u32); 4] = [
(9 << 7, 100 << 6),
(42 << 7, 80 << 6),
(110 << 7, 60 << 6),
(66 << 7, 120 << 6),
];
#[test]
fn round_trips_in_range_values() {
for &(fs, decay) in &MODELS {
let values: alloc::vec::Vec<i32> = (-20..=20).collect();
let mut enc = RangeEncoder::new(256);
let mut coded = alloc::vec::Vec::new();
for &v in &values {
coded.push(ec_laplace_encode(&mut enc, v, fs, decay));
}
assert_eq!(coded, values, "fs={fs} decay={decay}");
let enc_rng = enc.range_size();
let buf = enc.finalize().expect("within budget");
let mut dec = RangeDecoder::new(&buf);
for &v in &values {
assert_eq!(ec_laplace_decode(&mut dec, fs, decay), v, "fs={fs} decay={decay}");
}
assert_eq!(dec.range_size(), enc_rng, "encoder/decoder rng agreement");
}
}
#[test]
fn saturates_extreme_values_consistently() {
let (fs, decay) = MODELS[0];
let mut enc = RangeEncoder::new(64);
let coded = ec_laplace_encode(&mut enc, 30_000, fs, decay);
assert!(coded < 30_000, "value must saturate");
let buf = enc.finalize().expect("within budget");
let mut dec = RangeDecoder::new(&buf);
assert_eq!(ec_laplace_decode(&mut dec, fs, decay), coded);
}
extern crate alloc;
}