pub const NUM_STATES: usize = 256;
const fn build_table() -> ([u8; 512], [u16; 256]) {
let mut next = [0u8; 512];
let mut prob = [2048u16; 256];
let mut n0 = [0u16; 256];
let mut n1 = [0u16; 256];
n0[0] = 0;
n1[0] = 0;
let mut state_idx: usize = 1;
let mut total: u16 = 1;
while total <= 8 {
let mut b: u16 = 0;
while b <= total {
let a = total - b;
if state_idx < 256 {
n0[state_idx] = a;
n1[state_idx] = b;
state_idx += 1;
}
b += 1;
}
total += 1;
}
total = 9;
while total <= 20 {
let mut b: u16 = 0;
while b <= total {
let a = total - b;
let ratio_ok = {
let min_val = if a < b { a } else { b };
let max_val = if a > b { a } else { b };
min_val == 0 || max_val <= min_val * 4
};
if ratio_ok && state_idx < 256 {
n0[state_idx] = a;
n1[state_idx] = b;
state_idx += 1;
}
b += 1;
}
total += 1;
}
let mut run: u16 = 21;
while run <= 60 && state_idx < 256 {
n0[state_idx] = run;
n1[state_idx] = 0;
state_idx += 1;
n0[state_idx] = 0;
n1[state_idx] = run;
state_idx += 1;
run += 3;
}
while state_idx < 256 {
n0[state_idx] = 1;
n1[state_idx] = 1;
state_idx += 1;
}
let mut s: usize = 0;
while s < 256 {
let t = n0[s] as u32 + n1[s] as u32 + 2;
let p = ((n1[s] as u32 + 1) * 4095 + t / 2) / t;
let p = if p < 1 {
1
} else if p > 4095 {
4095
} else {
p
};
prob[s] = p as u16;
s += 1;
}
s = 0;
while s < 256 {
let s_n0 = n0[s];
let s_n1 = n1[s];
let (target_n0_0, target_n1_0) = scale_counts(s_n0 + 1, s_n1);
next[s * 2] = find_closest_state(&n0, &n1, target_n0_0, target_n1_0, s);
let (target_n0_1, target_n1_1) = scale_counts(s_n0, s_n1 + 1);
next[s * 2 + 1] = find_closest_state(&n0, &n1, target_n0_1, target_n1_1, s);
s += 1;
}
(next, prob)
}
const fn scale_counts(a: u16, b: u16) -> (u16, u16) {
let total = a + b;
if total <= 20 {
(a, b)
} else if total <= 40 {
if a >= b {
let new_b = b * 3 / 4;
(
a,
if new_b > 0 {
new_b
} else if b > 0 {
1
} else {
0
},
)
} else {
let new_a = a * 3 / 4;
(
if new_a > 0 {
new_a
} else if a > 0 {
1
} else {
0
},
b,
)
}
} else {
if a >= b {
let new_b = b / 2;
let new_a = if a > 60 { 60 } else { a };
(new_a, new_b)
} else {
let new_a = a / 2;
let new_b = if b > 60 { 60 } else { b };
(new_a, new_b)
}
}
}
const fn find_closest_state(
n0: &[u16; 256],
n1: &[u16; 256],
target_n0: u16,
target_n1: u16,
current: usize,
) -> u8 {
let mut best: usize = 0;
let mut best_dist: u32 = u32::MAX;
let mut i: usize = 0;
while i < 256 {
if i != current || current == 0 {
let d0 = (n0[i] as u32).abs_diff(target_n0 as u32);
let d1 = (n1[i] as u32).abs_diff(target_n1 as u32);
let target_total = target_n0 as u32 + target_n1 as u32;
let state_total = n0[i] as u32 + n1[i] as u32;
let total_diff = state_total.abs_diff(target_total);
let dist = d0 * 3 + d1 * 3 + total_diff;
if dist < best_dist {
best_dist = dist;
best = i;
}
}
i += 1;
}
best as u8
}
static TABLE: ([u8; 512], [u16; 256]) = build_table();
pub struct StateTable;
impl StateTable {
#[inline(always)]
pub fn next(s: u8, bit: u8) -> u8 {
TABLE.0[s as usize * 2 + (bit as usize & 1)]
}
#[inline(always)]
pub fn prob(s: u8) -> u16 {
TABLE.1[s as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn initial_state_is_balanced() {
assert_eq!(StateTable::prob(0), 2048);
}
#[test]
fn state_0_transitions_are_distinct() {
let next0 = StateTable::next(0, 0);
let next1 = StateTable::next(0, 1);
assert_ne!(next0, next1, "state 0 transitions should differ");
assert!(
StateTable::prob(next0) < 2048,
"bit=0 should go to low-probability state, got {}",
StateTable::prob(next0)
);
assert!(
StateTable::prob(next1) > 2048,
"bit=1 should go to high-probability state, got {}",
StateTable::prob(next1)
);
}
#[test]
fn probabilities_are_in_range() {
for s in 0..NUM_STATES {
let p = StateTable::prob(s as u8);
assert!((1..=4095).contains(&p), "state {s}: prob {p} out of range");
}
}
#[test]
fn transitions_stay_in_range() {
for s in 0..NUM_STATES {
for bit in 0..2u8 {
let next = StateTable::next(s as u8, bit);
assert!(
(next as usize) < NUM_STATES,
"state {s}, bit {bit}: next={next} out of range"
);
}
}
}
#[test]
fn repeated_zeros_decrease_probability() {
let mut s = 0u8;
s = StateTable::next(s, 0); let p1 = StateTable::prob(s);
s = StateTable::next(s, 0); let p2 = StateTable::prob(s);
assert!(p2 <= p1, "more 0s should decrease P(1): p1={p1}, p2={p2}");
}
#[test]
fn repeated_ones_increase_probability() {
let mut s = 0u8;
s = StateTable::next(s, 1); let p1 = StateTable::prob(s);
s = StateTable::next(s, 1); let p2 = StateTable::prob(s);
assert!(p2 >= p1, "more 1s should increase P(1): p1={p1}, p2={p2}");
}
#[test]
fn no_state_maps_to_zero_from_nonzero() {
for s in 1..NUM_STATES {
for bit in 0..2u8 {
let next = StateTable::next(s as u8, bit);
if s > 2 {
assert!(
next != 0,
"state {s}, bit {bit}: should not transition back to state 0"
);
}
}
}
}
#[test]
fn convergence_all_zeros() {
let mut s = 0u8;
for _ in 0..50 {
s = StateTable::next(s, 0);
}
let p = StateTable::prob(s);
assert!(p < 200, "50 zeros should give very low P(1): {p}");
}
#[test]
fn convergence_all_ones() {
let mut s = 0u8;
for _ in 0..50 {
s = StateTable::next(s, 1);
}
let p = StateTable::prob(s);
assert!(p > 3900, "50 ones should give very high P(1): {p}");
}
#[test]
fn mixed_sequence_stays_moderate() {
let mut s = 0u8;
for i in 0..100 {
s = StateTable::next(s, (i & 1) as u8);
}
let p = StateTable::prob(s);
assert!(
(500..=3500).contains(&p),
"alternating bits should give moderate P(1): {p}"
);
}
}