local create = coroutine.create
local wrap = coroutine.wrap
local resume = coroutine.resume
local yield = coroutine.yield

-- Test stack overflow handling on return from coroutine.
do
  wrap(function()
    local co = create(function()
      yield(string.byte(string.rep(" ", 100), 1, 100))
    end)
    assert(select('#', resume(co)) == 101)
  end)()
end

do
  wrap(function()
    local f = wrap(function()
      yield(string.byte(string.rep(" ", 100), 1, 100))
    end)
    assert(select('#', f()) == 100)
  end)()
end

do
  local function cogen(x)
    return wrap(function(n) repeat x = x+n; n = yield(x) until false end),
	   wrap(function(n) repeat x = x*n; n = yield(x) until false end)
  end

  local a,b=cogen(3)
  local c,d=cogen(5)
  assert(d(b(c(a(d(b(c(a(1)))))))) == 168428160)
end

do
  local function verify(what, expect, ...)
    local got = {...}
    for i=1,100 do
      if expect[i] ~= got[i] then
	error("FAIL " .. what)
      end
      if expect[i] == nil then
	break
      end
    end
  end

  local function cofunc(...)
    verify("call", { 1, "foo" }, ...)
    verify("yield", { "bar" }, yield(2, "test"))
    verify("pcall yield", { true, "again" }, pcall(yield, "from pcall"))
    return "end"
  end

  local co = create(cofunc)
  verify("resume", { true, 2, "test" }, resume(co, 1, "foo"))
  verify("resume pcall", { true, "from pcall" }, resume(co, "bar"))
  verify("resume end", { true, "end" }, resume(co, "again"))
end

do
  local function verify(expect, func, ...)
    local co = create(func)
    for i=1,100 do
      local ok, res = resume(co, ...)
      if not ok then
	if expect[i] ~= nil then
	  error("too few results: ["..i.."] = "..tostring(expect[i]).." (got: "..tostring(res)..")")
	end
	break
      end
      if expect[i] ~= res then
	error("bad result: ["..i.."] = "..tostring(res).." (should be: "..tostring(expect[i])..")")
      end
    end
  end

  verify({ 42, 99 },
    function(x) pcall(yield, x) return 99 end,
    42)

  verify({ 42, 99 },
    function(x) pcall(function(y) yield(y) end, x) return 99 end,
    42)

  verify({ 42, 99 },
    function(x) xpcall(yield, debug.traceback, x) return 99 end,
    42)

  verify({ 45, 44, 43, 42, 99 },
    function(x, y)
      for i in
	function(o, k)
	  yield(o+k)
	  if k ~= 0 then return k-1 end
	end,x,y do
      end
      return 99
    end,
    42, 3)

  verify({ 84, 99 },
    function(x)
      local o = setmetatable({ x },
	{__add = function(a, b) yield(a[1]+b[1]) return 99 end })
      return o+o
    end,
    42)
end

