diff --git a/src/util.h b/src/util.h index 43480785ee..ce99249952 100644 --- a/src/util.h +++ b/src/util.h @@ -11,6 +11,7 @@ #include "hash.h" #include +#include #include #include #include @@ -160,13 +161,25 @@ inline int64_t abs64(int64_t n) return (n >= 0 ? n : -n); } -// Small class to represent fractions. We could do more sophisticated things like reduction using GCD, and overloaded -// multiplication, but we don't need it, because this is used in very limited places, and we actually in many of the -// algorithms where this needs to be used need to carefully control the order of multiplication and division using the -// numerator and denominator. +//! +//! \brief Class to represent fractions and common operations with built in simplification. This supports integer operations +//! for consensus critical code where floating point would cause problems across different architectures and/or compiler +//! implementations +//! class Fraction { public: - Fraction() {} + Fraction() + : m_numerator(0) + , m_denominator(1) + {} + + Fraction(const Fraction& f) + : Fraction(f.GetNumerator(), f.GetDenominator()) + {} + + Fraction(const Fraction& f, const bool& simplify) + : Fraction(f.GetNumerator(), f.GetDenominator(), simplify) + {} Fraction(const int64_t& numerator, const int64_t& denominator) @@ -178,9 +191,44 @@ class Fraction { } } - bool isNonZero() + Fraction(const int64_t& numerator, + const int64_t& denominator, + const bool& simplify) + : Fraction(numerator, denominator) + { + if (simplify) { + Simplify(); + } + } + + Fraction(const int64_t& numerator) + : Fraction(numerator, 1) + {} + + bool isZero() const + { + // The denominator cannot be zero by construction rules. + return m_numerator == 0; + } + + bool isNonZero() const + { + return !isZero(); + } + + bool isPositive() const + { + return (m_denominator > 0 && m_numerator > 0) || (m_denominator < 0 && m_numerator < 0); + } + + bool isNonNegative() const { - return m_denominator != 0 && m_numerator != 0; + return isPositive() || isZero(); + } + + bool isNegative() const + { + return !isNonNegative(); } constexpr int64_t GetNumerator() const @@ -193,9 +241,226 @@ class Fraction { return m_denominator; } + bool IsSimplified() const + { + return std::gcd(m_numerator, m_denominator) == (int64_t) 1; + } + + void Simplify() + { + // Nice that we are at C++17! :) + int64_t gcd = std::gcd(m_numerator, m_denominator); + + // If both numerator and denominator are negative, + // change the sign of gcd to flip both to positive. + if (m_numerator < 0 && m_denominator < 0) { + gcd = -gcd; + } + + m_numerator = m_numerator / gcd; + m_denominator = m_denominator / gcd; + + } + + Fraction operator=(const Fraction& rhs) + { + m_numerator = rhs.GetNumerator(); + m_denominator = rhs.GetDenominator(); + + return *this; + } + + Fraction operator+(const Fraction& rhs) const + { + Fraction slhs(*this, true); + Fraction srhs(rhs, true); + + return Fraction(slhs.GetNumerator() * srhs.GetDenominator() + slhs.GetDenominator() * srhs.GetNumerator(), + slhs.GetDenominator() * srhs.GetDenominator(), true); + } + + Fraction operator+(const int64_t& rhs) const + { + Fraction slhs(*this, true); + + return Fraction(slhs.GetNumerator() + rhs * slhs.GetDenominator(), slhs.GetDenominator(), true); + } + + Fraction operator-(const Fraction& rhs) const + { + return (*this + Fraction(-rhs.GetNumerator(), rhs.GetDenominator())); + } + + Fraction operator-(const int64_t& rhs) const + { + return (*this + -rhs); + } + + Fraction operator*(const Fraction& rhs) const + { + Fraction slhs(*this, true); + Fraction srhs(rhs, true); + + return Fraction(slhs.GetNumerator() * srhs.GetNumerator(), slhs.GetDenominator() * srhs.GetDenominator(), true); + } + + Fraction operator*(const int64_t& rhs) const + { + Fraction slhs(*this, true); + + return Fraction(slhs.GetNumerator() * rhs, slhs.GetDenominator(), true); + } + + Fraction operator/(const Fraction& rhs) const + { + return (*this * Fraction(rhs.GetDenominator(), rhs.GetNumerator())); + } + + Fraction operator/(const int64_t& rhs) const + { + Fraction slhs(*this, true); + + return Fraction(slhs.GetNumerator(), slhs.GetDenominator() * rhs, true); + } + + Fraction operator+=(const Fraction& rhs) + { + Simplify(); + + *this = *this + rhs; + + return *this; + } + + Fraction operator+=(const int64_t& rhs) + { + Simplify(); + + *this = *this + rhs; + + return *this; + } + + Fraction operator-=(const Fraction& rhs) + { + Simplify(); + + *this = *this - rhs; + + return *this; + } + + Fraction operator-=(const int64_t& rhs) + { + Simplify(); + + *this = *this - rhs; + + return *this; + } + + Fraction operator*=(const Fraction& rhs) + { + Simplify(); + + *this = *this * rhs; + + return *this; + } + + Fraction operator*=(const int64_t& rhs) + { + Simplify(); + + *this = *this * rhs; + + return *this; + } + + Fraction operator/=(const Fraction& rhs) + { + Simplify(); + + *this = *this / rhs; + + return *this; + } + + Fraction operator/=(const int64_t& rhs) + { + Simplify(); + + *this = *this / rhs; + + return *this; + } + + bool operator==(const Fraction& rhs) const + { + Fraction slhs(*this, true); + Fraction srhs(rhs, true); + + return (slhs.GetNumerator() == srhs.GetNumerator() && slhs.GetDenominator() == slhs.GetDenominator()); + } + + bool operator!=(const Fraction& rhs) const + { + return !(*this == rhs); + } + + bool operator<=(const Fraction& rhs) const + { + return (rhs - *this).isNonNegative(); + } + + bool operator>=(const Fraction& rhs) const + { + return (*this - rhs).isNonNegative(); + } + + bool operator<(const Fraction& rhs) const + { + return (rhs - *this).isPositive(); + } + + bool operator>(const Fraction& rhs) const + { + return (*this - rhs).isPositive(); + } + + bool operator==(const int64_t& rhs) const + { + return (*this == Fraction(rhs)); + } + + bool operator!=(const int64_t& rhs) const + { + return !(*this == rhs); + } + + bool operator<=(const int64_t& rhs) const + { + return *this <= Fraction(rhs); + } + + bool operator>=(const int64_t& rhs) const + { + return *this >= Fraction(rhs); + } + + bool operator<(const int64_t& rhs) const + { + return *this < Fraction(rhs); + } + + bool operator>(const int64_t& rhs) const + { + return *this > Fraction(rhs); + } + private: - int64_t m_numerator = 0; - int64_t m_denominator = 1; + int64_t m_numerator; + int64_t m_denominator; }; inline std::string leftTrim(std::string src, char chr)