#include <iostream>
#include <cmath>
#include <type_traits>
#include <complex>

/* Common metafunctions: */

template<typename T>
struct zero;

template<typename T>
struct one;


template<typename LHS , typename RHS>
struct add;

template<typename LHS , typename RHS>
struct sub;

template<typename LHS , typename RHS>
struct mul;


template<int base , int exponent>
struct positive_pow : public std::integral_constant<long long int , base * positive_pow<base,exponent-1>::value> {};

template<int base>
struct positive_pow<base,0> : public std::integral_constant<long long int,1> {};

template<long long int number , int shift>
struct decimal_leftshift : public std::integral_constant<long long int,number * positive_pow<10, shift>::value> {};

template<long long int number , int shift>
struct decimal_rightshift : public std::integral_constant<long long int,number / positive_pow<10, shift>::value> {};

template<bool CONDITION , int NUMBER , int SHIFT>
struct decimal_shift_chooser
{
    using shifter = decimal_leftshift<NUMBER,SHIFT>;
};

template<long long NUMBER , int SHIFT>
struct decimal_shift_chooser<false,NUMBER,SHIFT>
{
    using shifter = decimal_rightshift<NUMBER,-SHIFT>;
};

template<int number , int shift>
struct decimal_shift
{
    using shifter = typename decimal_shift_chooser<( shift >= 0 ) , number , shift>::shifter;
    static const long long int value = shifter::value;
};

/* Fixed-point implementation: */

using fpbits = long long int;
using fbcount = unsigned int; //Fractional bits count (Precision)

const fbcount DEFAULT_FRACTIONAL_PRECISION = 8; 

template<fpbits BITS , fbcount PRECISION = DEFAULT_FRACTIONAL_PRECISION>
struct fixed_point
{
    operator float()
    {
        return (float)BITS * std::pow(10.0f,-(float)PRECISION);
    };
};

template<int mantissa , int exponent = 0> // MANTISSA x 10^EXPONENT
using decimal = fixed_point<decimal_shift<mantissa , DEFAULT_FRACTIONAL_PRECISION + exponent>::value>; 


template<fpbits BITS , fbcount PRECISION>
struct zero<fixed_point<BITS,PRECISION>> : public fixed_point<0,PRECISION> {};

template<fpbits BITS , fbcount PRECISION>
struct one<fixed_point<BITS,PRECISION>> : public fixed_point<decimal_leftshift<1,PRECISION>::value,PRECISION> {};


template<fpbits BITS1 , fpbits BITS2 , fbcount PRECISION>
struct add<fixed_point<BITS1,PRECISION> , fixed_point<BITS2,PRECISION>> : public fixed_point<BITS1+BITS2 , PRECISION> {};

template<fpbits BITS1 , fpbits BITS2 , fbcount PRECISION>
struct sub<fixed_point<BITS1,PRECISION> , fixed_point<BITS2,PRECISION>> : public fixed_point<BITS1-BITS2 , PRECISION> {};

template<fpbits BITS1 , fpbits BITS2 , fbcount PRECISION>
struct mul<fixed_point<BITS1,PRECISION> , fixed_point<BITS2,PRECISION>> : public fixed_point<decimal_rightshift<BITS1*BITS2,PRECISION>::value , PRECISION> {};


/* Example: */

using pi = decimal<3141592 , -6>; //3141592 x 10^-6 (3,141592)
using pi_2 = mul<pi,decimal<2>>;

using a = decimal<1,-3>;
using b = decimal<1,3>;

using c = mul<a,b>;
using d = sub<b,a>;
using e = add<a,b>;
    
int main()
{
    std::cout << "pi: " << pi() << std::endl;
    std::cout << "2*pi: " << pi_2() << std::endl;

    std::cout << "a: " << a() << std::endl;
    std::cout << "b: " << b() << std::endl;
    std::cout << "a*b: " << c() << std::endl;
    std::cout << "b-a: " << d() << std::endl;
    std::cout << "a+b: " << e() << std::endl;
}