diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8f4782a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug test_stack", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test_stack.exe", + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe", + "preLaunchTask": "Build via Makefile" + }, + { + "name": "Debug test_numbers", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test_numbers.exe", + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe", + "preLaunchTask": "Build via Makefile" + }, + { + "name": "Debug test_bintree", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test_bintree.exe", + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe", + "preLaunchTask": "Build via Makefile" + } + + + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..dad5165 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build via Makefile", + "type": "shell", + "command": "mingw32-make", + "args": ["${input:target}"], + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$gcc" + } + ], + "inputs": [ + { + "id": "target", + "type": "pickString", + "description": "Welches Makefile-Target soll gebaut werden?", + "options": ["test_stack", "test_numbers", "test_bintree", "doble", "unitTests"] + } + ] +} \ No newline at end of file diff --git a/bintree.c b/bintree.c index 5cf82a9..91f0430 100644 --- a/bintree.c +++ b/bintree.c @@ -1,36 +1,148 @@ + +#include #include #include "stack.h" #include "bintree.h" -//TODO: binären Suchbaum implementieren -/* * `addToTree`: fügt ein neues Element in den Baum ein (rekursiv), - * `clearTree`: gibt den gesamten Baum frei (rekursiv), - * `treeSize`: zählt die Knoten im Baum (rekursiv), - * `nextTreeData`: Traversierung mit Hilfe des zuvor implementierten Stacks. */ - -// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates -// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). -TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) +/* + Schiebt einen Knoten und alle seine linken Nachfolger + (entlang der "linken Kante") auf den Iterator-Stack. + iterStackPtr: Zeiger auf den Top-Zeiger des Stacks (LIFO) für die Wiederholung + knoten: aktueller Startknoten, dessen linke Kette abgelegt wird +*/ +static void bintree_pushLefts(StackNode **iterStackPtr, TreeNode *knoten) { - + while (knoten != NULL) + { + *iterStackPtr = push(*iterStackPtr, knoten); // aktuellen Knoten oben auf den Stack legen + knoten = knoten->left; // zum linken Kind weiterlaufen + } } -// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction. -// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element, -// push the top node and push all its left nodes. -void *nextTreeData(TreeNode *root) -{ +/* + Fügt eine Kopie der Daten (Speicherbereich von 'data' mit Länge 'dataSize') in den Baum ein. + Die Ordnung wird über 'compareFct' festgelegt. + Duplikate: + - Wenn 'isDuplicate' != NULL übergeben wird: Duplikate werden NICHT eingefügt, + stattdessen wird *isDuplicate = 1 gesetzt (bei neuem Eintrag = 0). + - Wenn 'isDuplicate' == NULL: Duplikate SIND erlaubt; der Duplikat-Eintrag + wird in den rechten Teilbaum eingefügt. + + Rückgabe: + - Zeiger auf die (ggf. unveränderte oder neue) Wurzel des Teilbaums. + - NULL bei Speicherfehlern beim Anlegen des ersten Knotens. +*/ +TreeNode *addToTree(TreeNode *wurzel, + const void *daten, + size_t datenGroesse, + CompareFctType vergleichFkt, + int *istDuplikat) +{ + // Standardmäßig annehmen: kein Duplikat (falls Ausgabefeld vorhanden) + if (istDuplikat != NULL) + *istDuplikat = 0; + + // Leerer Baum/Teilbaum: neuen Knoten erzeugen + if (wurzel == NULL) + { + TreeNode *neuerKnoten = (TreeNode *)malloc(sizeof(TreeNode)); + if (neuerKnoten == NULL) + return NULL; // Speicherfehler + + neuerKnoten->data = malloc(datenGroesse); + if (neuerKnoten->data == NULL) + { + free(neuerKnoten); + return NULL; // Speicherfehler für Datenbereich + } + + memcpy(neuerKnoten->data, daten, datenGroesse); // tiefe Kopie der Nutzdaten + neuerKnoten->left = neuerKnoten->right = NULL; // Blatt + return neuerKnoten; + } + + // Vergleich der einzufügenden Daten mit dem aktuellen Knoten + int vergleich = vergleichFkt(daten, wurzel->data); + + if (vergleich < 0) + { + // links einfügen + wurzel->left = addToTree(wurzel->left, daten, datenGroesse, vergleichFkt, istDuplikat); + } + else if (vergleich > 0) + { + // rechts einfügen + wurzel->right = addToTree(wurzel->right, daten, datenGroesse, vergleichFkt, istDuplikat); + } + else + { + // Gleichheit (potenzielles Duplikat) + if (istDuplikat != NULL) + { + *istDuplikat = 1; // Duplikat erkannt, NICHT einfügen + // keine Änderung am Baum + } + else + { + // Duplikate erlaubt -> konventionell in den rechten Teilbaum einfügen + wurzel->right = addToTree(wurzel->right, daten, datenGroesse, vergleichFkt, istDuplikat); + } + } + + return wurzel; } -// Releases all memory resources (including data copies). -void clearTree(TreeNode *root) +void *nextTreeData(TreeNode *wurzel) { + static StackNode *iteratorStack = NULL; // interner Zustand über Aufrufe hinweg + // Neuer Baum übergeben -> Iterator zurücksetzen/initialisieren + if (wurzel != NULL) + { + clearStack(iteratorStack); // ggf. alten Stack leeren (Speicher freigeben) + iteratorStack = NULL; // Top-Zeiger zurücksetzen + bintree_pushLefts(&iteratorStack, wurzel); // Wurzel und linke Kette ablegen + } + + // Kein weiterer Eintrag? + if (iteratorStack == NULL) + return NULL; + + // Nächsten Knoten holen (oberstes Stack-Element) + TreeNode *aktuellerKnoten = (TreeNode *)top(iteratorStack); + iteratorStack = pop(iteratorStack); + + // Falls rechter Teilbaum existiert: dessen linke Kette ablegen + if (aktuellerKnoten->right != NULL) + bintree_pushLefts(&iteratorStack, aktuellerKnoten->right); + + // Daten des aktuellen Knotens zurückgeben + return aktuellerKnoten->data; } -// Returns the number of entries in the tree given by root. -unsigned int treeSize(const TreeNode *root) +/* + Gibt den gesamten Baum frei (inkl. der tief kopierten Daten). +*/ +void clearTree(TreeNode *wurzel) { + if (wurzel == NULL) + return; -} \ No newline at end of file + clearTree(wurzel->left); + clearTree(wurzel->right); + + free(wurzel->data); + free(wurzel); +} + +/* + Liefert die Anzahl der Knoten/Einträge im Teilbaum 'wurzel'. +*/ +unsigned int treeSize(const TreeNode *wurzel) +{ + if (wurzel == NULL) + return 0; + + return 1U + treeSize(wurzel->left) + treeSize(wurzel->right); +} diff --git a/bintree.h b/bintree.h index 25e16b2..7ea1ff1 100644 --- a/bintree.h +++ b/bintree.h @@ -1,27 +1,81 @@ + #ifndef BINTREE_H #define BINTREE_H #include +/* + Typdefinition für die Vergleichsfunktion. + + Die Funktion muss zwei Datenzeiger vergleichen und zurückgeben: + - < 0, wenn arg1 kleiner als arg2 ist + - 0, wenn arg1 gleich arg2 ist + - > 0, wenn arg1 größer als arg2 ist +*/ typedef int (*CompareFctType)(const void *arg1, const void *arg2); +/* + Struktur für einen Binärbaum-Knoten. + + Enthält: + - data: Zeiger auf die gespeicherten Daten (beliebiger Typ, dynamisch allokiert) + - left: Zeiger auf linken Teilbaum + - right: Zeiger auf rechten Teilbaum +*/ typedef struct node { - void *data; - struct node *left; - struct node *right; + void *data; // Zeiger auf die Nutzdaten + struct node *left; // Zeiger auf linken Kindknoten + struct node *right; // Zeiger auf rechten Kindknoten } TreeNode; -// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates -// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). +/* + Fügt eine Kopie der Daten in den Binärbaum ein. + + Parameter: + root : Wurzel des (Teil-)Baums + data : Zeiger auf die einzufügenden Daten + dataSize : Größe der Daten in Bytes + compareFct : Vergleichsfunktion für die Ordnung + isDuplicate : Optionaler Zeiger: + - Wenn NULL: Duplikate sind erlaubt + - Wenn != NULL: Duplikate werden ignoriert und *isDuplicate wird gesetzt: + - 0: neuer Eintrag eingefügt + - 1: Duplikat erkannt, nicht eingefügt + + Rückgabe: + Zeiger auf die (ggf. neue) Wurzel des Baums oder NULL bei Speicherfehler. +*/ TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate); -// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction. -// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element, -// push the top node and push all its left nodes. + +/* + Iteriert über den Baum in Inorder-Reihenfolge. + + Verhalten: + - Erster Aufruf mit root != NULL: Iterator initialisieren + - Folgeaufrufe mit root == NULL: nächstes Element zurückgeben + + Rückgabe: + Zeiger auf die Daten des nächsten Knotens oder NULL, wenn Ende erreicht. + + Hinweis: + Intern wird ein Stack verwendet. Nicht threadsicher. +*/ void *nextTreeData(TreeNode *root); -// Releases all memory resources (including data copies). + +/* + Gibt den gesamten Baum frei (inklusive der gespeicherten Daten). + + Nach dem Aufruf sind alle Zeiger ungültig. +*/ void clearTree(TreeNode *root); -// Returns the number of entries in the tree given by root. + +/* + Liefert die Anzahl der Knoten im Baum. + + Rückgabe: + Anzahl der Knoten (0 bei leerem Baum). +*/ unsigned int treeSize(const TreeNode *root); -#endif \ No newline at end of file +#endif diff --git a/doble.exe b/doble.exe new file mode 100644 index 0000000..04ae889 Binary files /dev/null and b/doble.exe differ diff --git a/doble_initial.exe b/doble_initial.exe new file mode 100644 index 0000000..a91c009 Binary files /dev/null and b/doble_initial.exe differ diff --git a/highscores.txt b/highscores.txt index 4edd5a7..cda4174 100644 --- a/highscores.txt +++ b/highscores.txt @@ -1 +1,10 @@ -player1;3999 +Player1;19887 +Player1;19843 +Lena;19811 +Lena;19702 +Player1;19578 +player_name;9981 +Lena;9980 +Lena;9978 +Lena;9978 +Lena;9976 diff --git a/makefile b/makefile index 1f15f75..c707aae 100644 --- a/makefile +++ b/makefile @@ -29,14 +29,38 @@ program_obj_files = stack.o bintree.o numbers.o timer.o highscore.o doble : main.o $(program_obj_files) $(CC) $(FLAGS) $^ -o doble -$(program_obj_filesobj_files): %.o: %.c - $(CC) -c $(FLAGS) $^ -o $@ +# pattern rule to build .o from .c +%.o: %.c + $(CC) -c $(FLAGS) $< -o $@ # -------------------------- # Unit Tests # -------------------------- -unitTests: - echo "needs to be implemented" +unitTests: test_stack test_numbers test_bintree + @total=0; passed=0; failed=0; \ + for t in test_stack test_numbers test_bintree; do \ + total=$$((total+1)); \ + printf "Running %s...\n" "$$t"; \ + ./$$t; rc=$$?; \ + if [ $$rc -eq 0 ]; then \ + printf "%s: PASS\n\n" "$$t"; \ + passed=$$((passed+1)); \ + else \ + printf "%s: FAIL (exit %d)\n\n" "$$t" $$rc; \ + failed=$$((failed+1)); \ + fi; \ + done; \ + printf "Summary: %d tests run, %d passed, %d failed\n" $$total $$passed $$failed; \ + exit $$failed + +test_stack: test_stack.c stack.c + $(CC) $(FLAGS) test_stack.c stack.c -o test_stack + +test_numbers: test_numbers.c numbers.c bintree.c stack.c + $(CC) $(FLAGS) test_numbers.c numbers.c bintree.c stack.c -o test_numbers + +test_bintree: test_bintree.c bintree.c stack.c + $(CC) $(FLAGS) test_bintree.c bintree.c stack.c -o test_bintree # -------------------------- # Clean diff --git a/numbers.c b/numbers.c index f59d9a2..319f4c3 100644 --- a/numbers.c +++ b/numbers.c @@ -1,3 +1,4 @@ + #include #include #include @@ -5,22 +6,194 @@ #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. */ - -// 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) +/** + * @brief Vergleichsfunktion für unsigned int-Werte zur Verwendung im Binärbaum. + * + * Diese Funktion wird von der Binärbaum-Implementierung genutzt, um die + * Ordnung der Knoten zu bestimmen. Sie vergleicht die dereferenzierten + * unsigned int-Werte a und b. + * + * @param a Pointer auf einen unsigned int-Wert (linker Operand) + * @param b Pointer auf einen unsigned int-Wert (rechter Operand) + * @return -1, falls *a < *b; 1, falls *a > *b; 0, falls *a == *b + */ +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. +/** + * @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; + return 0; +} + +/** + * @brief Erzeugt ein Array aus len Zufallszahlen im Bereich [1 .. 2*len], + * das genau einen duplizierten Wert enthält (d. h. len-1 eindeutige + 1 Duplikat), + * und mischt die Reihenfolge zufällig. + * + * Funktionsweise: + * - Es werden zunächst len-1 eindeutige Zufallszahlen erzeugt. Die Eindeutigkeit wird + * mithilfe eines Binärsuchbaums (BST) geprüft: addToTree() fügt die Zahl ein + * und signalisiert per isDup, ob sie bereits vorhanden war. + * - 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“, + * isDup == 0 bedeutet „neuer Wert eingefügt (oder Fehler)“. + * - Bei Speicherfehler gibt addToTree NULL zurück und isDup bleibt 0. + * - clearTree(root) darf mit NULL-Argument aufgerufen werden (No-Op). + * + * Komplexität: + * - Durchschnittlich O(len * log(len)) für die len-1 Einfügungen in den BST. + * - Shuffle in O(len). + * + * @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) + return NULL; + + unsigned int *numbers = (unsigned int *)malloc(sizeof(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)); + seeded = 1; + } + + TreeNode *root = NULL; + unsigned int range = 2 * len; + + // Schritt 1: len-1 eindeutige Zufallszahlen erzeugen + 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, um „alte“ Werte zu vermeiden + val = (unsigned int)(rand() % range) + 1; // Wertebereich [1 .. 2*len] + + // addToTree kann bei Erfolg einen (ggf. neuen) Wurzelzeiger liefern. + // Zur Vermeidung eines Speicherlecks bei Fehlern zunächst in temp speichern. + TreeNode *newRoot = addToTree(root, &val, sizeof(val), compareUInt, &isDup); + + if (newRoot == NULL && isDup == 0) { + // Vermutlich Speicher-/Insertionsfehler: aufräumen und abbrechen + free(numbers); + clearTree(root); // root zeigt noch auf den gültigen Teilbaum + return NULL; + } + + if (!isDup) { + // Einfügen war erfolgreich und der Wert ist eindeutig. + root = newRoot; + numbers[i] = val; + break; + } + // Andernfalls Duplikat: Neue Zufallszahl versuchen. + } + } + + // Schritt 2: Eine der bestehenden Zahlen zufällig duplizieren + unsigned int idx = (unsigned int)(rand() % (len - 1)); // Index im Bereich [0 .. len-2] + numbers[len - 1] = numbers[idx]; + + // Schritt 3: Fisher–Yates-Shuffle über das gesamte Array + for (unsigned int i = len - 1; i > 0; i--) + { + unsigned int j = (unsigned int)(rand() % (i + 1)); + unsigned int tmp = numbers[i]; + numbers[i] = numbers[j]; + numbers[j] = tmp; + } + + // Aufräumen: Baum freigeben + clearTree(root); + return numbers; +} + +/** + * @brief Findet den einzigen duplizierten Wert in einem Array aus len unsigned int. + * + * Funktionsweise: + * - Es wird eine Kopie des Eingabearrays erstellt, um die Reihenfolge des + * Originalarrays nicht zu verändern. + * - Die Kopie wird mittels qsort() aufsteigend sortiert. + * - Beim Durchlauf werden benachbarte Elemente verglichen. Da genau ein Wert + * doppelt vorkommt, finden wir ihn als erstes Paar gleicher Nachbarn. + * + * Fehlerbehandlung: + * - Bei ungültigen Parametern (numbers == NULL oder len < 2) wird 0 geliefert. + * - Bei Speicherfehlern beim Kopieren ebenfalls 0. + * + * Komplexität: + * - Sortieren in O(len * log(len)), anschließender Linearpass O(len). + * + * @param numbers Pointer auf das Eingabearray + * @param len Länge des Arrays (muss >= 2 sein) + * @return Der doppelte Wert; 0 bei Fehlern oder falls kein Duplikat gefunden wurde + * (gemäß Aufgabenstellung sollte aber genau ein Duplikat existieren). + */ unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) { + if (numbers == NULL || len < 2) + return 0; -} \ No newline at end of file + // Kopie erstellen, damit das Original unangetastet bleibt + unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len); + if (copy == NULL) + return 0; + + memcpy(copy, numbers, sizeof(unsigned int) * len); + + // Sortieren der Kopie + qsort(copy, len, sizeof(unsigned int), qsort_uint_cmp); + + // Linearer Scan: erstes Paar identischer Nachbarn ist das Duplikat + unsigned int result = 0; + for (unsigned int i = 0; i + 1 < len; i++) + { + if (copy[i] == copy[i + 1]) { + result = copy[i]; + break; + } + } + + free(copy); + return result; +} diff --git a/stack.c b/stack.c index e3a90d4..9891622 100644 --- a/stack.c +++ b/stack.c @@ -1,33 +1,47 @@ #include #include "stack.h" -//TODO: grundlegende Stackfunktionen implementieren: -/* * `push`: legt ein Element oben auf den Stack, - * `pop`: entfernt das oberste Element, - * `top`: liefert das oberste Element zurück, - * `clearStack`: gibt den gesamten Speicher frei. */ - -// Pushes data as pointer onto the stack. +// Push Daten auf den Stack legen. StackNode *push(StackNode *stack, void *data) { + StackNode *node = malloc(sizeof(StackNode)); + if(node == NULL) + return stack; // allocation failed -> return unchanged stack + node->data = data; // Setze Daten + node->next = stack; // Setze nächsten Knoten auf aktuellen Stack + return node; } // Deletes the top element of the stack (latest added element) and releases its memory. (Pointer to data has to be // freed by caller.) StackNode *pop(StackNode *stack) { + if(stack == NULL) + return NULL; + StackNode *next = stack->next; + // Speicher des aktuellen Knotens freigeben + free(stack); + return next; } // Returns the data of the top element. void *top(StackNode *stack) { - + if(stack == NULL) + return NULL; + return stack->data; } // Clears stack and releases all memory. void clearStack(StackNode *stack) { - + while(stack != NULL) + { + StackNode *next = stack->next; + // Do NOT free stack->data here; caller owns the pointed data + free(stack); + stack = next; + } } \ No newline at end of file diff --git a/stack.h b/stack.h index f7d542d..e2ca791 100644 --- a/stack.h +++ b/stack.h @@ -7,7 +7,12 @@ The latest element is taken from the stack. */ #include -//TODO: passenden Datentyp als struct anlegen +// Stack node for linked-list based stack. Data pointer is stored but not freed by stack operations. +typedef struct StackNode +{ + void *data; + struct StackNode *next; +} StackNode; // Pushes data as pointer onto the stack. StackNode *push(StackNode *stack, void *data); diff --git a/test_bintree b/test_bintree new file mode 100755 index 0000000..f594787 Binary files /dev/null and b/test_bintree differ diff --git a/test_bintree.c b/test_bintree.c new file mode 100644 index 0000000..fbcc0d6 --- /dev/null +++ b/test_bintree.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "bintree.h" + +// comparator for unsigned int values stored by value in heap +static int cmp_uint_ptr(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; +} + +int main(void) +{ + TreeNode *root = NULL; + unsigned int vals[] = {5, 2, 8, 1, 3, 7, 9}; + const size_t n = sizeof(vals)/sizeof(vals[0]); + + // insert values (copy made by addToTree) + for(size_t i = 0; i < n; i++) + { + int isDup = 0; + root = addToTree(root, &vals[i], sizeof(unsigned int), cmp_uint_ptr, &isDup); + if(root == NULL && isDup == 0) { fprintf(stderr, "addToTree allocation failed\n"); return 1; } + } + + unsigned int sz = treeSize(root); + if(sz != n) { fprintf(stderr, "treeSize expected %zu got %u\n", n, sz); clearTree(root); return 2; } + + // inorder traversal using nextTreeData + unsigned int last = 0; + int first = 1; + unsigned int *data = nextTreeData(root); + while(data != NULL) + { + unsigned int v = *data; + if(!first && v < last) { fprintf(stderr, "inorder traversal not sorted: %u after %u\n", v, last); clearTree(root); return 3; } + last = v; first = 0; + data = nextTreeData(NULL); + } + + clearTree(root); + printf("test_bintree: OK\n"); + return 0; +} diff --git a/test_bintree.dSYM/Contents/Info.plist b/test_bintree.dSYM/Contents/Info.plist new file mode 100644 index 0000000..fe95a38 --- /dev/null +++ b/test_bintree.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_bintree + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/test_bintree.dSYM/Contents/Resources/DWARF/test_bintree b/test_bintree.dSYM/Contents/Resources/DWARF/test_bintree new file mode 100644 index 0000000..0d498e1 Binary files /dev/null and b/test_bintree.dSYM/Contents/Resources/DWARF/test_bintree differ diff --git a/test_bintree.dSYM/Contents/Resources/Relocations/x86_64/test_bintree.yml b/test_bintree.dSYM/Contents/Resources/Relocations/x86_64/test_bintree.yml new file mode 100644 index 0000000..4fab309 --- /dev/null +++ b/test_bintree.dSYM/Contents/Resources/Relocations/x86_64/test_bintree.yml @@ -0,0 +1,20 @@ +--- +triple: 'x86_64-apple-darwin' +binary-path: test_bintree +relocations: + - { offset: 0x26, size: 0x8, addend: 0x0, symName: _main, symObjAddr: 0x0, symBinAddr: 0x1000007A0, symSize: 0x220 } + - { offset: 0x49, size: 0x8, addend: 0x0, symName: _main, symObjAddr: 0x0, symBinAddr: 0x1000007A0, symSize: 0x220 } + - { offset: 0x121, size: 0x8, addend: 0x0, symName: _cmp_uint_ptr, symObjAddr: 0x220, symBinAddr: 0x1000009C0, symSize: 0x5A } + - { offset: 0x21B, size: 0x8, addend: 0x0, symName: _addToTree, symObjAddr: 0x0, symBinAddr: 0x100000A20, symSize: 0x1C0 } + - { offset: 0x228, size: 0x8, addend: 0x0, symName: _nextTreeData, symObjAddr: 0x1C0, symBinAddr: 0x100000BE0, symSize: 0xC0 } + - { offset: 0x24D, size: 0x8, addend: 0x0, symName: _nextTreeData.iterStack, symObjAddr: 0xFB0, symBinAddr: 0x100002000, symSize: 0x0 } + - { offset: 0x2EC, size: 0x8, addend: 0x0, symName: _addToTree, symObjAddr: 0x0, symBinAddr: 0x100000A20, symSize: 0x1C0 } + - { offset: 0x376, size: 0x8, addend: 0x0, symName: _bintree_pushLefts, symObjAddr: 0x280, symBinAddr: 0x100000CA0, symSize: 0x50 } + - { offset: 0x3A8, size: 0x8, addend: 0x0, symName: _clearTree, symObjAddr: 0x2D0, symBinAddr: 0x100000CF0, symSize: 0x60 } + - { offset: 0x3CC, size: 0x8, addend: 0x0, symName: _treeSize, symObjAddr: 0x330, symBinAddr: 0x100000D50, symSize: 0x56 } + - { offset: 0x47F, size: 0x8, addend: 0x0, symName: _push, symObjAddr: 0x0, symBinAddr: 0x100000DB0, symSize: 0x60 } + - { offset: 0x4C3, size: 0x8, addend: 0x0, symName: _push, symObjAddr: 0x0, symBinAddr: 0x100000DB0, symSize: 0x60 } + - { offset: 0x507, size: 0x8, addend: 0x0, symName: _pop, symObjAddr: 0x60, symBinAddr: 0x100000E10, symSize: 0x50 } + - { offset: 0x53D, size: 0x8, addend: 0x0, symName: _top, symObjAddr: 0xB0, symBinAddr: 0x100000E60, symSize: 0x40 } + - { offset: 0x565, size: 0x8, addend: 0x0, symName: _clearStack, symObjAddr: 0xF0, symBinAddr: 0x100000EA0, symSize: 0x3F } +... diff --git a/test_numbers b/test_numbers new file mode 100755 index 0000000..f45378c Binary files /dev/null and b/test_numbers differ diff --git a/test_numbers.c b/test_numbers.c new file mode 100644 index 0000000..e14e98b --- /dev/null +++ b/test_numbers.c @@ -0,0 +1,91 @@ + +#include +#include +#include +#include "numbers.h" + +/** + * @brief Selbsttest für createNumbers() und getDuplicate(). + * + * Erzeugt ein Array aus len Zufallszahlen mit genau einem duplizierten Wert, + * validiert die Eigenschaften per Zähl-Array (Wertebereich, Häufigkeiten) + * und prüft anschließend, ob getDuplicate() dasselbe Duplikat ermittelt. + * + * Rückgabecodes: + * 0: OK + * 1: createNumbers() fehlgeschlagen + * 2: Speicherfehler für counts + * 3: Wert außerhalb des Bereichs [1..2*len] + * 4: Ein Wert erscheint öfter als zweimal + * 5: Nicht genau ein Duplikat gefunden + * 6: getDuplicate() liefert anderes Ergebnis als Zählung + */ +int main(void) +{ + unsigned int len = 100; // Anzahl zu erzeugender Zahlen + unsigned int *nums = createNumbers(len); + if (nums == NULL) { + fprintf(stderr, "createNumbers returned NULL\n"); + return 1; // Erzeugung fehlgeschlagen + } + + unsigned int maxVal = 2 * len; // Erlaubter Bereich: [1 .. 2*len] + + // Zähl-Array für Häufigkeiten pro Wert (Index 0 bleibt ungenutzt) + unsigned int *counts = calloc(maxVal + 1, sizeof(unsigned int)); + if (counts == NULL) { + free(nums); + return 2; // Speicherfehler bei counts + } + + // Häufigkeiten bestimmen und gleichzeitig Bereich prüfen + for (unsigned int i = 0; i < len; i++) { + unsigned int v = nums[i]; + if (v == 0 || v > maxVal) { // sollte nicht passieren, wenn createNumbers korrekt ist + fprintf(stderr, "value out of expected range\n"); + free(nums); + free(counts); + return 3; + } + counts[v]++; // Auftreten des Werts v zählen + } + + // Exakt ein Wert muss doppelt vorkommen; keiner darf >2-mal vorkommen + int duplicatesFound = 0; + unsigned int duplicateValue = 0; + + for (unsigned int v = 1; v <= maxVal; v++) { + if (counts[v] == 2) { + duplicatesFound++; + duplicateValue = v; // den doppelten Wert merken + } else if (counts[v] > 2) { + fprintf(stderr, "value %u appears more than twice\n", v); + free(nums); + free(counts); + return 4; // Vertragsbruch: zu häufiges Auftreten + } + // counts[v] == 0 oder 1 sind unkritisch + } + + if (duplicatesFound != 1) { + fprintf(stderr, "expected exactly one duplicated value, found %d\n", duplicatesFound); + free(nums); + free(counts); + return 5; // zu wenige/zu viele Duplikate + } + + // Ergebnis von getDuplicate() mit der Zählung abgleichen + unsigned int found = getDuplicate(nums, len); + if (found != duplicateValue) { + fprintf(stderr, "getDuplicate returned %u expected %u\n", found, duplicateValue); + free(nums); + free(counts); + return 6; // Abweichung zwischen Methoden + } + + // Ressourcen freigeben und Erfolg melden + free(nums); + free(counts); + printf("test_numbers: OK (duplicate = %u)\n", duplicateValue); + return 0; +} diff --git a/test_numbers.dSYM/Contents/Info.plist b/test_numbers.dSYM/Contents/Info.plist new file mode 100644 index 0000000..3520253 --- /dev/null +++ b/test_numbers.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_numbers + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/test_numbers.dSYM/Contents/Resources/DWARF/test_numbers b/test_numbers.dSYM/Contents/Resources/DWARF/test_numbers new file mode 100644 index 0000000..1ea4795 Binary files /dev/null and b/test_numbers.dSYM/Contents/Resources/DWARF/test_numbers differ diff --git a/test_numbers.dSYM/Contents/Resources/Relocations/x86_64/test_numbers.yml b/test_numbers.dSYM/Contents/Resources/Relocations/x86_64/test_numbers.yml new file mode 100644 index 0000000..703cd2f --- /dev/null +++ b/test_numbers.dSYM/Contents/Resources/Relocations/x86_64/test_numbers.yml @@ -0,0 +1,24 @@ +--- +triple: 'x86_64-apple-darwin' +binary-path: test_numbers +relocations: + - { offset: 0x26, size: 0x8, addend: 0x0, symName: _main, symObjAddr: 0x0, symBinAddr: 0x1000013A0, symSize: 0x287 } + - { offset: 0x41, size: 0x8, addend: 0x0, symName: _main, symObjAddr: 0x0, symBinAddr: 0x1000013A0, symSize: 0x287 } + - { offset: 0x12E, size: 0x8, addend: 0x0, symName: _createNumbers, symObjAddr: 0x0, symBinAddr: 0x100001630, symSize: 0x1D0 } + - { offset: 0x152, size: 0x8, addend: 0x0, symName: _createNumbers, symObjAddr: 0x0, symBinAddr: 0x100001630, symSize: 0x1D0 } + - { offset: 0x23E, size: 0x8, addend: 0x0, symName: _compareUInt, symObjAddr: 0x1D0, symBinAddr: 0x100001800, symSize: 0x60 } + - { offset: 0x290, size: 0x8, addend: 0x0, symName: _getDuplicate, symObjAddr: 0x230, symBinAddr: 0x100001860, symSize: 0x110 } + - { offset: 0x2FE, size: 0x8, addend: 0x0, symName: _qsort_uint_cmp, symObjAddr: 0x340, symBinAddr: 0x100001970, symSize: 0x5A } + - { offset: 0x3C5, size: 0x8, addend: 0x0, symName: _addToTree, symObjAddr: 0x0, symBinAddr: 0x1000019D0, symSize: 0x1C0 } + - { offset: 0x3D2, size: 0x8, addend: 0x0, symName: _nextTreeData, symObjAddr: 0x1C0, symBinAddr: 0x100001B90, symSize: 0xC0 } + - { offset: 0x3F7, size: 0x8, addend: 0x0, symName: _nextTreeData.iterStack, symObjAddr: 0xFB0, symBinAddr: 0x100003000, symSize: 0x0 } + - { offset: 0x496, size: 0x8, addend: 0x0, symName: _addToTree, symObjAddr: 0x0, symBinAddr: 0x1000019D0, symSize: 0x1C0 } + - { offset: 0x520, size: 0x8, addend: 0x0, symName: _bintree_pushLefts, symObjAddr: 0x280, symBinAddr: 0x100001C50, symSize: 0x50 } + - { offset: 0x552, size: 0x8, addend: 0x0, symName: _clearTree, symObjAddr: 0x2D0, symBinAddr: 0x100001CA0, symSize: 0x60 } + - { offset: 0x576, size: 0x8, addend: 0x0, symName: _treeSize, symObjAddr: 0x330, symBinAddr: 0x100001D00, symSize: 0x56 } + - { offset: 0x629, size: 0x8, addend: 0x0, symName: _push, symObjAddr: 0x0, symBinAddr: 0x100001D60, symSize: 0x60 } + - { offset: 0x66D, size: 0x8, addend: 0x0, symName: _push, symObjAddr: 0x0, symBinAddr: 0x100001D60, symSize: 0x60 } + - { offset: 0x6B1, size: 0x8, addend: 0x0, symName: _pop, symObjAddr: 0x60, symBinAddr: 0x100001DC0, symSize: 0x50 } + - { offset: 0x6E7, size: 0x8, addend: 0x0, symName: _top, symObjAddr: 0xB0, symBinAddr: 0x100001E10, symSize: 0x40 } + - { offset: 0x70F, size: 0x8, addend: 0x0, symName: _clearStack, symObjAddr: 0xF0, symBinAddr: 0x100001E50, symSize: 0x3F } +... diff --git a/test_stack.c b/test_stack.c new file mode 100644 index 0000000..91d187a --- /dev/null +++ b/test_stack.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include "stack.h" + +int main(void) +{ + StackNode *s = NULL; + + // push 3 integers + int *a = malloc(sizeof(int)); + *a = 1; + s = push(s, a); + int *b = malloc(sizeof(int)); + *b = 2; + s = push(s, b); + int *c = malloc(sizeof(int)); + *c = 3; + s = push(s, c); + + // oben liegt c (3) + int *topv = (int *)top(s); + if(topv == NULL || *topv != 3) { fprintf(stderr, "stack top expected 3\n"); return 1; } + + // oben liegt nun 2 + s = pop(s); + topv = (int *)top(s); + if(topv == NULL || *topv != 2) { fprintf(stderr, "stack top expected 2 after pop\n"); return 2; } + + // oben liegt nun 1 + s = pop(s); + topv = (int *)top(s); + if(topv == NULL || *topv != 1) { fprintf(stderr, "stack top expected 1 after pop\n"); return 3; } + + // letzen Stapelinhalt holen + s = pop(s); + if(s != NULL) { fprintf(stderr, "stack expected empty after popping all\n"); return 4; } + + // Eigenen Speicher freigeben + free(a); + free(b); + free(c); + + // test clearStack mit leerem Stack + s = push(s, malloc(sizeof(int))); + s = push(s, malloc(sizeof(int))); + clearStack(s); // clearStack must free nodes but not payload; free payloads not necessary because we leaked intentionally for test of API + // Note: above payloads are not freed (stack API spec); this test ensures clearStack doesn't crash. + // Funktioniert alles + printf("test_stack: OK\n"); + return 0; +} diff --git a/test_stack.dSYM/Contents/Info.plist b/test_stack.dSYM/Contents/Info.plist new file mode 100644 index 0000000..eca9d8f --- /dev/null +++ b/test_stack.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.test_stack + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/test_stack.dSYM/Contents/Resources/DWARF/test_stack b/test_stack.dSYM/Contents/Resources/DWARF/test_stack new file mode 100644 index 0000000..aa57c7c Binary files /dev/null and b/test_stack.dSYM/Contents/Resources/DWARF/test_stack differ diff --git a/test_stack.dSYM/Contents/Resources/Relocations/x86_64/test_stack.yml b/test_stack.dSYM/Contents/Resources/Relocations/x86_64/test_stack.yml new file mode 100644 index 0000000..6432f19 --- /dev/null +++ b/test_stack.dSYM/Contents/Resources/Relocations/x86_64/test_stack.yml @@ -0,0 +1,12 @@ +--- +triple: 'x86_64-apple-darwin' +binary-path: test_stack +relocations: + - { offset: 0x26, size: 0x8, addend: 0x0, symName: _main, symObjAddr: 0x0, symBinAddr: 0x100000B80, symSize: 0x249 } + - { offset: 0x4E, size: 0x8, addend: 0x0, symName: _main, symObjAddr: 0x0, symBinAddr: 0x100000B80, symSize: 0x249 } + - { offset: 0x10A, size: 0x8, addend: 0x0, symName: _push, symObjAddr: 0x0, symBinAddr: 0x100000DD0, symSize: 0x60 } + - { offset: 0x14E, size: 0x8, addend: 0x0, symName: _push, symObjAddr: 0x0, symBinAddr: 0x100000DD0, symSize: 0x60 } + - { offset: 0x192, size: 0x8, addend: 0x0, symName: _pop, symObjAddr: 0x60, symBinAddr: 0x100000E30, symSize: 0x50 } + - { offset: 0x1C8, size: 0x8, addend: 0x0, symName: _top, symObjAddr: 0xB0, symBinAddr: 0x100000E80, symSize: 0x40 } + - { offset: 0x1F0, size: 0x8, addend: 0x0, symName: _clearStack, symObjAddr: 0xF0, symBinAddr: 0x100000EC0, symSize: 0x3F } +... diff --git a/test_stack.exe b/test_stack.exe new file mode 100644 index 0000000..b2c0199 Binary files /dev/null and b/test_stack.exe differ