blob: 66f960a7993e802d59390b708c592665335b06b5 [file] [log] [blame]
using J2N.Numerics;
using Lucene.Net.Diagnostics;
using System;
using System.Runtime.CompilerServices;
namespace Lucene.Net.Util
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <summary>
/// <see cref="Sorter"/> implementation based on the
/// <a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">TimSort</a>
/// algorithm.
/// <para/>This implementation is especially good at sorting partially-sorted
/// arrays and sorts small arrays with binary sort.
/// <para/><b>NOTE</b>:There are a few differences with the original implementation:
/// <list type="bullet">
/// <item><description><a name="maxTempSlots"/>The extra amount of memory to perform merges is
/// configurable. This allows small merges to be very fast while large merges
/// will be performed in-place (slightly slower). You can make sure that the
/// fast merge routine will always be used by having <c>maxTempSlots</c>
/// equal to half of the length of the slice of data to sort.</description></item>
/// <item><description>Only the fast merge routine can gallop (the one that doesn't run
/// in-place) and it only gallops on the longest slice.</description></item>
/// </list>
/// <para/>
/// @lucene.internal
/// </summary>
public abstract class TimSorter : Sorter
{
internal const int MINRUN = 32;
internal new const int THRESHOLD = 64;
internal const int STACKSIZE = 40; // depends on MINRUN
internal const int MIN_GALLOP = 7;
internal readonly int maxTempSlots;
internal int minRun;
internal int to;
internal int stackSize;
internal int[] runEnds;
/// <summary>
/// Create a new <see cref="TimSorter"/>. </summary>
/// <param name="maxTempSlots"> The <a href="#maxTempSlots">maximum amount of extra memory to run merges</a> </param>
protected TimSorter(int maxTempSlots)
: base()
{
runEnds = new int[1 + STACKSIZE];
this.maxTempSlots = maxTempSlots;
}
/// <summary>
/// Minimum run length for an array of length <paramref name="length"/>. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int MinRun(int length)
{
if (Debugging.AssertsEnabled) Debugging.Assert(length >= MINRUN);
int n = length;
int r = 0;
while (n >= 64)
{
r |= n & 1;
n = n.TripleShift(1);
}
int minRun = n + r;
if (Debugging.AssertsEnabled) Debugging.Assert(minRun >= MINRUN && minRun <= THRESHOLD);
return minRun;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int RunLen(int i)
{
int off = stackSize - i;
return runEnds[off] - runEnds[off - 1];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int RunBase(int i)
{
return runEnds[stackSize - i - 1];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int RunEnd(int i) // LUCENENET TODO: API - change to indexer
{
return runEnds[stackSize - i];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void SetRunEnd(int i, int runEnd)
{
runEnds[stackSize - i] = runEnd;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void PushRunLen(int len)
{
runEnds[stackSize + 1] = runEnds[stackSize] + len;
++stackSize;
}
/// <summary>
/// Compute the length of the next run, make the run sorted and return its
/// length.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int NextRun()
{
int runBase = RunEnd(0);
if (Debugging.AssertsEnabled) Debugging.Assert(runBase < to);
if (runBase == to - 1)
{
return 1;
}
int o = runBase + 2;
if (Compare(runBase, runBase + 1) > 0)
{
// run must be strictly descending
while (o < to && Compare(o - 1, o) > 0)
{
++o;
}
Reverse(runBase, o);
}
else
{
// run must be non-descending
while (o < to && Compare(o - 1, o) <= 0)
{
++o;
}
}
int runHi = Math.Max(o, Math.Min(to, runBase + minRun));
BinarySort(runBase, runHi, o);
return runHi - runBase;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void EnsureInvariants()
{
while (stackSize > 1)
{
int runLen0 = RunLen(0);
int runLen1 = RunLen(1);
if (stackSize > 2)
{
int runLen2 = RunLen(2);
if (runLen2 <= runLen1 + runLen0)
{
// merge the smaller of 0 and 2 with 1
if (runLen2 < runLen0)
{
MergeAt(1);
}
else
{
MergeAt(0);
}
continue;
}
}
if (runLen1 <= runLen0)
{
MergeAt(0);
continue;
}
break;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void ExhaustStack()
{
while (stackSize > 1)
{
MergeAt(0);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void Reset(int from, int to)
{
stackSize = 0;
Array.Clear(runEnds, 0, runEnds.Length);
runEnds[0] = from;
this.to = to;
int length = to - from;
this.minRun = length <= THRESHOLD ? length : MinRun(length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void MergeAt(int n)
{
if (Debugging.AssertsEnabled) Debugging.Assert(stackSize >= 2);
Merge(RunBase(n + 1), RunBase(n), RunEnd(n));
for (int j = n + 1; j > 0; --j)
{
SetRunEnd(j, RunEnd(j - 1));
}
--stackSize;
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal virtual void Merge(int lo, int mid, int hi)
{
if (Compare(mid - 1, mid) <= 0)
{
return;
}
lo = Upper2(lo, mid, mid);
hi = Lower2(mid, hi, mid - 1);
if (hi - mid <= mid - lo && hi - mid <= maxTempSlots)
{
MergeHi(lo, mid, hi);
}
else if (mid - lo <= maxTempSlots)
{
MergeLo(lo, mid, hi);
}
else
{
MergeInPlace(lo, mid, hi);
}
}
/// <summary>
/// Sort the slice which starts at <paramref name="from"/> (inclusive) and ends at
/// <paramref name="to"/> (exclusive).
/// </summary>
public override void Sort(int from, int to)
{
CheckRange(from, to);
if (to - from <= 1)
{
return;
}
Reset(from, to);
do
{
EnsureInvariants();
PushRunLen(NextRun());
} while (RunEnd(0) < to);
ExhaustStack();
if (Debugging.AssertsEnabled) Debugging.Assert(RunEnd(0) == to);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override void DoRotate(int lo, int mid, int hi)
{
int len1 = mid - lo;
int len2 = hi - mid;
if (len1 == len2)
{
while (mid < hi)
{
Swap(lo++, mid++);
}
}
else if (len2 < len1 && len2 <= maxTempSlots)
{
Save(mid, len2);
for (int i = lo + len1 - 1, j = hi - 1; i >= lo; --i, --j)
{
Copy(i, j);
}
for (int i = 0, j = lo; i < len2; ++i, ++j)
{
Restore(i, j);
}
}
else if (len1 <= maxTempSlots)
{
Save(lo, len1);
for (int i = mid, j = lo; i < hi; ++i, ++j)
{
Copy(i, j);
}
for (int i = 0, j = lo + len2; j < hi; ++i, ++j)
{
Restore(i, j);
}
}
else
{
Reverse(lo, mid);
Reverse(mid, hi);
Reverse(lo, hi);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void MergeLo(int lo, int mid, int hi)
{
if (Debugging.AssertsEnabled) Debugging.Assert(Compare(lo, mid) > 0);
int len1 = mid - lo;
Save(lo, len1);
Copy(mid, lo);
int i = 0, j = mid + 1, dest = lo + 1;
for (; ; )
{
for (int count = 0; count < MIN_GALLOP; )
{
if (i >= len1 || j >= hi)
{
goto outerBreak;
}
else if (CompareSaved(i, j) <= 0)
{
Restore(i++, dest++);
count = 0;
}
else
{
Copy(j++, dest++);
++count;
}
}
// galloping...
int next = LowerSaved3(j, hi, i);
for (; j < next; ++dest)
{
Copy(j++, dest);
}
Restore(i++, dest++);
//outerContinue: ; // LUCENENET NOTE: Not referenced
}
outerBreak:
for (; i < len1; ++dest)
{
Restore(i++, dest);
}
if (Debugging.AssertsEnabled) Debugging.Assert(j == dest);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual void MergeHi(int lo, int mid, int hi)
{
if (Debugging.AssertsEnabled) Debugging.Assert(Compare(mid - 1, hi - 1) > 0);
int len2 = hi - mid;
Save(mid, len2);
Copy(mid - 1, hi - 1);
int i = mid - 2, j = len2 - 1, dest = hi - 2;
for (; ; )
{
for (int count = 0; count < MIN_GALLOP; )
{
if (i < lo || j < 0)
{
goto outerBreak;
}
else if (CompareSaved(j, i) >= 0)
{
Restore(j--, dest--);
count = 0;
}
else
{
Copy(i--, dest--);
++count;
}
}
// galloping
int next = UpperSaved3(lo, i + 1, j);
while (i >= next)
{
Copy(i--, dest--);
}
Restore(j--, dest--);
//outerContinue: ; // LUCENENET NOTE: Not referenced
}
outerBreak:
for (; j >= 0; --dest)
{
Restore(j--, dest);
}
if (Debugging.AssertsEnabled) Debugging.Assert(i == dest);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int LowerSaved(int from, int to, int val)
{
int len = to - from;
while (len > 0)
{
int half = len.TripleShift(1);
int mid = from + half;
if (CompareSaved(val, mid) > 0)
{
from = mid + 1;
len = len - half - 1;
}
else
{
len = half;
}
}
return from;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int UpperSaved(int from, int to, int val)
{
int len = to - from;
while (len > 0)
{
int half = len.TripleShift(1);
int mid = from + half;
if (CompareSaved(val, mid) < 0)
{
len = half;
}
else
{
from = mid + 1;
len = len - half - 1;
}
}
return from;
}
// faster than lowerSaved when val is at the beginning of [from:to[
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int LowerSaved3(int from, int to, int val)
{
int f = from, t = f + 1;
while (t < to)
{
if (CompareSaved(val, t) <= 0)
{
return LowerSaved(f, t, val);
}
int delta = t - f;
f = t;
t += delta << 1;
}
return LowerSaved(f, to, val);
}
//faster than upperSaved when val is at the end of [from:to[
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal virtual int UpperSaved3(int from, int to, int val)
{
int f = to - 1, t = to;
while (f > from)
{
if (CompareSaved(val, f) >= 0)
{
return UpperSaved(f, t, val);
}
int delta = t - f;
t = f;
f -= delta << 1;
}
return UpperSaved(from, t, val);
}
/// <summary>
/// Copy data from slot <paramref name="src"/> to slot <paramref name="dest"/>>. </summary>
protected abstract void Copy(int src, int dest);
/// <summary>
/// Save all elements between slots <paramref name="i"/> and <paramref name="i"/>+<paramref name="len"/>
/// into the temporary storage.
/// </summary>
protected abstract void Save(int i, int len);
/// <summary>
/// Restore element <paramref name="j"/> from the temporary storage into slot <paramref name="i"/>. </summary>
protected abstract void Restore(int i, int j);
/// <summary>
/// Compare element <paramref name="i"/> from the temporary storage with element
/// <paramref name="j"/> from the slice to sort, similarly to
/// <see cref="Sorter.Compare(int, int)"/>.
/// </summary>
protected abstract int CompareSaved(int i, int j);
}
}