use crate::traits::*;
use super::{RiceRead, RiceWrite, len_rice, pi_tables};
#[must_use]
#[inline(always)]
#[allow(clippy::collapsible_if)]
pub fn len_pi_param<const USE_TABLE: bool>(mut n: u64, k: usize) -> usize {
debug_assert!(k < 64);
if USE_TABLE {
if k == pi_tables::K {
if n < pi_tables::LEN.len() as u64 {
return pi_tables::LEN[n as usize] as usize;
}
}
}
debug_assert!(n < u64::MAX);
n += 1;
let λ = n.ilog2() as usize;
len_rice(λ as u64, k) + λ
}
#[must_use]
#[inline(always)]
pub fn len_pi(n: u64, k: usize) -> usize {
len_pi_param::<true>(n, k)
}
pub trait PiRead<E: Endianness>: BitRead<E> {
fn read_pi(&mut self, k: usize) -> Result<u64, Self::Error>;
fn read_pi2(&mut self) -> Result<u64, Self::Error>;
}
pub trait PiReadParam<E: Endianness>: BitRead<E> {
fn read_pi_param(&mut self, k: usize) -> Result<u64, Self::Error>;
fn read_pi2_param<const USE_TABLE: bool>(&mut self) -> Result<u64, Self::Error>;
}
impl<B: BitRead<BE>> PiReadParam<BE> for B {
#[inline(always)]
fn read_pi_param(&mut self, k: usize) -> Result<u64, B::Error> {
default_read_pi(self, k)
}
#[inline(always)]
fn read_pi2_param<const USE_TABLE: bool>(&mut self) -> Result<u64, B::Error> {
const {
if USE_TABLE {
pi_tables::check_read_table(B::PEEK_BITS)
}
}
if USE_TABLE {
let (len_with_flag, value_or_lambda) = pi_tables::read_table_be(self);
if len_with_flag > 0 {
return Ok(value_or_lambda);
} else if len_with_flag < 0 {
let λ = value_or_lambda;
debug_assert!(λ < 64);
return Ok((1 << λ) + self.read_bits(λ as usize)? - 1);
}
}
default_read_pi(self, 2)
}
}
impl<B: BitRead<LE>> PiReadParam<LE> for B {
#[inline(always)]
fn read_pi_param(&mut self, k: usize) -> Result<u64, B::Error> {
default_read_pi(self, k)
}
#[inline(always)]
fn read_pi2_param<const USE_TABLE: bool>(&mut self) -> Result<u64, B::Error> {
const {
if USE_TABLE {
pi_tables::check_read_table(B::PEEK_BITS)
}
}
if USE_TABLE {
let (len_with_flag, value_or_lambda) = pi_tables::read_table_le(self);
if len_with_flag > 0 {
return Ok(value_or_lambda);
} else if len_with_flag < 0 {
let λ = value_or_lambda;
debug_assert!(λ < 64);
return Ok((1 << λ) + self.read_bits(λ as usize)? - 1);
}
}
default_read_pi(self, 2)
}
}
#[inline(always)]
fn default_read_pi<E: Endianness, B: BitRead<E>>(
backend: &mut B,
k: usize,
) -> Result<u64, B::Error> {
debug_assert!(k < 64);
let λ = backend.read_rice(k)?;
debug_assert!(λ < 64);
Ok((1 << λ) + backend.read_bits(λ as usize)? - 1)
}
pub trait PiWrite<E: Endianness>: BitWrite<E> {
fn write_pi(&mut self, n: u64, k: usize) -> Result<usize, Self::Error>;
fn write_pi2(&mut self, n: u64) -> Result<usize, Self::Error>;
}
pub trait PiWriteParam<E: Endianness>: BitWrite<E> {
fn write_pi_param(&mut self, n: u64, k: usize) -> Result<usize, Self::Error>;
fn write_pi2_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error>;
}
impl<B: BitWrite<BE>> PiWriteParam<BE> for B {
#[inline(always)]
fn write_pi_param(&mut self, n: u64, k: usize) -> Result<usize, Self::Error> {
default_write_pi(self, n, k)
}
#[inline(always)]
#[allow(clippy::collapsible_if)]
fn write_pi2_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error> {
if USE_TABLE {
if let Some(len) = pi_tables::write_table_be(self, n)? {
return Ok(len);
}
}
default_write_pi(self, n, 2)
}
}
impl<B: BitWrite<LE>> PiWriteParam<LE> for B {
#[inline(always)]
fn write_pi_param(&mut self, n: u64, k: usize) -> Result<usize, Self::Error> {
default_write_pi(self, n, k)
}
#[inline(always)]
#[allow(clippy::collapsible_if)]
fn write_pi2_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error> {
if USE_TABLE {
if let Some(len) = pi_tables::write_table_le(self, n)? {
return Ok(len);
}
}
default_write_pi(self, n, 2)
}
}
#[inline(always)]
fn default_write_pi<E: Endianness, B: BitWrite<E>>(
backend: &mut B,
mut n: u64,
k: usize,
) -> Result<usize, B::Error> {
debug_assert!(k < 64);
debug_assert!(n < u64::MAX);
n += 1;
let λ = n.ilog2() as usize;
#[cfg(feature = "checks")]
{
n ^= 1 << λ;
}
Ok(backend.write_rice(λ as u64, k)? + backend.write_bits(n, λ)?)
}
#[cfg(test)]
mod tests {
use crate::{
codes::pi::{PiReadParam, PiWriteParam},
prelude::*,
};
#[test]
fn test_roundtrip() {
let k = 3;
for value in (0..64).map(|i| 1 << i).chain(0..1024).chain([u64::MAX - 1]) {
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi(value, k).unwrap();
assert_eq!(code_len, len_pi(value, k));
drop(writer);
let mut reader = <BufBitReader<BE, _>>::new(MemWordReader::new_inf(&data));
assert_eq!(
reader.read_pi(k).unwrap(),
value,
"for value: {} with k {}",
value,
k
);
}
}
#[test]
fn test_roundtrip_pi2() {
for value in (0..64).map(|i| 1 << i).chain(0..1024).chain([u64::MAX - 1]) {
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi2(value).unwrap();
assert_eq!(code_len, len_pi(value, 2));
drop(writer);
let mut reader = <BufBitReader<BE, _>>::new(MemWordReader::new_inf(&data));
assert_eq!(reader.read_pi2().unwrap(), value, "BE for value: {}", value,);
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<LE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi2(value).unwrap();
assert_eq!(code_len, len_pi(value, 2));
drop(writer);
let mut reader = <BufBitReader<LE, _>>::new(MemWordReader::new_inf(&data));
assert_eq!(reader.read_pi2().unwrap(), value, "LE for value: {}", value,);
}
}
#[test]
fn test_roundtrip_pi2_param() {
for value in (0..64).map(|i| 1 << i).chain(0..1024).chain([u64::MAX - 1]) {
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi2_param::<true>(value).unwrap();
assert_eq!(code_len, len_pi(value, 2));
drop(writer);
let mut reader = <BufBitReader<BE, _>>::new(MemWordReader::new_inf(&data));
assert_eq!(
reader.read_pi2_param::<true>().unwrap(),
value,
"BE table for value: {}",
value,
);
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi2_param::<false>(value).unwrap();
assert_eq!(code_len, len_pi(value, 2));
drop(writer);
let mut reader = <BufBitReader<BE, _>>::new(MemWordReader::new_inf(&data));
assert_eq!(
reader.read_pi2_param::<false>().unwrap(),
value,
"BE no table for value: {}",
value,
);
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<LE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi2_param::<true>(value).unwrap();
assert_eq!(code_len, len_pi(value, 2));
drop(writer);
let mut reader = <BufBitReader<LE, _>>::new(MemWordReader::new_inf(&data));
assert_eq!(
reader.read_pi2_param::<true>().unwrap(),
value,
"LE table for value: {}",
value,
);
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<LE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi2_param::<false>(value).unwrap();
assert_eq!(code_len, len_pi(value, 2));
drop(writer);
let mut reader = <BufBitReader<LE, _>>::new(MemWordReader::new_inf(&data));
assert_eq!(
reader.read_pi2_param::<false>().unwrap(),
value,
"LE no table for value: {}",
value,
);
}
}
#[test]
#[allow(clippy::unusual_byte_groupings)]
fn test_bits() {
for (k, value, expected) in [
(2, 20, 0b01_00_0101 << (64 - 8)),
(2, 0, 0b100 << (64 - 3)),
(2, 1, 0b1010 << (64 - 4)),
(2, 2, 0b1011 << (64 - 4)),
(2, 3, 0b1_1000 << (64 - 5)),
(2, 4, 0b1_1001 << (64 - 5)),
(2, 5, 0b1_1010 << (64 - 5)),
(2, 6, 0b1_1011 << (64 - 5)),
(2, 7, 0b11_1000 << (64 - 6)),
(3, 0, 0b1000 << (64 - 4)),
(3, 1, 0b1_0010 << (64 - 5)),
(3, 2, 0b1_0011 << (64 - 5)),
(3, 3, 0b1_01000 << (64 - 6)),
(3, 4, 0b1_01001 << (64 - 6)),
(3, 5, 0b1_01010 << (64 - 6)),
(3, 6, 0b1_01011 << (64 - 6)),
(3, 7, 0b101_1000 << (64 - 7)),
] {
let mut data = [0_u64; 10];
let mut writer = <BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data));
let code_len = writer.write_pi(value, k).unwrap();
assert_eq!(code_len, len_pi(value, k));
drop(writer);
assert_eq!(
data[0].to_be(),
expected,
"\nfor value: {} with k {}\ngot: {:064b}\nexp: {:064b}\ngot_len: {} exp_len: {}\n",
value,
k,
data[0].to_be(),
expected,
code_len,
len_pi(value, k),
);
}
}
#[test]
fn test_against_zeta() {
for k in 0..2 {
for value in 0..100 {
let mut data_pi = [0_u64; 10];
let mut data_zeta = [0_u64; 10];
let mut writer = <BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data_pi));
let code_len = writer.write_pi(value, k).unwrap();
assert_eq!(code_len, len_pi(value, k));
drop(writer);
let mut writer =
<BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data_zeta));
let code_len = writer.write_zeta(value, 1 << k).unwrap();
assert_eq!(code_len, len_zeta(value, 1 << k));
drop(writer);
assert_eq!(data_pi[0], data_zeta[0]);
}
}
for value in 0..100 {
let mut data_pi = [0_u64; 10];
let mut data_zeta = [0_u64; 10];
let mut writer = <BufBitWriter<LE, _>>::new(MemWordWriterSlice::new(&mut data_pi));
let code_len = writer.write_pi(value, 0).unwrap();
assert_eq!(code_len, len_pi(value, 0));
drop(writer);
let mut writer = <BufBitWriter<LE, _>>::new(MemWordWriterSlice::new(&mut data_zeta));
let code_len = writer.write_zeta(value, 1).unwrap();
assert_eq!(code_len, len_zeta(value, 1));
drop(writer);
assert_eq!(data_pi[0], data_zeta[0]);
}
for value in 0..100 {
assert_eq!(len_pi(value, 1), len_zeta(value, 2));
}
}
}