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 /// >= 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 /// >= 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<T></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