using System; using System.Collections.Generic; using System.Linq; using Collections.Pooled; namespace MoonTools.Graph { public class DirectedGraph : SimpleGraph, IUnweightedGraph where TNode : IEquatable { private class SimpleCycleComparer : IEqualityComparer> { public bool Equals(IEnumerable x, IEnumerable y) { return x.SequenceEqual(y); } public int GetHashCode(IEnumerable obj) { return obj.Aggregate(0, (current, next) => current.GetHashCode() ^ next.GetHashCode()); } } public virtual void AddEdge(TNode v, TNode u, TEdgeData edgeData) { BaseAddEdge(v, u, edgeData); } public virtual void AddEdges(params (TNode, TNode, TEdgeData)[] edges) { foreach (var edge in edges) { AddEdge(edge.Item1, edge.Item2, edge.Item3); } } public void RemoveNode(TNode node) { CheckNodes(node); var edgesToRemove = new PooledList<(TNode, TNode)>(ClearMode.Always); foreach (var entry in neighbors) { if (entry.Value.Contains(node)) { edgesToRemove.Add((entry.Key, node)); } } foreach (var edge in edgesToRemove) { RemoveEdge(edge.Item1, edge.Item2); } edgesToRemove.Dispose(); nodes.Remove(node); neighbors.Remove(node); } public virtual void RemoveEdge(TNode v, TNode u) { CheckEdge(v, u); neighbors[v].Remove(u); edges.Remove((v, u)); edgeToEdgeData.Remove((v, u)); } public IEnumerable PreorderNodeDFS() { var dfsStack = new PooledStack(ClearMode.Always); var dfsDiscovered = new PooledSet(ClearMode.Always); foreach (var node in Nodes) { if (!dfsDiscovered.Contains(node)) { dfsStack.Push(node); while (dfsStack.Count > 0) { var current = dfsStack.Pop(); if (!dfsDiscovered.Contains(current)) { dfsDiscovered.Add(current); yield return current; foreach (var neighbor in Neighbors(current)) { dfsStack.Push(neighbor); } } } } } dfsStack.Dispose(); dfsDiscovered.Dispose(); } private IEnumerable PostorderNodeDFSHelper(PooledSet discovered, TNode v) { discovered.Add(v); foreach (var neighbor in Neighbors(v)) { if (!discovered.Contains(neighbor)) { foreach (var node in PostorderNodeDFSHelper(discovered, neighbor)) { yield return node; } } } yield return v; } public IEnumerable PostorderNodeDFS() { var dfsDiscovered = new PooledSet(ClearMode.Always); foreach (var node in Nodes) { if (!dfsDiscovered.Contains(node)) { foreach (var thing in PostorderNodeDFSHelper(dfsDiscovered, node)) { yield return thing; } } } } public IEnumerable NodeBFS() { var bfsQueue = new PooledQueue(ClearMode.Always); var bfsDiscovered = new PooledSet(ClearMode.Always); foreach (var node in Nodes) { if (!bfsDiscovered.Contains(node)) { bfsQueue.Enqueue(node); while (bfsQueue.Count > 0) { var current = bfsQueue.Dequeue(); foreach (var neighbor in Neighbors(current)) { if (!bfsDiscovered.Contains(neighbor)) { bfsDiscovered.Add(neighbor); bfsQueue.Enqueue(neighbor); yield return neighbor; } } } } } bfsQueue.Dispose(); bfsDiscovered.Dispose(); } List> lexicographicSets = new List>(); HashSet> replacedSets = new HashSet>(); public IEnumerable LexicographicBFS() { lexicographicSets.Add(Nodes.ToPooledSet()); while (lexicographicSets.Count > 0) { var firstSet = lexicographicSets[0]; var node = firstSet.First(); firstSet.Remove(node); if (firstSet.Count == 0) { lexicographicSets.RemoveAt(0); } yield return node; foreach (var neighbor in Neighbors(node)) { if (lexicographicSets.Any(set => set.Contains(neighbor))) { var s = lexicographicSets.Find(set => set.Contains(neighbor)); var sIndex = lexicographicSets.IndexOf(s); PooledSet t; if (replacedSets.Contains(s) && sIndex > 0) { t = lexicographicSets[sIndex - 1]; } else { t = new PooledSet(ClearMode.Always); lexicographicSets.Insert(sIndex, t); replacedSets.Add(s); } s.Remove(neighbor); t.Add(neighbor); if (s.Count == 0) { lexicographicSets.Remove(s); replacedSets.Remove(s); s.Dispose(); } } } } lexicographicSets.Clear(); replacedSets.Clear(); } public bool Cyclic() { return StronglyConnectedComponents().Any((scc) => scc.Count() > 1); } public IEnumerable TopologicalSort() { return PostorderNodeDFS().Reverse(); } List> sccs = new List>(); public IEnumerable> StronglyConnectedComponents() { foreach (var scc in sccs) { scc.Dispose(); } sccs.Clear(); var preorder = new PooledDictionary(ClearMode.Always); var lowlink = new PooledDictionary(ClearMode.Always); var sccFound = new PooledDictionary(ClearMode.Always); var sccQueue = new PooledStack(ClearMode.Always); uint preorderCounter = 0; foreach (var source in Nodes) { if (!sccFound.ContainsKey(source)) { var queue = new Stack(); queue.Push(source); while (queue.Count > 0) { var v = queue.Peek(); if (!preorder.ContainsKey(v)) { preorderCounter++; preorder[v] = preorderCounter; } var done = true; var vNeighbors = Neighbors(v); foreach (var w in vNeighbors) { if (!preorder.ContainsKey(w)) { queue.Push(w); done = false; break; } } if (done) { lowlink[v] = preorder[v]; foreach (var w in vNeighbors) { if (!sccFound.ContainsKey(w)) { if (preorder[w] > preorder[v]) { lowlink[v] = Math.Min(lowlink[v], lowlink[w]); } else { lowlink[v] = Math.Min(lowlink[v], preorder[w]); } } } queue.Pop(); if (lowlink[v] == preorder[v]) { sccFound[v] = true; var scc = new PooledList(ClearMode.Always) { v }; while (sccQueue.Count > 0 && preorder[sccQueue.Peek()] > preorder[v]) { var k = sccQueue.Pop(); sccFound[k] = true; scc.Add(k); } sccs.Add(scc); yield return scc; } else { sccQueue.Push(v); } } } } } } public IEnumerable> SimpleCycles() { void unblock(TNode thisnode, HashSet blocked, Dictionary> B) //refactor to remove closure { var stack = new Stack(); stack.Push(thisnode); while (stack.Count > 0) { var node = stack.Pop(); if (blocked.Contains(thisnode)) { blocked.Remove(thisnode); if (B.ContainsKey(node)) { foreach (var n in B[node]) { if (!stack.Contains(n)) { stack.Push(n); } } B[node].Clear(); } } } } List> result = new List>(); var subGraph = Clone(); var sccs = new Stack>(); foreach (var scc in StronglyConnectedComponents()) { sccs.Push(scc); } while (sccs.Count > 0) { var scc = new Stack(sccs.Pop()); var startNode = scc.Pop(); var path = new Stack(); path.Push(startNode); var blocked = new HashSet { startNode }; var closed = new HashSet(); var B = new Dictionary>(); var stack = new Stack>>(); stack.Push(Tuple.Create(startNode, new Stack(subGraph.Neighbors(startNode)))); while (stack.Count > 0) { var entry = stack.Peek(); var thisnode = entry.Item1; var neighbors = entry.Item2; if (neighbors.Count > 0) { var nextNode = neighbors.Pop(); if (nextNode.Equals(startNode)) { var resultPath = new List(); foreach (var v in path) { resultPath.Add(v); } result.Add(resultPath); foreach (var v in path) { closed.Add(v); } } else if (!blocked.Contains(nextNode)) { path.Push(nextNode); stack.Push(Tuple.Create(nextNode, new Stack(subGraph.Neighbors(nextNode)))); closed.Remove(nextNode); blocked.Add(nextNode); continue; } } if (neighbors.Count == 0) { if (closed.Contains(thisnode)) { unblock(thisnode, blocked, B); } else { foreach (var neighbor in subGraph.Neighbors(thisnode)) { if (!B.ContainsKey(neighbor)) { B[neighbor] = new HashSet(); } B[neighbor].Add(thisnode); } } stack.Pop(); path.Pop(); } } subGraph.RemoveNode(startNode); var H = subGraph.SubGraph(scc.ToArray()); var HSccs = H.StronglyConnectedComponents(); foreach (var HScc in HSccs) { sccs.Push(HScc); } } return result.Distinct(new SimpleCycleComparer()); } public DirectedGraph Clone() { var clone = new DirectedGraph(); clone.AddNodes(Nodes.ToArray()); foreach (var v in Nodes) { foreach (var n in Neighbors(v)) { clone.AddEdge(v, n, EdgeData(v, n)); } } return clone; } public DirectedGraph SubGraph(params TNode[] subVertices) { var subGraph = new DirectedGraph(); subGraph.AddNodes(subVertices.ToArray()); foreach (var n in Nodes) { if (Nodes.Contains(n)) { var neighbors = Neighbors(n); foreach (var u in neighbors) { if (subVertices.Contains(u)) { subGraph.AddEdge(n, u, EdgeData(n, u)); } } } } return subGraph; } public override void Clear() { base.Clear(); } } }