Skip to content

Commit 0649f8b

Browse files
authored
Add CPP version of selection sort (#542)
1 parent 674fc62 commit 0649f8b

File tree

6 files changed

+78
-7
lines changed

6 files changed

+78
-7
lines changed

pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#include <Python.h>
22
#include "quick_sort.hpp"
3-
#include "bubble_sort.hpp"
3+
#include "n2_square_sort.hpp"
44

55
static PyMethodDef algorithms_PyMethodDef[] = {
66
{"quick_sort", (PyCFunction) quick_sort,
77
METH_VARARGS | METH_KEYWORDS, ""},
88
{"bubble_sort", (PyCFunction) bubble_sort,
99
METH_VARARGS | METH_KEYWORDS, ""},
10+
{"selection_sort", (PyCFunction) selection_sort,
11+
METH_VARARGS | METH_KEYWORDS, ""},
1012
{NULL, NULL, 0, NULL} /* Sentinel */
1113
};
1214

pydatastructs/linear_data_structures/_backend/cpp/algorithms/bubble_sort.hpp renamed to pydatastructs/linear_data_structures/_backend/cpp/algorithms/n2_square_sort.hpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_BUBBLE_SORT_HPP
2-
#define LINEAR_DATA_STRUCTURES_ALGORITHMS_BUBBLE_SORT_HPP
1+
#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_N2_SQUARE_SORT_HPP
2+
#define LINEAR_DATA_STRUCTURES_ALGORITHMS_N2_SQUARE_SORT_HPP
33

44
#define PY_SSIZE_T_CLEAN
55
#include <Python.h>
66
#include "../arrays/OneDimensionalArray.hpp"
77
#include "../arrays/DynamicOneDimensionalArray.hpp"
88
#include "../../../../utils/_backend/cpp/utils.hpp"
99

10+
// Bubble Sort
1011
static PyObject* bubble_sort_impl(PyObject* array, size_t lower, size_t upper,
1112
PyObject* comp, size_t arr_length) {
1213
for (size_t i = 0; i < arr_length - 1; i++) {
@@ -65,4 +66,65 @@ static PyObject* bubble_sort(PyObject* self, PyObject* args, PyObject* kwds) {
6566
return args0;
6667
}
6768

69+
70+
// Selection Sort
71+
static PyObject* selection_sort_impl(PyObject* array, size_t lower, size_t upper,
72+
PyObject* comp) {
73+
for (size_t i = lower; i < upper + 1; i++) {
74+
PyObject* j_min_PyObject = PyLong_FromSize_t(i);
75+
PyObject* i_PyObject = PyLong_FromSize_t(i);
76+
for (size_t j = i + 1; j < upper + 1; j++) {
77+
PyObject* j_PyObject = PyLong_FromSize_t(j);
78+
if( _comp(PyObject_GetItem(array, j_min_PyObject),
79+
PyObject_GetItem(array, j_PyObject), comp) != 1 ) {
80+
j_min_PyObject = j_PyObject;
81+
}
82+
}
83+
PyObject* tmp = PyObject_GetItem(array, j_min_PyObject);
84+
PyObject_SetItem(array, j_min_PyObject,
85+
PyObject_GetItem(array, i_PyObject));
86+
PyObject_SetItem(array, i_PyObject, tmp);
87+
}
88+
return array;
89+
}
90+
91+
92+
static PyObject* selection_sort(PyObject* self, PyObject* args, PyObject* kwds) {
93+
PyObject *args0 = NULL, *start = NULL, *end = NULL;
94+
PyObject *comp = NULL, *pick_pivot_element = NULL;
95+
size_t lower, upper;
96+
args0 = PyObject_GetItem(args, PyZero);
97+
int is_DynamicOneDimensionalArray = _check_type(args0, &DynamicOneDimensionalArrayType);
98+
int is_OneDimensionalArray = _check_type(args0, &OneDimensionalArrayType);
99+
if( !is_DynamicOneDimensionalArray && !is_OneDimensionalArray ) {
100+
raise_exception_if_not_array(args0);
101+
return NULL;
102+
}
103+
comp = PyObject_GetItem(kwds, PyUnicode_FromString("comp"));
104+
if( comp == NULL ) {
105+
PyErr_Clear();
106+
}
107+
start = PyObject_GetItem(kwds, PyUnicode_FromString("start"));
108+
if( start == NULL ) {
109+
PyErr_Clear();
110+
lower = 0;
111+
} else {
112+
lower = PyLong_AsSize_t(start);
113+
}
114+
end = PyObject_GetItem(kwds, PyUnicode_FromString("end"));
115+
if( end == NULL ) {
116+
PyErr_Clear();
117+
upper = PyObject_Length(args0) - 1;
118+
} else {
119+
upper = PyLong_AsSize_t(end);
120+
}
121+
122+
args0 = selection_sort_impl(args0, lower, upper, comp);
123+
if( is_DynamicOneDimensionalArray ) {
124+
PyObject_CallMethod(args0, "_modify", "O", Py_True);
125+
}
126+
Py_INCREF(args0);
127+
return args0;
128+
}
129+
68130
#endif

pydatastructs/linear_data_structures/algorithms.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,8 +1431,9 @@ def selection_sort(array, **kwargs):
14311431
14321432
.. [1] https://en.wikipedia.org/wiki/Selection_sort
14331433
"""
1434-
raise_if_backend_is_not_python(
1435-
selection_sort, kwargs.get('backend', Backend.PYTHON))
1434+
backend = kwargs.pop("backend", Backend.PYTHON)
1435+
if backend == Backend.CPP:
1436+
return _algorithms.bubble_sort(array, **kwargs)
14361437
start = kwargs.get('start', 0)
14371438
end = kwargs.get('end', len(array) - 1)
14381439
comp = kwargs.get('comp', lambda u, v: u <= v)

pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import random, timeit, functools, os, pytest
22
from pydatastructs import (OneDimensionalArray, Backend,
3-
DynamicOneDimensionalArray, quick_sort, bubble_sort)
3+
DynamicOneDimensionalArray, quick_sort, bubble_sort, selection_sort)
44

55
def _test_common_sort(sort, **kwargs):
66
cpp = Backend.CPP
@@ -39,3 +39,8 @@ def test_quick_sort():
3939
@pytest.mark.xfail
4040
def test_bubble_sort():
4141
_test_common_sort(bubble_sort, size=2000)
42+
43+
44+
@pytest.mark.xfail
45+
def test_selection_sort():
46+
_test_common_sort(selection_sort, size=2000)

pydatastructs/linear_data_structures/tests/test_algorithms.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ def test_bubble_sort():
121121

122122
def test_selection_sort():
123123
_test_common_sort(selection_sort)
124+
_test_common_sort(selection_sort, backend=Backend.CPP)
124125

125126
def test_insertion_sort():
126127
_test_common_sort(insertion_sort)

pydatastructs/utils/testing_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414

1515
# Root pydatastructs directory
16-
ROOT_DIR = pathlib.Path(os.path.abspath(__file__)).parents[2]
16+
ROOT_DIR = pathlib.Path(os.path.abspath(__file__)).parents[1]
1717

1818

1919
SKIP_FILES = ['testing_util.py']

0 commit comments

Comments
 (0)