#ifndef STD_OPTIONAL_H
#define STD_OPTIONAL_H

#include <functional>
#include <new>
#include <type_traits>
#include <utility>
#include <memory>
#include <assert.h>

namespace std {

struct nullopt_t
{
	struct __tag { explicit __tag() = default; };
	constexpr explicit nullopt_t(__tag) noexcept {}
};

inline constexpr nullopt_t nullopt{nullopt_t::__tag{}};

template <typename T>
class optional
{
public:
	using value_type = T;

private:
	static_assert(!is_same_v<remove_cv_t<remove_reference_t<value_type>>, in_place_t>);
	static_assert(!is_same_v<remove_cv_t<remove_reference_t<value_type>>, nullopt_t>);
	static_assert(!is_reference_v<value_type>);
	static_assert(is_destructible_v<value_type>);
	static_assert(!is_array_v<value_type>);

	template <typename Opt>
	inline constexpr static bool constructible_from_opt = (
		is_constructible_v<T, Opt&> ||
		is_constructible_v<T, const Opt&> ||
		is_constructible_v<T, Opt&&> ||
		is_constructible_v<T, const Opt&&> ||
		is_convertible_v<Opt&, T> ||
		is_convertible_v<const Opt&, T> ||
		is_convertible_v<Opt&&, T> ||
		is_convertible_v<const Opt&&, T>);

	template <typename Opt>
	inline constexpr static bool assignable_from_opt = (
		is_assignable_v<T&, Opt&> ||
		is_assignable_v<T&, const Opt&> ||
		is_assignable_v<T&, Opt&&> ||
		is_assignable_v<T&, const Opt&&>);

public:
	constexpr optional() noexcept
		: null_state()
		, active(false)
	{}

	constexpr optional(nullopt_t) noexcept
		: null_state()
		, active(false)
	{}

	constexpr optional(const optional&)
		requires(is_trivially_copy_constructible_v<value_type>) = default;
	constexpr optional(const optional&)
		requires(!is_copy_constructible_v<value_type>) = delete;
	constexpr optional(const optional& o)
	{
		construct_from(o);
	}

	constexpr optional(optional&&)
		requires(is_trivially_move_constructible_v<value_type>) = default;
	constexpr optional(optional&& o)
		noexcept(is_nothrow_move_constructible_v<value_type>)
		requires(!is_trivially_move_constructible_v<value_type> &&
				is_move_constructible_v<value_type>)
	{
		construct_from(move(o));
	}

	template <typename U>
	explicit(!is_convertible_v<const U&, T>) optional(const optional<U>& v)
		requires(!is_same_v<T, U>&&
				 is_constructible_v<T, const U&> &&
				 !constructible_from_opt<U>)
	{
		construct_from(v);
	}

	template <typename U>
		explicit(!is_convertible_v<const U&, T>) optional(optional<U>&& v)
			requires(!is_same_v<T, U>&&
					 is_constructible_v<T, const U&> &&
					 !constructible_from_opt<U>)
	{
		construct_from(move(v));
	}

	template <typename... Args>
	constexpr explicit optional(in_place_t, Args&&... args)
		requires(is_constructible_v<T, Args...>)
		: val(forward<Args>(args)...)
		, active(true)
	{}
	
	template<typename U = value_type>
	constexpr explicit(!is_convertible_v<U&&, T>)
		optional(U&& v) requires(is_constructible_v<T, U> &&
								 !is_same_v<remove_cvref_t<U>, in_place_t> &&
								 !is_same_v<remove_cvref_t<U>, optional>) :
		val(std::forward<U>(v)), active(true)
	{
	}

	constexpr optional& operator=(nullopt_t) noexcept
	{
		reset();
		return *this;
	}

	constexpr optional& operator=(const optional&)
		requires(!is_copy_constructible_v<T> || !is_copy_assignable_v<T>) = delete;
	constexpr optional& operator=(const optional&)
		requires(is_trivially_destructible_v<value_type>&&
				 is_trivially_copy_constructible_v<value_type>&&
				 is_trivially_copy_assignable_v<value_type>) = default;
	constexpr optional& operator=(const optional& o)	
	{
		assign_from(o);
		return *this;
	}

	constexpr optional& operator=(optional&& o) noexcept
		requires(is_trivially_destructible_v<value_type>&&
				 is_trivially_move_constructible_v<value_type>&&
				 is_trivially_move_assignable_v<value_type>) = default;
	constexpr optional& operator=(optional&& o)
		noexcept(is_nothrow_move_assignable_v<value_type> &&
				 is_nothrow_move_constructible_v<value_type>)
		requires(is_move_constructible_v<T> && is_move_assignable_v<T>)
	{
		assign_from(move(o));
		return *this;
	}

	template <typename U = value_type>
	constexpr optional& operator=(U&& v)
		requires(!is_same_v<remove_cvref_t<U>, optional> &&
				 (!is_same_v<remove_cvref_t<U>, value_type> || !is_scalar_v<value_type>) &&
				 is_constructible_v<value_type, U>&&
				 is_assignable_v<value_type&, U >)
	{
		if(has_value())
			value() = forward<U>(v);
		else
			construct(forward<U>(v));
		return *this;
	}

	template <typename U>
	constexpr optional& operator=(const optional<U>& v)
		requires(!is_same_v<T, U>&&
				 is_constructible_v<T, const U&>&&
				 is_assignable_v<T&, const U&> &&
				 !constructible_from_opt<U> &&
				 !assignable_from_opt<U>)
	{
		assign_from(v);
		return *this;
	}

	template <typename U>
	constexpr optional& operator=(optional<U>&& v)
		requires(!is_same_v<T, U>&&
				 is_constructible_v<T, const U&>&&
				 is_assignable_v<T&, const U&> &&
				 !constructible_from_opt<U> &&
				 !assignable_from_opt<U>)
	{
		assign_from(move(v));
		return *this;
	}

#ifdef __clang__
	//HACK: clang doesn't support conditionally trivial special member functions yet
	//this is a HACK to get it to work suboptimally
	~optional()
	{
		if constexpr(!is_trivially_destructible_v<T>)
		{
			if(active) val.~value_type();
		}
	}
#else
	~optional() requires(!is_trivially_destructible_v<T>)
	{
		if(active) val.~value_type();
	}
	~optional() requires(is_trivially_destructible_v<T>) = default;
#endif
	template <typename... Args, typename = enable_if<is_constructible_v<value_type, Args...>>>
	T& emplace(Args&&... args)
	{
		reset();
		construct(forward<Args>(args)...);
		return value();
	}

	void swap(optional& o)
		noexcept(is_nothrow_move_constructible_v<value_type>&&
				 is_nothrow_swappable_v<value_type>)
	{
		if(has_value() == o.has_value())
		{
			if(has_value())
				std::swap(value(), o.value());
		}
		else
		{
			if(has_value())
			{
				o.construct(move(value()));
				reset();
			}
			else
			{
				construct(move(o.value()));
				o.reset();
			}
		}
	}

	constexpr add_pointer_t<value_type const> operator->() const
	{
		assert(has_value());
		return addressof(value());
	}

	constexpr add_pointer_t<value_type> operator->()
	{
		assert(has_value());
		return addressof(value());
	}

	constexpr const value_type& operator*() const&
	{
		assert(has_value());
		return value();
	}

	constexpr value_type& operator*()&
	{
		assert(has_value());
		return value();
	}

	constexpr value_type&& operator*()&&
	{
		assert(has_value());
		return move(value());
	}

	constexpr const value_type&& operator*() const&&
	{
		assert(has_value());
		return move(value());
	}

	constexpr explicit operator bool() const noexcept { return has_value(); }

	template <typename U>
	constexpr value_type value_or(U&& v) const&
	{
		static_assert(is_copy_constructible_v<value_type>);
		static_assert(is_convertible_v<U, value_type>);

		return has_value() ? value() :
			static_cast<value_type>(forward<U>(v));
	}

	template <typename U>
	constexpr value_type value_or(U&& v)&&
	{
		static_assert(is_move_constructible_v<value_type>);
		static_assert(is_convertible_v<U, value_type>);

		return has_value() ? move(value()) :
			static_cast<value_type>(forward<U>(v));
	}

	constexpr void reset() noexcept
		requires (is_trivially_destructible_v<T>)
	{
		if(active) active = false;
	}

	constexpr void reset() noexcept
		requires (!is_trivially_destructible_v<T>)
	{
		if(active)
		{
			val.~value_type();
			active = false;
		}
	}

	constexpr bool has_value() const noexcept
	{
		return active;
	}

	constexpr value_type& value()&
	{
		return val;
	}

	constexpr const value_type& value() const&
	{
		return val;
	}

	constexpr value_type&& value()&&
	{
		return move(val);
	}

	constexpr const value_type&& value() const&&
	{
		return move(val);
	}

private:
	static_assert(is_object_v<value_type>);
	union
	{
		char null_state;
		value_type val;
	};
	bool active;

	template <typename... Args>
	void construct(Args&&... args)
	{
		assert(!has_value());
		::new((void*)addressof(val)) value_type(forward<Args>(args)...);
		active = true;
	}

	template <typename Other>
	void construct_from(Other&& o)
	{
		if(o.has_value())
			construct(forward<Other>(o).value());
	}

	template <typename Other>
	void assign_from(Other&& o)
	{
		if(has_value() == o.has_value())
		{
			if(has_value())
				val = forward<Other>(o).value();
		}
		else
		{
			if(has_value())
				reset();
			else
				construct(forward<Other>(o).value());
		}
	}
};

template<typename T>
optional(T) -> optional<T>;

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() == declval<const U&>()), bool>, bool>
operator==(const optional<T>& x, const optional<U>& y)
{
	if(static_cast<bool>(x) != static_cast<bool>(y))
		return false;
	if(!static_cast<bool>(x))
		return true;
	return *x == *y;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() != declval<const U&>()), bool>, bool>
operator!=(const optional<T>& x, const optional<U>& y)
{
	if(static_cast<bool>(x) != static_cast<bool>(y))
		return true;
	if(!static_cast<bool>(x))
		return false;
	return *x != *y;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() < declval<const U&>()), bool>, bool>
operator<(const optional<T>& x, const optional<U>& y)
{
	if(!static_cast<bool>(y))
		return false;
	if(!static_cast<bool>(x))
		return true;
	return *x < *y;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>()> declval<const U&>()), bool>, bool>
operator>(const optional<T>& x, const optional<U>& y)
{
	if(!static_cast<bool>(x))
		return false;
	if(!static_cast<bool>(y))
		return true;
	return *x > *y;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() <= declval<const U&>()), bool>, bool>
operator<=(const optional<T>& x, const optional<U>& y)
{
	if(!static_cast<bool>(x))
		return true;
	if(!static_cast<bool>(y))
		return false;
	return *x <= *y;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() >= declval<const U&>()), bool>, bool>
operator>=(const optional<T>& x, const optional<U>& y)
{
	if(!static_cast<bool>(y))
		return true;
	if(!static_cast<bool>(x))
		return false;
	return *x >= *y;
}

template <typename T>
constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept
{
	return !static_cast<bool>(x);
}

template <typename T>
constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept
{
	return !static_cast<bool>(x);
}

template <typename T>
constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept
{
	return static_cast<bool>(x);
}

template <typename T>
constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept
{
	return static_cast<bool>(x);
}

template <typename T>
constexpr bool operator<(const optional<T>&, nullopt_t) noexcept
{
	return false;
}

template <typename T>
constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept
{
	return static_cast<bool>(x);
}

template <typename T>
constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept
{
	return !static_cast<bool>(x);
}

template <typename T>
constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept
{
	return true;
}

template <typename T>
constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept
{
	return static_cast<bool>(x);
}

template <typename T>
constexpr bool operator>(nullopt_t, const optional<T>&) noexcept
{
	return false;
}

template <typename T>
constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept
{
	return true;
}

template <typename T>
constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept
{
	return !static_cast<bool>(x);
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() == declval<const U&>()), bool>, bool>
operator==(const optional<T>& x, const U& v)
{
	return static_cast<bool>(x) ? *x == v : false;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() == declval<const U&>()), bool>, bool>
operator==(const T& v, const optional<U>& x)
{
	return static_cast<bool>(x) ? v == *x : false;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() != declval<const U&>()), bool>, bool>
operator!=(const optional<T>& x, const U& v)
{
	return static_cast<bool>(x) ? *x != v : true;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() != declval<const U&>()), bool>, bool>
operator!=(const T& v, const optional<U>& x)
{
	return static_cast<bool>(x) ? v != *x : true;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() < declval<const U&>()), bool>, bool>
operator<(const optional<T>& x, const U& v)
{
	return static_cast<bool>(x) ? *x < v : true;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() < declval<const U&>()), bool>, bool>
operator<(const T& v, const optional<U>& x)
{
	return static_cast<bool>(x) ? v < *x : false;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() <= declval<const U&>()), bool>, bool>
operator<=(const optional<T>& x, const U& v)
{
	return static_cast<bool>(x) ? *x <= v : true;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() <= declval<const U&>()), bool>, bool>
operator<=(const T& v, const optional<U>& x)
{
	return static_cast<bool>(x) ? v <= *x : false;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() > declval<const U&>()), bool>, bool>
	operator>(const optional<T>& x, const U& v)
{
	return static_cast<bool>(x) ? *x > v : false;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() > declval<const U&>()), bool>, bool>
operator>(const T& v, const optional<U>& x)
{
	return static_cast<bool>(x) ? v > *x : true;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() >= declval<const U&>()), bool>, bool>
operator>=(const optional<T>& x, const U& v)
{
	return static_cast<bool>(x) ? *x >= v : false;
}

template <typename T, typename U>
constexpr enable_if_t<is_convertible_v<
	decltype(declval<const T&>() >= declval<const U&>()), bool>, bool>
operator>=(const T& v, const optional<U>& x)
{
	return static_cast<bool>(x) ? v >= *x : true;
}

template <typename T>
inline enable_if_t<is_move_constructible_v<T> && is_swappable_v<T>, void>
swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)))
{
	x.swap(y);
}

template <typename T>
constexpr optional<decay_t<T>> make_optional(T&& v)
{
	return optional<decay_t<T>>(forward<T>(v));
}

template <typename T, typename... Args>
constexpr optional<T> make_optional(Args&&... args)
{
	return optional<T>(in_place, forward<Args>(args)...);
}
}

#endif