diff --git a/homework_7/CMakeLists.txt b/homework_7/CMakeLists.txt index 6c91639..fac6836 100644 --- a/homework_7/CMakeLists.txt +++ b/homework_7/CMakeLists.txt @@ -3,3 +3,4 @@ project(homework_7) set(homeworkName "${PROJECT_NAME}") add_subdirectory(task_1) +add_subdirectory(task_2) diff --git a/homework_7/README.md b/homework_7/README.md index 3507fa2..01ead18 100644 --- a/homework_7/README.md +++ b/homework_7/README.md @@ -1,3 +1,5 @@ # Homework 7 [Task 1. Expression tree](/homework_7/task_1) + +[Task 2. Search tree](/homework_7/task_2) diff --git a/homework_7/task_2/CMakeLists.txt b/homework_7/task_2/CMakeLists.txt new file mode 100644 index 0000000..7e6d313 --- /dev/null +++ b/homework_7/task_2/CMakeLists.txt @@ -0,0 +1,9 @@ +project("${homeworkName}_task_2") + +add_library(intStringDictionary intStringDictionary.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} intStringDictionary) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test intStringDictionary) diff --git a/homework_7/task_2/intStringDictionary.c b/homework_7/task_2/intStringDictionary.c new file mode 100644 index 0000000..9c262cc --- /dev/null +++ b/homework_7/task_2/intStringDictionary.c @@ -0,0 +1,201 @@ +#include "intStringDictionary.h" + +#include +#include +#include + +typedef struct Node { + int key; + char *value; + struct Node *left; + struct Node *right; +} Node; + +typedef struct IntStringDictionary { + Node *root; +} IntStringDictionary; + +bool createDictionary(IntStringDictionary **dictionary) { + *dictionary = malloc(sizeof(IntStringDictionary)); + if (*dictionary == NULL) { + return false; + } + + (*dictionary)->root = NULL; + + return true; +} + +static bool createNode(Node **node, int key, const char *value) { + *node = malloc(sizeof(Node)); + if (*node == NULL) { + return false; + } + + (*node)->key = key; + + char *valueCopy = strdup(value); + if (valueCopy == NULL) { + return false; + } + (*node)->value = valueCopy; + + (*node)->left = NULL; + (*node)->right = NULL; + + return true; +} + +static bool addAfterNode(Node *node, int key, const char *value) { + Node *parent = NULL; + Node *child = node; + while (child != NULL) { + parent = child; + if (key > parent->key) { + child = parent->right; + } else if (key < parent->key) { + child = parent->left; + } else { + free(parent->value); + char *valueCopy = strdup(value); + if (valueCopy == NULL) { + return false; + } + parent->value = valueCopy; + return true; + } + } + + if (parent == NULL) { + return false; + } + + Node **childPointer = key > parent->key ? &parent->right: &parent->left; + + return createNode(childPointer, key, value); +} + +bool addToDictionary(IntStringDictionary *dictionary, int key, const char *value) { + if (dictionary->root == NULL) { + return createNode(&dictionary->root, key, value); + } + + return addAfterNode(dictionary->root, key, value); +} + +Node *getNode(Node *node, int key) { + while (true) { + if (node == NULL) { + return NULL; + } + + if (key < node->key) { + node = node->left; + } else if (key > node->key) { + node = node->right; + } else { + break; + } + } + + return node; +} + +char *getValue(IntStringDictionary *dictionary, int key) { + Node *node = getNode(dictionary->root, key); + + return node == NULL ? NULL : node->value; +} + +bool containsKey(IntStringDictionary *dictionary, int key) { + Node *node = getNode(dictionary->root, key); + + return node != NULL; +} + +// returns rightmost child of given node, or node itself if right is NULL +Node *getRightmost(Node *node) { + while (node->right != NULL) { + node = node->right; + } + return node; +} + +bool removeFromDictionary(IntStringDictionary *dictionary, int key) { + Node *node = dictionary->root; + Node *previous = NULL; + while (true) { + if (node == NULL) { + return false; + } + + if (key < node->key) { + previous = node; + node = node->left; + } else if (key > node->key) { + previous = node; + node = node->right; + } else { + break; + } + } + + if (node == dictionary->root) { + if (node->left == NULL) { + dictionary->root = node->right; + free(node); + return true; + } + + if (node->right == NULL) { + dictionary->root = node->left; + free(node); + return true; + } + + Node *rightmostInLeft = getRightmost(node->left); + rightmostInLeft->right = node->right; + dictionary->root = node->left; + + free(node); + + return true; + } + + Node **setNewNodeTo = key < previous->key ? &previous->left : &previous->right; + + if (node->left == NULL) { + *setNewNodeTo = node->right; + free(node); + return true; + } + + if (node->right == NULL) { + *setNewNodeTo = node->left; + free(node); + return true; + } + + Node *rightmostInLeft = getRightmost(node->left); + rightmostInLeft->right = node->right; + *setNewNodeTo = node->left; + free(node); + return true; +} + +void disposeNode(Node *node) { + if (node == NULL) { + return; + } + + disposeNode(node->left); + disposeNode(node->right); + + free(node->value); + free(node); +} + +void disposeDictionary(IntStringDictionary *dictionary) { + disposeNode(dictionary->root); + free(dictionary); +} diff --git a/homework_7/task_2/intStringDictionary.h b/homework_7/task_2/intStringDictionary.h new file mode 100644 index 0000000..a080eb8 --- /dev/null +++ b/homework_7/task_2/intStringDictionary.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +/// @brief Dictionary of `int` keys associated with `char *` values +typedef struct IntStringDictionary IntStringDictionary; + +/// @brief Creates new dictionary +/// @param dictionary Pointer to write new dictionary to +/// @return `true` if created successfully, `false` otherwise (allocation failed) +bool createDictionary(IntStringDictionary **dictionary); + +/// @brief Adds key and associated with it value to dictionary +/// @param dictionary Dictionary to add key and value to +/// @param key Key to add +/// @param value Value to add +/// @return `true` if added successfully, `false` otherwise (allocation failed) +bool addToDictionary(IntStringDictionary *dictionary, int key, const char *value); + +/// @brief Gets value corresponding to given key +/// @param dictionary Dictionary to get value from +/// @param key Key associated with value +/// @return String associated with key, or `NULL` if value was not found +char *getValue(IntStringDictionary *dictionary, int key); + +/// @brief Checks if dictionary contains given key +/// @param dictionary Dictionary to check +/// @param key Key to check +/// @return `true` if key exists in dictionary, `false` otherwise +bool containsKey(IntStringDictionary *dictionary, int key); + +/// @brief Removes value from dictionary using given key +/// @param dictionary Dictionary to remove value from +/// @param key Key associated with value to be removed +/// @return `true` if removed successfully, `false` otherwise (key does not exist in dictionary) +bool removeFromDictionary(IntStringDictionary *dictionary, int key); + +/// @brief Disposes dictionary and all of its entries +/// @param dictionary Dictionary to dispose +void disposeDictionary(IntStringDictionary *dictionary); diff --git a/homework_7/task_2/main.c b/homework_7/task_2/main.c new file mode 100644 index 0000000..f122493 --- /dev/null +++ b/homework_7/task_2/main.c @@ -0,0 +1,140 @@ +#include +#include + +#include "intStringDictionary.h" + +void addEntryCommand(IntStringDictionary *dictionary) { + printf("Enter key to add: "); + int key = 0; + if (scanf("%d", &key) != 1) { + printf("Error: not a number\n"); + return; + } + + printf("Enter value to add: "); + char buffer[256] = { 0 }; + if (scanf("%255s", buffer) != 1) { + printf("Error: not a string\n"); + return; + } + + if (!addToDictionary(dictionary, key, buffer)) { + printf("Error: cannot add new entry\n"); + } +} + +void getValueCommand(IntStringDictionary *dictionary) { + printf("Enter a key of value to retrieve: "); + int key = 0; + if (scanf("%d", &key) != 1) { + printf("Error: not a number\n"); + return; + } + + char *value = getValue(dictionary, key); + if (value == NULL) { + printf("Value with key '%d' does not exist in dictionary\n", key); + } else { + printf("Found value with key '%d': \"%s\"\n", key, value); + } +} + +void checkIfExistsCommand(IntStringDictionary *dictionary) { + printf("Enter a key of value to check: "); + int key = 0; + if (scanf("%d", &key) != 1) { + printf("Error: not a number\n"); + return; + } + + printf("Key '%d' %s in dictionary\n", key, containsKey(dictionary, key) ? "exists" : "does not exist"); +} + +void removeEntryCommand(IntStringDictionary *dictionary) { + printf("Enter a key of value to remove: "); + int key = 0; + if (scanf("%d", &key) != 1) { + printf("Error: not a number\n"); + return; + } + + if (!removeFromDictionary(dictionary, key)) { + printf("Error: cannot remove value; it is not stored in dictionary\n"); + } +} + +int readCommand(void) { + int command = -1; + if (scanf("%d", &command) != 1) { + command = -1; + } + while (getchar() != '\n') {} + switch (command) { + case 0: + case 1: + case 2: + case 3: + case 4: + return command; + default: + printf("Error: unknown command\n"); + return -1; + } +} + +bool doConversation(IntStringDictionary *dictionary) { + printf("Dictionary\n"); + printf("Available commands: \n"); + printf(" 0 - exit;\n"); + printf(" 1 - add new entry (key and value);\n"); + printf(" 2 - get value by key;\n"); + printf(" 3 - check if key exists in dictionary;\n"); + printf(" 4 - remove entry by key;\n"); + + while (true) { + printf("dictionary> "); + int command = readCommand(); + switch (command) + { + case -1: + break; + + case 0: + return true; + + case 1: + addEntryCommand(dictionary); + break; + + case 2: + getValueCommand(dictionary); + break; + + case 3: + checkIfExistsCommand(dictionary); + break; + + case 4: + removeEntryCommand(dictionary); + break; + + default: + printf("Error: unknown state\n"); + return false; + } + } +} + +int main(void) { + IntStringDictionary *dictionary = NULL; + if (!createDictionary(&dictionary)) { + printf("Error: allocation failed\n"); + return 1; + } + + if (!doConversation(dictionary)) { + disposeDictionary(dictionary); + return 1; + } + disposeDictionary(dictionary); +} diff --git a/homework_7/task_2/test.c b/homework_7/task_2/test.c new file mode 100644 index 0000000..22791e9 --- /dev/null +++ b/homework_7/task_2/test.c @@ -0,0 +1,104 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "intStringDictionary.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +IntStringDictionary *createDict(void) { + IntStringDictionary *dictionary = NULL; + ASSERT_TRUE(createDictionary(&dictionary)); + return dictionary; +} + +void addToDict(IntStringDictionary *dict, int key, const char *value) { + ASSERT_TRUE(addToDictionary(dict, key, value)); +} + +CTEST(dictonaryTests, emptyTest) { + IntStringDictionary *dict = createDict(); + disposeDictionary(dict); +} + +CTEST(dictonaryTests, singleEntryTest) { + IntStringDictionary *dict = createDict(); + + const int key = 0; + const char *value = "string_0"; + addToDict(dict, key, value); + ASSERT_TRUE(containsKey(dict, key)); + + ASSERT_STR(getValue(dict, key), value); + + ASSERT_TRUE(removeFromDictionary(dict, key)); + ASSERT_FALSE(removeFromDictionary(dict, key)); + ASSERT_FALSE(containsKey(dict, key)); + ASSERT_NULL(getValue(dict, key)); + + disposeDictionary(dict); +} + +void assertAddingAndRemoving(int keys[], const char *values[], int size) { + IntStringDictionary *dict = createDict(); + + for (int i = 0; i < size; ++i) { + addToDict(dict, keys[i], values[i]); + ASSERT_TRUE(containsKey(dict, keys[i])); + ASSERT_STR(getValue(dict, keys[i]), values[i]); + } + + for (int i = 0; i < size; ++i) { + ASSERT_TRUE(containsKey(dict, keys[i])); + ASSERT_STR(getValue(dict, keys[i]), values[i]); + } + + for (int i = 0; i < size; ++i) { + // offset index so values will be removed a bit randomly + int index = (i + (size * 2 / 3)) % size; + + ASSERT_TRUE(removeFromDictionary(dict, keys[index])); + ASSERT_FALSE(removeFromDictionary(dict, keys[index])); + ASSERT_FALSE(containsKey(dict, keys[index])); + ASSERT_NULL(getValue(dict, keys[index])); + } + + disposeDictionary(dict); +} + +CTEST(dictonaryTests, binaryEntriesTest) { + int keys[15] = { 32, 16, 48, 8, 24, 40, 56, 4, 12, 20, 28, 36, 44, 52, 60 }; + const char *values[15] = { "32", "16", "48", "8", "24", "40", "56", "4", "12", "20", "28", "36", "44", "52", "60" }; + assertAddingAndRemoving(keys, values, 15); +} + +CTEST(dictonaryTests, randomEntriesTest) { +#define size 16384 + int keys[size] = { 0 }; + const char *values[size] = { 0 }; + + const char *string = "random_string"; + for (int i = 0; i < size; ++i) { + keys[i] = i * 2 - 24517; + values[i] = string; + } + + srand(453563458); + + for (int i = 0; i < size; ++i) { + int index = (rand() % (size - i)) + i; + + int key = keys[i]; + keys[i] = keys[index]; + keys[index] = key; + + // dont swap values, they are all the same + } + + assertAddingAndRemoving(keys, values, size); +#undef size +}