#ifndef COMPOSITE_STORAGE_TYPE_AT_HPP_INCLUDED
#define COMPOSITE_STORAGE_TYPE_AT_HPP_INCLUDED
#define TYPE_AT_N3761 0
//implementation uses code from:
//  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3761.html
#define TYPE_AT_MPL   (1+ TYPE_AT_N3761)
//implementation uses code from:
//  http://www.boost.org/doc/libs/1_56_0/libs/mpl/doc/refmanual/at-c.html
#define TYPE_AT_ANNOT (1+ TYPE_AT_MPL)
//implementation uses code from page 9, item 3, of:
//  http://atpp.irrequietus.eu/atpp-c79f4b7.pdf
//NOTE: as of 2014-09-10, no known compiler implements
//the syntax described in this reference.
#define TYPE_AT_PIGEON_HOLE (1+ TYPE_AT_ANNOT)
//implementation uses code based on that of:
//  http://cpptruths.blogspot.com/2014/05/the-pigeonhole-principle-in-c.html
//===============================================

#ifndef TYPE_AT_IMPL
  #define TYPE_AT_IMPL TYPE_AT_N3761
#endif
#if TYPE_AT_MPL == TYPE_AT_IMPL
  #include <boost/mpl/at.hpp>
  #include <boost/mpl/vector.hpp>
#elif TYPE_AT_PIGEON_HOLE == TYPE_AT_IMPL
  #include <utility>//for std::declval
#endif
#include <cstdlib>//for std::size_t
namespace boost
{
namespace composite_storage
{
#if TYPE_AT_ANNOT == TYPE_AT_IMPL
#else
  template <std::size_t N, typename... T>
  struct type_at
  ;
#endif
#if TYPE_AT_N3761 == TYPE_AT_IMPL
  template <std::size_t N, typename S, typename ...T>
  struct type_at<N, S, T...>
  {
      using type = typename type_at<N - 1, T...>::type;
  };
  
  template <typename S, typename ...T>
  struct type_at<0, S, T...> 
  {
      using type = S;
  };
#elif TYPE_AT_MPL == TYPE_AT_IMPL
  template <std::size_t N, typename S, typename... T>
  struct type_at<N,S,T...>
  {
        using 
      type 
        = typename 
          boost::mpl::at_c
          < boost::mpl::vector<S,T...>
          , N
          >::type
        ;
  };
#elif TYPE_AT_ANNOT == TYPE_AT_IMPL
    template 
    <std::size_t N
    , typename... T
      { 0
      : ( N < sizeof...(T) //check if N is  valid index.
        ? sizeof...(T) //When N is valid index.
        : 0 //When N is invalid, produces invalid interval: {0:0}
        )
      }
    >
    //specialized class template.  If N not valid
    //index, this specialization won't be chosen.
  struct type_at
    < N
    , T...
    >
  {
        using 
      type 
        = T{N}
        ;
  };
#elif TYPE_AT_PIGEON_HOLE == TYPE_AT_IMPL
namespace detail
{
  struct any 
  { 
      template<typename T> 
      any(T&& t)
      /**
       * @brief
       *   Provide a conversion constructor
       *   for any type.
       */
      {
      } 
  };

    template
    < typename T //the desired type.
    , typename U //An element in pack expansion
    > 
  struct first
    /**@brief
     *  Used to produce N repetitions of T
     *  where N is sizeof...(Us)
     *  and Us is declared as typename... Us
     *  and U is one of the elements in Us.
     **@usage
     *  typename first<T,Us>::type...
     */ 
  { 
      typedef T type; 
  };

    template
    < typename ...Ts
      //sizeof...(Ts)+1 is the index of the argument desired
      //in the static select method below.
    >
    struct 
  select_impl 
  {
        template<typename U, typename ...Vs>
          static 
        U&&
      select
        ( typename first<any, Ts>::type... 
          //Produces sizeof...(Ts) any formal arguments.
          //Since each any can be constructed from 
          //T&& for any T, the 1st sizeof...(Ts)
          //actual arguments to this function can
          //converted to an any.
          //This leaves the next , unconverted, actual arg 
          //in argument list as the argument desired.
        , U&& u //the desired argument [the (sizeof...(Ts)+1)-th argument].
        , Vs&&...
        ) 
      {
          return static_cast<U&&>(u);
      }
  };

    template
    < std::size_t... Idx
    , typename... Ts
    >
      static 
    auto 
  select
    ( const std::index_sequence<Idx...>&
    , Ts&&... ts
    )
    /**
     *  @brief
     *    Return (N+1)-th ts, where N=sizeof...(Idx).
     */
  {
      return select_impl
             < decltype(Idx)... 
               //converts std::size_t args to types(all of type std::size_t)
               //because called function uses detail::first metafunction
               //which requires type args.
             >::select
                ( static_cast<Ts&&>(ts)... //forward the actual arguments.
                );
  }
}//exit detail namespace

    template<std::size_t N, typename ...T>
    auto 
  value_at(T &&...t)
    /**
     * @brief
     *    return (N+1)-th t.
     */
  {
      return detail::select(std::make_index_sequence<N>(), static_cast<T&&>(t)...);
  }

  template <std::size_t N, typename S,typename... T>
  struct type_at<N,S,T...>
  {
        using 
      type 
        = decltype
          ( value_at<N>
            ( std::declval<S>()
            , std::declval<T>()...
            )
          )
        ;
  };
  
#endif
}//composite_storage namespace
}//boost namespace
#endif
