#include <iostream>
using value_t = unsigned long long int;
namespace peano
{
struct zero {
static constexpr value_t value = 0;
using type = zero;
};
template <typename T>
struct succ {
static constexpr value_t value = 1 + T::value;
using pred = typename T::type;
using type = succ<typename T::type>;
};
template <typename S, typename T>
struct add: succ<typename add<S, typename T::pred>::type>::type {};
template <typename S>
struct add<S, zero>: S::type {};
template <typename S, typename T>
struct mul: add<typename mul<S, typename T::pred>::type, S>::type {};
template <typename S>
struct mul<S, zero>: zero {};
template <typename S>
struct factorial: mul<S, typename factorial<typename S::pred>::type>::type {};
template <>
struct factorial<zero>: succ<zero>::type {};
// constants
using _0 = zero;
using _1 = succ<_0>::type;
using _2 = succ<_1>::type;
using _3 = succ<_2>::type;
using _4 = succ<_3>::type;
using _5 = succ<_4>::type;
using _6 = succ<_5>::type;
using _7 = succ<_6>::type;
using _8 = succ<_7>::type;
using _9 = succ<_8>::type;
} // namespace peano
int main() {
// 9 + 9 == 18
std::cout
<< peano::add<peano::_9, peano::_9>::value
<< std::endl;
// 5 * 7 == 35
std::cout
<< peano::mul<peano::_5, peano::_7>::value
<< std::endl;
// let's try a more complex expression
// 2 * 7 + 3 * (4 + 5) == 41
std::cout <<
peano::add<
peano::mul<peano::_2, peano::_7>,
peano::mul<
peano::_3,
peano::add<peano::_4, peano::_5>
>
>::value
<< std::endl;
// and now factorial: 5! == 120
std::cout
<< peano::factorial<peano::_5>::value
<< std::endl;
return 0;
}