// LoesungKlausur4_komplett

////////////
// Aufgabe 1
////////////

// Complex.cpp
#include <iostream>
#include <cmath>
using namespace std;


class Complex {
	int re;
	int im;
public:
  Complex(int re = 0, int im = 0) : re(re), im(im) { }

  friend const Complex operator*(const Complex& left, const Complex& right) {
  	return Complex(
  	  left.re * right.re - left.im * right.im,
      left.im * right.re + left.re * right.im);
  }

  friend const Complex operator+(const Complex& left, const Complex& right) {
  	return Complex(left.re + right.re, left.im + right.im);
  }

  const Complex operator-() const {
  	return Complex(-re, -im);
  }

  friend const Complex operator-(const Complex& left, const Complex& right) {
  	return Complex(left.re - right.re, left.im - right.im);
  }

  friend ostream& operator<<(ostream& os, const Complex& complex) {
   	if (complex.re == 0 && complex.im == 0) return os << 0;
  	if (complex.re != 0) {
  		os << complex.re;
  		if (complex.im == 0) return os;
  	  if (complex.im == 1) return os << " + i";
  	  if (complex.im == -1) return os << " - i";
  	  if (complex.im > 0) os << " + ";
  	  if (complex.im < 0) os << " - ";
  	}
  	else {
  	  if (complex.im == 1) return os << "i";
  	  if (complex.im == -1) return os << "-i";
  	  if (complex.im < 0) os << "-";
  	}
  	return os << abs(complex.im) << "*i";
  }
};

const Complex i(0, 1);


////////////


// Fraction.cpp
#include <iostream>
#include <cmath>
using namespace std;


class Fraction {
	int num;
	int denom;
public:
  Fraction(int num, int denom = 1) : num(num), denom(denom) { }

  friend const Fraction operator/(const Fraction& left, const Fraction& right) {
  	return Fraction(left.num * right.denom, left.denom * right.num);
  }

  friend bool operator==(const Fraction& left, const Fraction& right) {
  	return left.num * right.denom == left.denom * right.num;
  }

  friend bool operator!=(const Fraction& left, const Fraction& right) {
  	return left.num * right.denom != left.denom * right.num;
  }

  friend bool operator>(const Fraction& left, const Fraction& right) {
  	return left.num * right.denom > left.denom * right.num;
  }

  friend bool operator<(const Fraction& left, const Fraction& right) {
  	return left.num * right.denom < left.denom * right.num;
  }

  friend Fraction abs(const Fraction& fraction) {
  	return Fraction(abs(fraction.num), abs(fraction.denom));
  }

  friend ostream& operator<<(ostream& os, const Fraction& fraction) {
  	if (fraction.num == 0) return os << 0;
   	if (fraction.denom == 1) return os << fraction.num;
   	if (fraction.denom == -1) return os << -fraction.num;
   	if (fraction.num * fraction.denom < 1) os << "-";
   	return os << abs(fraction.num) << "/" << abs(fraction.denom);
  }
};


////////////


// FracComplex.cpp
#include <iostream>
#include <cmath>
using namespace std;


class FracComplex {
	Fraction re;
	Fraction im;
public:
  FracComplex(Fraction re = 0, Fraction im = 0) : re(re), im(im) { }

  friend ostream& operator<<(ostream& os, const FracComplex& fracComplex) {
   	if (fracComplex.re == 0 && fracComplex.im == 0) return os << 0;
  	if (fracComplex.re != 0) {
  		os << fracComplex.re;
  		if (fracComplex.im == 0) return os;
  	  if (fracComplex.im == 1) return os << " + i";
  	  if (fracComplex.im == -1) return os << " - i";
  	  if (fracComplex.im > 0) os << " + ";
  	  if (fracComplex.im < 0) os << " - ";
  	}
  	else {
  	  if (fracComplex.im == 1) return os << "i";
  	  if (fracComplex.im == -1) return os << "-i";
  	  if (fracComplex.im < 0) os << "-";
  	}
  	return os << abs(fracComplex.im) << " *i";
  }
};


////////////


// main.cpp
#include <iostream>
#include <cmath>
#include "Complex.cpp"
#include "Fraction.cpp"
#include "FracComplex.cpp"

using namespace std;


int main() {
	Complex c1;               cout << "c1              = " << c1  << endl;
	Complex c2(1, 1);         cout << "c2(1, 1)        = " << c2  << endl;
	Complex c3(-1, 1);        cout << "c3(-1, 1)       = " << c3  << endl;
	Complex c4(1, -1);        cout << "c4(1, -1)       = " << c4  << endl;
	Complex c5(-1, 2);        cout << "c5(-1, 2)       = " << c5  << endl;
	Complex c6(5, -2);        cout << "c6(5, -2)       = " << c6  << endl;
	Complex c7(-12, 0);       cout << "c7(-12, 0)      = " << c7  << endl;
	Complex c8(0, -21);       cout << "c8(0, -21)      = " << c8  << endl;
	Complex c9(0, -1);        cout << "c9(0, -1)       = " << c9  << endl;
	Complex c10(0, 2);        cout << "c10(0, 2)       = " << c10 << endl;
	Complex c11(-1);          cout << "c11(-1)         = " << c11 << endl;
	Complex c12(2);           cout << "c12(2)          = " << c12 << endl;

	Complex c13 = c6;         cout << "c13 = c6        = " << c13 << endl;
	Complex c14; c14 = c6;    cout << "c14 = c6        = " << c14 << endl;
	Complex c15 = -3 + 2*i;   cout << "c15 = -3 + 2*i  = " << c15 << endl;
	Complex c16 = -21*i;      cout << "c16 = -21*i     = " << c16 << endl;
	Complex c17 = -i + 11;    cout << "c17 = -i + 11   = " << c17 << endl;
	Complex c18 = i * 3 - 4;  cout << "c18 = i * 3 - 4 = " << c18 << endl;

	Fraction f1(0, 7);              cout << "f1(0, 7);             = " << f1  << endl;
	Fraction f2(3, 1);              cout << "f2(3, 1);             = " << f2  << endl;
	Fraction f3(3, -1);             cout << "f3(3, -1);            = " << f3  << endl;
	Fraction f4(1, -1);             cout << "f4(1, -1);            = " << f4  << endl;
	Fraction f5(-1, 2);             cout << "f5(-1, 2);            = " << f5  << endl;
	Fraction f6(1, -2);             cout << "f6(1, -2);            = " << f6  << endl;
	Fraction f7 = 0;                cout << "f7 = 0;               = " << f7  << endl;
	Fraction f8 = Fraction(-1)/5;   cout << "f8 = Fraction(-1)/5;  = " << f8  << endl;
	Fraction f9 = -1/Fraction(5);   cout << "f9 = -1/Fraction(5);  = " << f9  << endl;
	Fraction f10 = (Fraction)-1/5;  cout << "f10 = (Fraction)-1/5; = " << f10 << endl;
	Fraction f11 = -1/5;            cout << "f11 = -1/5;           = " << f11 << endl;  // !!!
	Fraction f12(5);                cout << "f12(5);               = " << f12 << endl;
	Fraction f13 = -1/f12;          cout << "f13 = -1/f12;         = " << f13 << endl;
	Fraction f14 = f12/f13;         cout << "f14 = f12/f13;        = " << f14 << endl;

	FracComplex fc1(Fraction(1, 5), Fraction(-3, 8));  cout << "fc1(Fraction(1, 5), Fraction(-3, 8)) = " << fc1 << endl;
	FracComplex fc2(Fraction(15), Fraction(-3));       cout << "fc2(Fraction(15), Fraction(-3))      = " << fc2 << endl;
	FracComplex fc3(15, -3);                           cout << "fc3(15, -3)                          = " << fc3 << endl;

	return 0;
}



////////////
// Aufgabe 2
////////////

// geomFig.cpp
#include <iostream>
using namespace std;


class Shape {
  double x, y;
public:
  Shape(double x, double y) {
    setX(x);
    setY(y);
    cout << "Shape() called" << endl;
  }

  virtual ~Shape() = 0;

  double getX() const  { return x; }
  double getY() const  { return y; }
  void setX(double xx) { x = xx; }
  void setY(double yy) { y = yy; }

  void move(double dx, double dy) {
    x += dx;
    y += dy;
  }

  virtual void print() const {
  	cout << '(' << x << ", " << y << ')';
  }
};

Shape::~Shape() { cout << "~Shape() called" << endl; }


class Circle : public Shape {
  double radius;
public:
  Circle(double x, double y, double r);

  ~Circle()  { cout << "~Circle() called" << endl; }

  double getRadius() const { return radius; }

  bool setRadius(double r) {
    if (r >= 0) {
      radius = r;
      return true;
    }
    else return false;
  }

  void print() const;
};


// AUFGABE 2.a
// Korrigieren Sie die Definition des untenstehenden Circle-Konstruktors:
//   inline Circle::Circle(double x, double y, double r)
//   : x(xx), y(yy), radius(r) { }

// Korrekte Definition des Circle-Konstruktors:
inline Circle::Circle(double x, double y, double r)
: Shape(x, y), radius(r) {
  cout << "Circle() called" << endl;
}


// AUFGABE 2.b
// Schreiben Sie die fehlende Definition der Funktion print() der Klasse Circle.
// Die Funktion gibt den Mittelpunkt und den Radius eines Circle aus.
inline void Circle::print() const {
  cout <<         "Circle: Mittelpunkt: ";
  Shape::print();
  cout << endl << "        Radius: " << radius << endl;
}

// Leiten Sie von der Klasse Shape die Klasse Rectangle und von dieser die
// Klasse Square ab. Der Konstruktor fuer Rectangle erhalte als Argumente den
// Koordinatenbezugspunkt (origin) in der linken oberen Ecke (positive x-Achse
// nach rechts, positive y-Achse nach unten) sowie Hoehe und Breite. Nutzen Sie
// geerbte Instanzvariablen.
// Definieren Sie einen angepassten Konstruktor fuer Square.
// Definieren Sie fuer beide Klassen eine print-Funktion.
class Rectangle : public Shape {
   double height, width;
public:
  Rectangle(double x, double y, double h, double w)
  : Shape(x,y), height(h), width(w) {
    cout << "Rectangle() called" << endl;
  }

  ~Rectangle()  { cout << "~Rectangle() called" << endl; }

   double getHeight() const { return height; }

   void print() const {
     cout <<         "Rectangle: Ursprung: ";
     Shape::print();
     cout << endl << "           Hoehe: " << height << ", Breite: " << width << endl;
  }
};


class Square : public Rectangle {
 public:
   Square( double x, double y, double side) : Rectangle(x, y, side, side) {
    cout << "Square() called" << endl;
  }

  ~Square()  { cout << "~Square() called" << endl; }

   void print() const {
     cout <<         "Square: Ursprung: ";
     Shape::print();
     cout << endl << "        Seite: " << Rectangle::getHeight() << endl;
  }
};


// Realisieren Sie ein main mit einem Array, das drei Zeiger auf Shapes auf-
// weist. Die Zeiger sollen auf einen Circle, einen Rectangle und einen Square
// zeigen, die alle auf dem Heap angelegt wurden.
// Iterieren Sie ueber das Array und senden Sie via Zeiger die Funktion print
// an die Shapes (ohne cast!). Nehmen Sie ggf. noetige Aenderungen an print vor.
// Zerstoeren Sie am Schluss alle Objekte vom hoechsten zum niedrigsten Index.
// Nehmen Sie auch hier noetige Ergaenzungen in den Klassen vor.
int main() {
	Shape *shapes[] = {
		new Circle(1, 2, 10), new Rectangle(10, 10, 2, 6), new Square(3, 3, 7)
	};
	cout << endl;

	for (int i = 0; i < sizeof shapes / sizeof(Shape *); ++i) {
		shapes[i]->print();
		cout << endl;
	}

	for (int i = sizeof shapes / sizeof(Shape *) - 1; i >= 0 ; --i) {
		delete(shapes[i]);
		shapes[i] = 0;
	}
	system("pause");
}

