#ifndef RATIONAL_H_INC
#define RATIONAL_H_INC

#include <algorithm>
#include <ostream>
#include <stdexcept>

#define BOOST_FORCE_SYMMETRIC_OPERATORS
#include <boost/operators.hpp>

struct DivisionDurchNullException : public std::domain_error
{
  explicit DivisionDurchNullException(std::string const& what_arg) : std::domain_error(what_arg) {}
};


class Rational : boost::ordered_euclidian_ring_operators<Rational //Operatoren +, -, *, /, >, >=, <=, !=
               , boost::unit_steppable<Rational           //Postinkrement und -dekrement
               , boost::equivalent<Rational              //operator== 
               , boost::ordered_euclidian_ring_operators<Rational, int
               , boost::equivalent<Rational, int
               , boost::ordered_euclidian_ring_operators<double, Rational
               , boost::equivalent<double, Rational
               > > > > > > >
{
  //Invariante: zaehler und nenner sind immer vollstaendig gekuerzt, 
	//            nenner immer positiv (Vorzeichen befindet sich am zaehler)
  int zaehler;
  int nenner;

  void kuerzen()  //koennte const sein, da am eigentlichen Zustand nichts geaendert wird, 
                  //allerdings wirds wegen der Invariante sowieso nur bei nonconst-Funktionen aufgerufen.
  {
    //ggt bestimmen durch Euklids Algorithmus
    int z = zaehler>0 ? zaehler : -zaehler;
    int n = nenner;
    if (n>z) std::swap(n,z);
    while (n>0)
    {
      z %= n;
      std::swap(z,n);
    }
    //ggt ist jetzt z
    zaehler /= z;
    nenner /= z;
  }

public:
  //Konstruktoren:
  Rational(int z = 0, unsigned int n = 1)
    : zaehler(n>0?z:-z),
    nenner(n>0?n:-n)
  { 
    if (n == 0) throw DivisionDurchNullException("Division durch Null beim Erstellen eines Rational-Objektes.");
    kuerzen(); 
  } //Default- und Konvertierungs-Konstruktor für ints gleich mit eingebaut...
  //Copy-Ctor compilergeneriert

  //Destruktor compilergeneriert

  //Assginment für Rational compilergeneriert
  //Assignment für int implizit generiert durch Konvertierungs-Konstruktor

  //Umwandlungsfunktionen:
  Rational kehrwert() const
  {
    Rational tmp(nenner, zaehler);
    return tmp;
  }

  double toDouble() const
  {
    return static_cast<double>(zaehler)/nenner;
  }

  //Vorzeichen
  Rational operator- () const
  {
    Rational tmp(*this);
    tmp.zaehler *= -1;
    return tmp;
  }
  Rational operator+ () const { return *this; }

  //Grundrechenarten
  Rational& operator+= (Rational const& rhs)
  {
    zaehler *= rhs.nenner;
    zaehler += nenner*rhs.zaehler;
    nenner *= rhs.nenner;
    kuerzen();
    return *this;
  }
  Rational& operator-= (Rational const& rhs)
  {
    return operator+=(-rhs);
  }

  Rational& operator*= (Rational const& rhs)
  {
    zaehler *= rhs.zaehler;
    nenner *= rhs.nenner;
    kuerzen();
    return *this;
  }

  Rational& operator/= (Rational const& rhs)
  {
    return operator*=(rhs.kehrwert());
  }  

  //Inkrement, Dekrement
  Rational& operator++()
  {
    zaehler += nenner;
    return *this;
  }
  Rational& operator--()
  {
    zaehler -= nenner;
    return *this;
  }

  //Vergleich, als freie friend-Funktion
  friend bool operator< (Rational const& lhs, Rational const& rhs)
  {
    return lhs.zaehler*rhs.nenner < rhs.zaehler*lhs.nenner;
  }

 
  // INT
#define INTOP(OP) \
  Rational& operator OP (int rhs) { return (*this) OP Rational(rhs); }
  INTOP(+=);
  INTOP(-=);
  INTOP(*=);
  INTOP(/=);
#undef INTOP

  friend bool operator < (Rational const& lhs, int rhs)
  { return lhs < Rational(rhs); }
  friend bool operator > (Rational const& lhs, int rhs)
  { return lhs > Rational(rhs); }
 

  // DOUBLE
#define DBLOP(OP) \
  friend double& operator OP (double& lhs, Rational const& rhs) \
  { return lhs OP rhs.toDouble(); }
  DBLOP(+=);
  DBLOP(-=);
  DBLOP(*=);
  DBLOP(/=);
#undef DBLOP

  friend bool operator< (double const& lhs, Rational const& rhs)
  { return lhs < rhs.toDouble(); }
  friend bool operator> (double const& lhs, Rational const& rhs)
  { return lhs > rhs.toDouble(); }

private: //für boost::XXX2_left
  operator double() const { return toDouble(); }
public:
#ifdef BOOST_NO_OPERATORS_IN_NAMESPACE
#  define BOOST_NS
#else
#  define BOOST_NS boost::
#endif
  friend double BOOST_NS operator/ (Rational const&, double const&);
  friend double BOOST_NS operator- (Rational const&, double const&);
#undef BOOST_NS
  
  // I/O 
  friend std::ostream& operator<< (std::ostream& os, Rational const& rat)
  {
    os << "(" << rat.zaehler << "/" << rat.nenner << ")";
    return os;
  }
};

#endif //#ifdef RATIONAL_H_INC
