const TABLE_C2: [(u16, u8, u8, u8); 47] = [
(22017, 1, 1, 1), (13313, 2, 6, 0), (6145, 3, 9, 0), (2753, 4, 12, 0), (1313, 5, 29, 0), (545, 38, 33, 0), (22017, 7, 6, 1), (21505, 8, 14, 0), (18433, 9, 14, 0), (14337, 10, 14, 0), (12289, 11, 17, 0), (9217, 12, 18, 0), (7169, 13, 20, 0), (5633, 29, 21, 0), (22017, 15, 14, 1), (21505, 16, 14, 0), (20737, 17, 15, 0), (18433, 18, 16, 0), (14337, 19, 17, 0), (13313, 20, 18, 0), (12289, 21, 19, 0), (10241, 22, 19, 0), (9217, 23, 20, 0), (8705, 24, 21, 0), (7169, 25, 22, 0), (6145, 26, 23, 0), (5633, 27, 24, 0), (5121, 28, 25, 0), (4609, 29, 26, 0), (4353, 30, 27, 0), (2753, 31, 28, 0), (2497, 32, 29, 0), (2209, 33, 30, 0), (1313, 34, 31, 0), (1089, 35, 32, 0), (673, 36, 33, 0), (545, 37, 34, 0), (321, 38, 35, 0), (273, 39, 36, 0), (133, 40, 37, 0), (73, 41, 38, 0), (37, 42, 39, 0), (21, 43, 40, 0), (9, 44, 41, 0), (5, 45, 42, 0), (1, 45, 43, 0), (22017, 46, 46, 0), ];
#[derive(Clone, Copy, Debug)]
pub(crate) struct MqContext {
pub index: u8,
pub mps: u8,
}
impl MqContext {
pub(crate) fn new(index: u8) -> Self {
MqContext { index, mps: 0 }
}
fn row(&self) -> (u16, u8, u8, u8) {
TABLE_C2[usize::from(self.index).min(TABLE_C2.len() - 1)]
}
}
pub(crate) struct MqDecoder<'a> {
data: &'a [u8],
c: u32,
a: u32,
ct: u32,
bp: usize,
}
impl<'a> MqDecoder<'a> {
pub(crate) fn new(data: &'a [u8]) -> Self {
let mut dec = MqDecoder {
data,
c: 0,
a: 0,
ct: 0,
bp: 0,
};
dec.c = u32::from(dec.byte(dec.bp)) << 16;
dec.byte_in();
dec.c <<= 7;
dec.ct -= 7;
dec.a = 0x8000;
dec
}
fn byte(&self, i: usize) -> u8 {
self.data.get(i).copied().unwrap_or(255)
}
fn byte_in(&mut self) {
if self.byte(self.bp) == 255 {
if self.byte(self.bp + 1) > 143 {
self.c = self.c.wrapping_add(0xFF00);
self.ct = 8;
} else {
self.bp += 1;
self.c = self.c.wrapping_add(u32::from(self.byte(self.bp)) << 9);
self.ct = 7;
}
} else {
self.bp += 1;
self.c = self.c.wrapping_add(u32::from(self.byte(self.bp)) << 8);
self.ct = 8;
}
}
fn renormd(&mut self) {
for _ in 0..16 {
if self.ct == 0 {
self.byte_in();
}
self.a <<= 1;
self.c <<= 1;
self.ct -= 1;
if self.a & 0x8000 != 0 {
break;
}
}
}
fn mps_exchange(&self, cx: &mut MqContext) -> u8 {
let (qe, nmps, nlps, switch) = cx.row();
if self.a < u32::from(qe) {
let d = 1 - cx.mps;
if switch == 1 {
cx.mps = 1 - cx.mps;
}
cx.index = nlps;
d
} else {
let d = cx.mps;
cx.index = nmps;
d
}
}
fn lps_exchange(&mut self, cx: &mut MqContext) -> u8 {
let (qe, nmps, nlps, switch) = cx.row();
let d = if self.a < u32::from(qe) {
cx.index = nmps;
cx.mps
} else {
let d = 1 - cx.mps;
if switch == 1 {
cx.mps = 1 - cx.mps;
}
cx.index = nlps;
d
};
self.a = u32::from(qe);
d
}
pub(crate) fn decode(&mut self, cx: &mut MqContext) -> u32 {
let qe = u32::from(cx.row().0);
self.a = self.a.wrapping_sub(qe);
let d = if (self.c >> 16) < qe {
let d = self.lps_exchange(cx);
self.renormd();
d
} else {
self.c = self.c.wrapping_sub(qe << 16);
if self.a & 0x8000 == 0 {
let d = self.mps_exchange(cx);
self.renormd();
d
} else {
cx.mps
}
};
u32::from(d)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn decode_run(data: &[u8], contexts: &mut [MqContext], n: usize) -> String {
let mut dec = MqDecoder::new(data);
(0..n)
.map(|i| {
let d = dec.decode(&mut contexts[i % contexts.len()]);
assert!(d <= 1, "decision {i} was {d}, not a binary decision");
if d == 0 {
'0'
} else {
'1'
}
})
.collect()
}
#[test]
fn table_c2_shape() {
assert_eq!(TABLE_C2.len(), 47);
for (idx, &(qe, nmps, nlps, switch)) in TABLE_C2.iter().enumerate() {
assert!(usize::from(nmps) < 47, "row {idx} NMPS out of range");
assert!(usize::from(nlps) < 47, "row {idx} NLPS out of range");
assert!(switch <= 1, "row {idx} SWITCH must be 0 or 1");
assert!(qe >= 1, "row {idx} Qe must be non-zero");
assert!(qe <= 22017, "row {idx} Qe exceeds the half interval");
}
}
#[test]
fn table_c2_irregular_rows() {
assert_eq!(TABLE_C2[0], (22017, 1, 1, 1));
assert_eq!(TABLE_C2[4], (1313, 5, 29, 0));
assert_eq!(TABLE_C2[5], (545, 38, 33, 0));
assert_eq!(TABLE_C2[6], (22017, 7, 6, 1));
assert_eq!(TABLE_C2[13], (5633, 29, 21, 0));
assert_eq!(TABLE_C2[14], (22017, 15, 14, 1));
assert_eq!(TABLE_C2[45], (1, 45, 43, 0));
assert_eq!(TABLE_C2[46], (22017, 46, 46, 0));
let switches: Vec<usize> = TABLE_C2
.iter()
.enumerate()
.filter(|(_, row)| row.3 == 1)
.map(|(idx, _)| idx)
.collect();
assert_eq!(switches, vec![0, 6, 14]);
}
#[test]
fn the_estimator_moves_the_right_direction() {
for (idx, &(qe, nmps, nlps, switch)) in TABLE_C2.iter().enumerate() {
let after_mps = TABLE_C2[usize::from(nmps)].0;
assert!(after_mps <= qe, "row {idx}: an MPS raised Qe");
if switch == 0 {
let after_lps = TABLE_C2[usize::from(nlps)].0;
assert!(after_lps >= qe, "row {idx}: an LPS lowered Qe");
}
}
}
#[test]
fn all_zero_stream_matches_the_hand_trace() {
let mut cx = [MqContext::new(0)];
let bits = decode_run(&[0u8; 64], &mut cx, 16);
assert_eq!(bits, "0110101010101010");
assert_eq!(cx[0].index, 6);
assert_eq!(cx[0].mps, 0);
}
#[test]
fn mps_branch_matches_the_hand_trace() {
let mut cx = [MqContext::new(0)];
let bits = decode_run(&[0x80, 0x00, 0x00, 0x00], &mut cx, 5);
assert_eq!(bits, "00000");
assert_eq!(cx[0].index, 2);
assert_eq!(cx[0].mps, 0);
}
#[test]
fn stuffed_byte_branch_matches_the_hand_trace() {
let mut cx = [MqContext::new(0)];
let bits = decode_run(&[0xFF, 0x00], &mut cx, 4);
assert_eq!(bits, "1111");
assert_eq!(cx[0].index, 2);
assert_eq!(cx[0].mps, 1);
}
#[test]
fn stuffed_bit_shift_is_nine_not_eight() {
let mut cx = [MqContext::new(0)];
let bits = decode_run(&[0xFF, 0x8E], &mut cx, 24);
assert_eq!(bits, "111111111111111100011111");
assert_eq!((cx[0].index, cx[0].mps), (26, 1));
}
#[test]
fn matches_the_coder_family_oracle_over_random_data() {
let data: Vec<u8> = (0..96u32).map(|i| ((i * 37 + 11) % 256) as u8).collect();
let mut cx = [MqContext::new(0); 8];
let bits = decode_run(&data, &mut cx, 256);
let want = concat!(
"0111011100010100000111110011111000010110011111000001111100011111",
"0011111000011111000111000011110000111100000111110101111101111110",
"1001110010011110001101010011110100010101110111110001110100111100",
"1101110000011100100111000001011110011110001111111011110100011111",
);
assert_eq!(bits, want);
let states: Vec<(u8, u8)> = cx.iter().map(|c| (c.index, c.mps)).collect();
assert_eq!(
states,
vec![
(16, 0),
(22, 0),
(17, 0),
(5, 1),
(22, 1),
(5, 1),
(14, 0),
(14, 0)
]
);
}
#[test]
fn empty_segment_decodes_from_ff_padding() {
let mut cx = [MqContext::new(0)];
let bits = decode_run(&[], &mut cx, 8);
assert_eq!(bits, "11111111");
assert_eq!((cx[0].index, cx[0].mps), (3, 1));
}
#[test]
fn exhausted_segments_keep_producing_decisions() {
for data in [
vec![],
vec![0xFF],
vec![0xFF, 0x8F],
vec![0xFF, 0x90],
vec![0xFF, 0xFF],
vec![0x84, 0xC7, 0x3B],
vec![0x00],
] {
let mut dec = MqDecoder::new(&data);
let mut cx = [MqContext::new(0); 4];
for i in 0..10_000 {
let d = dec.decode(&mut cx[i % 4]);
assert!(d <= 1, "{data:?} yielded {d} at step {i}");
}
}
}
#[test]
fn state_46_never_adapts() {
let data: Vec<u8> = (0..64u32).map(|i| ((i * 151 + 3) % 256) as u8).collect();
let mut dec = MqDecoder::new(&data);
let mut cx = MqContext::new(46);
for _ in 0..500 {
let d = dec.decode(&mut cx);
assert!(d <= 1);
assert_eq!(cx.index, 46, "the uniform context left state 46");
}
}
#[test]
fn decoding_is_deterministic() {
let data: Vec<u8> = (0..48u32).map(|i| ((i * 91 + 17) % 256) as u8).collect();
let run = || {
let mut cx = [MqContext::new(0); 3];
decode_run(&data, &mut cx, 200)
};
let first = run();
assert_eq!(first, run());
assert!(first.contains('0') && first.contains('1'));
}
}