local Q={}local m=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local function V(Q)assert(type(Q)=="\116\097\098\108\101","\070\105\114\115\116 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")local m={}for Q,e in pairs(Q)do if type(e)=="\116\097\098\108\101"then m[Q]=V(e)else m[Q]=e end end return m end local function e(Q,m)assert(type(Q)=="\116\097\098\108\101","\070\105\114\115\116 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")assert(type(m)=="\116\097\098\108\101","\083\101\099\111\110\100 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")for B,d in pairs(Q)do local p=m[B]if p==nil then Q[B]=nil elseif type(d)~=type(p)then if type(p)=="\116\097\098\108\101"then Q[B]=V(p)else Q[B]=p end elseif type(d)=="\116\097\098\108\101"then e(d,p)end end for m,e in pairs(m)do local B=Q[m]if B==nil then if type(e)=="\116\097\098\108\101"then Q[m]=V(e)else Q[m]=e end end end end local function B(Q,m)local V=#Q Q[m]=Q[V]Q[V]=nil end local function d(Q,m)assert(type(Q)=="\116\097\098\108\101","\070\105\114\115\116 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")assert(type(m)=="\102\117\110\099\116\105\111\110","\083\101\099\111\110\100 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097\110 \097\114\114\097\121")local V={}for e,B in pairs(Q)do V[e]=m(B,e,Q)end return V end local function p(Q,m)assert(type(Q)=="\116\097\098\108\101","\070\105\114\115\116 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")assert(type(m)=="\102\117\110\099\116\105\111\110","\083\101\099\111\110\100 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097\110 \097\114\114\097\121")local V={}if#Q>0 then local e=0 for B=1,#Q,1 do local d=Q[B]if m(d,B,Q)then e=e+1 V[e]=d end end else for e,B in pairs(Q)do if m(B,e,Q)then V[e]=B end end end return V end local function X(Q,m,V)assert(type(Q)=="\116\097\098\108\101","\070\105\114\115\116 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")assert(type(m)=="\102\117\110\099\116\105\111\110","\083\101\099\111\110\100 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097\110 \097\114\114\097\121")assert(V==nil or type(V)=="\110\117\109\098\101\114","\084\104\105\114\100 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114 \111\114 \110\105\108")local e=V or 0 for V,B in pairs(Q)do e=m(e,B,V,Q)end return e end local function Y(Q,m,V)assert(type(Q)=="\116\097\098\108\101","\070\105\114\115\116 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")assert(m==nil or type(m)=="\115\116\114\105\110\103","\083\101\099\111\110\100 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103 \111\114 \110\105\108")m=m or"\084\065\066\076\069"local e={}local B=" - "local function d(Q,m)e[#e+1]=B:rep(m)..(Q.."\n")end local function p(Q,m)return tostring(Q.k)<tostring(m.k)end local function X(Q,m,e)d(e.."\058",m-1)local B={}local Y={}local D=0 for Q,m in pairs(Q)do if type(m)=="\116\097\098\108\101"then table.insert(Y,{k=Q;v=m})else table.insert(B,{k=Q;v="\091"..(typeof(m)..("\093 "..tostring(m)))})end local V=#tostring(Q)+1 if V>D then D=V end end table.sort(B,p)table.sort(Y,p)for Q,V in pairs(B)do d(tostring(V.k)..("\058"..((" "):rep(D-#tostring(V.k))..V.v)),m)end if V then for Q,V in pairs(Y)do X(V.v,m+1,tostring(V.k)..((" "):rep(D-#tostring(V.k)).." \091\084\097\098\108\101\093"))end else for Q,V in pairs(Y)do d(tostring(V.k)..("\058"..((" "):rep(D-#tostring(V.k)).."\091\084\097\098\108\101\093")),m)end end end X(Q,1,m)print(table.concat(e,""))end local function D(Q,m)for V=1,#Q,1 do if Q[V]==m then return V end end return nil end local function I(Q)local m={}local V=#Q for e=1,V,1 do m[e]=Q[(V-e)+1]end return m end local function z(Q)assert(type(Q)=="\116\097\098\108\101","\070\105\114\115\116 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \116\097\098\108\101")for m=#Q,2,-1 do local V=math.random(m)Q[m],Q[V]=Q[V],Q[m]end end local function b(Q)return next(Q)==nil end local function M(Q)return m:JSONEncode(Q)end local function U(Q)return m:JSONDecode(Q)end local function L(Q,m)local V=D(Q,m)if V then B(Q,V)return true,V end return false,nil end Q.Copy=V Q.Sync=e Q.FastRemove=B Q.FastRemoveFirstValue=L Q.Print=Y Q.Map=d Q.Filter=p Q.Reduce=X Q.IndexOf=D Q.Reverse=I Q.Shuffle=z Q.IsEmpty=b Q.EncodeJSON=M Q.DecodeJSON=U return Q
--[[
	
	TableUtil.Copy(Table tbl)
	TableUtil.Sync(Table tbl, Table templateTbl)
	TableUtil.Print(Table tbl, String label, Boolean deepPrint)
	TableUtil.FastRemove(Table tbl, Number index)
	TableUtil.FastRemoveFirstValue(Table tbl, Variant value)
	TableUtil.Map(Table tbl, Function callback)
	TableUtil.Filter(Table tbl, Function callback)
	TableUtil.Reduce(Table tbl, Function callback [, Number initialValue])
	TableUtil.IndexOf(Table tbl, Variant item)
	TableUtil.Reverse(Table tbl)
	TableUtil.Shuffle(Table tbl)
	TableUtil.IsEmpty(Table tbl)
	TableUtil.EncodeJSON(Table tbl)
	TableUtil.DecodeJSON(String json)

	EXAMPLES:

		Copy:

			Performs a deep copy of the given table.

			local tbl = {"a", "b", "c"}
			local tblCopy = TableUtil.Copy(tbl)


		Sync:

			Synchronizes a table to a template table. If the table does not have an
			item that exists within the template, it gets added. If the table has
			something that the template does not have, it gets removed.

			local tbl1 = {kills = 0; deaths = 0; points = 0}
			local tbl2 = {points = 0}
			TableUtil.Sync(tbl2, tbl1)  -- In words: "Synchronize table2 to table1"
			print(tbl2.deaths)


		Print:

			Prints out the table to the output in an easy-to-read format. Good for
			debugging tables. If deep printing, avoid cyclical references.

			local tbl = {a = 32; b = 64; c = 128; d = {x = 0; y = 1; z = 2}}
			TableUtil.Print(tbl, "My Table", true)


		FastRemove:

			Removes an item from an array at a given index. Only use this if you do
			NOT care about the order of your array. This works by simply popping the
			last item in the array and overwriting the given index with the last
			item. This is O(1), compared to table.remove's O(n) speed.

			local tbl = {"hello", "there", "this", "is", "a", "test"}
			TableUtil.FastRemove(tbl, 2)   -- Remove "there" in the array
			print(table.concat(tbl, " "))  -- > hello this is a test


		FastRemoveFirstValue:

			Calls FastRemove on the first index that holds the given value.

			local tbl = {"abc", "hello", "hi", "goodbye", "hello", "hey"}
			local removed, atIndex = TableUtil.FastRemoveFirstValue(tbl, "hello")
			if (removed) then
				print("Removed at index " .. atIndex)
				print(table.concat(tbl, " "))  -- > abc hi goodbye hello hey
			else
				print("Did not find value")
			end

		
		Map:

			This allows you to construct a new table by calling the given function
			on each item in the table.

			local peopleData = {
				{firstName = "Bob"; lastName = "Smith"};
				{firstName = "John"; lastName = "Doe"};
				{firstName = "Jane"; lastName = "Doe"};
			}

			local people = TableUtil.Map(peopleData, function(item)
				return {Name = item.firstName .. " " .. item.lastName}
			end)

			-- 'people' is now an array that looks like: { {Name = "Bob Smith"}; ... }


		Filter:

			This allows you to create a table based on the given table and a filter
			function. If the function returns 'true', the item remains in the new
			table; if the function returns 'false', the item is discluded from the
			new table.

			local people = {
				{Name = "Bob Smith"; Age = 42};
				{Name = "John Doe"; Age = 34};
				{Name = "Jane Doe"; Age = 37};
			}

			local peopleUnderForty = TableUtil.Filter(people, function(item)
				return item.Age < 40
			end)


		Reduce:

			This allows you to reduce an array to a single value. Useful for quickly
			summing up an array.

			local tbl = {40, 32, 9, 5, 44}
			local tblSum = TableUtil.Reduce(tbl, function(accumulator, value)
				return accumulator + value
			end)
			print(tblSum)  -- > 130


		IndexOf:

			Returns the index of the given item in the table. If not found, this
			will return nil.

			local tbl = {"Hello", 32, true, "abc"}
			local abcIndex = TableUtil.IndexOf("abc")     -- > 4
			local helloIndex = TableUtil.IndexOf("Hello") -- > 1
			local numberIndex = TableUtil.IndexOf(64)     -- > nil


		Reverse:

			Creates a reversed version of the array. Note: This is a shallow
			copy, so existing references will remain within the new table.

			local tbl = {2, 4, 6, 8}
			local rblReversed = TableUtil.Reverse(tbl)  -- > {8, 6, 4, 2}


		Shuffle:

			Shuffles (i.e. randomizes) an array. This uses the Fisher-Yates algorithm.

			local tbl = {1, 2, 3, 4, 5, 6, 7, 8, 9}
			TableUtil.Shuffle(tbl)
			print(table.concat(tbl, ", "))  -- e.g. > 3, 6, 9, 2, 8, 4, 1, 7, 5
	
--]]

local TableUtil = {}

local http = game:GetService("HttpService")

local function CopyTable(t)
	assert(type(t) == "table", "First argument must be a table")
	local tCopy = {}
	for k, v in pairs(t) do
		if type(v) == "table" then
			tCopy[k] = CopyTable(v)
		else
			tCopy[k] = v
		end
	end
	return tCopy
end

local function Sync(tbl, templateTbl)
	assert(type(tbl) == "table", "First argument must be a table")
	assert(type(templateTbl) == "table", "Second argument must be a table")

	-- If 'tbl' has something 'templateTbl' doesn't, then remove it from 'tbl'
	-- If 'tbl' has something of a different type than 'templateTbl', copy from 'templateTbl'
	-- If 'templateTbl' has something 'tbl' doesn't, then add it to 'tbl'
	for k, v in pairs(tbl) do
		local vTemplate = templateTbl[k]

		-- Remove keys not within template:
		if vTemplate == nil then
			tbl[k] = nil

		-- Synchronize data types:
		elseif type(v) ~= type(vTemplate) then
			if type(vTemplate) == "table" then
				tbl[k] = CopyTable(vTemplate)
			else
				tbl[k] = vTemplate
			end

		-- Synchronize sub-tables:
		elseif type(v) == "table" then
			Sync(v, vTemplate)
		end
	end

	-- Add any missing keys:
	for k, vTemplate in pairs(templateTbl) do
		local v = tbl[k]

		if v == nil then
			if type(vTemplate) == "table" then
				tbl[k] = CopyTable(vTemplate)
			else
				tbl[k] = vTemplate
			end
		end
	end
end

local function FastRemove(t, i)
	local n = #t
	t[i] = t[n]
	t[n] = nil
end

local function Map(t, f)
	assert(type(t) == "table", "First argument must be a table")
	assert(type(f) == "function", "Second argument must be an array")
	local newT = {}
	for k, v in pairs(t) do
		newT[k] = f(v, k, t)
	end
	return newT
end

local function Filter(t, f)
	assert(type(t) == "table", "First argument must be a table")
	assert(type(f) == "function", "Second argument must be an array")
	local newT = {}
	if #t > 0 then
		local n = 0
		for i = 1, #t do
			local v = t[i]
			if f(v, i, t) then
				n = (n + 1)
				newT[n] = v
			end
		end
	else
		for k, v in pairs(t) do
			if f(v, k, t) then
				newT[k] = v
			end
		end
	end
	return newT
end

local function Reduce(t, f, init)
	assert(type(t) == "table", "First argument must be a table")
	assert(type(f) == "function", "Second argument must be an array")
	assert(init == nil or type(init) == "number", "Third argument must be a number or nil")
	local result = (init or 0)
	for k, v in pairs(t) do
		result = f(result, v, k, t)
	end
	return result
end

local function Print(tbl, label, deepPrint)
	assert(type(tbl) == "table", "First argument must be a table")
	assert(label == nil or type(label) == "string", "Second argument must be a string or nil")

	label = (label or "TABLE")

	local strTbl = {}
	local indent = " - "

	-- Insert(string, indentLevel)
	local function Insert(s, l)
		strTbl[#strTbl + 1] = (indent:rep(l) .. s .. "\n")
	end

	local function AlphaKeySort(a, b)
		return (tostring(a.k) < tostring(b.k))
	end

	local function PrintTable(t, lvl, lbl)
		Insert(lbl .. ":", lvl - 1)
		local nonTbls = {}
		local tbls = {}
		local keySpaces = 0
		for k, v in pairs(t) do
			if type(v) == "table" then
				table.insert(tbls, { k = k, v = v })
			else
				table.insert(nonTbls, { k = k, v = "[" .. typeof(v) .. "] " .. tostring(v) })
			end
			local spaces = #tostring(k) + 1
			if spaces > keySpaces then
				keySpaces = spaces
			end
		end
		table.sort(nonTbls, AlphaKeySort)
		table.sort(tbls, AlphaKeySort)
		for _, v in pairs(nonTbls) do
			Insert(tostring(v.k) .. ":" .. (" "):rep(keySpaces - #tostring(v.k)) .. v.v, lvl)
		end
		if deepPrint then
			for _, v in pairs(tbls) do
				PrintTable(v.v, lvl + 1, tostring(v.k) .. (" "):rep(keySpaces - #tostring(v.k)) .. " [Table]")
			end
		else
			for _, v in pairs(tbls) do
				Insert(tostring(v.k) .. ":" .. (" "):rep(keySpaces - #tostring(v.k)) .. "[Table]", lvl)
			end
		end
	end

	PrintTable(tbl, 1, label)

	print(table.concat(strTbl, ""))
end

local function IndexOf(tbl, item)
	for i = 1, #tbl do
		if tbl[i] == item then
			return i
		end
	end
	return nil
end

local function Reverse(tbl)
	local tblRev = {}
	local n = #tbl
	for i = 1, n do
		tblRev[i] = tbl[n - i + 1]
	end
	return tblRev
end

local function Shuffle(tbl)
	assert(type(tbl) == "table", "First argument must be a table")
	for i = #tbl, 2, -1 do
		local j = math.random(i)
		tbl[i], tbl[j] = tbl[j], tbl[i]
	end
end

local function IsEmpty(tbl)
	return (next(tbl) == nil)
end

local function EncodeJSON(tbl)
	return http:JSONEncode(tbl)
end

local function DecodeJSON(str)
	return http:JSONDecode(str)
end

local function FastRemoveFirstValue(t, v)
	local index = IndexOf(t, v)
	if index then
		FastRemove(t, index)
		return true, index
	end
	return false, nil
end

TableUtil.Copy = CopyTable
TableUtil.Sync = Sync
TableUtil.FastRemove = FastRemove
TableUtil.FastRemoveFirstValue = FastRemoveFirstValue
TableUtil.Print = Print
TableUtil.Map = Map
TableUtil.Filter = Filter
TableUtil.Reduce = Reduce
TableUtil.IndexOf = IndexOf
TableUtil.Reverse = Reverse
TableUtil.Shuffle = Shuffle
TableUtil.IsEmpty = IsEmpty
TableUtil.EncodeJSON = EncodeJSON
TableUtil.DecodeJSON = DecodeJSON

return TableUtil
