#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Class {
Space,
Break,
Lower,
Upper,
Digit,
Punct,
High,
}
pub const CLASSES: usize = 7;
impl Class {
pub const ALL: [Self; CLASSES] = [
Self::Space,
Self::Break,
Self::Lower,
Self::Upper,
Self::Digit,
Self::Punct,
Self::High,
];
#[must_use]
pub const fn of(b: u8) -> Self {
match b {
b' ' | b'\t' => Self::Space,
b'\n' | b'\r' => Self::Break,
b'a'..=b'z' => Self::Lower,
b'A'..=b'Z' => Self::Upper,
b'0'..=b'9' => Self::Digit,
0x21..=0x2F | 0x3A..=0x40 | 0x5B..=0x60 | 0x7B..=0x7E => Self::Punct,
_ => Self::High,
}
}
}
const MEMBERS: [f64; CLASSES] = {
let mut n = [0.0f64; CLASSES];
let mut b = 0u8;
loop {
n[Class::of(b) as usize] += 1.0;
if b == u8::MAX {
break n;
}
b += 1;
}
};
#[must_use]
pub const fn members(class: Class) -> f64 {
MEMBERS[class as usize]
}
#[derive(Debug, Clone, Copy)]
pub struct Chain {
pub next: [[f64; CLASSES]; CLASSES],
pub start: [f64; CLASSES],
}
impl Chain {
#[must_use]
pub fn bytes_after(&self, from: Class) -> [f64; 256] {
let row = self.next[from as usize];
let mut w = [0.0f64; 256];
for (slot, b) in w.iter_mut().zip(0..=u8::MAX) {
let class = Class::of(b) as usize;
*slot = row[class] / MEMBERS[class];
}
w
}
#[must_use]
pub const fn memoryless(marginal: [f64; CLASSES]) -> Self {
Self {
next: [marginal; CLASSES],
start: marginal,
}
}
}
const UNIFORM: Chain = Chain::memoryless({
let mut m = MEMBERS;
let mut i = 0;
while i < CLASSES {
m[i] /= 256.0;
i += 1;
}
m
});
const TEXT: Chain = Chain::memoryless([0.222, 0.030, 0.487, 0.055, 0.015, 0.191, 0.0001]);
#[rustfmt::skip]
pub const SOURCE: Chain = Chain {
next: [
[0.451677, 0.000000, 0.333127, 0.042422, 0.016962, 0.149964, 0.005848], [0.713848, 0.104793, 0.063638, 0.016030, 0.000283, 0.101210, 0.000199], [0.082433, 0.008042, 0.768254, 0.030242, 0.009533, 0.101450, 0.000046], [0.042460, 0.007471, 0.514333, 0.356499, 0.002727, 0.076414, 0.000095], [0.110313, 0.037564, 0.075528, 0.023526, 0.386326, 0.365925, 0.000818], [0.211487, 0.139038, 0.299459, 0.076937, 0.020348, 0.252417, 0.000314], [0.066678, 0.008770, 0.001777, 0.000535, 0.001748, 0.003810, 0.916681], ],
start: [0.181717, 0.027072, 0.570280, 0.055983, 0.018575, 0.132492, 0.013881],
};
pub const SOURCE_BYTES: [f64; 256] = [
0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000,
0.00000000, 0.02348194, 0.02707233, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000,
0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000,
0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000,
0.15823466, 0.00093228, 0.01212123, 0.00084917, 0.00016688, 0.00035293, 0.00058606, 0.00179882,
0.01068197, 0.01038673, 0.00162193, 0.00150419, 0.01070865, 0.00637019, 0.01385492, 0.00496256,
0.00523016, 0.00400453, 0.00219353, 0.00122989, 0.00159107, 0.00078858, 0.00147180, 0.00072862,
0.00097900, 0.00035768, 0.00707436, 0.00110169, 0.00086292, 0.00846172, 0.00127486, 0.00011155,
0.00020109, 0.00435993, 0.00116575, 0.00333640, 0.00214529, 0.00586268, 0.00200241, 0.00110330,
0.00083414, 0.00317845, 0.00018502, 0.00039273, 0.00256395, 0.00203484, 0.00377334, 0.00244100,
0.00207299, 0.00029789, 0.00431163, 0.00501794, 0.00464331, 0.00189854, 0.00103260, 0.00053590,
0.00033695, 0.00035935, 0.00009711, 0.00275525, 0.00427699, 0.00275255, 0.00001399, 0.01390198,
0.00442084, 0.03637424, 0.00817733, 0.02109737, 0.02265156, 0.07370121, 0.01314766, 0.01015872,
0.01218381, 0.04055988, 0.00110193, 0.00451404, 0.02458769, 0.01465999, 0.04106374, 0.03908288,
0.01906260, 0.00161610, 0.04674633, 0.03927468, 0.05454883, 0.01670062, 0.00695385, 0.00526917,
0.00823928, 0.00747249, 0.00133413, 0.00376073, 0.00083514, 0.00375906, 0.00002829, 0.00000000,
0.00421794, 0.00000110, 0.00001260, 0.00000134, 0.00000124, 0.00000061, 0.00015301, 0.00000790,
0.00001624, 0.00002082, 0.00000115, 0.00000051, 0.00000202, 0.00000034, 0.00000030, 0.00000021,
0.00012277, 0.00000094, 0.00015467, 0.00000512, 0.00420130, 0.00011869, 0.00000476, 0.00001790,
0.00000150, 0.00000086, 0.00000103, 0.00000013, 0.00000583, 0.00000068, 0.00000024, 0.00000232,
0.00000219, 0.00000112, 0.00000693, 0.00000202, 0.00000609, 0.00000750, 0.00002823, 0.00001946,
0.00000007, 0.00000179, 0.00000098, 0.00000192, 0.00000080, 0.00000022, 0.00000025, 0.00000016,
0.00000077, 0.00000482, 0.00000504, 0.00000228, 0.00000031, 0.00000454, 0.00000109, 0.00003290,
0.00000122, 0.00000089, 0.00000067, 0.00000204, 0.00000256, 0.00000174, 0.00000012, 0.00000018,
0.00000000, 0.00000000, 0.00006262, 0.00002132, 0.00000003, 0.00000016, 0.00000000, 0.00000001,
0.00000000, 0.00000305, 0.00000147, 0.00000147, 0.00000119, 0.00000000, 0.00001351, 0.00000446,
0.00000129, 0.00000048, 0.00000001, 0.00000000, 0.00000009, 0.00000000, 0.00000000, 0.00000000,
0.00000024, 0.00000055, 0.00000000, 0.00000006, 0.00000000, 0.00000000, 0.00000000, 0.00000000,
0.00000001, 0.00000106, 0.00454794, 0.00000024, 0.00000058, 0.00000043, 0.00000042, 0.00000010,
0.00000021, 0.00000003, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000021,
0.00000083, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000,
0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Prior {
Uniform,
Text,
Source,
}
pub const DEFAULT_CHAINS: [Chain; Prior::ALL.len()] = [
Prior::Uniform.chain(),
Prior::Text.chain(),
Prior::Source.chain(),
];
impl Prior {
pub const ALL: [Self; 3] = [Self::Uniform, Self::Text, Self::Source];
#[must_use]
pub const fn chain(self) -> Chain {
match self {
Self::Uniform => UNIFORM,
Self::Text => TEXT,
Self::Source => SOURCE,
}
}
#[must_use]
pub fn byte_freq(self) -> [f64; 256] {
match self {
Self::Source => SOURCE_BYTES,
other => {
let chain = other.chain();
let mut out = [0.0f64; 256];
for (slot, b) in out.iter_mut().zip(0..=u8::MAX) {
let class = Class::of(b);
*slot = chain.start[class as usize] / members(class);
}
out
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_class_holds_at_least_one_byte_and_they_partition_256() {
assert_eq!(MEMBERS.iter().sum::<f64>(), 256.0);
assert!(MEMBERS.iter().all(|&n| n > 0.0));
}
#[test]
fn every_row_of_every_chain_is_a_distribution() {
for prior in Prior::ALL {
let chain = prior.chain();
for (i, row) in chain.next.iter().enumerate() {
let sum: f64 = row.iter().sum();
assert!((sum - 1.0).abs() < 1e-3, "{prior:?} row {i} sums to {sum}");
}
let start: f64 = chain.start.iter().sum();
assert!(
(start - 1.0).abs() < 1e-3,
"{prior:?} start sums to {start}"
);
for class in Class::ALL {
let bytes: f64 = chain.bytes_after(class).iter().sum();
assert!(
(bytes - 1.0).abs() < 1e-3,
"{prior:?} after {class:?}: {bytes}"
);
}
}
}
#[test]
fn the_measured_chain_is_persistent_where_the_memoryless_one_is_not() {
for class in Class::ALL {
let i = class as usize;
let marginal = SOURCE.start[i];
let repeat = SOURCE.next[i][i];
assert!(
repeat > marginal,
"{class:?} must be likelier to repeat than to occur: {repeat} vs {marginal}"
);
assert!(
(TEXT.next[i][i] - TEXT.start[i]).abs() < 1e-9,
"a memoryless prior is one with no persistence to speak of"
);
}
}
#[test]
fn the_sparse_classes_are_the_ones_a_memoryless_prior_misprices() {
for class in [Class::Digit, Class::Upper, Class::High] {
let i = class as usize;
let ratio = SOURCE.next[i][i] / SOURCE.start[i];
assert!(ratio > 6.0, "{class:?} persistence ratio only {ratio}");
}
}
}