/// <summary> /// Count all the items in a custom range, under and including node. /// </summary> /// <param name="rangeTester">The delegate that defines the range.</param> /// <param name="node">Node to begin enumeration. May be null.</param> /// <param name="belowRangeTop">This node and all under it are either in the range or below it.</param> /// <param name="aboveRangeBottom">This node and all under it are either in the range or above it.</param> /// <returns>The number of items in the range, under and include node.</returns> private int CountRangeUnderNode(RangeTester rangeTester, Node node, bool belowRangeTop, bool aboveRangeBottom) { if (node != null) { if (belowRangeTop && aboveRangeBottom) { // This node and all below it must be in the range. Use the predefined count. return node.Count; } int compare = rangeTester(node.item); int count; if (compare == 0) { count = 1; // the node itself count += CountRangeUnderNode(rangeTester, node.left, true, aboveRangeBottom); count += CountRangeUnderNode(rangeTester, node.right, belowRangeTop, true); } else if (compare < 0) { count = CountRangeUnderNode(rangeTester, node.right, belowRangeTop, aboveRangeBottom); } else { // compare > 0 count = CountRangeUnderNode(rangeTester, node.left, belowRangeTop, aboveRangeBottom); } return count; } else { return 0; } }
/// <summary> /// Enumerate all the items in a custom range, under and including node, in reversed order. /// </summary> /// <param name="rangeTester">Tests an item against the custom range.</param> /// <param name="node">Node to begin enumeration. May be null.</param> /// <returns>An enumerable of the items, in reversed oreder.</returns> /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception> private IEnumerable<T> EnumerateRangeInReversedOrder(RangeTester rangeTester, Node node) { int startStamp = changeStamp; if (node != null) { int compare = rangeTester(node.item); if (compare <= 0) { // At least part of the range lies to the right. foreach (T item in EnumerateRangeInReversedOrder(rangeTester, node.right)) { yield return item; CheckEnumerationStamp(startStamp); } } if (compare == 0) { // The item is within the range. yield return node.item; CheckEnumerationStamp(startStamp); } if (compare >= 0) { // At least part of the range may lie to the left. foreach (T item in EnumerateRangeInReversedOrder(rangeTester, node.left)) { yield return item; CheckEnumerationStamp(startStamp); } } } }
public void Should_create_empty_range() { var target = new RangeTester<int>(); Assert.That(target.Lower, Is.EqualTo(default(int))); Assert.That(target.Upper, Is.EqualTo(default(int))); }
public void Should_be_able_to_set_upper_case_to_inclusive() { var target = new RangeTester<int>(0, 1); target.IsUpperInclusive = true; Assert.That(target.IsUpperInclusive, Is.True); }
public void Should_create_range_of_specified_type() { var expectedLowerBound = 0; var expectedUpperBound = 1; var target = new RangeTester<int>(expectedLowerBound, expectedUpperBound); Assert.That(target, Is.Not.Null); Assert.That(target.Lower, Is.EqualTo(expectedLowerBound)); Assert.That(target.Upper, Is.EqualTo(expectedUpperBound)); Assert.That(target.IsLowerInclusive, Is.True); Assert.That(target.IsUpperInclusive, Is.True); }
/// <summary> /// Find the last item in a custom range in the tree, and it's index. The range is determined /// by a RangeTester delegate. /// </summary> /// <param name="rangeTester">The delegate that defines the range.</param> /// <param name="item">Returns the item found, if true was returned.</param> /// <returns>Index of the item if range is non-empty, -1 otherwise.</returns> public int LastItemInRange(RangeTester rangeTester, out T item) { Node node = root, found = null; int curCount = 0, foundIndex = -1; while (node != null) { int compare = rangeTester(node.item); if (compare == 0) { found = node; if (node.left != null) { foundIndex = curCount + node.left.Count; } else { foundIndex = curCount; } } if (compare <= 0) { if (node.left != null) { curCount += node.left.Count + 1; } else { curCount += 1; } node = node.right; } else { node = node.left; } } if (found != null) { item = found.item; return(foundIndex); } else { item = default(T); return(foundIndex); } }
/// <summary> /// Delete all the items in a range, identified by a RangeTester delegate. /// </summary> /// <param name="rangeTester">The delegate that defines the range to delete.</param> /// <returns>The number of items deleted.</returns> public int DeleteRange(RangeTester rangeTester) { bool deleted; int count = 0; T dummy; do { deleted = DeleteItemFromRange(rangeTester, true, out dummy); if (deleted) ++count; } while (deleted); return count; }
public IEnumerable<S> EnumerateRange (RangeTester rangeTester) { yield break; }
/// <summary> /// Deletes either the first or last item from a range, as identified by a RangeTester /// delegate. If the range is empty, returns false. /// </summary> /// <remarks>Top-down algorithm from Weiss. Basic plan is to move down in the tree, /// rotating and recoloring along the way to always keep the current node red, which /// ensures that the node we delete is red. The details are quite complex, however! </remarks> /// <param name="rangeTester">Range to delete from.</param> /// <param name="deleteFirst">If true, delete the first item from the range, else the last.</param> /// <param name="item">Returns the item that was deleted, if true returned.</param> /// <returns>True if an element was deleted, false if the range is empty.</returns> public bool DeleteItemFromRange(RangeTester rangeTester, bool deleteFirst, out T item) { Node node; // The current node. Node parent; // Parent of the current node. Node gparent; // Grandparent of the current node. Node sib; // Sibling of the current node. Node keyNode; // Node with the key that is being removed. // The tree may be changed. StopEnumerations(); if (root == null) { // Nothing in the tree. Go home now. item = default(T); return false; } // We decrement counts on the way down the tree. If we end up not finding an item to delete // we need a stack to adjust the counts back. Node[] nodeStack = GetNodeStack(); int nodeStackPtr = 0; // first free item on the stack. // Start at the root. node = root; sib = parent = gparent = null; keyNode = null; // Proceed down the tree, making the current node red so it can be removed. for (; ; ) { Debug.Assert(parent == null || parent.IsRed); Debug.Assert(sib == null || !sib.IsRed); Debug.Assert(!node.IsRed); if ((node.left == null || !node.left.IsRed) && (node.right == null || !node.right.IsRed)) { // node has two black children (null children are considered black). if (parent == null) { // Special case for the root. Debug.Assert(node == root); node.IsRed = true; } else if ((sib.left == null || !sib.left.IsRed) && (sib.right == null || !sib.right.IsRed)) { // sib has two black children. node.IsRed = true; sib.IsRed = true; parent.IsRed = false; } else { if (parent.left == node && (sib.right == null || !sib.right.IsRed)) { // sib has a black child on the opposite side as node. Node tleft = sib.left; Rotate(parent, sib, tleft); sib = tleft; } else if (parent.right == node && (sib.left == null || !sib.left.IsRed)) { // sib has a black child on the opposite side as node. Node tright = sib.right; Rotate(parent, sib, tright); sib = tright; } // sib has a red child. Rotate(gparent, parent, sib); node.IsRed = true; sib.IsRed = true; sib.left.IsRed = false; sib.right.IsRed = false; sib.DecrementCount(); nodeStack[nodeStackPtr - 1] = sib; parent.DecrementCount(); nodeStack[nodeStackPtr++] = parent; } } // Compare the key and move down the tree to the correct child. do { Node nextNode, nextSib; // Node we've moving to, and it's sibling. node.DecrementCount(); nodeStack[nodeStackPtr++] = node; // Determine which way to move in the tree by comparing the // current item to what we're looking for. int compare = rangeTester(node.item); if (compare == 0) { // We've found the node to remove. Remember it, then keep traversing the // tree to either find the first/last of equal keys, and if needed, the predecessor // or successor (the actual node to be removed). keyNode = node; if (deleteFirst) { nextNode = node.left; nextSib = node.right; } else { nextNode = node.right; nextSib = node.left; } } else if (compare > 0) { nextNode = node.left; nextSib = node.right; } else { nextNode = node.right; nextSib = node.left; } // Have we reached the end of our tree walk? if (nextNode == null) goto FINISHED; // Move down the tree. gparent = parent; parent = node; node = nextNode; sib = nextSib; } while (!parent.IsRed && node.IsRed); if (!parent.IsRed) { Debug.Assert(!node.IsRed); // moved to a black child. Rotate(gparent, parent, sib); sib.DecrementCount(); nodeStack[nodeStackPtr - 1] = sib; parent.DecrementCount(); nodeStack[nodeStackPtr++] = parent; sib.IsRed = false; parent.IsRed = true; gparent = sib; sib = (parent.left == node) ? parent.right : parent.left; } } FINISHED: if (keyNode == null) { // We never found a node to delete. // Return counts back to their previous value. for (int i = 0; i < nodeStackPtr; ++i) nodeStack[i].IncrementCount(); // Color the root black, in case it was colored red above. if (root != null) root.IsRed = false; item = default(T); return false; } // Return the item from the node we're deleting. item = keyNode.item; // At a leaf or a node with one child which is a leaf. Remove the node. if (keyNode != node) { // The node we want to delete is interior. Move the item from the // node we're actually deleting to the key node. keyNode.item = node.item; } // If we have one child, replace the current with the child, otherwise, // replace the current node with null. Node replacement; if (node.left != null) { replacement = node.left; Debug.Assert(!node.IsRed && replacement.IsRed); replacement.IsRed = false; } else if (node.right != null) { replacement = node.right; Debug.Assert(!node.IsRed && replacement.IsRed); replacement.IsRed = false; } else replacement = null; if (parent == null) { Debug.Assert(root == node); root = replacement; } else if (parent.left == node) parent.left = replacement; else { Debug.Assert(parent.right == node); parent.right = replacement; } // Color the root black, in case it was colored red above. if (root != null) root.IsRed = false; // Update item count. count -= 1; // And we're done. return true; }
/// <summary> /// Enumerate the items in a custom range in the tree, in reversed order. The range is determined by /// a RangeTest delegate. /// </summary> /// <param name="rangeTester">Tests an item against the custom range.</param> /// <returns>An IEnumerable<T> that enumerates the custom range in reversed order.</returns> /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception> public IEnumerable<T> EnumerateRangeReversed(RangeTester rangeTester) { return EnumerateRangeInReversedOrder(rangeTester, root); }
/// <summary> /// Enumerate the items in a custom range in the tree. The range is determined by /// a RangeTest delegate. /// </summary> /// <param name="rangeTester">Tests an item against the custom range.</param> /// <returns>An IEnumerable<T> that enumerates the custom range in order.</returns> /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception> public IEnumerable<T> EnumerateRange(RangeTester rangeTester) { return EnumerateRangeInOrder(rangeTester, root); }
/// <summary> /// Count the items in a custom range in the tree. The range is determined by /// a RangeTester delegate. /// </summary> /// <param name="rangeTester">The delegate that defines the range.</param> /// <returns>The number of items in the range.</returns> public int CountRange(RangeTester rangeTester) { return CountRangeUnderNode(rangeTester, root, false, false); }
public void EnumerateRange(RangeTester rangeTester) { }