#![allow(non_camel_case_types)]
cfg_if::cfg_if! {
if #[cfg(nasm_x86_64)] {
pub use crate::asm::x86::ec::*;
} else {
pub use self::rust::*;
}
}
use crate::context::CDFContextLog;
use crate::util::{msb, ILog};
use bitstream_io::{BigEndian, BitWrite, BitWriter};
use std::io;
pub const OD_BITRES: u8 = 3;
const EC_PROB_SHIFT: u32 = 6;
const EC_MIN_PROB: u32 = 4;
type ec_window = u32;
pub trait Writer {
fn symbol(&mut self, s: u32, cdf: &[u16]);
fn symbol_bits(&self, s: u32, cdf: &[u16]) -> u32;
fn symbol_with_update<const CDF_LEN: usize>(
&mut self, s: u32, cdf: &mut [u16; CDF_LEN], log: &mut CDFContextLog,
);
fn bool(&mut self, val: bool, f: u16);
fn bit(&mut self, bit: u16);
fn literal(&mut self, bits: u8, s: u32);
fn write_golomb(&mut self, level: u32);
fn write_quniform(&mut self, n: u32, v: u32);
fn count_quniform(&self, n: u32, v: u32) -> u32;
fn write_subexp(&mut self, n: u32, k: u8, v: u32);
fn count_subexp(&self, n: u32, k: u8, v: u32) -> u32;
fn write_unsigned_subexp_with_ref(&mut self, v: u32, mx: u32, k: u8, r: u32);
fn count_unsigned_subexp_with_ref(
&self, v: u32, mx: u32, k: u8, r: u32,
) -> u32;
fn write_signed_subexp_with_ref(
&mut self, v: i32, low: i32, high: i32, k: u8, r: i32,
);
fn count_signed_subexp_with_ref(
&self, v: i32, low: i32, high: i32, k: u8, r: i32,
) -> u32;
fn tell(&mut self) -> u32;
fn tell_frac(&mut self) -> u32;
fn checkpoint(&mut self) -> WriterCheckpoint;
fn rollback(&mut self, _: &WriterCheckpoint);
fn add_bits_frac(&mut self, bits_frac: u32);
}
pub trait StorageBackend {
fn store(&mut self, fl: u16, fh: u16, nms: u16);
fn stream_bytes(&mut self) -> usize;
fn checkpoint(&mut self) -> WriterCheckpoint;
fn rollback(&mut self, _: &WriterCheckpoint);
}
#[derive(Debug, Clone)]
pub struct WriterBase<S> {
rng: u16,
cnt: i16,
#[cfg(feature = "desync_finder")]
debug: bool,
fake_bits_frac: u32,
s: S,
}
#[derive(Debug, Clone)]
pub struct WriterCounter {
bytes: usize,
}
#[derive(Debug, Clone)]
pub struct WriterRecorder {
storage: Vec<(u16, u16, u16)>,
bytes: usize,
}
#[derive(Debug, Clone)]
pub struct WriterEncoder {
precarry: Vec<u16>,
low: ec_window,
}
#[derive(Clone)]
pub struct WriterCheckpoint {
stream_bytes: usize,
backend_var: usize,
rng: u16,
cnt: i16,
}
impl WriterCounter {
#[inline]
pub const fn new() -> WriterBase<WriterCounter> {
WriterBase::new(WriterCounter { bytes: 0 })
}
}
impl WriterRecorder {
#[inline]
pub const fn new() -> WriterBase<WriterRecorder> {
WriterBase::new(WriterRecorder { storage: Vec::new(), bytes: 0 })
}
}
impl WriterEncoder {
#[inline]
pub const fn new() -> WriterBase<WriterEncoder> {
WriterBase::new(WriterEncoder { precarry: Vec::new(), low: 0 })
}
}
impl StorageBackend for WriterBase<WriterCounter> {
#[inline]
fn store(&mut self, fl: u16, fh: u16, nms: u16) {
let (_l, r) = self.lr_compute(fl, fh, nms);
let d = 16 - ILog::ilog(r);
let mut s = self.cnt + (d as i16);
self.s.bytes += (s >= 0) as usize + (s >= 8) as usize;
s -= 8 * ((s >= 0) as i16 + (s >= 8) as i16);
self.rng = r << d;
self.cnt = s;
}
#[inline]
fn stream_bytes(&mut self) -> usize {
self.s.bytes
}
#[inline]
fn checkpoint(&mut self) -> WriterCheckpoint {
WriterCheckpoint {
stream_bytes: self.s.bytes,
backend_var: 0,
rng: self.rng,
cnt: self.cnt,
}
}
#[inline]
fn rollback(&mut self, checkpoint: &WriterCheckpoint) {
self.rng = checkpoint.rng;
self.cnt = checkpoint.cnt;
self.s.bytes = checkpoint.stream_bytes;
}
}
impl StorageBackend for WriterBase<WriterRecorder> {
#[inline]
fn store(&mut self, fl: u16, fh: u16, nms: u16) {
let (_l, r) = self.lr_compute(fl, fh, nms);
let d = 16 - ILog::ilog(r);
let mut s = self.cnt + (d as i16);
self.s.bytes += (s >= 0) as usize + (s >= 8) as usize;
s -= 8 * ((s >= 0) as i16 + (s >= 8) as i16);
self.rng = r << d;
self.cnt = s;
self.s.storage.push((fl, fh, nms));
}
#[inline]
fn stream_bytes(&mut self) -> usize {
self.s.bytes
}
#[inline]
fn checkpoint(&mut self) -> WriterCheckpoint {
WriterCheckpoint {
stream_bytes: self.s.bytes,
backend_var: self.s.storage.len(),
rng: self.rng,
cnt: self.cnt,
}
}
#[inline]
fn rollback(&mut self, checkpoint: &WriterCheckpoint) {
self.rng = checkpoint.rng;
self.cnt = checkpoint.cnt;
self.s.bytes = checkpoint.stream_bytes;
self.s.storage.truncate(checkpoint.backend_var);
}
}
impl StorageBackend for WriterBase<WriterEncoder> {
fn store(&mut self, fl: u16, fh: u16, nms: u16) {
let (l, r) = self.lr_compute(fl, fh, nms);
let mut low = l + self.s.low;
let mut c = self.cnt;
let d = 16 - ILog::ilog(r);
let mut s = c + (d as i16);
if s >= 0 {
c += 16;
let mut m = (1 << c) - 1;
if s >= 8 {
self.s.precarry.push((low >> c) as u16);
low &= m;
c -= 8;
m >>= 8;
}
self.s.precarry.push((low >> c) as u16);
s = c + (d as i16) - 24;
low &= m;
}
self.s.low = low << d;
self.rng = r << d;
self.cnt = s;
}
#[inline]
fn stream_bytes(&mut self) -> usize {
self.s.precarry.len()
}
#[inline]
fn checkpoint(&mut self) -> WriterCheckpoint {
WriterCheckpoint {
stream_bytes: self.s.precarry.len(),
backend_var: self.s.low as usize,
rng: self.rng,
cnt: self.cnt,
}
}
fn rollback(&mut self, checkpoint: &WriterCheckpoint) {
self.rng = checkpoint.rng;
self.cnt = checkpoint.cnt;
self.s.low = checkpoint.backend_var as ec_window;
self.s.precarry.truncate(checkpoint.stream_bytes);
}
}
impl<S> WriterBase<S> {
#[inline]
#[cfg(not(feature = "desync_finder"))]
const fn new(storage: S) -> Self {
WriterBase { rng: 0x8000, cnt: -9, fake_bits_frac: 0, s: storage }
}
#[inline]
#[cfg(feature = "desync_finder")]
fn new(storage: S) -> Self {
WriterBase {
rng: 0x8000,
cnt: -9,
debug: std::env::var_os("RAV1E_DEBUG").is_some(),
fake_bits_frac: 0,
s: storage,
}
}
fn lr_compute(&mut self, fl: u16, fh: u16, nms: u16) -> (ec_window, u16) {
let u: u32;
let v: u32;
let mut r = self.rng as u32;
debug_assert!(32768 <= r);
if fl < 32768 {
u = (((r >> 8) * (fl as u32 >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT))
+ EC_MIN_PROB * nms as u32;
v = (((r >> 8) * (fh as u32 >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT))
+ EC_MIN_PROB * (nms - 1) as u32;
(r - u, (u - v) as u16)
} else {
r -= (((r >> 8) * (fh as u32 >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT))
+ EC_MIN_PROB * (nms - 1) as u32;
(0, r as u16)
}
}
fn frac_compute(nbits_total: u32, mut rng: u32) -> u32 {
let nbits = nbits_total << OD_BITRES;
let mut l = 0;
for _ in 0..OD_BITRES {
rng = (rng * rng) >> 15;
let b = rng >> 16;
l = (l << 1) | b;
rng >>= b;
}
nbits - l
}
const fn recenter(r: u32, v: u32) -> u32 {
if v > (r << 1) {
v
} else if v >= r {
(v - r) << 1
} else {
((r - v) << 1) - 1
}
}
#[cfg(feature = "desync_finder")]
fn print_backtrace(&self, s: u32) {
let mut depth = 3;
backtrace::trace(|frame| {
let ip = frame.ip();
depth -= 1;
if depth == 0 {
backtrace::resolve(ip, |symbol| {
if let Some(name) = symbol.name() {
println!("Writing symbol {} from {}", s, name);
}
});
false
} else {
true
}
});
}
}
impl WriterBase<WriterRecorder> {
pub fn replay(&mut self, dest: &mut dyn StorageBackend) {
for &(fl, fh, nms) in &self.s.storage {
dest.store(fl, fh, nms);
}
self.rng = 0x8000;
self.cnt = -9;
self.s.storage.truncate(0);
self.s.bytes = 0;
}
}
impl WriterBase<WriterEncoder> {
pub fn done(&mut self) -> Vec<u8> {
let l = self.s.low;
let mut c = self.cnt;
let mut s = 10;
let m = 0x3FFF;
let mut e = ((l + m) & !m) | (m + 1);
s += c;
if s > 0 {
let mut n = (1 << (c + 16)) - 1;
loop {
self.s.precarry.push((e >> (c + 16)) as u16);
e &= n;
s -= 8;
c -= 8;
n >>= 8;
if s <= 0 {
break;
}
}
}
let mut c = 0;
let mut offs = self.s.precarry.len();
let mut out = vec![0_u8; offs];
while offs > 0 {
offs -= 1;
c += self.s.precarry[offs];
out[offs] = c as u8;
c >>= 8;
}
out
}
}
impl<S> Writer for WriterBase<S>
where
WriterBase<S>: StorageBackend,
{
fn bool(&mut self, val: bool, f: u16) {
debug_assert!(0 < f);
debug_assert!(f < 32768);
self.symbol(u32::from(val), &[f, 0]);
}
fn bit(&mut self, bit: u16) {
self.bool(bit == 1, 16384);
}
fn add_bits_frac(&mut self, bits_frac: u32) {
self.fake_bits_frac += bits_frac
}
fn literal(&mut self, bits: u8, s: u32) {
for bit in (0..bits).rev() {
self.bit((1 & (s >> bit)) as u16);
}
}
#[inline(always)]
fn symbol(&mut self, s: u32, cdf: &[u16]) {
debug_assert!(cdf[cdf.len() - 1] < (1 << EC_PROB_SHIFT));
let s = s as usize;
debug_assert!(s < cdf.len());
let nms = cdf.len() - s;
let fl = if s > 0 {
unsafe { *cdf.get_unchecked(s - 1) }
} else {
32768
};
let fh = unsafe { *cdf.get_unchecked(s) };
debug_assert!((fh >> EC_PROB_SHIFT) <= (fl >> EC_PROB_SHIFT));
debug_assert!(fl <= 32768);
self.store(fl, fh, nms as u16);
}
fn symbol_with_update<const CDF_LEN: usize>(
&mut self, s: u32, cdf: &mut [u16; CDF_LEN], log: &mut CDFContextLog,
) {
#[cfg(feature = "desync_finder")]
{
if self.debug {
self.print_backtrace(s);
}
}
log.push(cdf);
self.symbol(s, cdf);
update_cdf(cdf, s);
}
fn symbol_bits(&self, s: u32, cdf: &[u16]) -> u32 {
let mut bits = 0;
debug_assert!(cdf[cdf.len() - 1] < (1 << EC_PROB_SHIFT));
debug_assert!(32768 <= self.rng);
let rng = (self.rng >> 8) as u32;
let fh = cdf[s as usize] as u32 >> EC_PROB_SHIFT;
let r = if s > 0 {
let fl = cdf[s as usize - 1] as u32 >> EC_PROB_SHIFT;
((rng * fl) >> (7 - EC_PROB_SHIFT)) - ((rng * fh) >> (7 - EC_PROB_SHIFT))
+ EC_MIN_PROB
} else {
let nms1 = cdf.len() as u32 - s - 1;
self.rng as u32
- ((rng * fh) >> (7 - EC_PROB_SHIFT))
- nms1 * EC_MIN_PROB
};
let pre = Self::frac_compute((self.cnt + 9) as u32, self.rng as u32);
let d = 16 - ILog::ilog(r);
let mut c = self.cnt;
let mut sh = c + (d as i16);
if sh >= 0 {
c += 16;
if sh >= 8 {
bits += 8;
c -= 8;
}
bits += 8;
sh = c + (d as i16) - 24;
}
Self::frac_compute((bits + sh + 9) as u32, r << d) - pre
}
fn write_golomb(&mut self, level: u32) {
let x = level + 1;
let length = 32 - x.leading_zeros();
for _ in 0..length - 1 {
self.bit(0);
}
for i in (0..length).rev() {
self.bit(((x >> i) & 0x01) as u16);
}
}
fn write_quniform(&mut self, n: u32, v: u32) {
if n > 1 {
let l = msb(n as i32) as u8 + 1;
let m = (1 << l) - n;
if v < m {
self.literal(l - 1, v);
} else {
self.literal(l - 1, m + ((v - m) >> 1));
self.literal(1, (v - m) & 1);
}
}
}
fn count_quniform(&self, n: u32, v: u32) -> u32 {
let mut bits = 0;
if n > 1 {
let l = (msb(n as i32) + 1) as u32;
let m = (1 << l) - n;
bits += (l - 1) << OD_BITRES;
if v >= m {
bits += 1 << OD_BITRES;
}
}
bits
}
fn write_subexp(&mut self, n: u32, k: u8, v: u32) {
let mut i = 0;
let mut mk = 0;
loop {
let b = if i != 0 { k + i - 1 } else { k };
let a = 1 << b;
if n <= mk + 3 * a {
self.write_quniform(n - mk, v - mk);
break;
} else {
let t = v >= mk + a;
self.bool(t, 16384);
if t {
i += 1;
mk += a;
} else {
self.literal(b, v - mk);
break;
}
}
}
}
fn count_subexp(&self, n: u32, k: u8, v: u32) -> u32 {
let mut i = 0;
let mut mk = 0;
let mut bits = 0;
loop {
let b = if i != 0 { k + i - 1 } else { k };
let a = 1 << b;
if n <= mk + 3 * a {
bits += self.count_quniform(n - mk, v - mk);
break;
} else {
let t = v >= mk + a;
bits += 1 << OD_BITRES;
if t {
i += 1;
mk += a;
} else {
bits += (b as u32) << OD_BITRES;
break;
}
}
}
bits
}
fn write_unsigned_subexp_with_ref(&mut self, v: u32, n: u32, k: u8, r: u32) {
if (r << 1) <= n {
self.write_subexp(n, k, Self::recenter(r, v));
} else {
self.write_subexp(n, k, Self::recenter(n - 1 - r, n - 1 - v));
}
}
fn count_unsigned_subexp_with_ref(
&self, v: u32, n: u32, k: u8, r: u32,
) -> u32 {
if (r << 1) <= n {
self.count_subexp(n, k, Self::recenter(r, v))
} else {
self.count_subexp(n, k, Self::recenter(n - 1 - r, n - 1 - v))
}
}
fn write_signed_subexp_with_ref(
&mut self, v: i32, low: i32, high: i32, k: u8, r: i32,
) {
self.write_unsigned_subexp_with_ref(
(v - low) as u32,
(high - low) as u32,
k,
(r - low) as u32,
);
}
fn count_signed_subexp_with_ref(
&self, v: i32, low: i32, high: i32, k: u8, r: i32,
) -> u32 {
self.count_unsigned_subexp_with_ref(
(v - low) as u32,
(high - low) as u32,
k,
(r - low) as u32,
)
}
fn tell(&mut self) -> u32 {
(((self.stream_bytes() * 8) as i32) + (self.cnt as i32) + 10) as u32
+ (self.fake_bits_frac >> 8)
}
fn tell_frac(&mut self) -> u32 {
Self::frac_compute(self.tell(), self.rng as u32) + self.fake_bits_frac
}
fn checkpoint(&mut self) -> WriterCheckpoint {
StorageBackend::checkpoint(self)
}
fn rollback(&mut self, wc: &WriterCheckpoint) {
StorageBackend::rollback(self, wc)
}
}
pub trait BCodeWriter {
fn recenter_nonneg(&mut self, r: u16, v: u16) -> u16;
fn recenter_finite_nonneg(&mut self, n: u16, r: u16, v: u16) -> u16;
fn write_quniform(&mut self, n: u16, v: u16) -> Result<(), std::io::Error>;
fn write_subexpfin(
&mut self, n: u16, k: u16, v: u16,
) -> Result<(), std::io::Error>;
fn write_refsubexpfin(
&mut self, n: u16, k: u16, r: i16, v: i16,
) -> Result<(), std::io::Error>;
fn write_s_refsubexpfin(
&mut self, n: u16, k: u16, r: i16, v: i16,
) -> Result<(), std::io::Error>;
}
impl<W: io::Write> BCodeWriter for BitWriter<W, BigEndian> {
fn recenter_nonneg(&mut self, r: u16, v: u16) -> u16 {
if v > (r << 1) {
v
} else if v >= r {
(v - r) << 1
} else {
((r - v) << 1) - 1
}
}
fn recenter_finite_nonneg(&mut self, n: u16, r: u16, v: u16) -> u16 {
if (r << 1) <= n {
self.recenter_nonneg(r, v)
} else {
self.recenter_nonneg(n - 1 - r, n - 1 - v)
}
}
fn write_quniform(&mut self, n: u16, v: u16) -> Result<(), std::io::Error> {
if n > 1 {
let l = msb(n as i32) as u8 + 1;
let m = (1 << l) - n;
if v < m {
self.write(l as u32 - 1, v)
} else {
self.write(l as u32 - 1, m + ((v - m) >> 1))?;
self.write(1, (v - m) & 1)
}
} else {
Ok(())
}
}
fn write_subexpfin(
&mut self, n: u16, k: u16, v: u16,
) -> Result<(), std::io::Error> {
let mut i = 0;
let mut mk = 0;
loop {
let b = if i > 0 { k + i - 1 } else { k };
let a = 1 << b;
if n <= mk + 3 * a {
return self.write_quniform(n - mk, v - mk);
} else {
let t = v >= mk + a;
self.write_bit(t)?;
if t {
i += 1;
mk += a;
} else {
return self.write(b as u32, v - mk);
}
}
}
}
fn write_refsubexpfin(
&mut self, n: u16, k: u16, r: i16, v: i16,
) -> Result<(), std::io::Error> {
let recentered_v = self.recenter_finite_nonneg(n, r as u16, v as u16);
self.write_subexpfin(n, k, recentered_v)
}
fn write_s_refsubexpfin(
&mut self, n: u16, k: u16, r: i16, v: i16,
) -> Result<(), std::io::Error> {
self.write_refsubexpfin(
(n << 1) - 1,
k,
r + (n - 1) as i16,
v + (n - 1) as i16,
)
}
}
pub(crate) fn cdf_to_pdf<const CDF_LEN: usize>(
cdf: &[u16; CDF_LEN],
) -> [u16; CDF_LEN] {
let mut pdf = [0; CDF_LEN];
let mut z = 32768u16 >> EC_PROB_SHIFT;
for (d, &a) in pdf.iter_mut().zip(cdf.iter()) {
*d = z - (a >> EC_PROB_SHIFT);
z = a >> EC_PROB_SHIFT;
}
pdf
}
pub(crate) mod rust {
#[inline]
pub fn update_cdf(cdf: &mut [u16], val: u32) {
use crate::context::CDF_LEN_MAX;
let nsymbs = cdf.len();
let mut rate = 3 + (nsymbs >> 1).min(2);
if let Some(count) = cdf.last_mut() {
rate += (*count >> 4) as usize;
*count += 1 - (*count >> 5);
} else {
return;
}
for (i, v) in
cdf[..nsymbs - 1].iter_mut().enumerate().take(CDF_LEN_MAX - 1)
{
if i as u32 >= val {
*v -= *v >> rate;
} else {
*v += (32768 - *v) >> rate;
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
const WINDOW_SIZE: i16 = 32;
const LOTS_OF_BITS: i16 = 0x4000;
#[derive(Debug)]
struct Reader<'a> {
buf: &'a [u8],
bptr: usize,
dif: ec_window,
rng: u16,
cnt: i16,
}
impl<'a> Reader<'a> {
fn new(buf: &'a [u8]) -> Self {
let mut r = Reader {
buf,
bptr: 0,
dif: (1 << (WINDOW_SIZE - 1)) - 1,
rng: 0x8000,
cnt: -15,
};
r.refill();
r
}
fn refill(&mut self) {
let mut s = WINDOW_SIZE - 9 - (self.cnt + 15);
while s >= 0 && self.bptr < self.buf.len() {
assert!(s <= WINDOW_SIZE - 8);
self.dif ^= (self.buf[self.bptr] as ec_window) << s;
self.cnt += 8;
s -= 8;
self.bptr += 1;
}
if self.bptr >= self.buf.len() {
self.cnt = LOTS_OF_BITS;
}
}
fn normalize(&mut self, dif: ec_window, rng: u32) {
assert!(rng <= 65536);
let d = rng.leading_zeros() - 16;
self.cnt -= d as i16;
self.dif = ((dif + 1) << d) - 1;
self.rng = (rng << d) as u16;
if self.cnt < 0 {
self.refill()
}
}
fn bool(&mut self, f: u32) -> bool {
assert!(f < 32768);
let r = self.rng as u32;
assert!(self.dif >> (WINDOW_SIZE - 16) < r);
assert!(32768 <= r);
let v = (((r >> 8) * (f >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT))
+ EC_MIN_PROB;
let vw = v << (WINDOW_SIZE - 16);
let (dif, rng, ret) = if self.dif >= vw {
(self.dif - vw, r - v, false)
} else {
(self.dif, v, true)
};
self.normalize(dif, rng);
ret
}
fn symbol(&mut self, icdf: &[u16]) -> i32 {
let r = self.rng as u32;
assert!(self.dif >> (WINDOW_SIZE - 16) < r);
assert!(32768 <= r);
let n = icdf.len() as u32 - 1;
let c = self.dif >> (WINDOW_SIZE - 16);
let mut v = self.rng as u32;
let mut ret = 0i32;
let mut u = v;
v = ((r >> 8) * (icdf[ret as usize] as u32 >> EC_PROB_SHIFT))
>> (7 - EC_PROB_SHIFT);
v += EC_MIN_PROB * (n - ret as u32);
while c < v {
u = v;
ret += 1;
v = ((r >> 8) * (icdf[ret as usize] as u32 >> EC_PROB_SHIFT))
>> (7 - EC_PROB_SHIFT);
v += EC_MIN_PROB * (n - ret as u32);
}
assert!(v < u);
assert!(u <= r);
let new_dif = self.dif - (v << (WINDOW_SIZE - 16));
self.normalize(new_dif, u - v);
ret
}
}
#[test]
fn booleans() {
let mut w = WriterEncoder::new();
w.bool(false, 1);
w.bool(true, 2);
w.bool(false, 3);
w.bool(true, 1);
w.bool(true, 2);
w.bool(false, 3);
let b = w.done();
let mut r = Reader::new(&b);
assert!(!r.bool(1));
assert!(r.bool(2));
assert!(!r.bool(3));
assert!(r.bool(1));
assert!(r.bool(2));
assert!(!r.bool(3));
}
#[test]
fn cdf() {
let cdf = [7296, 3819, 1716, 0];
let mut w = WriterEncoder::new();
w.symbol(0, &cdf);
w.symbol(0, &cdf);
w.symbol(0, &cdf);
w.symbol(1, &cdf);
w.symbol(1, &cdf);
w.symbol(1, &cdf);
w.symbol(2, &cdf);
w.symbol(2, &cdf);
w.symbol(2, &cdf);
let b = w.done();
let mut r = Reader::new(&b);
assert_eq!(r.symbol(&cdf), 0);
assert_eq!(r.symbol(&cdf), 0);
assert_eq!(r.symbol(&cdf), 0);
assert_eq!(r.symbol(&cdf), 1);
assert_eq!(r.symbol(&cdf), 1);
assert_eq!(r.symbol(&cdf), 1);
assert_eq!(r.symbol(&cdf), 2);
assert_eq!(r.symbol(&cdf), 2);
assert_eq!(r.symbol(&cdf), 2);
}
#[test]
fn mixed() {
let cdf = [7296, 3819, 1716, 0];
let mut w = WriterEncoder::new();
w.symbol(0, &cdf);
w.bool(true, 2);
w.symbol(0, &cdf);
w.bool(true, 2);
w.symbol(0, &cdf);
w.bool(true, 2);
w.symbol(1, &cdf);
w.bool(true, 1);
w.symbol(1, &cdf);
w.bool(false, 2);
w.symbol(1, &cdf);
w.symbol(2, &cdf);
w.symbol(2, &cdf);
w.symbol(2, &cdf);
let b = w.done();
let mut r = Reader::new(&b);
assert_eq!(r.symbol(&cdf), 0);
assert!(r.bool(2));
assert_eq!(r.symbol(&cdf), 0);
assert!(r.bool(2));
assert_eq!(r.symbol(&cdf), 0);
assert!(r.bool(2));
assert_eq!(r.symbol(&cdf), 1);
assert!(r.bool(1));
assert_eq!(r.symbol(&cdf), 1);
assert!(!r.bool(2));
assert_eq!(r.symbol(&cdf), 1);
assert_eq!(r.symbol(&cdf), 2);
assert_eq!(r.symbol(&cdf), 2);
assert_eq!(r.symbol(&cdf), 2);
}
}