luau/tests/conformance/sort.lua
Andy Friesen ba67fb275e
Sync to upstream/release/571 (#895)
* `table.sort` was improved further. It now guarentees N*log(N) time
complexity in the worst case.
* Fix https://github.com/Roblox/luau/issues/880

We are also working on fixing final bugs and crashes in the new type
solver.

On the CodeGen front we have a few things going on:
* We have a smarter register allocator for the x86 JIT
* We lower more instructions on arm64
* The vector constructor builtin is now translated to IR

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-04-07 14:01:29 -07:00

161 lines
4.2 KiB
Lua
Raw Permalink Blame History

-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
-- This file is based on Lua 5.x tests -- https://github.com/lua/lua/tree/master/testes
print"testing sort"
function checksort(t, f, ...)
assert(#t == select('#', ...))
local copy = table.clone(t)
table.sort(copy, f)
for i=1,#t do assert(copy[i] == select(i, ...)) end
end
-- basic edge cases
checksort({}, nil)
checksort({1}, nil, 1)
-- small inputs
checksort({1, 2}, nil, 1, 2)
checksort({2, 1}, nil, 1, 2)
checksort({1, 2, 3}, nil, 1, 2, 3)
checksort({2, 1, 3}, nil, 1, 2, 3)
checksort({1, 3, 2}, nil, 1, 2, 3)
checksort({3, 2, 1}, nil, 1, 2, 3)
checksort({3, 1, 2}, nil, 1, 2, 3)
-- "large" input
checksort({3, 8, 1, 7, 10, 2, 5, 4, 9, 6}, nil, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
checksort({"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, nil, "Apr", "Aug", "Dec", "Feb", "Jan", "Jul", "Jun", "Mar", "May", "Nov", "Oct", "Sep")
-- duplicates
checksort({3, 1, 1, 7, 1, 3, 5, 1, 9, 3}, nil, 1, 1, 1, 1, 3, 3, 3, 5, 7, 9)
-- predicates
checksort({3, 8, 1, 7, 10, 2, 5, 4, 9, 6}, function (a, b) return a > b end, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
-- can't sort readonly tables
assert(pcall(table.sort, table.freeze({2, 1})) == false)
-- first argument must be a table, second argument must be nil or function
assert(pcall(table.sort) == false)
assert(pcall(table.sort, "abc") == false)
assert(pcall(table.sort, {}, 42) == false)
assert(pcall(table.sort, {}, {}) == false)
-- legacy Lua tests
function check (a, f)
f = f or function (x,y) return x<y end;
for n=table.getn(a),2,-1 do
assert(not f(a[n], a[n-1]))
end
end
a = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"}
table.sort(a)
check(a)
limit = 30000
if rawget(_G, "_soft") then limit = 5000 end
a = {}
for i=1,limit do
a[i] = math.random()
end
local x = os.clock()
table.sort(a)
print(string.format("Sorting %d elements in %.2f sec.", limit, os.clock()-x))
check(a)
x = os.clock()
table.sort(a)
print(string.format("Re-sorting %d elements in %.2f sec.", limit, os.clock()-x))
check(a)
a = {}
for i=1,limit do
a[i] = math.random()
end
x = os.clock(); i=0
table.sort(a, function(x,y) i=i+1; return y<x end)
print(string.format("Invert-sorting other %d elements in %.2f sec., with %i comparisons",
limit, os.clock()-x, i))
check(a, function(x,y) return y<x end)
table.sort{} -- empty array
for i=1,limit do a[i] = false end
x = os.clock();
table.sort(a, function(x,y) return nil end)
print(string.format("Sorting %d equal elements in %.2f sec.", limit, os.clock()-x))
check(a, function(x,y) return nil end)
for i,v in pairs(a) do assert(not v or i=='n' and v==limit) end
a = {"<EFBFBD>lo", "\0first :-)", "alo", "then this one", "45", "and a new"}
table.sort(a)
check(a)
local ok = pcall(table.sort, a, function (x, y)
loadstring(string.format("a[%q] = ''", x))()
collectgarbage()
return x<y
end)
assert(not ok)
tt = {__lt = function (a,b) return a.val < b.val end}
a = {}
for i=1,10 do a[i] = {val=math.random(100)}; setmetatable(a[i], tt); end
table.sort(a)
check(a, tt.__lt)
check(a)
-- force quicksort to degrade to heap sort
do
-- discover quick sort killer (this triggers heap sort which is what we want more or less; note that the "internal" heap sort iterations will result in a different order that wouldn't fully defeat a vanilla quicksort)
-- see https://igoro.com/archive/quicksort-killer/
local keys = {}
local candidate = 0
local next = 0
local t = table.create(100, 0)
for k in t do
t[k] = k
end
table.sort(t, function (x, y)
if keys[x] == nil and keys[y] == nil then
if x == candidate then keys[x] = next else keys[y] = next end
next += 1
end
if keys[x] == nil then
candidate = x
return true
end
if keys[y] == nil then
candidate = y
return false
end
return keys[x] < keys[y]
end)
-- repeat the sort for the generated sequence; it should produce an integer sequence and trigger heap sort, although we can't confirm the latter
local arr = table.create(#t)
for k,v in t do
arr[v] = k
end
table.sort(arr)
for k in arr do
assert(arr[k] == k)
end
end
return"OK"