#ifndef RATIONAL_HPP_ #define RATIONAL_HPP_ #include #include #include #include #include #include #include "gcd.hpp" #include "ioflags.hpp" /// Represent a rational number (fraction) as a numerator and denominator. template class rational { public: /// Convenience typedef for the integral type of the numerator and denominator. typedef T value_type; /// Exception class if the denominator is ever zero. class zero_denominator : public std::logic_error { public: /// Construct the exception object. zero_denominator(std::string const& what) : logic_error(what) {} }; /// Default constructor and constructor from a single value. /// As a default constructor, initializes to zero. /// Otherwise, initializes to the integer @p num. /// @param num The integer value to use as the initial value rational(value_type num = 0): numerator_(num), denominator_(1) {} /// Construct a rational number /// @param num numerator /// @param den denominator /// @throws zero_denominator if @p den == 0 rational(value_type num, value_type den); /// Initialize the rational number with an approximation of @p r /// @param r the initial value rational(double r); /// Copy from a different type of rational. template rational(rational const& that); /// Return the numerator value_type numerator() const { return numerator_; } /// Return the denominator value_type denominator() const { return denominator_; } /// Convert the rational number to another type, especially floating-point. template U as() const { return static_cast(numerator()) / denominator(); } /// Assignment of an integer rational& operator=(value_type); // optimization to avoid an unneeded call to reduce() /// Assignment of a rational with a different size. template rational& operator=(rational const& rhs); /// Addition assignment operator rational& operator+=(rational const& rhs); /// Addition assignment operator rational& operator+=(value_type const& rhs); /// Subtraction assignment operator rational& operator-=(rational const& rhs); /// Subtraction assignment operator rational& operator-=(value_type const& rhs); /// Multiplication assignment operator rational& operator*=(rational const& rhs); /// Multiplication assignment operator rational& operator*=(value_type const& rhs); /// Division assignment operator rational& operator/=(rational const& rhs); /// Division assignment operator rational& operator/=(value_type const& rhs); /// Pre-increment rational& operator++(); /// Pre-decrement rational& operator--(); /// Post-increment rational operator++(int); /// Post-decrement rational operator--(int); private: /// Reduce the numerator and denominator by their GCD. void reduce(); /// Reduce the numerator and denominator, and normalize the signs of both, /// that is, ensure denominator is not negative. void normalize(); /// Scale an integer of type @p U to the value_type. If @p U has more /// digits than @p value_type shift @p value to the right. template value_type scale(U value); value_type numerator_; value_type denominator_; }; template rational::rational(value_type num, value_type den) : numerator_(num), denominator_(den == value_type() ? throw zero_denominator("zero denominator") : den) { normalize(); } template rational::rational(double r) : numerator_(static_cast(r / 100000)), denominator_(static_cast(100000)) {} template template rational::rational(rational const& that) : numerator_(scale(that.numerator())), denominator_(scale(that.denominator())) { reduce(); } template template T rational::scale(U value) { if (std::numeric_limits::digits >= std::numeric_limits::digits) return T(value); else return T(value >> (std::numeric_limits::digits - std::numeric_limits::digits)); } template void rational::normalize() { if (denominator_ < value_type()) { denominator_ = -denominator_; numerator_ = -numerator_; } reduce(); } template void rational::reduce() { value_type div(gcd(numerator(), denominator())); if (div == value_type()) throw zero_denominator("zero denominator"); numerator_ /= div; denominator_ /= div; } template rational& rational::operator=(T num) { numerator_ = num; denominator_ = value_type(1); return *this; } template template rational& rational::operator=(rational const& rhs) { numerator_ = scale(rhs.numerator()); denominator_ = scale(rhs.denominator()); reduce(); return *this; } template rational& rational::operator+=(rational const& rhs) { numerator_ = numerator() * rhs.denominator() + rhs.numerator() * denominator(); denominator_ *= rhs.denominator(); reduce(); return *this; } template rational& rational::operator+=(value_type const& rhs) { numerator_ = numerator() + rhs * denominator(); reduce(); return *this; } template rational& rational::operator-=(rational const& rhs) { numerator_ = numerator() * rhs.denominator() - rhs.numerator() * denominator(); denominator_ *= rhs.denominator(); reduce(); return *this; } template rational& rational::operator-=(value_type const& rhs) { numerator_ = numerator() - rhs * denominator(); reduce(); return *this; } template rational& rational::operator*=(rational const& rhs) { numerator_ *= rhs.numerator(); denominator_ *= rhs.denominator(); reduce(); return *this; } template rational& rational::operator*=(value_type const& rhs) { numerator_ *= rhs; reduce(); return *this; } template rational& rational::operator/=(rational const& rhs) { if (rhs.numerator() == value_type()) throw zero_denominator("divide by zero"); numerator_ *= rhs.denominator(); denominator_ *= rhs.numerator(); normalize(); return *this; } template rational& rational::operator/=(value_type const& rhs) { if (rhs == value_type()) throw zero_denominator("divide by zero"); denominator_ *= rhs; normalize(); return *this; } template rational& rational::operator++() { numerator_ += denominator(); return *this; } template rational rational::operator++(int) { rational result(*this); ++*this; return result; } template rational& rational::operator--() { numerator_ -= denominator(); return *this; } template rational rational::operator--(int) { rational result(*this); --*this; return result; } /// Negate a rational number template rational operator-(rational const& r) { return rational(-r.numerator(), r.denominator()); } template rational absval(rational const& r) { using namespace std; return rational(abs(r.numerator()), r.denominator()); } /// Addition template rational operator+(rational lhs, rational const& rhs) { lhs += rhs; return lhs; } /// Addition template rational operator+(rational lhs, T const& rhs) { lhs += rhs; return lhs; } /// Addition template rational operator+(T const& lhs, rational rhs) { rhs += lhs; return rhs; } /// Subtraction template rational operator-(rational lhs, rational const& rhs) { lhs -= rhs; return lhs; } /// Subtraction template rational operator-(rational lhs, T const& rhs) { lhs -= rhs; return lhs; } /// Subtraction template rational operator-(T const& lhs, rational rhs) { // Gotta be a little tricky. rhs += -lhs; return -rhs; } /// Multiplication template rational operator*(rational lhs, rational const& rhs) { lhs *= rhs; return lhs; } /// Multiplication template rational operator*(rational lhs, T const& rhs) { lhs *= rhs; return lhs; } /// Multiplication template rational operator*(T const& lhs, rational rhs) { rhs *= lhs; return rhs; } /// Division template rational operator/(rational lhs, rational const& rhs) { lhs /= rhs; return lhs; } /// Division template rational operator/(rational lhs, T const& rhs) { lhs /= rhs; return lhs; } /// Division template rational operator/(T const& lhs, rational rhs) { return rational(lhs * rhs.denominator(), rhs.numerator()); } /// Equality comparison template bool operator==(rational const& a, rational const& b) { return a.numerator() == b.numerator() and a.denominator() == b.denominator(); } /// Equality comparison template bool operator==(rational const& lhs, T rhs) { return lhs.denominator() == 1 and lhs.numerator() == rhs; } /// Equality comparison template bool operator==(T lhs, rational const& rhs) { return rhs.denominator() == 1 and rhs.numerator() == lhs; } /// Less-than comparison template bool operator<(rational const& a, rational const& b) { return a.numerator() * b.denominator() < b.numerator() * a.denominator(); } /// Less-than comparison template bool operator<(rational const& a, T const& b) { return a.numerator() < b * a.denominator(); } /// Less-than comparison template bool operator<(T const& a, rational const& b) { return a * b.denominator() < b.numerator(); } /// Inequality comparison template inline bool operator!=(rational const& a, rational const& b) { return not (a == b); } /// Inequality comparison template inline bool operator!=(rational const& a, T b) { return not (a == b); } /// Inequality comparison template inline bool operator!=(T a, rational const& b) { return not (a == b); } /// Less-than-or-equal comparison template inline bool operator<=(rational const& a, rational const& b) { return not (b < a); } /// Less-than-or-equal comparison template inline bool operator<=(rational const& a, T const& b) { return not (b < a); } /// Less-than-or-equal comparison template inline bool operator<=(T const& a, rational const& b) { return not (b < a); } /// Greater-than comparison template inline bool operator>(rational const& a, rational const& b) { return b < a; } /// Greater-than comparison template inline bool operator>(rational const& a, T const& b) { return b < a; } /// Greater-than comparison template inline bool operator>(T const& a, rational const& b) { return b < a; } /// Greater-than-or-equal comparison template inline bool operator>=(rational const& a, rational const& b) { return not (b > a); } /// Greater-than-or-equal comparison template inline bool operator>=(rational const& a, T const& b) { return not (b > a); } /// Greater-than-or-equal comparison template inline bool operator>=(T const& a, rational const& b) { return not (b > a); } /// Input operator template std::basic_istream& operator>>(std::basic_istream& in, rational& rat) { typename std::basic_istream::sentry sentry(in, false); ioflags flags(in); T n = T(); if (not (in >> n)) // Error reading the numerator. return in; in >> std::noskipws; char sep('\0'); if (not (in >> sep)) // Error reading the separator character. return in; else if (sep != '/') { // Push sep back into the input stream, so the next input operation // will read it. in.unget(); rat = n; return in; } else { T d = T(); if (in >> d) // Successfully read numerator, separator, and denominator. rat = rational(n, d); } return in; } /// Output operator template std::basic_ostream& operator<<(std::basic_ostream& out, rational const& rat) { typename std::basic_ostream::sentry sentry(out); std::ostringstream stream; stream << rat.numerator() << '/' << rat.denominator(); out << stream.str(); return out; } #endif