Skip to content

Commit 9ddbb91

Browse files
committed
Added Graph::is_cyclicic_node algorithm
1 parent 7d91581 commit 9ddbb91

File tree

2 files changed

+82
-15
lines changed

2 files changed

+82
-15
lines changed

src/librustc_data_structures/graph/mod.rs

+40-11
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,14 @@ impl<N: Debug, E: Debug> Graph<N, E> {
231231

232232
// # Iterating over nodes, edges
233233

234-
pub fn all_nodes_enumerated(&self) -> Nodes<N> {
235-
Nodes {
234+
pub fn enumerated_nodes(&self) -> EnumeratedNodes<N> {
235+
EnumeratedNodes {
236236
iter: self.nodes.iter().enumerate()
237237
}
238238
}
239239

240-
pub fn all_edges_enumerated(&self) -> Edges<E> {
241-
Edges {
240+
pub fn enumerated_edges(&self) -> EnumeratedEdges<E> {
241+
EnumeratedEdges {
242242
iter: self.edges.iter().enumerate()
243243
}
244244
}
@@ -247,14 +247,14 @@ impl<N: Debug, E: Debug> Graph<N, E> {
247247
where F: FnMut(NodeIndex, &'a Node<N>) -> bool
248248
{
249249
//! Iterates over all edges defined in the graph.
250-
self.all_nodes_enumerated().all(|(node_idx, node)| f(node_idx, node))
250+
self.enumerated_nodes().all(|(node_idx, node)| f(node_idx, node))
251251
}
252252

253253
pub fn each_edge<'a, F>(&'a self, mut f: F) -> bool
254254
where F: FnMut(EdgeIndex, &'a Edge<E>) -> bool
255255
{
256256
//! Iterates over all edges defined in the graph
257-
self.all_edges_enumerated().all(|(edge_idx, edge)| f(edge_idx, edge))
257+
self.enumerated_edges().all(|(edge_idx, edge)| f(edge_idx, edge))
258258
}
259259

260260
pub fn outgoing_edges(&self, source: NodeIndex) -> AdjacentEdges<N, E> {
@@ -295,7 +295,7 @@ impl<N: Debug, E: Debug> Graph<N, E> {
295295
while changed {
296296
changed = false;
297297
iteration += 1;
298-
for (edge_index, edge) in self.all_edges_enumerated() {
298+
for (edge_index, edge) in self.enumerated_edges() {
299299
changed |= op(iteration, edge_index, edge);
300300
}
301301
}
@@ -307,31 +307,60 @@ impl<N: Debug, E: Debug> Graph<N, E> {
307307
-> DepthFirstTraversal<'a, N, E> {
308308
DepthFirstTraversal::with_start_node(self, start, direction)
309309
}
310+
311+
/// Whether or not a node can be reached from itself.
312+
pub fn is_node_cyclic(&self, starting_node_index: NodeIndex) -> bool {
313+
// This is similar to depth traversal below, but we
314+
// can't use that, because depth traversal doesn't show
315+
// the starting node a second time.
316+
let mut visited = BitVector::new(self.len_nodes());
317+
let mut stack = vec![starting_node_index];
318+
319+
while let Some(current_node_index) = stack.pop() {
320+
visited.insert(current_node_index.0);
321+
322+
// Directionality doesn't change the answer,
323+
// so just use outgoing edges.
324+
for (_, edge) in self.outgoing_edges(current_node_index) {
325+
let target_node_index = edge.target();
326+
327+
if target_node_index == starting_node_index {
328+
return true;
329+
}
330+
331+
if !visited.contains(target_node_index.0) {
332+
stack.push(target_node_index);
333+
}
334+
}
335+
}
336+
337+
false
338+
}
310339
}
311340

312341
// # Iterators
313342

314-
pub struct Nodes<'g, N>
343+
pub struct EnumeratedNodes<'g, N>
315344
where N: 'g,
316345
{
317346
iter: ::std::iter::Enumerate<::std::slice::Iter<'g, Node<N>>>
318347
}
319348

320-
impl<'g, N: Debug> Iterator for Nodes<'g, N> {
349+
impl<'g, N: Debug> Iterator for EnumeratedNodes<'g, N> {
321350
type Item = (NodeIndex, &'g Node<N>);
322351

323352
fn next(&mut self) -> Option<(NodeIndex, &'g Node<N>)> {
324353
self.iter.next().map(|(idx, n)| (NodeIndex(idx), n))
325354
}
326355
}
327356

328-
pub struct Edges<'g, E>
357+
pub struct EnumeratedEdges<'g, E>
329358
where E: 'g,
330359
{
331360
iter: ::std::iter::Enumerate<::std::slice::Iter<'g, Edge<E>>>
332361
}
333362

334-
impl<'g, E: Debug> Iterator for Edges<'g, E> {
363+
impl<'g, E: Debug> Iterator for EnumeratedEdges<'g, E> {
335364
type Item = (EdgeIndex, &'g Edge<E>);
336365

337366
fn next(&mut self) -> Option<(EdgeIndex, &'g Edge<E>)> {

src/librustc_data_structures/graph/tests.rs

+42-4
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ fn create_graph() -> TestGraph {
2020

2121
// Create a simple graph
2222
//
23-
// A -+> B --> C
24-
// | | ^
25-
// | v |
26-
// F D --> E
23+
// F
24+
// |
25+
// V
26+
// A --> B --> C
27+
// | ^
28+
// v |
29+
// D --> E
2730

2831
let a = graph.add_node("A");
2932
let b = graph.add_node("B");
@@ -42,6 +45,29 @@ fn create_graph() -> TestGraph {
4245
return graph;
4346
}
4447

48+
fn create_graph_with_cycle() -> TestGraph {
49+
let mut graph = Graph::new();
50+
51+
// Create a graph with a cycle.
52+
//
53+
// A --> B <-- +
54+
// | |
55+
// v |
56+
// C --> D
57+
58+
let a = graph.add_node("A");
59+
let b = graph.add_node("B");
60+
let c = graph.add_node("C");
61+
let d = graph.add_node("D");
62+
63+
graph.add_edge(a, b, "AB");
64+
graph.add_edge(b, c, "BC");
65+
graph.add_edge(c, d, "CD");
66+
graph.add_edge(d, b, "DB");
67+
68+
return graph;
69+
}
70+
4571
#[test]
4672
fn each_node() {
4773
let graph = create_graph();
@@ -139,3 +165,15 @@ fn each_adjacent_from_d() {
139165
let graph = create_graph();
140166
test_adjacent_edges(&graph, NodeIndex(3), "D", &[("BD", "B")], &[("DE", "E")]);
141167
}
168+
169+
#[test]
170+
fn is_node_cyclic_a() {
171+
let graph = create_graph_with_cycle();
172+
assert!(!graph.is_node_cyclic(NodeIndex(0)));
173+
}
174+
175+
#[test]
176+
fn is_node_cyclic_b() {
177+
let graph = create_graph_with_cycle();
178+
assert!(graph.is_node_cyclic(NodeIndex(1)));
179+
}

0 commit comments

Comments
 (0)