#ifndef RATIONAL_H
#define RATIONAL_H

#include <iostream>
using namespace std;

// Rational<T> Numbers Template

template<class T>
class Rational {
public:
  // default constructor
  Rational() : num(0), den(1) {}

  // constructor
  Rational(T n, T d = 1);

  // copy constructor
  Rational(const Rational<T>& rhs) : num(rhs.num), den(rhs.den) {}

  // destructor
  ~Rational(void) {}

  // access functions
  T numerator(void) const { return num; }
  T denominator(void) const { return den; }

  // assignment operators
  Rational<T>& operator=(const Rational<T>& rhs);
  Rational<T>& operator=(T rhs);

  // unary operators
  Rational<T> operator+(void) const { return *this; }
  Rational<T> operator-(void) const { return Rational<T>(-num, den); }
  Rational<T> invert(void) const { return Rational<T>(den, num); }

  // binary shortcut operators
  const Rational<T>& operator+=(const Rational<T>& rhs);
  const Rational<T>& operator-=(const Rational<T>& rhs);
  const Rational<T>& operator*=(const Rational<T>& rhs);
  const Rational<T>& operator/=(const Rational<T>& rhs);
  const Rational<T>& operator+=(T rhs);
  const Rational<T>& operator-=(T rhs);
  const Rational<T>& operator*=(T rhs);
  const Rational<T>& operator/=(T rhs);

  // increment/decrement iterators
  const Rational<T>& operator++();
  const Rational<T>  operator++(int);
  const Rational<T>& operator--();
  const Rational<T>  operator--(int);

  // -- better implemented as explicit conversion
  // -- function toDouble (see below)
  // operator double(void) const { return double(num)/den; }

private:
  // Data
  T num;  // numerator
  T den;  // denominator (keep > 0!)

  // auxillary helper function to normalize the Rationals
  T gcd(T, T);
};

// assignment operators
template<class T>
inline Rational<T>& Rational<T>::operator=(const Rational<T>& rhs) {
  num = rhs.num;
  den = rhs.den;
  return *this;
}

template<class T>
inline Rational<T>& Rational<T>::operator=(T rhs) {
  num = rhs;
  den = 1;
  return *this;
}

// Rational -> double conversion
template<class T>
inline double toDouble (const Rational<T>& r) {
  return double(r.numerator())/r.denominator();
}

// Rational -> T conversions
template<class T>
inline T trunc(const Rational<T>& r) {
  return r.numerator() / r.denominator();
}

template<class T>
inline T floor(const Rational<T>& r) {
  T q = r.numerator() / r.denominator();
  return (r.numerator() < 0 && r.denominator() != 1) ? --q : q;
}

template<class T>
inline T ceil(const Rational<T>& r) {
  T q = r.numerator() / r.denominator();
  return (r.numerator() >= 0 && r.denominator() != 1) ? ++q : q;
}

// double -> Rational conversion
template<class T>
Rational<T> toRational(double x, int iterations = 5);

// binary operators
template<class T>
const Rational<T> operator+(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
const Rational<T> operator-(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
const Rational<T> operator/(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
Rational<T> rabs(const Rational<T>& rhs);

// boolean operators
template<class T>
bool operator==(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
bool operator!=(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
bool operator<=(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
bool operator>=(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
bool operator<(const Rational<T>& lhs, const Rational<T>& rhs);
template<class T>
bool operator>(const Rational<T>& lhs, const Rational<T>& rhs);

// output operator
template<class T>
ostream& operator<< (ostream& ostr, const Rational<T>& r);
template<class T>
istream& operator>> (istream& istr, Rational<T>& r);

#include "Rational.cpp"
#endif