From a2489db3eea07e11e457c1f60519006432425843 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:14:11 +0300 Subject: [PATCH 1/6] Added homework 6 --- CMakeLists.txt | 2 ++ homework_6/CMakeLists.txt | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 homework_6/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e2e18b..d6f9f05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,3 +3,5 @@ project(homework LANGUAGES C) set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD_REQUIRED TRUE) + +add_subdirectory(homework_6) diff --git a/homework_6/CMakeLists.txt b/homework_6/CMakeLists.txt new file mode 100644 index 0000000..1431304 --- /dev/null +++ b/homework_6/CMakeLists.txt @@ -0,0 +1,3 @@ +project(homework_6) + +set(homeworkName "${PROJECT_NAME}") From 89fc764da6c928539d408a87b946139e7bf431f0 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:19:27 +0300 Subject: [PATCH 2/6] Homework 6 - task 1 --- homework_6/CMakeLists.txt | 2 + homework_6/task_1/.gitignore | 2 + homework_6/task_1/CMakeLists.txt | 9 + homework_6/task_1/entryList.c | 218 ++++++++++++++++++ homework_6/task_1/entryList.h | 32 +++ homework_6/task_1/main.c | 36 +++ homework_6/task_1/test.c | 76 ++++++ homework_6/task_1/testFiles/empty_in.txt | 1 + homework_6/task_1/testFiles/empty_out.txt | 1 + homework_6/task_1/testFiles/oneEntry_in.txt | 1 + homework_6/task_1/testFiles/oneEntry_out.txt | 1 + .../task_1/testFiles/someEntries_in.txt | 5 + .../testFiles/someEntries_out_byName.txt | 5 + .../testFiles/someEntries_out_byPhone.txt | 5 + homework_6/task_1/testFiles/twoEntries_in.txt | 2 + .../testFiles/twoEntries_out_byName.txt | 2 + .../testFiles/twoEntries_out_byPhone.txt | 2 + 17 files changed, 400 insertions(+) create mode 100644 homework_6/task_1/.gitignore create mode 100644 homework_6/task_1/CMakeLists.txt create mode 100644 homework_6/task_1/entryList.c create mode 100644 homework_6/task_1/entryList.h create mode 100644 homework_6/task_1/main.c create mode 100644 homework_6/task_1/test.c create mode 100644 homework_6/task_1/testFiles/empty_in.txt create mode 100644 homework_6/task_1/testFiles/empty_out.txt create mode 100644 homework_6/task_1/testFiles/oneEntry_in.txt create mode 100644 homework_6/task_1/testFiles/oneEntry_out.txt create mode 100644 homework_6/task_1/testFiles/someEntries_in.txt create mode 100644 homework_6/task_1/testFiles/someEntries_out_byName.txt create mode 100644 homework_6/task_1/testFiles/someEntries_out_byPhone.txt create mode 100644 homework_6/task_1/testFiles/twoEntries_in.txt create mode 100644 homework_6/task_1/testFiles/twoEntries_out_byName.txt create mode 100644 homework_6/task_1/testFiles/twoEntries_out_byPhone.txt diff --git a/homework_6/CMakeLists.txt b/homework_6/CMakeLists.txt index 1431304..d0bc946 100644 --- a/homework_6/CMakeLists.txt +++ b/homework_6/CMakeLists.txt @@ -1,3 +1,5 @@ project(homework_6) set(homeworkName "${PROJECT_NAME}") + +add_subdirectory(task_1) diff --git a/homework_6/task_1/.gitignore b/homework_6/task_1/.gitignore new file mode 100644 index 0000000..62302a3 --- /dev/null +++ b/homework_6/task_1/.gitignore @@ -0,0 +1,2 @@ +list.txt +testFiles/output.txt diff --git a/homework_6/task_1/CMakeLists.txt b/homework_6/task_1/CMakeLists.txt new file mode 100644 index 0000000..6beb21b --- /dev/null +++ b/homework_6/task_1/CMakeLists.txt @@ -0,0 +1,9 @@ +project("${homeworkName}_task_1") + +add_library(entryList entryList.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} entryList) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test entryList) diff --git a/homework_6/task_1/entryList.c b/homework_6/task_1/entryList.c new file mode 100644 index 0000000..08e1e47 --- /dev/null +++ b/homework_6/task_1/entryList.c @@ -0,0 +1,218 @@ +#include "entryList.h" + +#include +#include +#include +#include + +typedef struct Entry { + char *name; + char *phoneNumber; + struct Entry *next; +} Entry; + +typedef struct { + Entry *begin; + Entry *end; +} ListSpan; + +typedef struct EntryList { + ListSpan span; +} EntryList; + +static void addToSpan(ListSpan *span, Entry *entry) { + if (span->begin == NULL) { + span->begin = entry; + } else { + span->end->next = entry; + } + + span->end = entry; +} + +static bool addEntry(EntryList *list, const char *name, const char *phoneNumber) { + Entry *entry = malloc(sizeof(Entry)); + if (entry == NULL) { + return false; + } + + entry->name = strdup(name); + entry->phoneNumber = strdup(phoneNumber); + entry->next = NULL; + + addToSpan(&list->span, entry); +} + +bool readToList(EntryList **list, FILE *stream) { + EntryList *newList = malloc(sizeof(EntryList)); + if (newList == NULL) { + return false; + } + + newList->span.begin = NULL; + newList->span.end = NULL; + + while (!feof(stream)) { + char nameBuffer[256] = { 0 }; + char phoneBuffer[256] = { 0 }; + + if (fscanf(stream, "%255s - %255s", nameBuffer, phoneBuffer) != 2) { + break; + } + + if (!addEntry(newList, nameBuffer, phoneBuffer)) { + return false; + } + } + + *list = newList; + + return true; +} + +static int compare(const Entry *left, const Entry *right, SortChoice choice) { + switch (choice) + { + case SORT_BY_NAME: + return strcmp(left->name, right->name); + + case SORT_BY_PHONE: + return strcmp(left->phoneNumber, right->phoneNumber); + + default: + return 0; + } +} + +static ListSpan merge(EntryList *into, ListSpan left, ListSpan right, Entry *entryBefore, SortChoice choice) { + Entry *entryAfter = right.end->next; + + Entry *leftEntry = left.begin; + Entry *rightEntry = right.begin; + + left.end->next = NULL; + right.end->next = NULL; + + ListSpan newSpan = { .begin = NULL, .end = NULL }; + + while (!(leftEntry == NULL && rightEntry == NULL)) { + Entry *leftNext = leftEntry == NULL ? NULL : leftEntry->next; + Entry *rightNext = rightEntry == NULL ? NULL : rightEntry->next; + + Entry *lesser = leftEntry == NULL + ? rightEntry + : (rightEntry == NULL + ? leftEntry + : compare(leftEntry, rightEntry, choice) <= 0 ? leftEntry : rightEntry); + + addToSpan(&newSpan, lesser); + + if (lesser == leftEntry) { + leftEntry = leftNext; + } else { + rightEntry = rightNext; + } + } + + if (entryBefore != NULL) { + entryBefore->next = newSpan.begin; + } else { + into->span.begin = newSpan.begin; + } + newSpan.end->next = entryAfter; + if (entryAfter == NULL) { + into->span.end = newSpan.end; + } + + return newSpan; +} + +static ListSpan captureSpan(Entry *firstEntry, int maxLength) { + ListSpan span = { .begin = firstEntry, .end = firstEntry }; + if (firstEntry->next == NULL) { + // span is single element, return + return span; + } + + for (int i = 1; i < maxLength; ++i) { + span.end = span.end->next; + if (span.end->next == NULL) { + break; + } + } + return span; +} + +static void sortStep(EntryList *resultList, int length, SortChoice choice) { + Entry *lastEntry = resultList->span.begin; + Entry *entryBeforeLast = NULL; + + int localstep = 0; + while (true) { + ListSpan leftSpan = captureSpan(lastEntry, length); + if (leftSpan.end->next == NULL) { + // not enough elements to capture rightSpan, and all elements in leftSpan are already sorted in previous step, break + break; + } + ListSpan rightSpan = captureSpan(leftSpan.end->next, length); + + ListSpan mergedSpan = merge(resultList, leftSpan, rightSpan, entryBeforeLast, choice); + + entryBeforeLast = mergedSpan.end; + lastEntry = entryBeforeLast->next; + if (lastEntry == NULL) { + // last element in rightSpan is last element in list, so step is complete, break + break; + } + + ++localstep; + } + + if (localstep == 0) { + // no merge happened, list sorted + return; + } + + sortStep(resultList, length * 2, choice); +} + +void sortList(EntryList *list, SortChoice choice) { + if (list->span.begin == list->span.end) { + return; + } + + sortStep(list, 1, choice); +} + +void printList(EntryList *list, FILE *stream) { + Entry *entry = list->span.begin; + if (entry == NULL) { + fprintf(stream, "\n"); + return; + } + + do { + fprintf(stream, "%s - %s", entry->name, entry->phoneNumber); + + if (entry == list->span.end || entry->next == NULL) { + fprintf(stream, ".\n"); + break; + } + entry = entry->next; + fprintf(stream, ";\n"); + } while (true); +} + +void disposeList(EntryList *list) { + Entry *last = list->span.begin; + while (last != NULL) { + free(last->name); + free(last->phoneNumber); + + Entry *next = last->next; + free(last); + last = next; + } + + free(list); +} diff --git a/homework_6/task_1/entryList.h b/homework_6/task_1/entryList.h new file mode 100644 index 0000000..c3ecfee --- /dev/null +++ b/homework_6/task_1/entryList.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +/// @brief List of entries in format `name` - `phone number` +typedef struct EntryList EntryList; + +typedef enum { + SORT_BY_PHONE, + SORT_BY_NAME +} SortChoice; + +/// @brief Reads entries from `stream` to `list` +/// @param list Pointer to store `EntryList` to +/// @param stream Stream to read entries from +/// @return `true` if read successfully, `false` otherwise (allocation failed) +bool readToList(EntryList **list, FILE *stream); + +/// @brief Sorts list +/// @param list List to sort +/// @param choice What sorting key to use (name or phone number) +void sortList(EntryList *list, SortChoice choice); + +/// @brief Prints list to specified stream +/// @param list List to print +/// @param stream Stream to write to +void printList(EntryList *list, FILE *stream); + +/// @brief Disposes list and all of its elements +/// @param list List to dispose +void disposeList(EntryList *list); diff --git a/homework_6/task_1/main.c b/homework_6/task_1/main.c new file mode 100644 index 0000000..68181ce --- /dev/null +++ b/homework_6/task_1/main.c @@ -0,0 +1,36 @@ +#include + +#include "entryList.h" + +int main(void) { + FILE *file = fopen("list.txt", "r"); + + if (file == NULL) { + printf("couldn't open file\n"); + return 1; + } + + EntryList *list = NULL; + if (!readToList(&list, file)) { + printf("allocation error\n"); + return 1; + } + + fclose(file); + + printf("type 'n' to sort entries by name (default);\n"); + printf("type 'p' to sort entries by phone number;\n"); + printf("> "); + + SortChoice choice = SORT_BY_NAME; + char choiceChar = '\0'; + if (scanf("%c", &choiceChar) == 1) { + choice = choiceChar == 'p' ? SORT_BY_PHONE : SORT_BY_NAME; + } + + printf("sorted entries by %s:\n", choice == SORT_BY_NAME ? "name" : "phone number"); + + sortList(list, choice); + printList(list, stdout); + disposeList(list); +} diff --git a/homework_6/task_1/test.c b/homework_6/task_1/test.c new file mode 100644 index 0000000..8648e4b --- /dev/null +++ b/homework_6/task_1/test.c @@ -0,0 +1,76 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "entryList.h" + +#define TEST_FILES "testFiles/" +#define IN "_in.txt" +#define OUT "_out.txt" + +#define BY_NAME_OUT "_out_byName.txt" +#define BY_PHONE_OUT "_out_byPhone.txt" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +EntryList *readList(const char *filename) { + FILE *file = fopen(filename, "r"); + ASSERT_NOT_NULL(file); + + EntryList *list = NULL; + ASSERT_TRUE(readToList(&list, file)); + + ASSERT_EQUAL(fclose(file), 0); + return list; +} + +void assertTwoFiles(const char *input, const char *expectedOutput, SortChoice choice) { + EntryList *list = readList(input); + + sortList(list, choice); + + const char *outputName = TEST_FILES "output.txt"; + FILE *outputFile = fopen(outputName, "w+"); + ASSERT_NOT_NULL(outputFile); + + printList(list, outputFile); + ASSERT_EQUAL(fclose(outputFile), 0); + + FILE *expectedOutputFile = fopen(expectedOutput, "r"); + ASSERT_NOT_NULL(expectedOutputFile); + FILE *realOutputFile = fopen(outputName, "r"); + ASSERT_NOT_NULL(outputFile); + + while (!feof(expectedOutputFile) && !feof(realOutputFile)) { + char expected = fgetc(expectedOutputFile); + char real = fgetc(realOutputFile); + ASSERT_EQUAL(expected, real); + } + + ASSERT_EQUAL(fclose(expectedOutputFile), 0); + ASSERT_EQUAL(fclose(realOutputFile), 0); + + disposeList(list); +} + +CTEST(entryListTests, emptyListTest) { + assertTwoFiles(TEST_FILES "empty" IN, TEST_FILES "empty" OUT, SORT_BY_NAME); +} + +CTEST(entryListTests, oneEntryTest) { + assertTwoFiles(TEST_FILES "oneEntry" IN, TEST_FILES "oneEntry" OUT, SORT_BY_NAME); +} + +CTEST(entryListTests, twoEntriesTest) { + assertTwoFiles(TEST_FILES "twoEntries" IN, TEST_FILES "twoEntries" BY_NAME_OUT, SORT_BY_NAME); + assertTwoFiles(TEST_FILES "twoEntries" IN, TEST_FILES "twoEntries" BY_PHONE_OUT, SORT_BY_PHONE); +} + +CTEST(entryListTests, someEntriesTest) { + assertTwoFiles(TEST_FILES "someEntries" IN, TEST_FILES "someEntries" BY_NAME_OUT, SORT_BY_NAME); + assertTwoFiles(TEST_FILES "someEntries" IN, TEST_FILES "someEntries" BY_PHONE_OUT, SORT_BY_PHONE); +} diff --git a/homework_6/task_1/testFiles/empty_in.txt b/homework_6/task_1/testFiles/empty_in.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/homework_6/task_1/testFiles/empty_in.txt @@ -0,0 +1 @@ + diff --git a/homework_6/task_1/testFiles/empty_out.txt b/homework_6/task_1/testFiles/empty_out.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/homework_6/task_1/testFiles/empty_out.txt @@ -0,0 +1 @@ + diff --git a/homework_6/task_1/testFiles/oneEntry_in.txt b/homework_6/task_1/testFiles/oneEntry_in.txt new file mode 100644 index 0000000..18156c0 --- /dev/null +++ b/homework_6/task_1/testFiles/oneEntry_in.txt @@ -0,0 +1 @@ +name_1 - 123 diff --git a/homework_6/task_1/testFiles/oneEntry_out.txt b/homework_6/task_1/testFiles/oneEntry_out.txt new file mode 100644 index 0000000..c88dc1f --- /dev/null +++ b/homework_6/task_1/testFiles/oneEntry_out.txt @@ -0,0 +1 @@ +name_1 - 123. diff --git a/homework_6/task_1/testFiles/someEntries_in.txt b/homework_6/task_1/testFiles/someEntries_in.txt new file mode 100644 index 0000000..336d776 --- /dev/null +++ b/homework_6/task_1/testFiles/someEntries_in.txt @@ -0,0 +1,5 @@ +abc - 12345 +name - 4484232 +John - 19223455 +Mary - 15552340 +Pete - 76630023 diff --git a/homework_6/task_1/testFiles/someEntries_out_byName.txt b/homework_6/task_1/testFiles/someEntries_out_byName.txt new file mode 100644 index 0000000..5ca9e15 --- /dev/null +++ b/homework_6/task_1/testFiles/someEntries_out_byName.txt @@ -0,0 +1,5 @@ +John - 19223455; +Mary - 15552340; +Pete - 76630023; +abc - 12345; +name - 4484232. diff --git a/homework_6/task_1/testFiles/someEntries_out_byPhone.txt b/homework_6/task_1/testFiles/someEntries_out_byPhone.txt new file mode 100644 index 0000000..31ee1c5 --- /dev/null +++ b/homework_6/task_1/testFiles/someEntries_out_byPhone.txt @@ -0,0 +1,5 @@ +abc - 12345; +Mary - 15552340; +John - 19223455; +name - 4484232; +Pete - 76630023. diff --git a/homework_6/task_1/testFiles/twoEntries_in.txt b/homework_6/task_1/testFiles/twoEntries_in.txt new file mode 100644 index 0000000..69fae07 --- /dev/null +++ b/homework_6/task_1/testFiles/twoEntries_in.txt @@ -0,0 +1,2 @@ +name_1 - 456 +name_2 - 123 diff --git a/homework_6/task_1/testFiles/twoEntries_out_byName.txt b/homework_6/task_1/testFiles/twoEntries_out_byName.txt new file mode 100644 index 0000000..d3aaa18 --- /dev/null +++ b/homework_6/task_1/testFiles/twoEntries_out_byName.txt @@ -0,0 +1,2 @@ +name_1 - 456; +name_2 - 123. diff --git a/homework_6/task_1/testFiles/twoEntries_out_byPhone.txt b/homework_6/task_1/testFiles/twoEntries_out_byPhone.txt new file mode 100644 index 0000000..14e6457 --- /dev/null +++ b/homework_6/task_1/testFiles/twoEntries_out_byPhone.txt @@ -0,0 +1,2 @@ +name_2 - 123; +name_1 - 456. From ba6b3a7cb8c42de5ce22d89e07bbaa54de0b2148 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:47:46 +0300 Subject: [PATCH 3/6] Added missing return statement (hw6 task 1) --- homework_6/task_1/entryList.c | 1 + 1 file changed, 1 insertion(+) diff --git a/homework_6/task_1/entryList.c b/homework_6/task_1/entryList.c index 08e1e47..98445bd 100644 --- a/homework_6/task_1/entryList.c +++ b/homework_6/task_1/entryList.c @@ -41,6 +41,7 @@ static bool addEntry(EntryList *list, const char *name, const char *phoneNumber) entry->next = NULL; addToSpan(&list->span, entry); + return true; } bool readToList(EntryList **list, FILE *stream) { From 5cd8b072b656e1800566744afbeab4ba854b09be Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Tue, 10 Dec 2024 04:18:49 +0300 Subject: [PATCH 4/6] Fixed assertion (hw6 task 1) --- homework_6/task_1/test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework_6/task_1/test.c b/homework_6/task_1/test.c index 8648e4b..0f42361 100644 --- a/homework_6/task_1/test.c +++ b/homework_6/task_1/test.c @@ -43,7 +43,7 @@ void assertTwoFiles(const char *input, const char *expectedOutput, SortChoice ch FILE *expectedOutputFile = fopen(expectedOutput, "r"); ASSERT_NOT_NULL(expectedOutputFile); FILE *realOutputFile = fopen(outputName, "r"); - ASSERT_NOT_NULL(outputFile); + ASSERT_NOT_NULL(realOutputFile); while (!feof(expectedOutputFile) && !feof(realOutputFile)) { char expected = fgetc(expectedOutputFile); From 064a53f93bf50a281c6fba41b800691ce33a8d04 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Wed, 25 Dec 2024 19:28:24 +0300 Subject: [PATCH 5/6] Added README (hw6) --- homework_6/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 homework_6/README.md diff --git a/homework_6/README.md b/homework_6/README.md new file mode 100644 index 0000000..4a2824e --- /dev/null +++ b/homework_6/README.md @@ -0,0 +1 @@ +# Homework 6 From 07f0508f11068471caaccb4b2f1b7e2a1e181a19 Mon Sep 17 00:00:00 2001 From: ilya-krivtsov <180809461+ilya-krivtsov@users.noreply.github.com> Date: Wed, 25 Dec 2024 19:29:42 +0300 Subject: [PATCH 6/6] Added task name to README (hw6 task 1) --- homework_6/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homework_6/README.md b/homework_6/README.md index 4a2824e..e173a60 100644 --- a/homework_6/README.md +++ b/homework_6/README.md @@ -1 +1,3 @@ # Homework 6 + +[Task 1. Merge sort](/homework_6/task_1)