// Ganz einfacher Taschenrechner mit Bruch-Klasse:
// + - * / von links nach rechts ohne Vorrang und ohne Klammern
//
// Input vom Terminal
// Mit Exceptions zur Fehlerbehandlung
//
// Aufruf: bruch 
//
// Klaus Kusche, 2015

#include <iostream>
#include <cstdlib>
#include <cstring>

using namespace std;

class Bruch
{
  friend ostream &operator<<(ostream &outFile, const Bruch &a);

  public:
    // 3 Aufrufsmöglichkeiten:
    // Standardkonstruktor: Bruch() liefert 0/1, also 0
    // Für eine ganze Zahl: Bruch(z) liefert z/1, also z
    // Für einen Bruch: Bruch(z, n) liefert z/n
    Bruch(int zaehler = 0, int nenner = 1) : z(zaehler), n(nenner) {
      Kuerze();  // kürze den soeben gespeicherten Zähler und Nenner
    }

    Bruch(const char *str);

    Bruch operator+(const Bruch &r) const {
      return Bruch(z * r.n + r.z * n, n * r.n);
    }
    
    Bruch operator-(const Bruch &r) const {
      return Bruch(z * r.n - r.z * n, n * r.n);
    }
    
    Bruch operator*(const Bruch &r) const {
      return Bruch(z * r.z, n * r.n);
    }
    
    Bruch operator/(const Bruch &r) const {
      return Bruch(z * r.n, n * r.z);
    }

    // Typumwandlungs-Operator
    // Kein Returntyp:
    // Operator-Name = Returntyp = Typ, auf den umgewandelt wird
    operator double() const {
      return double(z) / double(n);
    }
    
  private:
    int z;
    int n;

    // Liefert keinen neuen Bruch als Ergebnis,
    // sondern kürzt das Bruch-Objekt, für das "Kuerze" aufgerufen wird
    void Kuerze();
};

Bruch::Bruch(const char *str)
{
  // strtol speichert in diesen beiden Pointern
  // einen Pointer auf das erste Zeichen hinter der Zähler- und Nenner-Zahl
  char *endz, *endn;

  z = strtol(str, &endz, 10);
  if (endz == str) {
    // erstes Zeichen hinter der Zähler-Zahl ist Anfang des Strings
    // ==> Gar keine Zähler-Zahl vorhanden!
    throw "Kein Zaehler?";
  } else if (*endz == '\0') {
    // erstes Zeichen hinter der Zähler-Zahl ist Ende des Strings
    // ==> str enthält nur Zähler, keinen '/ und keinen Nenner
    n = 1;
  } else if (*endz != '/') {
    // erstes Zeichen hinter der Zähler-Zahl ist *nicht* '/'
    // ==> str ist fehlerhaft
    throw "Ungueltiges Zeichen im Zaehler?";
  } else {
    // endz zeigt auf den '/'
    // lies den Nenner ab 1 Zeichen hinter dem '/'
    n = strtol(endz + 1, &endn, 10);
    if (endn == endz + 1) {
      // strtol hat keine einzige Ziffer im Nenner gefunden
      throw "Kein Nenner?";
    } else if (*endn != '\0') {
      // strtol hat eine Zahl im Nenner verarbeitet
      // aber der verarbeitete Nenner geht nicht bis zum String-Ende
      throw "Ungueltiges Zeichen im Nenner?";
    }

    // kürze den soeben gespeicherten Zähler und Nenner
    Kuerze();  // prüft auch auf /0  
  }
}

int ggT(int a, int b);

void Bruch::Kuerze()
{
  if (n == 0) {
    throw "Division durch 0"; 
  }
  
  if (n < 0) {
    z = -z;
    n = -n;
  }

  int g = ggT(z, n);
  z /= g;
  n /= g;
}

int ggT(int a, int b)
{
  // ggt wird von den Zahlen ohne Vorzeichen gerechnet!
  if (a < 0) a = -a;
  if (b < 0) b = -b;
  
  while (b > 0) {
    int r = a % b;
    a = b;
    b = r;
  }
  
  return a;
}

ostream &operator<<(ostream &outFile, const Bruch &a)
{
  if (a.n == 1) {
    outFile << a.z;
  } else {
    outFile << a.z << '/' << a.n;
  }
  
  return outFile;
}

Bruch LiesBruch()
{
  char str[81];
  for (;;) {
    try {
      cin >> str;
      return Bruch(str);
    }
    catch (const char *e) {
      cout << e << " bei Bruch " << str << endl;
      cin.ignore(10000, '\n');
      if (cin.eof()) exit(EXIT_FAILURE);
    }
  }
}

int main(void)
{
  char rz;  // Das Rechenzeichen: Einzelnes Zeichen, kein String!

  // Bisheriges Ergebnis = linker Operand für die nächste Rechnung
  // Am Anfang: Erster Bruch
  Bruch result = LiesBruch();
  do {
    try {
      cin >> rz;
      switch (rz) {
        case '=':
          break;
        case '+':
          result = result + LiesBruch();
          break;
        case '-':
          result = result - LiesBruch();
          break;
        case '*':
           result = result * LiesBruch();
          break;
        case '/':
          result = result / LiesBruch();
          break;
        default:
          throw "Falsches Rechenzeichen!";
      }
    }
    catch (const char *e) {
      cout << e << " bei Rechenzeichen " << rz << endl;
      cin.ignore(10000, '\n');
      if (cin.eof()) exit(EXIT_FAILURE);
    }
  } while (rz != '=');

  cout << "Ergebnis: " << result << endl;
  // Aufruf des Typumwandlungs-Operators,
  // ... << ((double) result) << ... ginge genauso
  cout << "Als Kommazahl: " << double(result) << endl;
  
  exit(EXIT_SUCCESS);
}
