#pragma once

#include <iostream>
#include <string>
#include <algorithm>
#include <chrono>
using namespace std;


long long gcd(long long n1, long long n2) {
	if (n2 != 0)
		return gcd(n2, n1 % n2);
	else
		return n1;
}

class Fraction {
private:
	long long num;
	long long den;

	void Reduce() {
		if (!den) {
			num = 1;
			return;
		}
		//long long n = num, d = den;
		// get the gcd using Euclidean algorithm

		num /= gcd(num, den);
		den /= gcd(num, den);
	}

public:
	Fraction() : num(1), den(1) {
		//empty;
	}

	Fraction(const long long &numerator, const long long &denominator)
		: num(numerator), den(denominator)
	{
		if (0 && !(den = denominator))
			throw new string("(Fraction constructor) Divide by zero error!\n");
	}

	void setNumerator(const long long &numerator) {
		num = numerator;
		Reduce();
	}

	void setDenominator(const long long &denominator) {
		if (!(den = denominator))
			throw new string("(Fraction setDenominator) Divide by zero error!\n");
	}

	long long getDenominator() const {
		return den;
	}

	long long getNumerator() const {
		return num;
	}

	/*friend ostream& operator<<(ostream& os, const Fraction &rhs) {
	if (rhs.num < 0)
	os << '-' << -rhs.num << '/' << rhs.den;
	else if (rhs.den < 0)
	os << '-' << rhs.num << '/' << -rhs.den;
	else
	os << rhs.num << '/' << rhs.den;
	return os;
	}*/

	/*friend istream& operator>>(istream& is, Fraction &rhs) {
	char ch;
	is >> rhs.num >> ch >> rhs.den;
	if (ch != '/')
	throw new string("(Fraction operator>>) Input failure. Use <numerator>/<denominator>\n");
	if (!rhs.den)
	throw new string("(Fraction operator>>) Divide by zero error!\n");
	return is;
	}*/



	Fraction &operator=(Fraction rhs) {
		num = rhs.num;
		den = rhs.den;
		if (0 && !den) // remove "0 &&" to disallow /0
			throw new string("(Fraction operator=) Divide by zero error!\n");
		return *this;
	}

	Fraction operator+(Fraction rhs) const {
		Fraction result;
		result.num = num * rhs.den + rhs.num*den;
		result.den = den * rhs.den;
		if (!result.den)
			throw new string("(Fraction operator+) Divide by zero error!\n");
		result.Reduce();
		return result;
	}

	Fraction operator-(Fraction rhs) const {
		Fraction result;
		result.num = num * rhs.den - rhs.num*den;
		result.den = den * rhs.den;
		if (!result.den)
			throw new string("(Fraction operator-) Divide by zero error!\n");
		result.Reduce();
		return result;
	}

	Fraction &operator+=(Fraction rhs) {
		num = num * rhs.den + rhs.num*den;
		den = den * rhs.den;
		if (!den)
			throw new string("(Fraction operator+=) Divide by zero error!\n");
		Reduce();
		return *this;
	}

	Fraction &operator-=(Fraction rhs) {
		num = num * rhs.den - rhs.num*den;
		den = den * rhs.den;
		if (!den)
			throw new string("(Fraction operator-=) Divide by zero error!\n");
		Reduce();
		return *this;
	}

	Fraction operator*(Fraction rhs) const {
		Fraction result;
		result.num = num * rhs.num;
		result.den = den * rhs.den;
		if (!result.den)
			throw new string("(Fraction operator*) Divide by zero error!\n");
		result.Reduce();
		return result;
	}

	Fraction operator/(Fraction rhs) const {
		Fraction result;
		result.num = num * rhs.den;
		result.den = den * rhs.num;
		if (!result.den)
			throw new string("(Fraction operator/) Divide by zero error!\n");
		result.Reduce();
		return result;
	}

	Fraction &operator*=(Fraction rhs) {
		num = num * rhs.num;
		den = den * rhs.den;
		if (!den)
			throw new string("(Fraction operator*=) Divide by zero error!\n");
		Reduce();
		return *this;
	}

	Fraction &operator/=(Fraction rhs) {
		num = num * rhs.den;
		den = den * rhs.num;
		if (!den)
			throw new string("(Fraction operator/=) Divide by zero error!\n");
		Reduce();
		return *this;
	}

	void showCF() {
		cout << '[' << num / den << ';';
		Fraction proper(num%den, den);
		while (proper.num != 1) {
			cout << proper.den / proper.num << ',';
			proper = Fraction(proper.den % proper.num, proper.num);
		}
		cout << proper.num << "]";
	}

	Fraction mediant(const Fraction& Mprime) {
		//Fraction Mdnt;
		//Mdnt.setNumerator(num + Mprime.getNumerator());
		//Mdnt.setDenominator(den + Mprime.getDenominator());
		return Fraction(num + Mprime.getNumerator(),
						den + Mprime.getDenominator());
	}
};

ostream& operator<<(ostream& os, const Fraction &rhs) {
	if (rhs.getNumerator() < 0)
		os << '-' << -rhs.getNumerator() << '/' << rhs.getDenominator();
	else if (rhs.getDenominator() < 0)
		os << '-' << rhs.getNumerator() << '/' << -rhs.getDenominator();
	else
		os << rhs.getNumerator() << '/' << rhs.getDenominator();
	return os;
}

istream& operator>>(istream& is, Fraction &rhs) {
	char ch;
	long long n, d;
	is >> n >> ch >> d;
	if (ch != '/')
		throw new string("(Fraction operator>>) Input failure. Use <numerator>/<denominator>\n");
	rhs.setDenominator(d);
	rhs.setNumerator(n);
	if (!rhs.getDenominator())
		throw new string("(Fraction operator>>) Divide by zero error!\n");
	return is;
}