minerva 0.2.0

Causal ordering for distributed systems
extern crate alloc;

use alloc::collections::BTreeMap;
use alloc::vec::Vec;

use crate::kairos::Kairos;
use crate::metis::{Anchor, DotStore, Locus, Rhapsody};

use super::Dot;

/// The naive reference order, built independently of `Rhapsody::order`.
pub(super) fn reference_order(rhapsody: &Rhapsody) -> Vec<Dot> {
    enum Frame {
        Visit(Dot),
        Emit(Dot),
    }

    // Reconstruct the reachable skeleton through public reads, chasing visible
    // dots' anchors up to the origin.
    let mut loci: BTreeMap<Dot, Locus> = BTreeMap::new();
    let mut frontier: Vec<Dot> = rhapsody.dots().collect();
    while let Some(dot) = frontier.pop() {
        if loci.contains_key(&dot) {
            continue;
        }
        if let Some(locus) = rhapsody.locus(dot) {
            let _ = loci.insert(dot, locus);
            // An anchor is a raw coordinate; one naming the non-dot zero
            // names no locus, so it never joins the frontier (R-91).
            if let Some(Ok(anchor)) = locus.anchor.dot().map(Dot::try_from) {
                frontier.push(anchor);
            }
        }
    }

    let mut children: BTreeMap<Anchor, Vec<Dot>> = BTreeMap::new();
    for (&dot, locus) in &loci {
        children.entry(locus.anchor).or_default().push(dot);
    }
    for bucket in children.values_mut() {
        bucket.sort_by(|a, b| {
            let (ra, rb): (Kairos, Kairos) = (loci[a].rank, loci[b].rank);
            rb.cmp(&ra).then_with(|| a.cmp(b))
        });
    }

    let mut order = Vec::new();
    let mut stack: Vec<Frame> = children
        .get(&Anchor::Origin)
        .map(|roots| roots.iter().rev().map(|&d| Frame::Visit(d)).collect())
        .unwrap_or_default();
    while let Some(frame) = stack.pop() {
        match frame {
            Frame::Emit(dot) => {
                if rhapsody.is_visible(dot) {
                    order.push(dot);
                }
            }
            Frame::Visit(dot) => {
                if let Some(kids) = children.get(&Anchor::After(dot.into())) {
                    for &kid in kids.iter().rev() {
                        stack.push(Frame::Visit(kid));
                    }
                }
                stack.push(Frame::Emit(dot));
                if let Some(kids) = children.get(&Anchor::Before(dot.into())) {
                    for &kid in kids {
                        stack.push(Frame::Visit(kid));
                    }
                }
            }
        }
    }
    order
}