// Einfache Array-Klasse + Function Pointer
// Plus abgel. Klasse mit Minimum und Maximum
//
// Aufruf: array val1 val2 val3 ...
//
// Klaus Kusche, 2012

#include <iostream>
#include <cstdlib>
#include <cmath>

using namespace std;

// eine der Funktionen, mit der wir apply aufrufen wollen
double square(double val);
// die Vergleichsfunktion für qsort
int cmp(const void *p1, const void *p2);

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

  public:
    Array(unsigned int sz) : size(sz), used(0), data(new double[sz]) {}
    ~Array() {
      delete[] data;
    }

    bool append(double val);

    // 4. Argument cmp: Pointer auf unsere Vergleichs-Funktion
    void sort() {
      qsort(data, used, sizeof(double), cmp);
    }
    // das Argument von apply ist ein Pointer auf eine Funktion,
    //   die einen double-Parameter hat und einen double-Returnwert liefert.
    void apply(double (*func)(double));
    
    // man sollte Array-Objekte weder zuweisen noch kopieren
    Array(const Array &orig) = delete;
    Array &operator=(const Array &orig) = delete;
    
  protected:
    unsigned int size;  // Größe, mit der das Array angelegt wurde
    unsigned int used;  // Anzahl der gerade belegten Elemente
    double *data;       // Pointer auf das Array
};

bool Array::append(double val)
{
  if (used < size) {
    // hinten anhängen
    data[used] = val;
    ++used;
    return true;
  } else {
    return false;
  }
}

void Array::apply(double (*func)(double))
{
  // für jedes der Elemente die Funktion aufrufen
  for (unsigned int i = 0; i < used; ++i) {
    data[i] = (*func)(data[i]);
  }
}

ostream &operator<<(ostream &outFile, const Array &a)
{
  for (unsigned int i = 0; i < a.used; ++i) {
    outFile << a.data[i] << ' ';
  }
  outFile << endl;
  
  return outFile;
}

class MinmaxArray : public Array
{  
  public:
    MinmaxArray(unsigned int sz) : Array(sz), min(HUGE_VAL), max(-HUGE_VAL) {}

    bool append(double val);

    void apply(double (*func)(double));

    double getMin() { return min; }
    double getMax() { return max; }
    
  private:
    double min, max;
};

bool MinmaxArray::append(double val)
{
  if (!Array::append(val)) return false;

  if (val < min) min = val;
  if (val > max) max = val;

  return true;
}

void MinmaxArray::apply(double (*func)(double))
{
  unsigned int i;
  
  Array::apply(func);
  
  min = HUGE_VAL; max = -HUGE_VAL;
  for (i = 0; i < used; ++ i) {
    if (data[i] < min) min = data[i];
    if (data[i] > max) max = data[i];
  }
}

// eine der Funktionen, mit der wir apply aufrufen wollen
// (in C++ 11 auch direkt im Aufruf möglich, siehe unten!)
double square(double val)
{
  return val * val;
}

// die Vergleichsfunktion für qsort
// p1 und p2 zeigen auf die zu vergleichenden Array-Elemente
//   (void *, weil qsort deren Typ ja nicht kennt)
// Returnwert ist wie bei strcmp <0, =0, >0
int cmp(const void *p1, const void *p2)
{
  // p1 und p2 in double-Pointer verwandeln
  // und den Wert laden, auf den sie zeigen
  double d1 = *((const double *) p1);
  double d2 = *((const double *) p2);

  if (d1 < d2) return -1;
  else if (d1 > d2) return 1;
  else return 0;
}

int main(int argc, const char *argv[])
{
  MinmaxArray data(argc - 1);

  for (int i = 1; i < argc; ++i) {
    data.append(atof(argv[i]));
  }

  for (;;) {
    cout << data;
    cout << "Minimum: " << data.getMin()
         << ", Maximum: " << data.getMax() << endl;
    cout << "(w)urzel (q)uadrat (l)og (e)xp (s)ort e(x)it ?" << endl;
    char c;
    cin >> c;
    // switch wäre eventuell schöner!
    if (c == 'w') data.apply(sqrt);
    else if (c == 'q') data.apply(square);
    // alternative Variante mit "Lambda-Expression" (anonymer Funktionscode)
    // anstatt Funktion "square"
    // nur ab C++ 11 !!!
    //else if (c == 'q') data.apply( [] (double val) { return val * val; } );
    else if (c == 'l') data.apply(log);
    else if (c == 'e') data.apply(exp);
    else if (c == 's') data.sort();
    else if (c == 'x') break;
  }

  exit(EXIT_SUCCESS);
}
