diff --git a/Inc/sort/bubble_sort.h b/Inc/sort/bubble_sort.h index 9fa12ee..245e028 100644 --- a/Inc/sort/bubble_sort.h +++ b/Inc/sort/bubble_sort.h @@ -38,7 +38,9 @@ #include "typedefs.h" /** - * @brief Sort elements using bubble sort algorithm. + * @brief Sort elements using bubble sort algorithm. It compares the adjacent elements and swaps them + * if they are in the wrong order. It is done repeatedly until all elements are sorted. + * Time complexity: O(N^2) * * @param[in/out] *buffer Pointer to the buffer that contains elements that will be sorted. * @param[in] number_of_elements Number of elements in the buffer. diff --git a/Inc/sort/heap_sort.h b/Inc/sort/heap_sort.h index 4d3a10b..c49502b 100644 --- a/Inc/sort/heap_sort.h +++ b/Inc/sort/heap_sort.h @@ -38,7 +38,10 @@ #include "typedefs.h" /** - * @brief Sort elements using heap sort algorithm. + * @brief Sort elements using heap sort algorithm. It uses heapify to convert array into max heap. + * It deletes the root node of the max heap (one by one) and replaces it with the last node. After that, + * heapify is done again. Whole process is repeated while heap size is greater than 1. + * Time complexity: O(N log N) * * @param[in/out] *buffer Pointer to the buffer that contains elements that will be sorted. * @param[in] number_of_elements Number of elements in the buffer. diff --git a/Inc/sort/insertion_sort.h b/Inc/sort/insertion_sort.h index de93250..2dee881 100644 --- a/Inc/sort/insertion_sort.h +++ b/Inc/sort/insertion_sort.h @@ -40,9 +40,11 @@ #define MAX_ELEMENT_SIZE (64) /** - * @brief Sort elements using insertion sort algorithm. Insertion sort algorithm is a simple - * sorting algorithm that has O(N^2) time complexity. It is efficient on smaller lists, and - * much less efficient on large lists. + * @brief Sort elements using insertion sort algorithm. Divide given list into sorted and unsorted part + * (first element is considered sorted). Iterate through each element in the unsorted part. Pick one + * element at a time, and insert it into correct position in the sorted part. It is efficient on smaller + * lists, and much less efficient on large lists. + * Time complexity: O(N^2) * * NOTE: implemented that the max element size is 64 B. If element size is larger, then * MAX_ELEMENT_SIZE macro must be updated. diff --git a/Inc/sort/merge_sort.h b/Inc/sort/merge_sort.h new file mode 100644 index 0000000..ed7f9fa --- /dev/null +++ b/Inc/sort/merge_sort.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * + * Copyright (c) 2025 IMProject Development Team. All rights reserved. + * Authors: Juraj Ciberlin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name IMProject nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef UTILITY_MERGE_SORT_H_ +#define UTILITY_MERGE_SORT_H_ + +#include "typedefs.h" + +#define MAX_HALVES_SIZE (64) + +/** + * @brief Sort elements using merge sort algorithm. Merge sort algorithm works on a principle that it + * divides the list/array into two halves. Sort each one. Merge them back together. Repeat the whole + * process until the entire array is sorted. This algorithm does not use recursion since it is not MISRA + * compliant. + * Time complexity: O(N log N) + * + * @param[in/out] *buffer Pointer to the buffer that contains elements that will be sorted. + * @param[in] number_of_elements Number of elements in the buffer. + * @param[in] element_size Size of the element, in bytes. + * @param[in] *compareFun Pointer to compare function. Compare function has two parameters (pointer to + * first element and pointer to second element). As a result, it returns boolean, + * true if first element is greater than second element, otherwise false. + */ +void MergeSort_sort(byte_t* buffer, int32_t number_of_elements, int32_t element_size, + bool (*compareFun)(void* first, void* second)); + +#endif /* UTILITY_MERGE_SORT_H_ */ diff --git a/Inc/sort/selection_sort.h b/Inc/sort/selection_sort.h index 7a5c843..991ddc4 100644 --- a/Inc/sort/selection_sort.h +++ b/Inc/sort/selection_sort.h @@ -38,13 +38,13 @@ #include "typedefs.h" /** - * @brief Sort elements using selection sort algorithm. Selection sort algorithm is an in-place - * comparison sorting algorithm that has O(N^2) time complexity. It divides the input array into + * @brief Sort elements using selection sort algorithm. It divides the input array into * two parts, sorted sub-array of items which is built up from left to right and unsorted items * that occupies the rest of the array. The sorted sub-array is empty and the unsorted sub-array * is the entire input array. The algorithm proceeds by finding the smallest element in the * unsorted sub-array, swapping it with the leftmost unsorted element (putting it in sorted order), * and moving the sub-array boundaries one element to the right. + * Time complexity: O(N^2) * * @param[in/out] *buffer Pointer to the buffer that contains elements that will be sorted. * @param[in] number_of_elements Number of elements in the buffer. diff --git a/Makefile b/Makefile index 76d9dea..b54f3da 100644 --- a/Makefile +++ b/Makefile @@ -111,6 +111,7 @@ IMUTILITY_FILES=\ Src/sort/bubble_sort.c \ Src/sort/heap_sort.c \ Src/sort/insertion_sort.c \ + Src/sort/merge_sort.c \ Src/sort/selection_sort.c \ Src/json.c \ Src/map.c \ @@ -136,6 +137,7 @@ SRC_FILES+=$(IMUTILITY_FILES) \ Tests/test_insertion_sort.c \ Tests/test_json.c \ Tests/test_map.c \ + Tests/test_merge_sort.c \ Tests/test_priority_queue.c \ Tests/test_queue.c \ Tests/test_scheduler.c \ diff --git a/README.md b/README.md index acc2a52..eb3c1fa 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ Join us on the Discord channel https://discord.gg/R6nZxZqDH3 - BubbleSort_sort - HeapSort_sort - InsertionSort_sort +- MergeSort_sort - SelectionSort_sort ### Utils diff --git a/Src/json.c b/Src/json.c index df8a23f..da55c36 100644 --- a/Src/json.c +++ b/Src/json.c @@ -43,7 +43,6 @@ Json_startString(char* buffer, size_t buffer_size) { bool success = false; if (buffer_size >= MINIMAL_SIZE) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[0], "{"); success = true; } @@ -59,25 +58,19 @@ Json_addData(char* buffer, size_t buffer_size, const char* key, const char* valu size_t total_size = strlen("\"\":\"\"") + strlen(key) + strlen(value) + 1U; if (0 == strcmp(&buffer[index - 1U], "\"")) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[index], ","); ++index; } if ((total_size + index) <= buffer_size) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[index], "\""); index += strlen("\""); - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[index], key); index += strlen(key); - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[index], "\":\""); index += strlen("\":\""); - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[index], value); index += strlen(value); - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[index], "\""); success = true; @@ -92,7 +85,6 @@ Json_endString(char* buffer, size_t buffer_size) { size_t index = strlen(buffer); if (buffer_size >= (MINIMAL_SIZE + index)) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case, therefore it is not used strcpy(&buffer[index], "}"); success = true; } diff --git a/Src/map.c b/Src/map.c index 628aab3..50119ac 100644 --- a/Src/map.c +++ b/Src/map.c @@ -73,15 +73,12 @@ Map_insert(Map_t* map, const byte_t* key, const byte_t* value) { int32_t index = GetIndex(map, key, map->current_size); if (index == INDEX_NOT_FOUND) { if (map->current_size != map->max_map_size) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&map->keys[map->current_size * map->key_size], key, (size_t)map->key_size); - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&map->values[map->current_size * map->value_size], value, (size_t)map->value_size); ++map->current_size; status = true; } } else { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&map->values[index * map->value_size], value, (size_t)map->value_size); status = true; } @@ -95,7 +92,6 @@ Map_getValue(const Map_t* map, const byte_t* key, byte_t* value) { if (map != NULL_PTR) { int32_t index = GetIndex(map, key, map->current_size); if (index != INDEX_NOT_FOUND) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(value, &map->values[index * map->value_size], (size_t)map->value_size); status = true; } diff --git a/Src/priority_queue.c b/Src/priority_queue.c index 396303a..6ff1ce4 100644 --- a/Src/priority_queue.c +++ b/Src/priority_queue.c @@ -96,7 +96,6 @@ PriorityQueue_enqueue(PriorityQueue_t* queue, const PriorityQueueItem_t* item) { bool status = false; if (!Full(queue)) { uint8_t* buffer = queue->buffer; - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&buffer[queue->size * queue->element_size], item->element, queue->element_size); queue->priority_array[queue->size] = *(item->priority); queue->size = queue->size + 1U; @@ -109,12 +108,10 @@ PriorityQueue_enqueue(PriorityQueue_t* queue, const PriorityQueueItem_t* item) { queue->size = queue->size - 1U; const uint32_t current_size = queue->size; for (uint32_t i = lowest_priority_index; i < current_size; ++i) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&buffer[i * queue->element_size], &buffer[(i * queue->element_size) + queue->element_size], queue->element_size); queue->priority_array[i] = queue->priority_array[i + 1U]; } - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&buffer[queue->size * queue->element_size], item->element, queue->element_size); queue->priority_array[queue->size] = *(item->priority); queue->size = queue->size + 1U; @@ -130,12 +127,10 @@ PriorityQueue_dequeue(PriorityQueue_t* queue, uint8_t* element) { status = true; uint32_t highest_priority_index = GetHighestPriorityIndex(queue); uint8_t* buffer = queue->buffer; - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(element, &buffer[highest_priority_index * queue->element_size], queue->element_size); queue->size = queue->size - 1U; const uint32_t current_size = queue->size; for (uint32_t i = highest_priority_index; i < current_size; ++i) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&buffer[i * queue->element_size], &buffer[(i * queue->element_size) + queue->element_size], queue->element_size); queue->priority_array[i] = queue->priority_array[i + 1U]; diff --git a/Src/queue.c b/Src/queue.c index 789f42e..a6796ae 100644 --- a/Src/queue.c +++ b/Src/queue.c @@ -67,7 +67,6 @@ Queue_enqueue(Queue_t* queue, const uint8_t* element) { if ((queue != NULL_PTR) && (element != NULL_PTR)) { if (!Queue_full(queue)) { uint8_t* buffer = queue->buffer; - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&buffer[((queue->rear + 1U) % queue->capacity) * queue->element_size], element, queue->element_size); queue->rear = (queue->rear + 1U) % queue->capacity; queue->size = queue->size + 1U; @@ -83,7 +82,6 @@ Queue_dequeue(Queue_t* queue, uint8_t* element) { if ((queue != NULL_PTR) && (element != NULL_PTR)) { if (!Queue_empty(queue)) { const uint8_t* buffer = (const uint8_t*)queue->buffer; - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(element, &buffer[queue->front * queue->element_size], queue->element_size); queue->front = (queue->front + 1U) % queue->capacity; queue->size = queue->size - 1U; @@ -99,7 +97,6 @@ Queue_front(const Queue_t* queue, uint8_t* element) { if ((queue != NULL_PTR) && (element != NULL_PTR)) { if (!Queue_empty(queue)) { const uint8_t* buffer = (const uint8_t*)queue->buffer; - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(element, &buffer[queue->front * queue->element_size], queue->element_size); status = true; } @@ -113,7 +110,6 @@ Queue_rear(const Queue_t* queue, uint8_t* element) { if ((queue != NULL_PTR) && (element != NULL_PTR)) { if (!Queue_empty(queue)) { const uint8_t* buffer = (const uint8_t*)queue->buffer; - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(element, &buffer[queue->rear * queue->element_size], queue->element_size); status = true; } diff --git a/Src/sort/insertion_sort.c b/Src/sort/insertion_sort.c index 5c26c7a..1d7ce35 100644 --- a/Src/sort/insertion_sort.c +++ b/Src/sort/insertion_sort.c @@ -44,19 +44,16 @@ InsertionSort_sort(byte_t* buffer, int32_t number_of_elements, int32_t element_s byte_t* element = &max_element[0]; for (int32_t i = 1; i < number_of_elements; ++i) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(element, &elements[i * element_size], (size_t)element_size); int32_t j = i - 1; bool compare = compareFun(&elements[j * element_size], element); while ((j >= 0) && compare) { - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&elements[(j + 1) * element_size], &elements[j * element_size], (size_t)element_size); --j; compare = compareFun(&elements[j * element_size], element); } - // cppcheck-suppress misra-c2012-17.7; return value is not needed in this case memcpy(&elements[(j + 1) * element_size], element, (size_t)element_size); } } diff --git a/Src/sort/merge_sort.c b/Src/sort/merge_sort.c new file mode 100644 index 0000000..68ac3ed --- /dev/null +++ b/Src/sort/merge_sort.c @@ -0,0 +1,104 @@ +/**************************************************************************** + * + * Copyright (c) 2025 IMProject Development Team. All rights reserved. + * Authors: Juraj Ciberlin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name IMProject nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include "merge_sort.h" + +#include + +static void +MergeHalves(byte_t* buffer, int32_t element_size, int32_t left, int32_t mid, int32_t right, + bool (*compareFun)(void* first, void* second)) { + byte_t* elements = buffer; + byte_t left_half[MAX_HALVES_SIZE]; + byte_t right_half[MAX_HALVES_SIZE]; + int32_t i; + int32_t j; + int32_t k; + int32_t number_of_elements_1 = mid - left + 1; + int32_t number_of_elements_2 = right - mid; + + for (i = 0; i < number_of_elements_1; ++i) { + memcpy(&left_half[i * element_size], &elements[(left + i) * element_size], (size_t)element_size); + } + + for (j = 0; j < number_of_elements_2; ++j) { + memcpy(&right_half[j * element_size], &elements[(mid + 1 + j) * element_size], (size_t)element_size); + } + + i = 0; + j = 0; + k = left; + + while ((i < number_of_elements_1) && (j < number_of_elements_2)) { + bool compare = compareFun(&right_half[j * element_size], &left_half[i * element_size]); + if (compare) { + memcpy(&elements[k * element_size], &left_half[i * element_size], (size_t)element_size); + ++i; + } else { + memcpy(&elements[k * element_size], &right_half[j * element_size], (size_t)element_size); + ++j; + } + ++k; + } + + while (i < number_of_elements_1) { + memcpy(&elements[k * element_size], &left_half[i * element_size], (size_t)element_size); + ++i; + ++k; + } + + while (j < number_of_elements_2) { + memcpy(&elements[k * element_size], &right_half[j * element_size], (size_t)element_size); + ++j; + ++k; + } +} + +void +MergeSort_sort(byte_t* buffer, int32_t number_of_elements, int32_t element_size, + bool (*compareFun)(void* first, void* second)) { + byte_t* elements = buffer; + for (int32_t current_size = 1; current_size <= number_of_elements - 1; current_size *= 2) { + for (int32_t left = 0; left < number_of_elements - 1; left += (2 * current_size)) { + int32_t compare_first_1 = left + current_size - 1; + int32_t compare_second = number_of_elements - 1; + int32_t mid = (compare_first_1 < compare_second) ? compare_first_1 : compare_second; + + int32_t compare_first_2 = left + (2 * current_size) - 1; + int32_t right = (compare_first_2 < compare_second) ? compare_first_2 : compare_second; + + MergeHalves(elements, element_size, left, mid, right, compareFun); + } + } +} diff --git a/Tests/test_main.c b/Tests/test_main.c index 69065f4..773f47f 100644 --- a/Tests/test_main.c +++ b/Tests/test_main.c @@ -15,6 +15,7 @@ RunAllTests(void) { RUN_TEST_GROUP(InsertionSort); RUN_TEST_GROUP(Json); RUN_TEST_GROUP(Map); + RUN_TEST_GROUP(MergeSort); RUN_TEST_GROUP(PriorityQueue); RUN_TEST_GROUP(Queue); RUN_TEST_GROUP(Scheduler); diff --git a/Tests/test_merge_sort.c b/Tests/test_merge_sort.c new file mode 100644 index 0000000..90cd02e --- /dev/null +++ b/Tests/test_merge_sort.c @@ -0,0 +1,53 @@ +#include "merge_sort.h" + +#include "unity.h" +#include "unity_fixture.h" + +#include "helper/sort_functions.h" + +TEST_GROUP(MergeSort); + +TEST_SETUP(MergeSort) { +} + +TEST_TEAR_DOWN(MergeSort) { +} + +TEST_GROUP_RUNNER(MergeSort) { + RUN_TEST_CASE(MergeSort, MergeSort_int32); + RUN_TEST_CASE(MergeSort, MergeSort_float64); + RUN_TEST_CASE(MergeSort, MergeSort_uint64); +} + +TEST(MergeSort, MergeSort_int32) { + int32_t unsorted_array[] = {5, 2, 3, 1000000, 9, 10, 11, 8, 9, 100}; + const int32_t sorted_array[] = {2, 3, 5, 8, 9, 9, 10, 11, 100, 1000000}; + MergeSort_sort((byte_t*)unsorted_array, sizeof(unsorted_array) / sizeof(unsorted_array[0]), sizeof(unsorted_array[0]), + CompareInt32); + + for (uint32_t i = 0U; i < (sizeof(unsorted_array) / sizeof(unsorted_array[0])); ++i) { + TEST_ASSERT_EQUAL_INT32(sorted_array[i], unsorted_array[i]); + } +} + +TEST(MergeSort, MergeSort_float64) { + float64_t unsorted_array[] = {5.8, 2.2, 3.1, 1.1, 9.1, 10.3, 11.2, 8.4, 9.2, 100.9}; + const float64_t sorted_array[] = {1.1, 2.2, 3.1, 5.8, 8.4, 9.1, 9.2, 10.3, 11.2, 100.9}; + MergeSort_sort((byte_t*)unsorted_array, sizeof(unsorted_array) / sizeof(unsorted_array[0]), sizeof(unsorted_array[0]), + CompareFloat64); + + for (uint32_t i = 0U; i < (sizeof(unsorted_array) / sizeof(unsorted_array[0])); ++i) { + TEST_ASSERT_EQUAL_DOUBLE(sorted_array[i], unsorted_array[i]); + } +} + +TEST(MergeSort, MergeSort_uint64) { + uint64_t unsorted_array[] = {1111111U, 55555555U, 44444U, 10000000000000U, 212121U, 1111U, 1U, 2U, 5U, 3U}; + const uint64_t sorted_array[] = {1U, 2U, 3U, 5U, 1111U, 44444U, 212121U, 1111111U, 55555555U, 10000000000000U}; + MergeSort_sort((byte_t*)unsorted_array, sizeof(unsorted_array) / sizeof(unsorted_array[0]), sizeof(unsorted_array[0]), + CompareUint64); + + for (uint32_t i = 0U; i < (sizeof(unsorted_array) / sizeof(unsorted_array[0])); ++i) { + TEST_ASSERT_EQUAL_UINT64(sorted_array[i], unsorted_array[i]); + } +}