pub type Tick = u64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FutureFn {
Future,
Rising,
Falling,
Steady,
Changing,
}
pub fn first_tick_after(ticks: &[Tick], t: Tick) -> Option<Tick> {
ticks.iter().copied().find(|&g| g > t)
}
pub fn action_tick(global_ticks: &[Tick], attempt_end: Tick) -> Option<Tick> {
first_tick_after(global_ticks, attempt_end)
}
pub fn eval(f: FutureFn, cur: u8, next: u8) -> u8 {
let truth = |b: bool| if b { b'1' } else { b'0' };
match f {
FutureFn::Future => next,
FutureFn::Rising => truth(next == b'1' && cur != b'1'),
FutureFn::Falling => truth(next == b'0' && cur != b'0'),
FutureFn::Steady => truth(cur == next),
FutureFn::Changing => truth(cur != next),
}
}
pub fn eval_at(f: FutureFn, samples: &[u8], i: usize) -> Option<u8> {
let next = *samples.get(i + 1)?;
Some(eval(f, samples[i], next))
}
#[cfg(test)]
mod tests {
use super::*;
const SIG: &[u8] = b"110000111000";
const CLK: &[u8] = b"110011001100";
const TICKS: [Tick; 12] = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110];
fn at(time: Tick) -> usize {
TICKS
.iter()
.position(|&t| t == time)
.expect("a global tick")
}
fn row(time: Tick) -> [u8; 6] {
let i = at(time);
[
SIG[i],
eval_at(FutureFn::Future, SIG, i).unwrap(),
eval_at(FutureFn::Rising, SIG, i).unwrap(),
eval_at(FutureFn::Falling, SIG, i).unwrap(),
eval_at(FutureFn::Changing, SIG, i).unwrap(),
eval_at(FutureFn::Steady, SIG, i).unwrap(),
]
}
#[test]
fn table_16_2_rows() {
assert_eq!(&row(10), b"100110");
assert_eq!(&row(30), b"000001");
assert_eq!(&row(40), b"000001");
assert_eq!(&row(50), b"011010");
assert_eq!(&row(80), b"100110");
}
#[test]
fn the_last_tick_has_no_future() {
assert_eq!(eval_at(FutureFn::Future, SIG, SIG.len() - 1), None);
assert_eq!(eval_at(FutureFn::Steady, SIG, SIG.len() - 1), None);
assert!(eval_at(FutureFn::Steady, SIG, SIG.len() - 2).is_some());
}
#[test]
fn four_state_edges_and_equality() {
for (cur, next, rising, falling, steady) in [
(b'0', b'1', b'1', b'0', b'0'),
(b'1', b'0', b'0', b'1', b'0'),
(b'x', b'1', b'1', b'0', b'0'),
(b'1', b'x', b'0', b'0', b'0'),
(b'x', b'0', b'0', b'1', b'0'),
(b'x', b'x', b'0', b'0', b'1'),
(b'z', b'x', b'0', b'0', b'0'),
(b'1', b'1', b'0', b'0', b'1'),
] {
let case = format!("{} -> {}", cur as char, next as char);
assert_eq!(eval(FutureFn::Rising, cur, next), rising, "{case}");
assert_eq!(eval(FutureFn::Falling, cur, next), falling, "{case}");
assert_eq!(eval(FutureFn::Steady, cur, next), steady, "{case}");
assert_eq!(eval(FutureFn::Changing, cur, next), steady ^ 1, "{case}");
assert_eq!(eval(FutureFn::Future, cur, next), next, "{case}");
}
}
#[test]
fn first_tick_after_is_strict() {
assert_eq!(first_tick_after(&TICKS, 80), Some(90));
assert_eq!(first_tick_after(&TICKS, 85), Some(90));
assert_eq!(first_tick_after(&TICKS, 79), Some(80));
assert_eq!(first_tick_after(&TICKS, 110), None);
assert_eq!(first_tick_after(&[], 0), None);
}
fn violations(rst: Option<&[u8]>) -> Vec<Tick> {
(0..SIG.len())
.filter(|&i| rst.is_none_or(|r| r[i] != b'1'))
.filter(|&i| {
let (Some(ch), Some(fall)) = (
eval_at(FutureFn::Changing, SIG, i),
eval_at(FutureFn::Falling, CLK, i),
) else {
return false;
};
ch == b'1' && fall != b'1'
})
.map(|i| TICKS[i])
.collect()
}
#[test]
fn example_1_fails_at_80_and_the_error_executes_at_90() {
assert_eq!(violations(None), [80]);
let end = violations(None)[0];
assert_eq!(action_tick(&TICKS, end), Some(90));
assert_eq!(end, 80);
}
#[test]
fn example_3_an_ended_attempt_is_not_disabled_by_a_later_rst() {
let rst_at = |t: Tick| u8::from((82..=84).contains(&t)) + b'0';
let sampled: Vec<u8> = TICKS.iter().map(|&t| rst_at(t)).collect();
assert!(sampled.iter().all(|&b| b == b'0'));
assert_eq!(violations(Some(&sampled)), [80]);
let mut at_80 = sampled.clone();
at_80[at(80)] = b'1';
assert_eq!(violations(Some(&at_80)), Vec::<Tick>::new());
}
#[test]
fn example_4_stable_one_tick_later_is_steady() {
let stable = |i: usize| SIG[i] == if i > 0 { SIG[i - 1] } else { b'x' };
for i in 0..SIG.len() - 1 {
let a4 = stable(i + 1);
let a5 = eval_at(FutureFn::Steady, SIG, i) == Some(b'1');
assert_eq!(a4, a5, "attempt at tick {i}");
}
assert!(!stable(0));
assert_eq!(eval_at(FutureFn::Steady, SIG, 0), Some(b'1'));
}
}