// File: ex6-5.cpp

#include <iostream>
#include <cassert>
using namespace std;

class Fraction
{
    int numer, denom;
public:
    Fraction(int = 0, int = 1);
    void operator!() const;            // print the Fraction
    Fraction& operator~();            // reduce the Fraction
    Fraction operator-() const;        // negative of Fraction
    Fraction operator*() const;        // reciprocal of Fraction
    Fraction& operator+=(const Fraction&);
    Fraction& operator-=(const Fraction&);
    Fraction& operator*=(const Fraction&);
    Fraction& operator/=(const Fraction&);
    Fraction operator+(int) const;
    Fraction operator-(int) const;
    Fraction operator*(int) const;
    Fraction operator/(int) const;
    bool operator>(const Fraction&) const;
    bool operator<(const Fraction&) const;
    bool operator>=(const Fraction&) const;
    bool operator<=(const Fraction&) const;
    bool operator==(const Fraction&) const;
    bool operator!=(const Fraction&) const;
    Fraction operator+(const Fraction&) const;
    Fraction operator-(const Fraction&) const;
    Fraction operator*(const Fraction&) const;
    Fraction operator/(const Fraction&) const;
    Fraction& operator++();            // prefix operator returns by reference
    Fraction operator++(int);        // postix operator returns by value
};

// member function definitions
Fraction::Fraction(int n, int d) : numer(n), denom(d)
{
    assert(d != 0);
}

void Fraction::operator!() const {
    cout << numer << '/' << denom << endl;
}

Fraction& Fraction::operator~() {
    int min;
    // find the minimum of the denom and numer
    min = denom < numer ? denom : numer;
    for (int i = 2; i <= min; i++)
    {
        while ((numer % i == 0) && (denom % i == 0))
        {
            numer /= i;
            denom /= i;
        }
    }
    return *this;
}

Fraction Fraction::operator-() const {
    return Fraction(-numer,denom);
}

Fraction Fraction::operator*() const {
    return Fraction(denom,numer);
}

Fraction& Fraction::operator+=(const Fraction& f) {
    numer = numer*f.denom+denom*f.numer;
    denom = denom*f.denom;
    return *this;
}

Fraction& Fraction::operator-=(const Fraction& f) {
    *this += (-f);
    return *this;
}

Fraction& Fraction::operator*=(const Fraction& f) {
    numer = numer*f.numer;
    denom = denom*f.denom;
    return *this;
}

Fraction& Fraction::operator/=(const Fraction& f) {
    *this *= (*f);
    return *this;
}

bool Fraction::operator>(const Fraction& f) const {
    return (float) numer/denom > (float) f.numer/f.denom;
}

bool Fraction::operator<(const Fraction& f) const {
    return f>*this;
}

bool Fraction::operator==(const Fraction& f) const {
    return numer*f.denom == denom*f.numer;
}

bool Fraction::operator!=(const Fraction& f) const {
    return !(*this == f);
}

bool Fraction::operator<=(const Fraction& f) const {
    return !(*this > f);
}

bool Fraction::operator>=(const Fraction& f) const {
    return !(*this<f);
}

Fraction Fraction::operator+(const Fraction& f) const {
    return Fraction(numer*f.denom+denom*f.numer,denom*f.denom);
}

Fraction Fraction::operator-(const Fraction& f) const {
    return Fraction(numer*f.denom-denom*f.numer,denom*f.denom);
}

Fraction Fraction::operator*(const Fraction& f) const {
    return Fraction(numer*f.numer,denom*f.denom);
}

Fraction Fraction::operator/(const Fraction& f) const {
    return (*this) * (*f);
}

Fraction Fraction::operator+(int i) const {
    return Fraction(numer+i*denom,denom);
}

Fraction Fraction::operator-(int i) const {
    return (*this) + -i;
}

Fraction Fraction::operator*(int i) const {
    return Fraction(numer*i,denom);
}

Fraction Fraction::operator/(int i) const {
    return Fraction(numer,i*denom);
}

// prefix increment operator
Fraction& Fraction::operator++() {
    numer += denom;
    return *this;
}

// postfix increment operator
Fraction Fraction::operator++(int) {        // Note dummy int argument
    Fraction temp(*this);
    ++*this;                            // call the prefix operator
    return temp;
}


int main()
{
    Fraction f(3,4);           // initialize Fraction f & g
    Fraction g(1,2);
    cout << "!f ";  !f;
    cout << "!g ";  !g;
    cout << endl;
    cout << "-g ";  !-g;
    cout << "*g ";  !*g;
    Fraction h = g + f;
    cout << endl;
    cout << "h=g+f " << " !h ";  !h;
    cout << "!~h ";  !~h;
    cout << endl;
    cout << "f+g ";  ! (f + g);
    cout << "f-g ";  ! (f - g);
    cout << "f*g ";  ! (f * g);
    cout << "f/g ";  ! (f / g);
    cout << endl;
    cout << "f+=g "; !~(f+=g);
    cout << "f-=g "; !~(f-=g);
    cout << "f*=g "; !~(f*=g);
    cout << "f/=g "; !~(f/=g);
    cout << endl;
    cout << "f<g " << (f<g) << endl;
    cout << "f>g " << (f>g) << endl;
    cout << "f==g " << (f==g) << endl;
    cout << "f!=g " << (f!=g) << endl;
    cout << "f<=g " << (f<=g) << endl;
    cout << "f>=g " << (f>=g) << endl;
    cout << endl;
    cout << "f+5 ";  !(f+5);
    cout << "f-5 ";  !(f-5);
    cout << "f*5 ";  !(f*5);
    cout << "f/5 ";  !(f/5);
    cout << endl;
    cout << "f+=5 "; f+=5;   cout << "!~f ";  !~f;  // How does this work?
    cout << "++f "; !++f; cout << "f="; !f;
    cout << "f++ "; !f++; cout << "f="; !f;
}