1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 /*============================================================
11 ** Purpose: class to sort arrays
14 ===========================================================*/
17 using System.Globalization;
18 using System.Runtime.CompilerServices;
19 using System.Diagnostics;
20 using System.Diagnostics.Contracts;
21 using System.Runtime.Versioning;
23 using System.Diagnostics.Private;
26 namespace System.Collections.Generic
28 #region ArraySortHelper for single arrays
30 internal interface IArraySortHelper<TKey>
32 void Sort(TKey[] keys, int index, int length, IComparer<TKey> comparer);
33 int BinarySearch(TKey[] keys, int index, int length, TKey value, IComparer<TKey> comparer);
36 internal static class IntrospectiveSortUtilities
38 // This is the threshold where Introspective sort switches to Insertion sort.
39 // Imperically, 16 seems to speed up most cases without slowing down others, at least for integers.
40 // Large value types may benefit from a smaller number.
41 internal const int IntrosortSizeThreshold = 16;
43 internal static int FloorLog2(int n)
54 internal static void ThrowOrIgnoreBadComparer(Object comparer)
56 throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", comparer));
60 [TypeDependencyAttribute("System.Collections.Generic.GenericArraySortHelper`1")]
61 internal class ArraySortHelper<T>
64 private static volatile IArraySortHelper<T> defaultArraySortHelper;
66 public static IArraySortHelper<T> Default
70 IArraySortHelper<T> sorter = defaultArraySortHelper;
72 sorter = CreateArraySortHelper();
78 private static IArraySortHelper<T> CreateArraySortHelper()
81 if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
83 defaultArraySortHelper = (IArraySortHelper<T>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string>).TypeHandle.Instantiate(new Type[] { typeof(T) }));
88 defaultArraySortHelper = new ArraySortHelper<T>();
90 return defaultArraySortHelper;
93 #region IArraySortHelper<T> Members
95 public void Sort(T[] keys, int index, int length, IComparer<T> comparer)
97 Debug.Assert(keys != null, "Check the arguments in the caller!");
98 Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
100 // Add a try block here to detect IComparers (or their
101 // underlying IComparables, etc) that are bogus.
104 if (comparer == null)
106 comparer = Comparer<T>.Default;
109 IntrospectiveSort(keys, index, length, comparer.Compare);
111 catch (IndexOutOfRangeException)
113 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
117 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
121 public int BinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
125 if (comparer == null)
127 comparer = Comparer<T>.Default;
130 return InternalBinarySearch(array, index, length, value, comparer);
134 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
140 internal static void Sort(T[] keys, int index, int length, Comparison<T> comparer)
142 Debug.Assert(keys != null, "Check the arguments in the caller!");
143 Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
144 Debug.Assert(comparer != null, "Check the arguments in the caller!");
146 // Add a try block here to detect bogus comparisons
149 IntrospectiveSort(keys, index, length, comparer);
151 catch (IndexOutOfRangeException)
153 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
157 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
161 internal static int InternalBinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
163 Contract.Requires(array != null, "Check the arguments in the caller!");
164 Contract.Requires(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
167 int hi = index + length - 1;
170 int i = lo + ((hi - lo) >> 1);
171 int order = comparer.Compare(array[i], value);
173 if (order == 0) return i;
187 private static void SwapIfGreater(T[] keys, Comparison<T> comparer, int a, int b)
191 if (comparer(keys[a], keys[b]) > 0)
200 private static void Swap(T[] a, int i, int j)
210 internal static void IntrospectiveSort(T[] keys, int left, int length, Comparison<T> comparer)
212 Contract.Requires(keys != null);
213 Contract.Requires(comparer != null);
214 Contract.Requires(left >= 0);
215 Contract.Requires(length >= 0);
216 Contract.Requires(length <= keys.Length);
217 Contract.Requires(length + left <= keys.Length);
222 IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer);
225 private static void IntroSort(T[] keys, int lo, int hi, int depthLimit, Comparison<T> comparer)
227 Contract.Requires(keys != null);
228 Contract.Requires(comparer != null);
229 Contract.Requires(lo >= 0);
230 Contract.Requires(hi < keys.Length);
234 int partitionSize = hi - lo + 1;
235 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
237 if (partitionSize == 1)
241 if (partitionSize == 2)
243 SwapIfGreater(keys, comparer, lo, hi);
246 if (partitionSize == 3)
248 SwapIfGreater(keys, comparer, lo, hi - 1);
249 SwapIfGreater(keys, comparer, lo, hi);
250 SwapIfGreater(keys, comparer, hi - 1, hi);
254 InsertionSort(keys, lo, hi, comparer);
260 Heapsort(keys, lo, hi, comparer);
265 int p = PickPivotAndPartition(keys, lo, hi, comparer);
266 // Note we've already partitioned around the pivot and do not have to move the pivot again.
267 IntroSort(keys, p + 1, hi, depthLimit, comparer);
272 private static int PickPivotAndPartition(T[] keys, int lo, int hi, Comparison<T> comparer)
274 Contract.Requires(keys != null);
275 Contract.Requires(comparer != null);
276 Contract.Requires(lo >= 0);
277 Contract.Requires(hi > lo);
278 Contract.Requires(hi < keys.Length);
279 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
281 // Compute median-of-three. But also partition them, since we've done the comparison.
282 int middle = lo + ((hi - lo) / 2);
284 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
285 SwapIfGreater(keys, comparer, lo, middle); // swap the low with the mid point
286 SwapIfGreater(keys, comparer, lo, hi); // swap the low with the high
287 SwapIfGreater(keys, comparer, middle, hi); // swap the middle with the high
289 T pivot = keys[middle];
290 Swap(keys, middle, hi - 1);
291 int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
295 while (comparer(keys[++left], pivot) < 0) ;
296 while (comparer(pivot, keys[--right]) < 0) ;
301 Swap(keys, left, right);
304 // Put pivot in the right location.
305 Swap(keys, left, (hi - 1));
309 private static void Heapsort(T[] keys, int lo, int hi, Comparison<T> comparer)
311 Contract.Requires(keys != null);
312 Contract.Requires(comparer != null);
313 Contract.Requires(lo >= 0);
314 Contract.Requires(hi > lo);
315 Contract.Requires(hi < keys.Length);
318 for (int i = n / 2; i >= 1; i = i - 1)
320 DownHeap(keys, i, n, lo, comparer);
322 for (int i = n; i > 1; i = i - 1)
324 Swap(keys, lo, lo + i - 1);
325 DownHeap(keys, 1, i - 1, lo, comparer);
329 private static void DownHeap(T[] keys, int i, int n, int lo, Comparison<T> comparer)
331 Contract.Requires(keys != null);
332 Contract.Requires(comparer != null);
333 Contract.Requires(lo >= 0);
334 Contract.Requires(lo < keys.Length);
336 T d = keys[lo + i - 1];
341 if (child < n && comparer(keys[lo + child - 1], keys[lo + child]) < 0)
345 if (!(comparer(d, keys[lo + child - 1]) < 0))
347 keys[lo + i - 1] = keys[lo + child - 1];
350 keys[lo + i - 1] = d;
353 private static void InsertionSort(T[] keys, int lo, int hi, Comparison<T> comparer)
355 Contract.Requires(keys != null);
356 Contract.Requires(lo >= 0);
357 Contract.Requires(hi >= lo);
358 Contract.Requires(hi <= keys.Length);
362 for (i = lo; i < hi; i++)
366 while (j >= lo && comparer(t, keys[j]) < 0)
368 keys[j + 1] = keys[j];
377 internal class GenericArraySortHelper<T>
378 : IArraySortHelper<T>
379 where T : IComparable<T>
381 // Do not add a constructor to this class because ArraySortHelper<T>.CreateSortHelper will not execute it
383 #region IArraySortHelper<T> Members
385 public void Sort(T[] keys, int index, int length, IComparer<T> comparer)
387 Debug.Assert(keys != null, "Check the arguments in the caller!");
388 Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
392 if (comparer == null || comparer == Comparer<T>.Default)
394 IntrospectiveSort(keys, index, length);
398 ArraySortHelper<T>.IntrospectiveSort(keys, index, length, comparer.Compare);
401 catch (IndexOutOfRangeException)
403 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
407 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
411 public int BinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
413 Debug.Assert(array != null, "Check the arguments in the caller!");
414 Debug.Assert(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
418 if (comparer == null || comparer == Comparer<T>.Default)
420 return BinarySearch(array, index, length, value);
424 return ArraySortHelper<T>.InternalBinarySearch(array, index, length, value, comparer);
429 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
435 // This function is called when the user doesn't specify any comparer.
436 // Since T is constrained here, we can call IComparable<T>.CompareTo here.
437 // We can avoid boxing for value type and casting for reference types.
438 private static int BinarySearch(T[] array, int index, int length, T value)
441 int hi = index + length - 1;
444 int i = lo + ((hi - lo) >> 1);
446 if (array[i] == null)
448 order = (value == null) ? 0 : -1;
452 order = array[i].CompareTo(value);
473 private static void SwapIfGreaterWithItems(T[] keys, int a, int b)
475 Contract.Requires(keys != null);
476 Contract.Requires(0 <= a && a < keys.Length);
477 Contract.Requires(0 <= b && b < keys.Length);
481 if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
490 private static void Swap(T[] a, int i, int j)
500 internal static void IntrospectiveSort(T[] keys, int left, int length)
502 Contract.Requires(keys != null);
503 Contract.Requires(left >= 0);
504 Contract.Requires(length >= 0);
505 Contract.Requires(length <= keys.Length);
506 Contract.Requires(length + left <= keys.Length);
511 IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length));
514 private static void IntroSort(T[] keys, int lo, int hi, int depthLimit)
516 Contract.Requires(keys != null);
517 Contract.Requires(lo >= 0);
518 Contract.Requires(hi < keys.Length);
522 int partitionSize = hi - lo + 1;
523 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
525 if (partitionSize == 1)
529 if (partitionSize == 2)
531 SwapIfGreaterWithItems(keys, lo, hi);
534 if (partitionSize == 3)
536 SwapIfGreaterWithItems(keys, lo, hi - 1);
537 SwapIfGreaterWithItems(keys, lo, hi);
538 SwapIfGreaterWithItems(keys, hi - 1, hi);
542 InsertionSort(keys, lo, hi);
548 Heapsort(keys, lo, hi);
553 int p = PickPivotAndPartition(keys, lo, hi);
554 // Note we've already partitioned around the pivot and do not have to move the pivot again.
555 IntroSort(keys, p + 1, hi, depthLimit);
560 private static int PickPivotAndPartition(T[] keys, int lo, int hi)
562 Contract.Requires(keys != null);
563 Contract.Requires(lo >= 0);
564 Contract.Requires(hi > lo);
565 Contract.Requires(hi < keys.Length);
566 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
568 // Compute median-of-three. But also partition them, since we've done the comparison.
569 int middle = lo + ((hi - lo) / 2);
571 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
572 SwapIfGreaterWithItems(keys, lo, middle); // swap the low with the mid point
573 SwapIfGreaterWithItems(keys, lo, hi); // swap the low with the high
574 SwapIfGreaterWithItems(keys, middle, hi); // swap the middle with the high
576 T pivot = keys[middle];
577 Swap(keys, middle, hi - 1);
578 int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
584 while (left < (hi - 1) && keys[++left] == null) ;
585 while (right > lo && keys[--right] != null) ;
589 while (pivot.CompareTo(keys[++left]) > 0) ;
590 while (pivot.CompareTo(keys[--right]) < 0) ;
596 Swap(keys, left, right);
599 // Put pivot in the right location.
600 Swap(keys, left, (hi - 1));
604 private static void Heapsort(T[] keys, int lo, int hi)
606 Contract.Requires(keys != null);
607 Contract.Requires(lo >= 0);
608 Contract.Requires(hi > lo);
609 Contract.Requires(hi < keys.Length);
612 for (int i = n / 2; i >= 1; i = i - 1)
614 DownHeap(keys, i, n, lo);
616 for (int i = n; i > 1; i = i - 1)
618 Swap(keys, lo, lo + i - 1);
619 DownHeap(keys, 1, i - 1, lo);
623 private static void DownHeap(T[] keys, int i, int n, int lo)
625 Contract.Requires(keys != null);
626 Contract.Requires(lo >= 0);
627 Contract.Requires(lo < keys.Length);
629 T d = keys[lo + i - 1];
634 if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
638 if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
640 keys[lo + i - 1] = keys[lo + child - 1];
643 keys[lo + i - 1] = d;
646 private static void InsertionSort(T[] keys, int lo, int hi)
648 Contract.Requires(keys != null);
649 Contract.Requires(lo >= 0);
650 Contract.Requires(hi >= lo);
651 Contract.Requires(hi <= keys.Length);
655 for (i = lo; i < hi; i++)
659 while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
661 keys[j + 1] = keys[j];
671 #region ArraySortHelper for paired key and value arrays
673 internal interface IArraySortHelper<TKey, TValue>
675 void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer);
678 [TypeDependencyAttribute("System.Collections.Generic.GenericArraySortHelper`2")]
679 internal class ArraySortHelper<TKey, TValue>
680 : IArraySortHelper<TKey, TValue>
682 private static volatile IArraySortHelper<TKey, TValue> defaultArraySortHelper;
684 public static IArraySortHelper<TKey, TValue> Default
688 IArraySortHelper<TKey, TValue> sorter = defaultArraySortHelper;
690 sorter = CreateArraySortHelper();
696 private static IArraySortHelper<TKey, TValue> CreateArraySortHelper()
699 if (typeof(IComparable<TKey>).IsAssignableFrom(typeof(TKey)))
701 defaultArraySortHelper = (IArraySortHelper<TKey, TValue>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string, string>).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }));
706 defaultArraySortHelper = new ArraySortHelper<TKey, TValue>();
708 return defaultArraySortHelper;
711 public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer)
713 Debug.Assert(keys != null, "Check the arguments in the caller!"); // Precondition on interface method
714 Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
716 // Add a try block here to detect IComparers (or their
717 // underlying IComparables, etc) that are bogus.
720 if (comparer == null || comparer == Comparer<TKey>.Default)
722 comparer = Comparer<TKey>.Default;
725 IntrospectiveSort(keys, values, index, length, comparer);
727 catch (IndexOutOfRangeException)
729 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
733 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
737 private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, IComparer<TKey> comparer, int a, int b)
739 Contract.Requires(keys != null);
740 Contract.Requires(values == null || values.Length >= keys.Length);
741 Contract.Requires(comparer != null);
742 Contract.Requires(0 <= a && a < keys.Length);
743 Contract.Requires(0 <= b && b < keys.Length);
747 if (comparer.Compare(keys[a], keys[b]) > 0)
754 TValue value = values[a];
755 values[a] = values[b];
762 private static void Swap(TKey[] keys, TValue[] values, int i, int j)
771 TValue v = values[i];
772 values[i] = values[j];
778 internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length, IComparer<TKey> comparer)
780 Contract.Requires(keys != null);
781 Contract.Requires(values != null);
782 Contract.Requires(comparer != null);
783 Contract.Requires(left >= 0);
784 Contract.Requires(length >= 0);
785 Contract.Requires(length <= keys.Length);
786 Contract.Requires(length + left <= keys.Length);
787 Contract.Requires(length + left <= values.Length);
792 IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer);
795 private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit, IComparer<TKey> comparer)
797 Contract.Requires(keys != null);
798 Contract.Requires(values != null);
799 Contract.Requires(comparer != null);
800 Contract.Requires(lo >= 0);
801 Contract.Requires(hi < keys.Length);
805 int partitionSize = hi - lo + 1;
806 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
808 if (partitionSize == 1)
812 if (partitionSize == 2)
814 SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
817 if (partitionSize == 3)
819 SwapIfGreaterWithItems(keys, values, comparer, lo, hi - 1);
820 SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
821 SwapIfGreaterWithItems(keys, values, comparer, hi - 1, hi);
825 InsertionSort(keys, values, lo, hi, comparer);
831 Heapsort(keys, values, lo, hi, comparer);
836 int p = PickPivotAndPartition(keys, values, lo, hi, comparer);
837 // Note we've already partitioned around the pivot and do not have to move the pivot again.
838 IntroSort(keys, values, p + 1, hi, depthLimit, comparer);
843 private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
845 Contract.Requires(keys != null);
846 Contract.Requires(values != null);
847 Contract.Requires(comparer != null);
848 Contract.Requires(lo >= 0);
849 Contract.Requires(hi > lo);
850 Contract.Requires(hi < keys.Length);
851 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
853 // Compute median-of-three. But also partition them, since we've done the comparison.
854 int middle = lo + ((hi - lo) / 2);
856 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
857 SwapIfGreaterWithItems(keys, values, comparer, lo, middle); // swap the low with the mid point
858 SwapIfGreaterWithItems(keys, values, comparer, lo, hi); // swap the low with the high
859 SwapIfGreaterWithItems(keys, values, comparer, middle, hi); // swap the middle with the high
861 TKey pivot = keys[middle];
862 Swap(keys, values, middle, hi - 1);
863 int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
867 while (comparer.Compare(keys[++left], pivot) < 0) ;
868 while (comparer.Compare(pivot, keys[--right]) < 0) ;
873 Swap(keys, values, left, right);
876 // Put pivot in the right location.
877 Swap(keys, values, left, (hi - 1));
881 private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
883 Contract.Requires(keys != null);
884 Contract.Requires(values != null);
885 Contract.Requires(comparer != null);
886 Contract.Requires(lo >= 0);
887 Contract.Requires(hi > lo);
888 Contract.Requires(hi < keys.Length);
891 for (int i = n / 2; i >= 1; i = i - 1)
893 DownHeap(keys, values, i, n, lo, comparer);
895 for (int i = n; i > 1; i = i - 1)
897 Swap(keys, values, lo, lo + i - 1);
898 DownHeap(keys, values, 1, i - 1, lo, comparer);
902 private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo, IComparer<TKey> comparer)
904 Contract.Requires(keys != null);
905 Contract.Requires(comparer != null);
906 Contract.Requires(lo >= 0);
907 Contract.Requires(lo < keys.Length);
909 TKey d = keys[lo + i - 1];
910 TValue dValue = (values != null) ? values[lo + i - 1] : default(TValue);
915 if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
919 if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
921 keys[lo + i - 1] = keys[lo + child - 1];
923 values[lo + i - 1] = values[lo + child - 1];
926 keys[lo + i - 1] = d;
928 values[lo + i - 1] = dValue;
931 private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
933 Contract.Requires(keys != null);
934 Contract.Requires(values != null);
935 Contract.Requires(comparer != null);
936 Contract.Requires(lo >= 0);
937 Contract.Requires(hi >= lo);
938 Contract.Requires(hi <= keys.Length);
943 for (i = lo; i < hi; i++)
947 tValue = (values != null) ? values[i + 1] : default(TValue);
948 while (j >= lo && comparer.Compare(t, keys[j]) < 0)
950 keys[j + 1] = keys[j];
952 values[j + 1] = values[j];
957 values[j + 1] = tValue;
962 internal class GenericArraySortHelper<TKey, TValue>
963 : IArraySortHelper<TKey, TValue>
964 where TKey : IComparable<TKey>
966 public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer)
968 Debug.Assert(keys != null, "Check the arguments in the caller!");
969 Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
971 // Add a try block here to detect IComparers (or their
972 // underlying IComparables, etc) that are bogus.
975 if (comparer == null || comparer == Comparer<TKey>.Default)
977 IntrospectiveSort(keys, values, index, length);
981 ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, index, length, comparer);
984 catch (IndexOutOfRangeException)
986 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
990 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
994 private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, int a, int b)
998 if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
1005 TValue value = values[a];
1006 values[a] = values[b];
1013 private static void Swap(TKey[] keys, TValue[] values, int i, int j)
1022 TValue v = values[i];
1023 values[i] = values[j];
1029 internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length)
1031 Contract.Requires(keys != null);
1032 Contract.Requires(values != null);
1033 Contract.Requires(left >= 0);
1034 Contract.Requires(length >= 0);
1035 Contract.Requires(length <= keys.Length);
1036 Contract.Requires(length + left <= keys.Length);
1037 Contract.Requires(length + left <= values.Length);
1042 IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length));
1045 private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit)
1047 Contract.Requires(keys != null);
1048 Contract.Requires(values != null);
1049 Contract.Requires(lo >= 0);
1050 Contract.Requires(hi < keys.Length);
1054 int partitionSize = hi - lo + 1;
1055 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
1057 if (partitionSize == 1)
1061 if (partitionSize == 2)
1063 SwapIfGreaterWithItems(keys, values, lo, hi);
1066 if (partitionSize == 3)
1068 SwapIfGreaterWithItems(keys, values, lo, hi - 1);
1069 SwapIfGreaterWithItems(keys, values, lo, hi);
1070 SwapIfGreaterWithItems(keys, values, hi - 1, hi);
1074 InsertionSort(keys, values, lo, hi);
1078 if (depthLimit == 0)
1080 Heapsort(keys, values, lo, hi);
1085 int p = PickPivotAndPartition(keys, values, lo, hi);
1086 // Note we've already partitioned around the pivot and do not have to move the pivot again.
1087 IntroSort(keys, values, p + 1, hi, depthLimit);
1092 private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi)
1094 Contract.Requires(keys != null);
1095 Contract.Requires(values != null);
1096 Contract.Requires(lo >= 0);
1097 Contract.Requires(hi > lo);
1098 Contract.Requires(hi < keys.Length);
1099 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
1101 // Compute median-of-three. But also partition them, since we've done the comparison.
1102 int middle = lo + ((hi - lo) / 2);
1104 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
1105 SwapIfGreaterWithItems(keys, values, lo, middle); // swap the low with the mid point
1106 SwapIfGreaterWithItems(keys, values, lo, hi); // swap the low with the high
1107 SwapIfGreaterWithItems(keys, values, middle, hi); // swap the middle with the high
1109 TKey pivot = keys[middle];
1110 Swap(keys, values, middle, hi - 1);
1111 int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
1113 while (left < right)
1117 while (left < (hi - 1) && keys[++left] == null) ;
1118 while (right > lo && keys[--right] != null) ;
1122 while (pivot.CompareTo(keys[++left]) > 0) ;
1123 while (pivot.CompareTo(keys[--right]) < 0) ;
1129 Swap(keys, values, left, right);
1132 // Put pivot in the right location.
1133 Swap(keys, values, left, (hi - 1));
1137 private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi)
1139 Contract.Requires(keys != null);
1140 Contract.Requires(values != null);
1141 Contract.Requires(lo >= 0);
1142 Contract.Requires(hi > lo);
1143 Contract.Requires(hi < keys.Length);
1145 int n = hi - lo + 1;
1146 for (int i = n / 2; i >= 1; i = i - 1)
1148 DownHeap(keys, values, i, n, lo);
1150 for (int i = n; i > 1; i = i - 1)
1152 Swap(keys, values, lo, lo + i - 1);
1153 DownHeap(keys, values, 1, i - 1, lo);
1157 private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo)
1159 Contract.Requires(keys != null);
1160 Contract.Requires(lo >= 0);
1161 Contract.Requires(lo < keys.Length);
1163 TKey d = keys[lo + i - 1];
1164 TValue dValue = (values != null) ? values[lo + i - 1] : default(TValue);
1169 if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
1173 if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
1175 keys[lo + i - 1] = keys[lo + child - 1];
1177 values[lo + i - 1] = values[lo + child - 1];
1180 keys[lo + i - 1] = d;
1182 values[lo + i - 1] = dValue;
1185 private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi)
1187 Contract.Requires(keys != null);
1188 Contract.Requires(values != null);
1189 Contract.Requires(lo >= 0);
1190 Contract.Requires(hi >= lo);
1191 Contract.Requires(hi <= keys.Length);
1196 for (i = lo; i < hi; i++)
1200 tValue = (values != null) ? values[i + 1] : default(TValue);
1201 while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
1203 keys[j + 1] = keys[j];
1205 values[j + 1] = values[j];
1210 values[j + 1] = tValue;