diff --git a/pydatastructs/miscellaneous_data_structures/__init__.py b/pydatastructs/miscellaneous_data_structures/__init__.py index 24086ec0b..16e23e366 100644 --- a/pydatastructs/miscellaneous_data_structures/__init__.py +++ b/pydatastructs/miscellaneous_data_structures/__init__.py @@ -5,7 +5,8 @@ binomial_trees, queue, disjoint_set, - sparse_table + sparse_table, + _extensions, ) from .binomial_trees import ( diff --git a/pydatastructs/miscellaneous_data_structures/_backend/__init__.py b/pydatastructs/miscellaneous_data_structures/_backend/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pydatastructs/miscellaneous_data_structures/_backend/cpp/__init__.py b/pydatastructs/miscellaneous_data_structures/_backend/cpp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pydatastructs/miscellaneous_data_structures/_backend/cpp/stack/ArrayStack.hpp b/pydatastructs/miscellaneous_data_structures/_backend/cpp/stack/ArrayStack.hpp new file mode 100644 index 000000000..5b51d0d4c --- /dev/null +++ b/pydatastructs/miscellaneous_data_structures/_backend/cpp/stack/ArrayStack.hpp @@ -0,0 +1,177 @@ +#ifndef MISCELLANEOUS_DATA_STRUCTURES_ARRAYSTACK_HPP +#define MISCELLANEOUS_DATA_STRUCTURES_ARRAYSTACK_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include "../../../../linear_data_structures/_backend/cpp/arrays/DynamicOneDimensionalArray.hpp" + +typedef struct { + PyObject_HEAD + DynamicOneDimensionalArray* _items; +} ArrayStack; + +static void ArrayStack_dealloc(ArrayStack *self) { + DynamicOneDimensionalArray_dealloc(self->_items); + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* ArrayStack__new__(PyTypeObject *type, PyObject *args, PyObject *kwds) { + ArrayStack *self = reinterpret_cast(type->tp_alloc(type, 0)); + + static char *kwlist[] = {"items", "dtype", NULL}; + PyObject *initial_values = Py_None, *dtype = Py_None; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &initial_values, &dtype)) { + PyErr_SetString(PyExc_ValueError, "Error creating ArrayStack"); + return NULL; + } + + if (initial_values != Py_None && PyType_Check(initial_values)) { + PyErr_SetString(PyExc_TypeError, "`items` must be an instance of list or tuple, received a type instead\n" + "Did you mean to instantiate an ArrayStack with only the data type? " + "if so, send the type parameter as a named argument " + "for example: dtype=int"); + return NULL; + } + + PyObject* items = NULL; + PyObject* doda_kwds = Py_BuildValue("{}"); + if (initial_values == Py_None) { + // If the only argument is the dtype, redefine the args as a tuple (dtype, 0) + // where 0 is the initial array size + PyObject* extended_args = PyTuple_Pack(2, dtype, PyLong_FromLong(0)); + + items = DynamicOneDimensionalArray___new__(&DynamicOneDimensionalArrayType, extended_args, doda_kwds); + } else { + // If the user provides dtype and initial values list, let the array initializer handle the checks. + PyObject* doda_args = PyTuple_Pack(2, dtype, initial_values); + items = DynamicOneDimensionalArray___new__(&DynamicOneDimensionalArrayType, doda_args, doda_kwds); + } + + if (!items) { + return NULL; + } + + DynamicOneDimensionalArray* tmp = self->_items; + self->_items = reinterpret_cast(items); + + return reinterpret_cast(self); +} + +static PyObject* ArrayStack_is_empty(ArrayStack *self) { + bool is_empty = self->_items->_last_pos_filled == -1; + + if (is_empty) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject* ArrayStack_push(ArrayStack *self, PyObject* args) { + size_t len_args = PyObject_Length(args); + if (len_args != 1) { + PyErr_SetString(PyExc_ValueError, "Expected one argument"); + return NULL; + } + + if (PyObject_IsTrue(ArrayStack_is_empty(self))) { + self->_items->_one_dimensional_array->_dtype = reinterpret_cast( + Py_TYPE(PyObject_GetItem(args, PyZero)) + ); + } + + DynamicOneDimensionalArray_append(self->_items, args); + + Py_RETURN_NONE; +} + +static PyObject* ArrayStack_pop(ArrayStack *self) { + if (PyObject_IsTrue(ArrayStack_is_empty(self))) { + PyErr_SetString(PyExc_IndexError, "Stack is empty"); + return NULL; + } + + PyObject *top_element = DynamicOneDimensionalArray___getitem__( + self->_items, PyLong_FromLong(self->_items->_last_pos_filled) + ); + + PyObject* last_pos_arg = PyTuple_Pack(1, PyLong_FromLong(self->_items->_last_pos_filled)); + DynamicOneDimensionalArray_delete(self->_items, last_pos_arg); + return top_element; +} + +static PyObject* ArrayStack_peek(ArrayStack *self, void *closure) { + return DynamicOneDimensionalArray___getitem__( + self->_items, PyLong_FromLong(self->_items->_last_pos_filled) + ); +} + +static Py_ssize_t ArrayStack__len__(ArrayStack *self) { + return self->_items->_num; +} + +static PyObject* ArrayStack__str__(ArrayStack* self){ + return DynamicOneDimensionalArray___str__(self->_items); +} + +static struct PyMethodDef ArrayStack_PyMethodDef[] = { + {"push", (PyCFunction) ArrayStack_push, METH_VARARGS, NULL}, + {"pop", (PyCFunction) ArrayStack_pop, METH_VARARGS, NULL}, + {NULL} +}; + +static PyMappingMethods ArrayStack_PyMappingMethods = { + (lenfunc) ArrayStack__len__, +}; + +static PyGetSetDef ArrayStack_GetterSetters[] = { + {"peek", (getter) ArrayStack_peek, NULL, "peek top value", NULL}, + {"is_empty", (getter) ArrayStack_is_empty, NULL, "check if the stack is empty", NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject ArrayStackType = { + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "ArrayStack", + /* tp_basicsize */ sizeof(ArrayStack), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor) ArrayStack_dealloc, + /* tp_print */ 0, + /* tp_getattr */ 0, + /* tp_setattr */ 0, + /* tp_reserved */ 0, + /* tp_repr */ 0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ &ArrayStack_PyMappingMethods, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ (reprfunc) ArrayStack__str__, + /* tp_getattro */ 0, + /* tp_setattro */ 0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ 0, + /* tp_traverse */ 0, + /* tp_clear */ 0, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, + /* tp_methods */ ArrayStack_PyMethodDef, + /* tp_members */ 0, + /* tp_getset */ ArrayStack_GetterSetters, + /* tp_base */ 0, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ ArrayStack__new__, +}; + + +#endif diff --git a/pydatastructs/miscellaneous_data_structures/_backend/cpp/stack/stack.cpp b/pydatastructs/miscellaneous_data_structures/_backend/cpp/stack/stack.cpp new file mode 100644 index 000000000..2ba1bec7a --- /dev/null +++ b/pydatastructs/miscellaneous_data_structures/_backend/cpp/stack/stack.cpp @@ -0,0 +1,48 @@ +#include +#include "ArrayStack.hpp" + +static struct PyModuleDef stack_struct = { + PyModuleDef_HEAD_INIT, + "_stack", + 0, + -1, + NULL, +}; + +PyMODINIT_FUNC PyInit__stack(void) { + Py_Initialize(); + PyObject *stack = PyModule_Create(&stack_struct); + + if (PyType_Ready(&ArrayStackType) < 0) { + return NULL; + } + Py_INCREF(&ArrayStackType); + PyModule_AddObject(stack, "ArrayStack", reinterpret_cast(&ArrayStackType)); + + if (PyType_Ready(&ArrayType) < 0) { + return NULL; + } + Py_INCREF(&ArrayType); + PyModule_AddObject(stack, "Array", reinterpret_cast(&ArrayType)); + + if (PyType_Ready(&OneDimensionalArrayType) < 0) { + return NULL; + } + Py_INCREF(&OneDimensionalArrayType); + PyModule_AddObject(stack, "OneDimensionalArray", reinterpret_cast(&OneDimensionalArrayType)); + + if (PyType_Ready(&DynamicArrayType) < 0) { + return NULL; + } + Py_INCREF(&DynamicArrayType); + PyModule_AddObject(stack, "DynamicArray", reinterpret_cast(&DynamicArrayType)); + + if (PyType_Ready(&DynamicOneDimensionalArrayType) < 0) { + return NULL; + } + Py_INCREF(&DynamicOneDimensionalArrayType); + PyModule_AddObject(stack, "DynamicOneDimensionalArray", reinterpret_cast(&DynamicOneDimensionalArrayType)); + + + return stack; +} diff --git a/pydatastructs/miscellaneous_data_structures/_extensions.py b/pydatastructs/miscellaneous_data_structures/_extensions.py new file mode 100644 index 000000000..47d77d6cb --- /dev/null +++ b/pydatastructs/miscellaneous_data_structures/_extensions.py @@ -0,0 +1,16 @@ +from setuptools import Extension + +project = 'pydatastructs' + +module = 'miscellaneous_data_structures' + +backend = '_backend' + +cpp = 'cpp' + +stack = '.'.join([project, module, backend, cpp, '_stack']) +stack_sources = ['/'.join([project, module, backend, cpp, 'stack', 'stack.cpp'])] + +extensions = [ + Extension(stack, sources=stack_sources), +] diff --git a/pydatastructs/miscellaneous_data_structures/stack.py b/pydatastructs/miscellaneous_data_structures/stack.py index cafbc7809..38f72b43f 100644 --- a/pydatastructs/miscellaneous_data_structures/stack.py +++ b/pydatastructs/miscellaneous_data_structures/stack.py @@ -1,4 +1,5 @@ from pydatastructs.linear_data_structures import DynamicOneDimensionalArray, SinglyLinkedList +from pydatastructs.miscellaneous_data_structures._backend.cpp import _stack from pydatastructs.utils.misc_util import ( _check_type, NoneType, Backend, raise_if_backend_is_not_python) @@ -53,18 +54,22 @@ class Stack(object): """ def __new__(cls, implementation='array', **kwargs): - raise_if_backend_is_not_python( - cls, kwargs.get('backend', Backend.PYTHON)) + backend = kwargs.get('backend', Backend.PYTHON) if implementation == 'array': - return ArrayStack( - kwargs.get('items', None), - kwargs.get('dtype', int)) + items = kwargs.get('items', None) + dtype = kwargs.get('dtype', int) + if backend == Backend.CPP: + return _stack.ArrayStack(items, dtype) + + return ArrayStack(items, dtype) if implementation == 'linked_list': + raise_if_backend_is_not_python(cls, backend) + return LinkedListStack( - kwargs.get('items',None) + kwargs.get('items', None) ) raise NotImplementedError( - "%s hasn't been implemented yet."%(implementation)) + "%s hasn't been implemented yet."%(implementation)) @classmethod def methods(cls): diff --git a/pydatastructs/miscellaneous_data_structures/tests/test_stack.py b/pydatastructs/miscellaneous_data_structures/tests/test_stack.py index 52756f9cf..2d9d08b82 100644 --- a/pydatastructs/miscellaneous_data_structures/tests/test_stack.py +++ b/pydatastructs/miscellaneous_data_structures/tests/test_stack.py @@ -1,7 +1,9 @@ from pydatastructs.miscellaneous_data_structures import Stack from pydatastructs.miscellaneous_data_structures.stack import ArrayStack, LinkedListStack +from pydatastructs.miscellaneous_data_structures._backend.cpp import _stack from pydatastructs.utils.raises_util import raises -from pydatastructs.utils.misc_util import _check_type +from pydatastructs.utils.misc_util import _check_type, Backend + def test_Stack(): s = Stack(implementation='array') @@ -12,6 +14,11 @@ def test_Stack(): assert _check_type(s2, LinkedListStack) is True assert raises(NotImplementedError, lambda: Stack(implementation='')) + s3 = Stack(backend=Backend.CPP) + assert _check_type(s3, _stack.ArrayStack) is True + s4 = Stack(implementation="array", backend=Backend.CPP) + assert _check_type(s4, _stack.ArrayStack) is True + def test_ArrayStack(): s = Stack(implementation='array') s.push(1) @@ -28,6 +35,22 @@ def test_ArrayStack(): assert str(_s) == '[1, 2, 3]' assert len(_s) == 3 + # Cpp test + s1 = Stack(implementation="array", backend=Backend.CPP) + s1.push(1) + s1.push(2) + s1.push(3) + assert s1.peek == 3 + assert str(s1) == "['1', '2', '3']" + assert s1.pop() == 3 + assert s1.pop() == 2 + assert s1.pop() == 1 + assert s1.is_empty is True + assert raises(IndexError, lambda : s1.pop()) + _s1 = Stack(items=[1, 2, 3], backend=Backend.CPP) + assert str(_s1) == "['1', '2', '3']" + assert len(_s1) == 3 + def test_LinkedListStack(): s = Stack(implementation='linked_list') s.push(1) diff --git a/scripts/build/dummy_submodules_data.py b/scripts/build/dummy_submodules_data.py index 03af5afa0..2fa19414c 100644 --- a/scripts/build/dummy_submodules_data.py +++ b/scripts/build/dummy_submodules_data.py @@ -1,9 +1,9 @@ project = 'pydatastructs' -modules = ['linear_data_structures'] +modules = ['linear_data_structures', 'miscellaneous_data_structures'] backend = '_backend' cpp = 'cpp' -dummy_submodules_list = [('_arrays.py', '_algorithms.py')] +dummy_submodules_list = [('_arrays.py', '_algorithms.py'), ('_stack.py',)] diff --git a/setup.py b/setup.py index ed168371f..58cffd677 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ import setuptools from pydatastructs import linear_data_structures +from pydatastructs import miscellaneous_data_structures with open("README.md", "r") as fh: long_description = fh.read() @@ -7,6 +8,7 @@ extensions = [] extensions.extend(linear_data_structures._extensions.extensions) +extensions.extend(miscellaneous_data_structures._extensions.extensions) setuptools.setup( name="cz-pydatastructs",