using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace Robust.Shared.Utility { public sealed class TopologicalSort { public static IEnumerable Sort(IEnumerable> nodes) { var totalVerts = 0; var empty = new Queue>(); var nodesArray = nodes.ToArray(); foreach (var node in nodesArray) { totalVerts += 1; foreach (var dep in node.Dependant) { dep.DependsOnCount += 1; } } foreach (var node in nodesArray) { if (node.DependsOnCount == 0) empty.Enqueue(node); } while (empty.TryDequeue(out var node)) { yield return node.Value; totalVerts -= 1; foreach (var dep in node.Dependant) { dep.DependsOnCount -= 1; if (dep.DependsOnCount == 0) empty.Enqueue(dep); } } if (totalVerts != 0) throw new InvalidOperationException("Graph contained cycle(s)."); } // I will never stop using the word "datum". public static IEnumerable> FromBeforeAfter( IEnumerable data, Func keySelector, Func> beforeSelector, Func> afterSelector, bool allowMissing = false) where TValue : notnull { return FromBeforeAfter(data, keySelector, keySelector, afterSelector, beforeSelector, allowMissing); } public static IEnumerable> FromBeforeAfter( IEnumerable data, Func keySelector, Func valueSelector, Func> beforeSelector, Func> afterSelector, bool allowMissing=false) where TKey : notnull { var dict = new Dictionary node)>(); foreach (var datum in data) { var key = keySelector(datum); var value = valueSelector(datum); dict.Add(key, (datum, new GraphNode(value))); } foreach (var (key, (datum, node)) in dict) { foreach (var before in beforeSelector(datum)) { if (dict.TryGetValue(before, out var entry)) { node.Dependant.Add(entry.node); } else if (!allowMissing) { throw new InvalidOperationException($"Vertex '{before}' referenced by '{key}' was not found in the graph."); } } foreach (var after in afterSelector(datum)) { if (dict.TryGetValue(after, out var entry)) { entry.node.Dependant.Add(node); } else if (!allowMissing) { throw new InvalidOperationException($"Vertex '{after}' referenced by '{key}' was not found in the graph."); } } } return dict.Values.Select(c => c.node); } [DebuggerDisplay("GraphNode: {" + nameof(Value) + "}")] public sealed class GraphNode { public readonly T Value; public readonly List> Dependant = new(); // Used internal by sort implementation, do not touch. internal int DependsOnCount; public GraphNode(T value) { Value = value; } } } }