1use std::ops::ControlFlow;
23use rustc_index::bit_set::DenseBitSet;
4use rustc_index::{IndexSlice, IndexVec};
56use super::{DirectedGraph, StartNode, Successors};
78#[cfg(test)]
9mod tests;
1011pub fn post_order_from<G: DirectedGraph + Successors>(
12 graph: &G,
13 start_node: G::Node,
14) -> Vec<G::Node> {
15post_order_from_to(graph, start_node, None)
16}
1718pub fn post_order_from_to<G: DirectedGraph + Successors>(
19 graph: &G,
20 start_node: G::Node,
21 end_node: Option<G::Node>,
22) -> Vec<G::Node> {
23let mut visited: IndexVec<G::Node, bool> = IndexVec::from_elem_n(false, graph.num_nodes());
24let mut result: Vec<G::Node> = Vec::with_capacity(graph.num_nodes());
25if let Some(end_node) = end_node {
26visited[end_node] = true;
27 }
28post_order_walk(graph, start_node, &mut result, &mut visited);
29result30}
3132fn post_order_walk<G: DirectedGraph + Successors>(
33 graph: &G,
34 node: G::Node,
35 result: &mut Vec<G::Node>,
36 visited: &mut IndexSlice<G::Node, bool>,
37) {
38struct PostOrderFrame<Node, Iter> {
39 node: Node,
40 iter: Iter,
41 }
4243if visited[node] {
44return;
45 }
4647let mut stack = <[_]>::into_vec(::alloc::boxed::box_new([PostOrderFrame {
node,
iter: graph.successors(node),
}]))vec![PostOrderFrame { node, iter: graph.successors(node) }];
4849'recurse: while let Some(frame) = stack.last_mut() {
50let node = frame.node;
51 visited[node] = true;
5253for successor in frame.iter.by_ref() {
54if !visited[successor] {
55 stack.push(PostOrderFrame { node: successor, iter: graph.successors(successor) });
56continue 'recurse;
57 }
58 }
5960let _ = stack.pop();
61 result.push(node);
62 }
63}
6465pub fn reverse_post_order<G: DirectedGraph + Successors>(
66 graph: &G,
67 start_node: G::Node,
68) -> Vec<G::Node> {
69let mut vec = post_order_from(graph, start_node);
70vec.reverse();
71vec72}
7374/// A "depth-first search" iterator for a directed graph.
75pub struct DepthFirstSearch<G>
76where
77G: DirectedGraph + Successors,
78{
79 graph: G,
80 stack: Vec<G::Node>,
81 visited: DenseBitSet<G::Node>,
82}
8384impl<G> DepthFirstSearch<G>
85where
86G: DirectedGraph + Successors,
87{
88pub fn new(graph: G) -> Self {
89Self { stack: ::alloc::vec::Vec::new()vec![], visited: DenseBitSet::new_empty(graph.num_nodes()), graph }
90 }
9192/// Version of `push_start_node` that is convenient for chained
93 /// use.
94pub fn with_start_node(mut self, start_node: G::Node) -> Self {
95self.push_start_node(start_node);
96self97 }
9899/// Pushes another start node onto the stack. If the node
100 /// has not already been visited, then you will be able to
101 /// walk its successors (and so forth) after the current
102 /// contents of the stack are drained. If multiple start nodes
103 /// are added into the walk, then their mutual successors
104 /// will all be walked. You can use this method once the
105 /// iterator has been completely drained to add additional
106 /// start nodes.
107pub fn push_start_node(&mut self, start_node: G::Node) {
108if self.visited.insert(start_node) {
109self.stack.push(start_node);
110 }
111 }
112113/// Searches all nodes reachable from the current start nodes.
114 /// This is equivalent to just invoke `next` repeatedly until
115 /// you get a `None` result.
116pub fn complete_search(&mut self) {
117for _ in self.by_ref() {}
118 }
119120/// Returns true if node has been visited thus far.
121 /// A node is considered "visited" once it is pushed
122 /// onto the internal stack; it may not yet have been yielded
123 /// from the iterator. This method is best used after
124 /// the iterator is completely drained.
125pub fn visited(&self, node: G::Node) -> bool {
126self.visited.contains(node)
127 }
128129/// Returns a reference to the set of nodes that have been visited, with
130 /// the same caveats as [`Self::visited`].
131 ///
132 /// When incorporating the visited nodes into another bitset, using bulk
133 /// operations like `union` or `intersect` can be more efficient than
134 /// processing each node individually.
135pub fn visited_set(&self) -> &DenseBitSet<G::Node> {
136&self.visited
137 }
138}
139140impl<G> std::fmt::Debugfor DepthFirstSearch<G>
141where
142G: DirectedGraph + Successors,
143{
144fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145let mut f = fmt.debug_set();
146for n in self.visited.iter() {
147 f.entry(&n);
148 }
149f.finish()
150 }
151}
152153impl<G> Iteratorfor DepthFirstSearch<G>
154where
155G: DirectedGraph + Successors,
156{
157type Item = G::Node;
158159fn next(&mut self) -> Option<G::Node> {
160let DepthFirstSearch { stack, visited, graph } = self;
161let n = stack.pop()?;
162stack.extend(graph.successors(n).filter(|&m| visited.insert(m)));
163Some(n)
164 }
165}
166167/// The status of a node in the depth-first search.
168///
169/// See the documentation of `TriColorDepthFirstSearch` to see how a node's status is updated
170/// during DFS.
171#[derive(#[automatically_derived]
impl ::core::clone::Clone for NodeStatus {
#[inline]
fn clone(&self) -> NodeStatus { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NodeStatus { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for NodeStatus {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
NodeStatus::Visited => "Visited",
NodeStatus::Settled => "Settled",
})
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for NodeStatus {
#[inline]
fn eq(&self, other: &NodeStatus) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NodeStatus {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq)]
172pub enum NodeStatus {
173/// This node has been examined by the depth-first search but is not yet `Settled`.
174 ///
175 /// Also referred to as "gray" or "discovered" nodes in [CLR].
176 ///
177 /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
178Visited,
179180/// This node and all nodes reachable from it have been examined by the depth-first search.
181 ///
182 /// Also referred to as "black" or "finished" nodes in [CLR].
183 ///
184 /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
185Settled,
186}
187188struct Event<N> {
189 node: N,
190 becomes: NodeStatus,
191}
192193/// A depth-first search that also tracks when all successors of a node have been examined.
194///
195/// This is based on the DFS described in [Introduction to Algorithms (1st ed.)][CLR], hereby
196/// referred to as **CLR**. However, we use the terminology in [`NodeStatus`] above instead of
197/// "discovered"/"finished" or "white"/"grey"/"black". Each node begins the search with no status,
198/// becomes `Visited` when it is first examined by the DFS and is `Settled` when all nodes
199/// reachable from it have been examined. This allows us to differentiate between "tree", "back"
200/// and "forward" edges (see [`TriColorVisitor::node_examined`]).
201///
202/// Unlike the pseudocode in [CLR], this implementation is iterative and does not use timestamps.
203/// We accomplish this by storing `Event`s on the stack that result in a (possible) state change
204/// for each node. A `Visited` event signifies that we should examine this node if it has not yet
205/// been `Visited` or `Settled`. When a node is examined for the first time, we mark it as
206/// `Visited` and push a `Settled` event for it on stack followed by `Visited` events for all of
207/// its predecessors, scheduling them for examination. Multiple `Visited` events for a single node
208/// may exist on the stack simultaneously if a node has multiple predecessors, but only one
209/// `Settled` event will ever be created for each node. After all `Visited` events for a node's
210/// successors have been popped off the stack (as well as any new events triggered by visiting
211/// those successors), we will pop off that node's `Settled` event.
212///
213/// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
214pub struct TriColorDepthFirstSearch<'graph, G>
215where
216G: ?Sized + DirectedGraph + Successors,
217{
218 graph: &'graph G,
219 stack: Vec<Event<G::Node>>,
220 visited: DenseBitSet<G::Node>,
221 settled: DenseBitSet<G::Node>,
222}
223224impl<'graph, G> TriColorDepthFirstSearch<'graph, G>
225where
226G: ?Sized + DirectedGraph + Successors,
227{
228pub fn new(graph: &'graph G) -> Self {
229TriColorDepthFirstSearch {
230graph,
231 stack: ::alloc::vec::Vec::new()vec![],
232 visited: DenseBitSet::new_empty(graph.num_nodes()),
233 settled: DenseBitSet::new_empty(graph.num_nodes()),
234 }
235 }
236237/// Performs a depth-first search, starting from the given `root`.
238 ///
239 /// This won't visit nodes that are not reachable from `root`.
240pub fn run_from<V>(mut self, root: G::Node, visitor: &mut V) -> Option<V::BreakVal>
241where
242V: TriColorVisitor<G>,
243 {
244use NodeStatus::{Settled, Visited};
245246self.stack.push(Event { node: root, becomes: Visited });
247248loop {
249match self.stack.pop()? {
250Event { node, becomes: Settled } => {
251let not_previously_settled = self.settled.insert(node);
252if !not_previously_settled {
{
::core::panicking::panic_fmt(format_args!("A node should be settled exactly once"));
}
};assert!(not_previously_settled, "A node should be settled exactly once");
253if let ControlFlow::Break(val) = visitor.node_settled(node) {
254return Some(val);
255 }
256 }
257258Event { node, becomes: Visited } => {
259let not_previously_visited = self.visited.insert(node);
260let prior_status = if not_previously_visited {
261None262 } else if self.settled.contains(node) {
263Some(Settled)
264 } else {
265Some(Visited)
266 };
267268if let ControlFlow::Break(val) = visitor.node_examined(node, prior_status) {
269return Some(val);
270 }
271272// If this node has already been examined, we are done.
273if prior_status.is_some() {
274continue;
275 }
276277// Otherwise, push a `Settled` event for this node onto the stack, then
278 // schedule its successors for examination.
279self.stack.push(Event { node, becomes: Settled });
280for succ in self.graph.successors(node) {
281if !visitor.ignore_edge(node, succ) {
282self.stack.push(Event { node: succ, becomes: Visited });
283 }
284 }
285 }
286 }
287 }
288 }
289}
290291impl<G> TriColorDepthFirstSearch<'_, G>
292where
293G: ?Sized + DirectedGraph + Successors + StartNode,
294{
295/// Performs a depth-first search, starting from `G::start_node()`.
296 ///
297 /// This won't visit nodes that are not reachable from the start node.
298pub fn run_from_start<V>(self, visitor: &mut V) -> Option<V::BreakVal>
299where
300V: TriColorVisitor<G>,
301 {
302let root = self.graph.start_node();
303self.run_from(root, visitor)
304 }
305}
306307/// What to do when a node is examined or becomes `Settled` during DFS.
308pub trait TriColorVisitor<G>
309where
310G: ?Sized + DirectedGraph,
311{
312/// The value returned by this search.
313type BreakVal;
314315/// Called when a node is examined by the depth-first search.
316 ///
317 /// By checking the value of `prior_status`, this visitor can determine whether the edge
318 /// leading to this node was a tree edge (`None`), forward edge (`Some(Settled)`) or back edge
319 /// (`Some(Visited)`). For a full explanation of each edge type, see the "Depth-first Search"
320 /// chapter in [CLR] or [wikipedia].
321 ///
322 /// If you want to know *both* nodes linked by each edge, you'll need to modify
323 /// `TriColorDepthFirstSearch` to store a `source` node for each `Visited` event.
324 ///
325 /// [wikipedia]: https://en.wikipedia.org/wiki/Depth-first_search#Output_of_a_depth-first_search
326 /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms
327fn node_examined(
328&mut self,
329 _node: G::Node,
330 _prior_status: Option<NodeStatus>,
331 ) -> ControlFlow<Self::BreakVal> {
332 ControlFlow::Continue(())
333 }
334335/// Called after all nodes reachable from this one have been examined.
336fn node_settled(&mut self, _node: G::Node) -> ControlFlow<Self::BreakVal> {
337 ControlFlow::Continue(())
338 }
339340/// Behave as if no edges exist from `source` to `target`.
341fn ignore_edge(&mut self, _source: G::Node, _target: G::Node) -> bool {
342false
343}
344}
345346/// This `TriColorVisitor` looks for back edges in a graph, which indicate that a cycle exists.
347pub struct CycleDetector;
348349impl<G> TriColorVisitor<G> for CycleDetector350where
351G: ?Sized + DirectedGraph,
352{
353type BreakVal = ();
354355fn node_examined(
356&mut self,
357 _node: G::Node,
358 prior_status: Option<NodeStatus>,
359 ) -> ControlFlow<Self::BreakVal> {
360match prior_status {
361Some(NodeStatus::Visited) => ControlFlow::Break(()),
362_ => ControlFlow::Continue(()),
363 }
364 }
365}