Skip to content

Commit

Permalink
Add generic sorting algorithm stabilization
Browse files Browse the repository at this point in the history
  • Loading branch information
appgurueu committed Mar 7, 2022
1 parent 7f48674 commit 6fcc44f
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 1 deletion.
52 changes: 52 additions & 0 deletions .spec/sorting/stabilize_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
describe("Sorting stabilization", function()
local stabilize = require("sorting.stabilize")
local quicksort = require("sorting.quicksort")
local stabilized_quicksort = stabilize(quicksort()) -- random quicksort
local is_sorted = require("sorting.is_sorted")

-- Sort by the first element (= value) of the pair
local function comparator(a, b)
return a[1] < b[1]
end

-- Verifies whether the sort is stable by checking results (stored as the second element of the pair)
local function verifying_comparator(a, b)
if a[1] == b[1] then
return a[2] < b[2]
end
return a[1] < b[1]
end

it("should fail if not stabilized and work if stabilized", function()
-- Inefficient but deterministic quicksort which can be used to ensure instable sorting
local bad_quicksort = quicksort(function(i, _)
return i
end)
local list = { { 1, 1 }, { 2, 2 }, { 1, 3 } }
bad_quicksort(list, comparator)
assert.falsy(is_sorted(list, verifying_comparator))
list = { { 1, 1 }, { 2, 2 }, { 1, 3 } }
stabilize(bad_quicksort)(list, comparator)
assert.truthy(is_sorted(list, verifying_comparator))
end)

it("should handle edge cases", function()
local list = {}
stabilized_quicksort(list)
assert.same({}, list)
list = { 1 }
stabilized_quicksort(list)
assert.same({ 1 }, list)
end)

it("should sort random lists", function()
for _ = 1, 100 do
local list = {}
for index = 1, 100 do
list[index] = { math.random(20), index }
end
stabilized_quicksort(list, comparator)
assert.truthy(is_sorted(list, verifying_comparator))
end
end)
end)
6 changes: 5 additions & 1 deletion src/class.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
-- Implementation helper for metatable-based "classes"
return function(class_table)
return function(class_table, superclass_table)
if superclass_table then
class_table.super = superclass_table
setmetatable(class_table, superclass_table.metatable)
end
local new = assert(class_table.new)
local metatable = { __index = class_table }
function class_table.new(...)
Expand Down
34 changes: 34 additions & 0 deletions src/sorting/stabilize.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- Stabilizes any sorting algorithm, using linear auxiliary space (for the indices) per sorting
return function(
sort -- function(list, less_than)
)
-- stabilized sorting function
return function(list, less_than)
less_than = less_than or function(a, b)
return a < b
end
-- Build a list of indices
local indices = {}
for index = 1, #list do
indices[index] = index
end
-- Sort the list of indices according to values; compare indices only if they have the same value
sort(indices, function(index_a, index_b)
if less_than(list[index_a], list[index_b]) then
return true
end
if less_than(list[index_b], list[index_a]) then
return false
end
return index_a < index_b
end)
-- Map indices to values
for index = 1, #list do
indices[index] = list[indices[index]]
end
-- Replace elements in original list (sorting is supposed to be in-place)
for index = 1, #list do
list[index] = indices[index]
end
end
end

0 comments on commit 6fcc44f

Please sign in to comment.