diff --git a/CMakeLists.txt b/CMakeLists.txt index e68a470..3d8feb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,3 +9,4 @@ add_compile_options(-Wall -Wextra) add_subdirectory(homework_2) add_subdirectory(homework_3) add_subdirectory(homework_4) +add_subdirectory(homework_5) diff --git a/homework_5/CMakeLists.txt b/homework_5/CMakeLists.txt new file mode 100644 index 0000000..170e71d --- /dev/null +++ b/homework_5/CMakeLists.txt @@ -0,0 +1,7 @@ +project(homework_5) + +set(homeworkName "${PROJECT_NAME}") + +add_subdirectory(stack) + +add_subdirectory(task_1) diff --git a/homework_5/README.md b/homework_5/README.md new file mode 100644 index 0000000..72e66a9 --- /dev/null +++ b/homework_5/README.md @@ -0,0 +1,3 @@ +# Homework 5 + +[Task 1. Advanced brackets balance](/homework_5/task_1) diff --git a/homework_5/stack/CMakeLists.txt b/homework_5/stack/CMakeLists.txt new file mode 100644 index 0000000..0589179 --- /dev/null +++ b/homework_5/stack/CMakeLists.txt @@ -0,0 +1,6 @@ +project("${homeworkName}_stack") + +add_library(stack stack.c) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test stack) diff --git a/homework_5/stack/stack.c b/homework_5/stack/stack.c new file mode 100644 index 0000000..1c8f40f --- /dev/null +++ b/homework_5/stack/stack.c @@ -0,0 +1,84 @@ +#include "stack.h" + +#include +#include +#include + +typedef struct Node { + uint64_t value; + struct Node *previous; +} Node; + +typedef struct Stack { + Node *head; +} Stack; + +static void removeLastNode(Stack *stack) { + Node *prev = stack->head; + stack->head = stack->head->previous; + free(prev); +} + +bool stackCreate(Stack **stack) { + *stack = malloc(sizeof(Stack)); + if (*stack == NULL) { + return false; + } + + (*stack)->head = NULL; + + return true; +} + +bool stackPush(Stack *stack, uint64_t value) { + Node *node = malloc(sizeof(Node)); + if (node == NULL) { + return false; + } + + if (stackIsEmpty(stack)) { + node->previous = NULL; + } else { + node->previous = stack->head; + } + + node->value = value; + stack->head = node; + + return true; +} + + +bool stackTryPop(Stack *stack, uint64_t *value) { + if (stackIsEmpty(stack)) { + return false; + } + + *value = stack->head->value; + + removeLastNode(stack); + + return true; +} + +bool stackTryPeek(Stack *stack, uint64_t *value) { + if (stackIsEmpty(stack)) { + return false; + } + + *value = stack->head->value; + + return true; +} + +bool stackIsEmpty(Stack *stack) { + return stack->head == NULL; +} + +void stackDispose(Stack *stack) { + while (!stackIsEmpty(stack)) { + removeLastNode(stack); + } + + free(stack); +} diff --git a/homework_5/stack/stack.h b/homework_5/stack/stack.h new file mode 100644 index 0000000..2aafdaa --- /dev/null +++ b/homework_5/stack/stack.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +/// @brief Stack that stores all of its elements as `uint64_t` +typedef struct Stack Stack; + +/// @brief Creates `Stack` +/// @param stack Pointer to store created `Stack` to +/// @return `true` if successful, `false` otherwise (allocation failed) +bool stackCreate(Stack **stack); + +/// @brief Pushes value on top of the stack +/// @param stack Pointer to `Stack` instance +/// @return `true` if successful, `false` otherwise (allocation failed) +bool stackPush(Stack *stack, uint64_t value); + +/// @brief Pops value from the top of the stack +/// @param stack Pointer to `Stack` instance +/// @param value Pointer to write value to +/// @return `true` if stack was non-empty, `false` otherwise +bool stackTryPop(Stack *stack, uint64_t *value); + +/// @brief Returns value from the top of the stack without removing it +/// @param stack Pointer to `Stack` instance +/// @param value Pointer to write value to +/// @return `true` if stack was non-empty, `false` otherwise +bool stackTryPeek(Stack *stack, uint64_t *value); + +/// @brief Checks if stack is empty +/// @param stack Pointer to `Stack` instance +/// @return `true` if stack is empty, `false` otherwise +bool stackIsEmpty(Stack *stack); + +/// @brief Disposes stack and all of its elements +/// @param stack `Stack` to dispose +void stackDispose(Stack *stack); diff --git a/homework_5/stack/test.c b/homework_5/stack/test.c new file mode 100644 index 0000000..e8cca3f --- /dev/null +++ b/homework_5/stack/test.c @@ -0,0 +1,147 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "stack.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +#pragma region Helpers + +Stack *createStack(void) { + Stack *stack; + ASSERT_TRUE(stackCreate(&stack)); + ASSERT_NOT_NULL(stack); + + return stack; +} + +void assertPop(Stack *stack, uint64_t value) { + uint64_t top = 0; + ASSERT_TRUE(stackTryPop(stack, &top)); + ASSERT_EQUAL_U(value, top); +} + +void assertPeek(Stack *stack, uint64_t value) { + uint64_t top = 0; + ASSERT_TRUE(stackTryPeek(stack, &top)); + ASSERT_EQUAL_U(value, top); +} + +#pragma endregion + +CTEST(stackTests, createTest) { + Stack *stack = createStack(); + ASSERT_TRUE(stackIsEmpty(stack)); + stackDispose(stack); +} + +#pragma region Push & Pop + +CTEST(stackTests, pushPopTestA) { + Stack *stack = createStack(); + + ASSERT_TRUE(stackPush(stack, 0)); + assertPop(stack, 0); + + ASSERT_TRUE(stackIsEmpty(stack)); + + stackDispose(stack); +} + +CTEST(stackTests, pushPopTestB) { + Stack *stack = createStack(); + + ASSERT_TRUE(stackPush(stack, 0)); + assertPop(stack, 0); + ASSERT_TRUE(stackIsEmpty(stack)); + + ASSERT_TRUE(stackPush(stack, 47)); + assertPop(stack, 47); + ASSERT_TRUE(stackIsEmpty(stack)); + + ASSERT_TRUE(stackPush(stack, 1245)); + ASSERT_TRUE(stackPush(stack, 44362)); + assertPop(stack, 44362); + assertPop(stack, 1245); + ASSERT_TRUE(stackIsEmpty(stack)); + + stackDispose(stack); +} + +CTEST(stackTests, pushPopTestC) { + Stack *stack = createStack(); + + ASSERT_TRUE(stackPush(stack, 2352)); + ASSERT_TRUE(stackPush(stack, 43634)); + assertPop(stack, 43634); + ASSERT_FALSE(stackIsEmpty(stack)); + + ASSERT_TRUE(stackPush(stack, 9999)); + ASSERT_TRUE(stackPush(stack, 7777777)); + assertPop(stack, 7777777); + assertPop(stack, 9999); + assertPop(stack, 2352); + ASSERT_TRUE(stackIsEmpty(stack)); + + stackDispose(stack); +} + +#pragma endregion + +#pragma region Push & Peek + +CTEST(stackTests, pushPeekTestA) { + Stack *stack = createStack(); + + ASSERT_TRUE(stackPush(stack, 0)); + assertPeek(stack, 0); + + ASSERT_FALSE(stackIsEmpty(stack)); + + stackDispose(stack); +} + +CTEST(stackTests, pushPeekTestB) { + Stack *stack = createStack(); + + ASSERT_TRUE(stackPush(stack, 0)); + assertPeek(stack, 0); + ASSERT_FALSE(stackIsEmpty(stack)); + + ASSERT_TRUE(stackPush(stack, 47)); + assertPeek(stack, 47); + ASSERT_FALSE(stackIsEmpty(stack)); + + ASSERT_TRUE(stackPush(stack, 1245)); + ASSERT_TRUE(stackPush(stack, 44362)); + assertPeek(stack, 44362); + assertPeek(stack, 44362); + ASSERT_FALSE(stackIsEmpty(stack)); + + stackDispose(stack); +} + +CTEST(stackTests, pushPeekTestC) { + Stack *stack = createStack(); + + ASSERT_TRUE(stackPush(stack, 2352)); + ASSERT_TRUE(stackPush(stack, 43634)); + assertPeek(stack, 43634); + ASSERT_FALSE(stackIsEmpty(stack)); + + ASSERT_TRUE(stackPush(stack, 9999)); + ASSERT_TRUE(stackPush(stack, 7777777)); + assertPeek(stack, 7777777); + assertPeek(stack, 7777777); + assertPeek(stack, 7777777); + ASSERT_FALSE(stackIsEmpty(stack)); + + stackDispose(stack); +} + +#pragma endregion diff --git a/homework_5/task_1/CMakeLists.txt b/homework_5/task_1/CMakeLists.txt new file mode 100644 index 0000000..bcab224 --- /dev/null +++ b/homework_5/task_1/CMakeLists.txt @@ -0,0 +1,11 @@ +project("${homeworkName}_task_1") + +add_library(bracketsBalance bracketsBalance.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} bracketsBalance) +target_link_libraries(${PROJECT_NAME} stack) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test bracketsBalance) +target_link_libraries(${PROJECT_NAME}_test stack) diff --git a/homework_5/task_1/bracketsBalance.c b/homework_5/task_1/bracketsBalance.c new file mode 100644 index 0000000..8e56a06 --- /dev/null +++ b/homework_5/task_1/bracketsBalance.c @@ -0,0 +1,44 @@ +#include "bracketsBalance.h" + +#include +#include + +#include "../stack/stack.h" + +BalanceResult checkIfBracketsBalanced(const char *string) { + Stack *stack = NULL; + if (!stackCreate(&stack)) { + return ALLOCATION_FAILED; + } + + bool checkedAll = true; + + for (int i = 0; string[i] != '\0'; ++i) { + char chr = string[i]; + + if (chr == '(' || chr == '[' || chr == '{') { + stackPush(stack, chr); + } else if (chr == ')' || chr == ']' || chr == '}') { + uint64_t value = -1; + if (!stackTryPop(stack, &value)) { + checkedAll = false; + break; + } + + char popped = (char)value; + + if ((popped == '(' && chr != ')') || + (popped == '[' && chr != ']') || + (popped == '{' && chr != '}')) { + checkedAll = false; + break; + } + } + } + + BalanceResult result = checkedAll && stackIsEmpty(stack) ? BALANCED : UNBALANCED; + + stackDispose(stack); + + return result; +} diff --git a/homework_5/task_1/bracketsBalance.h b/homework_5/task_1/bracketsBalance.h new file mode 100644 index 0000000..39ef4eb --- /dev/null +++ b/homework_5/task_1/bracketsBalance.h @@ -0,0 +1,13 @@ +#pragma once + +typedef enum { + BALANCED, + UNBALANCED, + ALLOCATION_FAILED +} BalanceResult; + +/// @brief Checks if brackets are balanced in given string, all other characters are ignored +/// @param string String to be checked +/// @return `BALANCED` if brackets are balanced, `UNBALANCED` otherwise, +/// `ALLOCATION_FAILED` if allocation failed +BalanceResult checkIfBracketsBalanced(const char *string); diff --git a/homework_5/task_1/main.c b/homework_5/task_1/main.c new file mode 100644 index 0000000..1762764 --- /dev/null +++ b/homework_5/task_1/main.c @@ -0,0 +1,28 @@ +#include + +#include "bracketsBalance.h" + +int main(void) { + printf("input string: "); + char buffer[1024] = { 0 }; + + if (scanf("%1023s", buffer) != 1) { + printf("reading string failed\n"); + return 1; + } + + switch (checkIfBracketsBalanced(buffer)) + { + case BALANCED: + printf("brackets are balanced\n"); + break; + + case UNBALANCED: + printf("brackets are unbalanced\n"); + break; + + case ALLOCATION_FAILED: + printf("allocation failed\n"); + return 1; + } +} diff --git a/homework_5/task_1/test.c b/homework_5/task_1/test.c new file mode 100644 index 0000000..1ea947a --- /dev/null +++ b/homework_5/task_1/test.c @@ -0,0 +1,53 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include "bracketsBalance.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +CTEST(bracketsBalanceTest, emptyStringTest) { + ASSERT_EQUAL(checkIfBracketsBalanced(""), BALANCED); +} + +CTEST(bracketsBalanceTest, randomCharactersTest) { + ASSERT_EQUAL(checkIfBracketsBalanced("sdfhJGGjGJHJhgJhgjhgFygYIOTyF:bKFyGFTKYtygGhggFYTfhkuasbvV2346T362T7*$&^I^&26234R623"), BALANCED); +} + +CTEST(bracketsBalanceTest, singleOpeningBracketTest) { + ASSERT_EQUAL(checkIfBracketsBalanced("("), UNBALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("["), UNBALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("{"), UNBALANCED); +} + +CTEST(bracketsBalanceTest, singleClosingBracketTest) { + ASSERT_EQUAL(checkIfBracketsBalanced(")"), UNBALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("]"), UNBALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("}"), UNBALANCED); +} + +CTEST(bracketsBalanceTest, singlePairTest) { + ASSERT_EQUAL(checkIfBracketsBalanced("()"), BALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("[]"), BALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("{}"), BALANCED); +} + +CTEST(bracketsBalanceTest, nonNestedPairsTest) { + ASSERT_EQUAL(checkIfBracketsBalanced("()[]{}{}"), BALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("[](){}[][][]"), BALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("{}[]()(){}[]"), BALANCED); +} + +CTEST(bracketsBalanceTest, nestedPairsTest) { + ASSERT_EQUAL(checkIfBracketsBalanced("({[]})"), BALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("{{{}}}[[[]]]((()))"), BALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("{()[({}()[]){[]}()({})]}"), BALANCED); +} + +CTEST(bracketsBalanceTest, unbalancedTest) { + ASSERT_EQUAL(checkIfBracketsBalanced("{({)}}"), UNBALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("(((((([))))))]"), UNBALANCED); + ASSERT_EQUAL(checkIfBracketsBalanced("(({}(({}(([))){})))]"), UNBALANCED); +}