use super::VarVecError;
use dsi_bitstream::prelude::{Codes, CodesStats};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Codec {
#[default]
Gamma,
Delta,
Unary,
Rice { log2_b: Option<u8> },
Zeta { k: Option<u64> },
Golomb { b: Option<u64> },
Omega,
Pi { k: Option<u64> },
ExpGolomb { k: Option<u64> },
VByteLe,
VByteBe,
Auto,
Explicit(Codes),
}
#[deprecated(since = "0.6.0", note = "renamed to `Codec`; use `Codec` instead")]
pub type VariableCodecSpec = Codec;
impl Codec {
#[inline]
pub(crate) fn requires_analysis(&self) -> bool {
matches!(
self,
Codec::Auto
| Codec::Rice { log2_b: None }
| Codec::Zeta { k: None }
| Codec::Golomb { b: None }
| Codec::Pi { k: None }
| Codec::ExpGolomb { k: None }
)
}
}
pub(crate) fn resolve_codec<U>(input: &[U], spec: Codec) -> Result<Codes, VarVecError>
where
U: Into<u64> + Copy,
{
match spec {
Codec::Gamma => Ok(Codes::Gamma),
Codec::Delta => Ok(Codes::Delta),
Codec::Unary => Ok(Codes::Unary),
Codec::Omega => Ok(Codes::Omega),
Codec::VByteLe => Ok(Codes::VByteLe),
Codec::VByteBe => Ok(Codes::VByteBe),
Codec::Explicit(codes) => Ok(codes),
Codec::Rice { log2_b: Some(p) } => Ok(Codes::Rice(p as usize)),
Codec::Zeta { k: Some(p) } => Ok(Codes::Zeta(p as usize)),
Codec::Golomb { b: Some(p) } => Ok(Codes::Golomb(p)),
Codec::Pi { k: Some(p) } => Ok(Codes::Pi(p as usize)),
Codec::ExpGolomb { k: Some(p) } => Ok(Codes::ExpGolomb(p as usize)),
Codec::Auto
| Codec::Rice { log2_b: None }
| Codec::Zeta { k: None }
| Codec::Golomb { b: None }
| Codec::Pi { k: None }
| Codec::ExpGolomb { k: None } => {
if input.is_empty() {
return Ok(Codes::Gamma); }
type DefaultCodesStats = CodesStats<10, 20, 10, 10, 10>;
let mut stats = DefaultCodesStats::default();
for &value in input {
stats.update(value.into());
}
match spec {
Codec::Auto => {
let (best_code, _) = stats.best_code();
Ok(best_code.canonicalize())
}
Codec::Rice { log2_b: None } => {
let (best_param, _) = stats
.rice
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0)); Ok(Codes::Rice(best_param))
}
Codec::Zeta { k: None } => {
let (best_param, _) = stats
.zeta
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::Zeta(best_param + 1)) }
Codec::Golomb { b: None } => {
let (best_param, _) = stats
.golomb
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::Golomb((best_param + 1) as u64)) }
Codec::Pi { k: None } => {
let (best_param, _) = stats
.pi
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::Pi(best_param + 2)) }
Codec::ExpGolomb { k: None } => {
let (best_param, _) = stats
.exp_golomb
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::ExpGolomb(best_param))
}
_ => unreachable!(),
}
}
}
}
pub(crate) fn resolve_codec_from_iter<I>(iter: I, spec: Codec) -> Result<Codes, VarVecError>
where
I: Iterator<Item = u64>,
{
type DefaultCodesStats = CodesStats<10, 20, 10, 10, 10>;
let mut stats = DefaultCodesStats::default();
let mut count = 0usize;
for value in iter {
stats.update(value);
count += 1;
}
if count == 0 {
return Ok(Codes::Gamma);
}
match spec {
Codec::Auto => {
let (best_code, _) = stats.best_code();
Ok(best_code.canonicalize())
}
Codec::Rice { log2_b: None } => {
let (best_param, _) = stats
.rice
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::Rice(best_param))
}
Codec::Zeta { k: None } => {
let (best_param, _) = stats
.zeta
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::Zeta(best_param + 1))
}
Codec::Golomb { b: None } => {
let (best_param, _) = stats
.golomb
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::Golomb((best_param + 1) as u64))
}
Codec::Pi { k: None } => {
let (best_param, _) = stats
.pi
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::Pi(best_param + 2))
}
Codec::ExpGolomb { k: None } => {
let (best_param, _) = stats
.exp_golomb
.iter()
.enumerate()
.min_by_key(|&(_, cost)| cost)
.unwrap_or((0, &0));
Ok(Codes::ExpGolomb(best_param))
}
_ => unreachable!("resolve_codec_from_iter called with non-analysis codec"),
}
}