diff --git a/CMakeLists.txt b/CMakeLists.txt index b49a297..cc58e27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,3 +7,4 @@ set(CMAKE_C_STANDARD_REQUIRED TRUE) add_compile_options(-Wall -Wextra) add_subdirectory(homework_2) +add_subdirectory(homework_3) diff --git a/build.sh b/build.sh old mode 100755 new mode 100644 diff --git a/homework_3/CMakeLists.txt b/homework_3/CMakeLists.txt new file mode 100644 index 0000000..f445d32 --- /dev/null +++ b/homework_3/CMakeLists.txt @@ -0,0 +1,7 @@ +project(homework_3) + +set(homeworkName "${PROJECT_NAME}") + +add_subdirectory(task_1) +add_subdirectory(task_2) +add_subdirectory(task_3) diff --git a/homework_3/README.md b/homework_3/README.md new file mode 100644 index 0000000..38adbb9 --- /dev/null +++ b/homework_3/README.md @@ -0,0 +1,7 @@ +# Homework 3 + +[Task 1. Most frequent element](/homework_3/task_1) + +[Task 2. Search](/homework_3/task_2) + +[Task 3. Smart QSort](/homework_3/task_3) diff --git a/homework_3/task_1/CMakeLists.txt b/homework_3/task_1/CMakeLists.txt new file mode 100644 index 0000000..c2b82bd --- /dev/null +++ b/homework_3/task_1/CMakeLists.txt @@ -0,0 +1,9 @@ +project("${homeworkName}_task_1") + +add_library(frequencySearch frequencySearch.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} frequencySearch) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test frequencySearch) diff --git a/homework_3/task_1/frequencySearch.c b/homework_3/task_1/frequencySearch.c new file mode 100644 index 0000000..a12b4ca --- /dev/null +++ b/homework_3/task_1/frequencySearch.c @@ -0,0 +1,84 @@ +#include "frequencySearch.h" + +#include +#include + +int partition(int *array, int left, int right) { + int first = array[left], middle = array[(left + right) / 2], last = array[right]; + + int pivot = first; + if (first <= middle && middle <= last) { + pivot = middle; + } else if (middle <= first && first <= last) { + pivot = first; + } else { + pivot = last; + } + + --left; + ++right; + + while (true) { + do { + ++left; + } while (array[left] < pivot); + do { + --right; + } while (array[right] > pivot); + + if (left >= right) break; + + int t = array[left]; + array[left] = array[right]; + array[right] = t; + } + + return right; +} + +void quickSort(int *array, int left, int right) { + if (left >= right) { + return; + } + + int part = partition(array, left, right); + quickSort(array, left, part); + quickSort(array, part + 1, right); +} + +bool findMostFrequentElement(int *array, int arrayLength, int *mostFrequentElement, int *occurrences) { + if (arrayLength == 0) { + return false; + } + + quickSort(array, 0, arrayLength - 1); + + int element = array[0], count = 0; + int maxCountElement = element, maxCount = count; + + for (int i = 0; i < arrayLength; ++i) { + if (array[i] != element) { + if (count > maxCount) { + maxCount = count; + maxCountElement = element; + } + count = 0; + element = array[i]; + } + ++count; + } + + if (count > maxCount) { + maxCount = count; + maxCountElement = element; + } + + if (mostFrequentElement != NULL) { + *mostFrequentElement = maxCountElement; + } + if (occurrences != NULL) { + *occurrences = maxCount; + } + + return true; +} diff --git a/homework_3/task_1/frequencySearch.h b/homework_3/task_1/frequencySearch.h new file mode 100644 index 0000000..f087b91 --- /dev/null +++ b/homework_3/task_1/frequencySearch.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +bool findMostFrequentElement(int *array, int arrayLength, int *mostFrequentElement, int *occurrences); diff --git a/homework_3/task_1/main.c b/homework_3/task_1/main.c new file mode 100644 index 0000000..0253e85 --- /dev/null +++ b/homework_3/task_1/main.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "frequencySearch.h" + +void randomizeArray(int *array, int arrayLength, int minValue, int maxValue) { + for (int i = 0; i < arrayLength; ++i) { + array[i] = rand() * (maxValue - minValue) / RAND_MAX + minValue; + } +} + +int readValue(const char *prompt, const char *incorrectValueMessage) { + int value; + printf("%s", prompt); + while ((scanf("%d", &value) != 1) || value < 0) { + while (getchar() != '\n') {} + printf("%s", incorrectValueMessage); + } + return value; +} + +int main(void) { + int arrayLength = readValue("enter array length: ", "incorrect value: array length cannot be less than zero; try again: "); + + int *array = calloc(arrayLength, sizeof(int)); + if (array == NULL) { + return 1; + } + + srand(time(NULL)); + randomizeArray(array, arrayLength, 0, 128); + + int mostFrequentElement = 0, occurrences = 0; + if (findMostFrequentElement(array, arrayLength, &mostFrequentElement, &occurrences)) { + printf("most frequent element is %d; it appears %d times in array", mostFrequentElement, occurrences); + } else { + printf("couldn't find most frequent element: the array is empty\n"); + } + + free(array); +} diff --git a/homework_3/task_1/test.c b/homework_3/task_1/test.c new file mode 100644 index 0000000..e5f329b --- /dev/null +++ b/homework_3/task_1/test.c @@ -0,0 +1,47 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "frequencySearch.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +CTEST(frequencySearchTests, emptyArrayTest) { + ASSERT_FALSE(findMostFrequentElement(NULL, 0, NULL, NULL)); +} + +CTEST(frequencySearchTests, oneElementArrayTest) { +#define size 1 + int array[size] = { 42 }; + int mostFrequentExpected = 42; + int mostFrequentResult = 0; + ASSERT_TRUE(findMostFrequentElement(array, size, &mostFrequentResult, NULL)); + ASSERT_EQUAL(mostFrequentResult, mostFrequentExpected); +#undef size +} + +CTEST(frequencySearchTests, sameElementsArrayTest) { +#define size 8 + int array[size] = { 42, 42, 42, 42, 42, 42, 42, 42 }; + int mostFrequentExpected = 42, occurrencesExpected = size; + int mostFrequentResult = 0, occurrencesResult = 0; + ASSERT_TRUE(findMostFrequentElement(array, size, &mostFrequentResult, &occurrencesResult)); + ASSERT_EQUAL(mostFrequentResult, mostFrequentExpected); + ASSERT_EQUAL(occurrencesResult, occurrencesExpected); +#undef size +} + +CTEST(frequencySearchTests, constantArrayTest) { +#define size 10 + int array[size] = { 1, 2, 3, 3, 3, 7, 9, 12, 8, 3 }; + int mostFrequentExpected = 3, occurrencesExpected = 4; + int mostFrequentResult = 0, occurrencesResult = 0; + ASSERT_TRUE(findMostFrequentElement(array, size, &mostFrequentResult, &occurrencesResult)); + ASSERT_EQUAL(mostFrequentResult, mostFrequentExpected); + ASSERT_EQUAL(occurrencesResult, occurrencesExpected); +#undef size +} diff --git a/homework_3/task_2/CMakeLists.txt b/homework_3/task_2/CMakeLists.txt new file mode 100644 index 0000000..be8494c --- /dev/null +++ b/homework_3/task_2/CMakeLists.txt @@ -0,0 +1,9 @@ +project("${homeworkName}_task_2") + +add_library(sortAndSearch sortAndSearch.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} sortAndSearch) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test sortAndSearch) diff --git a/homework_3/task_2/main.c b/homework_3/task_2/main.c new file mode 100644 index 0000000..f4f0fee --- /dev/null +++ b/homework_3/task_2/main.c @@ -0,0 +1,53 @@ +#include +#include +#include + +#include "sortAndSearch.h" + +int getRandom(int minValue, int maxValue) { + int r = rand(); + return r * (maxValue - minValue) / RAND_MAX + minValue; +} + +int readValue(const char *prompt, const char *incorrectValueMessage) { + int value; + printf("%s", prompt); + while ((scanf("%d", &value) != 1) || value < 0) { + while (getchar() != '\n') {} + printf("%s", incorrectValueMessage); + } + return value; +} + +int main(void) { + int arrayLength = readValue("enter array length: ", "incorrect value: array length cannot be less than zero; try again: "); + int numbersToCheckCount = readValue("enter amount of numbers to check: ", "incorrect value: amount cannot be less than zero; try again: "); + + int *array = calloc(arrayLength, sizeof(int)); + if (array == NULL) { + return 1; + } + + int minRandom = 0, + maxRandom = 128; + + srand(time(NULL)); + + for (int i = 0; i < arrayLength; ++i) { + array[i] = getRandom(minRandom, maxRandom); + } + + quickSort(array, arrayLength); + + int foundNumbers = 0; + for (int i = 0; i < numbersToCheckCount; ++i) { + int number = getRandom(minRandom, maxRandom); + if (containsElement(array, arrayLength, number)) { + ++foundNumbers; + } + } + + printf("out of %d randomly generated numbers %d %s in the array\n", numbersToCheckCount, foundNumbers, foundNumbers == 1 ? "was" : "were"); + + free(array); +} diff --git a/homework_3/task_2/sortAndSearch.c b/homework_3/task_2/sortAndSearch.c new file mode 100644 index 0000000..7d76288 --- /dev/null +++ b/homework_3/task_2/sortAndSearch.c @@ -0,0 +1,73 @@ +#include "sortAndSearch.h" + +#include + +int partition(int *array, int left, int right) { + int first = array[left], middle = array[(left + right) / 2], last = array[right]; + + int pivot = first; + if (first <= middle && middle <= last) { + pivot = middle; + } else if (middle <= first && first <= last) { + pivot = first; + } else { + pivot = last; + } + + --left; + ++right; + + while (true) { + do { + ++left; + } while (array[left] < pivot); + do { + --right; + } while (array[right] > pivot); + + if (left >= right) break; + + int t = array[left]; + array[left] = array[right]; + array[right] = t; + } + + return right; +} + +void quickSortInternal(int *array, int left, int right) { + if (left >= right) { + return; + } + + int part = partition(array, left, right); + quickSortInternal(array, left, part); + quickSortInternal(array, part + 1, right); +} + +void quickSort(int *array, int arrayLength) { + quickSortInternal(array, 0, arrayLength - 1); +} + +bool containsElement(int *array, int arrayLength, int value) { + if (arrayLength == 0) { + return false; + } + + int left = 0, right = arrayLength - 1; + + while (left <= right) { + int middlePoint = (left + right) / 2, + middleElement = array[middlePoint]; + + if (value < middleElement) { + right = middlePoint - 1; + } else if (value > middleElement) { + left = middlePoint + 1; + } else { + return true; + } + } + + return array[left] == value; +} diff --git a/homework_3/task_2/sortAndSearch.h b/homework_3/task_2/sortAndSearch.h new file mode 100644 index 0000000..274dd08 --- /dev/null +++ b/homework_3/task_2/sortAndSearch.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +void quickSort(int *array, int arrayLength); + +/// array must be sorted before calling this function +bool containsElement(int *array, int arrayLength, int value); diff --git a/homework_3/task_2/test.c b/homework_3/task_2/test.c new file mode 100644 index 0000000..78ca674 --- /dev/null +++ b/homework_3/task_2/test.c @@ -0,0 +1,47 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "sortAndSearch.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +CTEST(binSearchTests, emptyArrayTest) { + ASSERT_FALSE(containsElement(NULL, 0, 0)); +} + +CTEST(binSearchTests, oneElementArrayTest) { +#define size 1 + int array[size] = { 42 }; + ASSERT_TRUE(containsElement(array, size, 42)); + ASSERT_FALSE(containsElement(array, size, 21)); +#undef size +} + +CTEST(binSearchTests, sameElementsArrayTest) { +#define size 8 + int array[size] = { 42, 42, 42, 42, 42, 42, 42, 42 }; + + ASSERT_TRUE(containsElement(array, size, 42)); + ASSERT_FALSE(containsElement(array, size, 21)); +#undef size +} + +CTEST(binSearchTests, constantArrayTest) { +#define size 10 + int array[size] = { 1, 2, 3, 7, 10, 13, 15, 16, 19, 20 }; + for (int i = 0; i < size; ++i) { + ASSERT_TRUE(containsElement(array, size, array[i])); + } + ASSERT_FALSE(containsElement(array, size, 0)); + ASSERT_FALSE(containsElement(array, size, -1)); + ASSERT_FALSE(containsElement(array, size, -4)); + ASSERT_FALSE(containsElement(array, size, 40)); + ASSERT_FALSE(containsElement(array, size, 50)); + ASSERT_FALSE(containsElement(array, size, 70)); +#undef size +} diff --git a/homework_3/task_3/CMakeLists.txt b/homework_3/task_3/CMakeLists.txt new file mode 100644 index 0000000..fc49c87 --- /dev/null +++ b/homework_3/task_3/CMakeLists.txt @@ -0,0 +1,9 @@ +project("${homeworkName}_task_3") + +add_library(smartQSort smartQSort.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} smartQSort) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test smartQSort) diff --git a/homework_3/task_3/main.c b/homework_3/task_3/main.c new file mode 100644 index 0000000..4f75ecb --- /dev/null +++ b/homework_3/task_3/main.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +#include "smartQSort.h" + +void randomizeArray(int *array, int arrayLength, int minValue, int maxValue) { + for (int i = 0; i < arrayLength; ++i) { + array[i] = rand() * (maxValue - minValue) / RAND_MAX + minValue; + } +} + +int readValue(const char *prompt, const char *incorrectValueMessage) { + int value; + printf("%s", prompt); + while ((scanf("%d", &value) != 1) || value < 0) { + while (getchar() != '\n') {} + printf("%s", incorrectValueMessage); + } + return value; +} + +long long measureTime(void (*sort)(int *, int), int *array, int arrayLength) { + struct timespec startTime, endTime; + clock_gettime(CLOCK_MONOTONIC, &startTime); + sort(array, arrayLength); + clock_gettime(CLOCK_MONOTONIC, &endTime); + return (endTime.tv_nsec - startTime.tv_nsec) + (endTime.tv_sec - startTime.tv_sec) * 1000 * 1000 * 1000; +} + +int main(void) { + int arrayLength = readValue("enter array length: ", "incorrect value: array length cannot be less than zero; try again: "); + + int *arrayA = calloc(arrayLength, sizeof(int)), + *arrayB = calloc(arrayLength, sizeof(int)); + + if (arrayA == NULL || arrayB == NULL) { + return 1; + } + + srand(time(NULL)); + randomizeArray(arrayA, arrayLength, 0, 1024); + arrayB = memcpy(arrayB, arrayA, arrayLength * sizeof(int)); + + printf("qsort: %.2f ms\n", measureTime(quickSort, arrayA, arrayLength) / 1000.0 / 1000.0); + printf("smart qsort: %.2f ms\n", measureTime(smartQuickSort, arrayB, arrayLength) / 1000.0 / 1000.0); + + free(arrayA); + free(arrayB); +} diff --git a/homework_3/task_3/smartQSort.c b/homework_3/task_3/smartQSort.c new file mode 100644 index 0000000..7d7d978 --- /dev/null +++ b/homework_3/task_3/smartQSort.c @@ -0,0 +1,79 @@ +#include "smartQSort.h" + +#include + +void insertionSort(int *array, int left, int right) { + for (int i = left + 1; i <= right; ++i) { + for (int j = i - 1; j >= left; --j) { + if (array[j] <= array[j + 1]) { + break; + } + + int temp = array[j]; + array[j] = array[j + 1]; + array[j + 1] = temp; + } + } +} + +int partition(int *array, int left, int right) { + int first = array[left], middle = array[(left + right) / 2], last = array[right]; + + int pivot = first; + if (first <= middle && middle <= last) { + pivot = middle; + } else if (middle <= first && first <= last) { + pivot = first; + } else { + pivot = last; + } + + --left; + ++right; + + while (true) { + do { + ++left; + } while (array[left] < pivot); + do { + --right; + } while (array[right] > pivot); + + if (left >= right) break; + + int t = array[left]; + array[left] = array[right]; + array[right] = t; + } + + return right; +} + +void quickSortInternal(int *array, int left, int right) { + if (left >= right) { + return; + } + + int part = partition(array, left, right); + quickSortInternal(array, left, part); + quickSortInternal(array, part + 1, right); +} + +void smartQuickSortInternal(int *array, int left, int right) { + if (right - left + 1 <= 10) { + insertionSort(array, left, right); + return; + } + + int part = partition(array, left, right); + smartQuickSortInternal(array, left, part); + smartQuickSortInternal(array, part + 1, right); +} + +void quickSort(int *array, int arrayLength) { + quickSortInternal(array, 0, arrayLength - 1); +} + +void smartQuickSort(int *array, int arrayLength) { + smartQuickSortInternal(array, 0, arrayLength - 1); +} diff --git a/homework_3/task_3/smartQSort.h b/homework_3/task_3/smartQSort.h new file mode 100644 index 0000000..14f7325 --- /dev/null +++ b/homework_3/task_3/smartQSort.h @@ -0,0 +1,4 @@ +#pragma once + +void quickSort(int *array, int arrayLength); +void smartQuickSort(int *array, int arrayLength); diff --git a/homework_3/task_3/test.c b/homework_3/task_3/test.c new file mode 100644 index 0000000..52c0860 --- /dev/null +++ b/homework_3/task_3/test.c @@ -0,0 +1,68 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "smartQSort.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +void assertSorted(int *array, int arrayLength) { + for (int i = 1; i < arrayLength; ++i) { + ASSERT_GE(array[i], array[i - 1]); + } +} + +void sortAndAssert(int *array, int arrayLength) { + smartQuickSort(array, arrayLength); + + for (int i = 1; i < arrayLength; ++i) { + ASSERT_GE(array[i], array[i - 1]); + } +} + +void randomizeArray(int *array, int arrayLength, int minValue, int maxValue) { + for (int i = 0; i < arrayLength; ++i) { + array[i] = rand() * (maxValue - minValue) / RAND_MAX + minValue; + } +} + +CTEST(smartQSortTests, emptyArrayTest) { + smartQuickSort(NULL, 0); +} + +CTEST(smartQSortTests, oneElementArrayTest) { +#define size 1 + int array[size] = { 24 }; + sortAndAssert(array, size); +#undef size +} + +CTEST(smartQSortTests, sameElementsArrayTest) { +#define size 8 + int array[size] = { 31, 31, 31, 31, 31, 31, 31, 31 }; + sortAndAssert(array, size); +#undef size +} + +CTEST(smartQSortTests, constantArrayTest) { +#define size 10 + int array[size] = { 23, 12, 4, 35, 20, -15, 17, 8, 19, 40 }; + sortAndAssert(array, size); +#undef size +} + +CTEST(smartQSortTests, randomArrayTest) { +#define size 100 + int array[size]; + + srand(968475267); + + randomizeArray(array, size, -128, 128); + + sortAndAssert(array, size); +#undef size +}