use std::{
fmt::{Debug, Display},
hash::Hash,
};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct HfmnCode {
code: usize,
length: u8,
}
#[derive(Debug, FromPrimitive, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub enum Bit {
Zero = 0,
One = 1,
}
impl HfmnCode {
#[must_use]
pub const fn new() -> Self {
Self { code: 0, length: 0 }
}
#[must_use]
pub const fn from(code: usize, length: u8) -> Self {
Self { code, length }
}
#[allow(clippy::cast_possible_truncation)]
pub fn push(&mut self, bit: Bit) {
assert!(self.length < { (std::mem::size_of::<usize>() * 8) as u8 });
self.code <<= 1;
self.code |= bit as usize;
self.length = self.length.saturating_add(1);
}
#[allow(clippy::len_without_is_empty)]
#[must_use]
pub const fn len(&self) -> u8 {
self.length
}
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn first_n_bits(&self, n: usize) -> Self {
assert!(n <= std::mem::size_of::<usize>() * 8);
let mask = (1_usize << n).wrapping_sub(1);
Self {
code: self.code & mask,
length: n as u8,
}
}
#[must_use]
pub fn inner_mut(&mut self) -> &mut usize {
&mut self.code
}
#[must_use]
pub fn length_mut(&mut self) -> &mut u8 {
&mut self.length
}
#[must_use]
pub const fn inner(&self) -> &usize {
&self.code
}
#[allow(clippy::cast_possible_truncation)]
pub fn resize(&mut self, size: usize) {
assert!(size <= std::mem::size_of::<usize>() * 8);
self.code <<= size.saturating_sub(self.length as usize);
self.length = size as u8;
}
#[must_use]
pub fn bits(&self) -> Vec<Bit> {
let mut bits = Vec::new();
for i in (0..self.length).rev() {
let mask = 1 << i;
bits.push(Bit::from_usize((self.code & mask) >> i).expect("Code invalid"));
}
bits
}
}
impl Hash for HfmnCode {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.code.hash(state);
}
}
impl Default for HfmnCode {
fn default() -> Self {
Self::new()
}
}
impl Display for HfmnCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let string = format!("{:064b}", self.code);
let start_idx = string.len().saturating_sub(self.length as usize);
write!(f, "{}", &string[start_idx..])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn direct_construction() {
let input = 0b0101_1110;
let symbol = HfmnCode::from(input, 8);
assert_eq!(symbol.length, 8);
assert_eq!(symbol.code, input);
}
#[test]
fn push_construction() {
let bits = vec![Bit::Zero, Bit::One, Bit::Zero, Bit::Zero];
let mut symbol = HfmnCode::new();
bits.into_iter().for_each(|b| symbol.push(b));
assert_eq!(symbol.length, 4);
assert_eq!(symbol.code, 0b0100);
}
#[test]
fn retrieve_n_bits() {
let bits = 0b11_0010;
let symbol = HfmnCode::from(bits, 6);
assert_eq!(symbol.first_n_bits(3), HfmnCode::from(bits & 0b111, 3));
assert_eq!(symbol.first_n_bits(6), HfmnCode::from(bits, 6));
}
#[test]
fn resize() {
let mut symbol = HfmnCode::from(0b01_0101, 6);
symbol.resize(8);
assert_eq!(symbol.length, 8);
assert_eq!(symbol.code, 0b0101_0100);
}
#[test]
fn bits() {
let symbol = HfmnCode::from(0b01_1001, 6);
let expected = vec![
Bit::Zero,
Bit::One,
Bit::One,
Bit::Zero,
Bit::Zero,
Bit::One,
];
assert_eq!(symbol.bits(), expected);
}
}