// racional.cpp - Definición de la clase CRacional
#include "racional.h"
#include <limits>
using namespace std;

/////////////////////////////////////////////////////////////////
// Métodos de CRacional
// Constructor
CRacional::CRacional(long num, long den):
numerador(num), denominador(den)
{
  if (den == 0) denominador = 1;
  if (den < 0)
  {
    numerador = -numerador;
    denominador = -denominador;
  }
  Simplificar();
}

CRacional& CRacional::Simplificar()
{
  // Máximo común divisor. Algoritmo de Euclides
  long mcd, temp, resto;
  mcd = labs(numerador);
  temp = denominador;

  while (temp > 0)
  {
    resto = mcd % temp;
    mcd = temp;
    temp = resto;
  }

  // Simplificar
  if (mcd > 1)
  {
    numerador /= mcd;
    denominador /= mcd;
  }
  return *this;
}

// Conversión de CRacional a double
CRacional::operator double()
{
  return (double)numerador/(double)denominador;
}

// Operador +=
CRacional& CRacional::operator+=(const CRacional& r)
{
  numerador = numerador * r.denominador +
              denominador * r.numerador;
  denominador = denominador * r.denominador;
  return (*this).Simplificar();
}

// Operador -=
CRacional& CRacional::operator-=(const CRacional& r)
{
  numerador = numerador * r.denominador -
              denominador * r.numerador;
  denominador = denominador * r.denominador;
  return (*this).Simplificar();
}
/*
// Operador + binario
const CRacional CRacional::operator+(const CRacional& r)
{
  CRacional temp(numerador * r.denominador +
                 denominador * r.numerador,
                 denominador * r.denominador);
  return temp;
}

// Operador - binario
const CRacional CRacional::operator-(const CRacional& r)
{
  CRacional temp(numerador * r.denominador -
                 denominador * r.numerador,
                 denominador * r.denominador);
  return temp;
}

const CRacional CRacional::operator+(const CRacional& r)
{
  CRacional temp = *this;
  return temp += r;
}

const CRacional CRacional::operator-(const CRacional& r)
{
  CRacional temp = *this;
  return temp -= r;
}
*/
bool CRacional::operator!()
{
  return !numerador; // devuelve true o false
}

// Operador ++ como prefijo
CRacional CRacional::operator++()
{
  numerador += denominador;
  return *this;
}

// Operador ++ como sufijo
CRacional CRacional::operator++(int)
{
  CRacional temp = *this;
  numerador += denominador;
  return temp;
}

// Operador -- como prefijo
CRacional CRacional::operator--()
{
  numerador -= denominador;
  return *this;
}

// Operador -- como sufijo
CRacional CRacional::operator--(int)
{
  CRacional temp = *this;
  numerador -= denominador;
  return temp;
}

// Operador - unario
CRacional CRacional::operator-()
{
  CRacional temp(-numerador, denominador);
  return temp;
}

/////////////////////////////////////////////////////////////////
// Funciones externas
const CRacional operator+(const double d, const CRacional& r)
{
  CRacional temp = static_cast<long>(d);
  return temp += r;
}

/////////////////////////////////////////////////////////////////
// Funciones externas y amigas de la clase
// Operador + binario
const CRacional operator+(const CRacional& r1, const CRacional& r2)
{
  return CRacional(r1.numerador * r2.denominador +
                   r1.denominador * r2.numerador,
                   r1.denominador * r2.denominador);
}

// Operador - binario
const CRacional operator-(const CRacional& r1, const CRacional& r2)
{
  return CRacional(r1.numerador * r2.denominador -
                   r1.denominador * r2.numerador,
                   r1.denominador * r2.denominador);
}

// Operador *
const CRacional operator*(const CRacional& r1, const CRacional& r2)
{
  return CRacional(r1.numerador * r2.numerador,
                   r1.denominador * r2.denominador);
}

// Operador /
const CRacional operator/(const CRacional& r1, const CRacional& r2)
{
  return CRacional(r1.numerador * r2.denominador,
                   r1.denominador * r2.numerador);
}

// Operador ==
bool operator==(const CRacional& r1, const CRacional& r2)
{
  return  r1.numerador * r2.denominador ==
          r1.denominador * r2.numerador;
}

// Operador <
bool operator<(const CRacional& r1, const CRacional& r2)
{
  return  r1.numerador * r2.denominador <
          r1.denominador * r2.numerador;
}

// Operador >
bool operator>(const CRacional& r1, const CRacional& r2)
{
  return  r1.numerador * r2.denominador >
          r1.denominador * r2.numerador;
}

// Mostrar un número racional
ostream& operator<<(ostream& os, const CRacional& r)
{
  return (os << r.numerador << '/' << r.denominador);
}

// Asignar un número racional
istream& operator>>(istream& is, CRacional& r)
{
  long num = 0, den = 1;
  char car = '\0';

  cout << "(entero[/entero]) ";
  is >> num;        // leer el numerador
  while(is.fail())  // mientras el dato sea incorrecto
  {
    is.clear();
    is.ignore(numeric_limits<int>::max(), '\n');
    cout << "(entero[/entero]) ";
    is >> num;
  }
  if (is.peek() != '\n') // si hay denominador, leerlo
  {
    is >> car;
    if (car == '/')
      is >> den;
    else
      is.clear(ios::badbit); // activar el indicador de error
  }
  if (is) r = CRacional(num, den); // llamar al constructor
  return is;
}
/////////////////////////////////////////////////////////////////

