// Operatoren, dyn. Arrays: Klassen für Messwerte, Version als Template
//
// Aufruf: messw filename1 filename2 ...
//
// Klaus Kusche, 2018

#include <iostream>
#include <fstream>
// Nötig für Bruch
#include <cstring>

using namespace std;

////////////////////////////////////////////////////////////////////////////////
// aus der Bruch-Musterlösung

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

  public:
    Bruch(int zaehler = 0, int nenner = 1) : z(zaehler), n(nenner) {
      Kuerze();
    }

    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);
    }

    // neu: Typumwandlung nur explizit
    // (sonst ist das / im Mittelwert von Messw nicht eindeutig)
    //operator double() const {
    explicit operator double() const {
      return double(z) / double(n);
    }
    
  private:
    int z;
    int n;

    void Kuerze();
};

Bruch::Bruch(const char *str)
{
  z = atoi(str);       // Zahl bis vor dem '/'
  const char *p = strchr(str, '/');
  if (p == nullptr) {  // kein '/'
    n = 1;
  } else {
    n = atoi(p + 1);   // Zahl ab 1 Zeichen hinter dem '/'
    Kuerze();          // kürze den soeben gespeicherten Zähler und Nenner
  }
}

int ggT(int a, int b);

void Bruch::Kuerze()
{
  if (n == 0) {
    cerr << "Nenner ist 0!" << endl;
    exit(EXIT_FAILURE);
  }
  
  if (n < 0) {
    z = -z;
    n = -n;
  }

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

int ggT(int a, int b)
{
  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;
}

////////////////////////////////////////////////////////////////////////////////

// neu: Bruch einlesen
istream &operator>>(istream &inFile, Bruch &a)
{
  string str;   
  inFile >> str;
  a = Bruch(str.c_str());

  return inFile;
}


// notwendig für den folgenden Prototyp:
// Vorab-Deklaration der Template-Klasse
template <class T>
class Messw;
// notwendig, damit das friend im class-Template klappt:
// Prototyp der Funktion
template <class T>
ostream &operator<<(ostream &outFile, const Messw<T> &a);

template <class T>
class Messw
{
  // das <T> nach dem operator<< ist notwendig!
  friend ostream &operator<< <T>(ostream &outFile, const Messw<T> &r);

  public:
    Messw() : anz(0), arr(nullptr) { } // Standard-Konstruktor: "Leeres" Objekt
    Messw(const Messw<T> &orig);
  
    ~Messw() {
      delete [] arr;
    }

    // ~ liefert die Anzahl der Messwerte im Objekt = Arraygröße
    int operator~() const {
      return anz;
    }

    // "Not": Ist das Objekt leer?
    bool operator!() const {
      return anz == 0;
    }

    // [] nur zum Lesen des i-ten Elementes
    // (zum Lesen und Ändern müsste eine Referenz zurückgegeben werden!)
    T operator[](int i) const {
      if ((i < 0) || (i >= anz)) {
        return 0;
      }
      return arr[i];
    }

    // füge einen einzelnen Wert zu einer Messreihe dazu
    Messw<T> operator+(T x) const;
    // hänge zwei Messreihen zusammen
    Messw<T> operator+(const Messw<T> &r) const;

    // optional: +=
    // erweitere eine Messreihe um einen Wert
    Messw<T> &operator+=(T x);
    // erweitere eine Messreihe um eine Messreihe
    Messw<T> &operator+=(const Messw<T> &r);

    // Typumwandlungs-Operator von Messreihe auf T
    // (Returntyp implizit T)
    // liefert den Mittelwert der Messreihe
    operator T() const;

    Messw<T> &operator=(const Messw<T> &r);
    
  private:
    // wir machen das Array immer genau so groß,
    // wie wir es für die momentan zu speichernden Messwerte brauchen
    // "angelegte Arraygröße" und "Anzahl belegter Elemente" ist dasselbe!
    int anz;
    T *arr;  // Pointer auf das dynamisch angelegte Array

    // nur klassenintern verwendbarer Konstruktor:
    // liefert ein Messw-Objekt mit einem Array der angegebenen Größe
    // die Elemente des Arrays bleiben uninitialisiert!
    Messw(int a) : anz(a), arr(new T[a]) { }
};

template <class T>
Messw<T>::Messw(const Messw<T> &orig) : anz(orig.anz), arr(new T[anz])
{
  for (int i = 0; i < anz; ++i) {
    arr[i] = orig.arr[i];
  }
}

template <class T>
Messw<T> Messw<T>::operator+(T x) const
{
  Messw<T> ergeb(anz + 1);
  for (int i = 0; i < anz; ++i)
  {
    ergeb.arr[i] = arr[i];
  }
  ergeb.arr[anz] = x;
  return ergeb;
}

template <class T>
Messw<T> Messw<T>::operator+(const Messw<T> &r) const
{
  Messw<T> ergeb(anz + r.anz);
  for (int i = 0; i < anz; ++i)
  {
    ergeb.arr[i] = arr[i];
  }
  for (int i = 0; i < r.anz; ++i)
  {
    ergeb.arr[anz + i] = r.arr[i];
  }
  return ergeb;
}

template <class T>
Messw<T> &Messw<T>::operator+=(T x)
{
  // Kein neues Objekt, sondern nur ein neues Array anlegen
  // (in einer Hilfsvariable, denn wir brauchen das alte Array noch!)
  T *arr_neu = new T[anz + 1]; 
  for (int i = 0; i < anz; ++i)
  {
    arr_neu[i] = arr[i];
  }
  arr_neu[anz] = x;
  // Altes Array im eigenen Objekt freigeben und durch neues Array ersetzen
  delete [] arr;
  arr = arr_neu;
  // ... und eigene Größe aktualisieren
  ++anz;
  // Wie bei operator=: Geändertes eigenes Objekt (= linker Operand) zurückgeben
  return *this;
}

template <class T>
Messw<T> &Messw<T>::operator+=(const Messw<T> &r)
{
  // Gleiche Vorgehensweise wie bei vorigem +=
  T *arr_neu = new T[anz + r.anz]; 
  for (int i = 0; i < anz; ++i)
  {
    arr_neu[i] = arr[i];
  }
  for (int i = 0; i < r.anz; ++i)
  {
    arr_neu[anz + i] = r.arr[i];
  }
  delete [] arr;
  arr = arr_neu;
  anz += r.anz;
  return *this;
}

template <class T>
Messw<T>::operator T() const
{
  if (anz == 0) {
    return 0;
  }
  T sum = 0;
  for (int i = 0; i < anz; ++i) {
    // Bruch kennt kein +=
    //sum += arr[i];
    sum = sum + arr[i];
  }
  return sum / anz;
}

template <class T>
Messw<T> &Messw<T>::operator=(const Messw<T> &r)
{
  if (this != &r) {       // Schutz gegen Selbstzuweisung
    if (r.anz != anz) {   // eigenes Array hat falsche Größe
      delete [] arr;      // ==> wegwerfen und neu anlegen
      anz = r.anz;
      arr = new T[anz];
    }
    for (int i = 0; i < r.anz; ++i)
    {
      arr[i] = r.arr[i];
    }
  }
  return *this;
}

template <class T>
ostream &operator<<(ostream &outFile, const Messw<T> &r)
{
  for (int i = 0; i < r.anz; ++i) {
    outFile << r.arr[i] << " ";
  }
  return outFile; 
}

int main(int argc, const char *argv[])
{
  Messw<Bruch> alle;

  for (int i = 1; i < argc; ++i) {
    ifstream inFile(argv[i]);
    if (!inFile) {
      cerr << argv[0] << ": Fehler beim Öffnen von "
           << argv[i] << endl;
      continue;  // überspring den File, mach mit dem nächsten File weiter
    }
    Messw<Bruch> diese;
    Bruch a;
    
    while (inFile >> a) {
      // Wahlweise: Einfaches + für einen Wert ...
      //diese = diese + a;
      // ... oder += für einen Wert
      diese += a;
    }
    
    cout << "Datei " << argv[i] << ": ";
    if (!diese) {
      cout << "Keine Werte!" << endl;
    } else {
      cout << diese << endl;
    }
    cout << "Mittelwert: " << Bruch(diese)
           << " (" << ~diese << " Werte)\n" << endl;

    // Wahlweise: Einfache + für eine ganze Messreihe 
    //alle = alle + diese;
    // ... oder += für eine ganze Messreihe
    alle += diese;
  }
  
  cout << alle[0] << " ... " << alle[~alle - 1] << endl;
  cout << "Mittelwert insgesamt: " << Bruch(alle)
       << " (" << ~alle << " Werte)" << endl;
  
  return 0;
}
