private static RemovalResult RemoveEdgeRecursive(INodeProvider <Guid, object, EdgeData> nodes, Guid nodeId, EdgeData data, int treeOrder) { var node = nodes.GetNode(nodeId, NodeAccess.ReadWrite); try { if (node.Data.Equals(LeafNodeData)) { var removeResult = node.Edges.Remove(data); return(new RemovalResult(node.Edges.Count, removeResult, false)); } else { var edgeIndex = FindLeadingEdgeIndex(data, node); var edge = node.Edges[node.Edges.Keys[edgeIndex]]; var res = RemoveEdgeRecursive(nodes, edge.ToNodeId, data, treeOrder); if (!res.WasRemoved) { return(res); } if (res.RemainingCount < treeOrder / 2) { #region Reorganize sub nodes // Take data from a sibling if (edgeIndex < node.Edges.Count - 1) { return(MergeNodes(nodes, node, edgeIndex, edgeIndex + 1, treeOrder)); } else { return(MergeNodes(nodes, node, edgeIndex - 1, edgeIndex, treeOrder)); } #endregion } else { return(res); } } } finally { nodes.SetNode(nodeId, node); } }
private static RemovalResult MergeNodes(INodeProvider <Guid, object, EdgeData> nodes, Node <Guid, object, EdgeData> node, int leftIndex, int rightIndex, int treeOrder) { var leftKey = node.Edges.Keys[leftIndex]; var leftNodeId = node.Edges[leftKey].ToNodeId; var leftNode = nodes.GetNode(leftNodeId, NodeAccess.ReadWrite); var rightKey = node.Edges.Keys[rightIndex]; var rightNodeId = node.Edges[rightKey].ToNodeId; var rightNode = nodes.GetNode(rightNodeId, NodeAccess.ReadWrite); if (!leftNode.Data.Equals(rightNode.Data)) { throw new InvalidOperationException("Both nodes must be of same type"); } if (leftNode.Data.Equals(InternalNodeData)) { var maxEdgeKey = leftNode.Edges.Keys[leftNode.Edges.Count - 1]; var maxEdge = leftNode.Edges[maxEdgeKey]; leftNode.Edges.Remove(maxEdgeKey); leftNode.AddEdge(new Edge <Guid, EdgeData>(maxEdge.ToNodeId, LeftEdge(nodes, rightNode).Data)); } if ((leftNode.Edges.Count + rightNode.Edges.Count) <= treeOrder) { // Take all from left node for (int i = 0; i < leftNode.Edges.Count; i++) { EdgeData removalKey = leftNode.Edges.Keys[i]; rightNode.AddEdge(leftNode.Edges[removalKey]); } nodes.SetNode(rightNodeId, rightNode); nodes.Remove(leftNodeId); node.Edges.Remove(leftKey); if (node.Edges.Count == 1) { // When no more nodes remaining copy from child node.SetData(rightNode.Data); node.Edges.Clear(); foreach (var childEdge in rightNode.Edges.Values) { node.AddEdge(childEdge); } nodes.Remove(rightNodeId); } return(new RemovalResult(node.Edges.Count, true, true)); } else { // Decide how many edges to take from right node int numberToTake = (leftNode.Edges.Count + rightNode.Edges.Count) / 2 - leftNode.Edges.Count; // Copy or merge? if (numberToTake > 0) { // Copy edges from right -> left for (int i = 0; i < numberToTake; i++) { EdgeData removalKey = rightNode.Edges.Keys[0]; leftNode.AddEdge(rightNode.Edges[removalKey]); rightNode.Edges.Remove(removalKey); } node.Edges.Remove(leftKey); node.AddEdge(new Edge <Guid, EdgeData>(leftNodeId, LeftEdge(nodes, rightNode).Data)); nodes.SetNode(leftNodeId, leftNode); nodes.SetNode(rightNodeId, rightNode); // No edges were removed return(new RemovalResult(node.Edges.Count, true, true)); } else if (numberToTake < 0) { // Copy edges from left -> right for (int i = 0; i < Math.Abs(numberToTake); i++) { EdgeData removalKey = leftNode.Edges.Keys[leftNode.Edges.Count - 1]; rightNode.AddEdge(leftNode.Edges[removalKey]); leftNode.Edges.Remove(removalKey); } node.Edges.Remove(leftKey); node.AddEdge(new Edge <Guid, EdgeData>(leftNodeId, LeftEdge(nodes, rightNode).Data)); nodes.SetNode(leftNodeId, leftNode); nodes.SetNode(rightNodeId, rightNode); // No edges were removed return(new RemovalResult(node.Edges.Count, true, true)); } throw new ArgumentException("Nothing to copy"); } }
/// <summary> /// Makes room in the tree for new data /// </summary> /// <param name="nodes">Node provider which contains tree nodes</param> /// <param name="nodeId">Current node ID</param> /// <param name="isRoot">Determines if current node is root node</param> /// <param name="data">Data to make room for</param> /// <returns>Result of the operation</returns> private static SplitResult SplitTree(INodeProvider <Guid, object, EdgeData> nodes, Guid nodeId, bool isRoot, EdgeData data, int treeOrder) { var currentNode = nodes.GetNode(nodeId, NodeAccess.ReadWrite); if (isRoot) { if (currentNode.Data.Equals(InternalNodeData)) { #region Is root internal ? var leadingEdge = FindLeadingEdge(data, currentNode); var result = SplitTree(nodes, leadingEdge.ToNodeId, false, data, treeOrder); if (result != null) { currentNode.AddEdge(new Edge <Guid, EdgeData>(result.CreatedNodeId, result.RightKey)); if (currentNode.Edges.Count == (treeOrder + 1)) { // Split root to 2 internal nodes Guid leftNodeId = Guid.NewGuid(); Guid rightNodeId = Guid.NewGuid(); Node <Guid, object, EdgeData> leftNode = new Node <Guid, object, EdgeData>(NodeType.TreeInternal, InternalNodeData); Node <Guid, object, EdgeData> rightNode = new Node <Guid, object, EdgeData>(NodeType.TreeInternal, InternalNodeData); var keys = currentNode.Edges.Keys; for (int i = 0; i < keys.Count; i++) { if (i < keys.Count / 2) { leftNode.AddEdge(currentNode.Edges[keys[i]]); } else { rightNode.AddEdge(currentNode.Edges[keys[i]]); } } //Set last edges SetLastInternalKey(leftNode); SetLastInternalKey(rightNode); currentNode.Edges.Clear(); currentNode.AddEdge(new Edge <Guid, EdgeData>(leftNodeId, LeftEdge(nodes, rightNode).Data)); currentNode.AddEdge(new Edge <Guid, EdgeData>(rightNodeId, EdgeData.MaxValue)); nodes.SetNode(leftNodeId, leftNode); nodes.SetNode(rightNodeId, rightNode); } nodes.SetNode(nodeId, currentNode); } else { return(null); } #endregion } else { #region Root is a leaf if (currentNode.Edges.Count == treeOrder) { // Split root to internal nodes Guid leftNodeId = Guid.NewGuid(); Guid rightNodeId = Guid.NewGuid(); Node <Guid, object, EdgeData> leftNode = new Node <Guid, object, EdgeData>(NodeType.TreeLeaf, LeafNodeData); Node <Guid, object, EdgeData> rightNode = new Node <Guid, object, EdgeData>(NodeType.TreeLeaf, LeafNodeData); var keys = currentNode.Edges.Keys; for (int i = 0; i < keys.Count; i++) { if (i < keys.Count / 2) { leftNode.AddEdge(currentNode.Edges[keys[i]]); } else { rightNode.AddEdge(currentNode.Edges[keys[i]]); } } currentNode.Edges.Clear(); currentNode.AddEdge(new Edge <Guid, EdgeData>(leftNodeId, LeftEdge(nodes, rightNode).Data)); currentNode.AddEdge(new Edge <Guid, EdgeData>(rightNodeId, EdgeData.MaxValue)); currentNode.SetData(InternalNodeData); nodes.SetNode(leftNodeId, leftNode); nodes.SetNode(rightNodeId, rightNode); nodes.SetNode(nodeId, currentNode); } #endregion } return(null); } else { if (currentNode.Data.Equals(InternalNodeData)) { var leadingEdge = FindLeadingEdge(data, currentNode); var result = SplitTree(nodes, leadingEdge.ToNodeId, false, data, treeOrder); if (result != null) { currentNode.AddEdge(new Edge <Guid, EdgeData>(result.CreatedNodeId, result.RightKey)); if (currentNode.Edges.Count == treeOrder) { Guid newInternalId = Guid.NewGuid(); Node <Guid, object, EdgeData> newInternal = new Node <Guid, object, EdgeData>(NodeType.TreeInternal, InternalNodeData); var keys = currentNode.Edges.Keys; for (int i = 0; i < keys.Count / 2; i++) { newInternal.AddEdge(currentNode.Edges[keys[i]]); } int nrToRemove = keys.Count / 2; for (int i = 0; i < nrToRemove; i++) { currentNode.Edges.RemoveAt(0); } // Set last edge of left internal node to MaxInt SetLastInternalKey(newInternal); nodes.SetNode(newInternalId, newInternal); nodes.SetNode(nodeId, currentNode); return(new SplitResult(newInternalId, newInternal, LeftEdge(nodes, currentNode).Data)); } else { nodes.SetNode(nodeId, currentNode); return(null); } } else { return(null); } } else if (currentNode.Data.Equals(LeafNodeData)) { Guid newLeafId = Guid.NewGuid(); Node <Guid, object, EdgeData> newLeaf = new Node <Guid, object, EdgeData>(NodeType.TreeLeaf, LeafNodeData); var keys = currentNode.Edges.Keys; for (int i = 0; i < keys.Count / 2; i++) { newLeaf.AddEdge(currentNode.Edges[keys[i]]); } int nrToRemove = keys.Count / 2; for (int i = 0; i < nrToRemove; i++) { currentNode.Edges.RemoveAt(0); } nodes.SetNode(newLeafId, newLeaf); nodes.SetNode(nodeId, currentNode); return(new SplitResult(newLeafId, newLeaf, LeftEdge(nodes, currentNode).Data)); } else { throw new ArgumentException("Unexpected node data"); } } }
/// <summary> /// Gets next leaf in tree compared to given sample data /// </summary> /// <param name="nodes">Node provider</param> /// <param name="rootNodeId">Root node id</param> /// <param name="sampleData">Sample data in the current leaf</param> /// <returns>Leaf node next in comparison to sample data, or null if no more leaves are found</returns> private static Node <Guid, object, EdgeData> NextLeaf(INodeProvider <Guid, object, EdgeData> nodes, Guid rootNodeId, EdgeData sampleData) { var node = nodes.GetNode(rootNodeId, NodeAccess.Read); Guid id = rootNodeId; Guid nextInternalParentId = Guid.Empty; while (node.Data.Equals(InternalNodeData)) { var leadingEdgeIndex = FindLeadingEdgeIndex(sampleData, node); if (leadingEdgeIndex < node.Edges.Count - 1) { nextInternalParentId = node.Edges[node.Edges.Keys[leadingEdgeIndex + 1]].ToNodeId; } // Advance to next internal node node = nodes.GetNode(node.Edges[node.Edges.Keys[leadingEdgeIndex]].ToNodeId, NodeAccess.Read); } if (nextInternalParentId.Equals(Guid.Empty)) { return(null); } else { return(LeftLeaf(nodes, nodes.GetNode(nextInternalParentId, NodeAccess.Read))); } }
/// <summary> /// Finds an edge which leads to given hash /// </summary> /// <param name="data">Data to find</param> /// <param name="node">Internal node</param> /// <returns>Edge which leads to hash</returns> private static Edge <Guid, EdgeData> FindLeadingEdge(EdgeData data, Node <Guid, object, EdgeData> node) { return(node.Edges[node.Edges.Keys[FindLeadingEdgeIndex(data, node)]]); }
/// <summary> /// Finds an edge index which leads to given hash /// </summary> /// <param name="data">Data to find</param> /// <param name="node">Internal node</param> /// <returns>Edge index which leads to hash</returns> private static int FindLeadingEdgeIndex(EdgeData data, Node <Guid, object, EdgeData> node) { if (node.Data.Equals(InternalNodeData)) { // Find tree edge which leads to the hash var keys = node.Edges.Keys; int i = 0; int step = keys.Count; while (true) { if (step > 1) { step = step / 2; } if (data.CompareTo(keys[i]) < 0) { // Data is smaller, try going left if (i == 0) { // There is no place to go return(i); } if (data.CompareTo(keys[i - 1]) >= 0) { // Going left would lead us into the region of greater return(i); } else { i = i - step; } } else { // Data is greater, try going right if (i == keys.Count - 1) { // There is no place to go return(i); } else { i = i + step; } } //while (i < keys.Count && data.CompareTo(keys[i]) >= 0) //{ // i++; //} } //return i; } else { throw new ArgumentException("Internal node expected"); } }
/// <summary> /// Looks for leaf tree node which is supposed to have an edge with given hash /// </summary> /// <param name="nodes">Node provider</param> /// <param name="rootNodeId">Root node ID to start the search from</param> /// <param name="edgeHash">Hash to find</param> /// <returns>Leaf node which is supposed to contain the edge</returns> private static FindResult FindLeafNode(INodeProvider <Guid, object, EdgeData> nodes, Guid rootNodeId, EdgeData data) { var node = nodes.GetNode(rootNodeId, NodeAccess.Read); Guid id = rootNodeId; while (node.Data.Equals(InternalNodeData)) { var leadingEdge = FindLeadingEdge(data, node); // Advance to next internal node node = nodes.GetNode(leadingEdge.ToNodeId, NodeAccess.Read); id = leadingEdge.ToNodeId; } return(new FindResult(id, node)); }
/// <summary> /// Removes the edge from the tree /// </summary> /// <param name="nodes">Node provider which hosts the nodes</param> /// <param name="rootNodeId">Tree root node ID</param> /// <param name="data">Edge to find</param> /// <returns>True if edge was removed</returns> public static bool RemoveEdge(INodeProvider <Guid, object, EdgeData> nodes, Guid rootNodeId, EdgeData data, int treeOrder) { var result = RemoveEdgeRecursive(nodes, rootNodeId, data, treeOrder); return(result.WasRemoved); }
/// <summary> /// Finds the edge in tree /// </summary> /// <param name="nodes">Node provider which hosts the nodes</param> /// <param name="rootNodeId">Tree root node ID</param> /// <param name="data">Edge to find</param> /// <param name="toNodeId">New to node ID</param> /// <returns>True if edge was found</returns> public static bool TrySetEdgeToNode(INodeProvider <Guid, object, EdgeData> nodes, Guid rootNodeId, EdgeData data, Guid toNodeId) { var result = FindLeafNode(nodes, rootNodeId, data); if (result == null) { throw new KeyNotFoundException(); } else { if (!result.Node.Data.Equals(LeafNodeData)) { throw new InvalidOperationException("Leaf node expected"); } result.Node.SetEdgeToNode(data, toNodeId); nodes.SetNode(result.NodeId, result.Node); return(true); } }
/// <summary> /// Finds the edge in tree /// </summary> /// <param name="nodes">Node provider which hosts the nodes</param> /// <param name="rootNodeId">Tree root node ID</param> /// <param name="data">Edge to find</param> /// <param name="value">Edge result</param> /// <returns>True if edge was found</returns> public static bool TryFindEdge(INodeProvider <Guid, object, EdgeData> nodes, Guid rootNodeId, EdgeData data, out Edge <Guid, EdgeData> value) { var result = FindLeafNode(nodes, rootNodeId, data); if (result == null) { throw new KeyNotFoundException(); } else { if (!result.Node.Data.Equals(LeafNodeData)) { throw new InvalidOperationException("Leaf node expected"); } return(result.Node.Edges.TryGetValue(data, out value)); } }
/// <summary> /// Creates new instance of SplitResult type /// </summary> /// <param name="createdNodeId">New node ID which was created</param> /// <param name="createdNode">Node which was created</param> /// <param name="rightKey">Minimum key value of the right sibling node of the new node</param> public SplitResult(Guid createdNodeId, Node <Guid, object, EdgeData> createdNode, EdgeData rightKey) { this.CreatedNodeId = createdNodeId; this.CreatedNode = createdNode; this.RightKey = rightKey; }