// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _EZCXX_TYPE_TRAITS
#define _EZCXX_TYPE_TRAITS

#pragma clang system_header

#include <__config>
#include <cstddef>

namespace std {

//------------------------------------------------------------------------------
// helper classes
//------------------------------------------------------------------------------

template<class _Tp, _Tp __value> struct integral_constant {
    static const _Tp value = __value;
    using value_type = _Tp;
    using type = integral_constant;
    _EZCXX_INLINE constexpr operator value_type()   const noexcept { return value; }
    _EZCXX_INLINE constexpr value_type operator()() const noexcept { return value; }
};

template<bool __value> using bool_constant = integral_constant<bool, __value>;
using false_type = bool_constant<false>;
using true_type  = bool_constant<true>;

//------------------------------------------------------------------------------
// helper traits
//------------------------------------------------------------------------------

template<class _Tp> struct type_identity { using type = _Tp; };
template<class _Tp> using type_identity_t = typename type_identity<_Tp>::type;

template<bool, class = void> struct enable_if {};
template<class _Tp> struct enable_if<true, _Tp> : type_identity<_Tp> {};
template<bool _Ep, class _Tp = void> using enable_if_t = typename enable_if<_Ep, _Tp>::type;

template<bool _Cp, class _Tp, class _Fp> struct conditional                  : type_identity<_Tp> {};
template<class _Tp, class _Fp>           struct conditional<false, _Tp, _Fp> : type_identity<_Fp> {};
template<bool _Cp, class _Tp, class _Fp> using conditional_t = typename conditional<_Cp, _Tp, _Fp>::type;

template<class...> using void_t = void;

template<class...> using __require = true_type;

//------------------------------------------------------------------------------
// type relationships
//------------------------------------------------------------------------------

template<class, class> struct is_same           : false_type {};
template<class _Tp>    struct is_same<_Tp, _Tp> : true_type  {};
template<class _Lp, class _Rp> inline constexpr bool is_same_v = is_same<_Lp, _Rp>::value;

#if __has_builtin(__is_base_of)
template<class _Lp, class _Rp> inline constexpr bool is_base_of_v = __is_base_of(_Lp, _Rp);
template<class _Lp, class _Rp> using is_base_of = bool_constant<is_base_of_v<_Lp, _Rp>>;
#endif

#if __has_builtin(__is_convertible)
template<class _Lp, class _Rp> inline constexpr bool is_convertible_v = __is_convertible(_Lp, _Rp);
template<class _Lp, class _Rp> using is_convertible = bool_constant<is_convertible_v<_Lp, _Rp>>;
#endif

//------------------------------------------------------------------------------
// logical operator traits
//------------------------------------------------------------------------------

template<class...>                struct conjunction              : true_type                                           {};
template<class _Tp>               struct conjunction<_Tp>         : _Tp                                                 {};
template<class _Lp, class... _Rs> struct conjunction<_Lp, _Rs...> : conditional_t<_Lp::value, conjunction<_Rs...>, _Lp> {};
template<class... _Ts> inline constexpr bool conjunction_v = conjunction<_Ts...>::value;

template<class...>                struct disjunction              : false_type                                          {};
template<class _Tp>               struct disjunction<_Tp>         : _Tp                                                 {};
template<class _Lp, class... _Rs> struct disjunction<_Lp, _Rs...> : conditional_t<_Lp::value, _Lp, disjunction<_Rs...>> {};
template<class... _Ts> inline constexpr bool disjunction_v = disjunction<_Ts...>::value;

template<class _Tp> inline constexpr bool negation_v = !bool(_Tp::value);
template<class _Tp> using negation = bool_constant<negation_v<_Tp>>;

//------------------------------------------------------------------------------
// const/volatile removal traits
//------------------------------------------------------------------------------

template<class _Tp> struct remove_const            : type_identity<_Tp> {};
template<class _Tp> struct remove_const<_Tp const> : type_identity<_Tp> {};
template<class _Tp> using remove_const_t = typename remove_const<_Tp>::type;

template<class _Tp> struct remove_volatile               : type_identity<_Tp> {};
template<class _Tp> struct remove_volatile<_Tp volatile> : type_identity<_Tp> {};
template<class _Tp> using remove_volatile_t = typename remove_volatile<_Tp>::type;

template<class _Tp> using remove_cv = remove_const<remove_volatile_t<_Tp>>;
template<class _Tp> using remove_cv_t = typename remove_cv<_Tp>::type;

//------------------------------------------------------------------------------
// primary type categories
//------------------------------------------------------------------------------

#if __has_builtin(__is_void)
template<class _Tp> inline constexpr bool is_void_v = __is_void(_Tp);
template<class _Tp> using is_void = bool_constant<is_void_v<_Tp>>;
#endif

template<class _Tp> using is_null_pointer = is_same<remove_cv_t<_Tp>, nullptr_t>;
template<class _Tp> inline constexpr bool is_null_pointer_v = is_null_pointer<_Tp>::value;

#if __has_builtin(__is_integral)
template<class _Tp> inline constexpr bool is_integral_v = __is_integral(_Tp);
template<class _Tp> using is_integral = bool_constant<is_integral_v<_Tp>>;
#endif

#if __has_builtin(__is_floating_point)
template<class _Tp> inline constexpr bool is_floating_point_v = __is_floating_point(_Tp);
template<class _Tp> using is_floating_point = bool_constant<is_floating_point_v<_Tp>>;
#endif

#if __has_builtin(__is_array)
template<class _Tp> inline constexpr bool is_array_v = __is_array(_Tp);
template<class _Tp> using is_array = bool_constant<is_array_v<_Tp>>;
#endif

#if __has_builtin(__is_enum)
template<class _Tp> inline constexpr bool is_enum_v = __is_enum(_Tp);
template<class _Tp> using is_enum = bool_constant<is_enum_v<_Tp>>;
#endif

#if __has_builtin(__is_union)
template<class _Tp> inline constexpr bool is_union_v = __is_union(_Tp);
template<class _Tp> using is_union = bool_constant<is_union_v<_Tp>>;
#endif

#if __has_builtin(__is_class)
template<class _Tp> inline constexpr bool is_class_v = __is_class(_Tp);
template<class _Tp> using is_class = bool_constant<is_class_v<_Tp>>;
#endif

#if __has_builtin(__is_function)
template<class _Tp> inline constexpr bool is_function_v = __is_function(_Tp);
template<class _Tp> using is_function = bool_constant<is_function_v<_Tp>>;
#endif

#if __has_builtin(__is_pointer)
template<class _Tp> inline constexpr bool is_pointer_v = __is_pointer(_Tp);
template<class _Tp> using is_pointer = bool_constant<is_pointer_v<_Tp>>;
#endif

#if __has_builtin(__is_lvalue_reference)
template<class _Tp> inline constexpr bool is_lvalue_reference_v = __is_lvalue_reference(_Tp);
template<class _Tp> using is_lvalue_reference = bool_constant<is_lvalue_reference_v<_Tp>>;
#endif

#if __has_builtin(__is_rvalue_reference)
template<class _Tp> inline constexpr bool is_rvalue_reference_v = __is_rvalue_reference(_Tp);
template<class _Tp> using is_rvalue_reference = bool_constant<is_rvalue_reference_v<_Tp>>;
#endif

#if __has_builtin(__is_member_object_pointer)
template<class _Tp> inline constexpr bool is_member_object_pointer_v = __is_member_object_pointer(_Tp);
template<class _Tp> using is_member_object_pointer = bool_constant<is_member_object_pointer_v<_Tp>>;
#endif

#if __has_builtin(__is_member_function_pointer)
template<class _Tp> inline constexpr bool is_member_function_pointer_v = __is_member_function_pointer(_Tp);
template<class _Tp> using is_member_function_pointer = bool_constant<is_member_function_pointer_v<_Tp>>;
#endif

//------------------------------------------------------------------------------
// composite type categories
//------------------------------------------------------------------------------

#if __has_builtin(__is_fundamental)
template<class _Tp> inline constexpr bool is_fundamental_v = __is_fundamental(_Tp);
template<class _Tp> using is_fundamental = bool_constant<is_fundamental_v<_Tp>>;
#endif

#if __has_builtin(__is_arithmetic)
template<class _Tp> inline constexpr bool is_arithmetic_v = __is_arithmetic(_Tp);
template<class _Tp> using is_arithmetic = bool_constant<is_arithmetic_v<_Tp>>;
#endif

#if __has_builtin(__is_scalar)
template<class _Tp> inline constexpr bool is_scalar_v = __is_scalar(_Tp);
template<class _Tp> using is_scalar = bool_constant<is_scalar_v<_Tp>>;
#endif

#if __has_builtin(__is_object)
template<class _Tp> inline constexpr bool is_object_v = __is_object(_Tp);
template<class _Tp> using is_object = bool_constant<is_object_v<_Tp>>;
#endif

#if __has_builtin(__is_compound)
template<class _Tp> inline constexpr bool is_compound_v = __is_compound(_Tp);
template<class _Tp> using is_compound = bool_constant<is_compound_v<_Tp>>;
#endif

#if __has_builtin(__is_reference)
template<class _Tp> inline constexpr bool is_reference_v = __is_reference(_Tp);
template<class _Tp> using is_reference = bool_constant<is_reference_v<_Tp>>;
#endif

#if __has_builtin(__is_member_pointer)
template<class _Tp> inline constexpr bool is_member_pointer_v = __is_member_pointer(_Tp);
template<class _Tp> using is_member_pointer = bool_constant<is_member_pointer_v<_Tp>>;
#endif

//------------------------------------------------------------------------------
// type properties
//------------------------------------------------------------------------------

#if __has_builtin(__is_const)
template<class _Tp> inline constexpr bool is_const_v = __is_const(_Tp);
template<class _Tp> using is_const = bool_constant<is_const_v<_Tp>>;
#endif

#if __has_builtin(__is_volatile)
template<class _Tp> inline constexpr bool is_volatile_v = __is_volatile(_Tp);
template<class _Tp> using is_volatile = bool_constant<is_volatile_v<_Tp>>;
#endif

#if __has_builtin(__is_trivial)
template<class _Tp> inline constexpr bool is_trivial_v = __is_trivial(_Tp);
template<class _Tp> using is_trivial = bool_constant<is_trivial_v<_Tp>>;
#endif

#if __has_builtin(__is_trivially_copyable)
template<class _Tp> inline constexpr bool is_trivially_copyable_v = __is_trivially_copyable(_Tp);
template<class _Tp> using is_trivially_copyable = bool_constant<is_trivially_copyable_v<_Tp>>;
#endif

#if __has_builtin(__is_standard_layout)
template<class _Tp> inline constexpr bool is_standard_layout_v = __is_standard_layout(_Tp);
template<class _Tp> using is_standard_layout = bool_constant<is_standard_layout_v<_Tp>>;
#endif

#if __has_builtin(__is_pod)
template<class _Tp> inline constexpr bool is_pod_v = __is_pod(_Tp);
template<class _Tp> using is_pod = bool_constant<is_pod_v<_Tp>>;
#endif

#if __has_builtin(__is_literal_type)
template<class _Tp> inline constexpr bool is_literal_type_v = __is_literal_type(_Tp);
template<class _Tp> using is_literal_type = bool_constant<is_literal_type_v<_Tp>>;
#endif

#if __has_builtin(__has_unique_object_representations)
template<class _Tp> inline constexpr bool has_unique_object_representations_v = __has_unique_object_representations(_Tp);
template<class _Tp> using has_unique_object_representations = bool_constant<has_unique_object_representations_v<_Tp>>;
#endif

#if __has_builtin(__is_empty)
template<class _Tp> inline constexpr bool is_empty_v = __is_empty(_Tp);
template<class _Tp> using is_empty = bool_constant<is_empty_v<_Tp>>;
#endif

#if __has_builtin(__is_polymorphic)
template<class _Tp> inline constexpr bool is_polymorphic_v = __is_polymorphic(_Tp);
template<class _Tp> using is_polymorphic = bool_constant<is_polymorphic_v<_Tp>>;
#endif

#if __has_builtin(__is_abstract)
template<class _Tp> inline constexpr bool is_abstract_v = __is_abstract(_Tp);
template<class _Tp> using is_abstract = bool_constant<is_abstract_v<_Tp>>;
#endif

#if __has_builtin(__is_final)
template<class _Tp> inline constexpr bool is_final_v = __is_final(_Tp);
template<class _Tp> using is_final = bool_constant<is_final_v<_Tp>>;
#endif

#if __has_builtin(__is_aggregate)
template<class _Tp> inline constexpr bool is_aggregate_v = __is_aggregate(_Tp);
template<class _Tp> using is_aggregate = bool_constant<is_aggregate_v<_Tp>>;
#endif

#if __has_builtin(__is_signed)
template<class _Tp> inline constexpr bool is_signed_v = __is_signed(_Tp);
template<class _Tp> using is_signed = bool_constant<is_signed_v<_Tp>>;
#endif

#if __has_builtin(__is_unsigned)
template<class _Tp> inline constexpr bool is_unsigned_v = __is_unsigned(_Tp);
template<class _Tp> using is_unsigned = bool_constant<is_unsigned_v<_Tp>>;
#endif

//------------------------------------------------------------------------------
// const/volatile addition traits
//------------------------------------------------------------------------------

template<class _Tp> using add_const =
    conditional<disjunction_v<is_reference<_Tp>, is_function<_Tp>, is_const<_Tp>>, _Tp, _Tp const>;
template<class _Tp> using add_const_t = typename add_const<_Tp>::type;

template<class _Tp> using add_volatile =
    conditional<disjunction_v<is_reference<_Tp>, is_function<_Tp>, is_volatile<_Tp>>, _Tp, _Tp volatile>;
template<class _Tp> using add_volatile_t = typename add_volatile<_Tp>::type;

template<class _Tp> using add_cv = add_const<add_volatile_t<_Tp>>;
template<class _Tp> using add_cv_t = typename add_cv<_Tp>::type;

//------------------------------------------------------------------------------
// reference/pointer transformation traits
//------------------------------------------------------------------------------

template<class _Tp> struct remove_pointer                      : type_identity<_Tp> {};
template<class _Tp> struct remove_pointer<_Tp*>                : type_identity<_Tp> {};
template<class _Tp> struct remove_pointer<_Tp* const>          : type_identity<_Tp> {};
template<class _Tp> struct remove_pointer<_Tp*       volatile> : type_identity<_Tp> {};
template<class _Tp> struct remove_pointer<_Tp* const volatile> : type_identity<_Tp> {};
template<class _Tp> using remove_pointer_t = typename remove_pointer<_Tp>::type;

template<class _Tp> struct remove_reference        : type_identity<_Tp> {};
template<class _Tp> struct remove_reference<_Tp&>  : type_identity<_Tp> {};
template<class _Tp> struct remove_reference<_Tp&&> : type_identity<_Tp> {};
template<class _Tp> using remove_reference_t = typename remove_reference<_Tp>::type;

template<class _Tp> using __remove_cvref_t = typename remove_cv<typename remove_reference<_Tp>::type>::type;
template<class _Tp> struct remove_cvref { using type = __remove_cvref_t<_Tp>; };
template<class _Tp> using remove_cvref_t = typename remove_cvref<_Tp>::type;

template<class _Tp> auto __ezcxx_add_pointer(int) -> type_identity<_Tp*>;
template<class _Tp> auto __ezcxx_add_pointer(...) -> type_identity<_Tp>;
template<class _Tp> using add_pointer = decltype(__ezcxx_add_pointer<_Tp>(0));
template<class _Tp> using add_pointer_t = typename add_pointer<_Tp>::type;

template<class _Tp> auto __ezcxx_add_lvalue_reference(int) -> type_identity<_Tp&>;
template<class _Tp> auto __ezcxx_add_lvalue_reference(...) -> type_identity<_Tp>;
template<class _Tp> using add_lvalue_reference = decltype(__ezcxx_add_lvalue_reference<_Tp>(0));
template<class _Tp> using add_lvalue_reference_t = typename add_lvalue_reference<_Tp>::type;

template<class _Tp> auto __ezcxx_add_rvalue_reference(int) -> type_identity<_Tp&&>;
template<class _Tp> auto __ezcxx_add_rvalue_reference(...) -> type_identity<_Tp>;
template<class _Tp> using add_rvalue_reference = decltype(__ezcxx_add_rvalue_reference<_Tp>(0));
template<class _Tp> using add_rvalue_reference_t = typename add_rvalue_reference<_Tp>::type;

//------------------------------------------------------------------------------
// property queries
//------------------------------------------------------------------------------

// alignment_of:
template<class _Tp> struct alignment_of
    : public integral_constant<size_t, alignof(_Tp)> {};
template<class _Tp> inline constexpr size_t alignment_of_v = alignment_of<_Tp>::value;

// rank/extent:
template<class _Tp> struct rank
    : public integral_constant<size_t, 0> {};
template<class _Tp> struct rank<_Tp[]>
    : public integral_constant<size_t, rank<_Tp>::value + 1> {};
template<class _Tp, size_t _Np> struct rank<_Tp[_Np]>
    : public integral_constant<size_t, rank<_Tp>::value + 1> {};
template<class _Tp> inline constexpr size_t rank_v = rank<_Tp>::value;

template<class _Tp, unsigned _Ip = 0> struct extent
    : public integral_constant<size_t, 0> {};
template<class _Tp> struct extent<_Tp[], 0>
    : public integral_constant<size_t, 0> {};
template<class _Tp, unsigned _Ip> struct extent<_Tp[], _Ip>
    : public integral_constant<size_t, extent<_Tp, _Ip-1>::value> {};
template<class _Tp, size_t _Np> struct extent<_Tp[_Np], 0>
    : public integral_constant<size_t, _Np> {};
template<class _Tp, size_t _Np, unsigned _Ip> struct extent<_Tp[_Np], _Ip>
    : public integral_constant<size_t, extent<_Tp, _Ip-1>::value> {};
template<class _Tp, unsigned _Ip = 0> inline constexpr size_t extent_v = extent<_Tp, _Ip>::value;

template<class _Tp> struct remove_extent { typedef _Tp type; };
template<class _Tp> struct remove_extent<_Tp[]> { typedef _Tp type; };
template<class _Tp, size_t _Np> struct remove_extent<_Tp[_Np]> { typedef _Tp type; };
template<class _Tp> using remove_extent_t = typename remove_extent<_Tp>::type;

template<class _Tp> struct remove_all_extents { typedef _Tp type; };
template<class _Tp> struct remove_all_extents<_Tp[]> { typedef typename remove_all_extents<_Tp>::type type; };
template<class _Tp, size_t _Np> struct remove_all_extents<_Tp[_Np]> { typedef typename remove_all_extents<_Tp>::type type; };
template<class _Tp> using remove_all_extents_t = typename remove_all_extents<_Tp>::type;

template<class _Tp>
struct is_bounded_array: false_type {};
template<class _Tp, size_t _N>
struct is_bounded_array<_Tp[_N]> : true_type {};
template<class _Tp> inline constexpr bool is_bounded_array_v = is_bounded_array<_Tp>::value;

template<class _Tp>
struct is_unbounded_array: false_type {};
template<class _Tp>
struct is_unbounded_array<_Tp[]> : true_type {};
template<class _Tp> inline constexpr bool is_unbounded_array_v = is_unbounded_array<_Tp>::value;

// decay:
template<class _Tp>
struct decay {
private:
    typedef typename std::remove_reference<_Tp>::type _Up;
public:
    typedef typename std::conditional<
        std::is_array<_Up>::value,
        typename std::add_pointer<typename std::remove_extent<_Up>::type>::type,
        typename std::conditional<
            std::is_function<_Up>::value,
            typename std::add_pointer<_Up>::type,
            typename std::remove_cv<_Up>::type
        >::type
    >::type type;
};
template<class _Tp> using decay_t = typename decay<_Tp>::type;

// underlying_type:

#if __has_builtin(__underlying_type)
template<class _Tp, bool = is_enum<_Tp>::value> struct __underlying_type_impl;
template<class _Tp> struct __underlying_type_impl<_Tp, false> {};
template<class _Tp> struct __underlying_type_impl<_Tp, true> { typedef __underlying_type(_Tp) type; };

template<class _Tp> struct underlying_type  : __underlying_type_impl<_Tp, is_enum<_Tp>::value> {};
template<class _Tp> using underlying_type_t = typename underlying_type<_Tp>::type;
#endif

// <utility> functions:
template<class _Tp> add_rvalue_reference_t<_Tp> declval() noexcept;

//------------------------------------------------------------------------------
// member classification traits
//------------------------------------------------------------------------------

template<class _Tp, class... _As> auto __constructible(int) -> __require<decltype(_Tp(declval<_As>()...))>;
template<class, class...>         auto __constructible(...) -> false_type;
template<class _Tp, class... _As> using is_constructible = decltype(__constructible<_Tp, _As...>(0));
template<class _Tp, class... _As> inline constexpr bool is_constructible_v = is_constructible<_Tp, _As...>::value;

#if __has_builtin(__is_trivially_constructible)
template<class _Tp, class... _As> inline constexpr bool is_trivially_constructible_v = __is_trivially_constructible(_Tp, _As...);
template<class _Tp, class... _As> using is_trivially_constructible = bool_constant<is_trivially_constructible_v<_Tp, _As...>>;
#endif

template<class _Tp, class... _As> auto __nt_constructible(int) -> enable_if_t<noexcept(_Tp(declval<_As>()...)), true_type>;
template<class, class...>         auto __nt_constructible(...) -> false_type;
template<class _Tp, class... _As> using is_nothrow_constructible = decltype(__nt_constructible<_Tp, _As...>(0));
template<class _Tp, class... _As> inline constexpr bool is_nothrow_constructible_v = is_nothrow_constructible<_Tp, _As...>::value;

template<class _Tp> using is_default_constructible = is_constructible<_Tp>;
template<class _Tp> inline constexpr bool is_default_constructible_v = is_default_constructible<_Tp>::value;

template<class _Tp> using is_trivially_default_constructible = is_trivially_constructible<_Tp>;
template<class _Tp> inline constexpr bool is_trivially_default_constructible_v = is_trivially_default_constructible<_Tp>::value;

template<class _Tp> using is_nothrow_default_constructible = is_nothrow_constructible<_Tp>;
template<class _Tp> inline constexpr bool is_nothrow_default_constructible_v = is_nothrow_default_constructible<_Tp>::value;

template<class _Tp> using is_copy_constructible = is_constructible<_Tp, add_lvalue_reference_t<add_const_t<_Tp>>>;
template<class _Tp> inline constexpr bool is_copy_constructible_v = is_copy_constructible<_Tp>::value;

template<class _Tp> using is_trivially_copy_constructible = is_trivially_constructible<_Tp, add_lvalue_reference_t<add_const_t<_Tp>>>;
template<class _Tp> inline constexpr bool is_trivially_copy_constructible_v = is_trivially_copy_constructible<_Tp>::value;

template<class _Tp> using is_nothrow_copy_constructible = is_nothrow_constructible<_Tp, add_lvalue_reference_t<add_const_t<_Tp>>>;
template<class _Tp> inline constexpr bool is_nothrow_copy_constructible_v = is_nothrow_copy_constructible<_Tp>::value;

template<class _Tp> using is_move_constructible = is_constructible<_Tp, add_rvalue_reference_t<_Tp>>;
template<class _Tp> inline constexpr bool is_move_constructible_v = is_move_constructible<_Tp>::value;

template<class _Tp> using is_trivially_move_constructible = is_trivially_constructible<_Tp, add_rvalue_reference_t<_Tp>>;
template<class _Tp> inline constexpr bool is_trivially_move_constructible_v = is_trivially_move_constructible<_Tp>::value;

template<class _Tp> using is_nothrow_move_constructible = is_nothrow_constructible<_Tp, add_rvalue_reference_t<_Tp>>;
template<class _Tp> inline constexpr bool is_nothrow_move_constructible_v = is_nothrow_move_constructible<_Tp>::value;

template<class _Lp, class _Rp> auto __assignable(int) -> __require<decltype(declval<_Lp>() = declval<_Rp>())>;
template<class, class>         auto __assignable(...) -> false_type;
template<class _Lp, class _Rp> using is_assignable = decltype(__assignable<_Lp, _Rp>(0));
template<class _Lp, class _Rp> inline constexpr bool is_assignable_v = is_assignable<_Lp, _Rp>::value;

#if __has_builtin(__is_trivially_assignable)
template<class _Lp, class _Rp> inline constexpr bool is_trivially_assignable_v = __is_trivially_assignable(_Lp, _Rp);
template<class _Lp, class _Rp> using is_trivially_assignable = bool_constant<is_trivially_assignable_v<_Lp, _Rp>>;
#endif

template<class _Lp, class _Rp> auto __nt_assignable(int) -> enable_if_t<noexcept(declval<_Lp>() = declval<_Rp>()), true_type>;
template<class, class>         auto __nt_assignable(...) -> false_type;
template<class _Lp, class _Rp> using is_nothrow_assignable = decltype(__nt_assignable<_Lp, _Rp>(0));
template<class _Lp, class _Rp> inline constexpr bool is_nothrow_assignable_v = is_nothrow_assignable<_Lp, _Rp>::value;

template<class _Tp> using is_copy_assignable =
    is_assignable<add_lvalue_reference_t<_Tp>, add_lvalue_reference_t<add_const_t<_Tp>>>;
template<class _Tp> inline constexpr bool is_copy_assignable_v = is_copy_assignable<_Tp>::value;

template<class _Tp> struct is_trivially_copy_assignable
    : public is_trivially_assignable<add_lvalue_reference_t<_Tp>, add_lvalue_reference_t<add_const_t<_Tp>>> {};
template<class _Tp> inline constexpr bool is_trivially_copy_assignable_v = is_trivially_copy_assignable<_Tp>::value;

template<class _Tp> using is_nothrow_copy_assignable =
    is_nothrow_assignable<add_lvalue_reference_t<_Tp>, add_lvalue_reference_t<add_const_t<_Tp>>>;
template<class _Tp> inline constexpr bool is_nothrow_copy_assignable_v = is_nothrow_copy_assignable<_Tp>::value;

template<class _Tp> using is_move_assignable =
    is_assignable<add_lvalue_reference_t<_Tp>, add_rvalue_reference_t<_Tp>>;
template<class _Tp> inline constexpr bool is_move_assignable_v = is_move_assignable<_Tp>::value;

template<class _Tp> struct is_trivially_move_assignable
    : public is_trivially_assignable<add_lvalue_reference_t<_Tp>, add_rvalue_reference_t<_Tp>> {};
template<class _Tp> inline constexpr bool is_trivially_move_assignable_v = is_trivially_move_assignable<_Tp>::value;

template<class _Tp> using is_nothrow_move_assignable =
    is_nothrow_assignable<add_lvalue_reference_t<_Tp>, add_rvalue_reference_t<_Tp>>;
template<class _Tp> inline constexpr bool is_nothrow_move_assignable_v = is_nothrow_move_assignable<_Tp>::value;

/* Clang 16.0.0 required */
#if 0
#if __has_builtin(__is_destructible)
template<class _Tp> inline constexpr bool is_destructible_v = __is_destructible(_Tp);
template<class _Tp> using is_destructible = bool_constant<is_destructible_v<_Tp>>;
#endif
#elif __cplusplus >= 201907L
template<class _Tp> struct is_destructible
    : std::integral_constant<bool, requires(_Tp _Object) { _Object.~_Tp(); }>
{};
template<class _Tp> inline constexpr bool is_destructible_v = is_destructible<_Tp>::value;
#endif

#if __has_builtin(__is_trivially_destructible)
template<class _Tp> inline constexpr bool is_trivially_destructible_v = __is_trivially_destructible(_Tp);
template<class _Tp> using is_trivially_destructible = bool_constant<is_trivially_destructible_v<_Tp>>;
#endif

/* Clang 16.0.0 required */
#if 0
#if __has_builtin(__is_nothrow_destructible)
template<class _Tp> inline constexpr bool is_nothrow_destructible_v = __is_nothrow_destructible(_Tp);
template<class _Tp> using is_nothrow_destructible = bool_constant<is_nothrow_destructible_v<_Tp>>;
#endif
#elif __cplusplus >= 201907L
template<class _Tp> struct is_nothrow_destructible
    : std::integral_constant<bool, requires(_Tp _Object) { {_Object.~_Tp()} noexcept; }>
{};
template<class _Tp> inline constexpr bool is_nothrow_destructible_v = is_nothrow_destructible<_Tp>::value;
#endif

#if __has_builtin(__has_virtual_destructor)
template<class _Tp> inline constexpr bool has_virtual_destructor_v = __has_virtual_destructor(_Tp);
template<class _Tp> using has_virtual_destructor = bool_constant<has_virtual_destructor_v<_Tp>>;
#endif

// member relations:

inline constexpr bool is_constant_evaluated() noexcept {
    return __builtin_is_constant_evaluated();
}

//------------------------------------------------------------------------------
// internal utilities
//------------------------------------------------------------------------------

// copy_cv

template <class _From>
struct __copy_cv {
    template <class _To>
    using __apply _EZCXX_NODEBUG = _To;
};

template <class _From>
struct __copy_cv<const _From> {
    template <class _To>
    using __apply _EZCXX_NODEBUG = const _To;
};

template <class _From>
struct __copy_cv<volatile _From> {
    template <class _To>
    using __apply _EZCXX_NODEBUG = volatile _To;
};

template <class _From>
struct __copy_cv<const volatile _From> {
    template <class _To>
    using __apply _EZCXX_NODEBUG = const volatile _To;
};

template <class _From, class _To>
using __copy_cv_t _EZCXX_NODEBUG = typename __copy_cv<_From>::template __apply<_To>;

// copy_cvref

template <class _From>
struct __copy_cvref {
    template <class _To>
    using __apply _EZCXX_NODEBUG = __copy_cv_t<_From, _To>;
};

template <class _From>
struct __copy_cvref<_From&> {
    template <class _To>
    using __apply _EZCXX_NODEBUG = add_lvalue_reference_t<__copy_cv_t<_From, _To> >;
};

template <class _From>
struct __copy_cvref<_From&&> {
    template <class _To>
    using __apply _EZCXX_NODEBUG = add_rvalue_reference_t<__copy_cv_t<_From, _To> >;
};

template <class _From, class _To>
using __copy_cvref_t _EZCXX_NODEBUG = typename __copy_cvref<_From>::template __apply<_To>;

// type_list

template <class... _Types>
struct __type_list {};

template <class>
struct __type_list_head;

template <class _Head, class... _Tail>
struct __type_list_head<__type_list<_Head, _Tail...> > {
    using type _EZCXX_NODEBUG = _Head;
};

template <class _TypeList, size_t _Size, bool = _Size <= sizeof(typename __type_list_head<_TypeList>::type)>
struct __find_first;

template <class _Head, class... _Tail, size_t _Size>
struct __find_first<__type_list<_Head, _Tail...>, _Size, true> {
    using type _EZCXX_NODEBUG = _Head;
};

template <class _Head, class... _Tail, size_t _Size>
struct __find_first<__type_list<_Head, _Tail...>, _Size, false> {
    using type _EZCXX_NODEBUG = typename __find_first<__type_list<_Tail...>, _Size>::type;
};

// nat

struct __nat {
    __nat()                        = delete;
    __nat(const __nat&)            = delete;
    __nat& operator=(const __nat&) = delete;
    ~__nat()                       = delete;
};

// promote

template <class... _Args>
class __promote {
    static_assert((is_arithmetic<_Args>::value && ...));

    static float __test(float);
    static double __test(char);
    static double __test(int);
    static double __test(unsigned);
    static double __test(long);
    static double __test(unsigned long);
    static double __test(signed __int48);
    static double __test(unsigned __int48);
    static double __test(long long);
    static double __test(unsigned long long);
    static double __test(double);
    static long double __test(long double);

public:
    using type = decltype((__test(_Args()) + ...));
};

//------------------------------------------------------------------------------
// make_signed make_unsigned
//------------------------------------------------------------------------------

using __signed_types =
__type_list<
    signed char,
    signed short,
    signed int,
    signed long,
    signed __int48,
    signed long long
>;

using __unsigned_types =
__type_list<
    unsigned char,
    unsigned short,
    unsigned int,
    unsigned long,
    unsigned __int48,
    unsigned long long
>;

template <class _Tp, bool = is_integral<_Tp>::value || is_enum<_Tp>::value>
struct __ezcxx_make_signed{};

template <class _Tp>
struct __ezcxx_make_signed<_Tp, true> {
typedef typename __find_first<__signed_types, sizeof(_Tp)>::type type;
};

template <class _Tp, bool = is_integral<_Tp>::value || is_enum<_Tp>::value>
struct __ezcxx_make_unsigned{};

template <class _Tp>
struct __ezcxx_make_unsigned<_Tp, true> {
    typedef typename __find_first<__unsigned_types, sizeof(_Tp)>::type type;
};

template <> struct __ezcxx_make_unsigned<bool,               true> {};
template <> struct __ezcxx_make_unsigned<  signed short,     true> {typedef unsigned short     type;};
template <> struct __ezcxx_make_unsigned<unsigned short,     true> {typedef unsigned short     type;};
template <> struct __ezcxx_make_unsigned<  signed int,       true> {typedef unsigned int       type;};
template <> struct __ezcxx_make_unsigned<unsigned int,       true> {typedef unsigned int       type;};
template <> struct __ezcxx_make_unsigned<  signed long,      true> {typedef unsigned long      type;};
template <> struct __ezcxx_make_unsigned<unsigned long,      true> {typedef unsigned long      type;};
template <> struct __ezcxx_make_unsigned<  signed __int48,   true> {typedef unsigned __int48   type;};
template <> struct __ezcxx_make_unsigned<unsigned __int48,   true> {typedef unsigned __int48   type;};
template <> struct __ezcxx_make_unsigned<  signed long long, true> {typedef unsigned long long type;};
template <> struct __ezcxx_make_unsigned<unsigned long long, true> {typedef unsigned long long type;};

template <> struct __ezcxx_make_signed<bool,               true> {};
template <> struct __ezcxx_make_signed<  signed short,     true> {typedef short     type;};
template <> struct __ezcxx_make_signed<unsigned short,     true> {typedef short     type;};
template <> struct __ezcxx_make_signed<  signed int,       true> {typedef int       type;};
template <> struct __ezcxx_make_signed<unsigned int,       true> {typedef int       type;};
template <> struct __ezcxx_make_signed<  signed long,      true> {typedef long      type;};
template <> struct __ezcxx_make_signed<unsigned long,      true> {typedef long      type;};
template <> struct __ezcxx_make_signed<  signed __int48,   true> {typedef __int48   type;};
template <> struct __ezcxx_make_signed<unsigned __int48,   true> {typedef __int48   type;};
template <> struct __ezcxx_make_signed<  signed long long, true> {typedef long long type;};
template <> struct __ezcxx_make_signed<unsigned long long, true> {typedef long long type;};

template <class _Tp>
using __make_signed_t = __copy_cv_t<_Tp, typename __ezcxx_make_signed<remove_cv_t<_Tp> >::type>;

template <class _Tp>
using __make_unsigned_t = __copy_cv_t<_Tp, typename __ezcxx_make_unsigned<remove_cv_t<_Tp> >::type>;

template <class _Tp>
struct make_signed {
using type _EZCXX_NODEBUG = __make_signed_t<_Tp>;
};

template <class _Tp>
struct make_unsigned {
using type _EZCXX_NODEBUG = __make_unsigned_t<_Tp>;
};

template <class _Tp> using make_signed_t = __make_signed_t<_Tp>;

template <class _Tp> using make_unsigned_t = __make_unsigned_t<_Tp>;

//------------------------------------------------------------------------------
// common_type
//------------------------------------------------------------------------------

#if __cplusplus >= 201907L
// Let COND_RES(X, Y) be:
template <class _Tp, class _Up>
using __cond_type _EZCXX_NODEBUG = decltype(false ? std::declval<_Tp>() : std::declval<_Up>());

template <class _Tp, class _Up, class = void>
struct __common_type3 {};

// sub-bullet 4 - "if COND_RES(CREF(D1), CREF(D2)) denotes a type..."
template <class _Tp, class _Up>
struct __common_type3<_Tp, _Up, void_t<__cond_type<const _Tp&, const _Up&>>> {
    using type _EZCXX_NODEBUG = remove_cvref_t<__cond_type<const _Tp&, const _Up&>>;
};

template <class _Tp, class _Up, class = void>
struct __common_type2_imp : __common_type3<_Tp, _Up> {};
#else // __cplusplus >= 201907L
template <class _Tp, class _Up, class = void>
struct __common_type2_imp {};
#endif // __cplusplus >= 201907L

template <class _Tp, class _Up>
struct __common_type2_imp<_Tp, _Up, void_t<decltype(true ? std::declval<_Tp>() : std::declval<_Up>())> > {
    using type _EZCXX_NODEBUG = decay_t<decltype(true ? std::declval<_Tp>() : std::declval<_Up>())>;
};

template <class, class = void>
struct __common_type_impl {};

template <class... _Tp>
struct __common_types;
template <class... _Tp>
struct _EZCXX_TEMPLATE_VIS common_type;

template <class _Tp, class _Up>
struct __common_type_impl< __common_types<_Tp, _Up>, void_t<typename common_type<_Tp, _Up>::type> > {
    typedef typename common_type<_Tp, _Up>::type type;
};

template <class _Tp, class _Up, class _Vp, class... _Rest>
struct __common_type_impl<__common_types<_Tp, _Up, _Vp, _Rest...>, void_t<typename common_type<_Tp, _Up>::type> >
    : __common_type_impl<__common_types<typename common_type<_Tp, _Up>::type, _Vp, _Rest...> > {};

template <>
struct _EZCXX_TEMPLATE_VIS common_type<> {};

template <class _Tp>
struct _EZCXX_TEMPLATE_VIS common_type<_Tp> : public common_type<_Tp, _Tp> {};

template <class _Tp, class _Up>
struct _EZCXX_TEMPLATE_VIS common_type<_Tp, _Up>
    : conditional_t<is_same<_Tp, decay_t<_Tp> >::value && is_same<_Up, decay_t<_Up> >::value,
    __common_type2_imp<_Tp, _Up>, common_type<decay_t<_Tp>, decay_t<_Up> > > {};

template <class _Tp, class _Up, class _Vp, class... _Rest>
struct _EZCXX_TEMPLATE_VIS common_type<_Tp, _Up, _Vp, _Rest...>
    : __common_type_impl<__common_types<_Tp, _Up, _Vp, _Rest...> > {};

template <class... _Tp> using common_type_t = typename common_type<_Tp...>::type;

//------------------------------------------------------------------------------
// common_reference basic_common_reference
//------------------------------------------------------------------------------

#if __cplusplus >= 201907L

// Let COND_RES(X, Y) be:
template <class _Xp, class _Yp>
using __cond_res _EZCXX_NODEBUG = decltype(false ? std::declval<_Xp (&)()>()() : std::declval<_Yp (&)()>()());

// Let `XREF(A)` denote a unary alias template `T` such that `T<U>` denotes the same type as `U`
// with the addition of `A`'s cv and reference qualifiers, for a non-reference cv-unqualified type
// `U`.
// [Note: `XREF(A)` is `__xref<A>::template __apply`]
template <class _Tp>
struct __xref {
    template <class _Up>
    using __apply _EZCXX_NODEBUG = __copy_cvref_t<_Tp, _Up>;
};

// Given types A and B, let X be remove_reference_t<A>, let Y be remove_reference_t<B>,
// and let COMMON-REF(A, B) be:
template <class _Ap, class _Bp, class _Xp = remove_reference_t<_Ap>, class _Yp = remove_reference_t<_Bp>>
struct __common_ref;

template <class _Xp, class _Yp>
using __common_ref_t _EZCXX_NODEBUG = typename __common_ref<_Xp, _Yp>::__type;

template <class _Xp, class _Yp>
using __cv_cond_res _EZCXX_NODEBUG = __cond_res<__copy_cv_t<_Xp, _Yp>&, __copy_cv_t<_Yp, _Xp>&>;

//    If A and B are both lvalue reference types, COMMON-REF(A, B) is
//    COND-RES(COPYCV(X, Y)&, COPYCV(Y, X)&) if that type exists and is a reference type.
// clang-format off
template <class _Ap, class _Bp, class _Xp, class _Yp>
    requires requires { typename __cv_cond_res<_Xp, _Yp>; } &&
    is_reference_v<__cv_cond_res<_Xp, _Yp>>
struct __common_ref<_Ap&, _Bp&, _Xp, _Yp> {
    using __type _EZCXX_NODEBUG = __cv_cond_res<_Xp, _Yp>;
};
// clang-format on

//    Otherwise, let C be remove_reference_t<COMMON-REF(X&, Y&)>&&. ...
template <class _Xp, class _Yp>
using __common_ref_C _EZCXX_NODEBUG = remove_reference_t<__common_ref_t<_Xp&, _Yp&>>&&;

//    .... If A and B are both rvalue reference types, C is well-formed, and
//    is_convertible_v<A, C> && is_convertible_v<B, C> is true, then COMMON-REF(A, B) is C.
// clang-format off
template <class _Ap, class _Bp, class _Xp, class _Yp>
    requires requires { typename __common_ref_C<_Xp, _Yp>; } &&
    is_convertible_v<_Ap&&, __common_ref_C<_Xp, _Yp>> &&
    is_convertible_v<_Bp&&, __common_ref_C<_Xp, _Yp>>
struct __common_ref<_Ap&&, _Bp&&, _Xp, _Yp> {
    using __type _EZCXX_NODEBUG = __common_ref_C<_Xp, _Yp>;
};
// clang-format on

//    Otherwise, let D be COMMON-REF(const X&, Y&). ...
template <class _Tp, class _Up>
using __common_ref_D _EZCXX_NODEBUG = __common_ref_t<const _Tp&, _Up&>;

//    ... If A is an rvalue reference and B is an lvalue reference and D is well-formed and
//    is_convertible_v<A, D> is true, then COMMON-REF(A, B) is D.
// clang-format off
template <class _Ap, class _Bp, class _Xp, class _Yp>
    requires requires { typename __common_ref_D<_Xp, _Yp>; } &&
    is_convertible_v<_Ap&&, __common_ref_D<_Xp, _Yp>>
struct __common_ref<_Ap&&, _Bp&, _Xp, _Yp> {
    using __type _EZCXX_NODEBUG = __common_ref_D<_Xp, _Yp>;
};
// clang-format on

//    Otherwise, if A is an lvalue reference and B is an rvalue reference, then
//    COMMON-REF(A, B) is COMMON-REF(B, A).
template <class _Ap, class _Bp, class _Xp, class _Yp>
struct __common_ref<_Ap&, _Bp&&, _Xp, _Yp> : __common_ref<_Bp&&, _Ap&> {};

//    Otherwise, COMMON-REF(A, B) is ill-formed.
template <class _Ap, class _Bp, class _Xp, class _Yp>
struct __common_ref {};

// Note C: For the common_reference trait applied to a parameter pack [...]

template <class...>
struct common_reference;

template <class... _Types>
using common_reference_t = typename common_reference<_Types...>::type;

// bullet 1 - sizeof...(T) == 0
template <>
struct common_reference<> {};

// bullet 2 - sizeof...(T) == 1
template <class _Tp>
struct common_reference<_Tp> {
    using type _EZCXX_NODEBUG = _Tp;
};

// bullet 3 - sizeof...(T) == 2
template <class _Tp, class _Up>
struct __common_reference_sub_bullet3;
template <class _Tp, class _Up>
struct __common_reference_sub_bullet2 : __common_reference_sub_bullet3<_Tp, _Up> {};
template <class _Tp, class _Up>
struct __common_reference_sub_bullet1 : __common_reference_sub_bullet2<_Tp, _Up> {};

// sub-bullet 1 - If T1 and T2 are reference types and COMMON-REF(T1, T2) is well-formed, then
// the member typedef `type` denotes that type.
template <class _Tp, class _Up>
struct common_reference<_Tp, _Up> : __common_reference_sub_bullet1<_Tp, _Up> {};

template <class _Tp, class _Up>
    requires is_reference_v<_Tp> && is_reference_v<_Up> && requires { typename __common_ref_t<_Tp, _Up>; }
struct __common_reference_sub_bullet1<_Tp, _Up> {
    using type _EZCXX_NODEBUG = __common_ref_t<_Tp, _Up>;
};

// sub-bullet 2 - Otherwise, if basic_common_reference<remove_cvref_t<T1>, remove_cvref_t<T2>, XREF(T1), XREF(T2)>::type
// is well-formed, then the member typedef `type` denotes that type.
template <class, class, template <class> class, template <class> class>
struct basic_common_reference {};

template <class _Tp, class _Up>
using __basic_common_reference_t _EZCXX_NODEBUG =
    typename basic_common_reference<
        remove_cvref_t<_Tp>,
        remove_cvref_t<_Up>,
        __xref<_Tp>::template __apply,
        __xref<_Up>::template __apply
    >::type;

template <class _Tp, class _Up>
    requires requires { typename __basic_common_reference_t<_Tp, _Up>; }
struct __common_reference_sub_bullet2<_Tp, _Up> {
    using type _EZCXX_NODEBUG = __basic_common_reference_t<_Tp, _Up>;
};

// sub-bullet 3 - Otherwise, if COND-RES(T1, T2) is well-formed,
// then the member typedef `type` denotes that type.
template <class _Tp, class _Up>
    requires requires { typename __cond_res<_Tp, _Up>; }
struct __common_reference_sub_bullet3<_Tp, _Up> {
    using type _EZCXX_NODEBUG = __cond_res<_Tp, _Up>;
};

// sub-bullet 4 & 5 - Otherwise, if common_type_t<T1, T2> is well-formed,
//                    then the member typedef `type` denotes that type.
//                  - Otherwise, there shall be no member `type`.
template <class _Tp, class _Up>
struct __common_reference_sub_bullet3 : common_type<_Tp, _Up> {};

// bullet 4 - If there is such a type `C`, the member typedef type shall denote the same type, if
//            any, as `common_reference_t<C, Rest...>`.
template <class _Tp, class _Up, class _Vp, class... _Rest>
    requires requires { typename common_reference_t<_Tp, _Up>; }
struct common_reference<_Tp, _Up, _Vp, _Rest...> : common_reference<common_reference_t<_Tp, _Up>, _Vp, _Rest...> {};

// bullet 5 - Otherwise, there shall be no member `type`.
template <class...>
struct common_reference {};

#endif // __cplusplus >= 201907L

//------------------------------------------------------------------------------
// move swap
//------------------------------------------------------------------------------

template <class _Tp>
_EZCXX_NODISCARD _EZCXX_INLINE constexpr remove_reference_t<_Tp>&&
move(_EZCXX_LIFETIMEBOUND _Tp&& __t) noexcept {
    using _Up _EZCXX_NODEBUG = remove_reference_t<_Tp>;
    return static_cast<_Up&&>(__t);
}

template <class _Tp>
_EZCXX_NODISCARD _EZCXX_INLINE constexpr
conditional_t<!is_nothrow_move_constructible<_Tp>::value && is_copy_constructible<_Tp>::value, const _Tp&, _Tp&&>
move_if_noexcept(_EZCXX_LIFETIMEBOUND _Tp& __x) noexcept {
    return std::move(__x);
}

template <class _Tp>
_EZCXX_INLINE
#if __cplusplus >= 201402L
constexpr
#endif
enable_if_t<is_move_constructible<_Tp>::value && is_move_assignable<_Tp>::value>
swap(_Tp& __x, _Tp& __y)
noexcept(is_nothrow_move_constructible<_Tp>::value && is_nothrow_move_assignable<_Tp>::value) {
    _Tp __t(std::move(__x));
    __x = std::move(__y);
    __y = std::move(__t);
}

//------------------------------------------------------------------------------
// swappable classification traits
//------------------------------------------------------------------------------

template <class _Tp, class _Up, class = void>
inline const bool __is_swappable_with_v = false;

template <class _Tp>
inline const bool __is_swappable_v = __is_swappable_with_v<_Tp&, _Tp&>;

template <class _Tp, class _Up, bool = __is_swappable_with_v<_Tp, _Up> >
inline const bool __is_nothrow_swappable_with_v = false;

template <class _Tp>
inline const bool __is_nothrow_swappable_v = __is_nothrow_swappable_with_v<_Tp&, _Tp&>;

template <class _Tp, size_t _Np, enable_if_t<__is_swappable_v<_Tp>, int> >
_EZCXX_INLINE
#if __cplusplus >= 201402L
constexpr
#endif
void swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np])
noexcept(__is_nothrow_swappable_v<_Tp>) {
    for (size_t __i = 0; __i != _Np; ++__i) {
        swap(__a[__i], __b[__i]);
    }
}

template <class _Tp, class _Up>
inline const bool __is_swappable_with_v<
    _Tp, _Up, void_t<
        decltype(swap(std::declval<_Tp>(), std::declval<_Up>())),
        decltype(swap(std::declval<_Up>(), std::declval<_Tp>()))
    >
> = true;

template <class _Tp, class _Up>
inline const bool __is_nothrow_swappable_with_v<_Tp, _Up, true> =
    noexcept(swap(std::declval<_Tp>(), std::declval<_Up>())) &&
    noexcept(swap(std::declval<_Up>(), std::declval<_Tp>()));

template <class _Tp, class _Up>
inline constexpr bool is_swappable_with_v = __is_swappable_with_v<_Tp, _Up>;

template <class _Tp, class _Up>
struct _EZCXX_TEMPLATE_VIS is_swappable_with
    : bool_constant<is_swappable_with_v<_Tp, _Up>> {};

template <class _Tp>
inline constexpr bool is_swappable_v =
    is_swappable_with_v<add_lvalue_reference_t<_Tp>, add_lvalue_reference_t<_Tp>>;

template <class _Tp>
struct _EZCXX_TEMPLATE_VIS is_swappable : bool_constant<is_swappable_v<_Tp>> {};

template <class _Tp, class _Up>
inline constexpr bool is_nothrow_swappable_with_v = __is_nothrow_swappable_with_v<_Tp, _Up>;

template <class _Tp, class _Up>
struct _EZCXX_TEMPLATE_VIS is_nothrow_swappable_with
    : bool_constant<is_nothrow_swappable_with_v<_Tp, _Up>> {};

template <class _Tp>
inline constexpr bool is_nothrow_swappable_v =
    is_nothrow_swappable_with_v<add_lvalue_reference_t<_Tp>, add_lvalue_reference_t<_Tp>>;

template <class _Tp>
struct _EZCXX_TEMPLATE_VIS is_nothrow_swappable
    : bool_constant<is_nothrow_swappable_v<_Tp>> {};

} // namespace std

#endif // _EZCXX_TYPE_TRAITS
