public static Node BreadthFirstDeletion(Node startNode, HashSet<int> allIncludedNodes, int[] explicitlyIncludedNodes) { var stack = new Stack<Node>(); stack.Push(startNode); while (stack.Count != 0) { var current = stack.Pop(); if (allIncludedNodes.Contains(current.Id)) { MarkNodeInclusionType(explicitlyIncludedNodes, current); RemoveExcludedParents(allIncludedNodes, current.Parents); foreach (var child in current.Children) { stack.Push(child); } } else { RemoveNodeFromTheTree(current); } } return startNode; }
public static NodeClosures Analyse(Node root) { var results = new NodeClosures { AncestorClosures = new Dictionary<int, HashSet<int>>(), DescendantClosures = new Dictionary<int, HashSet<int>>() }; //walk the tree and build the closures lists var stack = new Stack<Node>(); stack.Push(root); var route = new Stack<Node>(); while (stack.Count != 0) { var current = stack.Pop(); while (route.Any() && !route.Peek().Children.Any(n => n.Id == current.Id)) { route.Pop(); } route.Push(current); AddNodeRelationships(results, current, route); foreach (var child in current.Children) { stack.Push(child); } } return results; }
public static dynamic BuildLargeGraph(int depth, int levelMaxSiblings) { var id = 0; var builtLevel = 0; var root = new Node {Id = id++}; var queue = new Queue<Node>(100000000); queue.Enqueue(root); while (builtLevel < depth) { var nextQueue = new Queue<Node>(); while (queue.Count != 0) { var node = queue.Dequeue(); var max = Random.Next(1, levelMaxSiblings); for (var siblingCount = 0; siblingCount < max; siblingCount++) { nextQueue.Enqueue(Node.CreateNode(id++, node)); } } queue = nextQueue; builtLevel++; } return new {MaxId = id, Tree = root}; }
private static void AddNodeRelationships(NodeClosures results, Node current, IEnumerable<Node> route) { foreach (var node in route) { results.AncestorClosures.GetOrCreateValuesList(current.Id).Add(node.Id); results.DescendantClosures.GetOrCreateValuesList(node.Id).Add(current.Id); } }
/// <summary> /// 0 /// / | \ /// 1 2 3 /// / \ | /// 4 5 6 /// / \ / \ /// 7 8 9 /// | /// 10 /// </summary> public static Node BuildExampleGraph() { var root = new Node {Id = 0}; var one = Node.CreateNode(1, root); Node.CreateNode(2, root); var three = Node.CreateNode(3, root); var four = Node.CreateNode(4, one); var five = Node.CreateNode(5, one); var six = Node.CreateNode(6, three); Node.CreateNode(7, four); var eight = Node.CreateNode(8, five); eight.Parents.Add(six); six.Children.Add(eight); var nine = Node.CreateNode(9, six); Node.CreateNode(10, nine); return root; }
public void CanDeleteNodes() { _root.FirstOrDefaultDescendant(n => n.Id == 6).IsExplicitlyIncluded = true; foreach (var i in new []{0, 3, 8, 9, 10}) { var capturedId = i; _root.FirstOrDefaultDescendant(n => n.Id == capturedId).IsImplicitlyIncluded = true; } _root = Node.BreadthFirstDeletion(_root, new HashSet<int>{0,3,6,8,9,10}, new []{6}); var removedNodes = new[] { 1, 2, 4, 5, 7 }; foreach (var id in removedNodes) { var capturedId = id; _root.FirstOrDefaultDescendant(n => n.Id == capturedId).Should().BeNull(); } var keptNodes = new[] { 0, 3, 6, 8, 9, 10 }; foreach (var id in keptNodes) { var capturedId = id; var match = _root.FirstOrDefaultDescendant(n => n.Id == capturedId); match.Should().NotBeNull(); match.IsExplicitlyIncluded.Should().Be(match.Id == 6); } }
public void Setup() { _root = GraphBuilder.BuildExampleGraph(); }
public static Node CreateNode(int id, Node parent) { var n = new Node { Id = id }; parent.Children.Add(n); n.Parents.Add(parent); return n; }
private static void RemoveNodeFromTheTree(Node current) { for (var i = 0; i < current.Parents.Count; i++) { var parent = current.Parents.ElementAt(i); parent.Children.Remove(current); } }
private static void ProcessParents(Node template, Node clone, Node root) { var stack = new Stack<Node>(); foreach (var parent in template.Parents) { stack.Push(parent); } while (stack.Count != 0) { var current = stack.Pop(); Node targetParent; if (root == null) { targetParent = new Node {Id = current.Id}; } else { targetParent = root.FirstOrDefaultDescendant(n => n.Id == current.Id) ?? new Node { Id = current.Id }; } clone.Parents.Add(targetParent); targetParent.Children.Add(clone); ProcessParents(current, targetParent, root); } }
private static void ProcessChildren(Node templateParent, Node copyParent) { var stack = new Stack<Node>(); foreach (var child in templateParent.Children) { stack.Push(child); } while (stack.Count != 0) { var template = stack.Pop(); var copy = new Node { Id = template.Id }; copy.Parents.Add(copyParent); copyParent.Children.Add(copy); ProcessChildren(template, copy); } }
private static void MarkNodeInclusionType(IEnumerable<int> explicitlyIncludedNodes, Node current) { if (explicitlyIncludedNodes.Contains(current.Id)) { current.IsExplicitlyIncluded = true; } else { current.IsImplicitlyIncluded = true; } }
protected bool Equals(Node other) { return Id == other.Id; }
public Node CloneBranch() { var n = new Node {Id = Id}; ProcessChildren(this, n); return n; }
public Node CloneAncestors(Node clone, Node root) { ProcessParents(this, clone, root); return clone.FirstOrDefaultAncestor(n => n.Parents.Count == 0); }
/// <summary> /// Partial Bottom Up Breadth first search for nodes which are neither explicitly nor implicitly included /// </summary> public static Node Filter(Node startNode, IEnumerable<int> includedNodes) { var explicitlyIncludedNodes = startNode.DescendantsWhere(n => includedNodes.Contains(n.Id)).ToArray(); if (!explicitlyIncludedNodes.Any()) { return null; } var closures = NodeClosureAnalyser.Analyse(startNode); var results = new HashSet<int>(); foreach (var node in explicitlyIncludedNodes) { var ancestors = closures.AncestorClosures[node.Id]; var descendants = closures.DescendantClosures[node.Id]; results = new HashSet<int>(ancestors.Union(descendants).Union(results)); } return BreadthFirstDeletion(startNode, results, includedNodes.ToArray()); return null; }