extern crate alloc;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::vec::Vec;
mod examples;
use super::support::dot;
use crate::metis::Dot;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) struct Contradiction {
pub(super) cycle: Vec<Dot>,
pub(super) witnesses: Vec<usize>,
}
#[derive(Clone, Debug, Default)]
pub(super) struct OrderLedger {
nodes: BTreeSet<Dot>,
edges: BTreeMap<(Dot, Dot), usize>,
observations: usize,
}
impl OrderLedger {
pub(super) const fn new() -> Self {
Self {
nodes: BTreeSet::new(),
edges: BTreeMap::new(),
observations: 0,
}
}
pub(super) fn observe(&mut self, state: &[Dot]) -> usize {
let index = self.observations;
for &dot in state {
let _ = self.nodes.insert(dot);
}
for pair in state.windows(2) {
let _ = self.edges.entry((pair[0], pair[1])).or_insert(index);
}
self.observations += 1;
index
}
pub(super) const fn observations(&self) -> usize {
self.observations
}
pub(super) fn verdict(&self) -> Result<Vec<Dot>, Contradiction> {
let mut incoming: BTreeMap<Dot, usize> = self.nodes.iter().map(|&n| (n, 0)).collect();
let mut forward: BTreeMap<Dot, BTreeSet<Dot>> = BTreeMap::new();
for &(u, v) in self.edges.keys() {
if forward.entry(u).or_default().insert(v) {
*incoming.entry(v).or_default() += 1;
}
}
let mut ready: BTreeSet<Dot> = incoming
.iter()
.filter_map(|(&n, °ree)| (degree == 0).then_some(n))
.collect();
let mut witness: Vec<Dot> = Vec::with_capacity(self.nodes.len());
while let Some(&next) = ready.iter().next() {
let _ = ready.remove(&next);
witness.push(next);
if let Some(successors) = forward.get(&next) {
for &v in successors {
let degree = incoming.get_mut(&v).expect("every edge end is a node");
*degree -= 1;
if *degree == 0 {
let _ = ready.insert(v);
}
}
}
}
if witness.len() == self.nodes.len() {
return Ok(witness);
}
Err(self.contradiction(&witness))
}
fn contradiction(&self, emitted: &[Dot]) -> Contradiction {
let emitted: BTreeSet<Dot> = emitted.iter().copied().collect();
let remaining: BTreeSet<Dot> = self.nodes.difference(&emitted).copied().collect();
let mut backward: BTreeMap<Dot, BTreeSet<Dot>> = BTreeMap::new();
for &(u, v) in self.edges.keys() {
if remaining.contains(&u) && remaining.contains(&v) {
let _ = backward.entry(v).or_default().insert(u);
}
}
let start = *remaining.iter().next().expect("a stall leaves a residue");
let mut path: Vec<Dot> = alloc::vec![start];
let mut seen_at: BTreeMap<Dot, usize> = BTreeMap::new();
let _ = seen_at.insert(start, 0);
loop {
let current = *path.last().expect("the path starts nonempty");
let predecessor = *backward
.get(¤t)
.and_then(|preds| preds.iter().next())
.expect("every stalled node keeps a stalled predecessor");
if let Some(&at) = seen_at.get(&predecessor) {
let mut cycle: Vec<Dot> = alloc::vec![predecessor];
cycle.extend(path[at..].iter().rev().copied());
let _ = cycle.pop();
let witnesses = cycle
.iter()
.enumerate()
.map(|(j, &u)| {
let v = cycle[(j + 1) % cycle.len()];
self.edges[&(u, v)]
})
.collect();
return Contradiction { cycle, witnesses };
}
let _ = seen_at.insert(predecessor, path.len());
path.push(predecessor);
}
}
}
#[test]
fn test_the_empty_ledger_and_the_single_state_pass() {
let mut ledger = OrderLedger::new();
assert_eq!(ledger.verdict(), Ok(Vec::new()));
let _ = ledger.observe(&[dot(1, 1), dot(1, 2), dot(2, 1)]);
assert_eq!(
ledger.verdict(),
Ok(alloc::vec![dot(1, 1), dot(1, 2), dot(2, 1)])
);
assert_eq!(ledger.observations(), 1);
}
#[test]
fn test_a_repeated_element_in_one_state_is_a_self_contradiction() {
let mut ledger = OrderLedger::new();
let index = ledger.observe(&[dot(1, 1), dot(1, 1)]);
let refusal = ledger.verdict().expect_err("a duplicate cannot be ordered");
assert_eq!(refusal.cycle, [dot(1, 1)]);
assert_eq!(refusal.witnesses, [index]);
}