From d603a3450f14161d0f1bc33c3a3c6b7540224435 Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Sun, 16 Nov 2025 19:12:43 +0300 Subject: [PATCH 01/11] Implemented a heapsort algorithm --- src/Tests/src/heapsort.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/Tests/src/heapsort.py diff --git a/src/Tests/src/heapsort.py b/src/Tests/src/heapsort.py new file mode 100644 index 0000000..22ea0fc --- /dev/null +++ b/src/Tests/src/heapsort.py @@ -0,0 +1,28 @@ +def heapsort(array: list) -> list: + length = len(array) + + for index in range(length // 2 - 1, -1, -1): + heapify(array, length, index) + + for index in range(length - 1, 0, -1): + array[0], array[index] = array[index], array[0] + heapify(array, index, 0) + + return array + +def heapify(array: list, length: int, index: int) -> None: + largest = index + left = 2 * index + 1 + right = 2 * index + 2 + + if left < length and array[left] > array[largest]: + largest = left + + if right < length and array[right] > array[largest]: + largest = right + + if largest != index: + array[index], array[largest] = array[largest], array[index] + heapify(array, length, largest) + + From 0d2b5e6fb16a9b4680b19e49bf80ff2480bb842d Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Sun, 16 Nov 2025 19:13:41 +0300 Subject: [PATCH 02/11] Implemented unit tests --- src/Tests/tests/__init__.py | 0 src/Tests/tests/test_heapsort.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/Tests/tests/__init__.py create mode 100644 src/Tests/tests/test_heapsort.py diff --git a/src/Tests/tests/__init__.py b/src/Tests/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/Tests/tests/test_heapsort.py b/src/Tests/tests/test_heapsort.py new file mode 100644 index 0000000..ee86605 --- /dev/null +++ b/src/Tests/tests/test_heapsort.py @@ -0,0 +1,29 @@ +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from src.heapsort import heapsort + +def test_sort_empty_list(): + assert heapsort([]) == [] + +def test_sort_single_element(): + assert heapsort([5]) == [5] + assert heapsort([-1]) == [-1] + assert heapsort([0]) == [0] + +def test_sort_positive_numbers(): + assert heapsort([3, 1, 4, 1, 5]) == [1, 1, 3, 4, 5] + assert heapsort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] + +def test_sort_negative_numbers(): + assert heapsort([-3, -1, -4, -1, -5]) == [-5, -4, -3, -1, -1] + assert heapsort([-1, -2, -3, -4, -5]) == [-5, -4, -3, -2, -1] + +def test_sort_mixed_numbers(): + assert heapsort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] + assert heapsort([0, -1, 1, -2, 2]) == [-2, -1, 0, 1, 2] + +def test_sort_strings(): + assert heapsort(['banana', 'apple', 'cherry']) == ['apple', 'banana', 'cherry'] + assert heapsort(['z', 'a', 'm']) == ['a', 'm', 'z'] From b448d0704f293ec9f23c6ea58aff0267c363f422 Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Sun, 16 Nov 2025 19:15:35 +0300 Subject: [PATCH 03/11] Implemented a bubble sort algorithm --- src/Tests/src/bubblesort.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/Tests/src/bubblesort.py diff --git a/src/Tests/src/bubblesort.py b/src/Tests/src/bubblesort.py new file mode 100644 index 0000000..6b5445e --- /dev/null +++ b/src/Tests/src/bubblesort.py @@ -0,0 +1,9 @@ +def bubble_sort(array: list) -> list: + for index_1 in range(len(array)): + for index_2 in range(index_1+1, len(array)): + if array[index_1] > array[index_2]: + array[index_1], array[index_2] = array[index_2], array[index_1] + return array + + +print(bubble_sort([4, 1, -4, 3, 2, 9])) \ No newline at end of file From ef2b7ac8e7d1eaf2f1f99cc521cce9bf42569278 Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Sun, 16 Nov 2025 19:21:55 +0300 Subject: [PATCH 04/11] Implemented a monkey sort algorithm --- src/Tests/src/monkeysort.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/Tests/src/monkeysort.py diff --git a/src/Tests/src/monkeysort.py b/src/Tests/src/monkeysort.py new file mode 100644 index 0000000..23e1ff3 --- /dev/null +++ b/src/Tests/src/monkeysort.py @@ -0,0 +1,14 @@ +from random import shuffle + +def monekey_sort(array: list) -> list: + while True: + for index in range(1, len(array)): + if array[index-1] > array[index]: + shuffle(array) + break + else: + return array + + + +print(monekey_sort([-4, 100, -30, 21, 42, -9, 0, 5])) \ No newline at end of file From 30c460dd44ba116c9a877dd468e77dc7fecae0f4 Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Sun, 16 Nov 2025 19:32:53 +0300 Subject: [PATCH 05/11] Small fixes is sorting functions --- src/Tests/src/bubblesort.py | 3 --- src/Tests/src/heapsort.py | 2 -- src/Tests/src/monkeysort.py | 5 +---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Tests/src/bubblesort.py b/src/Tests/src/bubblesort.py index 6b5445e..2ac9ac4 100644 --- a/src/Tests/src/bubblesort.py +++ b/src/Tests/src/bubblesort.py @@ -4,6 +4,3 @@ def bubble_sort(array: list) -> list: if array[index_1] > array[index_2]: array[index_1], array[index_2] = array[index_2], array[index_1] return array - - -print(bubble_sort([4, 1, -4, 3, 2, 9])) \ No newline at end of file diff --git a/src/Tests/src/heapsort.py b/src/Tests/src/heapsort.py index 22ea0fc..690aebf 100644 --- a/src/Tests/src/heapsort.py +++ b/src/Tests/src/heapsort.py @@ -24,5 +24,3 @@ def heapify(array: list, length: int, index: int) -> None: if largest != index: array[index], array[largest] = array[largest], array[index] heapify(array, length, largest) - - diff --git a/src/Tests/src/monkeysort.py b/src/Tests/src/monkeysort.py index 23e1ff3..646966c 100644 --- a/src/Tests/src/monkeysort.py +++ b/src/Tests/src/monkeysort.py @@ -8,7 +8,4 @@ def monekey_sort(array: list) -> list: break else: return array - - - -print(monekey_sort([-4, 100, -30, 21, 42, -9, 0, 5])) \ No newline at end of file + \ No newline at end of file From 748bb065dc2baa27151d0470b0e9f8c38eed974b Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Sun, 16 Nov 2025 19:36:40 +0300 Subject: [PATCH 06/11] Added a pytest configuration file --- src/Tests/pytest.ini | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/Tests/pytest.ini diff --git a/src/Tests/pytest.ini b/src/Tests/pytest.ini new file mode 100644 index 0000000..3b665da --- /dev/null +++ b/src/Tests/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +pythonpath = . .. src +testpaths = tests +addopts = -v --tb=short +python_files = test_*.py +python_classes = Test* +python_functions = test_* \ No newline at end of file From 931fe71caebd39418ead7a6c183a160084093c69 Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Sun, 16 Nov 2025 19:47:51 +0300 Subject: [PATCH 07/11] Renamed sorting functions --- src/Tests/src/bubblesort.py | 2 +- src/Tests/src/monkeysort.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tests/src/bubblesort.py b/src/Tests/src/bubblesort.py index 2ac9ac4..5bea8fb 100644 --- a/src/Tests/src/bubblesort.py +++ b/src/Tests/src/bubblesort.py @@ -1,4 +1,4 @@ -def bubble_sort(array: list) -> list: +def bubblesort(array: list) -> list: for index_1 in range(len(array)): for index_2 in range(index_1+1, len(array)): if array[index_1] > array[index_2]: diff --git a/src/Tests/src/monkeysort.py b/src/Tests/src/monkeysort.py index 646966c..7912dbd 100644 --- a/src/Tests/src/monkeysort.py +++ b/src/Tests/src/monkeysort.py @@ -1,6 +1,6 @@ from random import shuffle -def monekey_sort(array: list) -> list: +def monkeysort(array: list) -> list: while True: for index in range(1, len(array)): if array[index-1] > array[index]: From 03056a161bfef786f4c5d1d1bc1607e5915b9db6 Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Mon, 17 Nov 2025 00:12:21 +0300 Subject: [PATCH 08/11] Renamed names of functions --- src/Tests/src/{bubblesort.py => bubble_sort.py} | 2 +- src/Tests/src/{heapsort.py => heap_sort.py} | 2 +- src/Tests/src/monkeysort.py | 11 ----------- src/Tests/src/quick_sort.py | 10 ++++++++++ src/Tests/src/selection_sort.py | 10 ++++++++++ 5 files changed, 22 insertions(+), 13 deletions(-) rename src/Tests/src/{bubblesort.py => bubble_sort.py} (86%) rename src/Tests/src/{heapsort.py => heap_sort.py} (95%) delete mode 100644 src/Tests/src/monkeysort.py create mode 100644 src/Tests/src/quick_sort.py create mode 100644 src/Tests/src/selection_sort.py diff --git a/src/Tests/src/bubblesort.py b/src/Tests/src/bubble_sort.py similarity index 86% rename from src/Tests/src/bubblesort.py rename to src/Tests/src/bubble_sort.py index 5bea8fb..2ac9ac4 100644 --- a/src/Tests/src/bubblesort.py +++ b/src/Tests/src/bubble_sort.py @@ -1,4 +1,4 @@ -def bubblesort(array: list) -> list: +def bubble_sort(array: list) -> list: for index_1 in range(len(array)): for index_2 in range(index_1+1, len(array)): if array[index_1] > array[index_2]: diff --git a/src/Tests/src/heapsort.py b/src/Tests/src/heap_sort.py similarity index 95% rename from src/Tests/src/heapsort.py rename to src/Tests/src/heap_sort.py index 690aebf..a9708da 100644 --- a/src/Tests/src/heapsort.py +++ b/src/Tests/src/heap_sort.py @@ -1,4 +1,4 @@ -def heapsort(array: list) -> list: +def heap_sort(array: list) -> list: length = len(array) for index in range(length // 2 - 1, -1, -1): diff --git a/src/Tests/src/monkeysort.py b/src/Tests/src/monkeysort.py deleted file mode 100644 index 7912dbd..0000000 --- a/src/Tests/src/monkeysort.py +++ /dev/null @@ -1,11 +0,0 @@ -from random import shuffle - -def monkeysort(array: list) -> list: - while True: - for index in range(1, len(array)): - if array[index-1] > array[index]: - shuffle(array) - break - else: - return array - \ No newline at end of file diff --git a/src/Tests/src/quick_sort.py b/src/Tests/src/quick_sort.py new file mode 100644 index 0000000..32f549c --- /dev/null +++ b/src/Tests/src/quick_sort.py @@ -0,0 +1,10 @@ +def quick_sort(array: list) -> list: + if len(array) <= 1: + return array + + pivot = array[len(array) // 2] + left = [x for x in array if x < pivot] + middle = [x for x in array if x == pivot] + right = [x for x in array if x > pivot] + + return quick_sort(left) + middle + quick_sort(right) diff --git a/src/Tests/src/selection_sort.py b/src/Tests/src/selection_sort.py new file mode 100644 index 0000000..1bddf0b --- /dev/null +++ b/src/Tests/src/selection_sort.py @@ -0,0 +1,10 @@ +def selection_sort(array: list) -> list: + for index_1 in range(len(array)): + min_idx = index_1 + for index_2 in range(index_1+1, len(array)): + if array[index_2] < array[min_idx]: + min_idx = index_2 + + array[index_1], array[min_idx] = array[min_idx], array[index_1] + + return array From 668f564bcda06793e1763dda40f1f0088582a39b Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Mon, 17 Nov 2025 00:13:14 +0300 Subject: [PATCH 09/11] Changed name of a sorting function --- src/Tests/tests/test_heapsort.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Tests/tests/test_heapsort.py b/src/Tests/tests/test_heapsort.py index ee86605..e972dde 100644 --- a/src/Tests/tests/test_heapsort.py +++ b/src/Tests/tests/test_heapsort.py @@ -1,29 +1,29 @@ import sys -import os -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) -from src.heapsort import heapsort +sys.path.append('..') + +from Tests.src.heap_sort import heap_sort def test_sort_empty_list(): - assert heapsort([]) == [] + assert heap_sort([]) == [] def test_sort_single_element(): - assert heapsort([5]) == [5] - assert heapsort([-1]) == [-1] - assert heapsort([0]) == [0] + assert heap_sort([5]) == [5] + assert heap_sort([-1]) == [-1] + assert heap_sort([0]) == [0] def test_sort_positive_numbers(): - assert heapsort([3, 1, 4, 1, 5]) == [1, 1, 3, 4, 5] - assert heapsort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] + assert heap_sort([3, 1, 4, 1, 5]) == [1, 1, 3, 4, 5] + assert heap_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] def test_sort_negative_numbers(): - assert heapsort([-3, -1, -4, -1, -5]) == [-5, -4, -3, -1, -1] - assert heapsort([-1, -2, -3, -4, -5]) == [-5, -4, -3, -2, -1] + assert heap_sort([-3, -1, -4, -1, -5]) == [-5, -4, -3, -1, -1] + assert heap_sort([-1, -2, -3, -4, -5]) == [-5, -4, -3, -2, -1] def test_sort_mixed_numbers(): - assert heapsort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] - assert heapsort([0, -1, 1, -2, 2]) == [-2, -1, 0, 1, 2] + assert heap_sort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] + assert heap_sort([0, -1, 1, -2, 2]) == [-2, -1, 0, 1, 2] def test_sort_strings(): - assert heapsort(['banana', 'apple', 'cherry']) == ['apple', 'banana', 'cherry'] - assert heapsort(['z', 'a', 'm']) == ['a', 'm', 'z'] + assert heap_sort(['banana', 'apple', 'cherry']) == ['apple', 'banana', 'cherry'] + assert heap_sort(['z', 'a', 'm']) == ['a', 'm', 'z'] From d2ee48386b1b63d3d9301d417d308148002e925f Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Mon, 17 Nov 2025 01:02:17 +0300 Subject: [PATCH 10/11] Small fixes --- src/Tests/src/bubble_sort.py | 1 + src/Tests/tests/test_heapsort.py | 16 ++++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Tests/src/bubble_sort.py b/src/Tests/src/bubble_sort.py index 2ac9ac4..fea342a 100644 --- a/src/Tests/src/bubble_sort.py +++ b/src/Tests/src/bubble_sort.py @@ -3,4 +3,5 @@ def bubble_sort(array: list) -> list: for index_2 in range(index_1+1, len(array)): if array[index_1] > array[index_2]: array[index_1], array[index_2] = array[index_2], array[index_1] + return array diff --git a/src/Tests/tests/test_heapsort.py b/src/Tests/tests/test_heapsort.py index e972dde..4c637de 100644 --- a/src/Tests/tests/test_heapsort.py +++ b/src/Tests/tests/test_heapsort.py @@ -1,29 +1,25 @@ -import sys - -sys.path.append('..') - from Tests.src.heap_sort import heap_sort -def test_sort_empty_list(): +def test_sort_empty_list() -> None: assert heap_sort([]) == [] -def test_sort_single_element(): +def test_sort_single_element() -> None: assert heap_sort([5]) == [5] assert heap_sort([-1]) == [-1] assert heap_sort([0]) == [0] -def test_sort_positive_numbers(): +def test_sort_positive_numbers() -> None: assert heap_sort([3, 1, 4, 1, 5]) == [1, 1, 3, 4, 5] assert heap_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] -def test_sort_negative_numbers(): +def test_sort_negative_numbers() -> None: assert heap_sort([-3, -1, -4, -1, -5]) == [-5, -4, -3, -1, -1] assert heap_sort([-1, -2, -3, -4, -5]) == [-5, -4, -3, -2, -1] -def test_sort_mixed_numbers(): +def test_sort_mixed_numbers() -> None: assert heap_sort([3, -1, 0, -2, 5]) == [-2, -1, 0, 3, 5] assert heap_sort([0, -1, 1, -2, 2]) == [-2, -1, 0, 1, 2] -def test_sort_strings(): +def test_sort_strings() -> None: assert heap_sort(['banana', 'apple', 'cherry']) == ['apple', 'banana', 'cherry'] assert heap_sort(['z', 'a', 'm']) == ['a', 'm', 'z'] From 26b98bf4a8dadcc95f058a3b229c881431dd3647 Mon Sep 17 00:00:00 2001 From: Pavel Kuliaka Date: Mon, 17 Nov 2025 01:02:46 +0300 Subject: [PATCH 11/11] Implemented property based tests --- src/Tests/tests/test_property_based.py | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/Tests/tests/test_property_based.py diff --git a/src/Tests/tests/test_property_based.py b/src/Tests/tests/test_property_based.py new file mode 100644 index 0000000..f430785 --- /dev/null +++ b/src/Tests/tests/test_property_based.py @@ -0,0 +1,70 @@ +import pytest +import random +from collections import Counter + +from src.heap_sort import heap_sort +from src.quick_sort import quick_sort +from src.bubble_sort import bubble_sort +from src.selection_sort import selection_sort + +SORTING_FUNCTIONS = [ + heap_sort, + quick_sort, + bubble_sort, + selection_sort +] + +@pytest.fixture +def unsorted_lists() -> list[list]: + sizes = [5, 10, 15, 20, 30, 50] + lists = [] + for size in sizes: + array = [] + for _ in range(size): + array.append(random.randint(-100, 100)) + lists.append(array) + + return lists + +@pytest.mark.parametrize("sorting_function", SORTING_FUNCTIONS) +def test_orderliness(sorting_function, unsorted_lists) -> None: + for unsorted_list in unsorted_lists: + sorted_array = sorting_function(unsorted_list) + for index in range(1, len(sorted_array)): + assert sorted_array[index] >= sorted_array[index-1] + +@pytest.mark.parametrize("sorting_function", SORTING_FUNCTIONS) +def test_elements_sameness(sorting_function, unsorted_lists) -> None: + for unsorted_list in unsorted_lists: + sorted_list = sorting_function(unsorted_list) + assert Counter(sorted_list) == Counter(unsorted_list) + +@pytest.mark.parametrize("sorting_function", SORTING_FUNCTIONS) +def test_idempotency(sorting_function, unsorted_lists): + for unsorted_list in unsorted_lists: + first_sort = sorting_function(unsorted_list.copy()) + second_sort = sorting_function(first_sort.copy()) + + assert first_sort == second_sort + +@pytest.fixture(params=[ + [], + [1], + [1, 1, 1], + [1, 2, 3, 4, 5], + [5, 4, 3, 2, 1], + [1, 3, 2, 3, 1], + [-1, -5, 2, -3, 0], + [100, -100, 0, 50, -50], + [1] * 100, + list(range(100, 0, -1)), +]) +def edge_case_list(request): + return request.param + +@pytest.mark.parametrize("sorting_function", SORTING_FUNCTIONS) +def test_edge_cases(sorting_function, edge_case_list): + original = edge_case_list.copy() + sorted_list = sorting_function(edge_case_list) + + assert sorted_list == sorted(original)