Skip to content

Commit 8f419fd

Browse files
nubol23czgdp1807
andauthored
Adding support for stack.ArrayStack in C++ backend (#512)
Co-authored-by: Gagandeep Singh <[email protected]>
1 parent 3abc026 commit 8f419fd

File tree

10 files changed

+283
-11
lines changed

10 files changed

+283
-11
lines changed

pydatastructs/miscellaneous_data_structures/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
binomial_trees,
66
queue,
77
disjoint_set,
8-
sparse_table
8+
sparse_table,
9+
_extensions,
910
)
1011

1112
from .binomial_trees import (

pydatastructs/miscellaneous_data_structures/_backend/__init__.py

Whitespace-only changes.

pydatastructs/miscellaneous_data_structures/_backend/cpp/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#ifndef MISCELLANEOUS_DATA_STRUCTURES_ARRAYSTACK_HPP
2+
#define MISCELLANEOUS_DATA_STRUCTURES_ARRAYSTACK_HPP
3+
4+
#define PY_SSIZE_T_CLEAN
5+
#include <Python.h>
6+
#include <cstdlib>
7+
#include <iostream>
8+
#include <structmember.h>
9+
#include "../../../../linear_data_structures/_backend/cpp/arrays/DynamicOneDimensionalArray.hpp"
10+
11+
typedef struct {
12+
PyObject_HEAD
13+
DynamicOneDimensionalArray* _items;
14+
} ArrayStack;
15+
16+
static void ArrayStack_dealloc(ArrayStack *self) {
17+
DynamicOneDimensionalArray_dealloc(self->_items);
18+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
19+
}
20+
21+
static PyObject* ArrayStack__new__(PyTypeObject *type, PyObject *args, PyObject *kwds) {
22+
ArrayStack *self = reinterpret_cast<ArrayStack*>(type->tp_alloc(type, 0));
23+
24+
static char *kwlist[] = {"items", "dtype", NULL};
25+
PyObject *initial_values = Py_None, *dtype = Py_None;
26+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &initial_values, &dtype)) {
27+
PyErr_SetString(PyExc_ValueError, "Error creating ArrayStack");
28+
return NULL;
29+
}
30+
31+
if (initial_values != Py_None && PyType_Check(initial_values)) {
32+
PyErr_SetString(PyExc_TypeError, "`items` must be an instance of list or tuple, received a type instead\n"
33+
"Did you mean to instantiate an ArrayStack with only the data type? "
34+
"if so, send the type parameter as a named argument "
35+
"for example: dtype=int");
36+
return NULL;
37+
}
38+
39+
PyObject* items = NULL;
40+
PyObject* doda_kwds = Py_BuildValue("{}");
41+
if (initial_values == Py_None) {
42+
// If the only argument is the dtype, redefine the args as a tuple (dtype, 0)
43+
// where 0 is the initial array size
44+
PyObject* extended_args = PyTuple_Pack(2, dtype, PyLong_FromLong(0));
45+
46+
items = DynamicOneDimensionalArray___new__(&DynamicOneDimensionalArrayType, extended_args, doda_kwds);
47+
} else {
48+
// If the user provides dtype and initial values list, let the array initializer handle the checks.
49+
PyObject* doda_args = PyTuple_Pack(2, dtype, initial_values);
50+
items = DynamicOneDimensionalArray___new__(&DynamicOneDimensionalArrayType, doda_args, doda_kwds);
51+
}
52+
53+
if (!items) {
54+
return NULL;
55+
}
56+
57+
DynamicOneDimensionalArray* tmp = self->_items;
58+
self->_items = reinterpret_cast<DynamicOneDimensionalArray*>(items);
59+
60+
return reinterpret_cast<PyObject*>(self);
61+
}
62+
63+
static PyObject* ArrayStack_is_empty(ArrayStack *self) {
64+
bool is_empty = self->_items->_last_pos_filled == -1;
65+
66+
if (is_empty) {
67+
Py_RETURN_TRUE;
68+
}
69+
70+
Py_RETURN_FALSE;
71+
}
72+
73+
static PyObject* ArrayStack_push(ArrayStack *self, PyObject* args) {
74+
size_t len_args = PyObject_Length(args);
75+
if (len_args != 1) {
76+
PyErr_SetString(PyExc_ValueError, "Expected one argument");
77+
return NULL;
78+
}
79+
80+
if (PyObject_IsTrue(ArrayStack_is_empty(self))) {
81+
self->_items->_one_dimensional_array->_dtype = reinterpret_cast<PyObject*>(
82+
Py_TYPE(PyObject_GetItem(args, PyZero))
83+
);
84+
}
85+
86+
DynamicOneDimensionalArray_append(self->_items, args);
87+
88+
Py_RETURN_NONE;
89+
}
90+
91+
static PyObject* ArrayStack_pop(ArrayStack *self) {
92+
if (PyObject_IsTrue(ArrayStack_is_empty(self))) {
93+
PyErr_SetString(PyExc_IndexError, "Stack is empty");
94+
return NULL;
95+
}
96+
97+
PyObject *top_element = DynamicOneDimensionalArray___getitem__(
98+
self->_items, PyLong_FromLong(self->_items->_last_pos_filled)
99+
);
100+
101+
PyObject* last_pos_arg = PyTuple_Pack(1, PyLong_FromLong(self->_items->_last_pos_filled));
102+
DynamicOneDimensionalArray_delete(self->_items, last_pos_arg);
103+
return top_element;
104+
}
105+
106+
static PyObject* ArrayStack_peek(ArrayStack *self, void *closure) {
107+
return DynamicOneDimensionalArray___getitem__(
108+
self->_items, PyLong_FromLong(self->_items->_last_pos_filled)
109+
);
110+
}
111+
112+
static Py_ssize_t ArrayStack__len__(ArrayStack *self) {
113+
return self->_items->_num;
114+
}
115+
116+
static PyObject* ArrayStack__str__(ArrayStack* self){
117+
return DynamicOneDimensionalArray___str__(self->_items);
118+
}
119+
120+
static struct PyMethodDef ArrayStack_PyMethodDef[] = {
121+
{"push", (PyCFunction) ArrayStack_push, METH_VARARGS, NULL},
122+
{"pop", (PyCFunction) ArrayStack_pop, METH_VARARGS, NULL},
123+
{NULL}
124+
};
125+
126+
static PyMappingMethods ArrayStack_PyMappingMethods = {
127+
(lenfunc) ArrayStack__len__,
128+
};
129+
130+
static PyGetSetDef ArrayStack_GetterSetters[] = {
131+
{"peek", (getter) ArrayStack_peek, NULL, "peek top value", NULL},
132+
{"is_empty", (getter) ArrayStack_is_empty, NULL, "check if the stack is empty", NULL},
133+
{NULL} /* Sentinel */
134+
};
135+
136+
static PyTypeObject ArrayStackType = {
137+
/* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "ArrayStack",
138+
/* tp_basicsize */ sizeof(ArrayStack),
139+
/* tp_itemsize */ 0,
140+
/* tp_dealloc */ (destructor) ArrayStack_dealloc,
141+
/* tp_print */ 0,
142+
/* tp_getattr */ 0,
143+
/* tp_setattr */ 0,
144+
/* tp_reserved */ 0,
145+
/* tp_repr */ 0,
146+
/* tp_as_number */ 0,
147+
/* tp_as_sequence */ 0,
148+
/* tp_as_mapping */ &ArrayStack_PyMappingMethods,
149+
/* tp_hash */ 0,
150+
/* tp_call */ 0,
151+
/* tp_str */ (reprfunc) ArrayStack__str__,
152+
/* tp_getattro */ 0,
153+
/* tp_setattro */ 0,
154+
/* tp_as_buffer */ 0,
155+
/* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
156+
/* tp_doc */ 0,
157+
/* tp_traverse */ 0,
158+
/* tp_clear */ 0,
159+
/* tp_richcompare */ 0,
160+
/* tp_weaklistoffset */ 0,
161+
/* tp_iter */ 0,
162+
/* tp_iternext */ 0,
163+
/* tp_methods */ ArrayStack_PyMethodDef,
164+
/* tp_members */ 0,
165+
/* tp_getset */ ArrayStack_GetterSetters,
166+
/* tp_base */ 0,
167+
/* tp_dict */ 0,
168+
/* tp_descr_get */ 0,
169+
/* tp_descr_set */ 0,
170+
/* tp_dictoffset */ 0,
171+
/* tp_init */ 0,
172+
/* tp_alloc */ 0,
173+
/* tp_new */ ArrayStack__new__,
174+
};
175+
176+
177+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <Python.h>
2+
#include "ArrayStack.hpp"
3+
4+
static struct PyModuleDef stack_struct = {
5+
PyModuleDef_HEAD_INIT,
6+
"_stack",
7+
0,
8+
-1,
9+
NULL,
10+
};
11+
12+
PyMODINIT_FUNC PyInit__stack(void) {
13+
Py_Initialize();
14+
PyObject *stack = PyModule_Create(&stack_struct);
15+
16+
if (PyType_Ready(&ArrayStackType) < 0) {
17+
return NULL;
18+
}
19+
Py_INCREF(&ArrayStackType);
20+
PyModule_AddObject(stack, "ArrayStack", reinterpret_cast<PyObject*>(&ArrayStackType));
21+
22+
if (PyType_Ready(&ArrayType) < 0) {
23+
return NULL;
24+
}
25+
Py_INCREF(&ArrayType);
26+
PyModule_AddObject(stack, "Array", reinterpret_cast<PyObject*>(&ArrayType));
27+
28+
if (PyType_Ready(&OneDimensionalArrayType) < 0) {
29+
return NULL;
30+
}
31+
Py_INCREF(&OneDimensionalArrayType);
32+
PyModule_AddObject(stack, "OneDimensionalArray", reinterpret_cast<PyObject*>(&OneDimensionalArrayType));
33+
34+
if (PyType_Ready(&DynamicArrayType) < 0) {
35+
return NULL;
36+
}
37+
Py_INCREF(&DynamicArrayType);
38+
PyModule_AddObject(stack, "DynamicArray", reinterpret_cast<PyObject*>(&DynamicArrayType));
39+
40+
if (PyType_Ready(&DynamicOneDimensionalArrayType) < 0) {
41+
return NULL;
42+
}
43+
Py_INCREF(&DynamicOneDimensionalArrayType);
44+
PyModule_AddObject(stack, "DynamicOneDimensionalArray", reinterpret_cast<PyObject*>(&DynamicOneDimensionalArrayType));
45+
46+
47+
return stack;
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from setuptools import Extension
2+
3+
project = 'pydatastructs'
4+
5+
module = 'miscellaneous_data_structures'
6+
7+
backend = '_backend'
8+
9+
cpp = 'cpp'
10+
11+
stack = '.'.join([project, module, backend, cpp, '_stack'])
12+
stack_sources = ['/'.join([project, module, backend, cpp, 'stack', 'stack.cpp'])]
13+
14+
extensions = [
15+
Extension(stack, sources=stack_sources),
16+
]

pydatastructs/miscellaneous_data_structures/stack.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pydatastructs.linear_data_structures import DynamicOneDimensionalArray, SinglyLinkedList
2+
from pydatastructs.miscellaneous_data_structures._backend.cpp import _stack
23
from pydatastructs.utils.misc_util import (
34
_check_type, NoneType, Backend,
45
raise_if_backend_is_not_python)
@@ -53,18 +54,22 @@ class Stack(object):
5354
"""
5455

5556
def __new__(cls, implementation='array', **kwargs):
56-
raise_if_backend_is_not_python(
57-
cls, kwargs.get('backend', Backend.PYTHON))
57+
backend = kwargs.get('backend', Backend.PYTHON)
5858
if implementation == 'array':
59-
return ArrayStack(
60-
kwargs.get('items', None),
61-
kwargs.get('dtype', int))
59+
items = kwargs.get('items', None)
60+
dtype = kwargs.get('dtype', int)
61+
if backend == Backend.CPP:
62+
return _stack.ArrayStack(items, dtype)
63+
64+
return ArrayStack(items, dtype)
6265
if implementation == 'linked_list':
66+
raise_if_backend_is_not_python(cls, backend)
67+
6368
return LinkedListStack(
64-
kwargs.get('items',None)
69+
kwargs.get('items', None)
6570
)
6671
raise NotImplementedError(
67-
"%s hasn't been implemented yet."%(implementation))
72+
"%s hasn't been implemented yet."%(implementation))
6873

6974
@classmethod
7075
def methods(cls):

pydatastructs/miscellaneous_data_structures/tests/test_stack.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from pydatastructs.miscellaneous_data_structures import Stack
22
from pydatastructs.miscellaneous_data_structures.stack import ArrayStack, LinkedListStack
3+
from pydatastructs.miscellaneous_data_structures._backend.cpp import _stack
34
from pydatastructs.utils.raises_util import raises
4-
from pydatastructs.utils.misc_util import _check_type
5+
from pydatastructs.utils.misc_util import _check_type, Backend
6+
57

68
def test_Stack():
79
s = Stack(implementation='array')
@@ -12,6 +14,11 @@ def test_Stack():
1214
assert _check_type(s2, LinkedListStack) is True
1315
assert raises(NotImplementedError, lambda: Stack(implementation=''))
1416

17+
s3 = Stack(backend=Backend.CPP)
18+
assert _check_type(s3, _stack.ArrayStack) is True
19+
s4 = Stack(implementation="array", backend=Backend.CPP)
20+
assert _check_type(s4, _stack.ArrayStack) is True
21+
1522
def test_ArrayStack():
1623
s = Stack(implementation='array')
1724
s.push(1)
@@ -28,6 +35,22 @@ def test_ArrayStack():
2835
assert str(_s) == '[1, 2, 3]'
2936
assert len(_s) == 3
3037

38+
# Cpp test
39+
s1 = Stack(implementation="array", backend=Backend.CPP)
40+
s1.push(1)
41+
s1.push(2)
42+
s1.push(3)
43+
assert s1.peek == 3
44+
assert str(s1) == "['1', '2', '3']"
45+
assert s1.pop() == 3
46+
assert s1.pop() == 2
47+
assert s1.pop() == 1
48+
assert s1.is_empty is True
49+
assert raises(IndexError, lambda : s1.pop())
50+
_s1 = Stack(items=[1, 2, 3], backend=Backend.CPP)
51+
assert str(_s1) == "['1', '2', '3']"
52+
assert len(_s1) == 3
53+
3154
def test_LinkedListStack():
3255
s = Stack(implementation='linked_list')
3356
s.push(1)
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
project = 'pydatastructs'
22

3-
modules = ['linear_data_structures']
3+
modules = ['linear_data_structures', 'miscellaneous_data_structures']
44

55
backend = '_backend'
66

77
cpp = 'cpp'
88

9-
dummy_submodules_list = [('_arrays.py', '_algorithms.py')]
9+
dummy_submodules_list = [('_arrays.py', '_algorithms.py'), ('_stack.py',)]

setup.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import setuptools
22
from pydatastructs import linear_data_structures
3+
from pydatastructs import miscellaneous_data_structures
34

45
with open("README.md", "r") as fh:
56
long_description = fh.read()
67

78
extensions = []
89

910
extensions.extend(linear_data_structures._extensions.extensions)
11+
extensions.extend(miscellaneous_data_structures._extensions.extensions)
1012

1113
setuptools.setup(
1214
name="cz-pydatastructs",

0 commit comments

Comments
 (0)