use crate::raw::codec::freq::FREQUENCIES;
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap};
use std::sync::LazyLock;
pub static HUFFMAN_TREE: LazyLock<HuffmanNode> = LazyLock::new(build_tree);
pub static HUFFMAN_CODES: LazyLock<HashMap<char, (u64, u8)>> =
LazyLock::new(|| build_codes(&HUFFMAN_TREE));
#[derive(Eq, PartialEq)]
pub enum HuffmanNode {
Leaf {
ch: char,
freq: u64,
},
Internal {
freq: u64,
left: Box<Self>,
right: Box<Self>,
},
}
impl HuffmanNode {
#[must_use]
pub const fn freq(&self) -> u64 {
match self {
Self::Leaf { freq, .. } => *freq,
Self::Internal { freq, .. } => *freq,
}
}
}
impl Ord for HuffmanNode {
fn cmp(&self, other: &Self) -> Ordering {
other.freq().cmp(&self.freq())
}
}
impl PartialOrd for HuffmanNode {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[must_use]
pub fn build_tree() -> HuffmanNode {
let mut heap = BinaryHeap::new();
for &(ch, freq) in FREQUENCIES {
heap.push(HuffmanNode::Leaf { ch, freq });
}
while heap.len() > 1 {
let left = heap.pop().unwrap();
let right = heap.pop().unwrap();
let freq = left.freq() + right.freq();
heap.push(HuffmanNode::Internal {
freq,
left: Box::new(left),
right: Box::new(right),
});
}
heap.pop().unwrap()
}
#[must_use]
pub fn build_codes(node: &HuffmanNode) -> HashMap<char, (u64, u8)> {
let mut codes = HashMap::new();
walk(node, 0u64, 0u8, &mut codes);
codes
}
fn walk(node: &HuffmanNode, code: u64, depth: u8, codes: &mut HashMap<char, (u64, u8)>) {
match node {
HuffmanNode::Leaf { ch, .. } => {
codes.insert(*ch, (code, depth));
}
HuffmanNode::Internal { left, right, .. } => {
walk(left, code << 1, depth + 1, codes);
walk(right, (code << 1) | 1, depth + 1, codes);
}
}
}