434 lines
13 KiB
C#
434 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
using System.Threading.Tasks;
|
|
#endif
|
|
|
|
namespace BotSharp.Models.CRFLite.Utils
|
|
{
|
|
abstract public class BigArray<T> : IList<T> where T : IComparable<T>
|
|
{
|
|
public const long sizePerBlock = 1024 * 1024 * 64; //(<<26bits)
|
|
public const int moveBit = 26;
|
|
public long size_;
|
|
public List<T[]> arrList;
|
|
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
private ParallelOptions parallelOption;
|
|
private LimitedConcurrencyLevelTaskScheduler lcts;
|
|
#endif
|
|
public BigArray()
|
|
{
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
parallelOption = new ParallelOptions();
|
|
#endif
|
|
}
|
|
|
|
public int IndexOf(T item)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void Insert(int index, T item)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void RemoveAt(int index)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public T this[int i]
|
|
{
|
|
get
|
|
{
|
|
return this[(long)i];
|
|
}
|
|
set
|
|
{
|
|
this[(long)i] = value;
|
|
}
|
|
}
|
|
|
|
public abstract T this[long i]
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public void Add(T item)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
foreach (T[] item in arrList)
|
|
{
|
|
Array.Clear(item, 0, item.Length);
|
|
}
|
|
}
|
|
|
|
public bool Contains(T item)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void CopyTo(T[] array, int arrayIndex)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
return (int)LongLength;
|
|
}
|
|
}
|
|
|
|
public long LongLength
|
|
{
|
|
get
|
|
{
|
|
return size_;
|
|
}
|
|
}
|
|
|
|
public bool IsReadOnly
|
|
{
|
|
get { throw new NotImplementedException(); }
|
|
}
|
|
|
|
public bool Remove(T item)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
|
|
void swap(long pos1, long pos2)
|
|
{
|
|
int nBlock1 = (int)(pos1 >> moveBit);
|
|
int offset1 = (int)(pos1 & (sizePerBlock - 1));
|
|
|
|
int nBlock2 = (int)(pos2 >> moveBit);
|
|
int offset2 = (int)(pos2 & (sizePerBlock - 1));
|
|
|
|
T tmp = arrList[nBlock1][offset1];
|
|
arrList[nBlock1][offset1] = arrList[nBlock2][offset2];
|
|
arrList[nBlock2][offset2] = tmp;
|
|
}
|
|
|
|
|
|
private long med3(long a, long b, long c)
|
|
{
|
|
return this[a].CompareTo(this[b]) < 0 ? (this[b].CompareTo(this[c]) < 0 ? b : this[a].CompareTo(this[c]) < 0 ? c : a) : this[b].CompareTo(this[c]) > 0 ? b : this[a].CompareTo(this[c]) > 0 ? c : a;
|
|
}
|
|
|
|
private void vecswap(long a, long b, long n)
|
|
{
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
for (long i = 0;i < n;i++)
|
|
#else
|
|
Parallel.For(0, n, parallelOption, i =>
|
|
#endif
|
|
{
|
|
int nBlock1 = (int)((a + i) >> moveBit);
|
|
int offset1 = (int)((a + i) & (sizePerBlock - 1));
|
|
|
|
int nBlock2 = (int)((b + i) >> moveBit);
|
|
int offset2 = (int)((b + i) & (sizePerBlock - 1));
|
|
|
|
T tmp = arrList[nBlock1][offset1];
|
|
arrList[nBlock1][offset1] = arrList[nBlock2][offset2];
|
|
arrList[nBlock2][offset2] = tmp;
|
|
}
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
);
|
|
#endif
|
|
}
|
|
|
|
const int INSERT_SORT_THRESHOLD = 7;
|
|
public void QuickSort(long left, long right)
|
|
{
|
|
if (left >= right)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//use insert sort to handle small data
|
|
long len = right - left + 1;
|
|
if (len < INSERT_SORT_THRESHOLD)
|
|
{
|
|
for (long i = left; i <= right; i++)
|
|
{
|
|
T t = this[i];
|
|
long j = i;
|
|
for (; j > left && this[j - 1].CompareTo(t) > 0; j--)
|
|
{
|
|
this[j] = this[j - 1];
|
|
}
|
|
this[j] = t;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//Choose the pivot value
|
|
long mid = left + (len >> 1);
|
|
if (len > INSERT_SORT_THRESHOLD)
|
|
{
|
|
//Split the list into three parts, and find middle value in each part,
|
|
//and finally, use middle value in above three middle values as pivot.
|
|
long leftMid = left;
|
|
long rightMid = right;
|
|
if (len > 40)
|
|
{
|
|
long size = len / 8;
|
|
leftMid = med3(leftMid, leftMid + size, leftMid + 2 * size);
|
|
mid = med3(mid - size, mid, mid + size);
|
|
rightMid = med3(right - 2 * size, right - size, right);
|
|
}
|
|
mid = med3(leftMid, mid, rightMid);
|
|
}
|
|
|
|
T v = this[mid];
|
|
|
|
//Scan the list from two directions
|
|
long pivotLeftSide = left, leftScanIndex = pivotLeftSide;
|
|
long rightScanIndex = right, pivotRightSide = rightScanIndex;
|
|
|
|
|
|
int leftScanIndexBlock = (int)(leftScanIndex >> moveBit);
|
|
int leftScanIndexOffset = (int)(leftScanIndex & (sizePerBlock - 1));
|
|
T[] arrayLeft = arrList[leftScanIndexBlock];
|
|
|
|
int rightScanIndexBlock = (int)(rightScanIndex >> moveBit);
|
|
int rightScanIndexOffset = (int)(rightScanIndex & (sizePerBlock - 1));
|
|
T[] arrayRight = arrList[rightScanIndexBlock];
|
|
while (true)
|
|
{
|
|
//Try to find item which is bigger than pivot
|
|
while (leftScanIndex <= rightScanIndex)
|
|
{
|
|
int cmpRst = arrayLeft[leftScanIndexOffset].CompareTo(v);
|
|
if (cmpRst > 0)
|
|
{
|
|
//Found one.
|
|
break;
|
|
}
|
|
else if (cmpRst == 0)
|
|
{
|
|
//If the item is equal to pivot, exchange it with the item in left-side.
|
|
swap(pivotLeftSide++, leftScanIndex);
|
|
}
|
|
leftScanIndex++;
|
|
|
|
leftScanIndexOffset++;
|
|
if (leftScanIndexOffset == sizePerBlock)
|
|
{
|
|
leftScanIndexOffset = 0;
|
|
leftScanIndexBlock++;
|
|
if (leftScanIndexBlock == arrList.Count)
|
|
{
|
|
break;
|
|
}
|
|
arrayLeft = arrList[leftScanIndexBlock];
|
|
}
|
|
}
|
|
|
|
//Try to find item which is smaller than pivot
|
|
while (rightScanIndex >= leftScanIndex)
|
|
{
|
|
int cmpRst = arrayRight[rightScanIndexOffset].CompareTo(v);
|
|
if (cmpRst < 0)
|
|
{
|
|
//Found one.
|
|
break;
|
|
}
|
|
else if (cmpRst == 0)
|
|
{
|
|
//If the item is equal to pivot, exchange it with the item in left-side.
|
|
swap(rightScanIndex, pivotRightSide--);
|
|
}
|
|
rightScanIndex--;
|
|
|
|
rightScanIndexOffset--;
|
|
if (rightScanIndexOffset < 0)
|
|
{
|
|
rightScanIndexOffset = (int)(sizePerBlock - 1);
|
|
rightScanIndexBlock--;
|
|
if (rightScanIndexBlock < 0)
|
|
{
|
|
break;
|
|
}
|
|
arrayRight = arrList[rightScanIndexBlock];
|
|
}
|
|
}
|
|
|
|
if (leftScanIndex > rightScanIndex)
|
|
{
|
|
//Scan finished
|
|
break;
|
|
}
|
|
|
|
//Exchange two found items between pivot
|
|
T temp = arrayLeft[(int)leftScanIndexOffset];
|
|
arrayLeft[(int)leftScanIndexOffset] = arrayRight[(int)rightScanIndexOffset];
|
|
arrayRight[(int)rightScanIndexOffset] = temp;
|
|
|
|
leftScanIndex++;
|
|
rightScanIndex--;
|
|
|
|
leftScanIndexOffset++;
|
|
if (leftScanIndexOffset == sizePerBlock)
|
|
{
|
|
leftScanIndexOffset = 0;
|
|
leftScanIndexBlock++;
|
|
if (leftScanIndexBlock == arrList.Count)
|
|
{
|
|
break;
|
|
}
|
|
arrayLeft = arrList[leftScanIndexBlock];
|
|
}
|
|
|
|
rightScanIndexOffset--;
|
|
if (rightScanIndexOffset < 0)
|
|
{
|
|
rightScanIndexOffset = (int)(sizePerBlock - 1);
|
|
rightScanIndexBlock--;
|
|
if (rightScanIndexBlock < 0)
|
|
{
|
|
break;
|
|
}
|
|
arrayRight = arrList[rightScanIndexBlock];
|
|
}
|
|
}
|
|
|
|
//Continue to sort two sub-sections
|
|
long splitIndexLeft = leftScanIndex - pivotLeftSide;
|
|
long splitIndexRight = pivotRightSide - rightScanIndex;
|
|
if (splitIndexLeft > 1 && splitIndexRight > 1)
|
|
{
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
Parallel.Invoke(parallelOption,
|
|
() =>
|
|
#endif
|
|
{
|
|
//exchange items with same value into middle of the list
|
|
long size = Math.Min(pivotLeftSide - left, leftScanIndex - pivotLeftSide);
|
|
vecswap(left, leftScanIndex - size, size);
|
|
QuickSort(left, splitIndexLeft + left - 1);
|
|
}
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
,
|
|
() =>
|
|
#endif
|
|
{
|
|
//exchange items with same value into middle of the list
|
|
long size = Math.Min(pivotRightSide - rightScanIndex, right - pivotRightSide);
|
|
vecswap(leftScanIndex, right - size + 1, size);
|
|
QuickSort(right - splitIndexRight + 1, right);
|
|
}
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//exchange items with same value into middle of the list
|
|
long size = Math.Min(pivotLeftSide - left, leftScanIndex - pivotLeftSide);
|
|
vecswap(left, leftScanIndex - size, size);
|
|
|
|
size = Math.Min(pivotRightSide - rightScanIndex, right - pivotRightSide);
|
|
vecswap(leftScanIndex, right - size + 1, size);
|
|
|
|
if (splitIndexLeft > 1)
|
|
{
|
|
QuickSort(left, splitIndexLeft + left - 1);
|
|
}
|
|
|
|
if (splitIndexRight > 1)
|
|
{
|
|
QuickSort(right - splitIndexRight + 1, right);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public void Sort(long startIndex, long size, int threadnum = -1)
|
|
{
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
parallelOption.MaxDegreeOfParallelism = threadnum;
|
|
if (threadnum > 0)
|
|
{
|
|
lcts = new LimitedConcurrencyLevelTaskScheduler(threadnum * 2);
|
|
parallelOption.TaskScheduler = lcts;
|
|
}
|
|
#endif
|
|
QuickSort(startIndex, startIndex + size - 1);
|
|
}
|
|
|
|
public void Sort(int threadnum = -1)
|
|
{
|
|
#if NO_SUPPORT_PARALLEL_LIB
|
|
#else
|
|
parallelOption.MaxDegreeOfParallelism = threadnum;
|
|
if (threadnum > 0)
|
|
{
|
|
lcts = new LimitedConcurrencyLevelTaskScheduler(threadnum * 2);
|
|
parallelOption.TaskScheduler = lcts;
|
|
}
|
|
#endif
|
|
QuickSort(0, Count - 1);
|
|
}
|
|
|
|
|
|
public T BinarySearch(long low, long high, T goal)
|
|
{
|
|
long mid = 0;
|
|
|
|
while (low <= high)
|
|
{
|
|
mid = (high + low) / 2;
|
|
if (this[mid].CompareTo(goal) == 0)
|
|
{
|
|
return this[mid];
|
|
}
|
|
else if (this[mid].CompareTo(goal) > 0)
|
|
{
|
|
high = mid - 1;
|
|
}
|
|
else
|
|
{
|
|
low = mid + 1;
|
|
}
|
|
}
|
|
return default(T);
|
|
}
|
|
}
|
|
}
|