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

using namespace std;

class ExcGeneric {
  public:
    virtual string details() = 0;
};

class ExcZero : public ExcGeneric {
  public:
    string details() {     
        return "zero is not allowed here";
    }
};

class ExcNegative : public ExcGeneric {
  public:
    ExcNegative(double value) : 
        value(value) {}
  
    string details() {
        ostringstream stream;
        
        stream << "negative argument: " << this->value;
        
        return stream.str();
    }
    
  private:    
    double value;
};

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

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

    int getNumerator() const;
    int getDenominator() const;

    void input();

    Fraction &operator =(const Fraction &rhs);

private:
    int numerator;      // any integer
    int denominator;    // always > 0
};

Fraction::Fraction() {
    this->numerator = 0;
    this->denominator = 1;
}

Fraction::Fraction(int n, int d) {
    setValue(n, d);
}

void Fraction::setValue(int n, int d) {
   if (d == 0)
        throw ExcZero();
    else if (d < 0)
        throw ExcNegative(d);
    this->numerator = n;
    this->denominator = d;
}

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;
    bool error = 1;

    cout << "Enter fraction numerator: " << endl;
    cin >> n;
    cout << "Enter fraction denominator: " << endl;
    cin >> d;
    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 () {
    try {

        Fraction f1(1, 2), f2(1, 3), f3;
//        Fraction f1(1, 0), f2(1, -3), f3;

        // Testing arithmetic addition + operator

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

        // Testing arithmetic multiplication * operator

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

        f3.input();

        cout << f3 << endl;
    } catch (ExcGeneric &e) {
        cerr << e.details() << endl;
    }
    
    return 0; 
}
