/*!
 * \file    typelist.h
 * \brief   A template parameter "container"
 *
 * Copyright (C) 2018-2019 Christos Choutouridis
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __utl_meta_typelist_h__
#define __utl_meta_typelist_h__

#include <utl/core/impl.h>
#include <utl/meta/integral.h>
#include <utl/meta/detection.h>
#include <utl/meta/invoke.h>
#include <utl/meta/sfinae.h>
/*!
 * \ingroup meta
 * \defgroup typelist
 */
//! @{

namespace utl {
namespace meta {

   /*!
    * \brief
    *    A class template that just holds a parameter pack.
    *
    * The idea came from MPL's sequence concept [\ref link1 1] and from N4115 [\ref link2 2].
    * In addition to N4115's name "packer" we just prefer a name which is object, not a subject.
    * This way the name gives the feeling of a container and smells like Python.
    *
    * In addition to tuple we lack members, so typelist could serve as an empty base class,
    * and an object of the ultimate type could always be instantiated
    * (even if the parameter typelist contains void or some type that lacks
    * a default constructor).
    * \example
    * \code
    *    using l1 = typelist<int, void*, double, void>;
    *    l1 a {};
    * \endcode
    *
    * boost::hana [\ref link3 3] suggests a more powerful scheme were type invariant structures can be used
    * for metaprograming also. This lib does not need (yet) this kind of power (we afraid the
    * responsibility that comes along). So a simple python-like list with some extra vector-like
    * element access functionalities and no iterators is good enough(for now).
    *
    * \anchor link1 [1]: https://www.boost.org/doc/
    * \anchor link2 [2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4115.html
    * \anchor link3 [3]: https://github.com/boostorg/hana
    */
   template <typename... Ts>
   struct typelist {
      using type = typelist;   //!< act as identity

      //! \return sizeof...(Ts)
      static constexpr size_t size() noexcept {
         return sizeof...(Ts);
      }
      //! \return true if empty
      static constexpr bool empty() noexcept {
         return (sizeof...(Ts) == 0);
      }
      // ======= times utility =======
   private:
      template <typename... > struct cat_ { };
      template <typename... L1, typename... L2>
      struct cat_<typelist<L1...>, typelist<L2...>> {
         using type = typelist<L1..., L2...>;
      };

      template <size_t N, typename ...T>
      struct times_ {
         //static_assert( N >= 0, "Cannot make typelist of negative length" );
         using type = eval<
            cat_<
               eval<times_<N/2, T...>>,
               eval<times_<N - N/2, T...>>
            >
         >;
      };
      template <typename ...T>
      struct times_<1, T...> {
         using type = typelist<T...>;
      };
      template <typename ...T>
      struct times_<0, T...> {
         using type = typelist<>;
      };
   public:
      /*!
       * Generate typelist<Ts..., Ts..., ...> of size \c N arguments.
       * \example
       * \code
       *    static_assert (
       *       std::is_same<typelist<int, char>::times<2>,
       *                    typelist<int, char, int, char>
       *       >, "" );
       * \endcode
       * complexity \f$ O(logN) \f$
       */
      template<size_t N>
      using times = eval<
         times_<N, Ts...>
      >;
   };

   /*!
    * An integral constant wrapper that is the size of the \c meta::typelist
    *
    * Complexity \f$ O(1) \f$.
    *
    * \param List  A typelist
    * \return The size of the typelist
    */
   template <typename List>
   using size = size_<List::size()>;

   /*!
    * An Boolean constant wrapper that returns if the typelist is empty
    *
    * Complexity \f$ O(1) \f$.
    *
    * \param List  A typelist
    * \return Empty or not
    */
   template <typename List>
   using empty = bool_<List::empty()>;

   //! pair
   //! A special typelist with only 2 Types
   template <typename T1, typename T2>
   using pair = typelist<T1, T2>;


   //! repeat
   //! @{

   /*!
    * A wrapper to typelist<>::times<> utility for integer argument \p N
    */
   template <size_t N, typename ...Ts>
   using repeat_c = typename typelist<Ts...>::template times<N>;

   /*!
    * A wrapper to typelist<>::times<> utility for integral_c argument \p N
    */
   template <typename N, typename ...Ts>
   using repeat = repeat_c<N::type::value, Ts...>;
   //! @}

   /*!
    * Apply
    * An analogous to apply() implementation for tuples. We just use
    * Our typelist<> and integer_sequence<> types.
    */
   //! @{
   namespace apply_impl {
       template <typename Fn, typename Seq>
       struct apply_ { };

       //! \p Sequence == typelist<>
       template<typename Fn, typename ...List>
       struct apply_<Fn, typelist<List...>> {
          using type = invoke<Fn, List...>;
       };
       //! Sequence == integer_sequence<>
       template <typename Fn, typename T, T... Is>
       struct apply_<Fn, integer_sequence<T, Is...>> {
          using type = invoke<Fn, integral_<T, Is>...>;
       };
   }

  /*!
   * Apply the Invocable \p Fn using the types in the type \p Seq as arguments.
   * \note
   *    This is the opposed operation of typelist<Ts...>
   *
   * If \p Seq == typelist<> then
   *    Unpack typelist and apply to \c Fn
   * It \p Seq == integer_sequence<> then
   *    Unpack and use the integral_c<> of each integer
   */
   template <typename Fn, typename Seq>
   using apply = apply_impl::apply_<Fn, Seq>;

   template <typename Fn, typename Seq>
   using apply_t = eval <apply<Fn, Seq>>;

   //! @}

   /*
    * ========= element access ========
    */
   //! at: random element access
   //! @{
   namespace at_impl {

      template <typename T> struct _add_pointer { using type = T*; };
      template <typename T> using  add_pointer = eval < _add_pointer <T> >;

      template <typename ...>
      struct at_head_ { };

      template <typename... voids>
      struct at_head_ <typelist<voids...>> {
         // successful selection N voids, one T* and the rest
         template <typename T> static constexpr T select(voids..., T*, ...);
         // selection on error
         static constexpr nil_ select (...);
      };

      template<typename List, index_t N>
      struct at_ { };

      template<typename... List, index_t N>
      struct at_<typelist<List...>, N> {
         using head_ = at_head_<typelist<void*>::times<N>>;    //< make at_head_<> with N void*
         using type = decltype(
            head_::select(static_cast<add_pointer<List>>(nullptr)...)   //< pass all as List*...
         );
      };
   }

   /*!
    * Return the \p N th element in the \c meta::typelist \p List.
    *
    * Complexity \f$ O(logN) \f$.
    */
   template <typename List, index_t N>
   using at_c = eval<
      at_impl::at_<List, N>
   >;

   /*!
    * Return the \p N th element in the \c meta::typelist \p List.
    *
    * Complexity \f$ O(N) \f$.
    */
   template <typename List, typename N>
   using at = at_c<List, N::type::value>;
   //!@}


   //! front
   //! @{
   namespace front_impl {
      template <typename L>
      struct front_ { };

      template <typename Head, typename... Tail>
      struct front_<typelist<Head, Tail...>> {
         using type = Head;
      };
   }

   //! Return the first element in \c meta::typelist \p List.
   //! Complexity \f$ O(1) \f$.
   template <typename List>
   using front = eval<
      front_impl::front_<List>
   >;
   //! @}

   //! back
   //! @{
   namespace back_impl {
      template <typename List>
      struct back_ { };

      template <typename Head, typename... Tail>
      struct back_<typelist<Head, Tail...>> {
         using type = at_c <
            typelist<Head, Tail...>, sizeof...(Tail)
         >;
      };
   }

   //! Return the last element in \c meta::typelist \p List.
   //! Complexity \f$ O(N) \f$.
   template <typename List>
   using back = eval<
      back_impl::back_<List>
   >;
   //! @}
   /*
    * ========= typelist operations =========
    */

   //! Concatenation
   //! @{
   namespace cat_impl {
      template <typename... Lists>
      struct cat_ { };

      template <>
      struct cat_<> {
         using type = typelist<>;
      };

      template <typename... L1>
      struct cat_<typelist<L1...>> {
         using type = typelist<L1...>;
      };

      template <typename... L1, typename... L2>
      struct cat_<typelist<L1...>, typelist<L2...>> {
         using type = typelist<L1..., L2...>;
      };

      template <typename... L1, typename... L2, typename... Ln>
      struct cat_<typelist<L1...>, typelist<L2...>, Ln...>
         : cat_ <typelist<L1..., L2...>, Ln...> { };

   }

   /*!
    * Transformation that concatenates several lists into a single typelist.
    * The parameters must all be instantiations of \c meta::typelist.
    * Complexity: \f$ O(N) \f$
    *    where \f$ N \f$ is the number of lists passed to the algorithm.
    */
   template <typename... Lists>
   using cat = eval<
      cat_impl::cat_<Lists...>
   >;
   //! @}


   //! fold<List, V, Fn>, rev_fold<List, V, Fn>
   //! @{
   namespace fold_impl {
      // fold<<T1, T2, T3>, V, F> == F<F<F<V, T1>, T2>, T3>
      template<typename, typename, typename>
      struct fold_ { }; // ill formed

      // recursive call
      template<typename Head, typename... Tail,
               typename V,
               typename Fn>
      struct fold_<typelist<Head, Tail...>, V, Fn> {
         // recursive call of fold_ by consuming typelist and invoking Fn
         using type = eval<
            fold_<
               typelist<Tail...>,
               invoke<Fn, V, Head>,
               Fn
            >
         >;
      };
      // termination call
      template<typename V0, typename Fn>
      struct fold_<typelist<>, V0, Fn> {
         using type = V0;
      };
   }

   /*!
    * transform the \p List to a new one by doing a left fold using binary Invocable \p Fn
    * and initial value \p V
    * Complexity \f$ O(N) \f$
    * \example
    *    fold<typelist<T1, T2, T3>, V, F> == F<F<F<V, T1>, T2>, T3>
    * \example
    *    fold<typelist<>, V, F> == V
    * \param   List  The list to fold
    * \param   V     The initial item feeded to Fn
    * \param   Fn    The binary Invocable
    */
   template <typename List, typename V, typename Fn>
   using fold = eval<fold_impl::fold_<List, V, Fn>>;

   //! accumulate is an stl name for fold
   template <typename List, typename V, typename Fn>
   using accumulate = fold<List, V, Fn>;

   namespace rev_fold_impl {

      // rev_fold<<T1, T2, T3>, V, F> == F<T1, F<T2, F<T3, V>>>
      template<typename, typename, typename>
      struct rev_fold_ { }; // ill formed

      // recursive call
      template<typename Head, typename... Tail,
               typename V,
               typename Fn>
      struct rev_fold_<typelist<Head, Tail...>, V, Fn> {
         // recursive call inside invoke. This way the 2nd argument of Fn
         // becoming the recursive "thing", inside Fn<>
         using type = invoke <
            Fn, Head, eval<
               rev_fold_ <
                  typelist<Tail...>,
                  V,
                  Fn
               >>
         >;
      };
      // pre-termination call
      template<typename Tail, typename V, typename Fn>
      struct rev_fold_ <typelist<Tail>, V, Fn> {
         using type = invoke<Fn, Tail, V>;
      };
      // termination call
      template<typename V, typename Fn>
      struct rev_fold_ <typelist<>, V, Fn> {
         using type = V;
      };
   }

   /*!
    * transform the \p List to a new one by doing a right fold using binary Invocable \p Fn
    * and initial value \p V
    * Complexity \f$ O(N) \f$
    * \example
    *    rev_fold<typelist<T1, T2, T3>, V, F> == F<T1, F<T2, F<T3, V>>>
    * \example
    *    rev_fold<typelist<>, V, F> == V
    * \param   List  The list to fold
    * \param   V     The initial item fed to Fn
    * \param   Fn    The binary Invocable
    */
   template <typename List, typename V, typename Fn>
   using rev_fold = eval<
      rev_fold_impl::rev_fold_<List, V, Fn>
   >;
   //! @}

   /*!
    * Return a new \c typelist by adding the elements \p Ts to the front of \p List.
    * Complexity \f$ O(1) \f$
    */
   template <typename List, typename... Ts>
   using push_front = eval<
      apply <
         bind_front<quote<typelist>, Ts...>, List
      >
   >;

   /*!
    * Return a new \c typelist by adding the elements \p Ts to the back of \p List.
    * Complexity \f$ O(1) \f$
    */
   template <typename List, typename... Ts>
   using push_back = eval<
      apply <
         bind_back<quote<typelist>, Ts...>, List
      >
   >;

   //! reverse
   //! @{
   namespace reverse_impl {
      template <typename List, typename V = typelist<>>
      struct reverse_ {
         using type = fold<List, V, quote<push_front>>;
      };
   }

   /*!
    * Return a new \c typelist by reversing the elements in the list \p List.
    * Complexity \f$ O(N) \f$
    */
   template <typename List>
   using reverse = eval<
      reverse_impl::reverse_<List>
   >;
   //! @}

   //! pop_front
   //! @{
   namespace pop_front_impl {
      template <typename List>
      struct pop_front_ { };

      template <typename Head, typename... Tail>
      struct pop_front_<typelist <Head, Tail...>> {
         using type = typelist<Tail...>;
      };
   }

   /*!
    * Return a new \c typelist by removing the first element from the
    * front of \p List.
    * Complexity \f$ O(1) \f$
    */
   template <typename List>
   using pop_front = eval<
      pop_front_impl::pop_front_<List>
   >;
   //! @}

   //! pop_back
   //! @{
   namespace pop_back_impl {
      template <typename List>
      struct pop_back_ {
         using type = reverse<
            pop_front<reverse<List>>
         >;
      };
   }

   /*!
    * Return a new \c typelist by removing the last element from the \p List.
    * Complexity \f$ O(N) \f$.
    * \note
    *    This operation, in addition from other push/pop operations, is
    *    heavy(2 reverse operations).
    */
   template <typename List>
   using pop_back = eval <
      pop_back_impl::pop_back_<List>
   >;
   //! @}

   //! Transform
   //! @{
   namespace transform_impl {
      template <typename, typename = void>
      struct transform_ { };

      template <typename... Ts, typename Fn>
      struct transform_<typelist<typelist<Ts...>, Fn>,
                        void_t<invoke<Fn, Ts>...> > /* SFINAE check */ {
         using type = typelist<
            invoke_t<Fn, Ts>...
         >;
      };

      template <typename... Ts0, typename... Ts1, typename Fn>
      struct transform_<typelist<typelist<Ts0...>, typelist<Ts1...>, Fn>,
                        void_t<invoke<Fn, Ts0, Ts1>...>> /* SFINAE check */ {
         using type = typelist<
            invoke_t<Fn, Ts0, Ts1>...
         >;
      };
   }

   /*!
    * Transformation by applying an invocable \c Fn to one or two lists
    * and return the resulting typelist
    *
    * Complexity \f$ O(N) \f$.
    * \example
    * \code
    *    using l1 = typelist<char, int, ...>;
    *    using l2 = typelist<void, double, ...>;
    *    using r1 = transform<l1, F1>;       // F1, unary invocable
    *    using r2 = transform<l1, l2, F2>;   // F2, binary invocable
    * \endcode
    */
   template <typename... Args>
   using transform = eval<
      transform_impl::transform_<typelist<Args...>>
   >;
   //! @}

   //! Transform lazy
   //! @{
   namespace transform_lazy_impl {
      template <typename, typename = void>
      struct transform_lazy_ { };

      // Match for Unary Fn with one typelist
      template <typename... Ts, typename Fn>
      struct transform_lazy_<typelist<typelist<Ts...>, Fn>,
                             void_t<invoke<Fn, Ts>...> > /* SFINAE check */ {
         using type = typelist<
            invoke<Fn, Ts>...
         >;
      };

      // Match for Binary Fn with two typelists
      template <typename... Ts0, typename... Ts1, typename Fn>
      struct transform_lazy_<typelist<typelist<Ts0...>, typelist<Ts1...>, Fn>,
                             void_t<invoke<Fn, Ts0, Ts1>...>> /* SFINAE check */ {
         using type = typelist<
            invoke<Fn, Ts0, Ts1>...
         >;
      };
   }

   /*!
    * Transformation by applying an invocable \c Fn to one or two lists
    * and return a typelist containing the invocables with their arguments,
    * not their resulting types.
    *
    * Complexity \f$ O(N) \f$
    *
    * \example
    * \code
    *    using l1 = typelist<char, int, ...>;
    *    using l2 = typelist<void, void, ...>;
    *    using r1 = transform<l1, F1>;       // F1, unary invocable
    *    using r2 = transform<l1, l2, F2>;   // F2, binary invocable
    * \endcode
    */
   template <typename... Args>
   using transform_lazy = eval<
      transform_lazy_impl::transform_lazy_<typelist<Args...>>
   >;
   //! @}


   //! find_if, find
   //! @{
   namespace find_if_impl {
      template <typename, typename, index_t>
      struct find_if_ { };

      template<typename Head, typename... Tail, typename Fn, index_t N>
      struct find_if_<typelist<Head, Tail...>, Fn, N> {
         // Recursive call to find_if_ until Fn returns true_
         using type = if_ <
            invoke_t<Fn, Head>,
            index_<N>,       // done, return current index
            eval<find_if_<   // not done, re-call find_if_ with the Tail...
               typelist<Tail...>, Fn, N+1>
            >
         >;
      };

      // When empty or when we are one place after the last item return Npos
      template<typename Fn, index_t N>
      struct find_if_<typelist<>, Fn, N> {
         using type = Npos;
      };
   }

   /*!
    * Search for the first \c Item on the \p List for which the predicate \p Pred
    * returns true_ when `eval<invoke<Pred, Item>>`
    *
    * Complexity \f$ O(N) \f$
    *
    * \param   List  A typelist
    * \param   Pred  A Unary invocable predicate
    * \return  An integral constant of index_t with the location of the first match,
    *          or Npos otherwise.
    */
   template<typename List, typename Pred>
   using find_if = eval<
      find_if_impl::find_if_<List, Pred, 0>
   >;

   /*!
    * Search for the first occurrence of type \p T on a \p List
    */
   template <typename List, typename T>
   using find = find_if<List, same_as<T>>;
   //! @}

   //! seek_if
   //! @{
   namespace seek_if_impl {
      template <typename, typename, index_t>
      struct seek_if_ { };

      template<typename Head, typename... Tail, typename Fn, index_t N>
      struct seek_if_<typelist<Head, Tail...>, Fn, N> {
         // recursive call to seek_if_ until Fn returns true_
         using type = if_ <
            invoke_t<Fn, Head>,
            typelist<Head, Tail...>,   // done, return the typelist starting from here
            eval<seek_if_<             // not done, re-call seek_if_ with the Tail...
               typelist<Tail...>, Fn, N+1>
            >
         >;
      };

      // When empty or when we are one place after the last item return empty typelist
      template<typename Fn, index_t N>
      struct seek_if_<typelist<>, Fn, N> {
         using type = typelist<>;
      };
   }

   /*!
    * Search for the first \c Item on the \p List for which the predicate \p Pred
    * returns true_ when `eval<invoke<Pred, Item>>` and return the rest of the \p List
    * starting from that position as new typelist
    *
    * Complexity \f$ O(N) \f$
    *
    * \param   List  A typelist
    * \param   Pred  A Unary invocable predicate
    * \return  An integral constant with the location of the first match, on Npos otherwise
    */
   template <typename List, typename Pred>
   using seek_if = eval<
      seek_if_impl::seek_if_<List, Pred, 0>
   >;
   /*!
    * Search for the first \c Item on the \p List of type \p T and return the rest
    * of the \p List starting from that position as new typelist
    */
   template <typename List, typename T>
   using seek = seek_if <List, same_as<T>>;
   //! @}

   //! count_if
   //! @{
   namespace count_if_impl {
      template <typename, typename, size_t>
      struct count_if_ { };

      template<typename Head, typename... Tail, typename Fn, size_t N>
      struct count_if_<typelist<Head, Tail...>, Fn, N> {
         // Recursive call to count_if_ up to the end of List, counting all invokes of Fn
         // returning true_
         using type = if_ <
            invoke_t<Fn, Head>,
            eval<
               count_if_<typelist<Tail...>, Fn, N+1>  // increase and re-call
            >,
            eval<
               count_if_<typelist<Tail...>, Fn, N>    // re-call without increasing
            >
         >;
      };

      // At the end of the List return the counter
      template<typename Fn, size_t N>
      struct count_if_<typelist<>, Fn, N> {
         using type = size_<N>;
      };
   }

   /*!
    * Count all \c Items on the \p List for which the predicate \p Pred
    * returns true_ when `eval<invoke<Pred, Item>>`
    *
    * Complexity \f$ O(N) \f$
    *
    * \param   List  A typelist
    * \param   Pred  A Unary invocable predicate
    * \return  The total count of occurrences as an integral constant of size_t
    */
   template <typename List, typename Pred>
   using count_if = eval<
      count_if_impl::count_if_<List, Pred, 0>
   >;

   /*!
    * Count all occurrences of type \p T int \p List
    */
   template <typename List, typename T>
   using count = count_if<List, same_as<T>>;
   //! @}

   //! filter
   //! @{
   namespace filter_impl {
      template <typename, typename, typename>
      struct filter_ { };

      template<typename Head, typename... Tail, typename Fn, typename L>
      struct filter_<typelist<Head, Tail...>, Fn, L> {
         // Recursive call to filter_ up to the end of the List, creating a new list
         // of items for which the invoke of Fn returns true_
         using type = if_ <
            invoke_t <Fn, Head>,
            eval<filter_<typelist<Tail...>, Fn, cat<L, typelist<Head>>>>, // Add the element and re-call
            eval<filter_<typelist<Tail...>, Fn, L>>  // re-call with the same list
         >;
      };

      // At the end return the produced list
      template<typename Fn, typename L>
      struct filter_<typelist<>, Fn, L> {
         using type = L;
      };
   }

   /*!
    * Return a new typelist with elements, the elements of \p List that satisfy the
    * invocable \p Pred such that `eval<invoke<Pred, Item>>` is \c true_
    *
    * Complexity \f$ O(N) \f$
    *
    * \param   List  The input typelist
    * \param   Pred  A unary invocable predicate
    */
   template <typename List, typename Pred>
   using filter = eval<
      filter_impl::filter_<List, Pred, typelist<>>
   >;
   //! @}

   //! replace
   //! @{
   namespace replace_if_impl {
      template <typename, typename, typename, typename>
      struct replace_if_ { };

      template <typename Head, typename... Tail, typename Fn, typename T, typename Ret>
      struct replace_if_<typelist<Head, Tail...>, Fn, T, Ret> {
         // Recursive call to replace_if_ up to the end of the List, creating a new list
         // of items based on invocation of Fn
         using type = if_ <
            invoke_t<Fn, Head>,
            eval<replace_if_<typelist<Tail...>, Fn, T, cat<Ret, typelist<T>>>>,    // re-call with change to T
            eval<replace_if_<typelist<Tail...>, Fn, T, cat<Ret, typelist<Head>>>>  // re-call with no change
         >;
      };

      // At the end return the produced list
      template <typename Fn, typename T, typename Ret>
      struct replace_if_ <typelist<>, Fn, T, Ret> {
         using type = Ret;
      };
   }

   /*!
    * Return a new typelist where all the instances for which the invocation of\p Pred
    * returns \c true_, are replaced with \p T
    *
    * Complexity \f$ O(N) \f$
    *
    * \param   List  The input typelist
    * \param   Pred  A unary invocable predicate
    * \param   T     The new type to replace the item of the \p List, when eval<invoke<Pred, Item>>
    *                returns \c true_
    */
   template<typename List, typename Pred, typename T>
   using replace_if = eval<
      replace_if_impl::replace_if_<List, Pred, T, typelist<>>
   >;

   //! Alias wrapper that returns a new \c typelist where all instances of type \p T have
   //! been replaced with \p U.
   template <typename List, typename T, typename U>
   using replace = eval <
      replace_if <List, same_as<T>, U>
   >;
   //! @}


   //! Returns \c true_ if \p Pred returns \c true_ for all the elements in the \p List or if the
   //! \p List is empty and \c false_ otherwise.
   template <typename List, typename Pred>
   using all_of = if_ <
      empty <List>,
      false_,
      empty <
         filter <List, compose<quote<not_>, Pred>>
      >
   >;

   //! Returns \c true_ if \p Pred returns \c true_ for any of the elements in the \p List
   //! and \c false_ otherwise.
   template <typename List, typename Pred>
   using any_of = not_<
      empty<filter <List, Pred>>
   >;

   //! Returns \c true_ if \p Pred returns \c false_ for all the elements in the \p List
   //! or if the \p List is empty and \c false otherwise.
   template <typename List, typename Pred>
   using none_of = empty<
      filter <List, Pred>
   >;

}}

//! @}

#endif /* __utl_meta_typelist_h__ */
