Skip to main content

gamut_bitstream/
symbol.rs

1//! AV1 multi-symbol arithmetic (range) encoder (AV1 §8.2, encoder side).
2//!
3//! The AV1 spec only defines the *decoder* (§8.2 "Parsing process for symbol decoder"). This is
4//! the matching encoder: it produces a byte stream that the §8.2 decoder maps back to the symbols
5//! that were encoded. The arithmetic mirrors the well-known `od_ec` range coder (the same one in
6//! libaom / rav1e), which is purpose-built for this decoder.
7//!
8//! CDF convention (matches §8.2.6): a CDF for `N` symbols is a slice of `N` cumulative values in
9//! `[0, 32768]`, strictly non-decreasing, with `cdf[N - 1] == 32768`. `cdf[i]` is the cumulative
10//! probability (× 32768) of symbols `0..=i`. Two coding modes are provided:
11//! [`SymbolEncoder::encode_symbol`] codes against a *static* CDF (`disable_cdf_update = 1`), while
12//! [`SymbolEncoder::encode_symbol_adapt`] applies the §8.2.6 adaptation after each symbol
13//! (`disable_cdf_update = 0`), nudging the CDF toward the just-coded symbol. The adaptation counter
14//! the spec keeps as a trailing `cdf[N]` element is carried alongside as a separate `&mut u16`; a
15//! decoder must apply the identical update after each symbol to stay in lockstep.
16//!
17//! The hermetic `SymbolDecoder` in this module's tests is a direct transcription of §8.2 and is
18//! the oracle that proves the encoder correct without any external decoder.
19
20/// Number of bits to reduce CDF precision during arithmetic coding (AV1 `EC_PROB_SHIFT`, §3).
21const EC_PROB_SHIFT: u32 = 6;
22/// Minimum probability assigned to each symbol during arithmetic coding (AV1 `EC_MIN_PROB`, §3).
23const EC_MIN_PROB: u32 = 4;
24/// CDFs are expressed on a 1 << 15 scale (AV1 §8.2.6: `cdf[N - 1] == 1 << 15`).
25const CDF_PROB_TOP: u32 = 1 << 15;
26
27/// Encoder for the AV1 symbol (range) coder.
28///
29/// Feed symbols with [`SymbolEncoder::encode_symbol`] (CDF-coded) and equiprobable bits with
30/// [`SymbolEncoder::encode_literal`], then call [`SymbolEncoder::finish`] to flush and obtain the
31/// coded bytes. Those bytes are exactly what a decoder consumes via `init_symbol(sz)` (AV1 §8.2.2)
32/// where `sz` is the returned length.
33#[derive(Debug, Clone)]
34pub struct SymbolEncoder {
35    /// Low end of the coding interval, kept wider than 16 bits so carries accumulate losslessly
36    /// (resolved in [`SymbolEncoder::finish`]).
37    low: u64,
38    /// Current range, renormalised into `[1 << 15, 1 << 16)`.
39    rng: u32,
40    /// Bit counter; starts at `-9` so the first carry/byte crosses zero at the right moment.
41    cnt: i32,
42    /// Output bytes, each held as a `u16` so a pending carry lives in bit 8 until `finish`.
43    precarry: Vec<u16>,
44}
45
46impl Default for SymbolEncoder {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl SymbolEncoder {
53    /// Creates an encoder with the initial range state of AV1's symbol coder.
54    #[must_use]
55    pub fn new() -> Self {
56        Self {
57            low: 0,
58            rng: CDF_PROB_TOP,
59            cnt: -9,
60            precarry: Vec::new(),
61        }
62    }
63
64    /// Encodes `symbol` against a static cumulative `cdf` (`cdf.len()` symbols, `cdf[last] == 32768`).
65    ///
66    /// # Panics
67    ///
68    /// Debug builds assert `symbol < cdf.len()` and the CDF normalisation invariants.
69    pub fn encode_symbol(&mut self, symbol: usize, cdf: &[u16]) {
70        let nsyms = cdf.len();
71        debug_assert!(symbol < nsyms);
72        debug_assert_eq!(u32::from(cdf[nsyms - 1]), CDF_PROB_TOP);
73        // `f(j) = (1 << 15) - cdf[j]` is the inverse-CDF term used by the §8.2.6 decoder; `fl`/`fh`
74        // bracket the chosen symbol's sub-interval. For symbol 0, the upper bracket is the full top.
75        let fl = if symbol > 0 {
76            CDF_PROB_TOP - u32::from(cdf[symbol - 1])
77        } else {
78            CDF_PROB_TOP
79        };
80        let fh = CDF_PROB_TOP - u32::from(cdf[symbol]);
81        self.encode_q15(fl, fh, symbol as u32, nsyms as u32);
82    }
83
84    /// Encodes `symbol` against an *adapting* cumulative `cdf`, then applies the §8.2.6 CDF
85    /// adaptation in place (`disable_cdf_update = 0`). `count` is the spec's trailing `cdf[N]`
86    /// adaptation counter (start it at 0 for a freshly initialised context); it is bumped here, up to
87    /// a maximum of 32. A decoder must apply the identical §8.2.6 update (decode the symbol, then the
88    /// same adaptation) with the same `count` so its CDF tracks the encoder's exactly.
89    ///
90    /// # Panics
91    ///
92    /// Debug builds assert `symbol < cdf.len()` and the CDF normalisation invariants.
93    pub fn encode_symbol_adapt(&mut self, symbol: usize, cdf: &mut [u16], count: &mut u16) {
94        self.encode_symbol(symbol, cdf);
95        update_cdf(cdf, symbol, count);
96    }
97
98    /// Encodes the low `n` bits of `value` as equiprobable bits, most-significant bit first.
99    ///
100    /// This is the inverse of the decoder's `read_literal(n)` (AV1 §8.2.5), which itself calls
101    /// `read_bool()` (§8.2.3) with the fixed CDF `{1 << 14, 1 << 15}`.
102    pub fn encode_literal(&mut self, value: u32, n: u32) {
103        const BOOL_CDF: [u16; 2] = [1 << 14, 1 << 15];
104        for i in (0..n).rev() {
105            self.encode_symbol(((value >> i) & 1) as usize, &BOOL_CDF);
106        }
107    }
108
109    /// Core interval update for one symbol; `fl`/`fh` are the inverse-CDF brackets, `s` the symbol,
110    /// `nsyms` the alphabet size. Mirrors `od_ec_encode_q15`, which inverts the §8.2.6 boundaries.
111    fn encode_q15(&mut self, fl: u32, fh: u32, s: u32, nsyms: u32) {
112        let mut low = self.low;
113        let mut r = self.rng;
114        debug_assert!(r >= CDF_PROB_TOP);
115        let n = nsyms - 1;
116        if fl < CDF_PROB_TOP {
117            let u = (((r >> 8) * (fl >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT))
118                + EC_MIN_PROB * (n - (s - 1));
119            let v =
120                (((r >> 8) * (fh >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT)) + EC_MIN_PROB * (n - s);
121            debug_assert!(u <= r && v < u);
122            low += u64::from(r - u);
123            r = u - v;
124        } else {
125            // Symbol 0: the interval reaches the top, so `low` is unchanged.
126            let v =
127                (((r >> 8) * (fh >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT)) + EC_MIN_PROB * (n - s);
128            debug_assert!(v < r);
129            r -= v;
130        }
131        self.normalize(low, r);
132    }
133
134    /// Renormalises `(low, rng)` back into `[1 << 15, 1 << 16)`, emitting completed bytes into
135    /// `precarry`. Mirrors `od_ec_enc_normalize`.
136    fn normalize(&mut self, mut low: u64, rng: u32) {
137        // `d` = number of left shifts to bring `rng` to 16 bits. `rng` is in `[1, 0xFFFF]` here.
138        let d = rng.leading_zeros() - 16;
139        let mut c = self.cnt;
140        let mut s = c + d as i32;
141        if s >= 0 {
142            c += 16;
143            let mut m = (1u64 << c) - 1;
144            if s >= 8 {
145                self.precarry.push((low >> c) as u16);
146                low &= m;
147                c -= 8;
148                m = (1u64 << c) - 1;
149            }
150            self.precarry.push((low >> c) as u16);
151            s = c + d as i32 - 24;
152            low &= m;
153        }
154        self.low = low << d;
155        self.rng = rng << d;
156        self.cnt = s;
157    }
158
159    /// Flushes the coder and returns the coded bytes. Mirrors `od_ec_enc_done`: it emits the
160    /// minimum number of bits that decode correctly regardless of trailing padding, then resolves
161    /// the buffered carries into a big-endian byte stream.
162    #[must_use]
163    pub fn finish(mut self) -> Vec<u8> {
164        let l = self.low;
165        let mut c = self.cnt;
166        let mut s = 10 + c;
167        let m: u64 = 0x3FFF;
168        let mut e = ((l + m) & !m) | (m + 1);
169        if s > 0 {
170            let mut n = (1u64 << (c + 16)) - 1;
171            loop {
172                self.precarry.push((e >> (c + 16)) as u16);
173                e &= n;
174                s -= 8;
175                c -= 8;
176                n >>= 8;
177                if s <= 0 {
178                    break;
179                }
180            }
181        }
182        // Resolve carries from least- to most-significant byte (big-endian output).
183        let mut out = vec![0u8; self.precarry.len()];
184        let mut carry: u32 = 0;
185        for i in (0..self.precarry.len()).rev() {
186            let val = u32::from(self.precarry[i]) + carry;
187            out[i] = (val & 0xff) as u8;
188            carry = val >> 8;
189        }
190        out
191    }
192}
193
194/// Adapts a cumulative `cdf` toward the just-coded `symbol` and bumps the adaptation `count`, per
195/// AV1 §8.2.6 (`disable_cdf_update = 0`). `cdf` is the gamut `N`-entry cumulative form
196/// (`cdf[N - 1] == 32768`, which is never touched); `count` is the spec's trailing `cdf[N]`
197/// counter, capped at 32 — a higher count slows adaptation. The encoder and a conformant decoder
198/// invoke this identically after coding each symbol, so their CDFs evolve in lockstep.
199fn update_cdf(cdf: &mut [u16], symbol: usize, count: &mut u16) {
200    let n = cdf.len();
201    // rate = 3 + (count > 15) + (count > 31) + Min(FloorLog2(N), 2).
202    let rate = 3
203        + u32::from(*count > 15)
204        + u32::from(*count > 31)
205        + (31 - (n as u32).leading_zeros()).min(2);
206    // §8.2.6 with the loop's `tmp` 0/32768 split made explicit. The fixed top entry
207    // (cdf[N - 1] == 32768) is never updated; entries before `symbol` move toward 0 and those from
208    // `symbol` up to N - 2 move toward 32768, each by `delta >> rate`.
209    let (_top, body) = cdf.split_last_mut().expect("a CDF has at least one entry");
210    for v in &mut body[..symbol] {
211        *v -= *v >> rate;
212    }
213    for v in &mut body[symbol..] {
214        *v += ((1u16 << 15) - *v) >> rate;
215    }
216    if *count < 32 {
217        *count += 1;
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224
225    /// Direct transcription of the AV1 §8.2 symbol decoder — the hermetic oracle for the encoder.
226    struct SymbolDecoder<'a> {
227        data: &'a [u8],
228        bit_pos: usize,
229        value: u32,
230        range: u32,
231        max_bits: i64,
232    }
233
234    impl<'a> SymbolDecoder<'a> {
235        /// `f(n)` parsing process (AV1 §8.1): MSB-first, zero-padded past the end of `data`.
236        fn read_f(&mut self, n: u32) -> u32 {
237            let mut x = 0u32;
238            for _ in 0..n {
239                let idx = self.bit_pos >> 3;
240                let bit = if idx < self.data.len() {
241                    (self.data[idx] >> (7 - (self.bit_pos & 7))) & 1
242                } else {
243                    0
244                };
245                x = (x << 1) | u32::from(bit);
246                self.bit_pos += 1;
247            }
248            x
249        }
250
251        /// `init_symbol(sz)` (AV1 §8.2.2).
252        fn new(data: &'a [u8]) -> Self {
253            let sz = data.len();
254            let mut d = Self {
255                data,
256                bit_pos: 0,
257                value: 0,
258                range: 1 << 15,
259                max_bits: 8 * sz as i64 - 15,
260            };
261            let num_bits = core::cmp::min(sz * 8, 15) as u32;
262            let buf = d.read_f(num_bits);
263            let padded = buf << (15 - num_bits);
264            d.value = ((1 << 15) - 1) ^ padded;
265            d
266        }
267
268        /// `read_symbol(cdf)` (AV1 §8.2.6); `cdf` is the cumulative form (no trailing count needed
269        /// because adaptation is disabled).
270        fn read_symbol(&mut self, cdf: &[u16]) -> usize {
271            let n = cdf.len() as u32;
272            let mut cur = self.range;
273            let mut symbol: i64 = -1;
274            let mut prev;
275            loop {
276                symbol += 1;
277                prev = cur;
278                let f = (1u32 << 15) - u32::from(cdf[symbol as usize]);
279                cur = ((self.range >> 8) * (f >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT);
280                cur += EC_MIN_PROB * (n - symbol as u32 - 1);
281                if self.value >= cur {
282                    break;
283                }
284            }
285            self.range = prev - cur;
286            self.value -= cur;
287            // Renormalisation (AV1 §8.2.6 ordered steps).
288            let bits = 15 - (31 - self.range.leading_zeros());
289            self.range <<= bits;
290            let num_bits = core::cmp::min(i64::from(bits), self.max_bits.max(0)) as u32;
291            let new_data = self.read_f(num_bits);
292            let padded = new_data << (bits - num_bits);
293            self.value = padded ^ (((self.value + 1) << bits) - 1);
294            self.max_bits -= i64::from(bits);
295            symbol as usize
296        }
297
298        /// `read_symbol(cdf)` with the §8.2.6 adaptation enabled — the decoder mirror of
299        /// [`SymbolEncoder::encode_symbol_adapt`]. Decodes against the current CDF, then applies the
300        /// same [`update_cdf`] so the oracle's CDF tracks the encoder's.
301        fn read_symbol_adapt(&mut self, cdf: &mut [u16], count: &mut u16) -> usize {
302            let s = self.read_symbol(cdf);
303            update_cdf(cdf, s, count);
304            s
305        }
306
307        fn read_literal(&mut self, n: u32) -> u32 {
308            const BOOL_CDF: [u16; 2] = [1 << 14, 1 << 15];
309            let mut x = 0;
310            for _ in 0..n {
311                x = (x << 1) | self.read_symbol(&BOOL_CDF) as u32;
312            }
313            x
314        }
315    }
316
317    /// Small deterministic LCG so tests are reproducible without `rand`.
318    struct Lcg(u64);
319    impl Lcg {
320        fn next_u32(&mut self) -> u32 {
321            self.0 = self
322                .0
323                .wrapping_mul(6364136223846793005)
324                .wrapping_add(1442695040888963407);
325            (self.0 >> 32) as u32
326        }
327        fn below(&mut self, bound: u32) -> u32 {
328            self.next_u32() % bound
329        }
330    }
331
332    /// Builds a random strictly-increasing cumulative CDF for `nsyms` symbols, `cdf[last] = 32768`.
333    fn random_cdf(rng: &mut Lcg, nsyms: usize) -> Vec<u16> {
334        // Pick `nsyms - 1` distinct breakpoints in 1..32768, sorted, then append 32768.
335        let mut points = Vec::new();
336        while points.len() < nsyms - 1 {
337            let p = 1 + rng.below(32767) as u16;
338            if !points.contains(&p) {
339                points.push(p);
340            }
341        }
342        points.sort_unstable();
343        points.push(32768);
344        points
345    }
346
347    #[test]
348    fn empty_stream_roundtrips() {
349        let enc = SymbolEncoder::new();
350        let bytes = enc.finish();
351        // Nothing to decode; just ensure init does not panic.
352        let _ = SymbolDecoder::new(&bytes);
353    }
354
355    #[test]
356    fn single_symbol_streams_roundtrip() {
357        // Exhaustively exercise small alphabets with a skewed CDF and every symbol value.
358        for nsyms in 2..=12usize {
359            let mut cdf: Vec<u16> = (1..nsyms).map(|i| (i * 32768 / nsyms) as u16).collect();
360            cdf.push(32768);
361            for s in 0..nsyms {
362                let mut enc = SymbolEncoder::new();
363                enc.encode_symbol(s, &cdf);
364                let bytes = enc.finish();
365                let mut dec = SymbolDecoder::new(&bytes);
366                assert_eq!(dec.read_symbol(&cdf), s, "nsyms={nsyms} s={s}");
367            }
368        }
369    }
370
371    #[test]
372    fn long_random_symbol_stream_roundtrips() {
373        let mut rng = Lcg(0x1234_5678_9abc_def0);
374        // Pre-generate a mix of CDFs of varying sizes.
375        let cdfs: Vec<Vec<u16>> = (2..=14).map(|n| random_cdf(&mut rng, n)).collect();
376        let mut events = Vec::new();
377        let mut enc = SymbolEncoder::new();
378        for _ in 0..20_000 {
379            let cdf = &cdfs[rng.below(cdfs.len() as u32) as usize];
380            let s = rng.below(cdf.len() as u32) as usize;
381            enc.encode_symbol(s, cdf);
382            events.push((s, cdf.clone()));
383        }
384        let bytes = enc.finish();
385        let mut dec = SymbolDecoder::new(&bytes);
386        for (i, (s, cdf)) in events.iter().enumerate() {
387            assert_eq!(dec.read_symbol(cdf), *s, "event {i}");
388        }
389    }
390
391    #[test]
392    fn literals_roundtrip() {
393        let mut rng = Lcg(0xdead_beef_0bad_f00d);
394        let mut enc = SymbolEncoder::new();
395        let mut events = Vec::new();
396        for _ in 0..5000 {
397            let n = 1 + rng.below(16);
398            let v = rng.next_u32() & ((1u32 << n) - 1);
399            enc.encode_literal(v, n);
400            events.push((v, n));
401        }
402        let bytes = enc.finish();
403        let mut dec = SymbolDecoder::new(&bytes);
404        for (v, n) in events {
405            assert_eq!(dec.read_literal(n), v);
406        }
407    }
408
409    #[test]
410    fn mixed_symbols_and_literals_roundtrip() {
411        let mut rng = Lcg(0x0f0f_0f0f_1234_9999);
412        let cdf = random_cdf(&mut rng, 8);
413        let mut enc = SymbolEncoder::new();
414        let mut events: Vec<(bool, u32)> = Vec::new(); // (is_literal, payload)
415        for _ in 0..8000 {
416            if rng.next_u32() & 1 == 0 {
417                let s = rng.below(cdf.len() as u32);
418                enc.encode_symbol(s as usize, &cdf);
419                events.push((false, s));
420            } else {
421                let v = rng.next_u32() & 0xff;
422                enc.encode_literal(v, 8);
423                events.push((true, v));
424            }
425        }
426        let bytes = enc.finish();
427        let mut dec = SymbolDecoder::new(&bytes);
428        for (is_lit, payload) in events {
429            if is_lit {
430                assert_eq!(dec.read_literal(8), payload);
431            } else {
432                assert_eq!(dec.read_symbol(&cdf) as u32, payload);
433            }
434        }
435    }
436
437    #[test]
438    fn update_cdf_matches_spec_formula() {
439        // Hand-computed from AV1 §8.2.6: rate = 3 + (count > 15) + (count > 31) + Min(FloorLog2(N), 2),
440        // then each entry before `symbol` moves toward 0 and each from `symbol` on moves toward 32768
441        // by `delta >> rate`. The round-trip tests below cannot pin this — the encoder and decoder
442        // adapt in lockstep even with a wrong-but-symmetric formula — so the exact values are checked
443        // here directly.
444        fn upd(cdf: &[u16], symbol: usize, count: u16) -> (Vec<u16>, u16) {
445            let mut c = cdf.to_vec();
446            let mut n = count;
447            update_cdf(&mut c, symbol, &mut n);
448            (c, n)
449        }
450        // N = 2 (FloorLog2 = 1). The count thresholds 15 and 31 each step the rate.
451        assert_eq!(upd(&[16384, 32768], 0, 0), (vec![17408, 32768], 1)); // rate 4: +(16384 >> 4)
452        assert_eq!(upd(&[16384, 32768], 1, 0), (vec![15360, 32768], 1)); // rate 4: -(16384 >> 4)
453        assert_eq!(upd(&[16384, 32768], 0, 15), (vec![17408, 32768], 16)); // 15 > 15 false ⇒ rate 4
454        assert_eq!(upd(&[16384, 32768], 0, 16), (vec![16896, 32768], 17)); // 16 > 15 true  ⇒ rate 5
455        assert_eq!(upd(&[16384, 32768], 0, 31), (vec![16896, 32768], 32)); // 31 > 31 false ⇒ rate 5
456        assert_eq!(upd(&[16384, 32768], 0, 32), (vec![16640, 32768], 32)); // 32 > 31 true ⇒ rate 6, count saturates
457        // N = 3 (FloorLog2 = 1): a mid-symbol update with count 20.
458        assert_eq!(
459            upd(&[10000, 20000, 32768], 1, 20),
460            (vec![9688, 20399, 32768], 21)
461        ); // rate 5
462        // N = 8 (FloorLog2 = 3, capped to 2) pins Min(.., 2) and the full sweep.
463        assert_eq!(
464            upd(
465                &[4096, 8192, 12288, 16384, 20480, 24576, 28672, 32768],
466                3,
467                0
468            ),
469            (
470                vec![3968, 7936, 11904, 16896, 20864, 24832, 28800, 32768],
471                1
472            ) // rate 5
473        );
474    }
475
476    #[test]
477    fn adaptive_single_cdf_roundtrips() {
478        // Encode a long, skewed stream against one adapting CDF. The decoder, starting from the same
479        // initial CDF and applying the identical update, must recover every symbol and end with a
480        // byte-identical CDF + count.
481        let mut rng = Lcg(0xa1b2_c3d4_e5f6_0719);
482        let init = random_cdf(&mut rng, 6);
483        let mut enc = SymbolEncoder::new();
484        let mut ecdf = init.clone();
485        let mut ecount = 0u16;
486        let mut syms = Vec::new();
487        for _ in 0..10_000 {
488            // Skew toward symbol 0 so the CDF moves substantially.
489            let s = (rng.below(6) * rng.below(2)) as usize;
490            enc.encode_symbol_adapt(s, &mut ecdf, &mut ecount);
491            syms.push(s);
492        }
493        let bytes = enc.finish();
494        let mut dec = SymbolDecoder::new(&bytes);
495        let mut dcdf = init.clone();
496        let mut dcount = 0u16;
497        for (i, &s) in syms.iter().enumerate() {
498            assert_eq!(
499                dec.read_symbol_adapt(&mut dcdf, &mut dcount),
500                s,
501                "event {i}"
502            );
503        }
504        assert_eq!(ecdf, dcdf, "encoder/decoder CDFs diverged");
505        assert_eq!(ecount, dcount);
506        assert_ne!(
507            ecdf, init,
508            "CDF should have adapted away from its initial state"
509        );
510    }
511
512    #[test]
513    fn adaptive_multi_context_roundtrips() {
514        // Several independent adapting contexts, interleaved — the realistic usage where each syntax
515        // element has its own CDF + count and they must not cross-contaminate.
516        let mut rng = Lcg(0x0011_2233_4455_6677);
517        let inits: Vec<Vec<u16>> = (2..=10).map(|n| random_cdf(&mut rng, n)).collect();
518        let mut enc = SymbolEncoder::new();
519        let mut ecdfs = inits.clone();
520        let mut ecounts = vec![0u16; inits.len()];
521        let mut events = Vec::new();
522        for _ in 0..15_000 {
523            let ctx = rng.below(inits.len() as u32) as usize;
524            let s = rng.below(ecdfs[ctx].len() as u32) as usize;
525            enc.encode_symbol_adapt(s, &mut ecdfs[ctx], &mut ecounts[ctx]);
526            events.push((ctx, s));
527        }
528        let bytes = enc.finish();
529        let mut dec = SymbolDecoder::new(&bytes);
530        let mut dcdfs = inits.clone();
531        let mut dcounts = vec![0u16; inits.len()];
532        for (i, &(ctx, s)) in events.iter().enumerate() {
533            assert_eq!(
534                dec.read_symbol_adapt(&mut dcdfs[ctx], &mut dcounts[ctx]),
535                s,
536                "event {i} ctx {ctx}"
537            );
538        }
539        assert_eq!(ecdfs, dcdfs);
540        assert_eq!(ecounts, dcounts);
541    }
542}