using System; using System.Collections.Generic; using System.Linq; namespace ReClassNET.Util { public class DirectedGraph { private readonly IDictionary> adjacencyList = new Dictionary>(); /// /// Gets an enumeration of all vertices in the graph. /// public IEnumerable Vertices => adjacencyList.Keys; /// /// Adds the vertex to the graph. /// /// /// public bool AddVertex(T vertex) { if (adjacencyList.ContainsKey(vertex)) { return false; } adjacencyList.Add(vertex, new HashSet()); return true; } /// /// Adds the vertices to the graph. /// /// public void AddVertices(IEnumerable vertices) { foreach (var vertex in vertices) { AddVertex(vertex); } } /// /// Tests if the graph contains the given vertex. /// /// /// public bool ContainsVertex(T vertex) { return adjacencyList.ContainsKey(vertex); } /// /// Adds an edge between both vertices to the graph. /// /// /// /// True if a new edge was added, false otherwise. public bool AddEdge(T from, T to) { if (!ContainsVertex(to) || !adjacencyList.TryGetValue(from, out var edges)) { throw new ArgumentException("Vertex does not exist in graph."); } return edges.Add(to); } /// /// Tests if the graph contains an edge between both vertices. /// /// /// /// public bool ContainsEdge(T from, T to) { if (!ContainsVertex(to) || !adjacencyList.TryGetValue(from, out var edges)) { throw new ArgumentException("Vertex does not exist in graph."); } return edges.Contains(to); } /// /// Gets all neighbours of the given vertex. /// /// The vertex to check. /// An enumeration of all neighbours of the given vertex. public IEnumerable GetNeighbours(T vertex) { if (!adjacencyList.TryGetValue(vertex, out var edges)) { throw new ArgumentException("Vertex does not exist in graph."); } return edges; } /// /// Tests with a depth first search if the graph contains a cycle. /// /// True if a cycle exists, false otherwise. public bool ContainsCycle() { var visited = new HashSet(); var recursionStack = new HashSet(); bool IsCyclic(T source) { if (visited.Add(source)) { recursionStack.Add(source); foreach (var adjacent in GetNeighbours(source)) { if (!visited.Contains(adjacent) && IsCyclic(adjacent)) { return true; } if (recursionStack.Contains(adjacent)) { return true; } } } recursionStack.Remove(source); return false; } return adjacencyList.Keys.Any(IsCyclic); } } }