#pragma once

namespace util {


template <typename T, T V>
struct integral_constant {static constexpr T value = V;};

struct true_type : integral_constant<bool, true> {};
struct false_type : integral_constant<bool, false> {};

inline constexpr bool true_type_v = true_type::value;
inline constexpr bool false_type_v = false_type::value;

namespace detail {
template <class T> struct type_identity {
  using type = T;
}; // or use std::type_identity (since C++20)

template <class T> // Note that `cv void&` is a substitution failure
auto try_add_lvalue_reference(int) -> type_identity<T &>;
template <class T> // Handle T = cv void case
auto try_add_lvalue_reference(...) -> type_identity<T>;

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

} // namespace detail

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

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

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

template <class T> add_rvalue_reference_t<T> declval() noexcept {
  #pragma clang diagnostic push
  static_assert(false, "Calling declval is ill-formed, see N4892 [declval]/2.");
  #pragma clang diagnostic pop
}

namespace detail {

template <class T>
auto test_returnable(int)
    -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{});
template <class> auto test_returnable(...) -> false_type;

template <class From, class To>
auto test_implicitly_convertible(int)
    -> decltype(void(declval<void (&)(To)>()(declval<From>())),
                true_type{});
template <class, class>
auto test_implicitly_convertible(...) -> false_type;

} // namespace detail

template <typename T>
struct is_void : false_type{};

template<>
struct is_void<void> : true_type {};

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

template< class T > struct remove_reference      { typedef T type; };
template< class T > struct remove_reference<T&>  { typedef T type; };
template< class T > struct remove_reference<T&&> { typedef T type; };

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