// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#if __cplusplus >= 201703L && __has_include_next(<variant>)
#include_next <variant>
#else
// MPark.Variant
//
// Copyright Michael Park, 2015-2017
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

/*
   variant synopsis

namespace std {

  // 20.7.2, class template variant
  template <class... Types>
  class variant {
  public:

    // 20.7.2.1, constructors
    constexpr variant() noexcept(see below);
    variant(const variant&);
    variant(variant&&) noexcept(see below);

    template <class T> constexpr variant(T&&) noexcept(see below);

    template <class T, class... Args>
    constexpr explicit variant(in_place_type_t<T>, Args&&...);

    template <class T, class U, class... Args>
    constexpr explicit variant(
        in_place_type_t<T>, initializer_list<U>, Args&&...);

    template <size_t I, class... Args>
    constexpr explicit variant(in_place_index_t<I>, Args&&...);

    template <size_t I, class U, class... Args>
    constexpr explicit variant(
        in_place_index_t<I>, initializer_list<U>, Args&&...);

    // 20.7.2.2, destructor
    ~variant();

    // 20.7.2.3, assignment
    variant& operator=(const variant&);
    variant& operator=(variant&&) noexcept(see below);

    template <class T> variant& operator=(T&&) noexcept(see below);

    // 20.7.2.4, modifiers
    template <class T, class... Args>
    T& emplace(Args&&...);

    template <class T, class U, class... Args>
    T& emplace(initializer_list<U>, Args&&...);

    template <size_t I, class... Args>
    variant_alternative<I, variant>& emplace(Args&&...);

    template <size_t I, class U, class...  Args>
    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);

    // 20.7.2.5, value status
    constexpr bool valueless_by_exception() const noexcept;
    constexpr size_t index() const noexcept;

    // 20.7.2.6, swap
    void swap(variant&) noexcept(see below);
  };

  // 20.7.3, variant helper classes
  template <class T> struct variant_size; // undefined

  template <class T>
  constexpr size_t variant_size_v = variant_size<T>::value;

  template <class T> struct variant_size<const T>;
  template <class T> struct variant_size<volatile T>;
  template <class T> struct variant_size<const volatile T>;

  template <class... Types>
  struct variant_size<variant<Types...>>;

  template <size_t I, class T> struct variant_alternative; // undefined

  template <size_t I, class T>
  using variant_alternative_t = typename variant_alternative<I, T>::type;

  template <size_t I, class T> struct variant_alternative<I, const T>;
  template <size_t I, class T> struct variant_alternative<I, volatile T>;
  template <size_t I, class T> struct variant_alternative<I, const volatile T>;

  template <size_t I, class... Types>
  struct variant_alternative<I, variant<Types...>>;

  constexpr size_t variant_npos = -1;

  // 20.7.4, value access
  template <class T, class... Types>
  constexpr bool holds_alternative(const variant<Types...>&) noexcept;

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>>&
  get(variant<Types...>&);

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>>&&
  get(variant<Types...>&&);

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>> const&
  get(const variant<Types...>&);

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>> const&&
  get(const variant<Types...>&&);

  template <class T, class...  Types>
  constexpr T& get(variant<Types...>&);

  template <class T, class... Types>
  constexpr T&& get(variant<Types...>&&);

  template <class T, class... Types>
  constexpr const T& get(const variant<Types...>&);

  template <class T, class... Types>
  constexpr const T&& get(const variant<Types...>&&);

  template <size_t I, class... Types>
  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
  get_if(variant<Types...>*) noexcept;

  template <size_t I, class... Types>
  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
  get_if(const variant<Types...>*) noexcept;

  template <class T, class... Types>
  constexpr add_pointer_t<T>
  get_if(variant<Types...>*) noexcept;

  template <class T, class... Types>
  constexpr add_pointer_t<const T>
  get_if(const variant<Types...>*) noexcept;

  // 20.7.5, relational operators
  template <class... Types>
  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);

  // 20.7.6, visitation
  template <class Visitor, class... Variants>
  constexpr see below visit(Visitor&&, Variants&&...);

  // 20.7.7, class monostate
  struct monostate;

  // 20.7.8, monostate relational operators
  constexpr bool operator<(monostate, monostate) noexcept;
  constexpr bool operator>(monostate, monostate) noexcept;
  constexpr bool operator<=(monostate, monostate) noexcept;
  constexpr bool operator>=(monostate, monostate) noexcept;
  constexpr bool operator==(monostate, monostate) noexcept;
  constexpr bool operator!=(monostate, monostate) noexcept;

  // 20.7.9, specialized algorithms
  template <class... Types>
  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);

  // 20.7.10, class bad_variant_access
  class bad_variant_access;

  // 20.7.11, hash support
  template <class T> struct hash;
  template <class... Types> struct hash<variant<Types...>>;
  template <> struct hash<monostate>;

} // namespace std

*/

#include <cstddef>
#include <exception>
#include <functional>
#include <initializer_list>
#include <new>
#include <type_traits>
#include <utility>

// MPark.Variant
//
// Copyright Michael Park, 2015-2017
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#ifndef MPARK_CONFIG_HPP
#define MPARK_CONFIG_HPP

// MSVC 2015 Update 3.
#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
#error "MPark.Variant requires C++11 support."
#endif

#ifndef __has_builtin
#define __has_builtin(x) 0
#endif

#ifndef __has_include
#define __has_include(x) 0
#endif

#ifndef __has_feature
#define __has_feature(x) 0
#endif

#if __has_builtin(__builtin_addressof) || (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
#define MPARK_BUILTIN_ADDRESSOF
#endif

#if __has_builtin(__builtin_unreachable)
#define MPARK_BUILTIN_UNREACHABLE
#endif

#if __has_builtin(__type_pack_element)
#define MPARK_TYPE_PACK_ELEMENT
#endif

#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
#define MPARK_CPP14_CONSTEXPR
#endif

#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) ||                                  \
    (defined(_MSC_VER) && defined(_CPPUNWIND))
#define MPARK_EXCEPTIONS
#endif

#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
#define MPARK_GENERIC_LAMBDAS
#endif

#if defined(__cpp_lib_integer_sequence)
#define MPARK_INTEGER_SEQUENCE
#endif

#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
#define MPARK_RETURN_TYPE_DEDUCTION
#endif

#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
#define MPARK_TRANSPARENT_OPERATORS
#endif

#if defined(__cpp_variable_templates) || defined(_MSC_VER)
#define MPARK_VARIABLE_TEMPLATES
#endif

#if !defined(__GLIBCXX__) || __has_include(<codecvt>)  // >= libstdc++-5
#define MPARK_TRIVIALITY_TYPE_TRAITS
#endif

#endif  // MPARK_CONFIG_HPP

#include "in_place.h"

// MPark.Variant
//
// Copyright Michael Park, 2015-2017
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#ifndef MPARK_LIB_HPP
#define MPARK_LIB_HPP

#include <functional>
#include <memory>
#include <type_traits>
#include <utility>

#define RETURN(...)                                                                                \
  noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { return __VA_ARGS__; }

namespace mpark
{
namespace lib
{
template <typename T>
struct identity
{
  using type = T;
};

inline namespace cpp14
{
template <typename T, std::size_t N>
struct array
{
  constexpr const T& operator[](std::size_t index) const { return data[index]; }

  T data[N == 0 ? 1 : N];
};

template <typename T>
using add_pointer_t = typename std::add_pointer<T>::type;

template <typename... Ts>
using common_type_t = typename std::common_type<Ts...>::type;

template <typename T>
using decay_t = typename std::decay<T>::type;

template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;

template <typename T>
using remove_const_t = typename std::remove_const<T>::type;

template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;

template <typename T>
inline constexpr T&& forward(remove_reference_t<T>& t) noexcept
{
  return static_cast<T&&>(t);
}

template <typename T>
inline constexpr T&& forward(remove_reference_t<T>&& t) noexcept
{
  static_assert(!std::is_lvalue_reference<T>::value, "can not forward an rvalue as an lvalue");
  return static_cast<T&&>(t);
}

template <typename T>
inline constexpr remove_reference_t<T>&& move(T&& t) noexcept
{
  return static_cast<remove_reference_t<T>&&>(t);
}

#ifdef MPARK_INTEGER_SEQUENCE
using std::index_sequence;
using std::index_sequence_for;
using std::integer_sequence;
using std::make_index_sequence;
#else
template <typename T, T... Is>
struct integer_sequence
{
  using value_type = T;
  static constexpr std::size_t size() noexcept { return sizeof...(Is); }
};

template <std::size_t... Is>
using index_sequence = integer_sequence<std::size_t, Is...>;

template <typename Lhs, typename Rhs>
struct make_index_sequence_concat;

template <std::size_t... Lhs, std::size_t... Rhs>
struct make_index_sequence_concat<index_sequence<Lhs...>, index_sequence<Rhs...>>
    : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>>
{
};

template <std::size_t N>
struct make_index_sequence_impl;

template <std::size_t N>
using make_index_sequence = typename make_index_sequence_impl<N>::type;

template <std::size_t N>
struct make_index_sequence_impl
    : make_index_sequence_concat<make_index_sequence<N / 2>, make_index_sequence<N - (N / 2)>>
{
};

template <>
struct make_index_sequence_impl<0> : identity<index_sequence<>>
{
};

template <>
struct make_index_sequence_impl<1> : identity<index_sequence<0>>
{
};

template <typename... Ts>
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
#endif

// <functional>
#ifdef MPARK_TRANSPARENT_OPERATORS
using equal_to = std::equal_to<>;
#else
struct equal_to
{
  template <typename Lhs, typename Rhs>
  inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const
      RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
};
#endif

#ifdef MPARK_TRANSPARENT_OPERATORS
using not_equal_to = std::not_equal_to<>;
#else
struct not_equal_to
{
  template <typename Lhs, typename Rhs>
  inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const
      RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
};
#endif

#ifdef MPARK_TRANSPARENT_OPERATORS
using less = std::less<>;
#else
struct less
{
  template <typename Lhs, typename Rhs>
  inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const
      RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
};
#endif

#ifdef MPARK_TRANSPARENT_OPERATORS
using greater = std::greater<>;
#else
struct greater
{
  template <typename Lhs, typename Rhs>
  inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const
      RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
};
#endif

#ifdef MPARK_TRANSPARENT_OPERATORS
using less_equal = std::less_equal<>;
#else
struct less_equal
{
  template <typename Lhs, typename Rhs>
  inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const
      RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
};
#endif

#ifdef MPARK_TRANSPARENT_OPERATORS
using greater_equal = std::greater_equal<>;
#else
struct greater_equal
{
  template <typename Lhs, typename Rhs>
  inline constexpr auto operator()(Lhs&& lhs, Rhs&& rhs) const
      RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
};
#endif
}  // namespace cpp14

inline namespace cpp17
{
// <type_traits>
template <bool B>
using bool_constant = std::integral_constant<bool, B>;

template <typename...>
struct voider : identity<void>
{
};

template <typename... Ts>
using void_t = typename voider<Ts...>::type;

namespace detail
{
namespace swappable
{
using std::swap;

template <typename T>
struct is_swappable
{
private:
  template <typename U, typename = decltype(swap(std::declval<U&>(), std::declval<U&>()))>
  inline static std::true_type test(int);

  template <typename U>
  inline static std::false_type test(...);

public:
  static constexpr bool value = decltype(test<T>(0))::value;
};

template <typename T, bool = is_swappable<T>::value>
struct is_nothrow_swappable
{
  static constexpr bool value = noexcept(swap(std::declval<T&>(), std::declval<T&>()));
};

template <typename T>
struct is_nothrow_swappable<T, false> : std::false_type
{
};

}  // namespace swappable
}  // namespace detail

using detail::swappable::is_nothrow_swappable;
using detail::swappable::is_swappable;

// <functional>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)
#endif
template <typename F, typename... As>
inline constexpr auto invoke(F&& f, As&&... as) RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
#ifdef _MSC_VER
#pragma warning(pop)
#endif

    template <typename B, typename T, typename D>
    inline constexpr auto invoke(T B::*pmv, D&& d) RETURN(lib::forward<D>(d).*pmv)

        template <typename Pmv, typename Ptr>
        inline constexpr auto invoke(Pmv pmv, Ptr&& ptr) RETURN((*lib::forward<Ptr>(ptr)).*pmv)

            template <typename B, typename T, typename D, typename... As>
            inline constexpr auto invoke(T B::*pmf, D&& d, As&&... as)
                RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))

                    template <typename Pmf, typename Ptr, typename... As>
                    inline constexpr auto invoke(Pmf pmf, Ptr&& ptr, As&&... as)
                        RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))

                            namespace detail
{
  template <typename Void, typename, typename...>
  struct invoke_result
  {
  };

  template <typename F, typename... Args>
  struct invoke_result<void_t<decltype(lib::invoke(std::declval<F>(), std::declval<Args>()...))>, F,
                       Args...>
      : identity<decltype(lib::invoke(std::declval<F>(), std::declval<Args>()...))>
  {
  };

}  // namespace detail

template <typename F, typename... Args>
using invoke_result = detail::invoke_result<void, F, Args...>;

template <typename F, typename... Args>
using invoke_result_t = typename invoke_result<F, Args...>::type;

namespace detail
{
template <typename Void, typename, typename...>
struct is_invocable : std::false_type
{
};

template <typename F, typename... Args>
struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...> : std::true_type
{
};

template <typename Void, typename, typename, typename...>
struct is_invocable_r : std::false_type
{
};

template <typename R, typename F, typename... Args>
struct is_invocable_r<void_t<invoke_result_t<F, Args...>>, R, F, Args...>
    : std::is_convertible<invoke_result_t<F, Args...>, R>
{
};

}  // namespace detail

template <typename F, typename... Args>
using is_invocable = detail::is_invocable<void, F, Args...>;

template <typename R, typename F, typename... Args>
using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;

// <memory>
#ifdef MPARK_BUILTIN_ADDRESSOF
template <typename T>
inline constexpr T* addressof(T& arg)
{
  return __builtin_addressof(arg);
}
#else
namespace detail
{
namespace has_addressof_impl
{
struct fail;

template <typename T>
inline fail operator&(T&&);

template <typename T>
inline static constexpr bool impl()
{
  return (std::is_class<T>::value || std::is_union<T>::value) &&
         !std::is_same<decltype(&std::declval<T&>()), fail>::value;
}

}  // namespace has_addressof_impl

template <typename T>
using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;

template <typename T>
inline constexpr T* addressof(T& arg, std::true_type)
{
  return std::addressof(arg);
}

template <typename T>
inline constexpr T* addressof(T& arg, std::false_type)
{
  return &arg;
}

}  // namespace detail

template <typename T>
inline constexpr T* addressof(T& arg)
{
  return detail::addressof(arg, detail::has_addressof<T>{});
}
#endif

template <typename T>
inline constexpr T* addressof(const T&&) = delete;

}  // namespace cpp17

template <typename T>
struct remove_all_extents : identity<T>
{
};

template <typename T, std::size_t N>
struct remove_all_extents<array<T, N>> : remove_all_extents<T>
{
};

template <typename T>
using remove_all_extents_t = typename remove_all_extents<T>::type;

template <std::size_t N>
using size_constant = std::integral_constant<std::size_t, N>;

template <std::size_t I, typename T>
struct indexed_type : size_constant<I>, identity<T>
{
};

template <bool... Bs>
using all = std::is_same<integer_sequence<bool, true, Bs...>, integer_sequence<bool, Bs..., true>>;

#ifdef MPARK_TYPE_PACK_ELEMENT
template <std::size_t I, typename... Ts>
using type_pack_element_t = __type_pack_element<I, Ts...>;
#else
template <std::size_t I, typename... Ts>
struct type_pack_element_impl
{
private:
  template <typename>
  struct set;

  template <std::size_t... Is>
  struct set<index_sequence<Is...>> : indexed_type<Is, Ts>...
  {
  };

  template <typename T>
  inline static std::enable_if<true, T> impl(indexed_type<I, T>);

  inline static std::enable_if<false> impl(...);

public:
  using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
};

template <std::size_t I, typename... Ts>
using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;

template <std::size_t I, typename... Ts>
using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
#endif

#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
using std::is_trivially_copy_assignable;
using std::is_trivially_copy_constructible;
using std::is_trivially_move_assignable;
using std::is_trivially_move_constructible;
#else
template <typename T>
struct is_trivially_copy_constructible
    : bool_constant<std::is_copy_constructible<T>::value&& __has_trivial_copy(T)>
{
};

template <typename T>
struct is_trivially_move_constructible : bool_constant<__is_trivial(T)>
{
};

template <typename T>
struct is_trivially_copy_assignable
    : bool_constant<std::is_copy_assignable<T>::value&& __has_trivial_assign(T)>
{
};

template <typename T>
struct is_trivially_move_assignable : bool_constant<__is_trivial(T)>
{
};
#endif

template <typename T, bool>
struct dependent_type : T
{
};

template <typename Is, std::size_t J>
struct push_back;

template <typename Is, std::size_t J>
using push_back_t = typename push_back<Is, J>::type;

template <std::size_t... Is, std::size_t J>
struct push_back<index_sequence<Is...>, J>
{
  using type = index_sequence<Is..., J>;
};

}  // namespace lib
}  // namespace mpark

#undef RETURN

#endif  // MPARK_LIB_HPP

namespace mpark
{

#ifdef MPARK_RETURN_TYPE_DEDUCTION

#define AUTO auto
#define AUTO_RETURN(...)                                                                           \
  {                                                                                                \
    return __VA_ARGS__;                                                                            \
  }

#define AUTO_REFREF auto&&
#define AUTO_REFREF_RETURN(...)                                                                    \
  {                                                                                                \
    return __VA_ARGS__;                                                                            \
  }

#define DECLTYPE_AUTO decltype(auto)
#define DECLTYPE_AUTO_RETURN(...)                                                                  \
  {                                                                                                \
    return __VA_ARGS__;                                                                            \
  }

#else

#define AUTO auto
#define AUTO_RETURN(...)                                                                           \
  ->lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }

#define AUTO_REFREF auto
#define AUTO_REFREF_RETURN(...)                                                                    \
  ->decltype((__VA_ARGS__))                                                                        \
  {                                                                                                \
    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, "");                          \
    return __VA_ARGS__;                                                                            \
  }

#define DECLTYPE_AUTO auto
#define DECLTYPE_AUTO_RETURN(...)                                                                  \
  ->decltype(__VA_ARGS__) { return __VA_ARGS__; }

#endif

class bad_variant_access : public std::exception
{
public:
  virtual const char* what() const noexcept { return "bad_variant_access"; }
};

[[noreturn]] inline void throw_bad_variant_access()
{
#ifdef MPARK_EXCEPTIONS
  throw bad_variant_access{};
#else
  std::terminate();
#ifdef MPARK_BUILTIN_UNREACHABLE
  __builtin_unreachable();
#endif
#endif
}

template <typename... Ts>
class variant;

template <typename T>
struct variant_size;

#ifdef MPARK_VARIABLE_TEMPLATES
template <typename T>
constexpr std::size_t variant_size_v = variant_size<T>::value;
#endif

template <typename T>
struct variant_size<const T> : variant_size<T>
{
};

template <typename T>
struct variant_size<volatile T> : variant_size<T>
{
};

template <typename T>
struct variant_size<const volatile T> : variant_size<T>
{
};

template <typename... Ts>
struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)>
{
};

template <std::size_t I, typename T>
struct variant_alternative;

template <std::size_t I, typename T>
using variant_alternative_t = typename variant_alternative<I, T>::type;

template <std::size_t I, typename T>
struct variant_alternative<I, const T> : std::add_const<variant_alternative_t<I, T>>
{
};

template <std::size_t I, typename T>
struct variant_alternative<I, volatile T> : std::add_volatile<variant_alternative_t<I, T>>
{
};

template <std::size_t I, typename T>
struct variant_alternative<I, const volatile T> : std::add_cv<variant_alternative_t<I, T>>
{
};

template <std::size_t I, typename... Ts>
struct variant_alternative<I, variant<Ts...>>
{
  static_assert(I < sizeof...(Ts), "Index out of bounds in std::variant_alternative<>");
  using type = lib::type_pack_element_t<I, Ts...>;
};

constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);

namespace detail
{
constexpr std::size_t not_found = static_cast<std::size_t>(-1);
constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);

#ifdef MPARK_CPP14_CONSTEXPR
template <typename T, typename... Ts>
inline constexpr std::size_t find_index()
{
  constexpr lib::array<bool, sizeof...(Ts)> matches = {{std::is_same<T, Ts>::value...}};
  std::size_t result = not_found;
  for (std::size_t i = 0; i < sizeof...(Ts); ++i)
  {
    if (matches[i])
    {
      if (result != not_found)
      {
        return ambiguous;
      }
      result = i;
    }
  }
  return result;
}
#else
inline constexpr std::size_t find_index_impl(std::size_t result, std::size_t)
{
  return result;
}

template <typename... Bs>
inline constexpr std::size_t find_index_impl(std::size_t result, std::size_t idx, bool b, Bs... bs)
{
  return b ? (result != not_found ? ambiguous : find_index_impl(idx, idx + 1, bs...)) :
             find_index_impl(result, idx + 1, bs...);
}

template <typename T, typename... Ts>
inline constexpr std::size_t find_index()
{
  return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
}
#endif

template <std::size_t I>
using find_index_sfinae_impl =
    lib::enable_if_t<I != not_found && I != ambiguous, lib::size_constant<I>>;

template <typename T, typename... Ts>
using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;

template <std::size_t I>
struct find_index_checked_impl : lib::size_constant<I>
{
  static_assert(I != not_found, "the specified type is not found.");
  static_assert(I != ambiguous, "the specified type is ambiguous.");
};

template <typename T, typename... Ts>
using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;

struct valueless_t
{
};

enum class Trait
{
  TriviallyAvailable,
  Available,
  Unavailable
};

template <typename T, template <typename> class IsTriviallyAvailable,
          template <typename> class IsAvailable>
inline constexpr Trait trait()
{
  return IsTriviallyAvailable<T>::value ?
             Trait::TriviallyAvailable :
             IsAvailable<T>::value ? Trait::Available : Trait::Unavailable;
}

#ifdef MPARK_CPP14_CONSTEXPR
template <typename... Traits>
inline constexpr Trait common_trait(Traits... traits)
{
  Trait result = Trait::TriviallyAvailable;
  for (Trait t : {traits...})
  {
    if (static_cast<int>(t) > static_cast<int>(result))
    {
      result = t;
    }
  }
  return result;
}
#else
inline constexpr Trait common_trait_impl(Trait result)
{
  return result;
}

template <typename... Traits>
inline constexpr Trait common_trait_impl(Trait result, Trait t, Traits... ts)
{
  return static_cast<int>(t) > static_cast<int>(result) ? common_trait_impl(t, ts...) :
                                                          common_trait_impl(result, ts...);
}

template <typename... Traits>
inline constexpr Trait common_trait(Traits... ts)
{
  return common_trait_impl(Trait::TriviallyAvailable, ts...);
}
#endif

template <typename... Ts>
struct traits
{
  static constexpr Trait copy_constructible_trait = common_trait(
      trait<Ts, lib::is_trivially_copy_constructible, std::is_copy_constructible>()...);

  static constexpr Trait move_constructible_trait = common_trait(
      trait<Ts, lib::is_trivially_move_constructible, std::is_move_constructible>()...);

  static constexpr Trait copy_assignable_trait =
      common_trait(copy_constructible_trait,
                   trait<Ts, lib::is_trivially_copy_assignable, std::is_copy_assignable>()...);

  static constexpr Trait move_assignable_trait =
      common_trait(move_constructible_trait,
                   trait<Ts, lib::is_trivially_move_assignable, std::is_move_assignable>()...);

  static constexpr Trait destructible_trait =
      common_trait(trait<Ts, std::is_trivially_destructible, std::is_destructible>()...);
};

namespace access
{
struct recursive_union
{
#ifdef MPARK_RETURN_TYPE_DEDUCTION
  template <typename V>
  inline static constexpr auto&& get_alt(V&& v, in_place_index_t<0>)
  {
    return lib::forward<V>(v).head_;
  }

  template <typename V, std::size_t I>
  inline static constexpr auto&& get_alt(V&& v, in_place_index_t<I>)
  {
    return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
  }
#else
  template <std::size_t I, bool Dummy = true>
  struct get_alt_impl
  {
    template <typename V>
    inline constexpr AUTO_REFREF operator()(V&& v) const
        AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
  };

  template <bool Dummy>
  struct get_alt_impl<0, Dummy>
  {
    template <typename V>
    inline constexpr AUTO_REFREF operator()(V&& v) const
        AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
  };

  template <typename V, std::size_t I>
  inline static constexpr AUTO_REFREF get_alt(V&& v, in_place_index_t<I>)
      AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
#endif
};

struct base
{
  template <std::size_t I, typename V>
  inline static constexpr AUTO_REFREF get_alt(V&& v)
      AUTO_REFREF_RETURN(recursive_union::get_alt(data(lib::forward<V>(v)), in_place_index_t<I>{}))
};

struct variant
{
  template <std::size_t I, typename V>
  inline static constexpr AUTO_REFREF get_alt(V&& v)
      AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
};

}  // namespace access

namespace visitation
{
struct base
{
  template <typename T>
  inline static constexpr const T& at(const T& elem)
  {
    return elem;
  }

  template <typename T, std::size_t N, typename... Is>
  inline static constexpr const lib::remove_all_extents_t<T>& at(const lib::array<T, N>& elems,
                                                                 std::size_t i, Is... is)
  {
    return at(elems[i], is...);
  }

  template <typename F, typename... Fs>
  inline static constexpr int visit_visitor_return_type_check()
  {
    static_assert(lib::all<std::is_same<F, Fs>::value...>::value,
                  "`mpark::visit` requires the visitor to have a single "
                  "return type.");
    return 0;
  }

  template <typename... Fs>
  inline static constexpr lib::array<lib::common_type_t<lib::decay_t<Fs>...>, sizeof...(Fs)>
  make_farray(Fs&&... fs)
  {
    using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>, sizeof...(Fs)>;
    return visit_visitor_return_type_check<lib::decay_t<Fs>...>(),
           result{{lib::forward<Fs>(fs)...}};
  }

  template <std::size_t... Is>
  struct dispatcher
  {
    template <typename F, typename... Vs>
    struct impl
    {
      inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
          DECLTYPE_AUTO_RETURN(lib::invoke(static_cast<F>(f),
                                           access::base::get_alt<Is>(static_cast<Vs>(vs))...))
    };
  };

  template <typename F, typename... Vs, std::size_t... Is>
  inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
      AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)

          template <std::size_t I, typename F, typename... Vs>
          inline static constexpr AUTO make_fdiagonal_impl() AUTO_RETURN(
              make_dispatch<F, Vs...>(lib::index_sequence<lib::indexed_type<I, Vs>::value...>{}))

              template <typename F, typename... Vs, std::size_t... Is>
              inline static constexpr AUTO make_fdiagonal_impl(lib::index_sequence<Is...>)
                  AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))

                      template <typename F, typename V, typename... Vs>
                      inline static constexpr /* auto * */ auto make_fdiagonal()
                          -> decltype(make_fdiagonal_impl<F, V, Vs...>(
                              lib::make_index_sequence<lib::decay_t<V>::size()>{}))
  {
    static_assert(lib::all<(lib::decay_t<V>::size() == lib::decay_t<Vs>::size())...>::value,
                  "all of the variants must be the same size.");
    return make_fdiagonal_impl<F, V, Vs...>(lib::make_index_sequence<lib::decay_t<V>::size()>{});
  }

#ifdef MPARK_RETURN_TYPE_DEDUCTION
  template <typename F, typename... Vs, typename Is>
  inline static constexpr auto make_fmatrix_impl(Is is)
  {
    return make_dispatch<F, Vs...>(is);
  }

  template <typename F, typename... Vs, typename Is, std::size_t... Js, typename... Ls>
  inline static constexpr auto make_fmatrix_impl(Is, lib::index_sequence<Js...>, Ls... ls)
  {
    return make_farray(make_fmatrix_impl<F, Vs...>(lib::push_back_t<Is, Js>{}, ls...)...);
  }

  template <typename F, typename... Vs>
  inline static constexpr auto make_fmatrix()
  {
    return make_fmatrix_impl<F, Vs...>(lib::index_sequence<>{},
                                       lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
  }
#else
  template <typename F, typename... Vs>
  struct make_fmatrix_impl
  {
    template <typename...>
    struct impl;

    template <typename Is>
    struct impl<Is>
    {
      inline constexpr AUTO operator()() const AUTO_RETURN(make_dispatch<F, Vs...>(Is{}))
    };

    template <typename Is, std::size_t... Js, typename... Ls>
    struct impl<Is, lib::index_sequence<Js...>, Ls...>
    {
      inline constexpr AUTO operator()() const
          AUTO_RETURN(make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...))
    };
  };

  template <typename F, typename... Vs>
  inline static constexpr AUTO make_fmatrix()
      AUTO_RETURN(typename make_fmatrix_impl<F, Vs...>::template impl<
                  lib::index_sequence<>, lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
#endif
};  // namespace base

template <typename F, typename... Vs>
using FDiagonal = decltype(base::make_fdiagonal<F, Vs...>());

template <typename F, typename... Vs>
struct fdiagonal
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4268)
#endif
  static constexpr FDiagonal<F, Vs...> value = base::make_fdiagonal<F, Vs...>();
#ifdef _MSC_VER
#pragma warning(pop)
#endif
};

template <typename F, typename... Vs>
constexpr FDiagonal<F, Vs...> fdiagonal<F, Vs...>::value;

template <typename F, typename... Vs>
using FMatrix = decltype(base::make_fmatrix<F, Vs...>());

template <typename F, typename... Vs>
struct fmatrix
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4268)
#endif
  static constexpr FMatrix<F, Vs...> value = base::make_fmatrix<F, Vs...>();
#ifdef _MSC_VER
#pragma warning(pop)
#endif
};

template <typename F, typename... Vs>
constexpr FMatrix<F, Vs...> fmatrix<F, Vs...>::value;

struct alt
{
  template <typename Visitor, typename... Vs>
  inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, Visitor&& visitor,
                                                     Vs&&... vs)
      DECLTYPE_AUTO_RETURN(
          base::at(fdiagonal<Visitor&&, decltype(as_base(lib::forward<Vs>(vs)))...>::value,
                   index)(lib::forward<Visitor>(visitor), as_base(lib::forward<Vs>(vs))...))

          template <typename Visitor, typename... Vs>
          inline static constexpr DECLTYPE_AUTO
      visit_alt(Visitor&& visitor, Vs&&... vs) DECLTYPE_AUTO_RETURN(
          base::at(fmatrix<Visitor&&, decltype(as_base(lib::forward<Vs>(vs)))...>::value,
                   vs.index()...)(lib::forward<Visitor>(visitor), as_base(lib::forward<Vs>(vs))...))
};

struct variant
{
private:
  template <typename Visitor, typename... Values>
  struct visit_exhaustive_visitor_check
  {
    static_assert(lib::is_invocable<Visitor, Values...>::value,
                  "`mpark::visit` requires the visitor to be exhaustive.");

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)
#endif
    inline constexpr DECLTYPE_AUTO operator()(Visitor&& visitor, Values&&... values) const
        DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
                                         lib::forward<Values>(values)...))
#ifdef _MSC_VER
#pragma warning(pop)
#endif
  };

  template <typename Visitor>
  struct value_visitor
  {
    Visitor&& visitor_;

    template <typename... Alts>
    inline constexpr DECLTYPE_AUTO operator()(Alts&&... alts) const DECLTYPE_AUTO_RETURN(
        visit_exhaustive_visitor_check<Visitor, decltype((lib::forward<Alts>(alts).value))...>{}(
            lib::forward<Visitor>(visitor_), lib::forward<Alts>(alts).value...))
  };

  template <typename Visitor>
  inline static constexpr AUTO make_value_visitor(Visitor&& visitor)
      AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})

          public
      : template <typename Visitor, typename... Vs>
        inline static constexpr DECLTYPE_AUTO
        visit_alt_at(std::size_t index, Visitor&& visitor, Vs&&... vs)
            DECLTYPE_AUTO_RETURN(alt::visit_alt_at(index, lib::forward<Visitor>(visitor),
                                                   lib::forward<Vs>(vs).impl_...))

                template <typename Visitor, typename... Vs>
                inline static constexpr DECLTYPE_AUTO visit_alt(Visitor&& visitor, Vs&&... vs)
                    DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor),
                                                        lib::forward<Vs>(vs).impl_...))

                        template <typename Visitor, typename... Vs>
                        inline static constexpr DECLTYPE_AUTO
        visit_value_at(std::size_t index, Visitor&& visitor, Vs&&... vs)
            DECLTYPE_AUTO_RETURN(visit_alt_at(index,
                                              make_value_visitor(lib::forward<Visitor>(visitor)),
                                              lib::forward<Vs>(vs)...))

                template <typename Visitor, typename... Vs>
                inline static constexpr DECLTYPE_AUTO
        visit_value(Visitor&& visitor, Vs&&... vs)
            DECLTYPE_AUTO_RETURN(visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
                                           lib::forward<Vs>(vs)...))
};

}  // namespace visitation

template <std::size_t Index, typename T>
struct alt
{
  using value_type = T;

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244)
#endif
  template <typename... Args>
  inline explicit constexpr alt(in_place_t, Args&&... args) : value(lib::forward<Args>(args)...)
  {
  }
#ifdef _MSC_VER
#pragma warning(pop)
#endif

  T value;
};

template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
union recursive_union;

template <Trait DestructibleTrait, std::size_t Index>
union recursive_union<DestructibleTrait, Index>
{
};

#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)                              \
  template <std::size_t Index, typename T, typename... Ts>                                         \
  union recursive_union<destructible_trait, Index, T, Ts...>                                       \
  {                                                                                                \
  public:                                                                                          \
    inline explicit constexpr recursive_union(valueless_t) noexcept : dummy_{} {}                  \
                                                                                                   \
    template <typename... Args>                                                                    \
    inline explicit constexpr recursive_union(in_place_index_t<0>, Args&&... args)                 \
        : head_(in_place_t{}, lib::forward<Args>(args)...)                                         \
    {                                                                                              \
    }                                                                                              \
                                                                                                   \
    template <std::size_t I, typename... Args>                                                     \
    inline explicit constexpr recursive_union(in_place_index_t<I>, Args&&... args)                 \
        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...)                            \
    {                                                                                              \
    }                                                                                              \
                                                                                                   \
    recursive_union(const recursive_union&) = default;                                             \
    recursive_union(recursive_union&&) = default;                                                  \
                                                                                                   \
    destructor                                                                                     \
                                                                                                   \
        recursive_union&                                                                           \
        operator=(const recursive_union&) = default;                                               \
    recursive_union& operator=(recursive_union&&) = default;                                       \
                                                                                                   \
  private:                                                                                         \
    char dummy_;                                                                                   \
    alt<Index, T> head_;                                                                           \
    recursive_union<destructible_trait, Index + 1, Ts...> tail_;                                   \
                                                                                                   \
    friend struct access::recursive_union;                                                         \
  }

MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable, ~recursive_union() = default;);
MPARK_VARIANT_RECURSIVE_UNION(Trait::Available, ~recursive_union(){});
MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable, ~recursive_union() = delete;);

#undef MPARK_VARIANT_RECURSIVE_UNION

using index_t = unsigned int;

template <Trait DestructibleTrait, typename... Ts>
class base
{
public:
  inline explicit constexpr base(valueless_t tag) noexcept
      : data_(tag), index_(static_cast<index_t>(-1))
  {
  }

  template <std::size_t I, typename... Args>
  inline explicit constexpr base(in_place_index_t<I>, Args&&... args)
      : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...), index_(I)
  {
  }

  inline constexpr bool valueless_by_exception() const noexcept
  {
    return index_ == static_cast<index_t>(-1);
  }

  inline constexpr std::size_t index() const noexcept
  {
    return valueless_by_exception() ? variant_npos : index_;
  }

protected:
  using data_t = recursive_union<DestructibleTrait, 0, Ts...>;

  friend inline constexpr base& as_base(base& b) { return b; }
  friend inline constexpr const base& as_base(const base& b) { return b; }
  friend inline constexpr base&& as_base(base&& b) { return lib::move(b); }
  friend inline constexpr const base&& as_base(const base&& b) { return lib::move(b); }

  friend inline constexpr data_t& data(base& b) { return b.data_; }
  friend inline constexpr const data_t& data(const base& b) { return b.data_; }
  friend inline constexpr data_t&& data(base&& b) { return lib::move(b).data_; }
  friend inline constexpr const data_t&& data(const base&& b) { return lib::move(b).data_; }

  inline static constexpr std::size_t size() { return sizeof...(Ts); }

  data_t data_;
  index_t index_;

  friend struct access::base;
  friend struct visitation::base;
};

struct dtor
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)
#endif
  template <typename Alt>
  inline void operator()(Alt& alt) const noexcept
  {
    alt.~Alt();
  }
#ifdef _MSC_VER
#pragma warning(pop)
#endif
};

#if defined(_MSC_VER) && _MSC_VER < 1910
#define INHERITING_CTOR(type, base)                                                                \
  template <typename... Args>                                                                      \
  inline explicit constexpr type(Args&&... args) : base(lib::forward<Args>(args)...)               \
  {                                                                                                \
  }
#else
#define INHERITING_CTOR(type, base) using base::base;
#endif

template <typename Traits, Trait = Traits::destructible_trait>
class destructor;

#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy)                          \
  template <typename... Ts>                                                                        \
  class destructor<traits<Ts...>, destructible_trait> : public base<destructible_trait, Ts...>     \
  {                                                                                                \
    using super = base<destructible_trait, Ts...>;                                                 \
                                                                                                   \
  public:                                                                                          \
    INHERITING_CTOR(destructor, super)                                                             \
    using super::operator=;                                                                        \
                                                                                                   \
    destructor(const destructor&) = default;                                                       \
    destructor(destructor&&) = default;                                                            \
    definition destructor& operator=(const destructor&) = default;                                 \
    destructor& operator=(destructor&&) = default;                                                 \
                                                                                                   \
  protected:                                                                                       \
    destroy                                                                                        \
  }

MPARK_VARIANT_DESTRUCTOR(Trait::TriviallyAvailable, ~destructor() = default;
                         , inline void destroy() noexcept {
                           this->index_ = static_cast<index_t>(-1);
                         });

MPARK_VARIANT_DESTRUCTOR(Trait::Available, ~destructor() { destroy(); },
                         inline void destroy() noexcept {
                           if (!this->valueless_by_exception())
                           {
                             visitation::alt::visit_alt(dtor{}, *this);
                           }
                           this->index_ = static_cast<index_t>(-1);
                         });

MPARK_VARIANT_DESTRUCTOR(Trait::Unavailable, ~destructor() = delete;
                         , inline void destroy() noexcept = delete;);

#undef MPARK_VARIANT_DESTRUCTOR

template <typename Traits>
class constructor : public destructor<Traits>
{
  using super = destructor<Traits>;

public:
  INHERITING_CTOR(constructor, super)
  using super::operator=;

protected:
#ifndef MPARK_GENERIC_LAMBDAS
  struct ctor
  {
    template <typename LhsAlt, typename RhsAlt>
    inline void operator()(LhsAlt& lhs_alt, RhsAlt&& rhs_alt) const
    {
      constructor::construct_alt(lhs_alt, lib::forward<RhsAlt>(rhs_alt).value);
    }
  };
#endif

  template <std::size_t I, typename T, typename... Args>
  inline static T& construct_alt(alt<I, T>& a, Args&&... args)
  {
    ::new (static_cast<void*>(lib::addressof(a)))
        alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
    return a.value;
  }

  template <typename Rhs>
  inline static void generic_construct(constructor& lhs, Rhs&& rhs)
  {
    lhs.destroy();
    if (!rhs.valueless_by_exception())
    {
      visitation::alt::visit_alt_at(rhs.index(),
#ifdef MPARK_GENERIC_LAMBDAS
                                    [](auto& lhs_alt, auto&& rhs_alt) {
                                      constructor::construct_alt(
                                          lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
                                    }
#else
                                    ctor {}
#endif
                                    ,
                                    lhs, lib::forward<Rhs>(rhs));
      lhs.index_ = rhs.index_;
    }
  }
};

template <typename Traits, Trait = Traits::move_constructible_trait>
class move_constructor;

#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition)                       \
  template <typename... Ts>                                                                        \
  class move_constructor<traits<Ts...>, move_constructible_trait>                                  \
      : public constructor<traits<Ts...>>                                                          \
  {                                                                                                \
    using super = constructor<traits<Ts...>>;                                                      \
                                                                                                   \
  public:                                                                                          \
    INHERITING_CTOR(move_constructor, super)                                                       \
    using super::operator=;                                                                        \
                                                                                                   \
    move_constructor(const move_constructor&) = default;                                           \
    definition ~move_constructor() = default;                                                      \
    move_constructor& operator=(const move_constructor&) = default;                                \
    move_constructor& operator=(move_constructor&&) = default;                                     \
  }

MPARK_VARIANT_MOVE_CONSTRUCTOR(Trait::TriviallyAvailable,
                               move_constructor(move_constructor&& that) = default;);

MPARK_VARIANT_MOVE_CONSTRUCTOR(
    Trait::Available, move_constructor(move_constructor&& that) noexcept(
                          lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
    : move_constructor(valueless_t{}) { this->generic_construct(*this, lib::move(that)); });

MPARK_VARIANT_MOVE_CONSTRUCTOR(Trait::Unavailable, move_constructor(move_constructor&&) = delete;);

#undef MPARK_VARIANT_MOVE_CONSTRUCTOR

template <typename Traits, Trait = Traits::copy_constructible_trait>
class copy_constructor;

#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition)                       \
  template <typename... Ts>                                                                        \
  class copy_constructor<traits<Ts...>, copy_constructible_trait>                                  \
      : public move_constructor<traits<Ts...>>                                                     \
  {                                                                                                \
    using super = move_constructor<traits<Ts...>>;                                                 \
                                                                                                   \
  public:                                                                                          \
    INHERITING_CTOR(copy_constructor, super)                                                       \
    using super::operator=;                                                                        \
                                                                                                   \
    definition copy_constructor(copy_constructor&&) = default;                                     \
    ~copy_constructor() = default;                                                                 \
    copy_constructor& operator=(const copy_constructor&) = default;                                \
    copy_constructor& operator=(copy_constructor&&) = default;                                     \
  }

MPARK_VARIANT_COPY_CONSTRUCTOR(Trait::TriviallyAvailable,
                               copy_constructor(const copy_constructor& that) = default;);

MPARK_VARIANT_COPY_CONSTRUCTOR(Trait::Available, copy_constructor(const copy_constructor& that)
                               : copy_constructor(valueless_t{}) {
                                 this->generic_construct(*this, that);
                               });

MPARK_VARIANT_COPY_CONSTRUCTOR(Trait::Unavailable,
                               copy_constructor(const copy_constructor&) = delete;);

#undef MPARK_VARIANT_COPY_CONSTRUCTOR

template <typename Traits>
class assignment : public copy_constructor<Traits>
{
  using super = copy_constructor<Traits>;

public:
  INHERITING_CTOR(assignment, super)
  using super::operator=;

  template <std::size_t I, typename... Args>
  inline /* auto & */ auto emplace(Args&&... args)
      -> decltype(this->construct_alt(access::base::get_alt<I>(*this), lib::forward<Args>(args)...))
  {
    this->destroy();
    auto& result =
        this->construct_alt(access::base::get_alt<I>(*this), lib::forward<Args>(args)...);
    this->index_ = I;
    return result;
  }

protected:
#ifndef MPARK_GENERIC_LAMBDAS
  template <typename That>
  struct assigner
  {
    template <typename ThisAlt, typename ThatAlt>
    inline void operator()(ThisAlt& this_alt, ThatAlt&& that_alt) const
    {
      self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
    }
    assignment* self;
  };
#endif

  template <std::size_t I, typename T, typename Arg>
  inline void assign_alt(alt<I, T>& a, Arg&& arg)
  {
    if (this->index() == I)
    {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244)
#endif
      a.value = lib::forward<Arg>(arg);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
    }
    else
    {
      struct
      {
        void operator()(std::true_type) const { this_->emplace<I>(lib::forward<Arg>(arg_)); }
        void operator()(std::false_type) const { this_->emplace<I>(T(lib::forward<Arg>(arg_))); }
        assignment* this_;
        Arg&& arg_;
      } impl{this, lib::forward<Arg>(arg)};
      impl(lib::bool_constant < std::is_nothrow_constructible<T, Arg>::value ||
           !std::is_nothrow_move_constructible<T>::value > {});
    }
  }

  template <typename That>
  inline void generic_assign(That&& that)
  {
    if (this->valueless_by_exception() && that.valueless_by_exception())
    {
      // do nothing.
    }
    else if (that.valueless_by_exception())
    {
      this->destroy();
    }
    else
    {
      visitation::alt::visit_alt_at(
          that.index(),
#ifdef MPARK_GENERIC_LAMBDAS
          [this](auto& this_alt, auto&& that_alt) {
            this->assign_alt(this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
          }
#else
          assigner<That> { this }
#endif
          ,
          *this, lib::forward<That>(that));
    }
  }
};

template <typename Traits, Trait = Traits::move_assignable_trait>
class move_assignment;

#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition)                           \
  template <typename... Ts>                                                                        \
  class move_assignment<traits<Ts...>, move_assignable_trait> : public assignment<traits<Ts...>>   \
  {                                                                                                \
    using super = assignment<traits<Ts...>>;                                                       \
                                                                                                   \
  public:                                                                                          \
    INHERITING_CTOR(move_assignment, super)                                                        \
    using super::operator=;                                                                        \
                                                                                                   \
    move_assignment(const move_assignment&) = default;                                             \
    move_assignment(move_assignment&&) = default;                                                  \
    ~move_assignment() = default;                                                                  \
    move_assignment& operator=(const move_assignment&) = default;                                  \
    definition                                                                                     \
  }

MPARK_VARIANT_MOVE_ASSIGNMENT(Trait::TriviallyAvailable,
                              move_assignment& operator=(move_assignment&& that) = default;);

MPARK_VARIANT_MOVE_ASSIGNMENT(
    Trait::Available, move_assignment& operator=(move_assignment&& that) noexcept(
                          lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
                                    std::is_nothrow_move_assignable<Ts>::value)...>::value) {
      this->generic_assign(lib::move(that));
      return *this;
    });

MPARK_VARIANT_MOVE_ASSIGNMENT(Trait::Unavailable,
                              move_assignment& operator=(move_assignment&&) = delete;);

#undef MPARK_VARIANT_MOVE_ASSIGNMENT

template <typename Traits, Trait = Traits::copy_assignable_trait>
class copy_assignment;

#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition)                           \
  template <typename... Ts>                                                                        \
  class copy_assignment<traits<Ts...>, copy_assignable_trait>                                      \
      : public move_assignment<traits<Ts...>>                                                      \
  {                                                                                                \
    using super = move_assignment<traits<Ts...>>;                                                  \
                                                                                                   \
  public:                                                                                          \
    INHERITING_CTOR(copy_assignment, super)                                                        \
    using super::operator=;                                                                        \
                                                                                                   \
    copy_assignment(const copy_assignment&) = default;                                             \
    copy_assignment(copy_assignment&&) = default;                                                  \
    ~copy_assignment() = default;                                                                  \
    definition copy_assignment& operator=(copy_assignment&&) = default;                            \
  }

MPARK_VARIANT_COPY_ASSIGNMENT(Trait::TriviallyAvailable,
                              copy_assignment& operator=(const copy_assignment& that) = default;);

MPARK_VARIANT_COPY_ASSIGNMENT(Trait::Available,
                              copy_assignment& operator=(const copy_assignment& that) {
                                this->generic_assign(that);
                                return *this;
                              });

MPARK_VARIANT_COPY_ASSIGNMENT(Trait::Unavailable,
                              copy_assignment& operator=(const copy_assignment&) = delete;);

#undef MPARK_VARIANT_COPY_ASSIGNMENT

template <typename... Ts>
class impl : public copy_assignment<traits<Ts...>>
{
  using super = copy_assignment<traits<Ts...>>;

public:
  INHERITING_CTOR(impl, super)
  using super::operator=;

  template <std::size_t I, typename Arg>
  inline void assign(Arg&& arg)
  {
    this->assign_alt(access::base::get_alt<I>(*this), lib::forward<Arg>(arg));
  }

  inline void swap(impl& that)
  {
    if (this->valueless_by_exception() && that.valueless_by_exception())
    {
      // do nothing.
    }
    else if (this->index() == that.index())
    {
      visitation::alt::visit_alt_at(this->index(),
#ifdef MPARK_GENERIC_LAMBDAS
                                    [](auto& this_alt, auto& that_alt) {
                                      using std::swap;
                                      swap(this_alt.value, that_alt.value);
                                    }
#else
                                    swapper {}
#endif
                                    ,
                                    *this, that);
    }
    else
    {
      impl* lhs = this;
      impl* rhs = lib::addressof(that);
      if (lhs->move_nothrow() && !rhs->move_nothrow())
      {
        std::swap(lhs, rhs);
      }
      impl tmp(lib::move(*rhs));
#ifdef MPARK_EXCEPTIONS
      // EXTENSION: When the move construction of `lhs` into `rhs` throws
      // and `tmp` is nothrow move constructible then we move `tmp` back
      // into `rhs` and provide the strong exception safety guarantee.
      try
      {
        this->generic_construct(*rhs, lib::move(*lhs));
      }
      catch (...)
      {
        if (tmp.move_nothrow())
        {
          this->generic_construct(*rhs, lib::move(tmp));
        }
        throw;
      }
#else
      this->generic_construct(*rhs, lib::move(*lhs));
#endif
      this->generic_construct(*lhs, lib::move(tmp));
    }
  }

private:
#ifndef MPARK_GENERIC_LAMBDAS
  struct swapper
  {
    template <typename ThisAlt, typename ThatAlt>
    inline void operator()(ThisAlt& this_alt, ThatAlt& that_alt) const
    {
      using std::swap;
      swap(this_alt.value, that_alt.value);
    }
  };
#endif

  inline constexpr bool move_nothrow() const
  {
    return this->valueless_by_exception() ||
           lib::array<bool, sizeof...(Ts)>{
               {std::is_nothrow_move_constructible<Ts>::value...}}[this->index()];
  }
};

template <std::size_t I, typename T>
struct overload_leaf
{
  using F = lib::size_constant<I> (*)(T);
  operator F() const { return nullptr; }
};

template <typename... Ts>
struct overload_impl
{
private:
  template <typename>
  struct impl;

  template <std::size_t... Is>
  struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>...
  {
  };

public:
  using type = impl<lib::index_sequence_for<Ts...>>;
};

template <typename... Ts>
using overload = typename overload_impl<Ts...>::type;

template <typename T, typename... Ts>
using best_match = lib::invoke_result_t<overload<Ts...>, T&&>;

template <typename T>
struct is_in_place_index : std::false_type
{
};

template <std::size_t I>
struct is_in_place_index<in_place_index_t<I>> : std::true_type
{
};

template <typename T>
struct is_in_place_type : std::false_type
{
};

template <typename T>
struct is_in_place_type<in_place_type_t<T>> : std::true_type
{
};

}  // namespace detail

template <typename... Ts>
class variant
{
  static_assert(0 < sizeof...(Ts), "variant must consist of at least one alternative.");

  static_assert(lib::all<!std::is_array<Ts>::value...>::value,
                "variant can not have an array type as an alternative.");

  static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
                "variant can not have a reference type as an alternative.");

  static_assert(lib::all<!std::is_void<Ts>::value...>::value,
                "variant can not have a void type as an alternative.");

public:
  template <typename Front = lib::type_pack_element_t<0, Ts...>,
            lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
  inline constexpr variant() noexcept(std::is_nothrow_default_constructible<Front>::value)
      : impl_(in_place_index_t<0>{})
  {
  }

  variant(const variant&) = default;
  variant(variant&&) = default;

  template <typename Arg, typename Decayed = lib::decay_t<Arg>,
            lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
            lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
            lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
            std::size_t I = detail::best_match<Arg, Ts...>::value,
            typename T = lib::type_pack_element_t<I, Ts...>,
            lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
  inline constexpr variant(Arg&& arg) noexcept(std::is_nothrow_constructible<T, Arg>::value)
      : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg))
  {
  }

  template <std::size_t I, typename... Args, typename T = lib::type_pack_element_t<I, Ts...>,
            lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
  inline explicit constexpr variant(in_place_index_t<I>, Args&&... args) noexcept(
      std::is_nothrow_constructible<T, Args...>::value)
      : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...)
  {
  }

  template <
      std::size_t I, typename Up, typename... Args, typename T = lib::type_pack_element_t<I, Ts...>,
      lib::enable_if_t<std::is_constructible<T, std::initializer_list<Up>&, Args...>::value, int> =
          0>
  inline explicit constexpr variant(
      in_place_index_t<I>, std::initializer_list<Up> il,
      Args&&... args) noexcept(std::is_nothrow_constructible<T, std::initializer_list<Up>&,
                                                             Args...>::value)
      : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...)
  {
  }

  template <typename T, typename... Args,
            std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
            lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
  inline explicit constexpr variant(in_place_type_t<T>, Args&&... args) noexcept(
      std::is_nothrow_constructible<T, Args...>::value)
      : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...)
  {
  }

  template <typename T, typename Up, typename... Args,
            std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
            lib::enable_if_t<std::is_constructible<T, std::initializer_list<Up>&, Args...>::value,
                             int> = 0>
  inline explicit constexpr variant(
      in_place_type_t<T>, std::initializer_list<Up> il,
      Args&&... args) noexcept(std::is_nothrow_constructible<T, std::initializer_list<Up>&,
                                                             Args...>::value)
      : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...)
  {
  }

  ~variant() = default;

  variant& operator=(const variant&) = default;
  variant& operator=(variant&&) = default;

  template <
      typename Arg, lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value, int> = 0,
      std::size_t I = detail::best_match<Arg, Ts...>::value,
      typename T = lib::type_pack_element_t<I, Ts...>,
      lib::enable_if_t<(std::is_assignable<T&, Arg>::value && std::is_constructible<T, Arg>::value),
                       int> = 0>
  inline variant& operator=(Arg&& arg) noexcept((std::is_nothrow_assignable<T&, Arg>::value &&
                                                 std::is_nothrow_constructible<T, Arg>::value))
  {
    impl_.template assign<I>(lib::forward<Arg>(arg));
    return *this;
  }

  template <std::size_t I, typename... Args, typename T = lib::type_pack_element_t<I, Ts...>,
            lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
  inline T& emplace(Args&&... args)
  {
    return impl_.template emplace<I>(lib::forward<Args>(args)...);
  }

  template <
      std::size_t I, typename Up, typename... Args, typename T = lib::type_pack_element_t<I, Ts...>,
      lib::enable_if_t<std::is_constructible<T, std::initializer_list<Up>&, Args...>::value, int> =
          0>
  inline T& emplace(std::initializer_list<Up> il, Args&&... args)
  {
    return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
  }

  template <typename T, typename... Args,
            std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
            lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
  inline T& emplace(Args&&... args)
  {
    return impl_.template emplace<I>(lib::forward<Args>(args)...);
  }

  template <typename T, typename Up, typename... Args,
            std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
            lib::enable_if_t<std::is_constructible<T, std::initializer_list<Up>&, Args...>::value,
                             int> = 0>
  inline T& emplace(std::initializer_list<Up> il, Args&&... args)
  {
    return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
  }

  inline constexpr bool valueless_by_exception() const noexcept
  {
    return impl_.valueless_by_exception();
  }

  inline constexpr std::size_t index() const noexcept { return impl_.index(); }

  template <
      bool Dummy = true,
      lib::enable_if_t<
          lib::all<Dummy, (lib::dependent_type<std::is_move_constructible<Ts>, Dummy>::value &&
                           lib::dependent_type<lib::is_swappable<Ts>, Dummy>::value)...>::value,
          int> = 0>
  inline void
  swap(variant& that) noexcept(lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
                                         lib::is_nothrow_swappable<Ts>::value)...>::value)
  {
    impl_.swap(that.impl_);
  }

private:
  detail::impl<Ts...> impl_;

  friend struct detail::access::variant;
  friend struct detail::visitation::variant;
};

template <std::size_t I, typename... Ts>
inline constexpr bool holds_alternative(const variant<Ts...>& v) noexcept
{
  return v.index() == I;
}

template <typename T, typename... Ts>
inline constexpr bool holds_alternative(const variant<Ts...>& v) noexcept
{
  return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
}

namespace detail
{
template <std::size_t I, typename V>
struct generic_get_impl
{
  constexpr generic_get_impl(int) {}

  constexpr AUTO_REFREF operator()(V&& v) const
      AUTO_REFREF_RETURN(access::variant::get_alt<I>(lib::forward<V>(v)).value)
};

template <std::size_t I, typename V>
inline constexpr AUTO_REFREF generic_get(V&& v) AUTO_REFREF_RETURN(generic_get_impl<I, V>(
    holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(lib::forward<V>(v)))
}  // namespace detail

template <std::size_t I, typename... Ts>
inline constexpr variant_alternative_t<I, variant<Ts...>>& get(variant<Ts...>& v)
{
  return detail::generic_get<I>(v);
}

template <std::size_t I, typename... Ts>
inline constexpr variant_alternative_t<I, variant<Ts...>>&& get(variant<Ts...>&& v)
{
  return detail::generic_get<I>(lib::move(v));
}

template <std::size_t I, typename... Ts>
inline constexpr const variant_alternative_t<I, variant<Ts...>>& get(const variant<Ts...>& v)
{
  return detail::generic_get<I>(v);
}

template <std::size_t I, typename... Ts>
inline constexpr const variant_alternative_t<I, variant<Ts...>>&& get(const variant<Ts...>&& v)
{
  return detail::generic_get<I>(lib::move(v));
}

template <typename T, typename... Ts>
inline constexpr T& get(variant<Ts...>& v)
{
  return get<detail::find_index_checked<T, Ts...>::value>(v);
}

template <typename T, typename... Ts>
inline constexpr T&& get(variant<Ts...>&& v)
{
  return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
}

template <typename T, typename... Ts>
inline constexpr const T& get(const variant<Ts...>& v)
{
  return get<detail::find_index_checked<T, Ts...>::value>(v);
}

template <typename T, typename... Ts>
inline constexpr const T&& get(const variant<Ts...>&& v)
{
  return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
}

namespace detail
{
template <std::size_t I, typename V>
inline constexpr /* auto * */ AUTO generic_get_if(V* v) noexcept AUTO_RETURN(
    v&& holds_alternative<I>(*v) ? lib::addressof(access::variant::get_alt<I>(*v).value) : nullptr)

}  // namespace detail

template <std::size_t I, typename... Ts>
inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
get_if(variant<Ts...>* v) noexcept
{
  return detail::generic_get_if<I>(v);
}

template <std::size_t I, typename... Ts>
inline constexpr lib::add_pointer_t<const variant_alternative_t<I, variant<Ts...>>>
get_if(const variant<Ts...>* v) noexcept
{
  return detail::generic_get_if<I>(v);
}

template <typename T, typename... Ts>
inline constexpr lib::add_pointer_t<T> get_if(variant<Ts...>* v) noexcept
{
  return get_if<detail::find_index_checked<T, Ts...>::value>(v);
}

template <typename T, typename... Ts>
inline constexpr lib::add_pointer_t<const T> get_if(const variant<Ts...>* v) noexcept
{
  return get_if<detail::find_index_checked<T, Ts...>::value>(v);
}

template <typename... Ts>
inline constexpr bool operator==(const variant<Ts...>& lhs, const variant<Ts...>& rhs)
{
  using detail::visitation::variant;
  using lib::equal_to;
#ifdef MPARK_CPP14_CONSTEXPR
  if (lhs.index() != rhs.index())
    return false;
  if (lhs.valueless_by_exception())
    return true;
  return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
#else
  return lhs.index() == rhs.index() && (lhs.valueless_by_exception() ||
                                        variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
#endif
}

template <typename... Ts>
inline constexpr bool operator!=(const variant<Ts...>& lhs, const variant<Ts...>& rhs)
{
  using detail::visitation::variant;
  using lib::not_equal_to;
#ifdef MPARK_CPP14_CONSTEXPR
  if (lhs.index() != rhs.index())
    return true;
  if (lhs.valueless_by_exception())
    return false;
  return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
#else
  return lhs.index() != rhs.index() ||
         (!lhs.valueless_by_exception() &&
          variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
#endif
}

template <typename... Ts>
inline constexpr bool operator<(const variant<Ts...>& lhs, const variant<Ts...>& rhs)
{
  using detail::visitation::variant;
  using lib::less;
#ifdef MPARK_CPP14_CONSTEXPR
  if (rhs.valueless_by_exception())
    return false;
  if (lhs.valueless_by_exception())
    return true;
  if (lhs.index() < rhs.index())
    return true;
  if (lhs.index() > rhs.index())
    return false;
  return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
#else
  return !rhs.valueless_by_exception() &&
         (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
          (lhs.index() == rhs.index() && variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
#endif
}

template <typename... Ts>
inline constexpr bool operator>(const variant<Ts...>& lhs, const variant<Ts...>& rhs)
{
  using detail::visitation::variant;
  using lib::greater;
#ifdef MPARK_CPP14_CONSTEXPR
  if (lhs.valueless_by_exception())
    return false;
  if (rhs.valueless_by_exception())
    return true;
  if (lhs.index() > rhs.index())
    return true;
  if (lhs.index() < rhs.index())
    return false;
  return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
#else
  return !lhs.valueless_by_exception() &&
         (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
          (lhs.index() == rhs.index() &&
           variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
#endif
}

template <typename... Ts>
inline constexpr bool operator<=(const variant<Ts...>& lhs, const variant<Ts...>& rhs)
{
  using detail::visitation::variant;
  using lib::less_equal;
#ifdef MPARK_CPP14_CONSTEXPR
  if (lhs.valueless_by_exception())
    return true;
  if (rhs.valueless_by_exception())
    return false;
  if (lhs.index() < rhs.index())
    return true;
  if (lhs.index() > rhs.index())
    return false;
  return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
#else
  return lhs.valueless_by_exception() ||
         (!rhs.valueless_by_exception() &&
          (lhs.index() < rhs.index() ||
           (lhs.index() == rhs.index() &&
            variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
#endif
}

template <typename... Ts>
inline constexpr bool operator>=(const variant<Ts...>& lhs, const variant<Ts...>& rhs)
{
  using detail::visitation::variant;
  using lib::greater_equal;
#ifdef MPARK_CPP14_CONSTEXPR
  if (rhs.valueless_by_exception())
    return true;
  if (lhs.valueless_by_exception())
    return false;
  if (lhs.index() > rhs.index())
    return true;
  if (lhs.index() < rhs.index())
    return false;
  return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
#else
  return rhs.valueless_by_exception() ||
         (!lhs.valueless_by_exception() &&
          (lhs.index() > rhs.index() ||
           (lhs.index() == rhs.index() &&
            variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs))));
#endif
}

struct monostate
{
};

inline constexpr bool operator<(monostate, monostate) noexcept
{
  return false;
}

inline constexpr bool operator>(monostate, monostate) noexcept
{
  return false;
}

inline constexpr bool operator<=(monostate, monostate) noexcept
{
  return true;
}

inline constexpr bool operator>=(monostate, monostate) noexcept
{
  return true;
}

inline constexpr bool operator==(monostate, monostate) noexcept
{
  return true;
}

inline constexpr bool operator!=(monostate, monostate) noexcept
{
  return false;
}

#ifdef MPARK_CPP14_CONSTEXPR
namespace detail
{
inline constexpr bool all(std::initializer_list<bool> bs)
{
  for (bool b : bs)
  {
    if (!b)
    {
      return false;
    }
  }
  return true;
}

}  // namespace detail

template <typename Visitor, typename... Vs>
inline constexpr decltype(auto) visit(Visitor&& visitor, Vs&&... vs)
{
  return (detail::all({!vs.valueless_by_exception()...}) ? (void)0 : throw_bad_variant_access()),
         detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
                                                  lib::forward<Vs>(vs)...);
}
#else
namespace detail
{
template <std::size_t N>
inline constexpr bool all_impl(const lib::array<bool, N>& bs, std::size_t idx)
{
  return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
}

template <std::size_t N>
inline constexpr bool all(const lib::array<bool, N>& bs)
{
  return all_impl(bs, 0);
}

}  // namespace detail

template <typename Visitor, typename... Vs>
inline constexpr DECLTYPE_AUTO visit(Visitor&& visitor, Vs&&... vs) DECLTYPE_AUTO_RETURN(
    (detail::all(lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}}) ?
         (void)0 :
         throw_bad_variant_access()),
    detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
                                             lib::forward<Vs>(vs)...))
#endif

template <typename... Ts>
inline auto swap(variant<Ts...>& lhs, variant<Ts...>& rhs) noexcept(noexcept(lhs.swap(rhs)))
    -> decltype(lhs.swap(rhs))
{
  lhs.swap(rhs);
}

namespace detail
{
template <typename T, typename...>
using enabled_type = T;

namespace hash
{
template <typename H, typename K>
constexpr bool meets_requirements()
{
  return std::is_copy_constructible<H>::value && std::is_move_constructible<H>::value &&
         lib::is_invocable_r<std::size_t, H, const K&>::value;
}

template <typename K>
constexpr bool is_enabled()
{
  using H = std::hash<K>;
  return meets_requirements<H, K>() && std::is_default_constructible<H>::value &&
         std::is_copy_assignable<H>::value && std::is_move_assignable<H>::value;
}

}  // namespace hash

}  // namespace detail

#undef AUTO
#undef AUTO_RETURN

#undef AUTO_REFREF
#undef AUTO_REFREF_RETURN

#undef DECLTYPE_AUTO
#undef DECLTYPE_AUTO_RETURN

}  // namespace mpark

namespace std
{
template <typename... Ts>
struct hash<mpark::detail::enabled_type<
    mpark::variant<Ts...>, mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
                               mpark::lib::remove_const_t<Ts>>()...>::value>>>
{
  using argument_type = mpark::variant<Ts...>;
  using result_type = std::size_t;

  inline result_type operator()(const argument_type& v) const
  {
    using mpark::detail::visitation::variant;
    std::size_t result = v.valueless_by_exception() ?
                             299792458  // Random value chosen by the universe upon creation
                             :
                             variant::visit_alt(
#ifdef MPARK_GENERIC_LAMBDAS
                                 [](const auto& alt) {
                                   using alt_type = mpark::lib::decay_t<decltype(alt)>;
                                   using value_type =
                                       mpark::lib::remove_const_t<typename alt_type::value_type>;
                                   return hash<value_type>{}(alt.value);
                                 }
#else
                                 hasher {}
#endif
                                 ,
                                 v);
    return hash_combine(result, hash<std::size_t>{}(v.index()));
  }

private:
#ifndef MPARK_GENERIC_LAMBDAS
  struct hasher
  {
    template <typename Alt>
    inline std::size_t operator()(const Alt& alt) const
    {
      using alt_type = mpark::lib::decay_t<Alt>;
      using value_type = mpark::lib::remove_const_t<typename alt_type::value_type>;
      return hash<value_type>{}(alt.value);
    }
  };
#endif

  static std::size_t hash_combine(std::size_t lhs, std::size_t rhs)
  {
    return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
  }
};

template <>
struct hash<mpark::monostate>
{
  using argument_type = mpark::monostate;
  using result_type = std::size_t;

  inline result_type operator()(const argument_type&) const noexcept
  {
    return 66740831;  // return a fundamentally attractive random value.
  }
};

}  // namespace std

namespace std
{
using mpark::bad_variant_access;
using mpark::get;
using mpark::get_if;
using mpark::holds_alternative;
using mpark::monostate;
using mpark::variant;
using mpark::variant_alternative;
using mpark::variant_alternative_t;
using mpark::variant_size;
using mpark::visit;
}  // namespace std
#endif
