// Booleans

template <bool x>
struct bool_t
{
    static const bool value = x;
};

typedef bool_t<false> false_t;
typedef bool_t<true> true_t;

false_t false_;
true_t true_;

struct if_t
{
    template <class A, class B> A operator()(true_t, A, B) const;
    template <class A, class B> B operator()(false_t, A, B) const;
} if_;

struct or_t
{
    template <bool x> bool_t<x> operator()(false_t, bool_t<x>) const;
    template <bool x> true_t operator()(true_t, bool_t<x>) const;
} or_;

struct and_t
{
    template <bool x> bool_t<x> operator()(true_t, bool_t<x>) const;
    template <bool x> false_t operator()(false_t, bool_t<x>) const;
} and_;

// Integers

template <int n>
struct int_t
{
    int_t<n> operator()(...);

    static const int value = n;
};

int_t<0> zero;
int_t<1> one;
int_t<2> two;
int_t<3> three;
int_t<4> four;

struct add_t
{
    template <int x, int y>
    int_t<x+y> operator()(int_t<x>, int_t<y>) const;
} add;

struct times_t
{
    template <int x, int y>
    int_t<x*y> operator()(int_t<x>, int_t<y>) const;
} times;

struct divide_t
{
    template <int x, int y> int_t<x / y> operator()(int_t<x>, int_t<y>) const;
} divide;

struct dec_t
{
    template <int x> int_t<x-1> operator()(int_t<x>) const;
} dec;

struct equal_t
{
    template <int n> true_t operator()(int_t<n>, int_t<n>) const;
    template <int x, int y> false_t operator()(int_t<x>, int_t<y>) const;
} equal;

// Placeholders

template <int n> struct placeholder;

template <>
struct placeholder<1>
{
    template <class T> T operator()(T, ...) const;
};

template <>
struct placeholder<2>
{
    template <class T1, class T2> T2 operator()(T1, T2, ...) const;
};

placeholder<1> _1;
placeholder<2> _2;

// Bind implementation.

template <class Op>
Op identity(Op);

template <class Op, class T1>
struct apply1
{
    typedef typeof(identity(Op())(T1())) type;
};

template <class Op, class T1, class T2>
struct apply2
{
    typedef typeof(identity(Op())(T1(), T2())) type;
};

template <class Op, class B1>
struct bind1_impl
{
    template <class T1>
    typename apply1<Op,
        typename apply1<B1, T1>::type
    >::type operator()(T1) const;

    template <class T1, class T2>
    typename apply1<Op,
        typename apply2<B1, T1, T2>::type
    >::type operator()(T1) const;
};

template <class Op, class B1, class B2>
struct bind2_impl
{
    template <class T1>
    typename apply2<Op,
        typename apply1<B1, T1>::type,
        typename apply1<B2, T1>::type
    >::type operator()(T1) const;

    template <class T1, class T2>
    typename apply2<Op,
        typename apply2<B1, T1, T2>::type,
        typename apply2<B2, T1, T2>::type
    >::type operator()(T1) const;
};

struct bind_t
{
    template <class Op, class B1>
    bind1_impl<Op, B1> operator()(Op, B1) const;

    template <class Op, class B1, class B2>
    bind2_impl<Op, B1, B2> operator()(Op, B1, B2) const;
} bind;

// Factorial

struct factorial_t
{
    static factorial_t instance;

    template <class T>
    typeof(if_(equal(zero, T()), one, bind(times, _1, bind(instance, bind(dec, _1)))))(T())
        operator()(T) const;
} factorial;

#include <iostream>

int main()
{
    typedef typeof(factorial(int_t<10>())) result;
    std::cout<<"10! = "<<result::value<<'\n';
}
