use std::collections::HashSet;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Call {
Insert(u64),
InsertIfAbsent(u64),
GetOrInsert(u64),
Remove,
Get,
Clear,
}
#[derive(Clone, Copy, Debug)]
pub struct Event {
pub call: Call,
pub answer: Option<u64>,
pub invoked: u64,
pub answered: u64,
}
fn apply(call: Call, state: Option<u64>) -> (Option<u64>, Option<u64>) {
match call {
Call::Insert(v) => (state, Some(v)),
Call::InsertIfAbsent(v) => match state {
None => (None, Some(v)),
Some(cur) => (Some(cur), state),
},
Call::GetOrInsert(v) => match state {
None => (Some(v), Some(v)),
Some(cur) => (Some(cur), state),
},
Call::Remove => (state, None),
Call::Get => (state, state),
Call::Clear => (None, None),
}
}
pub fn linearizable(history: &[Event], initial: Option<u64>) -> bool {
assert!(history.len() <= 63, "a history of at most 63 calls");
let full: u64 = (1u64 << history.len()) - 1;
let mut seen: HashSet<(u64, Option<u64>)> = HashSet::new();
let mut stack = vec![(0u64, initial)];
while let Some((done, state)) = stack.pop() {
if done == full {
return true;
}
if !seen.insert((done, state)) {
continue;
}
let horizon = history
.iter()
.enumerate()
.filter(|(i, _)| done & (1 << i) == 0)
.map(|(_, e)| e.answered)
.min()
.unwrap_or(u64::MAX);
for (i, e) in history.iter().enumerate() {
if done & (1 << i) != 0 || e.invoked > horizon {
continue;
}
let (answer, next) = apply(e.call, state);
if answer == e.answer {
stack.push((done | (1 << i), next));
}
}
}
false
}