some tests cleaned up
This commit is contained in:
parent
997e7d1e5b
commit
c96aaecfb4
2 changed files with 1 additions and 186 deletions
|
|
@ -1,185 +0,0 @@
|
||||||
|
|
||||||
# design criteria:
|
|
||||||
# Generic code is expenisve wrt code size!
|
|
||||||
# So the implementation should be small.
|
|
||||||
# The sort should be stable.
|
|
||||||
#
|
|
||||||
|
|
||||||
proc sort[T](arr: var openArray[T], lo, hi: natural) =
|
|
||||||
var k = 0
|
|
||||||
if lo < hi:
|
|
||||||
var mid = (lo + hi) div 2
|
|
||||||
sort(arr, lo, mid)
|
|
||||||
inc(mid)
|
|
||||||
sort(arr, mid, hi)
|
|
||||||
while lo < mid and mid <= hi:
|
|
||||||
if arr[lo] < arr[mid]:
|
|
||||||
inc(lo)
|
|
||||||
else:
|
|
||||||
when swapIsExpensive(T):
|
|
||||||
var help = arr[mid]
|
|
||||||
for k in countdown(mid, succ(lo)):
|
|
||||||
arr[k] = arr[pred(k)]
|
|
||||||
arr[lo] = help
|
|
||||||
else:
|
|
||||||
for k in countdown(mid, succ(lo)):
|
|
||||||
swap(arr[k], arr[pred(k)])
|
|
||||||
inc(lo)
|
|
||||||
inc(mid)
|
|
||||||
|
|
||||||
type
|
|
||||||
TSortOrder* = enum
|
|
||||||
Descending = -1,
|
|
||||||
Ascending = 0
|
|
||||||
|
|
||||||
proc flip(x: int, order: TSortOrder): int {.inline.} =
|
|
||||||
result = x xor ord(order) - ord(order)
|
|
||||||
|
|
||||||
# We use a fixed size stack. This size is larger
|
|
||||||
# than can be overflowed on a 64-bit machine
|
|
||||||
const
|
|
||||||
stackSize = 66
|
|
||||||
minRunSize = 7
|
|
||||||
|
|
||||||
type
|
|
||||||
TRun = tuple[index, length: int]
|
|
||||||
TSortState[T] {.pure, final.} = object
|
|
||||||
storage: seq[T]
|
|
||||||
runs: array[0..stackSize-1, TRun]
|
|
||||||
stackHeight: int # The index of the first unwritten element of the stack.
|
|
||||||
partitionedUpTo, length: int
|
|
||||||
|
|
||||||
# We keep track of how far we've partitioned up
|
|
||||||
# to so we know where to start the next partition.
|
|
||||||
# The idea is that everything < partionedUpTo
|
|
||||||
# is on the stack, everything >= partionedUpTo
|
|
||||||
# is not yet on the stack. When partitionedUpTo == length
|
|
||||||
# we'll have put everything on the stack.
|
|
||||||
|
|
||||||
proc reverse[T](a: var openArray[T], first, last: int) =
|
|
||||||
for j in first .. < first+length div 2: swap(a[j], a[length-j-1])
|
|
||||||
|
|
||||||
proc insertionSort[T]( int xs[], int length) =
|
|
||||||
for i in 1.. < length:
|
|
||||||
# The array before i is sorted. Now insert xs[i] into it
|
|
||||||
var x = xs[i]
|
|
||||||
var j = i-1
|
|
||||||
# Move j down until it's either at the beginning or on
|
|
||||||
# something <= x, and everything to the right of it has
|
|
||||||
# been moved up one.
|
|
||||||
while j >= 0 and xs[j] > x:
|
|
||||||
xs[j+1] = xs[j]
|
|
||||||
dec j
|
|
||||||
xs[j+1] = x
|
|
||||||
|
|
||||||
proc boostRunLength(s: TSortState, run: var TRun) =
|
|
||||||
# Need to make sure we don't overshoot the end of the array
|
|
||||||
var length = min(s.length - run.index, minRunSize)
|
|
||||||
insertionSort(run.index, length)
|
|
||||||
run.length = length
|
|
||||||
|
|
||||||
proc nextPartition[T](a: var openarray[T], s: var TSortState): bool =
|
|
||||||
if s.partitionedUpTo >= s.length: return false
|
|
||||||
var startIndex = s.partitionedUpTo
|
|
||||||
# Find an increasing run starting from startIndex
|
|
||||||
var nextStartIndex = startIndex + 1
|
|
||||||
|
|
||||||
if nextStartIndex < s.length:
|
|
||||||
if a[nextStartIndex] < a[startIndex]:
|
|
||||||
# We have a decreasing sequence starting here.
|
|
||||||
while nextStartIndex < s.length:
|
|
||||||
if a[nextStartIndex] < a[nextStartIndex-1]: inc(nextStartIndex)
|
|
||||||
else: break
|
|
||||||
# Now reverse it in place.
|
|
||||||
reverse(a, startIndex, nextStartIndex)
|
|
||||||
else:
|
|
||||||
# We have an increasing sequence starting here.
|
|
||||||
while nextStartIndex < s.length:
|
|
||||||
if a[nextStartIndex] >= a[nextStartIndex-1]: inc(nextStartIndex)
|
|
||||||
else: break
|
|
||||||
|
|
||||||
# So now [startIndex, nextStartIndex) is an increasing run.
|
|
||||||
# Push it onto the stack.
|
|
||||||
var runToAdd: TRun = (startIndex, nextStartIndex - startIndex)
|
|
||||||
if runToAdd.length < minRunSize:
|
|
||||||
boostRunLength(s, runToAdd)
|
|
||||||
s.partitionedUpTo = startIndex + runToAdd.length
|
|
||||||
|
|
||||||
s.runs[s.stackHeight] = runToAdd
|
|
||||||
inc s.stackHeight
|
|
||||||
result = true
|
|
||||||
|
|
||||||
proc shouldCollapse(s: TSortState): bool =
|
|
||||||
if s.stackHeight > 2:
|
|
||||||
var h = s.stackHeight-1
|
|
||||||
var headLength = s.runs[h].length
|
|
||||||
var nextLength = s.runs[h-1].length
|
|
||||||
result = 2 * headLength > nextLength
|
|
||||||
|
|
||||||
proc merge(int target[], int p1[], int l1, int p2[], int l2, int storage[]) =
|
|
||||||
# Merge the sorted arrays p1, p2 of length l1, l2 into a single
|
|
||||||
# sorted array starting at target. target may overlap with either
|
|
||||||
# of p1 or p2 but must have enough space to store the array.
|
|
||||||
# Use the storage argument for temporary storage. It must have room for
|
|
||||||
# l1 + l2 ints.
|
|
||||||
int *merge_to = storage
|
|
||||||
|
|
||||||
# Current index into each of the two arrays we're writing
|
|
||||||
# from.
|
|
||||||
int i1, i2;
|
|
||||||
i1 = i2 = 0;
|
|
||||||
|
|
||||||
# The address to which we write the next element in the merge
|
|
||||||
int *next_merge_element = merge_to;
|
|
||||||
|
|
||||||
# Iterate over the two arrays, writing the least element at the
|
|
||||||
# current position to merge_to. When the two are equal we prefer
|
|
||||||
# the left one, because if we're merging left, right we want to
|
|
||||||
# ensure stability.
|
|
||||||
# Of course this doesn't matter for integers, but it's the thought
|
|
||||||
# that counts.
|
|
||||||
while i1 < l1 and i2 < l2:
|
|
||||||
if p1[i1] <= p2[i2]:
|
|
||||||
*next_merge_element = p1[i1];
|
|
||||||
i1++
|
|
||||||
else:
|
|
||||||
*next_merge_element = p2[i2];
|
|
||||||
i2++
|
|
||||||
next_merge_element++
|
|
||||||
|
|
||||||
# If we stopped short before the end of one of the arrays
|
|
||||||
# we now copy the rest over.
|
|
||||||
memcpy(next_merge_element, p1 + i1, sizeof(int) * (l1 - i1));
|
|
||||||
memcpy(next_merge_element, p2 + i2, sizeof(int) * (l2 - i2));
|
|
||||||
|
|
||||||
# We've now merged into our additional working space. Time
|
|
||||||
# to copy to the target.
|
|
||||||
memcpy(target, merge_to, sizeof(int) * (l1 + l2));
|
|
||||||
|
|
||||||
|
|
||||||
proc mergeCollapse(a: s: var TSortState) =
|
|
||||||
var X = s.runs[s.stackHeight-2]
|
|
||||||
var Y = s.runs[s.stackHeight-1]
|
|
||||||
|
|
||||||
merge(X.index, X.index, X.length, Y.index, Y.length, s.storage)
|
|
||||||
|
|
||||||
dec s.stackHeight
|
|
||||||
inc X.length, Y.length
|
|
||||||
s.runs[s.stackHeight-1] = X
|
|
||||||
|
|
||||||
proc sort[T](arr: var openArray[T], first, last: natural,
|
|
||||||
cmp: proc (x,y: T): int, order = TSortOrder.ascending) =
|
|
||||||
var s: TSortState
|
|
||||||
newSeq(s.storage, arr.len)
|
|
||||||
s.stackHeight = 0
|
|
||||||
s.partitionedUpTo = 0
|
|
||||||
s.length = arr.len
|
|
||||||
|
|
||||||
while nextPartition(s):
|
|
||||||
while shouldCollapse(s): mergeCollapse(s)
|
|
||||||
while s.stackHeight > 1: mergeCollapse(s)
|
|
||||||
|
|
||||||
proc sort[T](arr: var openArray[T], cmp: proc (x, y: T): int = cmp,
|
|
||||||
order = TSortOrder.ascending) =
|
|
||||||
sort(arr, 0, high(arr), order)
|
|
||||||
|
|
||||||
|
|
@ -7,6 +7,6 @@ template toSeq*(iter: expr): expr =
|
||||||
for x in iter: add(result, x)
|
for x in iter: add(result, x)
|
||||||
result
|
result
|
||||||
|
|
||||||
for x, y in items(toSeq(countup(2, 6))).withIndex:
|
for x in items(toSeq(countup(2, 6))):
|
||||||
stdout.write(x)
|
stdout.write(x)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue