-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
print("testing native code generation")

assert((function(x, y)
  -- trigger a linear sequence
  local t1 = x + 2
  local t2 = x - 7

  local a = x * 10
  local b = a + 1
  a = y -- update 'a' version
  local t = {} -- call to allocate table forces a spill
  local c = x * 10
  return c, b, t, t1, t2
end)(5, 10) == 50)

assert((function(x)
  local oops -- split to prevent inlining
  function oops()
  end

  -- x is checked to be a number here; we can not execute a reentry from oops() because optimizer assumes this holds until return
  local y = math.abs(x)
  oops()
  return y * x
end)("42") == 1764)

local function fuzzfail1(...)
  repeat
    _ = nil
  until not {}
  for _ in ... do
    for l0=_,_ do
    end
    return
  end
end

local function fuzzfail2()
  local _
  do
    repeat
      _ = typeof(_),{_=_,}
      _ = _(_._)
    until _
  end
end

assert(pcall(fuzzfail2) == false)

local function fuzzfail3()
  function _(...)
    _({_,_,true,},{...,},_,not _)
  end
  _()
end

assert(pcall(fuzzfail3) == false)

local function fuzzfail4()
  local _ = setmetatable({},setmetatable({_=_,},_))
  return _(_:_())
end

assert(pcall(fuzzfail4) == false)

local function fuzzfail5()
  local _ = bit32.band
  _(_(_,0),_)
  _(_,_)
end

assert(pcall(fuzzfail5) == false)

local function fuzzfail6(_)
  return bit32.extract(_,671088640,_)
end

assert(pcall(fuzzfail6, 1) == false)

local function fuzzfail7(_)
  return bit32.extract(_,_,671088640)
end

assert(pcall(fuzzfail7, 1) == false)

local function fuzzfail8(...)
  local _ = _,_
  _.n0,_,_,_,_,_,_,_,_._,_,_,_[...],_,_,_ = nil
  _,n0,_,_,_,_,_,_,_,_,l0,_,_,_,_ = nil
  function _()
  end
  _._,_,_,_,_,_,_,_,_,_,_[...],_,n0[l0],_ = nil
  _[...],_,_,_,_,_,_,_,_()[_],_,_,_,_,_ = _(),...
end

assert(pcall(fuzzfail8) == false)

local function fuzzfail9()
  local _ = bit32.bor
  local x = _(_(_,_),_(_,_),_(-16834560,_),_(_(- _,-2130706432)),- _),_(_(_,_),_(-16834560,-2130706432))
end

assert(pcall(fuzzfail9) == false)

local function fuzzfail10()
  local _
  _ = false,if _ then _ else _
  _ = not _
  l0,_[l0] = not _
end

assert(pcall(fuzzfail10) == false)

local function fuzzfail11(x, ...)
  return bit32.arshift(bit32.bnot(x),(...))
end

assert(fuzzfail11(0xffff0000, 8) == 0xff)

local function fuzzfail12()
  _,_,_,_,_,_,_,_ = not _, not _, not _, not _, not _, not _, not _, not _
end

assert(pcall(fuzzfail12) == true)

local function fuzzfail13()
  _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ = not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _
end

assert(pcall(fuzzfail13) == true)

local function fuzzfail14()
  for l0=771751936,_ do
    for l0=771751936,0 do
      while 538970624 do
      end
    end
  end
end

assert(pcall(fuzzfail14) == false)

local function fuzzfail15()
  local a
  if a then
    repeat until a
  else
    local b = `{a}`
    a = nil
  end
end

assert(pcall(fuzzfail15) == true)

local function fuzzfail16()
  _ = {[{[2]=77,_=_,[2]=_,}]=not _,}
  _ = {77,[2]=11008,[2]=_,[0]=_,}
end

assert(pcall(fuzzfail16) == true)

local function fuzzfail17()
  return bit32.extract(1293942816,1293942816)
end

assert(pcall(fuzzfail17) == false)

local function fuzzfail18()
  return bit32.extract(7890276,0)
end

assert(pcall(fuzzfail18) == true)
assert(fuzzfail18() == 0)

local function fuzzfail19()
  local _ = 2
  _ += _
  _ = _,_ >= _,{_ >= _,_ >= _,_(),}

  local _ = 2
  do
    _ = assert({n0=_,_,n0=_,}),{_={_[_()],},_,}
  end
end

assert(pcall(fuzzfail19) == false)

local function fuzzfail20()
  assert(true)
  assert(false,(_),true)
  _ = nil
end

assert(pcall(fuzzfail20) == false)

local function fuzzfail21(...)
  local _ = assert,_
  if _ then else return _ / _ end
  _(_)
  _(_,_)
  assert(...,_)
  _((not _),_)
  _(true,_ / _)
  _(_,_())
  return _
end

assert(pcall(fuzzfail21) == false)

local function fuzzfail22(...)
  local _ = {false,},true,...,l0
  while _ do
  _ = true,{unpack(0,_),},l0
  _.n126 = nil
  _ = {not _,_=not _,n0=_,_,n0=not _,},_ < _
  return _ > _
  end
  return `""`
end

assert(pcall(fuzzfail22) == false)

local function fuzzfail23(...)
  local _ = {false,},_,...,l0
  while _ do
  _ = true,{unpack(_),},l0
  _ = {{[_]=nil,_=not _,_,true,_=nil,},not _,not _,_,bxor=- _,}
  do end
  break
  end
  do end
  local _ = _,true
  do end
  local _ = _,true
end

assert(pcall(fuzzfail23) == false)

local function fuzzfail24(...)
  local _ = l4
  repeat
  local function l0()
  end
  until (...)[_][_]:n16((_),_,l4,bit32.btest(0,_,_,_) / bit32.btest(0,_,_,_)(_,_,_,0,((_)),_))
  do end
end

assert(pcall(fuzzfail24) == false)

local function fuzzfail25(...)
  local _ = fill
  while {[_ + _]={[bit32.bxor(65535,65535,_)]=bit32.bxor(-1610607103,65535,65535,_),},_=bit32.bxor(-1610607103,65535,65535,_),[538973697]=_,[_]=_,} do
  end
end

assert(pcall(fuzzfail25) == false)

local function fuzzfail26(...)
  local loops = 10
  for l0 in pcall,function<A...>(...):any
      for _ in xpcall,_,_,_ do
      end
      function _():any
      end
      l0,l0._G,l0 = _,_
    end do
    function _<Q...>():any
      l0,l0 = 0,{}
      _()
    end

    loops -= 1
    if loops == 0 then break end
  end
  return true
end

assert(pcall(fuzzfail26))

local function fuzzfail27(...)
  for _ = 1,67 do
    assert(_, _ .. _ or _, {x=_ .. _ // _ .. _ or _ .. _,})
  end
end

assert(pcall(fuzzfail27))

local function fuzzfail28(...)
  _,_.n0,_,_,_[_ // _],_[_ + _ % _],_,_,_,_,l255,_,_,_,_,_,_,_,_,_[_],_,_,_ = table.insert("")
  _,_.n0,_,_,_[_ // _],_[_ + _ % _],_,_,_,_,l255,_,_,_,_,_,_,_,_,_[_],_,_,_ = table.insert("")
end

assert(pcall(fuzzfail28) == false)

local function fuzzfail29(...)
  return (vector.create(335572085, 4, 5) // 2) == ...
end

assert(fuzzfail29() == false)

local function arraySizeInv1()
  local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}

  table.insert(t, 3)

  return t[10]
end

assert(arraySizeInv1() == true)

local function arraySizeInv2()
  local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}

  local u = {a = t}
  table.insert(u.a, 3) -- aliased modifiction of 't' register through other value

  return t[10]
end

assert(arraySizeInv2() == true)

local function nilInvalidatesSlot()
  local function tabs()
    local t = { x=1, y=2, z=3 }
    setmetatable(t, { __index = function(t, k) return 42 end })
    return t, t
  end

  local t1, t2 = tabs()

  for i=1,2 do
    local a = t1.x
    t2.x = nil
    local b = t1.x
    t2.x = 1
    assert(a == 1 and b == 42)
  end
end

nilInvalidatesSlot()

local function arraySizeOpt1(a)
  a[1] += 2
  a[1] *= 3

  table.insert(a, 3)
  table.insert(a, 4)
  table.insert(a, 5)
  table.insert(a, 6)

  a[1] += 4
  a[1] *= 5

  return a[1] + a[5]
end

assert(arraySizeOpt1({1}) == 71)

local function arraySizeOpt2(a, i)
  a[i] += 2
  a[i] *= 3

  table.insert(a, 3)
  table.insert(a, 4)
  table.insert(a, 5)
  table.insert(a, 6)

  a[i] += 4
  a[i] *= 5

  return a[i] + a[5]
end

assert(arraySizeOpt2({1}, 1) == 71)

function deadLoopBody(n)
  local r = 0
  if n and false then
    for i = 1, n do
      r += 1
    end
  end
  return r
end

assert(deadLoopBody(5) == 0)

function arrayIndexingSpecialNumbers1(a, b, c)
  local arr = table.create(100000)
  arr[a] = 9
  arr[b-1] = 80
  arr[b] = 700
  arr[b+1] = 6000
  arr[c-1] = 50000
  arr[c] = 400000
  arr[c+1] = 3000000

  return arr[1] + arr[255] + arr[256] + arr[257] + arr[65535] + arr[65536] + arr[65537]
end

assert(arrayIndexingSpecialNumbers1(1, 256, 65536) == 3456789)

function loopIteratorProtocol(a, t)
  local sum = 0

  do
    local a, b, c, d, e, f, g = {}, {}, {}, {}, {}, {}, {}
  end

  for k, v in ipairs(t) do
    if k == 10 then sum += math.abs('-8') end

    sum += k
  end

  return sum
end

assert(loopIteratorProtocol(0, table.create(100, 5)) == 5058)

function valueTrackingIssue1()
  local b = buffer.create(1)
  buffer.writeu8(b, 0, 0)
  local v1

  local function closure()
    assert(type(b) == "buffer") -- b is the first upvalue
    v1 = nil -- v1 is the second upvalue

    -- prevent inlining
    for i = 1, 100 do print(`{b} is {b}`) end
  end

  closure()
end

valueTrackingIssue1()

local function vec3compsum(a: vector)
  return a.X + a.Y + a.Z
end

assert(vec3compsum(vector.create(1, 2, 4)) == 7.0)

local function vec3add(a: vector, b: vector) return a + b end
local function vec3sub(a: vector, b: vector) return a - b end
local function vec3mul(a: vector, b: vector) return a * b end
local function vec3div(a: vector, b: vector) return a / b end
local function vec3neg(a: vector) return -a end

assert(vec3add(vector.create(10, 20, 40), vector.create(1, 0, 2)) == vector.create(11, 20, 42))
assert(vec3sub(vector.create(10, 20, 40), vector.create(1, 0, 2)) == vector.create(9, 20, 38))
assert(vec3mul(vector.create(10, 20, 40), vector.create(1, 0, 2)) == vector.create(10, 0, 80))
assert(vec3div(vector.create(10, 20, 40), vector.create(1, 0, 2)) == vector.create(10, math.huge, 20))
assert(vec3neg(vector.create(10, 20, 40)) == vector.create(-10, -20, -40))

local function vec3mulnum(a: vector, b: number) return a * b end
local function vec3mulconst(a: vector) return a * 4 end

assert(vec3mulnum(vector.create(10, 20, 40), 4) == vector.create(40, 80, 160))
assert(vec3mulconst(vector.create(10, 20, 40), 4) == vector.create(40, 80, 160))

local function bufferbounds(zero)
  local b1 = buffer.create(1)
  local b2 = buffer.create(2)
  local b4 = buffer.create(4)
  local b8 = buffer.create(8)
  local b10 = buffer.create(10)

  -- only one valid position and size for a 1 byte buffer
  buffer.writei8(b1, zero + 0, buffer.readi8(b1, zero + 0))
  buffer.writeu8(b1, zero + 0, buffer.readu8(b1, zero + 0))

  -- 2 byte buffer
  buffer.writei8(b2, zero + 0, buffer.readi8(b2, zero + 0))
  buffer.writeu8(b2, zero + 0, buffer.readu8(b2, zero + 0))
  buffer.writei8(b2, zero + 1, buffer.readi8(b2, zero + 1))
  buffer.writeu8(b2, zero + 1, buffer.readu8(b2, zero + 1))
  buffer.writei16(b2, zero + 0, buffer.readi16(b2, zero + 0))
  buffer.writeu16(b2, zero + 0, buffer.readu16(b2, zero + 0))

  -- 4 byte buffer
  buffer.writei8(b4, zero + 0, buffer.readi8(b4, zero + 0))
  buffer.writeu8(b4, zero + 0, buffer.readu8(b4, zero + 0))
  buffer.writei8(b4, zero + 3, buffer.readi8(b4, zero + 3))
  buffer.writeu8(b4, zero + 3, buffer.readu8(b4, zero + 3))
  buffer.writei16(b4, zero + 0, buffer.readi16(b4, zero + 0))
  buffer.writeu16(b4, zero + 0, buffer.readu16(b4, zero + 0))
  buffer.writei16(b4, zero + 2, buffer.readi16(b4, zero + 2))
  buffer.writeu16(b4, zero + 2, buffer.readu16(b4, zero + 2))
  buffer.writei32(b4, zero + 0, buffer.readi32(b4, zero + 0))
  buffer.writeu32(b4, zero + 0, buffer.readu32(b4, zero + 0))
  buffer.writef32(b4, zero + 0, buffer.readf32(b4, zero + 0))

  -- 8 byte buffer
  buffer.writei8(b8, zero + 0, buffer.readi8(b8, zero + 0))
  buffer.writeu8(b8, zero + 0, buffer.readu8(b8, zero + 0))
  buffer.writei8(b8, zero + 7, buffer.readi8(b8, zero + 7))
  buffer.writeu8(b8, zero + 7, buffer.readu8(b8, zero + 7))
  buffer.writei16(b8, zero + 0, buffer.readi16(b8, zero + 0))
  buffer.writeu16(b8, zero + 0, buffer.readu16(b8, zero + 0))
  buffer.writei16(b8, zero + 6, buffer.readi16(b8, zero + 6))
  buffer.writeu16(b8, zero + 6, buffer.readu16(b8, zero + 6))
  buffer.writei32(b8, zero + 0, buffer.readi32(b8, zero + 0))
  buffer.writeu32(b8, zero + 0, buffer.readu32(b8, zero + 0))
  buffer.writef32(b8, zero + 0, buffer.readf32(b8, zero + 0))
  buffer.writei32(b8, zero + 4, buffer.readi32(b8, zero + 4))
  buffer.writeu32(b8, zero + 4, buffer.readu32(b8, zero + 4))
  buffer.writef32(b8, zero + 4, buffer.readf32(b8, zero + 4))
  buffer.writef64(b8, zero + 0, buffer.readf64(b8, zero + 0))

  -- 'any' size buffer
  buffer.writei8(b10, zero + 0, buffer.readi8(b10, zero + 0))
  buffer.writeu8(b10, zero + 0, buffer.readu8(b10, zero + 0))
  buffer.writei8(b10, zero + 9, buffer.readi8(b10, zero + 9))
  buffer.writeu8(b10, zero + 9, buffer.readu8(b10, zero + 9))
  buffer.writei16(b10, zero + 0, buffer.readi16(b10, zero + 0))
  buffer.writeu16(b10, zero + 0, buffer.readu16(b10, zero + 0))
  buffer.writei16(b10, zero + 8, buffer.readi16(b10, zero + 8))
  buffer.writeu16(b10, zero + 8, buffer.readu16(b10, zero + 8))
  buffer.writei32(b10, zero + 0, buffer.readi32(b10, zero + 0))
  buffer.writeu32(b10, zero + 0, buffer.readu32(b10, zero + 0))
  buffer.writef32(b10, zero + 0, buffer.readf32(b10, zero + 0))
  buffer.writei32(b10, zero + 6, buffer.readi32(b10, zero + 6))
  buffer.writeu32(b10, zero + 6, buffer.readu32(b10, zero + 6))
  buffer.writef32(b10, zero + 6, buffer.readf32(b10, zero + 6))
  buffer.writef64(b10, zero + 0, buffer.readf64(b10, zero + 0))
  buffer.writef64(b10, zero + 2, buffer.readf64(b10, zero + 2))

  assert(is_native())
end

bufferbounds(0)

local function bufferboundsmerge1(b12: buffer, a: number, ...)
  local r1 = buffer.readi32(b12, a + 8) + buffer.readi32(b12, a + 4) + buffer.readi32(b12, a + 0)
  assert(r1 == 0)

  assert(is_native())
end

bufferboundsmerge1(buffer.create(12), 0)

function deadStoreChecks1()
  local a = 1.0
  local b = 0.0

  local function update()
    b += a
    for i = 1, 100 do print(`{b} is {b}`) end
  end

  update()
  a = 10
  update()
  a = 100
  update()

  return b
end

assert(deadStoreChecks1() == 111)

local function extramath1(a)
  return type(math.sign(a))
end

assert(extramath1(2) == "number")
assert(extramath1("2") == "number")

local function extramath2(a)
  return type(math.modf(a))
end

assert(extramath2(2) == "number")
assert(extramath2("2") == "number")

local function extramath3(a)
  local b, c = math.modf(a)
  return type(c)
end

assert(extramath3(2) == "number")
assert(extramath3("2") == "number")

local function scopedrestorefailurea(format: string, b: buffer): (string, buffer)
  local H1F, H2F, H3F, H4F = buffer.readu32(b, 0), buffer.readu32(b, 8), buffer.readu32(b, 16), buffer.readu32(b, 24)
  local H5F, H6F, _, _ = buffer.readu32(b, 32), buffer.readu32(b, 40), buffer.readu32(b, 48), buffer.readu32(b, 56)
  local H1B, H2B, H3B, H4B = buffer.readu32(b, 4), buffer.readu32(b, 12), buffer.readu32(b, 20), buffer.readu32(b, 28)
  local H5B, H6B, _, _ = buffer.readu32(b, 36), buffer.readu32(b, 44), buffer.readu32(b, 52), buffer.readu32(b, 60)

  return string.format(format, H1F, H1B, H2F, H2B, H3F, H3B, H4F, H4B, H5F, H5B, H6F, H6B), b
end

local function scopedrestorefailureb()
  local data = buffer.create(64)
  buffer.fill(data, 0, 0x12, 64)
  assert(scopedrestorefailurea(string.rep("%08x", 12), data) == string.rep("12", 48))
end

scopedrestorefailureb()

local function slotcachelimit1()
  local tbl = {
    f1 = function() return 1 end,
    f2 = function() return 2 end,
    f3 = function() return 3 end,
    f4 = function() return 4 end,
    f5 = function() return 5 end,
    f6 = function() return 6 end,
    f7 = function() return 7 end,
    f8 = function() return 8 end,
    f9 = function() return 9 end,
    f10 = function() return 10 end,
    f11 = function() return 11 end,
    f12 = function() return 12 end,
    f13 = function() return 13 end,
    f14 = function() return 14 end,
    f15 = function() return 15 end,
    f16 = function() return 16 end,
  }

  local lookup = {
    [tbl.f1] = 1,
    [tbl.f2] = 2,
    [tbl.f3] = 3,
    [tbl.f4] = 4,
    [tbl.f5] = 5,
    [tbl.f6] = 6,
    [tbl.f7] = 7,
    [tbl.f8] = 8,
    [tbl.f9] = 9,
    [tbl.f10] = 10,
    [tbl.f11] = 11,
    [tbl.f12] = 12,
    [tbl.f13] = 13,
    [tbl.f14] = 14,
    [tbl.f15] = 15,
    [tbl.f16] = 16,
  }

  assert(is_native())

  return lookup
end

slotcachelimit1()

local function slotcachelimit2(foo, size)
  local c1 = foo(vector.create(size.X, size.Y, size.Z))
  local c2 = foo(vector.create(-size.X, size.Y, size.Z))
  local c3 = foo(vector.create(-size.X, -size.Y, size.Z))
  local c4 = foo(vector.create(-size.X, -size.Y, -size.Z))
  local c5 = foo(vector.create(size.X, -size.Y, -size.Z))
  local c6 = foo(vector.create(size.X, size.Y, -size.Z))
  local c7 = foo(vector.create(size.X, -size.Y, size.Z))
  local c8 = foo(vector.create(-size.X, size.Y, -size.Z))
  local max = vector.create(math.max(c1.X, c2.X, c3.X, c4.X, c5.X, c6.X, c7.X, c8.X), math.max(c1.Y, c2.Y, c3.Y, c4.Y, c5.Y, c6.Y, c7.Y, c8.Y), math.max(c1.Z, c2.Z, c3.Z, c4.Z, c5.Z, c6.Z, c7.Z, c8.Z))
  local min = vector.create(math.min(c1.X, c2.X, c3.X, c4.X, c5.X, c6.X, c7.X, c8.X), math.min(c1.Y, c2.Y, c3.Y, c4.Y, c5.Y, c6.Y, c7.Y, c8.Y), math.min(c1.Z, c2.Z, c3.Z, c4.Z, c5.Z, c6.Z, c7.Z, c8.Z))

  assert(is_native())
  return max - min
end

slotcachelimit2(function(a) return -a end, vector.create(1, 2, 3))

local function forcedregisterspill1(x, y, z)
  local c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 = (x + 1), (x + 2), (x + 3), (x + 4), (x + 5), (x + 6), (x + 7), (x + 8), (x + 9), (x + 10)
  local d1, d2, d3, d4, d5, d6, d7, d8, d9, d10 = (y + 1), (y + 2), (y + 3), (y + 4), (y + 5), (y + 6), (y + 7), (y + 8), (y + 9), (y + 10)
  local e1, e2, e3, e4, e5, e6, e7, e8, e9, e10 = (z * 1), (z * 2), (z * 3), (z * 4), (z * 5), (z * 6), (z * 7), (z * 8), (z * 9), (z * 10)

  -- force store of all those values for a call and then bring all those registers back from VM slot spills
  local t = {
    c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + (x + 1) + (x + 2) + (x + 3) + (x + 4) + (x + 5) + (x + 6) + (x + 7) + (x + 8) + (x + 9) + (x + 10),
    d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + (y + 1) + (y + 2) + (y + 3) + (y + 4) + (y + 5) + (y + 6) + (y + 7) + (y + 8) + (y + 9) + (y + 10),
    e1 + e2 + e3 + e4 + e5 + e6 + e7 + e8 + e9 + e10 + (z * 1) + (z * 2) + (z * 3) + (z * 4) + (z * 5) + (z * 6) + (z * 7) + (z * 8) + (z * 9) + (z * 10),
  }

  -- bring them back once more time
  local s1 = c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + (x + 1) + (x + 2) + (x + 3) + (x + 4) + (x + 5) + (x + 6) + (x + 7) + (x + 8) + (x + 9) + (x + 10)
  local s2 = d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + (y + 1) + (y + 2) + (y + 3) + (y + 4) + (y + 5) + (y + 6) + (y + 7) + (y + 8) + (y + 9) + (y + 10)
  local s3 = e1 + e2 + e3 + e4 + e5 + e6 + e7 + e8 + e9 + e10 + (z * 1) + (z * 2) + (z * 3) + (z * 4) + (z * 5) + (z * 6) + (z * 7) + (z * 8) + (z * 9) + (z * 10)

  -- have not fallen back
  assert(is_native())
  return s1, s2, s3, t
end

do
  local a, b, c, t = forcedregisterspill1(2, 20, 10)
  assert(a == 150 and b == 510 and c == 1100)
  assert(t[1] == 150 and t[2] == 510 and t[3] == 1100)
end

local function splitStoreRestore(x)
  local c1; c1 = function() return 1 + x end
  local c2; c2 = function() return 2 + x end
  local c3; c3 = function() return 3 + x end
  local c4; c4 = function() return 4 + x end
  local c5; c5 = function() return 5 + x end
  local c6; c6 = function() return 6 + x end
  local c7; c7 = function() return 7 + x end
  local c8; c8 = function() return 8 + x end
  local c9; c9 = function() return 9 + x end
  local ca; ca = function() return 10 + x end
  local cb; cb = function() return 11 + x end
  local cc; cc = function() return 12 + x end
  local cd; cd = function() return 13 + x end
  local ce; ce = function() return 14 + x end
  local cf; cf = function() return 15 + x end

  local t = {}

  t.a1 = c1
  t.a2 = c2
  t.a3 = c3
  t.a4 = c4
  t.a5 = c5
  t.a6 = c6
  t.a7 = c7
  t.a8 = c8
  t.a9 = c9
  t.aa = ca
  t.ab = cb
  t.ac = cc
  t.ad = cd
  t.ae = ce
  t.af = cf

  assert(is_native())
  return t
end

assert(splitStoreRestore(2).ad() == 15)

local function guard1(a)
  local c = 0
  if type(a) == "number" then
    c = a * 2
  end
  return c
end

assert(guard1(4) == 8)
assert(guard1({}) == 0)

local function guard2(a)
  local c = 0
  if type(a) ~= "number" then
    c = 4
  end
  return c
end

assert(guard2(4) == 0)
assert(guard2({}) == 4)

local function guard3(a)
  local c = 0
  if typeof(a) == "number" then
    c = a * 2
  end
  return c
end

assert(guard3(4) == 8)
assert(guard3({}) == 0)

local function guard4(a)
  local c = 0
  if typeof(a) ~= "number" then
    c = 4
  end
  return c
end

assert(guard4(4) == 0)
assert(guard4({}) == 4)

return('OK')
