#ifndef MAPBOX_UTIL_VARIANT_HPP
#define MAPBOX_UTIL_VARIANT_HPP

#include <cassert>
#include <cstddef>   // size_t
#include <new>       // operator new
#include <stdexcept> // runtime_error
#include <string>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <utility>

#include "recursive_wrapper.hpp"
#include "variant_visitor.hpp"

// clang-format off
// [[deprecated]] is only available in C++14, use this for the time being
#if __cplusplus <= 201103L
# ifdef __GNUC__
#  define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER)
#  define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated)
# else
#  define MAPBOX_VARIANT_DEPRECATED
# endif
#else
#  define MAPBOX_VARIANT_DEPRECATED [[deprecated]]
#endif


#ifdef _MSC_VER
// https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx
# ifdef NDEBUG
#  define VARIANT_INLINE __forceinline
# else
#  define VARIANT_INLINE //__declspec(noinline)
# endif
#else
# ifdef NDEBUG
#  define VARIANT_INLINE //inline __attribute__((always_inline))
# else
#  define VARIANT_INLINE __attribute__((noinline))
# endif
#endif
// clang-format on

// Exceptions
#if defined( __EXCEPTIONS) || defined( _MSC_VER)
#define HAS_EXCEPTIONS
#endif

#define VARIANT_MAJOR_VERSION 1
#define VARIANT_MINOR_VERSION 1
#define VARIANT_PATCH_VERSION 0

#define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION)

//namespace mapbox {
//	namespace util {
namespace std {

		// XXX This should derive from std::logic_error instead of std::runtime_error.
		//     See https://github.com/mapbox/variant/issues/48 for details.
		class bad_variant_access : public std::runtime_error
		{

		public:
			explicit bad_variant_access(const std::string& what_arg)
				: runtime_error(what_arg) {}

			explicit bad_variant_access(const char* what_arg)
				: runtime_error(what_arg) {}

		}; // class bad_variant_access

		namespace detail {

			static constexpr std::size_t invalid_value = std::size_t(-1);

			template <typename T, typename... Types>
			struct direct_type;

			template <typename T, typename First, typename... Types>
			struct direct_type<T, First, Types...>
			{
				static constexpr std::size_t index = std::is_same<T, First>::value
					? sizeof...(Types)
					: direct_type<T, Types...>::index;
			};

			template <typename T>
			struct direct_type<T>
			{
				static constexpr std::size_t index = invalid_value;
			};

#if __cpp_lib_logical_traits >= 201510L

			using std::disjunction;

#else

			template <typename...>
			struct disjunction : std::false_type {};

			template <typename B1>
			struct disjunction<B1> : B1 {};

			template <typename B1, typename B2>
			struct disjunction<B1, B2> : std::conditional<B1::value, B1, B2>::type {};

			template <typename B1, typename... Bs>
			struct disjunction<B1, Bs...> : std::conditional<B1::value, B1, disjunction<Bs...>>::type {};

#endif

			template <typename T, typename... Types>
			struct convertible_type;

			template <typename T, typename First, typename... Types>
			struct convertible_type<T, First, Types...>
			{
				static constexpr std::size_t index = std::is_convertible<T, First>::value
					? disjunction<std::is_convertible<T, Types>...>::value ? invalid_value : sizeof...(Types)
					: convertible_type<T, Types...>::index;
			};

			template <typename T>
			struct convertible_type<T>
			{
				static constexpr std::size_t index = invalid_value;
			};

			template <typename T, typename... Types>
			struct value_traits
			{
				using value_type = typename std::remove_const<typename std::remove_reference<T>::type>::type;
				static constexpr std::size_t direct_index = direct_type<value_type, Types...>::index;
				static constexpr bool is_direct = direct_index != invalid_value;
				static constexpr std::size_t index = is_direct ? direct_index : convertible_type<value_type, Types...>::index;
				static constexpr bool is_valid = index != invalid_value;
				static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0;
				using target_type = typename std::tuple_element<tindex, std::tuple<void, Types...>>::type;
			};

			template <typename T, typename R = void>
			struct enable_if_type
			{
				using type = R;
			};

			template <typename F, typename V, typename Enable = void>
			struct result_of_unary_visit
			{
				using type = typename std::result_of<F(V&)>::type;
			};

			template <typename F, typename V>
			struct result_of_unary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
			{
				using type = typename F::result_type;
			};

			template <typename F, typename V, typename Enable = void>
			struct result_of_binary_visit
			{
				using type = typename std::result_of<F(V&, V&)>::type;
			};

			template <typename F, typename V>
			struct result_of_binary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
			{
				using type = typename F::result_type;
			};

			template <std::size_t arg1, std::size_t... others>
			struct static_max;

			template <std::size_t arg>
			struct static_max<arg>
			{
				static const std::size_t value = arg;
			};

			template <std::size_t arg1, std::size_t arg2, std::size_t... others>
			struct static_max<arg1, arg2, others...>
			{
				static const std::size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
			};

			template <typename... Types>
			struct variant_helper;

			template <typename T, typename... Types>
			struct variant_helper<T, Types...>
			{
				VARIANT_INLINE static void destroy(const std::size_t type_index, void* data)
				{
					if (type_index == sizeof...(Types))
					{
						reinterpret_cast<T*>(data)->~T();
					}
					else
					{
						variant_helper<Types...>::destroy(type_index, data);
					}
				}

				VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value)
				{
					if (old_type_index == sizeof...(Types))
					{
						new (new_value) T(std::move(*reinterpret_cast<T*>(old_value)));
					}
					else
					{
						variant_helper<Types...>::move(old_type_index, old_value, new_value);
					}
				}

				VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value)
				{
					if (old_type_index == sizeof...(Types))
					{
						new (new_value) T(*reinterpret_cast<const T*>(old_value));
					}
					else
					{
						variant_helper<Types...>::copy(old_type_index, old_value, new_value);
					}
				}
			};

			template <>
			struct variant_helper<>
			{
				VARIANT_INLINE static void destroy(const std::size_t, void*) {}
				VARIANT_INLINE static void move(const std::size_t, void*, void*) {}
				VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {}
			};

			template <typename T>
			struct unwrapper
			{
				static T const& apply_const(T const& obj) { return obj; }
				static T& apply(T& obj) { return obj; }
			};

			template <typename T>
			struct unwrapper<recursive_wrapper<T>>
			{
				static auto apply_const(recursive_wrapper<T> const& obj)
					-> typename recursive_wrapper<T>::type const&
				{
					return obj.get();
				}
				static auto apply(recursive_wrapper<T>& obj)
					-> typename recursive_wrapper<T>::type&
				{
					return obj.get();
				}
			};

			template <typename T>
			struct unwrapper<std::reference_wrapper<T>>
			{
				static auto apply_const(std::reference_wrapper<T> const& obj)
					-> typename std::reference_wrapper<T>::type const&
				{
					return obj.get();
				}
				static auto apply(std::reference_wrapper<T>& obj)
					-> typename std::reference_wrapper<T>::type&
				{
					return obj.get();
				}
			};

			template <typename F, typename V, typename R, typename... Types>
			struct dispatcher;

			template <typename F, typename V, typename R, typename T, typename... Types>
			struct dispatcher<F, V, R, T, Types...>
			{
				VARIANT_INLINE static R apply_const(V const& v, F&& f)
				{
					if (v.template is<T>())
					{
						return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
					}
					else
					{
						return dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
					}
				}

				VARIANT_INLINE static R apply(V& v, F&& f)
				{
					if (v.template is<T>())
					{
						return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
					}
					else
					{
						return dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
					}
				}
			};

			template <typename F, typename V, typename R, typename T>
			struct dispatcher<F, V, R, T>
			{
				VARIANT_INLINE static R apply_const(V const& v, F&& f)
				{
					return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
				}

				VARIANT_INLINE static R apply(V& v, F&& f)
				{
					return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
				}
			};

			template <typename F, typename V, typename R, typename T, typename... Types>
			struct binary_dispatcher_rhs;

			template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
			struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
			{
				VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
				{
					if (rhs.template is<T1>()) // call binary functor
					{
						return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
							unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
					}
					else
					{
						return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
					}
				}

				VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
				{
					if (rhs.template is<T1>()) // call binary functor
					{
						return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
							unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
					}
					else
					{
						return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
					}
				}
			};

			template <typename F, typename V, typename R, typename T0, typename T1>
			struct binary_dispatcher_rhs<F, V, R, T0, T1>
			{
				VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
				{
					return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
						unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
				}

				VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
				{
					return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
						unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
				}
			};

			template <typename F, typename V, typename R, typename T, typename... Types>
			struct binary_dispatcher_lhs;

			template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
			struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
			{
				VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
				{
					if (lhs.template is<T1>()) // call binary functor
					{
						return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
							unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
					}
					else
					{
						return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
					}
				}

				VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
				{
					if (lhs.template is<T1>()) // call binary functor
					{
						return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
							unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
					}
					else
					{
						return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
					}
				}
			};

			template <typename F, typename V, typename R, typename T0, typename T1>
			struct binary_dispatcher_lhs<F, V, R, T0, T1>
			{
				VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
				{
					return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
						unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
				}

				VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
				{
					return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
						unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
				}
			};

			template <typename F, typename V, typename R, typename... Types>
			struct binary_dispatcher;

			template <typename F, typename V, typename R, typename T, typename... Types>
			struct binary_dispatcher<F, V, R, T, Types...>
			{
				VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
				{
					if (v0.template is<T>())
					{
						if (v1.template is<T>())
						{
							return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
								unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
						}
						else
						{
							return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
						}
					}
					else if (v1.template is<T>())
					{
						return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
					}
					return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
				}

				VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
				{
					if (v0.template is<T>())
					{
						if (v1.template is<T>())
						{
							return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
								unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
						}
						else
						{
							return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
						}
					}
					else if (v1.template is<T>())
					{
						return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
					}
					return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
				}
			};

			template <typename F, typename V, typename R, typename T>
			struct binary_dispatcher<F, V, R, T>
			{
				VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
				{
					return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
						unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
				}

				VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
				{
					return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
						unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
				}
			};

			// comparator functors
			struct equal_comp
			{
				template <typename T>
				bool operator()(T const& lhs, T const& rhs) const
				{
					return lhs == rhs;
				}
			};

			struct less_comp
			{
				template <typename T>
				bool operator()(T const& lhs, T const& rhs) const
				{
					return lhs < rhs;
				}
			};

			template <typename Variant, typename Comp>
			class comparer
			{
			public:
				explicit comparer(Variant const& lhs) noexcept
					: lhs_(lhs) {}
				comparer& operator=(comparer const&) = delete;
				// visitor
				template <typename T>
				bool operator()(T const& rhs_content) const
				{
					T const& lhs_content = lhs_.template get_unchecked<T>();
					return Comp()(lhs_content, rhs_content);
				}

			private:
				Variant const& lhs_;
			};

		} // namespace detail

		struct no_init
		{
		};

		template <typename... Types>
		class variant
		{
			static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty");
			static_assert(!detail::disjunction<std::is_reference<Types>...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?");

		private:
			static const std::size_t data_size = detail::static_max<sizeof(Types)...>::value;
			static const std::size_t data_align = detail::static_max<alignof(Types)...>::value;

			using first_type = typename std::tuple_element<0, std::tuple<Types...>>::type;
			using data_type = typename std::aligned_storage<data_size, data_align>::type;
			using helper_type = detail::variant_helper<Types...>;

			std::size_t type_index;
			data_type data;

		public:
			VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible<first_type>::value)
				: type_index(sizeof...(Types)-1)
			{
				static_assert(std::is_default_constructible<first_type>::value, "First type in variant must be default constructible to allow default construction of variant");
				new (&data) first_type();
			}

			VARIANT_INLINE variant(no_init) noexcept
				: type_index(detail::invalid_value) {}

			// http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
			template <typename T, typename Traits = detail::value_traits<T, Types...>,
				typename Enable = typename std::enable_if<Traits::is_valid>::type>
				VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value)
				: type_index(Traits::index)
			{
				new (&data) typename Traits::target_type(std::forward<T>(val));
			}

			VARIANT_INLINE variant(variant<Types...> const& old)
				: type_index(old.type_index)
			{
				helper_type::copy(old.type_index, &old.data, &data);
			}

			VARIANT_INLINE variant(variant<Types...>&& old) noexcept(std::is_nothrow_move_constructible<std::tuple<Types...>>::value)
				: type_index(old.type_index)
			{
				helper_type::move(old.type_index, &old.data, &data);
			}

		private:
			VARIANT_INLINE void copy_assign(variant<Types...> const& rhs)
			{
				helper_type::destroy(type_index, &data);
				type_index = detail::invalid_value;
				helper_type::copy(rhs.type_index, &rhs.data, &data);
				type_index = rhs.type_index;
			}

			VARIANT_INLINE void move_assign(variant<Types...>&& rhs)
			{
				helper_type::destroy(type_index, &data);
				type_index = detail::invalid_value;
				helper_type::move(rhs.type_index, &rhs.data, &data);
				type_index = rhs.type_index;
			}

		public:
			VARIANT_INLINE variant<Types...>& operator=(variant<Types...>&& other)
			{
				move_assign(std::move(other));
				return *this;
			}

			VARIANT_INLINE variant<Types...>& operator=(variant<Types...> const& other)
			{
				copy_assign(other);
				return *this;
			}

			// conversions
			// move-assign
			template <typename T>
			VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
			{
				variant<Types...> temp(std::forward<T>(rhs));
				move_assign(std::move(temp));
				return *this;
			}

			// copy-assign
			template <typename T>
			VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
			{
				variant<Types...> temp(rhs);
				copy_assign(temp);
				return *this;
			}

			template <typename T, typename std::enable_if<
				(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE bool is() const
			{
				return type_index == detail::direct_type<T, Types...>::index;
			}

			template <typename T, typename std::enable_if<
				(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE bool is() const
			{
				return type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index;
			}

			VARIANT_INLINE bool valid() const
			{
				return type_index != detail::invalid_value;
			}

			template <typename T, typename... Args>
			VARIANT_INLINE void set(Args&&... args)
			{
				helper_type::destroy(type_index, &data);
				type_index = detail::invalid_value;
				new (&data) T(std::forward<Args>(args)...);
				type_index = detail::direct_type<T, Types...>::index;
			}

			// get_unchecked<T>()
			template <typename T, typename std::enable_if<
				(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T& get_unchecked()
			{
				return *reinterpret_cast<T*>(&data);
			}

#ifdef HAS_EXCEPTIONS
			// get<T>()
			template <typename T, typename std::enable_if<
				(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T& get()
			{
				if (type_index == detail::direct_type<T, Types...>::index)
				{
					return *reinterpret_cast<T*>(&data);
				}
				else
				{
					throw bad_variant_access("in get<T>()");
				}
			}
#endif

			template <typename T, typename std::enable_if<
				(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T const& get_unchecked() const
			{
				return *reinterpret_cast<T const*>(&data);
			}

#ifdef HAS_EXCEPTIONS
			template <typename T, typename std::enable_if<
				(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T const& get() const
			{
				if (type_index == detail::direct_type<T, Types...>::index)
				{
					return *reinterpret_cast<T const*>(&data);
				}
				else
				{
					throw bad_variant_access("in get<T>()");
				}
			}
#endif

			// get_unchecked<T>() - T stored as recursive_wrapper<T>
			template <typename T, typename std::enable_if<
				(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T& get_unchecked()
			{
				return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
			}

#ifdef HAS_EXCEPTIONS
			// get<T>() - T stored as recursive_wrapper<T>
			template <typename T, typename std::enable_if<
				(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T& get()
			{
				if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
				{
					return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
				}
				else
				{
					throw bad_variant_access("in get<T>()");
				}
			}
#endif

			template <typename T, typename std::enable_if<
				(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T const& get_unchecked() const
			{
				return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
			}

#ifdef HAS_EXCEPTIONS
			template <typename T, typename std::enable_if<
				(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T const& get() const
			{
				if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
				{
					return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
				}
				else
				{
					throw bad_variant_access("in get<T>()");
				}
			}
#endif

			// get_unchecked<T>() - T stored as std::reference_wrapper<T>
			template <typename T, typename std::enable_if<
				(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T& get_unchecked()
			{
				return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
			}

#ifdef HAS_EXCEPTIONS
			// get<T>() - T stored as std::reference_wrapper<T>
			template <typename T, typename std::enable_if<
				(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T& get()
			{
				if (type_index == detail::direct_type<std::reference_wrapper<T>, Types...>::index)
				{
					return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
				}
				else
				{
					throw bad_variant_access("in get<T>()");
				}
			}
#endif

			template <typename T, typename std::enable_if<
				(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T const& get_unchecked() const
			{
				return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
			}

#ifdef HAS_EXCEPTIONS
			template <typename T, typename std::enable_if<
				(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE T const& get() const
			{
				if (type_index == detail::direct_type<std::reference_wrapper<T const>, Types...>::index)
				{
					return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
				}
				else
				{
					throw bad_variant_access("in get<T>()");
				}
			}
#endif

			// This function is deprecated because it returns an internal index field.
			// Use which() instead.
			MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE std::size_t get_type_index() const
			{
				return type_index;
			}

			VARIANT_INLINE int which() const noexcept
			{
				return static_cast<int>(sizeof...(Types)-type_index - 1);
			}

			template <typename T, typename std::enable_if<
				(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
				VARIANT_INLINE static constexpr int which() noexcept
			{
				return static_cast<int>(sizeof...(Types)-detail::direct_type<T, Types...>::index - 1);
			}

			// visitor
			// unary
			template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
			auto VARIANT_INLINE static visit(V const& v, F&& f)
				-> decltype(detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f)))
			{
				return detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
			}
			// non-const
			template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
			auto VARIANT_INLINE static visit(V& v, F&& f)
				-> decltype(detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f)))
			{
				return detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
			}

			// binary
			// const
			template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
			auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F&& f)
				-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f)))
			{
				return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
			}
			// non-const
			template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
			auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F&& f)
				-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f)))
			{
				return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
			}

			// match
			// unary
			template <typename... Fs>
			auto VARIANT_INLINE match(Fs&&... fs) const
			-> decltype(variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...)))
			{
				return variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...));
			}
			// non-const
			template <typename... Fs>
			auto VARIANT_INLINE match(Fs&&... fs)
			-> decltype(variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...)))
			{
				return variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...));
			}

			~variant() noexcept // no-throw destructor
			{
				helper_type::destroy(type_index, &data);
			}

			// comparison operators
			// equality
			VARIANT_INLINE bool operator==(variant const& rhs) const
			{
				assert(valid() && rhs.valid());
				if (this->which() != rhs.which())
				{
					return false;
				}
				detail::comparer<variant, detail::equal_comp> visitor(*this);
				return visit(rhs, visitor);
			}

			VARIANT_INLINE bool operator!=(variant const& rhs) const
			{
				return !(*this == rhs);
			}

			// less than
			VARIANT_INLINE bool operator<(variant const& rhs) const
			{
				assert(valid() && rhs.valid());
				if (this->which() != rhs.which())
				{
					return this->which() < rhs.which();
				}
				detail::comparer<variant, detail::less_comp> visitor(*this);
				return visit(rhs, visitor);
			}
			VARIANT_INLINE bool operator>(variant const& rhs) const
			{
				return rhs < *this;
			}
			VARIANT_INLINE bool operator<=(variant const& rhs) const
			{
				return !(*this > rhs);
			}
			VARIANT_INLINE bool operator>=(variant const& rhs) const
			{
				return !(*this < rhs);
			}
		};

		// unary visitor interface
		// const
		template <typename F, typename V>
		auto VARIANT_INLINE apply_visitor(F&& f, V const& v) -> decltype(V::visit(v, std::forward<F>(f)))
		{
			return V::visit(v, std::forward<F>(f));
		}

		// non-const
		template <typename F, typename V>
		auto VARIANT_INLINE apply_visitor(F&& f, V& v) -> decltype(V::visit(v, std::forward<F>(f)))
		{
			return V::visit(v, std::forward<F>(f));
		}

		// binary visitor interface
		// const
		template <typename F, typename V>
		auto VARIANT_INLINE apply_visitor(F&& f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
		{
			return V::binary_visit(v0, v1, std::forward<F>(f));
		}

		// non-const
		template <typename F, typename V>
		auto VARIANT_INLINE apply_visitor(F&& f, V& v0, V& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
		{
			return V::binary_visit(v0, v1, std::forward<F>(f));
		}

		// getter interface

#ifdef HAS_EXCEPTIONS
		template <typename ResultType, typename T>
		auto get(T& var)->decltype(var.template get<ResultType>())
		{
			return var.template get<ResultType>();
		}
#endif

		template <typename ResultType, typename T>
		ResultType& get_unchecked(T& var)
		{
			return var.template get_unchecked<ResultType>();
		}

#ifdef HAS_EXCEPTIONS
		template <typename ResultType, typename T>
		auto get(T const& var)->decltype(var.template get<ResultType>())
		{
			return var.template get<ResultType>();
		}
#endif

		template <typename ResultType, typename T>
		ResultType const& get_unchecked(T const& var)
		{
			return var.template get_unchecked<ResultType>();
		}
	}
//	} // namespace util
//} // namespace mapbox

#endif // MAPBOX_UTIL_VARIANT_HPP