local l="\079\112\116\105\111\110"local s={}s.__index=s function s._new(L)local Q=setmetatable({ClassName=l,_v=L;_s=L~=nil},s)return Q end function s.Some(l)assert(l~=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 s._new(l)end function s.Wrap(l)if l==nil then return s.None else return s.Some(l)end end function s.Is(l)return type(l)=="\116\097\098\108\101"and getmetatable(l)==s end function s.Assert(l)assert(s.Is(l),"\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 s.Deserialize(L)assert(type(L)=="\116\097\098\108\101"and L.ClassName==l,"\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 L.Value==nil and s.None or s.Some(L.Value)end function s.Serialize(l)return{ClassName=l.ClassName,Value=l._v}end function s.Match(s,l)local L=l.Some local Q=l.None assert(type(L)=="\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(Q)=="\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 s:IsSome()then return L(s:Unwrap())else return Q()end end function s.IsSome(l)return l._s end function s.IsNone(l)return not l._s end function s.Expect(s,l)assert(s:IsSome(),l)return s._v end function s.ExpectNone(s,l)assert(s:IsNone(),l)end function s.Unwrap(l)return l: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 s.UnwrapOr(s,l)if s:IsSome()then return s:Unwrap()else return l end end function s.UnwrapOrElse(s,l)if s:IsSome()then return s:Unwrap()else return l()end end function s.And(L,l)if L:IsSome()then return l else return s.None end end function s.AndThen(L,l)if L:IsSome()then local Q=l(L:Unwrap())s.Assert(Q)return Q else return s.None end end function s.Or(s,l)if s:IsSome()then return s else return l end end function s.OrElse(L,l)if L:IsSome()then return L else local L=l()s.Assert(L)return L end end function s.XOr(L,l)local Q=L:IsSome()local H=l:IsSome()if Q==H then return s.None elseif Q then return L else return l end end function s.Filter(L,l)if L:IsNone()or not l(L._v)then return s.None else return L end end function s.Contains(s,l)return s:IsSome()and s._v==l end function s.__tostring(l)if l:IsSome()then return"\079\112\116\105\111\110\060"..(typeof(l._v).."\062")else return"\079\112\116\105\111\110\060\078\111\110\101\062"end end function s.__eq(L,l)if s.Is(l)then if L:IsSome()and l:IsSome()then return L:Unwrap()==l:Unwrap()elseif L:IsNone()and l:IsNone()then return true end end return false end s.None=s._new()return s



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
