namespace ErsatzTV.Core.Search; public class AdjGraph { private readonly List _edges = []; public void Clear() => _edges.Clear(); public void AddEdge(string from, string to) => _edges.Add(new Edge(from.ToLowerInvariant(), to.ToLowerInvariant())); public bool HasCycle(string from) { var visited = new System.Collections.Generic.HashSet(); var stack = new System.Collections.Generic.HashSet(); return HasCycleImpl(from.ToLowerInvariant(), visited, stack); } public bool HasAnyCycle() => _edges.Any(edge => HasCycle(edge.From)); private bool HasCycleImpl(string node, ISet visited, ISet stack) { if (stack.Contains(node)) { return true; } if (!visited.Add(node)) { return false; } stack.Add(node); foreach (Edge edge in _edges.Where(e => e.From == node)) { if (HasCycleImpl(edge.To, visited, stack)) { return true; } } stack.Remove(node); return false; } private record Edge(string From, string To); }