private static List <List <Node <TValue, TMetadata> > > FindCycles <TValue, TMetadata>(List <Node <TValue, TMetadata> > unsortedItems) where TValue : class where TMetadata : IOrderable { for (int i = 0; (i < unsortedItems.Count); ++i) { var n = unsortedItems[i]; n.Index = -1; n.LowIndex = -1; n.ContainedInKnownCycle = false; } List <List <Node <TValue, TMetadata> > > cycles = new List <List <Node <TValue, TMetadata> > >(); Stack <Node <TValue, TMetadata> > stack = new Stack <Node <TValue, TMetadata> >(unsortedItems.Count); int index = 0; for (int i = 0; (i < unsortedItems.Count); ++i) { var node = unsortedItems[i]; if (node.Index == -1) { Orderer.FindCycles(node, stack, ref index, cycles); Debug.Assert(stack.Count == 0); } } return(cycles); }
public static IList <Lazy <TValue, TMetadata> > Order <TValue, TMetadata>(IEnumerable <Lazy <TValue, TMetadata> > itemsToOrder) where TValue : class where TMetadata : IOrderable { if (itemsToOrder == null) { throw new ArgumentNullException(nameof(itemsToOrder)); } #if false && DEBUG Debug.WriteLine("Before ordering"); DumpGraph(itemsToOrder); #endif var roots = new Queue <Node <TValue, TMetadata> >(); var unsortedItems = new List <Node <TValue, TMetadata> >(); Orderer.PrepareGraph(itemsToOrder, roots, unsortedItems); IList <Lazy <TValue, TMetadata> > sortedItems = Orderer.TopologicalSort(roots, unsortedItems); #if false && DEBUG Debug.WriteLine("After ordering"); DumpGraph(sortedItems); #endif return(sortedItems); }
public void ClearBefore(Queue <Node <TValue, TMetadata> > roots) { List <Node <TValue, TMetadata> > newRoots = new List <Node <TValue, TMetadata> >(); foreach (Node <TValue, TMetadata> child in this.Before) { child.After.Remove(this); if (child.After.Count == 0) { newRoots.Add(child); } } this.Before.Clear(); Orderer.AddToRoots(roots, newRoots); }
private static void FindCycles <TValue, TMetadata>(Node <TValue, TMetadata> node, Stack <Node <TValue, TMetadata> > stack, ref int index, List <List <Node <TValue, TMetadata> > > cycles) where TValue : class where TMetadata : IOrderable { node.Index = index; node.LowIndex = index; ++index; stack.Push(node); foreach (Node <TValue, TMetadata> child in node.Before) { if (child.Index == -1) { Orderer.FindCycles(child, stack, ref index, cycles); node.LowIndex = Math.Min(node.LowIndex, child.LowIndex); } else if (!child.ContainedInKnownCycle) { node.LowIndex = Math.Min(node.LowIndex, child.Index); } } if (node.Index == node.LowIndex) { List <Node <TValue, TMetadata> > cycle = new List <Node <TValue, TMetadata> >(); while (stack.Count > 0) { Node <TValue, TMetadata> child = stack.Pop(); cycle.Add(child); child.ContainedInKnownCycle = true; if (child == node) { //Single unit cycles aren't interesting (since we are preventing node from linking to themselves in the Resolve code below). if (cycle.Count > 1) { cycles.Add(cycle); } break; } Debug.Assert(stack.Count > 0); } } }
private static IList <Lazy <TValue, TMetadata> > TopologicalSort <TValue, TMetadata>(Queue <Node <TValue, TMetadata> > roots, List <Node <TValue, TMetadata> > unsortedItems) where TValue : class where TMetadata : IOrderable { List <Lazy <TValue, TMetadata> > sortedItems = new List <Lazy <TValue, TMetadata> >(); while (unsortedItems.Count > 0) { Node <TValue, TMetadata> node = (roots.Count == 0) ? Orderer.BreakCircularReference(unsortedItems) : roots.Dequeue(); Debug.Assert(node.After.Count == 0); if (node.Item != null) { sortedItems.Add(node.Item); } unsortedItems.Remove(node); node.ClearBefore(roots); } return(sortedItems); }
private static Node <TValue, TMetadata> BreakCircularReference <TValue, TMetadata>(List <Node <TValue, TMetadata> > unsortedItems) where TValue : class where TMetadata : IOrderable { //We have a circular reference in the unsortedItems. //This is an error in the definition that we need to handle gracefully. //Find & report the cycle. List <List <Node <TValue, TMetadata> > > cycles = Orderer.FindCycles(unsortedItems); Debug.Assert(cycles.Count > 0); #if DEBUG Debug.WriteLine("Orderer found cycles:"); foreach (List <Node <TValue, TMetadata> > cycle in cycles) { foreach (Node <TValue, TMetadata> node in cycle) { Debug.Write("\t" + node.Name); } Debug.WriteLine(""); } #endif //Find the cycle with the fewest inbound links from other cycles. int bestInwardLinkCount = int.MaxValue; List <Node <TValue, TMetadata> > bestCycle = null; for (int i = 0; (i < cycles.Count); ++i) { var cycle = cycles[i]; int inwardLinkCount = 0; for (int j = 0; (j < cycle.Count); ++j) { var node = cycle[j]; foreach (Node <TValue, TMetadata> child in node.After) { if (child.LowIndex != node.LowIndex) { ++inwardLinkCount; break; } } } if (inwardLinkCount < bestInwardLinkCount) { bestCycle = cycle; bestInwardLinkCount = inwardLinkCount; } } //Given the best cycle we can find, pick the node that would break the smallest number of "after" constraints. Node <TValue, TMetadata> bestNode; if (bestCycle == null) { //Odd, no cycles were found so we need to guess at random. bestNode = unsortedItems[0]; Debug.Fail("Orderer was unable to find a cycle to break"); } else { bestNode = bestCycle[0]; for (int i = 1; (i < bestCycle.Count); ++i) { Node <TValue, TMetadata> node = bestCycle[i]; if (node.After.Count < bestNode.After.Count) { bestNode = node; } } } foreach (Node <TValue, TMetadata> a in bestNode.After) { a.Before.Remove(bestNode); } bestNode.After.Clear(); return(bestNode); }