use alloc::vec;
use alloc::vec::Vec;
use crate::{Config, Error, Result};
const RANS_L: u64 = 1 << 31;
pub const RANS_M: u32 = 1 << 15;
const RANS_BASE_BITS: u32 = 16;
const RANS_EMIT_SHIFT: u32 = 32;
const MAGIC: [u8; 4] = *b"RBAN";
const FORMAT_VERSION: u16 = 3;
const FLAG_RAW: u16 = 0x0001;
const HEADER_LEN: usize = 24;
fn check_replay(
state: u64,
payload: &[u8],
stored_state: u64,
stored_payload: &[u8],
) -> Result<()> {
if state != stored_state || payload != stored_payload {
return Err(Error::Entropy("entropy frame did not replay canonically"));
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct Model {
symbols: u32,
freq: Vec<u16>,
cum: Vec<u16>,
slot_to_symbol: Vec<u16>,
}
impl Model {
pub fn from_uniform(symbols: u32) -> Result<Self> {
if symbols == 0 || symbols > RANS_M {
return Err(Error::Entropy("model alphabet must be in 1..=32768"));
}
let base = RANS_M / symbols;
let extra = RANS_M % symbols;
let mut freq = Vec::with_capacity(symbols as usize);
for index in 0..symbols {
freq.push((base + u32::from(index < extra)) as u16);
}
Self::from_freqs(freq)
}
pub fn from_weights(weights: &[u32]) -> Result<Self> {
let symbols = weights.len();
if symbols == 0 || symbols > RANS_M as usize {
return Err(Error::Entropy(
"model weights must contain 1..=32768 entries",
));
}
let total: u64 = weights.iter().map(|&weight| u64::from(weight)).sum();
if total == 0 {
return Err(Error::Entropy("model weights must be positive"));
}
let scale = u64::from(RANS_M);
let mut freq: Vec<u32> = weights
.iter()
.map(|&weight| ((u64::from(weight) * scale) / total) as u32)
.collect();
for slot in &mut freq {
*slot = (*slot).max(1);
}
let mut sum: u32 = freq.iter().sum();
if sum > RANS_M {
let mut excess = sum - RANS_M;
let mut order: Vec<usize> = (0..symbols).collect();
order.sort_by(|&a, &b| freq[b].cmp(&freq[a]).then_with(|| a.cmp(&b)));
for &index in &order {
if excess == 0 {
break;
}
let reducible = freq[index] - 1;
let reduce = reducible.min(excess);
freq[index] -= reduce;
excess -= reduce;
}
debug_assert_eq!(excess, 0);
sum = freq.iter().sum();
}
let deficit = RANS_M - sum;
let mut order: Vec<usize> = (0..symbols).collect();
order.sort_by(|&a, &b| {
let remainder_a = (u64::from(weights[a]) * scale) % total;
let remainder_b = (u64::from(weights[b]) * scale) % total;
remainder_b.cmp(&remainder_a).then_with(|| a.cmp(&b))
});
for &index in order.iter().take(deficit as usize) {
freq[index] += 1;
}
let freq: Vec<u16> = freq.iter().map(|&value| value as u16).collect();
Self::from_freqs(freq)
}
fn from_freqs(freq: Vec<u16>) -> Result<Self> {
let symbols = freq.len();
if symbols == 0 {
return Err(Error::Entropy("empty model frequency table"));
}
let mut cum = Vec::with_capacity(symbols + 1);
cum.push(0u32);
for &value in &freq {
if value == 0 {
return Err(Error::Entropy("model frequency must be positive"));
}
let next = cum.last().copied().unwrap_or(0) + u32::from(value);
if next > RANS_M {
return Err(Error::Entropy(
"model frequencies exceed the total frequency mass",
));
}
cum.push(next);
}
if cum.last().copied().unwrap_or(0) != RANS_M {
return Err(Error::Entropy(
"model frequencies must sum to the total frequency mass",
));
}
let mut slot_to_symbol = vec![0u16; RANS_M as usize];
for symbol in 0..symbols {
let start = cum[symbol] as usize;
let end = cum[symbol + 1] as usize;
slot_to_symbol[start..end].fill(symbol as u16);
}
Ok(Self {
symbols: symbols as u32,
freq,
cum: cum.iter().map(|&value| value as u16).collect(),
slot_to_symbol,
})
}
pub const fn symbols(&self) -> u32 {
self.symbols
}
pub const fn total(&self) -> u32 {
RANS_M
}
pub fn cum(&self, symbol: u32) -> u32 {
if symbol as usize >= self.cum.len() {
0
} else {
u32::from(self.cum[symbol as usize])
}
}
pub fn freq(&self, symbol: u32) -> u32 {
if symbol as usize >= self.freq.len() {
0
} else {
u32::from(self.freq[symbol as usize])
}
}
fn symbol_for_slot(&self, slot: u32) -> u32 {
u32::from(self.slot_to_symbol[slot as usize])
}
}
impl PartialEq for Model {
fn eq(&self, other: &Self) -> bool {
self.symbols == other.symbols && self.freq == other.freq
}
}
impl Eq for Model {}
pub struct RansEncoder {
state: u64,
bytes: Vec<u8>,
}
impl RansEncoder {
pub fn new() -> Self {
Self {
state: RANS_L,
bytes: Vec::new(),
}
}
pub fn put_symbol(&mut self, model: &Model, symbol: u32) -> Result<()> {
let freq = model.freq(symbol);
if freq == 0 {
return Err(Error::Entropy("symbol outside the model alphabet"));
}
let cum = model.cum(symbol);
while self.state >= u64::from(freq) << RANS_EMIT_SHIFT {
self.bytes
.extend_from_slice(&(self.state as u16).to_le_bytes());
self.state >>= RANS_BASE_BITS;
}
let quotient = self.state / u64::from(freq);
let remainder = self.state % u64::from(freq);
self.state = quotient * u64::from(RANS_M) + u64::from(cum) + remainder;
Ok(())
}
pub fn finish(self) -> (u64, Vec<u8>) {
(self.state, self.bytes)
}
}
impl Default for RansEncoder {
fn default() -> Self {
Self::new()
}
}
pub struct RansDecoder<'a> {
state: u64,
bytes: &'a [u8],
pos: usize,
}
impl<'a> RansDecoder<'a> {
pub fn new(final_state: u64, payload: &'a [u8]) -> Self {
Self {
state: final_state,
bytes: payload,
pos: payload.len(),
}
}
fn renorm(&mut self) -> Result<()> {
while self.state < RANS_L {
if self.pos < 2 {
return Err(Error::Entropy("truncated rANS payload"));
}
self.pos -= 2;
let word = u16::from_le_bytes([self.bytes[self.pos], self.bytes[self.pos + 1]]);
self.state = (self.state << RANS_BASE_BITS) | u64::from(word);
}
Ok(())
}
pub fn get_symbol(&mut self, model: &Model) -> Result<u32> {
self.renorm()?;
let slot = (self.state % u64::from(RANS_M)) as u32;
let symbol = model.symbol_for_slot(slot);
let freq = model.freq(symbol);
let cum = model.cum(symbol);
let quotient = self.state / u64::from(RANS_M);
self.state = quotient * u64::from(freq) + u64::from(slot) - u64::from(cum);
Ok(symbol)
}
pub fn finish(self) -> Result<()> {
if self.state != RANS_L {
return Err(Error::Entropy("rANS final state did not verify"));
}
if self.pos != 0 {
return Err(Error::Entropy("trailing bytes in rANS payload"));
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct FieldModel {
pub name: &'static str,
pub model: Model,
}
#[derive(Clone, Debug)]
pub struct SchemaModel {
fields: Vec<FieldModel>,
}
impl SchemaModel {
pub fn from_reflect<T: crate::Reflect>() -> Self {
match T::SHAPE {
crate::TypeShape::Struct(fields) => {
let fields = fields
.iter()
.map(|field| FieldModel {
name: field.name,
model: model_for_field(field),
})
.collect();
Self { fields }
}
crate::TypeShape::Enum(variants) => {
let model = match Model::from_uniform(variants.len() as u32) {
Ok(model) => model,
Err(_) => Model::from_uniform(256).expect("byte alphabet is valid"),
};
Self {
fields: vec![FieldModel {
name: "variant",
model,
}],
}
}
}
}
pub fn fields(&self) -> &[FieldModel] {
&self.fields
}
pub fn models(&self) -> Vec<&Model> {
self.fields.iter().map(|field| &field.model).collect()
}
}
fn model_for_field(field: &crate::FieldInfo) -> Model {
let symbols = if field.symbols == 0 {
256
} else {
field.symbols
};
Model::from_uniform(symbols)
.unwrap_or_else(|_| Model::from_uniform(256).expect("byte alphabet is valid"))
}
type UnwrappedFrame<'a> = (u64, u64, bool, &'a [u8]);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EntropyConfig {
base: Config,
replay_verification: bool,
raw_fallback: bool,
}
impl EntropyConfig {
pub(crate) const fn new(base: Config) -> Self {
Self {
base,
replay_verification: true,
raw_fallback: true,
}
}
pub const fn base_config(self) -> Config {
self.base
}
pub const fn without_replay_verification(mut self) -> Self {
self.replay_verification = false;
self
}
pub const fn replay_verification(self) -> bool {
self.replay_verification
}
pub const fn without_raw_fallback(mut self) -> Self {
self.raw_fallback = false;
self
}
pub const fn raw_fallback(self) -> bool {
self.raw_fallback
}
pub fn encode_sequence(&self, models: &[&Model], symbols: &[u32]) -> Result<Vec<u8>> {
if models.len() != symbols.len() {
return Err(Error::Entropy("models and symbols length mismatch"));
}
let mut encoder = RansEncoder::new();
for (model, &symbol) in models.iter().zip(symbols) {
encoder.put_symbol(model, symbol)?;
}
let (final_state, payload) = encoder.finish();
self.wrap(final_state, symbols.len() as u64, payload, false)
}
pub fn decode_sequence(&self, models: &[&Model], input: &[u8]) -> Result<Vec<u32>> {
let (count, final_state, raw, payload) = self.unwrap(input)?;
let count = usize::try_from(count)
.map_err(|_| Error::Entropy("entropy frame symbol count does not fit usize"))?;
if count != models.len() {
return Err(Error::Entropy("entropy frame symbol count mismatch"));
}
if raw {
let expected = count
.checked_mul(4)
.ok_or(Error::Entropy("raw entropy frame length overflows"))?;
if payload.len() != expected {
return Err(Error::Entropy("raw entropy frame length mismatch"));
}
let mut symbols = Vec::with_capacity(count);
for chunk in payload.chunks_exact(4) {
symbols.push(u32::from_le_bytes(
chunk.try_into().expect("fixed chunk width"),
));
}
return Ok(symbols);
}
let mut decoder = RansDecoder::new(final_state, payload);
let mut symbols = Vec::with_capacity(count);
for model in models.iter().rev() {
symbols.push(decoder.get_symbol(model)?);
}
symbols.reverse();
decoder.finish()?;
if self.replay_verification {
let mut encoder = RansEncoder::new();
for (model, &symbol) in models.iter().zip(&symbols) {
encoder.put_symbol(model, symbol)?;
}
let (state, replay) = encoder.finish();
check_replay(state, &replay, final_state, payload)?;
}
Ok(symbols)
}
pub fn compress(&self, input: &[u8], model: &Model) -> Result<Vec<u8>> {
let mut encoder = RansEncoder::new();
for &byte in input {
encoder.put_symbol(model, u32::from(byte))?;
}
let (final_state, coded) = encoder.finish();
let raw = self.raw_fallback && coded.len() >= input.len();
let payload = if raw { input.to_vec() } else { coded };
self.wrap(final_state, input.len() as u64, payload, raw)
}
pub fn decompress(&self, input: &[u8], model: &Model) -> Result<Vec<u8>> {
let (count, final_state, raw, payload) = self.unwrap(input)?;
let count = usize::try_from(count)
.map_err(|_| Error::Entropy("entropy frame length does not fit usize"))?;
if raw {
if payload.len() != count {
return Err(Error::Entropy("raw entropy frame length mismatch"));
}
return Ok(payload.to_vec());
}
let mut decoder = RansDecoder::new(final_state, payload);
let mut output = Vec::with_capacity(count);
for _ in 0..count {
let symbol = decoder.get_symbol(model)?;
if symbol > 255 {
return Err(Error::Entropy(
"byte-alphabet rANS produced an out-of-range symbol",
));
}
output.push(symbol as u8);
}
output.reverse();
decoder.finish()?;
if self.replay_verification {
let mut encoder = RansEncoder::new();
for &byte in &output {
encoder.put_symbol(model, u32::from(byte))?;
}
let (state, replay) = encoder.finish();
check_replay(state, &replay, final_state, payload)?;
}
Ok(output)
}
fn wrap(&self, final_state: u64, count: u64, payload: Vec<u8>, raw: bool) -> Result<Vec<u8>> {
let total = HEADER_LEN
.checked_add(payload.len())
.ok_or(Error::SizeLimit { limit: u64::MAX })?;
self.enforce_byte_limit(total)?;
let stored_state = if raw { 0 } else { final_state };
let mut output = Vec::with_capacity(total);
output.extend_from_slice(&MAGIC);
output.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
output.extend_from_slice(&if raw { FLAG_RAW } else { 0 }.to_le_bytes());
output.extend_from_slice(&count.to_le_bytes());
output.extend_from_slice(&stored_state.to_le_bytes());
output.extend_from_slice(&payload);
Ok(output)
}
fn unwrap<'a>(&self, input: &'a [u8]) -> Result<UnwrappedFrame<'a>> {
self.enforce_byte_limit(input.len())?;
let header = input.get(..HEADER_LEN).ok_or(Error::UnexpectedEnd)?;
if header[..4] != MAGIC {
return Err(Error::InvalidFrame("entropy magic does not match"));
}
if u16::from_le_bytes([header[4], header[5]]) != FORMAT_VERSION {
return Err(Error::InvalidFrame("unsupported entropy format version"));
}
let flags = u16::from_le_bytes([header[6], header[7]]);
if flags & !FLAG_RAW != 0 {
return Err(Error::InvalidFrame("unknown entropy frame flags"));
}
let count = u64::from_le_bytes(header[8..16].try_into().expect("fixed header width"));
let final_state =
u64::from_le_bytes(header[16..24].try_into().expect("fixed header width"));
let payload = &input[HEADER_LEN..];
if flags & FLAG_RAW != 0 && final_state != 0 {
return Err(Error::InvalidFrame(
"raw entropy frame has a non-zero state",
));
}
Ok((count, final_state, flags & FLAG_RAW != 0, payload))
}
fn enforce_byte_limit(&self, length: usize) -> Result<()> {
if let Some(limit) = self.base.limit {
if length as u64 > limit {
return Err(Error::SizeLimit { limit });
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn roundtrip_model(model: &Model, symbols: &[u32]) {
let config = EntropyConfig::new(Config::standard());
let models: Vec<&Model> = core::iter::repeat_n(model, symbols.len()).collect();
let frame = config.encode_sequence(&models, symbols).unwrap();
let decoded = config.decode_sequence(&models, &frame).unwrap();
assert_eq!(decoded, symbols, "roundtrip for model {:?}", model);
}
#[test]
fn uniform_models_roundtrip_exhaustively() {
for symbols in 1..=64u32 {
let model = Model::from_uniform(symbols).unwrap();
assert_eq!(model.symbols(), symbols);
let mut data = Vec::new();
for index in 0..(symbols * 3) {
data.push(index % symbols);
}
roundtrip_model(&model, &data);
}
}
#[test]
fn non_power_of_two_alphabet_beats_ceil_bits() {
let model = Model::from_uniform(3).unwrap();
let config = EntropyConfig::new(Config::standard());
let symbols = [0u32, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2];
let models: Vec<&Model> = core::iter::repeat_n(&model, symbols.len()).collect();
let frame = config.encode_sequence(&models, &symbols).unwrap();
assert!(frame.len() < 24 + 12, "frame too large: {}", frame.len());
assert_eq!(config.decode_sequence(&models, &frame).unwrap(), symbols);
}
#[test]
fn weighted_models_roundtrip_and_scale() {
let model = Model::from_weights(&[9, 1]).unwrap();
assert_eq!(model.total(), RANS_M);
let symbols = [0u32, 0, 0, 0, 0, 0, 0, 0, 0, 1];
roundtrip_model(&model, &symbols);
let skewed = Model::from_weights(&[100, 1, 1]).unwrap();
assert_eq!(skewed.total(), RANS_M);
assert!(skewed.freq(0) > skewed.freq(1));
assert!(skewed.freq(1) == skewed.freq(2));
roundtrip_model(&skewed, &[0, 0, 1, 2, 0, 0, 0, 0, 1]);
}
#[test]
fn skewed_prior_compresses_repetitive_bytes() {
let model = Model::from_weights(&[255, 1]).unwrap();
let config = EntropyConfig::new(Config::standard());
let input = vec![0u8; 256];
let frame = config.compress(&input, &model).unwrap();
let decoded = config.decompress(&frame, &model).unwrap();
assert_eq!(decoded, input);
assert!(frame.len() < 24 + 16, "frame too large: {}", frame.len());
}
#[test]
fn compress_falls_back_to_raw_when_not_smaller() {
let model = Model::from_uniform(256).unwrap();
let config = EntropyConfig::new(Config::standard());
let input: Vec<u8> = (0..4096)
.map(|index| ((index * 37) ^ (index >> 3)) as u8)
.collect();
let frame = config.compress(&input, &model).unwrap();
assert!(frame.len() <= HEADER_LEN + input.len());
let flags = u16::from_le_bytes([frame[6], frame[7]]);
assert_eq!(
flags & FLAG_RAW != 0,
frame.len() == HEADER_LEN + input.len()
);
assert_eq!(config.decompress(&frame, &model).unwrap(), input);
}
#[test]
fn corrupt_frames_are_rejected() {
let mut weights = vec![1u32; 256];
weights[b'a' as usize] = 1000;
let model = Model::from_weights(&weights).unwrap();
let config = EntropyConfig::new(Config::standard());
let input = vec![b'a'; 64];
let frame = config.compress(&input, &model).unwrap();
assert!(frame.len() < HEADER_LEN + input.len(), "frame is not coded");
let mut truncated = frame.clone();
truncated.pop();
assert!(config.decompress(&truncated, &model).is_err());
let mut corrupted = frame.clone();
let payload_start = HEADER_LEN;
corrupted[payload_start] ^= 0x5a;
assert!(config.decompress(&corrupted, &model).is_err());
let mut wrong_magic = frame.clone();
wrong_magic[0] = b'X';
assert!(matches!(
config.decompress(&wrong_magic, &model),
Err(Error::InvalidFrame(_))
));
let mut wrong_state = frame.clone();
wrong_state[16] ^= 1;
assert!(config.decompress(&wrong_state, &model).is_err());
let mut wrong_count = frame.clone();
wrong_count[8] ^= 1;
assert!(config.decompress(&wrong_count, &model).is_err());
}
#[test]
fn replay_verification_catches_substitution() {
let mut weights = vec![1u32; 256];
weights[b'x' as usize] = 1000;
let model = Model::from_weights(&weights).unwrap();
let verified = EntropyConfig::new(Config::standard());
let input = vec![b'x'; 256];
let frame = verified.compress(&input, &model).unwrap();
assert!(frame.len() < HEADER_LEN + input.len(), "frame is not coded");
assert_eq!(verified.decompress(&frame, &model).unwrap(), input);
for offset in HEADER_LEN..frame.len() {
let mut tampered = frame.clone();
tampered[offset] ^= 0x40;
let result = verified.decompress(&tampered, &model);
match result {
Err(_) => {}
Ok(decoded) => assert_ne!(decoded, input, "corruption at {offset} is silent"),
}
}
let unchecked = verified.without_replay_verification();
let mut tampered = frame.clone();
tampered[HEADER_LEN] ^= 0x40;
if let Ok(decoded) = unchecked.decompress(&tampered, &model) {
assert_ne!(decoded, input);
}
}
#[test]
fn without_raw_fallback_keeps_every_frame_coded() {
let model = Model::from_uniform(256).unwrap();
let verified = EntropyConfig::new(Config::standard()).without_raw_fallback();
let input: Vec<u8> = (0..4096).map(|i| ((i * 37) ^ (i >> 3)) as u8).collect();
let frame = verified.compress(&input, &model).unwrap();
let flags = u16::from_le_bytes([frame[6], frame[7]]);
assert_eq!(flags & FLAG_RAW, 0, "raw fallback must be off");
assert!(frame.len() >= HEADER_LEN + input.len());
assert_eq!(verified.decompress(&frame, &model).unwrap(), input);
let mut tampered = frame.clone();
tampered[HEADER_LEN] ^= 0x80;
match verified.decompress(&tampered, &model) {
Err(_) => {}
Ok(bytes) => assert_ne!(bytes, input, "corruption is silent"),
}
}
#[test]
fn empty_sequence_roundtrips() {
let config = EntropyConfig::new(Config::standard());
let frame = config.encode_sequence(&[], &[]).unwrap();
assert_eq!(
config.decode_sequence(&[], &frame).unwrap(),
Vec::<u32>::new()
);
}
#[test]
fn schema_model_derives_per_field_alphabets() {
#[allow(dead_code)]
struct Telemetry {
kind: TelemetryKind,
priority: u8,
level: bool,
payload: u8,
}
impl crate::Reflect for Telemetry {
const TYPE_NAME: &'static str = "tests::Telemetry";
const SHAPE: crate::TypeShape = crate::TypeShape::Struct(&[
crate::FieldInfo {
name: "kind",
type_name: "TelemetryKind",
index: 0,
symbols: 0,
},
crate::FieldInfo {
name: "priority",
type_name: "u8",
index: 1,
symbols: 10,
},
crate::FieldInfo {
name: "level",
type_name: "bool",
index: 2,
symbols: 2,
},
crate::FieldInfo {
name: "payload",
type_name: "u8",
index: 3,
symbols: 256,
},
]);
}
#[allow(dead_code)]
enum TelemetryKind {
Temperature,
Pressure,
Humidity,
Wind,
}
impl crate::Reflect for TelemetryKind {
const TYPE_NAME: &'static str = "tests::TelemetryKind";
const SHAPE: crate::TypeShape = crate::TypeShape::Enum(&[
crate::VariantInfo {
name: "Temperature",
index: 0,
fields: &[],
},
crate::VariantInfo {
name: "Pressure",
index: 1,
fields: &[],
},
crate::VariantInfo {
name: "Humidity",
index: 2,
fields: &[],
},
crate::VariantInfo {
name: "Wind",
index: 3,
fields: &[],
},
]);
}
let schema = SchemaModel::from_reflect::<Telemetry>();
let names: Vec<&'static str> = schema.fields().iter().map(|f| f.name).collect();
assert_eq!(names, vec!["kind", "priority", "level", "payload"]);
assert_eq!(schema.fields()[0].model.symbols(), 256);
assert_eq!(schema.fields()[1].model.symbols(), 10);
assert_eq!(schema.fields()[2].model.symbols(), 2);
assert_eq!(schema.fields()[3].model.symbols(), 256);
let enum_schema = SchemaModel::from_reflect::<TelemetryKind>();
assert_eq!(enum_schema.fields()[0].name, "variant");
assert_eq!(enum_schema.fields()[0].model.symbols(), 4);
}
#[test]
fn schema_models_drive_a_sequence() {
#[allow(dead_code)]
struct Frame {
flag: bool,
lane: u8,
}
impl crate::Reflect for Frame {
const TYPE_NAME: &'static str = "tests::Frame";
const SHAPE: crate::TypeShape = crate::TypeShape::Struct(&[
crate::FieldInfo {
name: "flag",
type_name: "bool",
index: 0,
symbols: 2,
},
crate::FieldInfo {
name: "lane",
type_name: "u8",
index: 1,
symbols: 3,
},
]);
}
let schema = SchemaModel::from_reflect::<Frame>();
let field_models = schema.models();
let mut models = Vec::new();
for index in 0..6 {
models.push(field_models[index % 2]);
}
let config = EntropyConfig::new(Config::standard());
let symbols = [1u32, 2, 0, 1, 1, 0];
let frame = config.encode_sequence(&models, &symbols).unwrap();
assert_eq!(config.decode_sequence(&models, &frame).unwrap(), symbols);
}
#[test]
fn invalid_models_and_symbols_are_rejected() {
assert!(Model::from_uniform(0).is_err());
assert!(Model::from_uniform(RANS_M + 1).is_err());
assert!(Model::from_weights(&[]).is_err());
assert!(Model::from_weights(&[0, 0]).is_err());
let model = Model::from_uniform(3).unwrap();
let mut encoder = RansEncoder::new();
assert!(encoder.put_symbol(&model, 3).is_err());
let config = EntropyConfig::new(Config::standard());
let models: Vec<&Model> = core::iter::repeat_n(&model, 2).collect();
let frame = config.encode_sequence(&models, &[0, 1]).unwrap();
let one_model = &models[..1];
assert!(config.decode_sequence(one_model, &frame).is_err());
}
}