minerva 0.2.0

Causal ordering for distributed systems
use super::*;
use alloc::collections::BTreeSet;
use proptest::prelude::*;

/// A literal identity for the tapes below (test machinery: the strategies
/// draw one-based counters, so the crossing never refuses).
fn d(station: u32, counter: u64) -> Dot {
    Dot::from_parts(station, counter).expect("a test dot has a nonzero counter")
}

/// The contract form: a plain sorted dot set.
fn model_diff(held: &BTreeSet<Dot>, live: &BTreeSet<Dot>) -> BTreeSet<Dot> {
    held.symmetric_difference(live).copied().collect()
}

proptest! {
    /// The plane's one law (the shell discipline): over any op tape,
    /// the maintained plane equals a fresh build from the model set,
    /// and the changed-pages walk between any two snapshots extracts
    /// exactly the symmetric difference.
    #[test]
    fn prop_occupancy_agrees_with_the_set_model(
        ops in prop::collection::vec((0..3u32, 1..400u64, any::<bool>()), 1..120),
        cut in 0usize..120,
    ) {
        let mut plane = OccupancyPlane::new();
        let mut model: BTreeSet<Dot> = BTreeSet::new();
        let mut held_plane = OccupancyPlane::new();
        let mut held_model: BTreeSet<Dot> = BTreeSet::new();
        for (step, &(station, index, present)) in ops.iter().enumerate() {
            if step == cut {
                held_plane.adopt(&plane);
                held_model = model.clone();
            }
            plane.set(station, index, present);
            if present {
                let _ = model.insert(d(station, index));
            } else {
                let _ = model.remove(&d(station, index));
            }
            plane.check_against(model.iter().copied());
        }
        let walked: BTreeSet<Dot> = held_plane
            .changed_pages(&plane)
            .flat_map(|(key, held, live)| OccupancyPlane::dots_of(key, held ^ live))
            .collect();
        prop_assert_eq!(walked, model_diff(&held_model, &model));
    }
}

proptest! {
    /// The batched settles agree with the point form: over any
    /// sequence of sorted flip batches, `apply_flips` (staging plus
    /// `apply_pages`, whichever regime it picks) equals per-dot
    /// `set`, and the plane stays canonical against the model.
    #[test]
    fn prop_batched_flips_agree_with_the_point_form(
        batches in prop::collection::vec(
            prop::collection::btree_map((0..3u32, 1..400u64), any::<bool>(), 1..40),
            1..12,
        ),
    ) {
        let mut batched = OccupancyPlane::new();
        let mut pointwise = OccupancyPlane::new();
        let mut model: BTreeSet<Dot> = BTreeSet::new();
        for batch in batches {
            batched.apply_flips(
                batch
                    .iter()
                    .map(|(&(station, index), &present)| (d(station, index), present)),
            );
            for (&(station, index), &present) in &batch {
                pointwise.set(station, index, present);
                if present {
                    let _ = model.insert(d(station, index));
                } else {
                    let _ = model.remove(&d(station, index));
                }
            }
            prop_assert_eq!(&batched, &pointwise);
            batched.check_against(model.iter().copied());
        }
    }
}

proptest! {
    /// The run form agrees with the point form: over any interleaving
    /// of point flips and contiguous run sets (page-spanning, edge
    /// -partial, overlapping, and ceiling-truncating shapes included),
    /// `set_run` equals per-dot `set(_, _, true)` exactly, and the
    /// plane stays canonical against the model.
    #[test]
    fn prop_set_run_agrees_with_the_point_form(
        ops in prop::collection::vec(
            prop_oneof![
                (0..3u32, 1..400u64, any::<bool>())
                    .prop_map(|(station, index, present)| (station, index, 1u32, present)),
                (0..3u32, 1..400u64, 1..200u32)
                    .prop_map(|(station, first, len)| (station, first, len, true)),
                (0..3u32, (u64::MAX - 100)..=u64::MAX, 1..80u32)
                    .prop_map(|(station, first, len)| (station, first, len, true)),
            ],
            1..40,
        ),
    ) {
        let mut bulk = OccupancyPlane::new();
        let mut pointwise = OccupancyPlane::new();
        let mut model: BTreeSet<Dot> = BTreeSet::new();
        for &(station, first, len, present) in &ops {
            if len == 1 && !present {
                bulk.set(station, first, false);
                pointwise.set(station, first, false);
                let _ = model.remove(&d(station, first));
            } else {
                bulk.set_run(station, first, len);
                let last = first.saturating_add(u64::from(len) - 1);
                let mut dot = first;
                loop {
                    pointwise.set(station, dot, true);
                    let _ = model.insert(d(station, dot));
                    if dot >= last {
                        break;
                    }
                    dot += 1;
                }
            }
            prop_assert_eq!(&bulk, &pointwise);
        }
        bulk.check_against(model.iter().copied());
    }
}

#[test]
fn a_page_is_born_and_retires_with_its_bits() {
    let mut plane = OccupancyPlane::new();
    plane.set(1, 64, true);
    plane.set(1, 65, true);
    plane.set(1, 64, false);
    plane.check_against(core::iter::once(d(1, 65)));
    plane.set(1, 65, false);
    plane.check_against(core::iter::empty());
    assert_eq!(plane, OccupancyPlane::new(), "the drained plane is empty");
}

#[test]
fn the_changed_walk_reads_both_directions() {
    let held = OccupancyPlane::from_dots([d(1, 1), d(1, 2), d(2, 70)].into_iter());
    let live = OccupancyPlane::from_dots([d(1, 2), d(1, 3), d(3, 5)].into_iter());
    let changed: Vec<Dot> = held
        .changed_pages(&live)
        .flat_map(|(key, a, b)| OccupancyPlane::dots_of(key, a ^ b))
        .collect();
    assert_eq!(changed, [d(1, 1), d(1, 3), d(2, 70), d(3, 5)]);
}

#[test]
fn a_ceiling_dot_occupies_the_last_page() {
    let mut plane = OccupancyPlane::new();
    plane.set(7, u64::MAX, true);
    plane.check_against(core::iter::once(d(7, u64::MAX)));
    plane.set(7, u64::MAX, false);
    assert_eq!(plane, OccupancyPlane::new());
}