extern crate alloc;
use alloc::collections::BTreeSet;
use super::super::super::super::support::dot;
use crate::metis::{Dot, DotSet, DotStore};
#[derive(Clone, Debug)]
pub(super) struct Covered<S> {
store: S,
context: DotSet,
}
impl<S: DotStore> Covered<S> {
pub(super) fn new(store: S, context: DotSet) -> Self {
let covered = Self { store, context };
covered.assert_covered();
covered
}
pub(super) fn store(&self) -> &S {
&self.store
}
pub(super) fn context(&self) -> &DotSet {
&self.context
}
fn assert_covered(&self) {
for held in self.store.dots() {
let (station, counter) = (held.station(), held.counter());
assert!(
self.context.contains(held),
"harness input is uncovered at ({station}, {counter})"
);
}
}
}
fn check_survivor_law<S: DotStore>(a: &Covered<S>, b: &Covered<S>) {
let merged = a.store.causal_merge(&a.context, &b.store, &b.context);
let a_dots: BTreeSet<Dot> = a.store.dots().collect();
let b_dots: BTreeSet<Dot> = b.store.dots().collect();
let merged_dots: BTreeSet<Dot> = merged.dots().collect();
for station in 0u32..4 {
for counter in 1u64..9 {
let probe = dot(station, counter);
let in_a = a_dots.contains(&probe);
let in_b = b_dots.contains(&probe);
let survives = (in_a && (in_b || !b.context.contains(probe)))
|| (in_b && (in_a || !a.context.contains(probe)));
assert_eq!(
merged_dots.contains(&probe),
survives,
"survivor law disagrees at ({station}, {counter})"
);
}
}
}
fn check_commutative<S: DotStore + PartialEq + core::fmt::Debug>(a: &Covered<S>, b: &Covered<S>) {
let ab = a.store.causal_merge(&a.context, &b.store, &b.context);
let ba = b.store.causal_merge(&b.context, &a.store, &a.context);
assert_eq!(ab, ba, "causal_merge is not commutative");
}
fn check_associative<S: DotStore + PartialEq + core::fmt::Debug>(
a: &Covered<S>,
b: &Covered<S>,
c: &Covered<S>,
) {
let bc = b.store.causal_merge(&b.context, &c.store, &c.context);
let bc_ctx = b.context.merge(&c.context);
let left = a.store.causal_merge(&a.context, &bc, &bc_ctx);
let ab = a.store.causal_merge(&a.context, &b.store, &b.context);
let ab_ctx = a.context.merge(&b.context);
let right = ab.causal_merge(&ab_ctx, &c.store, &c.context);
assert_eq!(left, right, "causal_merge is not associative");
}
fn check_idempotent<S: DotStore + Clone + PartialEq + core::fmt::Debug>(a: &Covered<S>) {
let merged = a.store.causal_merge(&a.context, &a.store, &a.context);
assert_eq!(merged, a.store, "causal_merge is not idempotent");
}
fn check_in_place_agrees<S: DotStore + Clone + PartialEq + core::fmt::Debug>(
a: &Covered<S>,
b: &Covered<S>,
) {
let pure = a.store.causal_merge(&a.context, &b.store, &b.context);
let mut folded = a.store.clone();
folded.causal_merge_from(&a.context, &b.store, &b.context);
assert_eq!(
folded, pure,
"causal_merge_from disagrees with causal_merge"
);
}
fn check_bottom_identity<S: DotStore + Clone + PartialEq + core::fmt::Debug>(a: &Covered<S>) {
let bottom = S::default();
let empty = DotSet::new();
let right = a.store.causal_merge(&a.context, &bottom, &empty);
assert_eq!(right, a.store, "bottom is not a right identity");
let left = bottom.causal_merge(&empty, &a.store, &a.context);
assert_eq!(left, a.store, "bottom is not a left identity");
}
fn check_bottom_is_support_empty<S: DotStore>(a: &Covered<S>) {
assert!(
S::default().is_bottom(),
"the default store is not the bottom"
);
if a.store.is_bottom() {
assert!(
a.store.dots().next().is_none(),
"a bottom store must carry no support"
);
}
}
fn check_support_is_the_dots_fold<S: DotStore>(a: &Covered<S>) {
let support = a.store.support();
let want: BTreeSet<Dot> = a.store.dots().collect();
let got: BTreeSet<Dot> = support.dots().collect();
assert_eq!(got, want, "support disagrees with the dots enumeration");
}
fn check_witnessed_selection<S: DotStore + Clone + PartialEq + core::fmt::Debug>(
a: &Covered<S>,
b: &Covered<S>,
) {
let bare = a.store.novel_to(b.context());
let bottomed = a.store.novel_to_witnessed(b.context(), &DotSet::new());
assert_eq!(bottomed, bare, "a bottom witness must equal the bare read");
let witnessed = a.store.novel_to_witnessed(b.context(), &a.store.support());
let want: BTreeSet<Dot> = bare.dots().collect();
let got: BTreeSet<Dot> = witnessed.dots().collect();
assert_eq!(got, want, "a witness must not move support selection");
}
fn check_restriction<S: DotStore + Clone + PartialEq + core::fmt::Debug>(
a: &Covered<S>,
b: &Covered<S>,
roster: &[u32],
) {
let kept = a.store.restrict(roster.iter().copied());
let roster_set: BTreeSet<u32> = roster.iter().copied().collect();
let want: BTreeSet<Dot> = a
.store
.dots()
.filter(|held| roster_set.contains(&held.station()))
.collect();
let got: BTreeSet<Dot> = kept.dots().collect();
assert_eq!(got, want, "restrict is not fiber selection");
let merged = a.store.causal_merge(&a.context, &b.store, &b.context);
let restrict_then_merge = a.store.restrict(roster.iter().copied()).causal_merge(
&a.context.restrict(roster.iter().copied()),
&b.store.restrict(roster.iter().copied()),
&b.context.restrict(roster.iter().copied()),
);
assert_eq!(
merged.restrict(roster.iter().copied()),
restrict_then_merge,
"restrict does not commute with causal_merge"
);
}
pub(super) fn check_all_laws<S: DotStore + Clone + PartialEq + core::fmt::Debug>(
a: &Covered<S>,
b: &Covered<S>,
c: &Covered<S>,
roster: &[u32],
) {
a.assert_covered();
b.assert_covered();
c.assert_covered();
check_survivor_law(a, b);
check_commutative(a, b);
check_associative(a, b, c);
check_idempotent(a);
check_bottom_identity(a);
check_bottom_is_support_empty(a);
check_in_place_agrees(a, b);
check_support_is_the_dots_fold(a);
check_witnessed_selection(a, b);
check_restriction(a, b, roster);
}