3 Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>
\r
4 Permission is hereby granted, free of charge, to any person obtaining a copy
\r
5 of this software and associated documentation files (the "Software"), to deal
\r
6 in the Software without restriction, including without limitation the rights
\r
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
8 copies of the Software, and to permit persons to whom the Software is
\r
9 furnished to do so, subject to the following conditions:
\r
11 The above copyright notice and this permission notice shall be included in
\r
12 all copies or substantial portions of the Software.
\r
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
24 using System.Diagnostics;
\r
25 using MSG = System.Collections.Generic;
\r
29 /// Direction of enumeration order relative to original collection.
\r
31 public enum EnumerationDirection {
\r
37 /// Opposite direction
\r
43 class IC: IComparer<int>
\r
46 public int Compare(int a, int b) { return a > b ? 1 : a < b ? -1 : 0; }
\r
52 /// A hasher for int32
\r
54 public class IntHasher: IHasher<int>
\r
57 /// Get the hash code of this integer, i.e. itself
\r
59 /// <param name="item">The integer</param>
\r
60 /// <returns>The same</returns>
\r
62 public int GetHashCode(int item) { return item; }
\r
66 /// Check if two integers are equal
\r
68 /// <param name="i1">first integer</param>
\r
69 /// <param name="i2">second integer</param>
\r
70 /// <returns>True if equal</returns>
\r
72 public bool Equals(int i1, int i2) { return i1 == i2; }
\r
78 #region Natural Comparers
\r
82 /// A natural generic IComparer for an IComparable<T> item type
\r
84 public class NaturalComparer<T>: IComparer<T>
\r
85 where T: IComparable<T>
\r
88 /// Compare two items
\r
90 /// <param name="a">First item</param>
\r
91 /// <param name="b">Second item</param>
\r
92 /// <returns>a <=> b</returns>
\r
94 public int Compare(T a, T b) { return a.CompareTo(b); }
\r
100 /// A natural generic IComparer for a System.IComparable item type
\r
102 public class NaturalComparerO<T>: IComparer<T>
\r
103 where T: System.IComparable
\r
106 /// Compare two items
\r
108 /// <param name="a">First item</param>
\r
109 /// <param name="b">Second item</param>
\r
110 /// <returns>a <=> b</returns>
\r
112 public int Compare(T a, T b) { return a.CompareTo(b); }
\r
121 /// The default item hasher for a reference type. A trivial wrapper for calling
\r
122 /// the GetHashCode and Equals methods inherited from object.
\r
124 /// <p>Should only be instantiated with a reference type as generic type parameter.
\r
125 /// This is asserted at instatiation time in Debug builds.</p>
\r
127 public sealed class DefaultReferenceTypeHasher<T>: IHasher<T>
\r
129 static DefaultReferenceTypeHasher()
\r
131 Debug.Assert(!typeof(T).IsValueType, "DefaultReferenceTypeHasher instantiated with value type: " + typeof(T));
\r
135 /// Get the hash code with respect to this item hasher
\r
137 /// <param name="item">The item</param>
\r
138 /// <returns>The hash code</returns>
\r
140 public int GetHashCode(T item) { return item.GetHashCode(); }
\r
144 /// Check if two items are equal with respect to this item hasher
\r
146 /// <param name="i1">first item</param>
\r
147 /// <param name="i2">second item</param>
\r
148 /// <returns>True if equal</returns>
\r
150 public bool Equals(T i1, T i2)
\r
152 //For reference types, the (object) cast should be jitted as a noop.
\r
153 return (object)i1 == null ? (object)i2 == null : i1.Equals(i2);
\r
158 /// The default item hasher for a value type. A trivial wrapper for calling
\r
159 /// the GetHashCode and Equals methods inherited from object.
\r
161 /// <p>Should only be instantiated with a value type as generic type parameter.
\r
162 /// This is asserted at instatiation time in Debug builds.</p>
\r
163 /// <p>We cannot add the constraint "where T : struct" to get a compile time check
\r
164 /// because we need to instantiate this class in C5.HasherBuilder.ByPrototype[T].Examine()
\r
165 /// with a T that is only known at runtime to be a value type!</p>
\r
168 //Note: we could (now) add a constraint "where T : struct" to get a compile time check,
\r
170 public sealed class DefaultValueTypeHasher<T>: IHasher<T>
\r
172 static DefaultValueTypeHasher()
\r
174 Debug.Assert(typeof(T).IsValueType, "DefaultValueTypeHasher instantiated with reference type: " + typeof(T));
\r
177 /// Get the hash code with respect to this item hasher
\r
179 /// <param name="item">The item</param>
\r
180 /// <returns>The hash code</returns>
\r
182 public int GetHashCode(T item) { return item.GetHashCode(); }
\r
186 /// Check if two items are equal with respect to this item hasher
\r
188 /// <param name="i1">first item</param>
\r
189 /// <param name="i2">second item</param>
\r
190 /// <returns>True if equal</returns>
\r
192 public bool Equals(T i1, T i2) { return i1.Equals(i2); }
\r
200 /// A base class for implementing an IEnumerable<T>
\r
202 public abstract class EnumerableBase<T>: MSG.IEnumerable<T>
\r
205 /// Create an enumerator for this collection.
\r
207 /// <returns>The enumerator</returns>
\r
208 public abstract MSG.IEnumerator<T> GetEnumerator();
\r
210 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
\r
212 return GetEnumerator ();
\r
216 /// Count the number of items in an enumerable by enumeration
\r
218 /// <param name="items">The enumerable to count</param>
\r
219 /// <returns>The size of the enumerable</returns>
\r
220 protected static int countItems(MSG.IEnumerable<T> items)
\r
222 ICollectionValue<T> jtems = items as ICollectionValue<T>;
\r
225 return jtems.Count;
\r
229 using (MSG.IEnumerator<T> e = items.GetEnumerator())
\r
230 while (e.MoveNext()) count++;
\r
238 /// Base class for classes implementing ICollectionValue[T]
\r
240 public abstract class CollectionValueBase<T>: EnumerableBase<T>, ICollectionValue<T>
\r
242 //This forces our subclasses to make Count virtual!
\r
244 /// The number of items in this collection.
\r
246 /// <value></value>
\r
247 public abstract int Count { get;}
\r
250 /// The value is symbolic indicating the type of asymptotic complexity
\r
251 /// in terms of the size of this collection (worst-case or amortized as
\r
254 /// <value>A characterization of the speed of the
\r
255 /// <code>Count</code> property in this collection.</value>
\r
256 public abstract Speed CountSpeed { get; }
\r
260 /// Copy the items of this collection to part of an array.
\r
261 /// <exception cref="ArgumentOutOfRangeException"/> if i is negative.
\r
262 /// <exception cref="ArgumentException"/> if the array does not have room for the items.
\r
264 /// <param name="a">The array to copy to</param>
\r
265 /// <param name="i">The starting index.</param>
\r
267 public virtual void CopyTo(T[] a, int i)
\r
270 throw new ArgumentOutOfRangeException();
\r
272 if (i + Count > a.Length)
\r
273 throw new ArgumentException();
\r
275 foreach (T item in this) a[i++] = item;
\r
279 /// Create an array with the items of this collection (in the same order as an
\r
280 /// enumerator would output them).
\r
282 /// <returns>The array</returns>
\r
284 public virtual T[] ToArray()
\r
286 T[] res = new T[Count];
\r
289 foreach (T item in this) res[i++] = item;
\r
295 /// Apply an Applier<T> to this enumerable
\r
297 /// <param name="a">The applier delegate</param>
\r
299 public void Apply(Applier<T> a)
\r
301 foreach (T item in this)
\r
307 /// Check if there exists an item that satisfies a
\r
308 /// specific predicate in this collection.
\r
310 /// <param name="filter">A filter delegate
\r
311 /// (<see cref="T:C5.Filter!1"/>) defining the predicate</param>
\r
312 /// <returns>True is such an item exists</returns>
\r
314 public bool Exists(Filter<T> filter)
\r
316 foreach (T item in this)
\r
325 /// Check if all items in this collection satisfies a specific predicate.
\r
327 /// <param name="filter">A filter delegate
\r
328 /// (<see cref="T:C5.Filter!1"/>) defining the predicate</param>
\r
329 /// <returns>True if all items satisfies the predicate</returns>
\r
331 public bool All(Filter<T> filter)
\r
333 foreach (T item in this)
\r
341 /// Create an enumerator for this collection.
\r
343 /// <returns>The enumerator</returns>
\r
344 public override abstract MSG.IEnumerator<T> GetEnumerator();
\r
349 /// Base class (abstract) for ICollection implementations.
\r
351 public abstract class CollectionBase<T>: CollectionValueBase<T>
\r
355 object syncroot = new object();
\r
358 /// The underlying field of the ReadOnly property
\r
360 protected bool isReadOnly = false;
\r
363 /// The current stamp value
\r
365 protected int stamp;
\r
368 /// The number of items in the collection
\r
370 protected int size;
\r
373 /// The item hasher of the collection
\r
375 protected IHasher<T> itemhasher;
\r
377 int iUnSequencedHashCode, iUnSequencedHashCodeStamp = -1;
\r
383 //protected bool equals(T i1, T i2) { return itemhasher.Equals(i1, i2); }
\r
385 /// Utility method for range checking.
\r
386 /// <exception cref="ArgumentOutOfRangeException"/> if the start or count is negative
\r
387 /// <exception cref="ArgumentException"/> if the range does not fit within collection size.
\r
389 /// <param name="start">start of range</param>
\r
390 /// <param name="count">size of range</param>
\r
392 protected void checkRange(int start, int count)
\r
394 if (start < 0 || count < 0)
\r
395 throw new ArgumentOutOfRangeException();
\r
397 if (start + count > size)
\r
398 throw new ArgumentException();
\r
403 /// Compute the unsequenced hash code of a collection
\r
405 /// <param name="items">The collection to compute hash code for</param>
\r
406 /// <param name="itemhasher">The item hasher</param>
\r
407 /// <returns>The hash code</returns>
\r
409 public static int ComputeHashCode(ICollectionValue<T> items, IHasher<T> itemhasher)
\r
413 foreach (T item in items)
\r
414 h ^= itemhasher.GetHashCode(item);
\r
416 return (items.Count << 16) + h;
\r
421 /// Examine if tit and tat are equal as unsequenced collections
\r
422 /// using the specified item hasher (assumed compatible with the two collections).
\r
424 /// <param name="tit">The first collection</param>
\r
425 /// <param name="tat">The second collection</param>
\r
426 /// <param name="itemhasher">The item hasher to use for comparison</param>
\r
427 /// <returns>True if equal</returns>
\r
429 public static bool StaticEquals(ICollection<T> tit, ICollection<T> tat, IHasher<T> itemhasher)
\r
432 return tit == null;
\r
434 if (tit.Count != tat.Count)
\r
437 //This way we might run through both enumerations twice, but
\r
438 //probably not (if the hash codes are good)
\r
439 if (tit.GetHashCode() != tat.GetHashCode())
\r
442 if (!tit.AllowsDuplicates && (tat.AllowsDuplicates || tat.ContainsSpeed >= tit.ContainsSpeed))
\r
444 //TODO: use foreach construction
\r
445 using (MSG.IEnumerator<T> dit = tit.GetEnumerator())
\r
447 while (dit.MoveNext())
\r
448 if (!tat.Contains(dit.Current))
\r
452 else if (!tat.AllowsDuplicates)
\r
454 using (MSG.IEnumerator<T> dat = tat.GetEnumerator())
\r
456 while (dat.MoveNext())
\r
457 if (!tit.Contains(dat.Current))
\r
462 {//both are bags, we only have a slow one
\r
463 //unless the bags are based on a fast T->int dictinary (tree or hash)
\r
464 using (MSG.IEnumerator<T> dat = tat.GetEnumerator())
\r
466 while (dat.MoveNext())
\r
468 T item = dat.Current;
\r
470 if (tit.ContainsCount(item) != tat.ContainsCount(item))
\r
474 //That was O(n^3) - completely unacceptable.
\r
475 //If we use an auxiliary bool[] we can do the comparison in O(n^2)
\r
483 /// Get the unsequenced collection hash code of this collection: from the cached
\r
484 /// value if present and up to date, else (re)compute.
\r
486 /// <returns>The hash code</returns>
\r
487 protected int unsequencedhashcode()
\r
489 if (iUnSequencedHashCodeStamp == stamp)
\r
490 return iUnSequencedHashCode;
\r
492 iUnSequencedHashCode = ComputeHashCode(this, itemhasher);
\r
493 iUnSequencedHashCodeStamp = stamp;
\r
494 return iUnSequencedHashCode;
\r
499 /// Check if the contents of that is equal to the contents of this
\r
500 /// in the unsequenced sense. Using the item hasher of this collection.
\r
502 /// <param name="that">The collection to compare to.</param>
\r
503 /// <returns>True if equal</returns>
\r
504 protected bool unsequencedequals(ICollection<T> that)
\r
506 return StaticEquals((ICollection<T>)this, that, itemhasher);
\r
511 /// <exception cref="InvalidOperationException"/> if this collection has been updated
\r
512 /// since a target time
\r
514 /// <param name="thestamp">The stamp identifying the target time</param>
\r
515 protected virtual void modifycheck(int thestamp)
\r
517 if (this.stamp != thestamp)
\r
518 throw new InvalidOperationException("Collection was modified");
\r
523 /// Check if it is valid to perform update operations, and if so increment stamp
\r
525 protected virtual void updatecheck()
\r
528 throw new InvalidOperationException("Collection cannot be modified through this guard object");
\r
535 #region IEditableCollection<T> members
\r
540 /// <value>True if this collection is read only</value>
\r
542 public bool IsReadOnly { [Tested]get { return isReadOnly; } }
\r
546 #region ICollection<T> members
\r
550 /// <value>The size of this collection</value>
\r
552 public override int Count { [Tested]get { return size; } }
\r
555 /// The value is symbolic indicating the type of asymptotic complexity
\r
556 /// in terms of the size of this collection (worst-case or amortized as
\r
559 /// <value>A characterization of the speed of the
\r
560 /// <code>Count</code> property in this collection.</value>
\r
561 public override Speed CountSpeed { get { return Speed.Constant; } }
\r
566 #region ISink<T> members
\r
570 /// <value>A distinguished object to use for locking to synchronize multithreaded access</value>
\r
572 public object SyncRoot { get { return syncroot; } }
\r
578 /// <value>True is this collection is empty</value>
\r
580 public bool IsEmpty { [Tested]get { return size == 0; } }
\r
583 #region IEnumerable<T> Members
\r
585 /// Create an enumerator for this collection.
\r
587 /// <returns>The enumerator</returns>
\r
588 public override abstract MSG.IEnumerator<T> GetEnumerator();
\r
594 /// Base class (abstract) for sequenced collection implementations.
\r
596 public abstract class SequencedBase<T>: CollectionBase<T>
\r
600 int iSequencedHashCode, iSequencedHashCodeStamp = -1;
\r
607 /// Compute the unsequenced hash code of a collection
\r
609 /// <param name="items">The collection to compute hash code for</param>
\r
610 /// <param name="itemhasher">The item hasher</param>
\r
611 /// <returns>The hash code</returns>
\r
613 public static int ComputeHashCode(ISequenced<T> items, IHasher<T> itemhasher)
\r
615 //NOTE: It must be possible to devise a much stronger combined hashcode,
\r
616 //but unfortunately, it has to be universal. OR we could use a (strong)
\r
617 //family and initialise its parameter randomly at load time of this class!
\r
618 //(We would not want to have yet a flag to check for invalidation?!)
\r
619 int iIndexedHashCode = 0;
\r
621 foreach (T item in items)
\r
622 iIndexedHashCode = iIndexedHashCode * 31 + itemhasher.GetHashCode(item);
\r
624 return iIndexedHashCode;
\r
629 /// Examine if tit and tat are equal as sequenced collections
\r
630 /// using the specified item hasher (assumed compatible with the two collections).
\r
632 /// <param name="tit">The first collection</param>
\r
633 /// <param name="tat">The second collection</param>
\r
634 /// <param name="itemhasher">The item hasher to use for comparison</param>
\r
635 /// <returns>True if equal</returns>
\r
637 public static bool StaticEquals(ISequenced<T> tit, ISequenced<T> tat, IHasher<T> itemhasher)
\r
640 return tit == null;
\r
642 if (tit.Count != tat.Count)
\r
645 //This way we might run through both enumerations twice, but
\r
646 //probably not (if the hash codes are good)
\r
647 if (tit.GetHashCode() != tat.GetHashCode())
\r
650 using (MSG.IEnumerator<T> dat = tat.GetEnumerator(), dit = tit.GetEnumerator())
\r
652 while (dit.MoveNext())
\r
655 if (!itemhasher.Equals(dit.Current, dat.Current))
\r
665 /// Get the sequenced collection hash code of this collection: from the cached
\r
666 /// value if present and up to date, else (re)compute.
\r
668 /// <returns>The hash code</returns>
\r
669 protected int sequencedhashcode()
\r
671 if (iSequencedHashCodeStamp == stamp)
\r
672 return iSequencedHashCode;
\r
674 iSequencedHashCode = ComputeHashCode((ISequenced<T>)this, itemhasher);
\r
675 iSequencedHashCodeStamp = stamp;
\r
676 return iSequencedHashCode;
\r
681 /// Check if the contents of that is equal to the contents of this
\r
682 /// in the sequenced sense. Using the item hasher of this collection.
\r
684 /// <param name="that">The collection to compare to.</param>
\r
685 /// <returns>True if equal</returns>
\r
686 protected bool sequencedequals(ISequenced<T> that)
\r
688 return StaticEquals((ISequenced<T>)this, that, itemhasher);
\r
695 /// Create an enumerator for this collection.
\r
697 /// <returns>The enumerator</returns>
\r
698 public override abstract MSG.IEnumerator<T> GetEnumerator();
\r
702 /// <code>Forwards</code> if same, else <code>Backwards</code>
\r
704 /// <value>The enumeration direction relative to the original collection.</value>
\r
706 public EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } }
\r
711 /// Base class for collection classes of dynamic array type implementations.
\r
713 public class ArrayBase<T>: SequencedBase<T>
\r
717 /// The actual internal array container. Will be extended on demand.
\r
719 protected T[] array;
\r
722 /// The offset into the internal array container of the first item. The offset is 0 for a
\r
723 /// base dynamic array and may be positive for an updatable view into a base dynamic array.
\r
725 protected int offset;
\r
730 /// Double the size of the internal array.
\r
732 protected virtual void expand()
\r
734 expand(2 * array.Length, size);
\r
739 /// Expand the internal array container.
\r
741 /// <param name="newcapacity">The new size of the internal array -
\r
742 /// will be rounded upwards to a power of 2.</param>
\r
743 /// <param name="newsize">The (new) size of the (base) collection.</param>
\r
744 protected virtual void expand(int newcapacity, int newsize)
\r
746 Debug.Assert(newcapacity >= newsize);
\r
748 int newlength = array.Length;
\r
750 while (newlength < newcapacity) newlength *= 2;
\r
752 T[] newarray = new T[newlength];
\r
754 Array.Copy(array, newarray, newsize);
\r
760 /// Insert an item at a specific index, moving items to the right
\r
761 /// upwards and expanding the array if necessary.
\r
763 /// <param name="i">The index at which to insert.</param>
\r
764 /// <param name="item">The item to insert.</param>
\r
765 protected virtual void insert(int i, T item)
\r
767 if (size == array.Length)
\r
771 Array.Copy(array, i, array, i + 1, size - i);
\r
779 #region Constructors
\r
782 /// Create an empty ArrayBase object.
\r
784 /// <param name="capacity">The initial capacity of the internal array container.
\r
785 /// Will be rounded upwards to the nearest power of 2 greater than or equal to 8.</param>
\r
786 /// <param name="hasher">The item hasher to use, primarily for item equality</param>
\r
787 public ArrayBase(int capacity, IHasher<T> hasher)
\r
791 while (newlength < capacity) newlength *= 2;
\r
793 array = new T[newlength];
\r
794 itemhasher = hasher;
\r
799 #region IIndexed members
\r
802 /// <exception cref="IndexOutOfRangeException"/>.
\r
804 /// <value>The directed collection of items in a specific index interval.</value>
\r
805 /// <param name="start">The low index of the interval (inclusive).</param>
\r
806 /// <param name="count">The size of the range.</param>
\r
808 public IDirectedCollectionValue<T> this[int start, int count]
\r
813 checkRange(start, count);
\r
814 return new Range(this, start, count, true);
\r
820 #region IEditableCollection members
\r
822 /// Remove all items and reset size of internal array container.
\r
825 public virtual void Clear()
\r
834 /// Create an array containing (copies) of the items of this collection in enumeration order.
\r
836 /// <returns>The new array</returns>
\r
838 public override T[] ToArray()
\r
840 T[] res = new T[size];
\r
842 Array.Copy(array, res, size);
\r
848 /// Perform an internal consistency (invariant) test on the array base.
\r
850 /// <returns>True if test succeeds.</returns>
\r
852 public virtual bool Check()
\r
854 bool retval = true;
\r
856 if (size > array.Length)
\r
858 Console.WriteLine("Bad size ({0}) > array.Length ({1})", size, array.Length);
\r
862 for (int i = 0; i < size; i++)
\r
864 if ((object)(array[i]) == null)
\r
866 Console.WriteLine("Bad element: null at index {0}", i);
\r
876 #region IDirectedCollection<T> Members
\r
879 /// Create a directed collection with the same contents as this one, but
\r
880 /// opposite enumeration sequence.
\r
882 /// <returns>The mirrored collection.</returns>
\r
884 public IDirectedCollectionValue<T> Backwards() { return this[0, size].Backwards(); }
\r
888 #region IEnumerable<T> Members
\r
890 /// Create an enumerator for this array based collection.
\r
892 /// <returns>The enumerator</returns>
\r
894 public override MSG.IEnumerator<T> GetEnumerator()
\r
896 int thestamp = stamp, theend = size + offset, thestart = offset;
\r
898 for (int i = thestart; i < theend; i++)
\r
900 modifycheck(thestamp);
\r
901 yield return array[i];
\r
906 #region Range nested class
\r
908 /// A helper class for defining results of interval queries on array based collections.
\r
910 protected class Range: CollectionValueBase<T>, IDirectedCollectionValue<T>
\r
912 int start, count, delta, stamp;
\r
914 ArrayBase<T> thebase;
\r
917 internal Range(ArrayBase<T> thebase, int start, int count, bool forwards)
\r
919 this.thebase = thebase; stamp = thebase.stamp;
\r
920 delta = forwards ? 1 : -1;
\r
921 this.start = start + thebase.offset; this.count = count;
\r
928 /// <value>The number of items in the range</value>
\r
930 public override int Count { [Tested]get { thebase.modifycheck(stamp); return count; } }
\r
933 /// The value is symbolic indicating the type of asymptotic complexity
\r
934 /// in terms of the size of this collection (worst-case or amortized as
\r
937 /// <value>A characterization of the speed of the
\r
938 /// <code>Count</code> property in this collection.</value>
\r
939 public override Speed CountSpeed { get { thebase.modifycheck(stamp); return Speed.Constant; } }
\r
942 /// Create an enumerator for this range of an array based collection.
\r
944 /// <returns>The enumerator</returns>
\r
946 public override MSG.IEnumerator<T> GetEnumerator()
\r
948 for (int i = 0; i < count; i++)
\r
950 thebase.modifycheck(stamp);
\r
951 yield return thebase.array[start + delta * i];
\r
957 /// Create a araay collection range with the same contents as this one, but
\r
958 /// opposite enumeration sequence.
\r
960 /// <returns>The mirrored collection.</returns>
\r
962 public IDirectedCollectionValue<T> Backwards()
\r
964 thebase.modifycheck(stamp);
\r
966 Range res = (Range)MemberwiseClone();
\r
968 res.delta = -delta;
\r
969 res.start = start + (count - 1) * delta;
\r
974 IDirectedEnumerable<T> C5.IDirectedEnumerable<T>.Backwards()
\r
976 return Backwards();
\r
981 /// <code>Forwards</code> if same, else <code>Backwards</code>
\r
983 /// <value>The enumeration direction relative to the original collection.</value>
\r
985 public EnumerationDirection Direction
\r
990 thebase.modifycheck(stamp);
\r
991 return delta > 0 ? EnumerationDirection.Forwards : EnumerationDirection.Backwards;
\r
1002 /// A utility class with functions for sorting arrays with respect to an IComparer<T>
\r
1004 public class Sorting
\r
1007 /// Sort part of array in place using IntroSort
\r
1009 /// <param name="a">Array to sort</param>
\r
1010 /// <param name="f">Index of first position to sort</param>
\r
1011 /// <param name="b">Index of first position beyond the part to sort</param>
\r
1012 /// <param name="c">IComparer<T> to sort by</param>
\r
1014 public static void IntroSort<T>(T[] a, int f, int b, IComparer<T> c)
\r
1016 new Sorter<T>(a, c).IntroSort(f, b);
\r
1021 /// Sort part of array in place using Insertion Sort
\r
1023 /// <param name="a">Array to sort</param>
\r
1024 /// <param name="f">Index of first position to sort</param>
\r
1025 /// <param name="b">Index of first position beyond the part to sort</param>
\r
1026 /// <param name="c">IComparer<T> to sort by</param>
\r
1028 public static void InsertionSort<T>(T[] a, int f, int b, IComparer<T> c)
\r
1030 new Sorter<T>(a, c).InsertionSort(f, b);
\r
1035 /// Sort part of array in place using Heap Sort
\r
1037 /// <param name="a">Array to sort</param>
\r
1038 /// <param name="f">Index of first position to sort</param>
\r
1039 /// <param name="b">Index of first position beyond the part to sort</param>
\r
1040 /// <param name="c">IComparer<T> to sort by</param>
\r
1042 public static void HeapSort<T>(T[] a, int f, int b, IComparer<T> c)
\r
1044 new Sorter<T>(a, c).HeapSort(f, b);
\r
1055 internal Sorter(T[] a, IComparer<T> c) { this.a = a; this.c = c; }
\r
1058 internal void IntroSort(int f, int b)
\r
1062 int depth_limit = (int)Math.Floor(2.5 * Math.Log(b - f, 2));
\r
1064 introSort(f, b, depth_limit);
\r
1067 InsertionSort(f, b);
\r
1071 private void introSort(int f, int b, int depth_limit)
\r
1073 const int size_threshold = 14;//24;
\r
1075 if (depth_limit-- == 0)
\r
1077 else if (b - f <= size_threshold)
\r
1078 InsertionSort(f, b);
\r
1081 int p = partition(f, b);
\r
1083 introSort(f, p, depth_limit);
\r
1084 introSort(p, b, depth_limit);
\r
1089 private int compare(T i1, T i2) { return c.Compare(i1, i2); }
\r
1092 private int partition(int f, int b)
\r
1094 int bot = f, mid = (b + f) / 2, top = b - 1;
\r
1095 T abot = a[bot], amid = a[mid], atop = a[top];
\r
1097 if (compare(abot, amid) < 0)
\r
1099 if (compare(atop, abot) < 0)//atop<abot<amid
\r
1100 { a[top] = amid; amid = a[mid] = abot; a[bot] = atop; }
\r
1101 else if (compare(atop, amid) < 0) //abot<=atop<amid
\r
1102 { a[top] = amid; amid = a[mid] = atop; }
\r
1103 //else abot<amid<=atop
\r
1107 if (compare(amid, atop) > 0) //atop<amid<=abot
\r
1108 { a[bot] = atop; a[top] = abot; }
\r
1109 else if (compare(abot, atop) > 0) //amid<=atop<abot
\r
1110 { a[bot] = amid; amid = a[mid] = atop; a[top] = abot; }
\r
1111 else //amid<=abot<=atop
\r
1112 { a[bot] = amid; amid = a[mid] = abot; }
\r
1115 int i = bot, j = top;
\r
1119 while (compare(a[++i], amid) < 0);
\r
1121 while (compare(amid, a[--j]) < 0);
\r
1125 T tmp = a[i]; a[i] = a[j]; a[j] = tmp;
\r
1133 internal void InsertionSort(int f, int b)
\r
1135 for (int j = f + 1; j < b; j++)
\r
1137 T key = a[j], other;
\r
1140 if (c.Compare(other = a[i], key) > 0)
\r
1143 while (i > f && c.Compare(other = a[i - 1], key) > 0) { a[i--] = other; }
\r
1151 internal void HeapSort(int f, int b)
\r
1153 for (int i = (b + f) / 2; i >= f; i--) heapify(f, b, i);
\r
1155 for (int i = b - 1; i > f; i--)
\r
1157 T tmp = a[f]; a[f] = a[i]; a[i] = tmp;
\r
1163 private void heapify(int f, int b, int i)
\r
1165 T pv = a[i], lv, rv, max = pv;
\r
1166 int j = i, maxpt = j;
\r
1170 int l = 2 * j - f + 1, r = l + 1;
\r
1172 if (l < b && compare(lv = a[l], max) > 0) { maxpt = l; max = lv; }
\r
1174 if (r < b && compare(rv = a[r], max) > 0) { maxpt = r; max = rv; }
\r
1194 /// A modern random number generator based on (whatever)
\r
1196 public class C5Random : Random
\r
1198 private uint[] Q = new uint[16];
\r
1200 private uint c = 362436, i = 15;
\r
1203 private uint Cmwc()
\r
1205 ulong t, a = 487198574UL;
\r
1206 uint x, r = 0xfffffffe;
\r
1210 c = (uint)(t >> 32);
\r
1211 x = (uint)(t + c);
\r
1218 return Q[i] = r - x;
\r
1223 /// Get a new random System.Double value
\r
1225 /// <returns>The random double</returns>
\r
1226 public override double NextDouble()
\r
1228 return Cmwc() / 4294967296.0;
\r
1233 /// Get a new random System.Double value
\r
1235 /// <returns>The random double</returns>
\r
1236 protected override double Sample()
\r
1238 return NextDouble();
\r
1243 /// Get a new random System.Int32 value
\r
1245 /// <returns>The random int</returns>
\r
1246 public override int Next()
\r
1248 return (int)Cmwc();
\r
1253 /// Get a random non-negative integer less than a given upper bound
\r
1255 /// <param name="max">The upper bound (exclusive)</param>
\r
1256 /// <returns></returns>
\r
1257 public override int Next(int max)
\r
1260 throw new ApplicationException("max must be non-negative");
\r
1262 return (int)(Cmwc() / 4294967296.0 * max);
\r
1267 /// Get a random integer between two given bounds
\r
1269 /// <param name="min">The lower bound (inclusive)</param>
\r
1270 /// <param name="max">The upper bound (exclusive)</param>
\r
1271 /// <returns></returns>
\r
1272 public override int Next(int min, int max)
\r
1275 throw new ApplicationException("min must be less than or equal to max");
\r
1277 return min + (int)(Cmwc() / 4294967296.0 * max);
\r
1281 /// Fill a array of byte with random bytes
\r
1283 /// <param name="buffer">The array to fill</param>
\r
1284 public override void NextBytes(byte[] buffer)
\r
1286 for (int i = 0, length = buffer.Length; i < length; i++)
\r
1287 buffer[i] = (byte)Cmwc();
\r
1292 /// Create a random number generator seed by system time.
\r
1294 public C5Random() : this(DateTime.Now.Ticks)
\r
1300 /// Create a random number generator with a given seed
\r
1302 /// <param name="seed">The seed</param>
\r
1303 public C5Random(long seed)
\r
1306 throw new ApplicationException("Seed must be non-zero");
\r
1308 uint j = (uint)(seed & 0xFFFFFFFF);
\r
1310 for (int i = 0; i < 16; i++)
\r
1318 Q[15] = (uint)(seed ^ (seed >> 32));
\r
1324 #region Custom code attributes
\r
1327 /// A custom attribute to mark methods and properties as being tested
\r
1328 /// sufficiently in the regression test suite.
\r
1330 [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
\r
1331 public class TestedAttribute: Attribute
\r
1335 /// Optional reference to test case
\r
1338 public string via;
\r
1342 /// Pretty print attribute value
\r
1344 /// <returns>"Tested via " + via</returns>
\r
1346 public override string ToString() { return "Tested via " + via; }
\r