2 Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>
\r
3 Permission is hereby granted, free of charge, to any person obtaining a copy
\r
4 of this software and associated documentation files (the "Software"), to deal
\r
5 in the Software without restriction, including without limitation the rights
\r
6 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
7 copies of the Software, and to permit persons to whom the Software is
\r
8 furnished to do so, subject to the following conditions:
\r
10 The above copyright notice and this permission notice shall be included in
\r
11 all copies or substantial portions of the Software.
\r
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
16 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
23 using System.Diagnostics;
\r
24 using MSG = System.Collections.Generic;
\r
28 /// Direction of enumeration order relative to original collection.
\r
30 public enum EnumerationDirection {
\r
36 /// Opposite direction
\r
42 class IC: IComparer<int>
\r
45 public int Compare(int a, int b) { return a > b ? 1 : a < b ? -1 : 0; }
\r
51 /// A hasher for int32
\r
53 public class IntHasher: IHasher<int>
\r
56 /// Get the hash code of this integer, i.e. itself
\r
58 /// <param name="item">The integer</param>
\r
59 /// <returns>The same</returns>
\r
61 public int GetHashCode(int item) { return item; }
\r
65 /// Check if two integers are equal
\r
67 /// <param name="i1">first integer</param>
\r
68 /// <param name="i2">second integer</param>
\r
69 /// <returns>True if equal</returns>
\r
71 public bool Equals(int i1, int i2) { return i1 == i2; }
\r
77 #region Natural Comparers
\r
81 /// A natural generic IComparer for an IComparable<T> item type
\r
83 public class NaturalComparer<T>: IComparer<T>
\r
84 where T: IComparable<T>
\r
87 /// Compare two items
\r
89 /// <param name="a">First item</param>
\r
90 /// <param name="b">Second item</param>
\r
91 /// <returns>a <=> b</returns>
\r
93 public int Compare(T a, T b) { return a.CompareTo(b); }
\r
99 /// A natural generic IComparer for a System.IComparable item type
\r
101 public class NaturalComparerO<T>: IComparer<T>
\r
102 where T: System.IComparable
\r
105 /// Compare two items
\r
107 /// <param name="a">First item</param>
\r
108 /// <param name="b">Second item</param>
\r
109 /// <returns>a <=> b</returns>
\r
111 public int Compare(T a, T b) { return a.CompareTo(b); }
\r
120 /// The default item hasher for a reference type. A trivial wrapper for calling
\r
121 /// the GetHashCode and Equals methods inherited from object.
\r
123 /// <p>Should only be instantiated with a reference type as generic type parameter.
\r
124 /// This is asserted at instatiation time in Debug builds.</p>
\r
126 public sealed class DefaultReferenceTypeHasher<T>: IHasher<T>
\r
128 static DefaultReferenceTypeHasher()
\r
130 Debug.Assert(!typeof(T).IsValueType, "DefaultReferenceTypeHasher instantiated with value type: " + typeof(T));
\r
134 /// Get the hash code with respect to this item hasher
\r
136 /// <param name="item">The item</param>
\r
137 /// <returns>The hash code</returns>
\r
139 public int GetHashCode(T item) { return item.GetHashCode(); }
\r
143 /// Check if two items are equal with respect to this item hasher
\r
145 /// <param name="i1">first item</param>
\r
146 /// <param name="i2">second item</param>
\r
147 /// <returns>True if equal</returns>
\r
149 public bool Equals(T i1, T i2)
\r
151 //For reference types, the (object) cast should be jitted as a noop.
\r
152 return (object)i1 == null ? (object)i2 == null : i1.Equals(i2);
\r
157 /// The default item hasher for a value type. A trivial wrapper for calling
\r
158 /// the GetHashCode and Equals methods inherited from object.
\r
160 /// <p>Should only be instantiated with a value type as generic type parameter.
\r
161 /// This is asserted at instatiation time in Debug builds.</p>
\r
162 /// <p>We cannot add the constraint "where T : struct" to get a compile time check
\r
163 /// because we need to instantiate this class in C5.HasherBuilder.ByPrototype[T].Examine()
\r
164 /// with a T that is only known at runtime to be a value type!</p>
\r
167 //Note: we could (now) add a constraint "where T : struct" to get a compile time check,
\r
169 public sealed class DefaultValueTypeHasher<T>: IHasher<T>
\r
171 static DefaultValueTypeHasher()
\r
173 Debug.Assert(typeof(T).IsValueType, "DefaultValueTypeHasher instantiated with reference type: " + typeof(T));
\r
176 /// Get the hash code with respect to this item hasher
\r
178 /// <param name="item">The item</param>
\r
179 /// <returns>The hash code</returns>
\r
181 public int GetHashCode(T item) { return item.GetHashCode(); }
\r
185 /// Check if two items are equal with respect to this item hasher
\r
187 /// <param name="i1">first item</param>
\r
188 /// <param name="i2">second item</param>
\r
189 /// <returns>True if equal</returns>
\r
191 public bool Equals(T i1, T i2) { return i1.Equals(i2); }
\r
199 /// A base class for implementing an IEnumerable<T>
\r
201 public abstract class EnumerableBase<T>: MSG.IEnumerable<T>
\r
204 /// Create an enumerator for this collection.
\r
206 /// <returns>The enumerator</returns>
\r
207 public abstract MSG.IEnumerator<T> GetEnumerator();
\r
210 /// Count the number of items in an enumerable by enumeration
\r
212 /// <param name="items">The enumerable to count</param>
\r
213 /// <returns>The size of the enumerable</returns>
\r
214 protected static int countItems(MSG.IEnumerable<T> items)
\r
216 ICollectionValue<T> jtems = items as ICollectionValue<T>;
\r
219 return jtems.Count;
\r
223 using (MSG.IEnumerator<T> e = items.GetEnumerator())
\r
224 while (e.MoveNext()) count++;
\r
232 /// Base class for classes implementing ICollectionValue[T]
\r
234 public abstract class CollectionValueBase<T>: EnumerableBase<T>, ICollectionValue<T>
\r
236 //This forces our subclasses to make Count virtual!
\r
238 /// The number of items in this collection.
\r
240 /// <value></value>
\r
241 public abstract int Count { get;}
\r
244 /// The value is symbolic indicating the type of asymptotic complexity
\r
245 /// in terms of the size of this collection (worst-case or amortized as
\r
248 /// <value>A characterization of the speed of the
\r
249 /// <code>Count</code> property in this collection.</value>
\r
250 public abstract Speed CountSpeed { get; }
\r
254 /// Copy the items of this collection to part of an array.
\r
255 /// <exception cref="ArgumentOutOfRangeException"/> if i is negative.
\r
256 /// <exception cref="ArgumentException"/> if the array does not have room for the items.
\r
258 /// <param name="a">The array to copy to</param>
\r
259 /// <param name="i">The starting index.</param>
\r
261 public virtual void CopyTo(T[] a, int i)
\r
264 throw new ArgumentOutOfRangeException();
\r
266 if (i + Count > a.Length)
\r
267 throw new ArgumentException();
\r
269 foreach (T item in this) a[i++] = item;
\r
273 /// Create an array with the items of this collection (in the same order as an
\r
274 /// enumerator would output them).
\r
276 /// <returns>The array</returns>
\r
278 public virtual T[] ToArray()
\r
280 T[] res = new T[Count];
\r
283 foreach (T item in this) res[i++] = item;
\r
289 /// Apply an Applier<T> to this enumerable
\r
291 /// <param name="a">The applier delegate</param>
\r
293 public void Apply(Applier<T> a)
\r
295 foreach (T item in this)
\r
301 /// Check if there exists an item that satisfies a
\r
302 /// specific predicate in this collection.
\r
304 /// <param name="filter">A filter delegate
\r
305 /// (<see cref="T:C5.Filter!1"/>) defining the predicate</param>
\r
306 /// <returns>True is such an item exists</returns>
\r
308 public bool Exists(Filter<T> filter)
\r
310 foreach (T item in this)
\r
319 /// Check if all items in this collection satisfies a specific predicate.
\r
321 /// <param name="filter">A filter delegate
\r
322 /// (<see cref="T:C5.Filter!1"/>) defining the predicate</param>
\r
323 /// <returns>True if all items satisfies the predicate</returns>
\r
325 public bool All(Filter<T> filter)
\r
327 foreach (T item in this)
\r
335 /// Create an enumerator for this collection.
\r
337 /// <returns>The enumerator</returns>
\r
338 public override abstract MSG.IEnumerator<T> GetEnumerator();
\r
343 /// Base class (abstract) for ICollection implementations.
\r
345 public abstract class CollectionBase<T>: CollectionValueBase<T>
\r
349 object syncroot = new object();
\r
352 /// The underlying field of the ReadOnly property
\r
354 protected bool isReadOnly = false;
\r
357 /// The current stamp value
\r
359 protected int stamp;
\r
362 /// The number of items in the collection
\r
364 protected int size;
\r
367 /// The item hasher of the collection
\r
369 protected IHasher<T> itemhasher;
\r
371 int iUnSequencedHashCode, iUnSequencedHashCodeStamp = -1;
\r
377 //protected bool equals(T i1, T i2) { return itemhasher.Equals(i1, i2); }
\r
379 /// Utility method for range checking.
\r
380 /// <exception cref="ArgumentOutOfRangeException"/> if the start or count is negative
\r
381 /// <exception cref="ArgumentException"/> if the range does not fit within collection size.
\r
383 /// <param name="start">start of range</param>
\r
384 /// <param name="count">size of range</param>
\r
386 protected void checkRange(int start, int count)
\r
388 if (start < 0 || count < 0)
\r
389 throw new ArgumentOutOfRangeException();
\r
391 if (start + count > size)
\r
392 throw new ArgumentException();
\r
397 /// Compute the unsequenced hash code of a collection
\r
399 /// <param name="items">The collection to compute hash code for</param>
\r
400 /// <param name="itemhasher">The item hasher</param>
\r
401 /// <returns>The hash code</returns>
\r
403 public static int ComputeHashCode(ICollectionValue<T> items, IHasher<T> itemhasher)
\r
407 foreach (T item in items)
\r
408 h ^= itemhasher.GetHashCode(item);
\r
410 return (items.Count << 16) + h;
\r
415 /// Examine if tit and tat are equal as unsequenced collections
\r
416 /// using the specified item hasher (assumed compatible with the two collections).
\r
418 /// <param name="tit">The first collection</param>
\r
419 /// <param name="tat">The second collection</param>
\r
420 /// <param name="itemhasher">The item hasher to use for comparison</param>
\r
421 /// <returns>True if equal</returns>
\r
423 public static bool StaticEquals(ICollection<T> tit, ICollection<T> tat, IHasher<T> itemhasher)
\r
426 return tit == null;
\r
428 if (tit.Count != tat.Count)
\r
431 //This way we might run through both enumerations twice, but
\r
432 //probably not (if the hash codes are good)
\r
433 if (tit.GetHashCode() != tat.GetHashCode())
\r
436 if (!tit.AllowsDuplicates && (tat.AllowsDuplicates || tat.ContainsSpeed >= tit.ContainsSpeed))
\r
438 //TODO: use foreach construction
\r
439 using (MSG.IEnumerator<T> dit = tit.GetEnumerator())
\r
441 while (dit.MoveNext())
\r
442 if (!tat.Contains(dit.Current))
\r
446 else if (!tat.AllowsDuplicates)
\r
448 using (MSG.IEnumerator<T> dat = tat.GetEnumerator())
\r
450 while (dat.MoveNext())
\r
451 if (!tit.Contains(dat.Current))
\r
456 {//both are bags, we only have a slow one
\r
457 //unless the bags are based on a fast T->int dictinary (tree or hash)
\r
458 using (MSG.IEnumerator<T> dat = tat.GetEnumerator())
\r
460 while (dat.MoveNext())
\r
462 T item = dat.Current;
\r
464 if (tit.ContainsCount(item) != tat.ContainsCount(item))
\r
468 //That was O(n^3) - completely unacceptable.
\r
469 //If we use an auxiliary bool[] we can do the comparison in O(n^2)
\r
477 /// Get the unsequenced collection hash code of this collection: from the cached
\r
478 /// value if present and up to date, else (re)compute.
\r
480 /// <returns>The hash code</returns>
\r
481 protected int unsequencedhashcode()
\r
483 if (iUnSequencedHashCodeStamp == stamp)
\r
484 return iUnSequencedHashCode;
\r
486 iUnSequencedHashCode = ComputeHashCode(this, itemhasher);
\r
487 iUnSequencedHashCodeStamp = stamp;
\r
488 return iUnSequencedHashCode;
\r
493 /// Check if the contents of that is equal to the contents of this
\r
494 /// in the unsequenced sense. Using the item hasher of this collection.
\r
496 /// <param name="that">The collection to compare to.</param>
\r
497 /// <returns>True if equal</returns>
\r
498 protected bool unsequencedequals(ICollection<T> that)
\r
500 return StaticEquals((ICollection<T>)this, that, itemhasher);
\r
505 /// <exception cref="InvalidOperationException"/> if this collection has been updated
\r
506 /// since a target time
\r
508 /// <param name="thestamp">The stamp identifying the target time</param>
\r
509 protected virtual void modifycheck(int thestamp)
\r
511 if (this.stamp != thestamp)
\r
512 throw new InvalidOperationException("Collection was modified");
\r
517 /// Check if it is valid to perform update operations, and if so increment stamp
\r
519 protected virtual void updatecheck()
\r
522 throw new InvalidOperationException("Collection cannot be modified through this guard object");
\r
529 #region IEditableCollection<T> members
\r
534 /// <value>True if this collection is read only</value>
\r
536 public bool IsReadOnly { [Tested]get { return isReadOnly; } }
\r
540 #region ICollection<T> members
\r
544 /// <value>The size of this collection</value>
\r
546 public override int Count { [Tested]get { return size; } }
\r
549 /// The value is symbolic indicating the type of asymptotic complexity
\r
550 /// in terms of the size of this collection (worst-case or amortized as
\r
553 /// <value>A characterization of the speed of the
\r
554 /// <code>Count</code> property in this collection.</value>
\r
555 public override Speed CountSpeed { get { return Speed.Constant; } }
\r
560 #region ISink<T> members
\r
564 /// <value>A distinguished object to use for locking to synchronize multithreaded access</value>
\r
566 public object SyncRoot { get { return syncroot; } }
\r
572 /// <value>True is this collection is empty</value>
\r
574 public bool IsEmpty { [Tested]get { return size == 0; } }
\r
577 #region IEnumerable<T> Members
\r
579 /// Create an enumerator for this collection.
\r
581 /// <returns>The enumerator</returns>
\r
582 public override abstract MSG.IEnumerator<T> GetEnumerator();
\r
588 /// Base class (abstract) for sequenced collection implementations.
\r
590 public abstract class SequencedBase<T>: CollectionBase<T>
\r
594 int iSequencedHashCode, iSequencedHashCodeStamp = -1;
\r
601 /// Compute the unsequenced hash code of a collection
\r
603 /// <param name="items">The collection to compute hash code for</param>
\r
604 /// <param name="itemhasher">The item hasher</param>
\r
605 /// <returns>The hash code</returns>
\r
607 public static int ComputeHashCode(ISequenced<T> items, IHasher<T> itemhasher)
\r
609 //NOTE: It must be possible to devise a much stronger combined hashcode,
\r
610 //but unfortunately, it has to be universal. OR we could use a (strong)
\r
611 //family and initialise its parameter randomly at load time of this class!
\r
612 //(We would not want to have yet a flag to check for invalidation?!)
\r
613 int iIndexedHashCode = 0;
\r
615 foreach (T item in items)
\r
616 iIndexedHashCode = iIndexedHashCode * 31 + itemhasher.GetHashCode(item);
\r
618 return iIndexedHashCode;
\r
623 /// Examine if tit and tat are equal as sequenced collections
\r
624 /// using the specified item hasher (assumed compatible with the two collections).
\r
626 /// <param name="tit">The first collection</param>
\r
627 /// <param name="tat">The second collection</param>
\r
628 /// <param name="itemhasher">The item hasher to use for comparison</param>
\r
629 /// <returns>True if equal</returns>
\r
631 public static bool StaticEquals(ISequenced<T> tit, ISequenced<T> tat, IHasher<T> itemhasher)
\r
634 return tit == null;
\r
636 if (tit.Count != tat.Count)
\r
639 //This way we might run through both enumerations twice, but
\r
640 //probably not (if the hash codes are good)
\r
641 if (tit.GetHashCode() != tat.GetHashCode())
\r
644 using (MSG.IEnumerator<T> dat = tat.GetEnumerator(), dit = tit.GetEnumerator())
\r
646 while (dit.MoveNext())
\r
649 if (!itemhasher.Equals(dit.Current, dat.Current))
\r
659 /// Get the sequenced collection hash code of this collection: from the cached
\r
660 /// value if present and up to date, else (re)compute.
\r
662 /// <returns>The hash code</returns>
\r
663 protected int sequencedhashcode()
\r
665 if (iSequencedHashCodeStamp == stamp)
\r
666 return iSequencedHashCode;
\r
668 iSequencedHashCode = ComputeHashCode((ISequenced<T>)this, itemhasher);
\r
669 iSequencedHashCodeStamp = stamp;
\r
670 return iSequencedHashCode;
\r
675 /// Check if the contents of that is equal to the contents of this
\r
676 /// in the sequenced sense. Using the item hasher of this collection.
\r
678 /// <param name="that">The collection to compare to.</param>
\r
679 /// <returns>True if equal</returns>
\r
680 protected bool sequencedequals(ISequenced<T> that)
\r
682 return StaticEquals((ISequenced<T>)this, that, itemhasher);
\r
689 /// Create an enumerator for this collection.
\r
691 /// <returns>The enumerator</returns>
\r
692 public override abstract MSG.IEnumerator<T> GetEnumerator();
\r
696 /// <code>Forwards</code> if same, else <code>Backwards</code>
\r
698 /// <value>The enumeration direction relative to the original collection.</value>
\r
700 public EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
\r
705 /// Base class for collection classes of dynamic array type implementations.
\r
707 public class ArrayBase<T>: SequencedBase<T>
\r
711 /// The actual internal array container. Will be extended on demand.
\r
713 protected T[] array;
\r
716 /// The offset into the internal array container of the first item. The offset is 0 for a
\r
717 /// base dynamic array and may be positive for an updatable view into a base dynamic array.
\r
719 protected int offset;
\r
724 /// Double the size of the internal array.
\r
726 protected virtual void expand()
\r
728 expand(2 * array.Length, size);
\r
733 /// Expand the internal array container.
\r
735 /// <param name="newcapacity">The new size of the internal array -
\r
736 /// will be rounded upwards to a power of 2.</param>
\r
737 /// <param name="newsize">The (new) size of the (base) collection.</param>
\r
738 protected virtual void expand(int newcapacity, int newsize)
\r
740 Debug.Assert(newcapacity >= newsize);
\r
742 int newlength = array.Length;
\r
744 while (newlength < newcapacity) newlength *= 2;
\r
746 T[] newarray = new T[newlength];
\r
748 Array.Copy(array, newarray, newsize);
\r
754 /// Insert an item at a specific index, moving items to the right
\r
755 /// upwards and expanding the array if necessary.
\r
757 /// <param name="i">The index at which to insert.</param>
\r
758 /// <param name="item">The item to insert.</param>
\r
759 protected virtual void insert(int i, T item)
\r
761 if (size == array.Length)
\r
765 Array.Copy(array, i, array, i + 1, size - i);
\r
773 #region Constructors
\r
776 /// Create an empty ArrayBase object.
\r
778 /// <param name="capacity">The initial capacity of the internal array container.
\r
779 /// Will be rounded upwards to the nearest power of 2 greater than or equal to 8.</param>
\r
780 /// <param name="hasher">The item hasher to use, primarily for item equality</param>
\r
781 public ArrayBase(int capacity, IHasher<T> hasher)
\r
785 while (newlength < capacity) newlength *= 2;
\r
787 array = new T[newlength];
\r
788 itemhasher = hasher;
\r
793 #region IIndexed members
\r
796 /// <exception cref="IndexOutOfRangeException"/>.
\r
798 /// <value>The directed collection of items in a specific index interval.</value>
\r
799 /// <param name="start">The low index of the interval (inclusive).</param>
\r
800 /// <param name="count">The size of the range.</param>
\r
802 public IDirectedCollectionValue<T> this[int start, int count]
\r
807 checkRange(start, count);
\r
808 return new Range(this, start, count, true);
\r
814 #region IEditableCollection members
\r
816 /// Remove all items and reset size of internal array container.
\r
819 public virtual void Clear()
\r
828 /// Create an array containing (copies) of the items of this collection in enumeration order.
\r
830 /// <returns>The new array</returns>
\r
832 public override T[] ToArray()
\r
834 T[] res = new T[size];
\r
836 Array.Copy(array, res, size);
\r
842 /// Perform an internal consistency (invariant) test on the array base.
\r
844 /// <returns>True if test succeeds.</returns>
\r
846 public virtual bool Check()
\r
848 bool retval = true;
\r
850 if (size > array.Length)
\r
852 Console.WriteLine("Bad size ({0}) > array.Length ({1})", size, array.Length);
\r
856 for (int i = 0; i < size; i++)
\r
858 if ((object)(array[i]) == null)
\r
860 Console.WriteLine("Bad element: null at index {0}", i);
\r
870 #region IDirectedCollection<T> Members
\r
873 /// Create a directed collection with the same contents as this one, but
\r
874 /// opposite enumeration sequence.
\r
876 /// <returns>The mirrored collection.</returns>
\r
878 public IDirectedCollectionValue<T> Backwards() { return this[0, size].Backwards(); }
\r
882 #region IEnumerable<T> Members
\r
884 /// Create an enumerator for this array based collection.
\r
886 /// <returns>The enumerator</returns>
\r
888 public override MSG.IEnumerator<T> GetEnumerator()
\r
890 int thestamp = stamp, theend = size + offset, thestart = offset;
\r
892 for (int i = thestart; i < theend; i++)
\r
894 modifycheck(thestamp);
\r
895 yield return array[i];
\r
900 #region Range nested class
\r
902 /// A helper class for defining results of interval queries on array based collections.
\r
904 protected class Range: CollectionValueBase<T>, IDirectedCollectionValue<T>
\r
906 int start, count, delta, stamp;
\r
908 ArrayBase<T> thebase;
\r
911 internal Range(ArrayBase<T> thebase, int start, int count, bool forwards)
\r
913 this.thebase = thebase; stamp = thebase.stamp;
\r
914 delta = forwards ? 1 : -1;
\r
915 this.start = start + thebase.offset; this.count = count;
\r
922 /// <value>The number of items in the range</value>
\r
924 public override int Count { [Tested]get { thebase.modifycheck(stamp); return count; } }
\r
927 /// The value is symbolic indicating the type of asymptotic complexity
\r
928 /// in terms of the size of this collection (worst-case or amortized as
\r
931 /// <value>A characterization of the speed of the
\r
932 /// <code>Count</code> property in this collection.</value>
\r
933 public override Speed CountSpeed { get { thebase.modifycheck(stamp); return Speed.Constant; } }
\r
936 /// Create an enumerator for this range of an array based collection.
\r
938 /// <returns>The enumerator</returns>
\r
940 public override MSG.IEnumerator<T> GetEnumerator()
\r
942 for (int i = 0; i < count; i++)
\r
944 thebase.modifycheck(stamp);
\r
945 yield return thebase.array[start + delta * i];
\r
951 /// Create a araay collection range with the same contents as this one, but
\r
952 /// opposite enumeration sequence.
\r
954 /// <returns>The mirrored collection.</returns>
\r
956 public IDirectedCollectionValue<T> Backwards()
\r
958 thebase.modifycheck(stamp);
\r
960 Range res = (Range)MemberwiseClone();
\r
962 res.delta = -delta;
\r
963 res.start = start + (count - 1) * delta;
\r
968 IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards()
\r
970 return Backwards();
\r
975 /// <code>Forwards</code> if same, else <code>Backwards</code>
\r
977 /// <value>The enumeration direction relative to the original collection.</value>
\r
979 public EnumerationDirection Direction
\r
984 thebase.modifycheck(stamp);
\r
985 return delta > 0 ? EnumerationDirection.Forwards : EnumerationDirection.Backwards;
\r
996 /// A utility class with functions for sorting arrays with respect to an IComparer<T>
\r
998 public class Sorting
\r
1001 /// Sort part of array in place using IntroSort
\r
1003 /// <param name="a">Array to sort</param>
\r
1004 /// <param name="f">Index of first position to sort</param>
\r
1005 /// <param name="b">Index of first position beyond the part to sort</param>
\r
1006 /// <param name="c">IComparer<T> to sort by</param>
\r
1008 public static void IntroSort<T>(T[] a, int f, int b, IComparer<T> c)
\r
1010 new Sorter<T>(a, c).IntroSort(f, b);
\r
1015 /// Sort part of array in place using Insertion Sort
\r
1017 /// <param name="a">Array to sort</param>
\r
1018 /// <param name="f">Index of first position to sort</param>
\r
1019 /// <param name="b">Index of first position beyond the part to sort</param>
\r
1020 /// <param name="c">IComparer<T> to sort by</param>
\r
1022 public static void InsertionSort<T>(T[] a, int f, int b, IComparer<T> c)
\r
1024 new Sorter<T>(a, c).InsertionSort(f, b);
\r
1029 /// Sort part of array in place using Heap Sort
\r
1031 /// <param name="a">Array to sort</param>
\r
1032 /// <param name="f">Index of first position to sort</param>
\r
1033 /// <param name="b">Index of first position beyond the part to sort</param>
\r
1034 /// <param name="c">IComparer<T> to sort by</param>
\r
1036 public static void HeapSort<T>(T[] a, int f, int b, IComparer<T> c)
\r
1038 new Sorter<T>(a, c).HeapSort(f, b);
\r
1049 internal Sorter(T[] a, IComparer<T> c) { this.a = a; this.c = c; }
\r
1052 internal void IntroSort(int f, int b)
\r
1056 int depth_limit = (int)Math.Floor(2.5 * Math.Log(b - f, 2));
\r
1058 introSort(f, b, depth_limit);
\r
1061 InsertionSort(f, b);
\r
1065 private void introSort(int f, int b, int depth_limit)
\r
1067 const int size_threshold = 14;//24;
\r
1069 if (depth_limit-- == 0)
\r
1071 else if (b - f <= size_threshold)
\r
1072 InsertionSort(f, b);
\r
1075 int p = partition(f, b);
\r
1077 introSort(f, p, depth_limit);
\r
1078 introSort(p, b, depth_limit);
\r
1083 private int compare(T i1, T i2) { return c.Compare(i1, i2); }
\r
1086 private int partition(int f, int b)
\r
1088 int bot = f, mid = (b + f) / 2, top = b - 1;
\r
1089 T abot = a[bot], amid = a[mid], atop = a[top];
\r
1091 if (compare(abot, amid) < 0)
\r
1093 if (compare(atop, abot) < 0)//atop<abot<amid
\r
1094 { a[top] = amid; amid = a[mid] = abot; a[bot] = atop; }
\r
1095 else if (compare(atop, amid) < 0) //abot<=atop<amid
\r
1096 { a[top] = amid; amid = a[mid] = atop; }
\r
1097 //else abot<amid<=atop
\r
1101 if (compare(amid, atop) > 0) //atop<amid<=abot
\r
1102 { a[bot] = atop; a[top] = abot; }
\r
1103 else if (compare(abot, atop) > 0) //amid<=atop<abot
\r
1104 { a[bot] = amid; amid = a[mid] = atop; a[top] = abot; }
\r
1105 else //amid<=abot<=atop
\r
1106 { a[bot] = amid; amid = a[mid] = abot; }
\r
1109 int i = bot, j = top;
\r
1113 while (compare(a[++i], amid) < 0);
\r
1115 while (compare(amid, a[--j]) < 0);
\r
1119 T tmp = a[i]; a[i] = a[j]; a[j] = tmp;
\r
1127 internal void InsertionSort(int f, int b)
\r
1129 for (int j = f + 1; j < b; j++)
\r
1131 T key = a[j], other;
\r
1134 if (c.Compare(other = a[i], key) > 0)
\r
1137 while (i > f && c.Compare(other = a[i - 1], key) > 0) { a[i--] = other; }
\r
1145 internal void HeapSort(int f, int b)
\r
1147 for (int i = (b + f) / 2; i >= f; i--) heapify(f, b, i);
\r
1149 for (int i = b - 1; i > f; i--)
\r
1151 T tmp = a[f]; a[f] = a[i]; a[i] = tmp;
\r
1157 private void heapify(int f, int b, int i)
\r
1159 T pv = a[i], lv, rv, max = pv;
\r
1160 int j = i, maxpt = j;
\r
1164 int l = 2 * j - f + 1, r = l + 1;
\r
1166 if (l < b && compare(lv = a[l], max) > 0) { maxpt = l; max = lv; }
\r
1168 if (r < b && compare(rv = a[r], max) > 0) { maxpt = r; max = rv; }
\r
1188 /// A modern random number generator based on (whatever)
\r
1190 public class C5Random : Random
\r
1192 private uint[] Q = new uint[16];
\r
1194 private uint c = 362436, i = 15;
\r
1197 private uint Cmwc()
\r
1199 ulong t, a = 487198574UL;
\r
1200 uint x, r = 0xfffffffe;
\r
1204 c = (uint)(t >> 32);
\r
1205 x = (uint)(t + c);
\r
1212 return Q[i] = r - x;
\r
1217 /// Get a new random System.Double value
\r
1219 /// <returns>The random double</returns>
\r
1220 public override double NextDouble()
\r
1222 return Cmwc() / 4294967296.0;
\r
1227 /// Get a new random System.Double value
\r
1229 /// <returns>The random double</returns>
\r
1230 protected override double Sample()
\r
1232 return NextDouble();
\r
1237 /// Get a new random System.Int32 value
\r
1239 /// <returns>The random int</returns>
\r
1240 public override int Next()
\r
1242 return (int)Cmwc();
\r
1247 /// Get a random non-negative integer less than a given upper bound
\r
1249 /// <param name="max">The upper bound (exclusive)</param>
\r
1250 /// <returns></returns>
\r
1251 public override int Next(int max)
\r
1254 throw new ApplicationException("max must be non-negative");
\r
1256 return (int)(Cmwc() / 4294967296.0 * max);
\r
1261 /// Get a random integer between two given bounds
\r
1263 /// <param name="min">The lower bound (inclusive)</param>
\r
1264 /// <param name="max">The upper bound (exclusive)</param>
\r
1265 /// <returns></returns>
\r
1266 public override int Next(int min, int max)
\r
1269 throw new ApplicationException("min must be less than or equal to max");
\r
1271 return min + (int)(Cmwc() / 4294967296.0 * max);
\r
1275 /// Fill a array of byte with random bytes
\r
1277 /// <param name="buffer">The array to fill</param>
\r
1278 public override void NextBytes(byte[] buffer)
\r
1280 for (int i = 0, length = buffer.Length; i < length; i++)
\r
1281 buffer[i] = (byte)Cmwc();
\r
1286 /// Create a random number generator seed by system time.
\r
1288 public C5Random() : this(DateTime.Now.Ticks)
\r
1294 /// Create a random number generator with a given seed
\r
1296 /// <param name="seed">The seed</param>
\r
1297 public C5Random(long seed)
\r
1300 throw new ApplicationException("Seed must be non-zero");
\r
1302 uint j = (uint)(seed & 0xFFFFFFFF);
\r
1304 for (int i = 0; i < 16; i++)
\r
1312 Q[15] = (uint)(seed ^ (seed >> 32));
\r
1318 #region Custom code attributes
\r
1321 /// A custom attribute to mark methods and properties as being tested
\r
1322 /// sufficiently in the regression test suite.
\r
1324 [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
\r
1325 public class TestedAttribute: Attribute
\r
1329 /// Optional reference to test case
\r
1332 public string via;
\r
1336 /// Pretty print attribute value
\r
1338 /// <returns>"Tested via " + via</returns>
\r
1340 public override string ToString() { return "Tested via " + via; }
\r