2020-04-07 03:49:10 +00:00
|
|
|
--[[
|
2020-06-02 05:00:46 +00:00
|
|
|
various sorting routines
|
2020-04-07 03:49:10 +00:00
|
|
|
]]
|
2020-01-29 03:26:28 +00:00
|
|
|
|
2020-06-02 05:00:46 +00:00
|
|
|
--this is based on code from Dirk Laurie and Steve Fisher,
|
|
|
|
--used under license as follows:
|
2020-01-29 03:26:28 +00:00
|
|
|
|
|
|
|
--[[
|
|
|
|
Copyright © 2013 Dirk Laurie and Steve Fisher.
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
copy of this software and associated documentation files (the "Software"),
|
|
|
|
to deal in the Software without restriction, including without limitation
|
|
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
DEALINGS IN THE SOFTWARE.
|
|
|
|
]]
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
-- (modifications by Max Cahill 2018, 2020)
|
2020-01-29 03:26:28 +00:00
|
|
|
|
2020-06-02 05:00:46 +00:00
|
|
|
local sort = {}
|
2020-01-29 03:26:28 +00:00
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--tunable size for insertion sort "bottom out"
|
2020-06-02 05:00:46 +00:00
|
|
|
sort.max_chunk_size = 32
|
2020-01-29 03:26:28 +00:00
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--insertion sort on a section of array
|
2020-06-02 05:00:46 +00:00
|
|
|
function sort._insertion_sort_impl(array, first, last, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
for i = first + 1, last do
|
|
|
|
local k = first
|
|
|
|
local v = array[i]
|
|
|
|
for j = i, first + 1, -1 do
|
2020-03-10 02:14:26 +00:00
|
|
|
if less(v, array[j - 1]) then
|
|
|
|
array[j] = array[j - 1]
|
2020-01-29 03:26:28 +00:00
|
|
|
else
|
|
|
|
k = j
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
array[k] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--merge sorted adjacent sections of array
|
2020-06-02 05:00:46 +00:00
|
|
|
function sort._merge(array, workspace, low, middle, high, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
local i, j, k
|
|
|
|
i = 1
|
|
|
|
-- copy first half of array to auxiliary array
|
2022-03-02 18:03:57 +00:00
|
|
|
for w = low, middle do
|
|
|
|
workspace[i] = array[w]
|
2020-01-29 03:26:28 +00:00
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
-- sieve through
|
|
|
|
i = 1
|
|
|
|
j = middle + 1
|
|
|
|
k = low
|
|
|
|
while true do
|
|
|
|
if (k >= j) or (j > high) then
|
|
|
|
break
|
|
|
|
end
|
2020-03-10 02:14:26 +00:00
|
|
|
if less(array[j], workspace[i]) then
|
|
|
|
array[k] = array[j]
|
2020-01-29 03:26:28 +00:00
|
|
|
j = j + 1
|
|
|
|
else
|
2020-03-10 02:14:26 +00:00
|
|
|
array[k] = workspace[i]
|
2020-01-29 03:26:28 +00:00
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
k = k + 1
|
|
|
|
end
|
|
|
|
-- copy back any remaining elements of first half
|
2022-03-02 18:03:57 +00:00
|
|
|
for w = k, j - 1 do
|
|
|
|
array[w] = workspace[i]
|
2020-01-29 03:26:28 +00:00
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--implementation for the merge sort
|
2020-06-02 05:00:46 +00:00
|
|
|
function sort._merge_sort_impl(array, workspace, low, high, less)
|
|
|
|
if high - low <= sort.max_chunk_size then
|
|
|
|
sort._insertion_sort_impl(array, low, high, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
else
|
2020-03-10 02:14:26 +00:00
|
|
|
local middle = math.floor((low + high) / 2)
|
2020-06-02 05:00:46 +00:00
|
|
|
sort._merge_sort_impl(array, workspace, low, middle, less)
|
|
|
|
sort._merge_sort_impl(array, workspace, middle + 1, high, less)
|
|
|
|
sort._merge(array, workspace, low, middle, high, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-03-15 09:28:50 +00:00
|
|
|
--default comparison; hoisted for clarity
|
2022-05-23 04:48:52 +00:00
|
|
|
local _sorted_types = {
|
|
|
|
--a list of types that will be sorted by default_less
|
|
|
|
--provide a custom sort function to sort other types
|
|
|
|
["string"] = true,
|
|
|
|
["number"] = true,
|
|
|
|
}
|
2020-03-15 09:28:50 +00:00
|
|
|
local function default_less(a, b)
|
2022-05-23 04:48:52 +00:00
|
|
|
if not _sorted_types[type(a)] or not _sorted_types[type(b)] then
|
|
|
|
return false
|
|
|
|
end
|
2020-03-10 02:14:26 +00:00
|
|
|
return a < b
|
|
|
|
end
|
|
|
|
|
2020-01-29 03:26:28 +00:00
|
|
|
--inline common setup stuff
|
2020-06-02 05:00:46 +00:00
|
|
|
function sort._sort_setup(array, less)
|
2020-03-10 02:14:26 +00:00
|
|
|
--default less
|
|
|
|
less = less or default_less
|
|
|
|
--
|
2020-01-29 03:26:28 +00:00
|
|
|
local n = #array
|
|
|
|
--trivial cases; empty or 1 element
|
2020-03-10 02:14:26 +00:00
|
|
|
local trivial = (n <= 1)
|
|
|
|
if not trivial then
|
2020-01-29 03:26:28 +00:00
|
|
|
--check less
|
|
|
|
if less(array[1], array[1]) then
|
2020-03-10 02:14:26 +00:00
|
|
|
error("invalid order function for sorting; less(v, v) should not be true for any v.")
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
--setup complete
|
|
|
|
return trivial, n, less
|
|
|
|
end
|
|
|
|
|
2020-06-02 05:00:46 +00:00
|
|
|
function sort.stable_sort(array, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
--setup
|
2022-03-02 18:03:57 +00:00
|
|
|
local trivial, n
|
|
|
|
trivial, n, less = sort._sort_setup(array, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
if not trivial then
|
2020-03-10 02:14:26 +00:00
|
|
|
--temp storage; allocate ahead of time
|
2020-01-29 03:26:28 +00:00
|
|
|
local workspace = {}
|
2020-03-10 02:14:26 +00:00
|
|
|
local middle = math.ceil(n / 2)
|
|
|
|
workspace[middle] = array[1]
|
2020-01-29 03:26:28 +00:00
|
|
|
--dive in
|
2020-06-02 05:00:46 +00:00
|
|
|
sort._merge_sort_impl( array, workspace, 1, n, less )
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
|
|
|
return array
|
|
|
|
end
|
|
|
|
|
2020-06-02 05:00:46 +00:00
|
|
|
function sort.insertion_sort(array, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
--setup
|
2022-03-02 18:03:57 +00:00
|
|
|
local trivial, n
|
|
|
|
trivial, n, less = sort._sort_setup(array, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
if not trivial then
|
2020-06-02 05:00:46 +00:00
|
|
|
sort._insertion_sort_impl(array, 1, n, less)
|
2020-01-29 03:26:28 +00:00
|
|
|
end
|
|
|
|
return array
|
|
|
|
end
|
|
|
|
|
2020-06-02 05:00:46 +00:00
|
|
|
sort.unstable_sort = table.sort
|
2020-03-15 09:28:50 +00:00
|
|
|
|
2020-04-07 03:49:10 +00:00
|
|
|
--export sort core to the global table module
|
2020-06-02 05:00:46 +00:00
|
|
|
function sort:export()
|
|
|
|
table.insertion_sort = sort.insertion_sort
|
|
|
|
table.stable_sort = sort.stable_sort
|
|
|
|
table.unstable_sort = sort.unstable_sort
|
2020-04-07 03:49:10 +00:00
|
|
|
end
|
2020-03-15 09:28:50 +00:00
|
|
|
|
2020-06-02 05:00:46 +00:00
|
|
|
return sort
|