From e3ce68f47a23fb5f52745425baec8feaad562cd5 Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Tue, 8 Apr 2025 22:44:06 +0530 Subject: [PATCH 1/5] added c++ backend for cocktail sort --- .../_backend/cpp/algorithms/algorithms.cpp | 2 + .../cpp/algorithms/quadratic_time_sort.hpp | 74 +++++++++++++++++++ .../linear_data_structures/algorithms.py | 4 + 3 files changed, 80 insertions(+) diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp index 33df78323..4bc5362a6 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp @@ -4,6 +4,8 @@ #include "misc_algorithms.hpp" static PyMethodDef algorithms_PyMethodDef[] = { + {"cocktail_shaker_sort", (PyCFunction) cocktail_shaker_sort, + METH_VARARGS | METH_KEYWORDS, ""}, {"quick_sort", (PyCFunction) quick_sort, METH_VARARGS | METH_KEYWORDS, ""}, {"bubble_sort", (PyCFunction) bubble_sort, diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp index 45e2d6dee..ef1609a11 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp @@ -185,4 +185,78 @@ static PyObject* insertion_sort(PyObject* self, PyObject* args, PyObject* kwds) } +static PyObject* cocktail_shaker_sort_impl(PyObject* array, size_t lower, size_t upper, PyObject* comp) { + bool is_sorted = false; + + while(!is_sorted){ + is_sorted = true; + + for(size_t i = lower; i < upper; i++){ + PyObject* i_PyObject = PyLong_FromSize_t(i); + PyObject* i1_PyObject = PyLong_FromSize_t(i+1); + if(_comp(PyObject_GetItem(array, i_PyObject), PyObject_GetItem(array, i1_PyObject), comp) != 1){ + PyObject* tmp = PyObject_GetItem(array, i1_PyObject); + PyObject_SetItem(array, i1_PyObject, PyObject_GetItem(array, i_PyObject)); + PyObject_SetItem(array, i_PyObject, tmp); + is_sorted = false; + } + } + + for (size_t i = upper - 1; i > lower; i--) { + PyObject* i_PyObject = PyLong_FromSize_t(i); + PyObject* i1_PyObject = PyLong_FromSize_t(i - 1); + if (_comp(PyObject_GetItem(array, i1_PyObject), PyObject_GetItem(array, i_PyObject), comp) != 1) { + PyObject* tmp = PyObject_GetItem(array, i1_PyObject); + PyObject_SetItem(array, i1_PyObject, PyObject_GetItem(array, i_PyObject)); + PyObject_SetItem(array, i_PyObject, tmp); + is_sorted = false; + } + } + } + return array; +} + +static PyObject* cocktail_shaker_sort(PyObject* self, PyObject* args, PyObject* kwds) { + PyObject *args0 = NULL, *start = NULL, *end = NULL; + PyObject *comp = NULL; + size_t lower, upper; + + args0 = PyObject_GetItem(args, PyZero); + int is_DynamicOneDimensionalArray = _check_type(args0, &DynamicOneDimensionalArrayType); + int is_OneDimensionalArray = _check_type(args0, &OneDimensionalArrayType); + if (!is_DynamicOneDimensionalArray && !is_OneDimensionalArray) { + raise_exception_if_not_array(args0); + return NULL; + } + + comp = PyObject_GetItem(kwds, PyUnicode_FromString("comp")); + if (comp == NULL) { + PyErr_Clear(); + } + + start = PyObject_GetItem(kwds, PyUnicode_FromString("start")); + if (start == NULL) { + PyErr_Clear(); + lower = 0; + } else { + lower = PyLong_AsSize_t(start); + } + + end = PyObject_GetItem(kwds, PyUnicode_FromString("end")); + if (end == NULL) { + PyErr_Clear(); + upper = PyObject_Length(args0) - 1; + } else { + upper = PyLong_AsSize_t(end); + } + + args0 = cocktail_shaker_sort_impl(args0, lower, upper, comp); + if (is_DynamicOneDimensionalArray) { + PyObject_CallMethod(args0, "_modify", "O", Py_True); + } + + Py_INCREF(args0); + return args0; +} + #endif diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index 8b7295833..9177ba78d 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -658,6 +658,10 @@ def cocktail_shaker_sort(array: Array, **kwargs) -> Array: .. [1] https://en.wikipedia.org/wiki/Cocktail_shaker_sort """ + backend = kwargs.pop("backend", Backend.PYTHON) + if backend == Backend.CPP: + return _algorithms.cocktail_shaker_sort(array, **kwargs) + raise_if_backend_is_not_python( cocktail_shaker_sort, kwargs.get('backend', Backend.PYTHON)) def swap(i, j): From 5bb931e6a0a2179dbbfb49b14d5c20bf12d14848 Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 9 Apr 2025 15:02:27 +0530 Subject: [PATCH 2/5] added test for C++ Backend for cocktail shaker sort --- pydatastructs/linear_data_structures/tests/test_algorithms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index dc6336b48..31b1bb81b 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -110,6 +110,7 @@ def test_counting_sort(): def test_cocktail_shaker_sort(): _test_common_sort(cocktail_shaker_sort) + _test_common_sort(cocktail_shaker_sort, backend=Backend.CPP) def test_quick_sort(): _test_common_sort(quick_sort) From 1c9c67c7b40a7ca0f47798803d0c664dd5cb4086 Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 9 Apr 2025 15:27:41 +0530 Subject: [PATCH 3/5] added test for C++ Backend for cocktail shaker sort --- .../_backend/cpp/algorithms/algorithms.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp index 4bc5362a6..2663c9115 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp @@ -5,7 +5,7 @@ static PyMethodDef algorithms_PyMethodDef[] = { {"cocktail_shaker_sort", (PyCFunction) cocktail_shaker_sort, - METH_VARARGS | METH_KEYWORDS, ""}, + METH_VARARGS | METH_KEYWORDS, ""}, {"quick_sort", (PyCFunction) quick_sort, METH_VARARGS | METH_KEYWORDS, ""}, {"bubble_sort", (PyCFunction) bubble_sort, From 96b932b1926480b1c63fbab8a2779fe8fd297854 Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 9 Apr 2025 16:01:49 +0530 Subject: [PATCH 4/5] added seperate function to test cocktail shaker sort --- .../tests/test_algorithms.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index 31b1bb81b..6e8648680 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -109,8 +109,25 @@ def test_counting_sort(): assert counting_sort(arr)._data == expected_arr def test_cocktail_shaker_sort(): - _test_common_sort(cocktail_shaker_sort) - _test_common_sort(cocktail_shaker_sort, backend=Backend.CPP) + array = OneDimensionalArray(int, [5, 3, 8, 6, 2]) + cocktail_shaker_sort(array, backend=Backend.PYTHON) + + assert array._data[:5] == [2, 3, 5, 6, 8] + array = OneDimensionalArray(int, [10, 1, 7, 4, 3]) + cocktail_shaker_sort(array, backend=Backend.CPP) + assert array._data[:5] == [1, 3, 4, 7, 10] + + array = OneDimensionalArray(int, [20, 15, 10, 5, 0]) + cocktail_shaker_sort(array, backend=Backend.CPP) + assert array._data[:5] == [0, 5, 10, 15, 20] + + array = OneDimensionalArray(int, [1, 2, 3, 4, 5]) + cocktail_shaker_sort(array, backend=Backend.CPP) + assert array._data[:5] == [1, 2, 3, 4, 5] + + array = OneDimensionalArray(int, [5, 4, 3, 2, 1]) + cocktail_shaker_sort(array, backend=Backend.CPP) + assert array._data[:5] == [1, 2, 3, 4, 5] def test_quick_sort(): _test_common_sort(quick_sort) From b6004ad6573d724d0b68cb58be373947805847de Mon Sep 17 00:00:00 2001 From: arvinder004 Date: Wed, 16 Apr 2025 08:21:41 +0530 Subject: [PATCH 5/5] added benchmarks for cocktail shaker sort --- .../tests/benchmarks/test_algorithms.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py b/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py index da676c69c..4d9574718 100644 --- a/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py @@ -1,7 +1,7 @@ import random, timeit, functools, os, pytest from pydatastructs import (OneDimensionalArray, Backend, DynamicOneDimensionalArray, quick_sort, bubble_sort, selection_sort, - insertion_sort, is_ordered, linear_search, binary_search, jump_search) + insertion_sort, is_ordered, linear_search, binary_search, jump_search, cocktail_shaker_sort) def _test_common_sort(sort, **kwargs): cpp = Backend.CPP @@ -52,6 +52,10 @@ def test_insertion_sort(): _test_common_sort(insertion_sort, size=2000) +@pytest.mark.xfail +def test_cocktail_shaker_sort(): + _test_common_sort(cocktail_shaker_sort, size=2000) + @pytest.mark.xfail def test_is_ordered(): cpp = Backend.CPP