public void If() { // Artificially construct an if construct. var graph = TestGraphs.CreateIfElse(); var n1 = graph.GetNodeById(1); var n2 = graph.GetNodeById(2); var n3 = graph.GetNodeById(3); var n4 = graph.GetNodeById(4); // Record a depth first traversal. var traversal = new DepthFirstTraversal(); var recorder = new TraversalOrderRecorder(traversal); traversal.Run(n1); // Check if n1 is before any node in the traversal. Assert.All(graph.GetNodes(), n => Assert.True(n1 == n || recorder.GetIndex(n1) < recorder.GetIndex(n))); // DFS should either pick n2 or n3. If n2, then n4 is before n3, otherwise before n2. if (recorder.GetIndex(n2) < recorder.GetIndex(n3)) { Assert.True(recorder.GetIndex(n4) < recorder.GetIndex(n3)); } else { Assert.True(recorder.GetIndex(n4) < recorder.GetIndex(n2)); } }
public void PostOrderTest() { var graph = new Graph(); graph.Nodes.Add("0"); graph.Nodes.Add("1"); graph.Nodes.Add("2"); graph.Nodes.Add("3"); graph.Nodes.Add("4"); graph.Edges.Add("0", "2"); graph.Edges.Add("2", "1"); graph.Edges.Add("1", "0"); graph.Edges.Add("0", "3"); graph.Edges.Add("3", "4"); var traversal = new DepthFirstTraversal(); var recorder = new PostOrderRecorder(traversal); traversal.Run(graph.Nodes["0"]); var order = recorder.GetOrder(); Assert.Equal(order.Count, graph.Nodes.Count); Assert.All(graph.Nodes, n => Assert.True(order.IndexOf(graph.Nodes["0"]) >= order.IndexOf(n))); Assert.True(order.IndexOf(graph.Nodes["1"]) < order.IndexOf(graph.Nodes["2"])); Assert.True(order.IndexOf(graph.Nodes["4"]) < order.IndexOf(graph.Nodes["3"])); }
public void TestDFSUnDirectedGraph() { IGraph graph = new Graph(); graph.AddNode(new Node("s")); graph.AddNode(new Node("a")); graph.AddNode(new Node("b")); graph.AddNode(new Node("c")); graph.AddNode(new Node("d")); graph.AddNode(new Node("e")); graph.BuildEdge("s", "a", 0); graph.BuildEdge("s", "b", 0); graph.BuildEdge("a", "c", 0); graph.BuildEdge("a", "b", 0); graph.BuildEdge("b", "d", 0); graph.BuildEdge("c", "e", 0); graph.BuildEdge("c", "d", 0); graph.BuildEdge("d", "e", 0); DepthFirstTraversal dfs = new DepthFirstTraversal(graph, graph.GetNodeByID("s")); TraversalResult tr = dfs.Run(); Assert.Equal("s,a,c,e,d,b", string.Join(",", tr.Nodes.Select(node => node.ID))); }
public void TestDFSDirectedGraph() { INode node1 = new Node("1"); INode node2 = new Node("2"); INode node3 = new Node("3"); INode node4 = new Node("4"); INode node5 = new Node("5"); IGraph graph = new Graph(true); graph.AddNode(node1); graph.AddNode(node2); graph.AddNode(node3); graph.AddNode(node4); graph.AddNode(node5); graph.BuildEdge(node1, node2, 0); graph.BuildEdge(node1, node3, 0); graph.BuildEdge(node2, node4, 0); graph.BuildEdge(node2, node5, 0); DepthFirstTraversal dfs = new DepthFirstTraversal(graph, node1); TraversalResult traversalResult = dfs.Run(); string result = string.Join(",", traversalResult.Nodes.Select(node => node.ID)); Assert.Equal("1,2,4,5,3", result); }
public void DepthFirstDepthShouldBeTwoWhenEndNodeIsFoundTest() { // Arrange var start = Tree.Nodes["1"]; var stop = Tree.Nodes["5"]; var traversal = new DepthFirstTraversal(); Node result = null; int depth = 0; traversal.NodeDiscovered += (sender, args) => { if (args.NewNode == stop) { args.ContinueExploring = false; args.Abort = true; result = args.NewNode; depth = args.Depth; } }; // Act traversal.Run(start); // Assert Assert.Equal(stop, result); Assert.Equal(2, depth); }
public void SingleNode() { var graph = TestGraphs.CreateSingularGraph(); var startNode = graph.GetNodeById(1); // Record a depth first traversal. var traversal = new DepthFirstTraversal(); var recorder = new TraversalOrderRecorder(traversal); traversal.Run(startNode); Assert.Single(recorder.GetTraversal()); Assert.Equal(0, recorder.GetIndex(startNode)); }
public void DepthFirstOrderTest() { var traversal = new DepthFirstTraversal(); var recorder = new TraversalOrderRecorder(traversal); traversal.Run(Tree.Nodes["1"]); if (recorder.GetIndex(Tree.Nodes["2"]) < recorder.GetIndex(Tree.Nodes["3A"])) { Assert.True(recorder.GetIndex(Tree.Nodes["4A"]) < recorder.GetIndex(Tree.Nodes["3A"])); } else { Assert.True(recorder.GetIndex(Tree.Nodes["5"]) < recorder.GetIndex(Tree.Nodes["4A"])); } }
public void DepthFirstDepthShouldBeZeroOnFirstNodeTest() { // Arrange var start = new Node("7"); Tree.Nodes.Add(start); var traversal = new DepthFirstTraversal(); traversal.NodeDiscovered += (sender, args) => { Assert.Equal(0, args.Depth); }; // Act traversal.Run(start); // Assert done in NodeDiscoverd event handler. }
public void PathReversed() { // Artificially construct a path of four nodes in sequential order. var graph = TestGraphs.CreatePath(); // Record a depth first traversal. var traversal = new DepthFirstTraversal(true); var recorder = new TraversalOrderRecorder(traversal); traversal.Run(graph.GetNodeById(4)); // Traversal should exactly be the path. Assert.Equal(new INode[] { graph.GetNodeById(4), graph.GetNodeById(3), graph.GetNodeById(2), graph.GetNodeById(1), }, recorder.GetTraversal()); }
public void LoopReversed() { // Artificially construct a looping construct. var graph = TestGraphs.CreateLoop(); var n1 = graph.GetNodeById(1); var n2 = graph.GetNodeById(2); var n3 = graph.GetNodeById(3); var n4 = graph.GetNodeById(4); // Record a depth first traversal. var traversal = new DepthFirstTraversal(true); var recorder = new TraversalOrderRecorder(traversal); traversal.Run(n4); // Check if n1 is before any node in the traversal. Assert.All(graph.GetNodes(), n => Assert.True(n4 == n || recorder.GetIndex(n4) < recorder.GetIndex(n))); Assert.True(recorder.GetIndex(n1) > recorder.GetIndex(n3)); }
/// <summary> /// Determines whether a graph contains at least one cycle. /// </summary> /// <param name="graph">The graph to test.</param> /// <returns>True if the graph is cyclic, false otherwise.</returns> public static bool IsCyclic(this Graph graph) { var visited = new HashSet <Node>(); foreach (var node in graph.Nodes) { if (!visited.Add(node)) { return(false); } bool cycleDetected = false; var reachableNodes = new HashSet <Node>(); var visitedEdges = new HashSet <Edge>(); var traversal = new DepthFirstTraversal(); traversal.NodeDiscovered += (sender, args) => { if (visitedEdges.Add(args.Origin) && !reachableNodes.Add(args.NewNode)) { args.ContinueExploring = false; cycleDetected = true; } }; traversal.Run(node); if (cycleDetected) { return(true); } visited.UnionWith(reachableNodes); } return(false); }
public static ICollection <ISet <Node> > FindStronglyConnectedComponents(this Node entrypoint) { var graph = entrypoint.ParentGraph; var traversal = new DepthFirstTraversal(); var recorder = new PostOrderRecorder(traversal); traversal.Run(entrypoint); var transpose = graph.Transpose(); var visited = new HashSet <Node>(); var result = new List <ISet <Node> >(); foreach (var node in recorder.GetOrder().Reverse()) { if (!visited.Contains(node)) { var subTraversal = new DepthFirstTraversal(); var component = new HashSet <Node>(); subTraversal.NodeDiscovered += (sender, args) => { if (visited.Add(graph.Nodes[args.NewNode.Name])) { args.ContinueExploring = true; component.Add(graph.Nodes[args.NewNode.Name]); } }; subTraversal.Run(transpose.Nodes[node.Name]); result.Add(component); } } return(result); }
/// <summary> /// Computes the dominator tree of a control flow graph, defined by its entrypoint. /// </summary> /// <param name="entrypoint">The entrypoint of the control flow graph.</param> /// <returns>A dictionary mapping all the nodes to their immediate dominator.</returns> /// <remarks> /// The algorithm used is based on the one engineered by Lengauer and Tarjan. /// https://www.cs.princeton.edu/courses/archive/fall03/cs528/handouts/a%20fast%20algorithm%20for%20finding.pdf /// https://www.cl.cam.ac.uk/~mr10/lengtarj.pdf /// </remarks> private static IDictionary <IIdentifiedNode, IIdentifiedNode> GetImmediateDominators(IIdentifiedNode entrypoint) { var idom = new Dictionary <IIdentifiedNode, IIdentifiedNode>(); var semi = new Dictionary <IIdentifiedNode, IIdentifiedNode>(); var ancestor = new Dictionary <IIdentifiedNode, IIdentifiedNode>(); var bucket = new Dictionary <IIdentifiedNode, ISet <IIdentifiedNode> >(); var traversal = new DepthFirstTraversal(); var order = new TraversalOrderRecorder(traversal); var parents = new ParentRecorder(traversal); traversal.Run(entrypoint); var orderedNodes = order.GetTraversal(); foreach (var node in orderedNodes.Cast <IIdentifiedNode>()) { idom[node] = null; semi[node] = node; ancestor[node] = null; bucket[node] = new HashSet <IIdentifiedNode>(); } for (int i = orderedNodes.Count - 1; i >= 1; i--) { var current = (IIdentifiedNode)orderedNodes[i]; var parent = (IIdentifiedNode)parents.GetParent(current); // step 2 foreach (var predecessor in current.GetPredecessors().Cast <IIdentifiedNode>()) { var u = Eval(predecessor, ancestor, semi, order); if (order.GetIndex(semi[current]) > order.GetIndex(semi[u])) { semi[current] = semi[u]; } } bucket[semi[current]].Add(current); Link(parent, current, ancestor); // step 3 foreach (var bucketNode in bucket[parent]) { var u = Eval(bucketNode, ancestor, semi, order); if (order.GetIndex(semi[u]) < order.GetIndex(semi[bucketNode])) { idom[bucketNode] = u; } else { idom[bucketNode] = parent; } } bucket[parent].Clear(); } // step 4 for (int i = 1; i < orderedNodes.Count; i++) { var w = (IIdentifiedNode)orderedNodes[i]; if (idom[w] != semi[w]) { idom[w] = idom[idom[w]]; } } idom[entrypoint] = entrypoint; return(idom); }