2 // System.Collections.Generic.RBTree
5 // Raja R Harinath <rharinath@novell.com>
9 // Copyright (C) 2007, Novell, Inc.
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #define ONE_MEMBER_CACHE
35 using System
.Collections
;
37 namespace System
.Collections
.Generic
40 internal class RBTree
: IEnumerable
, IEnumerable
<RBTree
.Node
> {
41 public interface INodeHelper
<T
> {
42 int Compare (T key
, Node node
);
43 Node
CreateNode (T key
);
46 public abstract class Node
{
47 public Node left
, right
;
50 const uint black_mask
= 1;
51 const int black_shift
= 1;
53 get { return (size_black & black_mask) == black_mask; }
54 set { size_black = value ? (size_black | black_mask) : (size_black & ~black_mask); }
58 get { return size_black >> black_shift; }
59 set { size_black = (value << black_shift) | (size_black & black_mask); }
62 public uint FixSize ()
74 size_black
= 2; // Size == 1, IsBlack = false
77 public abstract void SwapValue (Node other
);
80 public int VerifyInvariants ()
82 int black_depth_l
= 0;
83 int black_depth_r
= 0;
85 bool child_is_red
= false;
87 black_depth_l
= left
.VerifyInvariants ();
89 child_is_red
|= !left
.IsBlack
;
93 black_depth_r
= right
.VerifyInvariants ();
95 child_is_red
|= !right
.IsBlack
;
98 if (black_depth_l
!= black_depth_r
)
99 throw new SystemException ("Internal error: black depth mismatch");
101 if (!IsBlack
&& child_is_red
)
102 throw new SystemException ("Internal error: red-red conflict");
104 throw new SystemException ("Internal error: metadata error");
106 return black_depth_l
+ (IsBlack
? 1 : 0);
109 public abstract void Dump (string indent
);
119 static readonly LocalDataStoreSlot _cachedPathStore
= System
.Threading
.Thread
.AllocateDataSlot ();
121 static List
<Node
> cached_path
{
122 get { return (List<Node>) System.Threading.Thread.GetData (_cachedPathStore); }
123 set { System.Threading.Thread.SetData (_cachedPathStore, value); }
127 static List
<Node
> cached_path
;
130 static List
<Node
> alloc_path ()
132 if (cached_path
== null)
133 return new List
<Node
> ();
135 List
<Node
> path
= cached_path
;
140 static void release_path (List
<Node
> path
)
142 if (cached_path
== null || cached_path
.Capacity
< path
.Capacity
) {
148 static List
<Node
> alloc_path ()
150 return new List
<Node
> ();
153 static void release_path (List
<Node
> path
)
158 public RBTree (object hlp
)
160 // hlp is INodeHelper<T> for some T
170 // if key is already in the tree, return the node associated with it
171 // if not, insert new_node into the tree, and return it
172 public Node Intern
<T
> (T key
, Node new_node
)
175 if (new_node
== null)
176 new_node
= ((INodeHelper
<T
>) hlp
).CreateNode (key
);
183 List
<Node
> path
= alloc_path ();
184 int in_tree_cmp
= find_key (key
, path
);
185 Node retval
= path
[path
.Count
- 1];
186 if (retval
== null) {
187 if (new_node
== null)
188 new_node
= ((INodeHelper
<T
>) hlp
).CreateNode (key
);
189 retval
= do_insert (in_tree_cmp
, new_node
, path
);
191 // no need for a try .. finally, this is only used to mitigate allocations
196 // returns the just-removed node (or null if the value wasn't in the tree)
197 public Node Remove
<T
> (T key
)
202 List
<Node
> path
= alloc_path ();
203 int in_tree_cmp
= find_key (key
, path
);
205 if (in_tree_cmp
== 0)
206 retval
= do_remove (path
);
207 // no need for a try .. finally, this is only used to mitigate allocations
212 public Node Lookup
<T
> (T key
)
214 INodeHelper
<T
> hlp
= (INodeHelper
<T
>) this.hlp
;
216 while (current
!= null) {
217 int c
= hlp
.Compare (key
, current
);
220 current
= c
< 0 ? current
.left
: current
.right
;
225 public void Bound
<T
> (T key
, ref Node lower
, ref Node upper
)
227 INodeHelper
<T
> hlp
= (INodeHelper
<T
>) this.hlp
;
229 while (current
!= null) {
230 int c
= hlp
.Compare (key
, current
);
237 current
= c
< 0 ? current
.left
: current
.right
;
242 get { return root == null ? 0 : (int) root.Size; }
245 public Node
this [int index
] {
247 if (index
< 0 || index
>= Count
)
248 throw new IndexOutOfRangeException ("index");
251 while (current
!= null) {
252 int left_size
= current
.left
== null ? 0 : (int) current
.left
.Size
;
253 if (index
== left_size
)
255 if (index
< left_size
) {
256 current
= current
.left
;
258 index
-= left_size
+ 1;
259 current
= current
.right
;
262 throw new SystemException ("Internal Error: index calculation");
266 public NodeEnumerator
GetEnumerator ()
268 return new NodeEnumerator (this);
271 // Get an enumerator that starts at 'key' or the next higher element in the tree
272 public NodeEnumerator GetSuffixEnumerator
<T
> (T key
)
274 var pennants
= new Stack
<Node
> ();
275 INodeHelper
<T
> hlp
= (INodeHelper
<T
>) this.hlp
;
277 while (current
!= null) {
278 int c
= hlp
.Compare (key
, current
);
280 pennants
.Push (current
);
283 current
= c
< 0 ? current
.left
: current
.right
;
285 return new NodeEnumerator (this, pennants
);
288 IEnumerator
<Node
> IEnumerable
<Node
>.GetEnumerator ()
290 return GetEnumerator ();
293 IEnumerator IEnumerable
.GetEnumerator ()
295 return GetEnumerator ();
299 public void VerifyInvariants ()
303 throw new SystemException ("Internal Error: root is not black");
304 root
.VerifyInvariants ();
315 // Pre-condition: root != null
316 int find_key
<T
> (T key
, List
<Node
> path
)
318 INodeHelper
<T
> hlp
= (INodeHelper
<T
>) this.hlp
;
326 while (current
!= null) {
327 c
= hlp
.Compare (key
, current
);
332 sibling
= current
.right
;
333 current
= current
.left
;
335 sibling
= current
.left
;
336 current
= current
.right
;
348 Node
do_insert (int in_tree_cmp
, Node current
, List
<Node
> path
)
350 path
[path
.Count
- 1] = current
;
351 Node parent
= path
[path
.Count
- 3];
354 parent
.left
= current
;
356 parent
.right
= current
;
357 for (int i
= 0; i
< path
.Count
- 2; i
+= 2)
361 rebalance_insert (path
);
364 throw new SystemException ("Internal error: root is not black");
370 Node
do_remove (List
<Node
> path
)
372 int curpos
= path
.Count
- 1;
374 Node current
= path
[curpos
];
375 if (current
.left
!= null) {
376 Node pred
= right_most (current
.left
, current
.right
, path
);
377 current
.SwapValue (pred
);
378 if (pred
.left
!= null) {
379 Node ppred
= pred
.left
;
380 path
.Add (null); path
.Add (ppred
);
381 pred
.SwapValue (ppred
);
383 } else if (current
.right
!= null) {
384 Node succ
= current
.right
;
385 path
.Add (null); path
.Add (succ
);
386 current
.SwapValue (succ
);
389 curpos
= path
.Count
- 1;
390 current
= path
[curpos
];
392 if (current
.Size
!= 1)
393 throw new SystemException ("Internal Error: red-black violation somewhere");
395 // remove it from our data structures
396 path
[curpos
] = null;
397 node_reparent (curpos
== 0 ? null : path
[curpos
-2], current
, 0, null);
399 for (int i
= 0; i
< path
.Count
- 2; i
+= 2)
402 if (current
.IsBlack
) {
403 current
.IsBlack
= false;
405 rebalance_delete (path
);
408 if (root
!= null && !root
.IsBlack
)
409 throw new SystemException ("Internal Error: root is not black");
415 // Pre-condition: current is red
416 void rebalance_insert (List
<Node
> path
)
418 int curpos
= path
.Count
- 1;
420 // parent == curpos-2, uncle == curpos-3, grandpa == curpos-4
421 if (path
[curpos
-3] == null || path
[curpos
-3].IsBlack
) {
422 rebalance_insert__rotate_final (curpos
, path
);
426 path
[curpos
-2].IsBlack
= path
[curpos
-3].IsBlack
= true;
428 curpos
-= 4; // move to the grandpa
430 if (curpos
== 0) // => current == root
432 path
[curpos
].IsBlack
= false;
433 } while (!path
[curpos
-2].IsBlack
);
436 // Pre-condition: current is black
437 void rebalance_delete (List
<Node
> path
)
439 int curpos
= path
.Count
- 1;
441 Node sibling
= path
[curpos
-1];
442 // current is black => sibling != null
443 if (!sibling
.IsBlack
) {
444 // current is black && sibling is red
445 // => both sibling.left and sibling.right are black, and are not null
446 curpos
= ensure_sibling_black (curpos
, path
);
447 // one of the nephews became the new sibling -- in either case, sibling != null
448 sibling
= path
[curpos
-1];
451 if ((sibling
.left
!= null && !sibling
.left
.IsBlack
) ||
452 (sibling
.right
!= null && !sibling
.right
.IsBlack
)) {
453 rebalance_delete__rotate_final (curpos
, path
);
457 sibling
.IsBlack
= false;
459 curpos
-= 2; // move to the parent
463 } while (path
[curpos
].IsBlack
);
464 path
[curpos
].IsBlack
= true;
467 void rebalance_insert__rotate_final (int curpos
, List
<Node
> path
)
469 Node current
= path
[curpos
];
470 Node parent
= path
[curpos
-2];
471 Node grandpa
= path
[curpos
-4];
473 uint grandpa_size
= grandpa
.Size
;
477 bool l1
= parent
== grandpa
.left
;
478 bool l2
= current
== parent
.left
;
480 grandpa
.left
= parent
.right
; parent
.right
= grandpa
;
482 } else if (l1
&& !l2
) {
483 grandpa
.left
= current
.right
; current
.right
= grandpa
;
484 parent
.right
= current
.left
; current
.left
= parent
;
486 } else if (!l1
&& l2
) {
487 grandpa
.right
= current
.left
; current
.left
= grandpa
;
488 parent
.left
= current
.right
; current
.right
= parent
;
490 } else { // (!l1 && !l2)
491 grandpa
.right
= parent
.left
; parent
.left
= grandpa
;
495 grandpa
.FixSize (); grandpa
.IsBlack
= false;
496 if (new_root
!= parent
)
497 parent
.FixSize (); /* parent is red already, so no need to set it */
499 new_root
.IsBlack
= true;
500 node_reparent (curpos
== 4 ? null : path
[curpos
-6], grandpa
, grandpa_size
, new_root
);
503 // Pre-condition: sibling is black, and one of sibling.left and sibling.right is red
504 void rebalance_delete__rotate_final (int curpos
, List
<Node
> path
)
506 //Node current = path [curpos];
507 Node sibling
= path
[curpos
-1];
508 Node parent
= path
[curpos
-2];
510 uint parent_size
= parent
.Size
;
511 bool parent_was_black
= parent
.IsBlack
;
514 if (parent
.right
== sibling
) {
515 // if far nephew is black
516 if (sibling
.right
== null || sibling
.right
.IsBlack
) {
517 // => near nephew is red, move it up
518 Node nephew
= sibling
.left
;
519 parent
.right
= nephew
.left
; nephew
.left
= parent
;
520 sibling
.left
= nephew
.right
; nephew
.right
= sibling
;
523 parent
.right
= sibling
.left
; sibling
.left
= parent
;
524 sibling
.right
.IsBlack
= true;
528 // if far nephew is black
529 if (sibling
.left
== null || sibling
.left
.IsBlack
) {
530 // => near nephew is red, move it up
531 Node nephew
= sibling
.right
;
532 parent
.left
= nephew
.right
; nephew
.right
= parent
;
533 sibling
.right
= nephew
.left
; nephew
.left
= sibling
;
536 parent
.left
= sibling
.right
; sibling
.right
= parent
;
537 sibling
.left
.IsBlack
= true;
542 parent
.FixSize (); parent
.IsBlack
= true;
543 if (new_root
!= sibling
)
544 sibling
.FixSize (); /* sibling is already black, so no need to set it */
546 new_root
.IsBlack
= parent_was_black
;
547 node_reparent (curpos
== 2 ? null : path
[curpos
-4], parent
, parent_size
, new_root
);
550 // Pre-condition: sibling is red (=> parent, sibling.left and sibling.right are black)
551 int ensure_sibling_black (int curpos
, List
<Node
> path
)
553 Node current
= path
[curpos
];
554 Node sibling
= path
[curpos
-1];
555 Node parent
= path
[curpos
-2];
557 bool current_on_left
;
558 uint parent_size
= parent
.Size
;
560 if (parent
.right
== sibling
) {
561 parent
.right
= sibling
.left
; sibling
.left
= parent
;
562 current_on_left
= true;
564 parent
.left
= sibling
.right
; sibling
.right
= parent
;
565 current_on_left
= false;
568 parent
.FixSize (); parent
.IsBlack
= false;
570 sibling
.IsBlack
= true;
571 node_reparent (curpos
== 2 ? null : path
[curpos
-4], parent
, parent_size
, sibling
);
573 // accomodate the rotation
574 if (curpos
+1 == path
.Count
) {
579 path
[curpos
-2] = sibling
;
580 path
[curpos
-1] = current_on_left
? sibling
.right
: sibling
.left
;
581 path
[curpos
] = parent
;
582 path
[curpos
+1] = current_on_left
? parent
.right
: parent
.left
;
583 path
[curpos
+2] = current
;
588 void node_reparent (Node orig_parent
, Node orig
, uint orig_size
, Node updated
)
590 if (updated
!= null && updated
.FixSize () != orig_size
)
591 throw new SystemException ("Internal error: rotation");
595 else if (orig
== orig_parent
.left
)
596 orig_parent
.left
= updated
;
597 else if (orig
== orig_parent
.right
)
598 orig_parent
.right
= updated
;
600 throw new SystemException ("Internal error: path error");
603 // Pre-condition: current != null
604 static Node
right_most (Node current
, Node sibling
, List
<Node
> path
)
609 if (current
.right
== null)
611 sibling
= current
.left
;
612 current
= current
.right
;
617 public struct NodeEnumerator
: IEnumerator
, IEnumerator
<Node
> {
621 Stack
<Node
> pennants
, init_pennants
;
623 internal NodeEnumerator (RBTree tree
)
627 version
= tree
.version
;
630 internal NodeEnumerator (RBTree tree
, Stack
<Node
> init_pennants
)
633 this.init_pennants
= init_pennants
;
642 public Node Current
{
643 get { return pennants.Peek (); }
646 object IEnumerator
.Current
{
653 public bool MoveNext ()
658 if (pennants
== null) {
659 if (tree
.root
== null)
661 if (init_pennants
!= null) {
662 pennants
= init_pennants
;
663 init_pennants
= null;
664 return pennants
.Count
!= 0;
666 pennants
= new Stack
<Node
> ();
669 if (pennants
.Count
== 0)
671 Node current
= pennants
.Pop ();
672 next
= current
.right
;
674 for (; next
!= null; next
= next
.left
)
675 pennants
.Push (next
);
677 return pennants
.Count
!= 0;
680 public void Dispose ()
686 void check_version ()
689 throw new ObjectDisposedException ("enumerator");
690 if (version
!= tree
.version
)
691 throw new InvalidOperationException ("tree modified");
694 internal void check_current ()
697 if (pennants
== null)
698 throw new InvalidOperationException ("state invalid before the first MoveNext()");
705 namespace Mono
.ValidationTest
{
706 using System
.Collections
.Generic
;
708 internal class TreeSet
<T
> : IEnumerable
<T
>, IEnumerable
710 public class Node
: RBTree
.Node
{
718 public override void SwapValue (RBTree
.Node other
)
720 Node o
= (Node
) other
;
726 public override void Dump (string indent
)
728 Console
.WriteLine ("{0}{1} {2}({3})", indent
, value, IsBlack
? "*" : "", Size
);
730 left
.Dump (indent
+ " /");
732 right
.Dump (indent
+ " \\");
736 public class NodeHelper
: RBTree
.INodeHelper
<T
> {
739 public int Compare (T
value, RBTree
.Node node
)
741 return cmp
.Compare (value, ((Node
) node
).value);
744 public RBTree
.Node
CreateNode (T
value)
746 return new Node (value);
749 private NodeHelper (IComparer
<T
> cmp
)
753 static NodeHelper Default
= new NodeHelper (Comparer
<T
>.Default
);
754 public static NodeHelper
GetHelper (IComparer
<T
> cmp
)
756 if (cmp
== null || cmp
== Comparer
<T
>.Default
)
758 return new NodeHelper (cmp
);
762 public struct Enumerator
: IDisposable
, IEnumerator
, IEnumerator
<T
> {
763 RBTree
.NodeEnumerator host
;
765 internal Enumerator (TreeSet
<T
> tree
)
767 host
= new RBTree
.NodeEnumerator (tree
.tree
);
770 void IEnumerator
.Reset ()
776 get { return ((Node) host.Current).value; }
779 object IEnumerator
.Current
{
780 get { return Current; }
783 public bool MoveNext ()
785 return host
.MoveNext ();
788 public void Dispose ()
796 public TreeSet () : this (null)
800 public TreeSet (IComparer
<T
> cmp
)
802 tree
= new RBTree (NodeHelper
.GetHelper (cmp
));
805 IEnumerator IEnumerable
.GetEnumerator ()
807 return GetEnumerator ();
810 IEnumerator
<T
> IEnumerable
<T
>.GetEnumerator ()
812 return GetEnumerator ();
815 public Enumerator
GetEnumerator ()
817 return new Enumerator (this);
820 // returns true if the value was inserted, false if the value already existed in the tree
821 public bool Insert (T
value)
823 RBTree
.Node n
= new Node (value);
824 return tree
.Intern (value, n
) == n
;
827 // returns true if the value was removed, false if the value didn't exist in the tree
828 public bool Remove (T
value)
830 return tree
.Remove (value) != null;
833 public bool Contains (T
value)
835 return tree
.Lookup (value) != null;
838 public T
this [int index
] {
839 get { return ((Node) tree [index]).value; }
843 get { return (int) tree.Count; }
846 public void VerifyInvariants ()
848 tree
.VerifyInvariants ();
858 static void Main (string [] args
)
860 Random r
= new Random ();
861 Dictionary
<int, int> d
= new Dictionary
<int, int> ();
862 TreeSet
<int> t
= new TreeSet
<int> ();
863 int iters
= args
.Length
== 0 ? 100000 : Int32
.Parse (args
[0]);
866 for (int i
= 0; i
< iters
; ++i
) {
867 if (i
>= watermark
) {
868 watermark
+= 1 + watermark
/4;
869 t
.VerifyInvariants ();
873 if (d
.ContainsKey (n
))
879 throw new Exception ("tree says it has a number it shouldn't");
881 throw new Exception ("tree says it has a number it shouldn't");
883 Console
.Error
.WriteLine ("Exception while inserting {0} in iteration {1}", n
, i
);
887 t
.VerifyInvariants ();
888 if (d
.Count
!= t
.Count
)
889 throw new Exception ("tree count is wrong?");
891 Console
.WriteLine ("Tree has {0} elements", t
.Count
);
893 foreach (int n
in d
.Keys
)
895 throw new Exception ("tree says it doesn't have a number it should");
897 Dictionary
<int, int> d1
= new Dictionary
<int, int> (d
);
900 foreach (int n
in t
) {
902 throw new Exception ("iteration out of order");
904 throw new Exception ("tree has a number it shouldn't");
909 throw new Exception ("tree has numbers it shouldn't");
911 for (int i
= 0; i
< iters
; ++i
) {
913 if (!d
.ContainsKey (n
)) {
915 throw new Exception ("tree says it doesn't have a number it should");
916 } else if (!t
.Contains (n
)) {
917 throw new Exception ("tree says it has a number it shouldn't");
922 foreach (int n
in d
.Keys
) {
923 if (count
<= watermark
) {
924 watermark
-= watermark
/4;
925 t
.VerifyInvariants ();
929 throw new Exception ("tree says it doesn't have a number it should");
931 if (t
.Count
!= count
)
932 throw new Exception ("Remove didn't remove exactly one element");
934 Console
.Error
.WriteLine ("While trying to remove {0} from tree of size {1}", n
, t
.Count
);
936 t
.VerifyInvariants ();
940 throw new Exception ("tree says it has a number it shouldn't");
942 t
.VerifyInvariants ();
945 throw new Exception ("tree claims to have elements");