use super::context::NUM_REGULAR_CONTEXTS;
pub const J: [i32; 31] = [
0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14,
15,
];
pub const RUN_THRESHOLD: [i32; 31] = [
1 << 0,
1 << 0,
1 << 0,
1 << 1,
1 << 1,
1 << 1,
1 << 1,
1 << 2,
1 << 2,
1 << 2,
1 << 2,
1 << 3,
1 << 3,
1 << 3,
1 << 3,
1 << 4,
1 << 4,
1 << 5,
1 << 5,
1 << 6,
1 << 6,
1 << 7,
1 << 7,
1 << 8,
1 << 9,
1 << 10,
1 << 11,
1 << 12,
1 << 13,
1 << 14,
1 << 15,
];
pub const MAX_RUN_INDEX: usize = 30;
pub const RUN_TERMINATION_CTX_BASE: usize = NUM_REGULAR_CONTEXTS;
pub const NUM_RUN_TERMINATION_CONTEXTS: usize = 2;
pub const TOTAL_CONTEXT_COUNT: usize = NUM_REGULAR_CONTEXTS + NUM_RUN_TERMINATION_CONTEXTS;
#[derive(Clone, Copy, Debug, Default)]
pub struct RunState {
pub run_index: usize,
pub run_value: i32,
}
impl RunState {
#[must_use]
pub fn new() -> Self {
Self {
run_index: 0,
run_value: 0,
}
}
pub fn reset_at_line_start(&mut self) {
self.run_index = 0;
}
}
#[inline]
#[must_use]
pub fn enter_run_lossless(d1: i32, d2: i32, d3: i32) -> bool {
d1 == 0 && d2 == 0 && d3 == 0
}
#[inline]
#[must_use]
pub fn enter_run_near(d1: i32, d2: i32, d3: i32, near: i32) -> bool {
d1.abs() <= near && d2.abs() <= near && d3.abs() <= near
}
#[inline]
pub fn bump_run_index(state: &mut RunState) {
if state.run_index < MAX_RUN_INDEX {
state.run_index += 1;
}
}
#[inline]
pub fn decrement_run_index(state: &mut RunState) {
if state.run_index > 0 {
state.run_index -= 1;
}
}
#[inline]
#[must_use]
pub fn run_termination_ctx(ra: i32, rb: i32) -> usize {
if ra == rb {
RUN_TERMINATION_CTX_BASE
} else {
RUN_TERMINATION_CTX_BASE + 1
}
}
#[inline]
#[must_use]
pub fn threshold_for(run_index: usize) -> i32 {
if run_index < RUN_THRESHOLD.len() {
RUN_THRESHOLD[run_index]
} else {
0
}
}
#[inline]
#[must_use]
pub fn j_for(run_index: usize) -> i32 {
if run_index < J.len() {
J[run_index]
} else {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn j_table_has_31_entries() {
assert_eq!(J.len(), 31);
assert_eq!(RUN_THRESHOLD.len(), 31);
}
#[test]
fn j_table_matches_iso_table_a5() {
assert_eq!(J[0], 0);
assert_eq!(J[2], 0);
assert_eq!(J[3], 1);
assert_eq!(J[7], 2);
assert_eq!(J[15], 4);
assert_eq!(J[16], 4);
assert_eq!(J[17], 5);
assert_eq!(J[30], 15);
}
#[test]
fn thresholds_consistent_with_j() {
for (idx, &j) in J.iter().enumerate() {
assert_eq!(
RUN_THRESHOLD[idx],
1 << j,
"RUN_THRESHOLD[{idx}] mismatched against 1 << J[{idx}] = 1 << {j}"
);
}
}
#[test]
fn enter_run_lossless_only_when_all_zero() {
assert!(enter_run_lossless(0, 0, 0));
assert!(!enter_run_lossless(1, 0, 0));
assert!(!enter_run_lossless(0, 1, 0));
assert!(!enter_run_lossless(0, 0, 1));
assert!(!enter_run_lossless(-1, 0, 0));
}
#[test]
fn enter_run_near_respects_near_bound() {
assert!(enter_run_near(0, 0, 0, 0));
assert!(enter_run_near(2, -2, 1, 2));
assert!(!enter_run_near(3, 0, 0, 2));
assert!(!enter_run_near(0, -3, 0, 2));
assert!(!enter_run_near(0, 0, 3, 2));
}
#[test]
fn bump_run_index_is_capped_at_max() {
let mut state = RunState::new();
for _ in 0..100 {
bump_run_index(&mut state);
}
assert_eq!(state.run_index, MAX_RUN_INDEX);
}
#[test]
fn decrement_run_index_saturates_at_zero() {
let mut state = RunState::new();
decrement_run_index(&mut state);
assert_eq!(state.run_index, 0);
bump_run_index(&mut state);
assert_eq!(state.run_index, 1);
decrement_run_index(&mut state);
assert_eq!(state.run_index, 0);
}
#[test]
fn termination_context_selects_365_or_366() {
assert_eq!(run_termination_ctx(100, 100), 365);
assert_eq!(run_termination_ctx(100, 50), 366);
assert_eq!(run_termination_ctx(0, 1), 366);
assert_eq!(run_termination_ctx(0, 0), 365);
}
#[test]
fn threshold_for_matches_run_threshold_table() {
for idx in 0..=MAX_RUN_INDEX {
assert_eq!(threshold_for(idx), RUN_THRESHOLD[idx]);
}
assert_eq!(threshold_for(MAX_RUN_INDEX + 1), 0);
}
#[test]
fn j_for_matches_j_table() {
for idx in 0..=MAX_RUN_INDEX {
assert_eq!(j_for(idx), J[idx]);
}
assert_eq!(j_for(MAX_RUN_INDEX + 1), 0);
}
#[test]
fn reset_at_line_start_zeroes_index() {
let mut state = RunState::new();
state.run_index = 17;
state.run_value = 42;
state.reset_at_line_start();
assert_eq!(state.run_index, 0);
}
#[test]
fn total_context_count_is_367() {
assert_eq!(TOTAL_CONTEXT_COUNT, 367);
assert_eq!(RUN_TERMINATION_CTX_BASE, 365);
}
}