// Zahlen mit Addition und Gedächtnis, Version als Template
//
// Aufruf: adder
//
// Klaus Kusche, 2011

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

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

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

  public:
    Number() : sum(0), vals(nullptr), size(0) {}
    Number(T x) : sum(x), vals(new T[1]), size(1) {
      vals[0] = x;
    }
    Number(const Number<T> &a);

    ~Number() {
      delete[] vals;
    }

    T getSum() { return sum; }
//  Alternative: Typumwandlungs-Operator von einem Number-Objekt auf T
//  operator T() { return sum; }

    Number<T> operator+(const Number<T> &a) const;
    Number<T> operator+(T a) const;

    Number<T> &operator=(const Number<T> &a);
    
  private:
    T sum;
    T *vals;
    int size;
};

template <class T>
Number<T>::Number(const Number<T> &a)
: sum(a.sum), vals(new T[a.size]), size(a.size)
{
  for (int i = 0; i < size; ++i) {
    vals[i] = a.vals[i];
  }
}

template <class T>
Number<T> Number<T>::operator+(const Number<T> &a) const
{
  Number<T> res;

  res.sum = sum + a.sum;
  res.size = size + a.size;
  res.vals = new T[res.size];
  
  for (int i = 0; i < size; ++i) {
    res.vals[i] = vals[i];
  }
  for (int i = 0; i < a.size; ++i) {
    res.vals[size + i] = a.vals[i];
  }

  return res;
}

template <class T>
Number<T> Number<T>::operator+(T val) const
{
  Number<T> res;

  res.sum = sum + val;
  res.size = size + 1;
  res.vals = new T[res.size];
  
  for (int i = 0; i < size; ++i) {
    res.vals[i] = vals[i];
  }
  res.vals[size] = val;

  return res;
}

template <class T>
Number<T> &Number<T>::operator=(const Number<T> &a)
{
  if (this != &a) {
    if (size != a.size) {
      delete[] vals;
      size = a.size;
      vals = new T[size];
    }
    sum = a.sum;
    for (int i = 0; i < size; ++i) {
      vals[i] = a.vals[i];
    }
  }

  return *this;
}

template <class T>
ostream &operator<<(ostream &outFile, const Number<T> &a)
{
  outFile << a.sum << " [ ";
  for (int i = 0; i < a.size; ++i) {
    outFile << a.vals[i] << ' ';
  }
  outFile << ']';
  
  return outFile;
}

// Das ist eine "Template-Spezialisierung" des Standard-Konstrukturs:
// Anderer Code als im allgemeinen Fall Number<T> (oben im Template)
// für den Spezialfall Number<string>
// Konkret: ... sum("") ... statt ... sum(0) ...
template<>
Number<string>::Number() : sum(""), vals(nullptr), size(0) {}

int main(void)
{
  Number<int> pos, neg;

  srand(time(nullptr));

  for (int i = 0; i < 10; ++i) {
    pos = pos + rand() % 10;
    neg = neg + -(rand() % 10);
    cout << pos << endl << neg << endl << "Summe: " << pos + neg << endl;
    cout << "Kurz: " << pos.getSum() << " + " << neg.getSum()
     << " = " << (pos + neg).getSum() << endl;
// Alternative mit Typumwandlungs-Operator:
//    cout << "Kurz: " << int(pos) << " + " << int(neg)
//         << " = " << int(pos + neg) << endl;
  }

  Number<string> text1, text2("Ein ");

  text1 = text1 + "Test";
  text1 = text1 + ": ";
  text2 = text2 + "Schwachsinn...";
  cout << text1 + text2 << endl;
  cout << "Kurz: " << (text1 + text2).getSum() << endl;
// Alternative mit Typumwandlungs-Operator:
//cout << "Kurz: " << string(text1 + text2) << endl;
  
  exit(EXIT_SUCCESS);
}
