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
34 using System.Collections;
36 namespace System.Collections.Generic
39 internal class RBTree : IEnumerable, IEnumerable<RBTree.Node> {
40 public interface INodeHelper<T> {
41 int Compare (T key, Node node);
42 Node CreateNode (T key);
45 public abstract class Node {
46 public Node left, right;
49 const uint black_mask = 1;
50 const int black_shift = 1;
52 get { return (size_black & black_mask) == black_mask; }
53 set { size_black = value ? (size_black | black_mask) : (size_black & ~black_mask); }
57 get { return size_black >> black_shift; }
58 set { size_black = (value << black_shift) | (size_black & black_mask); }
61 public uint FixSize ()
73 size_black = 2; // Size == 1, IsBlack = false
76 public abstract void SwapValue (Node other);
79 public int VerifyInvariants ()
81 int black_depth_l = 0;
82 int black_depth_r = 0;
84 bool child_is_red = false;
86 black_depth_l = left.VerifyInvariants ();
88 child_is_red |= !left.IsBlack;
92 black_depth_r = right.VerifyInvariants ();
94 child_is_red |= !right.IsBlack;
97 if (black_depth_l != black_depth_r)
98 throw new SystemException ("Internal error: black depth mismatch");
100 if (!IsBlack && child_is_red)
101 throw new SystemException ("Internal error: red-red conflict");
103 throw new SystemException ("Internal error: metadata error");
105 return black_depth_l + (IsBlack ? 1 : 0);
108 public abstract void Dump (string indent);
118 static readonly LocalDataStoreSlot _cachedPathStore = System.Threading.Thread.AllocateDataSlot ();
120 static List<Node> cached_path {
121 get { return (List<Node>) System.Threading.Thread.GetData (_cachedPathStore); }
122 set { System.Threading.Thread.SetData (_cachedPathStore, value); }
126 static List<Node> cached_path;
129 static List<Node> alloc_path ()
131 if (cached_path == null)
132 return new List<Node> ();
134 List<Node> path = cached_path;
139 static void release_path (List<Node> path)
141 if (cached_path == null || cached_path.Capacity < path.Capacity) {
147 static List<Node> alloc_path ()
149 return new List<Node> ();
152 static void release_path (List<Node> path)
157 public RBTree (object hlp)
159 // hlp is INodeHelper<T> for some T
169 // if key is already in the tree, return the node associated with it
170 // if not, insert new_node into the tree, and return it
171 public Node Intern<T> (T key, Node new_node)
174 if (new_node == null)
175 new_node = ((INodeHelper<T>) hlp).CreateNode (key);
182 List<Node> path = alloc_path ();
183 int in_tree_cmp = find_key (key, path);
184 Node retval = path [path.Count - 1];
185 if (retval == null) {
186 if (new_node == null)
187 new_node = ((INodeHelper<T>) hlp).CreateNode (key);
188 retval = do_insert (in_tree_cmp, new_node, path);
190 // no need for a try .. finally, this is only used to mitigate allocations
195 // returns the just-removed node (or null if the value wasn't in the tree)
196 public Node Remove<T> (T key)
201 List<Node> path = alloc_path ();
202 int in_tree_cmp = find_key (key, path);
204 if (in_tree_cmp == 0)
205 retval = do_remove (path);
206 // no need for a try .. finally, this is only used to mitigate allocations
211 public Node Lookup<T> (T key)
213 INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
215 while (current != null) {
216 int c = hlp.Compare (key, current);
219 current = c < 0 ? current.left : current.right;
224 public void Bound<T> (T key, ref Node lower, ref Node upper)
226 INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
228 while (current != null) {
229 int c = hlp.Compare (key, current);
236 current = c < 0 ? current.left : current.right;
241 get { return root == null ? 0 : (int) root.Size; }
244 public Node this [int index] {
246 if (index < 0 || index >= Count)
247 throw new IndexOutOfRangeException ("index");
250 while (current != null) {
251 int left_size = current.left == null ? 0 : (int) current.left.Size;
252 if (index == left_size)
254 if (index < left_size) {
255 current = current.left;
257 index -= left_size + 1;
258 current = current.right;
261 throw new SystemException ("Internal Error: index calculation");
265 public NodeEnumerator GetEnumerator ()
267 return new NodeEnumerator (this);
270 // Get an enumerator that starts at 'key' or the next higher element in the tree
271 public NodeEnumerator GetSuffixEnumerator<T> (T key)
273 var pennants = new Stack<Node> ();
274 INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
276 while (current != null) {
277 int c = hlp.Compare (key, current);
279 pennants.Push (current);
282 current = c < 0 ? current.left : current.right;
284 return new NodeEnumerator (this, pennants);
287 IEnumerator<Node> IEnumerable<Node>.GetEnumerator ()
289 return GetEnumerator ();
292 IEnumerator IEnumerable.GetEnumerator ()
294 return GetEnumerator ();
298 public void VerifyInvariants ()
302 throw new SystemException ("Internal Error: root is not black");
303 root.VerifyInvariants ();
314 // Pre-condition: root != null
315 int find_key<T> (T key, List<Node> path)
317 INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
325 while (current != null) {
326 c = hlp.Compare (key, current);
331 sibling = current.right;
332 current = current.left;
334 sibling = current.left;
335 current = current.right;
347 Node do_insert (int in_tree_cmp, Node current, List<Node> path)
349 path [path.Count - 1] = current;
350 Node parent = path [path.Count - 3];
353 parent.left = current;
355 parent.right = current;
356 for (int i = 0; i < path.Count - 2; i += 2)
360 rebalance_insert (path);
363 throw new SystemException ("Internal error: root is not black");
369 Node do_remove (List<Node> path)
371 int curpos = path.Count - 1;
373 Node current = path [curpos];
374 if (current.left != null) {
375 Node pred = right_most (current.left, current.right, path);
376 current.SwapValue (pred);
377 if (pred.left != null) {
378 Node ppred = pred.left;
379 path.Add (null); path.Add (ppred);
380 pred.SwapValue (ppred);
382 } else if (current.right != null) {
383 Node succ = current.right;
384 path.Add (null); path.Add (succ);
385 current.SwapValue (succ);
388 curpos = path.Count - 1;
389 current = path [curpos];
391 if (current.Size != 1)
392 throw new SystemException ("Internal Error: red-black violation somewhere");
394 // remove it from our data structures
395 path [curpos] = null;
396 node_reparent (curpos == 0 ? null : path [curpos-2], current, 0, null);
398 for (int i = 0; i < path.Count - 2; i += 2)
401 if (current.IsBlack) {
402 current.IsBlack = false;
404 rebalance_delete (path);
407 if (root != null && !root.IsBlack)
408 throw new SystemException ("Internal Error: root is not black");
414 // Pre-condition: current is red
415 void rebalance_insert (List<Node> path)
417 int curpos = path.Count - 1;
419 // parent == curpos-2, uncle == curpos-3, grandpa == curpos-4
420 if (path [curpos-3] == null || path [curpos-3].IsBlack) {
421 rebalance_insert__rotate_final (curpos, path);
425 path [curpos-2].IsBlack = path [curpos-3].IsBlack = true;
427 curpos -= 4; // move to the grandpa
429 if (curpos == 0) // => current == root
431 path [curpos].IsBlack = false;
432 } while (!path [curpos-2].IsBlack);
435 // Pre-condition: current is black
436 void rebalance_delete (List<Node> path)
438 int curpos = path.Count - 1;
440 Node sibling = path [curpos-1];
441 // current is black => sibling != null
442 if (!sibling.IsBlack) {
443 // current is black && sibling is red
444 // => both sibling.left and sibling.right are black, and are not null
445 curpos = ensure_sibling_black (curpos, path);
446 // one of the nephews became the new sibling -- in either case, sibling != null
447 sibling = path [curpos-1];
450 if ((sibling.left != null && !sibling.left.IsBlack) ||
451 (sibling.right != null && !sibling.right.IsBlack)) {
452 rebalance_delete__rotate_final (curpos, path);
456 sibling.IsBlack = false;
458 curpos -= 2; // move to the parent
462 } while (path [curpos].IsBlack);
463 path [curpos].IsBlack = true;
466 void rebalance_insert__rotate_final (int curpos, List<Node> path)
468 Node current = path [curpos];
469 Node parent = path [curpos-2];
470 Node grandpa = path [curpos-4];
472 uint grandpa_size = grandpa.Size;
476 bool l1 = parent == grandpa.left;
477 bool l2 = current == parent.left;
479 grandpa.left = parent.right; parent.right = grandpa;
481 } else if (l1 && !l2) {
482 grandpa.left = current.right; current.right = grandpa;
483 parent.right = current.left; current.left = parent;
485 } else if (!l1 && l2) {
486 grandpa.right = current.left; current.left = grandpa;
487 parent.left = current.right; current.right = parent;
489 } else { // (!l1 && !l2)
490 grandpa.right = parent.left; parent.left = grandpa;
494 grandpa.FixSize (); grandpa.IsBlack = false;
495 if (new_root != parent)
496 parent.FixSize (); /* parent is red already, so no need to set it */
498 new_root.IsBlack = true;
499 node_reparent (curpos == 4 ? null : path [curpos-6], grandpa, grandpa_size, new_root);
502 // Pre-condition: sibling is black, and one of sibling.left and sibling.right is red
503 void rebalance_delete__rotate_final (int curpos, List<Node> path)
505 //Node current = path [curpos];
506 Node sibling = path [curpos-1];
507 Node parent = path [curpos-2];
509 uint parent_size = parent.Size;
510 bool parent_was_black = parent.IsBlack;
513 if (parent.right == sibling) {
514 // if far nephew is black
515 if (sibling.right == null || sibling.right.IsBlack) {
516 // => near nephew is red, move it up
517 Node nephew = sibling.left;
518 parent.right = nephew.left; nephew.left = parent;
519 sibling.left = nephew.right; nephew.right = sibling;
522 parent.right = sibling.left; sibling.left = parent;
523 sibling.right.IsBlack = true;
527 // if far nephew is black
528 if (sibling.left == null || sibling.left.IsBlack) {
529 // => near nephew is red, move it up
530 Node nephew = sibling.right;
531 parent.left = nephew.right; nephew.right = parent;
532 sibling.right = nephew.left; nephew.left = sibling;
535 parent.left = sibling.right; sibling.right = parent;
536 sibling.left.IsBlack = true;
541 parent.FixSize (); parent.IsBlack = true;
542 if (new_root != sibling)
543 sibling.FixSize (); /* sibling is already black, so no need to set it */
545 new_root.IsBlack = parent_was_black;
546 node_reparent (curpos == 2 ? null : path [curpos-4], parent, parent_size, new_root);
549 // Pre-condition: sibling is red (=> parent, sibling.left and sibling.right are black)
550 int ensure_sibling_black (int curpos, List<Node> path)
552 Node current = path [curpos];
553 Node sibling = path [curpos-1];
554 Node parent = path [curpos-2];
556 bool current_on_left;
557 uint parent_size = parent.Size;
559 if (parent.right == sibling) {
560 parent.right = sibling.left; sibling.left = parent;
561 current_on_left = true;
563 parent.left = sibling.right; sibling.right = parent;
564 current_on_left = false;
567 parent.FixSize (); parent.IsBlack = false;
569 sibling.IsBlack = true;
570 node_reparent (curpos == 2 ? null : path [curpos-4], parent, parent_size, sibling);
572 // accomodate the rotation
573 if (curpos+1 == path.Count) {
578 path [curpos-2] = sibling;
579 path [curpos-1] = current_on_left ? sibling.right : sibling.left;
580 path [curpos] = parent;
581 path [curpos+1] = current_on_left ? parent.right : parent.left;
582 path [curpos+2] = current;
587 void node_reparent (Node orig_parent, Node orig, uint orig_size, Node updated)
589 if (updated != null && updated.FixSize () != orig_size)
590 throw new SystemException ("Internal error: rotation");
594 else if (orig == orig_parent.left)
595 orig_parent.left = updated;
596 else if (orig == orig_parent.right)
597 orig_parent.right = updated;
599 throw new SystemException ("Internal error: path error");
602 // Pre-condition: current != null
603 static Node right_most (Node current, Node sibling, List<Node> path)
608 if (current.right == null)
610 sibling = current.left;
611 current = current.right;
616 public struct NodeEnumerator : IEnumerator, IEnumerator<Node> {
620 Stack<Node> pennants, init_pennants;
622 internal NodeEnumerator (RBTree tree)
626 version = tree.version;
629 internal NodeEnumerator (RBTree tree, Stack<Node> init_pennants)
632 this.init_pennants = init_pennants;
641 public Node Current {
642 get { return pennants.Peek (); }
645 object IEnumerator.Current {
652 public bool MoveNext ()
657 if (pennants == null) {
658 if (tree.root == null)
660 if (init_pennants != null) {
661 pennants = init_pennants;
662 init_pennants = null;
663 return pennants.Count != 0;
665 pennants = new Stack<Node> ();
668 if (pennants.Count == 0)
670 Node current = pennants.Pop ();
671 next = current.right;
673 for (; next != null; next = next.left)
674 pennants.Push (next);
676 return pennants.Count != 0;
679 public void Dispose ()
685 void check_version ()
688 throw new ObjectDisposedException ("enumerator");
689 if (version != tree.version)
690 throw new InvalidOperationException ("tree modified");
693 internal void check_current ()
696 if (pennants == null)
697 throw new InvalidOperationException ("state invalid before the first MoveNext()");
704 namespace Mono.ValidationTest {
705 using System.Collections.Generic;
707 internal class TreeSet<T> : IEnumerable<T>, IEnumerable
709 public class Node : RBTree.Node {
717 public override void SwapValue (RBTree.Node other)
719 Node o = (Node) other;
725 public override void Dump (string indent)
727 Console.WriteLine ("{0}{1} {2}({3})", indent, value, IsBlack ? "*" : "", Size);
729 left.Dump (indent + " /");
731 right.Dump (indent + " \\");
735 public class NodeHelper : RBTree.INodeHelper<T> {
738 public int Compare (T value, RBTree.Node node)
740 return cmp.Compare (value, ((Node) node).value);
743 public RBTree.Node CreateNode (T value)
745 return new Node (value);
748 private NodeHelper (IComparer<T> cmp)
752 static NodeHelper Default = new NodeHelper (Comparer<T>.Default);
753 public static NodeHelper GetHelper (IComparer<T> cmp)
755 if (cmp == null || cmp == Comparer<T>.Default)
757 return new NodeHelper (cmp);
761 public struct Enumerator : IDisposable, IEnumerator, IEnumerator<T> {
762 RBTree.NodeEnumerator host;
764 internal Enumerator (TreeSet<T> tree)
766 host = new RBTree.NodeEnumerator (tree.tree);
769 void IEnumerator.Reset ()
775 get { return ((Node) host.Current).value; }
778 object IEnumerator.Current {
779 get { return Current; }
782 public bool MoveNext ()
784 return host.MoveNext ();
787 public void Dispose ()
795 public TreeSet () : this (null)
799 public TreeSet (IComparer<T> cmp)
801 tree = new RBTree (NodeHelper.GetHelper (cmp));
804 IEnumerator IEnumerable.GetEnumerator ()
806 return GetEnumerator ();
809 IEnumerator<T> IEnumerable<T>.GetEnumerator ()
811 return GetEnumerator ();
814 public Enumerator GetEnumerator ()
816 return new Enumerator (this);
819 // returns true if the value was inserted, false if the value already existed in the tree
820 public bool Insert (T value)
822 RBTree.Node n = new Node (value);
823 return tree.Intern (value, n) == n;
826 // returns true if the value was removed, false if the value didn't exist in the tree
827 public bool Remove (T value)
829 return tree.Remove (value) != null;
832 public bool Contains (T value)
834 return tree.Lookup (value) != null;
837 public T this [int index] {
838 get { return ((Node) tree [index]).value; }
842 get { return (int) tree.Count; }
845 public void VerifyInvariants ()
847 tree.VerifyInvariants ();
857 static void Main (string [] args)
859 Random r = new Random ();
860 Dictionary<int, int> d = new Dictionary<int, int> ();
861 TreeSet<int> t = new TreeSet<int> ();
862 int iters = args.Length == 0 ? 100000 : Int32.Parse (args [0]);
865 for (int i = 0; i < iters; ++i) {
866 if (i >= watermark) {
867 watermark += 1 + watermark/4;
868 t.VerifyInvariants ();
872 if (d.ContainsKey (n))
878 throw new Exception ("tree says it has a number it shouldn't");
880 throw new Exception ("tree says it has a number it shouldn't");
882 Console.Error.WriteLine ("Exception while inserting {0} in iteration {1}", n, i);
886 t.VerifyInvariants ();
887 if (d.Count != t.Count)
888 throw new Exception ("tree count is wrong?");
890 Console.WriteLine ("Tree has {0} elements", t.Count);
892 foreach (int n in d.Keys)
894 throw new Exception ("tree says it doesn't have a number it should");
896 Dictionary<int, int> d1 = new Dictionary<int, int> (d);
899 foreach (int n in t) {
901 throw new Exception ("iteration out of order");
903 throw new Exception ("tree has a number it shouldn't");
908 throw new Exception ("tree has numbers it shouldn't");
910 for (int i = 0; i < iters; ++i) {
912 if (!d.ContainsKey (n)) {
914 throw new Exception ("tree says it doesn't have a number it should");
915 } else if (!t.Contains (n)) {
916 throw new Exception ("tree says it has a number it shouldn't");
921 foreach (int n in d.Keys) {
922 if (count <= watermark) {
923 watermark -= watermark/4;
924 t.VerifyInvariants ();
928 throw new Exception ("tree says it doesn't have a number it should");
930 if (t.Count != count)
931 throw new Exception ("Remove didn't remove exactly one element");
933 Console.Error.WriteLine ("While trying to remove {0} from tree of size {1}", n, t.Count);
935 t.VerifyInvariants ();
939 throw new Exception ("tree says it has a number it shouldn't");
941 t.VerifyInvariants ();
944 throw new Exception ("tree claims to have elements");