// Little exercice: implementation of a rational class.
// Implemented in C++11
// (c) Luc Hermitte, 2013, under Boost SoftWare Licence.
#include <cmath>
#include <cassert>
#include <ostream>
template <typename Int> Int pgcd(Int a, Int b) {
while (a != 0) {
b %= a;
if (b == 0) return a;
a%= b;
}
return b;
}
/** Helper class to check invariants.
*/
template <typename CheckedClass>
struct InvariantChecker {
InvariantChecker(CheckedClass const& cc_) : m_cc(cc_)
{ m_cc.check_invariants(); }
~InvariantChecker()
{ m_cc.check_invariants(); }
private:
CheckedClass const& m_cc;
};
/**rational class.
* @invariant <tt>denominator() > 0</tt>
* @invariant visible objects are normalized.
* @note not optimized.
*/
struct rational
{
typedef long long int_type;
rational(int_type num_=0, int_type den_=1)
: rational(num_, den_, no_normalize{})
{
normalize();
check_invariants();
}
int_type numerator() const { return m_numerator;}
int_type denominator() const { return m_denominator;}
/**
*@pre <tt>m_numerator *rhs_.denominator() < std::numeric_limit<int_type>::max()</tt>, unchecked
*@pre <tt>m_denominator * rhs_.numerator() < std::numeric_limit<int_type>::max()</tt>, unchecked
*@pre <tt>lhs_.n * rhs_.d + lhs_.d * rhs_.n < std::numeric_limit<int_type>::max()</tt>, unchecked
*/
rational& operator+=(rational const& rhs_) {
InvariantChecker<rational> check(*this);
m_numerator = m_numerator *rhs_.denominator() + m_denominator * rhs_.numerator();
m_denominator *= rhs_.denominator();
normalize();
return *this;
}
rational& operator-=(rational const& rhs_) {
InvariantChecker<rational> check(*this);
return (*this) += - rhs_;
}
rational& operator*=(rational const& rhs_) {
InvariantChecker<rational> check(*this);
m_numerator *= rhs_.numerator();
m_denominator *= rhs_.denominator();
normalize();
return *this;
}
/**
*@pre <tt>rhs_ != 0</tt>
*/
rational& operator/=(rational const& rhs_) ;
rational operator-() const {
return rational(-numerator(), denominator(), no_normalize{});
}
private:
struct no_normalize{};
rational(int_type num_, int_type den_, no_normalize)
: m_numerator(num_), m_denominator(den_)
{}
void normalize() {
const int_type p = pgcd(std::abs(numerator()), denominator());
m_numerator /= p;
m_denominator /= p;
}
friend rational inverse(rational const&);
void check_invariants() const {
assert(denominator() && "Denominator can't be null");
assert(denominator()>0 && "Denominator can't be negative");
assert(pgcd(std::abs(numerator()), denominator()) == 1 && "The rational shall be normalized");
}
friend class InvariantChecker<rational>;
int_type m_numerator;
int_type m_denominator;
};
bool operator==(rational const& rhs_, rational const& lhs_)
{ return rhs_.numerator() == lhs_.numerator() && rhs_.denominator() == lhs_.denominator(); }
bool operator!=(rational const& rhs_, rational const& lhs_)
{ return ! (rhs_ == lhs_); }
/** Inverses a rational.
* @pre <tt>r != 0</tt>
*/
rational inverse(rational const& r) {
assert(r != 0 && "Zero cannot be inversed");
return rational(
r.numerator() < 0 ? - r.denominator() : r.denominator(),
std::abs(r.numerator()),
rational::no_normalize{});
}
rational& rational::operator/=(rational const& rhs_) {
assert(rhs_ != rational{0} && "Cannot divide by zero");
InvariantChecker<rational> check(*this);
m_numerator *= rhs_.denominator();
m_denominator *= rhs_.numerator();
normalize();
return *this;
}
std::ostream & operator<<(std::ostream & os, const rational & v)
{
return os << "\\frac{" << v.numerator() << "}{" << v.denominator() << "}";
}
rational operator+(rational rhs_, rational const& lhs_)
{ return rhs_ += lhs_; }
rational operator-(rational rhs_, rational const& lhs_)
{ return rhs_ -= lhs_; }
rational operator*(rational rhs_, rational const& lhs_)
{ return rhs_ *= lhs_; }
rational operator/(rational rhs_, rational const& lhs_)
{ return rhs_ /= lhs_; }
#include <iostream>
int main ()
{
assert(rational(0,1).numerator() == 0);
assert(rational(0,1).denominator() == 1);
assert(rational(10,5).numerator() == 2);
assert(( rational{-10,5} == - rational{2}));
assert(( inverse(rational{-11,5}) == rational{-5,11}));
assert((rational{1} + rational{3,2} == rational{5,2}));
assert((rational{1,4} + rational{2,3} == rational{11,12}));
assert((rational{5} * rational{3,2} == rational{15,2}));
assert((rational{10} * rational{3,2} == rational{15}));
assert((rational{5} / rational{3,2} == rational{10,3}));
assert((rational{10} / rational{3,2} == rational{20,3}));
assert((inverse(rational{1,3}) == rational{3}));
// precondition failures
// auto f1 = rational{1,2} / rational{0};
// auto f2 = inverse(rational{0});
std::cout << rational{4,8} + rational{2,3} << "\n";
}
// Vim: let $CXXFLAGS='-std=c++0x -g'