Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_subdirectory(homework_2)
add_subdirectory(homework_3)
add_subdirectory(homework_4)
add_subdirectory(homework_5)
add_subdirectory(homework_6)
add_subdirectory(homework_7)
add_subdirectory(homework_8)
add_subdirectory(homework_9)
5 changes: 5 additions & 0 deletions homework_6/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
project(homework_6)

set(homeworkName "${PROJECT_NAME}")

add_subdirectory(task_1)
3 changes: 3 additions & 0 deletions homework_6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Homework 6

[Task 1. Merge sort](/homework_6/task_1)
2 changes: 2 additions & 0 deletions homework_6/task_1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
list.txt
testFiles/output.txt
9 changes: 9 additions & 0 deletions homework_6/task_1/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
219 changes: 219 additions & 0 deletions homework_6/task_1/entryList.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#include "entryList.h"

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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);
return true;
}

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);
}
32 changes: 32 additions & 0 deletions homework_6/task_1/entryList.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <stdbool.h>
#include <stdio.h>

/// @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);
36 changes: 36 additions & 0 deletions homework_6/task_1/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <stdio.h>

#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);
}
76 changes: 76 additions & 0 deletions homework_6/task_1/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#define CTEST_MAIN
#define CTEST_SEGFAULT
#include "../../ctest/ctest.h"

#include <stdio.h>

#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(realOutputFile);

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);
}
1 change: 1 addition & 0 deletions homework_6/task_1/testFiles/empty_in.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading