// 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'