osu/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs

166 lines
5.4 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace osu.Game.Rulesets.Mania.MathUtils
{
/// <summary>
/// Provides access to .NET4.0 unstable sorting methods.
/// </summary>
/// <remarks>
/// Source: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/arraysorthelper.cs
/// Copyright (c) Microsoft Corporation. All rights reserved.
/// </remarks>
internal static class LegacySortHelper<T>
{
private const int quick_sort_depth_threshold = 32;
public static void Sort(T[] keys, IComparer<T> comparer)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
if (keys.Length == 0)
return;
comparer ??= Comparer<T>.Default;
depthLimitedQuickSort(keys, 0, keys.Length - 1, comparer, quick_sort_depth_threshold);
}
private static void depthLimitedQuickSort(T[] keys, int left, int right, IComparer<T> comparer, int depthLimit)
{
do
{
if (depthLimit == 0)
{
heapsort(keys, left, right, comparer);
return;
}
int i = left;
int j = right;
// pre-sort the low, middle (pivot), and high values in place.
// this improves performance in the face of already sorted data, or
// data that is made up of multiple sorted runs appended together.
int middle = i + ((j - i) >> 1);
swapIfGreater(keys, comparer, i, middle); // swap the low with the mid point
swapIfGreater(keys, comparer, i, j); // swap the low with the high
swapIfGreater(keys, comparer, middle, j); // swap the middle with the high
T x = keys[middle];
do
{
while (comparer.Compare(keys[i], x) < 0) i++;
while (comparer.Compare(x, keys[j]) < 0) j--;
Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
if (i > j) break;
if (i < j)
{
T key = keys[i];
keys[i] = keys[j];
keys[j] = key;
}
i++;
j--;
} while (i <= j);
// The next iteration of the while loop is to "recursively" sort the larger half of the array and the
// following calls recrusively sort the smaller half. So we subtrack one from depthLimit here so
// both sorts see the new value.
depthLimit--;
if (j - left <= right - i)
{
if (left < j) depthLimitedQuickSort(keys, left, j, comparer, depthLimit);
left = i;
}
else
{
if (i < right) depthLimitedQuickSort(keys, i, right, comparer, depthLimit);
right = j;
}
} while (left < right);
}
private static void heapsort(T[] keys, int lo, int hi, IComparer<T> comparer)
{
Contract.Requires(keys != null);
Contract.Requires(comparer != null);
Contract.Requires(lo >= 0);
Contract.Requires(hi > lo);
Contract.Requires(hi < keys.Length);
int n = hi - lo + 1;
for (int i = n / 2; i >= 1; i = i - 1)
{
downHeap(keys, i, n, lo, comparer);
}
for (int i = n; i > 1; i = i - 1)
{
swap(keys, lo, lo + i - 1);
downHeap(keys, 1, i - 1, lo, comparer);
}
}
private static void downHeap(T[] keys, int i, int n, int lo, IComparer<T> comparer)
{
Contract.Requires(keys != null);
Contract.Requires(comparer != null);
Contract.Requires(lo >= 0);
Contract.Requires(lo < keys.Length);
T d = keys[lo + i - 1];
while (i <= n / 2)
{
var child = 2 * i;
if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
{
child++;
}
if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
break;
keys[lo + i - 1] = keys[lo + child - 1];
i = child;
}
keys[lo + i - 1] = d;
}
private static void swap(T[] a, int i, int j)
{
if (i != j)
{
T t = a[i];
a[i] = a[j];
a[j] = t;
}
}
private static void swapIfGreater(T[] keys, IComparer<T> comparer, int a, int b)
{
if (a != b)
{
if (comparer.Compare(keys[a], keys[b]) > 0)
{
T key = keys[a];
keys[a] = keys[b];
keys[b] = key;
}
}
}
}
}