Program Listing for File fractions.hpp

Return to documentation for file (py2cpp/fractions.hpp)

// -*- coding: utf-16 -*-
#pragma once

#include <boost/operators.hpp>
#include <cmath>
#include <numeric>
#include <type_traits>

namespace fun
{

template <typename Mn>
constexpr auto gcd(Mn _m, Mn _n) -> Mn
{
    return _m == 0 ? abs(_n) : _n == 0 ? abs(_m) : gcd(_n, _m % _n);
}

template <typename Mn>
constexpr auto lcm(Mn _m, Mn _n) -> Mn
{
    return (_m != 0 && _n != 0) ? (abs(_m) / gcd(_m, _n)) * abs(_n) : 0;
}

template <typename Z>
struct Fraction : boost::totally_ordered<Fraction<Z>,
                      boost::totally_ordered2<Fraction<Z>, Z,
                          boost::multipliable2<Fraction<Z>, Z,
                              boost::dividable2<Fraction<Z>, 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 <typename U>
    constexpr auto cmp(const Fraction<U>& frac) const
    {
        // if (_denominator == frac._denominator) {
        //     return _numerator - frac._numerator;
        // }
        return _numerator * frac._denominator - _denominator * frac._numerator;
    }

    constexpr auto operator==(const Fraction<Z>& 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<Z>& 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<Z>& rhs)
    // {
    //     return lhs * rhs.denominator() < rhs.numerator();
    // }
};


template <typename Z>
constexpr auto operator+(const Z& c, const Fraction<Z>& frac) -> Fraction<Z>
{
    return frac + c;
}

template <typename Z>
constexpr auto operator-(const Z& c, const Fraction<Z>& frac) -> Fraction<Z>
{
    return c + (-frac);
}

// /*!
//  * @brief
//  *
//  * @param[in] c
//  * @param[in] frac
//  * @return Fraction<Z>
//  */
// template <typename Z>
// constexpr Fraction<Z> operator*(const Z& c, const Fraction<Z>& frac)
// {
//     return frac * c;
// }

template <typename Z>
constexpr auto operator+(int&& c, const Fraction<Z>& frac) -> Fraction<Z>
{
    return frac + c;
}

template <typename Z>
constexpr auto operator-(int&& c, const Fraction<Z>& frac) -> Fraction<Z>
{
    return (-frac) + c;
}

template <typename Z>
constexpr auto operator*(int&& c, const Fraction<Z>& frac) -> Fraction<Z>
{
    return frac * c;
}

template <typename Stream, typename Z>
auto operator<<(Stream& os, const Fraction<Z>& frac) -> Stream&
{
    os << frac.numerator() << "/" << frac.denominator();
    return os;
}

// For template deduction
// Integral{Z} Fraction(const Z &, const Z &) -> Fraction<Z>;

} // namespace fun