minerva 0.2.0

Causal ordering for distributed systems
extern crate alloc;

use alloc::vec::Vec;

use crate::kairos::Kairos;
use crate::metis::{Anchor, Locus, Rhapsody};
use proptest::prelude::*;

use super::d;

mod accounting;
mod dangling;
mod round_trip;
mod totality;

/// One generated weave: a woven dot, an anchor pick (into the already-woven dots,
/// or the origin), and a rank tick.
#[derive(Clone, Debug)]
pub(super) struct Weave {
    /// The station of the woven dot (`1..=3`).
    station: u32,
    /// The 1-based index of the woven dot (`1..=8`); the non-dot `0` is excluded
    /// so every weave takes.
    index: u64,
    /// The anchor pick: `None` at the origin, else an index into the woven-so-far
    /// list (modulo its length).
    anchor_pick: Option<usize>,
    /// Whether a dot-anchor pick hangs on the BEFORE side (the Fugue left child,
    /// S127) rather than the AFTER side; ignored for an origin pick.
    before_side: bool,
    /// The rank tick, made distinct per weave by the generator so sibling order is
    /// total.
    tick: u64,
}

/// A small weave history over three stations and dots `1..=8`, with anchor picks
/// that reach both the origin and prior dots, so the generated rhapsodies carry
/// multi-level anchor chains and (via the modulo pick) the occasional dangling
/// anchor once dots repeat.
pub(super) fn arb_weaves() -> impl Strategy<Value = Vec<Weave>> {
    prop::collection::vec(
        (
            1u32..=3,
            1u64..=8,
            prop::option::of(0usize..8),
            any::<bool>(),
            0u64..64,
        )
            .prop_map(|(station, index, anchor_pick, before_side, tick)| Weave {
                station,
                index,
                anchor_pick,
                before_side,
                tick,
            }),
        0..24,
    )
}

/// Builds a rhapsody by weaving every step; a repeated dot is refused by `weave` (a
/// dot names one write), so the skeleton holds each distinct dot once. Ranks are
/// distinct per step (the tick plus the step ordinal), so sibling order is total.
/// Some anchors land on not-yet-woven dots and stay dangling: a legal `Rhapsody` value
/// the frame must still round-trip.
pub(super) fn build(weaves: &[Weave]) -> Rhapsody {
    let mut rhapsody = Rhapsody::new();
    let mut woven: Vec<(u32, u64)> = Vec::new();
    for (ordinal, w) in weaves.iter().enumerate() {
        let dot = d(w.station, w.index);
        let anchor = w
            .anchor_pick
            .and_then(|pick| {
                if woven.is_empty() {
                    None
                } else {
                    Some(woven[pick % woven.len()])
                }
            })
            .map_or(Anchor::Origin, |a| {
                // A dot anchor hangs on either side (S127), so the round-trip
                // exercises both after and before records.
                if w.before_side {
                    Anchor::Before(a.into())
                } else {
                    Anchor::After(a.into())
                }
            });
        // Distinct rank: the generated tick in the high bits, the step ordinal in
        // the logical field, so no two weaves tie.
        let rank = Kairos::new(
            w.tick,
            u16::try_from(ordinal % 0xFFFF).unwrap_or(0),
            w.station,
            0u16,
        );
        if rhapsody.weave(dot, Locus { anchor, rank }) {
            woven.push(dot.into());
        }
    }
    rhapsody
}