| -- $Id: testes/sort.lua $ |
| -- See Copyright Notice in file lua.h |
| |
| print "testing (parts of) table library" |
| |
| local maxI = math.maxinteger |
| local minI = math.mininteger |
| |
| |
| local function checkerror (msg, f, ...) |
| local s, err = pcall(f, ...) |
| assert(not s and string.find(err, msg)) |
| end |
| |
| |
| do print "testing 'table.create'" |
| local N = 10000 |
| collectgarbage() |
| local m = collectgarbage("count") * 1024 |
| local t = table.create(N) |
| local memdiff = collectgarbage("count") * 1024 - m |
| assert(memdiff > N * 4) |
| for i = 1, 20 do |
| assert(#t == i - 1) |
| t[i] = 0 |
| end |
| for i = 1, 20 do t[#t + 1] = i * 10 end |
| assert(#t == 40 and t[39] == 190) |
| assert(not T or T.querytab(t) == N) |
| t = nil |
| collectgarbage() |
| m = collectgarbage("count") * 1024 |
| t = table.create(0, 1024) |
| memdiff = collectgarbage("count") * 1024 - m |
| assert(memdiff > 1024 * 12) |
| assert(not T or select(2, T.querytab(t)) == 1024) |
| |
| local maxint1 = 1 << (string.packsize("i") * 8 - 1) |
| checkerror("out of range", table.create, maxint1) |
| checkerror("out of range", table.create, 0, maxint1) |
| checkerror("table overflow", table.create, 0, maxint1 - 1) |
| end |
| |
| |
| print "testing unpack" |
| |
| local unpack = table.unpack |
| |
| |
| checkerror("wrong number of arguments", table.insert, {}, 2, 3, 4) |
| |
| local x,y,z,a,n |
| a = {}; local lim = _soft and 200 or 2000 |
| for i=1, lim do a[i]=i end |
| assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim) |
| x = unpack(a) |
| assert(x == 1) |
| x = {unpack(a)} |
| assert(#x == lim and x[1] == 1 and x[lim] == lim) |
| x = {unpack(a, lim-2)} |
| assert(#x == 3 and x[1] == lim-2 and x[3] == lim) |
| x = {unpack(a, 10, 6)} |
| assert(next(x) == nil) -- no elements |
| x = {unpack(a, 11, 10)} |
| assert(next(x) == nil) -- no elements |
| x,y = unpack(a, 10, 10) |
| assert(x == 10 and y == nil) |
| x,y,z = unpack(a, 10, 11) |
| assert(x == 10 and y == 11 and z == nil) |
| a,x = unpack{1} |
| assert(a==1 and x==nil) |
| a,x = unpack({1,2}, 1, 1) |
| assert(a==1 and x==nil) |
| |
| do |
| local maxi = (1 << 31) - 1 -- maximum value for an int (usually) |
| local mini = -(1 << 31) -- minimum value for an int (usually) |
| checkerror("too many results", unpack, {}, 0, maxi) |
| checkerror("too many results", unpack, {}, 1, maxi) |
| checkerror("too many results", unpack, {}, 0, maxI) |
| checkerror("too many results", unpack, {}, 1, maxI) |
| checkerror("too many results", unpack, {}, mini, maxi) |
| checkerror("too many results", unpack, {}, -maxi, maxi) |
| checkerror("too many results", unpack, {}, minI, maxI) |
| unpack({}, maxi, 0) |
| unpack({}, maxi, 1) |
| unpack({}, maxI, minI) |
| pcall(unpack, {}, 1, maxi + 1) |
| local a, b = unpack({[maxi] = 20}, maxi, maxi) |
| assert(a == 20 and b == nil) |
| a, b = unpack({[maxi] = 20}, maxi - 1, maxi) |
| assert(a == nil and b == 20) |
| local t = {[maxI - 1] = 12, [maxI] = 23} |
| a, b = unpack(t, maxI - 1, maxI); assert(a == 12 and b == 23) |
| a, b = unpack(t, maxI, maxI); assert(a == 23 and b == nil) |
| a, b = unpack(t, maxI, maxI - 1); assert(a == nil and b == nil) |
| t = {[minI] = 12.3, [minI + 1] = 23.5} |
| a, b = unpack(t, minI, minI + 1); assert(a == 12.3 and b == 23.5) |
| a, b = unpack(t, minI, minI); assert(a == 12.3 and b == nil) |
| a, b = unpack(t, minI + 1, minI); assert(a == nil and b == nil) |
| end |
| |
| do -- length is not an integer |
| local t = setmetatable({}, {__len = function () return 'abc' end}) |
| assert(#t == 'abc') |
| checkerror("object length is not an integer", table.insert, t, 1) |
| end |
| |
| print "testing pack" |
| |
| a = table.pack() |
| assert(a[1] == undef and a.n == 0) |
| |
| a = table.pack(table) |
| assert(a[1] == table and a.n == 1) |
| |
| a = table.pack(nil, nil, nil, nil) |
| assert(a[1] == nil and a.n == 4) |
| |
| |
| -- testing move |
| do |
| |
| checkerror("table expected", table.move, 1, 2, 3, 4) |
| |
| local function eqT (a, b) |
| for k, v in pairs(a) do assert(b[k] == v) end |
| for k, v in pairs(b) do assert(a[k] == v) end |
| end |
| |
| local a = table.move({10,20,30}, 1, 3, 2) -- move forward |
| eqT(a, {10,10,20,30}) |
| |
| -- move forward with overlap of 1 |
| a = table.move({10, 20, 30}, 1, 3, 3) |
| eqT(a, {10, 20, 10, 20, 30}) |
| |
| -- moving to the same table (not being explicit about it) |
| a = {10, 20, 30, 40} |
| table.move(a, 1, 4, 2, a) |
| eqT(a, {10, 10, 20, 30, 40}) |
| |
| a = table.move({10,20,30}, 2, 3, 1) -- move backward |
| eqT(a, {20,30,30}) |
| |
| a = {} -- move to new table |
| assert(table.move({10,20,30}, 1, 3, 1, a) == a) |
| eqT(a, {10,20,30}) |
| |
| a = {} |
| assert(table.move({10,20,30}, 1, 0, 3, a) == a) -- empty move (no move) |
| eqT(a, {}) |
| |
| a = table.move({10,20,30}, 1, 10, 1) -- move to the same place |
| eqT(a, {10,20,30}) |
| |
| -- moving on the fringes |
| a = table.move({[maxI - 2] = 1, [maxI - 1] = 2, [maxI] = 3}, |
| maxI - 2, maxI, -10, {}) |
| eqT(a, {[-10] = 1, [-9] = 2, [-8] = 3}) |
| |
| a = table.move({[minI] = 1, [minI + 1] = 2, [minI + 2] = 3}, |
| minI, minI + 2, -10, {}) |
| eqT(a, {[-10] = 1, [-9] = 2, [-8] = 3}) |
| |
| a = table.move({45}, 1, 1, maxI) |
| eqT(a, {45, [maxI] = 45}) |
| |
| a = table.move({[maxI] = 100}, maxI, maxI, minI) |
| eqT(a, {[minI] = 100, [maxI] = 100}) |
| |
| a = table.move({[minI] = 100}, minI, minI, maxI) |
| eqT(a, {[minI] = 100, [maxI] = 100}) |
| |
| a = setmetatable({}, { |
| __index = function (_,k) return k * 10 end, |
| __newindex = error}) |
| local b = table.move(a, 1, 10, 3, {}) |
| eqT(a, {}) |
| eqT(b, {nil,nil,10,20,30,40,50,60,70,80,90,100}) |
| |
| b = setmetatable({""}, { |
| __index = error, |
| __newindex = function (t,k,v) |
| t[1] = string.format("%s(%d,%d)", t[1], k, v) |
| end}) |
| table.move(a, 10, 13, 3, b) |
| assert(b[1] == "(3,100)(4,110)(5,120)(6,130)") |
| local stat, msg = pcall(table.move, b, 10, 13, 3, b) |
| assert(not stat and msg == b) |
| end |
| |
| do |
| -- for very long moves, just check initial accesses and interrupt |
| -- move with an error |
| local function checkmove (f, e, t, x, y) |
| local pos1, pos2 |
| local a = setmetatable({}, { |
| __index = function (_,k) pos1 = k end, |
| __newindex = function (_,k) pos2 = k; error() end, }) |
| local st, msg = pcall(table.move, a, f, e, t) |
| assert(not st and pos1 == x and pos2 == y) |
| end |
| checkmove(1, maxI, 0, 1, 0) |
| checkmove(0, maxI - 1, 1, maxI - 1, maxI) |
| checkmove(minI, -2, -5, -2, maxI - 6) |
| checkmove(minI + 1, -1, -2, -1, maxI - 3) |
| checkmove(minI, -2, 0, minI, 0) -- non overlapping |
| checkmove(minI + 1, -1, 1, minI + 1, 1) -- non overlapping |
| end |
| |
| checkerror("too many", table.move, {}, 0, maxI, 1) |
| checkerror("too many", table.move, {}, -1, maxI - 1, 1) |
| checkerror("too many", table.move, {}, minI, -1, 1) |
| checkerror("too many", table.move, {}, minI, maxI, 1) |
| checkerror("wrap around", table.move, {}, 1, maxI, 2) |
| checkerror("wrap around", table.move, {}, 1, 2, maxI) |
| checkerror("wrap around", table.move, {}, minI, -2, 2) |
| |
| |
| print"testing sort" |
| |
| |
| -- strange lengths |
| local a = setmetatable({}, {__len = function () return -1 end}) |
| assert(#a == -1) |
| table.sort(a, error) -- should not compare anything |
| a = setmetatable({}, {__len = function () return maxI end}) |
| checkerror("too big", table.sort, a) |
| |
| -- test checks for invalid order functions |
| local function check (t) |
| local function f(a, b) assert(a and b); return true end |
| checkerror("invalid order function", table.sort, t, f) |
| end |
| |
| check{1,2,3,4} |
| check{1,2,3,4,5} |
| check{1,2,3,4,5,6} |
| |
| |
| function check (a, f) |
| f = f or function (x,y) return x<y end; |
| for n = #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) |
| |
| local function perm (s, n) |
| n = n or #s |
| if n == 1 then |
| local t = {unpack(s)} |
| table.sort(t) |
| check(t) |
| else |
| for i = 1, n do |
| s[i], s[n] = s[n], s[i] |
| perm(s, n - 1) |
| s[i], s[n] = s[n], s[i] |
| end |
| end |
| end |
| |
| perm{} |
| perm{1} |
| perm{1,2} |
| perm{1,2,3} |
| perm{1,2,3,4} |
| perm{2,2,3,4} |
| perm{1,2,3,4,5} |
| perm{1,2,3,3,5} |
| perm{1,2,3,4,5,6} |
| perm{2,2,3,3,5,6} |
| |
| local function timesort (a, n, func, msg, pre) |
| local x = os.clock() |
| table.sort(a, func) |
| x = (os.clock() - x) * 1000 |
| pre = pre or "" |
| print(string.format("%ssorting %d %s elements in %.2f msec.", pre, n, msg, x)) |
| check(a, func) |
| end |
| |
| local limit = 50000 |
| if _soft then limit = 5000 end |
| |
| a = {} |
| for i=1,limit do |
| a[i] = math.random() |
| end |
| |
| timesort(a, limit, nil, "random") |
| |
| timesort(a, limit, nil, "sorted", "re-") |
| |
| a = {} |
| for i=1,limit do |
| a[i] = math.random() |
| end |
| |
| local x = os.clock(); local i = 0 |
| table.sort(a, function(x,y) i=i+1; return y<x end) |
| x = (os.clock() - x) * 1000 |
| print(string.format("Invert-sorting other %d elements in %.2f msec., with %i comparisons", |
| limit, x, i)) |
| check(a, function(x,y) return y<x end) |
| |
| |
| table.sort{} -- empty array |
| |
| for i=1,limit do a[i] = false end |
| timesort(a, limit, function(x,y) return nil end, "equal") |
| |
| for i,v in pairs(a) do assert(v == false) end |
| |
| AA = {"\xE1lo", "\0first :-)", "alo", "then this one", "45", "and a new"} |
| table.sort(AA) |
| check(AA) |
| |
| table.sort(AA, function (x, y) |
| load(string.format("AA[%q] = ''", x), "")() |
| collectgarbage() |
| return x<y |
| end) |
| |
| _G.AA = nil |
| |
| local 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) |
| |
| print"OK" |