weavatrix-graph 0.6.2

Deterministic, evidence-carrying graph core for Weavatrix repository intelligence
Documentation
use crate::IndexGraphView;
use crate::Vec;

#[must_use]
pub fn weakly_connected_components<G>(graph: &G) -> Vec<Vec<G::Node>>
where
    G: IndexGraphView,
{
    with_filter(graph, &|_| true)
}

#[must_use]
pub fn weakly_connected_components_filtered<G, F>(graph: &G, allows_edge: F) -> Vec<Vec<G::Node>>
where
    G: IndexGraphView,
    F: Fn(G::Edge) -> bool,
{
    with_filter(graph, &allows_edge)
}

fn with_filter<G, F>(graph: &G, allows_edge: &F) -> Vec<Vec<G::Node>>
where
    G: IndexGraphView,
    F: Fn(G::Edge) -> bool,
{
    let node_bound = graph.node_bound();
    let mut sets = DisjointSet::new(node_bound);
    for (edge, endpoints) in graph.edge_references() {
        if !allows_edge(edge) {
            continue;
        }
        let source = G::node_slot(endpoints.source());
        let target = G::node_slot(endpoints.target());
        if source < node_bound && target < node_bound {
            sets.union(source, target);
        }
    }

    let mut grouped = vec![Vec::new(); node_bound];
    for node in graph.node_indices() {
        let slot = G::node_slot(node);
        if slot < node_bound {
            grouped[sets.root(slot)].push(node);
        }
    }
    let mut components = grouped
        .into_iter()
        .filter(|component| !component.is_empty())
        .collect::<Vec<_>>();
    components.sort_unstable_by_key(|component| G::node_slot(component[0]));
    components
}

struct DisjointSet {
    parent: Vec<usize>,
    rank: Vec<u8>,
}

impl DisjointSet {
    fn new(len: usize) -> Self {
        Self {
            parent: (0..len).collect(),
            rank: vec![0; len],
        }
    }

    fn find(&mut self, mut node: usize) -> usize {
        loop {
            let parent = self.parent[node];
            if parent == node {
                return node;
            }
            self.parent[node] = self.parent[parent];
            node = parent;
        }
    }

    fn root(&self, mut node: usize) -> usize {
        loop {
            let parent = self.parent[node];
            if parent == node {
                return node;
            }
            node = parent;
        }
    }

    fn union(&mut self, left: usize, right: usize) {
        let mut left = self.find(left);
        let mut right = self.find(right);
        if left == right {
            return;
        }
        if self.rank[left] < self.rank[right] {
            core::mem::swap(&mut left, &mut right);
        }
        self.parent[right] = left;
        if self.rank[left] == self.rank[right] {
            self.rank[left] += 1;
        }
    }
}