#ifndef STD_TYPE_TRAITS_H
#define STD_TYPE_TRAITS_H

namespace std {
	template<typename T, T v>
	struct integral_constant 
	{
		static constexpr T value = v;
		using value_type = T;
		using type = integral_constant;
		constexpr operator value_type() const noexcept { return value; }
		constexpr value_type operator()() const noexcept { return value; } 
	};

	template<bool B, typename T = void>
	struct enable_if {};

	template<typename T>
	struct enable_if<true, T> { using type = T; };

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

	template<typename T> struct remove_reference { using type = T; };
	template<typename T> struct remove_reference<T&> { using type = T; };
	template<typename T> struct remove_reference<T&&> { using type = T; };

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

	template <typename T> struct is_trivially_copyable
		: public integral_constant<bool, __is_trivially_copyable(T)>
	{};

	template< typename T >
	inline constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;

	template <typename T> struct is_abstract
		: public integral_constant<bool, __is_abstract(T)> {};

	template<typename T>
	inline constexpr bool is_abstract_v = is_abstract<T>::value;

	template <typename T1, typename T2> struct is_convertible
		: public integral_constant<bool, __is_convertible_to(T1, T2) &&
		!is_abstract_v<T2>> {};

	template <typename From, typename To>
	inline constexpr bool is_convertible_v = is_convertible<From, To>::value;

	template<typename T>
	struct type_identity {
		using type = T;
	};

	template<typename T> struct remove_cv { using type = T; };
	template<typename T> struct remove_cv<const T> { using type = T; };
	template<typename T> struct remove_cv<volatile T> { using type = T; };
	template<typename T> struct remove_cv<const volatile T> { using type = T; };

	template<typename T>
	using remove_cv_t = typename remove_cv<T>::type;

	template<typename T>
	struct remove_cvref
	{
		using type = remove_cv_t<remove_reference_t<T>>;
	};

	template<typename T>
	using remove_cvref_t = typename remove_cvref<T>::type;

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

	template< typename T >
	using type_identity_t = typename type_identity<T>::type;

	template <typename T> struct is_integral
		: integral_constant<bool, __is_integral(T)> {};

	template <typename T>
	inline constexpr bool is_integral_v = __is_integral(T);

	template <typename T> struct is_floating_point
		: public integral_constant<bool, __is_floating_point(T)> {};
	
	template<typename T>
	inline constexpr bool is_floating_point_v = is_floating_point<T>::value;

	template<typename T>
	struct is_arithmetic : integral_constant<bool,
		is_integral_v<T> || is_floating_point_v<T>> {};

	template< typename T >
	inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;

	namespace detail {
		template<typename T, bool = is_arithmetic_v<T>>
		struct is_unsigned : integral_constant<bool, T(0) < T(-1) > {};

		template<typename T>
		struct is_unsigned<T, false> : false_type {};
	} // namespace detail

	template<typename T>
	struct is_unsigned : detail::is_unsigned<T>::type {};

	namespace detail {
		template<typename T, bool = is_arithmetic_v<T>>
		struct is_signed : integral_constant<bool, T(-1) < T(0) > {};

		template<typename T>
		struct is_signed<T, false> : false_type {};
	} // namespace detail

	template<typename T>
	struct is_signed : detail::is_signed<T>::type {};

	template<typename T>
	inline constexpr bool is_signed_v = is_signed<T>::value;

	template <typename T> struct is_enum
		: public integral_constant<bool, __is_enum(T)> {};

	template< class T >
	inline constexpr bool is_enum_v = is_enum<T>::value;

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

	template<typename T>
	struct is_pointer<T*> : std::true_type {};

	template<typename T>
	struct is_pointer<T* const> : std::true_type {};

	template<typename T>
	struct is_pointer<T* volatile> : std::true_type {};

	template<typename T>
	struct is_pointer<T* const volatile> : std::true_type {};

	template< typename T >
	inline constexpr bool is_pointer_v = is_pointer<T>::value;

	template<class T>
	struct is_member_pointer : integral_constant<bool, __is_member_pointer(T)> 
	{};

	template< typename T >
	inline constexpr bool is_member_pointer_v = is_member_pointer<T>::value;
	
	namespace detail {
		template <typename T> struct is_nullptr_t_impl : public false_type {};
		template <>           struct is_nullptr_t_impl<decltype(nullptr)> : public true_type {};

		template <typename T> struct is_nullptr_t
			: public is_nullptr_t_impl<remove_cv_t<T>> {};
	}

	template <typename T> struct is_null_pointer
		: public detail::is_nullptr_t<remove_cv_t<T>> {};

	template<typename T>
	inline constexpr bool is_null_pointer_v = is_null_pointer<T>::value;

	template<typename T>
	struct is_scalar : integral_constant<bool,
		is_arithmetic_v<T> || is_enum_v<T> ||
		is_pointer_v<T> || is_member_pointer_v<T> || is_null_pointer_v<T>> 
	{};

	template<typename T>
	inline constexpr bool is_scalar_v = is_scalar<T>::value;

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

	template<typename T>
	struct is_array<T[]> : std::true_type {};

	template<typename T, size_t N>
	struct is_array<T[N]> : std::true_type {};

	template <typename T>
	inline constexpr bool is_array_v = is_array<T>::value;

	template <typename T> 
	struct is_union : public integral_constant<bool, __is_union(T)> {};

	template <class T>
	inline constexpr bool is_union_v = is_union<T>::value;

	template <class T> 
	struct is_class : public integral_constant<bool, __is_class(T)> {};

	template <class T>
	inline constexpr bool is_class_v = is_class<T>::value;

	template<typename T>
	struct is_object : integral_constant<bool,
		is_scalar_v<T> || is_array_v<T> || is_union_v<T> || is_class_v<T>> 
	{};

	template<typename T>
	inline constexpr bool is_object_v = is_object<T>::value;

	template<typename T>
	struct is_lvalue_reference : integral_constant<bool, __is_lvalue_reference(T)> { };

	template<typename T>
	struct is_rvalue_reference : integral_constant<bool, __is_rvalue_reference(T)> { };

	template<typename T>
	struct is_reference : integral_constant<bool, __is_reference(T)> { };

	template <typename T>
	inline constexpr bool is_reference_v = __is_reference(T);
	template <typename T>
	inline constexpr bool is_lvalue_reference_v = __is_lvalue_reference(T);
	template <typename T>
	inline constexpr bool is_rvalue_reference_v = __is_rvalue_reference(T);

	template <typename T>
	struct is_empty : public integral_constant<bool, __is_empty(T)> {};

	template <typename T>
	inline constexpr bool is_empty_v = is_empty<T>::value;

	template <typename T>
	struct is_final : public integral_constant<bool, __is_final(T)> {};

	template <typename T>
	inline constexpr bool is_final_v = is_final<T>::value;

	template <typename T, typename U>
	struct is_same : public integral_constant<bool, __is_same(T, U)> { };

	template <typename T, typename U>
	inline constexpr bool is_same_v = is_same<T, U>::value;

	template <typename T, typename ...Args>
	struct is_constructible : public integral_constant<bool, __is_constructible(T, Args...)> { };

	template <typename T, typename ...Args>
	inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;

	template <typename T, typename ...Args>
	struct is_trivially_constructible : public integral_constant<bool, __is_trivially_constructible(T, Args...)> { };

	template <typename T, typename ...Args>
	inline constexpr bool is_trivially_constructible_v = is_trivially_constructible<T, Args...>::value;

	template <typename T, typename ...Args>
	struct is_nothrow_constructible : public integral_constant<bool, __is_nothrow_constructible(T, Args...)> { };

	template <typename T, typename ...Args>
	inline constexpr bool is_nothrow_constructible_v = is_nothrow_constructible<T, Args...>::value;

	template <typename T>
	struct is_default_constructible : public is_constructible<T>{};

	template <typename T>
	inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;

namespace detail 
{
	template <typename T>
	auto try_add_lvalue_reference(int)->type_identity<T&>;
	template <typename T>
	auto try_add_lvalue_reference(...)->type_identity<T>;

	template <typename T>
	auto try_add_rvalue_reference(int)->type_identity<T&&>;
	template <typename T>
	auto try_add_rvalue_reference(...)->type_identity<T>;
}

	template <typename T>
	struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {};

	template <typename T>
	struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {};

	template<typename T>
	using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;

	template<typename T>
	using add_rvalue_reference_t = typename add_rvalue_reference<T>::type;

	template<typename T>
	struct remove_extent { using type = T; };

	template<typename T>
	struct remove_extent<T[]> { using type = T; };

	template<typename T, size_t N>
	struct remove_extent<T[N]> { using type = T; };

	template<typename T>
	using remove_extent_t = typename remove_extent<T>::type;

	template<typename T>
	struct remove_all_extents 
	{ 
		using type = T; 
	};

	template<typename T>
	struct remove_all_extents<T[]>
	{
		using type = typename remove_all_extents<T>::type;
	};

	template<typename T, size_t N>
	struct remove_all_extents<T[N]> 
	{
		using type = typename remove_all_extents<T>::type;
	};

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

	template<typename T> struct is_const : std::false_type {};
	template<typename T> struct is_const<const T> : std::true_type {};

	template <typename T>
	inline constexpr bool is_const_v = is_const<T>::value;

namespace detail 
{
	template <typename T>
	auto try_add_pointer(int)->type_identity<remove_reference_t<T>*>;
	template <typename T>
	auto try_add_pointer(...)->type_identity<T>;
}
	template <typename T>
	struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {};
	
	template<typename T>
	using add_pointer_t = typename add_pointer<T>::type;

	template<typename T> struct add_cv { typedef const volatile T type; };
	template<typename T> struct add_const { typedef const T type; };
	template<typename T> struct add_volatile { typedef volatile T type; };

	template< typename T >
	using add_cv_t = typename add_cv<T>::type;

	template< typename T >
	using add_const_t = typename add_const<T>::type;

	template< typename T >
	using add_volatile_t = typename add_volatile<T>::type;

	template<bool B, typename T, typename F>
	struct conditional { using type = T; };

	template<typename T, typename F>
	struct conditional<false, T, F> { using type = F; };

	template< bool B, typename T, typename F >
	using conditional_t = typename conditional<B, T, F>::type;

	template<typename T>
	struct is_function : std::integral_constant< bool,
		!std::is_const_v<const T> && !std::is_reference_v<T>>
	{};

	template <typename T>
	inline constexpr bool is_function_v = is_function<T>::value;

	template<typename T>
	struct is_void : std::is_same<void, std::remove_cv_t<T>> {};

	template<typename T>
	inline constexpr bool is_void_v = is_void<T>::value;

	template<typename T, typename U>
	struct is_assignable : integral_constant<bool, __is_assignable(T, U)> { };

	template <typename T, typename Arg>
	inline constexpr bool is_assignable_v = is_assignable<T, Arg>::value;

	template<typename T, typename U>
	struct is_trivially_assignable : integral_constant<bool, __is_trivially_assignable(T, U)> { };

	template <typename T, typename Arg>
	inline constexpr bool is_trivially_assignable_v = is_trivially_assignable<T, Arg>::value;

	template<typename T, typename U>
	struct is_nothrow_assignable : integral_constant<bool, __is_nothrow_assignable(T, U)> { };

	template <typename T, typename Arg>
	inline constexpr bool is_nothrow_assignable_v = is_nothrow_assignable<T, Arg>::value;

	template<typename T>
	struct is_move_assignable
		: is_assignable<add_lvalue_reference_t<T>, add_rvalue_reference_t<T>> {};

	template<typename T>
	struct is_trivially_move_assignable
		: is_trivially_assignable<add_lvalue_reference_t<T>, add_rvalue_reference_t<T>> {};

	template<typename T>
	struct is_nothrow_move_assignable
		: is_nothrow_assignable<add_lvalue_reference_t<T>, add_rvalue_reference_t<T>> {};

	template<typename T>
	inline constexpr bool is_move_assignable_v = is_move_assignable<T>::value;
	template<typename T>
	inline constexpr bool is_trivially_move_assignable_v = is_trivially_move_assignable<T>::value;
	template<typename T>
	inline constexpr bool is_nothrow_move_assignable_v = is_nothrow_move_assignable<T>::value;

	template<typename T>
	struct is_move_constructible : is_constructible<T, add_rvalue_reference_t<T>> {};

	template<typename T>
	struct is_trivially_move_constructible : is_trivially_constructible<T, add_rvalue_reference_t<T>> {};

	template<typename T>
	struct is_nothrow_move_constructible : is_nothrow_constructible<T, add_rvalue_reference_t<T>> {};

	template<typename T>
	inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
	template<typename T>
	inline constexpr bool is_trivially_move_constructible_v = is_trivially_move_constructible<T>::value;
	template<typename T>
	inline constexpr bool is_nothrow_move_constructible_v = is_nothrow_move_constructible<T>::value;

	template <typename T>
	struct is_copy_constructible 
		: public is_constructible<T, add_lvalue_reference_t<add_const_t<T>>> 
	{};

	template <typename T>
	inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;

	template <typename T> struct is_trivially_copy_constructible
		: public is_trivially_constructible<T, add_lvalue_reference_t<const T>>{};

	template <typename T>
	inline constexpr bool is_trivially_copy_constructible_v = is_trivially_copy_constructible<T>::value;

	template <class T> 
	struct is_copy_assignable
		: public is_assignable<add_lvalue_reference_t<T>, add_lvalue_reference_t<add_const_t<T>>> 
	{};

	template <class T>
	inline constexpr bool is_copy_assignable_v = is_copy_assignable<T>::value;

	template <typename T> struct is_trivially_copy_assignable
		: public is_trivially_assignable<add_lvalue_reference_t<T>, add_lvalue_reference_t<add_const_t<T>>> {};

	template <typename T>
	inline constexpr bool is_trivially_copy_assignable_v = is_trivially_copy_assignable<T>::value;

	template <typename T> struct is_trivially_destructible : public integral_constant<bool, __is_trivially_destructible(T)> {};
	
	template<typename T>
	inline constexpr bool is_trivially_destructible_v = is_trivially_destructible<T>::value;

	template<typename T>
	struct decay 
	{
	private:
		using U = remove_reference_t<T>;
	public:
		using type = conditional_t<is_array_v<U>, remove_extent_t<U>*,
			conditional_t<is_function_v<U>, add_pointer_t<U>, remove_cv_t<U>>>;
	};

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

	template<typename T> constexpr bool always_false = false;
	template<typename T> constexpr bool always_true = true;

	template<typename T>
	add_rvalue_reference_t<T> declval() noexcept
	{
		static_assert(always_false<T>);
	}

// __swappable

	template <typename T> struct __is_swappable;
	template <typename T> struct __is_nothrow_swappable;

	template <typename T>
	inline constexpr enable_if_t<is_move_constructible_v<T>&& is_move_assignable_v<T>>
		swap(T& a, T& b) noexcept(	is_nothrow_move_constructible_v<T> &&
									is_nothrow_move_assignable_v<T>);

	template<typename T, size_t N>
	inline constexpr enable_if_t<__is_swappable<T>::value>
		swap(T(&a)[N], T(&b)[N]) noexcept(__is_nothrow_swappable<T>::value);

	namespace detail
	{
	template <typename T, typename U = T,
		bool NotVoid = !is_void_v<T> && !is_void_v<T>>
		struct swappable_with
	{
		template <typename Lhs, typename Rhs>
		static decltype(swap(declval<Lhs>(), declval<Rhs>())) test_swap(int);

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

		template <typename, typename>
		static nat test_swap(long);

		typedef decltype(test_swap<T, U>(0)) swap1;
		typedef decltype(test_swap<U, U>(0)) swap2;

		static const bool value =	!is_same_v<swap1, nat>
								&&	!is_same_v<swap2, nat>;

	};

	template <typename T, typename U>
	struct swappable_with<T, U, false> : false_type {};

	template <typename T, typename U = T, bool Swappable = swappable_with<T, U>::value>
	struct nothrow_swappable_with 
	{
		static const bool value =
			noexcept(swap(declval<T>(), declval<U>()))
			&& noexcept(swap(declval<U>(), declval<T>()));
	};

	template <typename T, typename U>
	struct nothrow_swappable_with<T, U, false> : false_type {};

	struct two { char lx[2]; };

	struct is_referenceable_impl 
	{
		template <typename T> static T& test(int);
		template <typename T> static two test(...);
	};

	template<typename T>
	inline constexpr bool is_referenceable_v
		= !is_same_v<decltype(is_referenceable_impl::test<T>(0)), two>;

	} // namespace detail

	template <typename T>
	struct __is_swappable
		: public integral_constant<bool, detail::swappable_with<T&>::value>
	{};

	template <typename T>
	struct __is_nothrow_swappable
		: public integral_constant<bool, detail::nothrow_swappable_with<T&>::value>
	{};

	template <typename T, typename U>
	struct is_swappable_with
		: public integral_constant<bool, detail::swappable_with<T, U>::value>
	{};

	template <typename T>
	struct is_swappable : public conditional<detail::is_referenceable_v<T>,
		is_swappable_with<add_lvalue_reference_t<T>, add_lvalue_reference_t<T>>,
		false_type>::type
	{};

	template <typename T, typename U>
	struct is_nothrow_swappable_with
		: public integral_constant<bool, detail::nothrow_swappable_with<T, U>::value>
	{};

	template <typename T>
	struct is_nothrow_swappable : public conditional<detail::is_referenceable_v<T>,
		is_nothrow_swappable_with<add_lvalue_reference_t<T>, add_lvalue_reference_t<T>>,
		false_type>::type
	{};

	template <typename T, typename U>
	inline constexpr bool is_swappable_with_v = is_swappable_with<T, U>::value;

	template <typename T>
	inline constexpr bool is_swappable_v = is_swappable<T>::value;

	template <typename T, typename U>
	inline constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with<T, U>::value;

	template <typename T>
	inline constexpr bool is_nothrow_swappable_v = is_nothrow_swappable<T>::value;

	namespace detail
	{
	template <typename>
	struct is_destructible_apply { using type = int; };

	template <typename T>
	struct is_destructor_wellformed {
		template <typename T1>
		static char test(
			typename is_destructible_apply<decltype(declval<T1&>().~T1())>::type);

		template <typename T1>
		static two test(...);

		static const bool value = sizeof(test<T>(12)) == sizeof(char);
	};

	template <typename T, bool>
	struct destructible_impl;

	template <typename T>
	struct destructible_impl<T, false>
		: public integral_constant<bool,
		is_destructor_wellformed<remove_all_extents_t<T>>::value> {};

	template <typename T>
	struct destructible_impl<T, true>
		: public true_type {};

	template <typename T, bool>
	struct destructible_false;

	template <typename T>
	struct destructible_false<T, false> : public destructible_impl<T, is_reference_v<T>> {};

	template <typename T>
	struct destructible_false<T, true> : public false_type {};
	}

	template <typename T>
	struct is_destructible
		: public detail::destructible_false<T, is_function<T>::value> {};

	template <typename T>
	struct is_destructible<T[]> : public false_type {};

	template <>
	struct is_destructible<void> : public false_type {};

	template <typename T>
	inline constexpr bool is_destructible_v = is_destructible<T>::value;

	template<typename T> struct remove_pointer { typedef T type; };
	template<typename T> struct remove_pointer<T*> { typedef T type; };
	template<typename T> struct remove_pointer<T* const> { typedef T type; };
	template<typename T> struct remove_pointer<T* volatile> { typedef T type; };
	template<typename T> struct remove_pointer<T* const volatile> { typedef T type; };

	template<typename Base, typename Derived>
	struct is_base_of
		: public integral_constant<bool, __is_base_of(Base, Derived)>
	{};

	template<typename Base, typename Derived>
	inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;

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