From bfd3be73c78af421cabf597bd642f66bd1e61c52 Mon Sep 17 00:00:00 2001 From: Harshita Kalani Date: Thu, 16 Oct 2025 09:20:05 +0530 Subject: [PATCH] feat: add llvm implementation of quick_sort --- .../_backend/cpp/algorithms/algorithms.cpp | 2 + .../cpp/algorithms/llvm_algorithms.py | 175 ++++++ .../cpp/algorithms/quadratic_time_sort.hpp | 1 + .../_backend/cpp/algorithms/quick_sort.hpp | 545 ++++++++++++++++++ .../linear_data_structures/algorithms.py | 2 + .../tests/test_algorithms.py | 1 + 6 files changed, 726 insertions(+) diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp index 2ce135b92..eb37b2716 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp @@ -6,6 +6,8 @@ static PyMethodDef algorithms_PyMethodDef[] = { {"quick_sort", (PyCFunction) quick_sort, METH_VARARGS | METH_KEYWORDS, ""}, + {"quick_sort_llvm", (PyCFunction)quick_sort_llvm, + METH_VARARGS | METH_KEYWORDS, ""}, {"bubble_sort", (PyCFunction) bubble_sort, METH_VARARGS | METH_KEYWORDS, ""}, {"bubble_sort_llvm", (PyCFunction)bubble_sort_llvm, diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/llvm_algorithms.py b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/llvm_algorithms.py index 24b14609d..08f25f550 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/llvm_algorithms.py +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/llvm_algorithms.py @@ -41,6 +41,14 @@ def get_bubble_sort_ptr(dtype: str) -> int: return _materialize(dtype) + +def get_quick_sort_ptr(dtype: str) -> int: + dtype = dtype.lower().strip() + if dtype not in _SUPPORTED: + raise ValueError(f"Unsupported dtype '{dtype}'. Supported: {list(_SUPPORTED)}") + + return _materialize_quick(dtype) + def _build_bubble_sort_ir(dtype: str) -> str: if dtype not in _SUPPORTED: raise ValueError(f"Unsupported dtype '{dtype}'. Supported: {list(_SUPPORTED)}") @@ -131,6 +139,134 @@ def _build_bubble_sort_ir(dtype: str) -> str: return str(mod) + +def _build_quick_sort_ir(dtype: str) -> str: + if dtype not in _SUPPORTED: + raise ValueError(f"Unsupported dtype '{dtype}'. Supported: {list(_SUPPORTED)}") + + T, _ = _SUPPORTED[dtype] + i32 = ir.IntType(32) + i64 = ir.IntType(64) + + mod = ir.Module(name=f"quick_sort_{dtype}_module") + fn_name = f"quick_sort_{dtype}" + + # void quick_sort(T* arr, int32 low, int32 high) + fn_ty = ir.FunctionType(ir.VoidType(), [T.as_pointer(), i32, i32]) + fn = ir.Function(mod, fn_ty, name=fn_name) + arr, low, high = fn.args + arr.name, low.name, high.name = "arr", "low", "high" + + entry = fn.append_basic_block("entry") + part = fn.append_basic_block("partition") + exit = fn.append_basic_block("exit") + + b = ir.IRBuilder(entry) + + # if (low < high) + cond = b.icmp_signed("<", low, high) + b.cbranch(cond, part, exit) + + # --- Partition block + b.position_at_end(part) + + # pivot = arr[high] + high_64 = b.sext(high, i64) + pivot_ptr = b.gep(arr, [high_64]) + pivot = b.load(pivot_ptr, name="pivot") + + # i = low - 1 + i = b.alloca(i32, name="i") + i_init = b.sub(low, ir.Constant(i32, 1)) + b.store(i_init, i) + + # j = low + j = b.alloca(i32, name="j") + b.store(low, j) + + loop = fn.append_basic_block("loop") + after_loop = fn.append_basic_block("after_loop") + body = fn.append_basic_block("body") + swap = fn.append_basic_block("swap") + skip_swap = fn.append_basic_block("skip_swap") + + b.branch(loop) + + # --- Loop: while (j < high) + b.position_at_end(loop) + j_val = b.load(j) + cond = b.icmp_signed("<", j_val, high) + b.cbranch(cond, body, after_loop) + + # --- Body + b.position_at_end(body) + j64 = b.sext(j_val, i64) + elem_ptr = b.gep(arr, [j64]) + elem = b.load(elem_ptr, name="elem") + + if isinstance(T, ir.IntType): + cmp = b.icmp_signed("<=", elem, pivot) + else: + cmp = b.fcmp_ordered("<=", elem, pivot, fastmath=True) + + b.cbranch(cmp, swap, skip_swap) + + # --- Swap block + b.position_at_end(swap) + i_val = b.load(i) + i_next = b.add(i_val, ir.Constant(i32, 1)) + b.store(i_next, i) + + i64v = b.sext(i_next, i64) + iptr = b.gep(arr, [i64v]) + ival = b.load(iptr) + # swap arr[i] and arr[j] + b.store(elem, iptr) + b.store(ival, elem_ptr) + + b.branch(skip_swap) + + # --- Skip swap + b.position_at_end(skip_swap) + j_next = b.add(j_val, ir.Constant(i32, 1)) + b.store(j_next, j) + b.branch(loop) + + # --- After loop + b.position_at_end(after_loop) + i_val = b.load(i) + i_plus1 = b.add(i_val, ir.Constant(i32, 1)) + + i64v = b.sext(i_plus1, i64) + iptr = b.gep(arr, [i64v]) + ival = b.load(iptr) + + # swap arr[i+1] and arr[high] + b.store(pivot, iptr) + b.store(ival, pivot_ptr) + + # Now i+1 is the partition index + pi = i_plus1 + + # Recursive calls: + # quick_sort(arr, low, pi - 1) + low_call = low + high_call1 = b.sub(pi, ir.Constant(i32, 1)) + b.call(fn, [arr, low_call, high_call1]) + + # quick_sort(arr, pi + 1, high) + low_call2 = b.add(pi, ir.Constant(i32, 1)) + high_call2 = high + b.call(fn, [arr, low_call2, high_call2]) + + b.branch(exit) + + # --- Exit + b.position_at_end(exit) + b.ret_void() + + return str(mod) + def _materialize(dtype: str) -> int: _ensure_target_machine() @@ -167,3 +303,42 @@ def _materialize(dtype: str) -> int: except Exception as e: raise RuntimeError(f"Failed to materialize function for dtype {dtype}: {e}") + + +def _materialize_quick(dtype: str) -> int: + _ensure_target_machine() + + key = f"quick_{dtype}" + if key in _fn_ptr_cache: + return _fn_ptr_cache[key] + + try: + llvm_ir = _build_quick_sort_ir(dtype) + mod = binding.parse_assembly(llvm_ir) + mod.verify() + + try: + pm = binding.ModulePassManager() + pm.add_instruction_combining_pass() + pm.add_reassociate_pass() + pm.add_gvn_pass() + pm.add_cfg_simplification_pass() + pm.run(mod) + except AttributeError: + pass + + engine = binding.create_mcjit_compiler(mod, _target_machine) + engine.finalize_object() + engine.run_static_constructors() + + addr = engine.get_function_address(f"quick_sort_{dtype}") + if not addr: + raise RuntimeError(f"Failed to get address for quick_sort_{dtype}") + + _fn_ptr_cache[key] = addr + _engines[key] = engine + + return addr + + except Exception as e: + raise RuntimeError(f"Failed to materialize quick sort function for dtype {dtype}: {e}") 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 210382455..1ee89c186 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 @@ -612,6 +612,7 @@ static PyObject* bubble_sort_llvm(PyObject* self, PyObject* args, PyObject* kwds Py_INCREF(arr_obj); return arr_obj; } + // Selection Sort static PyObject* selection_sort_impl(PyObject* array, size_t lower, size_t upper, PyObject* comp) { diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quick_sort.hpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quick_sort.hpp index f3ad0ed07..5c0e977da 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quick_sort.hpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quick_sort.hpp @@ -116,4 +116,549 @@ static PyObject* quick_sort(PyObject* self, PyObject* args, PyObject* kwds) { return args0; } +static PyObject* quick_sort_llvm(PyObject* self, PyObject* args, PyObject* kwds) { + static const char* kwlist[] = {"arr", "start", "end", "comp", "dtype", NULL}; + PyObject* arr_obj = NULL; + PyObject* start_obj = NULL; + PyObject* end_obj = NULL; + PyObject* comp_obj = NULL; + const char* dtype_cstr = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOs", (char**)kwlist, + &arr_obj, &start_obj, &end_obj, &comp_obj, &dtype_cstr)) { + return NULL; + } + + Py_ssize_t arr_len_ssize = PyObject_Length(arr_obj); + + size_t arr_len = (size_t)arr_len_ssize; + + bool is_dynamic_array = false; + PyObject* last_pos_attr = PyUnicode_FromString("_last_pos_filled"); + PyObject* num_attr = PyUnicode_FromString("_num"); + + if (last_pos_attr && num_attr && PyObject_HasAttr(arr_obj, last_pos_attr) && PyObject_HasAttr(arr_obj, num_attr)) { + is_dynamic_array = true; + } + + Py_XDECREF(last_pos_attr); + Py_XDECREF(num_attr); + + if (is_dynamic_array) { + PyObject* size_attr = PyUnicode_FromString("_size"); + if (size_attr && PyObject_HasAttr(arr_obj, size_attr)) { + PyObject* size_obj = PyObject_GetAttr(arr_obj, size_attr); + if (size_obj && PyLong_Check(size_obj)) { + Py_ssize_t size_val = PyLong_AsSsize_t(size_obj); + if (size_val >= 0) { + arr_len = (size_t)size_val; + } + } + Py_XDECREF(size_obj); + } + Py_XDECREF(size_attr); + } + + if (arr_len == 0) { + Py_INCREF(arr_obj); + return arr_obj; + } + + size_t lower = 0; + size_t upper = arr_len - 1; + + if (start_obj && start_obj != Py_None) { + Py_ssize_t start_val = PyLong_AsSsize_t(start_obj); + if (PyErr_Occurred()) return NULL; + lower = (size_t)start_val; + } + + if (end_obj && end_obj != Py_None) { + Py_ssize_t end_val = PyLong_AsSsize_t(end_obj); + if (PyErr_Occurred()) return NULL; + upper = (size_t)end_val; + } + + if (upper < lower || lower >= arr_len) { + Py_INCREF(arr_obj); + return arr_obj; + } + + if (upper >= arr_len) { + upper = arr_len - 1; + } + + if (comp_obj && comp_obj != Py_None) { + PyErr_SetString(PyExc_NotImplementedError, "LLVM backend does not support custom 'comp'."); + return NULL; + } + + std::string dtype = (dtype_cstr && *dtype_cstr) ? std::string(dtype_cstr) : std::string(); + auto infer_dtype = [&](PyObject* x) -> std::string { + if (x == Py_None) return ""; + if (PyFloat_Check(x)) return "float64"; + if (PyLong_Check(x)) return "int64"; + return "float64"; + }; + + std::vector non_none_values; + size_t none_count = 0; + const size_t N = upper - lower + 1; + + non_none_values.reserve(N); + + for (size_t i = 0; i < N; i++) { + size_t actual_index = lower + i; + + if (actual_index >= arr_len) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + PyErr_Format(PyExc_IndexError, "Index %zu out of bounds (array length: %zu)", + actual_index, arr_len); + return NULL; + } + + PyObject* item = NULL; + + if (PySequence_Check(arr_obj)) { + item = PySequence_GetItem(arr_obj, (Py_ssize_t)actual_index); + } else { + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (index_obj) { + item = PyObject_GetItem(arr_obj, index_obj); + Py_DECREF(index_obj); + } + } + + if (!item) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + + if (PyErr_ExceptionMatches(PyExc_IndexError)) { + PyErr_Format(PyExc_IndexError, "Cannot access index %zu in array", actual_index); + } + return NULL; + } + + if (item == Py_None) { + none_count++; + Py_DECREF(item); + } else { + non_none_values.push_back(item); + } + } + + if (dtype.empty() && !non_none_values.empty()) { + dtype = infer_dtype(non_none_values[0]); + } + + if (non_none_values.empty() || dtype.empty()) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + Py_INCREF(arr_obj); + return arr_obj; + } + + auto get_addr = [&](const char* dtype_str) -> PyObject* { + PyObject* sys = PyImport_ImportModule("sys"); + PyObject* sys_path = PyObject_GetAttrString(sys, "path"); + Py_DECREF(sys); + + Py_ssize_t original_len = PyList_Size(sys_path); + if (original_len == -1) { + Py_DECREF(sys_path); + return NULL; + } + + PyObject* path = PyUnicode_FromString("pydatastructs/linear_data_structures/_backend/cpp/algorithms"); + if (!path) { + Py_DECREF(sys_path); + return NULL; + } + + int append_result = PyList_Append(sys_path, path); + Py_DECREF(path); + + if (append_result != 0) { + Py_DECREF(sys_path); + return NULL; + } + + PyObject* mod = PyImport_ImportModule("llvm_algorithms"); + + if (PyList_SetSlice(sys_path, original_len, original_len + 1, NULL) != 0) { + PyErr_Clear(); + } + Py_DECREF(sys_path); + if (!mod) { + return NULL; + } + + PyObject* fn = PyObject_GetAttrString(mod, "get_quick_sort_ptr"); + Py_DECREF(mod); + if (!fn) { + return NULL; + } + + PyObject* arg = PyUnicode_FromString(dtype_str); + if (!arg) { + Py_DECREF(fn); + return NULL; + } + + PyObject* addr_obj = PyObject_CallFunctionObjArgs(fn, arg, NULL); + Py_DECREF(fn); + Py_DECREF(arg); + if (!addr_obj) { + return NULL; + } + return addr_obj; + }; + + if (N > INT32_MAX) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + PyErr_SetString(PyExc_OverflowError, "Slice length exceeds 32-bit limit for JIT function signature."); + return NULL; + } + + if (dtype == "int32" || dtype == "int64") { + bool is32 = (dtype == "int32"); + PyObject* addr_obj = get_addr(dtype.c_str()); + if (!addr_obj) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + return NULL; + } + + long long addr = PyLong_AsLongLong(addr_obj); + Py_DECREF(addr_obj); + if (addr == -1 && PyErr_Occurred()) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + return NULL; + } + + if (is32) { + std::vector buf; + buf.reserve(non_none_values.size()); + + for (size_t i = 0; i < non_none_values.size(); i++) { + PyObject* obj = non_none_values[i]; + + long v = PyLong_AsLong(obj); + if (PyErr_Occurred()) { + for (PyObject* cleanup_obj : non_none_values) { + Py_DECREF(cleanup_obj); + } + return NULL; + } + + if (v < INT32_MIN || v > INT32_MAX) { + for (PyObject* cleanup_obj : non_none_values) { + Py_DECREF(cleanup_obj); + } + PyErr_Format(PyExc_OverflowError, "Value %ld at index %zu out of int32 range", v, i); + return NULL; + } + buf.push_back((int32_t)v); + } + + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + non_none_values.clear(); + + if (buf.empty()) { + Py_INCREF(arr_obj); + return arr_obj; + } + + try { + auto fn = reinterpret_cast(addr); + fn(buf.data(), (int32_t)buf.size()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "LLVM function call failed"); + return NULL; + } + for (size_t i = 0; i < buf.size(); i++) { + size_t actual_index = lower + i; + + if (actual_index >= arr_len) { + PyErr_Format(PyExc_IndexError, "Assignment index %zu out of bounds (array length: %zu)", + actual_index, arr_len); + return NULL; + } + + PyObject* new_value = PyLong_FromLong((long)buf[i]); + if (!new_value) return NULL; + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + Py_DECREF(new_value); + return NULL; + } + + int result = PyObject_SetItem(arr_obj, index_obj, new_value); + Py_DECREF(index_obj); + Py_DECREF(new_value); + } + + for (size_t i = buf.size(); i < N; i++) { + size_t actual_index = lower + i; + + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + return NULL; + } + + Py_INCREF(Py_None); + int result = PyObject_SetItem(arr_obj, index_obj, Py_None); + Py_DECREF(index_obj); + } + + } else { + std::vector buf; + buf.reserve(non_none_values.size()); + + for (size_t i = 0; i < non_none_values.size(); i++) { + PyObject* obj = non_none_values[i]; + long long v = PyLong_AsLongLong(obj); + if (PyErr_Occurred()) { + for (PyObject* cleanup_obj : non_none_values) { + Py_DECREF(cleanup_obj); + } + return NULL; + } + buf.push_back((int64_t)v); + } + + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + non_none_values.clear(); + + if (buf.empty()) { + Py_INCREF(arr_obj); + return arr_obj; + } + + try { + auto fn = reinterpret_cast(addr); + if (!fn) { + PyErr_SetString(PyExc_RuntimeError, "Invalid function pointer"); + return NULL; + } + fn(buf.data(), (int32_t)buf.size()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "LLVM function call failed"); + return NULL; + } + for (size_t i = 0; i < buf.size(); i++) { + size_t actual_index = lower + i; + if (actual_index >= arr_len) { + PyErr_Format(PyExc_IndexError, "Assignment index %zu out of bounds", actual_index); + return NULL; + } + PyObject* new_value = PyLong_FromLongLong((long long)buf[i]); + if (!new_value) return NULL; + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + Py_DECREF(new_value); + return NULL; + } + int result = PyObject_SetItem(arr_obj, index_obj, new_value); + Py_DECREF(index_obj); + Py_DECREF(new_value); + } + + for (size_t i = buf.size(); i < N; i++) { + size_t actual_index = lower + i; + + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + return NULL; + } + Py_INCREF(Py_None); + int result = PyObject_SetItem(arr_obj, index_obj, Py_None); + Py_DECREF(index_obj); + } + } + } + else if (dtype == "float32" || dtype == "float64") { + bool is32 = (dtype == "float32"); + PyObject* addr_obj = get_addr(dtype.c_str()); + if (!addr_obj) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + return NULL; + } + long long addr = PyLong_AsLongLong(addr_obj); + Py_DECREF(addr_obj); + if (addr == -1 && PyErr_Occurred()) { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + return NULL; + } + if (is32) { + std::vector buf; + buf.reserve(non_none_values.size()); + + for (size_t i = 0; i < non_none_values.size(); i++) { + PyObject* obj = non_none_values[i]; + double v = PyFloat_AsDouble(obj); + if (PyErr_Occurred()) { + for (PyObject* cleanup_obj : non_none_values) { + Py_DECREF(cleanup_obj); + } + return NULL; + } + buf.push_back((float)v); + } + + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + non_none_values.clear(); + if (buf.empty()) { + Py_INCREF(arr_obj); + return arr_obj; + } + + try { + auto fn = reinterpret_cast(addr); + if (!fn) { + PyErr_SetString(PyExc_RuntimeError, "Invalid function pointer"); + return NULL; + } + fn(buf.data(), (int32_t)buf.size()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "LLVM function call failed"); + return NULL; + } + + for (size_t i = 0; i < buf.size(); i++) { + size_t actual_index = lower + i; + if (actual_index >= arr_len) { + PyErr_Format(PyExc_IndexError, "Assignment index %zu out of bounds", actual_index); + return NULL; + } + PyObject* new_value = PyFloat_FromDouble((double)buf[i]); + if (!new_value) return NULL; + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + Py_DECREF(new_value); + return NULL; + } + int result = PyObject_SetItem(arr_obj, index_obj, new_value); + Py_DECREF(index_obj); + Py_DECREF(new_value); + } + + for (size_t i = buf.size(); i < N; i++) { + size_t actual_index = lower + i; + + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + return NULL; + } + Py_INCREF(Py_None); + int result = PyObject_SetItem(arr_obj, index_obj, Py_None); + Py_DECREF(index_obj); + } + } + else { + std::vector buf; + buf.reserve(non_none_values.size()); + + for (size_t i = 0; i < non_none_values.size(); i++) { + PyObject* obj = non_none_values[i]; + double v = PyFloat_AsDouble(obj); + if (PyErr_Occurred()) { + for (PyObject* cleanup_obj : non_none_values) { + Py_DECREF(cleanup_obj); + } + return NULL; + } + buf.push_back(v); + } + + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + non_none_values.clear(); + + if (buf.empty()) { + Py_INCREF(arr_obj); + return arr_obj; + } + + try { + auto fn = reinterpret_cast(addr); + if (!fn) { + PyErr_SetString(PyExc_RuntimeError, "Invalid function pointer"); + return NULL; + } + fn(buf.data(), (int32_t)buf.size()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "LLVM function call failed"); + return NULL; + } + + for (size_t i = 0; i < buf.size(); i++) { + size_t actual_index = lower + i; + PyObject* new_value = PyFloat_FromDouble(buf[i]); + if (!new_value) return NULL; + + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + Py_DECREF(new_value); + return NULL; + } + + int result = PyObject_SetItem(arr_obj, index_obj, new_value); + Py_DECREF(index_obj); + Py_DECREF(new_value); + } + + for (size_t i = buf.size(); i < N; i++) { + size_t actual_index = lower + i; + PyObject* index_obj = PyLong_FromSize_t(actual_index); + if (!index_obj) { + return NULL; + } + Py_INCREF(Py_None); + int result = PyObject_SetItem(arr_obj, index_obj, Py_None); + Py_DECREF(index_obj); + } + } + + } else { + for (PyObject* obj : non_none_values) { + Py_DECREF(obj); + } + PyErr_SetString(PyExc_ValueError, "dtype must be one of: int32,int64,float32,float64"); + return NULL; + } + + if (is_dynamic_array) { + PyObject* modify_result = PyObject_CallMethod(arr_obj, "_modify", "O", Py_True); + if (!modify_result) { + PyErr_Clear(); + } else { + Py_DECREF(modify_result); + } + } + + Py_INCREF(arr_obj); + return arr_obj; +} + #endif diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index 6d383fdca..f6f9423e5 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -754,6 +754,8 @@ def quick_sort(array: Array, **kwargs) -> Array: backend = kwargs.pop("backend", Backend.PYTHON) if backend == Backend.CPP: return _algorithms.quick_sort(array, **kwargs) + if backend == Backend.LLVM: + return _algorithms.quick_sort_llvm(array, **kwargs) from pydatastructs import Stack comp = kwargs.get("comp", lambda u, v: u <= v) pick_pivot_element = kwargs.get("pick_pivot_element", diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index 3e287bb74..6504f51f5 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -114,6 +114,7 @@ def test_cocktail_shaker_sort(): def test_quick_sort(): _test_common_sort(quick_sort) _test_common_sort(quick_sort, backend=Backend.CPP) + _test_common_sort(quick_sort, backend=Backend.LLVM) def test_intro_sort(): _test_common_sort(intro_sort)