3 Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 #define MAINTAIN_RANKnot
25 #define MAINTAIN_HEIGHTnot
29 #define MAINTAIN_EXTREMAnot
34 #error BAG defined without MAINTAIN_SIZE!
40 using MSG
= System
.Collections
.Generic
;
42 // NOTE NOTE NOTE NOTE
43 // This source file is used to produce both TreeBag<T> and TreeBag<T>
44 // It should be copied to a file called TreeBag.cs in which all code mentions of
45 // TreeBag is changed to TreeBag and the preprocessor symbol BAG is defined.
46 // NOTE: there may be problems with documentation comments.
52 /// An implementation of Red-Black trees as an indexed, sorted collection with bag semantics,
53 /// cf. <a href="litterature.htm#CLRS">CLRS</a>. (<see cref="T:C5.TreeBag!1"/> for an
54 /// implementation with set semantics).
56 /// The comparer (sorting order) may be either natural, because the item type is comparable
57 /// (generic: <see cref="T:C5.IComparable!1"/> or non-generic: System.IComparable) or it can
58 /// be external and supplied by the user in the constructor.
60 /// Each distinct item is only kept in one place in the tree - together with the number
61 /// of times it is a member of the bag. Thus, if two items that are equal according
65 /// An implementation of Red-Black trees as an indexed, sorted collection with set semantics,
66 /// cf. <a href="litterature.htm#CLRS">CLRS</a>. <see cref="T:C5.TreeBag!1"/> for a version
67 /// with bag semantics. <see cref="T:C5.TreeDictionary!2"/> for a sorted dictionary
68 /// based on this tree implementation.
70 /// The comparer (sorting order) may be either natural, because the item type is comparable
71 /// (generic: <see cref="T:C5.IComparable!1"/> or non-generic: System.IComparable) or it can
72 /// be external and supplied by the user in the constructor.</p>
74 /// <p><i>TODO: describe performance here</i></p>
75 /// <p><i>TODO: discuss persistence and its useful usage modes. Warn about the space
76 /// leak possible with other usage modes.</i></p>
79 public class TreeBag
<T
>: SequencedBase
<T
>, IIndexedSorted
<T
>, IPersistentSorted
<T
>
83 /// A debugging aid for making the selected compilation alternatives
84 /// available to the user. (To be removed when selection is finally fixed
85 /// for production version).
88 public enum Feature
: short
95 /// Node copy persistence as explained in <a href="litterature.htm#Tarjan1">Tarjan1</a>
97 NodeCopyPersistence
= 2,
99 /// Maintain sub tree sizes
103 /// Maintain precise node heights
107 /// Maintain node ranks (~ black height)
111 /// Maintain unique ids on tree nodes.
118 static Feature features
= Feature
.Dummy
120 | Feature
.NodeCopyPersistence
138 /// A debugging aid for making the selected compilation alternatives
139 /// available to the user. (To be removed when selection is finally fixed
140 /// for production version).
142 public static Feature Features { get { return features; }
}
148 IComparer
<T
> comparer
;
154 //We double these stacks for the iterative add and remove on demand
155 private int[] dirs
= new int[2];
157 private Node
[] path
= new Node
[2];
159 private bool isSnapShot
= false;
161 private SnapData snapdata
;
163 private int generation
;
165 private int maxsnapid
= -1;
172 private short depth
= 0;
179 /// Fetch the left child of n taking node-copying persistence into
180 /// account if relevant.
182 /// <param name="n"></param>
183 /// <returns></returns>
184 private Node
left(Node n
)
190 Node
.Extra e
= n
.extra
;
192 if (e
!= null && e
.lastgeneration
>= treegen
&& e
.leftnode
)
195 if (n
.lastgeneration
>= generation
&& n
.leftnode
)
204 private Node
right(Node n
)
210 Node
.Extra e
= n
.extra
;
212 if (e
!= null && e
.lastgeneration
>= treegen
&& !e
.leftnode
)
215 if (n
.lastgeneration
>= generation
&& !n
.leftnode
)
224 //This method should be called by methods that use the internal
225 //traversal stack, unless certain that there is room enough
226 private void stackcheck()
228 while (dirs
.Length
< 2 * blackdepth
)
230 dirs
= new int[2 * dirs
.Length
];
231 path
= new Node
[2 * dirs
.Length
];
237 #region Node nested class
241 /// The type of node in a Red-Black binary tree
245 public bool red
= true;
258 public int items
= 1;
266 public short rank
= 1;
270 public int id
= sid
++;
271 public static int sid
= 0;
275 public int generation
;
279 public int lastgeneration
;
283 public bool leftnode
;
291 public int lastgeneration
= -1;
295 public bool leftnode
;
299 /// Update a child pointer
301 /// <param name="cursor"></param>
302 /// <param name="leftnode"></param>
303 /// <param name="child"></param>
304 /// <param name="maxsnapid"></param>
305 /// <param name="generation"></param>
306 /// <returns>True if node was *copied*</returns>
307 internal static bool update(ref Node cursor
, bool leftnode
, Node child
, int maxsnapid
, int generation
)
309 Node oldref
= leftnode
? cursor
.left
: cursor
.right
;
316 if (cursor
.generation
<= maxsnapid
)
319 if (cursor
.extra
== null)
321 Extra extra
= cursor
.extra
= new Extra();
323 extra
.leftnode
= leftnode
;
324 extra
.lastgeneration
= maxsnapid
;
325 extra
.oldref
= oldref
;
327 else if (cursor
.extra
.leftnode
!= leftnode
|| cursor
.extra
.lastgeneration
< maxsnapid
)
329 if (cursor
.lastgeneration
== -1)
331 cursor
.leftnode
= leftnode
;
332 cursor
.lastgeneration
= maxsnapid
;
333 cursor
.oldref
= oldref
;
335 else if (cursor
.leftnode
!= leftnode
|| cursor
.lastgeneration
< maxsnapid
)
338 CopyNode(ref cursor
, maxsnapid
, generation
);
346 cursor
.right
= child
;
352 //If cursor.extra.lastgeneration==maxsnapid, the extra pointer will
353 //always be used in the old copy of cursor. Therefore, after
354 //making the clone, we should update the old copy by restoring
355 //the child pointer and setting extra to null.
356 //OTOH then we cannot clean up unused Extra objects unless we link
357 //them together in a doubly linked list.
358 public static bool CopyNode(ref Node cursor
, int maxsnapid
, int generation
)
360 if (cursor
.generation
<= maxsnapid
)
362 cursor
= (Node
)(cursor
.MemberwiseClone());
363 cursor
.generation
= generation
;
367 cursor
.lastgeneration
= -1;
386 /// Create a red-black tree collection with natural comparer and item hasher.
390 comparer
= ComparerBuilder
.FromComparable
<T
>.Examine();
391 itemhasher
= HasherBuilder
.ByPrototype
<T
>.Examine();
396 /// Create a red-black tree collection with an external comparer (and natural item hasher,
397 /// assumed consistent).
399 /// <param name="c">The external comparer</param>
400 public TreeBag(IComparer
<T
> c
)
403 itemhasher
= HasherBuilder
.ByPrototype
<T
>.Examine();
408 /// Create a red-black tree collection with an external comparer aand an external
409 /// item hasher, assumed consistent.
411 /// <param name="c">The external comparer</param>
412 /// <param name="h">The external item hasher</param>
413 public TreeBag(IComparer
<T
> c
, IHasher
<T
> h
)
421 #region TreeBag.Enumerator nested class
424 /// An enumerator for a red-black tree collection. Based on an explicit stack
425 /// of subtrees waiting to be enumerated. Currently only used for the tree set
426 /// enumerators (tree bag enumerators use an iterator block based enumerator).
428 public class Enumerator
: MSG
.IEnumerator
<T
>
430 #region Private Fields
441 Node
[] path
; // stack of nodes
446 /// Create a tree enumerator
448 /// <param name="tree">The red-black tree to enumerate</param>
449 public Enumerator(TreeBag
<T
> tree
)
453 path
= new Node
[2 * tree
.blackdepth
];
455 cursor
.right
= tree
.root
;
460 /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
462 /// <value>The current item of the enumerator.</value>
472 throw new InvalidOperationException();
477 //Maintain a stack of nodes that are roots of
478 //subtrees not completely exported yet. Invariant:
479 //The stack nodes together with their right subtrees
480 //consists of exactly the items we have not passed
481 //yet (the top of the stack holds current item).
483 /// Move enumerator to next item in tree, or the first item if
484 /// this is the first call to MoveNext.
485 /// <exception cref="InvalidOperationException"/> if underlying tree was modified.
487 /// <returns>True if enumerator is valid now</returns>
489 public bool MoveNext()
491 tree
.modifycheck(stamp
);
492 if (cursor
.right
!= null)
494 path
[level
] = cursor
= cursor
.right
;
495 while (cursor
.left
!= null)
496 path
[++level
] = cursor
= cursor
.left
;
499 return valid
= false;
501 cursor
= path
[--level
];
503 current
= cursor
.item
;
508 #region IDisposable Members for Enumerator
514 /// Call Dispose(true) and then suppress finalization of this enumerator.
517 public void Dispose()
520 GC
.SuppressFinalize(this);
525 /// Remove the internal data (notably the stack array).
527 /// <param name="disposing">True if called from Dispose(),
528 /// false if called from the finalizer</param>
529 protected virtual void Dispose(bool disposing
)
537 current
= default(T
);
546 /// Finalizer for enumeratir
557 /// An enumerator for a snapshot of a node copy persistent red-black tree
560 public class SnapEnumerator
: MSG
.IEnumerator
<T
>
562 #region Private Fields
576 Node
[] path
; // stack of nodes
582 /// Creta an enumerator for a snapshot of a node copy persistent red-black tree
585 /// <param name="tree">The snapshot</param>
586 public SnapEnumerator(TreeBag
<T
> tree
)
590 path
= new Node
[2 * tree
.blackdepth
];
592 cursor
.right
= tree
.root
;
596 #region MSG.IEnumerator<T> Members
599 /// Move enumerator to next item in tree, or the first item if
600 /// this is the first call to MoveNext.
601 /// <exception cref="InvalidOperationException"/> if underlying tree was modified.
603 /// <returns>True if enumerator is valid now</returns>
605 public bool MoveNext()
607 tree
.modifycheck(stamp
);//???
613 Node next
= tree
.right(cursor
);
617 path
[level
] = cursor
= next
;
618 next
= tree
.left(cursor
);
621 path
[++level
] = cursor
= next
;
622 next
= tree
.left(cursor
);
626 return valid
= false;
628 cursor
= path
[--level
];
633 current
= cursor
.item
;
639 /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
641 /// <value>The current value of the enumerator.</value>
651 throw new InvalidOperationException();
657 #region IDisposable Members
660 void System
.IDisposable
.Dispose()
664 current
= default(T
);
674 #region IEnumerable<T> Members
676 private MSG
.IEnumerator
<T
> getEnumerator(Node node
, int origstamp
)
681 if (node
.left
!= null)
683 MSG
.IEnumerator
<T
> child
= getEnumerator(node
.left
, origstamp
);
685 while (child
.MoveNext())
687 modifycheck(origstamp
);
688 yield return child
.Current
;
692 int togo
= node
.items
;
695 modifycheck(origstamp
);
696 yield return node
.item
;
699 modifycheck(origstamp
);
700 yield return node
.item
;
702 if (node
.right
!= null)
704 MSG
.IEnumerator
<T
> child
= getEnumerator(node
.right
, origstamp
);
706 while (child
.MoveNext())
708 modifycheck(origstamp
);
709 yield return child
.Current
;
716 /// Create an enumerator for this tree
718 /// <returns>The enumerator</returns>
720 public override MSG
.IEnumerator
<T
> GetEnumerator()
724 return new SnapEnumerator(this);
727 return getEnumerator(root
,stamp
);
729 return new Enumerator(this);
735 #region ISink<T> Members
738 /// Add item to tree. If already there, return the found item in the second argument.
740 /// <param name="item">Item to add</param>
741 /// <param name="founditem">item found</param>
742 /// <param name="update">whether item in node should be updated</param>
743 /// <param name="wasfound">true if found in bag, false if not found or tre is a set</param>
744 /// <returns>True if item was added</returns>
745 bool addIterative(T item
, ref T founditem
, bool update
, out bool wasfound
)
754 root
.item
= min
= max
= item
;
759 root
.generation
= generation
;
774 int comp
= comparer
.Compare(cursor
.item
, item
);
778 founditem
= cursor
.item
;
783 Node
.CopyNode(ref cursor
, maxsnapid
, generation
);
796 Node
.CopyNode(ref cursor
, maxsnapid
, generation
);
808 cursor
= path
[level
];
810 Node
.update(ref cursor
, dirs
[level
] > 0, kid
, maxsnapid
, generation
);
830 Node child
= comp
> 0 ? cursor
.left
: cursor
.right
;
837 child
.generation
= generation
;
838 Node
.update(ref cursor
, comp
> 0, child
, maxsnapid
, generation
);
840 if (comp
> 0) { cursor.left = child; }
841 else { cursor.right = child; }
855 path
[level
++] = cursor
;
860 //We have just added the red node child to "cursor"
866 cursor
= path
[--level
];
869 Node
.update(ref cursor
, dirs
[level
] > 0, child
, maxsnapid
, generation
);
874 int comp
= dirs
[level
];
875 Node childsibling
= comp
> 0 ? cursor
.right
: cursor
.left
;
877 if (childsibling
!= null && childsibling
.red
)
888 Node
.update(ref cursor
, comp
< 0, childsibling
, maxsnapid
, generation
);
890 childsibling
.red
= false;
892 //color cursor red & take one step up the tree unless at root
904 cursor
= path
[--level
];
905 Node
.update(ref cursor
, dirs
[level
] > 0, child
, maxsnapid
, generation
);
919 int childcomp
= dirs
[level
+ 1];
927 Node
.update(ref cursor
, true, child
.right
, maxsnapid
, generation
);
928 Node
.update(ref child
, false, cursor
, maxsnapid
, generation
);
930 cursor
.left
= child
.right
;
931 child
.right
= cursor
;
937 Node badgrandchild
= child
.right
;
939 Node
.update(ref cursor
, true, badgrandchild
.right
, maxsnapid
, generation
);
940 Node
.update(ref child
, false, badgrandchild
.left
, maxsnapid
, generation
);
941 Node
.CopyNode(ref badgrandchild
, maxsnapid
, generation
);
943 cursor
.left
= badgrandchild
.right
;
944 child
.right
= badgrandchild
.left
;
946 badgrandchild
.left
= child
;
947 badgrandchild
.right
= cursor
;
948 cursor
= badgrandchild
;
956 Node
.update(ref cursor
, false, child
.left
, maxsnapid
, generation
);
957 Node
.update(ref child
, true, cursor
, maxsnapid
, generation
);
959 cursor
.right
= child
.left
;
966 Node badgrandchild
= child
.left
;
968 Node
.update(ref cursor
, false, badgrandchild
.left
, maxsnapid
, generation
);
969 Node
.update(ref child
, true, badgrandchild
.right
, maxsnapid
, generation
);
970 Node
.CopyNode(ref badgrandchild
, maxsnapid
, generation
);
972 cursor
.right
= badgrandchild
.left
;
973 child
.left
= badgrandchild
.right
;
975 badgrandchild
.right
= child
;
976 badgrandchild
.left
= cursor
;
977 cursor
= badgrandchild
;
988 cursor
.size
= n
.size
= (n
.left
== null ? 0 : n
.left
.size
) + (n
.right
== null ? 0 : n
.right
.size
) + n
.items
;
990 n
.size
= (n
.left
== null ? 0 : n
.left
.size
) + (n
.right
== null ? 0 : n
.right
.size
) + n
.items
;
991 cursor
.size
+= n
.size
+ cursor
.items
;
994 cursor
.size
= n
.size
= (n
.left
== null ? 0 : n
.left
.size
) + (n
.right
== null ? 0 : n
.right
.size
) + 1;
996 n
.size
= (n
.left
== null ? 0 : n
.left
.size
) + (n
.right
== null ? 0 : n
.right
.size
) + 1;
997 cursor
.size
+= n
.size
+ 1;
1001 fixheight(cursor
.right
);
1002 fixheight(cursor
.left
);
1013 cursor
= path
[--level
];
1016 Node
.update(ref cursor
, dirs
[level
] > 0, child
, maxsnapid
, generation
);
1018 if (dirs
[level
] > 0)
1019 cursor
.left
= child
;
1021 cursor
.right
= child
;
1034 bool stillmore
= true;
1038 Node child
= cursor
;
1040 cursor
= path
[--level
];
1044 stillmore
= Node
.update(ref cursor
, dirs
[level
] > 0, child
, maxsnapid
, generation
);
1060 /// Add an item to this collection if possible. If this collection has set
1061 /// semantics, the item will be added if not already in the collection. If
1062 /// bag semantics, the item will always be added.
1064 /// <param name="item">The item to add.</param>
1065 /// <returns>True if item was added.</returns>
1067 public bool Add(T item
)
1071 //Note: blackdepth of the tree is set inside addIterative
1075 if (addIterative(item
, ref j
, false, out tmp
))
1078 #if MAINTAIN_EXTREMA
1079 if (Compare(item
, min
) < 0)
1081 else if (Compare(item
, max
) > 0)
1085 depth
= root
.height
;
1095 /// Add the elements from another collection to this collection. If this
1096 /// collection has set semantics, only items not already in the collection
1099 /// <param name="items">The items to add.</param>
1101 public void AddAll(MSG
.IEnumerable
<T
> items
)
1108 foreach (T i
in items
)
1109 if (addIterative(i
, ref j
, false, out tmp
)) c
++;
1115 /// Add the elements from another collection with a more specialized item type
1116 /// to this collection. If this
1117 /// collection has set semantics, only items not already in the collection
1120 /// <typeparam name="U">The type of items to add</typeparam>
1121 /// <param name="items">The items to add</param>
1122 public void AddAll
<U
>(MSG
.IEnumerable
<U
> items
) where U
: T
1129 foreach (T i
in items
)
1130 if (addIterative(i
, ref j
, false, out tmp
)) c
++;
1137 /// Add all the items from another collection with an enumeration order that
1138 /// is increasing in the items. <para>The idea is that the implementation may use
1139 /// a faster algorithm to merge the two collections.</para>
1140 /// <exception cref="ArgumentException"/> if the enumerated items turns out
1141 /// not to be in increasing order.
1143 /// <param name="items">The collection to add.</param>
1145 public void AddSorted(MSG
.IEnumerable
<T
> items
)
1152 addSorted(items
, true);
1156 #region add-sorted helpers
1158 //Create a RB tree from x+2^h-1 (x < 2^h, h>=1) nodes taken from a
1159 //singly linked list of red nodes using only the right child refs.
1160 //The x nodes at depth h+1 will be red, the rest black.
1161 //(h is the blackdepth of the resulting tree)
1162 static Node
maketreer(ref Node rest
, int blackheight
, int maxred
, int red
)
1164 if (blackheight
== 1)
1175 top
.size
+= top
.left
.size
;
1186 top
.size
+= rest
.size
;
1190 top
.right
.right
= null;
1202 int lred
= red
> maxred
? maxred
: red
;
1203 Node left
= maketreer(ref rest
, blackheight
- 1, maxred
, lred
);
1210 top
.rank
= (short)blackheight
;
1212 top
.right
= maketreer(ref rest
, blackheight
- 1, maxred
, red
- lred
);
1214 top
.size
= top
.items
+ top
.left
.size
+ top
.right
.size
;
1216 top
.size
= (maxred
<< 1) - 1 + red
;
1223 void addSorted(MSG
.IEnumerable
<T
> items
, bool safe
)
1225 MSG
.IEnumerator
<T
> e
= items
.GetEnumerator();;
1227 throw new ApplicationException("This can't happen");
1232 //To count theCollect
1233 Node head
= new Node(), tail
= head
;
1235 T lastitem
= tail
.item
= e
.Current
;
1240 while (e
.MoveNext())
1243 T thisitem
= e
.Current
;
1244 int comp
= comparer
.Compare(lastitem
, thisitem
);
1246 throw new ArgumentException("Argument not sorted");
1254 tail
.size
= tail
.items
;
1256 tail
.right
= new Node();
1258 lastitem
= tail
.item
= thisitem
;
1260 tail
.generation
= generation
;
1265 tail
.right
= new Node();
1267 tail
.item
= e
.Current
;
1270 if (comparer
.Compare(lastitem
, tail
.item
) >= 0)
1271 throw new ArgumentException("Argument not sorted");
1273 lastitem
= tail
.item
;
1276 tail
.generation
= generation
;
1281 tail
.size
= tail
.items
;
1283 int blackheight
= 0, red
= z
, maxred
= 1;
1285 while (maxred
<= red
)
1292 root
= TreeBag
<T
>.maketreer(ref head
, blackheight
, maxred
, red
);
1293 blackdepth
= blackheight
;
1304 /// <summary></summary>
1305 /// <value>True since this collection has bag semantics.</value>
1307 public bool AllowsDuplicates { [Tested]get { return true; }
}
1309 /// <summary></summary>
1310 /// <value>False since this tree has set semantics.</value>
1312 public bool AllowsDuplicates { [Tested]get { return false; }
}
1317 #region IEditableCollection<T> Members
1321 /// The value is symbolic indicating the type of asymptotic complexity
1322 /// in terms of the size of this collection (worst-case or amortized as
1325 /// <value>Speed.Log</value>
1327 public Speed ContainsSpeed { [Tested]get { return Speed.Log; }
}
1331 int ICollection
<T
>.GetHashCode() { return unsequencedhashcode(); }
1335 bool ICollection
<T
>.Equals(ICollection
<T
> that
)
1336 { return unsequencedequals(that); }
1340 /// Check if this collection contains (an item equivalent to according to the
1341 /// itemhasher) a particular value.
1343 /// <param name="item">The value to check for.</param>
1344 /// <returns>True if the items is in this collection.</returns>
1346 public bool Contains(T item
)
1348 Node next
; int comp
= 0;
1351 while (next
!= null)
1353 comp
= comparer
.Compare(next
.item
, item
);
1357 next
= comp
< 0 ? right(next
) : left(next
);
1364 //Variant for dictionary use
1365 //Will return the actual matching item in the ref argument.
1367 /// Check if this collection contains an item equivalent according to the
1368 /// itemhasher to a particular value. If so, return in the ref argument (a
1369 /// binary copy of) the actual value found.
1371 /// <param name="item">The value to look for.</param>
1372 /// <returns>True if the items is in this collection.</returns>
1374 public bool Find(ref T item
)
1376 Node next
; int comp
= 0;
1379 while (next
!= null)
1381 comp
= comparer
.Compare(next
.item
, item
);
1388 next
= comp
< 0 ? right(next
) : left(next
);
1396 /// Find or add the item to the tree. If the tree does not contain
1397 /// an item equivalent to this item add it, else return the exisiting
1398 /// one in the ref argument.
1401 /// <param name="item"></param>
1402 /// <returns>True if item was found</returns>
1404 public bool FindOrAdd(ref T item
)
1409 //Note: blackdepth of the tree is set inside addIterative
1410 if (addIterative(item
, ref item
, false, out wasfound
))
1413 #if MAINTAIN_EXTREMA
1414 if (Compare(item
, min
) < 0)
1416 else if (Compare(item
, max
) > 0)
1420 depth
= root
.height
;
1430 //For dictionary use.
1431 //If found, the matching entry will be updated with the new item.
1433 /// Check if this collection contains an item equivalent according to the
1434 /// itemhasher to a particular value. If so, update the item in the collection
1435 /// to with a binary copy of the supplied value. If the collection has bag semantics,
1436 /// this updates all equivalent copies in
1439 /// <param name="item">Value to update.</param>
1440 /// <returns>True if the item was found and hence updated.</returns>
1442 public bool Update(T item
)
1453 while (cursor
!= null)
1455 comp
= comparer
.Compare(cursor
.item
, item
);
1459 Node
.CopyNode(ref cursor
, maxsnapid
, generation
);
1465 Node child
= cursor
;
1467 cursor
= path
[--level
];
1470 Node
.update(ref cursor
, dirs
[level
] > 0, child
, maxsnapid
, generation
);
1472 if (Node
.CopyNode(maxsnapid
, ref cursor
, generation
))
1474 if (dirs
[level
] > 0)
1475 cursor
.left
= child
;
1477 cursor
.right
= child
;
1488 path
[level
++] = cursor
;
1490 cursor
= comp
< 0 ? cursor
.right
: cursor
.left
;
1498 /// Check if this collection contains an item equivalent according to the
1499 /// itemhasher to a particular value. If so, update the item in the collection
1500 /// to with a binary copy of the supplied value; else add the value to the collection.
1502 /// <p>NOTE: the bag implementation is currently wrong!</p>
1504 /// <param name="item">Value to add or update.</param>
1505 /// <returns>True if the item was found and updated (hence not added).</returns>
1507 public bool UpdateOrAdd(T item
)
1512 //Note: blackdepth of the tree is set inside addIterative
1513 if (addIterative(item
, ref item
, true, out wasfound
))
1516 #if MAINTAIN_EXTREMA
1517 if (Compare(item
, min
) < 0)
1519 else if (Compare(item
, max
) > 0)
1523 depth
= root
.height
;
1533 /// Remove a particular item from this collection. If the collection has bag
1534 /// semantics only one copy equivalent to the supplied item is removed.
1536 /// <param name="item">The value to remove.</param>
1537 /// <returns>True if the item was found (and removed).</returns>
1539 public bool Remove(T item
)
1545 return removeIterative(ref item
, false);
1549 /// Remove a particular item from this collection if found. If the collection
1550 /// has bag semantics only one copy equivalent to the supplied item is removed,
1551 /// which one is implementation dependent.
1552 /// If an item was removed, report a binary copy of the actual item removed in
1555 /// <param name="item">The value to remove on input.</param>
1556 /// <returns>True if the item was found (and removed).</returns>
1558 public bool RemoveWithReturn(ref T item
)
1564 return removeIterative(ref item
, false);
1568 private bool removeIterative(ref T item
, bool all
)
1570 //Stage 1: find item
1573 int level
= 0, comp
;
1578 comp
= comparer
.Compare(cursor
.item
, item
);
1583 if (!all
&& cursor
.items
> 1)
1586 Node
.CopyNode(ref cursor
, maxsnapid
, generation
);
1594 cursor
= path
[level
];
1596 Node
.update(ref cursor
, dirs
[level
] > 0, kid
,maxsnapid
,generation
);
1608 Node child
= comp
> 0 ? cursor
.left
: cursor
.right
;
1614 path
[level
++] = cursor
;
1618 return removeIterativePhase2(cursor
, level
);
1622 private bool removeIterativePhase2(Node cursor
, int level
)
1630 #if MAINTAIN_EXTREMA
1631 if (Compare(cursor
.item
, min
) == 0)
1632 min
= cursor
.right
!= null ? cursor
.right
.item
: path
[level
- 1].item
;
1633 else if (Compare(cursor
.item
, max
) == 0)
1634 max
= cursor
.left
!= null ? cursor
.left
.item
: path
[level
- 1].item
;
1637 int removedcount
= cursor
.items
;
1638 size
-= removedcount
;
1640 //We are certain to remove one node:
1643 //Stage 2: if item's node has no null child, find predecessor
1644 int level_of_item
= level
;
1646 if (cursor
.left
!= null && cursor
.right
!= null)
1649 path
[level
++] = cursor
;
1650 cursor
= cursor
.left
;
1651 while (cursor
.right
!= null)
1654 path
[level
++] = cursor
;
1655 cursor
= cursor
.right
;
1658 Node
.CopyNode(ref path
[level_of_item
], maxsnapid
, generation
);
1660 path
[level_of_item
].item
= cursor
.item
;
1662 path
[level_of_item
].items
= cursor
.items
;
1666 //Stage 3: splice out node to be removed
1667 Node newchild
= cursor
.right
== null ? cursor
.left
: cursor
.right
;
1668 bool demote_or_rotate
= newchild
== null && !cursor
.red
;
1670 //assert newchild.red
1671 if (newchild
!= null)
1673 newchild
.red
= false;
1686 cursor
= path
[level
];
1689 int comp
= dirs
[level
];
1692 Node
.update(ref cursor
, comp
> 0, newchild
, maxsnapid
, generation
);
1695 cursor
.left
= newchild
;
1697 cursor
.right
= newchild
;
1699 childsibling
= comp
> 0 ? cursor
.right
: cursor
.left
;
1701 cursor
.size
-= removedcount
;
1709 //Stage 4: demote till we must rotate
1710 Node farnephew
= null, nearnephew
= null;
1712 while (demote_or_rotate
)
1714 if (childsibling
.red
)
1717 farnephew
= comp
> 0 ? childsibling
.right
: childsibling
.left
;
1718 if (farnephew
!= null && farnephew
.red
)
1721 nearnephew
= comp
> 0 ? childsibling
.left
: childsibling
.right
;
1722 if (nearnephew
!= null && nearnephew
.red
)
1726 childsibling
.red
= true;
1735 depth
= root
.height
;
1742 else if (cursor
.red
)
1745 demote_or_rotate
= false;
1746 break; //No rotation
1750 Node child
= cursor
;
1752 cursor
= path
[--level
];
1755 childsibling
= comp
> 0 ? cursor
.right
: cursor
.left
;
1757 Node
.update(ref cursor
, comp
> 0, child
, maxsnapid
, generation
);
1760 cursor
.size
-= removedcount
;
1771 if (demote_or_rotate
)
1774 //parent = cursor (temporary for swapping nodes)
1775 //childsibling is the sibling of the updated child (x)
1776 //cursor is always the top of the subtree
1777 Node parent
= cursor
;
1779 if (childsibling
.red
)
1780 {//Case 2 and perhaps more.
1781 //The y.rank == px.rank >= x.rank+2 >=2 so both nephews are != null
1782 //(and black). The grandnephews are children of nearnephew
1783 Node neargrandnephew
, fargrandnephew
;
1787 nearnephew
= childsibling
.left
;
1788 farnephew
= childsibling
.right
;
1789 neargrandnephew
= nearnephew
.left
;
1790 fargrandnephew
= nearnephew
.right
;
1794 nearnephew
= childsibling
.right
;
1795 farnephew
= childsibling
.left
;
1796 neargrandnephew
= nearnephew
.right
;
1797 fargrandnephew
= nearnephew
.left
;
1800 if (fargrandnephew
!= null && fargrandnephew
.red
)
1803 Node
.CopyNode(ref nearnephew
, maxsnapid
, generation
);
1805 //The end result of this will always be e copy of parent
1806 Node
.update(ref parent
, comp
< 0, neargrandnephew
, maxsnapid
, generation
);
1807 Node
.update(ref childsibling
, comp
> 0, nearnephew
, maxsnapid
, generation
);
1811 nearnephew
.left
= parent
;
1812 parent
.right
= neargrandnephew
;
1816 nearnephew
.right
= parent
;
1817 parent
.left
= neargrandnephew
;
1820 cursor
= childsibling
;
1821 childsibling
.red
= false;
1822 nearnephew
.red
= true;
1823 fargrandnephew
.red
= false;
1829 cursor
.size
= parent
.size
;
1830 nearnephew
.size
= cursor
.size
- cursor
.items
- farnephew
.size
;
1831 parent
.size
= nearnephew
.size
- nearnephew
.items
- fargrandnephew
.size
;
1833 cursor
.size
= parent
.size
;
1834 nearnephew
.size
= cursor
.size
- 1 - farnephew
.size
;
1835 parent
.size
= nearnephew
.size
- 1 - fargrandnephew
.size
;
1839 fixheight(nearnephew
);
1843 else if (neargrandnephew
!= null && neargrandnephew
.red
)
1846 Node
.CopyNode(ref neargrandnephew
, maxsnapid
, generation
);
1851 Node
.update(ref childsibling
, true, neargrandnephew
, maxsnapid
, generation
);
1852 Node
.update(ref nearnephew
, true, neargrandnephew
.right
, maxsnapid
, generation
);
1853 Node
.update(ref parent
, false, neargrandnephew
.left
, maxsnapid
, generation
);
1855 childsibling
.left
= neargrandnephew
;
1856 nearnephew
.left
= neargrandnephew
.right
;
1857 parent
.right
= neargrandnephew
.left
;
1859 neargrandnephew
.left
= parent
;
1860 neargrandnephew
.right
= nearnephew
;
1865 Node
.update(ref childsibling
, false, neargrandnephew
, maxsnapid
, generation
);
1866 Node
.update(ref nearnephew
, false, neargrandnephew
.left
, maxsnapid
, generation
);
1867 Node
.update(ref parent
, true, neargrandnephew
.right
, maxsnapid
, generation
);
1869 childsibling
.right
= neargrandnephew
;
1870 nearnephew
.right
= neargrandnephew
.left
;
1871 parent
.left
= neargrandnephew
.right
;
1873 neargrandnephew
.right
= parent
;
1874 neargrandnephew
.left
= nearnephew
;
1877 cursor
= childsibling
;
1878 childsibling
.red
= false;
1880 neargrandnephew
.rank
++;
1884 cursor
.size
= parent
.size
;
1885 parent
.size
= parent
.items
+ (parent
.left
== null ? 0 : parent
.left
.size
) + (parent
.right
== null ? 0 : parent
.right
.size
);
1886 nearnephew
.size
= nearnephew
.items
+ (nearnephew
.left
== null ? 0 : nearnephew
.left
.size
) + (nearnephew
.right
== null ? 0 : nearnephew
.right
.size
);
1887 neargrandnephew
.size
= neargrandnephew
.items
+ parent
.size
+ nearnephew
.size
;
1889 cursor
.size
= parent
.size
;
1890 parent
.size
= 1 + (parent
.left
== null ? 0 : parent
.left
.size
) + (parent
.right
== null ? 0 : parent
.right
.size
);
1891 nearnephew
.size
= 1 + (nearnephew
.left
== null ? 0 : nearnephew
.left
.size
) + (nearnephew
.right
== null ? 0 : nearnephew
.right
.size
);
1892 neargrandnephew
.size
= 1 + parent
.size
+ nearnephew
.size
;
1896 fixheight(nearnephew
);
1897 fixheight(neargrandnephew
);
1904 Node
.update(ref parent
, comp
< 0, nearnephew
, maxsnapid
, generation
);
1905 Node
.update(ref childsibling
, comp
> 0, parent
, maxsnapid
, generation
);
1909 childsibling
.left
= parent
;
1910 parent
.right
= nearnephew
;
1914 childsibling
.right
= parent
;
1915 parent
.left
= nearnephew
;
1918 cursor
= childsibling
;
1919 childsibling
.red
= false;
1920 nearnephew
.red
= true;
1925 cursor
.size
= parent
.size
;
1926 parent
.size
-= farnephew
.size
+ cursor
.items
;
1928 cursor
.size
= parent
.size
;
1929 parent
.size
-= farnephew
.size
+ 1;
1937 else if (farnephew
!= null && farnephew
.red
)
1939 nearnephew
= comp
> 0 ? childsibling
.left
: childsibling
.right
;
1941 Node
.update(ref parent
, comp
< 0, nearnephew
, maxsnapid
, generation
);
1942 Node
.CopyNode(ref childsibling
, maxsnapid
, generation
);
1945 childsibling
.left
= parent
;
1946 childsibling
.right
= farnephew
;
1950 childsibling
.right
= parent
;
1951 childsibling
.left
= farnephew
;
1956 childsibling
.left
= parent
;
1957 parent
.right
= nearnephew
;
1961 childsibling
.right
= parent
;
1962 parent
.left
= nearnephew
;
1965 cursor
= childsibling
;
1966 cursor
.red
= parent
.red
;
1968 farnephew
.red
= false;
1971 childsibling
.rank
++;
1975 cursor
.size
= parent
.size
;
1976 parent
.size
-= farnephew
.size
+ cursor
.items
;
1978 cursor
.size
= parent
.size
;
1979 parent
.size
-= farnephew
.size
+ 1;
1986 else if (nearnephew
!= null && nearnephew
.red
)
1989 Node
.CopyNode(ref nearnephew
, maxsnapid
, generation
);
1994 Node
.update(ref childsibling
, true, nearnephew
.right
, maxsnapid
, generation
);
1995 Node
.update(ref parent
, false, nearnephew
.left
, maxsnapid
, generation
);
1997 childsibling
.left
= nearnephew
.right
;
1998 parent
.right
= nearnephew
.left
;
2000 nearnephew
.left
= parent
;
2001 nearnephew
.right
= childsibling
;
2006 Node
.update(ref childsibling
, false, nearnephew
.left
, maxsnapid
, generation
);
2007 Node
.update(ref parent
, true, nearnephew
.right
, maxsnapid
, generation
);
2009 childsibling
.right
= nearnephew
.left
;
2010 parent
.left
= nearnephew
.right
;
2012 nearnephew
.right
= parent
;
2013 nearnephew
.left
= childsibling
;
2016 cursor
= nearnephew
;
2017 cursor
.red
= parent
.red
;
2024 cursor
.size
= parent
.size
;
2025 parent
.size
= parent
.items
+ (parent
.left
== null ? 0 : parent
.left
.size
) + (parent
.right
== null ? 0 : parent
.right
.size
);
2026 childsibling
.size
= childsibling
.items
+ (childsibling
.left
== null ? 0 : childsibling
.left
.size
) + (childsibling
.right
== null ? 0 : childsibling
.right
.size
);
2028 cursor
.size
= parent
.size
;
2029 parent
.size
= 1 + (parent
.left
== null ? 0 : parent
.left
.size
) + (parent
.right
== null ? 0 : parent
.right
.size
);
2030 childsibling
.size
= 1 + (childsibling
.left
== null ? 0 : childsibling
.left
.size
) + (childsibling
.right
== null ? 0 : childsibling
.right
.size
);
2034 fixheight(childsibling
);
2039 {//Case 1a can't happen
2040 throw new Exception("Case 1a can't happen here");
2052 cursor
= path
[--level
];
2055 Node
.update(ref cursor
, dirs
[level
] > 0, swap
, maxsnapid
, generation
);
2058 if (dirs
[level
] > 0)
2061 cursor
.right
= swap
;
2064 cursor
.size
-= removedcount
;
2074 //Stage 6: fixup to the root
2077 Node child
= cursor
;
2079 cursor
= path
[--level
];
2082 if (child
!= (dirs
[level
] > 0 ? cursor
.left
: cursor
.right
))
2083 Node
.update(ref cursor
, dirs
[level
] > 0, child
, maxsnapid
, generation
);
2086 cursor
.size
-= removedcount
;
2096 depth
= root
.height
;
2106 /// Remove all items from this collection.
2116 private void clear()
2128 /// Remove all items in another collection from this one. If this collection
2129 /// has bag semantics, take multiplicities into account.
2131 /// <param name="items">The items to remove.</param>
2133 public void RemoveAll(MSG
.IEnumerable
<T
> items
)
2139 foreach (T item
in items
)
2145 removeIterative(ref jtem
, false);
2151 /// Remove all items not in some other collection from this one. If this collection
2152 /// has bag semantics, take multiplicities into account.
2154 /// <param name="items">The items to retain.</param>
2156 public void RetainAll(MSG
.IEnumerable
<T
> items
)
2160 //A much more efficient version is possible if items is sorted like this.
2161 //Well, it is unclear how efficient it would be.
2162 //We could use a marking method!?
2163 TreeBag
<T
> t
= (TreeBag
<T
>)MemberwiseClone();
2166 foreach (T item
in items
)
2167 if (ContainsCount(item
) > t
.ContainsCount(item
))
2172 blackdepth
= t
.blackdepth
;
2180 /// Check if this collection contains all the values in another collection.
2181 /// If this collection has bag semantics (<code>NoDuplicates==false</code>)
2182 /// the check is made with respect to multiplicities, else multiplicities
2183 /// are not taken into account.
2185 /// <param name="items">The </param>
2186 /// <returns>True if all values in <code>items</code>is in this collection.</returns>
2188 public bool ContainsAll(MSG
.IEnumerable
<T
> items
)
2190 //This is worst-case O(m*logn)
2191 foreach (T item
in items
)
2192 if (!Contains(item
)) return false;
2200 /// Create a new indexed sorted collection consisting of the items of this
2201 /// indexed sorted collection satisfying a certain predicate.
2203 /// <param name="filter">The filter delegate defining the predicate.</param>
2204 /// <returns>The new indexed sorted collection.</returns>
2206 public IIndexedSorted
<T
> FindAll(Filter
<T
> filter
)
2208 TreeBag
<T
> res
= new TreeBag
<T
>(comparer
);
2209 MSG
.IEnumerator
<T
> e
= GetEnumerator();
2210 Node head
= null, tail
= null;
2215 while (e
.MoveNext())
2217 T thisitem
= e
.Current
;
2219 //We could document that filter will only be called
2220 //once on each unique item. That might even be good for the user!
2221 if (tail
!=null && comparer
.Compare(thisitem
, tail
.item
) == 0)
2228 if (filter(thisitem
))
2232 head
= tail
= new Node();
2237 tail
.size
= tail
.items
;
2239 tail
.right
= new Node();
2243 tail
.item
= thisitem
;
2249 tail
.size
= tail
.items
;
2255 int blackheight
= 0, red
= z
, maxred
= 1;
2257 while (maxred
<= red
)
2264 res
.root
= TreeBag
<T
>.maketreer(ref head
, blackheight
, maxred
, red
);
2265 res
.blackdepth
= blackheight
;
2275 /// Create a new indexed sorted collection consisting of the results of
2276 /// mapping all items of this list.
2277 /// <exception cref="ArgumentException"/> if the map is not increasing over
2278 /// the items of this collection (with respect to the two given comparison
2281 /// <param name="mapper">The delegate definging the map.</param>
2282 /// <param name="c">The comparion relation to use for the result.</param>
2283 /// <returns>The new sorted collection.</returns>
2285 public IIndexedSorted
<V
> Map
<V
>(Mapper
<T
,V
> mapper
, IComparer
<V
> c
)
2287 TreeBag
<V
> res
= new TreeBag
<V
>(c
);
2292 MSG
.IEnumerator
<T
> e
= GetEnumerator();
2293 TreeBag
<V
>.Node head
= null, tail
= null;
2294 V oldv
= default(V
);
2297 T lastitem
= default(T
);
2299 while (e
.MoveNext())
2301 T thisitem
= e
.Current
;
2303 //We could document that mapper will only be called
2304 //once on each unique item. That might even be good for the user!
2305 if (tail
!= null && comparer
.Compare(thisitem
, lastitem
) == 0)
2311 V newv
= mapper(thisitem
);
2315 head
= tail
= new TreeBag
<V
>.Node();
2320 int comp
= c
.Compare(oldv
, newv
);
2331 throw new ArgumentException("mapper not monotonic");
2333 tail
.size
= tail
.items
;
2335 tail
.right
= new TreeBag
<V
>.Node();
2340 lastitem
= thisitem
;
2342 tail
.item
= oldv
= newv
;
2346 tail
.size
= tail
.items
;
2349 int blackheight
= 0, red
= z
, maxred
= 1;
2351 while (maxred
<= red
)
2358 res
.root
= TreeBag
<V
>.maketreer(ref head
, blackheight
, maxred
, red
);
2359 res
.blackdepth
= blackheight
;
2365 //below is the bag utility stuff
2367 /// Count the number of items of the collection equal to a particular value.
2368 /// Returns 0 if and only if the value is not in the collection.
2370 /// <param name="item">The value to count.</param>
2371 /// <returns>The number of copies found.</returns>
2373 public int ContainsCount(T item
)
2376 Node next
; int comp
= 0;
2379 while (next
!= null)
2381 comp
= comparer
.Compare(next
.item
, item
);
2385 next
= comp
< 0 ? right(next
) : left(next
);
2390 //Since we are strictly NoDuplicates we just do
2391 return Contains(item
) ? 1 : 0;
2397 /// Remove all items equivalent to a given value.
2399 /// <param name="item">The value to remove.</param>
2401 public void RemoveAllCopies(T item
)
2405 removeIterative(ref item
, true);
2415 #region IIndexed<T> Members
2417 private Node
findNode(int i
)
2421 throw new NotSupportedException("Indexing not supported for snapshots");
2426 if (i
>= 0 && i
< size
)
2429 int j
= next
.left
== null ? 0 : next
.left
.size
;
2434 i
-= j
+ next
.items
;
2448 throw new IndexOutOfRangeException();
2450 throw new NotSupportedException();
2456 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
2457 /// >= the size of the collection.
2459 /// <value>The i'th item of this list.</value>
2460 /// <param name="i">the index to lookup</param>
2462 public T
this[int i
] { [Tested] get { return findNode(i).item; }
}
2466 /// Searches for an item in the list going forwrds from the start.
2468 /// <param name="item">Item to search for.</param>
2469 /// <returns>Index of item from start.</returns>
2471 public int IndexOf(T item
)
2475 return indexOf(item
, out upper
);
2479 private int indexOf(T item
, out int upper
)
2483 throw new NotSupportedException("Indexing not supported for snapshots");
2486 int ind
= 0; Node next
= root
;
2488 while (next
!= null)
2490 int comp
= comparer
.Compare(item
, next
.item
);
2496 int leftcnt
= next
.left
== null ? 0 : next
.left
.size
;
2501 upper
= ind
+ leftcnt
+ next
.items
- 1;
2502 return ind
+ leftcnt
;
2504 return upper
= ind
+ leftcnt
;
2510 ind
= ind
+ next
.items
+ leftcnt
;
2512 ind
= ind
+ 1 + leftcnt
;
2525 /// Searches for an item in the list going backwords from the end.
2527 /// <param name="item">Item to search for.</param>
2528 /// <returns>Index of of item from the end.</returns>
2530 public int LastIndexOf(T item
)
2534 indexOf(item
, out res
);
2537 //We have NoDuplicates==true for the set
2538 return IndexOf(item
);
2544 /// Remove the item at a specific position of the list.
2545 /// <exception cref="IndexOutOfRangeException"/> if i is negative or
2546 /// >= the size of the collection.
2548 /// <param name="i">The index of the item to remove.</param>
2549 /// <returns>The removed item.</returns>
2551 public T
RemoveAt(int i
)
2555 if (i
< 0 || i
>= size
)
2556 throw new IndexOutOfRangeException("Index out of range for sequenced collection");
2558 //We must follow the pattern of removeIterative()
2559 while (dirs
.Length
< 2 * blackdepth
)
2561 dirs
= new int[2 * dirs
.Length
];
2562 path
= new Node
[2 * dirs
.Length
];
2570 int j
= cursor
.left
== null ? 0 : cursor
.left
.size
;
2575 i
-= j
+ cursor
.items
;
2582 path
[level
++] = cursor
;
2583 cursor
= cursor
.right
;
2590 path
[level
++] = cursor
;
2591 cursor
= cursor
.left
;
2595 T retval
= cursor
.item
;
2600 resplicebag(level
, cursor
);
2605 removeIterativePhase2(cursor
, level
);
2608 throw new NotSupportedException();
2613 private void resplicebag(int level
, Node cursor
)
2616 Node
.CopyNode(ref cursor
, maxsnapid
, generation
);
2624 cursor
= path
[level
];
2626 Node
.update(ref cursor
, dirs
[level
] > 0, kid
, maxsnapid
, generation
);
2634 /// Remove all items in an index interval.
2635 /// <exception cref="IndexOutOfRangeException"/>???.
2637 /// <param name="start">The index of the first item to remove.</param>
2638 /// <param name="count">The number of items to remove.</param>
2640 public void RemoveInterval(int start
, int count
)
2642 if (start
< 0 || count
< 0)
2643 throw new ArgumentOutOfRangeException();
2645 if (start
+ count
> this.size
)
2646 throw new ArgumentException();
2650 //This is terrible for large count. We should split the tree at
2651 //the endpoints of the range and fuse the parts!
2652 //We really need good internal destructive split and catenate functions!
2653 for (int i
= 0; i
< count
; i
++)
2659 /// <exception cref="IndexOutOfRangeException"/>.
2661 /// <value>The directed collection of items in a specific index interval.</value>
2662 /// <param name="start">The low index of the interval (inclusive).</param>
2663 /// <param name="end">The high index of the interval (exclusive).</param>
2665 public IDirectedCollectionValue
<T
> this[int start
, int end
]
2670 checkRange(start
, end
- start
);
2671 return new Interval(this, start
, end
- start
, true);
2675 #region Interval nested class
2676 class Interval
: CollectionValueBase
<T
>, IDirectedCollectionValue
<T
>
2678 int start
, length
, stamp
;
2685 internal Interval(TreeBag
<T
> tree
, int start
, int count
, bool forwards
)
2688 if (tree
.isSnapShot
)
2689 throw new NotSupportedException("Indexing not supported for snapshots");
2691 this.start
= start
; this.length
= count
;this.forwards
= forwards
;
2692 this.tree
= tree
; this.stamp
= tree
.stamp
;
2697 public override int Count { [Tested]get { return length; }
}
2700 public override Speed CountSpeed { get { return Speed.Constant; }
}
2703 public override MSG
.IEnumerator
<T
> GetEnumerator()
2706 tree
.modifycheck(stamp
);
2710 Node cursor
= tree
.root
;
2711 Node
[] path
= new Node
[2 * tree
.blackdepth
];
2712 int level
= 0, totaltogo
= length
;
2723 int j
= cursor
.left
== null ? 0 : cursor
.left
.size
;
2728 i
-= j
+ cursor
.items
;
2731 togo
= cursor
.items
+ i
;
2737 cursor
= cursor
.right
;
2742 togo
= cursor
.items
;
2748 path
[level
++] = cursor
;
2749 cursor
= cursor
.left
;
2753 T current
= cursor
.item
;
2755 while (totaltogo
-- > 0)
2757 yield return current
;
2758 tree
.modifycheck(stamp
);
2763 if (cursor
.right
!= null)
2765 path
[level
] = cursor
= cursor
.right
;
2766 while (cursor
.left
!= null)
2767 path
[++level
] = cursor
= cursor
.left
;
2769 else if (level
== 0)
2772 cursor
= path
[--level
];
2774 current
= cursor
.item
;
2776 togo
= cursor
.items
;
2782 int i
= start
+ length
- 1;
2786 int j
= cursor
.left
== null ? 0 : cursor
.left
.size
;
2791 if (i
- j
< cursor
.items
)
2796 i
-= j
+ cursor
.items
;
2800 path
[level
++] = cursor
;
2801 cursor
= cursor
.right
;
2812 cursor
= cursor
.left
;
2816 T current
= cursor
.item
;
2818 while (totaltogo
-- > 0)
2820 yield return current
;
2821 tree
.modifycheck(stamp
);
2826 if (cursor
.left
!= null)
2828 path
[level
] = cursor
= cursor
.left
;
2829 while (cursor
.right
!= null)
2830 path
[++level
] = cursor
= cursor
.right
;
2832 else if (level
== 0)
2835 cursor
= path
[--level
];
2837 current
= cursor
.item
;
2839 togo
= cursor
.items
;
2845 throw new NotSupportedException();
2851 public IDirectedCollectionValue
<T
> Backwards()
2852 { return new Interval(tree, start, length, !forwards); }
2856 IDirectedEnumerable
<T
> C5
.IDirectedEnumerable
<T
>.Backwards()
2857 { return Backwards(); }
2861 public EnumerationDirection Direction
2866 return forwards
? EnumerationDirection
.Forwards
: EnumerationDirection
.Backwards
;
2873 /// Create a collection containing the same items as this collection, but
2874 /// whose enumerator will enumerate the items backwards. The new collection
2875 /// will become invalid if the original is modified. Method typicaly used as in
2876 /// <code>foreach (T x in coll.Backwards()) {...}</code>
2878 /// <returns>The backwards collection.</returns>
2880 public IDirectedCollectionValue
<T
> Backwards() { return RangeAll().Backwards(); }
2884 IDirectedEnumerable
<T
> IDirectedEnumerable
<T
>.Backwards() { return Backwards(); }
2888 #region ISequenced Members
2890 int ISequenced
<T
>.GetHashCode() { return sequencedhashcode(); }
2894 bool ISequenced
<T
>.Equals(ISequenced
<T
> that
) { return sequencedequals(that); }
2897 #region PriorityQueue Members
2900 /// The comparer object supplied at creation time for this collection
2902 /// <value>The comparer</value>
2903 public IComparer
<T
> Comparer { get { return comparer; }
}
2907 /// Find the current least item of this priority queue.
2909 /// <returns>The least item.</returns>
2914 throw new InvalidOperationException("Priority queue is empty");
2915 #if MAINTAIN_EXTREMA
2918 Node cursor
= root
, next
= left(cursor
);
2920 while (next
!= null)
2923 next
= left(cursor
);
2932 /// Remove the least item from this priority queue.
2934 /// <returns>The removed item.</returns>
2936 public T
DeleteMin()
2940 //persistence guard?
2942 throw new InvalidOperationException("Priority queue is empty");
2944 //We must follow the pattern of removeIterative()
2950 while (cursor
.left
!= null)
2953 path
[level
++] = cursor
;
2954 cursor
= cursor
.left
;
2957 T retval
= cursor
.item
;
2960 if (cursor
.items
> 1)
2962 resplicebag(level
, cursor
);
2967 removeIterativePhase2(cursor
, level
);
2973 /// Find the current largest item of this priority queue.
2975 /// <returns>The largest item.</returns>
2980 throw new InvalidOperationException("Priority queue is empty");
2982 #if MAINTAIN_EXTREMA
2985 Node cursor
= root
, next
= right(cursor
);
2987 while (next
!= null)
2990 next
= right(cursor
);
2999 /// Remove the largest item from this priority queue.
3001 /// <returns>The removed item.</returns>
3003 public T
DeleteMax()
3005 //persistence guard?
3008 throw new InvalidOperationException("Priority queue is empty");
3010 //We must follow the pattern of removeIterative()
3016 while (cursor
.right
!= null)
3019 path
[level
++] = cursor
;
3020 cursor
= cursor
.right
;
3023 T retval
= cursor
.item
;
3026 if (cursor
.items
> 1)
3028 resplicebag(level
, cursor
);
3033 removeIterativePhase2(cursor
, level
);
3038 #region IPredecesorStructure<T> Members
3041 /// Find the strict predecessor in the sorted collection of a particular value,
3042 /// i.e. the largest item in the collection less than the supplied value.
3043 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3044 /// supplied value is less than or equal to the minimum of this collection.)
3046 /// <param name="item">The item to find the predecessor for.</param>
3047 /// <returns>The predecessor.</returns>
3049 public T
Predecessor(T item
)
3051 Node cursor
= root
, bestsofar
= null;
3053 while (cursor
!= null)
3055 int comp
= comparer
.Compare(cursor
.item
, item
);
3060 cursor
= right(cursor
);
3064 cursor
= left(cursor
);
3065 while (cursor
!= null)
3068 cursor
= right(cursor
);
3072 cursor
= left(cursor
);
3075 if (bestsofar
!= null)
3076 return bestsofar
.item
;
3078 throw new ArgumentOutOfRangeException("item", item
, "Below minimum of set");
3083 /// Find the weak predecessor in the sorted collection of a particular value,
3084 /// i.e. the largest item in the collection less than or equal to the supplied value.
3085 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3086 /// supplied value is less than the minimum of this collection.)
3088 /// <param name="item">The item to find the weak predecessor for.</param>
3089 /// <returns>The weak predecessor.</returns>
3091 public T
WeakPredecessor(T item
)
3093 Node cursor
= root
, bestsofar
= null;
3095 while (cursor
!= null)
3097 int comp
= comparer
.Compare(cursor
.item
, item
);
3102 cursor
= right(cursor
);
3107 cursor
= left(cursor
);
3110 if (bestsofar
!= null)
3111 return bestsofar
.item
;
3113 throw new ArgumentOutOfRangeException("item", item
, "Below minimum of set");
3118 /// Find the strict successor in the sorted collection of a particular value,
3119 /// i.e. the least item in the collection greater than the supplied value.
3120 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3121 /// supplied value is greater than or equal to the maximum of this collection.)
3123 /// <param name="item">The item to find the successor for.</param>
3124 /// <returns>The successor.</returns>
3126 public T
Successor(T item
)
3128 Node cursor
= root
, bestsofar
= null;
3130 while (cursor
!= null)
3132 int comp
= comparer
.Compare(cursor
.item
, item
);
3137 cursor
= left(cursor
);
3141 cursor
= right(cursor
);
3142 while (cursor
!= null)
3145 cursor
= left(cursor
);
3149 cursor
= right(cursor
);
3152 if (bestsofar
!= null)
3153 return bestsofar
.item
;
3155 throw new ArgumentOutOfRangeException("item", item
, "Above maximum of set");
3160 /// Find the weak successor in the sorted collection of a particular value,
3161 /// i.e. the least item in the collection greater than or equal to the supplied value.
3162 /// <exception cref="InvalidOperationException"/> if no such element exists (the
3163 /// supplied value is greater than the maximum of this collection.)
3165 /// <param name="item">The item to find the weak successor for.</param>
3166 /// <returns>The weak successor.</returns>
3168 public T
WeakSuccessor(T item
)
3170 Node cursor
= root
, bestsofar
= null;
3172 while (cursor
!= null)
3174 int comp
= comparer
.Compare(cursor
.item
, item
);
3181 cursor
= left(cursor
);
3184 cursor
= right(cursor
);
3187 if (bestsofar
!= null)
3188 return bestsofar
.item
;
3190 throw new ArgumentOutOfRangeException("item", item
, "Above maximum of set");
3195 #region ISorted<T> Members
3198 /// Query this sorted collection for items greater than or equal to a supplied value.
3200 /// <param name="bot">The lower bound (inclusive).</param>
3201 /// <returns>The result directed collection.</returns>
3203 public IDirectedCollectionValue
<T
> RangeFrom(T bot
)
3204 { return new Range(this, true, bot, false, default(T), EnumerationDirection.Forwards); }
3208 /// Query this sorted collection for items between two supplied values.
3210 /// <param name="bot">The lower bound (inclusive).</param>
3211 /// <param name="top">The upper bound (exclusive).</param>
3212 /// <returns>The result directed collection.</returns>
3214 public IDirectedCollectionValue
<T
> RangeFromTo(T bot
, T top
)
3215 { return new Range(this, true, bot, true, top, EnumerationDirection.Forwards); }
3219 /// Query this sorted collection for items less than a supplied value.
3221 /// <param name="top">The upper bound (exclusive).</param>
3222 /// <returns>The result directed collection.</returns>
3224 public IDirectedCollectionValue
<T
> RangeTo(T top
)
3225 { return new Range(this, false, default(T), true, top, EnumerationDirection.Forwards); }
3229 /// Create a directed collection with the same items as this collection.
3231 /// <returns>The result directed collection.</returns>
3233 public IDirectedCollectionValue
<T
> RangeAll()
3234 { return new Range(this, false, default(T), false, default(T), EnumerationDirection.Forwards); }
3238 IDirectedEnumerable
<T
> ISorted
<T
>.RangeFrom(T bot
) { return RangeFrom(bot); }
3242 IDirectedEnumerable
<T
> ISorted
<T
>.RangeFromTo(T bot
, T top
) { return RangeFromTo(bot, top); }
3246 IDirectedEnumerable
<T
> ISorted
<T
>.RangeTo(T top
) { return RangeTo(top); }
3249 //Utility for CountXxxx. Actually always called with strict = true.
3250 private int countTo(T item
, bool strict
)
3254 throw new NotSupportedException("Indexing not supported for snapshots");
3257 int ind
= 0, comp
= 0; Node next
= root
;
3259 while (next
!= null)
3261 comp
= comparer
.Compare(item
, next
.item
);
3266 int leftcnt
= next
.left
== null ? 0 : next
.left
.size
;
3269 return strict
? ind
+ leftcnt
: ind
+ leftcnt
+ next
.items
;
3272 ind
= ind
+ next
.items
+ leftcnt
;
3277 return strict
? ind
+ leftcnt
: ind
+ leftcnt
+ 1;
3280 ind
= ind
+ 1 + leftcnt
;
3287 //if we get here, we are at the same side of the whole collection:
3290 throw new NotSupportedException("Code compiled w/o size!");
3296 /// Perform a search in the sorted collection for the ranges in which a
3297 /// non-decreasing function from the item type to <code>int</code> is
3298 /// negative, zero respectively positive. If the supplied cut function is
3299 /// not non-decreasing, the result of this call is undefined.
3301 /// <param name="c">The cut function <code>T</code> to <code>int</code>, given
3302 /// as an <code>IComparable<T></code> object, where the cut function is
3303 /// the <code>c.CompareTo(T that)</code> method.</param>
3304 /// <param name="low">Returns the largest item in the collection, where the
3305 /// cut function is negative (if any).</param>
3306 /// <param name="lowIsValid">True if the cut function is negative somewhere
3307 /// on this collection.</param>
3308 /// <param name="high">Returns the least item in the collection, where the
3309 /// cut function is positive (if any).</param>
3310 /// <param name="highIsValid">True if the cut function is positive somewhere
3311 /// on this collection.</param>
3312 /// <returns></returns>
3314 public bool Cut(IComparable
<T
> c
, out T low
, out bool lowIsValid
, out T high
, out bool highIsValid
)
3316 Node cursor
= root
, lbest
= null, rbest
= null;
3319 while (cursor
!= null)
3321 int comp
= c
.CompareTo(cursor
.item
);
3326 cursor
= right(cursor
);
3331 cursor
= left(cursor
);
3337 Node tmp
= left(cursor
);
3339 while (tmp
!= null && c
.CompareTo(tmp
.item
) == 0)
3348 if (c
.CompareTo(tmp
.item
) > 0)
3358 tmp
= right(cursor
);
3359 while (tmp
!= null && c
.CompareTo(tmp
.item
) == 0)
3368 if (c
.CompareTo(tmp
.item
) < 0)
3382 if (highIsValid
= (rbest
!= null))
3387 if (lowIsValid
= (lbest
!= null))
3397 /// Determine the number of items at or above a supplied threshold.
3399 /// <param name="bot">The lower bound (inclusive)</param>
3400 /// <returns>The number of matcing items.</returns>
3402 public int CountFrom(T bot
) { return size - countTo(bot, true); }
3406 /// Determine the number of items between two supplied thresholds.
3408 /// <param name="bot">The lower bound (inclusive)</param>
3409 /// <param name="top">The upper bound (exclusive)</param>
3410 /// <returns>The number of matcing items.</returns>
3412 public int CountFromTo(T bot
, T top
)
3414 if (comparer
.Compare(bot
, top
) >= 0)
3417 return countTo(top
, true) - countTo(bot
, true);
3422 /// Determine the number of items below a supplied threshold.
3424 /// <param name="top">The upper bound (exclusive)</param>
3425 /// <returns>The number of matcing items.</returns>
3427 public int CountTo(T top
) { return countTo(top, true); }
3431 /// Remove all items of this collection above or at a supplied threshold.
3433 /// <param name="low">The lower threshold (inclusive).</param>
3435 public void RemoveRangeFrom(T low
)
3439 int count
= CountFrom(low
);
3444 for (int i
= 0; i
< count
; i
++)
3450 /// Remove all items of this collection between two supplied thresholds.
3452 /// <param name="low">The lower threshold (inclusive).</param>
3453 /// <param name="hi">The upper threshold (exclusive).</param>
3455 public void RemoveRangeFromTo(T low
, T hi
)
3459 int count
= CountFromTo(low
, hi
);
3464 for (int i
= 0; i
< count
; i
++)
3465 Remove(Predecessor(hi
));
3470 /// Remove all items of this collection below a supplied threshold.
3472 /// <param name="hi">The upper threshold (exclusive).</param>
3474 public void RemoveRangeTo(T hi
)
3478 int count
= CountTo(hi
);
3483 for (int i
= 0; i
< count
; i
++)
3489 #region IPersistent<T> Members
3491 private bool disposed
;
3496 /// If this tree is a snapshot, remove registration in base tree
3499 public void Dispose()
3502 GC
.SuppressFinalize(this);
3506 private void Dispose(bool disposing
)
3514 snapdata
.Remove(generation
, disposing
);
3529 /// If this tree is a snapshot, remove registration in base tree
3538 /// Make a (read-only) snap shot of this collection.
3540 /// <returns>The snap shot.</returns>
3542 public ISorted
<T
> Snapshot()
3546 throw new InvalidOperationException("Cannot snapshot a snapshot");
3548 if (snapdata
== null)
3550 snapdata
= new SnapData(this);
3553 snapdata
.Add(generation
, this);
3555 TreeBag
<T
> res
= (TreeBag
<T
>)MemberwiseClone();
3557 res
.isReadOnly
= true; res
.isSnapShot
= true;
3558 maxsnapid
= generation
++;
3565 #region Snapdata nested class
3566 //In a class by itself: the tree itself and snapshots must have a ref to
3567 //the snapids, but we do not want snapshots to have a (strong) ref to the full
3568 //updatable tree, which should be garbagecollectable even if there are still
3571 //Access to SnapData should be thread safe since we expect finalisers
3572 //of snapshots to remove a snapid that is garbagecollected without
3573 //having been explicitly Disposed. Therefore we access SnapData through
3574 //synchronized method/properties.
3579 TreeBag
<int> snapids
= new TreeBag
<int>(new IC());
3581 WeakReference master
;
3584 internal SnapData(TreeBag
<T
> tree
)
3586 master
= new WeakReference(tree
);
3591 public bool Add(int i
, TreeBag
<T
> tree
)
3595 bool res
= snapids
.Add(i
);
3597 //assert the following will be i:
3598 tree
.maxsnapid
= snapids
.Count
> 0 ? snapids
.FindMax() : -1;
3605 public bool Remove(int i
, bool updmaxsnapid
)
3609 bool res
= snapids
.Remove(i
);
3613 //Is this safe or/and overkill?
3614 object t
= master
.Target
;
3616 if (t
!= null && master
.IsAlive
)
3618 ((TreeBag
<T
>)t
).maxsnapid
= snapids
.Count
> 0 ? snapids
.FindMax() : -1;
3630 #region TreeBag.Range nested class
3632 internal class Range
: CollectionValueBase
<T
>, IDirectedCollectionValue
<T
>
3634 //We actually need exclusive upper and lower bounds, and flags to
3635 //indicate whether the bound is present (we canot rely on default(T))
3638 private TreeBag
<T
> basis
;
3640 private T lowend
, highend
;
3642 private bool haslowend
, hashighend
;
3644 EnumerationDirection direction
;
3648 public Range(TreeBag
<T
> basis
, bool haslowend
, T lowend
, bool hashighend
, T highend
, EnumerationDirection direction
)
3651 stamp
= basis
.stamp
;
3653 //lowind will be const; should we cache highind?
3654 this.lowend
= lowend
; //Inclusive
3655 this.highend
= highend
;//Exclusive
3656 this.haslowend
= haslowend
;
3657 this.hashighend
= hashighend
;
3658 this.direction
= direction
;
3660 #region IEnumerable<T> Members
3663 #region TreeBag.Range.Enumerator nested class
3665 public class Enumerator
: MSG
.IEnumerator
<T
>
3667 #region Private Fields
3668 private bool valid
= false, ready
= true;
3670 private IComparer
<T
> comparer
;
3677 private Node cursor
;
3679 private Node
[] path
; // stack of nodes
3681 private int level
= 0;
3683 private Range range
;
3685 private bool forwards
;
3689 public Enumerator(Range range
)
3691 comparer
= range
.basis
.comparer
;
3692 path
= new Node
[2 * range
.basis
.blackdepth
];
3694 forwards
= range
.direction
== EnumerationDirection
.Forwards
;
3695 cursor
= new Node();
3697 cursor
.right
= range
.basis
.root
;
3699 cursor
.left
= range
.basis
.root
;
3700 range
.basis
.modifycheck(range
.stamp
);
3704 int compare(T i1
, T i2
) { return comparer.Compare(i1, i2); }
3708 /// Undefined if enumerator is not valid (MoveNext hash been called returning true)
3710 /// <value>The current value of the enumerator.</value>
3720 throw new InvalidOperationException();
3725 //Maintain a stack of nodes that are roots of
3726 //subtrees not completely exported yet. Invariant:
3727 //The stack nodes together with their right subtrees
3728 //consists of exactly the items we have not passed
3729 //yet (the top of the stack holds current item).
3731 /// Move enumerator to next item in tree, or the first item if
3732 /// this is the first call to MoveNext.
3733 /// <exception cref="InvalidOperationException"/> if underlying tree was modified.
3735 /// <returns>True if enumerator is valid now</returns>
3737 public bool MoveNext()
3739 range
.basis
.modifycheck(range
.stamp
);
3748 if (!valid
&& range
.haslowend
)
3750 cursor
= cursor
.right
;
3751 while (cursor
!= null)
3753 int comp
= compare(cursor
.item
, range
.lowend
);
3757 path
[level
++] = cursor
;
3759 cursor
= range
.basis
.left(cursor
);
3761 cursor
= cursor
.left
;
3767 cursor
= range
.basis
.right(cursor
);
3769 cursor
= cursor
.right
;
3774 path
[level
] = cursor
;
3782 return valid
= ready
= false;
3784 cursor
= path
[--level
];
3788 else if (range
.basis
.right(cursor
) != null)
3790 path
[level
] = cursor
= range
.basis
.right(cursor
);
3792 Node next
= range
.basis
.left(cursor
);
3794 while (next
!= null)
3796 path
[++level
] = cursor
= next
;
3797 next
= range
.basis
.left(cursor
);
3801 else if (cursor
.right
!= null)
3803 path
[level
] = cursor
= cursor
.right
;
3804 while (cursor
.left
!= null)
3805 path
[++level
] = cursor
= cursor
.left
;
3808 else if (level
== 0)
3809 return valid
= ready
= false;
3811 cursor
= path
[--level
];
3813 current
= cursor
.item
;
3814 if (range
.hashighend
&& compare(current
, range
.highend
) >= 0)
3815 return valid
= ready
= false;
3818 togo
= cursor
.items
;
3820 return valid
= true;
3824 if (!valid
&& range
.hashighend
)
3826 cursor
= cursor
.left
;
3827 while (cursor
!= null)
3829 int comp
= compare(cursor
.item
, range
.highend
);
3833 path
[level
++] = cursor
;
3835 cursor
= range
.basis
.right(cursor
);
3837 cursor
= cursor
.right
;
3843 cursor
= range
.basis
.left(cursor
);
3845 cursor
= cursor
.left
;
3853 return valid
= ready
= false;
3855 cursor
= path
[--level
];
3859 else if (range
.basis
.left(cursor
) != null)
3861 path
[level
] = cursor
= range
.basis
.left(cursor
);
3863 Node next
= range
.basis
.right(cursor
);
3865 while (next
!= null)
3867 path
[++level
] = cursor
= next
;
3868 next
= range
.basis
.right(cursor
);
3872 else if (cursor
.left
!= null)
3874 path
[level
] = cursor
= cursor
.left
;
3875 while (cursor
.right
!= null)
3876 path
[++level
] = cursor
= cursor
.right
;
3879 else if (level
== 0)
3880 return valid
= ready
= false;
3882 cursor
= path
[--level
];
3884 current
= cursor
.item
;
3885 if (range
.haslowend
&& compare(current
, range
.lowend
) < 0)
3886 return valid
= ready
= false;
3889 togo
= cursor
.items
;
3891 return valid
= true;
3897 public void Dispose()
3900 current
= default(T
);
3910 public override MSG
.IEnumerator
<T
> GetEnumerator() { return new Enumerator(this); }
3914 public EnumerationDirection Direction { [Tested]get { return direction; }
}
3923 return (!haslowend
|| basis
.comparer
.Compare(item
, lowend
) >= 0) && (!hashighend
|| basis
.comparer
.Compare(item
, highend
) < 0);
3929 if (stamp
< basis
.stamp
)
3930 throw new InvalidOperationException("Base collection was modified behind my back!");
3934 void syncstamp() { stamp = basis.stamp; }
3939 public IDirectedCollectionValue
<T
> Backwards()
3941 Range b
= (Range
)MemberwiseClone();
3943 b
.direction
= direction
== EnumerationDirection
.Forwards
? EnumerationDirection
.Backwards
: EnumerationDirection
.Forwards
;
3949 IDirectedEnumerable
<T
> IDirectedEnumerable
<T
>.Backwards() { return Backwards(); }
3953 public override int Count
3958 return haslowend
? (hashighend
? basis
.CountFromTo(lowend
, highend
) : basis
.CountFrom(lowend
)) : (hashighend
? basis
.CountTo(highend
) : basis
.Count
);
3962 //TODO: check that this is correct
3963 public override Speed CountSpeed { get { return Speed.Log; }
}
3969 #region fixheight utility
3972 public void fixheight(Node n
)
3974 int lh
= n
.left
== null ? 0 : n
.left
.height
+ 1;
3975 int rh
= n
.right
== null ? 0 : n
.right
.height
+ 1;
3977 n
.height
= (short)(lh
> rh
? lh
: rh
);
3985 /// Display this node on the console, and recursively its subnodes.
3987 /// <param name="n">Node to display</param>
3988 /// <param name="space">Indentation</param>
3989 private void minidump(Node n
, string space
)
3993 // System.Console.WriteLine(space + "null");
3997 minidump(n
.right
, space
+ " ");
3998 Console
.WriteLine(String
.Format("{0} {4} (rank={5}, size={1}, items={8}, h={2}, gen={3}, id={6}){7}", space
+ n
.item
,
4012 n
.red
? "RED" : "BLACK",
4025 n
.extra
== null ? "" : String
.Format(" [extra: lg={0}, c={1}, i={2}]", n
.extra
.lastgeneration
, n
.extra
.leftnode
? "L" : "R", n
.extra
.oldref
== null ? "()" : "" + n
.extra
.oldref
.item
),
4027 n
.lastgeneration
== -1 ? "" : String
.Format(" [extra: lg={0}, c={1}, i={2}]", n
.lastgeneration
, n
.leftnode
? "L" : "R", n
.oldref
== null ? "()" : "" + n
.oldref
.item
),
4038 minidump(n
.left
, space
+ " ");
4044 /// Print the tree structure to the console stdout.
4046 [Tested(via
= "Sawtooth")]
4047 public void dump() { dump(""); }
4051 /// Print the tree structure to the console stdout.
4053 [Tested(via
= "Sawtooth")]
4054 public void dump(string msg
)
4056 Console
.WriteLine(String
.Format(">>>>>>>>>>>>>>>>>>> dump {0} (count={1}, blackdepth={2}, depth={3}, gen={4})", msg
, size
, blackdepth
,
4068 check("", Console
.Out
); Console
.WriteLine("<<<<<<<<<<<<<<<<<<<");
4073 /// Display this tree on the console.
4075 /// <param name="msg">Identifying string of this call to dump</param>
4076 /// <param name="err">Extra (error)message to include</param>
4077 void dump(string msg
, string err
)
4079 Console
.WriteLine(String
.Format(">>>>>>>>>>>>>>>>>>> dump {0} (count={1}, blackdepth={2}, depth={3}, gen={4})", msg
, size
, blackdepth
,
4090 minidump(root
, ""); Console
.Write(err
);
4091 Console
.WriteLine("<<<<<<<<<<<<<<<<<<<");
4096 /// Print warning m on o if b is false.
4098 /// <param name="b">Condition that should hold</param>
4099 /// <param name="n">Place (used for id display)</param>
4100 /// <param name="m">Message</param>
4101 /// <param name="o">Output stream</param>
4102 /// <returns>b</returns>
4103 bool massert(bool b
, Node n
, string m
, System
.IO
.TextWriter o
)
4105 if (!b
) o
.WriteLine("*** Node (item={0}, id={1}): {2}", n
.item
,
4117 bool rbminicheck(Node n
, bool redp
, int prank
, System
.IO
.TextWriter o
, out T min
, out T max
, out int blackheight
, int maxgen
)
4118 {//Red-Black invariant
4121 res
= massert(!(n
.red
&& redp
), n
, "RED parent of RED node", o
) && res
;
4122 res
= massert(n
.left
== null || n
.right
!= null || n
.left
.red
, n
, "Left child black, but right child empty", o
) && res
;
4123 res
= massert(n
.right
== null || n
.left
!= null || n
.right
.red
, n
, "Right child black, but left child empty", o
) && res
;
4125 res
= massert(n
.red
== (n
.rank
== prank
), n
, "Bad color", o
) && res
;
4126 res
= massert(prank
<= n
.rank
+ 1, n
, "Parentrank-rank >= 2", o
) && res
;
4127 res
= massert((n
.left
!= null && n
.right
!= null) || n
.rank
== 1, n
, "Rank>1 but empty child", o
) && res
;
4130 bool sb
= n
.size
== (n
.left
== null ? 0 : n
.left
.size
) + (n
.right
== null ? 0 : n
.right
.size
) + n
.items
;
4132 res
= massert(sb
, n
, "Bad size", o
) && res
;
4134 bool sb
= n
.size
== (n
.left
== null ? 0 : n
.left
.size
) + (n
.right
== null ? 0 : n
.right
.size
) + 1;
4136 res
= massert(sb
, n
, "Bad size", o
) && res
;
4139 int lh
= n
.left
== null ? 0 : n
.left
.height
+ 1;
4140 int rh
= n
.right
== null ? 0 : n
.right
.height
+ 1;
4142 res
= massert(n
.height
== (lh
< rh
? rh
: lh
), n
, "Bad height", o
) && res
;
4153 int lbh
= 0, rbh
= 0;
4157 res
= rbminicheck(n
.left
, n
.red
, therank
, o
, out min
, out otherext
, out lbh
, generation
) && res
;
4158 res
= massert(comparer
.Compare(n
.item
, otherext
) > 0, n
, "Value not > all left children", o
) && res
;
4161 if (n
.right
!= null)
4163 res
= rbminicheck(n
.right
, n
.red
, therank
, o
, out otherext
, out max
, out rbh
, generation
) && res
;
4164 res
= massert(comparer
.Compare(n
.item
, otherext
) < 0, n
, "Value not < all right children", o
) && res
;
4167 res
= massert(rbh
== lbh
, n
, "Different blackheights of children", o
) && res
;
4168 blackheight
= n
.red
? rbh
: rbh
+ 1;
4170 //The rank is the number of black nodes from this one to
4171 //the leaves, not counting this one, but counting 1 for the empty
4172 //virtual leaf nodes.
4173 res
= massert(n
.rank
== rbh
+ 1, n
, "rank!=blackheight " + blackheight
, o
) && res
;
4183 bool rbminisnapcheck(Node n
, System
.IO
.TextWriter o
, out int size
, out T min
, out T max
)
4189 int lsz
= 0, rsz
= 0;
4192 Node
.Extra extra
= n
.extra
;
4193 Node child
= (extra
!= null && extra
.lastgeneration
>= treegen
&& extra
.leftnode
) ? extra
.oldref
: n
.left
;
4195 Node child
= (n
.lastgeneration
>= generation
&& n
.leftnode
) ? n
.oldref
: n
.left
;
4199 res
= rbminisnapcheck(child
, o
, out lsz
, out min
, out otherext
) && res
;
4200 res
= massert(comparer
.Compare(n
.item
, otherext
) > 0, n
, "Value not > all left children", o
) && res
;
4204 child
= (extra
!= null && extra
.lastgeneration
>= treegen
&& !extra
.leftnode
) ? extra
.oldref
: n
.right
;
4206 child
= (n
.lastgeneration
>= generation
&& !n
.leftnode
) ? n
.oldref
: n
.right
;
4210 res
= rbminisnapcheck(child
, o
, out rsz
, out otherext
, out max
) && res
;
4211 res
= massert(comparer
.Compare(n
.item
, otherext
) < 0, n
, "Value not < all right children", o
) && res
;
4214 size
= n
.items
+ lsz
+ rsz
;
4216 size
= 1 + lsz
+ rsz
;
4223 /// Checks red-black invariant. Dumps tree to console if bad
4225 /// <param name="name">Title of dump</param>
4226 /// <returns>false if invariant violation</returns>
4227 [Tested(via
= "Sawtooth")]
4228 public bool Check(string name
)
4230 System
.Text
.StringBuilder e
= new System
.Text
.StringBuilder();
4231 System
.IO
.TextWriter o
= new System
.IO
.StringWriter(e
);
4233 if (!check(name
, o
))
4237 dump(name
, e
.ToString());
4244 /// Checks red-black invariant. Dumps tree to console if bad
4246 /// <returns>false if invariant violation</returns>
4250 //return check("", System.IO.TextWriter.Null);
4251 //Console.WriteLine("bamse");
4256 bool check(string msg
, System
.IO
.TextWriter o
)
4265 //Console.WriteLine("Im'a snapshot");
4267 bool rv
= rbminisnapcheck(root
, o
, out thesize
, out min
, out max
);
4269 rv
= massert(size
== thesize
, root
, "bad snapshot size", o
) && rv
;
4274 bool res
= rbminicheck(root
, false, root
.rank
+ 1, o
, out min
, out max
, out blackheight
, generation
);
4276 res
= massert(root
.rank
== blackdepth
, root
, "blackdepth!=root.rank", o
) && res
;
4278 bool res
= rbminicheck(root
, false, 0, o
, out min
, out max
, out blackheight
, generation
);
4280 res
= massert(blackheight
== blackdepth
, root
, "bad blackh/d", o
) && res
;
4281 res
= massert(!root
.red
, root
, "root is red", o
) && res
;
4283 res
= massert(root
.size
== size
, root
, "count!=root.size", o
) && res
;
4286 res
= massert(root
.height
== depth
, root
, "depth!=root.height", o
) && res
;