use std::ops::{Index, IndexMut};
pub(super) struct VorbisCodebookNumberFrequenciesDecorator<T: AsMut<[u64]> + AsRef<[u64]>> {
number_frequencies: T,
number_index_map: Vec<usize>
}
impl<T: AsMut<[u64]> + AsRef<[u64]>> VorbisCodebookNumberFrequenciesDecorator<T> {
pub(super) fn new(number_frequencies: T) -> Self {
let mut number_index_map;
{
let number_frequencies = number_frequencies.as_ref();
number_index_map = Vec::with_capacity(number_frequencies.len());
for (index, frequency) in number_frequencies.iter().copied().enumerate() {
if frequency != 0 {
number_index_map.push(index);
}
}
number_index_map
.sort_unstable_by(|i, j| number_frequencies[*j].cmp(&number_frequencies[*i]));
number_index_map.shrink_to_fit(); }
Self {
number_frequencies,
number_index_map
}
}
pub(super) fn into_huffman_codeword_lengths(mut self) -> T {
let used_codeword_count = self.number_index_map.len();
match used_codeword_count {
0 => {
self.number_frequencies
}
1 => {
self[0] = 1;
self.number_frequencies
}
used_codeword_count => {
compute_huffman_codeword_lengths(self, used_codeword_count).number_frequencies
}
}
}
}
impl<T: AsMut<[u64]> + AsRef<[u64]>> Index<usize> for VorbisCodebookNumberFrequenciesDecorator<T> {
type Output = u64;
fn index(&self, index: usize) -> &Self::Output {
&self.number_frequencies.as_ref()[self.number_index_map[index]]
}
}
impl<T: AsMut<[u64]> + AsRef<[u64]>> IndexMut<usize>
for VorbisCodebookNumberFrequenciesDecorator<T>
{
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.number_frequencies.as_mut()[self.number_index_map[index]]
}
}
fn compute_huffman_codeword_lengths<T: IndexMut<usize, Output = u64>>(
number_frequencies: T,
frequency_count: usize
) -> T {
let mut w = number_frequencies;
let mut leaf = (frequency_count - 1) as i32;
let mut root = (frequency_count - 1) as i32;
for next in (1..frequency_count as i32).rev() {
if leaf < 0 || (root > next && w[root as usize] < w[leaf as usize]) {
w[next as usize] = w[root as usize];
w[root as usize] = next as u64;
root -= 1;
} else {
w[next as usize] = w[leaf as usize];
leaf -= 1;
}
if leaf < 0 || (root > next && w[root as usize] < w[leaf as usize]) {
w[next as usize] += w[root as usize];
w[root as usize] = next as u64;
root -= 1;
} else {
w[next as usize] += w[leaf as usize];
leaf -= 1;
}
}
w[1] = 0;
for next in 2..frequency_count {
w[next] = w[w[next] as usize] + 1;
}
let mut available = 1;
let mut used = 0;
let mut depth = 0;
root = 1;
let mut next = 0;
while available > 0 {
while root < frequency_count as i32 && w[root as usize] == depth {
used += 1;
root += 1;
}
while available > used {
w[next] = depth;
next += 1;
available -= 1;
}
available = 2 * used;
depth += 1;
used = 0;
}
w
}
#[cfg(test)]
mod test {
use super::*;
use crate::vorbis::codebook::VorbisCodebook;
#[test]
fn compute_huffman_codeword_lengths_works() {
const PAPER_EXAMPLE_FREQUENCIES_ARRAY: [u64; 10] = [20, 17, 6, 3, 2, 2, 2, 1, 1, 1];
const PAPER_EXAMPLE_CODELENGTHS_RESULT: [u64; 10] = [1, 2, 4, 5, 5, 5, 5, 5, 6, 6];
const PAPER_EXAMPLE_CODELENGTHS_RESULT_U8: [u8; 10] = [1, 2, 4, 5, 5, 5, 5, 5, 6, 6];
assert_eq!(
VorbisCodebookNumberFrequenciesDecorator::new(PAPER_EXAMPLE_FREQUENCIES_ARRAY)
.into_huffman_codeword_lengths(),
PAPER_EXAMPLE_CODELENGTHS_RESULT
);
VorbisCodebook::new(0, PAPER_EXAMPLE_CODELENGTHS_RESULT_U8).expect(
"It should be possible to construct a Huffman tree with the computed codeword lengths"
);
}
#[test]
fn compute_huffman_codeword_lengths_works_for_unsorted_and_unused_entries() {
const TWEAKED_PAPER_EXAMPLE_FREQUENCIES_ARRAY: [u64; 11] =
[1, 20, 2, 1, 6, 0, 2, 2, 3, 1, 17];
const TWEAKED_PAPER_EXAMPLE_CODELENGTHS_RESULT: [u64; 11] =
[5, 1, 5, 6, 4, 0, 5, 5, 5, 6, 2];
const TWEAKED_PAPER_EXAMPLE_CODELENGTHS_RESULT_U8: [u8; 11] =
[5, 1, 5, 6, 4, 0, 5, 5, 5, 6, 2];
assert_eq!(
VorbisCodebookNumberFrequenciesDecorator::new(TWEAKED_PAPER_EXAMPLE_FREQUENCIES_ARRAY)
.into_huffman_codeword_lengths(),
TWEAKED_PAPER_EXAMPLE_CODELENGTHS_RESULT
);
VorbisCodebook::new(0, TWEAKED_PAPER_EXAMPLE_CODELENGTHS_RESULT_U8).expect(
"It should be possible to construct a Huffman tree with the computed codeword lengths"
);
}
#[test]
fn compute_huffman_codeword_lengths_works_for_no_used_entries() {
assert_eq!(
VorbisCodebookNumberFrequenciesDecorator::new([]).into_huffman_codeword_lengths(),
[]
);
assert_eq!(
VorbisCodebookNumberFrequenciesDecorator::new([0, 0, 0])
.into_huffman_codeword_lengths(),
[0, 0, 0]
);
}
#[test]
fn compute_huffman_codeword_lengths_works_for_single_used_entry() {
assert_eq!(
VorbisCodebookNumberFrequenciesDecorator::new([22]).into_huffman_codeword_lengths(),
[1],
"Single-used entry codebooks should have a codeword length of 1 for that entry"
);
assert_eq!(
VorbisCodebookNumberFrequenciesDecorator::new([0, 22, 0, 0])
.into_huffman_codeword_lengths(),
[0, 1, 0, 0],
"Single-used entry codebooks should have a codeword length of 1 for that entry"
);
}
}