#if NET_2_0 /* Copyright (c) 2003-2004 Niels Kokholm and Peter Sestoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Diagnostics; using MSG = System.Collections.Generic; namespace C5 { /// /// Direction of enumeration order relative to original collection. /// public enum EnumerationDirection { /// /// Same direction /// Forwards, /// /// Opposite direction /// Backwards } #region int stuff class IC: IComparer { [Tested] public int Compare(int a, int b) { return a > b ? 1 : a < b ? -1 : 0; } } /// /// A hasher for int32 /// public class IntHasher: IHasher { /// /// Get the hash code of this integer, i.e. itself /// /// The integer /// The same [Tested] public int GetHashCode(int item) { return item; } /// /// Check if two integers are equal /// /// first integer /// second integer /// True if equal [Tested] public bool Equals(int i1, int i2) { return i1 == i2; } } #endregion #region Natural Comparers /// /// A natural generic IComparer for an IComparable<T> item type /// public class NaturalComparer: IComparer where T: IComparable { /// /// Compare two items /// /// First item /// Second item /// a <=> b [Tested] public int Compare(T a, T b) { return a.CompareTo(b); } } /// /// A natural generic IComparer for a System.IComparable item type /// public class NaturalComparerO: IComparer where T: System.IComparable { /// /// Compare two items /// /// First item /// Second item /// a <=> b [Tested] public int Compare(T a, T b) { return a.CompareTo(b); } } #endregion #region Hashers /// /// The default item hasher for a reference type. A trivial wrapper for calling /// the GetHashCode and Equals methods inherited from object. /// ///

Should only be instantiated with a reference type as generic type parameter. /// This is asserted at instatiation time in Debug builds.

///
public sealed class DefaultReferenceTypeHasher: IHasher { static DefaultReferenceTypeHasher() { Debug.Assert(!typeof(T).IsValueType, "DefaultReferenceTypeHasher instantiated with value type: " + typeof(T)); } /// /// Get the hash code with respect to this item hasher /// /// The item /// The hash code [Tested] public int GetHashCode(T item) { return item.GetHashCode(); } /// /// Check if two items are equal with respect to this item hasher /// /// first item /// second item /// True if equal [Tested] public bool Equals(T i1, T i2) { //For reference types, the (object) cast should be jitted as a noop. return (object)i1 == null ? (object)i2 == null : i1.Equals(i2); } } /// /// The default item hasher for a value type. A trivial wrapper for calling /// the GetHashCode and Equals methods inherited from object. /// ///

Should only be instantiated with a value type as generic type parameter. /// This is asserted at instatiation time in Debug builds.

///

We cannot add the constraint "where T : struct" to get a compile time check /// because we need to instantiate this class in C5.HasherBuilder.ByPrototype[T].Examine() /// with a T that is only known at runtime to be a value type!

///
//Note: we could (now) add a constraint "where T : struct" to get a compile time check, //but public sealed class DefaultValueTypeHasher: IHasher { static DefaultValueTypeHasher() { Debug.Assert(typeof(T).IsValueType, "DefaultValueTypeHasher instantiated with reference type: " + typeof(T)); } /// /// Get the hash code with respect to this item hasher /// /// The item /// The hash code [Tested] public int GetHashCode(T item) { return item.GetHashCode(); } /// /// Check if two items are equal with respect to this item hasher /// /// first item /// second item /// True if equal [Tested] public bool Equals(T i1, T i2) { return i1.Equals(i2); } } #endregion #region Bases /// /// A base class for implementing an IEnumerable<T> /// public abstract class EnumerableBase: MSG.IEnumerable { /// /// Create an enumerator for this collection. /// /// The enumerator public abstract MSG.IEnumerator GetEnumerator(); /// /// Count the number of items in an enumerable by enumeration /// /// The enumerable to count /// The size of the enumerable protected static int countItems(MSG.IEnumerable items) { ICollectionValue jtems = items as ICollectionValue; if (jtems != null) return jtems.Count; int count = 0; using (MSG.IEnumerator e = items.GetEnumerator()) while (e.MoveNext()) count++; return count; } } /// /// Base class for classes implementing ICollectionValue[T] /// public abstract class CollectionValueBase: EnumerableBase, ICollectionValue { //This forces our subclasses to make Count virtual! /// /// The number of items in this collection. /// /// public abstract int Count { get;} /// /// The value is symbolic indicating the type of asymptotic complexity /// in terms of the size of this collection (worst-case or amortized as /// relevant). /// /// A characterization of the speed of the /// Count property in this collection. public abstract Speed CountSpeed { get; } /// /// Copy the items of this collection to part of an array. /// if i is negative. /// if the array does not have room for the items. /// /// The array to copy to /// The starting index. [Tested] public virtual void CopyTo(T[] a, int i) { if (i < 0) throw new ArgumentOutOfRangeException(); if (i + Count > a.Length) throw new ArgumentException(); foreach (T item in this) a[i++] = item; } /// /// Create an array with the items of this collection (in the same order as an /// enumerator would output them). /// /// The array //[Tested] public virtual T[] ToArray() { T[] res = new T[Count]; int i = 0; foreach (T item in this) res[i++] = item; return res; } /// /// Apply an Applier<T> to this enumerable /// /// The applier delegate [Tested] public void Apply(Applier a) { foreach (T item in this) a(item); } /// /// Check if there exists an item that satisfies a /// specific predicate in this collection. /// /// A filter delegate /// () defining the predicate /// True is such an item exists [Tested] public bool Exists(Filter filter) { foreach (T item in this) if (filter(item)) return true; return false; } /// /// Check if all items in this collection satisfies a specific predicate. /// /// A filter delegate /// () defining the predicate /// True if all items satisfies the predicate [Tested] public bool All(Filter filter) { foreach (T item in this) if (!filter(item)) return false; return true; } /// /// Create an enumerator for this collection. /// /// The enumerator public override abstract MSG.IEnumerator GetEnumerator(); } /// /// Base class (abstract) for ICollection implementations. /// public abstract class CollectionBase: CollectionValueBase { #region Fields object syncroot = new object(); /// /// The underlying field of the ReadOnly property /// protected bool isReadOnly = false; /// /// The current stamp value /// protected int stamp; /// /// The number of items in the collection /// protected int size; /// /// The item hasher of the collection /// protected IHasher itemhasher; int iUnSequencedHashCode, iUnSequencedHashCodeStamp = -1; #endregion #region Util //protected bool equals(T i1, T i2) { return itemhasher.Equals(i1, i2); } /// /// Utility method for range checking. /// if the start or count is negative /// if the range does not fit within collection size. /// /// start of range /// size of range [Tested] protected void checkRange(int start, int count) { if (start < 0 || count < 0) throw new ArgumentOutOfRangeException(); if (start + count > size) throw new ArgumentException(); } /// /// Compute the unsequenced hash code of a collection /// /// The collection to compute hash code for /// The item hasher /// The hash code [Tested] public static int ComputeHashCode(ICollectionValue items, IHasher itemhasher) { int h = 0; foreach (T item in items) h ^= itemhasher.GetHashCode(item); return (items.Count << 16) + h; } /// /// Examine if tit and tat are equal as unsequenced collections /// using the specified item hasher (assumed compatible with the two collections). /// /// The first collection /// The second collection /// The item hasher to use for comparison /// True if equal [Tested] public static bool StaticEquals(ICollection tit, ICollection tat, IHasher itemhasher) { if (tat == null) return tit == null; if (tit.Count != tat.Count) return false; //This way we might run through both enumerations twice, but //probably not (if the hash codes are good) if (tit.GetHashCode() != tat.GetHashCode()) return false; if (!tit.AllowsDuplicates && (tat.AllowsDuplicates || tat.ContainsSpeed >= tit.ContainsSpeed)) { //TODO: use foreach construction using (MSG.IEnumerator dit = tit.GetEnumerator()) { while (dit.MoveNext()) if (!tat.Contains(dit.Current)) return false; } } else if (!tat.AllowsDuplicates) { using (MSG.IEnumerator dat = tat.GetEnumerator()) { while (dat.MoveNext()) if (!tit.Contains(dat.Current)) return false; } } else {//both are bags, we only have a slow one //unless the bags are based on a fast T->int dictinary (tree or hash) using (MSG.IEnumerator dat = tat.GetEnumerator()) { while (dat.MoveNext()) { T item = dat.Current; if (tit.ContainsCount(item) != tat.ContainsCount(item)) return false; } } //That was O(n^3) - completely unacceptable. //If we use an auxiliary bool[] we can do the comparison in O(n^2) } return true; } /// /// Get the unsequenced collection hash code of this collection: from the cached /// value if present and up to date, else (re)compute. /// /// The hash code protected int unsequencedhashcode() { if (iUnSequencedHashCodeStamp == stamp) return iUnSequencedHashCode; iUnSequencedHashCode = ComputeHashCode(this, itemhasher); iUnSequencedHashCodeStamp = stamp; return iUnSequencedHashCode; } /// /// Check if the contents of that is equal to the contents of this /// in the unsequenced sense. Using the item hasher of this collection. /// /// The collection to compare to. /// True if equal protected bool unsequencedequals(ICollection that) { return StaticEquals((ICollection)this, that, itemhasher); } /// /// if this collection has been updated /// since a target time /// /// The stamp identifying the target time protected virtual void modifycheck(int thestamp) { if (this.stamp != thestamp) throw new InvalidOperationException("Collection was modified"); } /// /// Check if it is valid to perform update operations, and if so increment stamp /// protected virtual void updatecheck() { if (isReadOnly) throw new InvalidOperationException("Collection cannot be modified through this guard object"); stamp++; } #endregion #region IEditableCollection members /// /// /// /// True if this collection is read only [Tested] public bool IsReadOnly { [Tested]get { return isReadOnly; } } #endregion #region ICollection members /// /// /// /// The size of this collection [Tested] public override int Count { [Tested]get { return size; } } /// /// The value is symbolic indicating the type of asymptotic complexity /// in terms of the size of this collection (worst-case or amortized as /// relevant). /// /// A characterization of the speed of the /// Count property in this collection. public override Speed CountSpeed { get { return Speed.Constant; } } #endregion #region ISink members /// /// /// /// A distinguished object to use for locking to synchronize multithreaded access [Tested] public object SyncRoot { get { return syncroot; } } /// /// /// /// True is this collection is empty [Tested] public bool IsEmpty { [Tested]get { return size == 0; } } #endregion #region IEnumerable Members /// /// Create an enumerator for this collection. /// /// The enumerator public override abstract MSG.IEnumerator GetEnumerator(); #endregion } /// /// Base class (abstract) for sequenced collection implementations. /// public abstract class SequencedBase: CollectionBase { #region Fields int iSequencedHashCode, iSequencedHashCodeStamp = -1; #endregion #region Util /// /// Compute the unsequenced hash code of a collection /// /// The collection to compute hash code for /// The item hasher /// The hash code [Tested] public static int ComputeHashCode(ISequenced items, IHasher itemhasher) { //NOTE: It must be possible to devise a much stronger combined hashcode, //but unfortunately, it has to be universal. OR we could use a (strong) //family and initialise its parameter randomly at load time of this class! //(We would not want to have yet a flag to check for invalidation?!) int iIndexedHashCode = 0; foreach (T item in items) iIndexedHashCode = iIndexedHashCode * 31 + itemhasher.GetHashCode(item); return iIndexedHashCode; } /// /// Examine if tit and tat are equal as sequenced collections /// using the specified item hasher (assumed compatible with the two collections). /// /// The first collection /// The second collection /// The item hasher to use for comparison /// True if equal [Tested] public static bool StaticEquals(ISequenced tit, ISequenced tat, IHasher itemhasher) { if (tat == null) return tit == null; if (tit.Count != tat.Count) return false; //This way we might run through both enumerations twice, but //probably not (if the hash codes are good) if (tit.GetHashCode() != tat.GetHashCode()) return false; using (MSG.IEnumerator dat = tat.GetEnumerator(), dit = tit.GetEnumerator()) { while (dit.MoveNext()) { dat.MoveNext(); if (!itemhasher.Equals(dit.Current, dat.Current)) return false; } } return true; } /// /// Get the sequenced collection hash code of this collection: from the cached /// value if present and up to date, else (re)compute. /// /// The hash code protected int sequencedhashcode() { if (iSequencedHashCodeStamp == stamp) return iSequencedHashCode; iSequencedHashCode = ComputeHashCode((ISequenced)this, itemhasher); iSequencedHashCodeStamp = stamp; return iSequencedHashCode; } /// /// Check if the contents of that is equal to the contents of this /// in the sequenced sense. Using the item hasher of this collection. /// /// The collection to compare to. /// True if equal protected bool sequencedequals(ISequenced that) { return StaticEquals((ISequenced)this, that, itemhasher); } #endregion /// /// Create an enumerator for this collection. /// /// The enumerator public override abstract MSG.IEnumerator GetEnumerator(); /// /// Forwards if same, else Backwards /// /// The enumeration direction relative to the original collection. [Tested] public EnumerationDirection Direction { [Tested]get { return EnumerationDirection.Forwards; } } } /// /// Base class for collection classes of dynamic array type implementations. /// public class ArrayBase: SequencedBase { #region Fields /// /// The actual internal array container. Will be extended on demand. /// protected T[] array; /// /// The offset into the internal array container of the first item. The offset is 0 for a /// base dynamic array and may be positive for an updatable view into a base dynamic array. /// protected int offset; #endregion #region Util /// /// Double the size of the internal array. /// protected virtual void expand() { expand(2 * array.Length, size); } /// /// Expand the internal array container. /// /// The new size of the internal array - /// will be rounded upwards to a power of 2. /// The (new) size of the (base) collection. protected virtual void expand(int newcapacity, int newsize) { Debug.Assert(newcapacity >= newsize); int newlength = array.Length; while (newlength < newcapacity) newlength *= 2; T[] newarray = new T[newlength]; Array.Copy(array, newarray, newsize); array = newarray; } /// /// Insert an item at a specific index, moving items to the right /// upwards and expanding the array if necessary. /// /// The index at which to insert. /// The item to insert. protected virtual void insert(int i, T item) { if (size == array.Length) expand(); if (i < size) Array.Copy(array, i, array, i + 1, size - i); array[i] = item; size++; } #endregion #region Constructors /// /// Create an empty ArrayBase object. /// /// The initial capacity of the internal array container. /// Will be rounded upwards to the nearest power of 2 greater than or equal to 8. /// The item hasher to use, primarily for item equality public ArrayBase(int capacity, IHasher hasher) { int newlength = 8; while (newlength < capacity) newlength *= 2; array = new T[newlength]; itemhasher = hasher; } #endregion #region IIndexed members /// /// . /// /// The directed collection of items in a specific index interval. /// The low index of the interval (inclusive). /// The size of the range. [Tested] public IDirectedCollectionValue this[int start, int count] { [Tested] get { checkRange(start, count); return new Range(this, start, count, true); } } #endregion #region IEditableCollection members /// /// Remove all items and reset size of internal array container. /// [Tested] public virtual void Clear() { updatecheck(); array = new T[8]; size = 0; } /// /// Create an array containing (copies) of the items of this collection in enumeration order. /// /// The new array [Tested] public override T[] ToArray() { T[] res = new T[size]; Array.Copy(array, res, size); return res; } /// /// Perform an internal consistency (invariant) test on the array base. /// /// True if test succeeds. [Tested] public virtual bool Check() { bool retval = true; if (size > array.Length) { Console.WriteLine("Bad size ({0}) > array.Length ({1})", size, array.Length); return false; } for (int i = 0; i < size; i++) { if ((object)(array[i]) == null) { Console.WriteLine("Bad element: null at index {0}", i); return false; } } return retval; } #endregion #region IDirectedCollection Members /// /// Create a directed collection with the same contents as this one, but /// opposite enumeration sequence. /// /// The mirrored collection. [Tested] public IDirectedCollectionValue Backwards() { return this[0, size].Backwards(); } #endregion #region IEnumerable Members /// /// Create an enumerator for this array based collection. /// /// The enumerator [Tested] public override MSG.IEnumerator GetEnumerator() { int thestamp = stamp, theend = size + offset, thestart = offset; for (int i = thestart; i < theend; i++) { modifycheck(thestamp); yield return array[i]; } } #endregion #region Range nested class /// /// A helper class for defining results of interval queries on array based collections. /// protected class Range: CollectionValueBase, IDirectedCollectionValue { int start, count, delta, stamp; ArrayBase thebase; internal Range(ArrayBase thebase, int start, int count, bool forwards) { this.thebase = thebase; stamp = thebase.stamp; delta = forwards ? 1 : -1; this.start = start + thebase.offset; this.count = count; } /// /// /// /// The number of items in the range [Tested] public override int Count { [Tested]get { thebase.modifycheck(stamp); return count; } } /// /// The value is symbolic indicating the type of asymptotic complexity /// in terms of the size of this collection (worst-case or amortized as /// relevant). /// /// A characterization of the speed of the /// Count property in this collection. public override Speed CountSpeed { get { thebase.modifycheck(stamp); return Speed.Constant; } } /// /// Create an enumerator for this range of an array based collection. /// /// The enumerator [Tested] public override MSG.IEnumerator GetEnumerator() { for (int i = 0; i < count; i++) { thebase.modifycheck(stamp); yield return thebase.array[start + delta * i]; } } /// /// Create a araay collection range with the same contents as this one, but /// opposite enumeration sequence. /// /// The mirrored collection. [Tested] public IDirectedCollectionValue Backwards() { thebase.modifycheck(stamp); Range res = (Range)MemberwiseClone(); res.delta = -delta; res.start = start + (count - 1) * delta; return res; } IDirectedEnumerable C5.IDirectedEnumerable.Backwards() { return Backwards(); } /// /// Forwards if same, else Backwards /// /// The enumeration direction relative to the original collection. [Tested] public EnumerationDirection Direction { [Tested] get { thebase.modifycheck(stamp); return delta > 0 ? EnumerationDirection.Forwards : EnumerationDirection.Backwards; } } } #endregion } #endregion #region Sorting /// /// A utility class with functions for sorting arrays with respect to an IComparer<T> /// public class Sorting { /// /// Sort part of array in place using IntroSort /// /// Array to sort /// Index of first position to sort /// Index of first position beyond the part to sort /// IComparer<T> to sort by [Tested] public static void IntroSort(T[] a, int f, int b, IComparer c) { new Sorter(a, c).IntroSort(f, b); } /// /// Sort part of array in place using Insertion Sort /// /// Array to sort /// Index of first position to sort /// Index of first position beyond the part to sort /// IComparer<T> to sort by [Tested] public static void InsertionSort(T[] a, int f, int b, IComparer c) { new Sorter(a, c).InsertionSort(f, b); } /// /// Sort part of array in place using Heap Sort /// /// Array to sort /// Index of first position to sort /// Index of first position beyond the part to sort /// IComparer<T> to sort by [Tested] public static void HeapSort(T[] a, int f, int b, IComparer c) { new Sorter(a, c).HeapSort(f, b); } class Sorter { T[] a; IComparer c; internal Sorter(T[] a, IComparer c) { this.a = a; this.c = c; } internal void IntroSort(int f, int b) { if (b - f > 31) { int depth_limit = (int)Math.Floor(2.5 * Math.Log(b - f, 2)); introSort(f, b, depth_limit); } else InsertionSort(f, b); } private void introSort(int f, int b, int depth_limit) { const int size_threshold = 14;//24; if (depth_limit-- == 0) HeapSort(f, b); else if (b - f <= size_threshold) InsertionSort(f, b); else { int p = partition(f, b); introSort(f, p, depth_limit); introSort(p, b, depth_limit); } } private int compare(T i1, T i2) { return c.Compare(i1, i2); } private int partition(int f, int b) { int bot = f, mid = (b + f) / 2, top = b - 1; T abot = a[bot], amid = a[mid], atop = a[top]; if (compare(abot, amid) < 0) { if (compare(atop, abot) < 0)//atop 0) //atop 0) //amid<=atop 0) { a[j] = other; while (i > f && c.Compare(other = a[i - 1], key) > 0) { a[i--] = other; } a[i] = key; } } } internal void HeapSort(int f, int b) { for (int i = (b + f) / 2; i >= f; i--) heapify(f, b, i); for (int i = b - 1; i > f; i--) { T tmp = a[f]; a[f] = a[i]; a[i] = tmp; heapify(f, i, f); } } private void heapify(int f, int b, int i) { T pv = a[i], lv, rv, max = pv; int j = i, maxpt = j; while (true) { int l = 2 * j - f + 1, r = l + 1; if (l < b && compare(lv = a[l], max) > 0) { maxpt = l; max = lv; } if (r < b && compare(rv = a[r], max) > 0) { maxpt = r; max = rv; } if (maxpt == j) break; a[j] = max; max = pv; j = maxpt; } if (j > i) a[j] = pv; } } } #endregion #region Random /// /// A modern random number generator based on (whatever) /// public class C5Random : Random { private uint[] Q = new uint[16]; private uint c = 362436, i = 15; private uint Cmwc() { ulong t, a = 487198574UL; uint x, r = 0xfffffffe; i = (i + 1) & 15; t = a * Q[i] + c; c = (uint)(t >> 32); x = (uint)(t + c); if (x < c) { x++; c++; } return Q[i] = r - x; } /// /// Get a new random System.Double value /// /// The random double public override double NextDouble() { return Cmwc() / 4294967296.0; } /// /// Get a new random System.Double value /// /// The random double protected override double Sample() { return NextDouble(); } /// /// Get a new random System.Int32 value /// /// The random int public override int Next() { return (int)Cmwc(); } /// /// Get a random non-negative integer less than a given upper bound /// /// The upper bound (exclusive) /// public override int Next(int max) { if (max < 0) throw new ApplicationException("max must be non-negative"); return (int)(Cmwc() / 4294967296.0 * max); } /// /// Get a random integer between two given bounds /// /// The lower bound (inclusive) /// The upper bound (exclusive) /// public override int Next(int min, int max) { if (min > max) throw new ApplicationException("min must be less than or equal to max"); return min + (int)(Cmwc() / 4294967296.0 * max); } /// /// Fill a array of byte with random bytes /// /// The array to fill public override void NextBytes(byte[] buffer) { for (int i = 0, length = buffer.Length; i < length; i++) buffer[i] = (byte)Cmwc(); } /// /// Create a random number generator seed by system time. /// public C5Random() : this(DateTime.Now.Ticks) { } /// /// Create a random number generator with a given seed /// /// The seed public C5Random(long seed) { if (seed == 0) throw new ApplicationException("Seed must be non-zero"); uint j = (uint)(seed & 0xFFFFFFFF); for (int i = 0; i < 16; i++) { j ^= j << 13; j ^= j >>17; j ^= j << 5; Q[i] = j; } Q[15] = (uint)(seed ^ (seed >> 32)); } } #endregion #region Custom code attributes /// /// A custom attribute to mark methods and properties as being tested /// sufficiently in the regression test suite. /// [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class TestedAttribute: Attribute { /// /// Optional reference to test case /// [Tested] public string via; /// /// Pretty print attribute value /// /// "Tested via " + via [Tested] public override string ToString() { return "Tested via " + via; } } #endregion } #endif