#include "NR.h"

#include <cassert>
#include <iostream>

NR::Int PGCD(NR::Int x, NR::Int y) {
	if (x == 0 || y == 0)
		return 1;
	else if (x % y == 0)
		return std::abs(y);
	else
		return PGCD(y, x % y);
}

NR::NR() : m_Numerator(0), m_Denominator(1) {}

NR::NR(NR::Int entier) : m_Numerator(entier), m_Denominator(1) {}

NR::NR(NR::Int numerator, NR::Int denominator) :
	m_Numerator((denominator > 0) ? numerator : -numerator), m_Denominator(std::abs(denominator)) {
	Reduce();
}

void NR::Reduce() {
	NR::Int divisor = PGCD(m_Denominator, m_Numerator);
	m_Denominator /= divisor;
	m_Numerator /= divisor;
	assert(m_Denominator != 0);
}

NR NR::Inverse() const {
	assert(*this != 0);
	return {m_Denominator, m_Numerator};
}

NR::Int NR::GetNumerator() const {
	return m_Numerator;
}

NR::Int NR::GetDenominator() const {
	return m_Denominator;
}

bool NR::operator==(const NR& opNR) const {
	return (m_Numerator * opNR.GetDenominator() == m_Denominator * opNR.GetNumerator());
}

bool NR::operator<(const NR& opNR) const {
	return (m_Numerator * opNR.GetDenominator() < m_Denominator * opNR.GetNumerator());
}

bool NR::operator>(const NR& opNR) const {
	return (m_Numerator * opNR.GetDenominator() > m_Denominator * opNR.GetNumerator());
}

bool NR::operator!=(const NR& opNR) const {
	return !(*this == opNR);
}

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

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

std::ostream& operator<<(std::ostream& os, const NR& opNR) {
	os << opNR.GetNumerator();
	if (opNR.GetDenominator() != 1)
		os << "/" << opNR.GetDenominator();
	return os;
}

std::istream& operator>>(std::istream& is, NR& opNR) {
	char slash;
	is >> opNR.m_Numerator >> slash;
	if (slash != '/') {
		// on revient un charactère en arrière
		is.seekg(is.tellg() - static_cast<std::streampos>(1));
		opNR.m_Denominator = 1;
	} else {
		is >> opNR.m_Denominator;
	}
	opNR.Reduce();
	return is;
}

NR NR::operator+(const NR& opNR) const {
	Int num, den;
	num = m_Numerator * opNR.GetDenominator();
	den = m_Denominator * opNR.GetDenominator();
	num += (opNR.GetNumerator() * m_Denominator);
	NR result(num, num == 0 ? 1 : den);
	return result;
}

NR NR::operator-(const NR& opNR) const {
	Int num, den;
	num = m_Numerator * opNR.GetDenominator();
	den = m_Denominator * opNR.GetDenominator();
	num -= (opNR.GetNumerator() * m_Denominator);
	NR result(num, num == 0 ? 1 : den);
	return result;
}

NR NR::operator*(const NR& opNR) const {
	Int num, den;
	num = m_Numerator * opNR.GetNumerator();
	den = m_Denominator * opNR.GetDenominator();
	NR result(num, num == 0 ? 1 : den);
	return result;
}

NR NR::operator/(const NR& opNR) const {
	Int num, den;
	num = m_Numerator * opNR.GetDenominator();
	den = m_Denominator * opNR.GetNumerator();
	NR result(num, num == 0 ? 1 : den);
	return result;
}

NR& NR::operator+=(const NR& opNR) {
	*this = *this + opNR;
	return *this;
}

NR& NR::operator-=(const NR& opNR) {
	*this = *this - opNR;
	return *this;
}

NR& NR::operator*=(const NR& opNR) {
	*this = *this * opNR;
	return *this;
}

NR& NR::operator/=(const NR& opNR) {
	*this = *this / opNR;
	return *this;
}

NR NR::operator-() const {
	return {-m_Numerator, m_Denominator};
}