diff --git a/test_2_1/CMakeLists.txt b/test_2_1/CMakeLists.txt index b955bc1..5a07805 100644 --- a/test_2_1/CMakeLists.txt +++ b/test_2_1/CMakeLists.txt @@ -3,3 +3,4 @@ project(test_2_1) set(testName "${PROJECT_NAME}") add_subdirectory(task_1) +add_subdirectory(task_2) diff --git a/test_2_1/task_2/CMakeLists.txt b/test_2_1/task_2/CMakeLists.txt new file mode 100644 index 0000000..642cbb6 --- /dev/null +++ b/test_2_1/task_2/CMakeLists.txt @@ -0,0 +1,6 @@ +project("${testName}_task_2") + +add_library(hashset hashset.c) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test hashset) diff --git a/test_2_1/task_2/hashset.c b/test_2_1/task_2/hashset.c new file mode 100644 index 0000000..eff1ac8 --- /dev/null +++ b/test_2_1/task_2/hashset.c @@ -0,0 +1,81 @@ +#include "hashset.h" + +#include +#include + +typedef struct Hashset { + char *elements[HASHSET_SIZE]; +} Hashset; + +bool createHashset(Hashset **hashset) { + *hashset = malloc(sizeof(Hashset)); + if (*hashset == NULL) { + return false; + } + + for (int i = 0; i < HASHSET_SIZE; ++i) { + (*hashset)->elements[i] = NULL; + } + + return true; +} + +static unsigned int getStringHash(const char *string) { + unsigned int hash = 0; + for (int i = 0; string[i] != '\0'; ++i) { + hash = hash * 57 + string[i]; + } + return hash; +} + +bool addToHashset(Hashset *hashset, const char *string) { + if (string == NULL) { + return false; + } + + unsigned int elementIndex = getStringHash(string) % HASHSET_SIZE; + + int counter = 0; + while (hashset->elements[elementIndex] != NULL) { + elementIndex = (elementIndex + 1) % HASHSET_SIZE; + ++counter; + if (counter >= HASHSET_SIZE) { + return false; + } + } + + char *stringCopy = strdup(string); + if (stringCopy == NULL) { + return false; + } + + hashset->elements[elementIndex] = stringCopy; + + return true; +} + +bool containedInHashset(Hashset *hashset, const char *string) { + unsigned int elementIndex = getStringHash(string) % HASHSET_SIZE; + + if (hashset->elements[elementIndex] == NULL) { + return false; + } + + int counter = 0; + while (strcmp(hashset->elements[elementIndex], string) != 0) { + elementIndex = (elementIndex + 1) % HASHSET_SIZE; + ++counter; + if (counter >= HASHSET_SIZE) { + return false; + } + } + + return true; +} + +void disposeHashset(Hashset *hashset) { + for (int i = 0; i < HASHSET_SIZE; ++i) { + free(hashset->elements[i]); + } + free(hashset); +} diff --git a/test_2_1/task_2/hashset.h b/test_2_1/task_2/hashset.h new file mode 100644 index 0000000..b679eea --- /dev/null +++ b/test_2_1/task_2/hashset.h @@ -0,0 +1,27 @@ +#pragma once + +#define HASHSET_SIZE 256 + +/// @brief Hashset of strings +typedef struct Hashset Hashset; + +/// @brief Creates new hashset +/// @param hashset Pointer to store new hashset to +/// @return `true` if created successfully, `false` otherwise (allocation failed) +bool createHashset(Hashset **hashset); + +/// @brief Adds string to hashtable +/// @param hashset Hashset to add string to +/// @param string String to add +/// @return `true` if added successfully, `false` otherwise (string was `NULL`, allocation failed, no space for new entries left) +bool addToHashset(Hashset *hashset, const char *string); + +/// @brief Searchs for specified string +/// @param hashset Hashset to search string in +/// @param string String to search +/// @return `true` if found, `false` otherwise +bool containedInHashset(Hashset *hashset, const char *string); + +/// @brief Disposes hashtable +/// @param hashset Hashtable to dispose +void disposeHashset(Hashset *hashset); diff --git a/test_2_1/task_2/test.c b/test_2_1/task_2/test.c new file mode 100644 index 0000000..5170a17 --- /dev/null +++ b/test_2_1/task_2/test.c @@ -0,0 +1,78 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "hashset.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +Hashset *createNewHashset(void) { + Hashset *hashset = NULL; + ASSERT_TRUE(createHashset(&hashset)); + return hashset; +} + +CTEST(hashsetTests, createTest) { + Hashset *hashset = createNewHashset(); + disposeHashset(hashset); +} + +CTEST(hashsetTests, singleElemenTest) { + Hashset *hashset = createNewHashset(); + + ASSERT_FALSE(containedInHashset(hashset, "element")); + ASSERT_TRUE(addToHashset(hashset, "element")); + ASSERT_TRUE(containedInHashset(hashset, "element")); + ASSERT_FALSE(containedInHashset(hashset, "not_element")); + + disposeHashset(hashset); +} + +CTEST(hashsetTests, randomElementsTest) { + char *elements[HASHSET_SIZE] = { 0 }; + + const char *elementTemplate = "key_000"; + const int elementOffset = strlen("key_"); + const int digitsCount = strlen(elementTemplate) - elementOffset; + + for (int i = 0; i < HASHSET_SIZE; ++i) { + char *element = strdup(elementTemplate); + + ASSERT_NOT_NULL(element); + + elements[i] = element; + + int number = i; + for (int j = digitsCount - 1; j >= 0; --j) { + char digit = (number % 10) + '0'; + element[elementOffset + j] = digit; + number /= 10; + } + } + + Hashset *hashset = createNewHashset(); + + srand(453563458); + for (int i = 0; i < HASHSET_SIZE; ++i) { + int index = (rand() % (HASHSET_SIZE - i)) + i; + + ASSERT_TRUE(addToHashset(hashset, elements[i])); + } + + for (int i = 0; i < HASHSET_SIZE; ++i) { + ASSERT_TRUE(containedInHashset(hashset, elements[i])); + } + + ASSERT_FALSE(containedInHashset(hashset, "random_element")); + ASSERT_FALSE(containedInHashset(hashset, "does_not_exist")); + + for (int i = 0; i < HASHSET_SIZE; ++i) { + free(elements[i]); + } + + disposeHashset(hashset); +}