Skip to content

Commit 6c28802

Browse files
authored
Added C++ backend for linear_data_structures.arrays.DynamicOneDimensionalArray (#472)
1 parent c10857e commit 6c28802

File tree

11 files changed

+475
-29
lines changed

11 files changed

+475
-29
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#ifndef LINEAR_DATA_STRUCTURES_DYNAMIC_ARRAY_HPP
2+
#define LINEAR_DATA_STRUCTURES_DYNAMIC_ARRAY_HPP
3+
4+
#define PY_SSIZE_T_CLEAN
5+
#include <Python.h>
6+
#include <structmember.h>
7+
#include "Array.hpp"
8+
9+
typedef struct {
10+
PyObject_HEAD
11+
} DynamicArray;
12+
13+
static PyTypeObject DynamicArrayType = {
14+
/* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "DynamicArray",
15+
/* tp_basicsize */ sizeof(DynamicArray),
16+
/* tp_itemsize */ 0,
17+
/* tp_dealloc */ 0,
18+
/* tp_print */ 0,
19+
/* tp_getattr */ 0,
20+
/* tp_setattr */ 0,
21+
/* tp_reserved */ 0,
22+
/* tp_repr */ 0,
23+
/* tp_as_number */ 0,
24+
/* tp_as_sequence */ 0,
25+
/* tp_as_mapping */ 0,
26+
/* tp_hash */ 0,
27+
/* tp_call */ 0,
28+
/* tp_str */ 0,
29+
/* tp_getattro */ 0,
30+
/* tp_setattro */ 0,
31+
/* tp_as_buffer */ 0,
32+
/* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
33+
/* tp_doc */ 0,
34+
/* tp_traverse */ 0,
35+
/* tp_clear */ 0,
36+
/* tp_richcompare */ 0,
37+
/* tp_weaklistoffset */ 0,
38+
/* tp_iter */ 0,
39+
/* tp_iternext */ 0,
40+
/* tp_methods */ 0,
41+
/* tp_members */ 0,
42+
/* tp_getset */ 0,
43+
/* tp_base */ &ArrayType,
44+
/* tp_dict */ 0,
45+
/* tp_descr_get */ 0,
46+
/* tp_descr_set */ 0,
47+
/* tp_dictoffset */ 0,
48+
/* tp_init */ 0,
49+
/* tp_alloc */ 0,
50+
/* tp_new */ 0,
51+
};
52+
53+
#endif
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
#ifndef LINEAR_DATA_STRUCTURES_DYNAMICONEDIMENSIONALARRAY_HPP
2+
#define LINEAR_DATA_STRUCTURES_DYNAMICONEDIMENSIONALARRAY_HPP
3+
4+
#define PY_SSIZE_T_CLEAN
5+
#include <Python.h>
6+
#include <structmember.h>
7+
#include <cstdlib>
8+
#include "DynamicArray.hpp"
9+
#include "OneDimensionalArray.hpp"
10+
#include "../../../../utils/_backend/cpp/utils.hpp"
11+
12+
typedef struct {
13+
PyObject_HEAD
14+
OneDimensionalArray* _one_dimensional_array;
15+
double _load_factor;
16+
long _num;
17+
long _last_pos_filled;
18+
long _size;
19+
} DynamicOneDimensionalArray;
20+
21+
static void DynamicOneDimensionalArray_dealloc(DynamicOneDimensionalArray *self) {
22+
OneDimensionalArray_dealloc(self->_one_dimensional_array);
23+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
24+
}
25+
26+
static PyObject* DynamicOneDimensionalArray___new__(PyTypeObject* type, PyObject *args,
27+
PyObject *kwds) {
28+
DynamicOneDimensionalArray *self;
29+
self = reinterpret_cast<DynamicOneDimensionalArray*>(type->tp_alloc(type, 0));
30+
PyObject* _one_dimensional_array = OneDimensionalArray___new__(&OneDimensionalArrayType, args, kwds);
31+
if( !_one_dimensional_array ) {
32+
return NULL;
33+
}
34+
self->_one_dimensional_array = reinterpret_cast<OneDimensionalArray*>(_one_dimensional_array);
35+
self->_size = (long) self->_one_dimensional_array->_size;
36+
37+
PyObject* _load_factor = PyObject_GetItem(kwds, PyUnicode_FromString("load_factor"));
38+
if( _load_factor == nullptr ) {
39+
PyErr_Clear();
40+
self->_load_factor = 0.25;
41+
} else {
42+
_load_factor = PyFloat_FromString(PyObject_Str(_load_factor));
43+
if( !_load_factor ) {
44+
return NULL;
45+
}
46+
self->_load_factor = PyFloat_AS_DOUBLE(_load_factor);
47+
}
48+
if( self->_one_dimensional_array->_size == 0 ||
49+
self->_one_dimensional_array->_data[0] == Py_None ) {
50+
self->_num = 0;
51+
} else {
52+
self->_num = (long) self->_one_dimensional_array->_size;
53+
}
54+
self->_last_pos_filled = (long) self->_num - 1;
55+
56+
return reinterpret_cast<PyObject*>(self);
57+
}
58+
59+
static PyObject* DynamicOneDimensionalArray___getitem__(DynamicOneDimensionalArray *self,
60+
PyObject* arg) {
61+
return OneDimensionalArray___getitem__(self->_one_dimensional_array, arg);
62+
}
63+
64+
static int DynamicOneDimensionalArray___setitem__(DynamicOneDimensionalArray *self,
65+
PyObject* arg, PyObject* value) {
66+
return OneDimensionalArray___setitem__(self->_one_dimensional_array, arg, value);
67+
}
68+
69+
static PyObject* DynamicOneDimensionalArray_fill(DynamicOneDimensionalArray *self, PyObject *args) {
70+
return OneDimensionalArray_fill(self->_one_dimensional_array, args);
71+
}
72+
73+
static Py_ssize_t DynamicOneDimensionalArray___len__(DynamicOneDimensionalArray *self) {
74+
return self->_one_dimensional_array->_size;
75+
}
76+
77+
static PyObject* DynamicOneDimensionalArray___str__(DynamicOneDimensionalArray *self) {
78+
PyObject** self__data = self->_one_dimensional_array->_data;
79+
return __str__(self__data, self->_one_dimensional_array->_size, self->_last_pos_filled);
80+
}
81+
82+
static PyObject* DynamicOneDimensionalArray__modify(DynamicOneDimensionalArray *self,
83+
PyObject* args) {
84+
PyObject* force = nullptr;
85+
if( args ) {
86+
force = PyObject_GetItem(args, PyZero);
87+
}
88+
if( !force ) {
89+
PyErr_Clear();
90+
force = Py_False;
91+
}
92+
93+
long i;
94+
PyObject** _data = self->_one_dimensional_array->_data;
95+
long _size = (long) self->_one_dimensional_array->_size;
96+
if( force == Py_True ) {
97+
i = -1;
98+
while( _data[i] == Py_None) {
99+
i--;
100+
}
101+
long _last_pos_filled = std::abs(i) % _size;
102+
if( i < 0 ) {
103+
_last_pos_filled += _size;
104+
}
105+
self->_last_pos_filled = _last_pos_filled;
106+
}
107+
108+
if( ((float) self->_num)/((float) _size) < self->_load_factor ) {
109+
long new_size = 2 * self->_num + 1;
110+
PyObject** arr_new = reinterpret_cast<PyObject**>(std::malloc(new_size * sizeof(PyObject*)));
111+
for( i = 0; i < new_size; i++ ) {
112+
arr_new[i] = Py_None;
113+
}
114+
long j = 0;
115+
for( i = 0; i <= self->_last_pos_filled; i++ ) {
116+
if( _data[i] != Py_None ) {
117+
arr_new[j] = _data[i];
118+
j += 1;
119+
}
120+
}
121+
self->_last_pos_filled = j - 1;
122+
self->_one_dimensional_array->_data = arr_new;
123+
self->_one_dimensional_array->_size = new_size;
124+
self->_size = new_size;
125+
}
126+
127+
Py_RETURN_NONE;
128+
}
129+
130+
static PyObject* DynamicOneDimensionalArray_append(DynamicOneDimensionalArray *self,
131+
PyObject* args) {
132+
PyObject* el = PyObject_GetItem(args, PyZero);
133+
if( !el ) {
134+
return NULL;
135+
}
136+
137+
long _size = (long) self->_one_dimensional_array->_size;
138+
PyObject** _data = self->_one_dimensional_array->_data;
139+
if( self->_last_pos_filled + 1 == _size ) {
140+
long new_size = 2 * _size + 1;
141+
PyObject** arr_new = reinterpret_cast<PyObject**>(std::malloc(new_size * sizeof(PyObject*)));
142+
long i;
143+
for( i = 0; i <= self->_last_pos_filled; i++ ) {
144+
arr_new[i] = _data[i];
145+
}
146+
for( ; i < new_size; i++ ) {
147+
arr_new[i] = Py_None;
148+
}
149+
arr_new[self->_last_pos_filled + 1] = el;
150+
self->_one_dimensional_array->_size = new_size;
151+
self->_size = new_size;
152+
self->_one_dimensional_array->_data = arr_new;
153+
} else {
154+
_data[self->_last_pos_filled + 1] = el;
155+
}
156+
self->_last_pos_filled += 1;
157+
self->_num += 1;
158+
return DynamicOneDimensionalArray__modify(self, NULL);
159+
}
160+
161+
static PyObject* DynamicOneDimensionalArray_delete(DynamicOneDimensionalArray *self,
162+
PyObject* args) {
163+
PyObject* idx_pyobject = PyObject_GetItem(args, PyZero);
164+
if( !idx_pyobject ) {
165+
return NULL;
166+
}
167+
long idx = PyLong_AsLong(idx_pyobject);
168+
if( idx == -1 && PyErr_Occurred() ) {
169+
return NULL;
170+
}
171+
172+
PyObject** _data = self->_one_dimensional_array->_data;
173+
if( idx <= self->_last_pos_filled && idx >= 0 &&
174+
_data[idx] != Py_None ) {
175+
_data[idx] = Py_None;
176+
self->_num -= 1;
177+
if( self->_last_pos_filled == idx ) {
178+
self->_last_pos_filled -= 1;
179+
}
180+
return DynamicOneDimensionalArray__modify(self, NULL);
181+
}
182+
183+
Py_RETURN_NONE;
184+
}
185+
186+
static PyMappingMethods DynamicOneDimensionalArray_PyMappingMethods = {
187+
(lenfunc) DynamicOneDimensionalArray___len__,
188+
(binaryfunc) DynamicOneDimensionalArray___getitem__,
189+
(objobjargproc) DynamicOneDimensionalArray___setitem__,
190+
};
191+
192+
static struct PyMethodDef DynamicOneDimensionalArray_PyMethodDef[] = {
193+
{"fill", (PyCFunction) DynamicOneDimensionalArray_fill, METH_VARARGS, NULL},
194+
{"_modify", (PyCFunction) DynamicOneDimensionalArray__modify, METH_VARARGS, NULL},
195+
{"append", (PyCFunction) DynamicOneDimensionalArray_append, METH_VARARGS, NULL},
196+
{"delete", (PyCFunction) DynamicOneDimensionalArray_delete, METH_VARARGS, NULL},
197+
{NULL}
198+
};
199+
200+
static struct PyMemberDef DynamicOneDimensionalArray_PyMemberDef[] = {
201+
{"size", T_LONG,
202+
offsetof(DynamicOneDimensionalArray, _size),
203+
READONLY, NULL},
204+
{"_num", T_LONG,
205+
offsetof(DynamicOneDimensionalArray, _num),
206+
READONLY, NULL},
207+
{"_last_pos_filled", T_LONG,
208+
offsetof(DynamicOneDimensionalArray, _last_pos_filled),
209+
READONLY, NULL},
210+
{NULL},
211+
};
212+
213+
static PyTypeObject DynamicOneDimensionalArrayType = {
214+
/* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "DynamicOneDimensionalArray",
215+
/* tp_basicsize */ sizeof(DynamicOneDimensionalArray),
216+
/* tp_itemsize */ 0,
217+
/* tp_dealloc */ (destructor) DynamicOneDimensionalArray_dealloc,
218+
/* tp_print */ 0,
219+
/* tp_getattr */ 0,
220+
/* tp_setattr */ 0,
221+
/* tp_reserved */ 0,
222+
/* tp_repr */ 0,
223+
/* tp_as_number */ 0,
224+
/* tp_as_sequence */ 0,
225+
/* tp_as_mapping */ &DynamicOneDimensionalArray_PyMappingMethods,
226+
/* tp_hash */ 0,
227+
/* tp_call */ 0,
228+
/* tp_str */ (reprfunc) DynamicOneDimensionalArray___str__,
229+
/* tp_getattro */ 0,
230+
/* tp_setattro */ 0,
231+
/* tp_as_buffer */ 0,
232+
/* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
233+
/* tp_doc */ 0,
234+
/* tp_traverse */ 0,
235+
/* tp_clear */ 0,
236+
/* tp_richcompare */ 0,
237+
/* tp_weaklistoffset */ 0,
238+
/* tp_iter */ 0,
239+
/* tp_iternext */ 0,
240+
/* tp_methods */ DynamicOneDimensionalArray_PyMethodDef,
241+
/* tp_members */ DynamicOneDimensionalArray_PyMemberDef,
242+
/* tp_getset */ 0,
243+
/* tp_base */ &DynamicArrayType,
244+
/* tp_dict */ 0,
245+
/* tp_descr_get */ 0,
246+
/* tp_descr_set */ 0,
247+
/* tp_dictoffset */ 0,
248+
/* tp_init */ 0,
249+
/* tp_alloc */ 0,
250+
/* tp_new */ DynamicOneDimensionalArray___new__,
251+
};
252+
253+
#endif

pydatastructs/linear_data_structures/_backend/cpp/arrays/OneDimensionalArray.hpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <Python.h>
66
#include <structmember.h>
77
#include <cstdlib>
8+
#include "Array.hpp"
89
#include "../../../../utils/_backend/cpp/utils.hpp"
910

1011
typedef struct {
@@ -57,7 +58,7 @@ static PyObject* OneDimensionalArray___new__(PyTypeObject* type, PyObject *args,
5758
} else {
5859
PyErr_SetString(PyExc_TypeError,
5960
"Expected type of size is int and "
60-
"expected type of data is list/tuple.");
61+
"expected type of data is list/tuple. 61");
6162
return NULL;
6263
}
6364
size_t len_data = PyObject_Length(data);
@@ -104,7 +105,7 @@ static PyObject* OneDimensionalArray___new__(PyTypeObject* type, PyObject *args,
104105
} else {
105106
PyErr_SetString(PyExc_TypeError,
106107
"Expected type of size is int and "
107-
"expected type of data is list/tuple.");
108+
"expected type of data is list/tuple. 108");
108109
return NULL;
109110
}
110111
}
@@ -155,9 +156,6 @@ static Py_ssize_t OneDimensionalArray___len__(OneDimensionalArray *self) {
155156

156157
static PyObject* OneDimensionalArray___str__(OneDimensionalArray *self) {
157158
PyObject** self__data = self->_data;
158-
if( !self__data ) {
159-
return NULL;
160-
}
161159
return __str__(self__data, self->_size);
162160
}
163161

pydatastructs/linear_data_structures/_backend/cpp/arrays/arrays.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include <Python.h>
22
#include "Array.hpp"
33
#include "OneDimensionalArray.hpp"
4+
#include "DynamicArray.hpp"
5+
#include "DynamicOneDimensionalArray.hpp"
46

57
static struct PyModuleDef arrays_struct = {
68
PyModuleDef_HEAD_INIT,
@@ -26,5 +28,17 @@ PyMODINIT_FUNC PyInit__arrays(void) {
2628
Py_INCREF(&OneDimensionalArrayType);
2729
PyModule_AddObject(arrays, "OneDimensionalArray", reinterpret_cast<PyObject*>(&OneDimensionalArrayType));
2830

31+
if (PyType_Ready(&DynamicArrayType) < 0) {
32+
return NULL;
33+
}
34+
Py_INCREF(&DynamicArrayType);
35+
PyModule_AddObject(arrays, "DynamicArray", reinterpret_cast<PyObject*>(&DynamicArrayType));
36+
37+
if (PyType_Ready(&DynamicOneDimensionalArrayType) < 0) {
38+
return NULL;
39+
}
40+
Py_INCREF(&DynamicOneDimensionalArrayType);
41+
PyModule_AddObject(arrays, "DynamicOneDimensionalArray", reinterpret_cast<PyObject*>(&DynamicOneDimensionalArrayType));
42+
2943
return arrays;
3044
}

pydatastructs/linear_data_structures/arrays.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,9 @@ class DynamicOneDimensionalArray(DynamicArray, OneDimensionalArray):
359359
__slots__ = ['_load_factor', '_num', '_last_pos_filled', '_size']
360360

361361
def __new__(cls, dtype=NoneType, *args, **kwargs):
362-
raise_if_backend_is_not_python(
363-
cls, kwargs.get('backend', Backend.PYTHON))
362+
backend = kwargs.get("backend", Backend.PYTHON)
363+
if backend == Backend.CPP:
364+
return _arrays.DynamicOneDimensionalArray(dtype, *args, **kwargs)
364365
obj = super().__new__(cls, dtype, *args, **kwargs)
365366
obj._load_factor = float(kwargs.get('load_factor', 0.25))
366367
obj._num = 0 if obj._size == 0 or obj[0] is None else obj._size
@@ -422,7 +423,7 @@ def size(self):
422423
return self._size
423424

424425
def __str__(self):
425-
to_be_printed = ['' for i in range(self._last_pos_filled + 1)]
426+
to_be_printed = ['' for _ in range(self._last_pos_filled + 1)]
426427
for i in range(self._last_pos_filled + 1):
427428
if self._data[i] is not None:
428429
to_be_printed[i] = str(self._data[i])

0 commit comments

Comments
 (0)