local P="\079\112\116\105\111\110"local x={}x.__index=x function x._new(g)local U=setmetatable({ClassName=P;_v=g,_s=g~=nil},x)return U end function x.Some(P)assert(P~=nil,"\079\112\116\105\111\110\046\083\111\109\101\040\041 \118\097\108\117\101 \099\097\110\110\111\116 \098\101 \110\105\108")return x._new(P)end function x.Wrap(P)if P==nil then return x.None else return x.Some(P)end end function x.Is(P)return type(P)=="\116\097\098\108\101"and getmetatable(P)==x end function x.Assert(P)assert(x.Is(P),"\082\101\115\117\108\116 \119\097\115 \110\111\116 \111\102 \116\121\112\101 \079\112\116\105\111\110")end function x.Deserialize(g)assert(type(g)=="\116\097\098\108\101"and g.ClassName==P,"\073\110\118\097\108\105\100 \100\097\116\097 \102\111\114 \100\101\115\101\114\105\097\108\105\122\105\110\103 \079\112\116\105\111\110")return g.Value==nil and x.None or x.Some(g.Value)end function x.Serialize(P)return{ClassName=P.ClassName,Value=P._v}end function x.Match(x,P)local g=P.Some local U=P.None assert(type(g)=="\102\117\110\099\116\105\111\110","\077\105\115\115\105\110\103 \'\083\111\109\101\' \109\097\116\099\104")assert(type(U)=="\102\117\110\099\116\105\111\110","\077\105\115\115\105\110\103 \'\078\111\110\101\' \109\097\116\099\104")if x:IsSome()then return g(x:Unwrap())else return U()end end function x.IsSome(P)return P._s end function x.IsNone(P)return not P._s end function x.Expect(x,P)assert(x:IsSome(),P)return x._v end function x.ExpectNone(x,P)assert(x:IsNone(),P)end function x.Unwrap(P)return P:Expect("\067\097\110\110\111\116 \117\110\119\114\097\112 \111\112\116\105\111\110 \111\102 \078\111\110\101 \116\121\112\101")end function x.UnwrapOr(x,P)if x:IsSome()then return x:Unwrap()else return P end end function x.UnwrapOrElse(x,P)if x:IsSome()then return x:Unwrap()else return P()end end function x.And(g,P)if g:IsSome()then return P else return x.None end end function x.AndThen(g,P)if g:IsSome()then local U=P(g:Unwrap())x.Assert(U)return U else return x.None end end function x.Or(x,P)if x:IsSome()then return x else return P end end function x.OrElse(g,P)if g:IsSome()then return g else local g=P()x.Assert(g)return g end end function x.XOr(g,P)local U=g:IsSome()local E=P:IsSome()if U==E then return x.None elseif U then return g else return P end end function x.Filter(g,P)if g:IsNone()or not P(g._v)then return x.None else return g end end function x.Contains(x,P)return x:IsSome()and x._v==P end function x.__tostring(P)if P:IsSome()then return"\079\112\116\105\111\110\060"..(typeof(P._v).."\062")else return"\079\112\116\105\111\110\060\078\111\110\101\062"end end function x.__eq(g,P)if x.Is(P)then if g:IsSome()and P:IsSome()then return g:Unwrap()==P:Unwrap()elseif g:IsNone()and P:IsNone()then return true end end return false end x.None=x._new()return x



local CLASSNAME = "Option"

local Option = {}
Option.__index = Option


function Option._new(value)
	local self = setmetatable({
		ClassName = CLASSNAME;
		_v = value;
		_s = (value ~= nil);
	}, Option)
	return self
end


function Option.Some(value)
	assert(value ~= nil, "Option.Some() value cannot be nil")
	return Option._new(value)
end


function Option.Wrap(value)
	if value == nil then
		return Option.None
	else
		return Option.Some(value)
	end
end


function Option.Is(obj)
	return type(obj) == "table" and getmetatable(obj) == Option
end


function Option.Assert(obj)
	assert(Option.Is(obj), "Result was not of type Option")
end


function Option.Deserialize(data) -- type data = {ClassName: string, Value: any}
	assert(type(data) == "table" and data.ClassName == CLASSNAME, "Invalid data for deserializing Option")
	return data.Value == nil and Option.None or Option.Some(data.Value)
end


function Option:Serialize()
	return {
		ClassName = self.ClassName;
		Value = self._v;
	}
end


function Option:Match(matches)
	local onSome = matches.Some
	local onNone = matches.None
	assert(type(onSome) == "function", "Missing 'Some' match")
	assert(type(onNone) == "function", "Missing 'None' match")
	if self:IsSome() then
		return onSome(self:Unwrap())
	else
		return onNone()
	end
end


function Option:IsSome()
	return self._s
end


function Option:IsNone()
	return (not self._s)
end


function Option:Expect(msg)
	assert(self:IsSome(), msg)
	return self._v
end


function Option:ExpectNone(msg)
	assert(self:IsNone(), msg)
end


function Option:Unwrap()
	return self:Expect("Cannot unwrap option of None type")
end


function Option:UnwrapOr(default)
	if self:IsSome() then
		return self:Unwrap()
	else
		return default
	end
end


function Option:UnwrapOrElse(defaultFunc)
	if self:IsSome() then
		return self:Unwrap()
	else
		return defaultFunc()
	end
end


function Option:And(optB)
	if self:IsSome() then
		return optB
	else
		return Option.None
	end
end


function Option:AndThen(andThenFunc)
	if self:IsSome() then
		local result = andThenFunc(self:Unwrap())
		Option.Assert(result)
		return result
	else
		return Option.None
	end
end


function Option:Or(optB)
	if self:IsSome() then
		return self
	else
		return optB
	end
end


function Option:OrElse(orElseFunc)
	if self:IsSome() then
		return self
	else
		local result = orElseFunc()
		Option.Assert(result)
		return result
	end
end


function Option:XOr(optB)
	local someOptA = self:IsSome()
	local someOptB = optB:IsSome()
	if someOptA == someOptB then
		return Option.None
	elseif someOptA then
		return self
	else
		return optB
	end
end


function Option:Filter(predicate)
	if self:IsNone() or not predicate(self._v) then
		return Option.None
	else
		return self
	end
end


function Option:Contains(value)
	return self:IsSome() and self._v == value
end


function Option:__tostring()
	if self:IsSome() then
		return ("Option<" .. typeof(self._v) .. ">")
	else
		return "Option<None>"
	end
end


function Option:__eq(opt)
	if Option.Is(opt) then
		if self:IsSome() and opt:IsSome() then
			return (self:Unwrap() == opt:Unwrap())
		elseif self:IsNone() and opt:IsNone() then
			return true
		end
	end
	return false
end


Option.None = Option._new()


return Option
