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}