#include <iostream>

/* SI unit exponents */
struct Unit {
    int M;
    int KG;
    int S;
};

constexpr Unit operator*(Unit a, Unit b) {
    return Unit{a.M + b.M, a.KG + b.KG, a.S + b.S};
}
constexpr Unit operator/(Unit a, Unit b) {
    return Unit{a.M - b.M, a.KG - b.KG, a.S - b.S};
}

/* quantity in SI unit */
template <Unit U>
class Quantity {
  public:
    double magnitude;
    constexpr explicit Quantity(double magnitude): magnitude{magnitude} {}

    bool operator==(Quantity const& other) const {
        return magnitude == other.magnitude;
    }
    std::partial_ordering operator<=>(Quantity const& other) const {
        return magnitude <=> other.magnitude;
    }
};

/* short names for quantities */
using Length = Quantity<Unit{1, 0, 0}>;         /* hossz, m */
using Area = Quantity<Unit{2, 0, 0}>;           /* terület, m^2 */
using Mass = Quantity<Unit{0, 1, 0}>;           /* tömeg, kg */
using Time = Quantity<Unit{0, 0, 1}>;           /* idő, s */
using Speed = Quantity<Unit{1, 0, -1}>;         /* sebesség, m/s */
using Acceleration = Quantity<Unit{1, 0, -2}>;  /* gyorsulás, m/s^2 */
using Force = Quantity<Unit{1, 1, -2}>;         /* erő, N=m*kg/s^2 */
using Energy = Quantity<Unit{2, 1, -2}>;        /* energia, J=m^2*kg/s^2 */
using Power = Quantity<Unit{2, 1, -3}>;         /* teljesítmény, Watt = Joule/s = m^2*kg/s^3 */


/* operators for quantities */
template <Unit U>
Quantity<U> operator+(Quantity<U> a, Quantity<U> b) {
    return Quantity<U>{a.magnitude + b.magnitude};
}

template <Unit U>
Quantity<U> operator-(Quantity<U> a, Quantity<U> b) {
    return Quantity<U>{a.magnitude - b.magnitude};
}

template <Unit U1, Unit U2>
Quantity<U1*U2> operator*(Quantity<U1> a, Quantity<U2> b) {
    return Quantity<U1*U2>{a.magnitude * b.magnitude};
}

template <Unit U1, Unit U2>
Quantity<U1/U2> operator/(Quantity<U1> a, Quantity<U2> b) {
    return Quantity<U1/U2>{a.magnitude / b.magnitude};
}

/* generic stream inserter operator for quantities */
template <Unit U>
std::ostream & operator<<(std::ostream & os, Quantity<U> m) {
    os << m.magnitude << ' ';
    bool elso = true;
    if (U.M != 0) {
        elso = false;
        os << "m^" << U.M;
    }
    if (U.KG != 0) {
        if (!elso) os << '*';
        elso = false;
        os << "kg^" << U.KG;
    }
    if (U.S != 0) {
        if (!elso) os << '*';
        elso = false;
        os << "s^" << U.S;
    }
    return os;
}


/* specialized stream inserter operator for force */
template <>
std::ostream & operator<<(std::ostream & os, Force m) {
    os << m.magnitude << " N";
    return os;
}


/* user-defined literals for SI quantities */
constexpr Length operator "" _m (long double magnitude) {
    return Length(magnitude);
}

constexpr Length operator "" _km (long double magnitude) {
    return Length(magnitude * 1000.0);
}

constexpr Time operator "" _s (long double magnitude) {
    return Time(magnitude);
}

constexpr Time operator "" _h (long double magnitude) {
    return Time(magnitude * 3600);
}

constexpr Mass operator "" _kg (long double magnitude) {
    return Mass(magnitude);
}


int main() {
    std::cout << "Ferrari ===" << std::endl;
    Speed v = 100.0_km / 1.0_h;
    std::cout << "100 km/h = " << v << std::endl;

    Acceleration a = v / 4.0_s;
    Force f = 1450.0_kg * a;          /* Newton */
    std::cout << "F = " << f << std::endl;
}

