1 //------------------------------------------------------------------------------
2 // <copyright file="Selection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
18 using System.Collections;
19 using System.Data.Common;
20 using System.Diagnostics;
22 internal enum RBTreeError {
24 // InvalidCompareDelegate = 2,
25 PagePositionInSlotInUse = 3,
27 InvalidStateinInsert = 5,
28 // InvalidStateinEndInsert = 6,
29 InvalidNextSizeInDelete = 7,
30 InvalidStateinDelete = 8,
31 InvalidNodeSizeinDelete = 9,
32 InvalidStateinEndDelete = 10,
33 CannotRotateInvalidsuccessorNodeinDelete = 11,
34 // IndexOutOfRange = 12,
35 IndexOutOFRangeinGetNodeByIndex = 13,
37 UnsupportedAccessMethod1 = 15,
38 UnsupportedAccessMethod2 = 16,
39 UnsupportedAccessMethodInNonNillRootSubtree = 17,
40 AttachedNodeWithZerorbTreeNodeId = 18, // DataRowCollection
41 CompareNodeInDataRowTree = 19, // DataRowCollection
42 CompareSateliteTreeNodeInDataRowTree = 20, // DataRowCollection
43 NestedSatelliteTreeEnumerator = 21,
46 internal enum TreeAccessMethod{
47 KEY_SEARCH_AND_INDEX = 1,
51 // an index represents location the tree
52 // a tree has an array of pages (max 2^16) (top 16 bits)
53 // a page has an array of nodes (max 2^16) (bottom 16 bits)
54 // nodes are indexed by RBTree.PageTable[index>>16].Slots[index&0xFFFF]
56 // a tree has an PageTableBitmap to indicate which allocated pages have free nodes
57 // a page has a SlotBitmap to indicate which slots are free
59 // intial page allocation (assuming no deletes)
60 // #page * #slot = #total, #cumulative
61 // ( 4) * 32 = 128, 127 (subtract 1 for NIL node)
62 // ( 32 - 4) * 256 = 7168, 7,295
63 // ( 128 - 32) * 1024 = 98304, 105,599
64 // ( 4096 - 128) * 4096 = 16252928, 16,358,527
65 // (32768 - 4096) * 8192 = 234881024, 251,239,551
66 // (65535 - 32768) * 65536 = 2147418112, 2,398,657,663 (excess nodes 251,174,016 > Int32.MaxValue)
68 // tree page size is GetIntValueFromBitMap(inUsePageCount) // return highest bit in array
69 //private static readonly int[] PageSize = new int[17] { // nobit + 16 bits == 17 position
70 // 32, 32, 32, // inUsePageCount < 4 0, 1, 2,
71 // 256, 256, 256, // inUsePageCount < 32 4, 8, 16,
72 // 1024, 1024, // inUsePageCount < 128 32, 64,
73 // 4096, 4096, 4096, 4096, 4096, // inUsePageCount < 4096 128, 256, 512, 1024, 2048,
74 // 8192, 8192, 8192, // inUsePageCount < 32768 4096, 8192, 16384
75 // 65535 // inUsePageCount <= 65535
78 // the in-ordering of nodes in the tree (the second graph has duplicate nodes)
79 // for the satellite tree, the main tree node is the clone, GetNodeByIndex always returns the satelliteRootid
83 // / \ / \ | / \ / \ / \
84 // 1 3 5 7 | 1 5 2 4 8 9
86 // PageTable (starting at 32) doubles in size on demand (^16 - ^5 = 11 grows to reach max PageTable size)
88 // if a page has no allocated slots, it will be dropped
89 // worst case scenario is to repeatedly add/remove on a boundary condition
91 // the primary change to support Index using Predicate<DataRow> or Comparison<DataRow> was to eliminate all
92 // unnecessary searching for the node in the main tree when operating on a node in the satellite branch
93 // in all cases except GetNodeByKey(K)& GetIndexByNode(int), we know what that mainTreeNodeID is and can avoid searching
95 internal abstract class RBTree<K> : System.Collections.IEnumerable {
96 // 2^16 #pages * 2^n == total number of nodes. 512 = 32 million, 1024 = 64 million, 2048 = 128m, 4096=256m, 8192=512m, 16284=1 billion
98 internal const int DefaultPageSize = 32; /* 512 = 2^9 32 million nodes*/
99 internal const int NIL = 0; // 0th page, 0th slot for each tree till CLR static & generics issue is fixed
101 private TreePage[] _pageTable; // initial size 4, then doubles (grows) - it never shrinks
102 private Int32[] _pageTableMap;
103 private int _inUsePageCount = 0; // contains count of allocated pages per tree, its <= the capacity of pageTable
104 private int nextFreePageLine; // used for keeping track of position of last used free page in pageTable
106 private int _version;
108 private int _inUseNodeCount = 0; // total number of nodes currently in use by this tree.
109 private int _inUseSatelliteTreeCount = 0; // total number of satellite associated with this tree.
110 private readonly TreeAccessMethod _accessMethod;
112 protected abstract int CompareNode (K record1, K record2);
113 protected abstract int CompareSateliteTreeNode (K record1, K record2);
115 protected RBTree (TreeAccessMethod accessMethod) {
116 _accessMethod = accessMethod;
120 private void InitTree() {
122 _pageTable = new TreePage[1 * TreePage.slotLineSize];
123 _pageTableMap = new Int32[(_pageTable.Length + TreePage.slotLineSize - 1) / TreePage.slotLineSize]; // Ceiling(size)
125 nextFreePageLine = 0;
126 AllocPage (DefaultPageSize);
128 // alloc storage for reserved NIL node. segment 0, slot 0; Initialize NIL
129 _pageTable[0].Slots[0].nodeColor = NodeColor.black;
130 _pageTable[0].SlotMap[0] = 0x1;
131 _pageTable[0].InUseCount = 1;
134 _inUseSatelliteTreeCount = 0; // total number of satellite associated with this tree.
137 private void FreePage (TreePage page)
140 _pageTable[page.PageId] = null;
145 * size : Allocates a page of the specified size.
147 * Look for an unallocated page entry.
148 * (1) If entry for an unallocated page exists in current pageTable - use it
149 * (2) else extend pageTable
151 private TreePage AllocPage (int size)
153 int freePageIndex = GetIndexOfPageWithFreeSlot (false);
155 if (freePageIndex != -1)
157 _pageTable[freePageIndex] = new TreePage (size);
158 nextFreePageLine = freePageIndex / TreePage.slotLineSize;
162 // no free position found, increase pageTable size
163 TreePage[] newPageTable = new TreePage[_pageTable.Length * 2];
164 System.Array.Copy (_pageTable, 0, newPageTable, 0, _pageTable.Length);
165 Int32[] newPageTableMap = new Int32[(newPageTable.Length + TreePage.slotLineSize - 1) / TreePage.slotLineSize];
166 System.Array.Copy (_pageTableMap, 0, newPageTableMap, 0, _pageTableMap.Length);
168 nextFreePageLine = _pageTableMap.Length;
169 freePageIndex = _pageTable.Length;
170 _pageTable = newPageTable;
171 _pageTableMap = newPageTableMap;
172 _pageTable[freePageIndex] = new TreePage (size);
174 _pageTable[freePageIndex].PageId = freePageIndex;
176 return _pageTable[freePageIndex];
180 * Mark the specified page "Full" as all its slots aer in use
182 private void MarkPageFull (TreePage page)
184 // set bit associated with page to mark it as full
186 int pageTableMapIndex = (page.PageId / TreePage.slotLineSize);
187 int pageTableMapOffset = (page.PageId % TreePage.slotLineSize);
188 Int32 pageBitMask = ((Int32)1) << pageTableMapOffset;
189 _pageTableMap[pageTableMapIndex] |= (pageBitMask);
191 _pageTableMap[page.PageId / TreePage.slotLineSize] |= (1 << (page.PageId % TreePage.slotLineSize));
195 * Mark the specified page as "Free". It has atleast 1 available slot.
197 private void MarkPageFree (TreePage page)
199 // set bit associated with page to mark it as free
201 int pageTableMapIndex = (page.PageId / TreePage.slotLineSize);
202 int pageTableMapOffset = (page.PageId % TreePage.slotLineSize);
203 Int32 pageBitMask = ((Int32)1) << pageTableMapOffset;
204 _pageTableMap[pageTableMapIndex] &= ~(pageBitMask);
206 _pageTableMap[page.PageId / TreePage.slotLineSize] &= ~(1 << (page.PageId % TreePage.slotLineSize));
209 private static int GetIntValueFromBitMap (UInt32 bitMap)
211 Int32 value = 0; // 0 based slot position
214 * Assumption: bitMap can have max, exactly 1 bit set.
215 * convert bitMap to int value giving number of 0's to its right
216 * return value between 0 and 31
218 if ((bitMap & 0xFFFF0000) != 0)
223 if ((bitMap & 0x0000FF00) != 0)
228 if ((bitMap & 0x000000F0) != 0)
233 if ((bitMap & 0x0000000C) != 0)
238 if ((bitMap & 0x00000002) != 0)
245 * nodeId: The nodeId of the node to be freed
247 private void FreeNode (int nodeId)
249 TreePage page = _pageTable[nodeId >> 16];
250 int slotIndex = nodeId & 0xFFFF;
252 page.Slots[slotIndex] = default(Node);
254 // clear slotMap entry associated with nodeId
255 page.SlotMap[slotIndex / TreePage.slotLineSize] &= ~( ((Int32)1) << (int)(slotIndex % TreePage.slotLineSize));
258 if (page.InUseCount == 0)
260 else if (page.InUseCount == page.Slots.Length - 1)
261 MarkPageFree (page); // With freeing of a node, a previous full page has a free slot.
265 * GetIndexOfPageWithFreeSlot()
266 * allocatedPage: If true, look for an allocatedPage with free slot else look for an unallocated page entry in pageTable
267 * return: if allocatedPage is true, return index of a page with at least 1 free slot
268 * else return index of an unallocated page, pageTable[index] is empty.
270 private int GetIndexOfPageWithFreeSlot (bool allocatedPage)
272 int pageTableMapPos = nextFreePageLine;
275 while (pageTableMapPos < _pageTableMap.Length)
277 if (((UInt32)_pageTableMap[pageTableMapPos]) < 0xFFFFFFFF)
279 UInt32 pageSegmentMap = (UInt32)_pageTableMap[pageTableMapPos];
280 while ((pageSegmentMap ^ (0xFFFFFFFF)) != 0) //atleast one "0" is there (same as <0xFFFFFFFF)
282 UInt32 pageWithFreeSlot = (UInt32)((~(pageSegmentMap)) & (pageSegmentMap + 1));
285 if ((_pageTableMap[pageTableMapPos] & pageWithFreeSlot) != 0) //paranoia check
286 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.PagePositionInSlotInUse);
288 pageIndex = (pageTableMapPos * TreePage.slotLineSize) + GetIntValueFromBitMap (pageWithFreeSlot); // segment + offset
291 if (_pageTable[pageIndex] != null)
296 if (_pageTable[pageIndex] == null)
297 return pageIndex; // pageIndex points to an unallocated Page
300 pageSegmentMap |= pageWithFreeSlot; // found "reset bit", but unallocated page, mark it as unavaiable and continue search
307 if (nextFreePageLine != 0)
309 //Try one more time, starting from 0th page segment position to locate a page with free slots
310 nextFreePageLine = 0;
311 pageIndex = GetIndexOfPageWithFreeSlot (allocatedPage);
318 Debug.Assert(_inUseNodeCount-1 == SubTreeSize(root), "count mismatch");
319 return (_inUseNodeCount-1);
323 public bool HasDuplicates {
325 return (0 != _inUseSatelliteTreeCount);
331 * Allocate storage for a new node and assign in the specified key.
333 * Find a page with free slots or allocate a new page.
334 * Use bitmap associated with page to allocate a slot.
335 * mark the slot as used and return its index.
337 private int GetNewNode (K key)
339 // find page with free slots, if none, allocate a new page
340 TreePage page = null;
342 int freePageIndex = GetIndexOfPageWithFreeSlot (true);
343 if (freePageIndex != -1)
344 page = _pageTable[freePageIndex];
345 else if (_inUsePageCount < (4))
346 page = AllocPage (DefaultPageSize); // First 128 slots
347 else if (_inUsePageCount < (32))
348 page = AllocPage (256);
349 else if (_inUsePageCount < (128))
350 page = AllocPage (1024);
351 else if (_inUsePageCount < (4096))
352 page = AllocPage (4096);
353 else if (_inUsePageCount < (32*1024))
354 page = AllocPage (8192); // approximately First 16 million slots (2^24)
356 page = AllocPage (64*1024); // Page size to accomodate more than 16 million slots (Max 2 Billion and 16 million slots)
358 // page contains atleast 1 free slot.
359 int slotId = page.AllocSlot (this);
363 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.NoFreeSlots);
365 // NodeId: Upper 16 bits pageId, lower bits slotId
366 page.Slots[slotId].selfId = (int)(((UInt32)page.PageId) << 16) | slotId;
367 Debug.Assert(page.Slots[slotId].leftId == NIL, "node not cleared");
368 Debug.Assert(page.Slots[slotId].rightId == NIL, "node not cleared");
369 Debug.Assert(page.Slots[slotId].parentId == NIL, "node not cleared");
370 Debug.Assert(page.Slots[slotId].nextId == NIL, "node not cleared");
371 page.Slots[slotId].subTreeSize = 1; // new Nodes have size 1.
372 page.Slots[slotId].keyOfNode = key;
373 Debug.Assert(page.Slots[slotId].nodeColor == NodeColor.red, "node not cleared");
374 return page.Slots[slotId].selfId;
377 private int Successor (int x_id)
379 if (Right (x_id) != NIL)
380 return Minimum (Right (x_id)); //return left most node in right sub-tree.
381 int y_id = Parent (x_id);
383 while (y_id != NIL && x_id == Right (y_id))
386 y_id = Parent (y_id);
391 private bool Successor(ref int nodeId, ref int mainTreeNodeId)
394 { // find first node, using branchNodeId as the root
395 nodeId = Minimum(mainTreeNodeId);
396 mainTreeNodeId = NIL;
400 nodeId = Successor(nodeId);
402 if ((NIL == nodeId) && (NIL != mainTreeNodeId))
403 { // done with satellite branch, move back to main tree
404 nodeId = Successor(mainTreeNodeId);
405 mainTreeNodeId = NIL;
409 { // test for satellite branch
410 if (NIL != Next(nodeId))
411 { // find first node of satellite branch
412 if (NIL != mainTreeNodeId)
413 { // satellite branch has satellite branch - very bad
414 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.NestedSatelliteTreeEnumerator);
416 mainTreeNodeId = nodeId;
417 nodeId = Minimum(Next(nodeId));
422 // else no value, done with main tree
426 private int Minimum (int x_id)
428 while (Left (x_id) != NIL) {
437 * It returns the node id for the root of the rotated tree
439 private int LeftRotate (int root_id, int x_id, int mainTreeNode)
441 int y_id = Right (x_id);
443 // Turn y's left subtree into x's right subtree
444 SetRight (x_id, Left (y_id));
445 if (Left (y_id) != NIL) {
446 SetParent (Left (y_id), x_id);
449 SetParent (y_id, Parent (x_id));
450 if (Parent (x_id) == NIL) {
451 if (root_id == NIL) {
455 SetNext (mainTreeNode, y_id);
456 SetKey (mainTreeNode, Key (y_id));
460 else if (x_id == Left (Parent (x_id))) { // x is left child of its parent
461 SetLeft (Parent (x_id), y_id);
464 SetRight (Parent (x_id), y_id);
467 SetLeft (y_id, x_id);
468 SetParent (x_id, y_id);
470 //maintain size: y_id = parent & x_id == child
472 SetSubTreeSize(x_id, (SubTreeSize(Left(x_id)) + SubTreeSize(Right(x_id)) + (Next(x_id) == NIL ? 1 : SubTreeSize(Next(x_id)))));
476 SetSubTreeSize(y_id, (SubTreeSize(Left(y_id)) + SubTreeSize(Right(y_id)) + (Next(y_id) == NIL ? 1 : SubTreeSize(Next(y_id)))));
485 * It returns the node id for the root of the rotated tree
487 private int RightRotate (int root_id, int x_id, int mainTreeNode)
489 int y_id = Left (x_id);
491 SetLeft (x_id, Right (y_id)); // Turn y's right subtree into x's left subtree
492 if (Right (y_id) != NIL) {
493 SetParent (Right (y_id), x_id);
496 SetParent (y_id, Parent (x_id));
497 if (Parent (x_id) == NIL) {
498 if (root_id == NIL) {
502 SetNext (mainTreeNode, y_id);
503 SetKey (mainTreeNode, Key (y_id));
507 else if (x_id == Left (Parent (x_id))) // x is left child of its parent
508 SetLeft (Parent (x_id), y_id);
510 SetRight (Parent (x_id), y_id);
512 SetRight (y_id, x_id);
513 SetParent (x_id, y_id);
515 //maintain size: y_id == parent && x_id == child.
517 SetSubTreeSize(x_id, (SubTreeSize(Left(x_id)) + SubTreeSize(Right(x_id)) + (Next(x_id) == NIL ? 1 : SubTreeSize(Next(x_id)))));
521 SetSubTreeSize(y_id, (SubTreeSize(Left(y_id)) + SubTreeSize(Right(y_id)) + (Next(y_id) == NIL ? 1 : SubTreeSize(Next(y_id)))));
527 // This helps validate the sorting of the tree to help catch instances of corruption much sooner.
528 // corruption happens when the data changes without telling the tree or when multi-threads do simultanous write operations
529 private int Compare(int root_id, int x_id, int z_id) {
530 Debug.Assert(NIL != x_id, "nil left");
531 Debug.Assert(NIL != z_id, "nil right");
532 return (root_id == NIL) ? CompareNode (Key (x_id), Key (z_id)) : CompareSateliteTreeNode (Key (x_id), Key (z_id));
538 * root_id: root_id of the tree to which a node has to be inserted. it is NIL for inserting to Main tree.
539 * x_id : node_id of node to be inserted
541 * returns: The root of the tree to which the specified node was added. its NIL if the node was added to Main RBTree.
543 * if root_id is NIL -> use CompareNode else use CompareSateliteTreeNode
545 * Satelite tree creation:
546 * First Duplicate value encountered. Create a *new* tree whose root will have the same key value as the current node.
547 * The Duplicate tree nodes have same key when used with CompareRecords but distinct record ids.
548 * The current record at all times will have the same *key* as the duplicate tree root.
550 private int RBInsert (int root_id, int x_id, int mainTreeNodeID, int position, bool append)
552 unchecked{_version++;}
554 // Insert Node x at the appropriate position
556 int z_id = (root_id == NIL) ? root : root_id; //if non NIL, then use the specifid root_id as tree's root.
558 if (_accessMethod == TreeAccessMethod.KEY_SEARCH_AND_INDEX && !append)
560 Debug.Assert(-1 == position, "KEY_SEARCH_AND_INDEX with bad position");
561 while (z_id != NIL) // in-order traverse and find node with a NILL left or right child
564 y_id = z_id; // y_id set to the proposed parent of x_id
566 int c = (root_id == NIL) ? CompareNode (Key (x_id), Key (z_id)) : CompareSateliteTreeNode (Key (x_id), Key (z_id));
570 Debug.Assert((NIL == Left(z_id)) || (0 > Compare(root_id, Left(z_id), z_id)), "Left is not left");
576 Debug.Assert((NIL == Right(z_id)) || (0 < Compare(root_id, Right(z_id), z_id)), "Right is not right");
581 // Multiple records with same key - insert it to the duplicate record tree associated with current node
582 if (root_id != NIL) {
583 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.InvalidStateinInsert);
585 if (Next(z_id) != NIL) {
586 root_id = RBInsert (Next (z_id), x_id, z_id, -1, false); // z_id is existing mainTreeNodeID
587 SetKey (z_id, Key (Next (z_id)));
589 (new NodePath(x_id, z_id)).VerifyPath(this); // verify x_id after its been added
593 int newMainTreeNodeId = NIL;
594 // The existing node is pushed into the Satellite Tree and a new Node
595 // is created in the main tree, whose's next points to satellite root.
596 newMainTreeNodeId = GetNewNode (Key (z_id));
597 _inUseSatelliteTreeCount++;
599 // copy contents of z_id to dupRootId (main tree node).
600 SetNext(newMainTreeNodeId, z_id);
601 SetColor(newMainTreeNodeId, color(z_id));
602 SetParent(newMainTreeNodeId, Parent(z_id));
603 SetLeft(newMainTreeNodeId, Left(z_id));
604 SetRight(newMainTreeNodeId, Right(z_id));
606 // Update z_id's non-nil parent
607 if( Left(Parent(z_id))==z_id)
608 SetLeft(Parent(z_id), newMainTreeNodeId);
609 else if (Right(Parent(z_id))==z_id)
610 SetRight(Parent(z_id), newMainTreeNodeId);
613 if (Left(z_id) != NIL)
614 SetParent(Left(z_id), newMainTreeNodeId);
615 if (Right(z_id) != NIL)
616 SetParent(Right(z_id), newMainTreeNodeId);
619 root = newMainTreeNodeId;
621 // Reset z_id's pointers to NIL. It will start as the satellite tree's root.
622 SetColor(z_id, NodeColor.black);
623 SetParent(z_id, NIL);
627 int savedSize = SubTreeSize(z_id);
628 SetSubTreeSize(z_id, 1);
629 // With z_id as satellite root, insert x_id
630 root_id = RBInsert (z_id, x_id, newMainTreeNodeId, -1, false);
632 SetSubTreeSize(newMainTreeNodeId, savedSize);
634 (new NodePath(x_id, newMainTreeNodeId)).VerifyPath(this); // verify x_id after its been added
641 else if (_accessMethod == TreeAccessMethod.INDEX_ONLY || append)
643 if (position == -1) {
644 position = SubTreeSize(root); // append
647 while (z_id != NIL) // in-order traverse and find node with a NILL left or right child
650 y_id = z_id; // y_id set to the proposed parent of x_id
652 //int c = (SubTreeSize(y_id)-(position)); // Actually it should be: SubTreeSize(y_id)+1 - (position + 1)
653 int c = (position) - (SubTreeSize(Left(y_id)));
659 //position = position - SubTreeSize(z_id);
662 position = c-1; //skip computation of position for leaf node
668 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.UnsupportedAccessMethod1);
671 SetParent (x_id, y_id);
674 if (root_id == NIL) {
679 // technically we should never come here. Satellite tree always has a root and atleast 1 child.
680 // if it has only root as it's node, then the satellite tree gets collapsed into the main tree.
682 (new NodePath(x_id, mainTreeNodeID)).VerifyPath(this); // verify x_id after its been added
684 SetNext(mainTreeNodeID, x_id);
685 SetKey(mainTreeNodeID, Key(x_id));
692 if (_accessMethod == TreeAccessMethod.KEY_SEARCH_AND_INDEX)
693 c = (root_id == NIL) ? CompareNode (Key(x_id), Key(y_id)) : CompareSateliteTreeNode (Key (x_id), Key (y_id));
694 else if (_accessMethod == TreeAccessMethod.INDEX_ONLY)
695 c = (position <= 0) ? -1 : 1;
697 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.UnsupportedAccessMethod2);
701 SetLeft (y_id, x_id);
703 SetRight (y_id, x_id);
707 SetRight (x_id, NIL);
708 SetColor (x_id, NodeColor.red);
709 z_id = x_id; // for verification later
712 while (color (Parent (x_id)) == NodeColor.red)
714 if (Parent (x_id) == Left (Parent (Parent (x_id)))) // if x.parent is a left child
716 y_id = Right (Parent (Parent (x_id))); // x.parent.parent.right;
717 if (color (y_id) == NodeColor.red) // my right uncle is red
719 SetColor (Parent (x_id), NodeColor.black); // x.parent.color = Color.black;
720 SetColor (y_id, NodeColor.black);
721 SetColor (Parent (Parent (x_id)), NodeColor.red); // x.parent.parent.color = Color.red;
722 x_id = Parent (Parent (x_id)); // x = x.parent.parent;
725 { // my right uncle is black
726 if (x_id == Right (Parent (x_id)))
728 x_id = Parent (x_id);
729 root_id = LeftRotate (root_id, x_id, mainTreeNodeID);
732 SetColor (Parent (x_id), NodeColor.black); // x.parent.color = Color.black;
733 SetColor (Parent (Parent (x_id)), NodeColor.red); // x.parent.parent.color = Color.red;
734 root_id = RightRotate (root_id, Parent (Parent (x_id)), mainTreeNodeID); // RightRotate (x.parent.parent);
738 { // x.parent is a right child
739 y_id = Left (Parent (Parent (x_id))); // y = x.parent.parent.left;
740 if (color (y_id) == NodeColor.red) // if (y.color == Color.red) // my right uncle is red
742 SetColor (Parent (x_id), NodeColor.black);
743 SetColor (y_id, NodeColor.black);
744 SetColor (Parent (Parent (x_id)), NodeColor.red); // x.parent.parent.color = Color.red;
745 x_id = Parent (Parent (x_id));
748 {// my right uncle is black
749 if (x_id == Left (Parent (x_id)))
751 x_id = Parent (x_id);
752 root_id = RightRotate (root_id, x_id, mainTreeNodeID);
755 SetColor (Parent (x_id), NodeColor.black); // x.parent.color = Color.black;
756 SetColor (Parent (Parent (x_id)), NodeColor.red); // x.parent.parent.color = Color.red;
757 root_id = LeftRotate (root_id, Parent (Parent (x_id)), mainTreeNodeID);
763 SetColor (root, NodeColor.black);
765 SetColor (root_id, NodeColor.black);
768 (new NodePath(z_id, mainTreeNodeID)).VerifyPath(this); // verify x_id after its been added
773 public void UpdateNodeKey(K currentKey, K newKey)
775 // swap oldRecord with NewRecord in nodeId associated with oldRecord
776 // if the matched node is a satellite root then also change the key in the associated main tree node.
777 NodePath x_id = GetNodeByKey (currentKey);
778 if (Parent(x_id.NodeID) == NIL && x_id.NodeID != root) //determine if x_id is a satellite root.
781 x_id.VerifyPath(this);
783 SetKey(x_id.MainTreeNodeID, newKey);
785 SetKey (x_id.NodeID, newKey);
788 public K DeleteByIndex(int i)
790 // This check was not correct, it should have been ((uint)this.Count <= (uint)i)
791 // Even then, the index will be checked by GetNodebyIndex which will throw either
792 // using RowOutOfRange or InternalRBTreeError depending on _accessMethod
794 //if (i >= (_inUseNodeCount - 1)) {
795 // throw ExceptionBuilder.InternalRBTreeError(RBTreeError.IndexOutOfRange);
799 NodePath x_id = GetNodeByIndex(i); // it'l throw if corresponding node does not exist
800 key = Key(x_id.NodeID);
801 RBDeleteX(NIL, x_id.NodeID, x_id.MainTreeNodeID);
805 public int RBDelete (int z_id)
807 // always perform delete operation on the main tree
808 Debug.Assert(_accessMethod == TreeAccessMethod.INDEX_ONLY, "not expecting anything else");
809 return RBDeleteX (NIL, z_id, NIL);
815 * root_id: root_id of the tree. it is NIL for Main tree.
816 * z_id : node_id of node to be deleted
818 * returns: The id of the spliced node
820 * Case 1: Node is in main tree only (decrease size in main tree)
821 * Case 2: Node's key is shared with a main tree node whose next is non-NIL
822 * (decrease size in both trees)
823 * Case 3: special case of case 2: After deletion, node leaves satelite tree with only 1 node (only root),
824 * it should collapse the satelite tree - go to case 4. (decrease size in both trees)
825 * Case 4: (1) Node is in Main tree and is a satelite tree root AND
826 * (2) It is the only node in Satelite tree
827 * (Do not decrease size in any tree, as its a collpase operation)
831 private int RBDeleteX(int root_id, int z_id, int mainTreeNodeID)
833 int x_id = NIL; // used for holding spliced node (y_id's) child
834 int y_id; // the spliced node
835 int py_id; // for holding spliced node (y_id's) parent
838 // by knowing the NodePath, when z_id is in a satellite branch we don't have to Search for mainTreeNodeID
839 (new NodePath(z_id, mainTreeNodeID)).VerifyPath(this);
841 if (Next (z_id) != NIL)
842 return RBDeleteX(Next(z_id), Next(z_id), z_id); // delete root of satelite tree.
844 // if we we reach here, we are guaranteed z_id.next is NIL.
845 bool isCase3 = false;
846 int mNode = ((_accessMethod == TreeAccessMethod.KEY_SEARCH_AND_INDEX) ? mainTreeNodeID : z_id);
848 if (Next (mNode) != NIL)
849 root_id = Next (mNode);
851 if (SubTreeSize (Next (mNode)) == 2) // Next(mNode) == root_id
853 else if (SubTreeSize (Next (mNode)) == 1) {
854 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.InvalidNextSizeInDelete);
857 if (Left (z_id) == NIL || Right (z_id) == NIL)
860 y_id = Successor (z_id);
862 if (Left (y_id) != NIL)
867 py_id = Parent(y_id);
869 SetParent (x_id, py_id);
871 if (py_id == NIL) // if the spliced node is the root.
873 // check for main tree or Satellite tree root
878 // spliced node is root of satellite tree
882 else if (y_id == Left (py_id)) // update y's parent to point to X as its child
883 SetLeft (py_id, x_id);
885 SetRight (py_id, x_id);
889 // assign all values from y (spliced node) to z (node containing key to be deleted)
892 SetKey (z_id, Key (y_id)); // assign all values from y to z
893 SetNext (z_id, Next (y_id)); //z.value = y.value;
896 if (Next(mNode) != NIL)
898 // update mNode to point to satellite tree root and have the same key value.
899 // mNode will have to be patched again after RBDeleteFixup as root_id can again change
900 if (root_id == NIL && z_id != mNode) {
901 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.InvalidStateinDelete);
903 // -- it's possible for Next(mNode) to be != NIL and root_id == NIL when, the spliced node is a mNode of some
904 // -- satellite tree and its "next" gets assigned to mNode
907 SetNext (mNode, root_id);
908 SetKey (mNode, Key (root_id));
912 // traverse from y_id's parent to root and decrement size by 1
913 int tmp_py_id = py_id;
915 while (tmp_py_id != NIL)
917 //DecreaseSize (py_id, (Next(y_id)==NIL)?1:Size(Next(y_id)));
918 RecomputeSize (tmp_py_id);
919 tmp_py_id = Parent (tmp_py_id);
922 //if satelite tree node deleted, decrease size in main tree as well.
927 while (nodeId != NIL)
929 DecreaseSize (nodeId);
930 nodeId = Parent (nodeId);
934 if (color (y_id) == NodeColor.black)
935 root_id = RBDeleteFixup (root_id, x_id, py_id, mainTreeNodeID); // passing x.parent as y.parent, to handle x=Node.NIL case.
939 // Collpase satelite tree, by swapping it with the main tree counterpart and freeing the main tree node
940 if (mNode == NIL || SubTreeSize(Next(mNode)) != 1) {
941 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.InvalidNodeSizeinDelete);
943 _inUseSatelliteTreeCount--;
944 int satelliteRootId = Next(mNode);
945 SetLeft(satelliteRootId, Left(mNode));
946 SetRight(satelliteRootId, Right(mNode));
947 SetSubTreeSize(satelliteRootId, SubTreeSize(mNode));
948 SetColor(satelliteRootId, color(mNode)); // Next of satelliteRootId is already NIL
949 if (Parent(mNode) != NIL)
951 SetParent(satelliteRootId, Parent(mNode));
952 if (Left(Parent(mNode)) == mNode) {
953 SetLeft(Parent(mNode), satelliteRootId);
956 SetRight(Parent(mNode), satelliteRootId);
960 // update mNode's children.
961 if (Left(mNode) != NIL) {
962 SetParent(Left(mNode), satelliteRootId);
964 if (Right(mNode) != NIL) {
965 SetParent(Right(mNode), satelliteRootId);
968 root = satelliteRootId;
974 else if (Next(mNode) != NIL)
976 // update mNode to point to satellite tree root and have the same key value
977 if (root_id == NIL && z_id != mNode) { //if mNode being deleted, its OK for root_id (it should be) NIL.
978 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.InvalidStateinEndDelete);
983 SetNext (mNode, root_id);
984 SetKey (mNode, Key (root_id));
989 // In order to pin a key to it's node, free deleted z_id instead of the spliced y_id
992 // we know that key, next and value are same for z_id and y_id
993 SetLeft (y_id, Left (z_id));
994 SetRight (y_id, Right (z_id));
995 SetColor (y_id, color (z_id));
996 SetSubTreeSize (y_id, SubTreeSize(z_id));
997 if (Parent(z_id) != NIL)
999 SetParent(y_id, Parent(z_id));
1000 if (Left(Parent(z_id)) == z_id) {
1001 SetLeft(Parent(z_id), y_id);
1004 SetRight(Parent(z_id), y_id);
1008 SetParent(y_id, NIL);
1012 if (Left(z_id) != NIL) {
1013 SetParent(Left(z_id), y_id);
1015 if (Right(z_id) != NIL) {
1016 SetParent(Right(z_id), y_id);
1022 else if (root_id == z_id) {
1025 // update a next reference to z_id (if any)
1026 if (mNode != NIL && Next(mNode) == z_id) {
1027 SetNext(mNode, y_id);
1031 unchecked{_version++;}
1037 * Fix the specified tree for RedBlack properties
1039 * returns: The id of the root
1041 private int RBDeleteFixup (int root_id, int x_id, int px_id /* px is parent of x */, int mainTreeNodeID)
1042 { //x is successor's non nil child or nil if both children are nil
1046 // by knowing the NodePath, when z_id is in a satellite branch we don't have to Search for mainTreeNodeID
1047 (new NodePath(root_id, mainTreeNodeID)).VerifyPath(this);
1050 if (x_id == NIL && px_id == NIL) {
1051 return NIL; //case of satelite tree root being deleted.
1054 while (((root_id == NIL ? root : root_id) != x_id) && color (x_id) == NodeColor.black)
1056 // (1) x's parent should have aleast 1 non-NIL child.
1057 // (2) check if x is a NIL left child or a non NIL left child
1058 if ((x_id != NIL && x_id == Left (Parent (x_id))) || (x_id == NIL && Left (px_id) == NIL))
1060 // we have from DELETE, then x cannot be NIL and be a right child of its parent
1061 // also from DELETE, if x is non nil, it will be a left child.
1062 w_id = (x_id == NIL) ? Right (px_id) : Right (Parent (x_id)); // w is x's right sibling and it cannot be NIL
1065 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.RBDeleteFixup);
1068 if (color (w_id) == NodeColor.red)
1070 SetColor (w_id, NodeColor.black);
1071 SetColor (px_id, NodeColor.red);
1072 root_id = LeftRotate (root_id, px_id, mainTreeNodeID);
1073 w_id = (x_id == NIL) ? Right (px_id) : Right (Parent (x_id));
1076 if (color (Left (w_id)) == NodeColor.black && color (Right (w_id)) == NodeColor.black)
1078 SetColor (w_id, NodeColor.red);
1080 px_id = Parent (px_id); //maintain px_id
1084 if (color (Right (w_id)) == NodeColor.black)
1086 SetColor (Left (w_id), NodeColor.black);
1087 SetColor (w_id, NodeColor.red);
1088 root_id = RightRotate (root_id, w_id, mainTreeNodeID);
1089 w_id = (x_id == NIL) ? Right (px_id) : Right (Parent (x_id));
1092 SetColor (w_id, color (px_id));
1093 SetColor (px_id, NodeColor.black);
1094 SetColor (Right (w_id), NodeColor.black);
1095 root_id = LeftRotate (root_id, px_id, mainTreeNodeID);
1097 x_id = (root_id == NIL) ? root : root_id;
1098 px_id = Parent (x_id);
1102 { //x is a right child or it is NIL
1103 w_id = Left (px_id);
1104 if (color (w_id) == NodeColor.red)
1105 { // x_id is y's (the spliced node) sole non-NIL child or NIL if y had no children
1106 SetColor (w_id, NodeColor.black);
1108 SetColor (px_id, NodeColor.red);
1109 root_id = RightRotate (root_id, px_id, mainTreeNodeID);
1110 w_id = (x_id == NIL) ? Left (px_id) : Left (Parent (x_id));
1113 //we have from DELETE, then x cannot be NIL and be a right child of its parent
1114 // w_id cannot be nil.
1115 SetColor (px_id, NodeColor.red);
1116 root_id = RightRotate (root_id, px_id, mainTreeNodeID);
1117 w_id = (x_id == NIL) ? Left (px_id) : Left (Parent (x_id));
1120 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.CannotRotateInvalidsuccessorNodeinDelete);
1125 if (color (Right (w_id)) == NodeColor.black && color (Left (w_id)) == NodeColor.black) {
1126 SetColor (w_id, NodeColor.red);
1128 px_id = Parent (px_id);
1131 if (color (Left (w_id)) == NodeColor.black)
1133 SetColor (Right (w_id), NodeColor.black);
1134 SetColor (w_id, NodeColor.red);
1135 root_id = LeftRotate (root_id, w_id, mainTreeNodeID);
1136 w_id = (x_id == NIL) ? Left (px_id) : Left (Parent (x_id));
1141 SetColor (w_id, color (px_id));
1142 SetColor (px_id, NodeColor.black);
1143 SetColor (Left (w_id), NodeColor.black);
1144 root_id = RightRotate (root_id, px_id, mainTreeNodeID);
1146 x_id = (root_id == NIL) ? root : root_id;
1147 px_id = Parent (x_id);
1151 SetColor (w_id, color (px_id));
1152 SetColor (px_id, NodeColor.black);
1153 SetColor (Left (w_id), NodeColor.black);
1154 root_id = RightRotate (root_id, px_id, mainTreeNodeID);
1156 x_id = (root_id == NIL) ? root : root_id;
1157 px_id = Parent (x_id);
1163 SetColor (x_id, NodeColor.black);
1167 private int SearchSubTree (int root_id, K key)
1169 if (root_id != NIL && _accessMethod!=TreeAccessMethod.KEY_SEARCH_AND_INDEX) {
1170 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.UnsupportedAccessMethodInNonNillRootSubtree);
1173 int x_id = (root_id == NIL) ? root : root_id;
1177 c = (root_id == NIL) ? CompareNode (key, Key (x_id)) : CompareSateliteTreeNode (key, Key (x_id));
1183 Debug.Assert((NIL == Left(x_id)) || (0 > Compare(root_id, Left(x_id), x_id)), "Search duplicate Left is not left");
1189 Debug.Assert((NIL == Right(x_id)) || (0 < Compare(root_id, Right(x_id), x_id)), "Search duplicate Right is not right");
1191 x_id = Right (x_id);
1197 // only works on the main tree - does not work with satelite tree
1198 public int Search (K key)
1199 { // for performance reasons, written as a while loop instead of a recursive method
1204 c = CompareNode (key, Key (x_id));
1210 Debug.Assert((NIL == Left(x_id)) || (0 > Compare(NIL, Left(x_id), x_id)), "Search Left is not left");
1216 Debug.Assert((NIL == Right(x_id)) || (0 < Compare(NIL, Right(x_id), x_id)), "Search Right is not right");
1218 x_id = Right (x_id);
1224 // To simulate direct access for records[index]= record
1226 /// return key associated with the specified value. Specifically, return record for specified index/value
1229 /// <exception cref="IndexOutOfRangeException"></exception>
1230 // return record i.e key at specified index
1231 public K this[int index]
1235 return Key(GetNodeByIndex(index).NodeID);
1239 // Get Record(s) having same key value as that of specified record. Then scan the matched nodes
1240 // and return the node with the matching record
1241 /// <returns>Determine node and the branch it took to get there.</returns>
1242 private NodePath GetNodeByKey(K key) //i.e. GetNodeByKey
1244 int nodeId = SearchSubTree(NIL, key);
1245 if (Next (nodeId) != NIL) {
1246 return new NodePath(SearchSubTree(Next(nodeId), key), nodeId);
1248 else if (!Key(nodeId).Equals(key)) {
1251 return new NodePath(nodeId, NIL);
1255 * GetIndexByRecord()
1256 * Gets index of the specified record. returns (-1) if specified record is not found.
1258 public int GetIndexByKey (K key)
1261 NodePath nodeId = GetNodeByKey (key);
1262 if (nodeId.NodeID != NIL) {
1263 nodeIndex = GetIndexByNodePath (nodeId);
1273 * If I am right child then size=my size + size of left child of my parent + 1
1274 * go up till root, if right child keep adding to the size.
1275 * (1) compute rank in main tree.
1276 * (2) if node member of a satelite tree, add to rank its relative rank in that tree.
1279 * Case 1: Node is in Main RBTree only
1280 * Its rank/index is its main tree index
1281 * Case 2: Node is in a Satelite tree only
1282 * Its rank/index is its satelite tree index
1283 * Case 3: Nodes is in both Main and Satelite RBTree (a main tree node can be a satelite tree root)
1284 * Its rank/index is its main tree index + its satelite tree index - 1
1285 * Returns the index of the specified node.
1286 * returns -1, if the specified Node is tree.NIL.
1288 * Assumption: The specified node always exist in the tree.
1290 // SQLBU 428961: Serious performance issue when creating DataView
1291 // this improves performance when used heavily, like with the default view (creating before rows added)
1292 public int GetIndexByNode (int node)
1294 Debug.Assert(NIL != node, "GetIndexByNode(NIL)");
1296 if (0 == _inUseSatelliteTreeCount)
1297 { // compute from the main tree when no satellite branches exist
1298 return ComputeIndexByNode(node);
1300 else if (NIL != Next(node))
1301 { // node is a main tree node
1302 #if VerifyIndex && VerifyPath
1303 (new NodePath(Next(node), node)).VerifyPath(this);
1305 return ComputeIndexWithSatelliteByNode(node);
1309 int mainTreeNodeId = SearchSubTree(NIL, Key(node));
1310 if (mainTreeNodeId == node)
1311 { // node is a main tree node
1312 #if VerifyIndex && VerifyPath
1313 (new NodePath(node, NIL)).VerifyPath(this);
1315 return ComputeIndexWithSatelliteByNode(node);
1318 { //compute the main tree rank + satellite branch rank
1319 #if VerifyIndex && VerifyPath
1320 (new NodePath(node, mainTreeNodeId)).VerifyPath(this);
1322 return ComputeIndexWithSatelliteByNode(mainTreeNodeId) +
1323 ComputeIndexByNode(node);
1328 /// <summary>Determine tree index position from node path.</summary>
1329 /// <remarks>This differs from GetIndexByNode which would search for the main tree node instead of just knowing it</remarks>
1330 private int GetIndexByNodePath(NodePath path)
1332 #if VerifyIndex && VerifyPath
1333 path.VerifyPath(this);
1335 if (0 == _inUseSatelliteTreeCount)
1336 { // compute from the main tree when no satellite branches exist
1337 return ComputeIndexByNode(path.NodeID);
1339 else if (NIL == path.MainTreeNodeID)
1340 { // compute from the main tree accounting for satellite branches
1341 return ComputeIndexWithSatelliteByNode(path.NodeID);
1344 { //compute the main tree rank + satellite branch rank
1345 return ComputeIndexWithSatelliteByNode(path.MainTreeNodeID) +
1346 ComputeIndexByNode(path.NodeID);
1350 private int ComputeIndexByNode(int nodeId) {
1352 Debug.Assert(NIL != nodeId, "ComputeIndexByNode(NIL)");
1354 int myRank = SubTreeSize(Left(nodeId));
1355 while (nodeId != NIL)
1357 #if VerifyIndex && VerifyPath
1358 Debug.Assert(NIL == Next(nodeId), "Next not NIL");
1360 int parent = Parent(nodeId);
1361 if (nodeId == Right(parent))
1363 myRank += (SubTreeSize(Left(parent)) + 1);
1370 private int ComputeIndexWithSatelliteByNode(int nodeId) {
1372 Debug.Assert(NIL != nodeId, "ComputeIndexWithSatelliteByNode(NIL)");
1374 int myRank = SubTreeSize(Left(nodeId));
1375 while (nodeId != NIL)
1377 int parent = Parent(nodeId);
1378 if (nodeId == Right(parent))
1380 myRank += (SubTreeSize(Left(parent)) + ((Next(parent) == NIL) ? 1 : SubTreeSize(Next(parent))));
1387 /// <returns>Determine node and the branch it took to get there.</returns>
1388 /// <exception cref="IndexOutOfRangeException"></exception>
1389 private NodePath GetNodeByIndex(int userIndex)
1391 int x_id, satelliteRootId;
1392 if (0 == _inUseSatelliteTreeCount) {
1393 // if rows were only contigously append, then using (userIndex -= _pageTable[i].InUseCount) would
1394 // be faster for the first 12 pages (about 5248) nodes before (log2 of Count) becomes faster again.
1395 // the additional complexity was deemed not worthy for the possible perf gain
1397 // computation cost is (log2 of Count)
1398 x_id = ComputeNodeByIndex(root, unchecked(userIndex + 1));
1399 satelliteRootId = NIL;
1402 // computation cost is ((log2 of Distinct Count) + (log2 of Duplicate Count))
1403 x_id = ComputeNodeByIndex(userIndex, out satelliteRootId);
1406 if (TreeAccessMethod.INDEX_ONLY == _accessMethod) {
1407 throw ExceptionBuilder.RowOutOfRange(userIndex);
1410 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.IndexOutOFRangeinGetNodeByIndex);
1413 return new NodePath(x_id, satelliteRootId);
1416 private int ComputeNodeByIndex(int index, out int satelliteRootId)
1418 index = unchecked(index + 1); // index is 0 based, while size is 1 based.
1419 satelliteRootId = NIL;
1423 while (x_id != NIL && !(((rank = SubTreeSize (Left (x_id)) + 1) == index) && Next (x_id) == NIL))
1428 else if (Next (x_id) != NIL && index >= rank && index <= rank + SubTreeSize (Next (x_id)) - 1)
1430 // node with matching index is in the associated satellite tree. continue searching for index in satellite tree.
1431 satelliteRootId = x_id;
1432 index = index - rank + 1; // rank is SubTreeSize(Node.left)+1, we do +1 here to offset +1 done in rank. index -= rank;
1433 return ComputeNodeByIndex(Next(x_id), index); //satellite tree root
1437 if (Next (x_id) == NIL)
1440 index -= rank + SubTreeSize (Next (x_id)) - 1;
1442 x_id = Right (x_id);
1448 private int ComputeNodeByIndex(int x_id, int index) {
1449 while (x_id != NIL) {
1450 Debug.Assert(NIL == Next(x_id), "has unexpected satellite tree");
1452 int y_id = Left(x_id);
1453 int rank = SubTreeSize(y_id) + 1;
1457 else if (rank < index) {
1469 // return true if all nodes are unique; i.e. no satelite trees.
1470 public bool CheckUnique (int curNodeId)
1472 if (curNodeId != NIL)
1474 if (Next (curNodeId) != NIL)
1475 return false; // atleast 1 duplicate found
1477 if (!CheckUnique (Left (curNodeId)) || !CheckUnique (Right (curNodeId)))
1485 public int Insert (K item)
1487 int nodeId = GetNewNode(item);
1489 RBInsert (NIL, nodeId, NIL, -1, false);
1493 // Begin: List of methods for making it easy to work with ArrayList
1495 public int Add(K item) //Insert (int record)
1497 int nodeId = GetNewNode (item);
1498 RBInsert(NIL, nodeId, NIL, -1, false);
1502 public IEnumerator GetEnumerator() {
1503 return new RBTreeEnumerator(this);
1506 // *****BruteForceImplementation*****
1508 // iterate over all nodes, InOrder and return index of node with the specified Item
1509 // For the short term use a recursive method, later re-write it based on a stack data structure (if needed)
1510 public int IndexOf (int nodeId, K item)
1513 // BIG ASSUMPTION: There is not satellite tree, this is INDEX_ONLY.
1516 if ( (Object) Key(nodeId) == (Object)item) {
1517 return GetIndexByNode(nodeId);
1519 if ( (index=IndexOf(Left(nodeId), item)) != -1) {
1522 if ( (index=IndexOf(Right(nodeId), item)) != -1) {
1530 public int Insert(int position, K item) //Insert (int record)
1532 return InsertAt(position, item, false);
1536 public int InsertAt(int position, K item, bool append)
1538 int nodeId = GetNewNode (item);
1539 RBInsert (NIL, nodeId, NIL, position, append);
1543 public void RemoveAt(int position)
1545 DeleteByIndex(position);
1551 unchecked{_version++;}
1554 public void CopyTo(Array array, int index) {
1556 throw ExceptionBuilder.ArgumentNull("array");
1559 throw ExceptionBuilder.ArgumentOutOfRange("index");
1563 if (array.Length - index < Count) {
1564 throw ExceptionBuilder.InvalidOffsetLength();
1567 int x_id = Minimum(root);
1568 for(int i = 0; i < count; ++i) {
1569 array.SetValue(Key(x_id), index + i);
1570 x_id = Successor(x_id);
1574 public void CopyTo(K[] array, int index) {
1576 throw ExceptionBuilder.ArgumentNull("array");
1579 throw ExceptionBuilder.ArgumentOutOfRange("index");
1582 if (array.Length - index < Count) {
1583 throw ExceptionBuilder.InvalidOffsetLength();
1586 int x_id = Minimum(root);
1587 for(int i = 0; i < count; ++i) {
1588 array[index + i] = Key(x_id);
1589 x_id = Successor(x_id);
1593 // End: List of methods for making it easy to work with ArrayList
1595 private void SetRight (int nodeId, int rightNodeId)
1598 TreePage page = _pageTable[nodeId >> 16];
1599 int slotIndex = nodeId & 0xFFFF;
1600 page.Slots[slotIndex].rightId = rightNodeId;
1602 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].rightId = rightNodeId;
1605 private void SetLeft (int nodeId, int leftNodeId)
1608 TreePage page = _pageTable[nodeId >> 16];
1609 int slotIndex = nodeId & 0xFFFF;
1610 page.Slots[slotIndex].leftId = leftNodeId;
1612 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].leftId = leftNodeId;
1615 private void SetParent (int nodeId, int parentNodeId)
1617 Debug.Assert(nodeId != NIL, " in SetParent nodeId == NIL");
1619 TreePage page = _pageTable[nodeId >> 16];
1620 int slotIndex = nodeId & 0xFFFF;
1621 page.Slots[slotIndex].parentId = parentNodeId;
1623 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].parentId = parentNodeId;
1626 private void SetColor (int nodeId, NodeColor color)
1628 Debug.Assert(nodeId != NIL, " in SetColor nodeId == NIL");
1630 TreePage page = _pageTable[nodeId >> 16];
1631 int slotIndex = nodeId & 0xFFFF;
1632 page.Slots[slotIndex].nodeColor = color;
1634 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].nodeColor = color;
1637 private void SetKey (int nodeId, K key)
1640 TreePage page = _pageTable[nodeId >> 16];
1641 int slotIndex = nodeId & 0xFFFF;
1642 page.Slots[slotIndex].keyOfNode = key;
1644 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].keyOfNode = key;
1647 private void SetNext (int nodeId, int nextNodeId)
1650 TreePage page = _pageTable[nodeId >> 16];
1651 int slotIndex = nodeId & 0xFFFF;
1652 page.Slots[slotIndex].nextId = nextNodeId;
1654 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].nextId = nextNodeId;
1657 private void SetSubTreeSize (int nodeId, int size)
1659 Debug.Assert(nodeId != NIL &&
1660 (size != 0 || _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].selfId == NIL) &&
1661 (size != 1 || _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].nextId == NIL), "SetSize");
1662 // SQLBU 428961: Serious performance issue when creating DataView
1663 // this improves performance by reducing the impact of this heavily used method
1664 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].subTreeSize = size;
1665 VerifySize(nodeId, size);
1668 private void IncreaseSize (int nodeId)
1671 TreePage page = _pageTable[nodeId >> 16];
1672 int slotIndex = nodeId & 0xFFFF;
1673 page.Slots[slotIndex].subTreeSize += 1;
1675 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].subTreeSize += 1;
1679 private void RecomputeSize(int nodeId)
1681 int myCorrectSize = SubTreeSize (Left (nodeId)) + SubTreeSize (Right (nodeId)) + (Next (nodeId) == NIL ? 1 : SubTreeSize (Next (nodeId)));
1683 TreePage page = _pageTable[nodeId >> 16];
1684 int slotIndex = nodeId & 0xFFFF;
1685 page.Slots[slotIndex].subTreeSize = myCorrectSize;
1687 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].subTreeSize = myCorrectSize;
1690 private void DecreaseSize (int nodeId)
1693 TreePage page = _pageTable[nodeId >> 16];
1694 int slotIndex = nodeId & 0xFFFF;
1695 page.Slots[slotIndex].subTreeSize -= 1;
1697 _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].subTreeSize -= 1;
1698 VerifySize(nodeId, _pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].subTreeSize);
1701 [ConditionalAttribute("DEBUG")]
1702 private void VerifySize(int nodeId, int size) {
1703 int myCorrectSize = SubTreeSize(Left(nodeId)) + SubTreeSize(Right(nodeId)) + (Next(nodeId) == NIL ? 1 : SubTreeSize(Next(nodeId)));
1704 Debug.Assert(myCorrectSize == size, "VerifySize");
1707 public int Right (int nodeId)
1710 TreePage page = _pageTable[nodeId >> 16];
1711 int slotIndex = nodeId & 0xFFFF;
1712 int rightId = page.Slots[slotIndex].rightId;
1715 return (_pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].rightId);
1718 public int Left (int nodeId)
1721 TreePage page = _pageTable[nodeId >> 16];
1722 int slotIndex = nodeId & 0xFFFF;
1723 int leftId = page.Slots[slotIndex].leftId;
1726 return (_pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].leftId);
1729 public int Parent (int nodeId)
1732 TreePage page = _pageTable[nodeId >> 16];
1733 int slotIndex = nodeId & 0xFFFF;
1734 int parentId = page.Slots[slotIndex].parentId;
1737 return (_pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].parentId);
1740 private NodeColor color (int nodeId)
1743 TreePage page = _pageTable[nodeId >> 16];
1744 int slotIndex = nodeId & 0xFFFF;
1745 NodeColor col = page.Slots[slotIndex].nodeColor;
1748 return (_pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].nodeColor);
1751 public int Next (int nodeId)
1754 TreePage page = _pageTable[nodeId >> 16];
1755 int slotIndex = nodeId & 0xFFFF;
1756 int nextId = page.Slots[slotIndex].nextId;
1759 return (_pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].nextId);
1762 public int SubTreeSize (int nodeId)
1765 TreePage page = _pageTable[nodeId >> 16];
1766 int slotIndex = nodeId & 0xFFFF;
1767 int size = page.Slots[slotIndex].subTreeSize;
1770 return (_pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].subTreeSize);
1773 public K Key (int nodeId)
1776 TreePage page = _pageTable[nodeId >> 16];
1777 int slotIndex = nodeId & 0xFFFF;
1778 K key = page.Slots[slotIndex].keyOfNode;
1781 return (_pageTable[nodeId >> 16].Slots[nodeId & 0xFFFF].keyOfNode);
1784 private enum NodeColor {
1791 internal int selfId;
1792 internal int leftId;
1793 internal int rightId;
1794 internal int parentId;
1795 internal int nextId; // multiple records associated with same key
1796 internal int subTreeSize; // number of nodes in subtree rooted at the current node
1797 internal K keyOfNode;
1798 internal NodeColor nodeColor;
1802 /// <summary>Represents the node in the tree and the satellite branch it took to get there.</summary>
1803 private struct NodePath
1805 /// <summary>Represents the node in the tree</summary>
1806 internal readonly int NodeID;
1809 /// When not NIL, it represents the fact NodeID is has duplicate values in the tree.
1810 /// This is the 'fake' node in the main tree that redirects to the root of the satellite tree.
1811 /// By tracking this value, we don't have to repeatedly search for this node.
1813 internal readonly int MainTreeNodeID;
1815 internal NodePath(int nodeID, int mainTreeNodeID)
1818 MainTreeNodeID = mainTreeNodeID;
1822 internal void VerifyPath(RBTree<K> tree)
1824 Debug.Assert(null != tree, "null tree");
1825 Debug.Assert((NIL == NodeID && NIL == MainTreeNodeID) || (NIL != NodeID), "MainTreeNodeID is not NIL");
1827 if (NIL != MainTreeNodeID)
1829 Debug.Assert(NIL != tree.Next(MainTreeNodeID), "MainTreeNodeID should have a Next");
1830 int node = MainTreeNodeID;
1831 while (NIL != tree.Parent(node))
1833 node = tree.Parent(node);
1835 Debug.Assert(tree.root == node, "MainTreeNodeID parent change doesn't align");
1839 Debug.Assert(NIL == tree.Next(NodeID), "NodeID should not have a Next");
1841 if (NIL == MainTreeNodeID) {
1842 while (NIL != tree.Parent(node))
1844 node = tree.Parent(node);
1848 while (NIL != tree.Parent(node))
1850 Debug.Assert(NIL == tree.Next(node), "duplicate node should not have a next");
1851 node = tree.Parent(node);
1854 Debug.Assert((NIL == MainTreeNodeID && tree.root == node) ||
1855 (tree.Next(MainTreeNodeID) == node), "NodeID parent change doesn't align");
1861 private sealed class TreePage {
1862 public const Int32 slotLineSize = 32;
1864 internal readonly Node[] Slots; // List of slots
1865 internal readonly Int32[] SlotMap; // CEILING(slots.size/slotLineSize)
1866 private Int32 _inUseCount; // 0 to _slots.size
1867 private Int32 _pageId; // Page's Id
1868 private Int32 _nextFreeSlotLine; // o based position of next free slot line
1871 * size: number of slots per page. Maximum allowed is 64K
1873 internal TreePage (int size)
1875 if (size > 64 * 1024) {
1876 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.InvalidPageSize);
1878 Slots = new Node[size];
1879 SlotMap = new Int32[(size + slotLineSize - 1) / slotLineSize];
1883 * Allocate a free slot from the current page belonging to the specified tree.
1884 * return the Id of the allocated slot, or -1 if the current page does not have any free slots.
1886 internal int AllocSlot (RBTree<K> tree)
1888 int segmentPos = 0; // index into _SlotMap
1889 Int32 freeSlot = 0; // Uint, slot offset within the segment
1890 int freeSlotId = -1; // 0 based slot position
1892 if (_inUseCount < Slots.Length)
1894 segmentPos = _nextFreeSlotLine;
1895 while (segmentPos < SlotMap.Length)
1897 if (((UInt32)SlotMap[segmentPos]) < 0xFFFFFFFF)
1900 freeSlot = (~(SlotMap[segmentPos])) & (SlotMap[segmentPos] + 1);
1902 // avoid string concat to allow debug code to run faster
1903 Debug.Assert((SlotMap[segmentPos] & freeSlot) == 0,"Slot position segment[segmentPos ]: [freeSlot] is in use. Expected to be empty");
1905 SlotMap[segmentPos] |= freeSlot; //mark free slot as used.
1907 if (_inUseCount == Slots.Length) // mark page as full
1908 tree.MarkPageFull (this);
1909 tree._inUseNodeCount++;
1911 // convert freeSlotPos to int value giving number of 0's to its right i.e. freeSlotId
1912 freeSlotId = GetIntValueFromBitMap((uint)freeSlot);
1914 _nextFreeSlotLine = segmentPos;
1915 freeSlotId = (segmentPos * TreePage.slotLineSize) + freeSlotId;
1924 if (freeSlotId == -1 && _nextFreeSlotLine != 0)
1926 //Try one more time, starting from 0th segment position to locate a free slot.
1927 _nextFreeSlotLine = 0;
1928 freeSlotId = AllocSlot (tree);
1932 return freeSlotId; // 0 based slot position
1935 internal Int32 InUseCount
1937 get {return _inUseCount;}
1938 set {_inUseCount = value;}
1941 internal Int32 PageId
1943 get { return _pageId; }
1944 set { _pageId = value; }
1949 // SQLBU 428961: Serious performance issue when creating DataView
1950 // this improves performance allowing to iterating of the index instead of computing record by index
1951 // changes are required to handle satellite nodes which do not exist in DataRowCollection
1952 // enumerator over index will not be handed to the user, only used internally
1954 // instance of this enumerator will be handed to the user via DataRowCollection.GetEnumerator()
1955 internal struct RBTreeEnumerator : System.Collections.Generic.IEnumerator<K>, System.Collections.IEnumerator
1957 private readonly RBTree<K> tree;
1958 private readonly int version;
1959 private int index, mainTreeNodeId;
1962 internal RBTreeEnumerator(RBTree<K> tree) {
1964 version = tree._version;
1966 mainTreeNodeId = tree.root;
1967 current = default(K);
1970 internal RBTreeEnumerator(RBTree<K> tree, int position)
1973 version = tree._version;
1977 mainTreeNodeId = tree.root;
1981 index = tree.ComputeNodeByIndex(position-1, out mainTreeNodeId);
1984 throw ExceptionBuilder.InternalRBTreeError(RBTreeError.IndexOutOFRangeinGetNodeByIndex);
1987 current = default(K);
1990 public void Dispose() {
1993 public bool MoveNext() {
1994 if (version != tree._version) {
1995 throw ExceptionBuilder.EnumeratorModified();
1998 bool hasCurrent = tree.Successor(ref index, ref mainTreeNodeId);
1999 current = tree.Key(index);
2009 Object System.Collections.IEnumerator.Current {
2015 void System.Collections.IEnumerator.Reset() {
2016 if (version != tree._version) {
2017 throw ExceptionBuilder.EnumeratorModified();
2021 mainTreeNodeId = tree.root;
2022 current = default(K);