text: /*!
  @file
  Defines `boost::hana::experimental::types`.
  
  @copyright Louis Dionne 2013-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 BOOST_HANA_EXPERIMENTAL_TYPES_HPP
  #define BOOST_HANA_EXPERIMENTAL_TYPES_HPP
  
  #include <boost/hana/bool.hpp>
  #include <boost/hana/concept/metafunction.hpp>
  #include <boost/hana/config.hpp>
  #include <boost/hana/detail/any_of.hpp>
  #include <boost/hana/detail/type_at.hpp>
  #include <boost/hana/fwd/at.hpp>
  #include <boost/hana/fwd/contains.hpp>
  #include <boost/hana/fwd/core/tag_of.hpp>
  #include <boost/hana/fwd/equal.hpp>
  #include <boost/hana/fwd/is_empty.hpp>
  #include <boost/hana/fwd/transform.hpp>
  #include <boost/hana/fwd/unpack.hpp>
  #include <boost/hana/type.hpp>
  
  #include <cstddef>
  #include <type_traits>
  #include <utility>
  
  
  BOOST_HANA_NAMESPACE_BEGIN
      namespace experimental {
          //! @ingroup group-experimental
          //! Container optimized for holding types.
          //!
          //! It is often useful to manipulate a sequence that contains types
          //! only, without any associated runtime value. This container allows
          //! storing and manipulating pure types in a much more compile-time
          //! efficient manner than using `hana::tuple`, which must assume that
          //! its contents might have runtime values.
          template <typename ...T>
          struct types;
  
          struct types_tag;
  
          template <typename ...T>
          struct types { };
      } // end namespace experimental
  
      template <typename ...T>
      struct tag_of<experimental::types<T...>> {
          using type = experimental::types_tag;
      };
  
      // Foldable
      template <>
      struct unpack_impl<hana::experimental::types_tag> {
          template <typename ...T, typename F, typename = typename std::enable_if<
              !hana::Metafunction<F>::value
          >::type>
          static constexpr decltype(auto) apply(hana::experimental::types<T...> const&, F&& f) {
              return static_cast<F&&>(f)(hana::type<T>{}...);
          }
  
          template <typename ...T, typename F, typename = typename std::enable_if<
              hana::Metafunction<F>::value
          >::type>
          static constexpr hana::type<typename F::template apply<T...>::type>
          apply(hana::experimental::types<T...> const&, F const&) { return {}; }
      };
  
      // Functor
      template <>
      struct transform_impl<hana::experimental::types_tag> {
          template <typename ...T, typename F, typename = typename std::enable_if<
              !hana::Metafunction<F>::value
          >::type>
          static constexpr auto apply(hana::experimental::types<T...> const&, F&& f)
              -> hana::experimental::types<typename decltype(+f(hana::type<T>{}))::type...>
          { return {}; }
  
          template <typename ...T, typename F, typename = typename std::enable_if<
              hana::Metafunction<F>::value
          >::type>
          static constexpr hana::experimental::types<typename F::template apply<T>::type...>
          apply(hana::experimental::types<T...> const&, F const&) { return {}; }
      };
  
      // Iterable
      template <>
      struct at_impl<hana::experimental::types_tag> {
          template <typename ...T, typename N>
          static constexpr auto
          apply(hana::experimental::types<T...> const&, N const&) {
              using Nth = typename detail::type_at<N::value, T...>::type;
              return hana::type<Nth>{};
          }
      };
  
      template <>
      struct is_empty_impl<hana::experimental::types_tag> {
          template <typename ...T>
          static constexpr hana::bool_<sizeof...(T) == 0>
          apply(hana::experimental::types<T...> const&)
          { return {}; }
      };
  
      template <>
      struct drop_front_impl<hana::experimental::types_tag> {
          template <std::size_t n, typename ...T, std::size_t ...i>
          static hana::experimental::types<typename detail::type_at<i + n, T...>::type...>
          helper(std::index_sequence<i...>);
  
          template <typename ...T, typename N>
          static constexpr auto
          apply(hana::experimental::types<T...> const&, N const&) {
              constexpr std::size_t n = N::value > sizeof...(T) ? sizeof...(T) : N::value;
              using Indices = std::make_index_sequence<sizeof...(T) - n>;
              return decltype(helper<n, T...>(Indices{})){};
          }
      };
  
      // Searchable
      template <>
      struct contains_impl<hana::experimental::types_tag> {
          template <typename U>
          struct is_same_as {
              template <typename T>
              struct apply {
                  static constexpr bool value = std::is_same<U, T>::value;
              };
          };
  
          template <typename ...T, typename U>
          static constexpr auto apply(hana::experimental::types<T...> const&, U const&)
              -> hana::bool_<
                  detail::any_of<is_same_as<typename U::type>::template apply, T...>::value
              >
          { return {}; }
  
          static constexpr hana::false_ apply(...) { return {}; }
      };
  
      // Comparable
      template <>
      struct equal_impl<hana::experimental::types_tag, hana::experimental::types_tag> {
          template <typename Types>
          static constexpr hana::true_ apply(Types const&, Types const&)
          { return {}; }
  
          template <typename Ts, typename Us>
          static constexpr hana::false_ apply(Ts const&, Us const&)
          { return {}; }
      };
  BOOST_HANA_NAMESPACE_END
  
  #endif // !BOOST_HANA_EXPERIMENTAL_TYPES_HPP
  