#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;
}
