import("util.lua")
-- This examples demonstrates that Lean objects are not very useful as Lua table keys.
local f = Const("f")
local m = {}
local env = environment()
env:add_var("T", Type())
env:add_var("f", mk_arrow(Const("T"), Const("T")))
for i = 1, 100 do
   env:add_var("a" .. i, Const("T"))
   local t = f(Const("a" .. i))
   -- Any object can be a key of a Lua table.
   -- But, Lua does not use the method __eq for comparing keys.
   -- The problem is that Lua uses its own hashcode that may not
   -- be compatible with the __eq implemantion.
   -- By non-compatible, we mean two objects my be equal by __eq, but
   -- the hashcodes may be different.
   m[t] = i
end

for t1, i in pairs(m) do
   local t2 = f(Const("a" .. i))
   -- print(t1, i, t2)
   assert(m[t1] == i)
   -- t1 and t2 are structurally equal
   assert(t1 == t2)
   -- t1 and t2 are different objects
   assert(not t1:is_eqp(t2))
   -- t2 is not a key of map
   assert(m[t2] == nil)
   assert(env:normalize(t1) == t1)
   assert(t1:instantiate(Const("a")) == t1)
   local t1_prime = t1:instantiate(Const("a"))
   -- t1 and t1_prime are structurally equal
   assert(t1 == t1_prime)
   -- Moreover, they are references to the same Lean object
   assert(t1:is_eqp(t1_prime))
   -- But, they are wrapped by different Lua userdata
   assert(m[t1_prime] == nil)
end

-- We can store elements that implement __lt and __eq metamethods in splay_maps.
-- The implementation assumes that the elements stored in the splay map can be totally ordered by __lt
m = splay_map()
m:insert(Const("a"), 10)
assert(m:contains(Const("a")))
assert(not m:contains(Const("b")))
assert(m:find(Const("a")) == 10)
local a, b = Consts("a, b")
m:insert(f(a, b), 20)
assert(m:find(f(a, b)) == 20)
assert(m:find(f(a, a)) == nil)
assert(m:size() == 2)
assert(#m == 2)
m:erase(f(a, a))
assert(m:size() == 2)
m:erase(f(a, b))
assert(m:size() == 1)
for i = 1, 100 do
   local t = f(Const("a" .. i))
   m:insert(t, i)
   assert(m:contains(t))
   assert(m:find(t) == i)
end

assert(m:size() == 101)

for i = 1, 100 do
   local t = f(Const("a" .. i))
   assert(m:find(t) == i)
end

-- The following call fails because integers cannot be compared with Lean expressions
assert(not pcall(function() m:insert(10, 20) end))

-- Splay maps copy operation is O(1)
local m2 = m:copy()
m2:insert(b, 20)
assert(m:size() == 101)
assert(m2:size() == 102)

-- We can also traverse all elements in the map
local num = 0
m:for_each(
   function(k, v)
      print(tostring(k) .. " -> " .. v)
      num = num + 1
   end
)
assert(num == 101)