local y={}do local g={}function y.easyIndex(y)for y=1,y,1 do if g[y]==nil then g[y]={__index=function(H,k)H[k]=setmetatable({},g[y-1])return H[k]end}end end return g[y]end end function y.assign(y,...)for g,H in ipairs({...})do for g,H in pairs(H)do y[g]=H end end return y end function y.makeToString(y)return function(g)return("\037\115\040\037\115\041"):format(y,(getmetatable(g)).name)end end function y.concat(y,...)local g={...}local H={}for g=1,#y,1 do H[g]=y[g]end for y=1,#g,1 do local k=g[y]for y=1,#k,1 do H[#H+1]=k[y]end end return H end function y.callCounter()return setmetatable({call=function(y,g)y[g]=y[g]+1 end},{__index=function(y,g)y[g]=0 return 0 end})end function y.deepCopy(g)if type(g)=="\116\097\098\108\101"then local H={}for g,k in pairs(g)do H[g]=y.deepCopy(k)end return H else return g end end function y.deepEquals(g,H)if type(g)~="\116\097\098\108\101"or type(H)~="\116\097\098\108\101"then return g==H end for k in pairs(g)do local o=g[k]local T=H[k]if type(o)=="\116\097\098\108\101"and type(T)=="\116\097\098\108\101"then local g=y.deepEquals(o,T)if not g then return false end elseif o~=T then return false end end for y in pairs(H)do if g[y]==nil then return false end end return true end function y.requireAllInAnd(g,H,k)for g,o in ipairs(g:GetChildren())do if o:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then H(k,require(o))else y.requireAllInAnd(o,H,k)end end end function y.mapArray(y,g)local H={}for y,k in ipairs(y)do H[y]=g(k,y)end return H end function y.mapTuple(y,...)local g={}for H,k in ipairs({...})do g[H]=y(k)end return unpack(g)end return y
local Util = {}

do
	local metatables = {}
	function Util.easyIndex(levels)
		for i = 1, levels do
			if metatables[i] == nil then
				metatables[i] = {
					__index = function(self, k)
						self[k] = setmetatable({}, metatables[i - 1])
						return self[k]
					end
				}
			end
		end

		return metatables[levels]
	end
end

function Util.assign(toObj, ...)
	for _, fromObj in ipairs({...}) do
		for key, value in pairs(fromObj) do
			toObj[key] = value
		end
	end

	return toObj
end

function Util.makeToString(staticName)
	return function(self)
		return ("%s(%s)"):format(staticName, getmetatable(self).name)
	end
end

function Util.concat(list, ...)
	local args = { ... }
	local result = {}
	for i = 1, #list do
		result[i] = list[i]
	end
	for i = 1, #args do
		local value = args[i]
		for j = 1, #value do
			result[#result + 1] = value[j]
		end
	end
	return result
end

function Util.callCounter()
	return setmetatable({
		call = function(self, key)
			self[key] = self[key] + 1
		end
	}, {
		__index = function(self, key)
			self[key] = 0
			return 0
		end
	})
end

function Util.deepCopy(t)
	if type(t) == "table" then
		local n = {}
		for i,v in pairs(t) do
			n[i] = Util.deepCopy(v)
		end
		return n
	else
		return t
	end
end

function Util.deepEquals(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then
		return a == b
	end

	for k in pairs(a) do
		local av = a[k]
		local bv = b[k]
		if type(av) == "table" and type(bv) == "table" then
			local result = Util.deepEquals(av, bv)
			if not result then
				return false
			end
		elseif av ~= bv then
			return false
		end
	end

	-- extra keys in b
	for k in pairs(b) do
		if a[k] == nil then
			return false
		end
	end

	return true
end

function Util.requireAllInAnd(instance, callback, self)
	for _, object in ipairs(instance:GetChildren()) do
		if object:IsA("ModuleScript") then
			callback(self, require(object))
		else
			Util.requireAllInAnd(object, callback, self)
		end
	end
end

--- Maps values of an array through a callback and returns an array of mapped values
function Util.mapArray(array, callback)
	local results = {}

	for i, v in ipairs(array) do
		results[i] = callback(v, i)
	end

	return results
end

--- Maps arguments #2-n through callback and returns values as tuple
function Util.mapTuple(callback, ...)
	local results = {}
	for i, value in ipairs({...}) do
		results[i] = callback(value)
	end
	return unpack(results)
end

return Util
