**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.C5 / trees / RedBlackTreeBag.cs
blob89b8afcf19c567fc2a8298dd58b788da1a6c7443
1 #if NET_2_0
2 /*
3 Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
23 #define MAINTAIN_SIZE
24 #define MAINTAIN_RANKnot
25 #define MAINTAIN_HEIGHTnot
26 #define BAG
27 #define NCP
29 #define MAINTAIN_EXTREMAnot
30 #define TRACE_IDnot
32 #if BAG
33 #if !MAINTAIN_SIZE
34 #error BAG defined without MAINTAIN_SIZE!
35 #endif
36 #endif
39 using System;
40 using MSG = System.Collections.Generic;
42 // NOTE NOTE NOTE NOTE
43 // This source file is used to produce both TreeBag<T> and TreeBag<T>
44 // It should be copied to a file called TreeBag.cs in which all code mentions of
45 // TreeBag is changed to TreeBag and the preprocessor symbol BAG is defined.
46 // NOTE: there may be problems with documentation comments.
48 namespace C5
50 #if BAG
51 /// <summary>
52 /// An implementation of Red-Black trees as an indexed, sorted collection with bag semantics,
53 /// cf. <a href="litterature.htm#CLRS">CLRS</a>. (<see cref="T:C5.TreeBag!1"/> for an
54 /// implementation with set semantics).
55 /// <br/>
56 /// The comparer (sorting order) may be either natural, because the item type is comparable
57 /// (generic: <see cref="T:C5.IComparable!1"/> or non-generic: System.IComparable) or it can
58 /// be external and supplied by the user in the constructor.
59 /// <br/>
60 /// Each distinct item is only kept in one place in the tree - together with the number
61 /// of times it is a member of the bag. Thus, if two items that are equal according
62 /// </summary>
63 #else
64 /// <summary>
65 /// An implementation of Red-Black trees as an indexed, sorted collection with set semantics,
66 /// cf. <a href="litterature.htm#CLRS">CLRS</a>. <see cref="T:C5.TreeBag!1"/> for a version
67 /// with bag semantics. <see cref="T:C5.TreeDictionary!2"/> for a sorted dictionary
68 /// based on this tree implementation.
69 /// <p>
70 /// The comparer (sorting order) may be either natural, because the item type is comparable
71 /// (generic: <see cref="T:C5.IComparable!1"/> or non-generic: System.IComparable) or it can
72 /// be external and supplied by the user in the constructor.</p>
73 ///
74 /// <p><i>TODO: describe performance here</i></p>
75 /// <p><i>TODO: discuss persistence and its useful usage modes. Warn about the space
76 /// leak possible with other usage modes.</i></p>
77 /// </summary>
78 #endif
79 public class TreeBag<T>: SequencedBase<T>, IIndexedSorted<T>, IPersistentSorted<T>
81 #region Feature
82 /// <summary>
83 /// A debugging aid for making the selected compilation alternatives
84 /// available to the user. (To be removed when selection is finally fixed
85 /// for production version).
86 /// </summary>
87 [Flags]
88 public enum Feature: short
90 /// <summary>
91 /// Nothing
92 /// </summary>
93 Dummy = 0,
94 /// <summary>
95 /// Node copy persistence as explained in <a href="litterature.htm#Tarjan1">Tarjan1</a>
96 /// </summary>
97 NodeCopyPersistence = 2,
98 /// <summary>
99 /// Maintain sub tree sizes
100 /// </summary>
101 Sizes = 4,
102 /// <summary>
103 /// Maintain precise node heights
104 /// </summary>
105 Heights = 8,
106 /// <summary>
107 /// Maintain node ranks (~ black height)
108 /// </summary>
109 Ranks = 16,
110 /// <summary>
111 /// Maintain unique ids on tree nodes.
112 /// </summary>
113 Traceid = 32
118 static Feature features = Feature.Dummy
119 #if NCP
120 | Feature.NodeCopyPersistence
121 #endif
122 #if MAINTAIN_RANK
123 |Feature.Ranks
124 #endif
125 #if MAINTAIN_HEIGHT
126 |Feature.Heights
127 #endif
128 #if MAINTAIN_SIZE
129 | Feature.Sizes
130 #endif
131 #if TRACE_ID
132 | Feature.Traceid
133 #endif
137 /// <summary>
138 /// A debugging aid for making the selected compilation alternatives
139 /// available to the user. (To be removed when selection is finally fixed
140 /// for production version).
141 /// </summary>
142 public static Feature Features { get { return features; } }
144 #endregion
146 #region Fields
148 IComparer<T> comparer;
150 Node root;
152 int blackdepth = 0;
154 //We double these stacks for the iterative add and remove on demand
155 private int[] dirs = new int[2];
157 private Node[] path = new Node[2];
158 #if NCP
159 private bool isSnapShot = false;
161 private SnapData snapdata;
163 private int generation;
165 private int maxsnapid = -1;
167 #endif
168 #if MAINTAIN_EXTREMA
169 T min, max;
170 #endif
171 #if MAINTAIN_HEIGHT
172 private short depth = 0;
173 #endif
174 #endregion
176 #region Util
178 /// <summary>
179 /// Fetch the left child of n taking node-copying persistence into
180 /// account if relevant.
181 /// </summary>
182 /// <param name="n"></param>
183 /// <returns></returns>
184 private Node left(Node n)
186 #if NCP
187 if (isSnapShot)
189 #if SEPARATE_EXTRA
190 Node.Extra e = n.extra;
192 if (e != null && e.lastgeneration >= treegen && e.leftnode)
193 return e.oldref;
194 #else
195 if (n.lastgeneration >= generation && n.leftnode)
196 return n.oldref;
197 #endif
199 #endif
200 return n.left;
204 private Node right(Node n)
206 #if NCP
207 if (isSnapShot)
209 #if SEPARATE_EXTRA
210 Node.Extra e = n.extra;
212 if (e != null && e.lastgeneration >= treegen && !e.leftnode)
213 return e.oldref;
214 #else
215 if (n.lastgeneration >= generation && !n.leftnode)
216 return n.oldref;
217 #endif
219 #endif
220 return n.right;
224 //This method should be called by methods that use the internal
225 //traversal stack, unless certain that there is room enough
226 private void stackcheck()
228 while (dirs.Length < 2 * blackdepth)
230 dirs = new int[2 * dirs.Length];
231 path = new Node[2 * dirs.Length];
235 #endregion
237 #region Node nested class
240 /// <summary>
241 /// The type of node in a Red-Black binary tree
242 /// </summary>
243 class Node
245 public bool red = true;
247 public T item;
249 public Node left;
251 public Node right;
253 #if MAINTAIN_SIZE
254 public int size = 1;
255 #endif
257 #if BAG
258 public int items = 1;
259 #endif
261 #if MAINTAIN_HEIGHT
262 public short height;
263 #endif
265 #if MAINTAIN_RANK
266 public short rank = 1;
267 #endif
269 #if TRACE_ID
270 public int id = sid++;
271 public static int sid = 0;
272 #endif
274 #if NCP
275 public int generation;
276 #if SEPARATE_EXTRA
277 internal class Extra
279 public int lastgeneration;
281 public Node oldref;
283 public bool leftnode;
285 //public Node next;
288 public Extra extra;
290 #else
291 public int lastgeneration = -1;
293 public Node oldref;
295 public bool leftnode;
296 #endif
298 /// <summary>
299 /// Update a child pointer
300 /// </summary>
301 /// <param name="cursor"></param>
302 /// <param name="leftnode"></param>
303 /// <param name="child"></param>
304 /// <param name="maxsnapid"></param>
305 /// <param name="generation"></param>
306 /// <returns>True if node was *copied*</returns>
307 internal static bool update(ref Node cursor, bool leftnode, Node child, int maxsnapid, int generation)
309 Node oldref = leftnode ? cursor.left : cursor.right;
311 if (child == oldref)
312 return false;
314 bool retval = false;
316 if (cursor.generation <= maxsnapid)
318 #if SEPARATE_EXTRA
319 if (cursor.extra == null)
321 Extra extra = cursor.extra = new Extra();
323 extra.leftnode = leftnode;
324 extra.lastgeneration = maxsnapid;
325 extra.oldref = oldref;
327 else if (cursor.extra.leftnode != leftnode || cursor.extra.lastgeneration < maxsnapid)
328 #else
329 if (cursor.lastgeneration == -1)
331 cursor.leftnode = leftnode;
332 cursor.lastgeneration = maxsnapid;
333 cursor.oldref = oldref;
335 else if (cursor.leftnode != leftnode || cursor.lastgeneration < maxsnapid)
336 #endif
338 CopyNode(ref cursor, maxsnapid, generation);
339 retval = true;
343 if (leftnode)
344 cursor.left = child;
345 else
346 cursor.right = child;
348 return retval;
352 //If cursor.extra.lastgeneration==maxsnapid, the extra pointer will
353 //always be used in the old copy of cursor. Therefore, after
354 //making the clone, we should update the old copy by restoring
355 //the child pointer and setting extra to null.
356 //OTOH then we cannot clean up unused Extra objects unless we link
357 //them together in a doubly linked list.
358 public static bool CopyNode(ref Node cursor, int maxsnapid, int generation)
360 if (cursor.generation <= maxsnapid)
362 cursor = (Node)(cursor.MemberwiseClone());
363 cursor.generation = generation;
364 #if SEPARATE_EXTRA
365 cursor.extra = null;
366 #else
367 cursor.lastgeneration = -1;
368 #endif
369 #if TRACE_ID
370 cursor.id = sid++;
371 #endif
372 return true;
374 else
375 return false;
378 #endif
381 #endregion
383 #region Constructors
385 /// <summary>
386 /// Create a red-black tree collection with natural comparer and item hasher.
387 /// </summary>
388 public TreeBag()
390 comparer = ComparerBuilder.FromComparable<T>.Examine();
391 itemhasher = HasherBuilder.ByPrototype<T>.Examine();
395 /// <summary>
396 /// Create a red-black tree collection with an external comparer (and natural item hasher,
397 /// assumed consistent).
398 /// </summary>
399 /// <param name="c">The external comparer</param>
400 public TreeBag(IComparer<T> c)
402 comparer = c;
403 itemhasher = HasherBuilder.ByPrototype<T>.Examine();
407 /// <summary>
408 /// Create a red-black tree collection with an external comparer aand an external
409 /// item hasher, assumed consistent.
410 /// </summary>
411 /// <param name="c">The external comparer</param>
412 /// <param name="h">The external item hasher</param>
413 public TreeBag(IComparer<T> c, IHasher<T> h)
415 comparer = c;
416 itemhasher = h;
419 #endregion
421 #region TreeBag.Enumerator nested class
423 /// <summary>
424 /// An enumerator for a red-black tree collection. Based on an explicit stack
425 /// of subtrees waiting to be enumerated. Currently only used for the tree set
426 /// enumerators (tree bag enumerators use an iterator block based enumerator).
427 /// </summary>
428 public class Enumerator: MSG.IEnumerator<T>
430 #region Private Fields
431 TreeBag<T> tree;
433 bool valid = false;
435 int stamp;
437 T current;
439 Node cursor;
441 Node[] path; // stack of nodes
443 int level = 0;
444 #endregion
445 /// <summary>
446 /// Create a tree enumerator
447 /// </summary>
448 /// <param name="tree">The red-black tree to enumerate</param>
449 public Enumerator(TreeBag<T> tree)
451 this.tree = tree;
452 stamp = tree.stamp;
453 path = new Node[2 * tree.blackdepth];
454 cursor = new Node();
455 cursor.right = tree.root;
459 /// <summary>
460 /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
461 /// </summary>
462 /// <value>The current item of the enumerator.</value>
463 [Tested]
464 public T Current
466 [Tested]
469 if (valid)
470 return current;
471 else
472 throw new InvalidOperationException();
477 //Maintain a stack of nodes that are roots of
478 //subtrees not completely exported yet. Invariant:
479 //The stack nodes together with their right subtrees
480 //consists of exactly the items we have not passed
481 //yet (the top of the stack holds current item).
482 /// <summary>
483 /// Move enumerator to next item in tree, or the first item if
484 /// this is the first call to MoveNext.
485 /// <exception cref="InvalidOperationException"/> if underlying tree was modified.
486 /// </summary>
487 /// <returns>True if enumerator is valid now</returns>
488 [Tested]
489 public bool MoveNext()
491 tree.modifycheck(stamp);
492 if (cursor.right != null)
494 path[level] = cursor = cursor.right;
495 while (cursor.left != null)
496 path[++level] = cursor = cursor.left;
498 else if (level == 0)
499 return valid = false;
500 else
501 cursor = path[--level];
503 current = cursor.item;
504 return valid = true;
508 #region IDisposable Members for Enumerator
510 bool disposed;
513 /// <summary>
514 /// Call Dispose(true) and then suppress finalization of this enumerator.
515 /// </summary>
516 [Tested]
517 public void Dispose()
519 Dispose(true);
520 GC.SuppressFinalize(this);
524 /// <summary>
525 /// Remove the internal data (notably the stack array).
526 /// </summary>
527 /// <param name="disposing">True if called from Dispose(),
528 /// false if called from the finalizer</param>
529 protected virtual void Dispose(bool disposing)
531 if (!disposed)
533 if (disposing)
537 current = default(T);
538 cursor = null;
539 path = null;
540 disposed = true;
545 /// <summary>
546 /// Finalizer for enumeratir
547 /// </summary>
548 ~Enumerator()
550 Dispose(false);
552 #endregion
555 #if NCP
556 /// <summary>
557 /// An enumerator for a snapshot of a node copy persistent red-black tree
558 /// collection.
559 /// </summary>
560 public class SnapEnumerator: MSG.IEnumerator<T>
562 #region Private Fields
563 TreeBag<T> tree;
565 bool valid = false;
567 int stamp;
568 #if BAG
569 int togo;
570 #endif
572 T current;
574 Node cursor;
576 Node[] path; // stack of nodes
578 int level;
579 #endregion
581 /// <summary>
582 /// Creta an enumerator for a snapshot of a node copy persistent red-black tree
583 /// collection
584 /// </summary>
585 /// <param name="tree">The snapshot</param>
586 public SnapEnumerator(TreeBag<T> tree)
588 this.tree = tree;
589 stamp = tree.stamp;
590 path = new Node[2 * tree.blackdepth];
591 cursor = new Node();
592 cursor.right = tree.root;
596 #region MSG.IEnumerator<T> Members
598 /// <summary>
599 /// Move enumerator to next item in tree, or the first item if
600 /// this is the first call to MoveNext.
601 /// <exception cref="InvalidOperationException"/> if underlying tree was modified.
602 /// </summary>
603 /// <returns>True if enumerator is valid now</returns>
604 [Tested]
605 public bool MoveNext()
607 tree.modifycheck(stamp);//???
609 #if BAG
610 if (--togo>0)
611 return true;
612 #endif
613 Node next = tree.right(cursor);
615 if (next != null)
617 path[level] = cursor = next;
618 next = tree.left(cursor);
619 while (next != null)
621 path[++level] = cursor = next;
622 next = tree.left(cursor);
625 else if (level == 0)
626 return valid = false;
627 else
628 cursor = path[--level];
630 #if BAG
631 togo = cursor.items;
632 #endif
633 current = cursor.item;
634 return valid = true;
638 /// <summary>
639 /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
640 /// </summary>
641 /// <value>The current value of the enumerator.</value>
642 [Tested]
643 public T Current
645 [Tested]
648 if (valid)
649 return current;
650 else
651 throw new InvalidOperationException();
655 #endregion
657 #region IDisposable Members
659 [Tested]
660 void System.IDisposable.Dispose()
662 tree = null;
663 valid = false;
664 current = default(T);
665 cursor = null;
666 path = null;
669 #endregion
671 #endif
672 #endregion
674 #region IEnumerable<T> Members
676 private MSG.IEnumerator<T> getEnumerator(Node node, int origstamp)
678 if (node == null)
679 yield break;
681 if (node.left != null)
683 MSG.IEnumerator<T> child = getEnumerator(node.left, origstamp);
685 while (child.MoveNext())
687 modifycheck(origstamp);
688 yield return child.Current;
691 #if BAG
692 int togo = node.items;
693 while (togo-- > 0)
695 modifycheck(origstamp);
696 yield return node.item;
698 #else
699 modifycheck(origstamp);
700 yield return node.item;
701 #endif
702 if (node.right != null)
704 MSG.IEnumerator<T> child = getEnumerator(node.right, origstamp);
706 while (child.MoveNext())
708 modifycheck(origstamp);
709 yield return child.Current;
715 /// <summary>
716 /// Create an enumerator for this tree
717 /// </summary>
718 /// <returns>The enumerator</returns>
719 [Tested]
720 public override MSG.IEnumerator<T> GetEnumerator()
722 #if NCP
723 if (isSnapShot)
724 return new SnapEnumerator(this);
725 #endif
726 #if BAG
727 return getEnumerator(root,stamp);
728 #else
729 return new Enumerator(this);
730 #endif
733 #endregion
735 #region ISink<T> Members
737 /// <summary>
738 /// Add item to tree. If already there, return the found item in the second argument.
739 /// </summary>
740 /// <param name="item">Item to add</param>
741 /// <param name="founditem">item found</param>
742 /// <param name="update">whether item in node should be updated</param>
743 /// <param name="wasfound">true if found in bag, false if not found or tre is a set</param>
744 /// <returns>True if item was added</returns>
745 bool addIterative(T item, ref T founditem, bool update, out bool wasfound)
747 wasfound = false;
748 if (root == null)
750 root = new Node();
751 root.red = false;
752 blackdepth = 1;
753 #if MAINTAIN_EXTREMA
754 root.item = min = max = item;
755 #else
756 root.item = item;
757 #endif
758 #if NCP
759 root.generation = generation;
760 #endif
761 #if MAINTAIN_HEIGHT
762 depth = 0;
763 #endif
764 return true;
767 stackcheck();
769 int level = 0;
770 Node cursor = root;
772 while (true)
774 int comp = comparer.Compare(cursor.item, item);
776 if (comp == 0)
778 founditem = cursor.item;
780 #if BAG
781 wasfound = true;
782 #if NCP
783 Node.CopyNode(ref cursor, maxsnapid, generation);
784 #endif
785 cursor.items++;
786 cursor.size++;
787 if (update)
788 cursor.item = item;
790 update = true;
792 #else
793 if (update)
795 #if NCP
796 Node.CopyNode(ref cursor, maxsnapid, generation);
797 #endif
798 cursor.item = item;
800 #endif
802 while (level-- > 0)
804 if (update)
806 Node kid = cursor;
808 cursor = path[level];
809 #if NCP
810 Node.update(ref cursor, dirs[level] > 0, kid, maxsnapid, generation);
811 #endif
812 #if BAG
813 cursor.size++;
814 #endif
817 path[level] = null;
819 #if BAG
820 return true;
821 #else
822 if (update)
823 root = cursor;
825 return false;
826 #endif
829 //else
830 Node child = comp > 0 ? cursor.left : cursor.right;
832 if (child == null)
834 child = new Node();
835 child.item = item;
836 #if NCP
837 child.generation = generation;
838 Node.update(ref cursor, comp > 0, child, maxsnapid, generation);
839 #else
840 if (comp > 0) { cursor.left = child; }
841 else { cursor.right = child; }
842 #endif
843 #if MAINTAIN_SIZE
844 cursor.size++;
845 #endif
846 #if MAINTAIN_HEIGHT
847 fixheight(cursor);
848 #endif
849 dirs[level] = comp;
850 break;
852 else
854 dirs[level] = comp;
855 path[level++] = cursor;
856 cursor = child;
860 //We have just added the red node child to "cursor"
861 while (cursor.red)
863 //take one step up:
864 Node child = cursor;
866 cursor = path[--level];
867 path[level] = null;
868 #if NCP
869 Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
870 #endif
871 #if MAINTAIN_SIZE
872 cursor.size++;
873 #endif
874 int comp = dirs[level];
875 Node childsibling = comp > 0 ? cursor.right : cursor.left;
877 if (childsibling != null && childsibling.red)
879 //Promote
880 #if MAINTAIN_RANK
881 cursor.rank++;
882 #endif
883 #if MAINTAIN_HEIGHT
884 fixheight(cursor);
885 #endif
886 child.red = false;
887 #if NCP
888 Node.update(ref cursor, comp < 0, childsibling, maxsnapid, generation);
889 #endif
890 childsibling.red = false;
892 //color cursor red & take one step up the tree unless at root
893 if (level == 0)
895 root = cursor;
896 blackdepth++;
897 return true;
899 else
901 cursor.red = true;
902 #if NCP
903 child = cursor;
904 cursor = path[--level];
905 Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
906 #endif
907 path[level] = null;
908 #if MAINTAIN_SIZE
909 cursor.size++;
910 #endif
911 #if MAINTAIN_HEIGHT
912 fixheight(cursor);
913 #endif
916 else
918 //ROTATE!!!
919 int childcomp = dirs[level + 1];
921 cursor.red = true;
922 if (comp > 0)
924 if (childcomp > 0)
925 {//zagzag
926 #if NCP
927 Node.update(ref cursor, true, child.right, maxsnapid, generation);
928 Node.update(ref child, false, cursor, maxsnapid, generation);
929 #else
930 cursor.left = child.right;
931 child.right = cursor;
932 #endif
933 cursor = child;
935 else
936 {//zagzig
937 Node badgrandchild = child.right;
938 #if NCP
939 Node.update(ref cursor, true, badgrandchild.right, maxsnapid, generation);
940 Node.update(ref child, false, badgrandchild.left, maxsnapid, generation);
941 Node.CopyNode(ref badgrandchild, maxsnapid, generation);
942 #else
943 cursor.left = badgrandchild.right;
944 child.right = badgrandchild.left;
945 #endif
946 badgrandchild.left = child;
947 badgrandchild.right = cursor;
948 cursor = badgrandchild;
951 else
952 {//comp < 0
953 if (childcomp < 0)
954 {//zigzig
955 #if NCP
956 Node.update(ref cursor, false, child.left, maxsnapid, generation);
957 Node.update(ref child, true, cursor, maxsnapid, generation);
958 #else
959 cursor.right = child.left;
960 child.left = cursor;
961 #endif
962 cursor = child;
964 else
965 {//zigzag
966 Node badgrandchild = child.left;
967 #if NCP
968 Node.update(ref cursor, false, badgrandchild.left, maxsnapid, generation);
969 Node.update(ref child, true, badgrandchild.right, maxsnapid, generation);
970 Node.CopyNode(ref badgrandchild, maxsnapid, generation);
971 #else
972 cursor.right = badgrandchild.left;
973 child.left = badgrandchild.right;
974 #endif
975 badgrandchild.right = child;
976 badgrandchild.left = cursor;
977 cursor = badgrandchild;
981 cursor.red = false;
983 #if MAINTAIN_SIZE
984 Node n;
986 #if BAG
987 n = cursor.right;
988 cursor.size = n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + n.items;
989 n = cursor.left;
990 n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + n.items;
991 cursor.size += n.size + cursor.items;
992 #else
993 n = cursor.right;
994 cursor.size = n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + 1;
995 n = cursor.left;
996 n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + 1;
997 cursor.size += n.size + 1;
998 #endif
999 #endif
1000 #if MAINTAIN_HEIGHT
1001 fixheight(cursor.right);
1002 fixheight(cursor.left);
1003 fixheight(cursor);
1004 #endif
1005 if (level == 0)
1007 root = cursor;
1008 return true;
1010 else
1012 child = cursor;
1013 cursor = path[--level];
1014 path[level] = null;
1015 #if NCP
1016 Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
1017 #else
1018 if (dirs[level] > 0)
1019 cursor.left = child;
1020 else
1021 cursor.right = child;
1022 #endif
1023 #if MAINTAIN_SIZE
1024 cursor.size++;
1025 #endif
1026 #if MAINTAIN_HEIGHT
1027 fixheight(cursor);
1028 #endif
1029 break;
1033 #if NCP
1034 bool stillmore = true;
1035 #endif
1036 while (level > 0)
1038 Node child = cursor;
1040 cursor = path[--level];
1041 path[level] = null;
1042 #if NCP
1043 if (stillmore)
1044 stillmore = Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
1045 #endif
1046 #if MAINTAIN_SIZE
1047 cursor.size++;
1048 #endif
1049 #if MAINTAIN_HEIGHT
1050 fixheight(cursor);
1051 #endif
1054 root = cursor;
1055 return true;
1059 /// <summary>
1060 /// Add an item to this collection if possible. If this collection has set
1061 /// semantics, the item will be added if not already in the collection. If
1062 /// bag semantics, the item will always be added.
1063 /// </summary>
1064 /// <param name="item">The item to add.</param>
1065 /// <returns>True if item was added.</returns>
1066 [Tested]
1067 public bool Add(T item)
1069 updatecheck();
1071 //Note: blackdepth of the tree is set inside addIterative
1072 T j = default(T);
1073 bool tmp;
1075 if (addIterative(item, ref j, false, out tmp))
1077 size++;
1078 #if MAINTAIN_EXTREMA
1079 if (Compare(item, min) < 0)
1080 min = item;
1081 else if (Compare(item, max) > 0)
1082 max = item;
1083 #endif
1084 #if MAINTAIN_HEIGHT
1085 depth = root.height;
1086 #endif
1087 return true;
1089 else
1090 return false;
1094 /// <summary>
1095 /// Add the elements from another collection to this collection. If this
1096 /// collection has set semantics, only items not already in the collection
1097 /// will be added.
1098 /// </summary>
1099 /// <param name="items">The items to add.</param>
1100 [Tested]
1101 public void AddAll(MSG.IEnumerable<T> items)
1103 int c = 0;
1104 T j = default(T);
1105 bool tmp;
1107 updatecheck();
1108 foreach (T i in items)
1109 if (addIterative(i, ref j, false, out tmp)) c++;
1111 size += c;
1114 /// <summary>
1115 /// Add the elements from another collection with a more specialized item type
1116 /// to this collection. If this
1117 /// collection has set semantics, only items not already in the collection
1118 /// will be added.
1119 /// </summary>
1120 /// <typeparam name="U">The type of items to add</typeparam>
1121 /// <param name="items">The items to add</param>
1122 public void AddAll<U>(MSG.IEnumerable<U> items) where U : T
1124 int c = 0;
1125 T j = default(T);
1126 bool tmp;
1128 updatecheck();
1129 foreach (T i in items)
1130 if (addIterative(i, ref j, false, out tmp)) c++;
1132 size += c;
1136 /// <summary>
1137 /// Add all the items from another collection with an enumeration order that
1138 /// is increasing in the items. <para>The idea is that the implementation may use
1139 /// a faster algorithm to merge the two collections.</para>
1140 /// <exception cref="ArgumentException"/> if the enumerated items turns out
1141 /// not to be in increasing order.
1142 /// </summary>
1143 /// <param name="items">The collection to add.</param>
1144 [Tested]
1145 public void AddSorted(MSG.IEnumerable<T> items)
1147 if (size > 0)
1148 AddAll(items);
1149 else
1151 updatecheck();
1152 addSorted(items, true);
1156 #region add-sorted helpers
1158 //Create a RB tree from x+2^h-1 (x < 2^h, h>=1) nodes taken from a
1159 //singly linked list of red nodes using only the right child refs.
1160 //The x nodes at depth h+1 will be red, the rest black.
1161 //(h is the blackdepth of the resulting tree)
1162 static Node maketreer(ref Node rest, int blackheight, int maxred, int red)
1164 if (blackheight == 1)
1166 Node top = rest;
1168 rest = rest.right;
1169 if (red > 0)
1171 top.right = null;
1172 rest.left = top;
1173 top = rest;
1174 #if BAG
1175 top.size += top.left.size;
1176 #elif MAINTAIN_SIZE
1177 top.size = 1 + red;
1178 #endif
1179 rest = rest.right;
1180 red--;
1183 if (red > 0)
1185 #if BAG
1186 top.size += rest.size;
1187 #endif
1188 top.right = rest;
1189 rest = rest.right;
1190 top.right.right = null;
1192 else
1193 top.right = null;
1195 top.red = false;
1196 return top;
1198 else
1200 maxred >>=1;
1202 int lred = red > maxred ? maxred : red;
1203 Node left = maketreer(ref rest, blackheight - 1, maxred, lred);
1204 Node top = rest;
1206 rest = rest.right;
1207 top.left = left;
1208 top.red = false;
1209 #if MAINTAIN_RANK
1210 top.rank = (short)blackheight;
1211 #endif
1212 top.right = maketreer(ref rest, blackheight - 1, maxred, red - lred);
1213 #if BAG
1214 top.size = top.items + top.left.size + top.right.size;
1215 #elif MAINTAIN_SIZE
1216 top.size = (maxred << 1) - 1 + red;
1217 #endif
1218 return top;
1223 void addSorted(MSG.IEnumerable<T> items, bool safe)
1225 MSG.IEnumerator<T> e = items.GetEnumerator();;
1226 if (size > 0)
1227 throw new ApplicationException("This can't happen");
1229 if (!e.MoveNext())
1230 return;
1232 //To count theCollect
1233 Node head = new Node(), tail = head;
1234 int z = 1;
1235 T lastitem = tail.item = e.Current;
1236 #if BAG
1237 int ec=0;
1238 #endif
1240 while (e.MoveNext())
1242 #if BAG
1243 T thisitem = e.Current;
1244 int comp = comparer.Compare(lastitem, thisitem);
1245 if (comp>0)
1246 throw new ArgumentException("Argument not sorted");
1247 if (comp == 0)
1249 tail.items++;
1250 ec++;
1252 else
1254 tail.size = tail.items;
1255 z++;
1256 tail.right = new Node();
1257 tail = tail.right;
1258 lastitem = tail.item = thisitem;
1259 #if NCP
1260 tail.generation = generation;
1261 #endif
1263 #else
1264 z++;
1265 tail.right = new Node();
1266 tail = tail.right;
1267 tail.item = e.Current;
1268 if (safe)
1270 if (comparer.Compare(lastitem, tail.item) >= 0)
1271 throw new ArgumentException("Argument not sorted");
1273 lastitem = tail.item;
1275 #if NCP
1276 tail.generation = generation;
1277 #endif
1278 #endif
1280 #if BAG
1281 tail.size = tail.items;
1282 #endif
1283 int blackheight = 0, red = z, maxred = 1;
1285 while (maxred <= red)
1287 red -= maxred;
1288 maxred <<= 1;
1289 blackheight++;
1292 root = TreeBag<T>.maketreer(ref head, blackheight, maxred, red);
1293 blackdepth = blackheight;
1294 size = z;
1295 #if BAG
1296 size += ec;
1297 #endif
1298 return;
1301 #endregion
1303 #if BAG
1304 /// <summary></summary>
1305 /// <value>True since this collection has bag semantics.</value>
1306 [Tested]
1307 public bool AllowsDuplicates { [Tested]get { return true; } }
1308 #else
1309 /// <summary></summary>
1310 /// <value>False since this tree has set semantics.</value>
1311 [Tested]
1312 public bool AllowsDuplicates { [Tested]get { return false; } }
1313 #endif
1315 #endregion
1317 #region IEditableCollection<T> Members
1320 /// <summary>
1321 /// The value is symbolic indicating the type of asymptotic complexity
1322 /// in terms of the size of this collection (worst-case or amortized as
1323 /// relevant).
1324 /// </summary>
1325 /// <value>Speed.Log</value>
1326 [Tested]
1327 public Speed ContainsSpeed { [Tested]get { return Speed.Log; } }
1330 [Tested]
1331 int ICollection<T>.GetHashCode() { return unsequencedhashcode(); }
1334 [Tested]
1335 bool ICollection<T>.Equals(ICollection<T> that)
1336 { return unsequencedequals(that); }
1339 /// <summary>
1340 /// Check if this collection contains (an item equivalent to according to the
1341 /// itemhasher) a particular value.
1342 /// </summary>
1343 /// <param name="item">The value to check for.</param>
1344 /// <returns>True if the items is in this collection.</returns>
1345 [Tested]
1346 public bool Contains(T item)
1348 Node next; int comp = 0;
1350 next = root;
1351 while (next != null)
1353 comp = comparer.Compare(next.item, item);
1354 if (comp == 0)
1355 return true;
1357 next = comp < 0 ? right(next) : left(next);
1360 return false;
1364 //Variant for dictionary use
1365 //Will return the actual matching item in the ref argument.
1366 /// <summary>
1367 /// Check if this collection contains an item equivalent according to the
1368 /// itemhasher to a particular value. If so, return in the ref argument (a
1369 /// binary copy of) the actual value found.
1370 /// </summary>
1371 /// <param name="item">The value to look for.</param>
1372 /// <returns>True if the items is in this collection.</returns>
1373 [Tested]
1374 public bool Find(ref T item)
1376 Node next; int comp = 0;
1378 next = root;
1379 while (next != null)
1381 comp = comparer.Compare(next.item, item);
1382 if (comp == 0)
1384 item = next.item;
1385 return true;
1388 next = comp < 0 ? right(next) : left(next);
1391 return false;
1395 /// <summary>
1396 /// Find or add the item to the tree. If the tree does not contain
1397 /// an item equivalent to this item add it, else return the exisiting
1398 /// one in the ref argument.
1400 /// </summary>
1401 /// <param name="item"></param>
1402 /// <returns>True if item was found</returns>
1403 [Tested]
1404 public bool FindOrAdd(ref T item)
1406 updatecheck();
1407 bool wasfound;
1409 //Note: blackdepth of the tree is set inside addIterative
1410 if (addIterative(item, ref item, false, out wasfound))
1412 size++;
1413 #if MAINTAIN_EXTREMA
1414 if (Compare(item, min) < 0)
1415 min = item;
1416 else if (Compare(item, max) > 0)
1417 max = item;
1418 #endif
1419 #if MAINTAIN_HEIGHT
1420 depth = root.height;
1421 #endif
1422 return wasfound;
1424 else
1425 return true;
1430 //For dictionary use.
1431 //If found, the matching entry will be updated with the new item.
1432 /// <summary>
1433 /// Check if this collection contains an item equivalent according to the
1434 /// itemhasher to a particular value. If so, update the item in the collection
1435 /// to with a binary copy of the supplied value. If the collection has bag semantics,
1436 /// this updates all equivalent copies in
1437 /// the collection.
1438 /// </summary>
1439 /// <param name="item">Value to update.</param>
1440 /// <returns>True if the item was found and hence updated.</returns>
1441 [Tested]
1442 public bool Update(T item)
1444 updatecheck();
1445 #if NCP
1446 stackcheck();
1448 int level = 0;
1449 #endif
1450 Node cursor = root;
1451 int comp = 0;
1453 while (cursor != null)
1455 comp = comparer.Compare(cursor.item, item);
1456 if (comp == 0)
1458 #if NCP
1459 Node.CopyNode(ref cursor, maxsnapid, generation);
1460 #endif
1461 cursor.item = item;
1462 #if NCP
1463 while (level > 0)
1465 Node child = cursor;
1467 cursor = path[--level];
1468 path[level] = null;
1469 #if NCP
1470 Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
1471 #else
1472 if (Node.CopyNode(maxsnapid, ref cursor, generation))
1474 if (dirs[level] > 0)
1475 cursor.left = child;
1476 else
1477 cursor.right = child;
1479 #endif
1482 root = cursor;
1483 #endif
1484 return true;
1486 #if NCP
1487 dirs[level] = comp;
1488 path[level++] = cursor;
1489 #endif
1490 cursor = comp < 0 ? cursor.right : cursor.left;
1493 return false;
1497 /// <summary>
1498 /// Check if this collection contains an item equivalent according to the
1499 /// itemhasher to a particular value. If so, update the item in the collection
1500 /// to with a binary copy of the supplied value; else add the value to the collection.
1502 /// <p>NOTE: the bag implementation is currently wrong!</p>
1503 /// </summary>
1504 /// <param name="item">Value to add or update.</param>
1505 /// <returns>True if the item was found and updated (hence not added).</returns>
1506 [Tested]
1507 public bool UpdateOrAdd(T item)
1509 updatecheck();
1510 bool wasfound;
1512 //Note: blackdepth of the tree is set inside addIterative
1513 if (addIterative(item, ref item, true, out wasfound))
1515 size++;
1516 #if MAINTAIN_EXTREMA
1517 if (Compare(item, min) < 0)
1518 min = item;
1519 else if (Compare(item, max) > 0)
1520 max = item;
1521 #endif
1522 #if MAINTAIN_HEIGHT
1523 depth = root.height;
1524 #endif
1525 return wasfound;
1527 else
1528 return true;
1532 /// <summary>
1533 /// Remove a particular item from this collection. If the collection has bag
1534 /// semantics only one copy equivalent to the supplied item is removed.
1535 /// </summary>
1536 /// <param name="item">The value to remove.</param>
1537 /// <returns>True if the item was found (and removed).</returns>
1538 [Tested]
1539 public bool Remove(T item)
1541 updatecheck();
1542 if (root == null)
1543 return false;
1545 return removeIterative(ref item, false);
1548 /// <summary>
1549 /// Remove a particular item from this collection if found. If the collection
1550 /// has bag semantics only one copy equivalent to the supplied item is removed,
1551 /// which one is implementation dependent.
1552 /// If an item was removed, report a binary copy of the actual item removed in
1553 /// the argument.
1554 /// </summary>
1555 /// <param name="item">The value to remove on input.</param>
1556 /// <returns>True if the item was found (and removed).</returns>
1557 [Tested]
1558 public bool RemoveWithReturn(ref T item)
1560 updatecheck();
1561 if (root == null)
1562 return false;
1564 return removeIterative(ref item, false);
1568 private bool removeIterative(ref T item, bool all)
1570 //Stage 1: find item
1571 stackcheck();
1573 int level = 0, comp;
1574 Node cursor = root;
1576 while (true)
1578 comp = comparer.Compare(cursor.item, item);
1579 if (comp == 0)
1581 item = cursor.item;
1582 #if BAG
1583 if (!all && cursor.items > 1)
1585 #if NCP
1586 Node.CopyNode(ref cursor, maxsnapid, generation);
1587 #endif
1588 cursor.items--;
1589 cursor.size--;
1590 while (level-- > 0)
1592 Node kid = cursor;
1594 cursor = path[level];
1595 #if NCP
1596 Node.update(ref cursor, dirs[level] > 0, kid,maxsnapid,generation);
1597 #endif
1598 cursor.size--;
1599 path[level] = null;
1601 size--;
1602 return true;
1604 #endif
1605 break;
1608 Node child = comp > 0 ? cursor.left : cursor.right;
1610 if (child == null)
1611 return false;
1613 dirs[level] = comp;
1614 path[level++] = cursor;
1615 cursor = child;
1618 return removeIterativePhase2(cursor, level);
1622 private bool removeIterativePhase2(Node cursor, int level)
1624 if (size == 1)
1626 clear();
1627 return true;
1630 #if MAINTAIN_EXTREMA
1631 if (Compare(cursor.item, min) == 0)
1632 min = cursor.right != null ? cursor.right.item : path[level - 1].item;
1633 else if (Compare(cursor.item, max) == 0)
1634 max = cursor.left != null ? cursor.left.item : path[level - 1].item;
1635 #endif
1636 #if BAG
1637 int removedcount = cursor.items;
1638 size -= removedcount;
1639 #else
1640 //We are certain to remove one node:
1641 size--;
1642 #endif
1643 //Stage 2: if item's node has no null child, find predecessor
1644 int level_of_item = level;
1646 if (cursor.left != null && cursor.right != null)
1648 dirs[level] = 1;
1649 path[level++] = cursor;
1650 cursor = cursor.left;
1651 while (cursor.right != null)
1653 dirs[level] = -1;
1654 path[level++] = cursor;
1655 cursor = cursor.right;
1657 #if NCP
1658 Node.CopyNode(ref path[level_of_item], maxsnapid, generation);
1659 #endif
1660 path[level_of_item].item = cursor.item;
1661 #if BAG
1662 path[level_of_item].items = cursor.items;
1663 #endif
1666 //Stage 3: splice out node to be removed
1667 Node newchild = cursor.right == null ? cursor.left : cursor.right;
1668 bool demote_or_rotate = newchild == null && !cursor.red;
1670 //assert newchild.red
1671 if (newchild != null)
1673 newchild.red = false;
1676 if (level == 0)
1678 root = newchild;
1679 #if MAINTAIN_HEIGHT
1680 depth = 0;
1681 #endif
1682 return true;
1685 level--;
1686 cursor = path[level];
1687 path[level] = null;
1689 int comp = dirs[level];
1690 Node childsibling;
1691 #if NCP
1692 Node.update(ref cursor, comp > 0, newchild, maxsnapid, generation);
1693 #else
1694 if (comp > 0)
1695 cursor.left = newchild;
1696 else
1697 cursor.right = newchild;
1698 #endif
1699 childsibling = comp > 0 ? cursor.right : cursor.left;
1700 #if BAG
1701 cursor.size -= removedcount;
1702 #elif MAINTAIN_SIZE
1703 cursor.size--;
1704 #endif
1705 #if MAINTAIN_HEIGHT
1706 fixheight(cursor);
1707 #endif
1709 //Stage 4: demote till we must rotate
1710 Node farnephew = null, nearnephew = null;
1712 while (demote_or_rotate)
1714 if (childsibling.red)
1715 break; //rotate 2+?
1717 farnephew = comp > 0 ? childsibling.right : childsibling.left;
1718 if (farnephew != null && farnephew.red)
1719 break; //rotate 1b
1721 nearnephew = comp > 0 ? childsibling.left : childsibling.right;
1722 if (nearnephew != null && nearnephew.red)
1723 break; //rotate 1c
1725 //demote cursor
1726 childsibling.red = true;
1727 #if MAINTAIN_RANK
1728 cursor.rank--;
1729 #endif
1730 if (level == 0)
1732 cursor.red = false;
1733 blackdepth--;
1734 #if MAINTAIN_HEIGHT
1735 depth = root.height;
1736 #endif
1737 #if NCP
1738 root = cursor;
1739 #endif
1740 return true;
1742 else if (cursor.red)
1744 cursor.red = false;
1745 demote_or_rotate = false;
1746 break; //No rotation
1748 else
1750 Node child = cursor;
1752 cursor = path[--level];
1753 path[level] = null;
1754 comp = dirs[level];
1755 childsibling = comp > 0 ? cursor.right : cursor.left;
1756 #if NCP
1757 Node.update(ref cursor, comp > 0, child, maxsnapid, generation);
1758 #endif
1759 #if BAG
1760 cursor.size -= removedcount;
1761 #elif MAINTAIN_SIZE
1762 cursor.size--;
1763 #endif
1764 #if MAINTAIN_HEIGHT
1765 fixheight(cursor);
1766 #endif
1770 //Stage 5: rotate
1771 if (demote_or_rotate)
1773 //At start:
1774 //parent = cursor (temporary for swapping nodes)
1775 //childsibling is the sibling of the updated child (x)
1776 //cursor is always the top of the subtree
1777 Node parent = cursor;
1779 if (childsibling.red)
1780 {//Case 2 and perhaps more.
1781 //The y.rank == px.rank >= x.rank+2 >=2 so both nephews are != null
1782 //(and black). The grandnephews are children of nearnephew
1783 Node neargrandnephew, fargrandnephew;
1785 if (comp > 0)
1787 nearnephew = childsibling.left;
1788 farnephew = childsibling.right;
1789 neargrandnephew = nearnephew.left;
1790 fargrandnephew = nearnephew.right;
1792 else
1794 nearnephew = childsibling.right;
1795 farnephew = childsibling.left;
1796 neargrandnephew = nearnephew.right;
1797 fargrandnephew = nearnephew.left;
1800 if (fargrandnephew != null && fargrandnephew.red)
1801 {//Case 2+1b
1802 #if NCP
1803 Node.CopyNode(ref nearnephew, maxsnapid, generation);
1805 //The end result of this will always be e copy of parent
1806 Node.update(ref parent, comp < 0, neargrandnephew, maxsnapid, generation);
1807 Node.update(ref childsibling, comp > 0, nearnephew, maxsnapid, generation);
1808 #endif
1809 if (comp > 0)
1811 nearnephew.left = parent;
1812 parent.right = neargrandnephew;
1814 else
1816 nearnephew.right = parent;
1817 parent.left = neargrandnephew;
1820 cursor = childsibling;
1821 childsibling.red = false;
1822 nearnephew.red = true;
1823 fargrandnephew.red = false;
1824 #if MAINTAIN_RANK
1825 nearnephew.rank++;
1826 parent.rank--;
1827 #endif
1828 #if BAG
1829 cursor.size = parent.size;
1830 nearnephew.size = cursor.size - cursor.items - farnephew.size;
1831 parent.size = nearnephew.size - nearnephew.items - fargrandnephew.size;
1832 #elif MAINTAIN_SIZE
1833 cursor.size = parent.size;
1834 nearnephew.size = cursor.size - 1 - farnephew.size;
1835 parent.size = nearnephew.size - 1 - fargrandnephew.size;
1836 #endif
1837 #if MAINTAIN_HEIGHT
1838 fixheight(parent);
1839 fixheight(nearnephew);
1840 fixheight(cursor);
1841 #endif
1843 else if (neargrandnephew != null && neargrandnephew.red)
1844 {//Case 2+1c
1845 #if NCP
1846 Node.CopyNode(ref neargrandnephew, maxsnapid, generation);
1847 #endif
1848 if (comp > 0)
1850 #if NCP
1851 Node.update(ref childsibling, true, neargrandnephew, maxsnapid, generation);
1852 Node.update(ref nearnephew, true, neargrandnephew.right, maxsnapid, generation);
1853 Node.update(ref parent, false, neargrandnephew.left, maxsnapid, generation);
1854 #else
1855 childsibling.left = neargrandnephew;
1856 nearnephew.left = neargrandnephew.right;
1857 parent.right = neargrandnephew.left;
1858 #endif
1859 neargrandnephew.left = parent;
1860 neargrandnephew.right = nearnephew;
1862 else
1864 #if NCP
1865 Node.update(ref childsibling, false, neargrandnephew, maxsnapid, generation);
1866 Node.update(ref nearnephew, false, neargrandnephew.left, maxsnapid, generation);
1867 Node.update(ref parent, true, neargrandnephew.right, maxsnapid, generation);
1868 #else
1869 childsibling.right = neargrandnephew;
1870 nearnephew.right = neargrandnephew.left;
1871 parent.left = neargrandnephew.right;
1872 #endif
1873 neargrandnephew.right = parent;
1874 neargrandnephew.left = nearnephew;
1877 cursor = childsibling;
1878 childsibling.red = false;
1879 #if MAINTAIN_RANK
1880 neargrandnephew.rank++;
1881 parent.rank--;
1882 #endif
1883 #if BAG
1884 cursor.size = parent.size;
1885 parent.size = parent.items + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
1886 nearnephew.size = nearnephew.items + (nearnephew.left == null ? 0 : nearnephew.left.size) + (nearnephew.right == null ? 0 : nearnephew.right.size);
1887 neargrandnephew.size = neargrandnephew.items + parent.size + nearnephew.size;
1888 #elif MAINTAIN_SIZE
1889 cursor.size = parent.size;
1890 parent.size = 1 + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
1891 nearnephew.size = 1 + (nearnephew.left == null ? 0 : nearnephew.left.size) + (nearnephew.right == null ? 0 : nearnephew.right.size);
1892 neargrandnephew.size = 1 + parent.size + nearnephew.size;
1893 #endif
1894 #if MAINTAIN_HEIGHT
1895 fixheight(parent);
1896 fixheight(nearnephew);
1897 fixheight(neargrandnephew);
1898 fixheight(cursor);
1899 #endif
1901 else
1902 {//Case 2 only
1903 #if NCP
1904 Node.update(ref parent, comp < 0, nearnephew, maxsnapid, generation);
1905 Node.update(ref childsibling, comp > 0, parent, maxsnapid, generation);
1906 #else
1907 if (comp > 0)
1909 childsibling.left = parent;
1910 parent.right = nearnephew;
1912 else
1914 childsibling.right = parent;
1915 parent.left = nearnephew;
1917 #endif
1918 cursor = childsibling;
1919 childsibling.red = false;
1920 nearnephew.red = true;
1921 #if MAINTAIN_RANK
1922 parent.rank--;
1923 #endif
1924 #if BAG
1925 cursor.size = parent.size;
1926 parent.size -= farnephew.size + cursor.items;
1927 #elif MAINTAIN_SIZE
1928 cursor.size = parent.size;
1929 parent.size -= farnephew.size + 1;
1930 #endif
1931 #if MAINTAIN_HEIGHT
1932 fixheight(parent);
1933 fixheight(cursor);
1934 #endif
1937 else if (farnephew != null && farnephew.red)
1938 {//Case 1b
1939 nearnephew = comp > 0 ? childsibling.left : childsibling.right;
1940 #if NCP
1941 Node.update(ref parent, comp < 0, nearnephew, maxsnapid, generation);
1942 Node.CopyNode(ref childsibling, maxsnapid, generation);
1943 if (comp > 0)
1945 childsibling.left = parent;
1946 childsibling.right = farnephew;
1948 else
1950 childsibling.right = parent;
1951 childsibling.left = farnephew;
1953 #else
1954 if (comp > 0)
1956 childsibling.left = parent;
1957 parent.right = nearnephew;
1959 else
1961 childsibling.right = parent;
1962 parent.left = nearnephew;
1964 #endif
1965 cursor = childsibling;
1966 cursor.red = parent.red;
1967 parent.red = false;
1968 farnephew.red = false;
1970 #if MAINTAIN_RANK
1971 childsibling.rank++;
1972 parent.rank--;
1973 #endif
1974 #if BAG
1975 cursor.size = parent.size;
1976 parent.size -= farnephew.size + cursor.items;
1977 #elif MAINTAIN_SIZE
1978 cursor.size = parent.size;
1979 parent.size -= farnephew.size + 1;
1980 #endif
1981 #if MAINTAIN_HEIGHT
1982 fixheight(parent);
1983 fixheight(cursor);
1984 #endif
1986 else if (nearnephew != null && nearnephew.red)
1987 {//Case 1c
1988 #if NCP
1989 Node.CopyNode(ref nearnephew, maxsnapid, generation);
1990 #endif
1991 if (comp > 0)
1993 #if NCP
1994 Node.update(ref childsibling, true, nearnephew.right, maxsnapid, generation);
1995 Node.update(ref parent, false, nearnephew.left, maxsnapid, generation);
1996 #else
1997 childsibling.left = nearnephew.right;
1998 parent.right = nearnephew.left;
1999 #endif
2000 nearnephew.left = parent;
2001 nearnephew.right = childsibling;
2003 else
2005 #if NCP
2006 Node.update(ref childsibling, false, nearnephew.left, maxsnapid, generation);
2007 Node.update(ref parent, true, nearnephew.right, maxsnapid, generation);
2008 #else
2009 childsibling.right = nearnephew.left;
2010 parent.left = nearnephew.right;
2011 #endif
2012 nearnephew.right = parent;
2013 nearnephew.left = childsibling;
2016 cursor = nearnephew;
2017 cursor.red = parent.red;
2018 parent.red = false;
2019 #if MAINTAIN_RANK
2020 nearnephew.rank++;
2021 parent.rank--;
2022 #endif
2023 #if BAG
2024 cursor.size = parent.size;
2025 parent.size = parent.items + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
2026 childsibling.size = childsibling.items + (childsibling.left == null ? 0 : childsibling.left.size) + (childsibling.right == null ? 0 : childsibling.right.size);
2027 #elif MAINTAIN_SIZE
2028 cursor.size = parent.size;
2029 parent.size = 1 + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
2030 childsibling.size = 1 + (childsibling.left == null ? 0 : childsibling.left.size) + (childsibling.right == null ? 0 : childsibling.right.size);
2031 #endif
2032 #if MAINTAIN_HEIGHT
2033 fixheight(parent);
2034 fixheight(childsibling);
2035 fixheight(cursor);
2036 #endif
2038 else
2039 {//Case 1a can't happen
2040 throw new Exception("Case 1a can't happen here");
2043 //Resplice cursor:
2044 if (level == 0)
2046 root = cursor;
2048 else
2050 Node swap = cursor;
2052 cursor = path[--level];
2053 path[level] = null;
2054 #if NCP
2055 Node.update(ref cursor, dirs[level] > 0, swap, maxsnapid, generation);
2056 #else
2058 if (dirs[level] > 0)
2059 cursor.left = swap;
2060 else
2061 cursor.right = swap;
2062 #endif
2063 #if BAG
2064 cursor.size -= removedcount;
2065 #elif MAINTAIN_SIZE
2066 cursor.size--;
2067 #endif
2068 #if MAINTAIN_HEIGHT
2069 fixheight(cursor);
2070 #endif
2074 //Stage 6: fixup to the root
2075 while (level > 0)
2077 Node child = cursor;
2079 cursor = path[--level];
2080 path[level] = null;
2081 #if NCP
2082 if (child != (dirs[level] > 0 ? cursor.left : cursor.right))
2083 Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
2084 #endif
2085 #if BAG
2086 cursor.size -= removedcount;
2087 #elif MAINTAIN_SIZE
2088 cursor.size--;
2089 #endif
2090 #if MAINTAIN_HEIGHT
2091 fixheight(cursor);
2092 #endif
2095 #if MAINTAIN_HEIGHT
2096 depth = root.height;
2097 #endif
2098 #if NCP
2099 root = cursor;
2100 #endif
2101 return true;
2105 /// <summary>
2106 /// Remove all items from this collection.
2107 /// </summary>
2108 [Tested]
2109 public void Clear()
2111 updatecheck();
2112 clear();
2116 private void clear()
2118 size = 0;
2119 root = null;
2120 blackdepth = 0;
2121 #if MAINTAIN_HEIGHT
2122 depth = 0;
2123 #endif
2127 /// <summary>
2128 /// Remove all items in another collection from this one. If this collection
2129 /// has bag semantics, take multiplicities into account.
2130 /// </summary>
2131 /// <param name="items">The items to remove.</param>
2132 [Tested]
2133 public void RemoveAll(MSG.IEnumerable<T> items)
2135 updatecheck();
2137 T jtem;
2139 foreach (T item in items)
2141 if (root == null)
2142 break;
2144 jtem = item;
2145 removeIterative(ref jtem, false);
2150 /// <summary>
2151 /// Remove all items not in some other collection from this one. If this collection
2152 /// has bag semantics, take multiplicities into account.
2153 /// </summary>
2154 /// <param name="items">The items to retain.</param>
2155 [Tested]
2156 public void RetainAll(MSG.IEnumerable<T> items)
2158 updatecheck();
2160 //A much more efficient version is possible if items is sorted like this.
2161 //Well, it is unclear how efficient it would be.
2162 //We could use a marking method!?
2163 TreeBag<T> t = (TreeBag<T>)MemberwiseClone();
2165 t.Clear();
2166 foreach (T item in items)
2167 if (ContainsCount(item) > t.ContainsCount(item))
2168 t.Add(item);
2170 root = t.root;
2171 size = t.size;
2172 blackdepth = t.blackdepth;
2173 #if MAINTAIN_HEIGHT
2174 depth = t.depth;
2175 #endif
2179 /// <summary>
2180 /// Check if this collection contains all the values in another collection.
2181 /// If this collection has bag semantics (<code>NoDuplicates==false</code>)
2182 /// the check is made with respect to multiplicities, else multiplicities
2183 /// are not taken into account.
2184 /// </summary>
2185 /// <param name="items">The </param>
2186 /// <returns>True if all values in <code>items</code>is in this collection.</returns>
2187 [Tested]
2188 public bool ContainsAll(MSG.IEnumerable<T> items)
2190 //This is worst-case O(m*logn)
2191 foreach (T item in items)
2192 if (!Contains(item)) return false;
2194 return true;
2198 //Higher order:
2199 /// <summary>
2200 /// Create a new indexed sorted collection consisting of the items of this
2201 /// indexed sorted collection satisfying a certain predicate.
2202 /// </summary>
2203 /// <param name="filter">The filter delegate defining the predicate.</param>
2204 /// <returns>The new indexed sorted collection.</returns>
2205 [Tested]
2206 public IIndexedSorted<T> FindAll(Filter<T> filter)
2208 TreeBag<T> res = new TreeBag<T>(comparer);
2209 MSG.IEnumerator<T> e = GetEnumerator();
2210 Node head = null, tail = null;
2211 int z = 0;
2212 #if BAG
2213 int ec = 0;
2214 #endif
2215 while (e.MoveNext())
2217 T thisitem = e.Current;
2218 #if BAG
2219 //We could document that filter will only be called
2220 //once on each unique item. That might even be good for the user!
2221 if (tail!=null && comparer.Compare(thisitem, tail.item) == 0)
2223 tail.items++;
2224 ec++;
2225 continue;
2227 #endif
2228 if (filter(thisitem))
2230 if (head == null)
2232 head = tail = new Node();
2234 else
2236 #if BAG
2237 tail.size = tail.items;
2238 #endif
2239 tail.right = new Node();
2240 tail = tail.right;
2243 tail.item = thisitem;
2244 z++;
2247 #if BAG
2248 if (tail!=null)
2249 tail.size = tail.items;
2250 #endif
2252 if (z == 0)
2253 return res;
2255 int blackheight = 0, red = z, maxred = 1;
2257 while (maxred <= red)
2259 red -= maxred;
2260 maxred <<= 1;
2261 blackheight++;
2264 res.root = TreeBag<T>.maketreer(ref head, blackheight, maxred, red);
2265 res.blackdepth = blackheight;
2266 res.size = z;
2267 #if BAG
2268 res.size += ec;
2269 #endif
2270 return res;
2274 /// <summary>
2275 /// Create a new indexed sorted collection consisting of the results of
2276 /// mapping all items of this list.
2277 /// <exception cref="ArgumentException"/> if the map is not increasing over
2278 /// the items of this collection (with respect to the two given comparison
2279 /// relations).
2280 /// </summary>
2281 /// <param name="mapper">The delegate definging the map.</param>
2282 /// <param name="c">The comparion relation to use for the result.</param>
2283 /// <returns>The new sorted collection.</returns>
2284 [Tested]
2285 public IIndexedSorted<V> Map<V>(Mapper<T,V> mapper, IComparer<V> c)
2287 TreeBag<V> res = new TreeBag<V>(c);
2289 if (size == 0)
2290 return res;
2292 MSG.IEnumerator<T> e = GetEnumerator();
2293 TreeBag<V>.Node head = null, tail = null;
2294 V oldv = default(V);
2295 int z = 0;
2296 #if BAG
2297 T lastitem = default(T);
2298 #endif
2299 while (e.MoveNext())
2301 T thisitem = e.Current;
2302 #if BAG
2303 //We could document that mapper will only be called
2304 //once on each unique item. That might even be good for the user!
2305 if (tail != null && comparer.Compare(thisitem, lastitem) == 0)
2307 tail.items++;
2308 continue;
2310 #endif
2311 V newv = mapper(thisitem);
2313 if (head == null)
2315 head = tail = new TreeBag<V>.Node();
2316 z++;
2318 else
2320 int comp = c.Compare(oldv, newv);
2321 #if BAG
2322 if (comp == 0)
2324 tail.items++;
2325 continue;
2327 if (comp > 0)
2328 #else
2329 if (comp >= 0)
2330 #endif
2331 throw new ArgumentException("mapper not monotonic");
2332 #if BAG
2333 tail.size = tail.items;
2334 #endif
2335 tail.right = new TreeBag<V>.Node();
2336 tail = tail.right;
2337 z++;
2339 #if BAG
2340 lastitem = thisitem;
2341 #endif
2342 tail.item = oldv = newv;
2345 #if BAG
2346 tail.size = tail.items;
2347 #endif
2349 int blackheight = 0, red = z, maxred = 1;
2351 while (maxred <= red)
2353 red -= maxred;
2354 maxred <<= 1;
2355 blackheight++;
2358 res.root = TreeBag<V>.maketreer(ref head, blackheight, maxred, red);
2359 res.blackdepth = blackheight;
2360 res.size = size;
2361 return res;
2365 //below is the bag utility stuff
2366 /// <summary>
2367 /// Count the number of items of the collection equal to a particular value.
2368 /// Returns 0 if and only if the value is not in the collection.
2369 /// </summary>
2370 /// <param name="item">The value to count.</param>
2371 /// <returns>The number of copies found.</returns>
2372 [Tested]
2373 public int ContainsCount(T item)
2375 #if BAG
2376 Node next; int comp = 0;
2378 next = root;
2379 while (next != null)
2381 comp = comparer.Compare(next.item, item);
2382 if (comp == 0)
2383 return next.items;
2385 next = comp < 0 ? right(next) : left(next);
2388 return 0;
2389 #else
2390 //Since we are strictly NoDuplicates we just do
2391 return Contains(item) ? 1 : 0;
2392 #endif
2396 /// <summary>
2397 /// Remove all items equivalent to a given value.
2398 /// </summary>
2399 /// <param name="item">The value to remove.</param>
2400 [Tested]
2401 public void RemoveAllCopies(T item)
2403 #if BAG
2404 updatecheck();
2405 removeIterative(ref item, true);
2406 #else
2408 Remove(item);
2409 #endif
2413 #endregion
2415 #region IIndexed<T> Members
2417 private Node findNode(int i)
2419 #if NCP
2420 if (isSnapShot)
2421 throw new NotSupportedException("Indexing not supported for snapshots");
2422 #endif
2423 #if MAINTAIN_SIZE
2424 Node next = root;
2426 if (i >= 0 && i < size)
2427 while (true)
2429 int j = next.left == null ? 0 : next.left.size;
2431 if (i > j)
2433 #if BAG
2434 i -= j + next.items;
2435 if (i<0)
2436 return next;
2437 #else
2438 i -= j + 1;
2439 #endif
2440 next = next.right;
2442 else if (i == j)
2443 return next;
2444 else
2445 next = next.left;
2448 throw new IndexOutOfRangeException();
2449 #else
2450 throw new NotSupportedException();
2451 #endif
2455 /// <summary>
2456 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
2457 /// &gt;= the size of the collection.
2458 /// </summary>
2459 /// <value>The i'th item of this list.</value>
2460 /// <param name="i">the index to lookup</param>
2461 [Tested]
2462 public T this[int i] { [Tested] get { return findNode(i).item; } }
2465 /// <summary>
2466 /// Searches for an item in the list going forwrds from the start.
2467 /// </summary>
2468 /// <param name="item">Item to search for.</param>
2469 /// <returns>Index of item from start.</returns>
2470 [Tested]
2471 public int IndexOf(T item)
2473 int upper;
2475 return indexOf(item, out upper);
2479 private int indexOf(T item, out int upper)
2481 #if NCP
2482 if (isSnapShot)
2483 throw new NotSupportedException("Indexing not supported for snapshots");
2484 #endif
2485 #if MAINTAIN_SIZE
2486 int ind = 0; Node next = root;
2488 while (next != null)
2490 int comp = comparer.Compare(item, next.item);
2492 if (comp < 0)
2493 next = next.left;
2494 else
2496 int leftcnt = next.left == null ? 0 : next.left.size;
2498 if (comp == 0)
2500 #if BAG
2501 upper = ind + leftcnt + next.items - 1;
2502 return ind + leftcnt;
2503 #else
2504 return upper = ind + leftcnt;
2505 #endif
2507 else
2509 #if BAG
2510 ind = ind + next.items + leftcnt;
2511 #else
2512 ind = ind + 1 + leftcnt;
2513 #endif
2514 next = next.right;
2518 #endif
2519 upper = -1;
2520 return -1;
2524 /// <summary>
2525 /// Searches for an item in the list going backwords from the end.
2526 /// </summary>
2527 /// <param name="item">Item to search for.</param>
2528 /// <returns>Index of of item from the end.</returns>
2529 [Tested]
2530 public int LastIndexOf(T item)
2532 #if BAG
2533 int res;
2534 indexOf(item, out res);
2535 return res;
2536 #else
2537 //We have NoDuplicates==true for the set
2538 return IndexOf(item);
2539 #endif
2543 /// <summary>
2544 /// Remove the item at a specific position of the list.
2545 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
2546 /// &gt;= the size of the collection.
2547 /// </summary>
2548 /// <param name="i">The index of the item to remove.</param>
2549 /// <returns>The removed item.</returns>
2550 [Tested]
2551 public T RemoveAt(int i)
2553 updatecheck();
2554 #if MAINTAIN_SIZE
2555 if (i < 0 || i >= size)
2556 throw new IndexOutOfRangeException("Index out of range for sequenced collection");
2558 //We must follow the pattern of removeIterative()
2559 while (dirs.Length < 2 * blackdepth)
2561 dirs = new int[2 * dirs.Length];
2562 path = new Node[2 * dirs.Length];
2565 int level = 0;
2566 Node cursor = root;
2568 while (true)
2570 int j = cursor.left == null ? 0 : cursor.left.size;
2572 if (i > j)
2574 #if BAG
2575 i -= j + cursor.items;
2576 if (i<0)
2577 break;
2578 #else
2579 i -= j + 1;
2580 #endif
2581 dirs[level] = -1;
2582 path[level++] = cursor;
2583 cursor = cursor.right;
2585 else if (i == j)
2586 break;
2587 else
2589 dirs[level] = 1;
2590 path[level++] = cursor;
2591 cursor = cursor.left;
2595 T retval = cursor.item;
2597 #if BAG
2598 if (cursor.items>1)
2600 resplicebag(level, cursor);
2601 size--;
2602 return retval;
2604 #endif
2605 removeIterativePhase2(cursor, level);
2606 return retval;
2607 #else
2608 throw new NotSupportedException();
2609 #endif
2612 #if BAG
2613 private void resplicebag(int level, Node cursor)
2615 #if NCP
2616 Node.CopyNode(ref cursor, maxsnapid, generation);
2617 #endif
2618 cursor.items--;
2619 cursor.size--;
2620 while (level-- > 0)
2622 Node kid = cursor;
2624 cursor = path[level];
2625 #if NCP
2626 Node.update(ref cursor, dirs[level] > 0, kid, maxsnapid, generation);
2627 #endif
2628 cursor.size--;
2629 path[level] = null;
2632 #endif
2633 /// <summary>
2634 /// Remove all items in an index interval.
2635 /// <exception cref="IndexOutOfRangeException"/>???.
2636 /// </summary>
2637 /// <param name="start">The index of the first item to remove.</param>
2638 /// <param name="count">The number of items to remove.</param>
2639 [Tested]
2640 public void RemoveInterval(int start, int count)
2642 if (start < 0 || count < 0)
2643 throw new ArgumentOutOfRangeException();
2645 if (start + count > this.size)
2646 throw new ArgumentException();
2648 updatecheck();
2650 //This is terrible for large count. We should split the tree at
2651 //the endpoints of the range and fuse the parts!
2652 //We really need good internal destructive split and catenate functions!
2653 for (int i = 0; i < count; i++)
2654 RemoveAt(start);
2658 /// <summary>
2659 /// <exception cref="IndexOutOfRangeException"/>.
2660 /// </summary>
2661 /// <value>The directed collection of items in a specific index interval.</value>
2662 /// <param name="start">The low index of the interval (inclusive).</param>
2663 /// <param name="end">The high index of the interval (exclusive).</param>
2664 [Tested]
2665 public IDirectedCollectionValue<T> this[int start, int end]
2667 [Tested]
2670 checkRange(start, end - start);
2671 return new Interval(this, start, end - start, true);
2675 #region Interval nested class
2676 class Interval: CollectionValueBase<T>, IDirectedCollectionValue<T>
2678 int start, length, stamp;
2680 bool forwards;
2682 TreeBag<T> tree;
2685 internal Interval(TreeBag<T> tree, int start, int count, bool forwards)
2687 #if NCP
2688 if (tree.isSnapShot)
2689 throw new NotSupportedException("Indexing not supported for snapshots");
2690 #endif
2691 this.start = start; this.length = count;this.forwards = forwards;
2692 this.tree = tree; this.stamp = tree.stamp;
2696 [Tested]
2697 public override int Count { [Tested]get { return length; } }
2700 public override Speed CountSpeed { get { return Speed.Constant; } }
2702 [Tested]
2703 public override MSG.IEnumerator<T> GetEnumerator()
2705 #if MAINTAIN_SIZE
2706 tree.modifycheck(stamp);
2707 #if BAG
2708 int togo;
2709 #endif
2710 Node cursor = tree.root;
2711 Node[] path = new Node[2 * tree.blackdepth];
2712 int level = 0, totaltogo = length;
2714 if (totaltogo == 0)
2715 yield break;
2717 if (forwards)
2719 int i = start;
2721 while (true)
2723 int j = cursor.left == null ? 0 : cursor.left.size;
2725 if (i > j)
2727 #if BAG
2728 i -= j + cursor.items;
2729 if (i < 0)
2731 togo = cursor.items + i;
2732 break;
2734 #else
2735 i -= j + 1;
2736 #endif
2737 cursor = cursor.right;
2739 else if (i == j)
2741 #if BAG
2742 togo = cursor.items;
2743 #endif
2744 break;
2746 else
2748 path[level++] = cursor;
2749 cursor = cursor.left;
2753 T current = cursor.item;
2755 while (totaltogo-- > 0)
2757 yield return current;
2758 tree.modifycheck(stamp);
2759 #if BAG
2760 if (--togo > 0)
2761 continue;
2762 #endif
2763 if (cursor.right != null)
2765 path[level] = cursor = cursor.right;
2766 while (cursor.left != null)
2767 path[++level] = cursor = cursor.left;
2769 else if (level == 0)
2770 yield break;
2771 else
2772 cursor = path[--level];
2774 current = cursor.item;
2775 #if BAG
2776 togo = cursor.items;
2777 #endif
2780 else
2782 int i = start + length - 1;
2784 while (true)
2786 int j = cursor.left == null ? 0 : cursor.left.size;
2788 if (i > j)
2790 #if BAG
2791 if (i - j < cursor.items)
2793 togo = i - j + 1;
2794 break;
2796 i -= j + cursor.items;
2797 #else
2798 i -= j + 1;
2799 #endif
2800 path[level++] = cursor;
2801 cursor = cursor.right;
2803 else if (i == j)
2805 #if BAG
2806 togo = 1;
2807 #endif
2808 break;
2810 else
2812 cursor = cursor.left;
2816 T current = cursor.item;
2818 while (totaltogo-- > 0)
2820 yield return current;
2821 tree.modifycheck(stamp);
2822 #if BAG
2823 if (--togo > 0)
2824 continue;
2825 #endif
2826 if (cursor.left != null)
2828 path[level] = cursor = cursor.left;
2829 while (cursor.right != null)
2830 path[++level] = cursor = cursor.right;
2832 else if (level == 0)
2833 yield break;
2834 else
2835 cursor = path[--level];
2837 current = cursor.item;
2838 #if BAG
2839 togo = cursor.items;
2840 #endif
2844 #else
2845 throw new NotSupportedException();
2846 #endif
2850 [Tested]
2851 public IDirectedCollectionValue<T> Backwards()
2852 { return new Interval(tree, start, length, !forwards); }
2855 [Tested]
2856 IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards()
2857 { return Backwards(); }
2860 [Tested]
2861 public EnumerationDirection Direction
2863 [Tested]
2866 return forwards ? EnumerationDirection.Forwards : EnumerationDirection.Backwards;
2870 #endregion
2872 /// <summary>
2873 /// Create a collection containing the same items as this collection, but
2874 /// whose enumerator will enumerate the items backwards. The new collection
2875 /// will become invalid if the original is modified. Method typicaly used as in
2876 /// <code>foreach (T x in coll.Backwards()) {...}</code>
2877 /// </summary>
2878 /// <returns>The backwards collection.</returns>
2879 [Tested]
2880 public IDirectedCollectionValue<T> Backwards() { return RangeAll().Backwards(); }
2883 [Tested]
2884 IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
2886 #endregion
2888 #region ISequenced Members
2889 [Tested]
2890 int ISequenced<T>.GetHashCode() { return sequencedhashcode(); }
2893 [Tested]
2894 bool ISequenced<T>.Equals(ISequenced<T> that) { return sequencedequals(that); }
2895 #endregion
2897 #region PriorityQueue Members
2899 /// <summary>
2900 /// The comparer object supplied at creation time for this collection
2901 /// </summary>
2902 /// <value>The comparer</value>
2903 public IComparer<T> Comparer { get { return comparer; } }
2906 /// <summary>
2907 /// Find the current least item of this priority queue.
2908 /// </summary>
2909 /// <returns>The least item.</returns>
2910 [Tested]
2911 public T FindMin()
2913 if (size == 0)
2914 throw new InvalidOperationException("Priority queue is empty");
2915 #if MAINTAIN_EXTREMA
2916 return min;
2917 #else
2918 Node cursor = root, next = left(cursor);
2920 while (next != null)
2922 cursor = next;
2923 next = left(cursor);
2926 return cursor.item;
2927 #endif
2931 /// <summary>
2932 /// Remove the least item from this priority queue.
2933 /// </summary>
2934 /// <returns>The removed item.</returns>
2935 [Tested]
2936 public T DeleteMin()
2938 updatecheck();
2940 //persistence guard?
2941 if (size == 0)
2942 throw new InvalidOperationException("Priority queue is empty");
2944 //We must follow the pattern of removeIterative()
2945 stackcheck();
2947 int level = 0;
2948 Node cursor = root;
2950 while (cursor.left != null)
2952 dirs[level] = 1;
2953 path[level++] = cursor;
2954 cursor = cursor.left;
2957 T retval = cursor.item;
2959 #if BAG
2960 if (cursor.items > 1)
2962 resplicebag(level, cursor);
2963 size--;
2964 return retval;
2966 #endif
2967 removeIterativePhase2(cursor, level);
2968 return retval;
2972 /// <summary>
2973 /// Find the current largest item of this priority queue.
2974 /// </summary>
2975 /// <returns>The largest item.</returns>
2976 [Tested]
2977 public T FindMax()
2979 if (size == 0)
2980 throw new InvalidOperationException("Priority queue is empty");
2982 #if MAINTAIN_EXTREMA
2983 return max;
2984 #else
2985 Node cursor = root, next = right(cursor);
2987 while (next != null)
2989 cursor = next;
2990 next = right(cursor);
2993 return cursor.item;
2994 #endif
2998 /// <summary>
2999 /// Remove the largest item from this priority queue.
3000 /// </summary>
3001 /// <returns>The removed item.</returns>
3002 [Tested]
3003 public T DeleteMax()
3005 //persistence guard?
3006 updatecheck();
3007 if (size == 0)
3008 throw new InvalidOperationException("Priority queue is empty");
3010 //We must follow the pattern of removeIterative()
3011 stackcheck();
3013 int level = 0;
3014 Node cursor = root;
3016 while (cursor.right != null)
3018 dirs[level] = -1;
3019 path[level++] = cursor;
3020 cursor = cursor.right;
3023 T retval = cursor.item;
3025 #if BAG
3026 if (cursor.items > 1)
3028 resplicebag(level, cursor);
3029 size--;
3030 return retval;
3032 #endif
3033 removeIterativePhase2(cursor, level);
3034 return retval;
3036 #endregion
3038 #region IPredecesorStructure<T> Members
3040 /// <summary>
3041 /// Find the strict predecessor in the sorted collection of a particular value,
3042 /// i.e. the largest item in the collection less than the supplied value.
3043 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3044 /// supplied value is less than or equal to the minimum of this collection.)
3045 /// </summary>
3046 /// <param name="item">The item to find the predecessor for.</param>
3047 /// <returns>The predecessor.</returns>
3048 [Tested]
3049 public T Predecessor(T item)
3051 Node cursor = root, bestsofar = null;
3053 while (cursor != null)
3055 int comp = comparer.Compare(cursor.item, item);
3057 if (comp < 0)
3059 bestsofar = cursor;
3060 cursor = right(cursor);
3062 else if (comp == 0)
3064 cursor = left(cursor);
3065 while (cursor != null)
3067 bestsofar = cursor;
3068 cursor = right(cursor);
3071 else
3072 cursor = left(cursor);
3075 if (bestsofar != null)
3076 return bestsofar.item;
3077 else
3078 throw new ArgumentOutOfRangeException("item", item, "Below minimum of set");
3082 /// <summary>
3083 /// Find the weak predecessor in the sorted collection of a particular value,
3084 /// i.e. the largest item in the collection less than or equal to the supplied value.
3085 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3086 /// supplied value is less than the minimum of this collection.)
3087 /// </summary>
3088 /// <param name="item">The item to find the weak predecessor for.</param>
3089 /// <returns>The weak predecessor.</returns>
3090 [Tested]
3091 public T WeakPredecessor(T item)
3093 Node cursor = root, bestsofar = null;
3095 while (cursor != null)
3097 int comp = comparer.Compare(cursor.item, item);
3099 if (comp < 0)
3101 bestsofar = cursor;
3102 cursor = right(cursor);
3104 else if (comp == 0)
3105 return cursor.item;
3106 else
3107 cursor = left(cursor);
3110 if (bestsofar != null)
3111 return bestsofar.item;
3112 else
3113 throw new ArgumentOutOfRangeException("item", item, "Below minimum of set");
3117 /// <summary>
3118 /// Find the strict successor in the sorted collection of a particular value,
3119 /// i.e. the least item in the collection greater than the supplied value.
3120 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3121 /// supplied value is greater than or equal to the maximum of this collection.)
3122 /// </summary>
3123 /// <param name="item">The item to find the successor for.</param>
3124 /// <returns>The successor.</returns>
3125 [Tested]
3126 public T Successor(T item)
3128 Node cursor = root, bestsofar = null;
3130 while (cursor != null)
3132 int comp = comparer.Compare(cursor.item, item);
3134 if (comp > 0)
3136 bestsofar = cursor;
3137 cursor = left(cursor);
3139 else if (comp == 0)
3141 cursor = right(cursor);
3142 while (cursor != null)
3144 bestsofar = cursor;
3145 cursor = left(cursor);
3148 else
3149 cursor = right(cursor);
3152 if (bestsofar != null)
3153 return bestsofar.item;
3154 else
3155 throw new ArgumentOutOfRangeException("item", item, "Above maximum of set");
3159 /// <summary>
3160 /// Find the weak successor in the sorted collection of a particular value,
3161 /// i.e. the least item in the collection greater than or equal to the supplied value.
3162 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3163 /// supplied value is greater than the maximum of this collection.)
3164 /// </summary>
3165 /// <param name="item">The item to find the weak successor for.</param>
3166 /// <returns>The weak successor.</returns>
3167 [Tested]
3168 public T WeakSuccessor(T item)
3170 Node cursor = root, bestsofar = null;
3172 while (cursor != null)
3174 int comp = comparer.Compare(cursor.item, item);
3176 if (comp == 0)
3177 return cursor.item;
3178 else if (comp > 0)
3180 bestsofar = cursor;
3181 cursor = left(cursor);
3183 else
3184 cursor = right(cursor);
3187 if (bestsofar != null)
3188 return bestsofar.item;
3189 else
3190 throw new ArgumentOutOfRangeException("item", item, "Above maximum of set");
3193 #endregion
3195 #region ISorted<T> Members
3197 /// <summary>
3198 /// Query this sorted collection for items greater than or equal to a supplied value.
3199 /// </summary>
3200 /// <param name="bot">The lower bound (inclusive).</param>
3201 /// <returns>The result directed collection.</returns>
3202 [Tested]
3203 public IDirectedCollectionValue<T> RangeFrom(T bot)
3204 { return new Range(this, true, bot, false, default(T), EnumerationDirection.Forwards); }
3207 /// <summary>
3208 /// Query this sorted collection for items between two supplied values.
3209 /// </summary>
3210 /// <param name="bot">The lower bound (inclusive).</param>
3211 /// <param name="top">The upper bound (exclusive).</param>
3212 /// <returns>The result directed collection.</returns>
3213 [Tested]
3214 public IDirectedCollectionValue<T> RangeFromTo(T bot, T top)
3215 { return new Range(this, true, bot, true, top, EnumerationDirection.Forwards); }
3218 /// <summary>
3219 /// Query this sorted collection for items less than a supplied value.
3220 /// </summary>
3221 /// <param name="top">The upper bound (exclusive).</param>
3222 /// <returns>The result directed collection.</returns>
3223 [Tested]
3224 public IDirectedCollectionValue<T> RangeTo(T top)
3225 { return new Range(this, false, default(T), true, top, EnumerationDirection.Forwards); }
3228 /// <summary>
3229 /// Create a directed collection with the same items as this collection.
3230 /// </summary>
3231 /// <returns>The result directed collection.</returns>
3232 [Tested]
3233 public IDirectedCollectionValue<T> RangeAll()
3234 { return new Range(this, false, default(T), false, default(T), EnumerationDirection.Forwards); }
3237 [Tested]
3238 IDirectedEnumerable<T> ISorted<T>.RangeFrom(T bot) { return RangeFrom(bot); }
3241 [Tested]
3242 IDirectedEnumerable<T> ISorted<T>.RangeFromTo(T bot, T top) { return RangeFromTo(bot, top); }
3245 [Tested]
3246 IDirectedEnumerable<T> ISorted<T>.RangeTo(T top) { return RangeTo(top); }
3249 //Utility for CountXxxx. Actually always called with strict = true.
3250 private int countTo(T item, bool strict)
3252 #if NCP
3253 if (isSnapShot)
3254 throw new NotSupportedException("Indexing not supported for snapshots");
3255 #endif
3256 #if MAINTAIN_SIZE
3257 int ind = 0, comp = 0; Node next = root;
3259 while (next != null)
3261 comp = comparer.Compare(item, next.item);
3262 if (comp < 0)
3263 next = next.left;
3264 else
3266 int leftcnt = next.left == null ? 0 : next.left.size;
3267 #if BAG
3268 if (comp == 0)
3269 return strict ? ind + leftcnt : ind + leftcnt + next.items;
3270 else
3272 ind = ind + next.items + leftcnt;
3273 next = next.right;
3275 #else
3276 if (comp == 0)
3277 return strict ? ind + leftcnt : ind + leftcnt + 1;
3278 else
3280 ind = ind + 1 + leftcnt;
3281 next = next.right;
3283 #endif
3287 //if we get here, we are at the same side of the whole collection:
3288 return ind;
3289 #else
3290 throw new NotSupportedException("Code compiled w/o size!");
3291 #endif
3295 /// <summary>
3296 /// Perform a search in the sorted collection for the ranges in which a
3297 /// non-decreasing function from the item type to <code>int</code> is
3298 /// negative, zero respectively positive. If the supplied cut function is
3299 /// not non-decreasing, the result of this call is undefined.
3300 /// </summary>
3301 /// <param name="c">The cut function <code>T</code> to <code>int</code>, given
3302 /// as an <code>IComparable&lt;T&gt;</code> object, where the cut function is
3303 /// the <code>c.CompareTo(T that)</code> method.</param>
3304 /// <param name="low">Returns the largest item in the collection, where the
3305 /// cut function is negative (if any).</param>
3306 /// <param name="lowIsValid">True if the cut function is negative somewhere
3307 /// on this collection.</param>
3308 /// <param name="high">Returns the least item in the collection, where the
3309 /// cut function is positive (if any).</param>
3310 /// <param name="highIsValid">True if the cut function is positive somewhere
3311 /// on this collection.</param>
3312 /// <returns></returns>
3313 [Tested]
3314 public bool Cut(IComparable<T> c, out T low, out bool lowIsValid, out T high, out bool highIsValid)
3316 Node cursor = root, lbest = null, rbest = null;
3317 bool res = false;
3319 while (cursor != null)
3321 int comp = c.CompareTo(cursor.item);
3323 if (comp > 0)
3325 lbest = cursor;
3326 cursor = right(cursor);
3328 else if (comp < 0)
3330 rbest = cursor;
3331 cursor = left(cursor);
3333 else
3335 res = true;
3337 Node tmp = left(cursor);
3339 while (tmp != null && c.CompareTo(tmp.item) == 0)
3340 tmp = left(tmp);
3342 if (tmp != null)
3344 lbest = tmp;
3345 tmp = right(tmp);
3346 while (tmp != null)
3348 if (c.CompareTo(tmp.item) > 0)
3350 lbest = tmp;
3351 tmp = right(tmp);
3353 else
3354 tmp = left(tmp);
3358 tmp = right(cursor);
3359 while (tmp != null && c.CompareTo(tmp.item) == 0)
3360 tmp = right(tmp);
3362 if (tmp != null)
3364 rbest = tmp;
3365 tmp = left(tmp);
3366 while (tmp != null)
3368 if (c.CompareTo(tmp.item) < 0)
3370 rbest = tmp;
3371 tmp = left(tmp);
3373 else
3374 tmp = right(tmp);
3378 break;
3382 if (highIsValid = (rbest != null))
3383 high = rbest.item;
3384 else
3385 high = default(T);
3387 if (lowIsValid = (lbest != null))
3388 low = lbest.item;
3389 else
3390 low = default(T);
3392 return res;
3396 /// <summary>
3397 /// Determine the number of items at or above a supplied threshold.
3398 /// </summary>
3399 /// <param name="bot">The lower bound (inclusive)</param>
3400 /// <returns>The number of matcing items.</returns>
3401 [Tested]
3402 public int CountFrom(T bot) { return size - countTo(bot, true); }
3405 /// <summary>
3406 /// Determine the number of items between two supplied thresholds.
3407 /// </summary>
3408 /// <param name="bot">The lower bound (inclusive)</param>
3409 /// <param name="top">The upper bound (exclusive)</param>
3410 /// <returns>The number of matcing items.</returns>
3411 [Tested]
3412 public int CountFromTo(T bot, T top)
3414 if (comparer.Compare(bot, top) >= 0)
3415 return 0;
3417 return countTo(top, true) - countTo(bot, true);
3421 /// <summary>
3422 /// Determine the number of items below a supplied threshold.
3423 /// </summary>
3424 /// <param name="top">The upper bound (exclusive)</param>
3425 /// <returns>The number of matcing items.</returns>
3426 [Tested]
3427 public int CountTo(T top) { return countTo(top, true); }
3430 /// <summary>
3431 /// Remove all items of this collection above or at a supplied threshold.
3432 /// </summary>
3433 /// <param name="low">The lower threshold (inclusive).</param>
3434 [Tested]
3435 public void RemoveRangeFrom(T low)
3437 updatecheck();
3439 int count = CountFrom(low);
3441 if (count == 0)
3442 return;
3444 for (int i = 0; i < count; i++)
3445 DeleteMax();
3449 /// <summary>
3450 /// Remove all items of this collection between two supplied thresholds.
3451 /// </summary>
3452 /// <param name="low">The lower threshold (inclusive).</param>
3453 /// <param name="hi">The upper threshold (exclusive).</param>
3454 [Tested]
3455 public void RemoveRangeFromTo(T low, T hi)
3457 updatecheck();
3459 int count = CountFromTo(low, hi);
3461 if (count == 0)
3462 return;
3464 for (int i = 0; i < count; i++)
3465 Remove(Predecessor(hi));
3469 /// <summary>
3470 /// Remove all items of this collection below a supplied threshold.
3471 /// </summary>
3472 /// <param name="hi">The upper threshold (exclusive).</param>
3473 [Tested]
3474 public void RemoveRangeTo(T hi)
3476 updatecheck();
3478 int count = CountTo(hi);
3480 if (count == 0)
3481 return;
3483 for (int i = 0; i < count; i++)
3484 DeleteMin();
3487 #endregion
3489 #region IPersistent<T> Members
3491 private bool disposed;
3495 /// <summary>
3496 /// If this tree is a snapshot, remove registration in base tree
3497 /// </summary>
3498 [Tested]
3499 public void Dispose()
3501 Dispose(true);
3502 GC.SuppressFinalize(this);
3506 private void Dispose(bool disposing)
3508 if (!disposed)
3510 if (disposing) { }
3511 #if NCP
3512 if (isSnapShot)
3514 snapdata.Remove(generation, disposing);
3515 snapdata = null;
3516 root = null;
3517 dirs = null;
3518 path = null;
3519 comparer = null;
3520 disposed = true;
3522 else { }
3523 #endif
3528 /// <summary>
3529 /// If this tree is a snapshot, remove registration in base tree
3530 /// </summary>
3531 ~TreeBag()
3533 Dispose(false);
3537 /// <summary>
3538 /// Make a (read-only) snap shot of this collection.
3539 /// </summary>
3540 /// <returns>The snap shot.</returns>
3541 [Tested]
3542 public ISorted<T> Snapshot()
3544 #if NCP
3545 if (isSnapShot)
3546 throw new InvalidOperationException("Cannot snapshot a snapshot");
3548 if (snapdata == null)
3550 snapdata = new SnapData(this);
3553 snapdata.Add(generation, this);
3555 TreeBag<T> res = (TreeBag<T>)MemberwiseClone();
3557 res.isReadOnly = true; res.isSnapShot = true;
3558 maxsnapid = generation++;
3559 return res;
3560 #endif
3563 #endregion
3565 #region Snapdata nested class
3566 //In a class by itself: the tree itself and snapshots must have a ref to
3567 //the snapids, but we do not want snapshots to have a (strong) ref to the full
3568 //updatable tree, which should be garbagecollectable even if there are still
3569 //live snapshots!
3571 //Access to SnapData should be thread safe since we expect finalisers
3572 //of snapshots to remove a snapid that is garbagecollected without
3573 //having been explicitly Disposed. Therefore we access SnapData through
3574 //synchronized method/properties.
3576 #if NCP
3577 class SnapData
3579 TreeBag<int> snapids = new TreeBag<int>(new IC());
3581 WeakReference master;
3584 internal SnapData(TreeBag<T> tree)
3586 master = new WeakReference(tree);
3590 [Tested]
3591 public bool Add(int i, TreeBag<T> tree)
3593 lock (this)
3595 bool res = snapids.Add(i);
3597 //assert the following will be i:
3598 tree.maxsnapid = snapids.Count > 0 ? snapids.FindMax() : -1;
3599 return res;
3604 [Tested]
3605 public bool Remove(int i, bool updmaxsnapid)
3607 lock (this)
3609 bool res = snapids.Remove(i);
3611 if (updmaxsnapid)
3613 //Is this safe or/and overkill?
3614 object t = master.Target;
3616 if (t != null && master.IsAlive)
3618 ((TreeBag<T>)t).maxsnapid = snapids.Count > 0 ? snapids.FindMax() : -1;
3622 return res;
3627 #endif
3628 #endregion
3630 #region TreeBag.Range nested class
3632 internal class Range: CollectionValueBase<T>, IDirectedCollectionValue<T>
3634 //We actually need exclusive upper and lower bounds, and flags to
3635 //indicate whether the bound is present (we canot rely on default(T))
3636 private int stamp;
3638 private TreeBag<T> basis;
3640 private T lowend, highend;
3642 private bool haslowend, hashighend;
3644 EnumerationDirection direction;
3647 [Tested]
3648 public Range(TreeBag<T> basis, bool haslowend, T lowend, bool hashighend, T highend, EnumerationDirection direction)
3650 this.basis = basis;
3651 stamp = basis.stamp;
3653 //lowind will be const; should we cache highind?
3654 this.lowend = lowend; //Inclusive
3655 this.highend = highend;//Exclusive
3656 this.haslowend = haslowend;
3657 this.hashighend = hashighend;
3658 this.direction = direction;
3660 #region IEnumerable<T> Members
3663 #region TreeBag.Range.Enumerator nested class
3665 public class Enumerator: MSG.IEnumerator<T>
3667 #region Private Fields
3668 private bool valid = false, ready = true;
3670 private IComparer<T> comparer;
3672 private T current;
3673 #if BAG
3674 int togo;
3675 #endif
3677 private Node cursor;
3679 private Node[] path; // stack of nodes
3681 private int level = 0;
3683 private Range range;
3685 private bool forwards;
3687 #endregion
3688 [Tested]
3689 public Enumerator(Range range)
3691 comparer = range.basis.comparer;
3692 path = new Node[2 * range.basis.blackdepth];
3693 this.range = range;
3694 forwards = range.direction == EnumerationDirection.Forwards;
3695 cursor = new Node();
3696 if (forwards)
3697 cursor.right = range.basis.root;
3698 else
3699 cursor.left = range.basis.root;
3700 range.basis.modifycheck(range.stamp);
3704 int compare(T i1, T i2) { return comparer.Compare(i1, i2); }
3707 /// <summary>
3708 /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
3709 /// </summary>
3710 /// <value>The current value of the enumerator.</value>
3711 [Tested]
3712 public T Current
3714 [Tested]
3717 if (valid)
3718 return current;
3719 else
3720 throw new InvalidOperationException();
3725 //Maintain a stack of nodes that are roots of
3726 //subtrees not completely exported yet. Invariant:
3727 //The stack nodes together with their right subtrees
3728 //consists of exactly the items we have not passed
3729 //yet (the top of the stack holds current item).
3730 /// <summary>
3731 /// Move enumerator to next item in tree, or the first item if
3732 /// this is the first call to MoveNext.
3733 /// <exception cref="InvalidOperationException"/> if underlying tree was modified.
3734 /// </summary>
3735 /// <returns>True if enumerator is valid now</returns>
3736 [Tested]
3737 public bool MoveNext()
3739 range.basis.modifycheck(range.stamp);
3740 if (!ready)
3741 return false;
3742 #if BAG
3743 if (--togo> 0)
3744 return true;
3745 #endif
3746 if (forwards)
3748 if (!valid && range.haslowend)
3750 cursor = cursor.right;
3751 while (cursor != null)
3753 int comp = compare(cursor.item, range.lowend);
3755 if (comp > 0)
3757 path[level++] = cursor;
3758 #if NCP
3759 cursor = range.basis.left(cursor);
3760 #else
3761 cursor = cursor.left;
3762 #endif
3764 else if (comp < 0)
3766 #if NCP
3767 cursor = range.basis.right(cursor);
3768 #else
3769 cursor = cursor.right;
3770 #endif
3772 else
3774 path[level] = cursor;
3775 break;
3779 if (cursor == null)
3781 if (level == 0)
3782 return valid = ready = false;
3783 else
3784 cursor = path[--level];
3787 #if NCP
3788 else if (range.basis.right(cursor) != null)
3790 path[level] = cursor = range.basis.right(cursor);
3792 Node next = range.basis.left(cursor);
3794 while (next != null)
3796 path[++level] = cursor = next;
3797 next = range.basis.left(cursor);
3800 #else
3801 else if (cursor.right != null)
3803 path[level] = cursor = cursor.right;
3804 while (cursor.left != null)
3805 path[++level] = cursor = cursor.left;
3807 #endif
3808 else if (level == 0)
3809 return valid = ready = false;
3810 else
3811 cursor = path[--level];
3813 current = cursor.item;
3814 if (range.hashighend && compare(current, range.highend) >= 0)
3815 return valid = ready = false;
3817 #if BAG
3818 togo = cursor.items;
3819 #endif
3820 return valid = true;
3822 else
3824 if (!valid && range.hashighend)
3826 cursor = cursor.left;
3827 while (cursor != null)
3829 int comp = compare(cursor.item, range.highend);
3831 if (comp < 0)
3833 path[level++] = cursor;
3834 #if NCP
3835 cursor = range.basis.right(cursor);
3836 #else
3837 cursor = cursor.right;
3838 #endif
3840 else
3842 #if NCP
3843 cursor = range.basis.left(cursor);
3844 #else
3845 cursor = cursor.left;
3846 #endif
3850 if (cursor == null)
3852 if (level == 0)
3853 return valid = ready = false;
3854 else
3855 cursor = path[--level];
3858 #if NCP
3859 else if (range.basis.left(cursor) != null)
3861 path[level] = cursor = range.basis.left(cursor);
3863 Node next = range.basis.right(cursor);
3865 while (next != null)
3867 path[++level] = cursor = next;
3868 next = range.basis.right(cursor);
3871 #else
3872 else if (cursor.left != null)
3874 path[level] = cursor = cursor.left;
3875 while (cursor.right != null)
3876 path[++level] = cursor = cursor.right;
3878 #endif
3879 else if (level == 0)
3880 return valid = ready = false;
3881 else
3882 cursor = path[--level];
3884 current = cursor.item;
3885 if (range.haslowend && compare(current, range.lowend) < 0)
3886 return valid = ready = false;
3888 #if BAG
3889 togo = cursor.items;
3890 #endif
3891 return valid = true;
3896 [Tested]
3897 public void Dispose()
3899 comparer = null;
3900 current = default(T);
3901 cursor = null;
3902 path = null;
3903 range = null;
3907 #endregion
3909 [Tested]
3910 public override MSG.IEnumerator<T> GetEnumerator() { return new Enumerator(this); }
3913 [Tested]
3914 public EnumerationDirection Direction { [Tested]get { return direction; } }
3917 #endregion
3919 #region Utility
3921 bool inside(T item)
3923 return (!haslowend || basis.comparer.Compare(item, lowend) >= 0) && (!hashighend || basis.comparer.Compare(item, highend) < 0);
3927 void checkstamp()
3929 if (stamp < basis.stamp)
3930 throw new InvalidOperationException("Base collection was modified behind my back!");
3934 void syncstamp() { stamp = basis.stamp; }
3936 #endregion
3938 [Tested]
3939 public IDirectedCollectionValue<T> Backwards()
3941 Range b = (Range)MemberwiseClone();
3943 b.direction = direction == EnumerationDirection.Forwards ? EnumerationDirection.Backwards : EnumerationDirection.Forwards;
3944 return b;
3948 [Tested]
3949 IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
3952 [Tested]
3953 public override int Count
3955 [Tested]
3958 return haslowend ? (hashighend ? basis.CountFromTo(lowend, highend) : basis.CountFrom(lowend)) : (hashighend ? basis.CountTo(highend) : basis.Count);
3962 //TODO: check that this is correct
3963 public override Speed CountSpeed { get { return Speed.Log; } }
3967 #endregion
3969 #region fixheight utility
3971 #if MAINTAIN_HEIGHT
3972 public void fixheight(Node n)
3974 int lh = n.left == null ? 0 : n.left.height + 1;
3975 int rh = n.right == null ? 0 : n.right.height + 1;
3977 n.height = (short)(lh > rh ? lh : rh);
3979 #endif
3981 #endregion
3983 #region Diagnostics
3984 /// <summary>
3985 /// Display this node on the console, and recursively its subnodes.
3986 /// </summary>
3987 /// <param name="n">Node to display</param>
3988 /// <param name="space">Indentation</param>
3989 private void minidump(Node n, string space)
3991 if (n == null)
3993 // System.Console.WriteLine(space + "null");
3995 else
3997 minidump(n.right, space + " ");
3998 Console.WriteLine(String.Format("{0} {4} (rank={5}, size={1}, items={8}, h={2}, gen={3}, id={6}){7}", space + n.item,
3999 #if MAINTAIN_SIZE
4000 n.size,
4001 #else
4003 #endif
4004 #if MAINTAIN_HEIGHT
4005 n.height,
4006 #else
4008 #endif
4009 #if NCP
4010 n.generation,
4011 #endif
4012 n.red ? "RED" : "BLACK",
4013 #if MAINTAIN_RANK
4014 n.rank,
4015 #else
4017 #endif
4018 #if TRACE_ID
4019 n.id,
4020 #else
4022 #endif
4023 #if NCP
4024 #if SEPARATE_EXTRA
4025 n.extra == null ? "" : String.Format(" [extra: lg={0}, c={1}, i={2}]", n.extra.lastgeneration, n.extra.leftnode ? "L" : "R", n.extra.oldref == null ? "()" : "" + n.extra.oldref.item),
4026 #else
4027 n.lastgeneration == -1 ? "" : String.Format(" [extra: lg={0}, c={1}, i={2}]", n.lastgeneration, n.leftnode ? "L" : "R", n.oldref == null ? "()" : "" + n.oldref.item),
4028 #endif
4029 #else
4031 #endif
4032 #if BAG
4033 n.items
4034 #else
4036 #endif
4038 minidump(n.left, space + " ");
4043 /// <summary>
4044 /// Print the tree structure to the console stdout.
4045 /// </summary>
4046 [Tested(via = "Sawtooth")]
4047 public void dump() { dump(""); }
4050 /// <summary>
4051 /// Print the tree structure to the console stdout.
4052 /// </summary>
4053 [Tested(via = "Sawtooth")]
4054 public void dump(string msg)
4056 Console.WriteLine(String.Format(">>>>>>>>>>>>>>>>>>> dump {0} (count={1}, blackdepth={2}, depth={3}, gen={4})", msg, size, blackdepth,
4057 #if MAINTAIN_HEIGHT
4058 depth
4059 #else
4061 #endif
4063 #if NCP
4064 generation
4065 #endif
4067 minidump(root, "");
4068 check("", Console.Out); Console.WriteLine("<<<<<<<<<<<<<<<<<<<");
4072 /// <summary>
4073 /// Display this tree on the console.
4074 /// </summary>
4075 /// <param name="msg">Identifying string of this call to dump</param>
4076 /// <param name="err">Extra (error)message to include</param>
4077 void dump(string msg, string err)
4079 Console.WriteLine(String.Format(">>>>>>>>>>>>>>>>>>> dump {0} (count={1}, blackdepth={2}, depth={3}, gen={4})", msg, size, blackdepth,
4080 #if MAINTAIN_HEIGHT
4081 depth
4082 #else
4084 #endif
4086 #if NCP
4087 generation
4088 #endif
4090 minidump(root, ""); Console.Write(err);
4091 Console.WriteLine("<<<<<<<<<<<<<<<<<<<");
4095 /// <summary>
4096 /// Print warning m on o if b is false.
4097 /// </summary>
4098 /// <param name="b">Condition that should hold</param>
4099 /// <param name="n">Place (used for id display)</param>
4100 /// <param name="m">Message</param>
4101 /// <param name="o">Output stream</param>
4102 /// <returns>b</returns>
4103 bool massert(bool b, Node n, string m, System.IO.TextWriter o)
4105 if (!b) o.WriteLine("*** Node (item={0}, id={1}): {2}", n.item,
4106 #if TRACE_ID
4107 n.id
4108 #else
4110 #endif
4111 , m);
4113 return b;
4117 bool rbminicheck(Node n, bool redp, int prank, System.IO.TextWriter o, out T min, out T max, out int blackheight, int maxgen)
4118 {//Red-Black invariant
4119 bool res = true;
4121 res = massert(!(n.red && redp), n, "RED parent of RED node", o) && res;
4122 res = massert(n.left == null || n.right != null || n.left.red, n, "Left child black, but right child empty", o) && res;
4123 res = massert(n.right == null || n.left != null || n.right.red, n, "Right child black, but left child empty", o) && res;
4124 #if MAINTAIN_RANK
4125 res = massert(n.red == (n.rank == prank), n, "Bad color", o) && res;
4126 res = massert(prank <= n.rank + 1, n, "Parentrank-rank >= 2", o) && res;
4127 res = massert((n.left != null && n.right != null) || n.rank == 1, n, "Rank>1 but empty child", o) && res;
4128 #endif
4129 #if BAG
4130 bool sb = n.size == (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + n.items;
4132 res = massert(sb, n, "Bad size", o) && res;
4133 #elif MAINTAIN_SIZE
4134 bool sb = n.size == (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + 1;
4136 res = massert(sb, n, "Bad size", o) && res;
4137 #endif
4138 #if MAINTAIN_HEIGHT
4139 int lh = n.left == null ? 0 : n.left.height + 1;
4140 int rh = n.right == null ? 0 : n.right.height + 1;
4142 res = massert(n.height == (lh < rh ? rh : lh), n, "Bad height", o) && res;
4143 #endif
4144 int therank =
4145 #if MAINTAIN_RANK
4146 n.rank;
4147 #else
4149 #endif
4150 min = max = n.item;
4152 T otherext;
4153 int lbh = 0, rbh = 0;
4155 if (n.left != null)
4157 res = rbminicheck(n.left, n.red, therank, o, out min, out otherext, out lbh, generation) && res;
4158 res = massert(comparer.Compare(n.item, otherext) > 0, n, "Value not > all left children", o) && res;
4161 if (n.right != null)
4163 res = rbminicheck(n.right, n.red, therank, o, out otherext, out max, out rbh, generation) && res;
4164 res = massert(comparer.Compare(n.item, otherext) < 0, n, "Value not < all right children", o) && res;
4167 res = massert(rbh == lbh, n, "Different blackheights of children", o) && res;
4168 blackheight = n.red ? rbh : rbh + 1;
4169 #if MAINTAIN_RANK
4170 //The rank is the number of black nodes from this one to
4171 //the leaves, not counting this one, but counting 1 for the empty
4172 //virtual leaf nodes.
4173 res = massert(n.rank == rbh + 1, n, "rank!=blackheight " + blackheight, o) && res;
4174 #endif
4175 return res;
4181 #if NCP
4183 bool rbminisnapcheck(Node n, System.IO.TextWriter o, out int size, out T min, out T max)
4185 bool res = true;
4187 min = max = n.item;
4189 int lsz = 0, rsz = 0;
4190 T otherext;
4191 #if SEPARATE_EXTRA
4192 Node.Extra extra = n.extra;
4193 Node child = (extra != null && extra.lastgeneration >= treegen && extra.leftnode) ? extra.oldref : n.left;
4194 #else
4195 Node child = (n.lastgeneration >= generation && n.leftnode) ? n.oldref : n.left;
4196 #endif
4197 if (child != null)
4199 res = rbminisnapcheck(child, o, out lsz, out min, out otherext) && res;
4200 res = massert(comparer.Compare(n.item, otherext) > 0, n, "Value not > all left children", o) && res;
4203 #if SEPARATE_EXTRA
4204 child = (extra != null && extra.lastgeneration >= treegen && !extra.leftnode) ? extra.oldref : n.right;
4205 #else
4206 child = (n.lastgeneration >= generation && !n.leftnode) ? n.oldref : n.right;
4207 #endif
4208 if (child != null)
4210 res = rbminisnapcheck(child, o, out rsz, out otherext, out max) && res;
4211 res = massert(comparer.Compare(n.item, otherext) < 0, n, "Value not < all right children", o) && res;
4213 #if BAG
4214 size = n.items + lsz + rsz;
4215 #else
4216 size = 1 + lsz + rsz;
4217 #endif
4218 return res;
4220 #endif
4222 /// <summary>
4223 /// Checks red-black invariant. Dumps tree to console if bad
4224 /// </summary>
4225 /// <param name="name">Title of dump</param>
4226 /// <returns>false if invariant violation</returns>
4227 [Tested(via = "Sawtooth")]
4228 public bool Check(string name)
4230 System.Text.StringBuilder e = new System.Text.StringBuilder();
4231 System.IO.TextWriter o = new System.IO.StringWriter(e);
4233 if (!check(name, o))
4234 return true;
4235 else
4237 dump(name, e.ToString());
4238 return false;
4243 /// <summary>
4244 /// Checks red-black invariant. Dumps tree to console if bad
4245 /// </summary>
4246 /// <returns>false if invariant violation</returns>
4247 [Tested]
4248 public bool Check()
4250 //return check("", System.IO.TextWriter.Null);
4251 //Console.WriteLine("bamse");
4252 return Check("-");
4256 bool check(string msg, System.IO.TextWriter o)
4258 if (root != null)
4260 T max, min;
4261 int blackheight;
4262 #if NCP
4263 if (isSnapShot)
4265 //Console.WriteLine("Im'a snapshot");
4266 int thesize;
4267 bool rv = rbminisnapcheck(root, o, out thesize, out min, out max);
4269 rv = massert(size == thesize, root, "bad snapshot size", o) && rv;
4270 return !rv;
4272 #endif
4273 #if MAINTAIN_RANK
4274 bool res = rbminicheck(root, false, root.rank + 1, o, out min, out max, out blackheight, generation);
4276 res = massert(root.rank == blackdepth, root, "blackdepth!=root.rank", o) && res;
4277 #else
4278 bool res = rbminicheck(root, false, 0, o, out min, out max, out blackheight, generation);
4279 #endif
4280 res = massert(blackheight == blackdepth, root, "bad blackh/d", o) && res;
4281 res = massert(!root.red, root, "root is red", o) && res;
4282 #if MAINTAIN_SIZE
4283 res = massert(root.size == size, root, "count!=root.size", o) && res;
4284 #endif
4285 #if MAINTAIN_HEIGHT
4286 res = massert(root.height == depth, root, "depth!=root.height", o) && res;
4287 #endif
4288 return !res;
4290 else
4291 return false;
4293 #endregion
4297 #endif