local K=table.remove local M=table.concat local D=table.insert local b=setmetatable({},{__index=table,__call=function(K,M)return setmetatable(K.copy(M),{__index=table})end})function b.map(K,M)assert(type(K)=="\116\097\098\108\101","\065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\109\097\112 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116 "..type(K))assert(type(M)=="\102\117\110\099\116\105\111\110","\065\114\103\117\109\101\110\116 \035\050 \116\111 \116\097\098\108\101\046\109\097\112 - \101\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \103\111\116 "..type(M))local D={}for a=1,#K,1 do local U,Z=M(K[a],a)if Z then D[Z]=U else b.insert(D,U)end end return D end function b.filter(K,M)assert(type(K)=="\116\097\098\108\101","\065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\102\105\108\116\101\114 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116 "..type(K))assert(type(M)=="\102\117\110\099\116\105\111\110","\065\114\103\117\109\101\110\116 \035\050 \116\111 \116\097\098\108\101\046\102\105\108\116\101\114 - \101\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \103\111\116 "..type(M))local b={}for a=1,#K,1 do local U=K[a]if M(U)then D(b,U)end end return b end function b.contains(K,M)assert(type(K)=="\116\097\098\108\101","\065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\099\111\110\116\097\105\110\115 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116 "..type(K))assert(M~=nil,"\065\114\103\117\109\101\110\116 \035\050 \116\111 \116\097\098\108\101\046\099\111\110\116\097\105\110\115 - \101\120\112\101\099\116\101\100 \110\111\110-\110\105\108 \118\097\108\117\101 \103\111\116 "..type(M))for D=1,#K,1 do local b=K[D]if b==M then return true end end return false end function b.find(K,M)assert(type(K)=="\116\097\098\108\101","\065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\102\105\110\100 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116 "..type(K))assert(type(M)=="\102\117\110\099\116\105\111\110","\065\114\103\117\109\101\110\116 \035\050 \116\111 \116\097\098\108\101\046\102\105\110\100 - \101\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \103\111\116 "..type(M))for D=1,#K,1 do local b=K[D]if M(b,D)then return b,D end end end local a=0 local function U(K,D)D=D or{}local Z=D.pretty and D.whitespace or nil local A=D.showTableString local m={}local k=type(Z)=="\115\116\114\105\110\103"and Z or"\t"for K,M in next,K do local y=type(K)=="\110\117\109\098\101\114"if type(M)=="\116\097\098\108\101"then a=a+1 if A then M="\040"..(tostring(M)..("\041 "..U(M,D)))else M=U(M,D)end a=a-1 elseif type(M)=="\115\116\114\105\110\103"then M="\""..(M.."\"")elseif type(M)=="\102\117\110\099\116\105\111\110"then M="\060\102\117\110\099\116\105\111\110\062"elseif typeof(M)=="\073\110\115\116\097\110\099\101"then M="\060"..(M:GetFullName().."\062")elseif type(M)=="\117\115\101\114\100\097\116\097"then M="\060"..(tostring(M).."\062")end b.insert(m,(Z and"\037\115"..(k.."\037\115 \061 \037\115")or"\037\115\037\115 \061 \037\115"):format(Z and k:rep(a)or"",y and"\091"..(tostring(K).."\093")or K,tostring(M)))end return"\123"..((Z and"\n"or"")..(M(m,Z and"\044\n"or"\044 ")..((Z and"\n"..k:rep(a)or"").."\125")))end function b.tostring(K,M)if typeof(M)=="\116\097\098\108\101"then return U(K,M)else return U(K,{pretty=M~=nil;whitespace=type(M)=="\115\116\114\105\110\103"and M or"\t"})end end function b.concat(K,a)local U={}for M=1,#K,1 do local Z=K[M]if type(Z)=="\116\097\098\108\101"then D(U,"\091 "..(b.concat(Z,a).." \093"))else D(U,tostring(Z))end end return M(U,a)end function b.remove(M,D)if type(D)=="\110\117\109\098\101\114"then return K(M,D)elseif type(D)=="\102\117\110\099\116\105\111\110"then for b,a in next,M do if D(a,b)then K(M,b)end end else for b,a in next,M do if a==D then return K(M,b)end end end end function b.copy(K)assert(type(K)=="\116\097\098\108\101","\073\110\118\097\108\105\100 \065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\099\111\112\121 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116"..typeof(K))local M={}for K,D in next,K do if type(D)=="\116\097\098\108\101"then M[K]=b.copy(D)else M[K]=D end end return M end function b.sub(K,M,b)assert(type(K)=="\116\097\098\108\101","\073\110\118\097\108\105\100 \065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\099\111\112\121 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116"..typeof(K))assert(type(M)=="\110\117\109\098\101\114"or M==nil,"\073\110\118\097\108\105\100 \065\114\103\117\109\101\110\116 \035\050 \116\111 \116\097\098\108\101\046\099\111\112\121 - \101\120\112\101\099\116\101\100 \110\117\109\098\101\114 \103\111\116"..typeof(M))assert(type(b)=="\110\117\109\098\101\114"or b==nil,"\073\110\118\097\108\105\100 \065\114\103\117\109\101\110\116 \035\051 \116\111 \116\097\098\108\101\046\099\111\112\121 - \101\120\112\101\099\116\101\100 \110\117\109\098\101\114 \103\111\116"..typeof(b))b=b or#K M=M or 1 local a={}for M=M,b,1 do D(a,K[M])end return a end function b.truncate(K)assert(type(K)=="\116\097\098\108\101","\065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\116\114\117\110\099\097\116\101 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116 "..typeof(K))for M=1,#K,1 do if K[M]==nil then b.remove(K,M)M=M-1 end end end function b.insert(K,M,D)assert(type(K)=="\116\097\098\108\101","\065\114\103\117\109\101\110\116 \035\049 \116\111 \116\097\098\108\101\046\105\110\115\101\114\116 - \101\120\112\101\099\116\101\100 \116\097\098\108\101 \103\111\116 "..typeof(K))assert(type(D)=="\110\117\109\098\101\114"or D==nil,"\065\114\103\117\109\101\110\116 \035\051 \116\111 \116\097\098\108\101\046\105\110\115\101\114\116 - \101\120\112\101\099\116\101\100 \110\117\109\098\101\114 \103\111\116 "..typeof(D))if D then for D=1,#D,1 do K[#K+1]=M end else K[#K+1]=M end end function b.join(...)local K={...}local M={}for K,b in next,K do assert(type(b)=="\116\097\098\108\101")for K,b in next,b do if type(K)=="\115\116\114\105\110\103"then M[K]=b else D(M,b)end end end return M end function b.keys(K)local M={}for K in next,K do b.insert(M,K)end return M end function b.values(K)local M={}for K,D in next,K do b.insert(M,D)end return M end return b
local table_remove = table.remove
local table_concat = table.concat
local table_insert = table.insert
local table =
	setmetatable(
	{},
	{
		__index = table,
		__call = function(self, onTable)
			return setmetatable(self.copy(onTable), {__index = table})
		end
	}
)

function table.map(t, mapFn)
	assert(type(t) == "table", "Argument #1 to table.map - expected table got " .. type(t))
	assert(type(mapFn) == "function", "Argument #2 to table.map - expected function got " .. type(mapFn))

	local mapped = {}

	for i = 1, #t do
		local nextValue, nextKey = mapFn(t[i], i)
		if nextKey then
			mapped[nextKey] = nextValue
		else
			table.insert(mapped, nextValue)
		end
	end

	return mapped
end

function table.filter(t, filterFn)
	assert(type(t) == "table", "Argument #1 to table.filter - expected table got " .. type(t))
	assert(type(filterFn) == "function", "Argument #2 to table.filter - expected function got " .. type(filterFn))

	local filtered = {}
	for i = 1, #t do
		local value = t[i]
		if filterFn(value) then
			table_insert(filtered, value)
		end
	end

	return filtered
end

function table.contains(t, value)
	assert(type(t) == "table", "Argument #1 to table.contains - expected table got " .. type(t))
	assert(value ~= nil, "Argument #2 to table.contains - expected non-nil value got " .. type(value))

	for i = 1, #t do
		local tvalue = t[i]
		if tvalue == value then
			return true
		end
	end

	return false
end

function table.find(t, findFn)
	assert(type(t) == "table", "Argument #1 to table.find - expected table got " .. type(t))
	assert(type(findFn) == "function", "Argument #2 to table.find - expected function got " .. type(findFn))

	for i = 1, #t do
		local value = t[i]
		if findFn(value, i) then
			return value, i
		end
	end
end

local indent = 0
local function table_to_str(t, options)
	options = options or {}

	local prettyPrint = options.pretty and options.whitespace or nil
	local displayTableString = options.showTableString

	local values = {}
	local char = type(prettyPrint) == "string" and prettyPrint or "\t"
	for k, value in next, t do
		local isNumericKey = type(k) == "number"

		if type(value) == "table" then
			indent = indent + 1

			if displayTableString then
				-- luacov: enable
				-- luacov: disable
				value = "(" .. tostring(value) .. ") " .. table_to_str(value, options)
			else
				value = table_to_str(value, options)
			end

			indent = indent - 1
		elseif type(value) == "string" then
			value = '"' .. value .. '"'
		elseif type(value) == "function" then
			value = "<function>"
		elseif typeof(value) == "Instance" then
			value = "<" .. value:GetFullName() .. ">"
		elseif type(value) == "userdata" then
			value = "<" .. tostring(value) .. ">"
		end

		table.insert(
			values,
			(prettyPrint and "%s" .. char .. "%s = %s" or "%s%s = %s"):format(
				prettyPrint and (char):rep(indent) or "",
				isNumericKey and "[" .. tostring(k) .. "]" or k,
				tostring(value)
			)
		)
	end

	return "{" ..
		(prettyPrint and "\n" or "") ..
			table_concat(values, prettyPrint and ",\n" or ", ") .. (prettyPrint and "\n" .. (char):rep(indent) or "") .. "}"
end

function table.tostring(t, options)
	if typeof(options) == "table" then
		return table_to_str(t, options)
	else
		return table_to_str(t, {pretty = options ~= nil, whitespace = type(options) == "string" and options or "\t"})
	end
end

-- Improved table.concat
function table.concat(t, sep)
	local values = {}
	for i = 1, #t do
		local value = t[i]
		if type(value) == "table" then
			table_insert(values, "[ " .. table.concat(value, sep) .. " ]")
		else
			table_insert(values, tostring(value))
		end
	end

	return table_concat(values, sep)
end

-- Improved table.remove
function table.remove(t, query)
	if type(query) == "number" then
		return table_remove(t, query)
	elseif type(query) == "function" then
		for index, value in next, t do
			if (query(value, index)) then
				table_remove(t, index)
			end
		end
	else
		for index, value in next, t do
			if (value == query) then
				return table_remove(t, index)
			end
		end
	end
end

function table.copy(t)
	assert(type(t) == "table", "Invalid Argument #1 to table.copy - expected table got" .. typeof(t))

	local newTable = {}

	for key, value in next, t do
		if type(value) == "table" then
			newTable[key] = table.copy(value)
		else
			newTable[key] = value
		end
	end

	return newTable
end

function table.sub(tbl, start, finish)
	assert(type(tbl) == "table", "Invalid Argument #1 to table.copy - expected table got" .. typeof(tbl))
	assert(
		type(start) == "number" or start == nil,
		"Invalid Argument #2 to table.copy - expected number got" .. typeof(start)
	)
	assert(
		type(finish) == "number" or finish == nil,
		"Invalid Argument #3 to table.copy - expected number got" .. typeof(finish)
	)

	finish = finish or #tbl
	start = start or 1

	local result = {}
	for i = start, finish do
		table_insert(result, tbl[i])
	end

	return result
end

function table.truncate(t)
	assert(type(t) == "table", "Argument #1 to table.truncate - expected table got " .. typeof(t))
	for i = 1, #t do
		if t[i] == nil then
			table.remove(t, i)
			-- luacheck: ignore
			i = i - 1
		end
	end
end

function table.insert(t, value, rep)
	assert(type(t) == "table", "Argument #1 to table.insert - expected table got " .. typeof(t))
	assert(type(rep) == "number" or rep == nil, "Argument #3 to table.insert - expected number got " .. typeof(rep))

	if rep then
		for _ = 1, #rep do
			--table_insert(t, value)
			t[#t + 1] = value
		end
	else
		t[#t + 1] = value
	end
end

function table.join(...)
	local tables = {...}
	local newTable = {}
	for _, t in next, tables do
		assert(type(t) == "table")
		for k, v in next, t do
			if (type(k) == "string") then
				newTable[k] = v
			else
				table_insert(newTable, v)
			end
		end
	end
	return newTable
end

function table.keys(tbl)
	local targetTable = {}

	for key in next, tbl do
		table.insert(targetTable, key);
	end

	return targetTable
end

function table.values(tbl)
	local targetTable = {}

	for _, value in next, tbl do
		table.insert(targetTable, value);
	end

	return targetTable
end

return table
