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