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;
226 get { return root == null ? 0 : (int) root.Size; }
229 public Node this [int index] {
231 if (index < 0 || index >= Count)
232 throw new IndexOutOfRangeException ("index");
235 while (current != null) {
236 int left_size = current.left == null ? 0 : (int) current.left.Size;
237 if (index == left_size)
239 if (index < left_size) {
240 current = current.left;
242 index -= left_size + 1;
243 current = current.right;
246 throw new SystemException ("Internal Error: index calculation");
250 public NodeEnumerator GetEnumerator ()
252 return new NodeEnumerator (this);
255 IEnumerator<Node> IEnumerable<Node>.GetEnumerator ()
257 return GetEnumerator ();
260 IEnumerator IEnumerable.GetEnumerator ()
262 return GetEnumerator ();
266 public void VerifyInvariants ()
270 throw new SystemException ("Internal Error: root is not black");
271 root.VerifyInvariants ();
282 // Pre-condition: root != null
283 int find_key<T> (T key, List<Node> path)
285 INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
293 while (current != null) {
294 c = hlp.Compare (key, current);
299 sibling = current.right;
300 current = current.left;
302 sibling = current.left;
303 current = current.right;
315 Node do_insert (int in_tree_cmp, Node current, List<Node> path)
317 path [path.Count - 1] = current;
318 Node parent = path [path.Count - 3];
321 parent.left = current;
323 parent.right = current;
324 for (int i = 0; i < path.Count - 2; i += 2)
328 rebalance_insert (path);
331 throw new SystemException ("Internal error: root is not black");
337 Node do_remove (List<Node> path)
339 int curpos = path.Count - 1;
341 Node current = path [curpos];
342 if (current.left != null) {
343 Node pred = right_most (current.left, current.right, path);
344 current.SwapValue (pred);
345 if (pred.left != null) {
346 Node ppred = pred.left;
347 path.Add (null); path.Add (ppred);
348 pred.SwapValue (ppred);
350 } else if (current.right != null) {
351 Node succ = current.right;
352 path.Add (null); path.Add (succ);
353 current.SwapValue (succ);
356 curpos = path.Count - 1;
357 current = path [curpos];
359 if (current.Size != 1)
360 throw new SystemException ("Internal Error: red-black violation somewhere");
362 // remove it from our data structures
363 path [curpos] = null;
364 node_reparent (curpos == 0 ? null : path [curpos-2], current, 0, null);
366 for (int i = 0; i < path.Count - 2; i += 2)
369 if (current.IsBlack) {
370 current.IsBlack = false;
372 rebalance_delete (path);
375 if (root != null && !root.IsBlack)
376 throw new SystemException ("Internal Error: root is not black");
382 // Pre-condition: current is red
383 void rebalance_insert (List<Node> path)
385 int curpos = path.Count - 1;
387 // parent == curpos-2, uncle == curpos-3, grandpa == curpos-4
388 if (path [curpos-3] == null || path [curpos-3].IsBlack) {
389 rebalance_insert__rotate_final (curpos, path);
393 path [curpos-2].IsBlack = path [curpos-3].IsBlack = true;
395 curpos -= 4; // move to the grandpa
397 if (curpos == 0) // => current == root
399 path [curpos].IsBlack = false;
400 } while (!path [curpos-2].IsBlack);
403 // Pre-condition: current is black
404 void rebalance_delete (List<Node> path)
406 int curpos = path.Count - 1;
408 Node sibling = path [curpos-1];
409 // current is black => sibling != null
410 if (!sibling.IsBlack) {
411 // current is black && sibling is red
412 // => both sibling.left and sibling.right are black, and are not null
413 curpos = ensure_sibling_black (curpos, path);
414 // one of the nephews became the new sibling -- in either case, sibling != null
415 sibling = path [curpos-1];
418 if ((sibling.left != null && !sibling.left.IsBlack) ||
419 (sibling.right != null && !sibling.right.IsBlack)) {
420 rebalance_delete__rotate_final (curpos, path);
424 sibling.IsBlack = false;
426 curpos -= 2; // move to the parent
430 } while (path [curpos].IsBlack);
431 path [curpos].IsBlack = true;
434 void rebalance_insert__rotate_final (int curpos, List<Node> path)
436 Node current = path [curpos];
437 Node parent = path [curpos-2];
438 Node grandpa = path [curpos-4];
440 uint grandpa_size = grandpa.Size;
444 bool l1 = parent == grandpa.left;
445 bool l2 = current == parent.left;
447 grandpa.left = parent.right; parent.right = grandpa;
449 } else if (l1 && !l2) {
450 grandpa.left = current.right; current.right = grandpa;
451 parent.right = current.left; current.left = parent;
453 } else if (!l1 && l2) {
454 grandpa.right = current.left; current.left = grandpa;
455 parent.left = current.right; current.right = parent;
457 } else { // (!l1 && !l2)
458 grandpa.right = parent.left; parent.left = grandpa;
462 grandpa.FixSize (); grandpa.IsBlack = false;
463 if (new_root != parent)
464 parent.FixSize (); /* parent is red already, so no need to set it */
466 new_root.IsBlack = true;
467 node_reparent (curpos == 4 ? null : path [curpos-6], grandpa, grandpa_size, new_root);
470 // Pre-condition: sibling is black, and one of sibling.left and sibling.right is red
471 void rebalance_delete__rotate_final (int curpos, List<Node> path)
473 //Node current = path [curpos];
474 Node sibling = path [curpos-1];
475 Node parent = path [curpos-2];
477 uint parent_size = parent.Size;
478 bool parent_was_black = parent.IsBlack;
481 if (parent.right == sibling) {
482 // if far nephew is black
483 if (sibling.right == null || sibling.right.IsBlack) {
484 // => near nephew is red, move it up
485 Node nephew = sibling.left;
486 parent.right = nephew.left; nephew.left = parent;
487 sibling.left = nephew.right; nephew.right = sibling;
490 parent.right = sibling.left; sibling.left = parent;
491 sibling.right.IsBlack = true;
495 // if far nephew is black
496 if (sibling.left == null || sibling.left.IsBlack) {
497 // => near nephew is red, move it up
498 Node nephew = sibling.right;
499 parent.left = nephew.right; nephew.right = parent;
500 sibling.right = nephew.left; nephew.left = sibling;
503 parent.left = sibling.right; sibling.right = parent;
504 sibling.left.IsBlack = true;
509 parent.FixSize (); parent.IsBlack = true;
510 if (new_root != sibling)
511 sibling.FixSize (); /* sibling is already black, so no need to set it */
513 new_root.IsBlack = parent_was_black;
514 node_reparent (curpos == 2 ? null : path [curpos-4], parent, parent_size, new_root);
517 // Pre-condition: sibling is red (=> parent, sibling.left and sibling.right are black)
518 int ensure_sibling_black (int curpos, List<Node> path)
520 Node current = path [curpos];
521 Node sibling = path [curpos-1];
522 Node parent = path [curpos-2];
524 bool current_on_left;
525 uint parent_size = parent.Size;
527 if (parent.right == sibling) {
528 parent.right = sibling.left; sibling.left = parent;
529 current_on_left = true;
531 parent.left = sibling.right; sibling.right = parent;
532 current_on_left = false;
535 parent.FixSize (); parent.IsBlack = false;
537 sibling.IsBlack = true;
538 node_reparent (curpos == 2 ? null : path [curpos-4], parent, parent_size, sibling);
540 // accomodate the rotation
541 if (curpos+1 == path.Count) {
546 path [curpos-2] = sibling;
547 path [curpos-1] = current_on_left ? sibling.right : sibling.left;
548 path [curpos] = parent;
549 path [curpos+1] = current_on_left ? parent.right : parent.left;
550 path [curpos+2] = current;
555 void node_reparent (Node orig_parent, Node orig, uint orig_size, Node updated)
557 if (updated != null && updated.FixSize () != orig_size)
558 throw new SystemException ("Internal error: rotation");
562 else if (orig == orig_parent.left)
563 orig_parent.left = updated;
564 else if (orig == orig_parent.right)
565 orig_parent.right = updated;
567 throw new SystemException ("Internal error: path error");
570 // Pre-condition: current != null
571 static Node right_most (Node current, Node sibling, List<Node> path)
576 if (current.right == null)
578 sibling = current.left;
579 current = current.right;
584 public struct NodeEnumerator : IEnumerator, IEnumerator<Node> {
588 Stack<Node> pennants;
590 internal NodeEnumerator (RBTree tree)
593 version = tree.version;
603 public Node Current {
604 get { return pennants.Peek (); }
607 object IEnumerator.Current {
614 public bool MoveNext ()
619 if (pennants == null) {
620 if (tree.root == null)
622 pennants = new Stack<Node> ();
625 if (pennants.Count == 0)
627 Node current = pennants.Pop ();
628 next = current.right;
630 for (; next != null; next = next.left)
631 pennants.Push (next);
633 return pennants.Count != 0;
636 public void Dispose ()
642 void check_version ()
645 throw new ObjectDisposedException ("enumerator");
646 if (version != tree.version)
647 throw new InvalidOperationException ("tree modified");
650 internal void check_current ()
653 if (pennants == null)
654 throw new InvalidOperationException ("state invalid before the first MoveNext()");
661 namespace Mono.ValidationTest {
662 using System.Collections.Generic;
664 internal class TreeSet<T> : IEnumerable<T>, IEnumerable
666 public class Node : RBTree.Node {
674 public override void SwapValue (RBTree.Node other)
676 Node o = (Node) other;
682 public override void Dump (string indent)
684 Console.WriteLine ("{0}{1} {2}({3})", indent, value, IsBlack ? "*" : "", Size);
686 left.Dump (indent + " /");
688 right.Dump (indent + " \\");
692 public class NodeHelper : RBTree.INodeHelper<T> {
695 public int Compare (T value, RBTree.Node node)
697 return cmp.Compare (value, ((Node) node).value);
700 public RBTree.Node CreateNode (T value)
702 return new Node (value);
705 private NodeHelper (IComparer<T> cmp)
709 static NodeHelper Default = new NodeHelper (Comparer<T>.Default);
710 public static NodeHelper GetHelper (IComparer<T> cmp)
712 if (cmp == null || cmp == Comparer<T>.Default)
714 return new NodeHelper (cmp);
718 public struct Enumerator : IDisposable, IEnumerator, IEnumerator<T> {
719 RBTree.NodeEnumerator host;
721 internal Enumerator (TreeSet<T> tree)
723 host = new RBTree.NodeEnumerator (tree.tree);
726 void IEnumerator.Reset ()
732 get { return ((Node) host.Current).value; }
735 object IEnumerator.Current {
736 get { return Current; }
739 public bool MoveNext ()
741 return host.MoveNext ();
744 public void Dispose ()
752 public TreeSet () : this (null)
756 public TreeSet (IComparer<T> cmp)
758 tree = new RBTree (NodeHelper.GetHelper (cmp));
761 IEnumerator IEnumerable.GetEnumerator ()
763 return GetEnumerator ();
766 IEnumerator<T> IEnumerable<T>.GetEnumerator ()
768 return GetEnumerator ();
771 public Enumerator GetEnumerator ()
773 return new Enumerator (this);
776 // returns true if the value was inserted, false if the value already existed in the tree
777 public bool Insert (T value)
779 RBTree.Node n = new Node (value);
780 return tree.Intern (value, n) == n;
783 // returns true if the value was removed, false if the value didn't exist in the tree
784 public bool Remove (T value)
786 return tree.Remove (value) != null;
789 public bool Contains (T value)
791 return tree.Lookup (value) != null;
794 public T this [int index] {
795 get { return ((Node) tree [index]).value; }
799 get { return (int) tree.Count; }
802 public void VerifyInvariants ()
804 tree.VerifyInvariants ();
814 static void Main (string [] args)
816 Random r = new Random ();
817 Dictionary<int, int> d = new Dictionary<int, int> ();
818 TreeSet<int> t = new TreeSet<int> ();
819 int iters = args.Length == 0 ? 100000 : Int32.Parse (args [0]);
822 for (int i = 0; i < iters; ++i) {
823 if (i >= watermark) {
824 watermark += 1 + watermark/4;
825 t.VerifyInvariants ();
829 if (d.ContainsKey (n))
835 throw new Exception ("tree says it has a number it shouldn't");
837 throw new Exception ("tree says it has a number it shouldn't");
839 Console.Error.WriteLine ("Exception while inserting {0} in iteration {1}", n, i);
843 t.VerifyInvariants ();
844 if (d.Count != t.Count)
845 throw new Exception ("tree count is wrong?");
847 Console.WriteLine ("Tree has {0} elements", t.Count);
849 foreach (int n in d.Keys)
851 throw new Exception ("tree says it doesn't have a number it should");
853 Dictionary<int, int> d1 = new Dictionary<int, int> (d);
856 foreach (int n in t) {
858 throw new Exception ("iteration out of order");
860 throw new Exception ("tree has a number it shouldn't");
865 throw new Exception ("tree has numbers it shouldn't");
867 for (int i = 0; i < iters; ++i) {
869 if (!d.ContainsKey (n)) {
871 throw new Exception ("tree says it doesn't have a number it should");
872 } else if (!t.Contains (n)) {
873 throw new Exception ("tree says it has a number it shouldn't");
878 foreach (int n in d.Keys) {
879 if (count <= watermark) {
880 watermark -= watermark/4;
881 t.VerifyInvariants ();
885 throw new Exception ("tree says it doesn't have a number it should");
887 if (t.Count != count)
888 throw new Exception ("Remove didn't remove exactly one element");
890 Console.Error.WriteLine ("While trying to remove {0} from tree of size {1}", n, t.Count);
892 t.VerifyInvariants ();
896 throw new Exception ("tree says it has a number it shouldn't");
898 t.VerifyInvariants ();
901 throw new Exception ("tree claims to have elements");