//-----------------------------------------------------------------------------
// boost mpl/aux_/full_lambda.hpp header file
// See http://www.boost.org for updates, documentation, and revision history.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2001-02
// Aleksey Gurtovoy
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee, 
// provided that the above copyright notice appears in all copies and 
// that both the copyright notice and this permission notice appear in 
// supporting documentation. No representations are made about the 
// suitability of this software for any purpose. It is provided "as is" 
// without express or implied warranty.

#if !defined(BOOST_PP_IS_ITERATING)

///// header body

#ifndef BOOST_MPL_AUX_FULL_LAMBDA_HPP_INCLUDED
#define BOOST_MPL_AUX_FULL_LAMBDA_HPP_INCLUDED

#if !defined(BOOST_MPL_PREPROCESSING_MODE)
#   include "boost/mpl/lambda_fwd.hpp"
#   include "boost/mpl/bind.hpp"
#   include "boost/mpl/protect.hpp"
#   include "boost/mpl/quote.hpp"
#   include "boost/mpl/bool.hpp"
#   include "boost/mpl/int_fwd.hpp"
#   include "boost/mpl/aux_/template_arity.hpp"
#   include "boost/mpl/aux_/config/ttp.hpp"
#endif

#include "boost/mpl/aux_/lambda_expr.hpp"
#include "boost/mpl/aux_/lambda_arity_param.hpp"
#include "boost/mpl/aux_/config/use_preprocessed.hpp"

#if !defined(BOOST_MPL_NO_PREPROCESSED_HEADERS) \
 && !defined(BOOST_MPL_PREPROCESSING_MODE)

#   define BOOST_MPL_PREPROCESSED_HEADER full_lambda.hpp
#   include "boost/mpl/aux_/include_preprocessed.hpp"

#else

#   include "boost/mpl/limits/arity.hpp"
#   include "boost/mpl/aux_/preprocessor/default_params.hpp"
#   include "boost/mpl/aux_/preprocessor/params.hpp"
#   include "boost/mpl/aux_/preprocessor/enum.hpp"
#   include "boost/mpl/aux_/preprocessor/repeat.hpp"

#   include "boost/preprocessor/iterate.hpp"
#   include "boost/preprocessor/comma_if.hpp"
#   include "boost/preprocessor/inc.hpp"
#   include "boost/preprocessor/cat.hpp"

namespace boost {
namespace mpl {

// local macros, #undef-ined at the end of the header
#   define AUX_LAMBDA_PARAMS(i, param) \
    BOOST_MPL_PP_PARAMS(i, param) \
    /**/

#   define AUX_LAMBDA_BIND_PARAMS(param) \
    BOOST_MPL_PP_PARAMS( \
          BOOST_MPL_METAFUNCTION_MAX_ARITY \
        , param \
        ) \
    /**/

#   define AUX_LAMBDA_BIND_N_PARAMS(i, param) \
    BOOST_PP_COMMA_IF(i) \
    BOOST_MPL_PP_PARAMS(i, param) \
    /**/

#   define AUX_ARITY_PARAM(param) \
    BOOST_MPL_AUX_LAMBDA_ARITY_PARAM(param) \
    /**/

template<
      typename T
    , typename Protect = false_
    AUX_ARITY_PARAM(typename Arity = int_< aux::template_arity<T>::value >)
    >
struct lambda_impl
{
    BOOST_MPL_AUX_IS_LAMBDA_EXPR(false_)
    typedef T type;
};

template<
      typename T
    AUX_ARITY_PARAM(typename Arity = int_< aux::template_arity<T>::value >)
    >
struct lambda
    : lambda_impl<T,false_ AUX_ARITY_PARAM(Arity)>
{
};

#if !defined(BOOST_MPL_NO_LAMBDA_HEURISTIC)

#define n BOOST_MPL_METAFUNCTION_MAX_ARITY
namespace aux {

template<
      BOOST_MPL_PP_DEFAULT_PARAMS(n,bool C,false)
    >
struct lambda_or
    : true_
{
};

template<>
struct lambda_or< BOOST_MPL_PP_ENUM(n,false) >
    : false_
{
};

} // namespace aux
#undef n

template< int N, typename Protect >
struct lambda_impl< arg<N>, Protect AUX_ARITY_PARAM(int_<-1>) >
{
    BOOST_MPL_AUX_IS_LAMBDA_EXPR(true_)
    typedef arg<N> type;
};

#endif // BOOST_MPL_NO_LAMBDA_HEURISTIC

#define BOOST_PP_ITERATION_LIMITS (0, BOOST_MPL_METAFUNCTION_MAX_ARITY)
#define BOOST_PP_FILENAME_1 "boost/mpl/aux_/full_lambda.hpp"
#include BOOST_PP_ITERATE()

//: special case for 'protect'
template< typename T, typename Protect >
struct lambda_impl< protect<T>, Protect AUX_ARITY_PARAM(int_<1>) >
{
    BOOST_MPL_AUX_IS_LAMBDA_EXPR(false_)
    typedef protect<T> type;
};

//: specializations for main 'bind', 'bind1st' and 'bind2nd' forms
template<
      typename F, AUX_LAMBDA_BIND_PARAMS(typename T)
    , typename Protect
    >
struct lambda_impl<
      bind<F,AUX_LAMBDA_BIND_PARAMS(T)>
    , Protect 
    AUX_ARITY_PARAM(int_<BOOST_PP_INC(BOOST_MPL_METAFUNCTION_MAX_ARITY)>)
    >
{
    BOOST_MPL_AUX_IS_LAMBDA_EXPR(false_)
    typedef bind<F, AUX_LAMBDA_BIND_PARAMS(T)> type;
};

template<
      typename F, typename T
    , typename Protect
    >
struct lambda_impl< bind1st<F,T>, Protect AUX_ARITY_PARAM(int_<2>) >
{
    BOOST_MPL_AUX_IS_LAMBDA_EXPR(false_)
    typedef bind1st<F,T> type;
};

template<
      typename F, typename T
    , typename Protect
    >
struct lambda_impl< bind2nd<F,T>, Protect AUX_ARITY_PARAM(int_<2>) >
{
    BOOST_MPL_AUX_IS_LAMBDA_EXPR(false_)
    typedef bind2nd<F,T> type;
};

#   undef AUX_ARITY_PARAM
#   undef AUX_LAMBDA_BIND_N_PARAMS
#   undef AUX_LAMBDA_BIND_PARAMS
#   undef AUX_LAMBDA_PARAMS

} // namespace mpl
} // namespace boost

#endif // BOOST_MPL_USE_PREPROCESSED_HEADERS
#endif // BOOST_MPL_AUX_FULL_LAMBDA_HPP_INCLUDED

///// iteration, depth == 1

#elif BOOST_PP_ITERATION_DEPTH() == 1
#define i BOOST_PP_FRAME_ITERATION(1)

#if i > 0

template<
      template< AUX_LAMBDA_PARAMS(i, typename P) > class F
    , AUX_LAMBDA_PARAMS(i, typename T)
    >
struct lambda< F<AUX_LAMBDA_PARAMS(i, T)> AUX_ARITY_PARAM(int_<i>) >
    : lambda_impl< F<AUX_LAMBDA_PARAMS(i, T)>, true_ AUX_ARITY_PARAM(int_<i>) >
{
};

#if defined(BOOST_MPL_NO_LAMBDA_HEURISTIC)

template<
      template< AUX_LAMBDA_PARAMS(i, typename P) > class F
    , AUX_LAMBDA_PARAMS(i, typename T)
    , typename Protect
    >
struct lambda_impl< 
      F<AUX_LAMBDA_PARAMS(i, T)>, Protect AUX_ARITY_PARAM(int_<i>)
    >
{
#   define AUX_LAMBDA_INVOCATION(unused, i, T) \
    BOOST_PP_COMMA_IF(i) \
    typename lambda_impl< BOOST_PP_CAT(T, BOOST_PP_INC(i)) >::type \
    /**/

    typedef BOOST_PP_CAT(bind,i)<
          BOOST_PP_CAT(quote,i)<F>
        , BOOST_MPL_PP_REPEAT(i, AUX_LAMBDA_INVOCATION, T)
        > type;

#   undef AUX_LAMBDA_INVOCATION
};

#else // BOOST_MPL_NO_LAMBDA_HEURISTIC

#   define AUX_LAMBDA_RESULT(unused, i, T) \
    BOOST_PP_COMMA_IF(i) \
    typename BOOST_PP_CAT(T, BOOST_PP_INC(i))::type \
    /**/

namespace aux {

template<
      typename IsLE, typename Protect
    , template< AUX_LAMBDA_PARAMS(i, typename P) > class F
    , AUX_LAMBDA_PARAMS(i, typename L)
    >
struct BOOST_PP_CAT(le_result,i)
{
    typedef F<
          BOOST_MPL_PP_REPEAT(i, AUX_LAMBDA_RESULT, L)
        > type;
};

template<
      template< AUX_LAMBDA_PARAMS(i, typename P) > class F
    , AUX_LAMBDA_PARAMS(i, typename L)
    >
struct BOOST_PP_CAT(le_result,i)< true_,false_,F,AUX_LAMBDA_PARAMS(i, L) >
{
    typedef BOOST_PP_CAT(bind,i)<
          BOOST_PP_CAT(quote,i)<F>
        , BOOST_MPL_PP_REPEAT(i, AUX_LAMBDA_RESULT, L)
        > type;
};

template<
      template< AUX_LAMBDA_PARAMS(i, typename P) > class F
    , AUX_LAMBDA_PARAMS(i, typename L)
    >
struct BOOST_PP_CAT(le_result,i)< true_,true_,F,AUX_LAMBDA_PARAMS(i, L) >
{
    typedef protect< BOOST_PP_CAT(bind,i)<
          BOOST_PP_CAT(quote,i)<F>
        , BOOST_MPL_PP_REPEAT(i, AUX_LAMBDA_RESULT, L)
        > > type;
};

} // namespace aux

#   define AUX_LAMBDA_INVOCATION(unused, i, T) \
    typedef lambda_impl< BOOST_PP_CAT(T, BOOST_PP_INC(i)) > \
        BOOST_PP_CAT(l,BOOST_PP_INC(i)); \
    /**/

#   define AUX_IS_LAMBDA_EXPR(unused, i, unused2) \
    BOOST_PP_COMMA_IF(i) \
    BOOST_PP_CAT(l,BOOST_PP_INC(i))::is_le::value \
    /**/

template<
      template< AUX_LAMBDA_PARAMS(i, typename P) > class F
    , AUX_LAMBDA_PARAMS(i, typename T)
    , typename Protect
    >
struct lambda_impl< 
      F<AUX_LAMBDA_PARAMS(i, T)>, Protect AUX_ARITY_PARAM(int_<i>)
    >
{
    BOOST_MPL_PP_REPEAT(i, AUX_LAMBDA_INVOCATION, T)
    typedef aux::lambda_or<
          BOOST_MPL_PP_REPEAT(i, AUX_IS_LAMBDA_EXPR, unused)
        > is_le;

    typedef typename aux::BOOST_PP_CAT(le_result,i)<
          typename is_le::type
        , Protect
        , F
        , AUX_LAMBDA_PARAMS(i, l)
        >::type type;
};

#   undef AUX_IS_LAMBDA_EXPR
#   undef AUX_LAMBDA_INVOCATION
#   undef AUX_LAMBDA_RESULT

#endif // BOOST_MPL_NO_LAMBDA_HEURISTIC
#endif // i > 0

template<
      typename F AUX_LAMBDA_BIND_N_PARAMS(i, typename T)
    , typename Protect
    >
struct lambda_impl<
      BOOST_PP_CAT(bind,i)<F AUX_LAMBDA_BIND_N_PARAMS(i, T)>
    , Protect AUX_ARITY_PARAM(int_<BOOST_PP_INC(i)>)
    >
{
    BOOST_MPL_AUX_IS_LAMBDA_EXPR(false_)
    typedef BOOST_PP_CAT(bind,i)<
          F
        AUX_LAMBDA_BIND_N_PARAMS(i, T)
        > type;
};

#undef i
#endif // BOOST_PP_IS_ITERATING
