diff --git a/README.md b/README.md index 9503f9a..cf54e8e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # cpp_samples -A repository of samples in C++ \ No newline at end of file +A repository of samples in C++ + +- peano.cpp: Pattern Matching and Type Arithmetic in C++ diff --git a/peano.cpp b/peano.cpp new file mode 100644 index 0000000..e727343 --- /dev/null +++ b/peano.cpp @@ -0,0 +1,214 @@ +// pattern matching and type arithmetic in C++ +// see https://en.wikipedia.org/wiki/Peano_axioms +// see https://en.wikipedia.org/wiki/Successor_function + +// compile and test: "c++ peano.cpp -o peano && ./peano" + +// +// zero is our first natural number +// + +struct zero {}; + +// +// all other natural numbers are either a successor of zero +// or a successor of another successor +// + +template // let's forget that type (see ::sum below) +struct successor {}; + +// +// sum = first + second +// + +// non-empty declaration: this is the end of the recursion +// (second is zero) +template +struct sum { + using type = first; +}; + +// recursive specialization +// the pattern "successor" isn't matching zero +// and is giving us the type of the predecessor we +// forgot in the declaration of ::successor +template +struct sum> { + // second is decremented by pattern matching + using recursion = typename sum::type; + // and the result is incremented + using type = successor; +}; + +// +// product = multiplicand * multiplier +// + +// non-empty declaration: this is the end of the recursion +// (multiplier is zero) +template +struct product { + using type = zero; +}; + +// recursive specialization when multiplier isn't zero +template +struct product> { + using recursion = typename product::type; + using type = typename sum::type; +}; + +// +// difference = minuend - subtrahend +// + +// empty declaration: compilation error if the result is negative +template +struct difference { +}; + +// end of the recursion +template +struct difference { + using type = minuend; +}; + +// recursive specialization +template +struct difference, successor> { + using recursion = difference; + using type = typename recursion::type; +}; + +// +// quotient, remainder = dividend / divisor +// + +namespace impl { + + // NOTE: for the division, we decrement divisor and + // increment _remainder until divisor is zero, at + // this point we increment the result, swap _remainder + // with divisor to restore its value, until dividend is + // zero and _remainder is the final remainder + + // empty declaration: compilation error when dividing by zero + template + struct quotient { + }; + + // specialization when the dividend is zero, this is the end + // of the recursion when the remainder isn't zero + template + struct quotient { + using type = zero; + using remainder = _remainder; + }; + + // specialization when both dividend and divisor are zero, + // the result is incremented and this is the end of the + // recursion when the remainder is zero + template + struct quotient> { + using type = successor; + using remainder = zero; + }; + + // recursive specialization when divisor is zero + // the result is incremented and divisor is restored + // from _remainder + template + struct quotient> { + using recursion = quotient, zero>; + using type = successor; + using remainder = typename recursion::remainder; + }; + + // recursive specialization + template + struct quotient, successor, _remainder> { + using recursion = quotient>; + using type = typename recursion::type; + using remainder = typename recursion::remainder; + }; + +} // namespace impl + +// let's hide the third parameter +template +using quotient = impl::quotient; + +// +// natural_to_peano: convert from unsigned integer +// + +template +struct natural_to_peano { + using recursion = typename natural_to_peano::type; + using type = successor; +}; + +template<> +struct natural_to_peano<0u> { + using type = zero; +}; + +// +// peano_to_natural: convert to unsigned integer +// + +template +struct peano_to_natural { + enum : unsigned { value = 0u }; +}; + +template +struct peano_to_natural> { + enum : unsigned { + recursion = peano_to_natural::value, + value = recursion + 1u + }; +}; + +// +// now let's test! +// + +#include + +int main () { + using four = natural_to_peano<4u>::type; + using six = natural_to_peano<6u>::type; + using ten = sum::type; + using fourty = product::type; + using thirty_four = difference::type; + + // output: thirty_four = 34 + printf("thirty_four = %u\n", + peano_to_natural::value); + + // compilation error + // using negative_difference = difference::type; + + using fourty_over_ten = quotient; + using thirty_four_over_ten = quotient; + + // compilation errors: + // using divide_by_zero = quotient; + // printf("divide_by_zero = %u, %u\n", + // peano_to_natural::value, + // peano_to_natural::value); + + // output: fourty_over_ten = 4, 0 + printf("fourty_over_ten = %u, %u\n", + peano_to_natural::value, + peano_to_natural::value); + + // output: thirty_four_over_ten = 3, 4 + printf("thirty_four_over_ten = %u, %u\n", + peano_to_natural::value, + peano_to_natural::value); + + return 0; +}