////////////////////////////////////////////////////////////////////////
///
/// \file lambda_functor.cpp
///
/// \brief Example demonstrating how functors with a sig template can
///        simplify the usage of the cmath functions overloaded for
///        Boost.Units' quantity type in functors created with the
///        Boost.Lambda library and stored in Boost.Function objects.
///
/// \author Torsten Maehne
/// \date   2008-06-06
///
////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <boost/function.hpp>
#include <boost/units/io.hpp>
#include <boost/units/cmath.hpp>
#include <boost/units/pow.hpp>
#include <boost/units/systems/si.hpp>

// Include boost/units/lambda.hpp instead of boost/lambda/lambda.hpp
// for a convenient usage of Boost.Units' quantity, unit, and absolute
// types in lambda expressions. The header augments Boost.Lambda's
// return type detuction system to recognize the new types so that not
// for each arithmetic operation the return type needs to be
// explicitely specified.
#include <boost/units/lambda.hpp>

#include <boost/lambda/bind.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>

/// \brief Functor for the sin function from cmath.hpp.
struct sin_t {
    template<typename Args>
    class sig {
    private:
        typedef typename boost::tuples::element<1, Args>::type arg1_type;
        BOOST_TYPEOF_NESTED_TYPEDEF(nested, (boost::units::sin(boost::units::typeof_::make<arg1_type>())))
    public:
        typedef typename nested::type type;
    };

    template<typename T>
    typename sig<typename boost::tuples::tuple<sin_t, T> >::type
    operator()(const T& arg) const { return boost::units::sin(arg); }
};

/// \brief Templatized functor for the pow function from pow.hpp.
template<typename Rat>
struct pow_t {
    template<typename Args>
    class sig {
    private:
        typedef typename boost::tuples::element<1, Args>::type arg1_type;
        BOOST_TYPEOF_NESTED_TYPEDEF(nested, (boost::units::pow<Rat>(boost::units::typeof_::make<arg1_type>())))
    public:
        typedef typename nested::type type;
    };

    template<typename Y>
    typename sig<typename boost::tuples::tuple<pow_t<Rat>, Y> >::type
    operator()(const Y& arg) const { return boost::units::pow<Rat>(arg); }
};


int main(int argc, char **argv) {

    using namespace std;
    namespace bl = boost::lambda;
    namespace bu = boost::units;
    namespace si = boost::units::si;

    // Test if an instance of sin_t works like boost::units::sin()
    cout << "sin_t()(0.5 * M_PI * si::radian) = "
         << sin_t()(0.5 * M_PI * si::radian)
         << endl;

    // Test sig template
    BOOST_STATIC_ASSERT((boost::is_same<sin_t::sig<boost::tuples::tuple<sin_t, bu::quantity<si::plane_angle> > >::type, bu::quantity<si::dimensionless> >::value == true));

    // Create a functor object using Boost.Lambda's bind
    boost::function<bu::quantity<si::dimensionless> (bu::quantity<si::plane_angle>)>
        sin_f = bl::bind(sin_t(), bl::_1);

    cout << "sin_f(0.5 * M_PI * si::radian) = "
         << sin_f(0.5 * M_PI * si::radian)
         << endl;


    // Create a functor describing a sinusoidal waveform
    boost::function<bu::quantity<si::electric_potential> (bu::quantity<si::time>)>
        wave_f = 1.5 * si::volt * bl::bind(sin_t(), 2.0 * M_PI * si::radian * 50.0 * si::hertz * bl::_1);

    cout << "wave_f(5.0e-3 * si::second) = "
         << wave_f(5.0e-3 * si::second)
         << endl;


    // Create a functor by binding a lambda placeholder to a templated functor
    boost::function<bu::quantity<bu::multiply_typeof_helper<si::length, si::length>::type> (bu::quantity<si::length>)>
        pow2_f = bl::bind(pow_t<bu::static_rational<2> >(), bl::_1);

    cout << "pow2_f(2.0 * si::meter) = "
         << pow2_f(2.0 * si::meter) << endl;

    return 0;
}

