From 1c6721991aaaa671e2d55e73a24815993904bd1a Mon Sep 17 00:00:00 2001
From: Michael Guttmann <michael.guttmann@student.tugraz.at>
Date: Thu, 29 Apr 2021 15:51:30 +0200
Subject: [PATCH] init exercise

---
 2021-oop1/ku/exercise_fraction/README.md | 136 ++++++++++
 2021-oop1/ku/exercise_fraction/test.cpp  | 319 +++++++++++++++++++++++
 2 files changed, 455 insertions(+)
 create mode 100644 2021-oop1/ku/exercise_fraction/README.md
 create mode 100644 2021-oop1/ku/exercise_fraction/test.cpp

diff --git a/2021-oop1/ku/exercise_fraction/README.md b/2021-oop1/ku/exercise_fraction/README.md
new file mode 100644
index 0000000..9863355
--- /dev/null
+++ b/2021-oop1/ku/exercise_fraction/README.md
@@ -0,0 +1,136 @@
+# Übungsbeispiel: Bruch
+
+Ziel dieses Übungsbeispiels ist es, Operator-Overloading und Exception-Handling in einem kurzen Programm zu implementieren.
+
+### Aber um was geht es denn?
+
+Es soll eine Klasse `Fraction`, die einen Bruch - also eine rationale Zahl - darstellt, erstellt werden. Nach fertiger Implementierung soll mit dieser Klasse wie mit gewöhnlichen `int` oder `double` mit den Operatoren `+`, `*`, `==` ... gerechnet werden können.
+
+Beim Rechnen mit Brüchen ist außerdem zu beachten, dass der Nenner nie `0` werden darf. Insbesondere beim Erstellen eines neues Bruchs, aber auch beispielsweise beim Dividieren könnte es hier zu einem Problem kommen. Dafür soll eine eigene Exception `NullDivisionException` implementiert werden.
+
+## Klasse Fraction
+
+Die Klasse `Fraction` soll genau einen Bruch darstellen. Dafür werden zwei Attribute benötigt, nämlich ein Zähler (`nominator_`) und ein Nenner (`denominator_`).
+
+Ein Bruch soll intern nach jeder Operation immer in gekürzter Form gespeichert sein und der Nenner soll immer positiv sein. Beispielsweise `5/-15` soll als `-1/3` gespeichert werden.
+
+### Konstruktoren
+
+Ein Bruch soll auf drei Arten erstellt werden können:
+
+| Parameter | Beschreibung |
+| --------- | ------------ |
+| keiner | Der Bruch soll den Wert `0` besitzen. |
+| `int` | Der Bruch soll den Wert der Ganzzahl besitzen. |
+| `int`, `int` | Erster Paramter ist der Zähler, zweiter der Nenner. |
+
+
+Im letzten Fall ist außerdem zu überprüfen, dass es zu keiner Division durch `0` kommt, ansonsten soll eine `NullDivisionException` geworfen werden. Außerdem ist zu beachten, dass der Bruch in oben beschriebenen Form gespeichert werden soll.
+
+*Tipp: Es ist nicht notwendig drei unterschiedliche Konstruktoren zu erstellen. Wenn man es geschickt löst, kommt man mit einem aus.*
+
+Copy-Konstruktor bzw. Copy-Assignment-Operator sollen als Übung explizit implementiert werden. Wichtig beim Copy-Assignment-Operator ist es, dass auch folgendes möglich sein soll: `b1 = b2 = b3`.
+
+*Tipp: Überlege ob es hier auch zu einer Division durch `0` kommen kann bzw. ob der Bruch gekürzt werden muss.*
+
+### Methoden
+
+- `getNominator()`
+  - Getter für den Zähler
+- `getDenominator()`
+  - Getter für den Nenner
+- `value()`
+  - gibt den Wert des Bruchs als `double` zurück
+- `reduce()`
+  - private Methode, um den Bruch zu kürzen und den Nenner positiv zu machen
+
+*Tipp: Euklidischer Algorithmus. Ansonsten kann man auch alle möglichen Teiler durchprobieren, ist zwar nicht besonders effizient, aber gut genug für den Anfang.*
+
+### Ausgabeoperator
+
+Folgender Ausgabeoperator soll implementiert werden, `b1` ist ein Objekt der Klasse `Fraction`.
+
+- `std::cout << b1`
+  - `b1` soll in der Form: `<nominator_> / <denominator_>` ausgegeben werden
+
+### Arithmetische Operatoren
+
+Folgende Operationen sollten auf jeden Fall implementiert werden, wobei der Copy-Assignment-Operator bereits implementiert sein sollte. `b1`, `b2`, `b3` sind Brüche, `a` ist ein `int`.
+
+*Tipp: Manche Operatoren sind einfacher als Methode zu implementieren, andere wiederum müssen sogar unbedingt als Funktion implementiert werden.*
+
+- `b1 = b2 + b3`
+  - `b2` und `b3` werden zusammenaddiert, ändern sich aber nicht
+
+- `b1 = b2 + a`
+  - `b2` und `a` werden zusammenaddiert, ändern sich aber nicht (`a` ist ein `int`!)
+
+- `b1 = a + b3`
+  - `a` und `b3` werden zusammenaddiert, ändern sich aber nicht (`a` ist ein `int`!)
+
+- `b1 = b2 += b3`
+  - `b3` wird direkt auf `b2` addiert
+  - das Ergebnis kann an `b1` zugewiesen werden
+
+- `b1 = b2++`
+  - post-increment
+  - in `b1` wird der alte Wert von `b2` geschrieben
+  - anschließend wird `b2` um den Wert `1` erhöht
+
+- `b1 = ++b2`
+  - pre-increment
+  - `b2` wird um den Wert `1` erhöht 
+  - das Ergebnis wird in `b1` geschrieben
+
+- `b1 = -b2`
+  - `b1` wird das Negative von `b2` zugewiesen
+  - `b2` bleibt unverändert
+
+- `b1 = ~b2` (bitweiser Operator)
+  - `b1` wird der Kehrwert von `b2` zugewiesen
+    - der Nenner soll weiterhin positiv sein
+    - `NullDivisionException` falls der Nenner `0` ist
+  - `b2` bleibt unverändert
+
+Alle weiteren arithmetischen Operatoren, wie beispielsweise `-`, `-=`, `--`, `*`, `/`... können analog implementiert werden.
+
+### Logische Operatoren und Vergleichsoperatoren
+
+Folgende Operatoren sollten auf jeden Fall implementiert werden. `b1`, `b2` sind wiederum Brüche.
+
+- `b1`
+  - ist genau dann `false` sein, wenn der Bruch den Wert `0` besitzt
+  - **Wichtig! `int a = b1;` darf nicht gültig sein bzw. kompilieren. Daher ist das Schlüsselwort `explicit` zu verwenden.**
+
+- `!b1`
+  - ist genau dann `true` sein, wenn der Bruch den Wert `0` besitzt
+
+- `b1 == b2`
+  - ist genau dann `true`, falls `b1` und `b2` denselben Wert besitzen
+
+- `b1 != b2`
+  - ist genau dann `true`, falls `b1` und `b2` verschiedene Werte besitzen
+
+- `b1 < b2`
+  - ist genau dann `true`, falls `b1` echt kleiner als `b2` ist
+
+- `b1 <= b2`
+  - ist genau dann `true`, falls `b1 < b2` oder `b1 == b2`
+
+Alle weiteren Vergleichsoperatoren können analog implementiert werden.
+
+## Klasse NullDivisionException
+
+Diese Klasse stellt eine Exception dar und soll eine Fehlermeldung repräsentieren, wenn in einem Bruch der Nenner `0` werden würde. Daher soll diese Klasse auch von `std::exception` abgeleitet werden. Innerhalb der Klasse soll es genau ein Attribut gegeben, nämlich die Fehlermeldung `message_`, als `std::string`.
+
+### Konstruktor
+
+Weiters soll es genau einen Konstruktor geben, der als einzigen Parameter den Zähler des fehlerhaften Bruchs übergibt bekommt. Anschließend soll in `message_` die Fehlermeldung ```<nominator> / 0 - Dividing through 0 not valid!\n```, wobei `<nominator>` durch den übergebenen Parameter ersetzt werden soll. Der Copy-Konstruktor und der Copy-Assignment-Operator sollen gelöscht werden.
+
+### Methoden
+
+Außerdem soll in der Klasse genau eine Methode implementiert werden, nämlich die `what()`-Methode aus der Basisklasse. Diese Methode liefert als Rückgabewert einen `const char*`, ist selbst `const`, darf selbst keine Exception werfen (`noexcept`) und überschreibt die Methode aus der Basisklasse (`override`). Hier soll genau die `message_` zurückgegeben werden, aber als eben als `const char*`, nicht als `std::string`.
+
+## Testprogramm
+
+Ein kurzes Testprogramm, dass die wesentlichen Funktionalitäten überprüft steht bereits zur Verfügung. Die noch offenen Testcases werden in Kürze noch folgen.
diff --git a/2021-oop1/ku/exercise_fraction/test.cpp b/2021-oop1/ku/exercise_fraction/test.cpp
new file mode 100644
index 0000000..9646319
--- /dev/null
+++ b/2021-oop1/ku/exercise_fraction/test.cpp
@@ -0,0 +1,319 @@
+#include <iostream>
+#include <cassert>
+#include "Fraction.hpp"
+#include "NullDivisionException.hpp"
+
+/**
+  * If you have not implemented all functionality yet, you can simply
+  * deselect the testcases by setting the corresponding define to 0.
+  * Then your programm will compile and you can test the other testcases.
+  */
+#define CONSTRUCTORS 1
+#define CONSTRUCTOR_NULL_DIVISION_EXCEPTION 1
+#define REDUCE_FRACTION 1
+#define COPY_ASSIGNMENT 1
+#define OUT_OPERATOR 0 // will follow soon
+#define PLUS_OPERATORS 0 // will follow soon
+#define PLUS_OPERATORS_WITH_INT 0 // will follow soon
+#define INCREMENT_OPERATORS 0 // will follow soon
+#define NEGATION_INVERSION 0 // will follow soon
+#define BOOL_OPERATOR 0 // will follow soon
+#define COMPARE_OPERATORS 0 // will follow soon
+
+void constructors();
+void constructorNullDivisionException();
+void reduceFraction();
+void copyAssignment();
+void outOperator();
+void plusOperators();
+void plusOperatorsWithInt();
+void incrementOperators();
+void negationInversion();
+void boolOperators();
+void compareOperators();
+
+//------------------------------------------------------------------------------
+int main(void)
+{
+  std::cout << "Starting testcases!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+  constructors();
+  constructorNullDivisionException();
+  reduceFraction();
+  copyAssignment();
+  outOperator();
+  plusOperators();
+  plusOperatorsWithInt();
+  incrementOperators();
+  negationInversion();
+  boolOperators();
+  compareOperators();
+
+  std::cout << "Great! Passing all selected testcases!" << std::endl;
+
+  return 0;
+}
+
+//------------------------------------------------------------------------------
+void constructors()
+{
+#if CONSTRUCTORS == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a;
+  Fraction b(5);
+  Fraction c(1, 10);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+  std::cout << b.getNominator() << " / " << b.getDenominator() << std::endl;
+  std::cout << c.getNominator() << " / " << c.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 0 && a.getDenominator() == 1 &&
+    "Default constructor not correct!\n");
+  assert(b.getNominator() == 5 && b.getDenominator() == 1 &&
+    "Constructor with one paramter not correct!\n");
+  assert(c.getNominator() == 1 && c.getDenominator() == 10 &&
+    "Constructor with two parameters not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void constructorNullDivisionException()
+{
+#if CONSTRUCTOR_NULL_DIVISION_EXCEPTION == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  try
+  {
+    Fraction a(2, 0);
+
+    std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+    assert(false && "No NullDivisionlException thrown!\n");
+  }
+  catch (NullDivisionException& ex)
+  {
+    std::cout << "Error: " << ex.what();
+
+    assert(std::string(ex.what()) == "2 / 0 - Dividing through 0 not valid!\n"
+      && "NullDivisionException not correct!\n");
+  }
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void reduceFraction()
+{
+#if REDUCE_FRACTION == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+  Fraction b(65, -26);
+  Fraction c(-123, 126);
+  Fraction d(-12, -144);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+  std::cout << b.getNominator() << " / " << b.getDenominator() << std::endl;
+  std::cout << c.getNominator() << " / " << c.getDenominator() << std::endl;
+  std::cout << d.getNominator() << " / " << d.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+  assert(b.getNominator() == -5 && b.getDenominator() == 2 &&
+    "Reducing fraction not correct!\n");
+  assert(c.getNominator() == -41 && c.getDenominator() == 42 &&
+    "Reducing fraction not correct!\n");
+  assert(d.getNominator() == 1 && d.getDenominator() == 12 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void copyAssignment()
+{
+#if COPY_ASSIGNMENT == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(13, -169);
+  Fraction b(a);
+  Fraction c(20);
+  Fraction d;
+
+  a = c = d;
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+  std::cout << b.getNominator() << " / " << b.getDenominator() << std::endl;
+  std::cout << c.getNominator() << " / " << c.getDenominator() << std::endl;
+  std::cout << d.getNominator() << " / " << d.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 0 && a.getDenominator() == 1 &&
+    "Copy-Assignment-Operator not extendable!\n");
+  assert(b.getNominator() == -1 && b.getDenominator() == 13 &&
+    "Copy-Constructor not working!\n");
+  assert(c.getNominator() == 0 && c.getDenominator() == 1 &&
+    "Copy-Assignment-Operator not working!\n");
+  assert(a.getNominator() == 0 && a.getDenominator() == 1 &&
+    "Default Constructor not working!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void outOperator()
+{
+#if OUT_OPERATOR == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void plusOperators()
+{
+#if PLUS_OPERATORS == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void plusOperatorsWithInt()
+{
+#if PLUS_OPERATORS_WITH_INT == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void incrementOperators()
+{
+#if INCREMENT_OPERATORS == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void negationInversion()
+{
+#if NEGATION_INVERSION == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void boolOperators()
+{
+#if BOOL_OPERATOR == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
+
+//------------------------------------------------------------------------------
+void compareOperators()
+{
+#if COMPARE_OPERATORS == 1
+
+  std::cout << "Testcase: " << __func__ << std::endl << std::endl;
+
+  Fraction a(2, 6);
+
+  std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
+
+  assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
+    "Reducing fraction not correct!\n");
+
+  std::cout << "\nAll good!\n\n";
+  std::cout << "-----------------------------------------------\n" << std::endl;
+
+#endif
+}
-- 
GitLab