public static bool TryResolveConflicts <TItem>(this GraphNode <TItem> root) { // now we walk the tree as often as it takes to determine // which paths are accepted or rejected, based on conflicts occuring // between cousin packages var patience = 1000; var incomplete = true; while (incomplete && --patience != 0) { // Create a picture of what has not been rejected yet var tracker = new Tracker <TItem>(); root.ForEach(true, (node, state) => { if (!state || node.Disposition == Disposition.Rejected) { // Mark all nodes as rejected if they aren't already marked node.Disposition = Disposition.Rejected; return(false); } tracker.Track(node.Item); return(true); }); // Inform tracker of ambiguity beneath nodes that are not resolved yet // between: // a1->b1->d1->x1 // a1->c1->d2->z1 // first attempt // d1/d2 are considered disputed // x1 and z1 are considered ambiguous // d1 is rejected // second attempt // d1 is rejected, d2 is accepted // x1 is no longer seen, and z1 is not ambiguous // z1 is accepted root.ForEach("Walking", (node, state) => { if (node.Disposition == Disposition.Rejected) { return("Rejected"); } if (state == "Walking" && tracker.IsDisputed(node.Item)) { return("Ambiguous"); } if (state == "Ambiguous") { tracker.MarkAmbiguous(node.Item); } return(state); }); // Now mark unambiguous nodes as accepted or rejected root.ForEach(true, (node, state) => { if (!state || node.Disposition == Disposition.Rejected) { return(false); } if (tracker.IsAmbiguous(node.Item)) { return(false); } if (node.Disposition == Disposition.Acceptable) { node.Disposition = tracker.IsBestVersion(node.Item) ? Disposition.Accepted : Disposition.Rejected; } return(node.Disposition == Disposition.Accepted); }); incomplete = false; root.ForEach(node => incomplete |= node.Disposition == Disposition.Acceptable); } return(!incomplete); }