From a3861d19ffef148c4728041c8da8db3dcd22ac13 Mon Sep 17 00:00:00 2001 From: z004x46y Date: Mon, 15 Dec 2025 20:49:14 +0100 Subject: [PATCH] Unity-Tests numbers --- numbers.c | 231 ++++++++++++++++++++++++++++++++++++++++++++---- numbers_test2.c | 44 ++++----- numbers_test3.c | 221 --------------------------------------------- 3 files changed, 235 insertions(+), 261 deletions(-) delete mode 100644 numbers_test3.c diff --git a/numbers.c b/numbers.c index f59d9a2..ea94d8e 100644 --- a/numbers.c +++ b/numbers.c @@ -1,26 +1,221 @@ -#include -#include -#include -#include -#include "numbers.h" -#include "bintree.h" -//TODO: getDuplicate und createNumbers implementieren -/* * * Erzeugen eines Arrays mit der vom Nutzer eingegebenen Anzahl an Zufallszahlen. - * Sicherstellen, dass beim Befüllen keine Duplikate entstehen. - * Duplizieren eines zufälligen Eintrags im Array. - * in `getDuplicate()`: Sortieren des Arrays und Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente. */ +/************************************************************ + * BLOCK 0 – Zweck der Datei + * ---------------------------------------------------------- + * Diese Datei liefert zwei Funktionen für das Spiel: + * - createNumbers(len): erzeugt ein Array mit len Zufallszahlen, + * in dem GENAU EINE Zahl doppelt vorkommt. + * - getDuplicate(numbers, len): findet effizient die doppelte Zahl. + * + * Technik: + * - Beim Erzeugen verhindert ein Binärsuchbaum (BST) Duplikate. + * - Beim Finden sortieren wir eine Kopie und vergleichen Nachbarn. + ************************************************************/ -// Returns len random numbers between 1 and 2x len in random order which are all different, except for two entries. -// Returns NULL on errors. Use your implementation of the binary search tree to check for possible duplicates while -// creating random numbers. -unsigned int *createNumbers(unsigned int len) +#include // malloc, free, rand +#include // optional für Debug/printf +#include // time() für einmaliges srand-Seed +#include // memcpy +#include "numbers.h" // Deklarationen: createNumbers, getDuplicate +#include "bintree.h" // BST-API: addToTree, clearTree, CompareFctType + + +/************************************************************ + * BLOCK 1 – Gemeinsame Vergleichsfunktion + * ---------------------------------------------------------- + * compareUInt: + * - Definiert die Ordnung für unsigned int (aufsteigend). + * - Geeignet sowohl für den Binärbaum (addToTree) + * als auch für qsort. + * Rückgabewerte: + * - < 0 : a < b → im BST nach LINKS + * - = 0 : a == b → Duplikat + * - > 0 : a > b → im BST nach RECHTS + ************************************************************/ +static int compareUInt(const void *a, const void *b) { - + unsigned int va = *(const unsigned int *)a; + unsigned int vb = *(const unsigned int *)b; + if (va < vb) return -1; + if (va > vb) return 1; + return 0; } -// Returns only the only number in numbers which is present twice. Returns zero on errors. + +/************************************************************ + * BLOCK 2 – Zahlen erzeugen: createNumbers(len) + * ---------------------------------------------------------- + * Ziel: + * - Ein Array mit len Zufallszahlen im Bereich [1 .. 2*len]. + * - Zuerst ALLE eindeutig (via BST geprüft). + * - Danach GENAU EINE Zahl absichtlich duplizieren. + * - Zum Schluss das Array gleichverteilt mischen (Fisher–Yates). + * + * Rückgabe: + * - Pointer auf dynamisch allokiertes Array (Caller muss free). + * - NULL bei Fehlern (z. B. len < 2, malloc/Insert-Fehler). + ************************************************************/ +unsigned int *createNumbers(unsigned int len) +{ + /*********************** + * 2.1 Vorbedingungen & Speicher + ***********************/ + if (len < 2) // Ein Duplikat macht erst ab 2 Elementen Sinn + return NULL; + + unsigned int *numbers = (unsigned int *)malloc(sizeof(unsigned int) * len); + if (!numbers) // Speicherfehler + return NULL; + + /*********************** + * 2.2 Einmaliges Zufalls-Seed + * (falls main() nicht seedet, sorgen wir einmalig dafür) + ***********************/ + static int seeded = 0; // Merker: srand nur einmal pro Prozess + if (!seeded) { + srand((unsigned int)time(NULL)); + seeded = 1; + } + + /*********************** + * 2.3 BST-Setup & Wertebereich + ***********************/ + TreeNode *root = NULL; // Leerer Binärbaum zum Duplikat-Check + unsigned int range = 2 * len; // Zahlenbereich: 1..2*len + + /*********************** + * 2.4 len-1 EINDEUTIGE Zahlen erzeugen + * – jede Zahl sofort gegen den BST prüfen + ***********************/ + for (unsigned int i = 0; i < len - 1; i++) + { + unsigned int val; + int isDup; + + // Wiederholen, bis eine wirklich neue Zahl eingefügt wurde + for (;;) + { + isDup = 0; // vor jedem Insert zurücksetzen + val = (unsigned int)(rand() % range) + 1; // Kandidat in [1..2*len] + + // Versuch, val in den Baum einzufügen (Kopie wird im Knoten gespeichert) + TreeNode *newRoot = addToTree(root, &val, sizeof(val), compareUInt, &isDup); + + // Fehlerfall: Es wäre ein neuer Knoten, aber newRoot ist NULL (z. B. malloc-Fehler) + if (newRoot == NULL && isDup == 0) { + free(numbers); // Array freigeben + clearTree(root); // bisherige Baumknoten freigeben + return NULL; // sauber abbrechen + } + + // Wurzel ggf. aktualisieren (z. B. wenn Baum vorher leer war) + if (newRoot) { + root = newRoot; + } + + if (!isDup) { + // Wert war eindeutig → ins Array übernehmen und weiter zum nächsten i + numbers[i] = val; + break; + } + // sonst: Duplikat → neue Zufallszahl probieren + } + } + + /*********************** + * 2.5 GENAU EIN Duplikat erzeugen + * – eine der bestehenden Zahlen zufällig wählen und erneut eintragen + ***********************/ + unsigned int idx = (unsigned int)(rand() % (len - 1)); // Quelle in [0..len-2] + numbers[len - 1] = numbers[idx]; // Duplikat absichtlich erzeugt + + /*********************** + * 2.6 Gleichverteiltes Mischen (Fisher–Yates) + ***********************/ + for (unsigned int i = len - 1; i > 0; i--) + { + unsigned int j = (unsigned int)(rand() % (i + 1)); // j ∈ [0..i] + unsigned int tmp = numbers[i]; + numbers[i] = numbers[j]; + numbers[j] = tmp; + } + + /*********************** + * 2.7 Aufräumen & Rückgabe + ***********************/ + clearTree(root); // BST wird nicht mehr gebraucht + return numbers; // Ownership des Arrays liegt beim Aufrufer (free(numbers)) +} + + +/************************************************************ + * BLOCK 3 – Duplikat finden: getDuplicate(numbers, len) + * ---------------------------------------------------------- + * Idee: + * - Original-Array unangetastet lassen → KOPIE erstellen. + * - Kopie aufsteigend sortieren (qsort mit demselben Comparator). + * - Ein linearer Durchlauf findet das erste Paar identischer Nachbarn. + * + * Rückgabe: + * - die doppelte Zahl + * - 0 bei Fehlern (z. B. ungültige Parameter, malloc-Fehler). + ************************************************************/ unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) { + /*********************** + * 3.1 Vorbedingungen & Kopie allokieren + ***********************/ + if (!numbers || len < 2) // ungültig oder zu kurz + return 0; -} \ No newline at end of file + unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len); + if (!copy) // Speicherfehler + return 0; + + memcpy(copy, numbers, sizeof(unsigned int) * len); // Original in Kopie übertragen + + /*********************** + * 3.2 Kopie sortieren (qsort + compareUInt) + ***********************/ + qsort(copy, len, sizeof(unsigned int), compareUInt); + + /*********************** + * 3.3 Nachbarvergleich: erstes Paar gleicher Werte = Duplikat + ***********************/ + unsigned int result = 0; + for (unsigned int i = 0; i + 1 < len; i++) + { + if (copy[i] == copy[i + 1]) { + result = copy[i]; + break; // Duplikat gefunden → fertig + } + } + + /*********************** + * 3.4 Aufräumen & Rückgabe + ***********************/ + free(copy); // Kopie freigeben + return result; // 0, falls (unerwartet) kein Duplikat gefunden +} + + +/************************************************************ + * BLOCK 4 – Hinweise für die Vorstellung + * ---------------------------------------------------------- + * Ownership: + * - Das von createNumbers zurückgegebene Array muss vom Aufrufer + * später mit free(numbers) freigegeben werden. + * + * Fehlerbehandlung: + * - Bei jedem Fehlerpfad werden ALLLE angelegten Ressourcen sauber + * freigegeben (Array/Kopie/Baum). + * + * Komplexität: + * - Erzeugen: O(n log n) durch BST-Einfügen + O(n) fürs Shuffle. + * - Finden: O(n log n) durch qsort + O(n) für den Nachbarvergleich. + * + * Zusammenarbeit: + * - addToTree setzt bei Gleichheit isDuplicate=1 (Duplikat), + * und liefert bei neuen Werten die (ggf. neue) Wurzel zurück. + * - clearTree gibt ALLE Knoten inkl. Datenkopien frei. + ************************************************************/ diff --git a/numbers_test2.c b/numbers_test2.c index 319f4c3..0ca098c 100644 --- a/numbers_test2.c +++ b/numbers_test2.c @@ -17,28 +17,21 @@ * @param b Pointer auf einen unsigned int-Wert (rechter Operand) * @return -1, falls *a < *b; 1, falls *a > *b; 0, falls *a == *b */ + + + +/* +Fkt für Binärbaum Implementierung, um Ordnung der Knoten zu bestimmen +Entscheidet, ob links (kleiner) oder rechts (größer) einzufügen ist. +Ergibt der Vergleich = 0, wird das Duplikat erkannt +Geeignet für qsort() und für addToTree() (CompareFctType) +*/ static int compareUInt(const void *a, const void *b) { unsigned int va = *(const unsigned int *)a; unsigned int vb = *(const unsigned int *)b; - if (va < vb) return -1; - if (va > vb) return 1; - return 0; -} - -/** - * @brief Vergleichsfunktion für qsort() zur Sortierung von unsigned int-Arrays. - * - * @param a Pointer auf einen Arrayeintrag - * @param b Pointer auf einen Arrayeintrag - * @return -1, 0, 1 analog zu compareUInt() - */ -static int qsort_uint_cmp(const void *a, const void *b) -{ - unsigned int va = *(const unsigned int *)a; - unsigned int vb = *(const unsigned int *)b; - if (va < vb) return -1; - if (va > vb) return 1; + if (va < vb) return -1; // links + if (va > vb) return 1; // rechts return 0; } @@ -54,13 +47,16 @@ static int qsort_uint_cmp(const void *a, const void *b) * - Anschließend wird eine der bereits erzeugten Zahlen zufällig ausgewählt und * noch einmal an das Ende des Arrays geschrieben, um das geforderte Duplikat sicherzustellen. * - Zum Schluss wird das gesamte Array mittels Fisher–Yates-Algorithmus gemischt. - * + + + * Fehlerbehandlung: * - Bei len < 2 wird NULL zurückgegeben, da das Problem ein Duplikat erfordert. * - Bei Speicher- oder Baum-Insertionsfehlern wird aufgeräumt und NULL zurückgegeben. * Wichtig: Der Baumzeiger root wird erst nach erfolgreichem Insert aktualisiert, * um im Fehlerfall kein bereits aufgebautes Teilbaum-Objekt zu verlieren. - * + + * Randbedingungen / Annahmen: * - addToTree(root, &val, sizeof(val), compareUInt, &isDup) setzt isDup: * isDup == 1 bedeutet „Duplikat gefunden, Baum unverändert“, @@ -75,6 +71,8 @@ static int qsort_uint_cmp(const void *a, const void *b) * @param len Anzahl der zu erzeugenden Werte (muss >= 2 sein) * @return Pointer auf ein Array mit len Einträgen bei Erfolg; NULL bei Fehlern */ + + unsigned int *createNumbers(unsigned int len) { if (len < 2) @@ -84,9 +82,12 @@ unsigned int *createNumbers(unsigned int len) if (numbers == NULL) return NULL; + // Zufallszahlengenerator nur einmal pro Prozess initialisieren. // Hintergrund: Wird createNumbers mehrfach schnell hintereinander gerufen, // kann time(NULL) identische Seeds liefern und damit identische Zahlenfolgen erzeugen. + + static int seeded = 0; if (!seeded) { srand((unsigned int)time(NULL)); @@ -182,8 +183,7 @@ unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) memcpy(copy, numbers, sizeof(unsigned int) * len); // Sortieren der Kopie - qsort(copy, len, sizeof(unsigned int), qsort_uint_cmp); - + qsort(copy, len, sizeof(unsigned int), compareUInt); // Linearer Scan: erstes Paar identischer Nachbarn ist das Duplikat unsigned int result = 0; for (unsigned int i = 0; i + 1 < len; i++) diff --git a/numbers_test3.c b/numbers_test3.c deleted file mode 100644 index ea94d8e..0000000 --- a/numbers_test3.c +++ /dev/null @@ -1,221 +0,0 @@ - -/************************************************************ - * BLOCK 0 – Zweck der Datei - * ---------------------------------------------------------- - * Diese Datei liefert zwei Funktionen für das Spiel: - * - createNumbers(len): erzeugt ein Array mit len Zufallszahlen, - * in dem GENAU EINE Zahl doppelt vorkommt. - * - getDuplicate(numbers, len): findet effizient die doppelte Zahl. - * - * Technik: - * - Beim Erzeugen verhindert ein Binärsuchbaum (BST) Duplikate. - * - Beim Finden sortieren wir eine Kopie und vergleichen Nachbarn. - ************************************************************/ - -#include // malloc, free, rand -#include // optional für Debug/printf -#include // time() für einmaliges srand-Seed -#include // memcpy -#include "numbers.h" // Deklarationen: createNumbers, getDuplicate -#include "bintree.h" // BST-API: addToTree, clearTree, CompareFctType - - -/************************************************************ - * BLOCK 1 – Gemeinsame Vergleichsfunktion - * ---------------------------------------------------------- - * compareUInt: - * - Definiert die Ordnung für unsigned int (aufsteigend). - * - Geeignet sowohl für den Binärbaum (addToTree) - * als auch für qsort. - * Rückgabewerte: - * - < 0 : a < b → im BST nach LINKS - * - = 0 : a == b → Duplikat - * - > 0 : a > b → im BST nach RECHTS - ************************************************************/ -static int compareUInt(const void *a, const void *b) -{ - unsigned int va = *(const unsigned int *)a; - unsigned int vb = *(const unsigned int *)b; - if (va < vb) return -1; - if (va > vb) return 1; - return 0; -} - - -/************************************************************ - * BLOCK 2 – Zahlen erzeugen: createNumbers(len) - * ---------------------------------------------------------- - * Ziel: - * - Ein Array mit len Zufallszahlen im Bereich [1 .. 2*len]. - * - Zuerst ALLE eindeutig (via BST geprüft). - * - Danach GENAU EINE Zahl absichtlich duplizieren. - * - Zum Schluss das Array gleichverteilt mischen (Fisher–Yates). - * - * Rückgabe: - * - Pointer auf dynamisch allokiertes Array (Caller muss free). - * - NULL bei Fehlern (z. B. len < 2, malloc/Insert-Fehler). - ************************************************************/ -unsigned int *createNumbers(unsigned int len) -{ - /*********************** - * 2.1 Vorbedingungen & Speicher - ***********************/ - if (len < 2) // Ein Duplikat macht erst ab 2 Elementen Sinn - return NULL; - - unsigned int *numbers = (unsigned int *)malloc(sizeof(unsigned int) * len); - if (!numbers) // Speicherfehler - return NULL; - - /*********************** - * 2.2 Einmaliges Zufalls-Seed - * (falls main() nicht seedet, sorgen wir einmalig dafür) - ***********************/ - static int seeded = 0; // Merker: srand nur einmal pro Prozess - if (!seeded) { - srand((unsigned int)time(NULL)); - seeded = 1; - } - - /*********************** - * 2.3 BST-Setup & Wertebereich - ***********************/ - TreeNode *root = NULL; // Leerer Binärbaum zum Duplikat-Check - unsigned int range = 2 * len; // Zahlenbereich: 1..2*len - - /*********************** - * 2.4 len-1 EINDEUTIGE Zahlen erzeugen - * – jede Zahl sofort gegen den BST prüfen - ***********************/ - for (unsigned int i = 0; i < len - 1; i++) - { - unsigned int val; - int isDup; - - // Wiederholen, bis eine wirklich neue Zahl eingefügt wurde - for (;;) - { - isDup = 0; // vor jedem Insert zurücksetzen - val = (unsigned int)(rand() % range) + 1; // Kandidat in [1..2*len] - - // Versuch, val in den Baum einzufügen (Kopie wird im Knoten gespeichert) - TreeNode *newRoot = addToTree(root, &val, sizeof(val), compareUInt, &isDup); - - // Fehlerfall: Es wäre ein neuer Knoten, aber newRoot ist NULL (z. B. malloc-Fehler) - if (newRoot == NULL && isDup == 0) { - free(numbers); // Array freigeben - clearTree(root); // bisherige Baumknoten freigeben - return NULL; // sauber abbrechen - } - - // Wurzel ggf. aktualisieren (z. B. wenn Baum vorher leer war) - if (newRoot) { - root = newRoot; - } - - if (!isDup) { - // Wert war eindeutig → ins Array übernehmen und weiter zum nächsten i - numbers[i] = val; - break; - } - // sonst: Duplikat → neue Zufallszahl probieren - } - } - - /*********************** - * 2.5 GENAU EIN Duplikat erzeugen - * – eine der bestehenden Zahlen zufällig wählen und erneut eintragen - ***********************/ - unsigned int idx = (unsigned int)(rand() % (len - 1)); // Quelle in [0..len-2] - numbers[len - 1] = numbers[idx]; // Duplikat absichtlich erzeugt - - /*********************** - * 2.6 Gleichverteiltes Mischen (Fisher–Yates) - ***********************/ - for (unsigned int i = len - 1; i > 0; i--) - { - unsigned int j = (unsigned int)(rand() % (i + 1)); // j ∈ [0..i] - unsigned int tmp = numbers[i]; - numbers[i] = numbers[j]; - numbers[j] = tmp; - } - - /*********************** - * 2.7 Aufräumen & Rückgabe - ***********************/ - clearTree(root); // BST wird nicht mehr gebraucht - return numbers; // Ownership des Arrays liegt beim Aufrufer (free(numbers)) -} - - -/************************************************************ - * BLOCK 3 – Duplikat finden: getDuplicate(numbers, len) - * ---------------------------------------------------------- - * Idee: - * - Original-Array unangetastet lassen → KOPIE erstellen. - * - Kopie aufsteigend sortieren (qsort mit demselben Comparator). - * - Ein linearer Durchlauf findet das erste Paar identischer Nachbarn. - * - * Rückgabe: - * - die doppelte Zahl - * - 0 bei Fehlern (z. B. ungültige Parameter, malloc-Fehler). - ************************************************************/ -unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) -{ - /*********************** - * 3.1 Vorbedingungen & Kopie allokieren - ***********************/ - if (!numbers || len < 2) // ungültig oder zu kurz - return 0; - - unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len); - if (!copy) // Speicherfehler - return 0; - - memcpy(copy, numbers, sizeof(unsigned int) * len); // Original in Kopie übertragen - - /*********************** - * 3.2 Kopie sortieren (qsort + compareUInt) - ***********************/ - qsort(copy, len, sizeof(unsigned int), compareUInt); - - /*********************** - * 3.3 Nachbarvergleich: erstes Paar gleicher Werte = Duplikat - ***********************/ - unsigned int result = 0; - for (unsigned int i = 0; i + 1 < len; i++) - { - if (copy[i] == copy[i + 1]) { - result = copy[i]; - break; // Duplikat gefunden → fertig - } - } - - /*********************** - * 3.4 Aufräumen & Rückgabe - ***********************/ - free(copy); // Kopie freigeben - return result; // 0, falls (unerwartet) kein Duplikat gefunden -} - - -/************************************************************ - * BLOCK 4 – Hinweise für die Vorstellung - * ---------------------------------------------------------- - * Ownership: - * - Das von createNumbers zurückgegebene Array muss vom Aufrufer - * später mit free(numbers) freigegeben werden. - * - * Fehlerbehandlung: - * - Bei jedem Fehlerpfad werden ALLLE angelegten Ressourcen sauber - * freigegeben (Array/Kopie/Baum). - * - * Komplexität: - * - Erzeugen: O(n log n) durch BST-Einfügen + O(n) fürs Shuffle. - * - Finden: O(n log n) durch qsort + O(n) für den Nachbarvergleich. - * - * Zusammenarbeit: - * - addToTree setzt bei Gleichheit isDuplicate=1 (Duplikat), - * und liefert bei neuen Werten die (ggf. neue) Wurzel zurück. - * - clearTree gibt ALLE Knoten inkl. Datenkopien frei. - ************************************************************/