Skip to main content

oxideav_opus/
range_encoder.rs

1//! Range encoder primitives for the Opus codec.
2//!
3//! This module implements the bit-exact range *encoder* described in
4//! RFC 6716 §5.1 (`docs/audio/opus/rfc6716-opus.txt`). It is the exact
5//! counterpart of the [`crate::range_decoder`] module: any sequence of
6//! symbols written here decodes back to the identical sequence through
7//! [`crate::range_decoder::RangeDecoder`], and — per §5.1 — the encoder
8//! `rng` after a symbol matches the decoder `rng` after decoding the
9//! same symbol. The implementation is clean-room: every routine is
10//! transcribed from the prose and equations in RFC 6716 §5.1; no
11//! external library source was consulted.
12//!
13//! The range encoder is the SHARED entropy primitive that both the SILK
14//! and CELT layers of an Opus encoder invoke for every coded symbol.
15//!
16//! The following routines are wired up:
17//!
18//! * Initialization (§5.1).
19//! * [`RangeEncoder::encode`] — the generic `ec_encode(fl, fh, ft)`
20//!   symbol-update path (§5.1.1), with renormalization (§5.1.1.1) and
21//!   carry propagation / output buffering (§5.1.1.2).
22//! * [`RangeEncoder::encode_bin`] for power-of-two `ft = 1<<ftb`
23//!   (§5.1.2.1, `ec_encode_bin`).
24//! * [`RangeEncoder::enc_bit_logp`] for a single binary symbol with
25//!   probability `2**-logp` of a "1" (§5.1.2.2).
26//! * [`RangeEncoder::enc_icdf`] for inverse-CDF table encoding, sharing
27//!   the decoder's `icdf[]` tables verbatim (§5.1.2.3).
28//! * [`RangeEncoder::enc_bits`] for raw bits packed at the end of the
29//!   buffer (§5.1.3).
30//! * [`RangeEncoder::enc_uint`] for uniformly-distributed integers
31//!   (§5.1.4).
32//! * [`RangeEncoder::tell`] / [`RangeEncoder::tell_frac`] for
33//!   whole-bit / 1/8th-bit accounting (§5.1.6), matching the decoder's
34//!   [`crate::range_decoder::RangeDecoder::tell`] value bit-for-bit
35//!   after the same symbols.
36//! * [`RangeEncoder::finish`] — stream finalization (§5.1.5,
37//!   `ec_enc_done`), which selects the terminating code value and lays
38//!   out the range bytes and the trailing raw-bit region.
39
40/// Bit-exact CELT/SILK range encoder state per RFC 6716 §5.1.
41///
42/// The state four-tuple `(val, rng, rem, ext)` from §5.1 is carried
43/// directly: `val` is the low end of the current range, `rng` its size,
44/// `rem` a single buffered non-propagating output byte (or `-1` for
45/// "none yet"), and `ext` a count of pending carry-propagating (`255`)
46/// output bytes. Range-coder bytes accumulate front-to-back in `buf`;
47/// raw bits (§5.1.3) accumulate back-to-front in `end_bytes` /
48/// `end_window` and are appended as the buffer tail at [`Self::finish`].
49#[derive(Debug, Clone)]
50pub struct RangeEncoder {
51    /// Range-coder output bytes, in forward order (index 0 first).
52    buf: Vec<u8>,
53    /// Range size; the renormalization invariant is `rng > 2**23`.
54    rng: u32,
55    /// Low end of the current range (masked to 31 bits at rest).
56    val: u32,
57    /// Buffered non-propagating output byte, `0..=254`, or `-1` for
58    /// "no byte buffered yet" (§5.1.1.2).
59    rem: i32,
60    /// Count of pending carry-propagating (`255`) output bytes
61    /// (§5.1.1.2).
62    ext: u32,
63    /// Partial raw-bit window: the next bit to emit sits in bit 0
64    /// (§5.1.3). Holds fewer than 8 bits at rest.
65    end_window: u32,
66    /// Number of valid bits currently in `end_window` (0..=7 at rest).
67    nend_bits: u32,
68    /// Completed raw-bit bytes. `end_bytes[0]` is the LAST byte of the
69    /// finished stream, `end_bytes[1]` the second-to-last, and so on —
70    /// matching the decoder's back-to-front raw-bit reader (§4.1.4).
71    end_bytes: Vec<u8>,
72    /// Running tally of whole bits the range coder has produced
73    /// (RFC 6716 §5.1.6 / §4.1.6 `nbits_total`).
74    nbits_total: u32,
75    /// Number of raw bits emitted so far, added into the bit-usage
76    /// accounting on top of `nbits_total` (§4.1.6).
77    nbits_raw: u32,
78}
79
80impl Default for RangeEncoder {
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86impl RangeEncoder {
87    /// Renormalization threshold from §5.1.1.1: normalize until
88    /// `rng > 2**23`.
89    const RNG_MIN: u32 = 1 << 23;
90
91    /// Depth of the decoder's forward-read lookahead, in bytes. The
92    /// 31-bit range window (`val < 2**31`) plus the §4.1.1 initialization
93    /// pre-read means the decoder can consume up to one full range window
94    /// (4 bytes) beyond the last committed range byte. Used at
95    /// [`Self::finish`] to size the zero pad that isolates the raw-bit
96    /// tail from the range reader.
97    const RANGE_LOOKAHEAD_BYTES: usize = 4;
98
99    /// Initialize the range encoder per RFC 6716 §5.1.
100    ///
101    /// The state vector is `(val, rng, rem, ext) = (0, 2**31, -1, 0)`.
102    /// `nbits_total` starts at 33 so that [`Self::tell`] reports the
103    /// same value as a freshly-initialized decoder (which reaches
104    /// `nbits_total == 33`, `rng == 2**31`, `tell() == 1` after its
105    /// §4.1.1 initialization normalize).
106    pub fn new() -> Self {
107        Self {
108            buf: Vec::new(),
109            rng: 1 << 31,
110            val: 0,
111            rem: -1,
112            ext: 0,
113            end_window: 0,
114            nend_bits: 0,
115            end_bytes: Vec::new(),
116            // §4.1.6/§5.1.6: matches the decoder's post-init value so
117            // `tell()` agrees symbol-for-symbol.
118            nbits_total: 33,
119            nbits_raw: 0,
120        }
121    }
122
123    /// Whole-bit budget produced so far, per RFC 6716 §5.1.6 / §4.1.6.1.
124    ///
125    /// Equal to `nbits_total - ilog(rng) + nbits_raw`; matches the
126    /// decoder's `tell()` after the same symbols.
127    pub fn tell(&self) -> u32 {
128        let lg = 32 - self.rng.leading_zeros();
129        self.nbits_total
130            .saturating_sub(lg)
131            .saturating_add(self.nbits_raw)
132    }
133
134    /// 1/8th-bit-precision budget produced so far, per RFC 6716 §5.1.6 /
135    /// §4.1.6.2. Matches the decoder's `tell_frac()` after the same
136    /// symbols.
137    pub fn tell_frac(&self) -> u32 {
138        let lg0 = 32 - self.rng.leading_zeros();
139        let mut r_q15 = self.rng >> (lg0 - 16);
140        let mut lg_frac = lg0;
141        for _ in 0..3 {
142            r_q15 = (r_q15 * r_q15) >> 15;
143            let bit = r_q15 >> 16;
144            lg_frac = 2 * lg_frac + bit;
145            if bit == 1 {
146                r_q15 >>= 1;
147            }
148        }
149        self.nbits_total
150            .saturating_mul(8)
151            .saturating_sub(lg_frac)
152            .saturating_add(self.nbits_raw.saturating_mul(8))
153    }
154
155    /// Encode symbol `k` described by the three-tuple `(fl, fh, ft)`,
156    /// per RFC 6716 §5.1.1 (`ec_encode`).
157    ///
158    /// Requires `0 <= fl < fh <= ft` and `1 <= ft <= 2**16`. The §5.1.1
159    /// update narrows the range to the symbol's `[fl, fh)` sub-interval
160    /// of `[0, ft)`. The `fl == 0` branch subtracts `(rng/ft)*(ft - fh)`
161    /// from `rng` so the top symbol absorbs the integer-division
162    /// remainder — the exact mirror of the decoder's §4.1.2 update, which
163    /// is what keeps encoder `rng` equal to decoder `rng`.
164    pub fn encode(&mut self, fl: u32, fh: u32, ft: u32) {
165        debug_assert!(fl < fh && fh <= ft && ft >= 1);
166        let r = self.rng / ft;
167        if fl > 0 {
168            self.val = self.val.wrapping_add(self.rng - r.wrapping_mul(ft - fl));
169            self.rng = r.wrapping_mul(fh - fl);
170        } else {
171            self.rng -= r.wrapping_mul(ft - fh);
172        }
173        self.normalize();
174    }
175
176    /// Encode symbol `k` for a power-of-two total `ft = 1<<ftb`, per
177    /// RFC 6716 §5.1.2.1 (`ec_encode_bin`). Division-free equivalent of
178    /// [`Self::encode`] with `ft = 1<<ftb`.
179    pub fn encode_bin(&mut self, fl: u32, fh: u32, ftb: u32) {
180        let ft = 1u32 << ftb;
181        debug_assert!(fl < fh && fh <= ft);
182        let r = self.rng >> ftb;
183        if fl > 0 {
184            self.val = self.val.wrapping_add(self.rng - r.wrapping_mul(ft - fl));
185            self.rng = r.wrapping_mul(fh - fl);
186        } else {
187            self.rng -= r.wrapping_mul(ft - fh);
188        }
189        self.normalize();
190    }
191
192    /// Encode a single binary symbol whose "1" has probability
193    /// `2**-logp`, per RFC 6716 §5.1.2.2 (`ec_enc_bit_logp`).
194    ///
195    /// Equivalent to `ec_encode` with `(fl, fh, ft)` equal to
196    /// `(0, (1<<logp)-1, 1<<logp)` for a `0` and
197    /// `((1<<logp)-1, 1<<logp, 1<<logp)` for a `1`. Multiplication- and
198    /// division-free.
199    pub fn enc_bit_logp(&mut self, bit: bool, logp: u32) {
200        let r = self.rng >> logp;
201        if bit {
202            // fl = (1<<logp)-1, fh = ft = 1<<logp; fh-fl = 1, ft-fl = 1.
203            self.val = self.val.wrapping_add(self.rng - r);
204            self.rng = r;
205        } else {
206            // fl = 0, fh = (1<<logp)-1, ft = 1<<logp; ft-fh = 1.
207            self.rng -= r;
208        }
209        self.normalize();
210    }
211
212    /// Encode symbol index `k` against an inverse-CDF table, per
213    /// RFC 6716 §5.1.2.3 (`ec_enc_icdf`). Uses the SAME `icdf[]` tables
214    /// as the decoder's [`crate::range_decoder::RangeDecoder::dec_icdf`]:
215    /// `icdf[j]` stores `(1<<ftb) - fh[j]`, terminated by a `0` entry.
216    ///
217    /// Per §5.1.2.3, `fl[k] = (1<<ftb) - icdf[k-1]` (or `0` if `k == 0`),
218    /// `fh[k] = (1<<ftb) - icdf[k]`, `ft = 1<<ftb`. The symbol update
219    /// then depends only on the `icdf[]` differences, so no total is
220    /// needed.
221    pub fn enc_icdf(&mut self, k: usize, icdf: &[u8], ftb: u32) {
222        // A zero-width symbol (fh == fl: a zero-probability PDF cell)
223        // would collapse `rng` to zero and make the stream undecodable;
224        // callers must never encode one.
225        debug_assert!(
226            if k == 0 {
227                (icdf[0] as u32) < (1u32 << ftb)
228            } else {
229                icdf[k - 1] > icdf[k]
230            },
231            "zero-probability icdf cell {k}"
232        );
233        let r = self.rng >> ftb;
234        if k > 0 {
235            let hi = icdf[k - 1] as u32;
236            let lo = icdf[k] as u32;
237            // ft - fl = icdf[k-1]; fh - fl = icdf[k-1] - icdf[k].
238            self.val = self.val.wrapping_add(self.rng - r.wrapping_mul(hi));
239            self.rng = r.wrapping_mul(hi - lo);
240        } else {
241            // fl = 0; ft - fh = icdf[0].
242            self.rng -= r.wrapping_mul(icdf[0] as u32);
243        }
244        self.normalize();
245    }
246
247    /// Encode `bits` raw bits (low bits of `value`), per RFC 6716
248    /// §5.1.3 (`ec_enc_bits`). Raw bits are packed at the END of the
249    /// output buffer, LSB-first, mirroring the decoder's back-to-front
250    /// reader. The first bit emitted here is the one the decoder reads
251    /// first.
252    pub fn enc_bits(&mut self, value: u32, bits: u32) {
253        debug_assert!(bits <= 32);
254        if bits == 0 {
255            return;
256        }
257        let mask: u64 = if bits >= 32 {
258            0xFFFF_FFFF
259        } else {
260            (1u64 << bits) - 1
261        };
262        let mut window = (self.end_window as u64) | ((value as u64 & mask) << self.nend_bits);
263        let mut n = self.nend_bits + bits;
264        while n >= 8 {
265            self.end_bytes.push((window & 0xFF) as u8);
266            window >>= 8;
267            n -= 8;
268        }
269        self.end_window = window as u32;
270        self.nend_bits = n;
271        self.nbits_raw += bits;
272    }
273
274    /// Encode one of `ft` equiprobable values `t` in `0..ft`, per
275    /// RFC 6716 §5.1.4 (`ec_enc_uint`). `ft` may be as large as
276    /// `2**32 - 1`. Values `ft <= 1` degenerate to a no-op (matching the
277    /// decoder returning the constant `0`).
278    pub fn enc_uint(&mut self, t: u32, ft: u32) {
279        debug_assert!(ft >= 1 && t < ft.max(1));
280        if ft <= 1 {
281            return;
282        }
283        // ftb = ilog(ft - 1): bits needed to store ft-1.
284        let ftb = 32 - (ft - 1).leading_zeros();
285        if ftb <= 8 {
286            self.encode(t, t + 1, ft);
287        } else {
288            let split = ftb - 8;
289            let hi = t >> split;
290            let top = ((ft - 1) >> split) + 1;
291            self.encode(hi, hi + 1, top);
292            self.enc_bits(t & ((1u32 << split) - 1), split);
293        }
294    }
295
296    /// Finalize the stream (§5.1.5, `ec_enc_done`) and return the packed
297    /// output bytes.
298    ///
299    /// Chooses the terminating code value `end` inside `[val, val + rng)`
300    /// with the most trailing zero bits `b` such that
301    /// `end + (1<<b) - 1` is still in the interval, flushes it through
302    /// the carry buffer, then appends the raw-bit region (§5.1.3) as a
303    /// disjoint tail, separated from the range data by a zero pad so the
304    /// decoder's forward range reader can zero-extend past the committed
305    /// bytes without consuming a raw byte.
306    pub fn finish(mut self) -> Vec<u8> {
307        let val = self.val;
308        let rng = self.rng;
309        // Default b = 0: end = val is always in the interval.
310        let mut end = val;
311        // Pick the largest b in 1..=31 whose rounded-up multiple of 2**b
312        // keeps `end + (2**b - 1)` inside `[val, val + rng)`.
313        for b in (1..=31u32).rev() {
314            let m = (1u64 << b) - 1;
315            let end_b = ((val as u64) + m) & !m;
316            if end_b + m < (val as u64) + (rng as u64) {
317                end = end_b as u32;
318                break;
319            }
320        }
321        // Flush `end` through the carry buffer, 9 bits (top of `end`) at
322        // a time.
323        while end != 0 {
324            self.carry_out(end >> 23);
325            end = (end << 8) & 0x7FFF_FFFF;
326        }
327        // Flush the buffered byte to the output (§5.1.5): if `rem` holds
328        // a real non-zero byte, or a carry run is pending, emit 9 zero
329        // bits.
330        if (self.rem != -1 && self.rem != 0) || self.ext > 0 {
331            self.carry_out(0);
332        }
333        // Append the raw bits (§5.1.3) as a disjoint tail. The §5.1.5
334        // `end` finalization commits the range value to the front bytes
335        // and relies on the decoder reading TRAILING ZEROS beyond them
336        // (the chosen `end` maximizes trailing zero bits). The decoder's
337        // forward range reader runs up to `RANGE_LOOKAHEAD_BYTES` bytes
338        // ahead of the committed data — its §4.1.1 initialization alone
339        // pre-reads a full range window before any symbol — so those
340        // lookahead positions MUST read as zero. When raw bits are
341        // present they occupy the buffer tail; a zero pad of one full
342        // range window separates them from the range data so the range
343        // reader's lookahead never consumes a raw byte. The raw region
344        // is laid out so the decoder's back-to-front raw reader (§4.1.4)
345        // sees `end_bytes[0]` (the first-emitted 8 raw bits) as the very
346        // last byte, then earlier full bytes, then the partial window
347        // byte (the last-emitted, fewer-than-8 bits) closest to the pad.
348        let mut out = self.buf;
349        let have_raw = self.nend_bits > 0 || !self.end_bytes.is_empty();
350        if have_raw {
351            out.resize(out.len() + Self::RANGE_LOOKAHEAD_BYTES, 0);
352            if self.nend_bits > 0 {
353                out.push(self.end_window as u8);
354            }
355            for &b in self.end_bytes.iter().rev() {
356                out.push(b);
357            }
358        }
359        out
360    }
361
362    // ----- internal helpers -----
363
364    /// `ec_enc_normalize` per RFC 6716 §5.1.1.1: while `rng <= 2**23`,
365    /// spill the top 9 bits of `val` to the carry buffer and shift both
366    /// `val` and `rng` left by 8.
367    fn normalize(&mut self) {
368        while self.rng <= Self::RNG_MIN {
369            self.carry_out(self.val >> 23);
370            self.val = (self.val << 8) & 0x7FFF_FFFF;
371            self.rng <<= 8;
372            self.nbits_total = self.nbits_total.saturating_add(8);
373        }
374    }
375
376    /// `ec_enc_carry_out` per RFC 6716 §5.1.1.2. Takes a 9-bit value
377    /// `c` (8 data bits + 1 carry bit).
378    fn carry_out(&mut self, c: u32) {
379        if c == 0xFF {
380            // All-ones data with no carry: defer as a potential carry
381            // run.
382            self.ext += 1;
383            return;
384        }
385        let b = c >> 8; // carry bit, 0 or 1
386        if self.rem >= 0 {
387            self.buf.push((self.rem as u32 + b) as u8);
388        }
389        if self.ext > 0 {
390            // Resolve the deferred 255-run: 0x00 if the carry
391            // propagates, 0xFF otherwise.
392            let fill = if b != 0 { 0x00 } else { 0xFF };
393            for _ in 0..self.ext {
394                self.buf.push(fill);
395            }
396            self.ext = 0;
397        }
398        self.rem = (c & 0xFF) as i32;
399    }
400}
401
402#[cfg(test)]
403mod tests {
404    use super::*;
405    use crate::range_decoder::RangeDecoder;
406
407    /// A tiny deterministic PRNG so the fuzz roundtrips need no external
408    /// crate. Not cryptographic; only used to drive symbol choices.
409    struct Lcg(u64);
410    impl Lcg {
411        fn next_u32(&mut self) -> u32 {
412            // Numerical Recipes LCG constants.
413            self.0 = self
414                .0
415                .wrapping_mul(6364136223846793005)
416                .wrapping_add(1442695040888963407);
417            (self.0 >> 32) as u32
418        }
419        fn below(&mut self, n: u32) -> u32 {
420            if n == 0 {
421                0
422            } else {
423                self.next_u32() % n
424            }
425        }
426    }
427
428    /// §5.1: a freshly-initialized encoder reports the same `tell()` as a
429    /// freshly-initialized decoder: 1 bit.
430    #[test]
431    fn init_tell_is_one() {
432        let enc = RangeEncoder::new();
433        let dec = RangeDecoder::new(&[]);
434        assert_eq!(enc.tell(), 1);
435        assert_eq!(enc.tell(), dec.tell());
436        // tell_frac reports 1/8th bits: 1 whole bit == 8 eighths.
437        assert_eq!(enc.tell_frac(), 8);
438        assert_eq!(enc.tell_frac(), dec.tell_frac());
439    }
440
441    /// §5.1.2.3 / §4.1.3.3: encode a sequence of icdf symbols and decode
442    /// them back — the decoded indices must match exactly.
443    #[test]
444    fn roundtrip_icdf() {
445        // A valid inverse-CDF table (strictly decreasing, terminated by
446        // 0) with ftb = 8, i.e. ft = 256.
447        let icdf: [u8; 4] = [200, 120, 40, 0];
448        let ftb = 8;
449        let symbols = [0usize, 3, 1, 2, 2, 0, 1, 3, 3, 0, 2, 1];
450        let mut enc = RangeEncoder::new();
451        for &k in &symbols {
452            enc.enc_icdf(k, &icdf, ftb);
453        }
454        let bytes = enc.finish();
455        let mut dec = RangeDecoder::new(&bytes);
456        for &k in &symbols {
457            assert_eq!(dec.dec_icdf(&icdf, ftb) as usize, k);
458        }
459        assert!(!dec.has_error());
460    }
461
462    /// §5.1.2.2 / §4.1.3.2: bit_logp roundtrip.
463    #[test]
464    fn roundtrip_bit_logp() {
465        let bits = [
466            true, false, false, true, true, true, false, true, false, false,
467        ];
468        let logps = [1u32, 2, 4, 8, 3, 6, 2, 1, 5, 7];
469        let mut enc = RangeEncoder::new();
470        for (i, &bit) in bits.iter().enumerate() {
471            enc.enc_bit_logp(bit, logps[i]);
472        }
473        let bytes = enc.finish();
474        let mut dec = RangeDecoder::new(&bytes);
475        for (i, &bit) in bits.iter().enumerate() {
476            assert_eq!(dec.dec_bit_logp(logps[i]) == 1, bit);
477        }
478        assert!(!dec.has_error());
479    }
480
481    /// §5.1.3 / §4.1.4: full-width 32-bit raw reads and writes are
482    /// defined (a round-382 fuzz find: the decoder's u32 raw-bit window
483    /// overflowed its shifts on a 32-bit read).
484    #[test]
485    fn roundtrip_raw_bits_32_wide() {
486        let vals: [u32; 5] = [0xFFFF_FFFF, 0, 0x8000_0001, 0xDEAD_BEEF, 0x0BAD_F00D];
487        let mut enc = RangeEncoder::new();
488        // A 3-bit write first so the 32-bit reads straddle byte seams.
489        enc.enc_bits(0b101, 3);
490        for &v in &vals {
491            enc.enc_bits(v, 32);
492        }
493        let bytes = enc.finish();
494        let mut dec = RangeDecoder::new(&bytes);
495        assert_eq!(dec.dec_bits(3), 0b101);
496        for &v in &vals {
497            assert_eq!(dec.dec_bits(32), v);
498        }
499        assert!(!dec.has_error());
500    }
501
502    /// §5.1.3 / §4.1.4: raw-bit roundtrip, mixed widths.
503    #[test]
504    fn roundtrip_raw_bits() {
505        let vals: [(u32, u32); 6] = [
506            (1, 1),
507            (0b101, 3),
508            (0xAB, 8),
509            (0x1234, 16),
510            (0, 4),
511            (0x7F, 7),
512        ];
513        let mut enc = RangeEncoder::new();
514        for &(v, b) in &vals {
515            enc.enc_bits(v, b);
516        }
517        let bytes = enc.finish();
518        let mut dec = RangeDecoder::new(&bytes);
519        for &(v, b) in &vals {
520            assert_eq!(dec.dec_bits(b), v);
521        }
522        assert!(!dec.has_error());
523    }
524
525    /// §5.1.4 / §4.1.5: uint roundtrip across the small (ftb<=8) and
526    /// large (ftb>8, raw-bit tail) paths, interleaved.
527    #[test]
528    fn roundtrip_uint() {
529        let cases: [(u32, u32); 8] = [
530            (0, 1),
531            (3, 4),
532            (200, 256),
533            (1000, 1024),
534            (65535, 65536),
535            (7, 100),
536            (123456, 1_000_000),
537            (0, 300),
538        ];
539        let mut enc = RangeEncoder::new();
540        for &(t, ft) in &cases {
541            enc.enc_uint(t, ft);
542        }
543        let bytes = enc.finish();
544        let mut dec = RangeDecoder::new(&bytes);
545        for &(t, ft) in &cases {
546            assert_eq!(dec.dec_uint(ft).unwrap(), t);
547        }
548        assert!(!dec.has_error());
549    }
550
551    /// §5.1.1: generic ec_encode roundtrip using a small uniform model,
552    /// decoded via the split ec_decode / ec_dec_update path. Encoder and
553    /// decoder `rng` are compared symbol-for-symbol via the §5.1.6
554    /// `tell()` cross-check (`tell` is a pure function of `rng` and the
555    /// symbol-count-driven `nbits_total`, which both sides track
556    /// identically).
557    #[test]
558    fn roundtrip_ec_encode_uniform() {
559        let ft = 11u32; // arbitrary non-power-of-two
560        let symbols = [0u32, 10, 5, 3, 7, 1, 9, 2, 8, 4, 6, 0, 10, 5];
561        let mut enc = RangeEncoder::new();
562        let mut enc_tell = Vec::new();
563        for &k in &symbols {
564            enc.encode(k, k + 1, ft);
565            enc_tell.push(enc.tell());
566        }
567        let bytes = enc.finish();
568        let mut dec = RangeDecoder::new(&bytes);
569        for (i, &k) in symbols.iter().enumerate() {
570            let fs = dec.ec_decode(ft);
571            // Uniform model: fs directly identifies the symbol here.
572            assert_eq!(fs, k);
573            dec.ec_dec_update(fs, fs + 1, ft);
574            assert_eq!(dec.tell(), enc_tell[i], "tell (rng) desync at symbol {i}");
575        }
576        assert!(!dec.has_error());
577    }
578
579    /// §5.1.6: `tell()` / `tell_frac()` track the decoder bit-for-bit.
580    #[test]
581    fn tell_matches_decoder() {
582        let icdf: [u8; 3] = [170, 50, 0];
583        let symbols = [0usize, 2, 1, 1, 0, 2, 2, 1, 0];
584        let mut enc = RangeEncoder::new();
585        let mut enc_tell = Vec::new();
586        let mut enc_tell_frac = Vec::new();
587        for &k in &symbols {
588            enc.enc_icdf(k, &icdf, 8);
589            enc_tell.push(enc.tell());
590            enc_tell_frac.push(enc.tell_frac());
591        }
592        let bytes = enc.finish();
593        let mut dec = RangeDecoder::new(&bytes);
594        for (i, &k) in symbols.iter().enumerate() {
595            assert_eq!(dec.dec_icdf(&icdf, 8) as usize, k);
596            assert_eq!(dec.tell(), enc_tell[i], "tell desync at {i}");
597            assert_eq!(dec.tell_frac(), enc_tell_frac[i], "tell_frac desync at {i}");
598        }
599    }
600
601    /// Randomized fuzz: mix icdf / bit_logp / uint / raw-bit symbols and
602    /// require the decoder to recover every one across many seeds.
603    #[test]
604    fn fuzz_mixed_roundtrip() {
605        let icdf: [u8; 5] = [220, 150, 90, 30, 0];
606        for seed in 0..5000u64 {
607            let mut rng = Lcg(seed.wrapping_mul(0x9E3779B97F4A7C15).wrapping_add(1));
608            let count = 8 + rng.below(60);
609            let mut ops: Vec<u8> = Vec::new();
610            let mut icdf_syms: Vec<usize> = Vec::new();
611            let mut logp_bits: Vec<(bool, u32)> = Vec::new();
612            let mut uint_vals: Vec<(u32, u32)> = Vec::new();
613            let mut raw_vals: Vec<(u32, u32)> = Vec::new();
614
615            let mut enc = RangeEncoder::new();
616            for _ in 0..count {
617                match rng.below(4) {
618                    0 => {
619                        let k = rng.below(4) as usize; // 0..=3 valid symbols
620                        enc.enc_icdf(k, &icdf, 8);
621                        ops.push(0);
622                        icdf_syms.push(k);
623                    }
624                    1 => {
625                        let bit = rng.below(2) == 1;
626                        let logp = 1 + rng.below(8);
627                        enc.enc_bit_logp(bit, logp);
628                        ops.push(1);
629                        logp_bits.push((bit, logp));
630                    }
631                    2 => {
632                        let ft = 2 + rng.below(1 << 20);
633                        let t = rng.below(ft);
634                        enc.enc_uint(t, ft);
635                        ops.push(2);
636                        uint_vals.push((t, ft));
637                    }
638                    _ => {
639                        let b = 1 + rng.below(16);
640                        let v = rng.next_u32() & if b >= 32 { !0 } else { (1u32 << b) - 1 };
641                        enc.enc_bits(v, b);
642                        ops.push(3);
643                        raw_vals.push((v, b));
644                    }
645                }
646            }
647            let bytes = enc.finish();
648            let mut dec = RangeDecoder::new(&bytes);
649            let (mut ii, mut li, mut ui, mut ri) = (0usize, 0usize, 0usize, 0usize);
650            for &op in &ops {
651                match op {
652                    0 => {
653                        assert_eq!(
654                            dec.dec_icdf(&icdf, 8) as usize,
655                            icdf_syms[ii],
656                            "seed {seed}"
657                        );
658                        ii += 1;
659                    }
660                    1 => {
661                        let (bit, logp) = logp_bits[li];
662                        assert_eq!(dec.dec_bit_logp(logp) == 1, bit, "seed {seed}");
663                        li += 1;
664                    }
665                    2 => {
666                        let (t, ft) = uint_vals[ui];
667                        assert_eq!(dec.dec_uint(ft).unwrap(), t, "seed {seed}");
668                        ui += 1;
669                    }
670                    _ => {
671                        let (v, b) = raw_vals[ri];
672                        assert_eq!(dec.dec_bits(b), v, "seed {seed}");
673                        ri += 1;
674                    }
675                }
676            }
677            assert!(!dec.has_error(), "seed {seed} latched error");
678        }
679    }
680}