dsi_bitstream/codes/minimal_binary.rs
1/*
2 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2023 Inria
4 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
5 *
6 * SPDX-License-Identifier: Apache-2.0 OR MIT
7 */
8
9//! Minimal binary codes.
10//!
11//! A minimal binary code with upper bound *u* > 0 (AKA [truncated binary
12//! encoding]) is an optimal prefix-free code for the first *u* natural numbers
13//! with uniform distribution.
14//!
15//! There are several such codes, and the one implemented here is defined as
16//! follows: let *s* = ⌈log₂*u*⌉; then, given *x* < *u*, if *x* <
17//! 2*ˢ* − *u* then *x* is coded as the binary representation of *x*
18//! in *s* − 1 bits; otherwise, *x* is coded as the binary representation of *x*
19//! − *u* + 2*ˢ* in *s* bits.
20//!
21//! The supported range for *u* is [1 . . 2⁶⁴). Note that calling any method
22//! with *u* = 0 will cause an arithmetic error.
23//!
24//! See the [codes module documentation] for some elaboration on the difference
25//! between the big-endian and little-endian versions of the codes.
26//!
27//! [truncated binary encoding]: https://en.wikipedia.org/wiki/Truncated_binary_encoding
28//! [codes module documentation]: crate::codes
29
30use crate::traits::*;
31
32/// Returns the length of the minimal binary code for `n` with upper bound `u`.
33#[must_use]
34#[inline(always)]
35pub const fn len_minimal_binary(n: u64, u: u64) -> usize {
36 debug_assert!(n < u);
37 let l = u.ilog2();
38 let limit = ((1_u64 << l) << 1).wrapping_sub(u);
39 let mut result = l as usize;
40 if n >= limit {
41 result += 1;
42 }
43 result
44}
45
46/// Trait for reading minimal binary codes.
47pub trait MinimalBinaryRead<E: Endianness>: BitRead<E> {
48 #[inline(always)]
49 fn read_minimal_binary(&mut self, u: u64) -> Result<u64, Self::Error> {
50 let l = u.ilog2();
51 let mut prefix = self.read_bits(l as _)?;
52 let limit = ((1_u64 << l) << 1).wrapping_sub(u);
53
54 Ok(if prefix < limit {
55 prefix
56 } else {
57 prefix <<= 1;
58 prefix |= self.read_bits(1)?;
59 prefix - limit
60 })
61 }
62}
63
64/// Trait for writing minimal binary codes.
65pub trait MinimalBinaryWrite<E: Endianness>: BitWrite<E> {
66 #[inline(always)]
67 fn write_minimal_binary(&mut self, n: u64, u: u64) -> Result<usize, Self::Error> {
68 debug_assert!(n < u);
69 let l = u.ilog2();
70 let limit = ((1_u64 << l) << 1).wrapping_sub(u);
71
72 if n < limit {
73 self.write_bits(n, l as _)?;
74 Ok(l as usize)
75 } else {
76 let to_write = n + limit;
77 self.write_bits(to_write >> 1, l as _)?;
78 self.write_bits(to_write & 1, 1)?;
79 Ok((l + 1) as usize)
80 }
81 }
82}
83
84impl<E: Endianness, B: BitRead<E>> MinimalBinaryRead<E> for B {}
85impl<E: Endianness, B: BitWrite<E>> MinimalBinaryWrite<E> for B {}