using System.Diagnostics.CodeAnalysis; namespace InnovEnergy.Lib.Utils; public static class GraphTraversal { public static IEnumerable TraverseDepthFirstPreOrder(T root, Func> getChildren, IEqualityComparer? comparer = null) { return Traverse(root, TreeTraversal.TraverseDepthFirstPreOrder, getChildren, comparer); } public static IEnumerable TraverseDepthFirstPostOrder(T root, Func> getChildren, IEqualityComparer? comparer = null) { return Traverse(root, TreeTraversal.TraverseDepthFirstPostOrder, getChildren, comparer); } public static IEnumerable TraverseBreadthFirst(T root, Func> getChildren, IEqualityComparer? comparer = null) { return Traverse(root, TreeTraversal.TraverseBreadthFirst, getChildren, comparer); } public static IEnumerable TraverseDepthFirstPreOrder(IEnumerable sources, Func> getChildren, IEqualityComparer? comparer = null) { return Traverse(sources, TreeTraversal.TraverseDepthFirstPreOrder, getChildren, comparer); } public static IEnumerable TraverseDepthFirstPostOrder(IEnumerable sources, Func> getChildren, IEqualityComparer? comparer = null) { return Traverse(sources, TreeTraversal.TraverseDepthFirstPostOrder, getChildren, comparer); } public static IEnumerable TraverseBreadthFirst(IEnumerable sources, Func> getChildren, IEqualityComparer? comparer = null) { return Traverse(sources, TreeTraversal.TraverseBreadthFirst, getChildren, comparer); } private static IEnumerable Traverse(T root, Func>,IEnumerable> traversor, Func> getChildren, IEqualityComparer? comparer = null) { var getUniqueChildren = GetUniqueChildren(getChildren, root, comparer); return traversor(root, getUniqueChildren); } [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] private static IEnumerable Traverse(IEnumerable sources, Func>,IEnumerable> traversor, Func> getChildren, IEqualityComparer? comparer = null) { var set = new HashSet(sources, comparer ?? EqualityComparer.Default); IEnumerable GetUniqueChildren(T n) => getChildren(n).Where(set.Add); return from s in sources from e in traversor(s, GetUniqueChildren) select e; } // TODO: IEqualityComparer public static IEnumerable<(T source, T target)> Edges(T node, Func> getChildren) { return Edges(node.AsSingleEnumerable(), getChildren); } // TODO: IEqualityComparer public static IEnumerable<(T source, T target)> Edges(IEnumerable sources, Func> getChildren) { var hs = new HashSet<(T source, T target)>(); IEnumerable<(T source, T target)> GetChildEdges((T source, T target) edge) { return getChildren(edge.target) .Select(c => (edge.target, c)) .Where(hs!.Add); } return from src in sources.Select(s => (s, s)) from e in TreeTraversal.TraverseDepthFirstPreOrder(src, GetChildEdges).Skip(1) select e; } private static Func> GetUniqueChildren(Func> getChildren, T root, IEqualityComparer? comparer) { return GetUniqueChildren(getChildren, root.AsSingleEnumerable(), comparer); } private static Func> GetUniqueChildren(Func> getChildren, IEnumerable sources, IEqualityComparer? comparer) { var set = new HashSet(sources, comparer ?? EqualityComparer.Default); return n => getChildren(n).Where(set.Add); } }