local tuple = require'tuple'

local function collectall()
	local after = collectgarbage'count'
	repeat
		local before = after
		collectgarbage()
		after = collectgarbage'count'
	until after >= before
end

--test: leaf index found but has no tuple.
local t1 = tuple('a','b','c')
local t2 = tuple('a','b')
assert(t1 ~= t2)

--test: nil and nan values.
local ab = 'a'..string.char(string.byte('b')) --because constants are not collected
local e0 = tuple(ab)             assert(e0() == ab)
local e1 = tuple(ab,nil,1)       assert(e1(2) == nil); assert(e1(3) == 1)
local e2 = tuple(ab,nil,2)       assert(e2(2) == nil); assert(e2(3) == 2)
local e3 = tuple(ab,nil,2,nil)   assert(select('#', e3()) == 4)
local e4 = tuple(ab,0/0,2,nil)   assert(e4(2) ~= e4(2))
local a,b,c,d = e4()
assert(a==ab and b~=b and c==2 and d==nil)

--test: anchoring of index tables (insufficient).
ab = nil
collectall()
local ab = 'a'..'b'
assert(e0 == tuple(ab))
assert(e1 == tuple(ab,nil,1))
assert(e2 == tuple(ab,nil,2))
assert(e2 ~= tuple(ab,nil,2,nil))
assert(e3 == tuple(ab,nil,2,nil))
assert(e4 == tuple(ab,0/0,2,nil))

--test: tostring()
assert(tostring(e0) == '(ab)')
assert(tostring(tuple('b', 1, 'd')) == '(b, 1, d)')
if jit then --only luajit has 'nan' for 0/0
	assert(tostring(e4) == '(ab, nan, 2, nil)')
end

--test: anchoring of index tables.
local t = {}
for i=1,20 do
	for j = 1,20 do
		t[tuple(i, j)] = true
	end
end
collectall()
for i=1,20 do
	for j = 1,20 do
		assert(t[tuple(i, j)])
	end
end

--test: tuple spaces and wrapping.
local tuple = tuple.space(true)
local t1 = tuple(1, 2, 3)
collectall()
assert(tuple(1, 2, 3) == t1)
print(t1)

--test: tuple from array: allow for easy creation of sparse tuples
local t = tuple.from_array{n = 3}
assert(t == tuple(nil, nil, nil))

--test: tuple with fixed number of elements
local t = tuple.narg(3)
assert(t == tuple.narg(3, nil))
assert(t == tuple.narg(3, nil, nil))
assert(t == tuple(nil, nil, nil))

local t = tuple.narg(3, 1)
assert(t == tuple.narg(3, 1, nil))
assert(t == tuple.narg(3, 1, nil, nil))
assert(t == tuple(1, nil, nil))

--test: leaks
local weakvals_meta = {__mode = 'v'}
local function weakvals(t)
	return setmetatable(t or {}, weakvals_meta)
end
local function strongvals(t)
	return t or {}
end
local function testleaks(weak, s)
	local vals = weak and weakvals or strongvals
	local index, tuples = vals(), vals()
	local tuple = tuple.space(weak, index, tuples)
	local before = collectgarbage'count'
	for i=1,10^5 do
		local a = math.floor(i / 53512) % 23
		local b = math.floor(i / 1439) % 17
		local c = math.floor(i / 197) % 16
		local d = math.floor(i / 16) % 19
		local e = i % 10
		tuple(a, b, c, d, e)
	end
	collectall()
	local after = collectgarbage'count'
	if weak then --this is the actual test
		assert(next(index) == nil)
		assert(next(tuples) == nil)
	end
	print(string.format('leak (%s): %d KB', s, after - before))
end
testleaks(true, 'weak')
testleaks(false, 'strong')

--test: iterate & remove
local tp = tuple.space()
for i = 1, 1000 do
	local t = {}
	for j = 1, math.random(1, 10) do
		t[j] = math.random(1, 5)
	end
	local t = tp.from_array(t)
end
local n = 0
for t in tp.tuples() do
	tp.remove(t)
	n = n + 1
end
print(n)
for t in tp.tuples() do
	assert(false)
end
