Skip to main content

dsi_bitstream/codes/
omega.rs

1/*
2 * SPDX-FileCopyrightText: 2024 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR MIT
6 */
7
8//! Elias ω code.
9//!
10//! Elias [γ] and [δ] codes encode a number *n* by storing the binary
11//! representation of *n* + 1, with the most significant bit removed, prefixed
12//! by its length in unary or [γ] code, respectively. Thus, [δ] can be seen as
13//! adding one level of recursion in the length representation with respect to
14//! [γ]. The ω code encodes the length of the binary representation of *n* + 1
15//! recursively.
16//!
17//! The implied distribution for the ω code is difficult to write analytically,
18//! but essentially it is as close as possible to ≈ 1/*x* (as there is no code
19//! for that distribution).
20//!
21//! The supported range is [0 . . 2⁶⁴ – 1).
22//!
23//! For the ω code it is easier to describe the format of a codeword, rather
24//! than the encoding algorithm.
25//!
26//! A codeword is given by the concatenation of blocks *b*₀ *b*₁ …  *b*ₙ `0`,
27//! where each block *b*ᵢ is a binary string starting with `1` and *b*₀ = `10`
28//! or `11`. One can interpret the highest bit of each block as a continuation
29//! bit, and the last `0` as a terminator of the code.
30//!
31//! The condition for a valid codeword is that the value represented by each
32//! block, incremented by one, is the length of the following block, except for
33//! the last block.
34//!
35//! The value associated with a codeword is 0 if the code is `0`, and otherwise
36//! the value of the last block, decremented by one.
37//!
38//! For example, `1110110`, which is formed by the blocks `11`, `1011`, and `0`,
39//! represents the number 10.
40//!
41//! As discussed in the [codes module documentation], to make the code readable
42//! in the little-endian case, rather than reversing the bits of the blocks,
43//! which would be expensive, we simply rotate by one on the left each block,
44//! with the result that the most significant bit of the block is now the first
45//! bit in the stream, making it possible to check for the presence of a
46//! continuation bit. For example, in the little-endian case, the code for 10 is
47//! `0011111`, which is formed by the blocks `11`, `0111`, and `0`.
48//!
49//! # Table-Based Optimization
50//!
51//! Unlike [γ], [δ], and [ζ] codes, ω codes use a special optimization for
52//! partial decoding. Due to the recursive nature of ω codes, when a complete
53//! codeword cannot be read from the table the table still provides partial
54//! information about the blocks that were successfully decoded. This partial
55//! state is used to continue decoding efficiently, avoiding re-reading the
56//! initial blocks.
57//!
58//! # References
59//!
60//! Peter Elias. “[Universal codeword sets and representations of the
61//! integers]”. IEEE Transactions on Information Theory, 21(2):194–203, March
62//! 1975.
63//!
64//! [γ]: super::gamma
65//! [δ]: super::delta
66//! [codes module documentation]: crate::codes
67//! [ζ]: super::zeta
68//! [Universal codeword sets and representations of the integers]: https://doi.org/10.1109/TIT.1975.1055349
69
70use crate::codes::omega_tables;
71use crate::traits::*;
72use num_primitive::PrimitiveNumber;
73
74/// Returns the length of the ω code for `n`.
75#[must_use]
76#[inline(always)]
77pub const fn len_omega_param<const USE_TABLE: bool>(n: u64) -> usize {
78    debug_assert!(n < u64::MAX);
79    if USE_TABLE {
80        // We cannot use .get() here because n is a u64
81        if n < omega_tables::LEN.len() as u64 {
82            return omega_tables::LEN[n as usize] as usize;
83        }
84    }
85    recursive_len(n + 1)
86}
87
88/// Returns the length of the ω code for `n`, using
89/// a default value for `USE_TABLE`.
90#[must_use]
91#[inline(always)]
92pub const fn len_omega(n: u64) -> usize {
93    len_omega_param::<true>(n)
94}
95
96const fn recursive_len(n: u64) -> usize {
97    if n <= 1 {
98        return 1;
99    }
100    let λ = n.ilog2() as u64;
101    recursive_len(λ) + λ as usize + 1
102}
103
104/// Trait for reading ω codes.
105///
106/// This is the trait you should usually pull into scope to read ω codes.
107pub trait OmegaRead<E: Endianness>: BitRead<E> {
108    fn read_omega(&mut self) -> Result<u64, Self::Error>;
109}
110
111/// Parametric trait for reading ω codes.
112///
113/// This trait is more general than [`OmegaRead`], as it makes it possible
114/// to specify how to use tables using const parameters.
115///
116/// We provide an implementation of this trait for [`BitRead`]. An implementation
117/// of [`OmegaRead`] using default values is usually provided exploiting the
118/// [`crate::codes::params::ReadParams`] mechanism.
119pub trait OmegaReadParam<E: Endianness>: BitRead<E> {
120    fn read_omega_param<const USE_TABLE: bool>(&mut self) -> Result<u64, Self::Error>;
121}
122
123/// Default, internal non-table based implementation that works
124/// for any endianness.
125#[inline(always)]
126fn default_read_omega<E: Endianness, B: BitRead<E>>(backend: &mut B) -> Result<u64, B::Error> {
127    read_omega_from_state::<E, B>(backend, 1)
128}
129
130/// Internal implementation that continues reading from a given state.
131///
132/// This is used both by the default implementation (starting from state n=1)
133/// and by the table-accelerated version (continuing from partial state).
134/// The bits have already been skipped by the caller.
135#[inline(always)]
136fn read_omega_from_state<E: Endianness, B: BitRead<E>>(
137    backend: &mut B,
138    mut n: u64,
139) -> Result<u64, B::Error> {
140    loop {
141        let bit: u64 = backend.peek_bits(1)?.as_to();
142        if bit == 0 {
143            backend.skip_bits_after_peek(1);
144            return Ok(n - 1);
145        }
146
147        let λ = n;
148        n = backend.read_bits(λ as usize + 1)?;
149
150        if E::IS_LITTLE {
151            // Little-endian case: rotate right the lower λ + 1 bits (the lowest
152            // bit is a one) to reverse the rotation performed when writing
153            n = (n >> 1) | (1 << λ);
154        }
155    }
156}
157
158impl<B: BitRead<BE>> OmegaReadParam<BE> for B {
159    #[inline(always)]
160    fn read_omega_param<const USE_TABLE: bool>(&mut self) -> Result<u64, Self::Error> {
161        const {
162            if USE_TABLE {
163                omega_tables::check_read_table(B::PEEK_BITS)
164            }
165        }
166        if USE_TABLE {
167            let (len_with_flag, value) = omega_tables::read_table_be(self);
168            if len_with_flag > 0 {
169                // Complete code - bits already skipped in read_table
170                return Ok(value);
171            } else if len_with_flag < 0 {
172                // Partial code - bits already skipped in read_table, continue from partial_n
173                return read_omega_from_state::<BE, _>(self, value);
174            }
175            // len_with_flag == 0: not enough bits, fall through
176        }
177        default_read_omega(self)
178    }
179}
180
181impl<B: BitRead<LE>> OmegaReadParam<LE> for B {
182    #[inline(always)]
183    fn read_omega_param<const USE_TABLE: bool>(&mut self) -> Result<u64, Self::Error> {
184        const {
185            if USE_TABLE {
186                omega_tables::check_read_table(B::PEEK_BITS)
187            }
188        }
189        if USE_TABLE {
190            let (len_with_flag, value) = omega_tables::read_table_le(self);
191            if len_with_flag > 0 {
192                // Complete code - bits already skipped in read_table
193                return Ok(value);
194            } else if len_with_flag < 0 {
195                // Partial code - bits already skipped in read_table, continue from partial_n
196                return read_omega_from_state::<LE, _>(self, value);
197            }
198            // len_with_flag == 0: not enough bits, fall through
199        }
200        default_read_omega(self)
201    }
202}
203
204/// Trait for writing ω codes.
205///
206/// This is the trait you should usually pull into scope to write ω codes.
207pub trait OmegaWrite<E: Endianness>: BitWrite<E> {
208    fn write_omega(&mut self, n: u64) -> Result<usize, Self::Error>;
209}
210
211/// Parametric trait for writing ω codes.
212///
213/// This trait is more general than [`OmegaWrite`], as it makes it possible
214/// to specify how to use tables using const parameters.
215///
216/// We provide an implementation of this trait for [`BitWrite`]. An implementation
217/// of [`OmegaWrite`] using default values is usually provided exploiting the
218/// [`crate::codes::params::WriteParams`] mechanism.
219pub trait OmegaWriteParam<E: Endianness>: BitWrite<E> {
220    fn write_omega_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error>;
221}
222
223impl<B: BitWrite<BE>> OmegaWriteParam<BE> for B {
224    #[inline(always)]
225    fn write_omega_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error> {
226        debug_assert!(n < u64::MAX);
227        if USE_TABLE {
228            if let Some(len) = omega_tables::write_table_be(self, n)? {
229                return Ok(len);
230            }
231        }
232        Ok(recursive_omega_write::<BE, _>(n + 1, self)? + self.write_bits(0, 1)?)
233    }
234}
235
236impl<B: BitWrite<LE>> OmegaWriteParam<LE> for B {
237    #[inline(always)]
238    fn write_omega_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error> {
239        debug_assert!(n < u64::MAX);
240        if USE_TABLE {
241            if let Some(len) = omega_tables::write_table_le(self, n)? {
242                return Ok(len);
243            }
244        }
245        Ok(recursive_omega_write::<LE, _>(n + 1, self)? + self.write_bits(0, 1)?)
246    }
247}
248
249#[inline(always)]
250fn recursive_omega_write<E: Endianness, B: BitWrite<E>>(
251    mut n: u64,
252    writer: &mut B,
253) -> Result<usize, B::Error> {
254    if n <= 1 {
255        return Ok(0);
256    }
257    let λ = n.ilog2();
258    if E::IS_LITTLE {
259        #[cfg(feature = "checks")]
260        {
261            // Clean up after the lowest λ bits in case checks are enabled
262            n &= u64::MAX >> (u64::BITS - λ);
263        }
264        // Little-endian case: rotate left the lower λ + 1 bits (the bit in
265        // position λ is a one) so that the lowest bit can be peeked to find the
266        // block.
267        n = (n << 1) | 1;
268    }
269    Ok(recursive_omega_write::<E, _>(λ as u64, writer)? + writer.write_bits(n, λ as usize + 1)?)
270}
271
272#[cfg(test)]
273mod tests {
274    use crate::prelude::*;
275
276    #[test]
277    #[allow(clippy::unusual_byte_groupings)]
278    fn test_omega() {
279        for (value, expected_be, expected_le) in [
280            (0, 0, 0),
281            (1, 0b10_0 << (64 - 3), 0b0_01),
282            (2, 0b11_0 << (64 - 3), 0b0_11),
283            (3, 0b10_100_0 << (64 - 6), 0b0_001_01),
284            (4, 0b10_101_0 << (64 - 6), 0b0_011_01),
285            (5, 0b10_110_0 << (64 - 6), 0b0_101_01),
286            (6, 0b10_111_0 << (64 - 6), 0b0_111_01),
287            (7, 0b11_1000_0 << (64 - 7), 0b0_0001_11),
288            (15, 0b10_100_10000_0 << (64 - 11), 0b0_00001_001_01),
289            (99, 0b10_110_1100100_0 << (64 - 13), 0b0_1001001_101_01),
290            (
291                999,
292                0b11_1001_1111101000_0 << (64 - 17),
293                0b0_1111010001_0011_11,
294            ),
295            (
296                999_999,
297                0b10_100_10011_11110100001001000000_0 << (64 - 31),
298                0b0_11101000010010000001_00111_001_01,
299            ),
300        ] {
301            let mut data = [0_u64];
302            let mut writer = <BufBitWriter<BE, _>>::new(MemWordWriterSlice::new(&mut data));
303            writer.write_omega(value).unwrap();
304            drop(writer);
305            assert_eq!(
306                data[0].to_be(),
307                expected_be,
308                "\nfor value: {}\ngot: {:064b}\nexp: {:064b}\n",
309                value,
310                data[0].to_be(),
311                expected_be,
312            );
313
314            let mut data = [0_u64];
315            let mut writer = <BufBitWriter<LE, _>>::new(MemWordWriterSlice::new(&mut data));
316            writer.write_omega(value).unwrap();
317            drop(writer);
318            assert_eq!(
319                data[0].to_le(),
320                expected_le,
321                "\nfor value: {}\ngot: {:064b}\nexp: {:064b}\n",
322                value,
323                data[0].to_le(),
324                expected_le,
325            );
326        }
327    }
328}