#include <iostream>
#include <string>
#include <sstream>

using namespace std;

class Fraction {
public:
    explicit Fraction(int n = 0, int d = 1);

    bool setValue(int n, int d = 1);
    double getValue() const;

    int getNumerator() const;       // interface
    int getDenominator() const;     // interface

    void input();

    Fraction &operator=(const Fraction &rhs);

private:
    int numerator;      // any integer  (encapsulation, hermetization)
    int denominator;    // always > 0   (encapsulation, hermetization)
};

Fraction::Fraction(int n, int d) {
    if (setValue(n, d) == false)
        setValue(0, 1);
}

bool Fraction::setValue(int n, int d) {
    if (d <= 0) {
        cerr << "Denominator may not be <= 0." << endl;
        return false;
    }
    this->numerator = n;
    this->denominator = d;

    return true;
}

double Fraction::getValue() const {
    return (double)this->numerator / this->denominator;
}


int Fraction::getNumerator() const {
    return this->numerator;
}

int Fraction::getDenominator() const {
    return this->denominator;
}

void Fraction::input() {
    int n, d;

    cout << "Enter fraction numerator: " << endl;
    cin >> n;
    do {
        cout << "Enter fraction denominator: " << endl;
        cin >> d;
    } while (! setValue(n,d));
}

Fraction &Fraction::operator=(const Fraction &rhs) {

    if (this == &rhs)
        return *this;

    this->numerator = rhs.numerator;
    this->denominator = rhs.denominator;

    return *this;
}


ostream &operator<<(ostream &s, const Fraction &f) {
    s << f.getNumerator() <<  "/" << f.getDenominator();

    return s;
}


Fraction &operator+=(Fraction &lhs, const Fraction &rhs) {

    lhs.setValue(
            lhs.getNumerator() * rhs.getDenominator() + rhs.getNumerator() * lhs.getDenominator(),
            lhs.getDenominator() * rhs.getDenominator()
    );

    return lhs;
}

Fraction &operator*=(Fraction &lhs, const Fraction &rhs) {

    lhs.setValue(
            lhs.getNumerator() * rhs.getNumerator(),
            lhs.getDenominator() * rhs.getDenominator()
    );

    return lhs;
}


Fraction &operator++(Fraction &f) {

    f.setValue(f.getNumerator() + f.getDenominator(), f.getDenominator());

    return f;
}

Fraction operator++(Fraction &f, int) {
    Fraction old = f;

    operator++(f);

    return old;
}


Fraction operator+(const Fraction &lhs, const Fraction &rhs) {
    int n, d;

    n = lhs.getNumerator() * rhs.getDenominator() + rhs.getNumerator() * lhs.getDenominator();
    d = lhs.getDenominator() * rhs.getDenominator();

    return Fraction(n, d);
}

Fraction operator+(const int lhs, const Fraction &rhs) {
    int n, d;

    n = lhs * rhs.getDenominator() + rhs.getNumerator();
    d = rhs.getDenominator();

    return Fraction(n, d);
}

Fraction operator+(const Fraction &lhs, const int rhs) {
    int n, d;

    n = lhs.getNumerator() + rhs * lhs.getDenominator();
    d = lhs.getDenominator();

    return Fraction(n, d);
}

Fraction operator*(const Fraction &lhs, const Fraction &rhs) {
    int n, d;

    n = lhs.getNumerator() * rhs.getNumerator();
    d = lhs.getDenominator() * rhs.getDenominator();

    return Fraction(n, d);
}

bool operator==(const Fraction &lhs, const Fraction &rhs) {
    bool result;

    result = (lhs.getNumerator() * rhs.getDenominator() == rhs.getNumerator() * lhs.getDenominator());

    return result;
}


int main () {
    Fraction f1(1, 2), f2(1, 3), f3;

    // Testing arithmetic addition + operator

    cout << f1 << " + " << f2 << " = " << f1 + f2 << endl;

    // Testing arithmetic multiplication * operator

    cout << f1 << " * " << f2 << " = " << f1 * f2 << endl;

    // Testing arithmetic addition + and assignment = operators

    f3 = f1 + f2;

    cout << f3 << " = " << f1 << " + " << f2 << endl;

    // Testing assignment += operators

    f3 = f1;
    f1 += f2;

    cout << f3 << " += " << f2 << " --> " << f1 << endl;

    // Testing assignment *= operators

    f3 = f1;
    f1 *= f2;

    cout << f3 << " *= " << f2 << " --> " << f1 << endl;

    // Testing pre-increment operator

    cout << "++" << f1 << " --> " << ++f1 << " --> " << f1 << endl;

    // Testing post-increment operator

    cout << f1 << "++" << " --> " << f1++ << " --> " << f1 << endl;

    // Testing logical equality == operator

    if (f1 == f2)
        cout << "Fractions " << f1 << " and " << f2 << " are equal" << endl;
    else
        cout << "Fractions " << f1 << " and " << f2 << " are not equal" << endl;

    // Testing arithmetic addition + operator for int

    cout << 5 << " + " << f2 << " = " << 5 + f2 << endl;

    // Testing arithmetic addition + operator for int

    cout << f2 << " + " << 5 << " = " << f2 + 5 << endl;
}
