:github_url: https://github.com/svenevs/exhale-companion .. _program_listing_file_py2cpp_fractions.hpp: Program Listing for File fractions.hpp ====================================== |exhale_lsh| :ref:`Return to documentation for file ` (``py2cpp/fractions.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp // -*- coding: utf-16 -*- #pragma once #include #include #include #include namespace fun { template constexpr auto gcd(Mn _m, Mn _n) -> Mn { return _m == 0 ? abs(_n) : _n == 0 ? abs(_m) : gcd(_n, _m % _n); } template constexpr auto lcm(Mn _m, Mn _n) -> Mn { return (_m != 0 && _n != 0) ? (abs(_m) / gcd(_m, _n)) * abs(_n) : 0; } template struct Fraction : boost::totally_ordered< Fraction, boost::totally_ordered2< Fraction, Z, boost::multipliable2, Z, boost::dividable2, Z>>>> { Z _numerator; Z _denominator; constexpr Fraction(Z &&numerator, Z &&denominator) noexcept : _numerator{std::move(numerator)}, _denominator{ std::move(denominator)} { this->normalize(); } constexpr Fraction(const Z &numerator, const Z &denominator) : _numerator{numerator}, _denominator{denominator} { this->normalize(); } constexpr void normalize() { auto common = gcd(this->_numerator, this->_denominator); if (common == Z(1)) { return; } // if (common == Z(0)) [[unlikely]] return; // both num and den are zero if (this->_denominator < Z(0)) { common = -common; } this->_numerator /= common; this->_denominator /= common; } constexpr explicit Fraction(Z &&numerator) noexcept : _numerator{std::move(numerator)}, _denominator(Z(1)) {} constexpr explicit Fraction(const Z &numerator) : _numerator{numerator}, _denominator(Z(1)) {} constexpr auto numerator() const -> const Z & { return _numerator; } constexpr auto denominator() const -> const Z & { return _denominator; } constexpr auto abs() const -> Fraction { return Fraction(std::abs(_numerator), std::abs(_denominator)); } constexpr void reciprocal() { std::swap(_numerator, _denominator); } constexpr auto operator-() const -> Fraction { auto res = Fraction(*this); res._numerator = -res._numerator; return res; } constexpr auto operator+(const Fraction &frac) const -> Fraction { if (_denominator == frac._denominator) { return Fraction(_numerator + frac._numerator, _denominator); } auto d = _denominator * frac._denominator; auto n = frac._denominator * _numerator + _denominator * frac._numerator; return Fraction(n, d); } constexpr auto operator-(const Fraction &frac) const -> Fraction { return *this + (-frac); } constexpr auto operator*(const Fraction &frac) const -> Fraction { auto n = _numerator * frac._numerator; auto d = _denominator * frac._denominator; return Fraction(std::move(n), std::move(d)); } constexpr auto operator/(Fraction frac) const -> Fraction { frac.reciprocal(); return *this * frac; } constexpr auto operator+(const Z &i) const -> Fraction { auto n = _numerator + _denominator * i; return Fraction(std::move(n), _denominator); } constexpr auto operator-(const Z &i) const -> Fraction { return *this + (-i); } // /*! // * @brief // * // * @param[in] i // * @return Fraction // */ // constexpr Fraction operator*(const Z& i) const // { // auto n = _numerator * i; // return Fraction(n, _denominator); // } // /*! // * @brief // * // * @param[in] i // * @return Fraction // */ // constexpr Fraction operator/(const Z& i) const // { // auto d = _denominator * i; // return Fraction(_numerator, d); // } constexpr auto operator+=(const Fraction &frac) -> Fraction & { return *this = *this + frac; } constexpr auto operator-=(const Fraction &frac) -> Fraction & { return *this = *this - frac; } constexpr auto operator*=(const Fraction &frac) -> Fraction & { return *this = *this * frac; } constexpr auto operator/=(const Fraction &frac) -> Fraction & { return *this = *this / frac; } constexpr auto operator+=(const Z &i) -> Fraction & { return *this = *this + i; } constexpr auto operator-=(const Z &i) -> Fraction & { return *this = *this - i; } constexpr auto operator*=(const Z &i) -> Fraction & { const auto common = gcd(i, this->_denominator); if (common == Z(1)) { this->_numerator *= i; } // else if (common == Z(0)) [[unlikely]] // both i and den are zero // { // this->_numerator = Z(0); // } else { this->_numerator *= (i / common); this->_denominator /= common; } return *this; } constexpr auto operator/=(const Z &i) -> Fraction & { const auto common = gcd(this->_numerator, i); if (common == Z(1)) { this->_denominator *= i; } // else if (common == Z(0)) [[unlikely]] // both i and num are zero // { // this->_denominator = Z(0); // } else { this->_denominator *= (i / common); this->_numerator /= common; } return *this; } template constexpr auto cmp(const Fraction &frac) const { // if (_denominator == frac._denominator) { // return _numerator - frac._numerator; // } return _numerator * frac._denominator - _denominator * frac._numerator; } constexpr auto operator==(const Fraction &rhs) const -> bool { if (this->_denominator == rhs._denominator) { return this->_numerator == rhs._numerator; } return (this->_numerator * rhs._denominator) == (this->_denominator * rhs._numerator); } constexpr auto operator<(const Fraction &rhs) const -> bool { if (this->_denominator == rhs._denominator) { return this->_numerator < rhs._numerator; } return (this->_numerator * rhs._denominator) < (this->_denominator * rhs._numerator); } constexpr auto operator==(const Z &rhs) const -> bool { return this->_denominator == Z(1) && this->_numerator == rhs; } constexpr auto operator<(const Z &rhs) const -> bool { return this->_numerator < (this->_denominator * rhs); } constexpr auto operator>(const Z &rhs) const -> bool { return this->_numerator > (this->_denominator * rhs); } // /*! // * @brief // * // * @return double // */ // constexpr explicit operator double() // { // return double(_numerator) / _denominator; // } // /** // * @brief // * // */ // friend constexpr bool operator<(const Z& lhs, const Fraction& rhs) // { // return lhs * rhs.denominator() < rhs.numerator(); // } }; template constexpr auto operator+(const Z &c, const Fraction &frac) -> Fraction { return frac + c; } template constexpr auto operator-(const Z &c, const Fraction &frac) -> Fraction { return c + (-frac); } // /*! // * @brief // * // * @param[in] c // * @param[in] frac // * @return Fraction // */ // template // constexpr Fraction operator*(const Z& c, const Fraction& frac) // { // return frac * c; // } template constexpr auto operator+(int &&c, const Fraction &frac) -> Fraction { return frac + c; } template constexpr auto operator-(int &&c, const Fraction &frac) -> Fraction { return (-frac) + c; } template constexpr auto operator*(int &&c, const Fraction &frac) -> Fraction { return frac * c; } template auto operator<<(Stream &os, const Fraction &frac) -> Stream & { os << frac.numerator() << "/" << frac.denominator(); return os; } // For template deduction // Integral{Z} Fraction(const Z &, const Z &) -> Fraction; } // namespace fun