use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors}; use rustc_index::vec::{Idx, IndexVec}; #[cfg(test)] mod tests; pub struct VecGraph { /// Maps from a given node to an index where the set of successors /// for that node starts. The index indexes into the `edges` /// vector. To find the range for a given node, we look up the /// start for that node and then the start for the next node /// (i.e., with an index 1 higher) and get the range between the /// two. This vector always has an extra entry so that this works /// even for the max element. node_starts: IndexVec, edge_targets: Vec, } impl VecGraph { pub fn new(num_nodes: usize, mut edge_pairs: Vec<(N, N)>) -> Self { // Sort the edges by the source -- this is important. edge_pairs.sort(); let num_edges = edge_pairs.len(); // Store the *target* of each edge into `edge_targets`. let edge_targets: Vec = edge_pairs.iter().map(|&(_, target)| target).collect(); // Create the *edge starts* array. We are iterating over the // (sorted) edge pairs. We maintain the invariant that the // length of the `node_starts` array is enough to store the // current source node -- so when we see that the source node // for an edge is greater than the current length, we grow the // edge-starts array by just enough. let mut node_starts = IndexVec::with_capacity(num_edges); for (index, &(source, _)) in edge_pairs.iter().enumerate() { // If we have a list like `[(0, x), (2, y)]`: // // - Start out with `node_starts` of `[]` // - Iterate to `(0, x)` at index 0: // - Push one entry because `node_starts.len()` (0) is <= the source (0) // - Leaving us with `node_starts` of `[0]` // - Iterate to `(2, y)` at index 1: // - Push one entry because `node_starts.len()` (1) is <= the source (2) // - Push one entry because `node_starts.len()` (2) is <= the source (2) // - Leaving us with `node_starts` of `[0, 1, 1]` // - Loop terminates while node_starts.len() <= source.index() { node_starts.push(index); } } // Pad out the `node_starts` array so that it has `num_nodes + // 1` entries. Continuing our example above, if `num_nodes` is // be `3`, we would push one more index: `[0, 1, 1, 2]`. // // Interpretation of that vector: // // [0, 1, 1, 2] // ---- range for N=2 // ---- range for N=1 // ---- range for N=0 while node_starts.len() <= num_nodes { node_starts.push(edge_targets.len()); } assert_eq!(node_starts.len(), num_nodes + 1); Self { node_starts, edge_targets } } /// Gets the successors for `source` as a slice. pub fn successors(&self, source: N) -> &[N] { let start_index = self.node_starts[source]; let end_index = self.node_starts[source.plus(1)]; &self.edge_targets[start_index..end_index] } } impl DirectedGraph for VecGraph { type Node = N; } impl WithNumNodes for VecGraph { fn num_nodes(&self) -> usize { self.node_starts.len() - 1 } } impl WithNumEdges for VecGraph { fn num_edges(&self) -> usize { self.edge_targets.len() } } impl<'graph, N: Idx> GraphSuccessors<'graph> for VecGraph { type Item = N; type Iter = std::iter::Cloned>; } impl WithSuccessors for VecGraph { fn successors(&self, node: N) -> >::Iter { self.successors(node).iter().cloned() } }