1 /*
2  Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
3  Permission is hereby granted, free of charge, to any person obtaining a copy
4  of this software and associated documentation files (the "Software"), to deal
5  in the Software without restriction, including without limitation the rights
6  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  copies of the Software, and to permit persons to whom the Software is
8  furnished to do so, subject to the following conditions:
9 
10  The above copyright notice and this permission notice shall be included in
11  all copies or substantial portions of the Software.
12 
13  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19  SOFTWARE.
20 */
21 
22 #define MAINTAIN_SIZE
23 #define BAG
24 #define NCP
25 
26 #if BAG
27 #if !MAINTAIN_SIZE
28 #error  BAG defined without MAINTAIN_SIZE!
29 #endif
30 #endif
31 
32 
33 using System;
34 using SCG = System.Collections.Generic;
35 
36 // NOTE NOTE NOTE NOTE
37 // This source file is used to produce both TreeBag<T> and TreeBag<T>
38 // It should be copied to a file called TreeBag.cs in which all code mentions of
39 // TreeBag is changed to TreeBag and the preprocessor symbol BAG is defined.
40 // NOTE: there may be problems with documentation comments.
41 
42 namespace C5
43 {
44 #if BAG
45   /// <summary>
46   /// An implementation of Red-Black trees as an indexed, sorted collection with bag semantics,
47   /// cf. <a href="litterature.htm#CLRS">CLRS</a>. (<see cref="T:C5.TreeBag`1"/> for an
48   /// implementation with set semantics).
49   /// <br/>
50   /// The comparer (sorting order) may be either natural, because the item type is comparable
51   /// (generic: <see cref="T:C5.IComparable`1"/> or non-generic: System.IComparable) or it can
52   /// be external and supplied by the user in the constructor.
53   /// <br/>
54   /// Each distinct item is only kept in one place in the tree - together with the number
55   /// of times it is a member of the bag. Thus, if two items that are equal according
56   /// </summary>
57 #else
58   /// <summary>
59   /// An implementation of Red-Black trees as an indexed, sorted collection with set semantics,
60   /// cf. <a href="litterature.htm#CLRS">CLRS</a>. <see cref="T:C5.TreeBag`1"/> for a version
61   /// with bag semantics. <see cref="T:C5.TreeDictionary`2"/> for a sorted dictionary
62   /// based on this tree implementation.
63   /// <i>
64   /// The comparer (sorting order) may be either natural, because the item type is comparable
65   /// (generic: <see cref="T:C5.IComparable`1"/> or non-generic: System.IComparable) or it can
66   /// be external and supplied by the user in the constructor.</i>
67   ///
68   /// <i>TODO: describe performance here</i>
69   /// <i>TODO: discuss persistence and its useful usage modes. Warn about the space
70   /// leak possible with other usage modes.</i>
71   /// </summary>
72 #endif
73   [Serializable]
74   public class TreeBag<T> : SequencedBase<T>, IIndexedSorted<T>, IPersistentSorted<T>
75   {
76     #region Fields
77 
78     SCG.IComparer<T> comparer;
79 
80     Node root;
81 
82     //TODO: wonder if we should remove that
83     int blackdepth = 0;
84 
85     //We double these stacks for the iterative add and remove on demand
86     //TODO: refactor dirs[] into bool fields on Node (?)
87     private int[] dirs = new int[2];
88 
89     private Node[] path = new Node[2];
90 #if NCP
91     //TODO: refactor into separate class
92     bool isSnapShot = false;
93 
94     int generation;
95 
96     bool isValid = true;
97 
98     SnapRef snapList;
99 #endif
100     #endregion
101 
102     #region Events
103 
104     /// <summary>
105     ///
106     /// </summary>
107     /// <value></value>
108     public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } }
109 
110     #endregion
111     #region Util
112 
113     /// <summary>
114     /// Fetch the left child of n taking node-copying persistence into
115     /// account if relevant.
116     /// </summary>
117     /// <param name="n"></param>
118     /// <returns></returns>
left(Node n)119     private Node left(Node n)
120     {
121 #if NCP
122       if (isSnapShot)
123       {
124 #if SEPARATE_EXTRA
125 				Node.Extra e = n.extra;
126 
127 				if (e != null && e.lastgeneration >= treegen && e.leftnode)
128 					return e.oldref;
129 #else
130         if (n.lastgeneration >= generation && n.leftnode)
131           return n.oldref;
132 #endif
133       }
134 #endif
135       return n.left;
136     }
137 
138 
right(Node n)139     private Node right(Node n)
140     {
141 #if NCP
142       if (isSnapShot)
143       {
144 #if SEPARATE_EXTRA
145 				Node.Extra e = n.extra;
146 
147 				if (e != null && e.lastgeneration >= treegen && !e.leftnode)
148 					return e.oldref;
149 #else
150         if (n.lastgeneration >= generation && !n.leftnode)
151           return n.oldref;
152 #endif
153       }
154 #endif
155       return n.right;
156     }
157 
158 
159     //This method should be called by methods that use the internal
160     //traversal stack, unless certain that there is room enough
stackcheck()161     private void stackcheck()
162     {
163       while (dirs.Length < 2 * blackdepth)
164       {
165         dirs = new int[2 * dirs.Length];
166         path = new Node[2 * dirs.Length];
167       }
168     }
169 
170     #endregion
171 
172     #region Node nested class
173 
174 
175     /// <summary>
176     /// The type of node in a Red-Black binary tree
177     /// </summary>
178     [Serializable]
179     class Node
180     {
181       public bool red = true;
182 
183       public T item;
184 
185       public Node left;
186 
187       public Node right;
188 
189 #if MAINTAIN_SIZE
190       public int size = 1;
191 #endif
192 
193 #if BAG
194       public int items = 1;
195 #endif
196 
197 #if NCP
198       //TODO: move everything into (separate) Extra
199       public int generation;
200 #if SEPARATE_EXTRA
201 			internal class Extra
202 			{
203 				public int lastgeneration;
204 
205 				public Node oldref;
206 
207 				public bool leftnode;
208 
209 				//public Node next;
210 			}
211 
212 			public Extra extra;
213 
214 #else
215       public int lastgeneration = -1;
216 
217       public Node oldref;
218 
219       public bool leftnode;
220 #endif
221 
222       /// <summary>
223       /// Update a child pointer
224       /// </summary>
225       /// <param name="cursor"></param>
226       /// <param name="leftnode"></param>
227       /// <param name="child"></param>
228       /// <param name="maxsnapid"></param>
229       /// <param name="generation"></param>
230       /// <returns>True if node was *copied*</returns>
update(ref Node cursor, bool leftnode, Node child, int maxsnapid, int generation)231       internal static bool update(ref Node cursor, bool leftnode, Node child, int maxsnapid, int generation)
232       {
233         Node oldref = leftnode ? cursor.left : cursor.right;
234 
235         if (child == oldref)
236           return false;
237 
238         bool retval = false;
239 
240         if (cursor.generation <= maxsnapid)
241         {
242 #if SEPARATE_EXTRA
243 					if (cursor.extra == null)
244 					{
245 						Extra extra = cursor.extra = new Extra();
246 
247 						extra.leftnode = leftnode;
248 						extra.lastgeneration = maxsnapid;
249 						extra.oldref = oldref;
250 					}
251 					else if (cursor.extra.leftnode != leftnode || cursor.extra.lastgeneration < maxsnapid)
252 #else
253           if (cursor.lastgeneration == -1)
254           {
255             cursor.leftnode = leftnode;
256             cursor.lastgeneration = maxsnapid;
257             cursor.oldref = oldref;
258           }
259           else if (cursor.leftnode != leftnode || cursor.lastgeneration < maxsnapid)
260 #endif
261           {
262             CopyNode(ref cursor, maxsnapid, generation);
263             retval = true;
264           }
265         }
266 
267         if (leftnode)
268           cursor.left = child;
269         else
270           cursor.right = child;
271 
272         return retval;
273       }
274 
275 
276       //If cursor.extra.lastgeneration==maxsnapid, the extra pointer will
277       //always be used in the old copy of cursor. Therefore, after
278       //making the clone, we should update the old copy by restoring
279       //the child pointer and setting extra to null.
280       //OTOH then we cannot clean up unused Extra objects unless we link
281       //them together in a doubly linked list.
CopyNode(ref Node cursor, int maxsnapid, int generation)282       public static bool CopyNode(ref Node cursor, int maxsnapid, int generation)
283       {
284         if (cursor.generation <= maxsnapid)
285         {
286           cursor = (Node)(cursor.MemberwiseClone());
287           cursor.generation = generation;
288 #if SEPARATE_EXTRA
289 					cursor.extra = null;
290 #else
291           cursor.lastgeneration = -1;
292 #endif
293           return true;
294         }
295         else
296           return false;
297       }
298 
299 #endif
300     }
301 
302     #endregion
303 
304     #region Constructors
305 
306     /// <summary>
307     /// Create a red-black tree collection with natural comparer and item equalityComparer.
308     /// We assume that if <code>T</code> is comparable, its default equalityComparer
309     /// will be compatible with the comparer.
310     /// </summary>
311     /// <exception cref="NotComparableException">If <code>T</code> is not comparable.
312     /// </exception>
TreeBag()313     public TreeBag() : this(Comparer<T>.Default, EqualityComparer<T>.Default) { }
314 
315 
316     /// <summary>
317     /// Create a red-black tree collection with an external comparer.
318     /// <para>The itemequalityComparer will be a compatible
319     /// <see cref="T:C5.ComparerZeroHashCodeEqualityComparer`1"/> since the
320     /// default equalityComparer for T (<see cref="P:C5.EqualityComparer`1.Default"/>)
321     /// is unlikely to be compatible with the external comparer. This makes the
322     /// tree inadequate for use as item in a collection of unsequenced or sequenced sets or bags
323     /// (<see cref="T:C5.ICollection`1"/> and <see cref="T:C5.ISequenced`1"/>)
324     /// </para>
325     /// </summary>
326     /// <param name="comparer">The external comparer</param>
TreeBag(SCG.IComparer<T> comparer)327     public TreeBag(SCG.IComparer<T> comparer) : this(comparer, new ComparerZeroHashCodeEqualityComparer<T>(comparer)) { }
328 
329     /// <summary>
330     /// Create a red-black tree collection with an external comparer and an external
331     /// item equalityComparer, assumed consistent.
332     /// </summary>
333     /// <param name="comparer">The external comparer</param>
334     /// <param name="equalityComparer">The external item equalityComparer</param>
TreeBag(SCG.IComparer<T> comparer, SCG.IEqualityComparer<T> equalityComparer)335     public TreeBag(SCG.IComparer<T> comparer, SCG.IEqualityComparer<T> equalityComparer)
336       : base(equalityComparer)
337     {
338       if (comparer == null)
339         throw new NullReferenceException("Item comparer cannot be null");
340       this.comparer = comparer;
341     }
342 
343     #endregion
344 
345     #region TreeBag.Enumerator nested class
346 
347     /// <summary>
348     /// An enumerator for a red-black tree collection. Based on an explicit stack
349     /// of subtrees waiting to be enumerated. Currently only used for the tree set
350     /// enumerators (tree bag enumerators use an iterator block based enumerator).
351     /// </summary>
352     internal class Enumerator : SCG.IEnumerator<T>
353     {
354       #region Private Fields
355       TreeBag<T> tree;
356 
357       bool valid = false;
358 
359       int stamp;
360 
361       T current;
362 
363       Node cursor;
364 
365       Node[] path; // stack of nodes
366 
367       int level = 0;
368       #endregion
369       /// <summary>
370       /// Create a tree enumerator
371       /// </summary>
372       /// <param name="tree">The red-black tree to enumerate</param>
Enumerator(TreeBag<T> tree)373       public Enumerator(TreeBag<T> tree)
374       {
375         this.tree = tree;
376         stamp = tree.stamp;
377         path = new Node[2 * tree.blackdepth];
378         cursor = new Node();
379         cursor.right = tree.root;
380       }
381 
382 
383       /// <summary>
384       /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
385       /// </summary>
386       /// <value>The current item of the enumerator.</value>
387       [Tested]
388       public T Current
389       {
390         [Tested]
391         get
392         {
393           if (valid)
394             return current;
395           else
396             throw new InvalidOperationException();
397         }
398       }
399 
400 
401       //Maintain a stack of nodes that are roots of
402       //subtrees not completely exported yet. Invariant:
403       //The stack nodes together with their right subtrees
404       //consists of exactly the items we have not passed
405       //yet (the top of the stack holds current item).
406       /// <summary>
407       /// Move enumerator to next item in tree, or the first item if
408       /// this is the first call to MoveNext.
409       /// <exception cref="CollectionModifiedException"/> if underlying tree was modified.
410       /// </summary>
411       /// <returns>True if enumerator is valid now</returns>
412       [Tested]
MoveNext()413       public bool MoveNext()
414       {
415         tree.modifycheck(stamp);
416         if (cursor.right != null)
417         {
418           path[level] = cursor = cursor.right;
419           while (cursor.left != null)
420             path[++level] = cursor = cursor.left;
421         }
422         else if (level == 0)
423           return valid = false;
424         else
425           cursor = path[--level];
426 
427         current = cursor.item;
428         return valid = true;
429       }
430 
431 
432       #region IDisposable Members for Enumerator
433 
434       bool disposed;
435 
436 
437       /// <summary>
438       /// Call Dispose(true) and then suppress finalization of this enumerator.
439       /// </summary>
440       [Tested]
Dispose()441       public void Dispose()
442       {
443         Dispose(true);
444         GC.SuppressFinalize(this);
445       }
446 
447 
448       /// <summary>
449       /// Remove the internal data (notably the stack array).
450       /// </summary>
451       /// <param name="disposing">True if called from Dispose(),
452       /// false if called from the finalizer</param>
Dispose(bool disposing)453       protected virtual void Dispose(bool disposing)
454       {
455         if (!disposed)
456         {
457           if (disposing)
458           {
459           }
460 
461           current = default(T);
462           cursor = null;
463           path = null;
464           disposed = true;
465         }
466       }
467 
468 
469       /// <summary>
470       /// Finalizer for enumerator
471       /// </summary>
~Enumerator()472       ~Enumerator()
473       {
474         Dispose(false);
475       }
476       #endregion
477 
478 
479       #region IEnumerator Members
480 
481       object System.Collections.IEnumerator.Current
482       {
483         get { return Current; }
484       }
485 
System.Collections.IEnumerator.MoveNext()486       bool System.Collections.IEnumerator.MoveNext()
487       {
488         return MoveNext();
489       }
490 
System.Collections.IEnumerator.Reset()491       void System.Collections.IEnumerator.Reset()
492       {
493         throw new Exception("The method or operation is not implemented.");
494       }
495 
496       #endregion
497     }
498 #if NCP
499     /// <summary>
500     /// An enumerator for a snapshot of a node copy persistent red-black tree
501     /// collection.
502     /// </summary>
503     internal class SnapEnumerator : SCG.IEnumerator<T>
504     {
505       #region Private Fields
506       TreeBag<T> tree;
507 
508       bool valid = false;
509 
510       int stamp;
511 #if BAG
512       int togo;
513 #endif
514 
515       T current;
516 
517       Node cursor;
518 
519       Node[] path; // stack of nodes
520 
521       int level;
522       #endregion
523 
524       /// <summary>
525       /// Creta an enumerator for a snapshot of a node copy persistent red-black tree
526       /// collection
527       /// </summary>
528       /// <param name="tree">The snapshot</param>
SnapEnumerator(TreeBag<T> tree)529       public SnapEnumerator(TreeBag<T> tree)
530       {
531         this.tree = tree;
532         stamp = tree.stamp;
533         path = new Node[2 * tree.blackdepth];
534         cursor = new Node();
535         cursor.right = tree.root;
536       }
537 
538 
539       #region SCG.IEnumerator<T> Members
540 
541       /// <summary>
542       /// Move enumerator to next item in tree, or the first item if
543       /// this is the first call to MoveNext.
544       /// <exception cref="CollectionModifiedException"/> if underlying tree was modified.
545       /// </summary>
546       /// <returns>True if enumerator is valid now</returns>
547       [Tested]
MoveNext()548       public bool MoveNext()
549       {
550         tree.modifycheck(stamp);//???
551 
552 #if BAG
553         if (--togo > 0)
554           return true;
555 #endif
556         Node next = tree.right(cursor);
557 
558         if (next != null)
559         {
560           path[level] = cursor = next;
561           next = tree.left(cursor);
562           while (next != null)
563           {
564             path[++level] = cursor = next;
565             next = tree.left(cursor);
566           }
567         }
568         else if (level == 0)
569           return valid = false;
570         else
571           cursor = path[--level];
572 
573 #if BAG
574         togo = cursor.items;
575 #endif
576         current = cursor.item;
577         return valid = true;
578       }
579 
580 
581       /// <summary>
582       /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
583       /// </summary>
584       /// <value>The current value of the enumerator.</value>
585       [Tested]
586       public T Current
587       {
588         [Tested]
589         get
590         {
591           if (valid)
592             return current;
593           else
594             throw new InvalidOperationException();
595         }
596       }
597 
598       #endregion
599 
600       #region IDisposable Members
601 
602       [Tested]
System.IDisposable.Dispose()603       void System.IDisposable.Dispose()
604       {
605         tree = null;
606         valid = false;
607         current = default(T);
608         cursor = null;
609         path = null;
610       }
611 
612       #endregion
613 
614       #region IEnumerator Members
615 
616       object System.Collections.IEnumerator.Current
617       {
618         get { return Current; }
619       }
620 
System.Collections.IEnumerator.MoveNext()621       bool System.Collections.IEnumerator.MoveNext()
622       {
623         return MoveNext();
624       }
625 
System.Collections.IEnumerator.Reset()626       void System.Collections.IEnumerator.Reset()
627       {
628         throw new Exception("The method or operation is not implemented.");
629       }
630 
631       #endregion
632     }
633 #endif
634     #endregion
635 
636     #region IEnumerable<T> Members
637 
getEnumerator(Node node, int origstamp)638     private SCG.IEnumerator<T> getEnumerator(Node node, int origstamp)
639     {
640       if (node == null)
641         yield break;
642 
643       if (node.left != null)
644       {
645         SCG.IEnumerator<T> child = getEnumerator(node.left, origstamp);
646 
647         while (child.MoveNext())
648         {
649           modifycheck(origstamp);
650           yield return child.Current;
651         }
652       }
653 #if BAG
654       int togo = node.items;
655       while (togo-- > 0)
656       {
657         modifycheck(origstamp);
658         yield return node.item;
659       }
660 #else
661       modifycheck(origstamp);
662       yield return node.item;
663 #endif
664       if (node.right != null)
665       {
666         SCG.IEnumerator<T> child = getEnumerator(node.right, origstamp);
667 
668         while (child.MoveNext())
669         {
670           modifycheck(origstamp);
671           yield return child.Current;
672         }
673       }
674     }
675 
676     /// <summary>
677     ///
678     /// </summary>
679     /// <exception cref="NoSuchItemException">If tree is empty</exception>
680     /// <returns></returns>
681     [Tested]
Choose()682     public override T Choose()
683     {
684       if (!isValid)
685         throw new ViewDisposedException("Snapshot has been disposed");
686 
687       if (size == 0)
688         throw new NoSuchItemException();
689       return root.item;
690     }
691 
692 
693     /// <summary>
694     /// Create an enumerator for this tree
695     /// </summary>
696     /// <returns>The enumerator</returns>
697     [Tested]
GetEnumerator()698     public override SCG.IEnumerator<T> GetEnumerator()
699     {
700       if (!isValid)
701         throw new ViewDisposedException("Snapshot has been disposed");
702 #if NCP
703       if (isSnapShot)
704         return new SnapEnumerator(this);
705 #endif
706 #if BAG
707       return getEnumerator(root, stamp);
708 #else
709       return new Enumerator(this);
710 #endif
711     }
712 
713     #endregion
714 
715     #region ISink<T> Members
716 
717     /// <summary>
718     /// Add item to tree. If already there, return the found item in the second argument.
719     /// </summary>
720     /// <param name="item">Item to add</param>
721     /// <param name="founditem">item found</param>
722     /// <param name="update">whether item in node should be updated</param>
723     /// <param name="wasfound">true if found in bag, false if not found or tre is a set</param>
724     /// <returns>True if item was added</returns>
addIterative(T item, ref T founditem, bool update, out bool wasfound)725     bool addIterative(T item, ref T founditem, bool update, out bool wasfound)
726     {
727       wasfound = false;
728       if (root == null)
729       {
730         root = new Node();
731         root.red = false;
732         blackdepth = 1;
733         root.item = item;
734 #if NCP
735         root.generation = generation;
736 #endif
737         return true;
738       }
739 
740       stackcheck();
741 
742       int level = 0;
743       Node cursor = root;
744 
745       while (true)
746       {
747         int comp = comparer.Compare(cursor.item, item);
748 
749         if (comp == 0)
750         {
751           founditem = cursor.item;
752 #if BAG
753           wasfound = true;
754           bool nodeWasUpdated = true;
755 #if NCP
756           Node.CopyNode(ref cursor, maxsnapid, generation);
757 #endif
758           if (update)
759             cursor.item = item;
760           else
761           {
762             cursor.items++;
763             cursor.size++;
764           }
765 #else
766           bool nodeWasUpdated = update;
767           if (update)
768           {
769 #if NCP
770             Node.CopyNode(ref cursor, maxsnapid, generation);
771 #endif
772             cursor.item = item;
773           }
774 #endif
775 
776           while (level-- > 0)
777           {
778             if (nodeWasUpdated)
779             {
780               Node kid = cursor;
781 
782               cursor = path[level];
783 #if NCP
784               Node.update(ref cursor, dirs[level] > 0, kid, maxsnapid, generation);
785 #endif
786 #if BAG
787               if (!update)
788                 cursor.size++;
789 #endif
790             }
791 
792             path[level] = null;
793           }
794 #if BAG
795           return !update;
796 #else
797           if (update)
798             root = cursor;
799 
800           return false;
801 #endif
802         }
803 
804         //else
805         Node child = comp > 0 ? cursor.left : cursor.right;
806 
807         if (child == null)
808         {
809           child = new Node();
810           child.item = item;
811 #if NCP
812           child.generation = generation;
813           Node.update(ref cursor, comp > 0, child, maxsnapid, generation);
814 #else
815 					if (comp > 0) { cursor.left = child; }
816 					else { cursor.right = child; }
817 #endif
818 #if MAINTAIN_SIZE
819           cursor.size++;
820 #endif
821           dirs[level] = comp;
822           break;
823         }
824         else
825         {
826           dirs[level] = comp;
827           path[level++] = cursor;
828           cursor = child;
829         }
830       }
831 
832       //We have just added the red node child to "cursor"
833       while (cursor.red)
834       {
835         //take one step up:
836         Node child = cursor;
837 
838         cursor = path[--level];
839         path[level] = null;
840 #if NCP
841         Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
842 #endif
843 #if MAINTAIN_SIZE
844         cursor.size++;
845 #endif
846         int comp = dirs[level];
847         Node childsibling = comp > 0 ? cursor.right : cursor.left;
848 
849         if (childsibling != null && childsibling.red)
850         {
851           //Promote
852           child.red = false;
853 #if NCP
854           Node.update(ref cursor, comp < 0, childsibling, maxsnapid, generation);
855 #endif
856           childsibling.red = false;
857 
858           //color cursor red & take one step up the tree unless at root
859           if (level == 0)
860           {
861             root = cursor;
862             blackdepth++;
863             return true;
864           }
865           else
866           {
867             cursor.red = true;
868 #if NCP
869             child = cursor;
870             cursor = path[--level];
871             Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
872 #endif
873             path[level] = null;
874 #if MAINTAIN_SIZE
875             cursor.size++;
876 #endif
877           }
878         }
879         else
880         {
881           //ROTATE!!!
882           int childcomp = dirs[level + 1];
883 
884           cursor.red = true;
885           if (comp > 0)
886           {
887             if (childcomp > 0)
888             {//zagzag
889 #if NCP
890               Node.update(ref cursor, true, child.right, maxsnapid, generation);
891               Node.update(ref child, false, cursor, maxsnapid, generation);
892 #else
893 							cursor.left = child.right;
894 							child.right = cursor;
895 #endif
896               cursor = child;
897             }
898             else
899             {//zagzig
900               Node badgrandchild = child.right;
901 #if NCP
902               Node.update(ref cursor, true, badgrandchild.right, maxsnapid, generation);
903               Node.update(ref child, false, badgrandchild.left, maxsnapid, generation);
904               Node.CopyNode(ref badgrandchild, maxsnapid, generation);
905 #else
906 							cursor.left = badgrandchild.right;
907 							child.right = badgrandchild.left;
908 #endif
909               badgrandchild.left = child;
910               badgrandchild.right = cursor;
911               cursor = badgrandchild;
912             }
913           }
914           else
915           {//comp < 0
916             if (childcomp < 0)
917             {//zigzig
918 #if NCP
919               Node.update(ref cursor, false, child.left, maxsnapid, generation);
920               Node.update(ref child, true, cursor, maxsnapid, generation);
921 #else
922 							cursor.right = child.left;
923 							child.left = cursor;
924 #endif
925               cursor = child;
926             }
927             else
928             {//zigzag
929               Node badgrandchild = child.left;
930 #if NCP
931               Node.update(ref cursor, false, badgrandchild.left, maxsnapid, generation);
932               Node.update(ref child, true, badgrandchild.right, maxsnapid, generation);
933               Node.CopyNode(ref badgrandchild, maxsnapid, generation);
934 #else
935 							cursor.right = badgrandchild.left;
936 							child.left = badgrandchild.right;
937 #endif
938               badgrandchild.right = child;
939               badgrandchild.left = cursor;
940               cursor = badgrandchild;
941             }
942           }
943 
944           cursor.red = false;
945 
946 #if MAINTAIN_SIZE
947           Node n;
948 
949 #if BAG
950           n = cursor.right;
951           cursor.size = n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + n.items;
952           n = cursor.left;
953           n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + n.items;
954           cursor.size += n.size + cursor.items;
955 #else
956           n = cursor.right;
957           cursor.size = n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + 1;
958           n = cursor.left;
959           n.size = (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + 1;
960           cursor.size += n.size + 1;
961 #endif
962 #endif
963           if (level == 0)
964           {
965             root = cursor;
966             return true;
967           }
968           else
969           {
970             child = cursor;
971             cursor = path[--level];
972             path[level] = null;
973 #if NCP
974             Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
975 #else
976 						if (dirs[level] > 0)
977 							cursor.left = child;
978 						else
979 							cursor.right = child;
980 #endif
981 #if MAINTAIN_SIZE
982             cursor.size++;
983 #endif
984             break;
985           }
986         }
987       }
988 #if NCP
989       bool stillmore = true;
990 #endif
991       while (level > 0)
992       {
993         Node child = cursor;
994 
995         cursor = path[--level];
996         path[level] = null;
997 #if NCP
998         if (stillmore)
999           stillmore = Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
1000 #endif
1001 #if MAINTAIN_SIZE
1002         cursor.size++;
1003 #endif
1004       }
1005 
1006       root = cursor;
1007       return true;
1008     }
1009 
1010 
1011     /// <summary>
1012     /// Add an item to this collection if possible. If this collection has set
1013     /// semantics, the item will be added if not already in the collection. If
1014     /// bag semantics, the item will always be added.
1015     /// </summary>
1016     /// <param name="item">The item to add.</param>
1017     /// <returns>True if item was added.</returns>
1018     [Tested]
Add(T item)1019     public bool Add(T item)
1020     {
1021       if (!isValid)
1022         throw new ViewDisposedException("Snapshot has been disposed");
1023       updatecheck();
1024 
1025       //Note: blackdepth of the tree is set inside addIterative
1026       T jtem = default(T);
1027       if (!add(item, ref jtem))
1028         return false;
1029       if (ActiveEvents != 0)
1030         raiseForAdd(jtem);
1031       return true;
1032     }
1033 
1034     /// <summary>
1035     /// Add an item to this collection if possible. If this collection has set
1036     /// semantics, the item will be added if not already in the collection. If
1037     /// bag semantics, the item will always be added.
1038     /// </summary>
1039     /// <param name="item">The item to add.</param>
1040     [Tested]
Add(T item)1041     void SCG.ICollection<T>.Add(T item)
1042     {
1043         Add(item);
1044     }
1045 
add(T item, ref T j)1046     private bool add(T item, ref T j)
1047     {
1048       bool wasFound;
1049 
1050       if (addIterative(item, ref j, false, out wasFound))
1051       {
1052         size++;
1053         if (!wasFound)
1054           j = item;
1055         return true;
1056       }
1057       else
1058         return false;
1059     }
1060 
1061     /// <summary>
1062     /// Add the elements from another collection with a more specialized item type
1063     /// to this collection. If this
1064     /// collection has set semantics, only items not already in the collection
1065     /// will be added.
1066     /// </summary>
1067     /// <typeparam name="U">The type of items to add</typeparam>
1068     /// <param name="items">The items to add</param>
1069     [Tested]
1070     public void AddAll<U>(SCG.IEnumerable<U> items) where U : T
1071     {
1072       if (!isValid)
1073         throw new ViewDisposedException("Snapshot has been disposed");
1074       updatecheck();
1075 
1076       int c = 0;
1077       T j = default(T);
1078       bool tmp;
1079 
1080       bool raiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0;
1081       CircularQueue<T> wasAdded = raiseAdded ? new CircularQueue<T>() : null;
1082 
1083       foreach (T i in items)
1084         if (addIterative(i, ref j, false, out tmp))
1085         {
1086           c++;
1087           if (raiseAdded)
1088             wasAdded.Enqueue(tmp ? j : i);
1089         }
1090       if (c == 0)
1091         return;
1092 
1093       size += c;
1094       //TODO: implement a RaiseForAddAll() method
1095       if (raiseAdded)
1096         foreach (T item in wasAdded)
1097           raiseItemsAdded(item, 1);
1098       if (((ActiveEvents & EventTypeEnum.Changed) != 0))
1099         raiseCollectionChanged();
1100     }
1101 
1102 
1103     /// <summary>
1104     /// Add all the items from another collection with an enumeration order that
1105     /// is increasing in the items. <para>The idea is that the implementation may use
1106     /// a faster algorithm to merge the two collections.</para>
1107     /// <exception cref="ArgumentException"/> if the enumerated items turns out
1108     /// not to be in increasing order.
1109     /// </summary>
1110     /// <param name="items">The collection to add.</param>
1111     /// <typeparam name="U"></typeparam>
1112     [Tested]
1113     public void AddSorted<U>(SCG.IEnumerable<U> items) where U : T
1114     {
1115       if (size > 0)
1116         AddAll(items);
1117       else
1118       {
1119         if (!isValid)
1120           throw new ViewDisposedException("Snapshot has been disposed");
1121         updatecheck();
1122         addSorted(items, true, true);
1123       }
1124     }
1125 
1126     #region add-sorted helpers
1127 
1128     //Create a RB tree from x+2^h-1  (x < 2^h, h>=1) nodes taken from a
1129     //singly linked list of red nodes using only the right child refs.
1130     //The x nodes at depth h+1 will be red, the rest black.
1131     //(h is the blackdepth of the resulting tree)
maketreer(ref Node rest, int blackheight, int maxred, int red)1132     static Node maketreer(ref Node rest, int blackheight, int maxred, int red)
1133     {
1134       if (blackheight == 1)
1135       {
1136         Node top = rest;
1137 
1138         rest = rest.right;
1139         if (red > 0)
1140         {
1141           top.right = null;
1142           rest.left = top;
1143           top = rest;
1144 #if BAG
1145           top.size += top.left.size;
1146 #elif MAINTAIN_SIZE
1147           top.size = 1 + red;
1148 #endif
1149           rest = rest.right;
1150           red--;
1151         }
1152 
1153         if (red > 0)
1154         {
1155 #if BAG
1156           top.size += rest.size;
1157 #endif
1158           top.right = rest;
1159           rest = rest.right;
1160           top.right.right = null;
1161         }
1162         else
1163           top.right = null;
1164 
1165         top.red = false;
1166         return top;
1167       }
1168       else
1169       {
1170         maxred >>= 1;
1171 
1172         int lred = red > maxred ? maxred : red;
1173         Node left = maketreer(ref rest, blackheight - 1, maxred, lred);
1174         Node top = rest;
1175 
1176         rest = rest.right;
1177         top.left = left;
1178         top.red = false;
1179         top.right = maketreer(ref rest, blackheight - 1, maxred, red - lred);
1180 #if BAG
1181         top.size = top.items + top.left.size + top.right.size;
1182 #elif MAINTAIN_SIZE
1183         top.size = (maxred << 1) - 1 + red;
1184 #endif
1185         return top;
1186       }
1187     }
1188 
1189 
1190     void addSorted<U>(SCG.IEnumerable<U> items, bool safe, bool raise) where U : T
1191     {
1192       SCG.IEnumerator<U> e = items.GetEnumerator(); ;
1193       if (size > 0)
1194         throw new InternalException("This can't happen");
1195 
1196       if (!e.MoveNext())
1197         return;
1198 
1199       //To count theCollect
1200       Node head = new Node(), tail = head;
1201       int z = 1;
1202       T lastitem = tail.item = e.Current;
1203 #if BAG
1204       int ec = 0;
1205 #endif
1206 
1207       while (e.MoveNext())
1208       {
1209 #if BAG
1210         T thisitem = e.Current;
1211         int comp = comparer.Compare(lastitem, thisitem);
1212         if (comp > 0)
1213           throw new ArgumentException("Argument not sorted");
1214         if (comp == 0)
1215         {
1216           tail.items++;
1217           ec++;
1218         }
1219         else
1220         {
1221           tail.size = tail.items;
1222           z++;
1223           tail.right = new Node();
1224           tail = tail.right;
1225           lastitem = tail.item = thisitem;
1226 #if NCP
1227           tail.generation = generation;
1228 #endif
1229         }
1230 #else
1231         z++;
1232         tail.right = new Node();
1233         tail = tail.right;
1234         tail.item = e.Current;
1235         if (safe)
1236         {
1237           if (comparer.Compare(lastitem, tail.item) >= 0)
1238             throw new ArgumentException("Argument not sorted");
1239 
1240           lastitem = tail.item;
1241         }
1242 #if NCP
1243         tail.generation = generation;
1244 #endif
1245 #endif
1246       }
1247 #if BAG
1248       tail.size = tail.items;
1249 #endif
1250       int blackheight = 0, red = z, maxred = 1;
1251 
1252       while (maxred <= red)
1253       {
1254         red -= maxred;
1255         maxred <<= 1;
1256         blackheight++;
1257       }
1258 
1259       root = TreeBag<T>.maketreer(ref head, blackheight, maxred, red);
1260       blackdepth = blackheight;
1261       size = z;
1262 #if BAG
1263       size += ec;
1264 #endif
1265 
1266       if (raise)
1267       {
1268         if ((ActiveEvents & EventTypeEnum.Added) != 0)
1269         {
1270           CircularQueue<T> wasAdded = new CircularQueue<T>();
1271           foreach (T item in this)
1272             wasAdded.Enqueue(item);
1273           foreach (T item in wasAdded)
1274             raiseItemsAdded(item, 1);
1275         }
1276         if ((ActiveEvents & EventTypeEnum.Changed) != 0)
1277           raiseCollectionChanged();
1278       }
1279       return;
1280     }
1281 
1282     #endregion
1283 
1284 #if BAG
1285     /// <summary></summary>
1286     /// <value>True since this collection has bag semantics.</value>
1287     [Tested]
1288     public bool AllowsDuplicates { [Tested]get { return true; } }
1289 #else
1290     /// <summary></summary>
1291     /// <value>False since this tree has set semantics.</value>
1292     [Tested]
1293     public bool AllowsDuplicates { [Tested]get { return false; } }
1294 #endif
1295     /// <summary>
1296     /// By convention this is true for any collection with set semantics.
1297     /// </summary>
1298     /// <value>True if only one representative of a group of equal items
1299     /// is kept in the collection together with the total count.</value>
1300     public virtual bool DuplicatesByCounting { get { return true; } }
1301 
1302     #endregion
1303 
1304     #region IEditableCollection<T> Members
1305 
1306 
1307     /// <summary>
1308     /// The value is symbolic indicating the type of asymptotic complexity
1309     /// in terms of the size of this collection (worst-case or amortized as
1310     /// relevant).
1311     /// </summary>
1312     /// <value>Speed.Log</value>
1313     [Tested]
1314     public Speed ContainsSpeed { [Tested]get { return Speed.Log; } }
1315 
1316     /// <summary>
1317     /// Check if this collection contains (an item equivalent to according to the
1318     /// itemequalityComparer) a particular value.
1319     /// </summary>
1320     /// <param name="item">The value to check for.</param>
1321     /// <returns>True if the items is in this collection.</returns>
1322     [Tested]
Contains(T item)1323     public bool Contains(T item)
1324     {
1325       if (!isValid)
1326         throw new ViewDisposedException("Snapshot has been disposed");
1327       Node next; int comp = 0;
1328 
1329       next = root;
1330       while (next != null)
1331       {
1332         comp = comparer.Compare(next.item, item);
1333         if (comp == 0)
1334           return true;
1335 
1336         next = comp < 0 ? right(next) : left(next);
1337       }
1338 
1339       return false;
1340     }
1341 
1342 
1343     //Variant for dictionary use
1344     //Will return the actual matching item in the ref argument.
1345     /// <summary>
1346     /// Check if this collection contains an item equivalent according to the
1347     /// itemequalityComparer to a particular value. If so, return in the ref argument (a
1348     /// binary copy of) the actual value found.
1349     /// </summary>
1350     /// <param name="item">The value to look for.</param>
1351     /// <returns>True if the items is in this collection.</returns>
1352     [Tested]
Find(ref T item)1353     public bool Find(ref T item)
1354     {
1355       if (!isValid)
1356         throw new ViewDisposedException("Snapshot has been disposed");
1357       Node next; int comp = 0;
1358 
1359       next = root;
1360       while (next != null)
1361       {
1362         comp = comparer.Compare(next.item, item);
1363         if (comp == 0)
1364         {
1365           item = next.item;
1366           return true;
1367         }
1368 
1369         next = comp < 0 ? right(next) : left(next);
1370       }
1371 
1372       return false;
1373     }
1374 
1375 
1376     /// <summary>
1377     /// Find or add the item to the tree. If the tree does not contain
1378     /// an item equivalent to this item add it, else return the exisiting
1379     /// one in the ref argument.
1380     ///
1381     /// </summary>
1382     /// <param name="item"></param>
1383     /// <returns>True if item was found</returns>
1384     [Tested]
FindOrAdd(ref T item)1385     public bool FindOrAdd(ref T item)
1386     {
1387       if (!isValid)
1388         throw new ViewDisposedException("Snapshot has been disposed");
1389       updatecheck();
1390       bool wasfound;
1391 
1392       //Note: blackdepth of the tree is set inside addIterative
1393       if (addIterative(item, ref item, false, out wasfound))
1394       {
1395         size++;
1396         if (ActiveEvents != 0 && !wasfound)
1397           raiseForAdd(item);
1398         return wasfound;
1399       }
1400       else
1401         return true;
1402 
1403     }
1404 
1405 
1406     //For dictionary use.
1407     //If found, the matching entry will be updated with the new item.
1408     /// <summary>
1409     /// Check if this collection contains an item equivalent according to the
1410     /// itemequalityComparer to a particular value. If so, update the item in the collection
1411     /// to with a binary copy of the supplied value. If the collection has bag semantics,
1412     /// this updates all equivalent copies in
1413     /// the collection.
1414     /// </summary>
1415     /// <param name="item">Value to update.</param>
1416     /// <returns>True if the item was found and hence updated.</returns>
1417     [Tested]
Update(T item)1418     public bool Update(T item)
1419     {
1420       T olditem = item;
1421       return Update(item, out olditem);
1422     }
1423 
1424     /// <summary>
1425     /// Check if this collection contains an item equivalent according to the
1426     /// itemequalityComparer to a particular value. If so, update the item in the collection
1427     /// with a binary copy of the supplied value. If the collection has bag semantics,
1428     /// this updates all equivalent copies in
1429     /// the collection.
1430     /// </summary>
1431     /// <param name="item"></param>
1432     /// <param name="olditem"></param>
1433     /// <returns></returns>
Update(T item, out T olditem)1434     public bool Update(T item, out T olditem)
1435     {
1436       if (!isValid)
1437         throw new ViewDisposedException("Snapshot has been disposed");
1438       updatecheck();
1439 #if NCP
1440       stackcheck();
1441 
1442       int level = 0;
1443 #endif
1444       Node cursor = root;
1445       int comp = 0;
1446 
1447       while (cursor != null)
1448       {
1449         comp = comparer.Compare(cursor.item, item);
1450         if (comp == 0)
1451         {
1452 #if NCP
1453           Node.CopyNode(ref cursor, maxsnapid, generation);
1454 #endif
1455           olditem = cursor.item;
1456 #if BAG
1457           int items = cursor.items;
1458 #endif
1459           cursor.item = item;
1460 #if NCP
1461           while (level > 0)
1462           {
1463             Node child = cursor;
1464 
1465             cursor = path[--level];
1466             path[level] = null;
1467 #if NCP
1468             Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
1469 #else
1470 						if (Node.CopyNode(maxsnapid, ref cursor, generation))
1471 						{
1472 							if (dirs[level] > 0)
1473 								cursor.left = child;
1474 							else
1475 								cursor.right = child;
1476 						}
1477 #endif
1478           }
1479 
1480           root = cursor;
1481 #endif
1482 #if BAG
1483           if (ActiveEvents != 0)
1484             raiseForUpdate(item, olditem, items);
1485 #else
1486           if (ActiveEvents != 0)
1487             raiseForUpdate(item, olditem);
1488 #endif
1489           return true;
1490         }
1491 #if NCP
1492         dirs[level] = comp;
1493         path[level++] = cursor;
1494 #endif
1495         cursor = comp < 0 ? cursor.right : cursor.left;
1496       }
1497 
1498       olditem = default(T);
1499       return false;
1500     }
1501 
1502 
1503     /// <summary>
1504     /// Check if this collection contains an item equivalent according to the
1505     /// itemequalityComparer to a particular value. If so, update the item in the collection
1506     /// with a binary copy of the supplied value; else add the value to the collection.
1507     ///
1508     /// <i>NOTE: the bag implementation is currently wrong! ?????</i>
1509     /// </summary>
1510     /// <param name="item">Value to add or update.</param>
1511     /// <returns>True if the item was found and updated (hence not added).</returns>
1512     [Tested]
UpdateOrAdd(T item)1513     public bool UpdateOrAdd(T item)
1514     { T olditem; return UpdateOrAdd(item, out olditem); }
1515 
1516     /// <summary>
1517     ///
1518     /// </summary>
1519     /// <param name="item"></param>
1520     /// <param name="olditem"></param>
1521     /// <returns></returns>
UpdateOrAdd(T item, out T olditem)1522     public bool UpdateOrAdd(T item, out T olditem)
1523     {
1524       if (!isValid)
1525         throw new ViewDisposedException("Snapshot has been disposed");
1526       updatecheck();
1527       bool wasfound;
1528       olditem = default(T);
1529 
1530 
1531       //Note: blackdepth of the tree is set inside addIterative
1532       if (addIterative(item, ref olditem, true, out wasfound))
1533       {
1534         size++;
1535         if (ActiveEvents != 0)
1536           raiseForAdd(wasfound ? olditem : item);
1537         return wasfound;
1538       }
1539       else
1540       {
1541 #warning for bag implementation: count is wrong
1542         if (ActiveEvents != 0)
1543           raiseForUpdate(item, olditem, 1);
1544         return true;
1545       }
1546     }
1547 
1548 
1549     /// <summary>
1550     /// Remove a particular item from this collection. If the collection has bag
1551     /// semantics only one copy equivalent to the supplied item is removed.
1552     /// </summary>
1553     /// <param name="item">The value to remove.</param>
1554     /// <returns>True if the item was found (and removed).</returns>
1555     [Tested]
Remove(T item)1556     public bool Remove(T item)
1557     {
1558       if (!isValid)
1559         throw new ViewDisposedException("Snapshot has been disposed");
1560       updatecheck();
1561       if (root == null)
1562         return false;
1563 
1564       int junk;
1565       bool retval = removeIterative(ref item, false, out junk);
1566       if (ActiveEvents != 0 && retval)
1567         raiseForRemove(item);
1568       return retval;
1569     }
1570 
1571     /// <summary>
1572     /// Remove a particular item from this collection if found. If the collection
1573     /// has bag semantics only one copy equivalent to the supplied item is removed,
1574     /// which one is implementation dependent.
1575     /// If an item was removed, report a binary copy of the actual item removed in
1576     /// the argument.
1577     /// </summary>
1578     /// <param name="item">The value to remove.</param>
1579     /// <param name="removeditem">The removed value.</param>
1580     /// <returns>True if the item was found (and removed).</returns>
1581     [Tested]
Remove(T item, out T removeditem)1582     public bool Remove(T item, out T removeditem)
1583     {
1584       if (!isValid)
1585         throw new ViewDisposedException("Snapshot has been disposed");
1586       updatecheck();
1587       removeditem = item;
1588       if (root == null)
1589         return false;
1590 
1591       int junk;
1592       bool retval = removeIterative(ref removeditem, false, out junk);
1593       if (ActiveEvents != 0 && retval)
1594         raiseForRemove(item);
1595       return retval;
1596     }
1597 
1598     /// <summary>
1599     ///
1600     /// </summary>
1601     /// <param name="item">input: item to remove; output: item actually removed</param>
1602     /// <param name="all">If true, remove all copies</param>
1603     /// <param name="wasRemoved"></param>
1604     /// <returns></returns>
removeIterative(ref T item, bool all, out int wasRemoved)1605     private bool removeIterative(ref T item, bool all, out int wasRemoved)
1606     {
1607       wasRemoved = 0;
1608       //Stage 1: find item
1609       stackcheck();
1610 
1611       int level = 0, comp;
1612       Node cursor = root;
1613 
1614       while (true)
1615       {
1616         comp = comparer.Compare(cursor.item, item);
1617         if (comp == 0)
1618         {
1619           item = cursor.item;
1620 #if BAG
1621           if (!all && cursor.items > 1)
1622           {
1623 #if NCP
1624             Node.CopyNode(ref cursor, maxsnapid, generation);
1625 #endif
1626             cursor.items--;
1627             cursor.size--;
1628             while (level-- > 0)
1629             {
1630               Node kid = cursor;
1631 
1632               cursor = path[level];
1633 #if NCP
1634               Node.update(ref cursor, dirs[level] > 0, kid, maxsnapid, generation);
1635 #endif
1636               cursor.size--;
1637               path[level] = null;
1638             }
1639             size--;
1640             wasRemoved = 1;
1641             return true;
1642           }
1643           wasRemoved = cursor.items;
1644 #else
1645           wasRemoved = 1;
1646 #endif
1647           break;
1648         }
1649 
1650         Node child = comp > 0 ? cursor.left : cursor.right;
1651 
1652         if (child == null)
1653           return false;
1654 
1655         dirs[level] = comp;
1656         path[level++] = cursor;
1657         cursor = child;
1658       }
1659 
1660       return removeIterativePhase2(cursor, level);
1661     }
1662 
1663 
removeIterativePhase2(Node cursor, int level)1664 private bool removeIterativePhase2(Node cursor, int level)
1665     {
1666       if (size == 1)
1667       {
1668         clear();
1669         return true;
1670       }
1671 
1672 #if BAG
1673       size -= cursor.items;
1674 #else
1675       //We are certain to remove one node:
1676       size--;
1677 #endif
1678       //Stage 2: if item's node has no null child, find predecessor
1679       int level_of_item = level;
1680 
1681       if (cursor.left != null && cursor.right != null)
1682       {
1683         dirs[level] = 1;
1684         path[level++] = cursor;
1685         cursor = cursor.left;
1686         while (cursor.right != null)
1687         {
1688           dirs[level] = -1;
1689           path[level++] = cursor;
1690           cursor = cursor.right;
1691         }
1692 #if NCP
1693         Node.CopyNode(ref path[level_of_item], maxsnapid, generation);
1694 #endif
1695         path[level_of_item].item = cursor.item;
1696 #if BAG
1697         path[level_of_item].items = cursor.items;
1698 #endif
1699       }
1700 
1701       //Stage 3: splice out node to be removed
1702       Node newchild = cursor.right == null ? cursor.left : cursor.right;
1703       bool demote_or_rotate = newchild == null && !cursor.red;
1704 
1705       //assert newchild.red
1706       if (newchild != null)
1707       {
1708         newchild.red = false;
1709       }
1710 
1711       if (level == 0)
1712       {
1713         root = newchild;
1714         return true;
1715       }
1716 
1717       level--;
1718       cursor = path[level];
1719       path[level] = null;
1720 
1721       int comp = dirs[level];
1722       Node childsibling;
1723 #if NCP
1724       Node.update(ref cursor, comp > 0, newchild, maxsnapid, generation);
1725 #else
1726 			if (comp > 0)
1727 				cursor.left = newchild;
1728 			else
1729 				cursor.right = newchild;
1730 #endif
1731       childsibling = comp > 0 ? cursor.right : cursor.left;
1732 #if BAG
1733       cursor.size = cursor.items + (newchild == null ? 0 : newchild.size) + (childsibling == null ? 0 : childsibling.size);
1734 #elif MAINTAIN_SIZE
1735       cursor.size--;
1736 #endif
1737 
1738       //Stage 4: demote till we must rotate
1739       Node farnephew = null, nearnephew = null;
1740 
1741       while (demote_or_rotate)
1742       {
1743         if (childsibling.red)
1744           break; //rotate 2+?
1745 
1746         farnephew = comp > 0 ? childsibling.right : childsibling.left;
1747         if (farnephew != null && farnephew.red)
1748           break; //rotate 1b
1749 
1750         nearnephew = comp > 0 ? childsibling.left : childsibling.right;
1751         if (nearnephew != null && nearnephew.red)
1752           break; //rotate 1c
1753 
1754         //demote cursor
1755         childsibling.red = true;
1756         if (level == 0)
1757         {
1758           cursor.red = false;
1759           blackdepth--;
1760 #if NCP
1761           root = cursor;
1762 #endif
1763           return true;
1764         }
1765         else if (cursor.red)
1766         {
1767           cursor.red = false;
1768           demote_or_rotate = false;
1769           break; //No rotation
1770         }
1771         else
1772         {
1773           Node child = cursor;
1774 
1775           cursor = path[--level];
1776           path[level] = null;
1777           comp = dirs[level];
1778           childsibling = comp > 0 ? cursor.right : cursor.left;
1779 #if NCP
1780           Node.update(ref cursor, comp > 0, child, maxsnapid, generation);
1781 #endif
1782 #if BAG
1783           cursor.size = cursor.items + (child == null ? 0 : child.size) + (childsibling == null ? 0 : childsibling.size);
1784 #elif MAINTAIN_SIZE
1785           cursor.size--;
1786 #endif
1787         }
1788       }
1789 
1790       //Stage 5: rotate
1791       if (demote_or_rotate)
1792       {
1793         //At start:
1794         //parent = cursor (temporary for swapping nodes)
1795         //childsibling is the sibling of the updated child (x)
1796         //cursor is always the top of the subtree
1797         Node parent = cursor;
1798 
1799         if (childsibling.red)
1800         {//Case 2 and perhaps more.
1801           //The y.rank == px.rank >= x.rank+2 >=2 so both nephews are != null
1802           //(and black). The grandnephews are children of nearnephew
1803           Node neargrandnephew, fargrandnephew;
1804 
1805           if (comp > 0)
1806           {
1807             nearnephew = childsibling.left;
1808             farnephew = childsibling.right;
1809             neargrandnephew = nearnephew.left;
1810             fargrandnephew = nearnephew.right;
1811           }
1812           else
1813           {
1814             nearnephew = childsibling.right;
1815             farnephew = childsibling.left;
1816             neargrandnephew = nearnephew.right;
1817             fargrandnephew = nearnephew.left;
1818           }
1819 
1820           if (fargrandnephew != null && fargrandnephew.red)
1821           {//Case 2+1b
1822 #if NCP
1823             Node.CopyNode(ref nearnephew, maxsnapid, generation);
1824 
1825             //The end result of this will always be e copy of parent
1826             Node.update(ref parent, comp < 0, neargrandnephew, maxsnapid, generation);
1827             Node.update(ref childsibling, comp > 0, nearnephew, maxsnapid, generation);
1828 #endif
1829             if (comp > 0)
1830             {
1831               nearnephew.left = parent;
1832               parent.right = neargrandnephew;
1833             }
1834             else
1835             {
1836               nearnephew.right = parent;
1837               parent.left = neargrandnephew;
1838             }
1839 
1840             cursor = childsibling;
1841             childsibling.red = false;
1842             nearnephew.red = true;
1843             fargrandnephew.red = false;
1844 #if BAG
1845             cursor.size = parent.size;
1846             nearnephew.size = cursor.size - cursor.items - farnephew.size;
1847             parent.size = nearnephew.size - nearnephew.items - fargrandnephew.size;
1848 #elif MAINTAIN_SIZE
1849             cursor.size = parent.size;
1850             nearnephew.size = cursor.size - 1 - farnephew.size;
1851             parent.size = nearnephew.size - 1 - fargrandnephew.size;
1852 #endif
1853           }
1854           else if (neargrandnephew != null && neargrandnephew.red)
1855           {//Case 2+1c
1856 #if NCP
1857             Node.CopyNode(ref neargrandnephew, maxsnapid, generation);
1858 #endif
1859             if (comp > 0)
1860             {
1861 #if NCP
1862               Node.update(ref childsibling, true, neargrandnephew, maxsnapid, generation);
1863               Node.update(ref nearnephew, true, neargrandnephew.right, maxsnapid, generation);
1864               Node.update(ref parent, false, neargrandnephew.left, maxsnapid, generation);
1865 #else
1866 							childsibling.left = neargrandnephew;
1867 							nearnephew.left = neargrandnephew.right;
1868 							parent.right = neargrandnephew.left;
1869 #endif
1870               neargrandnephew.left = parent;
1871               neargrandnephew.right = nearnephew;
1872             }
1873             else
1874             {
1875 #if NCP
1876               Node.update(ref childsibling, false, neargrandnephew, maxsnapid, generation);
1877               Node.update(ref nearnephew, false, neargrandnephew.left, maxsnapid, generation);
1878               Node.update(ref parent, true, neargrandnephew.right, maxsnapid, generation);
1879 #else
1880 							childsibling.right = neargrandnephew;
1881 							nearnephew.right = neargrandnephew.left;
1882 							parent.left = neargrandnephew.right;
1883 #endif
1884               neargrandnephew.right = parent;
1885               neargrandnephew.left = nearnephew;
1886             }
1887 
1888             cursor = childsibling;
1889             childsibling.red = false;
1890 #if BAG
1891             cursor.size = parent.size;
1892             parent.size = parent.items + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
1893             nearnephew.size = nearnephew.items + (nearnephew.left == null ? 0 : nearnephew.left.size) + (nearnephew.right == null ? 0 : nearnephew.right.size);
1894             neargrandnephew.size = neargrandnephew.items + parent.size + nearnephew.size;
1895 #elif MAINTAIN_SIZE
1896             cursor.size = parent.size;
1897             parent.size = 1 + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
1898             nearnephew.size = 1 + (nearnephew.left == null ? 0 : nearnephew.left.size) + (nearnephew.right == null ? 0 : nearnephew.right.size);
1899             neargrandnephew.size = 1 + parent.size + nearnephew.size;
1900 #endif
1901           }
1902           else
1903           {//Case 2 only
1904 #if NCP
1905             Node.update(ref parent, comp < 0, nearnephew, maxsnapid, generation);
1906             Node.update(ref childsibling, comp > 0, parent, maxsnapid, generation);
1907 #else
1908 						if (comp > 0)
1909 						{
1910 							childsibling.left = parent;
1911 							parent.right = nearnephew;
1912 						}
1913 						else
1914 						{
1915 							childsibling.right = parent;
1916 							parent.left = nearnephew;
1917 						}
1918 #endif
1919             cursor = childsibling;
1920             childsibling.red = false;
1921             nearnephew.red = true;
1922 #if BAG
1923             cursor.size = parent.size;
1924             parent.size -= farnephew.size + cursor.items;
1925 #elif MAINTAIN_SIZE
1926             cursor.size = parent.size;
1927             parent.size -= farnephew.size + 1;
1928 #endif
1929           }
1930         }
1931         else if (farnephew != null && farnephew.red)
1932         {//Case 1b
1933           nearnephew = comp > 0 ? childsibling.left : childsibling.right;
1934 #if NCP
1935           Node.update(ref parent, comp < 0, nearnephew, maxsnapid, generation);
1936           Node.CopyNode(ref childsibling, maxsnapid, generation);
1937           if (comp > 0)
1938           {
1939             childsibling.left = parent;
1940             childsibling.right = farnephew;
1941           }
1942           else
1943           {
1944             childsibling.right = parent;
1945             childsibling.left = farnephew;
1946           }
1947 #else
1948 					if (comp > 0)
1949 					{
1950 						childsibling.left = parent;
1951 						parent.right = nearnephew;
1952 					}
1953 					else
1954 					{
1955 						childsibling.right = parent;
1956 						parent.left = nearnephew;
1957 					}
1958 #endif
1959           cursor = childsibling;
1960           cursor.red = parent.red;
1961           parent.red = false;
1962           farnephew.red = false;
1963 
1964 #if BAG
1965           cursor.size = parent.size;
1966           parent.size -= farnephew.size + cursor.items;
1967 #elif MAINTAIN_SIZE
1968           cursor.size = parent.size;
1969           parent.size -= farnephew.size + 1;
1970 #endif
1971         }
1972         else if (nearnephew != null && nearnephew.red)
1973         {//Case 1c
1974 #if NCP
1975           Node.CopyNode(ref nearnephew, maxsnapid, generation);
1976 #endif
1977           if (comp > 0)
1978           {
1979 #if NCP
1980             Node.update(ref childsibling, true, nearnephew.right, maxsnapid, generation);
1981             Node.update(ref parent, false, nearnephew.left, maxsnapid, generation);
1982 #else
1983 						childsibling.left = nearnephew.right;
1984 						parent.right = nearnephew.left;
1985 #endif
1986             nearnephew.left = parent;
1987             nearnephew.right = childsibling;
1988           }
1989           else
1990           {
1991 #if NCP
1992             Node.update(ref childsibling, false, nearnephew.left, maxsnapid, generation);
1993             Node.update(ref parent, true, nearnephew.right, maxsnapid, generation);
1994 #else
1995 						childsibling.right = nearnephew.left;
1996 						parent.left = nearnephew.right;
1997 #endif
1998             nearnephew.right = parent;
1999             nearnephew.left = childsibling;
2000           }
2001 
2002           cursor = nearnephew;
2003           cursor.red = parent.red;
2004           parent.red = false;
2005 #if BAG
2006           cursor.size = parent.size;
2007           parent.size = parent.items + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
2008           childsibling.size = childsibling.items + (childsibling.left == null ? 0 : childsibling.left.size) + (childsibling.right == null ? 0 : childsibling.right.size);
2009 #elif MAINTAIN_SIZE
2010           cursor.size = parent.size;
2011           parent.size = 1 + (parent.left == null ? 0 : parent.left.size) + (parent.right == null ? 0 : parent.right.size);
2012           childsibling.size = 1 + (childsibling.left == null ? 0 : childsibling.left.size) + (childsibling.right == null ? 0 : childsibling.right.size);
2013 #endif
2014         }
2015         else
2016         {//Case 1a can't happen
2017           throw new InternalException("Case 1a can't happen here");
2018         }
2019 
2020         //Resplice cursor:
2021         if (level == 0)
2022         {
2023           root = cursor;
2024         }
2025         else
2026         {
2027           Node swap = cursor;
2028 
2029           cursor = path[--level];
2030           path[level] = null;
2031 #if NCP
2032           Node.update(ref cursor, dirs[level] > 0, swap, maxsnapid, generation);
2033 #else
2034 
2035 					if (dirs[level] > 0)
2036 						cursor.left = swap;
2037 					else
2038 						cursor.right = swap;
2039 #endif
2040 #if BAG
2041           cursor.size = cursor.items + (cursor.right == null ? 0 : cursor.right.size) + (cursor.left == null ? 0 : cursor.left.size);
2042 #elif MAINTAIN_SIZE
2043           cursor.size--;
2044 #endif
2045         }
2046       }
2047 
2048       //Stage 6: fixup to the root
2049       while (level > 0)
2050       {
2051         Node child = cursor;
2052 
2053         cursor = path[--level];
2054         path[level] = null;
2055 #if NCP
2056         if (child != (dirs[level] > 0 ? cursor.left : cursor.right))
2057           Node.update(ref cursor, dirs[level] > 0, child, maxsnapid, generation);
2058 #endif
2059 #if BAG
2060         cursor.size = cursor.items + (cursor.right == null ? 0 : cursor.right.size) + (cursor.left == null ? 0 : cursor.left.size);
2061 #elif MAINTAIN_SIZE
2062         cursor.size--;
2063 #endif
2064       }
2065 
2066 #if NCP
2067       root = cursor;
2068 #endif
2069       return true;
2070     }
2071 
2072 
2073     /// <summary>
2074     /// Remove all items from this collection.
2075     /// </summary>
2076     [Tested]
Clear()2077     public void Clear()
2078     {
2079       if (!isValid)
2080         throw new ViewDisposedException("Snapshot has been disposed");
2081       updatecheck();
2082       if (size == 0)
2083         return;
2084       int oldsize = size;
2085       clear();
2086       if ((ActiveEvents & EventTypeEnum.Cleared) != 0)
2087         raiseCollectionCleared(true, oldsize);
2088       if ((ActiveEvents & EventTypeEnum.Changed) != 0)
2089         raiseCollectionChanged();
2090     }
2091 
2092 
clear()2093     private void clear()
2094     {
2095       size = 0;
2096       root = null;
2097       blackdepth = 0;
2098     }
2099 
2100 
2101     /// <summary>
2102     /// Remove all items in another collection from this one. If this collection
2103     /// has bag semantics, take multiplicities into account.
2104     /// </summary>
2105     /// <typeparam name="U"></typeparam>
2106     /// <param name="items">The items to remove.</param>
2107     [Tested]
2108     public void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
2109     {
2110       if (!isValid)
2111         throw new ViewDisposedException("Snapshot has been disposed");
2112       updatecheck();
2113 
2114       T jtem;
2115 
2116       bool mustRaise = (ActiveEvents & (EventTypeEnum.Removed | EventTypeEnum.Changed)) != 0;
2117       RaiseForRemoveAllHandler raiseHandler = mustRaise ? new RaiseForRemoveAllHandler(this) : null;
2118 
2119       foreach (T item in items)
2120       {
2121         if (root == null)
2122           break;
2123 
2124         jtem = item;
2125         int junk;
2126         if (removeIterative(ref jtem, false, out junk) && mustRaise)
2127           raiseHandler.Remove(jtem);
2128       }
2129       if (mustRaise)
2130         raiseHandler.Raise();
2131     }
2132 
2133     /// <summary>
2134     /// Remove all items not in some other collection from this one. If this collection
2135     /// has bag semantics, take multiplicities into account.
2136     /// </summary>
2137     /// <typeparam name="U"></typeparam>
2138     /// <param name="items">The items to retain.</param>
2139     [Tested]
2140     public void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
2141     {
2142       if (!isValid)
2143         throw new ViewDisposedException("Snapshot has been disposed");
2144       updatecheck();
2145 
2146       //A much more efficient version is possible if items is sorted like this.
2147       //Well, it is unclear how efficient it would be.
2148       //We could use a marking method!?
2149 #warning how does this work together with persistence?
2150       TreeBag<T> t = (TreeBag<T>)MemberwiseClone();
2151 
2152       T jtem = default(T);
2153       t.clear();
2154       foreach (T item in items)
2155         if (ContainsCount(item) > t.ContainsCount(item))
2156         {
2157           t.add(item, ref jtem);
2158         }
2159       if (size == t.size)
2160         return;
2161 
2162 #warning improve (mainly for bag) by using a Node iterator instead of ItemMultiplicities()
2163       CircularQueue<KeyValuePair<T, int>> wasRemoved = null;
2164       if ((ActiveEvents & EventTypeEnum.Removed) != 0)
2165       {
2166         wasRemoved = new CircularQueue<KeyValuePair<T, int>>();
2167         SCG.IEnumerator<KeyValuePair<T, int>> ie = ItemMultiplicities().GetEnumerator();
2168         foreach (KeyValuePair<T, int> p in t.ItemMultiplicities())
2169         {
2170           //We know p.Key is in this!
2171           while (ie.MoveNext())
2172           {
2173             if (comparer.Compare(ie.Current.Key, p.Key) == 0)
2174             {
2175 #if BAG
2176               int removed = ie.Current.Value - p.Value;
2177               if (removed > 0)
2178                 wasRemoved.Enqueue(new KeyValuePair<T,int>(p.Key, removed));
2179 #endif
2180               break;
2181             }
2182             else
2183               wasRemoved.Enqueue(ie.Current);
2184           }
2185         }
2186         while (ie.MoveNext())
2187           wasRemoved.Enqueue(ie.Current);
2188       }
2189 
2190       root = t.root;
2191       size = t.size;
2192       blackdepth = t.blackdepth;
2193       if (wasRemoved != null)
2194         foreach (KeyValuePair<T, int> p in wasRemoved)
2195           raiseItemsRemoved(p.Key, p.Value);
2196       if ((ActiveEvents & EventTypeEnum.Changed) != 0)
2197         raiseCollectionChanged();
2198     }
2199 
2200     /// <summary>
2201     /// Check if this collection contains all the values in another collection.
2202     /// If this collection has bag semantics (<code>AllowsDuplicates==true</code>)
2203     /// the check is made with respect to multiplicities, else multiplicities
2204     /// are not taken into account.
2205     /// </summary>
2206     /// <param name="items">The </param>
2207     /// <typeparam name="U"></typeparam>
2208     /// <returns>True if all values in <code>items</code>is in this collection.</returns>
2209     [Tested]
2210     public bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
2211     {
2212       //TODO: fix bag implementation
2213       if (!isValid)
2214         throw new ViewDisposedException("Snapshot has been disposed");
2215       //This is worst-case O(m*logn)
2216       foreach (T item in items)
2217         if (!Contains(item)) return false;
2218 
2219       return true;
2220     }
2221 
2222 
2223     //Higher order:
2224     /// <summary>
2225     /// Create a new indexed sorted collection consisting of the items of this
2226     /// indexed sorted collection satisfying a certain predicate.
2227     /// </summary>
2228     /// <param name="filter">The filter delegate defining the predicate.</param>
2229     /// <returns>The new indexed sorted collection.</returns>
2230     [Tested]
FindAll(Fun<T, bool> filter)2231     public IIndexedSorted<T> FindAll(Fun<T, bool> filter)
2232     {
2233       if (!isValid)
2234         throw new ViewDisposedException("Snapshot has been disposed");
2235       TreeBag<T> res = new TreeBag<T>(comparer);
2236       SCG.IEnumerator<T> e = GetEnumerator();
2237       Node head = null, tail = null;
2238       int z = 0;
2239 #if BAG
2240       int ec = 0;
2241 #endif
2242       while (e.MoveNext())
2243       {
2244         T thisitem = e.Current;
2245 #if BAG
2246         //We could document that filter will only be called
2247         //once on each unique item. That might even be good for the user!
2248         if (tail != null && comparer.Compare(thisitem, tail.item) == 0)
2249         {
2250           tail.items++;
2251           ec++;
2252           continue;
2253         }
2254 #endif
2255         if (filter(thisitem))
2256         {
2257           if (head == null)
2258           {
2259             head = tail = new Node();
2260           }
2261           else
2262           {
2263 #if BAG
2264             tail.size = tail.items;
2265 #endif
2266             tail.right = new Node();
2267             tail = tail.right;
2268           }
2269 
2270           tail.item = thisitem;
2271           z++;
2272         }
2273       }
2274 #if BAG
2275       if (tail != null)
2276         tail.size = tail.items;
2277 #endif
2278 
2279       if (z == 0)
2280         return res;
2281 
2282       int blackheight = 0, red = z, maxred = 1;
2283 
2284       while (maxred <= red)
2285       {
2286         red -= maxred;
2287         maxred <<= 1;
2288         blackheight++;
2289       }
2290 
2291       res.root = TreeBag<T>.maketreer(ref head, blackheight, maxred, red);
2292       res.blackdepth = blackheight;
2293       res.size = z;
2294 #if BAG
2295       res.size += ec;
2296 #endif
2297       return res;
2298     }
2299 
2300 
2301     /// <summary>
2302     /// Create a new indexed sorted collection consisting of the results of
2303     /// mapping all items of this list.
2304     /// <exception cref="ArgumentException"/> if the map is not increasing over
2305     /// the items of this collection (with respect to the two given comparison
2306     /// relations).
2307     /// </summary>
2308     /// <param name="mapper">The delegate definging the map.</param>
2309     /// <param name="c">The comparion relation to use for the result.</param>
2310     /// <returns>The new sorted collection.</returns>
2311     [Tested]
Map(Fun<T, V> mapper, SCG.IComparer<V> c)2312     public IIndexedSorted<V> Map<V>(Fun<T, V> mapper, SCG.IComparer<V> c)
2313     {
2314       if (!isValid)
2315         throw new ViewDisposedException("Snapshot has been disposed");
2316       TreeBag<V> res = new TreeBag<V>(c);
2317 
2318       if (size == 0)
2319         return res;
2320 
2321       SCG.IEnumerator<T> e = GetEnumerator();
2322       TreeBag<V>.Node head = null, tail = null;
2323       V oldv = default(V);
2324       int z = 0;
2325 #if BAG
2326       T lastitem = default(T);
2327 #endif
2328       while (e.MoveNext())
2329       {
2330         T thisitem = e.Current;
2331 #if BAG
2332         //We could document that mapper will only be called
2333         //once on each unique item. That might even be good for the user!
2334         if (tail != null && comparer.Compare(thisitem, lastitem) == 0)
2335         {
2336           tail.items++;
2337           continue;
2338         }
2339 #endif
2340         V newv = mapper(thisitem);
2341 
2342         if (head == null)
2343         {
2344           head = tail = new TreeBag<V>.Node();
2345           z++;
2346         }
2347         else
2348         {
2349           int comp = c.Compare(oldv, newv);
2350 #if BAG
2351           if (comp == 0)
2352           {
2353             tail.items++;
2354             continue;
2355           }
2356           if (comp > 0)
2357 #else
2358           if (comp >= 0)
2359 #endif
2360             throw new ArgumentException("mapper not monotonic");
2361 #if BAG
2362           tail.size = tail.items;
2363 #endif
2364           tail.right = new TreeBag<V>.Node();
2365           tail = tail.right;
2366           z++;
2367         }
2368 #if BAG
2369         lastitem = thisitem;
2370 #endif
2371         tail.item = oldv = newv;
2372       }
2373 
2374 #if BAG
2375       tail.size = tail.items;
2376 #endif
2377 
2378       int blackheight = 0, red = z, maxred = 1;
2379 
2380       while (maxred <= red)
2381       {
2382         red -= maxred;
2383         maxred <<= 1;
2384         blackheight++;
2385       }
2386 
2387       res.root = TreeBag<V>.maketreer(ref head, blackheight, maxred, red);
2388       res.blackdepth = blackheight;
2389       res.size = size;
2390       return res;
2391     }
2392 
2393 
2394     //below is the bag utility stuff
2395     /// <summary>
2396     /// Count the number of items of the collection equal to a particular value.
2397     /// Returns 0 if and only if the value is not in the collection.
2398     /// </summary>
2399     /// <param name="item">The value to count.</param>
2400     /// <returns>The number of copies found.</returns>
2401     [Tested]
ContainsCount(T item)2402     public int ContainsCount(T item)
2403     {
2404       if (!isValid)
2405         throw new ViewDisposedException("Snapshot has been disposed");
2406 #if BAG
2407       Node next; int comp = 0;
2408 
2409       next = root;
2410       while (next != null)
2411       {
2412         comp = comparer.Compare(next.item, item);
2413         if (comp == 0)
2414           return next.items;
2415 
2416         next = comp < 0 ? right(next) : left(next);
2417       }
2418 
2419       return 0;
2420 #else
2421       //Since we are strictly not AllowsDuplicates we just do
2422       return Contains(item) ? 1 : 0;
2423 #endif
2424     }
2425 
2426 #if BAG
2427     //TODO: make work with snapshots
2428     class Multiplicities : CollectionValueBase<KeyValuePair<T, int>>, ICollectionValue<KeyValuePair<T, int>>
2429     {
2430       TreeBag<T> treebag;
2431       int origstamp;
Multiplicities(TreeBag<T> treebag)2432       internal Multiplicities(TreeBag<T> treebag) { this.treebag = treebag; this.origstamp = treebag.stamp; }
Choose()2433       public override KeyValuePair<T, int> Choose() { return new KeyValuePair<T, int>(treebag.root.item, treebag.root.items); }
2434 
GetEnumerator()2435       public override SCG.IEnumerator<KeyValuePair<T, int>> GetEnumerator()
2436       {
2437         return getEnumerator(treebag.root, origstamp); //TODO: NBNBNB
2438       }
2439 
getEnumerator(Node node, int origstamp)2440       private SCG.IEnumerator<KeyValuePair<T, int>> getEnumerator(Node node, int origstamp)
2441       {
2442         if (node == null)
2443           yield break;
2444 
2445         if (node.left != null)
2446         {
2447           SCG.IEnumerator<KeyValuePair<T, int>> child = getEnumerator(node.left, origstamp);
2448 
2449           while (child.MoveNext())
2450           {
2451             treebag.modifycheck(origstamp);
2452             yield return child.Current;
2453           }
2454         }
2455         yield return new KeyValuePair<T, int>(node.item, node.items);
2456         if (node.right != null)
2457         {
2458           SCG.IEnumerator<KeyValuePair<T, int>> child = getEnumerator(node.right, origstamp);
2459 
2460           while (child.MoveNext())
2461           {
2462             treebag.modifycheck(origstamp);
2463             yield return child.Current;
2464           }
2465         }
2466       }
2467 
2468       public override bool IsEmpty { get { return treebag.IsEmpty; } }
2469       public override int Count { get { int i = 0; foreach (KeyValuePair<T, int> p in this) i++; return i; } } //TODO: make better
2470       public override Speed CountSpeed { get { return Speed.Linear; } } //TODO: make better
2471     }
2472 #endif
2473 
2474     /// <summary>
2475     ///
2476     /// </summary>
2477     /// <returns></returns>
UniqueItems()2478     public virtual ICollectionValue<T> UniqueItems()
2479     {
2480       if (!isValid)
2481         throw new ViewDisposedException("Snapshot has been disposed");
2482 #if BAG
2483       return new DropMultiplicity<T>(ItemMultiplicities());
2484 #else
2485       return this;
2486 #endif
2487     }
2488 
2489 
2490     /// <summary>
2491     ///
2492     /// </summary>
2493     /// <returns></returns>
ItemMultiplicities()2494     public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
2495     {
2496       if (!isValid)
2497         throw new ViewDisposedException("Snapshot has been disposed");
2498 #if BAG
2499       return new Multiplicities(this);
2500 #else
2501       return new MultiplicityOne<T>(this);
2502 #endif
2503     }
2504 
2505     /// <summary>
2506     /// Remove all items equivalent to a given value.
2507     /// </summary>
2508     /// <param name="item">The value to remove.</param>
2509     [Tested]
RemoveAllCopies(T item)2510     public void RemoveAllCopies(T item)
2511     {
2512 #if BAG
2513       if (!isValid)
2514         throw new ViewDisposedException("Snapshot has been disposed");
2515       updatecheck();
2516       int removed;
2517       if (removeIterative(ref item, true, out removed) && ActiveEvents != 0)
2518       {
2519         raiseForRemove(item, removed);
2520       }
2521 #else
2522       Remove(item);
2523 #endif
2524     }
2525 
2526 
2527     #endregion
2528 
2529     #region IIndexed<T> Members
2530 
findNode(int i)2531     private Node findNode(int i)
2532     {
2533 #if NCP
2534       if (isSnapShot)
2535         throw new NotSupportedException("Indexing not supported for snapshots");
2536 #endif
2537 #if MAINTAIN_SIZE
2538       Node next = root;
2539 
2540       if (i >= 0 && i < size)
2541         while (true)
2542         {
2543           int j = next.left == null ? 0 : next.left.size;
2544 
2545           if (i > j)
2546           {
2547 #if BAG
2548             i -= j + next.items;
2549             if (i < 0)
2550               return next;
2551 #else
2552             i -= j + 1;
2553 #endif
2554             next = next.right;
2555           }
2556           else if (i == j)
2557             return next;
2558           else
2559             next = next.left;
2560         }
2561 
2562       throw new IndexOutOfRangeException();
2563 #else
2564 			throw new NotSupportedException();
2565 #endif
2566     }
2567 
2568 
2569     /// <summary>
2570     /// <exception cref="IndexOutOfRangeException"/> if i is negative or
2571     /// &gt;= the size of the collection.
2572     /// </summary>
2573     /// <value>The i'th item of this list.</value>
2574     /// <param name="i">the index to lookup</param>
2575     [Tested]
2576     public T this[int i] { [Tested]	get { return findNode(i).item; } }
2577 
2578     /// <summary>
2579     ///
2580     /// </summary>
2581     /// <value></value>
2582     public virtual Speed IndexingSpeed { get { return Speed.Log; } }
2583 
2584 
2585     //TODO: return -upper instead of -1 in case of not found
2586     /// <summary>
2587     /// Searches for an item in this indexed collection going forwards from the start.
2588     /// </summary>
2589     /// <param name="item">Item to search for.</param>
2590     /// <returns>Index of first occurrence from start of the item
2591     /// if found, else the two-complement
2592     /// (always negative) of the index at which the item would be put if it was added.</returns>
2593     [Tested]
IndexOf(T item)2594     public int IndexOf(T item)
2595     {
2596       if (!isValid)
2597         throw new ViewDisposedException("Snapshot has been disposed");
2598       int upper;
2599       return indexOf(item, out upper);
2600     }
2601 
2602 
indexOf(T item, out int upper)2603     private int indexOf(T item, out int upper)
2604     {
2605 #if NCP
2606       if (isSnapShot)
2607         throw new NotSupportedException("Indexing not supported for snapshots");
2608 #endif
2609 #if MAINTAIN_SIZE
2610       int ind = 0; Node next = root;
2611 
2612       while (next != null)
2613       {
2614         int comp = comparer.Compare(item, next.item);
2615 
2616         if (comp < 0)
2617           next = next.left;
2618         else
2619         {
2620           int leftcnt = next.left == null ? 0 : next.left.size;
2621 
2622           if (comp == 0)
2623           {
2624 #if BAG
2625             upper = ind + leftcnt + next.items - 1;
2626             return ind + leftcnt;
2627 #else
2628             return upper = ind + leftcnt;
2629 #endif
2630           }
2631           else
2632           {
2633 #if BAG
2634             ind = ind + next.items + leftcnt;
2635 #else
2636             ind = ind + 1 + leftcnt;
2637 #endif
2638             next = next.right;
2639           }
2640         }
2641       }
2642 #endif
2643       upper = ~ind;
2644       return ~ind;
2645     }
2646 
2647 
2648     /// <summary>
2649     /// Searches for an item in the tree going backwords from the end.
2650     /// </summary>
2651     /// <param name="item">Item to search for.</param>
2652     /// <returns>Index of last occurrence from the end of item if found,
2653     /// else the two-complement (always negative) of the index at which
2654     /// the item would be put if it was added.</returns>
2655     [Tested]
LastIndexOf(T item)2656     public int LastIndexOf(T item)
2657     {
2658       if (!isValid)
2659         throw new ViewDisposedException("Snapshot has been disposed");
2660 #if BAG
2661       int res;
2662       indexOf(item, out res);
2663       return res;
2664 #else
2665       //We have AllowsDuplicates==false for the set
2666       return IndexOf(item);
2667 #endif
2668     }
2669 
2670 
2671     /// <summary>
2672     /// Remove the item at a specific position of the list.
2673     /// <exception cref="IndexOutOfRangeException"/> if i is negative or
2674     /// &gt;= the size of the collection.
2675     /// </summary>
2676     /// <param name="i">The index of the item to remove.</param>
2677     /// <returns>The removed item.</returns>
2678     [Tested]
RemoveAt(int i)2679     public T RemoveAt(int i)
2680     {
2681       if (!isValid)
2682         throw new ViewDisposedException("Snapshot has been disposed");
2683       updatecheck();
2684       T retval = removeAt(i);
2685       if (ActiveEvents != 0)
2686         raiseForRemove(retval);
2687       return retval;
2688     }
2689 
removeAt(int i)2690     T removeAt(int i)
2691     {
2692       if (!isValid)
2693         throw new ViewDisposedException("Snapshot has been disposed");
2694       updatecheck();
2695 #if MAINTAIN_SIZE
2696       if (i < 0 || i >= size)
2697         throw new IndexOutOfRangeException("Index out of range for sequenced collectionvalue");
2698 
2699       //We must follow the pattern of removeIterative()
2700       while (dirs.Length < 2 * blackdepth)
2701       {
2702         dirs = new int[2 * dirs.Length];
2703         path = new Node[2 * dirs.Length];
2704       }
2705 
2706       int level = 0;
2707       Node cursor = root;
2708 
2709       while (true)
2710       {
2711         int j = cursor.left == null ? 0 : cursor.left.size;
2712 
2713         if (i > j)
2714         {
2715 #if BAG
2716           i -= j + cursor.items;
2717           if (i < 0)
2718             break;
2719 #else
2720           i -= j + 1;
2721 #endif
2722           dirs[level] = -1;
2723           path[level++] = cursor;
2724           cursor = cursor.right;
2725         }
2726         else if (i == j)
2727           break;
2728         else
2729         {
2730           dirs[level] = 1;
2731           path[level++] = cursor;
2732           cursor = cursor.left;
2733         }
2734       }
2735 
2736       T retval = cursor.item;
2737 
2738 #if BAG
2739       if (cursor.items > 1)
2740       {
2741         resplicebag(level, cursor);
2742         size--;
2743         return retval;
2744       }
2745 #endif
2746       removeIterativePhase2(cursor, level);
2747       return retval;
2748 #else
2749 			throw new NotSupportedException();
2750 #endif
2751     }
2752 
2753 #if BAG
resplicebag(int level, Node cursor)2754     private void resplicebag(int level, Node cursor)
2755     {
2756 #if NCP
2757       Node.CopyNode(ref cursor, maxsnapid, generation);
2758 #endif
2759       cursor.items--;
2760       cursor.size--;
2761       while (level-- > 0)
2762       {
2763         Node kid = cursor;
2764 
2765         cursor = path[level];
2766 #if NCP
2767         Node.update(ref cursor, dirs[level] > 0, kid, maxsnapid, generation);
2768 #endif
2769         cursor.size--;
2770         path[level] = null;
2771       }
2772     }
2773 #endif
2774     /// <summary>
2775     /// Remove all items in an index interval.
2776     /// <exception cref="IndexOutOfRangeException"/>???.
2777     /// </summary>
2778     /// <param name="start">The index of the first item to remove.</param>
2779     /// <param name="count">The number of items to remove.</param>
2780     [Tested]
RemoveInterval(int start, int count)2781     public void RemoveInterval(int start, int count)
2782     {
2783       if (!isValid)
2784         throw new ViewDisposedException("Snapshot has been disposed");
2785       if (start < 0 || count < 0 || start + count > this.size)
2786         throw new ArgumentOutOfRangeException();
2787 
2788       updatecheck();
2789 
2790       if (count == 0)
2791         return;
2792 
2793       //This is terrible for large count. We should split the tree at
2794       //the endpoints of the range and fuse the parts!
2795       //We really need good internal destructive split and catenate functions!
2796       //Alternative for large counts: rebuild tree using maketree()
2797       for (int i = 0; i < count; i++)
2798         removeAt(start);
2799 
2800       if ((ActiveEvents & EventTypeEnum.Cleared) != 0)
2801         raiseCollectionCleared(false, count);
2802       if ((ActiveEvents & EventTypeEnum.Changed) != 0)
2803         raiseCollectionChanged();
2804     }
2805 
2806 
2807     /// <summary>
2808     /// <exception cref="IndexOutOfRangeException"/>.
2809     /// </summary>
2810     /// <value>The directed collection of items in a specific index interval.</value>
2811     /// <param name="start">The starting index of the interval (inclusive).</param>
2812     /// <param name="count">The length of the interval.</param>
2813     [Tested]
2814     public IDirectedCollectionValue<T> this[int start, int count]
2815     {
2816       [Tested]
2817       get
2818       {
2819         checkRange(start, count);
2820         return new Interval(this, start, count, true);
2821       }
2822     }
2823 
2824     #region Interval nested class
2825     class Interval : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
2826     {
2827       readonly int start, length, stamp;
2828 
2829       readonly bool forwards;
2830 
2831       readonly TreeBag<T> tree;
2832 
2833 
Interval(TreeBag<T> tree, int start, int count, bool forwards)2834       internal Interval(TreeBag<T> tree, int start, int count, bool forwards)
2835       {
2836 #if NCP
2837         if (tree.isSnapShot)
2838           throw new NotSupportedException("Indexing not supported for snapshots");
2839 #endif
2840         this.start = start; this.length = count; this.forwards = forwards;
2841         this.tree = tree; this.stamp = tree.stamp;
2842       }
2843 
2844       public override bool IsEmpty { get { return length == 0; } }
2845 
2846       [Tested]
2847       public override int Count { [Tested]get { return length; } }
2848 
2849 
2850       public override Speed CountSpeed { get { return Speed.Constant; } }
2851 
2852 
Choose()2853       public override T Choose()
2854       {
2855         if (length == 0)
2856           throw new NoSuchItemException();
2857         return tree[start];
2858       }
2859 
2860       [Tested]
GetEnumerator()2861       public override SCG.IEnumerator<T> GetEnumerator()
2862       {
2863 #if MAINTAIN_SIZE
2864         tree.modifycheck(stamp);
2865 #if BAG
2866         int togo;
2867 #endif
2868         Node cursor = tree.root;
2869         Node[] path = new Node[2 * tree.blackdepth];
2870         int level = 0, totaltogo = length;
2871 
2872         if (totaltogo == 0)
2873           yield break;
2874 
2875         if (forwards)
2876         {
2877           int i = start;
2878 
2879           while (true)
2880           {
2881             int j = cursor.left == null ? 0 : cursor.left.size;
2882 
2883             if (i > j)
2884             {
2885 #if BAG
2886               if (cursor.items > i - j) {
2887                 togo = cursor.items - (i - j);
2888                 break;
2889               }
2890               i -= j + cursor.items;
2891 #else
2892               i -= j + 1;
2893 #endif
2894               cursor = cursor.right;
2895             }
2896             else if (i == j)
2897             {
2898 #if BAG
2899               togo = cursor.items;
2900 #endif
2901               break;
2902             } // i < j, start point tree[start] is in left subtree
2903             else
2904             {
2905               path[level++] = cursor;
2906               cursor = cursor.left;
2907             }
2908           }
2909 
2910           T current = cursor.item;
2911 
2912           while (totaltogo-- > 0)
2913           {
2914             yield return current;
2915             tree.modifycheck(stamp);
2916 #if BAG
2917             if (--togo > 0)
2918               continue;
2919 #endif
2920             if (cursor.right != null)
2921             {
2922               path[level] = cursor = cursor.right;
2923               while (cursor.left != null)
2924                 path[++level] = cursor = cursor.left;
2925             }
2926             else if (level == 0)
2927               yield break;
2928             else
2929               cursor = path[--level];
2930 
2931             current = cursor.item;
2932 #if BAG
2933             togo = cursor.items;
2934 #endif
2935           }
2936         }
2937         else // backwards
2938         {
2939           int i = start + length - 1;
2940 
2941           while (true)
2942           {
2943             int j = cursor.left == null ? 0 : cursor.left.size;
2944 
2945             if (i > j)
2946             {
2947 #if BAG
2948               if (i - j < cursor.items)
2949               {
2950                 togo = i - j + 1;
2951                 break;
2952               }
2953               i -= j + cursor.items;
2954 #else
2955               i -= j + 1;
2956 #endif
2957               path[level++] = cursor;
2958               cursor = cursor.right;
2959             }
2960             else if (i == j)
2961             {
2962 #if BAG
2963               togo = 1;
2964 #endif
2965               break;
2966             }
2967             else // i <= j, end point tree[start+count-1] is in left subtree
2968             {
2969               cursor = cursor.left;
2970             }
2971           }
2972 
2973           T current = cursor.item;
2974 
2975           while (totaltogo-- > 0)
2976           {
2977             yield return current;
2978             tree.modifycheck(stamp);
2979 #if BAG
2980             if (--togo > 0)
2981               continue;
2982 #endif
2983             if (cursor.left != null)
2984             {
2985               path[level] = cursor = cursor.left;
2986               while (cursor.right != null)
2987                 path[++level] = cursor = cursor.right;
2988             }
2989             else if (level == 0)
2990               yield break;
2991             else
2992               cursor = path[--level];
2993 
2994             current = cursor.item;
2995 #if BAG
2996             togo = cursor.items;
2997 #endif
2998           }
2999         }
3000 
3001 #else
3002 			throw new NotSupportedException();
3003 #endif
3004       }
3005 
3006 
3007       [Tested]
Backwards()3008       public override IDirectedCollectionValue<T> Backwards()
3009       { return new Interval(tree, start, length, !forwards); }
3010 
3011 
3012       [Tested]
Backwards()3013       IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards()
3014       { return Backwards(); }
3015 
3016 
3017       [Tested]
3018       public override EnumerationDirection Direction
3019       {
3020         [Tested]
3021         get
3022         {
3023           return forwards ? EnumerationDirection.Forwards : EnumerationDirection.Backwards;
3024         }
3025       }
3026     }
3027     #endregion
3028 
3029     /// <summary>
3030     /// Create a collection containing the same items as this collection, but
3031     /// whose enumerator will enumerate the items backwards. The new collection
3032     /// will become invalid if the original is modified. Method typicaly used as in
3033     /// <code>foreach (T x in coll.Backwards()) {...}</code>
3034     /// </summary>
3035     /// <returns>The backwards collection.</returns>
3036     [Tested]
Backwards()3037     public override IDirectedCollectionValue<T> Backwards() { return RangeAll().Backwards(); }
3038 
3039 
3040     [Tested]
Backwards()3041     IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
3042 
3043     #endregion
3044 
3045     #region PriorityQueue Members
3046 
3047     /// <summary>
3048     /// The comparer object supplied at creation time for this collection
3049     /// </summary>
3050     /// <value>The comparer</value>
3051     public SCG.IComparer<T> Comparer { get { return comparer; } }
3052 
3053 
3054     /// <summary>
3055     /// Find the current least item of this priority queue.
3056     /// </summary>
3057     /// <returns>The least item.</returns>
3058     [Tested]
FindMin()3059     public T FindMin()
3060     {
3061       if (!isValid)
3062         throw new ViewDisposedException("Snapshot has been disposed");
3063       if (size == 0)
3064         throw new NoSuchItemException();
3065       Node cursor = root, next = left(cursor);
3066 
3067       while (next != null)
3068       {
3069         cursor = next;
3070         next = left(cursor);
3071       }
3072 
3073       return cursor.item;
3074     }
3075 
3076 
3077     /// <summary>
3078     /// Remove the least item from this  priority queue.
3079     /// </summary>
3080     /// <returns>The removed item.</returns>
3081     [Tested]
DeleteMin()3082     public T DeleteMin()
3083     {
3084       if (!isValid)
3085         throw new ViewDisposedException("Snapshot has been disposed");
3086       updatecheck();
3087 
3088       //persistence guard?
3089       if (size == 0)
3090         throw new NoSuchItemException();
3091 
3092       //We must follow the pattern of removeIterative()
3093       stackcheck();
3094 
3095       T retval = deleteMin();
3096       if (ActiveEvents != 0)
3097       {
3098         raiseItemsRemoved(retval, 1);
3099         raiseCollectionChanged();
3100       }
3101       return retval;
3102     }
3103 
deleteMin()3104     private T deleteMin()
3105     {
3106       int level = 0;
3107       Node cursor = root;
3108 
3109       while (cursor.left != null)
3110       {
3111         dirs[level] = 1;
3112         path[level++] = cursor;
3113         cursor = cursor.left;
3114       }
3115 
3116       T retval = cursor.item;
3117 
3118 #if BAG
3119       if (cursor.items > 1)
3120       {
3121         resplicebag(level, cursor);
3122         size--;
3123         return retval;
3124       }
3125 #endif
3126       removeIterativePhase2(cursor, level);
3127       return retval;
3128     }
3129 
3130 
3131     /// <summary>
3132     /// Find the current largest item of this priority queue.
3133     /// </summary>
3134     /// <returns>The largest item.</returns>
3135     [Tested]
FindMax()3136     public T FindMax()
3137     {
3138       if (!isValid)
3139         throw new ViewDisposedException("Snapshot has been disposed");
3140       if (size == 0)
3141         throw new NoSuchItemException();
3142 
3143       Node cursor = root, next = right(cursor);
3144 
3145       while (next != null)
3146       {
3147         cursor = next;
3148         next = right(cursor);
3149       }
3150 
3151       return cursor.item;
3152     }
3153 
3154 
3155     /// <summary>
3156     /// Remove the largest item from this  priority queue.
3157     /// </summary>
3158     /// <returns>The removed item.</returns>
3159     [Tested]
DeleteMax()3160     public T DeleteMax()
3161     {
3162       if (!isValid)
3163         throw new ViewDisposedException("Snapshot has been disposed");
3164       //persistence guard?
3165       updatecheck();
3166       if (size == 0)
3167         throw new NoSuchItemException();
3168 
3169       //We must follow the pattern of removeIterative()
3170       stackcheck();
3171 
3172       T retval = deleteMax();
3173       if (ActiveEvents != 0)
3174       {
3175         raiseItemsRemoved(retval, 1);
3176         raiseCollectionChanged();
3177       }
3178       return retval;
3179     }
3180 
deleteMax()3181     private T deleteMax()
3182     {
3183       int level = 0;
3184       Node cursor = root;
3185 
3186       while (cursor.right != null)
3187       {
3188         dirs[level] = -1;
3189         path[level++] = cursor;
3190         cursor = cursor.right;
3191       }
3192 
3193       T retval = cursor.item;
3194 
3195 #if BAG
3196       if (cursor.items > 1)
3197       {
3198         resplicebag(level, cursor);
3199         size--;
3200         return retval;
3201       }
3202 #endif
3203       removeIterativePhase2(cursor, level);
3204       return retval;
3205     }
3206     #endregion
3207 
3208     #region ISorted<T> Members
3209 
3210     /// <summary>
3211     /// Find the strict predecessor of item in the sorted collection,
3212     /// that is, the greatest item in the collection smaller than the item.
3213     /// </summary>
3214     /// <param name="item">The item to find the predecessor for.</param>
3215     /// <param name="res">The predecessor, if any; otherwise the default value for T.</param>
3216     /// <returns>True if item has a predecessor; otherwise false.</returns>
TryPredecessor(T item, out T res)3217     public bool TryPredecessor(T item, out T res)
3218     {
3219         if (!isValid)
3220             throw new ViewDisposedException("Snapshot has been disposed");
3221         Node cursor = root, bestsofar = null;
3222 
3223         while (cursor != null)
3224         {
3225             int comp = comparer.Compare(cursor.item, item);
3226 
3227             if (comp < 0)
3228             {
3229                 bestsofar = cursor;
3230                 cursor = right(cursor);
3231             }
3232             else if (comp == 0)
3233             {
3234                 cursor = left(cursor);
3235                 while (cursor != null)
3236                 {
3237                     bestsofar = cursor;
3238                     cursor = right(cursor);
3239                 }
3240             }
3241             else
3242                 cursor = left(cursor);
3243         }
3244         if (bestsofar == null)
3245         {
3246             res = default(T);
3247             return false;
3248         }
3249         else
3250         {
3251             res = bestsofar.item;
3252             return true;
3253         }
3254     }
3255 
3256 
3257     /// <summary>
3258     /// Find the strict successor of item in the sorted collection,
3259     /// that is, the least item in the collection greater than the supplied value.
3260     /// </summary>
3261     /// <param name="item">The item to find the successor for.</param>
3262     /// <param name="res">The successor, if any; otherwise the default value for T.</param>
3263     /// <returns>True if item has a successor; otherwise false.</returns>
TrySuccessor(T item, out T res)3264     public bool TrySuccessor(T item, out T res)
3265     {
3266         if (!isValid)
3267             throw new ViewDisposedException("Snapshot has been disposed");
3268         Node cursor = root, bestsofar = null;
3269 
3270         while (cursor != null)
3271         {
3272             int comp = comparer.Compare(cursor.item, item);
3273 
3274             if (comp > 0)
3275             {
3276                 bestsofar = cursor;
3277                 cursor = left(cursor);
3278             }
3279             else if (comp == 0)
3280             {
3281                 cursor = right(cursor);
3282                 while (cursor != null)
3283                 {
3284                     bestsofar = cursor;
3285                     cursor = left(cursor);
3286                 }
3287             }
3288             else
3289                 cursor = right(cursor);
3290         }
3291 
3292         if (bestsofar == null)
3293         {
3294             res = default(T);
3295             return false;
3296         }
3297         else
3298         {
3299             res = bestsofar.item;
3300             return true;
3301         }
3302     }
3303 
3304 
3305     /// <summary>
3306     /// Find the weak predecessor of item in the sorted collection,
3307     /// that is, the greatest item in the collection smaller than or equal to the item.
3308     /// </summary>
3309     /// <param name="item">The item to find the weak predecessor for.</param>
3310     /// <param name="res">The weak predecessor, if any; otherwise the default value for T.</param>
3311     /// <returns>True if item has a weak predecessor; otherwise false.</returns>
TryWeakPredecessor(T item, out T res)3312     public bool TryWeakPredecessor(T item, out T res)
3313     {
3314         if (!isValid)
3315             throw new ViewDisposedException("Snapshot has been disposed");
3316         Node cursor = root, bestsofar = null;
3317 
3318         while (cursor != null)
3319         {
3320             int comp = comparer.Compare(cursor.item, item);
3321 
3322             if (comp < 0)
3323             {
3324                 bestsofar = cursor;
3325                 cursor = right(cursor);
3326             }
3327             else if (comp == 0)
3328             {
3329                 res = cursor.item;
3330                 return true;
3331             }
3332             else
3333                 cursor = left(cursor);
3334         }
3335         if (bestsofar == null)
3336         {
3337             res = default(T);
3338             return false;
3339         }
3340         else
3341         {
3342             res = bestsofar.item;
3343             return true;
3344         }
3345     }
3346 
3347 
3348     /// <summary>
3349     /// Find the weak successor of item in the sorted collection,
3350     /// that is, the least item in the collection greater than or equal to the supplied value.
3351     /// </summary>
3352     /// <param name="item">The item to find the weak successor for.</param>
3353     /// <param name="res">The weak successor, if any; otherwise the default value for T.</param>
3354     /// <returns>True if item has a weak successor; otherwise false.</returns>
TryWeakSuccessor(T item, out T res)3355     public bool TryWeakSuccessor(T item, out T res)
3356     {
3357         if (!isValid)
3358             throw new ViewDisposedException("Snapshot has been disposed");
3359         Node cursor = root, bestsofar = null;
3360 
3361         while (cursor != null)
3362         {
3363             int comp = comparer.Compare(cursor.item, item);
3364 
3365             if (comp == 0)
3366             {
3367                 res = cursor.item;
3368                 return true;
3369             }
3370             else if (comp > 0)
3371             {
3372                 bestsofar = cursor;
3373                 cursor = left(cursor);
3374             }
3375             else
3376                 cursor = right(cursor);
3377         }
3378 
3379         if (bestsofar == null)
3380         {
3381             res = default(T);
3382             return false;
3383         }
3384         else
3385         {
3386             res = bestsofar.item;
3387             return true;
3388         }
3389     }
3390 
3391 
3392     /// <summary>
3393     /// Find the strict predecessor in the sorted collection of a particular value,
3394     /// i.e. the largest item in the collection less than the supplied value.
3395     /// </summary>
3396     /// <exception cref="NoSuchItemException"> if no such element exists (the
3397     /// supplied  value is less than or equal to the minimum of this collection.)</exception>
3398     /// <param name="item">The item to find the predecessor for.</param>
3399     /// <returns>The predecessor.</returns>
3400     [Tested]
Predecessor(T item)3401     public T Predecessor(T item)
3402     {
3403         T res;
3404         if (TryPredecessor(item, out res))
3405             return res;
3406         else
3407             throw new NoSuchItemException();
3408     }
3409 
3410 
3411     /// <summary>
3412     /// Find the weak predecessor in the sorted collection of a particular value,
3413     /// i.e. the largest item in the collection less than or equal to the supplied value.
3414     /// </summary>
3415     /// <exception cref="NoSuchItemException"> if no such element exists (the
3416     /// supplied  value is less than the minimum of this collection.)</exception>
3417     /// <param name="item">The item to find the weak predecessor for.</param>
3418     /// <returns>The weak predecessor.</returns>
3419     [Tested]
WeakPredecessor(T item)3420     public T WeakPredecessor(T item)
3421     {
3422         T res;
3423         if (TryWeakPredecessor(item, out res))
3424             return res;
3425         else
3426             throw new NoSuchItemException();
3427     }
3428 
3429 
3430     /// <summary>
3431     /// Find the strict successor in the sorted collection of a particular value,
3432     /// i.e. the least item in the collection greater than the supplied value.
3433     /// </summary>
3434     /// <exception cref="NoSuchItemException"> if no such element exists (the
3435     /// supplied  value is greater than or equal to the maximum of this collection.)</exception>
3436     /// <param name="item">The item to find the successor for.</param>
3437     /// <returns>The successor.</returns>
3438     [Tested]
Successor(T item)3439     public T Successor(T item)
3440     {
3441         T res;
3442         if (TrySuccessor(item, out res))
3443             return res;
3444         else
3445             throw new NoSuchItemException();
3446     }
3447 
3448 
3449     /// <summary>
3450     /// Find the weak successor in the sorted collection of a particular value,
3451     /// i.e. the least item in the collection greater than or equal to the supplied value.
3452     /// <exception cref="NoSuchItemException"> if no such element exists (the
3453     /// supplied  value is greater than the maximum of this collection.)</exception>
3454     /// </summary>
3455     /// <param name="item">The item to find the weak successor for.</param>
3456     /// <returns>The weak successor.</returns>
3457     [Tested]
WeakSuccessor(T item)3458     public T WeakSuccessor(T item)
3459     {
3460         T res;
3461         if (TryWeakSuccessor(item, out res))
3462             return res;
3463         else
3464             throw new NoSuchItemException();
3465     }
3466 
3467 
3468     /// <summary>
3469     /// Query this sorted collection for items greater than or equal to a supplied value.
3470     /// </summary>
3471     /// <param name="bot">The lower bound (inclusive).</param>
3472     /// <returns>The result directed collection.</returns>
3473     [Tested]
RangeFrom(T bot)3474     public IDirectedCollectionValue<T> RangeFrom(T bot)
3475     {
3476       if (!isValid)
3477         throw new ViewDisposedException("Snapshot has been disposed");
3478       return new Range(this, true, bot, false, default(T), EnumerationDirection.Forwards);
3479     }
3480 
3481 
3482     /// <summary>
3483     /// Query this sorted collection for items between two supplied values.
3484     /// </summary>
3485     /// <param name="bot">The lower bound (inclusive).</param>
3486     /// <param name="top">The upper bound (exclusive).</param>
3487     /// <returns>The result directed collection.</returns>
3488     [Tested]
RangeFromTo(T bot, T top)3489     public IDirectedCollectionValue<T> RangeFromTo(T bot, T top)
3490     {
3491       if (!isValid)
3492         throw new ViewDisposedException("Snapshot has been disposed");
3493       return new Range(this, true, bot, true, top, EnumerationDirection.Forwards);
3494     }
3495 
3496 
3497     /// <summary>
3498     /// Query this sorted collection for items less than a supplied value.
3499     /// </summary>
3500     /// <param name="top">The upper bound (exclusive).</param>
3501     /// <returns>The result directed collection.</returns>
3502     [Tested]
RangeTo(T top)3503     public IDirectedCollectionValue<T> RangeTo(T top)
3504     {
3505       if (!isValid)
3506         throw new ViewDisposedException("Snapshot has been disposed");
3507       return new Range(this, false, default(T), true, top, EnumerationDirection.Forwards);
3508     }
3509 
3510 
3511     /// <summary>
3512     /// Create a directed collection with the same items as this collection.
3513     /// </summary>
3514     /// <returns>The result directed collection.</returns>
3515     [Tested]
RangeAll()3516     public IDirectedCollectionValue<T> RangeAll()
3517     {
3518       if (!isValid)
3519         throw new ViewDisposedException("Snapshot has been disposed");
3520       return new Range(this, false, default(T), false, default(T), EnumerationDirection.Forwards);
3521     }
3522 
3523 
3524     [Tested]
RangeFrom(T bot)3525     IDirectedEnumerable<T> ISorted<T>.RangeFrom(T bot) { return RangeFrom(bot); }
3526 
3527 
3528     [Tested]
RangeFromTo(T bot, T top)3529     IDirectedEnumerable<T> ISorted<T>.RangeFromTo(T bot, T top) { return RangeFromTo(bot, top); }
3530 
3531 
3532     [Tested]
RangeTo(T top)3533     IDirectedEnumerable<T> ISorted<T>.RangeTo(T top) { return RangeTo(top); }
3534 
3535 
3536     //Utility for CountXxxx. Actually always called with strict = true.
countTo(T item, bool strict)3537     private int countTo(T item, bool strict)
3538     {
3539 #if NCP
3540       if (isSnapShot)
3541         throw new NotSupportedException("Indexing not supported for snapshots");
3542 #endif
3543 #if MAINTAIN_SIZE
3544       int ind = 0, comp = 0; Node next = root;
3545 
3546       while (next != null)
3547       {
3548         comp = comparer.Compare(item, next.item);
3549         if (comp < 0)
3550           next = next.left;
3551         else
3552         {
3553           int leftcnt = next.left == null ? 0 : next.left.size;
3554 #if BAG
3555           if (comp == 0)
3556             return strict ? ind + leftcnt : ind + leftcnt + next.items;
3557           else
3558           {
3559             ind = ind + next.items + leftcnt;
3560             next = next.right;
3561           }
3562 #else
3563           if (comp == 0)
3564             return strict ? ind + leftcnt : ind + leftcnt + 1;
3565           else
3566           {
3567             ind = ind + 1 + leftcnt;
3568             next = next.right;
3569           }
3570 #endif
3571         }
3572       }
3573 
3574       //if we get here, we are at the same side of the whole collection:
3575       return ind;
3576 #else
3577 			throw new NotSupportedException("Code compiled w/o size!");
3578 #endif
3579     }
3580 
3581 
3582     /// <summary>
3583     /// Perform a search in the sorted collection for the ranges in which a
3584     /// non-increasing (i.e. weakly decrerasing) function from the item type to
3585     /// <code>int</code> is
3586     /// negative, zero respectively positive. If the supplied cut function is
3587     /// not non-increasing, the result of this call is undefined.
3588     /// </summary>
3589     /// <param name="c">The cut function <code>T</code> to <code>int</code>, given
3590     /// as an <code>IComparable&lt;T&gt;</code> object, where the cut function is
3591     /// the <code>c.CompareTo(T that)</code> method.</param>
3592     /// <param name="low">Returns the largest item in the collection, where the
3593     /// cut function is positive (if any).</param>
3594     /// <param name="lowIsValid">True if the cut function is positive somewhere
3595     /// on this collection.</param>
3596     /// <param name="high">Returns the least item in the collection, where the
3597     /// cut function is negative (if any).</param>
3598     /// <param name="highIsValid">True if the cut function is negative somewhere
3599     /// on this collection.</param>
3600     /// <returns></returns>
3601     [Tested]
Cut(IComparable<T> c, out T low, out bool lowIsValid, out T high, out bool highIsValid)3602     public bool Cut(IComparable<T> c, out T low, out bool lowIsValid, out T high, out bool highIsValid)
3603     {
3604       if (!isValid)
3605         throw new ViewDisposedException("Snapshot has been disposed");
3606       Node cursor = root, lbest = null, rbest = null;
3607       bool res = false;
3608 
3609       while (cursor != null)
3610       {
3611         int comp = c.CompareTo(cursor.item);
3612 
3613         if (comp > 0)
3614         {
3615           lbest = cursor;
3616           cursor = right(cursor);
3617         }
3618         else if (comp < 0)
3619         {
3620           rbest = cursor;
3621           cursor = left(cursor);
3622         }
3623         else
3624         {
3625           res = true;
3626 
3627           Node tmp = left(cursor);
3628 
3629           while (tmp != null && c.CompareTo(tmp.item) == 0)
3630             tmp = left(tmp);
3631 
3632           if (tmp != null)
3633           {
3634             lbest = tmp;
3635             tmp = right(tmp);
3636             while (tmp != null)
3637             {
3638               if (c.CompareTo(tmp.item) > 0)
3639               {
3640                 lbest = tmp;
3641                 tmp = right(tmp);
3642               }
3643               else
3644                 tmp = left(tmp);
3645             }
3646           }
3647 
3648           tmp = right(cursor);
3649           while (tmp != null && c.CompareTo(tmp.item) == 0)
3650             tmp = right(tmp);
3651 
3652           if (tmp != null)
3653           {
3654             rbest = tmp;
3655             tmp = left(tmp);
3656             while (tmp != null)
3657             {
3658               if (c.CompareTo(tmp.item) < 0)
3659               {
3660                 rbest = tmp;
3661                 tmp = left(tmp);
3662               }
3663               else
3664                 tmp = right(tmp);
3665             }
3666           }
3667 
3668           break;
3669         }
3670       }
3671 
3672       if (highIsValid = (rbest != null))
3673         high = rbest.item;
3674       else
3675         high = default(T);
3676 
3677       if (lowIsValid = (lbest != null))
3678         low = lbest.item;
3679       else
3680         low = default(T);
3681 
3682       return res;
3683     }
3684 
3685 
3686     /// <summary>
3687     /// Determine the number of items at or above a supplied threshold.
3688     /// </summary>
3689     /// <param name="bot">The lower bound (inclusive)</param>
3690     /// <returns>The number of matcing items.</returns>
3691     [Tested]
CountFrom(T bot)3692     public int CountFrom(T bot)
3693     {
3694       if (!isValid)
3695         throw new ViewDisposedException("Snapshot has been disposed");
3696       return size - countTo(bot, true);
3697     }
3698 
3699 
3700     /// <summary>
3701     /// Determine the number of items between two supplied thresholds.
3702     /// </summary>
3703     /// <param name="bot">The lower bound (inclusive)</param>
3704     /// <param name="top">The upper bound (exclusive)</param>
3705     /// <returns>The number of matcing items.</returns>
3706     [Tested]
CountFromTo(T bot, T top)3707     public int CountFromTo(T bot, T top)
3708     {
3709       if (!isValid)
3710         throw new ViewDisposedException("Snapshot has been disposed");
3711       if (comparer.Compare(bot, top) >= 0)
3712         return 0;
3713 
3714       return countTo(top, true) - countTo(bot, true);
3715     }
3716 
3717 
3718     /// <summary>
3719     /// Determine the number of items below a supplied threshold.
3720     /// </summary>
3721     /// <param name="top">The upper bound (exclusive)</param>
3722     /// <returns>The number of matcing items.</returns>
3723     [Tested]
CountTo(T top)3724     public int CountTo(T top)
3725     {
3726       if (!isValid)
3727         throw new ViewDisposedException("Snapshot has been disposed");
3728       return countTo(top, true);
3729     }
3730 
3731 
3732     /// <summary>
3733     /// Remove all items of this collection above or at a supplied threshold.
3734     /// </summary>
3735     /// <param name="low">The lower threshold (inclusive).</param>
3736     [Tested]
RemoveRangeFrom(T low)3737     public void RemoveRangeFrom(T low)
3738     {
3739       if (!isValid)
3740         throw new ViewDisposedException("Snapshot has been disposed");
3741       updatecheck();
3742 
3743       int count = CountFrom(low);
3744 
3745       if (count == 0)
3746         return;
3747 
3748       stackcheck();
3749       CircularQueue<T> wasRemoved = (ActiveEvents & EventTypeEnum.Removed) != 0 ? new CircularQueue<T>() : null;
3750 
3751       for (int i = 0; i < count; i++)
3752       {
3753         T item = deleteMax();
3754         if (wasRemoved != null)
3755           wasRemoved.Enqueue(item);
3756       }
3757       if (wasRemoved != null)
3758         raiseForRemoveAll(wasRemoved);
3759       else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
3760         raiseCollectionChanged();
3761     }
3762 
3763 
3764     /// <summary>
3765     /// Remove all items of this collection between two supplied thresholds.
3766     /// </summary>
3767     /// <param name="low">The lower threshold (inclusive).</param>
3768     /// <param name="hi">The upper threshold (exclusive).</param>
3769     [Tested]
RemoveRangeFromTo(T low, T hi)3770     public void RemoveRangeFromTo(T low, T hi)
3771     {
3772       if (!isValid)
3773         throw new ViewDisposedException("Snapshot has been disposed");
3774       updatecheck();
3775 
3776       int count = CountFromTo(low, hi);
3777 
3778       if (count == 0)
3779         return;
3780 
3781       CircularQueue<T> wasRemoved = (ActiveEvents & EventTypeEnum.Removed) != 0 ? new CircularQueue<T>() : null;
3782       int junk;
3783       for (int i = 0; i < count; i++)
3784       {
3785         T item = Predecessor(hi);
3786         removeIterative(ref item, false, out junk);
3787         if (wasRemoved != null)
3788           wasRemoved.Enqueue(item);
3789       }
3790       if (wasRemoved != null)
3791         raiseForRemoveAll(wasRemoved);
3792       else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
3793         raiseCollectionChanged();
3794     }
3795 
3796 
3797     /// <summary>
3798     /// Remove all items of this collection below a supplied threshold.
3799     /// </summary>
3800     /// <param name="hi">The upper threshold (exclusive).</param>
3801     [Tested]
RemoveRangeTo(T hi)3802     public void RemoveRangeTo(T hi)
3803     {
3804       if (!isValid)
3805         throw new ViewDisposedException("Snapshot has been disposed");
3806       updatecheck();
3807 
3808       int count = CountTo(hi);
3809 
3810       if (count == 0)
3811         return;
3812 
3813       stackcheck();
3814       CircularQueue<T> wasRemoved = (ActiveEvents & EventTypeEnum.Removed) != 0 ? new CircularQueue<T>() : null;
3815 
3816       for (int i = 0; i < count; i++)
3817       {
3818         T item = deleteMin();
3819         if (wasRemoved != null)
3820           wasRemoved.Enqueue(item);
3821       }
3822       if (wasRemoved != null)
3823         raiseForRemoveAll(wasRemoved);
3824       else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
3825         raiseCollectionChanged();
3826     }
3827 
3828     #endregion
3829 
3830     #region IPersistent<T> Members
3831 #if NCP
3832     int maxsnapid { get { return snapList == null ? -1 : findLastLiveSnapShot(); } }
3833 
findLastLiveSnapShot()3834     int findLastLiveSnapShot()
3835     {
3836       if (snapList == null)
3837         return -1;
3838       SnapRef lastLiveSnapRef = snapList.Prev;
3839       object _snapshot = null;
3840       while (lastLiveSnapRef != null && (_snapshot = lastLiveSnapRef.Tree.Target) == null)
3841         lastLiveSnapRef = lastLiveSnapRef.Prev;
3842       if (lastLiveSnapRef == null)
3843       {
3844         snapList = null;
3845         return -1;
3846       }
3847       if (snapList.Prev != lastLiveSnapRef)
3848       {
3849         snapList.Prev = lastLiveSnapRef;
3850         lastLiveSnapRef.Next = snapList;
3851       }
3852       return ((TreeBag<T>)_snapshot).generation;
3853     }
3854 
3855     [Serializable]
3856     class SnapRef
3857     {
3858       public SnapRef Prev, Next;
3859       public WeakReference Tree;
SnapRef(TreeBag<T> tree)3860       public SnapRef(TreeBag<T> tree) { Tree = new WeakReference(tree); }
Dispose()3861       public void Dispose()
3862       {
3863         Next.Prev = Prev;
3864         if (Prev != null)
3865           Prev.Next = Next;
3866         Next = Prev = null;
3867       }
3868     }
3869 #endif
3870 
3871     /// <summary>
3872     /// If this tree is a snapshot, remove registration in base tree
3873     /// </summary>
3874     [Tested]
Dispose()3875     public void Dispose()
3876     {
3877 #if NCP
3878       if (!isValid)
3879         return;
3880       if (isSnapShot)
3881       {
3882         snapList.Dispose();
3883         snapDispose();
3884       }
3885       else
3886       {
3887         if (snapList != null)
3888         {
3889           SnapRef someSnapRef = snapList.Prev;
3890           while (someSnapRef != null)
3891           {
3892             TreeBag<T> lastsnap;
3893             if ((lastsnap = someSnapRef.Tree.Target as TreeBag<T>) != null)
3894               lastsnap.snapDispose();
3895             someSnapRef = someSnapRef.Prev;
3896           }
3897         }
3898         snapList = null;
3899         Clear();
3900       }
3901 #else
3902       Clear();
3903 #endif
3904     }
3905 
snapDispose()3906     private void snapDispose()
3907     {
3908       root = null;
3909       dirs = null;
3910       path = null;
3911       comparer = null;
3912       isValid = false;
3913       snapList = null;
3914     }
3915 
3916     /// <summary>
3917     /// Make a (read-only) snapshot of this collection.
3918     /// </summary>
3919     /// <returns>The snapshot.</returns>
3920     [Tested]
Snapshot()3921     public ISorted<T> Snapshot()
3922     {
3923 #if NCP
3924       if (isSnapShot)
3925         throw new InvalidOperationException("Cannot snapshot a snapshot");
3926 
3927       TreeBag<T> res = (TreeBag<T>)MemberwiseClone();
3928       SnapRef newSnapRef = new SnapRef(res);
3929       res.isReadOnlyBase = true;
3930       res.isSnapShot = true;
3931       res.snapList = newSnapRef;
3932 
3933       findLastLiveSnapShot();
3934       if (snapList == null)
3935         snapList = new SnapRef(this);
3936       SnapRef lastLiveSnapRef = snapList.Prev;
3937 
3938       newSnapRef.Prev = lastLiveSnapRef;
3939       if (lastLiveSnapRef != null)
3940         lastLiveSnapRef.Next = newSnapRef;
3941       newSnapRef.Next = snapList;
3942       snapList.Prev = newSnapRef;
3943 
3944       generation++;
3945 
3946       return res;
3947 #endif
3948     }
3949 
3950     #endregion
3951 
3952     #region TreeBag.Range nested class
3953 
3954     internal class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
3955     {
3956       //We actually need exclusive upper and lower bounds, and flags to
3957       //indicate whether the bound is present (we canot rely on default(T))
3958       private int stamp, size;
3959 
3960       private TreeBag<T> basis;
3961 
3962       private T lowend, highend;
3963 
3964       private bool haslowend, hashighend;
3965 
3966       EnumerationDirection direction;
3967 
3968 
3969       [Tested]
Range(TreeBag<T> basis, bool haslowend, T lowend, bool hashighend, T highend, EnumerationDirection direction)3970       public Range(TreeBag<T> basis, bool haslowend, T lowend, bool hashighend, T highend, EnumerationDirection direction)
3971       {
3972         this.basis = basis;
3973         stamp = basis.stamp;
3974 
3975         //lowind will be const; should we cache highind?
3976         this.lowend = lowend; //Inclusive
3977         this.highend = highend;//Exclusive
3978         this.haslowend = haslowend;
3979         this.hashighend = hashighend;
3980         this.direction = direction;
3981         if (!basis.isSnapShot)
3982           size = haslowend ?
3983               (hashighend ? basis.CountFromTo(lowend, highend) : basis.CountFrom(lowend)) :
3984               (hashighend ? basis.CountTo(highend) : basis.Count);
3985       }
3986 
3987       #region IEnumerable<T> Members
3988 
3989 
3990       #region TreeBag.Range.Enumerator nested class
3991 
3992       internal class Enumerator : SCG.IEnumerator<T>
3993       {
3994         #region Private Fields
3995         private bool valid = false, ready = true;
3996 
3997         private SCG.IComparer<T> comparer;
3998 
3999         private T current;
4000 #if BAG
4001         int togo;
4002 #endif
4003 
4004         private Node cursor;
4005 
4006         private Node[] path; // stack of nodes
4007 
4008         private int level = 0;
4009 
4010         private Range range;
4011 
4012         private bool forwards;
4013 
4014         #endregion
4015         [Tested]
Enumerator(Range range)4016         public Enumerator(Range range)
4017         {
4018           comparer = range.basis.comparer;
4019           path = new Node[2 * range.basis.blackdepth];
4020           this.range = range;
4021           forwards = range.direction == EnumerationDirection.Forwards;
4022           cursor = new Node();
4023           if (forwards)
4024             cursor.right = range.basis.root;
4025           else
4026             cursor.left = range.basis.root;
4027           range.basis.modifycheck(range.stamp);
4028         }
4029 
4030 
compare(T i1, T i2)4031         int compare(T i1, T i2) { return comparer.Compare(i1, i2); }
4032 
4033 
4034         /// <summary>
4035         /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
4036         /// </summary>
4037         /// <value>The current value of the enumerator.</value>
4038         [Tested]
4039         public T Current
4040         {
4041           [Tested]
4042           get
4043           {
4044             if (valid)
4045               return current;
4046             else
4047               throw new InvalidOperationException();
4048           }
4049         }
4050 
4051 
4052         //Maintain a stack of nodes that are roots of
4053         //subtrees not completely exported yet. Invariant:
4054         //The stack nodes together with their right subtrees
4055         //consists of exactly the items we have not passed
4056         //yet (the top of the stack holds current item).
4057         /// <summary>
4058         /// Move enumerator to next item in tree, or the first item if
4059         /// this is the first call to MoveNext.
4060         /// <exception cref="CollectionModifiedException"/> if underlying tree was modified.
4061         /// </summary>
4062         /// <returns>True if enumerator is valid now</returns>
4063         [Tested]
MoveNext()4064         public bool MoveNext()
4065         {
4066           range.basis.modifycheck(range.stamp);
4067           if (!ready)
4068             return false;
4069 #if BAG
4070           if (--togo > 0)
4071             return true;
4072 #endif
4073           if (forwards)
4074           {
4075             if (!valid && range.haslowend)
4076             {
4077               cursor = cursor.right;
4078               while (cursor != null)
4079               {
4080                 int comp = compare(cursor.item, range.lowend);
4081 
4082                 if (comp > 0)
4083                 {
4084                   path[level++] = cursor;
4085 #if NCP
4086                   cursor = range.basis.left(cursor);
4087 #else
4088 									cursor = cursor.left;
4089 #endif
4090                 }
4091                 else if (comp < 0)
4092                 {
4093 #if NCP
4094                   cursor = range.basis.right(cursor);
4095 #else
4096 									cursor = cursor.right;
4097 #endif
4098                 }
4099                 else
4100                 {
4101                   path[level] = cursor;
4102                   break;
4103                 }
4104               }
4105 
4106               if (cursor == null)
4107               {
4108                 if (level == 0)
4109                   return valid = ready = false;
4110                 else
4111                   cursor = path[--level];
4112               }
4113             }
4114 #if NCP
4115             else if (range.basis.right(cursor) != null)
4116             {
4117               path[level] = cursor = range.basis.right(cursor);
4118 
4119               Node next = range.basis.left(cursor);
4120 
4121               while (next != null)
4122               {
4123                 path[++level] = cursor = next;
4124                 next = range.basis.left(cursor);
4125               }
4126             }
4127 #else
4128 						else if (cursor.right != null)
4129 						{
4130 							path[level] = cursor = cursor.right;
4131 							while (cursor.left != null)
4132 								path[++level] = cursor = cursor.left;
4133 						}
4134 #endif
4135             else if (level == 0)
4136               return valid = ready = false;
4137             else
4138               cursor = path[--level];
4139 
4140             current = cursor.item;
4141             if (range.hashighend && compare(current, range.highend) >= 0)
4142               return valid = ready = false;
4143 
4144 #if BAG
4145             togo = cursor.items;
4146 #endif
4147             return valid = true;
4148           }
4149           else
4150           {
4151             if (!valid && range.hashighend)
4152             {
4153               cursor = cursor.left;
4154               while (cursor != null)
4155               {
4156                 int comp = compare(cursor.item, range.highend);
4157 
4158                 if (comp < 0)
4159                 {
4160                   path[level++] = cursor;
4161 #if NCP
4162                   cursor = range.basis.right(cursor);
4163 #else
4164 									cursor = cursor.right;
4165 #endif
4166                 }
4167                 else
4168                 {
4169 #if NCP
4170                   cursor = range.basis.left(cursor);
4171 #else
4172 									cursor = cursor.left;
4173 #endif
4174                 }
4175               }
4176 
4177               if (cursor == null)
4178               {
4179                 if (level == 0)
4180                   return valid = ready = false;
4181                 else
4182                   cursor = path[--level];
4183               }
4184             }
4185 #if NCP
4186             else if (range.basis.left(cursor) != null)
4187             {
4188               path[level] = cursor = range.basis.left(cursor);
4189 
4190               Node next = range.basis.right(cursor);
4191 
4192               while (next != null)
4193               {
4194                 path[++level] = cursor = next;
4195                 next = range.basis.right(cursor);
4196               }
4197             }
4198 #else
4199 						else if (cursor.left != null)
4200 						{
4201 							path[level] = cursor = cursor.left;
4202 							while (cursor.right != null)
4203 								path[++level] = cursor = cursor.right;
4204 						}
4205 #endif
4206             else if (level == 0)
4207               return valid = ready = false;
4208             else
4209               cursor = path[--level];
4210 
4211             current = cursor.item;
4212             if (range.haslowend && compare(current, range.lowend) < 0)
4213               return valid = ready = false;
4214 
4215 #if BAG
4216             togo = cursor.items;
4217 #endif
4218             return valid = true;
4219           }
4220         }
4221 
4222 
4223         [Tested]
Dispose()4224         public void Dispose()
4225         {
4226           comparer = null;
4227           current = default(T);
4228           cursor = null;
4229           path = null;
4230           range = null;
4231         }
4232 
4233         #region IEnumerator Members
4234 
4235         object System.Collections.IEnumerator.Current
4236         {
4237           get { return Current; }
4238         }
4239 
System.Collections.IEnumerator.MoveNext()4240         bool System.Collections.IEnumerator.MoveNext()
4241         {
4242           return MoveNext();
4243         }
4244 
System.Collections.IEnumerator.Reset()4245         void System.Collections.IEnumerator.Reset()
4246         {
4247           throw new Exception("The method or operation is not implemented.");
4248         }
4249 
4250         #endregion
4251       }
4252 
4253       #endregion
4254 
4255 
Choose()4256       public override T Choose()
4257       {
4258         if (size == 0) throw new NoSuchItemException();
4259         return lowend;
4260       }
4261 
4262       [Tested]
GetEnumerator()4263       public override SCG.IEnumerator<T> GetEnumerator() { return new Enumerator(this); }
4264 
4265 
4266       [Tested]
4267       public override EnumerationDirection Direction { [Tested]get { return direction; } }
4268 
4269 
4270       #endregion
4271 
4272       #region Utility
4273 
inside(T item)4274       bool inside(T item)
4275       {
4276         return (!haslowend || basis.comparer.Compare(item, lowend) >= 0) && (!hashighend || basis.comparer.Compare(item, highend) < 0);
4277       }
4278 
4279 
checkstamp()4280       void checkstamp()
4281       {
4282         if (stamp < basis.stamp)
4283           throw new CollectionModifiedException();
4284       }
4285 
4286 
syncstamp()4287       void syncstamp() { stamp = basis.stamp; }
4288 
4289       #endregion
4290 
4291       [Tested]
Backwards()4292       public override IDirectedCollectionValue<T> Backwards()
4293       {
4294         Range b = (Range)MemberwiseClone();
4295 
4296         b.direction = direction == EnumerationDirection.Forwards ? EnumerationDirection.Backwards : EnumerationDirection.Forwards;
4297         return b;
4298       }
4299 
4300 
4301       [Tested]
Backwards()4302       IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
4303 
4304 
4305       public override bool IsEmpty { get { return size == 0; } }
4306 
4307       [Tested]
4308       public override int Count { [Tested] get { return size; } }
4309 
4310       //TODO: check that this is correct
4311       public override Speed CountSpeed { get { return Speed.Constant; } }
4312 
4313     }
4314 
4315     #endregion
4316 
4317     #region Diagnostics
4318     /// <summary>
4319     /// Display this node on the console, and recursively its subnodes.
4320     /// </summary>
4321     /// <param name="n">Node to display</param>
4322     /// <param name="space">Indentation</param>
minidump(Node n, string space)4323     private void minidump(Node n, string space)
4324     {
4325       if (n == null)
4326       {
4327         //	System.Console.WriteLine(space + "null");
4328       }
4329       else
4330       {
4331         minidump(n.right, space + "  ");
4332         Console.WriteLine(String.Format("{0} {4} (size={1}, items={8}, h={2}, gen={3}, id={6}){7}", space + n.item,
4333 #if MAINTAIN_SIZE
4334  n.size,
4335 #else
4336 				0,
4337 #endif
4338  0,
4339 #if NCP
4340  n.generation,
4341 #endif
4342  n.red ? "RED" : "BLACK",
4343  0,
4344  0,
4345 #if NCP
4346 #if SEPARATE_EXTRA
4347 				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),
4348 #else
4349  n.lastgeneration == -1 ? "" : String.Format(" [extra: lg={0}, c={1}, i={2}]", n.lastgeneration, n.leftnode ? "L" : "R", n.oldref == null ? "()" : "" + n.oldref.item),
4350 #endif
4351 #else
4352 				"",
4353 #endif
4354 #if BAG
4355  n.items
4356 #else
4357  1
4358 #endif
4359 ));
4360         minidump(n.left, space + "  ");
4361       }
4362     }
4363 
4364 
4365     /// <summary>
4366     /// Print the tree structure to the console stdout.
4367     /// </summary>
4368     [Tested(via = "Sawtooth")]
dump()4369     public void dump() { dump(""); }
4370 
4371 
4372     /// <summary>
4373     /// Print the tree structure to the console stdout.
4374     /// </summary>
4375     [Tested(via = "Sawtooth")]
dump(string msg)4376     public void dump(string msg)
4377     {
4378       Console.WriteLine(String.Format(">>>>>>>>>>>>>>>>>>> dump {0} (count={1}, blackdepth={2}, depth={3}, gen={4})", msg, size, blackdepth,
4379       0
4380       ,
4381 #if NCP
4382  generation
4383 #endif
4384 ));
4385       minidump(root, "");
4386       check("", Console.Out); Console.WriteLine("<<<<<<<<<<<<<<<<<<<");
4387     }
4388 
4389 
4390     /// <summary>
4391     /// Display this tree on the console.
4392     /// </summary>
4393     /// <param name="msg">Identifying string of this call to dump</param>
4394     /// <param name="err">Extra (error)message to include</param>
dump(string msg, string err)4395     void dump(string msg, string err)
4396     {
4397       Console.WriteLine(String.Format(">>>>>>>>>>>>>>>>>>> dump {0} (count={1}, blackdepth={2}, depth={3}, gen={4})", msg, size, blackdepth,
4398       0
4399       ,
4400 #if NCP
4401  generation
4402 #endif
4403 ));
4404       minidump(root, ""); Console.Write(err);
4405       Console.WriteLine("<<<<<<<<<<<<<<<<<<<");
4406     }
4407 
4408 
4409     /// <summary>
4410     /// Print warning m on o if b is false.
4411     /// </summary>
4412     /// <param name="b">Condition that should hold</param>
4413     /// <param name="n">Place (used for id display)</param>
4414     /// <param name="m">Message</param>
4415     /// <param name="o">Output stream</param>
4416     /// <returns>b</returns>
massert(bool b, Node n, string m, System.IO.TextWriter o)4417     bool massert(bool b, Node n, string m, System.IO.TextWriter o)
4418     {
4419       if (!b) o.WriteLine("*** Node (item={0}, id={1}): {2}", n.item,
4420         0
4421         , m);
4422 
4423       return b;
4424     }
4425 
4426 
rbminicheck(Node n, bool redp, System.IO.TextWriter o, out T min, out T max, out int blackheight)4427     bool rbminicheck(Node n, bool redp, System.IO.TextWriter o, out T min, out T max, out int blackheight)
4428     {//Red-Black invariant
4429       bool res = true;
4430 
4431       res = massert(!(n.red && redp), n, "RED parent of RED node", o) && res;
4432       res = massert(n.left == null || n.right != null || n.left.red, n, "Left child black, but right child empty", o) && res;
4433       res = massert(n.right == null || n.left != null || n.right.red, n, "Right child black, but left child empty", o) && res;
4434 #if BAG
4435       bool sb = n.size == (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + n.items;
4436 
4437       res = massert(sb, n, "Bad size", o) && res;
4438 #elif MAINTAIN_SIZE
4439       bool sb = n.size == (n.left == null ? 0 : n.left.size) + (n.right == null ? 0 : n.right.size) + 1;
4440 
4441       res = massert(sb, n, "Bad size", o) && res;
4442 #endif
4443       min = max = n.item;
4444 
4445       T otherext;
4446       int lbh = 0, rbh = 0;
4447 
4448       if (n.left != null)
4449       {
4450         res = rbminicheck(n.left, n.red, o, out min, out otherext, out lbh) && res;
4451         res = massert(comparer.Compare(n.item, otherext) > 0, n, "Value not > all left children", o) && res;
4452       }
4453 
4454       if (n.right != null)
4455       {
4456         res = rbminicheck(n.right, n.red, o, out otherext, out max, out rbh) && res;
4457         res = massert(comparer.Compare(n.item, otherext) < 0, n, "Value not < all right children", o) && res;
4458       }
4459 
4460       res = massert(rbh == lbh, n, "Different blackheights of children", o) && res;
4461       blackheight = n.red ? rbh : rbh + 1;
4462       return res;
4463     }
4464 
4465 
4466 
4467 
4468 #if NCP
4469 
rbminisnapcheck(Node n, System.IO.TextWriter o, out int size, out T min, out T max)4470     bool rbminisnapcheck(Node n, System.IO.TextWriter o, out int size, out T min, out T max)
4471     {
4472       bool res = true;
4473 
4474       min = max = n.item;
4475 
4476       int lsz = 0, rsz = 0;
4477       T otherext;
4478 #if SEPARATE_EXTRA
4479 			Node.Extra extra = n.extra;
4480 			Node child = (extra != null && extra.lastgeneration >= treegen && extra.leftnode) ? extra.oldref : n.left;
4481 #else
4482       Node child = (n.lastgeneration >= generation && n.leftnode) ? n.oldref : n.left;
4483 #endif
4484       if (child != null)
4485       {
4486         res = rbminisnapcheck(child, o, out lsz, out min, out otherext) && res;
4487         res = massert(comparer.Compare(n.item, otherext) > 0, n, "Value not > all left children", o) && res;
4488       }
4489 
4490 #if SEPARATE_EXTRA
4491 			child = (extra != null && extra.lastgeneration >= treegen && !extra.leftnode) ? extra.oldref : n.right;
4492 #else
4493       child = (n.lastgeneration >= generation && !n.leftnode) ? n.oldref : n.right;
4494 #endif
4495       if (child != null)
4496       {
4497         res = rbminisnapcheck(child, o, out rsz, out otherext, out max) && res;
4498         res = massert(comparer.Compare(n.item, otherext) < 0, n, "Value not < all right children", o) && res;
4499       }
4500 #if BAG
4501       size = n.items + lsz + rsz;
4502 #else
4503       size = 1 + lsz + rsz;
4504 #endif
4505       return res;
4506     }
4507 #endif
4508 
4509     /// <summary>
4510     /// Checks red-black invariant. Dumps tree to console if bad
4511     /// </summary>
4512     /// <param name="name">Title of dump</param>
4513     /// <returns>false if invariant violation</returns>
4514     [Tested(via = "Sawtooth")]
Check(string name)4515     public bool Check(string name)
4516     {
4517       System.Text.StringBuilder e = new System.Text.StringBuilder();
4518       System.IO.TextWriter o = new System.IO.StringWriter(e);
4519 
4520       if (!check(name, o))
4521         return true;
4522       else
4523       {
4524         dump(name, e.ToString());
4525         return false;
4526       }
4527     }
4528 
4529 
4530     /// <summary>
4531     /// Checks red-black invariant. Dumps tree to console if bad
4532     /// </summary>
4533     /// <returns>false if invariant violation</returns>
4534     [Tested]
Check()4535     public bool Check()
4536     {
4537       //return check("", System.IO.TextWriter.Null);
4538       //Console.WriteLine("bamse");
4539       if (!isValid)
4540         return true;
4541       return Check("-");
4542     }
4543 
4544 
check(string msg, System.IO.TextWriter o)4545     bool check(string msg, System.IO.TextWriter o)
4546     {
4547       if (root != null)
4548       {
4549         T max, min;
4550         int blackheight;
4551 #if NCP
4552         if (isSnapShot)
4553         {
4554           //Console.WriteLine("Im'a snapshot");
4555           int thesize;
4556           bool rv = rbminisnapcheck(root, o, out thesize, out min, out max);
4557 
4558           rv = massert(size == thesize, root, "bad snapshot size", o) && rv;
4559           return !rv;
4560         }
4561 #endif
4562         bool res = rbminicheck(root, false, o, out min, out max, out blackheight);
4563         res = massert(blackheight == blackdepth, root, "bad blackh/d", o) && res;
4564         res = massert(!root.red, root, "root is red", o) && res;
4565 #if MAINTAIN_SIZE
4566         res = massert(root.size == size, root, "count!=root.size", o) && res;
4567 #endif
4568         return !res;
4569       }
4570       else
4571         return false;
4572     }
4573     #endregion
4574 
4575     #region ICloneable Members
4576 
4577     /// <summary>
4578     /// Make a shallow copy of this TreeBag.
4579     /// </summary>
4580     /// <returns></returns>
Clone()4581     public virtual object Clone()
4582     {
4583       TreeBag<T> clone = new TreeBag<T>(comparer, EqualityComparer);
4584       //TODO: make sure the TreeBag AddSorted copies tree bags smartly!!!
4585       clone.AddSorted(this);
4586       return clone;
4587     }
4588 
4589     #endregion
4590 
4591   }
4592 }
4593 
4594