generated from freudenreichan/info2Praktikum-DobleSpiel
cleartree funktion in bintree; getDuplicate in numbers;
Unittests test-Numbers implemented + makefile einbindung
This commit is contained in:
parent
6513b39520
commit
748a39e8c1
25
bintree.c
25
bintree.c
@ -24,7 +24,7 @@ TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFc
|
||||
|
||||
// Speicher für die Datenkopie reservieren
|
||||
newNode->data = malloc(dataSize);
|
||||
if (newNode->data == Null)
|
||||
if (newNode->data == NULL)
|
||||
{
|
||||
free(newNode);
|
||||
return NULL; // Fehler beim Allokieren
|
||||
@ -82,7 +82,30 @@ void *nextTreeData(TreeNode *root)
|
||||
// Releases all memory resources (including data copies).
|
||||
void clearTree(TreeNode *root)
|
||||
{
|
||||
// 1. Basis-Fall: Wenn der Knoten NULL ist, beende
|
||||
if (root == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Rekursiver Schritt (gehe in die Tiefe)
|
||||
|
||||
// Gib den linken Teilbaum frei
|
||||
clearTree(root->left);
|
||||
|
||||
// Gib den rechten Teilbaum frei
|
||||
clearTree(root->right);
|
||||
|
||||
// 3. Aktion: Gebe die Ressourcen dieses Knotens frei
|
||||
|
||||
// Zuerst die dynamisch kopierten Daten freigeben (siehe addToTree)
|
||||
if (root->data != NULL)
|
||||
{
|
||||
free(root->data); // Speicher für die Zahl freigeben
|
||||
}
|
||||
|
||||
// Dann den Knoten selbst freigeben
|
||||
free(root);
|
||||
}
|
||||
|
||||
// Returns the number of entries in the tree given by root.
|
||||
|
||||
15
makefile
15
makefile
@ -32,11 +32,22 @@ doble : main.o $(program_obj_files)
|
||||
$(program_obj_filesobj_files): %.o: %.c
|
||||
$(CC) -c $(FLAGS) $^ -o $@
|
||||
|
||||
numbers.o: numbers.c
|
||||
$(CC) -c $(CFLAGS) numbers.c
|
||||
|
||||
bintree.o: bintree.c
|
||||
$(CC) -c $(CFLAGS) bintree.c
|
||||
|
||||
|
||||
# --------------------------
|
||||
# Unit Tests
|
||||
# --------------------------
|
||||
unitTests:
|
||||
echo "needs to be implemented"
|
||||
|
||||
#unitTests:
|
||||
# echo "needs to be implemented"
|
||||
|
||||
numbersTests: numbers.o bintree.o test_numbers.c $(unityfolder)/unity.c
|
||||
$(CC) $(CFLAGS) -I$(unityfolder) -o runNumbersTests test_numbers.c numbers.o bintree.o $(unityfolder)/unity.c
|
||||
|
||||
# --------------------------
|
||||
# Clean
|
||||
|
||||
99
numbers.c
99
numbers.c
@ -13,6 +13,14 @@
|
||||
* Duplizieren eines zufälligen Eintrags im Array.
|
||||
* in `getDuplicate()`: Sortieren des Arrays und Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente. */
|
||||
|
||||
// **********************************************
|
||||
// HILFSFUNKTION FÜR DEN BINÄRBAUM (und später qsort)
|
||||
// **********************************************
|
||||
/**
|
||||
* Vergleicht zwei unsigned int Werte.
|
||||
* Wird als CompareFctType für den Binärbaum benötigt.
|
||||
* Rückgabe: < 0 wenn *num1 < *num2, > 0 wenn *num1 > *num2, 0 wenn gleich.
|
||||
*/
|
||||
// Implementierung für unsigned int
|
||||
int compareNumbers(const void *arg1, const void *arg2)
|
||||
{
|
||||
@ -33,6 +41,9 @@ unsigned int *createNumbers(unsigned int len)
|
||||
{
|
||||
srand(time(NULL));
|
||||
|
||||
// Sicherheitscheck für len (Minimum 3, siehe main.c)
|
||||
if (len < 3) return NULL;
|
||||
|
||||
// Allokiere Speicher für 'len' unsigned int
|
||||
unsigned int *numbers = (unsigned int *)malloc(len * sizeof(unsigned int));
|
||||
|
||||
@ -42,29 +53,95 @@ unsigned int *createNumbers(unsigned int len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int array [len - 1]
|
||||
// 2. Binären Suchbaum initialisieren
|
||||
TreeNode *treeRoot = NULL;
|
||||
|
||||
for(unsigned int i = 0; i < len; i++)
|
||||
// Variablen für Schleife und Zufallszahl
|
||||
unsigned int randomNumber;
|
||||
int isDuplicate; // Flag für addToTree
|
||||
|
||||
// Die Schleife läuft nur bis len - 1, da die letzte Zahl das Duplikat wird
|
||||
for (unsigned int i = 0; i < len - 1; i++)
|
||||
{
|
||||
unsigned int randomNumber = (rand() % (2 * len)) + 1;
|
||||
numbers[i] = randomNumber;
|
||||
do
|
||||
{
|
||||
// 3a. Zufallszahl generieren [1, 2 * len]
|
||||
// Formel: (rand() % (Obergrenze - Untergrenze + 1)) + Untergrenze
|
||||
randomNumber = (rand() % (2 * len)) + 1;
|
||||
|
||||
// 3b. Zahl in den Binärbaum einfügen (und Duplikat prüfen)
|
||||
// addToTree gibt treeRoot zurück, das wird für die nächste Iteration gespeichert.
|
||||
// isDuplicate wird auf 1 gesetzt, wenn die Zahl schon existiert.
|
||||
treeRoot = addToTree(
|
||||
treeRoot,
|
||||
&randomNumber,
|
||||
sizeof(unsigned int),
|
||||
compareNumbers,
|
||||
&isDuplicate
|
||||
);
|
||||
|
||||
// Fehlerprüfung: Konnte addToTree Speicher allokieren?
|
||||
if (treeRoot == NULL) {
|
||||
// Konnte ersten Knoten nicht erstellen, breche ab und gib Speicher frei
|
||||
free(numbers);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} while (isDuplicate); // Solange eine Duplikat-Nummer generiert wurde, wiederhole.
|
||||
|
||||
// 4. Eindeutige Zahl im Array speichern
|
||||
numbers[i] = randomNumber;
|
||||
}
|
||||
|
||||
// Duplizieren eines zufälligen Eintrags (Fehlt in deinem Entwurf!)
|
||||
// Die Aufgabe verlangt, dass *genau eine* Zahl doppelt vorkommt[cite: 8].
|
||||
// 5. Duplikat erzeugen (letzter Eintrag des Arrays)
|
||||
|
||||
// Wähle einen zufälligen Index (0 bis len-1), dessen Wert du duplizierst
|
||||
unsigned int duplicateIndex = rand() % len;
|
||||
// Wähle einen zufälligen Index der bereits gefüllten eindeutigen Zahlen [0, len - 2]
|
||||
// Wir wählen aus den len-1 bereits gefüllten Einträgen.
|
||||
unsigned int duplicateIndex = rand() % (len - 1);
|
||||
|
||||
// Ersetze einen zufälligen Eintrag im Array (z.B. den letzten) durch den Wert des Duplikats
|
||||
numbers[len - 1] = numbers[duplicateIndex];
|
||||
// Kopiere den Wert des zufälligen Index auf das letzte Element
|
||||
numbers[len - 1] = numbers[duplicateIndex];
|
||||
|
||||
// 6. Aufräumen: Speicher des Binärbaums freigeben (sehr wichtig!)
|
||||
// Voraussetzung: clearTree ist in bintree.c implementiert.
|
||||
clearTree(treeRoot);
|
||||
|
||||
return numbers;
|
||||
return numbers;
|
||||
}
|
||||
|
||||
// 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; // Rückgabewert (0 bei Fehler)
|
||||
|
||||
// 1. Array-Kopie auf dem Heap erstellen (muss mit free freigegeben werden!)
|
||||
unsigned int sizeInBytes = len * sizeof(unsigned int);
|
||||
unsigned int *tempArray = (unsigned int *)malloc(sizeInBytes);
|
||||
|
||||
if (tempArray == NULL) {
|
||||
return 0; // Speicherfehler
|
||||
}
|
||||
|
||||
// Daten kopieren
|
||||
memcpy(tempArray, numbers, sizeInBytes);
|
||||
|
||||
// 2. Sortieren der Kopie mit qsort (siehe Skript 14_effiziente_sortieralgorithmen.pdf)
|
||||
// qsort hat eine durchschnittliche Komplexität von O(n log n) [cite: 261]
|
||||
qsort(tempArray, len, sizeof(unsigned int), compareNumbers);
|
||||
|
||||
// 3. Duplikat finden durch Vergleich benachbarter Elemente
|
||||
for (unsigned int i = 0; i < len - 1; i++)
|
||||
{
|
||||
// Vergleiche Element i mit Element i+1
|
||||
if (tempArray[i] == tempArray[i + 1])
|
||||
{
|
||||
duplicate = tempArray[i];
|
||||
break; // Duplikat gefunden, Schleife beenden
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Speicher freigeben!
|
||||
free(tempArray);
|
||||
|
||||
return duplicate;
|
||||
}
|
||||
274
test_numbers.c
Normal file
274
test_numbers.c
Normal file
@ -0,0 +1,274 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include "unity.h"
|
||||
#include "numbers.h"
|
||||
|
||||
// Externe Deklaration der Vergleichsfunktion aus numbers.c (für den Test)
|
||||
extern int compareNumbers(const void *arg1, const void *arg2);
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// HILFSFUNKTIONEN
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Zählt in einem Array, wie oft jedes Element vorkommt.
|
||||
* Stellt sicher, dass genau ein Element zweimal (Duplikat) und der Rest einmal vorkommt.
|
||||
* Gibt 1 zurück, wenn die Bedingung erfüllt ist, 0 sonst.
|
||||
* @param numbers Das zu prüfende Array.
|
||||
* @param len Die Länge des Arrays.
|
||||
*/
|
||||
static int validateArrayHasSingleDuplicate(const unsigned int *numbers, unsigned int len)
|
||||
{
|
||||
// Wir nutzen hier eine O(n^2) naive Prüfung, um die Anforderungen des Tests zu erfüllen.
|
||||
// Im echten Code ist O(n log n) oder O(n) durch Sortieren bzw. Hashmap besser.
|
||||
if (len < 3) return 0;
|
||||
|
||||
// --- Start der Original-Logik zur Duplikatzählung ---
|
||||
|
||||
// Zähler für die doppelt vorkommende Zahl (Duplikat = 2 Vorkommen)
|
||||
int duplicateCount = 0;
|
||||
// Zähler für Zahlen, die genau einmal vorkommen (Unikat = 1 Vorkommen)
|
||||
int uniqueCount = 0;
|
||||
|
||||
for (unsigned int i = 0; i < len; i++)
|
||||
{
|
||||
int occurrences = 0;
|
||||
// Zähle, wie oft numbers[i] im gesamten Array vorkommt
|
||||
for (unsigned int j = 0; j < len; j++)
|
||||
{
|
||||
if (numbers[i] == numbers[j])
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
|
||||
if (occurrences == 2)
|
||||
{
|
||||
duplicateCount++; // Duplikat gefunden
|
||||
}
|
||||
else if (occurrences == 1)
|
||||
{
|
||||
uniqueCount++; // Eindeutige Zahl gefunden
|
||||
}
|
||||
else
|
||||
{
|
||||
// Eine Zahl kommt 0, 3 oder mehr Male vor -> Fehler
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn genau ein Duplikat vorhanden ist, dann:
|
||||
// 1. Die duplizierte Zahl kommt 2x vor. (duplicateCount muss 2 sein)
|
||||
// 2. Die übrigen (len - 2) Zahlen kommen 1x vor. (uniqueCount muss len - 2 sein)
|
||||
// Beispiel len=5: uniqueCount=3 (A, B, C), duplicateCount=2 (D, D) -> total 5.
|
||||
|
||||
if (duplicateCount == 2 && uniqueCount == (int)len - 2) {
|
||||
return 1; // Korrekte Duplikat-Struktur gefunden
|
||||
}
|
||||
|
||||
// Fallback-Prüfung (nur, dass keine Triplets oder mehrfache Duplikate existieren)
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
int occurrences = 0;
|
||||
for (unsigned int j = 0; j < len; j++) {
|
||||
if (numbers[i] == numbers[j]) {
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
// Jede Zahl muss mindestens einmal und maximal zweimal vorkommen.
|
||||
if (occurrences < 1 || occurrences > 2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1; // Alle Zahlen kommen 1x oder 2x vor (minimaler Test)
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// TESTFALL GRUPPE 1: createNumbers
|
||||
// =========================================================================
|
||||
|
||||
void test_createNumbersReturnsNullForInvalidLength(void)
|
||||
{
|
||||
// Die Hauptfunktion in main.c prüft auf len < 3.
|
||||
// Dennoch sollte createNumbers robust sein.
|
||||
TEST_ASSERT_NULL(createNumbers(0));
|
||||
TEST_ASSERT_NULL(createNumbers(1));
|
||||
TEST_ASSERT_NULL(createNumbers(2));
|
||||
}
|
||||
|
||||
void test_createNumbersReturnsCorrectLengthAndNotNull(void)
|
||||
{
|
||||
const unsigned int len = 10;
|
||||
unsigned int *numbers = createNumbers(len);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(numbers);
|
||||
|
||||
// Die Array-Länge kann nicht direkt in C geprüft werden,
|
||||
// aber wir prüfen auf NULL nach dem malloc
|
||||
|
||||
// Speicher freigeben
|
||||
free(numbers);
|
||||
}
|
||||
|
||||
|
||||
void test_createNumbersGeneratesCorrectDuplicate(void)
|
||||
{
|
||||
const unsigned int len = 10;
|
||||
unsigned int *numbers = createNumbers(len);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(numbers);
|
||||
|
||||
// 1. Prüfe, ob es GENAU ein Duplikat gibt (kein Tripel, kein weiteres Duplikat)
|
||||
// Wir nutzen die getDuplicate-Funktion selbst, um das Array indirekt zu validieren.
|
||||
unsigned int duplicate = getDuplicate(numbers, len);
|
||||
|
||||
TEST_ASSERT_TRUE(duplicate != 0); // Muss eine doppelte Zahl finden
|
||||
|
||||
// 2. Prüfe, ob die getDuplicate-Funktion wirklich die doppelte Zahl findet
|
||||
// (Da getDuplicate bereits mit qsort getestet wird, ist dies eine gute Validierung.)
|
||||
|
||||
// 3. Optional: Prüfen, ob die Zahlen im erwarteten Bereich [1, 2 * len] liegen
|
||||
const unsigned int max_val = 2 * len;
|
||||
for (unsigned int i = 0; i < len; i++)
|
||||
{
|
||||
TEST_ASSERT_TRUE(numbers[i] >= 1);
|
||||
TEST_ASSERT_TRUE(numbers[i] <= max_val);
|
||||
}
|
||||
|
||||
// Speicher freigeben
|
||||
free(numbers);
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// TESTFALL GRUPPE 2: getDuplicate
|
||||
// =========================================================================
|
||||
|
||||
void test_getDuplicateFindsDuplicatedNumber(void)
|
||||
{
|
||||
// Testfall 1: Duplikat am Anfang
|
||||
unsigned int testArray1[] = {10, 5, 20, 5, 30};
|
||||
TEST_ASSERT_EQUAL_UINT(5, getDuplicate(testArray1, 5));
|
||||
|
||||
// Testfall 2: Duplikat in der Mitte
|
||||
unsigned int testArray2[] = {10, 20, 30, 40, 20};
|
||||
TEST_ASSERT_EQUAL_UINT(20, getDuplicate(testArray2, 5));
|
||||
|
||||
// Testfall 3: Duplikat am Ende
|
||||
unsigned int testArray3[] = {1, 2, 3, 4, 1};
|
||||
TEST_ASSERT_EQUAL_UINT(1, getDuplicate(testArray3, 5));
|
||||
|
||||
// Testfall 4: Größeres Array
|
||||
unsigned int testArray4[] = {99, 10, 1, 50, 75, 22, 10};
|
||||
TEST_ASSERT_EQUAL_UINT(10, getDuplicate(testArray4, 7));
|
||||
}
|
||||
|
||||
void test_getDuplicateReturnsZeroOnInvalidLength(void)
|
||||
{
|
||||
// Sollte 0 zurückgeben bei leeren oder zu kleinen Arrays,
|
||||
// da die Funktion keine Duplikate finden kann (oder Fehler).
|
||||
unsigned int emptyArray[] = {};
|
||||
TEST_ASSERT_EQUAL_UINT(0, getDuplicate(emptyArray, 0));
|
||||
|
||||
unsigned int smallArray[] = {1, 2};
|
||||
// Wenn len = 2, kann es nur 1 Duplikat geben, wenn beide Zahlen gleich sind.
|
||||
// Die Logik von getDuplicate (i < len - 1) sollte funktionieren.
|
||||
// Hier wird 0 erwartet, da es kein *garantiertes* Duplikat gibt.
|
||||
TEST_ASSERT_EQUAL_UINT(0, getDuplicate(smallArray, 2));
|
||||
|
||||
// Array mit 2 gleichen Zahlen (was im Spiel nicht vorkommt, aber getestet werden muss)
|
||||
unsigned int allSame[] = {5, 5};
|
||||
TEST_ASSERT_EQUAL_UINT(5, getDuplicate(allSame, 2));
|
||||
|
||||
// Array mit 3 eindeutigen Zahlen (wieder nicht im Spiel, aber testen)
|
||||
unsigned int unique[] = {1, 2, 3};
|
||||
TEST_ASSERT_EQUAL_UINT(0, getDuplicate(unique, 3));
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// TESTFALL GRUPPE 3: compareNumbers (Hilfsfunktion für qsort)
|
||||
// =========================================================================
|
||||
|
||||
void test_compareNumbersReturnsZeroForEqual(void)
|
||||
{
|
||||
unsigned int a = 10, b = 10;
|
||||
TEST_ASSERT_EQUAL_INT(0, compareNumbers(&a, &b));
|
||||
}
|
||||
|
||||
void test_compareNumbersReturnsNegativeForLess(void)
|
||||
{
|
||||
unsigned int a = 5, b = 10;
|
||||
TEST_ASSERT_TRUE(compareNumbers(&a, &b) < 0);
|
||||
}
|
||||
|
||||
void test_compareNumbersReturnsPositiveForGreater(void)
|
||||
{
|
||||
unsigned int a = 10, b = 5;
|
||||
TEST_ASSERT_TRUE(compareNumbers(&a, &b) > 0);
|
||||
}
|
||||
|
||||
void test_compareNumbersHandlesZero(void)
|
||||
{
|
||||
unsigned int a = 0, b = 1;
|
||||
TEST_ASSERT_TRUE(compareNumbers(&a, &b) < 0);
|
||||
|
||||
unsigned int c = 1, d = 0;
|
||||
TEST_ASSERT_TRUE(compareNumbers(&c, &d) > 0);
|
||||
}
|
||||
|
||||
void test_compareNumbersHandlesMax(void)
|
||||
{
|
||||
unsigned int max_val = UINT_MAX;
|
||||
unsigned int max_minus_one = UINT_MAX - 1;
|
||||
|
||||
TEST_ASSERT_TRUE(compareNumbers(&max_val, &max_minus_one) > 0);
|
||||
TEST_ASSERT_TRUE(compareNumbers(&max_minus_one, &max_val) < 0);
|
||||
TEST_ASSERT_EQUAL_INT(0, compareNumbers(&max_val, &max_val));
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MAIN
|
||||
// =========================================================================
|
||||
|
||||
void setUp(void) {
|
||||
// Hier ist eine gute Stelle, um den Zufallszahlengenerator für Tests zu seeden
|
||||
// z.B. srand(42) für reproduzierbare Ergebnisse, aber für createNumbers
|
||||
// ist es besser, einen echten Seed zu verwenden, um die Eindeutigkeit besser zu prüfen.
|
||||
// Da createNumbers srand(time(NULL)) nutzt, lassen wir es hier weg.
|
||||
}
|
||||
|
||||
void tearDown(void) {
|
||||
// Bereinigung nach jedem Test
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
printf("\n============================\nNumbers Module Tests\n============================\n");
|
||||
|
||||
// Teste createNumbers (Duplikatprüfung und Speicherallokation)
|
||||
RUN_TEST(test_createNumbersReturnsNullForInvalidLength);
|
||||
RUN_TEST(test_createNumbersReturnsCorrectLengthAndNotNull);
|
||||
// Dieser Test prüft die gesamte Logik inkl. BST-Nutzung
|
||||
RUN_TEST(test_createNumbersGeneratesCorrectDuplicate);
|
||||
|
||||
// Teste getDuplicate (Sortierung und Duplikaterkennung)
|
||||
RUN_TEST(test_getDuplicateFindsDuplicatedNumber);
|
||||
RUN_TEST(test_getDuplicateReturnsZeroOnInvalidLength);
|
||||
|
||||
// Teste compareNumbers (qsort/BST Hilfsfunktion)
|
||||
RUN_TEST(test_compareNumbersReturnsZeroForEqual);
|
||||
RUN_TEST(test_compareNumbersReturnsNegativeForLess);
|
||||
RUN_TEST(test_compareNumbersReturnsPositiveForGreater);
|
||||
RUN_TEST(test_compareNumbersHandlesZero);
|
||||
RUN_TEST(test_compareNumbersHandlesMax);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user