//  Copyright Matt Borland 2021.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
//  https://en.cppreference.com/w/cpp/experimental/is_detected

#ifndef BOOST_MATH_TOOLS_IS_DETECTED_HPP
#define BOOST_MATH_TOOLS_IS_DETECTED_HPP

#include <b/math/tools/type_traits.hpp>

namespace boost { namespace math { namespace tools {

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

namespace detail {

template <typename Default, typename AlwaysVoid, template<typename...> class Op, typename... Args>
struct detector
{
    using value_t = boost::math::false_type;
    using type = Default;
};

template <typename Default, template<typename...> class Op, typename... Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...>
{
    using value_t = boost::math::true_type;
    using type = Op<Args...>;
};

} // Namespace detail

// Special type to indicate detection failure
struct nonesuch
{
    nonesuch() = delete;
    ~nonesuch() = delete;
    nonesuch(const nonesuch&) = delete;
    void operator=(const nonesuch&) = delete;
};

template <template<typename...> class Op, typename... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;

template <template<typename...> class Op, typename... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;

template <typename Default, template<typename...> class Op, typename... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;

}}} // Namespaces boost math tools

#endif // BOOST_MATH_TOOLS_IS_DETECTED_HPP
