#ifndef RATIONAL_H_INC
#define RATIONAL_H_INC

#include <stdexcept>

#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== 
{
  //Invarianten:
  //- zaehler und nenner sind immer vollstaendig gekuerzt
  //- der nenner ist immer positiv (Vorzeichen befindet sich am zaehler)
  int zaehler;
  int nenner;

  int ggt(int a, int b) 
  {
    //Euklidischer Algorithmus
    if (a<b) std::swap(a,b);
    while (b>0)
    {
      a %= b;
      std::swap(a,b);
    }
    return a;
  }

  void kuerzen()  
  //koennte const sein, da am eigentlichen Zustand nichts geaendert wird, 
  //allerdings wirds wegen der Invariante sowieso nur bei nonconst-Funktionen aufgerufen.
  {
    int z = zaehler>0 ? zaehler : -zaehler;
    z = ggt(z, nenner);
    zaehler /= z;
    nenner /= z;
  }

public:
  //Konstruktoren:
  //Default- und Konvertierungs-Konstruktor für ints gleich mit eingebaut...
  Rational(int z = 0, 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(); 
  } 

  //Copy-Ctor compilergeneriert
  //Destruktor compilergeneriert
  //Zuweisung für Rational compilergeneriert
  //Zuweisung für int implizit generiert durch Konvertierungs-Konstruktor

  //Vorzeichen:
  Rational operator- () const
  {
    Rational tmp(*this);
    tmp.zaehler *= -1;
    return tmp;
  }

  Rational operator+ () const
  {
    return *this;
  }

  //Umwandlungsfunktionen:
  Rational kehrwert() const
  {
    Rational tmp(nenner, zaehler);
    return tmp;
  }

  double toDouble() const
  {
    return static_cast<double>(zaehler)/nenner;
  }

  //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;
  }
};

#endif //#ifdef RATIONAL_H_INC
