1 Commits
Author SHA1 Message Date
wiesendsi102436 e4477b08dc set the seed for the RNG just once at the beginning of the program
this improves randomness for tests where createNumbers() is called in rapid succession
2025-11-30 09:11:32 +01:00
6 changed files with 183 additions and 334 deletions
+94 -56
View File
@@ -1,117 +1,155 @@
#include <string.h>
#include <stdlib.h>
#include "stack.h"
#include "bintree.h"
#include <stdlib.h>
static StackNode *iterStack = NULL;
static void pushLeftBranch(StackNode **stack, TreeNode *node);
// 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. */
// Inserts a new node into the BST.
// If isDuplicate == NULL → duplicates are allowed
// If isDuplicate != NULL → duplicates are ignored and *isDuplicate = 1
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize,
CompareFctType compareFct, int *isDuplicate)
// 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)
{
TreeNode *insertedNode;
// create a new node if the current node is NULL
if (root == NULL)
{
TreeNode *newNode = calloc(1, sizeof(TreeNode));
if (!newNode)
return NULL;
newNode->data = malloc(dataSize);
if (!newNode->data)
// it's important to zero the pointers for adjacent nodes
insertedNode = calloc(1, sizeof(TreeNode));
if (!insertedNode)
{
free(newNode);
return NULL;
}
memcpy(newNode->data, data, dataSize);
insertedNode->data = malloc(dataSize);
if (!insertedNode->data)
{
return NULL;
}
memcpy(insertedNode->data, data, dataSize);
// reset isDuplicate if it exists
if (isDuplicate)
{
*isDuplicate = 0;
return newNode;
}
return insertedNode;
}
int cmp = compareFct(data, root->data);
if (cmp < 0 || (cmp == 0 && isDuplicate == NULL))
// TODO: what is the correct data type here?
int cmpRes = (*compareFct)(data, root->data);
// insert into the left branch
if (cmpRes < 0 || (cmpRes == 0 && isDuplicate == NULL))
{
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
}
else if (cmp > 0)
// insert into the right branch
else if (cmpRes > 0)
{
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
}
// the data is equal to the current node
else
{
if (isDuplicate)
*isDuplicate = 1;
// the data already exists in the tree and duplicates are ignored (isDuplicate* not NULL)
*isDuplicate = 1;
}
return root;
}
static void pushLeftBranch(StackNode **stack, TreeNode *node)
// push all left descendants from @param node
static void pushLeftDesc(StackNode **stackPtr, TreeNode *node)
{
while (node)
if (!stackPtr || !node)
{
*stack = push(*stack, node);
node = node->left;
return;
}
TreeNode *curNode = node;
while (curNode->left)
{
*stackPtr = push(*stackPtr, curNode->left);
if (!*stackPtr)
{
return;
}
curNode = curNode->left;
}
}
// If root != NULL → reset iterator and start from new tree.
// If root == NULL → continue iterating.
// 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)
{
// Start new iteration
if (root != NULL)
{
// reset old iterator state
clearStack(iterStack);
iterStack = NULL;
// this creates a static variable that maintains an internal state
static StackNode *stack;
// push root and all left children
pushLeftBranch(&iterStack, root);
// create a new stack
if (root)
{
// clear possibly existing stacks
clearStack(stack);
// init a new stack
stack = push(NULL, root);
// init failed
if (!stack)
{
return NULL;
}
pushLeftDesc(&stack, root);
// return the first val
return nextTreeData(NULL);
}
// No active iterator
if (iterStack == NULL)
// neither stack nor root exist
if (!stack)
{
return NULL;
}
// Get next node
TreeNode *node = (TreeNode *)top(iterStack);
iterStack = pop(iterStack);
// get next val with stack
TreeNode *res = top(stack);
stack = pop(stack);
if (res->right)
{
stack = push(stack, res->right);
pushLeftDesc(&stack, res->right);
}
// push right subtree and its left descendants
if (node->right)
pushLeftBranch(&iterStack, node->right);
return node->data;
return res->data;
}
// Frees all nodes and also resets iterator.
// Releases all memory resources (including data copies).
void clearTree(TreeNode *root)
{
// this check is crucial for recursion
if (!root)
{
// nothing to clear
return;
}
// release the resources of child nodes first
clearTree(root->left);
clearTree(root->right);
// free the data (it's just a copy created in addToTree())
free(root->data);
free(root);
// If we clear the tree, iterator must not point into freed memory.
clearStack(iterStack);
iterStack = NULL;
}
// Returns the number of entries in the tree given by root.
unsigned int treeSize(const TreeNode *root)
{
// there are no nodes
if (!root)
{
return 0;
}
return 1 + treeSize(root->left) + treeSize(root->right);
}
}
-2
View File
@@ -1,6 +1,5 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "numbers.h"
#include "timer.h"
#include "highscore.h"
@@ -87,7 +86,6 @@ int main(int argc, char *argv[])
saveHighscores(highscorePath);
clearHighscores();
free(numbers);
exitCode = EXIT_SUCCESS;
}
+65 -62
View File
@@ -5,79 +5,82 @@
#include "numbers.h"
#include "bintree.h"
//Speicher für Array erstellen, zufällige Zahlen von 1-2xlen erzeugen, mittels Binärbaum checken, ob Zahlen einzigartig sind
//Eine Zahl duplizieren, an zufälliger Stelle einfügen und die Zahl an der Stelle ans Ende schieben
const int compare (const void *a, const void *b);
static int compareInt(const void *ptr1, const void *ptr2);
// 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.
/*
the implemented tree can't efficiently check if it contains a specific number, but we don't actually need that anyways
create numbers just counts and checks if the just inserted number sets the isDuplicate pointer
*/
// srand should have been called before this function
unsigned int *createNumbers(unsigned int len)
{
unsigned int *numbers = malloc (sizeof(unsigned int) * len);
unsigned int upperLimit = len * 2;
unsigned int *randomNumbers = malloc(len * sizeof(int));
// including upper limit
int upperLimit = len * 2;
int numberCnt = 0;
int isDuplicate = 0;
TreeNode *binTree = NULL;
for (unsigned int i = 0; i < len; i++) {
do
TreeNode *root = NULL;
// we only need len-1 numbers because 1 will be duplicated
while (numberCnt < len - 1)
{
// numbers up to and including upperLimit without 0
int randNum = rand() % upperLimit + 1;
// reset isDuplicate
isDuplicate = 0;
// don't forget to set the root here
root = addToTree(root, &randNum, sizeof(randNum), (CompareFctType)compareInt, &isDuplicate);
if (isDuplicate)
{
isDuplicate = 0;
numbers[i] = rand () % upperLimit + 1;
binTree = addToTree(binTree, &numbers[i], sizeof(unsigned int), compare, &isDuplicate);
} while (isDuplicate);
}
unsigned int duplicate = numbers[rand () % len];
int indexDuplicate;
do {
indexDuplicate = rand() % len;
} while (numbers[indexDuplicate] == duplicate);
if (numbers[len-1] != duplicate) {
numbers[len-1] = numbers[indexDuplicate];
// number already exists
continue;
}
randomNumbers[numberCnt++] = randNum;
}
numbers[indexDuplicate] = duplicate;
clearTree(binTree);
return numbers;
// select which number to duplicate
int dupNum = randomNumbers[rand() % numberCnt];
// ...and where to insert
int dupNumIdx = rand() % len;
// move the number currently at the dupNumIdx to the end
// and insert the dupNum at the index
// this also works if the last idx was selected for dupNum
randomNumbers[len - 1] = randomNumbers[dupNumIdx];
randomNumbers[dupNumIdx] = dupNum;
// clean up memory
clearTree(root);
return randomNumbers;
}
//Vergleichsfunktion von qsort
const int compare (const void *a, const void *b) {
const unsigned int *x = a;
const unsigned int *y = b;
if (*x < *y) {
return -1;
}
else if (*x > *y) {
return 1;
}
else {
return 0;
}
}
//Sortiert Zahlen mit qsort, vergleicht dann benachbarte Elemente und gibt bei Erfolg die doppelte Zahl zurück
// 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)
{
if (len < 2) {
return 0;
}
unsigned int *copy = malloc (sizeof(unsigned int) * len);
memcpy (copy, numbers, sizeof(unsigned int) * len);
qsort(copy, len, sizeof(unsigned int), compare);
for (int i = 0; i < len-1; i++) {
if (copy[i] == copy [i+1]) {
unsigned int result = copy [i];
free (copy);
return result;
qsort((void *)numbers, len, sizeof(int), compareInt); // sort the array
for (int i = 0; i < len - 1; i++)
{
if (numbers[i] == numbers[i + 1])
return numbers[i];
}
return 0; // zero on errors
}
free (copy);
return 0;
static int compareInt(const void *ptr1, const void *ptr2)
{
int num1 = *(int *)ptr1;
int num2 = *(int *)ptr2;
return num1 - num2;
}
+14 -120
View File
@@ -1,145 +1,39 @@
#include "unity.h"
#include "bintree.h"
#include <string.h>
#include <stdio.h>
static int compareInt(const void *a, const void *b)
{
int x = *(const int *)a;
int y = *(const int *)b;
return (x > y) - (x < y);
}
#include "string.h"
void setUp(void)
{
// set stuff up here
}
void tearDown(void)
{
// set stuff up here
}
/* ============================================================
TEST 1 — Strings einfügen + korrekte Reihenfolge prüfen
============================================================ */
void test_insert_and_retrieve_strings(void)
// this adds some strings and checks if they are returned in the right order
void test_insert_and_retrieve(void)
{
char *data1 = "a_this";
char *data2 = "b_is";
char *data3 = "c_testdata";
TreeNode *root = addToTree(NULL, data1, strlen(data1) + 1, (CompareFctType)strcmp, NULL);
addToTree(root, data2, strlen(data2) + 1, (CompareFctType)strcmp, NULL);
addToTree(root, data3, strlen(data3) + 1, (CompareFctType)strcmp, NULL);
TreeNode *root = addToTree(NULL, data1, strlen(data1) + 1, (CompareFctType)&strcmp, NULL);
addToTree(root, data2, strlen(data2) + 1, (CompareFctType)&strcmp, NULL);
addToTree(root, data3, strlen(data3) + 1, (CompareFctType)&strcmp, NULL);
TEST_ASSERT_EQUAL_STRING(data1, nextTreeData(root));
TEST_ASSERT_EQUAL_STRING(data2, nextTreeData(NULL));
TEST_ASSERT_EQUAL_STRING(data3, nextTreeData(NULL));
TEST_ASSERT_EQUAL_PTR(NULL, nextTreeData(NULL)); // Ende
TEST_ASSERT_EQUAL_STRING(data1, (char *)nextTreeData(root));
TEST_ASSERT_EQUAL_STRING(data2, (char *)nextTreeData(NULL));
TEST_ASSERT_EQUAL_STRING(data3, (char *)nextTreeData(NULL));
clearTree(root);
}
/* ============================================================
TEST 2 — Integer einfügen + Traversierung
============================================================ */
void test_insert_and_retrieve_ints(void)
{
int a = 2, b = 1, c = 3;
TreeNode *root = NULL;
root = addToTree(root, &a, sizeof(int), compareInt, NULL);
addToTree(root, &b, sizeof(int), compareInt, NULL);
addToTree(root, &c, sizeof(int), compareInt, NULL);
int *v1 = nextTreeData(root);
int *v2 = nextTreeData(NULL);
int *v3 = nextTreeData(NULL);
int *v4 = nextTreeData(NULL);
TEST_ASSERT_EQUAL_INT(1, *v1);
TEST_ASSERT_EQUAL_INT(2, *v2);
TEST_ASSERT_EQUAL_INT(3, *v3);
TEST_ASSERT_NULL(v4);
clearTree(root);
}
/* ============================================================
TEST 3 — treeSize korrekt?
============================================================ */
void test_tree_size(void)
{
TreeNode *root = NULL;
TEST_ASSERT_EQUAL_UINT(0, treeSize(root));
int x1 = 10, x2 = 5, x3 = 15;
root = addToTree(root, &x1, sizeof(int), compareInt, NULL);
addToTree(root, &x2, sizeof(int), compareInt, NULL);
addToTree(root, &x3, sizeof(int), compareInt, NULL);
TEST_ASSERT_EQUAL_UINT(3, treeSize(root));
clearTree(root);
}
/* ============================================================
TEST 4 — Duplikaterkennung
============================================================ */
void test_duplicate_detection(void)
{
int x = 42;
int dupFlag = -1;
TreeNode *root = addToTree(NULL, &x, sizeof(int), compareInt, &dupFlag);
TEST_ASSERT_EQUAL_INT(0, dupFlag);
addToTree(root, &x, sizeof(int), compareInt, &dupFlag);
TEST_ASSERT_EQUAL_INT(1, dupFlag);
TEST_ASSERT_EQUAL_UINT(1, treeSize(root));
clearTree(root);
}
/* ============================================================
TEST 5 — Iterator nach clearTree → sollte NULL liefern
============================================================ */
void test_iterator_after_cleartree(void)
{
int a = 5, b = 1, c = 9;
TreeNode *root = NULL;
root = addToTree(root, &a, sizeof(int), compareInt, NULL);
addToTree(root, &b, sizeof(int), compareInt, NULL);
addToTree(root, &c, sizeof(int), compareInt, NULL);
nextTreeData(root);
clearTree(root);
TEST_ASSERT_NULL(nextTreeData(NULL));
TEST_ASSERT_NULL(nextTreeData(NULL));
}
int main(void)
{
printf("============================\n");
printf("Bintree tests\n");
printf("============================\n");
printf("============================\nBintree tests\n============================\n");
UNITY_BEGIN();
RUN_TEST(test_insert_and_retrieve_strings);
RUN_TEST(test_insert_and_retrieve_ints);
RUN_TEST(test_tree_size);
RUN_TEST(test_duplicate_detection);
RUN_TEST(test_iterator_after_cleartree);
RUN_TEST(test_insert_and_retrieve);
return UNITY_END();
}
}
+6 -83
View File
@@ -1,9 +1,8 @@
#include "unity.h"
// #include "bintree.h"
// #include "string.h"
#include "numbers.h"
#include "stdlib.h"
#include "string.h"
static int compareInt(const void *ptr1, const void *ptr2);
void setUp(void)
{
@@ -17,7 +16,7 @@ void tearDown(void)
// getDuplicate on array without duplicats
// expects 0/error
void test_get_duplicate_without_duplicates(void)
void test_get_duplicate_error(void)
{
unsigned int input[] = {1, 5, 9, 2, 4};
unsigned int len = sizeof(input) / sizeof(input[0]);
@@ -25,21 +24,6 @@ void test_get_duplicate_without_duplicates(void)
TEST_ASSERT_EQUAL_UINT(0, getDuplicate(input, len));
}
// getDuplicate() on some arrays with 1 duplicate
void test_get_duplicate(void)
{
unsigned int arr1[] = {4, 15, 32, 5, 3, 8, 8};
unsigned int len1 = sizeof(arr1) / sizeof(arr1[0]);
unsigned int arr2[] = {1, 3, 3, 7};
unsigned int len2 = sizeof(arr2) / sizeof(arr2[0]);
unsigned int arr3[] = {7, 7, 8, 4, 9, 1};
unsigned int len3 = sizeof(arr3) / sizeof(arr3[0]);
TEST_ASSERT_EQUAL_UINT(8, getDuplicate(arr1, len1));
TEST_ASSERT_EQUAL_UINT(3, getDuplicate(arr2, len2));
TEST_ASSERT_EQUAL_UINT(7, getDuplicate(arr3, len3));
}
// this tries to brute force a triple
void test_for_triple(void)
{
@@ -49,79 +33,18 @@ void test_for_triple(void)
unsigned int *numbers = createNumbers(3);
if (numbers[0] == numbers[1] && numbers[1] == numbers[2])
{
TEST_FAIL_MESSAGE("triple generated");
// fail the test
TEST_ASSERT(0);
}
free(numbers);
}
}
// check if getDuplicate() modifies the original array (it should not)
void test_get_duplicate_does_modify()
{
unsigned int arr1[] = {1, 2, 3, 4, 5, 4, 3, 2, 1}; // sorting would change this
size_t len1 = sizeof(arr1) / sizeof(arr1[0]);
unsigned int arr1Copy[9];
memcpy(arr1Copy, arr1, len1 * sizeof(unsigned int));
getDuplicate(arr1, len1); // return value does not matter
// check if the arrays are still the same
if (memcmp(arr1, arr1Copy, len1 * sizeof(unsigned int)))
{
TEST_FAIL_MESSAGE("Arrays have diverged");
}
}
// checks if there is exactly 1 duplicate number at varying array sizes
void test_exactly_one_duplicate()
{
const size_t MAX_LIST_SIZE = 20; // max tested array len
const size_t ITERATIONS_PER_LEN = 20; // number of iterations for each tested array len
for (size_t len = 2; len < MAX_LIST_SIZE; len++) // start with smallest sensible size 2
{
for (size_t i = 0; i < ITERATIONS_PER_LEN; i++)
{
unsigned int *randTestList = createNumbers((unsigned int)len);
qsort(randTestList, len, sizeof(unsigned int), compareInt);
int cntDuplicate = 0;
for (size_t j = 0; j < len - 1; j++)
{
if (randTestList[j] == randTestList[j + 1])
{
cntDuplicate++;
}
}
// there should be exactly 1 duplicate
TEST_ASSERT_EQUAL_INT(1, cntDuplicate);
free(randTestList);
}
}
}
static int compareInt(const void *ptr1, const void *ptr2)
{
unsigned int num1 = *(int *)ptr1;
unsigned int num2 = *(int *)ptr2;
if (num1 < num2)
return -1;
if (num1 > num2)
return 1;
return 0;
}
int main(void)
{
printf("============================\nNumbers tests\n============================\n");
UNITY_BEGIN();
RUN_TEST(test_get_duplicate_without_duplicates);
RUN_TEST(test_get_duplicate_error);
RUN_TEST(test_for_triple);
RUN_TEST(test_exactly_one_duplicate);
RUN_TEST(test_get_duplicate);
RUN_TEST(test_get_duplicate_does_modify);
return UNITY_END();
}
+4 -11
View File
@@ -1,12 +1,6 @@
#include "timer.h"
#ifdef __linux__
// Defines strict posix compliance for CLOCK_MONOTONIC
#define _POSIX_C_SOURCE 199309L
#include <time.h>
#endif
#if __APPLE__ || __linux__
#if __APPLE__
#include <sys/time.h>
static struct timespec start = {0, 0};
@@ -20,15 +14,14 @@ void startTimer()
double stopTimer()
{
struct timespec end;
clock_gettime(CLOCK_MONOTONIC, &end);
unsigned long long delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
double measuredSeconds = (double)delta_us / 1000000.;
if (start.tv_nsec > 0)
{
if(start.tv_nsec > 0) {
start.tv_nsec = 0;
start.tv_sec = 0;
}
@@ -52,7 +45,7 @@ double stopTimer()
{
double measuredSeconds = (clock() - (double)startClocks) / CLOCKS_PER_SEC;
if (startClocks > 0)
if(startClocks > 0)
startClocks = 0;
else
measuredSeconds = -1;