using System; using System.Collections.Generic; using System.Linq; namespace BTCPayServer; public static class TopologicalSortExtensions { public static List TopologicalSort(this IReadOnlyCollection nodes, Func> dependsOn) { return nodes.TopologicalSort(dependsOn, k => k, k => k); } public static List TopologicalSort(this IReadOnlyCollection nodes, Func> dependsOn, Func getKey) { return nodes.TopologicalSort(dependsOn, getKey, o => o); } public static List TopologicalSort(this IReadOnlyCollection nodes, Func> dependsOn, Func getKey, Func getValue, IComparer solveTies = null) { if (nodes.Count == 0) return new List(); if (getKey == null) throw new ArgumentNullException(nameof(getKey)); if (getValue == null) throw new ArgumentNullException(nameof(getValue)); solveTies = solveTies ?? Comparer.Default; List result = new List(nodes.Count); HashSet allKeys = new HashSet(nodes.Count); var noDependencies = new SortedDictionary>(solveTies); foreach (var node in nodes) allKeys.Add(getKey(node)); var dependenciesByValues = nodes.ToDictionary(node => node, node => new HashSet(dependsOn(node).Where(n => allKeys.Contains(n)))); foreach (var e in dependenciesByValues.Where(x => x.Value.Count == 0)) { noDependencies.Add(e.Key, e.Value); } if (noDependencies.Count == 0) { throw new InvalidOperationException("Impossible to topologically sort a cyclic graph"); } while (noDependencies.Count > 0) { var nodep = noDependencies.First(); noDependencies.Remove(nodep.Key); dependenciesByValues.Remove(nodep.Key); var elemKey = getKey(nodep.Key); result.Add(getValue(nodep.Key)); foreach (var selem in dependenciesByValues) { if (selem.Value.Remove(elemKey) && selem.Value.Count == 0) noDependencies.Add(selem.Key, selem.Value); } } if (dependenciesByValues.Count != 0) { throw new InvalidOperationException("Impossible to topologically sort a cyclic graph"); } return result; } }