From 4baf01896313d2be138cab82fca27d1ddd861f57 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 16 Dec 2025 14:19:06 +0100 Subject: [PATCH] Finished --- bintree.c | 81 +++++++++++++++++++++++- bintreeTest.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++ makefile | 10 ++- numbers.c | 51 ++++++++++++++- stack.c | 23 +++++-- stack.h | 5 ++ test_numbers.c | 112 +++++++++++++++++++++++++++++++++ test_stack.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 602 insertions(+), 7 deletions(-) create mode 100644 bintreeTest.c create mode 100644 test_numbers.c create mode 100644 test_stack.c diff --git a/bintree.c b/bintree.c index 6d982b2..5f54aee 100644 --- a/bintree.c +++ b/bintree.c @@ -12,7 +12,44 @@ // 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) { -//Zzzzz + /* Neuer Knoten */ + if (root == NULL) { + TreeNode *node = malloc(sizeof(TreeNode)); + if (!node) + return NULL; + + node->data = malloc(dataSize); + if (!node->data) { + free(node); + return NULL; + } + + memcpy(node->data, data, dataSize); + node->left = NULL; + node->right = NULL; + + if (isDuplicate) + *isDuplicate = 0; + + return node; + } + + int cmp = compareFct(data, root->data); + + if (cmp == 0) { + /* Duplikat */ + if (isDuplicate) + *isDuplicate = 1; + return root; + } + else if (cmp < 0) { + root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); + } + else { + root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); + } + + return root; } // 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. @@ -20,17 +57,59 @@ TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFc // push the top node and push all its left nodes. void *nextTreeData(TreeNode *root) { + static StackNode *stack = NULL; + static TreeNode *currentRoot = NULL; + /* Neuer Baum → Iterator zurücksetzen */ + if (root != NULL) { + clearStack(stack); + stack = NULL; + currentRoot = root; + + /* Alle linken Knoten pushen */ + while (currentRoot) { + stack = push(stack, currentRoot); + currentRoot = currentRoot->left; + } + } + + if (stack == NULL) + return NULL; + + /* Nächsten Knoten holen */ + TreeNode *node = top(stack); + stack = pop(stack); + + void *result = node->data; + + /* Rechter Teilbaum → wieder alle linken pushen */ + node = node->right; + while (node) { + stack = push(stack, node); + node = node->left; + } + + return result; } // Releases all memory resources (including data copies). void clearTree(TreeNode *root) { + if (!root) + return; + clearTree(root->left); + clearTree(root->right); + + free(root->data); + free(root); } // Returns the number of entries in the tree given by root. unsigned int treeSize(const TreeNode *root) { + if (!root) + return 0; + return 1 + treeSize(root->left) + treeSize(root->right); } \ No newline at end of file diff --git a/bintreeTest.c b/bintreeTest.c new file mode 100644 index 0000000..6b83c58 --- /dev/null +++ b/bintreeTest.c @@ -0,0 +1,159 @@ +// +// Created by Johannes on 15.12.2025. +// +#include +#include +#include +#include "unity.h" +#include "bintree.h" +#include "stack.h" + +/* -------------------------------------------------- + * Pflichtfunktionen für Unity + * -------------------------------------------------- */ +void setUp(void) {} +void tearDown(void) {} + +/* -------------------------------------------------- + * Mini-Testframework (RUNTEST) + * -------------------------------------------------- */ +static int tests_run = 0; +static int tests_failed = 0; + +#define RUNTEST(test) do { \ + tests_run++; \ + printf("Running %-35s ... ", #test); \ + if (test()) { \ + printf("OK\n"); \ + } else { \ + printf("FAIL\n"); \ + tests_failed++; \ + } \ +} while (0) + +#define ASSERT_TRUE(expr) do { \ + if (!(expr)) return false; \ +} while (0) + +/* -------------------------------------------------- + * Hilfsfunktionen + * -------------------------------------------------- */ +static int compareInt(const void *a, const void *b) +{ + int x = *(const int *)a; + int y = *(const int *)b; + return (x > y) - (x < y); +} + +/* -------------------------------------------------- + * Tests + * -------------------------------------------------- */ + +// Test: Einfügen eines einzelnen Knotens +bool test_addToTree_single(void) +{ + TreeNode *tree = NULL; + int value = 10; + int isDup = -1; + + tree = addToTree(tree, &value, sizeof(int), compareInt, &isDup); + + ASSERT_TRUE(tree != NULL); + ASSERT_TRUE(*(int *)tree->data == 10); + ASSERT_TRUE(isDup == 0); + + clearTree(tree); + return true; +} + +// Test: Duplikate erkennen +bool test_addToTree_duplicate(void) +{ + TreeNode *tree = NULL; + int value = 5; + int isDup; + + tree = addToTree(tree, &value, sizeof(int), compareInt, &isDup); + tree = addToTree(tree, &value, sizeof(int), compareInt, &isDup); + + ASSERT_TRUE(isDup == 1); + ASSERT_TRUE(treeSize(tree) == 1); + + clearTree(tree); + return true; +} + +// Test: Baumgröße korrekt zählen +bool test_treeSize(void) +{ + TreeNode *tree = NULL; + int values[] = {5, 3, 7, 2, 4}; + + for (int i = 0; i < 5; i++) + tree = addToTree(tree, &values[i], sizeof(int), compareInt, NULL); + + ASSERT_TRUE(treeSize(tree) == 5); + + clearTree(tree); + return true; +} + +// Test: Inorder-Traversierung (nextTreeData) +bool test_nextTreeData_inorder(void) +{ + TreeNode *tree = NULL; + int values[] = {5, 3, 7, 2, 4}; + + for (int i = 0; i < 5; i++) + tree = addToTree(tree, &values[i], sizeof(int), compareInt, NULL); + + int expected[] = {2, 3, 4, 5, 7}; + + int *val = nextTreeData(tree); + for (int i = 0; i < 5; i++) { + ASSERT_TRUE(val != NULL); + ASSERT_TRUE(*val == expected[i]); + val = nextTreeData(NULL); + } + + ASSERT_TRUE(val == NULL); + + clearTree(tree); + return true; +} + +// Test: clearTree räumt vollständig auf +bool test_clearTree(void) +{ + TreeNode *tree = NULL; + int values[] = {1, 2, 3}; + + for (int i = 0; i < 3; i++) + tree = addToTree(tree, &values[i], sizeof(int), compareInt, NULL); + + clearTree(tree); + tree = NULL; + + ASSERT_TRUE(tree == NULL); + return true; +} + +/* -------------------------------------------------- + * main + * -------------------------------------------------- */ +int main(void) +{ + UNITY_BEGIN(); + + RUNTEST(test_addToTree_single); + RUNTEST(test_addToTree_duplicate); + RUNTEST(test_treeSize); + RUNTEST(test_nextTreeData_inorder); + RUNTEST(test_clearTree); + + printf("\nTests run: %d\n", tests_run); + printf("Tests failed: %d\n", tests_failed); + + UNITY_END(); + return tests_failed ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/makefile b/makefile index 1f15f75..8f6feb6 100644 --- a/makefile +++ b/makefile @@ -36,7 +36,15 @@ $(program_obj_filesobj_files): %.o: %.c # Unit Tests # -------------------------- unitTests: - echo "needs to be implemented" + +stackTests: stack.o test_stack.c $(unityfolder)/unity.c + $(CC) $(CFLAGS) -I$(unityfolder) -o stackTests test_stack.c stack.o $(unityfolder)/unity.c ${LDFLAGS} +bintreeTests: bintree.o stack.o bintreeTest.c $(unityfolder)/unity.c + $(CC) $(CFLAGS) -I$(unityfolder) -o bintreeTests bintreeTest.c bintree.o stack.o $(unityfolder)/unity.c ${LDFLAGS} + +numbersTests: numbers.o test_numbers.c $(unityfolder)/unity.c + $(CC) $(CFLAGS) -I$(unityfolder) -o numbersTests test_numbers.c numbers.o $(unityfolder)/unity.c ${LDFLAGS} + # -------------------------- # Clean diff --git a/numbers.c b/numbers.c index f59d9a2..b14020d 100644 --- a/numbers.c +++ b/numbers.c @@ -9,18 +9,67 @@ /* * * 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. */ + * in `getDuplicate()`: Sortieren des Arrays und Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente. + +srand(time(NULL)); // Generator initialisieren +int zufallsZahl = rand(); // Zufallszahl generieren +*/ // 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) { + srand(time(NULL)); + unsigned int *arr = malloc(len * sizeof(unsigned int)); + if(arr == NULL) + return NULL; + + for(int i = 0; i < len; i++) + arr[i] = 0; + + for(int j = 0; j < len; j++) { + do { + arr[j] = (rand() % (2 * len)) + 1; + if (j >= 1) { + for (int k = 0; k < j; k++) { + if (arr[j] == arr[k]) { + arr[j] = 0; + break; + } + } + } + } while (arr[j] == 0); + } + + unsigned int strc = 0; + unsigned int strv = 0; + do { + strv = rand() % len; + strc = rand() % len; + } while (strv == strc); + + arr[strv] = arr[strc]; + + return arr; } // Returns only the only number in numbers which is present twice. Returns zero on errors. unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) { + unsigned int duplicate = 0; + unsigned int counter = 0; + for(int i = 0; i < (len - 1); i++) { + for(int j = 1; j < (len - i); j++) { + if(numbers[i] == numbers[i+j]) { + duplicate = numbers[i]; + counter++; + } + } + } + if(counter != 1) + duplicate = 0; + return duplicate; } \ No newline at end of file diff --git a/stack.c b/stack.c index e3a90d4..72af6e4 100644 --- a/stack.c +++ b/stack.c @@ -10,24 +10,39 @@ // Pushes data as pointer onto the stack. StackNode *push(StackNode *stack, void *data) { - + StackNode *newNode = malloc(sizeof(StackNode)); + if (!newNode) + return stack; + newNode->data = data; + newNode->next = stack; + return newNode; } // 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) + return NULL; + StackNode *next = stack->next; + free(stack); // Speicher des Knotens freigeben + return next; } // Returns the data of the top element. void *top(StackNode *stack) { - + if (!stack) + return NULL; + return stack->data; } // Clears stack and releases all memory. void clearStack(StackNode *stack) { - + while (stack) { + StackNode *next = stack->next; + free(stack); + stack = next; + } } \ No newline at end of file diff --git a/stack.h b/stack.h index f7d542d..0ba7c08 100644 --- a/stack.h +++ b/stack.h @@ -8,6 +8,11 @@ The latest element is taken from the stack. */ #include //TODO: passenden Datentyp als struct anlegen +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_numbers.c b/test_numbers.c new file mode 100644 index 0000000..58caa7b --- /dev/null +++ b/test_numbers.c @@ -0,0 +1,112 @@ +// +// Created by Johannes on 15.12.2025. +// +#include +#include +#include +#include "numbers.h" + +//Framework +static int tests_run = 0; +static int tests_failed = 0; + +#define RUNTEST(test) do { \ + tests_run++; \ + printf("Running %-30s ... ", #test); \ + if (test()) { \ + printf("OK\n"); \ + } else { \ + printf("FAIL\n"); \ + tests_failed++; \ + } \ +} while (0) + +#define ASSERT_TRUE(expr) do { \ + if (!(expr)) return false; \ +} while (0) + + +void setUp(void) { +} + +void tearDown(void) { +} + +static bool array_contains(const unsigned int *arr, unsigned int len, unsigned int value) +{ + for (unsigned int i = 0; i < len; i++) + if (arr[i] == value) + return true; + return false; +} + +// Test: createNumbers gibt gültigen Pointer zurück +bool test_createNumbers_not_null(void) +{ + unsigned int len = 10; + unsigned int *nums = createNumbers(len); + + ASSERT_TRUE(nums != NULL); + + free(nums); + return true; +} + +// Test: alle Zahlen liegen im erlaubten Bereich +bool test_createNumbers_range(void) +{ + unsigned int len = 20; + unsigned int *nums = createNumbers(len); + + ASSERT_TRUE(nums != NULL); + + for (unsigned int i = 0; i < len; i++) { + ASSERT_TRUE(nums[i] >= 1); + ASSERT_TRUE(nums[i] <= 2 * len); + } + + free(nums); + return true; +} + +// Test: es existiert genau ein Duplikat +bool test_getDuplicate_finds_duplicate(void) +{ + unsigned int len = 15; + unsigned int *nums = createNumbers(len); + + ASSERT_TRUE(nums != NULL); + + unsigned int dup = getDuplicate(nums, len); + + ASSERT_TRUE(dup != 0); + ASSERT_TRUE(array_contains(nums, len, dup)); + + free(nums); + return true; +} + +// Test: getDuplicate gibt 0 bei Fehlerfall zurück +bool test_getDuplicate_no_duplicate(void) +{ + unsigned int nums[] = {1, 2, 3, 4, 5}; + unsigned int len = 5; + + unsigned int dup = getDuplicate(nums, len); + + ASSERT_TRUE(dup == 0); + return true; +} + +int main(void) +{ + RUNTEST(test_createNumbers_not_null); + RUNTEST(test_createNumbers_range); + RUNTEST(test_getDuplicate_finds_duplicate); + RUNTEST(test_getDuplicate_no_duplicate); + + printf("\nTests run: %d\n", tests_run); + printf("Tests failed: %d\n", tests_failed); + + return tests_failed ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/test_stack.c b/test_stack.c new file mode 100644 index 0000000..5ed8d5f --- /dev/null +++ b/test_stack.c @@ -0,0 +1,168 @@ +#include +#include +#include "unity.h" +#include "stack.h" + +// ------------------------------------------------------------- +// ---------------------- Tests für push ------------------------ +// ------------------------------------------------------------- + +void test_push_single_element(void) { + StackNode *stack = NULL; + + int *value = malloc(sizeof(int)); + *value = 10; + + stack = push(stack, value); + + TEST_ASSERT_NOT_NULL(stack); + TEST_ASSERT_EQUAL_INT(10, *(int*)top(stack)); + + clearStack(stack); + free(value); +} + +void test_push_multiple_elements(void) { + StackNode *stack = NULL; + + int *a = malloc(sizeof(int)); + int *b = malloc(sizeof(int)); + int *c = malloc(sizeof(int)); + + *a = 10; + *b = 20; + *c = 30; + + stack = push(stack, a); // Stack: [10] + stack = push(stack, b); // Stack: [20, 10] + stack = push(stack, c); // Stack: [30, 20, 10] + + TEST_ASSERT_EQUAL_INT(30, *(int*)top(stack)); + + clearStack(stack); + free(a); + free(b); + free(c); +} + +// ------------------------------------------------------------- +// ----------------------- Tests für top ------------------------ +// ------------------------------------------------------------- + +void test_top_on_empty_stack_returns_null(void) { + StackNode *stack = NULL; + + TEST_ASSERT_NULL(top(stack)); +} + +void test_top_after_pushes(void) { + StackNode *stack = NULL; + + int *a = malloc(sizeof(int)); + int *b = malloc(sizeof(int)); + + *a = 42; + *b = 99; + + stack = push(stack, a); // [42] + stack = push(stack, b); // [99, 42] + + TEST_ASSERT_EQUAL_INT(99, *(int*)top(stack)); + + clearStack(stack); + free(a); free(b); +} + +// ------------------------------------------------------------- +// ----------------------- Tests für pop ------------------------ +// ------------------------------------------------------------- + +void test_pop_on_empty_stack_returns_null(void) { + StackNode *stack = NULL; + + stack = pop(stack); + + TEST_ASSERT_NULL(stack); +} + +void test_pop_removes_elements_in_lifo_order(void) { + StackNode *stack = NULL; + + int *a = malloc(sizeof(int)); + int *b = malloc(sizeof(int)); + int *c = malloc(sizeof(int)); + + *a = 10; + *b = 20; + *c = 30; + + stack = push(stack, a); // [10] + stack = push(stack, b); // [20, 10] + stack = push(stack, c); // [30, 20, 10] + + stack = pop(stack); // remove 30 + TEST_ASSERT_EQUAL_INT(20, *(int*)top(stack)); + + stack = pop(stack); // remove 20 + TEST_ASSERT_EQUAL_INT(10, *(int*)top(stack)); + + stack = pop(stack); // remove 10 + TEST_ASSERT_NULL(stack); + + free(a); free(b); free(c); +} + +// ------------------------------------------------------------- +// -------------------- Tests für clearStack -------------------- +// ------------------------------------------------------------- + +void test_clearStack_empties_stack(void) { + StackNode *stack = NULL; + + int *a = malloc(sizeof(int)); + int *b = malloc(sizeof(int)); + + *a = 5; + *b = 15; + + stack = push(stack, a); + stack = push(stack, b); + + clearStack(stack); + + // Danach ist der Stack frei, aber 'stack' zeigt noch auf alten Wert + // Caller muss selbst auf NULL setzen + stack = NULL; + + TEST_ASSERT_NULL(stack); + + free(a); free(b); +} + +void setUp(void) { + // keine Vorbereitung nötig +} + +void tearDown(void) { + // kein Aufräumen notwendig, da jede Testfunktion selbst aufräumt +} +// ------------------------------------------------------------- +// --------------------------- MAIN ----------------------------- +// ------------------------------------------------------------- + +int main(void) { + UNITY_BEGIN(); + + RUN_TEST(test_push_single_element); + RUN_TEST(test_push_multiple_elements); + + RUN_TEST(test_top_on_empty_stack_returns_null); + RUN_TEST(test_top_after_pushes); + + RUN_TEST(test_pop_on_empty_stack_returns_null); + RUN_TEST(test_pop_removes_elements_in_lifo_order); + + RUN_TEST(test_clearStack_empties_stack); + + return UNITY_END(); +}