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:
10 The above copyright notice and this permission notice shall be included in
11 all copies or substantial portions of the Software.
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
22 #define IMPROVED_COLLECTION_HASHFUNCTION
25 using System.Diagnostics;
26 using SCG = System.Collections.Generic;
30 /// A base class for implementing an IEnumerable<T>
33 public abstract class EnumerableBase<T> : SCG.IEnumerable<T>
36 /// Create an enumerator for this collection.
38 /// <returns>The enumerator</returns>
39 public abstract SCG.IEnumerator<T> GetEnumerator();
42 /// Count the number of items in an enumerable by enumeration
44 /// <param name="items">The enumerable to count</param>
45 /// <returns>The size of the enumerable</returns>
47 protected static int countItems(SCG.IEnumerable<T> items)
49 ICollectionValue<T> jtems = items as ICollectionValue<T>;
56 using (SCG.IEnumerator<T> e = items.GetEnumerator())
57 while (e.MoveNext()) count++;
62 #region IEnumerable Members
64 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
66 return GetEnumerator();
74 /// Base class for classes implementing ICollectionValue[T]
77 public abstract class CollectionValueBase<T> : EnumerableBase<T>, ICollectionValue<T>, IShowable
79 #region Event handling
80 EventBlock<T> eventBlock;
85 public virtual EventTypeEnum ListenableEvents { get { return 0; } }
88 /// A flag bitmap of the events currently subscribed to by this collection.
91 public virtual EventTypeEnum ActiveEvents { get { return eventBlock == null ? 0 : eventBlock.events; } }
93 private void checkWillListen(EventTypeEnum eventType)
95 if ((ListenableEvents & eventType) == 0)
96 throw new UnlistenableEventException();
100 /// The change event. Will be raised for every change operation on the collection.
102 public virtual event CollectionChangedHandler<T> CollectionChanged
104 add { checkWillListen(EventTypeEnum.Changed); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionChanged += value; }
107 checkWillListen(EventTypeEnum.Changed);
108 if (eventBlock != null)
110 eventBlock.CollectionChanged -= value;
111 if (eventBlock.events == 0) eventBlock = null;
116 /// Fire the CollectionChanged event
118 protected virtual void raiseCollectionChanged()
119 { if (eventBlock != null) eventBlock.raiseCollectionChanged(this); }
122 /// The clear event. Will be raised for every Clear operation on the collection.
124 public virtual event CollectionClearedHandler<T> CollectionCleared
126 add { checkWillListen(EventTypeEnum.Cleared); (eventBlock ?? (eventBlock = new EventBlock<T>())).CollectionCleared += value; }
129 checkWillListen(EventTypeEnum.Cleared);
130 if (eventBlock != null)
132 eventBlock.CollectionCleared -= value;
133 if (eventBlock.events == 0) eventBlock = null;
138 /// Fire the CollectionCleared event
140 protected virtual void raiseCollectionCleared(bool full, int count)
141 { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count); }
144 /// Fire the CollectionCleared event
146 protected virtual void raiseCollectionCleared(bool full, int count, int? offset)
147 { if (eventBlock != null) eventBlock.raiseCollectionCleared(this, full, count, offset); }
150 /// The item added event. Will be raised for every individual addition to the collection.
152 public virtual event ItemsAddedHandler<T> ItemsAdded
154 add { checkWillListen(EventTypeEnum.Added); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsAdded += value; }
157 checkWillListen(EventTypeEnum.Added);
158 if (eventBlock != null)
160 eventBlock.ItemsAdded -= value;
161 if (eventBlock.events == 0) eventBlock = null;
166 /// Fire the ItemsAdded event
168 /// <param name="item">The item that was added</param>
169 /// <param name="count"></param>
170 protected virtual void raiseItemsAdded(T item, int count)
171 { if (eventBlock != null) eventBlock.raiseItemsAdded(this, item, count); }
174 /// The item removed event. Will be raised for every individual removal from the collection.
176 public virtual event ItemsRemovedHandler<T> ItemsRemoved
178 add { checkWillListen(EventTypeEnum.Removed); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemsRemoved += value; }
181 checkWillListen(EventTypeEnum.Removed);
182 if (eventBlock != null)
184 eventBlock.ItemsRemoved -= value;
185 if (eventBlock.events == 0) eventBlock = null;
190 /// Fire the ItemsRemoved event
192 /// <param name="item">The item that was removed</param>
193 /// <param name="count"></param>
194 protected virtual void raiseItemsRemoved(T item, int count)
195 { if (eventBlock != null) eventBlock.raiseItemsRemoved(this, item, count); }
198 /// The item added event. Will be raised for every individual addition to the collection.
200 public virtual event ItemInsertedHandler<T> ItemInserted
202 add { checkWillListen(EventTypeEnum.Inserted); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemInserted += value; }
205 checkWillListen(EventTypeEnum.Inserted);
206 if (eventBlock != null)
208 eventBlock.ItemInserted -= value;
209 if (eventBlock.events == 0) eventBlock = null;
214 /// Fire the ItemInserted event
216 /// <param name="item">The item that was added</param>
217 /// <param name="index"></param>
218 protected virtual void raiseItemInserted(T item, int index)
219 { if (eventBlock != null) eventBlock.raiseItemInserted(this, item, index); }
222 /// The item removed event. Will be raised for every individual removal from the collection.
224 public virtual event ItemRemovedAtHandler<T> ItemRemovedAt
226 add { checkWillListen(EventTypeEnum.RemovedAt); (eventBlock ?? (eventBlock = new EventBlock<T>())).ItemRemovedAt += value; }
229 checkWillListen(EventTypeEnum.RemovedAt);
230 if (eventBlock != null)
232 eventBlock.ItemRemovedAt -= value;
233 if (eventBlock.events == 0) eventBlock = null;
238 /// Fire the ItemRemovedAt event
240 /// <param name="item">The item that was removed</param>
241 /// <param name="index"></param>
242 protected virtual void raiseItemRemovedAt(T item, int index)
243 { if (eventBlock != null) eventBlock.raiseItemRemovedAt(this, item, index); }
245 #region Event support for IList
249 /// <param name="index"></param>
250 /// <param name="value"></param>
251 /// <param name="item"></param>
252 protected virtual void raiseForSetThis(int index, T value, T item)
254 if (ActiveEvents != 0)
256 raiseItemsRemoved(item, 1);
257 raiseItemRemovedAt(item, index);
258 raiseItemsAdded(value, 1);
259 raiseItemInserted(value, index);
260 raiseCollectionChanged();
266 /// <param name="i"></param>
267 /// <param name="item"></param>
268 protected virtual void raiseForInsert(int i, T item)
270 if (ActiveEvents != 0)
272 raiseItemInserted(item, i);
273 raiseItemsAdded(item, 1);
274 raiseCollectionChanged();
281 /// <param name="item"></param>
282 protected void raiseForRemove(T item)
284 if (ActiveEvents != 0)
286 raiseItemsRemoved(item, 1);
287 raiseCollectionChanged();
294 /// <param name="item"></param>
295 /// <param name="count"></param>
296 protected void raiseForRemove(T item, int count)
298 if (ActiveEvents != 0)
300 raiseItemsRemoved(item, count);
301 raiseCollectionChanged();
308 /// <param name="index"></param>
309 /// <param name="item"></param>
310 protected void raiseForRemoveAt(int index, T item)
312 if (ActiveEvents != 0)
314 raiseItemRemovedAt(item, index);
315 raiseItemsRemoved(item, 1);
316 raiseCollectionChanged();
322 #region Event Support for ICollection
326 /// <param name="newitem"></param>
327 /// <param name="olditem"></param>
328 protected virtual void raiseForUpdate(T newitem, T olditem)
330 if (ActiveEvents != 0)
332 raiseItemsRemoved(olditem, 1);
333 raiseItemsAdded(newitem, 1);
334 raiseCollectionChanged();
340 /// <param name="newitem"></param>
341 /// <param name="olditem"></param>
342 /// <param name="count"></param>
343 protected virtual void raiseForUpdate(T newitem, T olditem, int count)
345 if (ActiveEvents != 0)
347 raiseItemsRemoved(olditem, count);
348 raiseItemsAdded(newitem, count);
349 raiseCollectionChanged();
355 /// <param name="item"></param>
356 protected virtual void raiseForAdd(T item)
358 if (ActiveEvents != 0)
360 raiseItemsAdded(item, 1);
361 raiseCollectionChanged();
367 /// <param name="wasRemoved"></param>
368 protected virtual void raiseForRemoveAll(ICollectionValue<T> wasRemoved)
370 if ((ActiveEvents & EventTypeEnum.Removed) != 0)
371 foreach (T item in wasRemoved)
372 raiseItemsRemoved(item, 1);
373 if (wasRemoved != null && wasRemoved.Count > 0)
374 raiseCollectionChanged();
380 protected class RaiseForRemoveAllHandler
382 CollectionValueBase<T> collection;
383 CircularQueue<T> wasRemoved;
384 bool wasChanged = false;
389 /// <param name="collection"></param>
390 public RaiseForRemoveAllHandler(CollectionValueBase<T> collection)
392 this.collection = collection;
393 mustFireRemoved = (collection.ActiveEvents & EventTypeEnum.Removed) != 0;
394 MustFire = (collection.ActiveEvents & (EventTypeEnum.Removed | EventTypeEnum.Changed)) != 0;
397 bool mustFireRemoved;
401 public readonly bool MustFire;
406 /// <param name="item"></param>
407 public void Remove(T item)
411 if (wasRemoved == null)
412 wasRemoved = new CircularQueue<T>();
413 wasRemoved.Enqueue(item);
424 if (wasRemoved != null)
425 foreach (T item in wasRemoved)
426 collection.raiseItemsRemoved(item, 1);
428 collection.raiseCollectionChanged();
436 /// Check if collection is empty.
438 /// <value>True if empty</value>
439 public abstract bool IsEmpty { get;}
442 /// The number of items in this collection.
445 public abstract int Count { get;}
448 /// The value is symbolic indicating the type of asymptotic complexity
449 /// in terms of the size of this collection (worst-case or amortized as
452 /// <value>A characterization of the speed of the
453 /// <code>Count</code> property in this collection.</value>
454 public abstract Speed CountSpeed { get; }
457 /// Copy the items of this collection to part of an array.
459 /// <exception cref="ArgumentOutOfRangeException"> if <code>index</code>
460 /// is not a valid index
461 /// into the array (i.e. negative or greater than the size of the array)
462 /// or the array does not have room for the items.</exception>
463 /// <param name="array">The array to copy to.</param>
464 /// <param name="index">The starting index.</param>
466 public virtual void CopyTo(T[] array, int index)
468 if (index < 0 || index + Count > array.Length)
469 throw new ArgumentOutOfRangeException();
471 foreach (T item in this) array[index++] = item;
475 /// Create an array with the items of this collection (in the same order as an
476 /// enumerator would output them).
478 /// <returns>The array</returns>
480 public virtual T[] ToArray()
482 T[] res = new T[Count];
485 foreach (T item in this) res[i++] = item;
491 /// Apply an single argument action, <see cref="T:C5.Act`1"/> to this enumerable
493 /// <param name="action">The action delegate</param>
495 public virtual void Apply(Act<T> action)
497 foreach (T item in this)
503 /// Check if there exists an item that satisfies a
504 /// specific predicate in this collection.
506 /// <param name="predicate">A delegate
507 /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>)
508 /// defining the predicate</param>
509 /// <returns>True if such an item exists</returns>
511 public virtual bool Exists(Fun<T, bool> predicate)
513 foreach (T item in this)
521 /// Check if there exists an item that satisfies a
522 /// specific predicate in this collection and return the first one in enumeration order.
524 /// <param name="predicate">A delegate
525 /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
526 /// <param name="item"></param>
527 /// <returns>True is such an item exists</returns>
528 public virtual bool Find(Fun<T, bool> predicate, out T item)
530 foreach (T jtem in this)
541 /// Check if all items in this collection satisfies a specific predicate.
543 /// <param name="predicate">A delegate
544 /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>)
545 /// defining the predicate</param>
546 /// <returns>True if all items satisfies the predicate</returns>
548 public virtual bool All(Fun<T, bool> predicate)
550 foreach (T item in this)
551 if (!predicate(item))
558 /// Create an enumerable, enumerating the items of this collection that satisfies
559 /// a certain condition.
561 /// <param name="predicate">A delegate
562 /// (<see cref="T:C5.Fun`2"/> with <code>R = bool</code>)
563 /// defining the predicate</param>
564 /// <returns>The filtered enumerable</returns>
565 public virtual SCG.IEnumerable<T> Filter(Fun<T, bool> predicate)
567 foreach (T item in this)
573 /// Choose some item of this collection.
575 /// <exception cref="NoSuchItemException">if collection is empty.</exception>
576 /// <returns></returns>
577 public abstract T Choose();
581 /// Create an enumerator for this collection.
583 /// <returns>The enumerator</returns>
584 public override abstract SCG.IEnumerator<T> GetEnumerator();
586 #region IShowable Members
591 /// <param name="stringbuilder"></param>
592 /// <param name="rest"></param>
593 /// <param name="formatProvider"></param>
594 /// <returns></returns>
595 public virtual bool Show(System.Text.StringBuilder stringbuilder, ref int rest, IFormatProvider formatProvider)
597 return Showing.ShowCollectionValue<T>(this, stringbuilder, ref rest, formatProvider);
601 #region IFormattable Members
606 /// <param name="format"></param>
607 /// <param name="formatProvider"></param>
608 /// <returns></returns>
609 public virtual string ToString(string format, IFormatProvider formatProvider)
611 return Showing.ShowString(this, format, formatProvider);
619 /// <returns></returns>
620 public override string ToString()
622 return ToString(null, null);
630 /// <typeparam name="T"></typeparam>
631 public abstract class DirectedCollectionValueBase<T> : CollectionValueBase<T>, IDirectedCollectionValue<T>
634 /// <code>Forwards</code> if same, else <code>Backwards</code>
636 /// <value>The enumeration direction relative to the original collection.</value>
637 public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
642 /// <returns></returns>
643 public abstract IDirectedCollectionValue<T> Backwards();
645 IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); }
648 /// Check if there exists an item that satisfies a
649 /// specific predicate in this collection and return the first one in enumeration order.
651 /// <param name="predicate">A delegate
652 /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
653 /// <param name="item"></param>
654 /// <returns>True is such an item exists</returns>
655 public virtual bool FindLast(Fun<T, bool> predicate, out T item)
657 foreach (T jtem in Backwards())
669 /// Base class (abstract) for ICollection implementations.
672 public abstract class CollectionBase<T> : CollectionValueBase<T>
677 /// The underlying field of the ReadOnly property
679 protected bool isReadOnlyBase = false;
682 /// The current stamp value
687 /// The number of items in the collection
692 /// The item equalityComparer of the collection
694 protected readonly SCG.IEqualityComparer<T> itemequalityComparer;
696 int iUnSequencedHashCode, iUnSequencedHashCodeStamp = -1;
703 /// <param name="itemequalityComparer"></param>
704 protected CollectionBase(SCG.IEqualityComparer<T> itemequalityComparer)
706 if (itemequalityComparer == null)
707 throw new NullReferenceException("Item EqualityComparer cannot be null.");
708 this.itemequalityComparer = itemequalityComparer;
714 /// Utility method for range checking.
716 /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative or
717 /// if the range does not fit within collection size.</exception>
718 /// <param name="start">start of range</param>
719 /// <param name="count">size of range</param>
721 protected void checkRange(int start, int count)
723 if (start < 0 || count < 0 || start + count > size)
724 throw new ArgumentOutOfRangeException();
729 /// Compute the unsequenced hash code of a collection
731 /// <param name="items">The collection to compute hash code for</param>
732 /// <param name="itemequalityComparer">The item equalityComparer</param>
733 /// <returns>The hash code</returns>
735 public static int ComputeHashCode(ICollectionValue<T> items, SCG.IEqualityComparer<T> itemequalityComparer)
739 #if IMPROVED_COLLECTION_HASHFUNCTION
740 //But still heuristic:
741 //Note: the three odd factors should really be random,
742 //but there will be a problem with serialization/deserialization!
743 //Two products is too few
744 foreach (T item in items)
746 uint h1 = (uint)itemequalityComparer.GetHashCode(item);
748 h += (int)((h1 * 1529784657 + 1) ^ (h1 * 2912831877) ^ (h1 * 1118771817 + 2));
753 The pairs (-1657792980, -1570288808) and (1862883298, -272461342) gives the same
754 unsequenced hashcode with this hashfunction. The pair was found with code like
756 HashDictionary<int, int[]> set = new HashDictionary<int, int[]>();
757 Random rnd = new C5Random(12345);
760 int[] a = new int[2];
761 a[0] = rnd.Next(); a[1] = rnd.Next();
762 int h = unsequencedhashcode(a);
764 if (set.FindOrAdd(h, ref b))
766 Console.WriteLine("Code {5}, Pair ({1},{2}) number {0} matched other pair ({3},{4})", set.Count, a[0], a[1], b[0], b[1], h);
771 foreach (T item in items)
772 h ^= itemequalityComparer.GetHashCode(item);
774 return (items.Count << 16) + h;
778 static Type isortedtype = typeof(ISorted<T>);
781 /// Examine if collection1 and collection2 are equal as unsequenced collections
782 /// using the specified item equalityComparer (assumed compatible with the two collections).
784 /// <param name="collection1">The first collection</param>
785 /// <param name="collection2">The second collection</param>
786 /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param>
787 /// <returns>True if equal</returns>
789 public static bool StaticEquals(ICollection<T> collection1, ICollection<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer)
791 if (object.ReferenceEquals(collection1, collection2))
795 if (collection1 == null || collection2 == null)
798 if (collection1.Count != collection2.Count)
801 //This way we might run through both enumerations twice, but
802 //probably not (if the hash codes are good)
803 //TODO: check equal equalityComparers, at least here!
804 if (collection1.GetUnsequencedHashCode() != collection2.GetUnsequencedHashCode())
807 //TODO: move this to the sorted implementation classes?
808 //Really depends on speed of InstanceOfType: we could save a cast
810 ISorted<T> stit, stat;
811 if ((stit = collection1 as ISorted<T>) != null && (stat = collection2 as ISorted<T>) != null && stit.Comparer == stat.Comparer)
813 using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator())
815 while (dit.MoveNext())
818 if (!itemequalityComparer.Equals(dit.Current, dat.Current))
826 if (!collection1.AllowsDuplicates && (collection2.AllowsDuplicates || collection2.ContainsSpeed >= collection1.ContainsSpeed))
828 foreach (T x in collection1) if (!collection2.Contains(x)) return false;
830 else if (!collection2.AllowsDuplicates)
832 foreach (T x in collection2) if (!collection1.Contains(x)) return false;
834 // Now tit.AllowsDuplicates && tat.AllowsDuplicates
835 else if (collection1.DuplicatesByCounting && collection2.DuplicatesByCounting)
837 foreach (T item in collection2) if (collection1.ContainsCount(item) != collection2.ContainsCount(item)) return false;
841 // To avoid an O(n^2) algorithm, we make an aux hashtable to hold the count of items
842 // bug20101103: HashDictionary<T, int> dict = new HashDictionary<T, int>();
843 HashDictionary<T, int> dict = new HashDictionary<T, int>(itemequalityComparer);
844 foreach (T item in collection2)
847 if (dict.FindOrAdd(item, ref count))
848 dict[item] = count + 1;
850 foreach (T item in collection1)
853 if (dict.Find(item, out count) && count > 0)
854 dict[item] = count - 1;
866 /// Get the unsequenced collection hash code of this collection: from the cached
867 /// value if present and up to date, else (re)compute.
869 /// <returns>The hash code</returns>
870 public virtual int GetUnsequencedHashCode()
872 if (iUnSequencedHashCodeStamp == stamp)
873 return iUnSequencedHashCode;
875 iUnSequencedHashCode = ComputeHashCode(this, itemequalityComparer);
876 iUnSequencedHashCodeStamp = stamp;
877 return iUnSequencedHashCode;
882 /// Check if the contents of otherCollection is equal to the contents of this
883 /// in the unsequenced sense. Uses the item equality comparer of this collection
885 /// <param name="otherCollection">The collection to compare to.</param>
886 /// <returns>True if equal</returns>
887 public virtual bool UnsequencedEquals(ICollection<T> otherCollection)
889 return otherCollection != null && StaticEquals((ICollection<T>)this, otherCollection, itemequalityComparer);
894 /// Check if the collection has been modified since a specified time, expressed as a stamp value.
896 /// <exception cref="CollectionModifiedException"> if this collection has been updated
897 /// since a target time</exception>
898 /// <param name="thestamp">The stamp identifying the target time</param>
899 protected virtual void modifycheck(int thestamp)
901 if (this.stamp != thestamp)
902 throw new CollectionModifiedException();
907 /// Check if it is valid to perform update operations, and if so increment stamp.
909 /// <exception cref="ReadOnlyCollectionException">If colection is read-only</exception>
910 protected virtual void updatecheck()
913 throw new ReadOnlyCollectionException();
920 #region ICollection<T> members
925 /// <value>True if this collection is read only</value>
927 public virtual bool IsReadOnly { [Tested]get { return isReadOnlyBase; } }
931 #region ICollectionValue<T> members
935 /// <value>The size of this collection</value>
937 public override int Count { [Tested]get { return size; } }
940 /// The value is symbolic indicating the type of asymptotic complexity
941 /// in terms of the size of this collection (worst-case or amortized as
944 /// <value>A characterization of the speed of the
945 /// <code>Count</code> property in this collection.</value>
946 public override Speed CountSpeed { get { return Speed.Constant; } }
951 #region IExtensible<T> members
957 public virtual SCG.IEqualityComparer<T> EqualityComparer { get { return itemequalityComparer; } }
962 /// <value>True if this collection is empty</value>
964 public override bool IsEmpty { [Tested]get { return size == 0; } }
968 #region IEnumerable<T> Members
970 /// Create an enumerator for this collection.
972 /// <returns>The enumerator</returns>
973 public override abstract SCG.IEnumerator<T> GetEnumerator();
980 /// <typeparam name="T"></typeparam>
982 public abstract class DirectedCollectionBase<T> : CollectionBase<T>, IDirectedCollectionValue<T>
987 /// <param name="itemequalityComparer"></param>
988 protected DirectedCollectionBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { }
990 /// <code>Forwards</code> if same, else <code>Backwards</code>
992 /// <value>The enumeration direction relative to the original collection.</value>
993 public virtual EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
998 /// <returns></returns>
999 public abstract IDirectedCollectionValue<T> Backwards();
1001 IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return this.Backwards(); }
1004 /// Check if there exists an item that satisfies a
1005 /// specific predicate in this collection and return the first one in enumeration order.
1007 /// <param name="predicate">A delegate
1008 /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
1009 /// <param name="item"></param>
1010 /// <returns>True is such an item exists</returns>
1011 public virtual bool FindLast(Fun<T, bool> predicate, out T item)
1013 foreach (T jtem in Backwards())
1014 if (predicate(jtem))
1025 /// Base class (abstract) for sequenced collection implementations.
1028 public abstract class SequencedBase<T> : DirectedCollectionBase<T>, IDirectedCollectionValue<T>
1032 int iSequencedHashCode, iSequencedHashCodeStamp = -1;
1039 /// <param name="itemequalityComparer"></param>
1040 protected SequencedBase(SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer) { }
1044 //TODO: make random for release
1045 const int HASHFACTOR = 31;
1048 /// Compute the unsequenced hash code of a collection
1050 /// <param name="items">The collection to compute hash code for</param>
1051 /// <param name="itemequalityComparer">The item equalityComparer</param>
1052 /// <returns>The hash code</returns>
1054 public static int ComputeHashCode(ISequenced<T> items, SCG.IEqualityComparer<T> itemequalityComparer)
1056 //NOTE: It must be possible to devise a much stronger combined hashcode,
1057 //but unfortunately, it has to be universal. OR we could use a (strong)
1058 //family and initialise its parameter randomly at load time of this class!
1059 //(We would not want to have yet a flag to check for invalidation?!)
1060 //NBNBNB: the current hashcode has the very bad property that items with hashcode 0
1062 int iIndexedHashCode = 0;
1064 foreach (T item in items)
1065 iIndexedHashCode = iIndexedHashCode * HASHFACTOR + itemequalityComparer.GetHashCode(item);
1067 return iIndexedHashCode;
1072 /// Examine if tit and tat are equal as sequenced collections
1073 /// using the specified item equalityComparer (assumed compatible with the two collections).
1075 /// <param name="collection1">The first collection</param>
1076 /// <param name="collection2">The second collection</param>
1077 /// <param name="itemequalityComparer">The item equalityComparer to use for comparison</param>
1078 /// <returns>True if equal</returns>
1080 public static bool StaticEquals(ISequenced<T> collection1, ISequenced<T> collection2, SCG.IEqualityComparer<T> itemequalityComparer)
1082 if (object.ReferenceEquals(collection1, collection2))
1085 if (collection1.Count != collection2.Count)
1088 //This way we might run through both enumerations twice, but
1089 //probably not (if the hash codes are good)
1090 if (collection1.GetSequencedHashCode() != collection2.GetSequencedHashCode())
1093 using (SCG.IEnumerator<T> dat = collection2.GetEnumerator(), dit = collection1.GetEnumerator())
1095 while (dit.MoveNext())
1098 if (!itemequalityComparer.Equals(dit.Current, dat.Current))
1108 /// Get the sequenced collection hash code of this collection: from the cached
1109 /// value if present and up to date, else (re)compute.
1111 /// <returns>The hash code</returns>
1112 public virtual int GetSequencedHashCode()
1114 if (iSequencedHashCodeStamp == stamp)
1115 return iSequencedHashCode;
1117 iSequencedHashCode = ComputeHashCode((ISequenced<T>)this, itemequalityComparer);
1118 iSequencedHashCodeStamp = stamp;
1119 return iSequencedHashCode;
1124 /// Check if the contents of that is equal to the contents of this
1125 /// in the sequenced sense. Using the item equalityComparer of this collection.
1127 /// <param name="otherCollection">The collection to compare to.</param>
1128 /// <returns>True if equal</returns>
1129 public virtual bool SequencedEquals(ISequenced<T> otherCollection)
1131 return StaticEquals((ISequenced<T>)this, otherCollection, itemequalityComparer);
1138 /// Create an enumerator for this collection.
1140 /// <returns>The enumerator</returns>
1141 public override abstract SCG.IEnumerator<T> GetEnumerator();
1144 /// <code>Forwards</code> if same, else <code>Backwards</code>
1146 /// <value>The enumeration direction relative to the original collection.</value>
1148 public override EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
1151 /// Check if there exists an item that satisfies a
1152 /// specific predicate in this collection and return the index of the first one.
1154 /// <param name="predicate">A delegate
1155 /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
1156 /// <returns>the index, if found, a negative value else</returns>
1157 public int FindIndex(Fun<T, bool> predicate)
1160 foreach (T item in this)
1162 if (predicate(item))
1170 /// Check if there exists an item that satisfies a
1171 /// specific predicate in this collection and return the index of the last one.
1173 /// <param name="predicate">A delegate
1174 /// (<see cref="T:C5.Fun`2"/> with <code>R == bool</code>) defining the predicate</param>
1175 /// <returns>the index, if found, a negative value else</returns>
1176 public int FindLastIndex(Fun<T, bool> predicate)
1178 int index = Count - 1;
1179 foreach (T item in Backwards())
1181 if (predicate(item))
1192 /// Base class for collection classes of dynamic array type implementations.
1195 public abstract class ArrayBase<T> : SequencedBase<T>
1199 /// The actual internal array container. Will be extended on demand.
1201 protected T[] array;
1204 /// The offset into the internal array container of the first item. The offset is 0 for a
1205 /// base dynamic array and may be positive for an updatable view into a base dynamic array.
1207 protected int offset;
1212 /// Double the size of the internal array.
1214 protected virtual void expand()
1216 expand(2 * array.Length, size);
1221 /// Expand the internal array container.
1223 /// <param name="newcapacity">The new size of the internal array -
1224 /// will be rounded upwards to a power of 2.</param>
1225 /// <param name="newsize">The (new) size of the (base) collection.</param>
1226 protected virtual void expand(int newcapacity, int newsize)
1228 Debug.Assert(newcapacity >= newsize);
1230 int newlength = array.Length;
1232 while (newlength < newcapacity) newlength *= 2;
1234 T[] newarray = new T[newlength];
1236 Array.Copy(array, newarray, newsize);
1242 /// Insert an item at a specific index, moving items to the right
1243 /// upwards and expanding the array if necessary.
1245 /// <param name="i">The index at which to insert.</param>
1246 /// <param name="item">The item to insert.</param>
1247 protected virtual void insert(int i, T item)
1249 if (size == array.Length)
1253 Array.Copy(array, i, array, i + 1, size - i);
1261 #region Constructors
1264 /// Create an empty ArrayBase object.
1266 /// <param name="capacity">The initial capacity of the internal array container.
1267 /// Will be rounded upwards to the nearest power of 2 greater than or equal to 8.</param>
1268 /// <param name="itemequalityComparer">The item equalityComparer to use, primarily for item equality</param>
1269 protected ArrayBase(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
1270 : base(itemequalityComparer)
1273 while (newlength < capacity) newlength *= 2;
1274 array = new T[newlength];
1279 #region IIndexed members
1283 /// <exception cref="ArgumentOutOfRangeException">If the arguments does not describe a
1284 /// valid range in the indexed collection, cf. <see cref="M:C5.CollectionBase`1.checkRange(System.Int32,System.Int32)"/>.</exception>
1285 /// <value>The directed collection of items in a specific index interval.</value>
1286 /// <param name="start">The low index of the interval (inclusive).</param>
1287 /// <param name="count">The size of the range.</param>
1289 public virtual IDirectedCollectionValue<T> this[int start, int count]
1294 checkRange(start, count);
1295 return new Range(this, start, count, true);
1301 #region IEditableCollection members
1303 /// Remove all items and reset size of internal array container.
1306 public virtual void Clear()
1315 /// Create an array containing (copies) of the items of this collection in enumeration order.
1317 /// <returns>The new array</returns>
1319 public override T[] ToArray()
1321 T[] res = new T[size];
1323 Array.Copy(array, offset, res, 0, size);
1329 /// Perform an internal consistency (invariant) test on the array base.
1331 /// <returns>True if test succeeds.</returns>
1333 public virtual bool Check()
1337 if (size > array.Length)
1339 Console.WriteLine("Bad size ({0}) > array.Length ({1})", size, array.Length);
1343 for (int i = 0; i < size; i++)
1345 if ((object)(array[i]) == null)
1347 Console.WriteLine("Bad element: null at index {0}", i);
1357 #region IDirectedCollection<T> Members
1360 /// Create a directed collection with the same contents as this one, but
1361 /// opposite enumeration sequence.
1363 /// <returns>The mirrored collection.</returns>
1365 public override IDirectedCollectionValue<T> Backwards() { return this[0, size].Backwards(); }
1370 /// Choose some item of this collection. The result is the last item in the internal array,
1371 /// making it efficient to remove.
1373 /// <exception cref="NoSuchItemException">if collection is empty.</exception>
1374 /// <returns></returns>
1375 public override T Choose() { if (size > 0) return array[size - 1]; throw new NoSuchItemException(); }
1377 #region IEnumerable<T> Members
1379 /// Create an enumerator for this array based collection.
1381 /// <returns>The enumerator</returns>
1383 public override SCG.IEnumerator<T> GetEnumerator()
1385 int thestamp = stamp, theend = size + offset, thestart = offset;
1387 for (int i = thestart; i < theend; i++)
1389 modifycheck(thestamp);
1390 yield return array[i];
1395 #region Range nested class
1397 /// A helper class for defining results of interval queries on array based collections.
1399 protected class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
1401 int start, count, delta, stamp;
1403 ArrayBase<T> thebase;
1406 internal Range(ArrayBase<T> thebase, int start, int count, bool forwards)
1408 this.thebase = thebase; stamp = thebase.stamp;
1409 delta = forwards ? 1 : -1;
1410 this.start = start + thebase.offset; this.count = count;
1416 /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1417 /// <value>True if this collection is empty.</value>
1418 public override bool IsEmpty { get { thebase.modifycheck(stamp); return count == 0; } }
1424 /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1425 /// <value>The number of items in the range</value>
1427 public override int Count { [Tested]get { thebase.modifycheck(stamp); return count; } }
1430 /// The value is symbolic indicating the type of asymptotic complexity
1431 /// in terms of the size of this collection (worst-case or amortized as
1434 /// <value>A characterization of the speed of the
1435 /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1436 /// <code>Count</code> property in this collection.</value>
1437 public override Speed CountSpeed { get { thebase.modifycheck(stamp); return Speed.Constant; } }
1440 /// Choose some item of this collection.
1442 /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1443 /// <exception cref="NoSuchItemException">if range is empty.</exception>
1444 /// <returns></returns>
1445 public override T Choose()
1447 thebase.modifycheck(stamp);
1449 throw new NoSuchItemException();
1450 return thebase.array[start];
1455 /// Create an enumerator for this range of an array based collection.
1457 /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1458 /// <returns>The enumerator</returns>
1460 public override SCG.IEnumerator<T> GetEnumerator()
1462 for (int i = 0; i < count; i++)
1464 thebase.modifycheck(stamp);
1465 yield return thebase.array[start + delta * i];
1471 /// Create a araay collection range with the same contents as this one, but
1472 /// opposite enumeration sequence.
1474 /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1475 /// <returns>The mirrored collection.</returns>
1477 public override IDirectedCollectionValue<T> Backwards()
1479 thebase.modifycheck(stamp);
1481 Range res = (Range)MemberwiseClone();
1484 res.start = start + (count - 1) * delta;
1489 IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards()
1496 /// <code>Forwards</code> if same, else <code>Backwards</code>
1498 /// <exception cref="CollectionModifiedException">if underlying collection has been modified.</exception>
1499 /// <value>The enumeration direction relative to the original collection.</value>
1501 public override EnumerationDirection Direction
1506 thebase.modifycheck(stamp);
1507 return delta > 0 ? EnumerationDirection.Forwards : EnumerationDirection.Backwards;