function t.set(a)return t.map(a,t.literal(true))end do local a=t.keys(t.integer)function t.array(Q)assert(t.callback(Q))local d=t.values(Q)return function(Q)local B,c=a(Q)if B==false then return false,string.format("\091\097\114\114\097\121\093 \037\115",c or"")end local i=0 for a in ipairs(Q)do i=i+1 end for a in pairs(Q)do if a<1 or a>i then return false,string.format("\091\097\114\114\097\121\093 \107\101\121 \037\115 \109\117\115\116 \098\101 \115\101\113\117\101\110\116\105\097\108",tostring(a))end end local G,y=d(Q)if not G then return false,string.format("\091\097\114\114\097\121\093 \037\115",y or"")end return true end end function t.strictArray(...)local Q={...}assert((t.array(t.callback))(Q))return function(d)local B,c=a(d)if B==false then return false,string.format("\091\115\116\114\105\099\116\065\114\114\097\121\093 \037\115",c or"")end if#Q<#d then return false,string.format("\091\115\116\114\105\099\116\065\114\114\097\121\093 \065\114\114\097\121 \115\105\122\101 \101\120\099\101\101\100\115 \108\105\109\105\116 \111\102 \037\100",#Q)end for a,Q in pairs(Q)do local B,c=Q(d[a])if not B then return false,string.format("\091\115\116\114\105\099\116\065\114\114\097\121\093 \065\114\114\097\121 \105\110\100\101\120 \035\037\100 - \037\115",a,c)end end return true end end end do local a=t.array(t.callback)function t.union(...)local Q={...}assert(a(Q))return function(a)for Q,d in ipairs(Q)do if d(a)then return true end end return false,"\098\097\100 \116\121\112\101 \102\111\114 \117\110\105\111\110"end end t.some=t.union function t.intersection(...)local Q={...}assert(a(Q))return function(a)for Q,d in ipairs(Q)do local B,c=d(a)if not B then return false,c or""end end return true end end t.every=t.intersection end do local a=t.map(t.any,t.callback)function t.interface(Q)assert(a(Q))return function(a)local d,B=t.table(a)if d==false then return false,B or""end for Q,d in pairs(Q)do local B,c=d(a[Q])if B==false then return false,string.format("\091\105\110\116\101\114\102\097\099\101\093 \098\097\100 \118\097\108\117\101 \102\111\114 \037\115\058\n\t\037\115",tostring(Q),c or"")end end return true end end function t.strictInterface(Q)assert(a(Q))return function(a)local d,B=t.table(a)if d==false then return false,B or""end for Q,d in pairs(Q)do local B,c=d(a[Q])if B==false then return false,string.format("\091\105\110\116\101\114\102\097\099\101\093 \098\097\100 \118\097\108\117\101 \102\111\114 \037\115\058\n\t\037\115",tostring(Q),c or"")end end for a in pairs(a)do if not Q[a]then return false,string.format("\091\105\110\116\101\114\102\097\099\101\093 \117\110\101\120\112\101\099\116\101\100 \102\105\101\108\100 \037\113",tostring(a))end end return true end end end
--[[**
	ensures value is a table and all keys pass valueCheck and all values are true

	@param valueCheck The function to use to check the values

	@returns A function that will return true iff the condition is passed
**--]]
function t.set(valueCheck)
	return t.map(valueCheck, t.literal(true))
end

do
	local arrayKeysCheck = t.keys(t.integer)
	--[[**
		ensures value is an array and all values of the array match check

		@param check The check to compare all values with

		@returns A function that will return true iff the condition is passed
	**--]]
	function t.array(check)
		assert(t.callback(check))
		local valuesCheck = t.values(check)

		return function(value)
			local keySuccess, keyErrMsg = arrayKeysCheck(value)
			if keySuccess == false then
				return false, string.format("[array] %s", keyErrMsg or "")
			end

			-- # is unreliable for sparse arrays
			-- Count upwards using ipairs to avoid false positives from the behavior of #
			local arraySize = 0

			for _ in ipairs(value) do
				arraySize = arraySize + 1
			end

			for key in pairs(value) do
				if key < 1 or key > arraySize then
					return false, string.format("[array] key %s must be sequential", tostring(key))
				end
			end

			local valueSuccess, valueErrMsg = valuesCheck(value)
			if not valueSuccess then
				return false, string.format("[array] %s", valueErrMsg or "")
			end

			return true
		end
	end

	--[[**
		ensures value is an array of a strict makeup and size

		@param check The check to compare all values with

		@returns A function that will return true iff the condition is passed
	**--]]
	function t.strictArray(...)
		local valueTypes = { ... }
		assert(t.array(t.callback)(valueTypes))

		return function(value)
			local keySuccess, keyErrMsg = arrayKeysCheck(value)
			if keySuccess == false then
				return false, string.format("[strictArray] %s", keyErrMsg or "")
			end

			-- If there's more than the set array size, disallow
			if #valueTypes < #value then
				return false, string.format("[strictArray] Array size exceeds limit of %d", #valueTypes)
			end

			for idx, typeFn in pairs(valueTypes) do
				local typeSuccess, typeErrMsg = typeFn(value[idx])
				if not typeSuccess then
					return false, string.format("[strictArray] Array index #%d - %s", idx, typeErrMsg)
				end
			end

			return true
		end
	end
end

do
	local callbackArray = t.array(t.callback)
	--[[**
		creates a union type

		@param ... The checks to union

		@returns A function that will return true iff the condition is passed
	**--]]
	function t.union(...)
		local checks = { ... }
		assert(callbackArray(checks))

		return function(value)
			for _, check in ipairs(checks) do
				if check(value) then
					return true
				end
			end

			return false, "bad type for union"
		end
	end

	--[[**
		Alias for t.union
	**--]]
	t.some = t.union

	--[[**
		creates an intersection type

		@param ... The checks to intersect

		@returns A function that will return true iff the condition is passed
	**--]]
	function t.intersection(...)
		local checks = { ... }
		assert(callbackArray(checks))

		return function(value)
			for _, check in ipairs(checks) do
				local success, errMsg = check(value)
				if not success then
					return false, errMsg or ""
				end
			end

			return true
		end
	end

	--[[**
		Alias for t.intersection
	**--]]
	t.every = t.intersection
end

do
	local checkInterface = t.map(t.any, t.callback)
	--[[**
		ensures value matches given interface definition

		@param checkTable The interface definition

		@returns A function that will return true iff the condition is passed
	**--]]
	function t.interface(checkTable)
		assert(checkInterface(checkTable))
		return function(value)
			local tableSuccess, tableErrMsg = t.table(value)
			if tableSuccess == false then
				return false, tableErrMsg or ""
			end

			for key, check in pairs(checkTable) do
				local success, errMsg = check(value[key])
				if success == false then
					return false, string.format("[interface] bad value for %s:\n\t%s", tostring(key), errMsg or "")
				end
			end

			return true
		end
	end

	--[[**
		ensures value matches given interface definition strictly

		@param checkTable The interface definition

		@returns A function that will return true iff the condition is passed
	**--]]
	function t.strictInterface(checkTable)
		assert(checkInterface(checkTable))
		return function(value)
			local tableSuccess, tableErrMsg = t.table(value)
			if tableSuccess == false then
				return false, tableErrMsg or ""
			end

			for key, check in pairs(checkTable) do
				local success, errMsg = check(value[key])
				if success == false then
					return false, string.format("[interface] bad value for %s:\n\t%s", tostring(key), errMsg or "")
				end
			end

			for key in pairs(value) do
				if not checkTable[key] then
					return false, string.format("[interface] unexpected field %q", tostring(key))
				end
			end

			return true
		end
	end
end
