//****************************** // Written by Peter Golde // Copyright (connector) 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 namespace Netron.Diagramming.Core { /// /// 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 { /// /// Create an IEnumerable that enumerates an array. Make sure that only enumerable stuff /// is used and no downcasts to ICollection are taken advantage of. /// /// An array. /// An IEnumerable cast of the array /// a /// The following code /// /// /// /// public static IEnumerable EnumerableFromArray(T[] array) { foreach (T t in array) yield return t; } #region String representations /// /// 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. internal static string ToString(IEnumerable collection) { return collection.GetType().Name;// ToString(collection); } /// /// 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. internal 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 builder.Append(pair.Key.ToString()); builder.Append("->"); if (pair.Value == null) builder.Append("null"); else builder.Append(pair.Value.ToString()); firstItem = false; } builder.Append("}"); return builder.ToString(); } #endregion String representations #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 . internal 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 . internal 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 . [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] internal static ICollection RemoveWhere(ICollection collection, Predicate predicate) { if (collection == null) throw new ArgumentNullException("collection"); if (predicate == null) throw new ArgumentNullException("predicate"); 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. internal 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. internal 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 . internal 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); } #endregion Predicate operations #region Sorting /// /// 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("Cannot sort a 'null' list."); if(comparer == null) throw new ArgumentNullException("The comparer is 'null'."); // 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("The list is readonly.", "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; } } } #endregion } }