Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

11 changed files with 43 additions and 556 deletions

5
.gitignore vendored
View File

@ -1,5 +0,0 @@
*doble*
*.o
*.exe
.vscode
run*Tests

109
bintree.c
View File

@ -1,117 +1,36 @@
#include <string.h>
#include <stdlib.h>
#include "stack.h"
#include "bintree.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)
{
if (root == NULL)
{
TreeNode *newNode = calloc(1, sizeof(TreeNode));
if (!newNode)
return NULL;
newNode->data = malloc(dataSize);
if (!newNode->data)
{
free(newNode);
return NULL;
}
memcpy(newNode->data, data, dataSize);
if (isDuplicate)
*isDuplicate = 0;
return newNode;
}
int cmp = compareFct(data, root->data);
if (cmp < 0 || (cmp == 0 && isDuplicate == NULL))
{
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
}
else if (cmp > 0)
{
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
}
else
{
if (isDuplicate)
*isDuplicate = 1;
}
return root;
}
static void pushLeftBranch(StackNode **stack, TreeNode *node)
{
while (node)
{
*stack = push(*stack, node);
node = node->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;
// push root and all left children
pushLeftBranch(&iterStack, root);
}
// No active iterator
if (iterStack == NULL)
return NULL;
// Get next node
TreeNode *node = (TreeNode *)top(iterStack);
iterStack = pop(iterStack);
// push right subtree and its left descendants
if (node->right)
pushLeftBranch(&iterStack, node->right);
return node->data;
}
// Frees all nodes and also resets iterator.
// 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);
// 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)
{
if (!root)
return 0;
return 1 + treeSize(root->left) + treeSize(root->right);
}
}

5
main.c
View File

@ -1,6 +1,5 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "numbers.h"
#include "timer.h"
#include "highscore.h"
@ -40,9 +39,6 @@ int main(int argc, char *argv[])
{
int exitCode = EXIT_FAILURE;
// set seed
srand(time(NULL));
if(argc != 2)
{
fprintf(stderr, "Usage: %s <player name>\n", argv[0]);
@ -87,7 +83,6 @@ int main(int argc, char *argv[])
saveHighscores(highscorePath);
clearHighscores();
free(numbers);
exitCode = EXIT_SUCCESS;
}

View File

@ -1,5 +1,5 @@
CC = gcc
CFLAGS = -g -Wall -lm
FLAGS = -g -Wall -lm
ifeq ($(OS),Windows_NT)
include makefile_windows.variables
@ -27,29 +27,16 @@ doble_initial:
program_obj_files = stack.o bintree.o numbers.o timer.o highscore.o
doble : main.o $(program_obj_files)
$(CC) $(CFLAGS) $^ -o doble
$(CC) $(FLAGS) $^ -o doble
$(program_obj_files): %.o: %.c
$(CC) -c $(CFLAGS) $^ -o $@
$(program_obj_filesobj_files): %.o: %.c
$(CC) -c $(FLAGS) $^ -o $@
# --------------------------
# Unit Tests
# --------------------------
TEST_STACK_SOURCES = stack.c test_stack.c $(unityfolder)/unity.c
TEST_BINTREE_SOURCES = bintree.c test_bintree.c stack.c $(unityfolder)/unity.c
TEST_NUMBERS_SOURCES = stack.c numbers.c bintree.c $(unityfolder)/unity.c test_numbers.c
stackTests: $(TEST_STACK_SOURCES) stack.h
$(CC) $(CFLAGS) -I$(unityfolder) $(TEST_STACK_SOURCES) -o runStackTests
./runStackTests
bintreeTests: $(TEST_BINTREE_SOURCES) stack.h bintree.h
$(CC) $(CFLAGS) -I$(unityfolder) $(TEST_BINTREE_SOURCES) -o runBintreeTests
./runBintreeTests
numbersTests: $(TEST_NUMBERS_SOURCES) stack.h bintree.h numbers.h
$(CC) $(CFLAGS) -I$(unityfolder) $(TEST_NUMBERS_SOURCES) -o runNumbersTests
./runNumbersTests
unitTests:
echo "needs to be implemented"
# --------------------------
# Clean

View File

@ -5,79 +5,22 @@
#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);
//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)
{
unsigned int *numbers = malloc (sizeof(unsigned int) * len);
unsigned int upperLimit = len * 2;
int isDuplicate = 0;
TreeNode *binTree = NULL;
for (unsigned int i = 0; i < len; i++) {
do
{
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];
}
numbers[indexDuplicate] = duplicate;
clearTree(binTree);
return numbers;
}
//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;
}
}
free (copy);
return 0;
}

36
stack.c
View File

@ -1,55 +1,33 @@
#include <stdlib.h>
#include "stack.h"
// TODO: grundlegende Stackfunktionen implementieren:
//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. */
* `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.
StackNode *push(StackNode *stack, void *data)
{
// this is the new top node
StackNode *newTopNode = malloc(sizeof(StackNode));
if (newTopNode == NULL)
{
return NULL;
}
newTopNode->data = data;
newTopNode->next = stack;
return newTopNode;
}
// 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 *nextNode = stack->next;
free(stack);
return nextNode;
}
// 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 (pop(stack))
;
}

10
stack.h
View File

@ -1,17 +1,13 @@
#ifndef STACK_H
#define STACK_H
/* A stack is a special type of queue which uses the LIFO (last in, first out) principle.
This means that with each new element all other elements are pushed deeper into the stack.
/* A stack is a special type of queue which uses the LIFO (last in, first out) principle.
This means that with each new element all other elements are pushed deeper into the stack.
The latest element is taken from the stack. */
#include <stdlib.h>
typedef struct StackNode
{
struct StackNode *next;
void *data;
} StackNode;
//TODO: passenden Datentyp als struct anlegen
// Pushes data as pointer onto the stack.
StackNode *push(StackNode *stack, void *data);

View File

@ -1,145 +0,0 @@
#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);
}
void setUp(void)
{
}
void tearDown(void)
{
}
/* ============================================================
TEST 1 Strings einfügen + korrekte Reihenfolge prüfen
============================================================ */
void test_insert_and_retrieve_strings(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);
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
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");
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);
return UNITY_END();
}

View File

@ -1,127 +0,0 @@
#include "unity.h"
#include "numbers.h"
#include "stdlib.h"
#include "string.h"
static int compareInt(const void *ptr1, const void *ptr2);
void setUp(void)
{
// set stuff up here
}
void tearDown(void)
{
// set stuff up here
}
// getDuplicate on array without duplicats
// expects 0/error
void test_get_duplicate_without_duplicates(void)
{
unsigned int input[] = {1, 5, 9, 2, 4};
unsigned int len = sizeof(input) / sizeof(input[0]);
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)
{
// this test is less effective if srand is called inside createNumbers()
for (int i = 0; i < 100000; i++)
{
unsigned int *numbers = createNumbers(3);
if (numbers[0] == numbers[1] && numbers[1] == numbers[2])
{
TEST_FAIL_MESSAGE("triple generated");
}
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_for_triple);
RUN_TEST(test_exactly_one_duplicate);
RUN_TEST(test_get_duplicate);
RUN_TEST(test_get_duplicate_does_modify);
return UNITY_END();
}

View File

@ -1,47 +0,0 @@
#include "unity.h"
#include "stack.h"
int data1 = 10;
int data2 = 20;
int data3 = 30;
StackNode *stack = NULL;
void setUp(void)
{
// set stuff up here
}
void tearDown(void)
{
clearStack(stack);
}
void test_push_and_pop(void)
{
stack = push(stack, &data1);
stack = push(stack, &data2);
stack = push(stack, &data3);
TEST_ASSERT_EQUAL_PTR(top(stack), &data3);
stack = pop(stack);
TEST_ASSERT_EQUAL_PTR(top(stack), &data2);
stack = pop(stack);
TEST_ASSERT_EQUAL_PTR(top(stack), &data1);
stack = pop(stack);
}
void test_handle_NULL(void)
{
TEST_ASSERT_NULL(pop(stack));
TEST_ASSERT_NULL(top(stack));
}
int main(void)
{
printf("============================\nStack tests\n============================\n");
UNITY_BEGIN();
RUN_TEST(test_push_and_pop);
RUN_TEST(test_handle_NULL);
return UNITY_END();
}

15
timer.c
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;