//! A bijective map between node indices and a `TopologicalPosition`, to store //! the total topological order of the graph. //! //! This data structure is an implementation detail and is not exposed in the //! public API. use std::{collections::BTreeMap, fmt, ops::RangeBounds}; use crate::{ algo::{toposort, Cycle}, visit::{GraphBase, IntoNeighborsDirected, IntoNodeIdentifiers, NodeIndexable, Visitable}, }; /// A position in the topological order of the graph. /// /// This defines a total order over the set of nodes in the graph. /// /// Note that the positions of all nodes in a graph may not form a contiguous /// interval. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] #[repr(transparent)] pub struct TopologicalPosition(pub(super) usize); /// A bijective map between node indices and their position in a topological order. /// /// Note that this map does not check for injectivity or surjectivity, this /// must be enforced by the user. Map mutations that invalidate these properties /// are allowed to make it easy to perform batch modifications that temporarily /// break the invariants. #[derive(Clone)] pub(super) struct OrderMap { /// Map topological position to node index. pos_to_node: BTreeMap, /// The inverse of `pos_to_node`, i.e. map node indices to their position. /// /// This is a Vec, relying on `N: NodeIndexable` for indexing. node_to_pos: Vec, } impl Default for OrderMap { fn default() -> Self { Self { pos_to_node: Default::default(), node_to_pos: Default::default(), } } } impl fmt::Debug for OrderMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OrderMap") .field("order", &self.pos_to_node) .finish() } } impl OrderMap { pub(super) fn try_from_graph(graph: G) -> Result> where G: NodeIndexable + IntoNeighborsDirected + IntoNodeIdentifiers + Visitable, { // Compute the topological order. let topo_vec = toposort(graph, None)?; // Create the two map directions. let mut pos_to_node = BTreeMap::new(); let mut node_to_pos = vec![TopologicalPosition::default(); graph.node_bound()]; // Populate the maps. for (i, &id) in topo_vec.iter().enumerate() { let pos = TopologicalPosition(i); pos_to_node.insert(pos, id); node_to_pos[graph.to_index(id)] = pos; } Ok(Self { pos_to_node, node_to_pos, }) } pub(super) fn with_capacity(nodes: usize) -> Self { Self { pos_to_node: BTreeMap::new(), node_to_pos: Vec::with_capacity(nodes), } } /// Map a node to its position in the topological order. /// /// Panics if the node index is out of bounds. pub(super) fn get_position( &self, id: N, graph: impl NodeIndexable, ) -> TopologicalPosition { let idx = graph.to_index(id); assert!(idx < self.node_to_pos.len()); self.node_to_pos[idx] } /// Map a position in the topological order to a node, if it exists. pub(super) fn at_position(&self, pos: TopologicalPosition) -> Option { self.pos_to_node.get(&pos).copied() } /// Get an iterator over the nodes, ordered by their position. pub(super) fn nodes_iter(&self) -> impl Iterator + '_ { self.pos_to_node.values().copied() } /// Get an iterator over the nodes within the range of positions. pub(super) fn range( &self, range: impl RangeBounds, ) -> impl Iterator + '_ { self.pos_to_node.range(range).map(|(_, &n)| n) } /// Add a node to the order map and assign it an arbitrary position. /// /// Return the position of the new node. pub(super) fn add_node( &mut self, id: N, graph: impl NodeIndexable, ) -> TopologicalPosition { // The position and node index let new_pos = self .pos_to_node .iter() .next_back() .map(|(TopologicalPosition(idx), _)| TopologicalPosition(idx + 1)) .unwrap_or_default(); let idx = graph.to_index(id); // Make sure the order_inv is large enough. if idx >= self.node_to_pos.len() { self.node_to_pos .resize(graph.node_bound(), TopologicalPosition::default()); } // Insert both map directions. self.pos_to_node.insert(new_pos, id); self.node_to_pos[idx] = new_pos; new_pos } /// Remove a node from the order map. /// /// Panics if the node index is out of bounds. pub(super) fn remove_node(&mut self, id: N, graph: impl NodeIndexable) { let idx = graph.to_index(id); assert!(idx < self.node_to_pos.len()); let pos = self.node_to_pos[idx]; self.node_to_pos[idx] = TopologicalPosition::default(); self.pos_to_node.remove(&pos); } /// Set the position of a node. /// /// Panics if the node index is out of bounds. pub(super) fn set_position( &mut self, id: N, pos: TopologicalPosition, graph: impl NodeIndexable, ) { let idx = graph.to_index(id); assert!(idx < self.node_to_pos.len()); self.pos_to_node.insert(pos, id); self.node_to_pos[idx] = pos; } } impl super::Acyclic { /// Get the position of a node in the topological sort. /// /// Panics if the node index is out of bounds. pub fn get_position<'a>(&'a self, id: G::NodeId) -> TopologicalPosition where &'a G: NodeIndexable + GraphBase, { self.order_map.get_position(id, &self.graph) } /// Get the node at a given position in the topological sort, if it exists. pub fn at_position(&self, pos: TopologicalPosition) -> Option { self.order_map.at_position(pos) } }