
local a, b, i = 0, 0, 0

for i = 1, 5 do
	a = a + 1
	b = b + i
end

assertTrue (a == 5, 'For loop should iterate the correct number of times')
assertTrue (b == 15, 'For loop variable should hold the value of the current iteration')


local funcs = {}
local b = ''

for i = 1, 3 do
	table.insert(funcs, function () b = b..i end)
end

funcs[1]()
funcs[2]()
funcs[3]()
assertTrue (b == '123', 'Each loop of numeric for should maintain its own scope')




a = { a = 1, b = 2 }
b = 0

for _ in pairs(a) do b = b + 1 end

assertTrue (b == 2, 'For block should iterate over all properties of a table')


a.a = nil
b = 0

for _ in pairs(a) do b = b + 1 end

assertTrue (b == 1, 'Setting a table property to nil should remove that property from the table.')



b = {}

for i = 1, 3 do
	local c = i
	b[i] = function() return c end
end

assertTrue (b[1]() == 1, 'Local within a closure should keep its value [1]')
assertTrue (b[2]() == 2, 'Local within a closure should keep its value [2]')
assertTrue (b[3]() == 3, 'Local within a closure should keep its value [3]')

b = ''
i = 1
for i = 1, 4 do
	b = b..i
	break
end
assertTrue (b == '1', 'Break should exit numerical for loop')


a = ''
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau', [function () end] = 'test'}

for key, val in pairs(u) do
	a = a..'['..tostring(key)..'=='..tostring(val)..']'
end


assertTrue (string.find(a, '[6.28==tau]') ~= nil, 'for/pairs iteration should include items with double as key.')
assertTrue (string.find(a, '[@!#==qbert]') ~= nil, 'for/pairs iteration should include items with string as key.')
assertTrue (string.find(a, '[table: 0x%d+==1729]') ~= nil, 'for/pairs iteration should include items with table as key.')
assertTrue (string.find(a, '[function: 0x%d+==test]') ~= nil, 'for/pairs iteration should include items with function as key.')

a = ''
t = {1,2,3}
for key, val in pairs(t) do
	a = a..'['..tostring(key)..'=='..tostring(val)..']'
	break
end
assertTrue (a == '[1==1]', 'Break should exit generic for loop')


function iter(t, i)
  if i < 5 then
 	  return i + 1, i
	else
		return nil
	end
end
a = ''
for key, val in iter, {}, 2 do
	a = a..'['..tostring(key)..'=='..tostring(val)..']'
end
assertTrue (a == '[3==2][4==3][5==4]', 'Generic for loop should accept an expression list')

function iter(t, i)
	i = i + 1
	v = t[i]
	if v then
 	  return i, v, v * 2
	end
end
a = ''
for x,y,z in iter, {4,5,6}, 0 do
	a = a..'['..tostring(x)..','..tostring(y)..','..tostring(z)..']'
end
assertTrue (a == '[1,4,8][2,5,10][3,6,12]', 'Generic for loop should pass all values returned from iterator to the variables in the loop')


local funcs = {}
local a = {1,2,3}
local b = ''

for _, i in ipairs(a) do
	table.insert(funcs, function () b = b..i end)
end

funcs[1]()
funcs[2]()
funcs[3]()
assertTrue (b == '123', 'Each loop of generic for should maintain its own scope')


a = ''
b = 1
while #a < 15 do
	a = a..b
	b = b + 1
end

assertEqual (a, '123456789101112', 'While loop should iterate until condition is met')

local fail = false
while b < 0 do
	fail = true
end

assertEqual (fail, false, 'While loop should not iterate if condition is never met')

b = {}
i = 1
while i < 4 do
	local c = i
	b[i] = function() return c end
	i = i + 1
end

assertTrue (b[1]() == 1, 'Local within a while loop closure should keep its value [1]')
assertTrue (b[2]() == 2, 'Local within a while loop closure should keep its value [2]')
assertTrue (b[3]() == 3, 'Local within a while loop closure should keep its value [3]')

b = ''
i = 1
while i < 4 do
	b = b..i
	i = i + 1
	break
end
assertTrue (b == '1', 'Break should exit while loop')


local funcs = {}
local b = ''
i = 1

while i < 4 do
	table.insert(funcs, function () b = b..i end)
	i = i + 1
end

funcs[1]()
funcs[2]()
funcs[3]()
assertTrue (b == '444', 'Each loop of while should not maintain its own scope')



a = ''
b = 1
repeat
	a = a..b
	b = b + 1
until #a > 15

assertEqual (a, '12345678910111213', 'Repeat loop should iterate until condition is met')

b = 1
repeat
	b = b + 1
until b > 0

assertEqual (b, 2, 'Repeat loop should iterate once if condition is always met')

b = {}
i = 1
repeat
	local c = i
	b[i] = function() return c end
	i = i + 1
until i == 4

assertTrue (b[1]() == 1, 'Local within a while loop closure should keep its value [1]')
assertTrue (b[2]() == 2, 'Local within a while loop closure should keep its value [2]')
assertTrue (b[3]() == 3, 'Local within a while loop closure should keep its value [3]')


b = ''
i = 1
repeat
	b = b..i
	i = i + 1
	break
until i == 4
assertTrue (b == '1', 'Break should exit repeat loop')


local funcs = {}
local b = ''
i = 1

repeat
	table.insert(funcs, function () b = b..i end)
	i = i + 1
until i == 4

funcs[1]()
funcs[2]()
funcs[3]()
assertTrue (b == '444', 'Each loop of repeat should not maintain its own scope')


a = ':'
t = { 123, 456, x = 789, 10 }
for i, v in ipairs(t) do
	a = a..i..'='..v..':'
end

assertTrue (string.find(a, ':1=123:') ~= nil, 'for-in-ipairs loop should iterate over numeric keys [1]')
assertTrue (string.find(a, ':2=456:') ~= nil, 'for-in-ipairs loop should iterate over numeric keys [2]')
assertTrue (string.find(a, ':3=10:') ~= nil, 'for-in-ipairs loop should iterate over numeric keys [3]')
assertTrue (string.find(a, ':x=789:') == nil, 'for-in-ipairs loop should not iterate over non-numeric keys')
assertEqual (a, ':1=123:2=456:3=10:', 'for-in-ipairs loop should iterate over numeric keys in order')


b = nil
a = function ()
	return false, true
end

if a() then
	b = 'THIS SHOULD NOT EXECUTE'
end
assertEqual (b, nil, 'If clause with function call should only use first returned value. [1]')

b = nil
a = function ()
	return true, false
end

if a() then
	b = 'THIS SHOULD EXECUTE'
end
assertEqual (b, 'THIS SHOULD EXECUTE', 'If clause with function call should only use first returned value. [2]')


-- do block

local a = 1
local b = 10
do
	local a = 2
	b = 20
	assertEqual (a, 2, 'Do block should use local variables')
	assertEqual (b, 20, 'Do block should set upvalues')
end
assertEqual (a, 1, 'Do block should create its own local variable scope')
assertEqual (b, 20, 'Do block should update upvalues')

a = '1'
function testReturn()
	a = a..'2'
	do
		a = a..'3'
		return
	end
	a = a..'X'
end

testReturn()
assertEqual (a, '123', 'Do block containing return should return from parent function')
