//****************************** // Written by Peter Golde // Copyright (c) 2004-2005, Wintellect // // Use and restribution of this code is subject to the license agreement // contained in the file "License.txt" accompanying this file. //****************************** using System; using System.Collections; using System.Collections.Generic; #pragma warning disable 419 // Ambigious cref in XML comment // Make internals of this library available to the unit test framework. [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")] // Everything should be CLS compliant. [assembly: CLSCompliant(true)] namespace Wintellect.PowerCollections { /// /// The BinaryPredicate delegate type encapsulates a method that takes two /// items of the same type, and returns a boolean value representating /// some relationship between them. For example, checking whether two /// items are equal or equivalent is one kind of binary predicate. /// /// The first item. /// The second item. /// Whether item1 and item2 satisfy the relationship that the BinaryPredicate defines. public delegate bool BinaryPredicate(T item1, T item2); /// /// Algorithms contains a number of static methods that implement /// algorithms that work on collections. Most of the methods deal with /// the standard generic collection interfaces such as IEnumerable<T>, /// ICollection<T> and IList<T>. /// public static class Algorithms { #region Collection wrappers /// /// The class that is used to implement IList<T> to view a sub-range /// of a list. The object stores a wrapped list, and a start/count indicating /// a sub-range of the list. Insertion/deletions through the sub-range view /// cause the count to change also; insertions and deletions directly on /// the wrapped list do not. /// [Serializable] private class ListRange : ListBase, ICollection { private IList wrappedList; private int start; private int count; /// /// Create a sub-range view object on the indicate part /// of the list. /// /// List to wrap. /// The start index of the view in the wrapped list. /// The number of items in the view. public ListRange(IList wrappedList, int start, int count) { this.wrappedList = wrappedList; this.start = start; this.count = count; } public override int Count { get { return Math.Min(count, wrappedList.Count - start); } } public override void Clear() { if (wrappedList.Count - start < count) count = wrappedList.Count - start; while (count > 0) { wrappedList.RemoveAt(start + count - 1); --count; } } public override void Insert(int index, T item) { if (index < 0 || index > count) throw new ArgumentOutOfRangeException("index"); wrappedList.Insert(start + index, item); ++count; } public override void RemoveAt(int index) { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); wrappedList.RemoveAt(start + index); --count; } public override bool Remove(T item) { if (wrappedList.IsReadOnly) throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "Range")); else return base.Remove(item); } public override T this[int index] { get { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); return wrappedList[start + index]; } set { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); wrappedList[start + index] = value; } } bool ICollection.IsReadOnly { get { return wrappedList.IsReadOnly; } } } /// /// Returns a view onto a sub-range of a list. Items from are not copied; the /// returned IList<T> is simply a different view onto the same underlying items. Changes to /// are reflected in the view, and vice versa. Insertions and deletions in the view change the size of the /// view, but insertions and deletions in the underlying list do not. /// /// This method can be used to apply an algorithm to a portion of a list. For example: /// Algorithms.ReverseInPlace(Algorithms.Range(list, 3, 6)) /// will reverse the 6 items beginning at index 3. /// The type of the items in the list. /// The list to view. /// The starting index of the view. /// The number of items in the view. /// A list that is a view onto the given sub-list. /// is null. /// or is negative. /// + is greater than the /// size of . public static IList Range(IList list, int start, int count) { if (list == null) throw new ArgumentOutOfRangeException("list"); if (start < 0 || start > list.Count || (start == list.Count && count != 0)) throw new ArgumentOutOfRangeException("start"); if (count < 0 || count > list.Count || count + start > list.Count) throw new ArgumentOutOfRangeException("count"); return new ListRange(list, start, count); } /// /// The class that is used to implement IList<T> to view a sub-range /// of an array. The object stores a wrapped array, and a start/count indicating /// a sub-range of the array. Insertion/deletions through the sub-range view /// cause the count to change up to the size of the underlying array. Elements /// fall off the end of the underlying array. /// [Serializable] private class ArrayRange : ListBase { private T[] wrappedArray; private int start; private int count; /// /// Create a sub-range view object on the indicate part /// of the array. /// /// Array to wrap. /// The start index of the view in the wrapped list. /// The number of items in the view. public ArrayRange(T[] wrappedArray, int start, int count) { this.wrappedArray = wrappedArray; this.start = start; this.count = count; } public override int Count { get { return count; } } public override void Clear() { Array.Copy(wrappedArray, start + count, wrappedArray, start, wrappedArray.Length - (start + count)); Algorithms.FillRange(wrappedArray, wrappedArray.Length - count, count, default(T)); count = 0; } public override void Insert(int index, T item) { if (index < 0 || index > count) throw new ArgumentOutOfRangeException("index"); int i = start + index; if (i + 1 < wrappedArray.Length) Array.Copy(wrappedArray, i, wrappedArray, i + 1, wrappedArray.Length - i - 1); if (i < wrappedArray.Length) wrappedArray[i] = item; if (start + count < wrappedArray.Length) ++count; } public override void RemoveAt(int index) { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); int i = start + index; if (i < wrappedArray.Length - 1) Array.Copy(wrappedArray, i + 1, wrappedArray, i, wrappedArray.Length - i - 1); wrappedArray[wrappedArray.Length - 1] = default(T); --count; } public override T this[int index] { get { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); return wrappedArray[start + index]; } set { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); wrappedArray[start + index] = value; } } } /// /// Returns a view onto a sub-range of an array. Items from are not copied; the /// returned IList<T> is simply a different view onto the same underlying items. Changes to /// are reflected in the view, and vice versa. Insertions and deletions in the view change the size of the /// view. After an insertion, the last item in "falls off the end". After a deletion, the /// last item in array becomes the default value (0 or null). /// /// This method can be used to apply an algorithm to a portion of a array. For example: /// Algorithms.ReverseInPlace(Algorithms.Range(array, 3, 6)) /// will reverse the 6 items beginning at index 3. /// The array to view. /// The starting index of the view. /// The number of items in the view. /// A list that is a view onto the given sub-array. /// is null. /// or is negative. /// + is greater than the /// size of . public static IList Range(T[] array, int start, int count) { if (array == null) throw new ArgumentOutOfRangeException("array"); if (start < 0 || start > array.Length || (start == array.Length && count != 0)) throw new ArgumentOutOfRangeException("start"); if (count < 0 || count > array.Length || count + start > array.Length) throw new ArgumentOutOfRangeException("count"); return new ArrayRange(array, start, count); } /// /// The read-only ICollection<T> implementation that is used by the ReadOnly method. /// Methods that modify the collection throw a NotSupportedException, methods that don't /// modify are fowarded through to the wrapped collection. /// [Serializable] private class ReadOnlyCollection : ICollection { private ICollection wrappedCollection; // The collection we are wrapping (never null). /// /// Create a ReadOnlyCollection wrapped around the given collection. /// /// Collection to wrap. public ReadOnlyCollection(ICollection wrappedCollection) { this.wrappedCollection = wrappedCollection; } /// /// Throws an NotSupportedException stating that this collection cannot be modified. /// private void MethodModifiesCollection() { throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "read-only collection")); } public IEnumerator GetEnumerator() { return wrappedCollection.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)wrappedCollection).GetEnumerator(); } public bool Contains(T item) { return wrappedCollection.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { wrappedCollection.CopyTo(array, arrayIndex); } public int Count { get { return wrappedCollection.Count; } } public bool IsReadOnly { get { return true; } } public void Add(T item) { MethodModifiesCollection(); } public void Clear() { MethodModifiesCollection(); } public bool Remove(T item) { MethodModifiesCollection(); return false; } } /// /// Returns a read-only view onto a collection. The returned ICollection<T> interface /// only allows operations that do not change the collection: GetEnumerator, Contains, CopyTo, /// Count. The ReadOnly property returns false, indicating that the collection is read-only. All other /// methods on the interface throw a NotSupportedException. /// /// The data in the underlying collection is not copied. If the underlying /// collection is changed, then the read-only view also changes accordingly. /// The type of items in the collection. /// The collection to wrap. /// A read-only view onto . If is null, then null is returned. public static ICollection ReadOnly(ICollection collection) { if (collection == null) return null; else return new ReadOnlyCollection(collection); } /// /// The read-only IList<T> implementation that is used by the ReadOnly method. /// Methods that modify the list throw a NotSupportedException, methods that don't /// modify are fowarded through to the wrapped list. /// [Serializable] private class ReadOnlyList : IList { private IList wrappedList; // The list we are wrapping (never null). /// /// Create a ReadOnlyList wrapped around the given list. /// /// List to wrap. public ReadOnlyList(IList wrappedList) { this.wrappedList = wrappedList; } /// /// Throws an NotSupportedException stating that this collection cannot be modified. /// private void MethodModifiesCollection() { throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "read-only list")); } public IEnumerator GetEnumerator() { return wrappedList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)wrappedList).GetEnumerator(); } public int IndexOf(T item) { return wrappedList.IndexOf(item); } public bool Contains(T item) { return wrappedList.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { wrappedList.CopyTo(array, arrayIndex); } public int Count { get { return wrappedList.Count; } } public bool IsReadOnly { get { return true; } } public T this[int index] { get { return wrappedList[index]; } set { MethodModifiesCollection(); } } public void Add(T item) { MethodModifiesCollection(); } public void Clear() { MethodModifiesCollection(); } public void Insert(int index, T item) { MethodModifiesCollection(); } public void RemoveAt(int index) { MethodModifiesCollection(); } public bool Remove(T item) { MethodModifiesCollection(); return false; } } /// /// Returns a read-only view onto a list. The returned IList<T> interface /// only allows operations that do not change the list: GetEnumerator, Contains, CopyTo, /// Count, IndexOf, and the get accessor of the indexer. /// The IsReadOnly property returns true, indicating that the list is read-only. All other /// methods on the interface throw a NotSupportedException. /// /// The data in the underlying list is not copied. If the underlying /// list is changed, then the read-only view also changes accordingly. /// The type of items in the list. /// The list to wrap. /// A read-only view onto . Returns null if is null. /// If is already read-only, returns . public static IList ReadOnly(IList list) { if (list == null) return null; else if (list.IsReadOnly) return list; else return new ReadOnlyList(list); } /// /// The private class that implements a read-only wrapped for /// IDictionary <TKey,TValue>. /// [Serializable] private class ReadOnlyDictionary : IDictionary { // The dictionary that is wrapped private IDictionary wrappedDictionary; /// /// Create a read-only dictionary wrapped around the given dictionary. /// /// The IDictionary<TKey,TValue> to wrap. public ReadOnlyDictionary(IDictionary wrappedDictionary) { this.wrappedDictionary = wrappedDictionary; } /// /// Throws an NotSupportedException stating that this collection cannot be modified. /// private void MethodModifiesCollection() { throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "read-only dictionary")); } public void Add(TKey key, TValue value) { MethodModifiesCollection(); } public bool ContainsKey(TKey key) { return wrappedDictionary.ContainsKey(key); } public ICollection Keys { get { return ReadOnly(wrappedDictionary.Keys); } } public ICollection Values { get { return ReadOnly(wrappedDictionary.Values); } } public bool Remove(TKey key) { MethodModifiesCollection(); return false; // never reached } public bool TryGetValue(TKey key, out TValue value) { return wrappedDictionary.TryGetValue(key, out value); } public TValue this[TKey key] { get { return wrappedDictionary[key];} set { MethodModifiesCollection(); } } public void Add(KeyValuePair item) { MethodModifiesCollection(); } public void Clear() { MethodModifiesCollection(); } public bool Contains(KeyValuePair item) { return wrappedDictionary.Contains(item); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { wrappedDictionary.CopyTo(array, arrayIndex); } public int Count { get { return wrappedDictionary.Count; } } public bool IsReadOnly { get { return true; } } public bool Remove(KeyValuePair item) { MethodModifiesCollection(); return false; // never reached } public IEnumerator> GetEnumerator() { return wrappedDictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)wrappedDictionary).GetEnumerator(); } } /// /// Returns a read-only view onto a dictionary. The returned IDictionary<TKey,TValue> interface /// only allows operations that do not change the dictionary. /// The IsReadOnly property returns true, indicating that the dictionary is read-only. All other /// methods on the interface throw a NotSupportedException. /// /// The data in the underlying dictionary is not copied. If the underlying /// dictionary is changed, then the read-only view also changes accordingly. /// The dictionary to wrap. /// A read-only view onto . Returns null if is null. /// If is already read-only, returns . public static IDictionary ReadOnly(IDictionary dictionary) { if (dictionary == null) return null; else if (dictionary.IsReadOnly) return dictionary; else return new ReadOnlyDictionary(dictionary); } /// /// The class that provides a typed IEnumerator<T> /// view onto an untyped IEnumerator interface. /// [Serializable] private class TypedEnumerator : IEnumerator { private IEnumerator wrappedEnumerator; /// /// Create a typed IEnumerator<T> /// view onto an untyped IEnumerator interface /// /// IEnumerator to wrap. public TypedEnumerator(IEnumerator wrappedEnumerator) { this.wrappedEnumerator = wrappedEnumerator; } T IEnumerator.Current { get { return (T) wrappedEnumerator.Current; } } void IDisposable.Dispose() { if (wrappedEnumerator is IDisposable) ((IDisposable)wrappedEnumerator).Dispose(); } object IEnumerator.Current { get { return wrappedEnumerator.Current; } } bool IEnumerator.MoveNext() { return wrappedEnumerator.MoveNext(); } void IEnumerator.Reset() { wrappedEnumerator.Reset(); } } /// /// The class that provides a typed IEnumerable<T> view /// onto an untyped IEnumerable interface. /// [Serializable] private class TypedEnumerable : IEnumerable { private IEnumerable wrappedEnumerable; /// /// Create a typed IEnumerable<T> view /// onto an untyped IEnumerable interface. /// /// IEnumerable interface to wrap. public TypedEnumerable(IEnumerable wrappedEnumerable) { this.wrappedEnumerable = wrappedEnumerable; } public IEnumerator GetEnumerator() { return new TypedEnumerator(wrappedEnumerable.GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { return wrappedEnumerable.GetEnumerator(); } } /// /// Given a non-generic IEnumerable interface, wrap a generic IEnumerable<T> /// interface around it. The generic interface will enumerate the same objects as the /// underlying non-generic collection, but can be used in places that require a generic interface. /// The underlying non-generic collection must contain only items that /// are of type or a type derived from it. This method is useful /// when interfacing older, non-generic collections to newer code that uses generic interfaces. /// /// Some collections implement both generic and non-generic interfaces. For efficiency, /// this method will first attempt to cast to IEnumerable<T>. /// If that succeeds, it is returned; otherwise, a wrapper object is created. /// The item type of the wrapper collection. /// An untyped collection. This collection should only contain /// items of type or a type derived from it. /// A generic IEnumerable<T> wrapper around . /// If is null, then null is returned. public static IEnumerable TypedAs(IEnumerable untypedCollection) { if (untypedCollection == null) return null; else if (untypedCollection is IEnumerable) return (IEnumerable)untypedCollection; else return new TypedEnumerable(untypedCollection); } /// /// The class that provides a typed ICollection<T> view /// onto an untyped ICollection interface. The ICollection<T> /// is read-only. /// [Serializable] private class TypedCollection : ICollection { private ICollection wrappedCollection; /// /// Create a typed ICollection<T> view /// onto an untyped ICollection interface. /// /// ICollection interface to wrap. public TypedCollection(ICollection wrappedCollection) { this.wrappedCollection = wrappedCollection; } /// /// Throws an NotSupportedException stating that this collection cannot be modified. /// private void MethodModifiesCollection() { throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, "strongly-typed Collection")); } public void Add(T item) { MethodModifiesCollection(); } public void Clear() { MethodModifiesCollection(); } public bool Remove(T item) { MethodModifiesCollection(); return false; } public bool Contains(T item) { IEqualityComparer equalityComparer = EqualityComparer.Default; foreach (object obj in wrappedCollection) { if (obj is T && equalityComparer.Equals(item, (T)obj)) return true; } return false; } public void CopyTo(T[] array, int arrayIndex) { wrappedCollection.CopyTo(array, arrayIndex); } public int Count { get { return wrappedCollection.Count; } } public bool IsReadOnly { get { return true; } } public IEnumerator GetEnumerator() { return new TypedEnumerator(wrappedCollection.GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { return wrappedCollection.GetEnumerator(); } } /// /// Given a non-generic ICollection interface, wrap a generic ICollection<T> /// interface around it. The generic interface will enumerate the same objects as the /// underlying non-generic collection, but can be used in places that require a generic interface. /// The underlying non-generic collection must contain only items that /// are of type or a type derived from it. This method is useful /// when interfacing older, non-generic collections to newer code that uses generic interfaces. /// /// Some collections implement both generic and non-generic interfaces. For efficiency, /// this method will first attempt to cast to ICollection<T>. /// If that succeeds, it is returned; otherwise, a wrapper object is created. /// Unlike the generic interface, the non-generic ICollection interfaces does /// not contain methods for adding or removing items from the collection. For this reason, /// the returned ICollection<T> will be read-only. /// The item type of the wrapper collection. /// An untyped collection. This collection should only contain /// items of type or a type derived from it. /// A generic ICollection<T> wrapper around . /// If is null, then null is returned. public static ICollection TypedAs(ICollection untypedCollection) { if (untypedCollection == null) return null; else if (untypedCollection is ICollection) return (ICollection) untypedCollection; else return new TypedCollection(untypedCollection); } /// /// The class used to create a typed IList<T> view onto /// an untype IList interface. /// [Serializable] private class TypedList : IList { private IList wrappedList; /// /// Create a typed IList<T> view onto /// an untype IList interface. /// /// The IList to wrap. public TypedList(IList wrappedList) { this.wrappedList = wrappedList; } public IEnumerator GetEnumerator() { return new TypedEnumerator(wrappedList.GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { return wrappedList.GetEnumerator(); } public int IndexOf(T item) { return wrappedList.IndexOf(item); } public void Insert(int index, T item) { wrappedList.Insert(index, item); } public void RemoveAt(int index) { wrappedList.RemoveAt(index); } public void Add(T item) { wrappedList.Add(item); } public void Clear() { wrappedList.Clear(); } public bool Contains(T item) { return wrappedList.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { wrappedList.CopyTo(array, arrayIndex); } public T this[int index] { get { return (T)wrappedList[index]; } set { wrappedList[index] = value; } } public int Count { get { return wrappedList.Count ; } } public bool IsReadOnly { get { return wrappedList.IsReadOnly; } } public bool Remove(T item) { if (wrappedList.Contains(item)) { wrappedList.Remove(item); return true; } else { return false; } } } /// /// Given a non-generic IList interface, wrap a generic IList<T> /// interface around it. The generic interface will enumerate the same objects as the /// underlying non-generic list, but can be used in places that require a generic interface. /// The underlying non-generic list must contain only items that /// are of type or a type derived from it. This method is useful /// when interfacing older, non-generic lists to newer code that uses generic interfaces. /// /// Some collections implement both generic and non-generic interfaces. For efficiency, /// this method will first attempt to cast to IList<T>. /// If that succeeds, it is returned; otherwise, a wrapper object is created. /// The item type of the wrapper list. /// An untyped list. This list should only contain /// items of type or a type derived from it. /// A generic IList<T> wrapper around . /// If is null, then null is returned. public static IList TypedAs(IList untypedList) { if (untypedList == null) return null; else if (untypedList is IList) return (IList)untypedList; else return new TypedList(untypedList); } /// /// The class that is used to provide an untyped ICollection /// view onto a typed ICollection<T> interface. /// [Serializable] private class UntypedCollection : ICollection { private ICollection wrappedCollection; /// /// Create an untyped ICollection /// view onto a typed ICollection<T> interface. /// /// The ICollection<T> to wrap. public UntypedCollection(ICollection wrappedCollection) { this.wrappedCollection = wrappedCollection; } public void CopyTo(Array array, int index) { if (array == null) throw new ArgumentNullException("array"); int i = 0; int count = wrappedCollection.Count; if (index < 0) throw new ArgumentOutOfRangeException("index", index, Strings.ArgMustNotBeNegative); if (index >= array.Length || count > array.Length - index) throw new ArgumentException("index", Strings.ArrayTooSmall); foreach (T item in wrappedCollection) { if (i >= count) break; array.SetValue(item, index); ++index; ++i; } } public int Count { get { return wrappedCollection.Count; } } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return this; } } public IEnumerator GetEnumerator() { return ((IEnumerable)wrappedCollection).GetEnumerator(); } } /// /// Given a generic ICollection<T> interface, wrap a non-generic (untyped) /// ICollection interface around it. The non-generic interface will contain the same objects as the /// underlying generic collection, but can be used in places that require a non-generic interface. /// This method is useful when interfacing generic interfaces with older code that uses non-generic interfaces. /// /// Many generic collections already implement the non-generic interfaces directly. This /// method will first attempt to simply cast to ICollection. If that /// succeeds, it is returned; if it fails, then a wrapper object is created. /// The item type of the underlying collection. /// A typed collection to wrap. /// A non-generic ICollection wrapper around . /// If is null, then null is returned. public static ICollection Untyped(ICollection typedCollection) { if (typedCollection == null) return null; else if (typedCollection is ICollection) return (ICollection)typedCollection; else return new UntypedCollection(typedCollection); } /// /// The class that implements a non-generic IList wrapper /// around a generic IList<T> interface. /// [Serializable] private class UntypedList : IList { private IList wrappedList; /// /// Create a non-generic IList wrapper /// around a generic IList<T> interface. /// /// The IList<T> interface to wrap. public UntypedList(IList wrappedList) { this.wrappedList = wrappedList; } /// /// Convert the given parameter to T. Throw an ArgumentException /// if it isn't. /// /// parameter name /// parameter value private T ConvertToItemType(string name, object value) { try { return (T)value; } catch (InvalidCastException) { throw new ArgumentException(string.Format(Strings.WrongType, value, typeof(T)), name); } } public int Add(object value) { // We assume that Add always adds to the end. Is this true? wrappedList.Add(ConvertToItemType("value", value)); return wrappedList.Count - 1; } public void Clear() { wrappedList.Clear(); } public bool Contains(object value) { if (value is T) return wrappedList.Contains((T)value); else return false; } public int IndexOf(object value) { if (value is T) return wrappedList.IndexOf((T)value); else return -1; } public void Insert(int index, object value) { wrappedList.Insert(index, ConvertToItemType("value", value)); } public bool IsFixedSize { get { return false; } } public bool IsReadOnly { get { return wrappedList.IsReadOnly; } } public void Remove(object value) { if (value is T) wrappedList.Remove((T)value); } public void RemoveAt(int index) { wrappedList.RemoveAt(index);} public object this[int index] { get { return wrappedList[index]; } set { wrappedList[index] = ConvertToItemType("value", value); } } public void CopyTo(Array array, int index) { if (array == null) throw new ArgumentNullException("array"); int i = 0; int count = wrappedList.Count; if (index < 0) throw new ArgumentOutOfRangeException("index", index, Strings.ArgMustNotBeNegative); if (index >= array.Length || count > array.Length - index) throw new ArgumentException("index", Strings.ArrayTooSmall); foreach (T item in wrappedList) { if (i >= count) break; array.SetValue(item, index); ++index; ++i; } } public int Count { get { return wrappedList.Count; } } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return this; } } public IEnumerator GetEnumerator() { return ((IEnumerable)wrappedList).GetEnumerator(); } } /// /// Given a generic IList<T> interface, wrap a non-generic (untyped) /// IList interface around it. The non-generic interface will contain the same objects as the /// underlying generic list, but can be used in places that require a non-generic interface. /// This method is useful when interfacing generic interfaces with older code that uses non-generic interfaces. /// /// Many generic collections already implement the non-generic interfaces directly. This /// method will first attempt to simply cast to IList. If that /// succeeds, it is returned; if it fails, then a wrapper object is created. /// The item type of the underlying list. /// A typed list to wrap. /// A non-generic IList wrapper around . /// If is null, then null is returned. public static IList Untyped(IList typedList) { if (typedList == null) return null; else if (typedList is IList) return (IList)typedList; else return new UntypedList(typedList); } /// /// The class that is used to implement IList<T> to view an array /// in a read-write way. Insertions cause the last item in the array /// to fall off, deletions replace the last item with the default value. /// [Serializable] private class ArrayWrapper : ListBase, IList { private T[] wrappedArray; /// /// Create a list wrapper object on an array. /// /// Array to wrap. public ArrayWrapper(T[] wrappedArray) { this.wrappedArray = wrappedArray; } public override int Count { get { return wrappedArray.Length; } } public override void Clear() { int count = wrappedArray.Length; for (int i = 0; i < count; ++i) wrappedArray[i] = default(T); } public override void Insert(int index, T item) { if (index < 0 || index > wrappedArray.Length) throw new ArgumentOutOfRangeException("index"); if (index + 1 < wrappedArray.Length) Array.Copy(wrappedArray, index, wrappedArray, index + 1, wrappedArray.Length - index - 1); if (index < wrappedArray.Length) wrappedArray[index] = item; } public override void RemoveAt(int index) { if (index < 0 || index >= wrappedArray.Length) throw new ArgumentOutOfRangeException("index"); if (index < wrappedArray.Length - 1) Array.Copy(wrappedArray, index + 1, wrappedArray, index, wrappedArray.Length - index - 1); wrappedArray[wrappedArray.Length - 1] = default(T); } public override T this[int index] { get { if (index < 0 || index >= wrappedArray.Length) throw new ArgumentOutOfRangeException("index"); return wrappedArray[index]; } set { if (index < 0 || index >= wrappedArray.Length) throw new ArgumentOutOfRangeException("index"); wrappedArray[index] = value; } } public override void CopyTo(T[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); if (array.Length < wrappedArray.Length) throw new ArgumentException("array is too short", "array"); if (arrayIndex < 0 || arrayIndex >= array.Length) throw new ArgumentOutOfRangeException("arrayIndex"); if (array.Length + arrayIndex < wrappedArray.Length) throw new ArgumentOutOfRangeException("arrayIndex"); Array.Copy(wrappedArray, 0, array, arrayIndex, wrappedArray.Length); } public override IEnumerator GetEnumerator() { return ((IList)wrappedArray).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IList)wrappedArray).GetEnumerator(); } /// /// Return true, to indicate that the list is fixed size. /// bool IList.IsFixedSize { get { return true; } } } /// /// Creates a read-write IList<T> wrapper around an array. When an array is /// implicitely converted to an IList<T>, changes to the items in the array cannot /// be made through the interface. This method creates a read-write IList<T> wrapper /// on an array that can be used to make changes to the array. /// Use this method when you need to pass an array to an algorithms that takes an /// IList<T> and that tries to modify items in the list. Algorithms in this class generally do not /// need this method, since they have been design to operate on arrays even when they /// are passed as an IList<T>. /// /// Since arrays cannot be resized, inserting an item causes the last item in the array to be automatically /// removed. Removing an item causes the last item in the array to be replaced with a default value (0 or null). Clearing /// the list causes all the items to be replaced with a default value. /// The array to wrap. /// An IList<T> wrapper onto . public static IList ReadWriteList(T[] array) { if (array == null) throw new ArgumentNullException("array"); return new ArrayWrapper(array); } #endregion Collection wrappers #region Replacing /// /// Replace all items in a collection equal to a particular value with another values, yielding another collection. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The collection to process. /// The value to find and replace within . /// The new value to replace with. /// An new collection with the items from , in the same order, /// with the appropriate replacements made. public static IEnumerable Replace(IEnumerable collection, T itemFind, T replaceWith) { return Replace(collection, itemFind, replaceWith, EqualityComparer.Default); } /// /// Replace all items in a collection equal to a particular value with another values, yielding another collection. A passed /// IEqualityComparer is used to determine equality. /// /// The collection to process. /// The value to find and replace within . /// The new value to replace with. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. /// An new collection with the items from , in the same order, /// with the appropriate replacements made. public static IEnumerable Replace(IEnumerable collection, T itemFind, T replaceWith, IEqualityComparer equalityComparer) { if (collection == null) throw new ArgumentNullException("collection"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); foreach (T item in collection) { if (equalityComparer.Equals(item, itemFind)) yield return replaceWith; else yield return item; } } /// /// Replace all items in a collection that a predicate evalues at true with a value, yielding another collection. . /// /// The collection to process. /// The predicate used to evaluate items with the collection. If the predicate returns true for a particular /// item, the item is replaces with . /// The new value to replace with. /// An new collection with the items from , in the same order, /// with the appropriate replacements made. public static IEnumerable Replace(IEnumerable collection, Predicate predicate, T replaceWith) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); foreach (T item in collection) { if (predicate(item)) yield return replaceWith; else yield return item; } } /// /// Replace all items in a list or array equal to a particular value with another value. The replacement is done in-place, changing /// the list. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to process. /// The value to find and replace within . /// The new value to replace with. public static void ReplaceInPlace(IList list, T itemFind, T replaceWith) { ReplaceInPlace(list, itemFind, replaceWith, EqualityComparer.Default); } /// /// Replace all items in a list or array equal to a particular value with another values. /// The replacement is done in-place, changing /// the list. A passed IEqualityComparer is used to determine equality. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to process. /// The value to find and replace within . /// The new value to replace with. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. public static void ReplaceInPlace(IList list, T itemFind, T replaceWith, IEqualityComparer equalityComparer) { if (list == null) throw new ArgumentNullException("list"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); int listCount = list.Count; for (int index = 0; index < listCount; ++index) { if (equalityComparer.Equals(list[index], itemFind)) list[index] = replaceWith; } } /// /// Replace all items in a list or array that a predicate evaluates at true with a value. The replacement is done in-place, changing /// the list. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to process. /// The predicate used to evaluate items with the collection. If the predicate returns true for a particular /// item, the item is replaces with . /// The new value to replace with. public static void ReplaceInPlace(IList list, Predicate predicate, T replaceWith) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); int listCount = list.Count; for (int index = 0; index < listCount; ++index) { if (predicate(list[index])) list[index] = replaceWith; } } #endregion Replacing #region Consecutive items /// /// Remove consecutive equal items from a collection, yielding another collection. In each run of consecutive equal items /// in the collection, all items after the first item in the run are removed. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The collection to process. /// An new collection with the items from , in the same order, /// with consecutive duplicates removed. /// is null. public static IEnumerable RemoveDuplicates(IEnumerable collection) { return RemoveDuplicates(collection, EqualityComparer.Default); } /// /// Remove consecutive equal items from a collection, yielding another collection. In each run of consecutive equal items /// in the collection, all items after the first item in the run are removed. A passed /// IEqualityComparer is used to determine equality. /// /// The collection to process. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. /// An new collection with the items from , in the same order, /// with consecutive duplicates removed. /// or is null. public static IEnumerable RemoveDuplicates(IEnumerable collection, IEqualityComparer equalityComparer) { if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); return RemoveDuplicates(collection, equalityComparer.Equals); } /// /// Remove consecutive "equal" items from a collection, yielding another collection. In each run of consecutive equal items /// in the collection, all items after the first item in the run are removed. The passed /// BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being removed need not be true equality. /// The collection to process. /// The BinaryPredicate used to compare items for "equality". An item current is removed if predicate(first, current)==true, where /// first is the first item in the group of "duplicate" items. /// An new collection with the items from , in the same order, /// with consecutive "duplicates" removed. public static IEnumerable RemoveDuplicates(IEnumerable collection, BinaryPredicate predicate) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); T current = default(T); bool atBeginning = true; foreach (T item in collection) { // Is the new item different from the current item? if (atBeginning || !predicate(current, item)) { current = item; yield return item; } atBeginning = false; } } /// /// Remove consecutive equal items from a list or array. In each run of consecutive equal items /// in the list, all items after the first item in the run are removed. The removal is done in-place, changing /// the list. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to process. public static void RemoveDuplicatesInPlace(IList list) { RemoveDuplicatesInPlace(list, EqualityComparer.Default); } /// /// Remove subsequent consecutive equal items from a list or array. In each run of consecutive equal items /// in the list, all items after the first item in the run are removed. /// The replacement is done in-place, changing /// the list. A passed IEqualityComparer is used to determine equality. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to process. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. public static void RemoveDuplicatesInPlace(IList list, IEqualityComparer equalityComparer) { if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); RemoveDuplicatesInPlace(list, equalityComparer.Equals); } /// /// Remove consecutive "equal" items from a list or array. In each run of consecutive equal items /// in the list, all items after the first item in the run are removed. The replacement is done in-place, changing /// the list. The passed BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being tested for need not be true equality. /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to process. /// The BinaryPredicate used to compare items for "equality". public static void RemoveDuplicatesInPlace(IList list, BinaryPredicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); T current = default(T); T item; int i = -1, j = 0; int listCount = list.Count; // Remove duplicates, compressing items to lower in the list. while (j < listCount) { item = list[j]; if (i < 0 || !predicate(current, item)) { current = item; ++i; if (i != j) list[i] = current; } ++j; } ++i; if (i < listCount) { // remove items from the end. if (list is ArrayWrapper || (list is IList && ((IList)list).IsFixedSize)) { // An array or similar. Null out the last elements. while (i < listCount) list[i++] = default(T); } else { // Normal list. while (i < listCount) { list.RemoveAt(listCount - 1); --listCount; } } } } /// /// Finds the first occurence of consecutive equal items in the /// list. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The list to examine. /// The number of consecutive equal items to look for. The count must be at least 1. /// The index of the first item in the first run of consecutive equal items, or -1 if no such run exists.. public static int FirstConsecutiveEqual(IList list, int count) { return FirstConsecutiveEqual(list, count, EqualityComparer.Default); } /// /// Finds the first occurence of consecutive equal items in the /// list. A passed IEqualityComparer is used to determine equality. /// /// The list to examine. /// The number of consecutive equal items to look for. The count must be at least 1. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. /// The index of the first item in the first run of consecutive equal items, or -1 if no such run exists. public static int FirstConsecutiveEqual(IList list, int count, IEqualityComparer equalityComparer) { if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); return FirstConsecutiveEqual(list, count, equalityComparer.Equals); } /// /// Finds the first occurence of consecutive "equal" items in the /// list. The passed BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being tested for need not be true equality. /// The list to examine. /// The number of consecutive equal items to look for. The count must be at least 1. /// The BinaryPredicate used to compare items for "equality". /// The index of the first item in the first run of consecutive equal items, or -1 if no such run exists. public static int FirstConsecutiveEqual(IList list, int count, BinaryPredicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); if (count < 1) throw new ArgumentOutOfRangeException("count"); int listCount = list.Count; if (listCount < count) return -1; // Can't find run longer than the list itself. if (count == 1) return 0; // Run of 1 must be the first item in the list. int start = 0, index = 0; T current = default(T); int runLength = 0; // Go through the list, looking for a run of the given length. foreach (T item in list) { if (index > 0 && predicate(current, item)) { ++runLength; if (runLength >= count) return start; } else { current = item; start = index; runLength = 1; } ++index; } return -1; } /// /// Finds the first occurence of consecutive items in the /// list for which a given predicate returns true. /// /// The list to examine. /// The number of consecutive items to look for. The count must be at least 1. /// The predicate used to test each item. /// The index of the first item in the first run of items where /// returns true for all items in the run, or -1 if no such run exists. public static int FirstConsecutiveWhere(IList list, int count, Predicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); if (count < 1) throw new ArgumentOutOfRangeException("count"); int listCount = list.Count; if (count > listCount) return -1; // Can't find run longer than the list itself. int index = 0, start = -1; int runLength = 0; // Scan the list in order, looking for the number of consecutive true items. foreach (T item in list) { if (predicate(item)) { if (start < 0) start = index; ++runLength; if (runLength >= count) return start; } else { runLength = 0; start = -1; } ++index; } return -1; } #endregion Consecutive items #region Find and SearchForSubsequence /// /// Finds the first item in a collection that satisfies the condition /// defined by . /// /// If the default value for T could be present in the collection, and /// would be matched by the predicate, then this method is inappropriate, because /// you cannot disguish whether the default value for T was actually present in the collection, /// or no items matched the predicate. In this case, use TryFindFirstWhere. /// The collection to search. /// A delegate that defined the condition to check for. /// The first item in the collection that matches the condition, or the default value for T (0 or null) if no /// item that matches the condition is found. /// public static T FindFirstWhere(IEnumerable collection, Predicate predicate) { T retval; if (Algorithms.TryFindFirstWhere(collection, predicate, out retval)) return retval; else return default(T); } /// /// Finds the first item in a collection that satisfies the condition /// defined by . /// /// The collection to search. /// A delegate that defined the condition to check for. /// Outputs the first item in the collection that matches the condition, if the method returns true. /// True if an item satisfying the condition was found. False if no such item exists in the collection. /// public static bool TryFindFirstWhere(IEnumerable collection, Predicate predicate, out T foundItem) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); foreach (T item in collection) { if (predicate(item)) { foundItem = item; return true; } } // didn't find any item that matches. foundItem = default(T); return false; } /// /// Finds the last item in a collection that satisfies the condition /// defined by . /// /// If the collection implements IList<T>, then the list is scanned in reverse until a /// matching item is found. Otherwise, the entire collection is iterated in the forward direction. /// If the default value for T could be present in the collection, and /// would be matched by the predicate, then this method is inappropriate, because /// you cannot disguish whether the default value for T was actually present in the collection, /// or no items matched the predicate. In this case, use TryFindFirstWhere. /// The collection to search. /// A delegate that defined the condition to check for. /// The last item in the collection that matches the condition, or the default value for T (0 or null) if no /// item that matches the condition is found. /// public static T FindLastWhere(IEnumerable collection, Predicate predicate) { T retval; if (Algorithms.TryFindLastWhere(collection, predicate, out retval)) return retval; else return default(T); } /// /// Finds the last item in a collection that satisfies the condition /// defined by . /// /// If the collection implements IList<T>, then the list is scanned in reverse until a /// matching item is found. Otherwise, the entire collection is iterated in the forward direction. /// The collection to search. /// A delegate that defined the condition to check for. /// Outputs the last item in the collection that matches the condition, if the method returns true. /// True if an item satisfying the condition was found. False if no such item exists in the collection. /// public static bool TryFindLastWhere(IEnumerable collection, Predicate predicate, out T foundItem) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); IList list = collection as IList; if (list != null) { // If it's a list, we can iterate in reverse. for (int index = list.Count - 1; index >= 0; --index) { T item = list[index]; if (predicate(item)) { foundItem = item; return true; } } // didn't find any item that matches. foundItem = default(T); return false; } else { // Otherwise, iterate the whole thing and remember the last matching one. bool found = false; foundItem = default(T); foreach (T item in collection) { if (predicate(item)) { foundItem = item; found = true; } } return found; } } /// /// Enumerates all the items in that satisfy the condition defined /// by . /// /// The collection to check all the items in. /// A delegate that defines the condition to check for. /// An IEnumerable<T> that enumerates the items that satisfy the condition. public static IEnumerable FindWhere(IEnumerable collection, Predicate predicate) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); foreach (T item in collection) { if (predicate(item)) { yield return item; } } } /// /// Finds the index of the first item in a list that satisfies the condition /// defined by . /// /// The list to search. /// A delegate that defined the condition to check for. /// The index of the first item satisfying the condition. -1 if no such item exists in the list. public static int FindFirstIndexWhere(IList list, Predicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); int index = 0; foreach (T item in list) { if (predicate(item)) { return index; } ++index; } // didn't find any item that matches. return -1; } /// /// Finds the index of the last item in a list that satisfies the condition /// defined by . /// /// The list to search. /// A delegate that defined the condition to check for. /// The index of the last item satisfying the condition. -1 if no such item exists in the list. public static int FindLastIndexWhere(IList list, Predicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); for (int index = list.Count - 1; index >= 0; --index) { if (predicate(list[index])) { return index; } } // didn't find any item that matches. return -1; } /// /// Enumerates the indices of all the items in that satisfy the condition defined /// by . /// /// The list to check all the items in. /// A delegate that defines the condition to check for. /// An IEnumerable<T> that enumerates the indices of items that satisfy the condition. public static IEnumerable FindIndicesWhere(IList list, Predicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); int index = 0; foreach (T item in list) { if (predicate(item)) { yield return index; } ++index; } } /// /// Finds the index of the first item in a list equal to a given item. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The list to search. /// The item to search for. /// The index of the first item equal to . -1 if no such item exists in the list. public static int FirstIndexOf(IList list, T item) { return FirstIndexOf(list, item, EqualityComparer.Default); } /// /// Finds the index of the first item in a list equal to a given item. A passed /// IEqualityComparer is used to determine equality. /// /// The list to search. /// The item to search for. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. /// The index of the first item equal to . -1 if no such item exists in the list. public static int FirstIndexOf(IList list, T item, IEqualityComparer equalityComparer) { if (list == null) throw new ArgumentNullException("list"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); int index = 0; foreach (T x in list) { if (equalityComparer.Equals(x, item)) { return index; } ++index; } // didn't find any item that matches. return -1; } /// /// Finds the index of the last item in a list equal to a given item. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The list to search. /// The item to search for. /// The index of the last item equal to . -1 if no such item exists in the list. public static int LastIndexOf(IList list, T item) { return LastIndexOf(list, item, EqualityComparer.Default); } /// /// Finds the index of the last item in a list equal to a given item. A passed /// IEqualityComparer is used to determine equality. /// /// The list to search. /// The item to search for. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. /// The index of the last item equal to . -1 if no such item exists in the list. public static int LastIndexOf(IList list, T item, IEqualityComparer equalityComparer) { if (list == null) throw new ArgumentNullException("list"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); for (int index = list.Count - 1; index >= 0; --index) { if (equalityComparer.Equals(list[index], item)) { return index; } } // didn't find any item that matches. return -1; } /// /// Enumerates the indices of all the items in a list equal to a given item. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The list to search. /// The item to search for. /// An IEnumerable<T> that enumerates the indices of items equal to . public static IEnumerable IndicesOf(IList list, T item) { return IndicesOf(list, item, EqualityComparer.Default); } /// /// Enumerates the indices of all the items in a list equal to a given item. A passed /// IEqualityComparer is used to determine equality. /// /// The list to search. /// The item to search for. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. /// An IEnumerable<T> that enumerates the indices of items equal to . public static IEnumerable IndicesOf(IList list, T item, IEqualityComparer equalityComparer) { if (list == null) throw new ArgumentNullException("list"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); int index = 0; foreach (T x in list) { if (equalityComparer.Equals(x, item)) { yield return index; } ++index; } } /// /// Finds the index of the first item in a list equal to one of several given items. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The list to search. /// The items to search for. /// The index of the first item equal to any of the items in the collection . /// -1 if no such item exists in the list. public static int FirstIndexOfMany(IList list, IEnumerable itemsToLookFor) { return FirstIndexOfMany(list, itemsToLookFor, EqualityComparer.Default); } /// /// Finds the index of the first item in a list equal to one of several given items. A passed /// IEqualityComparer is used to determine equality. /// /// The list to search. /// The items to search for. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode methods will be called. /// The index of the first item equal to any of the items in the collection . /// -1 if no such item exists in the list. public static int FirstIndexOfMany(IList list, IEnumerable itemsToLookFor, IEqualityComparer equalityComparer) { if (list == null) throw new ArgumentNullException("list"); if (itemsToLookFor == null) throw new ArgumentNullException("itemsToLookFor"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); // Create a set of the items we are looking for, for efficient lookup. Set setToLookFor = new Set(itemsToLookFor, equalityComparer); // Scan the list for the items. int index = 0; foreach (T x in list) { if (setToLookFor.Contains(x)) { return index; } ++index; } // didn't find any item that matches. return -1; } /// /// Finds the index of the first item in a list "equal" to one of several given items. The passed /// BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being removed need not be true equality. This methods finds /// first item X which satisfies BinaryPredicate(X,Y), where Y is one of the items in /// The list to search. /// The items to search for. /// The BinaryPredicate used to compare items for "equality". /// The index of the first item "equal" to any of the items in the collection , using /// as the test for equality. /// -1 if no such item exists in the list. public static int FirstIndexOfMany(IList list, IEnumerable itemsToLookFor, BinaryPredicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (itemsToLookFor == null) throw new ArgumentNullException("itemsToLookFor"); if (predicate == null) throw new ArgumentNullException("predicate"); // Scan the list for the items. int index = 0; foreach (T x in list) { foreach (T y in itemsToLookFor) { if (predicate(x, y)) { return index; } } ++index; } // didn't find any item that matches. return -1; } /// /// Finds the index of the last item in a list equal to one of several given items. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The list to search. /// The items to search for. /// The index of the last item equal to any of the items in the collection . /// -1 if no such item exists in the list. public static int LastIndexOfMany(IList list, IEnumerable itemsToLookFor) { return LastIndexOfMany(list, itemsToLookFor, EqualityComparer.Default); } /// /// Finds the index of the last item in a list equal to one of several given items. A passed /// IEqualityComparer is used to determine equality. /// /// The list to search. /// The items to search for. /// The IEqualityComparer<T> used to compare items for equality. /// The index of the last item equal to any of the items in the collection . /// -1 if no such item exists in the list. public static int LastIndexOfMany(IList list, IEnumerable itemsToLookFor, IEqualityComparer equalityComparer) { if (list == null) throw new ArgumentNullException("list"); if (itemsToLookFor == null) throw new ArgumentNullException("itemsToLookFor"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); // Create a set of the items we are looking for, for efficient lookup. Set setToLookFor = new Set(itemsToLookFor, equalityComparer); // Scan the list for (int index = list.Count - 1; index >= 0; --index) { if (setToLookFor.Contains(list[index])) { return index; } } // didn't find any item that matches. return -1; } /// /// Finds the index of the last item in a list "equal" to one of several given items. The passed /// BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being removed need not be true equality. This methods finds /// last item X which satisfies BinaryPredicate(X,Y), where Y is one of the items in /// The list to search. /// The items to search for. /// The BinaryPredicate used to compare items for "equality". /// The index of the last item "equal" to any of the items in the collection , using /// as the test for equality. /// -1 if no such item exists in the list. public static int LastIndexOfMany(IList list, IEnumerable itemsToLookFor, BinaryPredicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (itemsToLookFor == null) throw new ArgumentNullException("itemsToLookFor"); if (predicate == null) throw new ArgumentNullException("predicate"); // Scan the list for (int index = list.Count - 1; index >= 0; --index) { foreach (T y in itemsToLookFor) { if (predicate(list[index], y)) { return index; } } } // didn't find any item that matches. return -1; } /// /// Enumerates the indices of all the items in a list equal to one of several given items. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The list to search. /// A collection of items to search for. /// An IEnumerable<T> that enumerates the indices of items equal to /// any of the items in the collection . public static IEnumerable IndicesOfMany(IList list, IEnumerable itemsToLookFor) { return IndicesOfMany(list, itemsToLookFor, EqualityComparer.Default); } /// /// Enumerates the indices of all the items in a list equal to one of several given items. A passed /// IEqualityComparer is used to determine equality. /// /// The list to search. /// A collection of items to search for. /// The IEqualityComparer<T> used to compare items for equality. /// An IEnumerable<T> that enumerates the indices of items equal to /// any of the items in the collection . public static IEnumerable IndicesOfMany(IList list, IEnumerable itemsToLookFor, IEqualityComparer equalityComparer) { if (list == null) throw new ArgumentNullException("list"); if (itemsToLookFor == null) throw new ArgumentNullException("itemsToLookFor"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); // Create a set of the items we are looking for, for efficient lookup. Set setToLookFor = new Set(itemsToLookFor, equalityComparer); // Scan the list int index = 0; foreach (T x in list) { if (setToLookFor.Contains(x)) { yield return index; } ++index; } } /// /// Enumerates the indices of all the items in a list equal to one of several given items. The passed /// BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being removed need not be true equality. This methods finds /// last item X which satisfies BinaryPredicate(X,Y), where Y is one of the items in /// The list to search. /// A collection of items to search for. /// The BinaryPredicate used to compare items for "equality". /// An IEnumerable<T> that enumerates the indices of items "equal" to any of the items /// in the collection , using /// as the test for equality. public static IEnumerable IndicesOfMany(IList list, IEnumerable itemsToLookFor, BinaryPredicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (itemsToLookFor == null) throw new ArgumentNullException("itemsToLookFor"); if (predicate == null) throw new ArgumentNullException("predicate"); // Scan the list for the items. int index = 0; foreach (T x in list) { foreach (T y in itemsToLookFor) { if (predicate(x, y)) { yield return index; } } ++index; } } /// /// Searchs a list for a sub-sequence of items that match a particular pattern. A subsequence /// of matches pattern at index i if list[i] is equal to the first item /// in , list[i+1] is equal to the second item in , /// and so forth for all the items in . /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The type of items in the list. /// The list to search. /// The sequence of items to search for. /// The first index with that matches the items in . public static int SearchForSubsequence(IList list, IEnumerable pattern) { return SearchForSubsequence(list, pattern, EqualityComparer.Default); } /// /// Searchs a list for a sub-sequence of items that match a particular pattern. A subsequence /// of matches pattern at index i if list[i] is "equal" to the first item /// in , list[i+1] is "equal" to the second item in , /// and so forth for all the items in . The passed /// BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being tested /// for in the pattern need not be equality. /// The type of items in the list. /// The list to search. /// The sequence of items to search for. /// The BinaryPredicate used to compare items for "equality". /// The first index with that matches the items in . public static int SearchForSubsequence(IList list, IEnumerable pattern, BinaryPredicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (pattern == null) throw new ArgumentNullException("pattern"); if (predicate == null) throw new ArgumentNullException("predicate"); // Put the pattern into an array for performance (don't keep allocating enumerators). T[] patternArray = Algorithms.ToArray(pattern); int listCount = list.Count, patternCount = patternArray.Length; if (patternCount == 0) return 0; // A zero-length pattern occurs anywhere. if (listCount == 0) return -1; // no room for a pattern; for (int start = 0; start <= listCount - patternCount; ++start) { for (int count = 0; count < patternCount; ++count) { if (!predicate(list[start + count], patternArray[count])) goto NOMATCH; } // Got through the whole pattern. We have a match. return start; NOMATCH: /* no match found at start. */ ; } // no match found anywhere. return -1; } /// /// Searchs a list for a sub-sequence of items that match a particular pattern. A subsequence /// of matches pattern at index i if list[i] is equal to the first item /// in , list[i+1] is equal to the second item in , /// and so forth for all the items in . The passed /// instance of IEqualityComparer<T> is used for determining if two items are equal. /// /// The type of items in the list. /// The list to search. /// The sequence of items to search for. /// The IEqualityComparer<T> used to compare items for equality. Only the Equals method will be called. /// The first index with that matches the items in . public static int SearchForSubsequence(IList list, IEnumerable pattern, IEqualityComparer equalityComparer) { if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); return SearchForSubsequence(list, pattern, equalityComparer.Equals); } #endregion Find and SearchForSubsequence #region Set operations (coded except EqualSets) /// /// Determines if one collection is a subset of another, considered as sets. The first set is a subset /// of the second set if every item in the first set also occurs in the second set. If an item appears X times in the first set, /// it must appear at least X times in the second set. /// /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the IsSubsetOf method on that class. /// /// The first collection. /// The second collection. /// True if is a subset of , considered as sets. /// or is null. public static bool IsSubsetOf(IEnumerable collection1, IEnumerable collection2) { return IsSubsetOf(collection1, collection2, EqualityComparer.Default); } /// /// Determines if one collection is a subset of another, considered as sets. The first set is a subset /// of the second set if every item in the first set also occurs in the second set. If an item appears X times in the first set, /// it must appear at least X times in the second set. /// /// /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the IsSubsetOf method on that class. /// /// The first collection. /// The second collection. /// The IEqualityComparer<T> used to compare items for equality. /// True if is a subset of , considered as sets. /// or is null. public static bool IsSubsetOf(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Bag bag1 = new Bag(collection1, equalityComparer); Bag bag2 = new Bag(collection2, equalityComparer); return bag2.IsSupersetOf(bag1); } /// /// Determines if one collection is a proper subset of another, considered as sets. The first set is a proper subset /// of the second set if every item in the first set also occurs in the second set, and the first set is strictly smaller than /// the second set. If an item appears X times in the first set, /// it must appear at least X times in the second set. /// /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the IsSubsetOf method on that class. /// /// The first collection. /// The second collection. /// True if is a subset of , considered as sets. /// or is null. public static bool IsProperSubsetOf(IEnumerable collection1, IEnumerable collection2) { return IsProperSubsetOf(collection1, collection2, EqualityComparer.Default); } /// /// Determines if one collection is a proper subset of another, considered as sets. The first set is a proper subset /// of the second set if every item in the first set also occurs in the second set, and the first set is strictly smaller than /// the second set. If an item appears X times in the first set, /// it must appear at least X times in the second set. /// /// /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the IsSubsetOf method on that class. /// /// The first collection. /// The second collection. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode member functions of this interface are called. /// True if is a proper subset of , considered as sets. /// or is null. public static bool IsProperSubsetOf(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Bag bag1 = new Bag(collection1, equalityComparer); Bag bag2 = new Bag(collection2, equalityComparer); return bag2.IsProperSupersetOf(bag1); } /// /// Determines if two collections are disjoint, considered as sets. Two sets are disjoint if they /// have no common items. /// /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the IsDisjoint method on that class. /// /// The first collection. /// The second collection. /// True if are are disjoint, considered as sets. /// or is null. public static bool DisjointSets(IEnumerable collection1, IEnumerable collection2) { return DisjointSets(collection1, collection2, EqualityComparer.Default); } /// /// Determines if two collections are disjoint, considered as sets. Two sets are disjoint if they /// have no common items. /// /// /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the IsDisjoint method on that class. /// /// The first collection. /// The second collection. /// The IEqualityComparerComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode member functions of this interface are called. /// True if are are disjoint, considered as sets. /// or is null. public static bool DisjointSets(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Set set1 = new Set(collection1, equalityComparer); foreach (T item2 in collection2) { if (set1.Contains(item2)) return false; } return true; } /// /// Determines if two collections are equal, considered as sets. Two sets are equal if they /// have have the same items, with order not being significant. /// /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the EqualTo method on that class. /// /// The first collection. /// The second collection. /// True if are are equal, considered as sets. /// or is null. public static bool EqualSets(IEnumerable collection1, IEnumerable collection2) { return EqualSets(collection1, collection2, EqualityComparer.Default); } /// /// Determines if two collections are equal, considered as sets. Two sets are equal if they /// have have the same items, with order not being significant. /// /// /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the EqualTo method on that class. /// /// The first collection. /// The second collection. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode member functions of this interface are called. /// True if are are equal, considered as sets. /// or is null. public static bool EqualSets(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Bag bag1 = new Bag(collection1, equalityComparer); Bag bag2 = new Bag(collection2, equalityComparer); return bag2.IsEqualTo(bag1); } /// /// Computes the set-theoretic intersection of two collections. The intersection of two sets /// is all items that appear in both of the sets. If an item appears X times in one set, /// and Y times in the other set, the intersection contains the item Minimum(X,Y) times. /// The source collections are not changed. /// A new collection is created with the intersection of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the Intersection or IntersectionWith methods on that class. /// /// The first collection to intersect. /// The second collection to intersect. /// The intersection of the two collections, considered as sets. /// or is null. public static IEnumerable SetIntersection(IEnumerable collection1, IEnumerable collection2) { return SetIntersection(collection1, collection2, EqualityComparer.Default); } /// /// Computes the set-theoretic intersection of two collections. The intersection of two sets /// is all items that appear in both of the sets. If an item appears X times in one set, /// and Y times in the other set, the intersection contains the item Minimum(X,Y) times. /// The source collections are not changed. /// A new collection is created with the intersection of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the Intersection or IntersectionWith methods on that class. /// /// The first collection to intersect. /// The second collection to intersect. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode member functions of this interface are called. /// The intersection of the two collections, considered as sets. /// or is null. public static IEnumerable SetIntersection(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Bag bag1 = new Bag(collection1, equalityComparer); Bag bag2 = new Bag(collection2, equalityComparer); return Util.CreateEnumerableWrapper(bag1.Intersection(bag2)); } /// /// Computes the set-theoretic union of two collections. The union of two sets /// is all items that appear in either of the sets. If an item appears X times in one set, /// and Y times in the other set, the union contains the item Maximum(X,Y) times. /// The source collections are not changed. /// A new collection is created with the union of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the Union or UnionWith methods on that class. /// /// The first collection to union. /// The second collection to union. /// The union of the two collections, considered as sets. /// or is null. public static IEnumerable SetUnion(IEnumerable collection1, IEnumerable collection2) { return SetUnion(collection1, collection2, EqualityComparer.Default); } /// /// Computes the set-theoretic union of two collections. The union of two sets /// is all items that appear in either of the sets. If an item appears X times in one set, /// and Y times in the other set, the union contains the item Maximum(X,Y) times. /// The source collections are not changed. /// A new collection is created with the union of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the union or unionWith methods on that class. /// /// The first collection to union. /// The second collection to union. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode member functions of this interface are called. /// The union of the two collections, considered as sets. /// or is null. public static IEnumerable SetUnion(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Bag bag1 = new Bag(collection1, equalityComparer); Bag bag2 = new Bag(collection2, equalityComparer); if (bag1.Count > bag2.Count) { bag1.UnionWith(bag2); return Util.CreateEnumerableWrapper(bag1); } else { bag2.UnionWith(bag1); return Util.CreateEnumerableWrapper(bag2); } } /// /// Computes the set-theoretic difference of two collections. The difference of two sets /// is all items that appear in the first set, but not in the second. If an item appears X times in the first set, /// and Y times in the second set, the difference contains the item X - Y times (0 times if X < Y). /// The source collections are not changed. /// A new collection is created with the difference of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the Difference or DifferenceWith methods on that class. /// /// The first collection to difference. /// The second collection to difference. /// The difference of and , considered as sets. /// or is null. public static IEnumerable SetDifference(IEnumerable collection1, IEnumerable collection2) { return SetDifference(collection1, collection2, EqualityComparer.Default); } /// /// Computes the set-theoretic difference of two collections. The difference of two sets /// is all items that appear in the first set, but not in the second. If an item appears X times in the first set, /// and Y times in the second set, the difference contains the item X - Y times (0 times if X < Y). /// The source collections are not changed. /// A new collection is created with the difference of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the difference or differenceWith methods on that class. /// /// The first collection to difference. /// The second collection to difference. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode member functions of this interface are called. /// The difference of and , considered as sets. /// or is null. public static IEnumerable SetDifference(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Bag bag1 = new Bag(collection1, equalityComparer); Bag bag2 = new Bag(collection2, equalityComparer); bag1.DifferenceWith(bag2); return Util.CreateEnumerableWrapper(bag1); } /// /// Computes the set-theoretic symmetric difference of two collections. The symmetric difference of two sets /// is all items that appear in the one of the sets, but not in the other. If an item appears X times in the one set, /// and Y times in the other set, the symmetric difference contains the item AbsoluteValue(X - Y) times. /// The source collections are not changed. /// A new collection is created with the symmetric difference of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the SymmetricDifference or SymmetricDifferenceWith methods on that class. /// /// The first collection to symmetric difference. /// The second collection to symmetric difference. /// The symmetric difference of and , considered as sets. /// or is null. public static IEnumerable SetSymmetricDifference(IEnumerable collection1, IEnumerable collection2) { return SetSymmetricDifference(collection1, collection2, EqualityComparer.Default); } /// /// Computes the set-theoretic symmetric difference of two collections. The symmetric difference of two sets /// is all items that appear in the one of the sets, but not in the other. If an item appears X times in the one set, /// and Y times in the other set, the symmetric difference contains the item AbsoluteValue(X - Y) times. /// The source collections are not changed. /// A new collection is created with the symmetric difference of the collections; the order of the /// items in this collection is undefined. /// /// /// When equal items appear in both collections, the returned collection will include an arbitrary choice of one of the /// two equal items. /// If both collections are Set, Bag, OrderedSet, or OrderedBag /// collections, it is more efficient to use the symmetric difference or symmetric differenceWith methods on that class. /// /// The first collection to symmetric difference. /// The second collection to symmetric difference. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals and GetHashCode member functions of this interface are called. /// The symmetric difference of and , considered as sets. /// or is null. public static IEnumerable SetSymmetricDifference(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentException("equalityComparer"); Bag bag1 = new Bag(collection1, equalityComparer); Bag bag2 = new Bag(collection2, equalityComparer); if (bag1.Count > bag2.Count) { bag1.SymmetricDifferenceWith(bag2); return Util.CreateEnumerableWrapper(bag1); } else { bag2.SymmetricDifferenceWith(bag1); return Util.CreateEnumerableWrapper(bag2); } } /// /// Computes the cartestian product of two collections: all possible pairs of items, with the first item taken from the first collection and /// the second item taken from the second collection. If the first collection has N items, and the second collection has M items, the cartesian /// product will have N * M pairs. /// /// The type of items in the first collection. /// The type of items in the second collection. /// The first collection. /// The second collection. /// An IEnumerable<Pair<TFirst, TSecond>> that enumerates the cartesian product of the two collections. public static IEnumerable> CartesianProduct(IEnumerable first, IEnumerable second) { if (first == null) throw new ArgumentNullException("first"); if (second == null) throw new ArgumentNullException("second"); foreach (TFirst itemFirst in first) foreach (TSecond itemSecond in second) yield return new Pair(itemFirst, itemSecond); } #endregion Set operations #region String representations (not yet coded) /// /// Gets a string representation of the elements in the collection. /// The string representation starts with "{", has a list of items separated /// by commas (","), and ends with "}". Each item in the collection is /// converted to a string by calling its ToString method (null is represented by "null"). /// Contained collections (except strings) are recursively converted to strings by this method. /// /// A collection to get the string representation of. /// The string representation of the collection. If is null, then the string "null" is returned. public static string ToString(IEnumerable collection) { return ToString(collection, true, "{", ",", "}"); } /// /// Gets a string representation of the elements in the collection. /// The string to used at the beginning and end, and to separate items, /// and supplied by parameters. Each item in the collection is /// converted to a string by calling its ToString method (null is represented by "null"). /// /// A collection to get the string representation of. /// If true, contained collections (except strings) are converted to strings by a recursive call to this method, instead /// of by calling ToString. /// The string to appear at the beginning of the output string. /// The string to appear between each item in the string. /// The string to appear at the end of the output string. /// The string representation of the collection. If is null, then the string "null" is returned. /// , , or /// is null. public static string ToString(IEnumerable collection, bool recursive, string start, string separator, string end) { if (start == null) throw new ArgumentNullException("start"); if (separator == null) throw new ArgumentNullException("separator"); if (end == null) throw new ArgumentNullException("end"); if (collection == null) return "null"; bool firstItem = true; System.Text.StringBuilder builder = new System.Text.StringBuilder(); builder.Append(start); // Call ToString on each item and put it in. foreach (T item in collection) { if (!firstItem) builder.Append(separator); if (item == null) builder.Append("null"); else if (recursive && item is IEnumerable && !(item is string)) builder.Append(Algorithms.ToString(Algorithms.TypedAs((IEnumerable)item), recursive, start, separator, end)); else builder.Append(item.ToString()); firstItem = false; } builder.Append(end); return builder.ToString(); } /// /// Gets a string representation of the mappings in a dictionary. /// The string representation starts with "{", has a list of mappings separated /// by commas (", "), and ends with "}". Each mapping is represented /// by "key->value". Each key and value in the dictionary is /// converted to a string by calling its ToString method (null is represented by "null"). /// Contained collections (except strings) are recursively converted to strings by this method. /// /// A dictionary to get the string representation of. /// The string representation of the collection, or "null" /// if is null. public static string ToString(IDictionary dictionary) { bool firstItem = true; if (dictionary == null) return "null"; System.Text.StringBuilder builder = new System.Text.StringBuilder(); builder.Append("{"); // Call ToString on each item and put it in. foreach (KeyValuePair pair in dictionary) { if (!firstItem) builder.Append(", "); if (pair.Key == null) builder.Append("null"); else if (pair.Key is IEnumerable && !(pair.Key is string)) builder.Append(Algorithms.ToString(Algorithms.TypedAs((IEnumerable)pair.Key), true, "{", ",", "}")); else builder.Append(pair.Key.ToString()); builder.Append("->"); if (pair.Value == null) builder.Append("null"); else if (pair.Value is IEnumerable && !(pair.Value is string)) builder.Append(Algorithms.ToString(Algorithms.TypedAs((IEnumerable)pair.Value), true, "{", ",", "}")); else builder.Append(pair.Value.ToString()); firstItem = false; } builder.Append("}"); return builder.ToString(); } #endregion String representations #region Shuffles and Permutations private static volatile Random myRandomGenerator; /// /// Return a private random number generator to use if the user /// doesn't supply one. /// /// The private random number generator. Only one is ever created /// and is always returned. private static Random GetRandomGenerator() { if (myRandomGenerator == null) { lock (typeof(Algorithms)) { if (myRandomGenerator == null) myRandomGenerator = new Random(); } } return myRandomGenerator; } /// /// Randomly shuffles the items in a collection, yielding a new collection. /// /// The type of the items in the collection. /// The collection to shuffle. /// An array with the same size and items as , but the items in a randomly chosen order. public static T[] RandomShuffle(IEnumerable collection) { return RandomShuffle(collection, GetRandomGenerator()); } /// /// Randomly shuffles the items in a collection, yielding a new collection. /// /// The type of the items in the collection. /// The collection to shuffle. /// The random number generator to use to select the random order. /// An array with the same size and items as , but the items in a randomly chosen order. public static T[] RandomShuffle(IEnumerable collection, Random randomGenerator) { // We have to copy all items anyway, and there isn't a way to produce the items // on the fly that is linear. So copying to an array and shuffling it is an efficient as we can get. if (collection == null) throw new ArgumentNullException("collection"); if (randomGenerator == null) throw new ArgumentNullException("randomGenerator"); T[] array = Algorithms.ToArray(collection); int count = array.Length; for (int i = count - 1; i >= 1; --i) { // Pick an random number 0 through i inclusive. int j = randomGenerator.Next(i + 1); // Swap array[i] and array[j] T temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } /// /// Randomly shuffles the items in a list or array, in place. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to shuffle. public static void RandomShuffleInPlace(IList list) { RandomShuffleInPlace(list, GetRandomGenerator()); } /// /// Randomly shuffles the items in a list or array, in place. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to shuffle. /// The random number generator to use to select the random order. public static void RandomShuffleInPlace(IList list, Random randomGenerator) { if (list == null) throw new ArgumentNullException("list"); if (randomGenerator == null) throw new ArgumentNullException("randomGenerator"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); int count = list.Count; for (int i = count - 1; i >= 1; --i) { // Pick an random number 0 through i inclusive. int j = randomGenerator.Next(i + 1); // Swap list[i] and list[j] T temp = list[i]; list[i] = list[j]; list[j] = temp; } } /// /// Picks a random subset of items from , and places /// those items into a random order. No item is selected more than once. /// /// If the collection implements IList<T>, then this method takes time O(). /// Otherwise, this method takes time O(N), where N is the number of items in the collection. /// The type of items in the collection. /// The collection of items to select from. This collection is not changed. /// The number of items in the subset to choose. /// An array of items, selected at random from . /// is negative or greater than .Count. public static T[] RandomSubset(IEnumerable collection, int count) { return RandomSubset(collection, count, GetRandomGenerator()); } /// /// Picks a random subset of items from , and places /// those items into a random order. No item is selected more than once. /// /// If the collection implements IList<T>, then this method takes time O(). /// Otherwise, this method takes time O(N), where N is the number of items in the collection. /// The type of items in the collection. /// The collection of items to select from. This collection is not changed. /// The number of items in the subset to choose. /// The random number generates used to make the selection. /// An array of items, selected at random from . /// is negative or greater than .Count. /// is null. public static T[] RandomSubset(IEnumerable collection, int count, Random randomGenerator) { if (collection == null) throw new ArgumentNullException("collection"); if (randomGenerator == null) throw new ArgumentNullException("randomGenerator"); // We need random access to the items in the collection. If it's not already an // IList, copy to a temporary list. IList list = collection as IList; if (list == null) { list = new List(collection); } int listCount = list.Count; if (count < 0 || count > listCount) throw new ArgumentOutOfRangeException("count"); T[] result = new T[count]; // the result array. Dictionary swappedValues = new Dictionary(count); // holds swapped values from the list. for (int i = 0; i < count; ++i) { // Set j to the index of the item to swap with, and value to the value to swap with. T value; int j = randomGenerator.Next(listCount - i) + i; // Swap values of i and j in the list. The list isn't actually changed; instead, // swapped values are stored in the dictionary swappedValues. if (!swappedValues.TryGetValue(j, out value)) value = list[j]; result[i] = value; if (i != j) { if (swappedValues.TryGetValue(i, out value)) swappedValues[j] = value; else swappedValues[j] = list[i]; } } return result; } /// /// Generates all the possible permutations of the items in . If /// has N items, then N factorial permutations will be generated. This method does not compare the items to determine if /// any of them are equal. If some items are equal, the same permutation may be generated more than once. For example, /// if the collections contains the three items A, A, and B, then this method will generate the six permutations, AAB, AAB, /// ABA, ABA, BAA, BAA (not necessarily in that order). To take equal items into account, use the GenerateSortedPermutations /// method. /// /// The type of items to permute. /// The collection of items to permute. /// An IEnumerable<T[]> that enumerations all the possible permutations of the /// items in . Each permutations is returned as an array. The items in the array /// should be copied if they need to be used after the next permutation is generated; each permutation may /// reuse the same array instance. public static IEnumerable GeneratePermutations(IEnumerable collection) { if (collection == null) throw new ArgumentNullException("collection"); T[] array = Algorithms.ToArray(collection); if (array.Length == 0) yield break; int[] state = new int[array.Length - 1]; int maxLength = state.Length; yield return array; if (array.Length == 1) yield break; // The following algorithm makes two swaps for each // permutation generated. // This is not optimal in terms of number of swaps, but // is still O(1), and shorter and clearer to understand. int i = 0; T temp; for (; ; ) { if (state[i] < i + 1) { if (state[i] > 0) { temp = array[i + 1]; array[i + 1] = array[state[i] - 1]; array[state[i] - 1] = temp; } temp = array[i + 1]; array[i + 1] = array[state[i]]; array[state[i]] = temp; yield return array; ++state[i]; i = 0; } else { temp = array[i + 1]; array[i + 1] = array[i]; array[i] = temp; state[i] = 0; ++i; if (i >= maxLength) yield break; } } } /// /// Generates all the possible permutations of the items in , in lexicographical order. /// Even if some items are equal, the same permutation will not be generated more than once. For example, /// if the collections contains the three items A, A, and B, then this method will generate only the three permutations, AAB, ABA, /// BAA. /// /// The type of items to permute. /// The collection of items to permute. /// An IEnumerable<T[]> that enumerations all the possible permutations of the /// items in . Each permutations is returned as an array. The items in the array /// should be copied if they need to be used after the next permutation is generated; each permutation may /// reuse the same array instance. public static IEnumerable GenerateSortedPermutations(IEnumerable collection) where T: IComparable { return GenerateSortedPermutations(collection, Comparer.Default); } /// /// Generates all the possible permutations of the items in , in lexicographical order. A /// supplied IComparer<T> instance is used to compare the items. /// Even if some items are equal, the same permutation will not be generated more than once. For example, /// if the collections contains the three items A, A, and B, then this method will generate only the three permutations, AAB, ABA, /// BAA. /// /// The type of items to permute. /// The collection of items to permute. /// The IComparer<T> used to compare the items. /// An IEnumerable<T[]> that enumerations all the possible permutations of the /// items in . Each permutations is returned as an array. The items in the array /// should be copied if they need to be used after the next permutation is generated; each permutation may /// reuse the same array instance. public static IEnumerable GenerateSortedPermutations(IEnumerable collection, IComparer comparer) { if (collection == null) throw new ArgumentNullException("collection"); if (comparer == null) throw new ArgumentNullException("comparer"); T[] array = Algorithms.ToArray(collection); int length = array.Length; if (length == 0) yield break; Array.Sort(array, comparer); yield return array; if (length == 1) yield break; // Keep generating the next permutation until we're done. Algorithm is // due to Jeffrey A. Johnson ("SEPA - a Simple Efficient Permutation Algorithm") int key, swap, i, j; T temp; for (; ; ) { // Find the key point -- where array[key]= 0) { --key; if (key < 0) yield break; } // Find the last item in the tail less than key. swap = length - 1; while (comparer.Compare(array[swap], array[key]) <= 0) --swap; // Swap it with the key. temp = array[key]; array[key] = array[swap]; array[swap] = temp; // Reverse the tail. i = key + 1; j = length - 1; while (i < j) { temp = array[i]; array[i] = array[j]; array[j] = temp; ++i; --j; } yield return array; } } /// /// Generates all the possible permutations of the items in , in lexicographical order. A /// supplied Comparison<T> delegate is used to compare the items. /// Even if some items are equal, the same permutation will not be generated more than once. For example, /// if the collections contains the three items A, A, and B, then this method will generate only the three permutations, AAB, ABA, /// BAA. /// /// The type of items to permute. /// The collection of items to permute. /// The Comparison<T> delegate used to compare the items. /// An IEnumerable<T[]> that enumerations all the possible permutations of the /// items in . Each permutations is returned as an array. The items in the array /// should be copied if they need to be used after the next permutation is generated; each permutation may /// reuse the same array instance. public static IEnumerable GenerateSortedPermutations(IEnumerable collection, Comparison comparison) { return GenerateSortedPermutations(collection, Comparers.ComparerFromComparison(comparison)); } #endregion Shuffles and Permutations #region Minimum and Maximum /// /// Finds the maximum value in a collection. /// /// Values in the collection are compared by using the IComparable<T> /// interfaces implementation on the type T. /// The type of items in the collection. /// The collection to search. /// The largest item in the collection. /// The collection is empty. /// is null. public static T Maximum(IEnumerable collection) where T : IComparable { return Maximum(collection, Comparer.Default); } /// /// Finds the maximum value in a collection. A supplied IComparer<T> is used /// to compare the items in the collection. /// /// The type of items in the collection. /// The collection to search. /// The comparer instance used to compare items in the collection. /// The largest item in the collection. /// The collection is empty. /// or is null. public static T Maximum(IEnumerable collection, IComparer comparer) { if (collection == null) throw new ArgumentNullException("collection"); if (comparer == null) throw new ArgumentNullException("comparer"); T maxSoFar = default(T); bool foundOne = false; // Go through the collection, keeping the maximum found so far. foreach (T item in collection) { if (!foundOne || comparer.Compare(maxSoFar, item) < 0) { maxSoFar = item; } foundOne = true; } // If the collection was empty, throw an exception. if (!foundOne) throw new InvalidOperationException(Strings.CollectionIsEmpty); else return maxSoFar; } /// /// Finds the maximum value in a collection. A supplied Comparison<T> delegate is used /// to compare the items in the collection. /// /// The type of items in the collection. /// The collection to search. /// The comparison used to compare items in the collection. /// The largest item in the collection. /// The collection is empty. /// or is null. public static T Maximum(IEnumerable collection, Comparison comparison) { return Maximum(collection, Comparers.ComparerFromComparison(comparison)); } /// /// Finds the minimum value in a collection. /// /// Values in the collection are compared by using the IComparable<T> /// interfaces implementation on the type T. /// The type of items in the collection. /// The collection to search. /// The smallest item in the collection. /// The collection is empty. /// is null. public static T Minimum(IEnumerable collection) where T : IComparable { return Minimum(collection, Comparer.Default); } /// /// Finds the minimum value in a collection. A supplied IComparer<T> is used /// to compare the items in the collection. /// /// The type of items in the collection. /// The collection to search. /// The comparer instance used to compare items in the collection. /// The smallest item in the collection. /// The collection is empty. /// or is null. public static T Minimum(IEnumerable collection, IComparer comparer) { if (collection == null) throw new ArgumentNullException("collection"); if (comparer == null) throw new ArgumentNullException("comparer"); T minSoFar = default(T); bool foundOne = false; // Go through the collection, keeping the minimum found so far. foreach (T item in collection) { if (!foundOne || comparer.Compare(minSoFar, item) > 0) { minSoFar = item; } foundOne = true; } // If the collection was empty, throw an exception. if (!foundOne) throw new InvalidOperationException(Strings.CollectionIsEmpty); else return minSoFar; } /// /// Finds the minimum value in a collection. A supplied Comparison<T> delegate is used /// to compare the items in the collection. /// /// The type of items in the collection. /// The collection to search. /// The comparison used to compare items in the collection. /// The smallest item in the collection. /// The collection is empty. /// or is null. public static T Minimum(IEnumerable collection, Comparison comparison) { return Minimum(collection, Comparers.ComparerFromComparison(comparison)); } /// /// Finds the index of the maximum value in a list. /// /// Values in the list are compared by using the IComparable<T> /// interfaces implementation on the type T. /// The type of items in the list. /// The list to search. /// The index of the largest item in the list. If the maximum value appears /// multiple times, the index of the first appearance is used. If the list is empty, -1 is returned. /// is null. public static int IndexOfMaximum(IList list) where T : IComparable { return IndexOfMaximum(list, Comparer.Default); } /// /// Finds the index of the maximum value in a list. A supplied IComparer<T> is used /// to compare the items in the collection. /// /// The type of items in the list. /// The list to search. /// The comparer instance used to compare items in the collection. /// The index of the largest item in the list. If the maximum value appears /// multiple times, the index of the first appearance is used. If the list is empty, -1 is returned. /// or is null. public static int IndexOfMaximum(IList list, IComparer comparer) { if (list == null) throw new ArgumentNullException("list"); if (comparer == null) throw new ArgumentNullException("comparer"); T maxSoFar = default(T); int indexSoFar = -1; // Go through the collection, keeping the maximum found so far. int i = 0; foreach (T item in list) { if (indexSoFar < 0 || comparer.Compare(maxSoFar, item) < 0) { maxSoFar = item; indexSoFar = i; } ++i; } return indexSoFar; } /// /// Finds the index of the maximum value in a list. A supplied Comparison<T> delegate is used /// to compare the items in the collection. /// /// The type of items in the list. /// The list to search. /// The comparison used to compare items in the collection. /// The index of the largest item in the list. If the maximum value appears /// multiple times, the index of the first appearance is used. If the list is empty, -1 is returned. /// or is null. public static int IndexOfMaximum(IList list, Comparison comparison) { return IndexOfMaximum(list, Comparers.ComparerFromComparison(comparison)); } /// /// Finds the index of the minimum value in a list. /// /// Values in the list are compared by using the IComparable<T> /// interfaces implementation on the type T. /// The type of items in the list. /// The list to search. /// The index of the smallest item in the list. If the minimum value appears /// multiple times, the index of the first appearance is used. /// The collection is empty. /// is null. public static int IndexOfMinimum(IList list) where T : IComparable { return IndexOfMinimum(list, Comparer.Default); } /// /// Finds the index of the minimum value in a list. A supplied IComparer<T> is used /// to compare the items in the collection. /// /// The type of items in the list. /// The list to search. /// The comparer instance used to compare items in the collection. /// The index of the smallest item in the list. If the minimum value appears /// multiple times, the index of the first appearance is used. /// The collection is empty. /// or is null. public static int IndexOfMinimum(IList list, IComparer comparer) { if (list == null) throw new ArgumentNullException("list"); if (comparer == null) throw new ArgumentNullException("comparer"); T minSoFar = default(T); int indexSoFar = -1; // Go through the collection, keeping the minimum found so far. int i = 0; foreach (T item in list) { if (indexSoFar < 0 || comparer.Compare(minSoFar, item) > 0) { minSoFar = item; indexSoFar = i; } ++i; } return indexSoFar; } /// /// Finds the index of the minimum value in a list. A supplied Comparison<T> delegate is used /// to compare the items in the collection. /// /// The type of items in the list. /// The list to search. /// The comparison delegate used to compare items in the collection. /// The index of the smallest item in the list. If the minimum value appears /// multiple times, the index of the first appearance is used. /// The collection is empty. /// or is null. public static int IndexOfMinimum(IList list, Comparison comparison) { return IndexOfMinimum(list, Comparers.ComparerFromComparison(comparison)); } #endregion Minimum and Maximum #region Sorting and operations on sorted collections /// /// Creates a sorted version of a collection. /// /// Values are compared by using the IComparable<T> /// interfaces implementation on the type T. /// The collection to sort. /// An array containing the sorted version of the collection. public static T[] Sort(IEnumerable collection) where T : IComparable { return Sort(collection, Comparer.Default); } /// /// Creates a sorted version of a collection. A supplied IComparer<T> is used /// to compare the items in the collection. /// /// The collection to sort. /// The comparer instance used to compare items in the collection. Only /// the Compare method is used. /// An array containing the sorted version of the collection. public static T[] Sort(IEnumerable collection, IComparer comparer) { T[] array; if (collection == null) throw new ArgumentNullException("collection"); if (comparer == null) throw new ArgumentNullException("comparer"); array = Algorithms.ToArray(collection); Array.Sort(array, comparer); return array; } /// /// Creates a sorted version of a collection. A supplied Comparison<T> delegate is used /// to compare the items in the collection. /// /// The collection to sort. /// The comparison delegate used to compare items in the collection. /// An array containing the sorted version of the collection. public static T[] Sort(IEnumerable collection, Comparison comparison) { return Sort(collection, Comparers.ComparerFromComparison(comparison)); } /// /// Sorts a list or array in place. /// /// The Quicksort algorithms is used to sort the items. In virtually all cases, /// this takes time O(N log N), where N is the number of items in the list. /// Values are compared by using the IComparable<T> /// interfaces implementation on the type T. /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to sort. public static void SortInPlace(IList list) where T : IComparable { SortInPlace(list, Comparer.Default); } /// /// Sorts a list or array in place. A supplied IComparer<T> is used /// to compare the items in the list. /// /// The Quicksort algorithms is used to sort the items. In virtually all cases, /// this takes time O(N log N), where N is the number of items in the list. /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to sort. /// The comparer instance used to compare items in the collection. Only /// the Compare method is used. public static void SortInPlace(IList list, IComparer comparer) { if (list == null) throw new ArgumentNullException("list"); if (comparer == null) throw new ArgumentNullException("comparer"); // If we have an array, use the built-in array sort (faster than going through IList accessors // with virtual calls). if (list is T[]) { Array.Sort((T[])list, comparer); return; } if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); // Instead of a recursive procedure, we use an explicit stack to hold // ranges that we still need to sort. int[] leftStack = new int[32], rightStack = new int[32]; int stackPtr = 0; int l = 0; // the inclusive left edge of the current range we are sorting. int r = list.Count - 1; // the inclusive right edge of the current range we are sorting. T partition; // The partition value. // Loop until we have nothing left to sort. On each iteration, l and r contains the bounds // of something to sort (unless r <= l), and leftStack/rightStack have a stack of unsorted // pieces (unles stackPtr == 0). for (; ; ) { if (l == r - 1) { // We have exactly 2 elements to sort. Compare them and swap if needed. T e1, e2; e1 = list[l]; e2 = list[r]; if (comparer.Compare(e1, e2) > 0) { list[r] = e1; list[l] = e2; } l = r; // sort complete, find other work from the stack. } else if (l < r) { // Sort the items in the inclusive range l .. r // Get the left, middle, and right-most elements and sort them, yielding e1=smallest, e2=median, e3=largest int m = l + (r - l) / 2; T e1 = list[l], e2 = list[m], e3 = list[r], temp; if (comparer.Compare(e1, e2) > 0) { temp = e1; e1 = e2; e2 = temp; } if (comparer.Compare(e1, e3) > 0) { temp = e3; e3 = e2; e2 = e1; e1 = temp; } else if (comparer.Compare(e2, e3) > 0) { temp = e2; e2 = e3; e3 = temp; } if (l == r - 2) { // We have exactly 3 elements to sort, and we've done that. Store back and we're done. list[l] = e1; list[m] = e2; list[r] = e3; l = r; // sort complete, find other work from the stack. } else { // Put the smallest at the left, largest in the middle, and the median at the right (which is the partitioning value) list[l] = e1; list[m] = e3; list[r] = partition = e2; // Partition into three parts, items <= partition, items == partition, and items >= partition int i = l, j = r; T item_i, item_j; for (; ; ) { do { ++i; item_i = list[i]; } while (comparer.Compare(item_i, partition) < 0); do { --j; item_j = list[j]; } while (comparer.Compare(item_j, partition) > 0); if (j < i) break; list[i] = item_j; list[j] = item_i; // swap items to continue the partition. } // Move the partition value into place. list[r] = item_i; list[i] = partition; ++i; // We have partitioned the list. // Items in the inclusive range l .. j are <= partition. // Items in the inclusive range i .. r are >= partition. // Items in the inclusive range j+1 .. i - 1 are == partition (and in the correct final position). // We now need to sort l .. j and i .. r. // To do this, we stack one of the lists for later processing, and change l and r to the other list. // If we always stack the larger of the two sub-parts, the stack cannot get greater // than log2(Count) in size; i.e., a 32-element stack is enough for the maximum list size. if ((j - l) > (r - i)) { // The right partition is smaller. Stack the left, and get ready to sort the right. leftStack[stackPtr] = l; rightStack[stackPtr] = j; l = i; } else { // The left partition is smaller. Stack the right, and get ready to sort the left. leftStack[stackPtr] = i; rightStack[stackPtr] = r; r = j; } ++stackPtr; } } else if (stackPtr > 0) { // We have a stacked sub-list to sort. Pop it off and sort it. --stackPtr; l = leftStack[stackPtr]; r = rightStack[stackPtr]; } else { // We have nothing left to sort. break; } } } /// /// Sorts a list or array in place. A supplied Comparison<T> delegate is used /// to compare the items in the list. /// /// The Quicksort algorithms is used to sort the items. In virtually all cases, /// this takes time O(N log N), where N is the number of items in the list. /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to sort. /// The comparison delegate used to compare items in the collection. public static void SortInPlace(IList list, Comparison comparison) { SortInPlace(list, Comparers.ComparerFromComparison(comparison)); } /// /// Creates a sorted version of a collection. The sort is stable, which means that if items X and Y are equal, /// and X precedes Y in the unsorted collection, X will precede Y is the sorted collection. /// /// Values are compared by using the IComparable<T> /// interfaces implementation on the type T. /// The collection to sort. /// An array containing the sorted version of the collection. public static T[] StableSort(IEnumerable collection) where T : IComparable { return StableSort(collection, Comparer.Default); } /// /// Creates a sorted version of a collection. The sort is stable, which means that if items X and Y are equal, /// and X precedes Y in the unsorted collection, X will precede Y is the sorted collection. A supplied IComparer<T> is used /// to compare the items in the collection. /// /// The collection to sort. /// The comparer instance used to compare items in the collection. Only /// the Compare method is used. /// An array containing the sorted version of the collection. public static T[] StableSort(IEnumerable collection, IComparer comparer) { T[] array; if (collection == null) throw new ArgumentNullException("collection"); if (comparer == null) throw new ArgumentNullException("comparer"); array = Algorithms.ToArray(collection); StableSortInPlace(Algorithms.ReadWriteList(array), comparer); return array; } /// /// Creates a sorted version of a collection. The sort is stable, which means that if items X and Y are equal, /// and X precedes Y in the unsorted collection, X will precede Y is the sorted collection. /// A supplied Comparison<T> delegate is used /// to compare the items in the collection. /// /// Values are compared by using the IComparable<T> /// interfaces implementation on the type T. /// The collection to sort. /// The comparison delegate used to compare items in the collection. /// An array containing the sorted version of the collection. public static T[] StableSort(IEnumerable collection, Comparison comparison) { return StableSort(collection, Comparers.ComparerFromComparison(comparison)); } /// /// Sorts a list or array in place. The sort is stable, which means that if items X and Y are equal, /// and X precedes Y in the unsorted collection, X will precede Y is the sorted collection. /// /// Values are compared by using the IComparable<T> /// interfaces implementation on the type T. /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to sort. public static void StableSortInPlace(IList list) where T : IComparable { StableSortInPlace(list, Comparer.Default); } /// /// Sorts a list or array in place. The sort is stable, which means that if items X and Y are equal, /// and X precedes Y in the unsorted collection, X will precede Y is the sorted collection. /// A supplied IComparer<T> is used /// to compare the items in the list. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to sort. /// The comparer instance used to compare items in the collection. Only /// the Compare method is used. public static void StableSortInPlace(IList list, IComparer comparer) { if (list == null) throw new ArgumentNullException("list"); if (comparer == null) throw new ArgumentNullException("comparer"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); // The stable sort algorithms also uses QuickSort. An additional array of indices (order) is // used to maintain the original order of items in the array, and that array is used // as a secondary compare when the primary compare returns equal. int[] order = new int[list.Count]; for (int x = 0; x < order.Length; ++x) order[x] = x; // Instead of a recursive procedure, we use an explicit stack to hold // ranges that we still need to sort. int[] leftStack = new int[32], rightStack = new int[32]; int stackPtr = 0; int l = 0; // the inclusive left edge of the current range we are sorting. int r = list.Count - 1; // the inclusive right edge of the current range we are sorting. T partition; // The partition value. int order_partition; // The order of the partition value; int c; // holds the result of a comparison temporarily. // Loop until we have nothing left to sort. On each iteration, l and r contains the bounds // of something to sort (unless r <= l), and leftStack/rightStack have a stack of unsorted // pieces (unles stackPtr == 0). for (; ; ) { if (l == r - 1) { // We have exactly 2 elements to sort. Compare them and swap if needed. T e1, e2; int o1, o2; e1 = list[l]; o1 = order[l]; e2 = list[r]; o2 = order[r]; if ((c = comparer.Compare(e1, e2)) > 0 || (c == 0 && o1 > o2)) { list[r] = e1; order[r] = o1; list[l] = e2; order[l] = o2; } l = r; // sort complete, find other work from the stack. } else if (l < r) { // Sort the items in the inclusive range l .. r // Get the left, middle, and right-most elements and sort them, yielding e1=smallest, e2=median, e3=largest int m = l + (r - l) / 2; T e1 = list[l], e2 = list[m], e3 = list[r], temp; int o1 = order[l], o2 = order[m], o3 = order[r], otemp; if ((c = comparer.Compare(e1, e2)) > 0 || (c == 0 && o1 > o2)) { temp = e1; e1 = e2; e2 = temp; otemp = o1; o1 = o2; o2 = otemp; } if ((c = comparer.Compare(e1, e3)) > 0 || (c == 0 && o1 > o3)) { temp = e3; e3 = e2; e2 = e1; e1 = temp; otemp = o3; o3 = o2; o2 = o1; o1 = otemp; } else if ((c = comparer.Compare(e2, e3)) > 0 || (c == 0 && o2 > o3)) { temp = e2; e2 = e3; e3 = temp; otemp = o2; o2 = o3; o3 = otemp; } if (l == r - 2) { // We have exactly 3 elements to sort, and we've done that. Store back and we're done. list[l] = e1; list[m] = e2; list[r] = e3; order[l] = o1; order[m] = o2; order[r] = o3; l = r; // sort complete, find other work from the stack. } else { // Put the smallest at the left, largest in the middle, and the median at the right (which is the partitioning value) list[l] = e1; order[l] = o1; list[m] = e3; order[m] = o3; list[r] = partition = e2; order[r] = order_partition = o2; // Partition into three parts, items <= partition, items == partition, and items >= partition int i = l, j = r; T item_i, item_j; int order_i, order_j; for (; ; ) { do { ++i; item_i = list[i]; order_i = order[i]; } while ((c = comparer.Compare(item_i, partition)) < 0 || (c == 0 && order_i < order_partition)); do { --j; item_j = list[j]; order_j = order[j]; } while ((c = comparer.Compare(item_j, partition)) > 0 || (c == 0 && order_j > order_partition)); if (j < i) break; list[i] = item_j; list[j] = item_i; // swap items to continue the partition. order[i] = order_j; order[j] = order_i; } // Move the partition value into place. list[r] = item_i; order[r] = order_i; list[i] = partition; order[i] = order_partition; ++i; // We have partitioned the list. // Items in the inclusive range l .. j are <= partition. // Items in the inclusive range i .. r are >= partition. // Items in the inclusive range j+1 .. i - 1 are == partition (and in the correct final position). // We now need to sort l .. j and i .. r. // To do this, we stack one of the lists for later processing, and change l and r to the other list. // If we always stack the larger of the two sub-parts, the stack cannot get greater // than log2(Count) in size; i.e., a 32-element stack is enough for the maximum list size. if ((j - l) > (r - i)) { // The right partition is smaller. Stack the left, and get ready to sort the right. leftStack[stackPtr] = l; rightStack[stackPtr] = j; l = i; } else { // The left partition is smaller. Stack the right, and get ready to sort the left. leftStack[stackPtr] = i; rightStack[stackPtr] = r; r = j; } ++stackPtr; } } else if (stackPtr > 0) { // We have a stacked sub-list to sort. Pop it off and sort it. --stackPtr; l = leftStack[stackPtr]; r = rightStack[stackPtr]; } else { // We have nothing left to sort. break; } } } /// /// Sorts a list or array in place. The sort is stable, which means that if items X and Y are equal, /// and X precedes Y in the unsorted collection, X will precede Y is the sorted collection. /// A supplied Comparison<T> delegate is used /// to compare the items in the list. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to sort. /// The comparison delegate used to compare items in the collection. public static void StableSortInPlace(IList list, Comparison comparison) { StableSortInPlace(list, Comparers.ComparerFromComparison(comparison)); } /// /// Searches a sorted list for an item via binary search. The list must be sorted /// by the natural ordering of the type (it's implementation of IComparable<T>). /// /// The sorted list to search. /// The item to search for. /// Returns the first index at which the item can be found. If the return /// value is zero, indicating that was not present in the list, then this /// returns the index at which could be inserted to maintain the sorted /// order of the list. /// The number of items equal to that appear in the list. public static int BinarySearch(IList list, T item, out int index) where T: IComparable { return BinarySearch(list, item, Comparer.Default, out index); } /// /// Searches a sorted list for an item via binary search. The list must be sorted /// by the ordering in the passed instance of IComparer<T>. /// /// The sorted list to search. /// The item to search for. /// The comparer instance used to sort the list. Only /// the Compare method is used. /// Returns the first index at which the item can be found. If the return /// value is zero, indicating that was not present in the list, then this /// returns the index at which could be inserted to maintain the sorted /// order of the list. /// The number of items equal to that appear in the list. public static int BinarySearch(IList list, T item, IComparer comparer, out int index) { if (list == null) throw new ArgumentNullException("list"); if (comparer == null) throw new ArgumentNullException("comparer"); int l = 0; int r = list.Count; while (r > l) { int m = l + (r - l) / 2; T middleItem = list[m]; int comp = comparer.Compare(middleItem, item); if (comp < 0) { // middleItem < item l = m + 1; } else if (comp > 0) { r = m; } else { // Found something equal to item at m. Now we need to find the start and end of this run of equal items. int lFound = l, rFound = r, found = m; // Find the start of the run. l = lFound; r = found; while (r > l) { m = l + (r - l) / 2; middleItem = list[m]; comp = comparer.Compare(middleItem, item); if (comp < 0) { // middleItem < item l = m + 1; } else { r = m; } } System.Diagnostics.Debug.Assert(l == r); index = l; // Find the end of the run. l = found; r = rFound; while (r > l) { m = l + (r - l) / 2; middleItem = list[m]; comp = comparer.Compare(middleItem, item); if (comp <= 0) { // middleItem <= item l = m + 1; } else { r = m; } } System.Diagnostics.Debug.Assert(l == r); return l - index; } } // We did not find the item. l and r must be equal. System.Diagnostics.Debug.Assert(l == r); index = l; return 0; } /// /// Searches a sorted list for an item via binary search. The list must be sorted /// by the ordering in the passed Comparison<T> delegate. /// /// The sorted list to search. /// The item to search for. /// The comparison delegate used to sort the list. /// Returns the first index at which the item can be found. If the return /// value is zero, indicating that was not present in the list, then this /// returns the index at which could be inserted to maintain the sorted /// order of the list. /// The number of items equal to that appear in the list. public static int BinarySearch(IList list, T item, Comparison comparison, out int index) { return BinarySearch(list, item, Comparers.ComparerFromComparison(comparison), out index); } /// /// Merge several sorted collections into a single sorted collection. Each input collection must be sorted /// by the natural ordering of the type (it's implementation of IComparable<T>). The merging /// is stable; equal items maintain their ordering, and equal items in different collections are placed /// in the order of the collections. /// /// The set of collections to merge. In many languages, this parameter /// can be specified as several individual parameters. /// An IEnumerable<T> that enumerates all the items in all the collections /// in sorted order. public static IEnumerable MergeSorted(params IEnumerable[] collections) where T : IComparable { return MergeSorted(Comparer.Default, collections); } /// /// Merge several sorted collections into a single sorted collection. Each input collection must be sorted /// by the ordering in the passed instance of IComparer<T>. The merging /// is stable; equal items maintain their ordering, and equal items in different collections are placed /// in the order of the collections. /// /// The set of collections to merge. In many languages, this parameter /// can be specified as several individual parameters. /// The comparer instance used to sort the list. Only /// the Compare method is used. /// An IEnumerable<T> that enumerates all the items in all the collections /// in sorted order. public static IEnumerable MergeSorted(IComparer comparer, params IEnumerable[] collections) { if (collections == null) throw new ArgumentNullException("collections"); if (comparer == null) throw new ArgumentNullException("comparer"); IEnumerator[] enumerators = new IEnumerator[collections.Length]; bool[] more = new bool[collections.Length]; T smallestItem = default(T); int smallestItemIndex; try { // Get enumerators from each collection, and advance to the first element. for (int i = 0; i < collections.Length; ++i) { if (collections[i] != null) { enumerators[i] = collections[i].GetEnumerator(); more[i] = enumerators[i].MoveNext(); } } for (; ; ) { // Find the smallest item, and which collection it is in. smallestItemIndex = -1; // -1 indicates no smallest yet. for (int i = 0; i < enumerators.Length; ++i) { if (more[i]) { T item = enumerators[i].Current; if (smallestItemIndex < 0 || comparer.Compare(smallestItem, item) > 0) { smallestItemIndex = i; smallestItem = item; } } } // If no smallest item found, we're done. if (smallestItemIndex == -1) yield break; // Yield the smallest item. yield return smallestItem; // Advance the enumerator it came from. more[smallestItemIndex] = enumerators[smallestItemIndex].MoveNext(); } } finally { // Dispose all enumerators. foreach (IEnumerator e in enumerators) { if (e != null) e.Dispose(); } } } /// /// Merge several sorted collections into a single sorted collection. Each input collection must be sorted /// by the ordering in the passed Comparison<T> delegate. The merging /// is stable; equal items maintain their ordering, and equal items in different collections are placed /// in the order of the collections. /// /// The set of collections to merge. In many languages, this parameter /// can be specified as several individual parameters. /// The comparison delegate used to sort the collections. /// An IEnumerable<T> that enumerates all the items in all the collections /// in sorted order. public static IEnumerable MergeSorted(Comparison comparison, params IEnumerable[] collections) { return MergeSorted(Comparers.ComparerFromComparison(comparison), collections); } /// /// Performs a lexicographical comparison of two sequences of values. A lexicographical comparison compares corresponding /// pairs of elements from two sequences in order. If the first element of sequence1 is less than the first element of sequence2, /// then the comparison ends and the first sequence is lexicographically less than the second. If the first elements of each sequence /// are equal, then the comparison proceeds to the second element of each sequence. If one sequence is shorter than the other, /// but corresponding elements are all equal, then the shorter sequence is considered less than the longer one. /// /// T must implement either IComparable<T> and this implementation is used /// to compare the items. /// Types of items to compare. This type must implement IComparable<T> to allow /// items to be compared. /// The first sequence to compare. /// The second sequence to compare. /// Less than zero if is lexicographically less than . /// Greater than zero if is lexicographically greater than . /// Zero if is equal to . /// T does not implement IComparable<T> or IComparable. public static int LexicographicalCompare(IEnumerable sequence1, IEnumerable sequence2) where T : IComparable { return LexicographicalCompare(sequence1, sequence2, Comparer.Default); } /// /// Performs a lexicographical comparison of two sequences of values, using a supplied comparison delegate. A lexicographical comparison compares corresponding /// pairs of elements from two sequences in order. If the first element of sequence1 is less than the first element of sequence2, /// then the comparison ends and the first sequence is lexicographically less than the second. If the first elements of each sequence /// are equal, then the comparison proceeds to the second element of each sequence. If one sequence is shorter than the other, /// but corresponding elements are all equal, then the shorter sequence is considered less than the longer one. /// /// Types of items to compare. /// The first sequence to compare. /// The second sequence to compare. /// The IComparison<T> delegate to compare items. /// Only the Compare member function of this interface is called. /// Less than zero if is lexicographically less than . /// Greater than zero if is lexicographically greater than . /// Zero if is equal to . public static int LexicographicalCompare(IEnumerable sequence1, IEnumerable sequence2, Comparison comparison) { return LexicographicalCompare(sequence1, sequence2, Comparers.ComparerFromComparison(comparison)); } /// /// Performs a lexicographical comparison of two sequences of values, using a supplied comparer interface. A lexicographical comparison compares corresponding /// pairs of elements from two sequences in order. If the first element of sequence1 is less than the first element of sequence2, /// then the comparison ends and the first sequence is lexicographically less than the second. If the first elements of each sequence /// are equal, then the comparison proceeds to the second element of each sequence. If one sequence is shorter than the other, /// but corresponding elements are all equal, then the shorter sequence is considered less than the longer one. /// /// Types of items to compare. /// The first sequence to compare. /// The second sequence to compare. /// The IComparer<T> used to compare items. /// Only the Compare member function of this interface is called. /// Less than zero if is lexicographically less than . /// Greater than zero if is lexicographically greater than . /// Zero if is equal to . /// , , or /// is null. public static int LexicographicalCompare(IEnumerable sequence1, IEnumerable sequence2, IComparer comparer) { if (sequence1 == null) throw new ArgumentNullException("sequence1"); if (sequence2 == null) throw new ArgumentNullException("sequence2"); if (comparer == null) throw new ArgumentNullException("comparer"); using (IEnumerator enum1 = sequence1.GetEnumerator(), enum2 = sequence2.GetEnumerator()) { bool continue1, continue2; for (; ; ) { continue1 = enum1.MoveNext(); continue2 = enum2.MoveNext(); if (!continue1 || !continue2) break; int compare = comparer.Compare(enum1.Current, enum2.Current); if (compare != 0) return compare; } // If both continue1 and continue2 are false, we reached the end of both sequences at the same // time and the sequences are equal. Otherwise, the shorter sequence is considered first. if (continue1 == continue2) return 0; else if (continue1) return 1; else return -1; } } #endregion Sorting #region Comparers/Comparison utilities /// /// A private class used by the LexicographicalComparer method to compare sequences /// (IEnumerable) of T by there Lexicographical ordering. /// [Serializable] private class LexicographicalComparerClass : IComparer> { IComparer itemComparer; /// /// Creates a new instance that comparer sequences of T by their lexicographical /// ordered. /// /// The IComparer used to compare individual items of type T. public LexicographicalComparerClass(IComparer itemComparer) { this.itemComparer = itemComparer; } public int Compare(IEnumerable x, IEnumerable y) { return LexicographicalCompare(x, y, itemComparer); } // For comparing this comparer to others. public override bool Equals(object obj) { if (obj is LexicographicalComparerClass) return this.itemComparer.Equals(((LexicographicalComparerClass)obj).itemComparer); else return false; } public override int GetHashCode() { return itemComparer.GetHashCode(); } } /// /// Creates an IComparer instance that can be used for comparing ordered /// sequences of type T; that is IEnumerable<Tgt;. This comparer can be used /// for collections or algorithms that use sequences of T as an item type. The Lexicographical /// ordered of sequences is for comparison. /// /// T must implement either IComparable<T> and this implementation is used /// to compare the items. /// At IComparer<IEnumerable<T>> that compares sequences of T. public static IComparer> GetLexicographicalComparer() where T: IComparable { return GetLexicographicalComparer(Comparer.Default); } /// /// Creates an IComparer instance that can be used for comparing ordered /// sequences of type T; that is IEnumerable<Tgt;. This comparer can be uses /// for collections or algorithms that use sequences of T as an item type. The Lexicographics /// ordered of sequences is for comparison. /// /// A comparer instance used to compare individual items of type T. /// At IComparer<IEnumerable<T>> that compares sequences of T. public static IComparer> GetLexicographicalComparer(IComparer comparer) { if (comparer == null) throw new ArgumentNullException("comparer"); return new LexicographicalComparerClass(comparer); } /// /// Creates an IComparer instance that can be used for comparing ordered /// sequences of type T; that is IEnumerable<Tgt;. This comparer can be uses /// for collections or algorithms that use sequences of T as an item type. The Lexicographics /// ordered of sequences is for comparison. /// /// A comparison delegate used to compare individual items of type T. /// At IComparer<IEnumerable<T>> that compares sequences of T. public static IComparer> GetLexicographicalComparer(Comparison comparison) { if (comparison == null) throw new ArgumentNullException("comparison"); return new LexicographicalComparerClass(Comparers.ComparerFromComparison(comparison)); } /// /// An IComparer instance that can be used to reverse the sense of /// a wrapped IComparer instance. /// [Serializable] private class ReverseComparerClass : IComparer { IComparer comparer; /// /// /// The comparer to reverse. public ReverseComparerClass(IComparer comparer) { this.comparer = comparer; } public int Compare(T x, T y) { return - comparer.Compare(x, y); } // For comparing this comparer to others. public override bool Equals(object obj) { if (obj is ReverseComparerClass) return this.comparer.Equals(((ReverseComparerClass)obj).comparer); else return false; } public override int GetHashCode() { return comparer.GetHashCode(); } } /// /// Reverses the order of comparison of an IComparer<T>. The resulting comparer can be used, /// for example, to sort a collection in descending order. Equality and hash codes are unchanged. /// /// The type of items thta are being compared. /// The comparer to reverse. /// An IComparer<T> that compares items in the reverse order of . /// is null. public static IComparer GetReverseComparer(IComparer comparer) { if (comparer == null) throw new ArgumentNullException("comparer"); return new ReverseComparerClass(comparer); } /// /// A class, implementing IEqualityComparer<T>, that compares objects /// for object identity only. Only Equals and GetHashCode can be used; /// this implementation is not appropriate for ordering. /// [Serializable] private class IdentityComparer : IEqualityComparer where T:class { public bool Equals(T x, T y) { return ((object)x == (object)y); } public int GetHashCode(T obj) { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); } // For comparing two IComparers to see if they compare the same thing. public override bool Equals(object obj) { return (obj != null && obj is IdentityComparer); } // For comparing two IComparers to see if they compare the same thing. public override int GetHashCode() { return 0x7143DDEF; } } /// /// Gets an IEqualityComparer<T> instance that can be used to compare objects /// of type T for object identity only. Two objects compare equal only if they /// are references to the same object. /// /// An IEqualityComparer<T> instance for identity comparison. public static IEqualityComparer GetIdentityComparer() where T : class { return new IdentityComparer(); } /// /// Reverses the order of comparison of an Comparison<T>. The resulting comparison can be used, /// for example, to sort a collection in descending order. /// /// The type of items that are being compared. /// The comparison to reverse. /// A Comparison<T> that compares items in the reverse order of . /// is null. public static Comparison GetReverseComparison(Comparison comparison) { if (comparison == null) throw new ArgumentNullException("comparison"); return delegate(T x, T y) { return -comparison(x, y); }; } /// /// Given a comparison delegate that compares two items of type T, gets an /// IComparer<T> instance that performs the same comparison. /// /// The comparison delegate to use. /// An IComparer<T> that performs the same comparing operation /// as . public static IComparer GetComparerFromComparison(Comparison comparison) { if (comparison == null) throw new ArgumentNullException("comparison"); return Comparers.ComparerFromComparison(comparison); } /// /// Given in IComparer<T> instenace that comparers two items from type T, /// gets a Comparison delegate that performs the same comparison. /// /// The IComparer<T> instance to use. /// A Comparison<T> delegate that performans the same comparing /// operation as . public static Comparison GetComparisonFromComparer(IComparer comparer) { if (comparer == null) throw new ArgumentNullException("comparer"); return comparer.Compare; } /// /// A private class used to implement GetCollectionEqualityComparer(). This /// class implements IEqualityComparer<IEnumerable<T>gt; to compare /// two enumerables for equality, where order is significant. /// [Serializable] private class CollectionEqualityComparer : IEqualityComparer> { private IEqualityComparer equalityComparer; public CollectionEqualityComparer(IEqualityComparer equalityComparer) { this.equalityComparer = equalityComparer; } public bool Equals(IEnumerable x, IEnumerable y) { return Algorithms.EqualCollections(x, y, equalityComparer); } public int GetHashCode(IEnumerable obj) { int hash = 0x374F293E; foreach (T t in obj) { int itemHash = Util.GetHashCode(t, equalityComparer); hash += itemHash; hash = (hash << 9) | (hash >> 23); } return hash & 0x7FFFFFFF; } } /// /// Gets an IEqualityComparer<IEnumerable<T>> implementation /// that can be used to compare collections of elements (of type T). Two collections /// of T's are equal if they have the same number of items, and corresponding /// items are equal, considered in order. This is the same notion of equality as /// in Algorithms.EqualCollections, but encapsulated in an IEqualityComparer<IEnumerable<T>> implementation. /// /// /// The following code creates a Dictionary where the keys are a collection of strings. /// /// Dictionary<IEnumerable<string>, int> = /// new Dictionary<IEnumerable<string>, int>(Algorithms.GetCollectionEqualityComparer<string>()); /// /// /// IEqualityComparer<IEnumerable<T>> implementation suitable for /// comparing collections of T for equality. /// public static IEqualityComparer> GetCollectionEqualityComparer() { return GetCollectionEqualityComparer(EqualityComparer.Default); } /// /// Gets an IEqualityComparer<IEnumerable<T>> implementation /// that can be used to compare collections of elements (of type T). Two collections /// of T's are equal if they have the same number of items, and corresponding /// items are equal, considered in order. This is the same notion of equality as /// in Algorithms.EqualCollections, but encapsulated in an IEqualityComparer<IEnumerable<T>> implementation. /// An IEqualityComparer<T> is used to determine if individual T's are equal /// /// /// The following code creates a Dictionary where the keys are a collection of strings, compared in a case-insensitive way /// /// Dictionary<IEnumerable<string>, int> = /// new Dictionary<IEnumerable<string>, int>(Algorithms.GetCollectionEqualityComparer<string>(StringComparer.CurrentCultureIgnoreCase)); /// /// /// An IEqualityComparer<T> implementation used to compare individual T's. /// IEqualityComparer<IEnumerable<T>> implementation suitable for /// comparing collections of T for equality. /// public static IEqualityComparer> GetCollectionEqualityComparer(IEqualityComparer equalityComparer) { if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); return new CollectionEqualityComparer(equalityComparer); } /// /// A private class used to implement GetSetEqualityComparer(). This /// class implements IEqualityComparer<IEnumerable<T>gt; to compare /// two enumerables for equality, where order is not significant. /// [Serializable] private class SetEqualityComparer : IEqualityComparer> { private IEqualityComparer equalityComparer; public SetEqualityComparer(IEqualityComparer equalityComparer) { this.equalityComparer = equalityComparer; } public bool Equals(IEnumerable x, IEnumerable y) { return Algorithms.EqualSets(x, y, equalityComparer); } public int GetHashCode(IEnumerable obj) { int hash = 0x624F273C; foreach (T t in obj) { int itemHash = Util.GetHashCode(t, equalityComparer); hash += itemHash; } return hash & 0x7FFFFFFF; } } /// /// Gets an IEqualityComparer<IEnumerable<T>> implementation /// that can be used to compare collections of elements (of type T). Two collections /// of T's are equal if they have the same number of items, and corresponding /// items are equal, without regard to order. This is the same notion of equality as /// in Algorithms.EqualSets, but encapsulated in an IEqualityComparer<IEnumerable<T>> implementation. /// An IEqualityComparer<T> is used to determine if individual T's are equal /// /// /// The following code creates a Dictionary where the keys are a set of strings, without regard to order /// /// Dictionary<IEnumerable<string>, int> = /// new Dictionary<IEnumerable<string>, int>(Algorithms.GetSetEqualityComparer<string>(StringComparer.CurrentCultureIgnoreCase)); /// /// /// IEqualityComparer<IEnumerable<T>> implementation suitable for /// comparing collections of T for equality, without regard to order. /// public static IEqualityComparer> GetSetEqualityComparer() { return GetSetEqualityComparer(EqualityComparer.Default); } /// /// Gets an IEqualityComparer<IEnumerable<T>> implementation /// that can be used to compare collections of elements (of type T). Two collections /// of T's are equal if they have the same number of items, and corresponding /// items are equal, without regard to order. This is the same notion of equality as /// in Algorithms.EqualSets, but encapsulated in an IEqualityComparer<IEnumerable<T>> implementation. /// /// /// The following code creates a Dictionary where the keys are a set of strings, without regard to order /// /// Dictionary<IEnumerable<string>, int> = /// new Dictionary<IEnumerable<string>, int>(Algorithms.GetSetEqualityComparer<string>()); /// /// /// An IEqualityComparer<T> implementation used to compare individual T's. /// IEqualityComparer<IEnumerable<T>> implementation suitable for /// comparing collections of T for equality, without regard to order. /// public static IEqualityComparer> GetSetEqualityComparer(IEqualityComparer equalityComparer) { if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); return new SetEqualityComparer(equalityComparer); } #endregion Sorting #region Predicate operations /// /// Determines if a collection contains any item that satisfies the condition /// defined by . /// /// The collection to check all the items in. /// A delegate that defines the condition to check for. /// True if the collection contains one or more items that satisfy the condition /// defined by . False if the collection does not contain /// an item that satisfies . public static bool Exists(IEnumerable collection, Predicate predicate) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); foreach (T item in collection) { if (predicate(item)) return true; } return false; } /// /// Determines if all of the items in the collection satisfy the condition /// defined by . /// /// The collection to check all the items in. /// A delegate that defines the condition to check for. /// True if all of the items in the collection satisfy the condition /// defined by , or if the collection is empty. False if one or more items /// in the collection do not satisfy . public static bool TrueForAll(IEnumerable collection, Predicate predicate) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); foreach (T item in collection) { if (!predicate(item)) return false; } return true; } /// /// Counts the number of items in the collection that satisfy the condition /// defined by . /// /// The collection to count items in. /// A delegate that defines the condition to check for. /// The number of items in the collection that satisfy . public static int CountWhere(IEnumerable collection, Predicate predicate) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); int count = 0; foreach (T item in collection) { if (predicate(item)) ++count; } return count; } /// /// Removes all the items in the collection that satisfy the condition /// defined by . /// /// If the collection if an array or implements IList<T>, an efficient algorithm that /// compacts items is used. If not, then ICollection<T>.Remove is used /// to remove items from the collection. If the collection is an array or fixed-size list, /// the non-removed elements are placed, in order, at the beginning of /// the list, and the remaining list items are filled with a default value (0 or null). /// The collection to check all the items in. /// A delegate that defines the condition to check for. /// Returns a collection of the items that were removed. This collection contains the /// items in the same order that they orginally appeared in . public static ICollection RemoveWhere(ICollection collection, Predicate predicate) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); if (collection is T[]) collection = new ArrayWrapper((T[])collection); if (collection.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "collection"); IList list = collection as IList; if (list != null) { T item; int i = -1, j = 0; int listCount = list.Count; List removed = new List(); // Remove item where predicate is true, compressing items to lower in the list. This is much more // efficient than the naive algorithm that uses IList.Remove(). while (j < listCount) { item = list[j]; if (predicate(item)) { removed.Add(item); } else { ++i; if (i != j) list[i] = item; } ++j; } ++i; if (i < listCount) { // remove items from the end. if (list is IList && ((IList)list).IsFixedSize) { // An array or similar. Null out the last elements. while (i < listCount) list[i++] = default(T); } else { // Normal list. while (i < listCount) { list.RemoveAt(listCount - 1); --listCount; } } } return removed; } else { // We have to copy all the items to remove to a List, because collections can't be modifed // during an enumeration. List removed = new List(); foreach (T item in collection) if (predicate(item)) removed.Add(item); foreach (T item in removed) collection.Remove(item); return removed; } } /// /// Convert a collection of items by applying a delegate to each item in the collection. The resulting collection /// contains the result of applying to each item in , in /// order. /// /// The type of items in the collection to convert. /// The type each item is being converted to. /// The collection of item being converted. /// A delegate to the method to call, passing each item in . /// The resulting collection from applying to each item in , in /// order. /// or is null. public static IEnumerable Convert(IEnumerable sourceCollection, Converter converter) { if (sourceCollection == null) throw new ArgumentNullException("sourceCollection"); if (converter == null) throw new ArgumentNullException("converter"); foreach (TSource sourceItem in sourceCollection) yield return converter(sourceItem); } /// /// Creates a delegate that converts keys to values by used a dictionary to map values. Keys /// that a not present in the dictionary are converted to the default value (zero or null). /// /// This delegate can be used as a parameter in Convert or ConvertAll methods to convert /// entire collections. /// The dictionary used to perform the conversion. /// A delegate to a method that converts keys to values. public static Converter GetDictionaryConverter(IDictionary dictionary) { return GetDictionaryConverter(dictionary, default(TValue)); } /// /// Creates a delegate that converts keys to values by used a dictionary to map values. Keys /// that a not present in the dictionary are converted to a supplied default value. /// /// This delegate can be used as a parameter in Convert or ConvertAll methods to convert /// entire collections. /// The dictionary used to perform the conversion. /// The result of the conversion for keys that are not present in the dictionary. /// A delegate to a method that converts keys to values. /// is null. public static Converter GetDictionaryConverter(IDictionary dictionary, TValue defaultValue) { if (dictionary == null) throw new ArgumentNullException("dictionary"); return delegate(TKey key) { TValue value; if (dictionary.TryGetValue(key, out value)) return value; else return defaultValue; }; } /// /// Performs the specified action on each item in a collection. /// /// The collection to process. /// An Action delegate which is invoked for each item in . public static void ForEach(IEnumerable collection, Action action) { if (collection == null) throw new ArgumentNullException("collection"); if (action == null) throw new ArgumentNullException("action"); foreach (T item in collection) action(item); } /// /// Partition a list or array based on a predicate. After partitioning, all items for which /// the predicate returned true precede all items for which the predicate returned false. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to partition. /// A delegate that defines the partitioning condition. /// The index of the first item in the second half of the partition; i.e., the first item for /// which returned false. If the predicate was true for all items /// in the list, list.Count is returned. public static int Partition(IList list, Predicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); // Move from opposite ends of the list, swapping when necessary. int i = 0, j = list.Count - 1; for (;;) { while (i <= j && predicate(list[i])) ++i; while (i <= j && !predicate(list[j])) --j; if (i > j) break; else { T temp = list[i]; list[i] = list[j]; list[j] = temp; ++i; --j; } } return i; } /// /// Partition a list or array based on a predicate. After partitioning, all items for which /// the predicate returned true precede all items for which the predicate returned false. /// The partition is stable, which means that if items X and Y have the same result from /// the predicate, and X precedes Y in the original list, X will precede Y in the /// partitioned list. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to partition. /// A delegate that defines the partitioning condition. /// The index of the first item in the second half of the partition; i.e., the first item for /// which returned false. If the predicate was true for all items /// in the list, list.Count is returned. public static int StablePartition(IList list, Predicate predicate) { if (list == null) throw new ArgumentNullException("list"); if (predicate == null) throw new ArgumentNullException("predicate"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); int listCount = list.Count; if (listCount == 0) return 0; T[] temp = new T[listCount]; // Copy from list to temp buffer, true items at fron, false item (in reverse order) at back. int i = 0, j = listCount - 1; foreach (T item in list) { if (predicate(item)) temp[i++] = item; else temp[j--] = item; } // Copy back to the original list. int index = 0; while (index < i) { list[index] = temp[index]; index++; } j = listCount - 1; while (index < listCount) list[index++] = temp[j--]; return i; } #endregion Predicate operations #region Miscellaneous operations on IEnumerable /// /// Concatenates all the items from several collections. The collections need not be of the same type, but /// must have the same item type. /// /// The set of collections to concatenate. In many languages, this parameter /// can be specified as several individual parameters. /// An IEnumerable that enumerates all the items in each of the collections, in order. public static IEnumerable Concatenate(params IEnumerable[] collections) { if (collections == null) throw new ArgumentNullException("collections"); foreach (IEnumerable coll in collections) { foreach (T item in coll) yield return item; } } /// /// Determines if the two collections contain equal items in the same order. The two collections do not need /// to be of the same type; it is permissible to compare an array and an OrderedBag, for instance. /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The type of items in the collections. /// The first collection to compare. /// The second collection to compare. /// True if the collections have equal items in the same order. If both collections are empty, true is returned. public static bool EqualCollections(IEnumerable collection1, IEnumerable collection2) { return EqualCollections(collection1, collection2, EqualityComparer.Default); } /// /// Determines if the two collections contain equal items in the same order. The passed /// instance of IEqualityComparer<T> is used for determining if two items are equal. /// /// The type of items in the collections. /// The first collection to compare. /// The second collection to compare. /// The IEqualityComparer<T> used to compare items for equality. /// Only the Equals member function of this interface is called. /// True if the collections have equal items in the same order. If both collections are empty, true is returned. /// , , or /// is null. public static bool EqualCollections(IEnumerable collection1, IEnumerable collection2, IEqualityComparer equalityComparer) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); using (IEnumerator enum1 = collection1.GetEnumerator(), enum2 = collection2.GetEnumerator()) { bool continue1, continue2; for (; ; ) { continue1 = enum1.MoveNext(); continue2 = enum2.MoveNext(); if (!continue1 || !continue2) break; if (!equalityComparer.Equals(enum1.Current, enum2.Current)) return false; // the two items are not equal. } // If both continue1 and continue2 are false, we reached the end of both sequences at the same // time and found success. If one is true and one is false, the sequences were of difference lengths -- failure. return (continue1 == continue2); } } /// /// Determines if the two collections contain "equal" items in the same order. The passed /// BinaryPredicate is used to determine if two items are "equal". /// /// Since an arbitrary BinaryPredicate is passed to this function, what is being tested /// for need not be equality. For example, the following code determines if each integer in /// list1 is less than or equal to the corresponding integer in list2. /// /// List<int> list1, list2; /// if (EqualCollections(list1, list2, delegate(int x, int y) { return x <= y; }) { /// // the check is true... /// } /// /// /// The type of items in the collections. /// The first collection to compare. /// The second collection to compare. /// The BinaryPredicate used to compare items for "equality". /// This predicate can compute any relation between two items; it need not represent equality or an equivalence relation. /// True if returns true for each corresponding pair of /// items in the two collections. If both collections are empty, true is returned. /// , , or /// is null. public static bool EqualCollections(IEnumerable collection1, IEnumerable collection2, BinaryPredicate predicate) { if (collection1 == null) throw new ArgumentNullException("collection1"); if (collection2 == null) throw new ArgumentNullException("collection2"); if (predicate == null) throw new ArgumentNullException("predicate"); using (IEnumerator enum1 = collection1.GetEnumerator(), enum2 = collection2.GetEnumerator()) { bool continue1, continue2; for (; ; ) { continue1 = enum1.MoveNext(); continue2 = enum2.MoveNext(); if (!continue1 || !continue2) break; if (!predicate(enum1.Current, enum2.Current)) return false; // the two items are not equal. } // If both continue1 and continue2 are false, we reached the end of both sequences at the same // time and found success. If one is true and one is false, the sequences were of difference lengths -- failure. return (continue1 == continue2); } } /// /// Create an array with the items in a collection. /// /// If implements ICollection<T>T, then /// ICollection<T>.CopyTo() is used to fill the array. Otherwise, the IEnumerable<T>.GetEnumerator() /// is used to fill the array. /// Element type of the collection. /// Collection to create array from. /// An array with the items from the collection, in enumeration order. /// is null. public static T[] ToArray(IEnumerable collection) { if (collection == null) throw new ArgumentNullException("collection"); ICollection coll = collection as ICollection; if (coll != null) { // Use ICollection methods to do it more efficiently. T[] array = new T[coll.Count]; coll.CopyTo(array, 0); return array; } else { // We can't allocate the correct size array now, because IEnumerable doesn't // have a Count property. We could enumerate twice, once to count and once // to copy. Or we could enumerate once, copying to a List, then copy the list // to the correct size array. The latter algorithm seems more efficient, although // it allocates extra memory for the list which is then discarded. List list = new List(collection); return list.ToArray(); } } /// /// Count the number of items in an IEnumerable<T> collection. If /// a more specific collection type is being used, it is more efficient to use /// the Count property, if one is provided. /// /// If the collection implements ICollection<T>, this method /// simply returns ICollection<T>.Count. Otherwise, it enumerates all items /// and counts them. /// The collection to count items in. /// The number of items in the collection. /// is null. public static int Count(IEnumerable collection) { if (collection == null) throw new ArgumentNullException("collection"); // If it's really an ICollection, use that Count property as it is much faster. if (collection is ICollection) return ((ICollection)collection).Count; // Traverse the collection and count the elements. int count = 0; foreach (T item in collection) ++count; return count; } /// /// Counts the number of items in the collection that are equal to . /// /// The default sense of equality for T is used, as defined by T's /// implementation of IComparable<T>.Equals or object.Equals. /// The collection to count items in. /// The item to compare to. /// The number of items in the collection that are equal to . public static int CountEqual(IEnumerable collection, T find) { return CountEqual(collection, find, EqualityComparer.Default); } /// /// Counts the number of items in the collection that are equal to . /// /// The collection to count items in. /// The item to compare to. /// The comparer to use to determine if two items are equal. Only the Equals /// member function will be called. /// The number of items in the collection that are equal to . /// or /// is null. public static int CountEqual(IEnumerable collection, T find, IEqualityComparer equalityComparer) { if (collection == null) throw new ArgumentException("collection"); if (equalityComparer == null) throw new ArgumentNullException("equalityComparer"); int count = 0; foreach (T item in collection) { if (equalityComparer.Equals(item, find)) ++count; } return count; } /// /// Creates an IEnumerator that enumerates a given item times. /// /// /// The following creates a list consisting of 1000 copies of the double 1.0. /// /// List<double> list = new List<double>(Algorithms.NCopiesOf(1000, 1.0)); /// /// The number of times to enumerate the item. /// The item that should occur in the enumeration. /// An IEnumerable<T> that yields copies /// of . /// The argument is less than zero. public static IEnumerable NCopiesOf(int n, T item) { if (n < 0) throw new ArgumentOutOfRangeException("n", n, Strings.ArgMustNotBeNegative); while (n-- > 0) { yield return item; } } #endregion Miscellaneous operations on IEnumerable #region Miscellaneous operations on IList /// /// Replaces each item in a list with a given value. The list does not change in size. /// /// The type of items in the list. /// The list to modify. /// The value to fill with. /// is a read-only list. /// is null. public static void Fill(IList list, T value) { if (list == null) throw new ArgumentNullException("list"); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); int count = list.Count; for (int i = 0; i < count; ++i) { list[i] = value; } } /// /// Replaces each item in a array with a given value. /// /// The array to modify. /// The value to fill with. /// is null. public static void Fill(T[] array, T value) { if (array == null) throw new ArgumentNullException("array"); for (int i = 0; i < array.Length; ++i) { array[i] = value; } } /// /// Replaces each item in a part of a list with a given value. /// /// The type of items in the list. /// The list to modify. /// The index at which to start filling. The first index in the list has index 0. /// The number of items to fill. /// The value to fill with. /// is a read-only list. /// or is negative, or /// + is greater than .Count. /// is null. public static void FillRange(IList list, int start, int count, T value) { if (list == null) throw new ArgumentNullException("list"); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); if (count == 0) return; if (start < 0 || start >= list.Count) throw new ArgumentOutOfRangeException("start"); if (count < 0 || count > list.Count || start > list.Count - count) throw new ArgumentOutOfRangeException("count"); for (int i = start; i < count + start; ++i) { list[i] = value; } } /// /// Replaces each item in a part of a array with a given value. /// /// The array to modify. /// The index at which to start filling. The first index in the array has index 0. /// The number of items to fill. /// The value to fill with. /// or is negative, or /// + is greater than .Length. /// is null. public static void FillRange(T[] array, int start, int count, T value) { if (array == null) throw new ArgumentNullException("array"); if (count == 0) return; if (start < 0 || start >= array.Length) throw new ArgumentOutOfRangeException("start"); if (count < 0 || count > array.Length || start > array.Length - count) throw new ArgumentOutOfRangeException("count"); for (int i = start; i < count + start; ++i) { array[i] = value; } } /// /// Copies all of the items from the collection to the list , starting /// at the index . If necessary, the size of the destination list is expanded. /// /// The collection that provide the source items. /// The list to store the items into. /// The index to begin copying items to. /// is negative or /// greater than .Count. /// or is null. public static void Copy(IEnumerable source, IList dest, int destIndex) { Copy(source, dest, destIndex, int.MaxValue); } /// /// Copies all of the items from the collection to the array , starting /// at the index . /// /// The collection that provide the source items. /// The array to store the items into. /// The index to begin copying items to. /// is negative or /// greater than .Length. /// or is null. /// The collection has more items than will fit into the array. In this case, the array /// has been filled with as many items as fit before the exception is thrown. public static void Copy(IEnumerable source, T[] dest, int destIndex) { if (source == null) throw new ArgumentNullException("source"); if (dest == null) throw new ArgumentNullException("dest"); if (destIndex < 0 || destIndex > dest.Length) throw new ArgumentOutOfRangeException("destIndex"); using (IEnumerator sourceEnum = source.GetEnumerator()) { // Overwrite items to the end of the destination array. If we hit the end, throw. while (sourceEnum.MoveNext()) { if (destIndex >= dest.Length) throw new ArgumentException(Strings.ArrayTooSmall, "array"); dest[destIndex++] = sourceEnum.Current; } } } /// /// Copies at most items from the collection to the list , starting /// at the index . If necessary, the size of the destination list is expanded. The source collection must not be /// the destination list or part thereof. /// /// The collection that provide the source items. /// The list to store the items into. /// The index to begin copying items to. /// The maximum number of items to copy. /// is negative or /// greater than .Count /// is negative. /// or is null. public static void Copy(IEnumerable source, IList dest, int destIndex, int count) { if (source == null) throw new ArgumentNullException("source"); if (dest == null) throw new ArgumentNullException("dest"); if (dest.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "dest"); int destCount = dest.Count; if (destIndex < 0 || destIndex > destCount) throw new ArgumentOutOfRangeException("destIndex"); if (count < 0) throw new ArgumentOutOfRangeException("count"); using (IEnumerator sourceEnum = source.GetEnumerator()) { // First, overwrite items to the end of the destination list. while (destIndex < destCount && count > 0 && sourceEnum.MoveNext()) { dest[destIndex++] = sourceEnum.Current; --count; } // Second, insert items until done. while (count > 0 && sourceEnum.MoveNext()) { dest.Insert(destCount++, sourceEnum.Current); --count; } } } /// /// Copies at most items from the collection to the array , starting /// at the index . The source collection must not be /// the destination array or part thereof. /// /// The collection that provide the source items. /// The array to store the items into. /// The index to begin copying items to. /// The maximum number of items to copy. The array must be large enought to fit this number of items. /// is negative or /// greater than .Length. /// is negative or + /// is greater than .Length. /// or is null. public static void Copy(IEnumerable source, T[] dest, int destIndex, int count) { if (source == null) throw new ArgumentNullException("source"); if (dest == null) throw new ArgumentNullException("dest"); int destCount = dest.Length; if (destIndex < 0 || destIndex > destCount) throw new ArgumentOutOfRangeException("destIndex"); if (count < 0 || destIndex + count > destCount) throw new ArgumentOutOfRangeException("count"); using (IEnumerator sourceEnum = source.GetEnumerator()) { // First, overwrite items to the end of the destination array. while (destIndex < destCount && count > 0 && sourceEnum.MoveNext()) { dest[destIndex++] = sourceEnum.Current; --count; } } } /// /// Copies items from the list , starting at the index , /// to the list , starting at the index . If necessary, the size of the destination list is expanded. /// The source and destination lists may be the same. /// /// The collection that provide the source items. /// The index within to begin copying items from. /// The list to store the items into. /// The index within to begin copying items to. /// The maximum number of items to copy. /// is negative or /// greater than .Count /// is negative or /// greater than .Count /// is negative or too large. /// or is null. public static void Copy(IList source, int sourceIndex, IList dest, int destIndex, int count) { if (source == null) throw new ArgumentNullException("source"); if (dest == null) throw new ArgumentNullException("dest"); if (dest.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "dest"); int sourceCount = source.Count; int destCount = dest.Count; if (sourceIndex < 0 || sourceIndex >= sourceCount) throw new ArgumentOutOfRangeException("sourceIndex"); if (destIndex < 0 || destIndex > destCount) throw new ArgumentOutOfRangeException("destIndex"); if (count < 0) throw new ArgumentOutOfRangeException("count"); if (count > sourceCount - sourceIndex) count = sourceCount - sourceIndex; if (source == dest && sourceIndex > destIndex) { while (count > 0) { dest[destIndex++] = source[sourceIndex++]; --count; } } else { int si, di; // First, insert any items needed at the end if (destIndex + count > destCount) { int numberToInsert = destIndex + count - destCount; si = sourceIndex + (count - numberToInsert); di = destCount; count -= numberToInsert; while (numberToInsert > 0) { dest.Insert(di++, source[si++]); --numberToInsert; } } // Do the copy, from end to beginning in case of overlap. si = sourceIndex + count - 1; di = destIndex + count - 1; while (count > 0) { dest[di--] = source[si--]; --count; } } } /// /// Copies items from the list or array , starting at the index , /// to the array , starting at the index . /// The source may be the same as the destination array. /// /// The list or array that provide the source items. /// The index within to begin copying items from. /// The array to store the items into. /// The index within to begin copying items to. /// The maximum number of items to copy. The destination array must be large enough to hold this many items. /// is negative or /// greater than .Count /// is negative or /// greater than .Length /// is negative or too large. /// or is null. public static void Copy(IList source, int sourceIndex, T[] dest, int destIndex, int count) { if (source == null) throw new ArgumentNullException("source"); if (dest == null) throw new ArgumentNullException("dest"); int sourceCount = source.Count; int destCount = dest.Length; if (sourceIndex < 0 || sourceIndex >= sourceCount) throw new ArgumentOutOfRangeException("sourceIndex"); if (destIndex < 0 || destIndex > destCount) throw new ArgumentOutOfRangeException("destIndex"); if (count < 0 || destIndex + count > destCount) throw new ArgumentOutOfRangeException("count"); if (count > sourceCount - sourceIndex) count = sourceCount - sourceIndex; if (source is T[]) { // Array.Copy is probably faster, and also handles any overlapping issues. Array.Copy((T[])source, sourceIndex, dest, destIndex, count); } else { int si = sourceIndex; int di = destIndex; while (count > 0) { dest[di++] = source[si++]; --count; } } } /// /// Reverses a list and returns the reversed list, without changing the source list. /// /// The list to reverse. /// A collection that contains the items from in reverse order. /// is null. public static IEnumerable Reverse(IList source) { if (source == null) throw new ArgumentNullException("source"); for (int i = source.Count - 1; i >= 0; --i) yield return source[i]; } /// /// Reverses a list or array in place. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to reverse. /// is null. /// is read only. public static void ReverseInPlace(IList list) { if (list == null) throw new ArgumentNullException("list"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); int i, j; i = 0; j = list.Count - 1; while (i < j) { T temp = list[i]; list[i] = list[j]; list[j] = temp; i++; j--; } } /// /// Rotates a list and returns the rotated list, without changing the source list. /// /// The list to rotate. /// The number of elements to rotate. This value can be positive or negative. /// For example, rotating by positive 3 means that source[3] is the first item in the returned collection. /// Rotating by negative 3 means that source[source.Count - 3] is the first item in the returned collection. /// A collection that contains the items from in rotated order. /// is null. public static IEnumerable Rotate(IList source, int amountToRotate) { if (source == null) throw new ArgumentNullException("source"); int count = source.Count; if (count != 0) { amountToRotate = amountToRotate % count; if (amountToRotate < 0) amountToRotate += count; // Do it in two parts. for (int i = amountToRotate; i < count; ++i) yield return source[i]; for (int i = 0; i < amountToRotate; ++i) yield return source[i]; } } /// /// Rotates a list or array in place. /// /// Although arrays cast to IList<T> are normally read-only, this method /// will work correctly and modify an array passed as . /// The list or array to rotate. /// The number of elements to rotate. This value can be positive or negative. /// For example, rotating by positive 3 means that list[3] is the first item in the resulting list. /// Rotating by negative 3 means that list[list.Count - 3] is the first item in the resulting list. /// is null. public static void RotateInPlace(IList list, int amountToRotate) { if (list == null) throw new ArgumentNullException("list"); if (list is T[]) list = new ArrayWrapper((T[])list); if (list.IsReadOnly) throw new ArgumentException(Strings.ListIsReadOnly, "list"); int count = list.Count; if (count != 0) { amountToRotate = amountToRotate % count; if (amountToRotate < 0) amountToRotate += count; int itemsLeft = count; int indexStart = 0; while (itemsLeft > 0) { // Rotate an orbit of items through the list. If itemsLeft is relatively prime // to count, this will rotate everything. If not, we need to do this several times until // all items have been moved. int index = indexStart; T itemStart = list[indexStart]; for (;;) { --itemsLeft; int nextIndex = index + amountToRotate; if (nextIndex >= count) nextIndex -= count; if (nextIndex == indexStart) { list[index] = itemStart; break; } else { list[index] = list[nextIndex]; index = nextIndex; } } // Move to the next orbit. ++indexStart; } } } #endregion Miscellaneous operations on IList } }