Skip to content

Commit ef7bf11

Browse files
Prerak SinghPrerak Singh
authored andcommitted
updated
1 parent f7a6296 commit ef7bf11

File tree

5 files changed

+174
-31
lines changed

5 files changed

+174
-31
lines changed

pydatastructs/graphs/_extensions.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from setuptools import Extension
2+
import os
3+
4+
project = 'pydatastructs'
5+
6+
module = 'graphs'
7+
8+
backend = '_backend'
9+
10+
cpp = 'cpp'
11+
12+
graph = '.'.join([project, module, backend, cpp, '_graph'])
13+
graph_sources = ['/'.join([project, module, backend, cpp,
14+
'graph.cpp']),"pydatastructs/utils/_backend/cpp/graph_utils.cpp"]
15+
16+
include_dir = os.path.abspath(os.path.join(project, 'utils', '_backend', 'cpp'))
17+
18+
extensions = [Extension(graph, sources=graph_sources,include_dirs=[include_dir], language="c++", extra_compile_args=["-std=c++17", "-stdlib=libc++"])]

pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@
66
#include <string>
77
#include <unordered_map>
88
#include "GraphNode.hpp"
9+
#include <variant>
910

1011
extern PyTypeObject AdjacencyListGraphNodeType;
1112

1213
typedef struct {
1314
PyObject_HEAD
1415
std::string name;
15-
PyObject* data;
16+
std::variant<std::monostate, int64_t, double, std::string> data;
17+
DataType data_type;
1618
std::unordered_map<std::string, PyObject*> adjacent;
1719
} AdjacencyListGraphNode;
1820

1921
static void AdjacencyListGraphNode_dealloc(AdjacencyListGraphNode* self) {
20-
Py_XDECREF(self->data);
2122
for (auto& pair : self->adjacent) {
2223
Py_XDECREF(pair.second);
2324
}
@@ -30,6 +31,9 @@ static PyObject* AdjacencyListGraphNode_new(PyTypeObject* type, PyObject* args,
3031
if (!self) return NULL;
3132
new (&self->adjacent) std::unordered_map<std::string, PyObject*>();
3233
new (&self->name) std::string();
34+
new (&self->data) std::variant<std::monostate, int64_t, double, std::string>();
35+
self->data_type = DataType::None;
36+
self->data = std::monostate{};
3337

3438
static char* kwlist[] = { "name", "data", "adjacency_list", NULL };
3539
const char* name;
@@ -42,8 +46,24 @@ static PyObject* AdjacencyListGraphNode_new(PyTypeObject* type, PyObject* args,
4246
}
4347

4448
self->name = std::string(name);
45-
Py_INCREF(data);
46-
self->data = data;
49+
50+
if (data == Py_None) {
51+
self->data_type = DataType::None;
52+
self->data = std::monostate{};
53+
} else if (PyLong_Check(data)) {
54+
self->data_type = DataType::Int;
55+
self->data = static_cast<int64_t>(PyLong_AsLongLong(data));
56+
} else if (PyFloat_Check(data)) {
57+
self->data_type = DataType::Double;
58+
self->data = PyFloat_AsDouble(data);
59+
} else if (PyUnicode_Check(data)) {
60+
const char* str = PyUnicode_AsUTF8(data);
61+
self->data_type = DataType::String;
62+
self->data = std::string(str);
63+
} else {
64+
PyErr_SetString(PyExc_TypeError, "Unsupported data type. Must be int, float, str, or None.");
65+
return NULL;
66+
}
4767

4868
if (PyList_Check(adjacency_list)) {
4969
Py_ssize_t size = PyList_Size(adjacency_list);
@@ -127,14 +147,41 @@ static int AdjacencyListGraphNode_set_name(AdjacencyListGraphNode* self, PyObjec
127147
}
128148

129149
static PyObject* AdjacencyListGraphNode_get_data(AdjacencyListGraphNode* self, void* closure) {
130-
Py_INCREF(self->data);
131-
return self->data;
150+
switch (self->data_type) {
151+
case DataType::Int:
152+
return PyLong_FromLongLong(std::get<int64_t>(self->data));
153+
case DataType::Double:
154+
return PyFloat_FromDouble(std::get<double>(self->data));
155+
case DataType::String:
156+
return PyUnicode_FromString(std::get<std::string>(self->data).c_str());
157+
case DataType::None:
158+
default:
159+
Py_RETURN_NONE;
160+
}
132161
}
133162

134163
static int AdjacencyListGraphNode_set_data(AdjacencyListGraphNode* self, PyObject* value, void* closure) {
135-
Py_XDECREF(self->data);
136-
Py_INCREF(value);
137-
self->data = value;
164+
if (value == Py_None) {
165+
self->data_type = DataType::None;
166+
self->data = std::monostate{};
167+
} else if (PyLong_Check(value)) {
168+
self->data_type = DataType::Int;
169+
self->data = static_cast<int64_t>(PyLong_AsLongLong(value));
170+
} else if (PyFloat_Check(value)) {
171+
self->data_type = DataType::Double;
172+
self->data = PyFloat_AsDouble(value);
173+
} else if (PyUnicode_Check(value)) {
174+
const char* str = PyUnicode_AsUTF8(value);
175+
if (!str) {
176+
PyErr_SetString(PyExc_ValueError, "Invalid UTF-8 string.");
177+
return -1;
178+
}
179+
self->data_type = DataType::String;
180+
self->data = std::string(str);
181+
} else {
182+
PyErr_SetString(PyExc_TypeError, "Unsupported data type. Must be int, float, str, or None.");
183+
return -1;
184+
}
138185
return 0;
139186
}
140187

pydatastructs/utils/_backend/cpp/AdjacencyMatrixGraphNode.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ typedef struct {
1111
} AdjacencyMatrixGraphNode;
1212

1313
static void AdjacencyMatrixGraphNode_dealloc(AdjacencyMatrixGraphNode* self){
14-
Py_XDECREF(self->super.data);
1514
Py_TYPE(self)->tp_free(reinterpret_cast<PyTypeObject*>(self));
1615
}
1716

pydatastructs/utils/_backend/cpp/GraphNode.hpp

Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,32 @@
44
#define PY_SSIZE_T_CLEAN
55
#include <Python.h>
66
#include <string>
7+
#include <variant>
8+
9+
enum class DataType {
10+
None,
11+
Int,
12+
Double,
13+
String
14+
};
715

816
typedef struct {
917
PyObject_HEAD
1018
std::string name;
11-
PyObject* data;
19+
std::variant<std::monostate, int64_t, double, std::string> data;
20+
DataType data_type;
1221
} GraphNode;
1322

1423
static void GraphNode_dealloc(GraphNode* self){
15-
Py_XDECREF(self->data);
1624
Py_TYPE(self)->tp_free(reinterpret_cast<PyTypeObject*>(self));
1725
}
1826

1927
static PyObject* GraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwds){
2028
GraphNode* self;
2129
self = reinterpret_cast<GraphNode*>(type->tp_alloc(type,0));
2230
new (&self->name) std::string();
31+
new (&self->data) std::variant<std::monostate, int64_t, double, std::string>();
32+
self->data_type = DataType::None;
2333
if (!self) return NULL;
2434

2535
static char* kwlist[] = { "name", "data", NULL };
@@ -32,54 +42,123 @@ static PyObject* GraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwd
3242
}
3343

3444
self->name = std::string(name);
35-
Py_INCREF(data);
36-
self->data = data;
45+
46+
if (data == Py_None) {
47+
self->data = std::monostate{};
48+
self->data_type = DataType::None;
49+
} else if (PyLong_Check(data)) {
50+
self->data = static_cast<int64_t>(PyLong_AsLongLong(data));
51+
self->data_type = DataType::Int;
52+
} else if (PyFloat_Check(data)) {
53+
self->data = PyFloat_AsDouble(data);
54+
self->data_type = DataType::Double;
55+
} else if (PyUnicode_Check(data)) {
56+
const char* s = PyUnicode_AsUTF8(data);
57+
self->data = std::string(s);
58+
self->data_type = DataType::String;
59+
} else {
60+
PyErr_SetString(PyExc_TypeError, "data must be int, float, str, or None");
61+
return NULL;
62+
}
3763

3864
return reinterpret_cast<PyObject*>(self);
3965
}
4066

4167
static PyObject* GraphNode_str(GraphNode* self) {
42-
return PyUnicode_FromString(("('" + self->name + "', " + PyUnicode_AsUTF8(PyObject_Str(self->data)) + ")").c_str());
68+
std::string repr = "('" + self->name + "', ";
69+
70+
switch (self->data_type) {
71+
case DataType::None:
72+
repr += "None";
73+
break;
74+
case DataType::Int:
75+
repr += std::to_string(std::get<int64_t>(self->data));
76+
break;
77+
case DataType::Double:
78+
repr += std::to_string(std::get<double>(self->data));
79+
break;
80+
case DataType::String:
81+
repr += "'" + std::get<std::string>(self->data) + "'";
82+
break;
83+
}
84+
repr += ")";
85+
return PyUnicode_FromString(repr.c_str());
4386
}
4487

4588
static PyObject* GraphNode_get(GraphNode* self, void *closure) {
4689
if (closure == (void*)"name") {
4790
return PyUnicode_FromString(self->name.c_str());
48-
}
49-
if (closure == (void*)"data") {
50-
Py_INCREF(self->data);
51-
return self->data;
91+
} else if (closure == (void*)"data") {
92+
switch (self->data_type) {
93+
case DataType::None:
94+
Py_RETURN_NONE;
95+
case DataType::Int:
96+
return PyLong_FromLongLong(std::get<int64_t>(self->data));
97+
case DataType::Double:
98+
return PyFloat_FromDouble(std::get<double>(self->data));
99+
case DataType::String:
100+
return PyUnicode_FromString(std::get<std::string>(self->data).c_str());
101+
}
52102
}
53103
Py_RETURN_NONE;
54104
}
55105

56106
static int GraphNode_set(GraphNode* self, PyObject *value, void *closure) {
57-
if (value == NULL) {
58-
PyErr_SetString(PyExc_ValueError, "value is NULL");
107+
if (!value) {
108+
PyErr_SetString(PyExc_ValueError, "Cannot delete attributes");
59109
return -1;
60110
}
61111

62112
if (closure == (void*)"name") {
63113
if (!PyUnicode_Check(value)) {
64-
PyErr_SetString(PyExc_TypeError, "value to be set must be a string");
114+
PyErr_SetString(PyExc_TypeError, "name must be a string");
65115
return -1;
66116
}
67117
self->name = PyUnicode_AsUTF8(value);
68118
}
69119
else if (closure == (void*)"data") {
70-
PyObject *tmp = self->data;
71-
Py_INCREF(value);
72-
self->data = value;
73-
Py_DECREF(tmp);
74-
}
75-
else {
120+
if (value == Py_None) {
121+
self->data = std::monostate{};
122+
self->data_type = DataType::None;
123+
} else if (PyLong_Check(value)) {
124+
self->data = static_cast<int64_t>(PyLong_AsLongLong(value));
125+
self->data_type = DataType::Int;
126+
} else if (PyFloat_Check(value)) {
127+
self->data = PyFloat_AsDouble(value);
128+
self->data_type = DataType::Double;
129+
} else if (PyUnicode_Check(value)) {
130+
self->data = std::string(PyUnicode_AsUTF8(value));
131+
self->data_type = DataType::String;
132+
} else {
133+
PyErr_SetString(PyExc_TypeError, "data must be int, float, str, or None");
134+
return -1;
135+
}
136+
} else {
76137
PyErr_SetString(PyExc_AttributeError, "Unknown attribute");
77138
return -1;
78139
}
79140

80141
return 0;
81142
}
82143

144+
static PyGetSetDef GraphNode_getsetters[] = {
145+
{
146+
const_cast<char*>("name"),
147+
reinterpret_cast<getter>(GraphNode_get),
148+
reinterpret_cast<setter>(GraphNode_set),
149+
const_cast<char*>("name"),
150+
reinterpret_cast<void*>(const_cast<char*>("name"))
151+
},
152+
{
153+
const_cast<char*>("data"),
154+
reinterpret_cast<getter>(GraphNode_get),
155+
reinterpret_cast<setter>(GraphNode_set),
156+
const_cast<char*>("data"),
157+
reinterpret_cast<void*>(const_cast<char*>("data"))
158+
},
159+
{nullptr}
160+
};
161+
83162
static PyTypeObject GraphNodeType = {
84163
/* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "GraphNode",
85164
/* tp_basicsize */ sizeof(GraphNode),
@@ -109,7 +188,7 @@ static PyTypeObject GraphNodeType = {
109188
/* tp_iternext */ 0,
110189
/* tp_methods */ 0,
111190
/* tp_members */ 0,
112-
/* tp_getset */ 0,
191+
/* tp_getset */ GraphNode_getsetters,
113192
/* tp_base */ &PyBaseObject_Type,
114193
/* tp_dict */ 0,
115194
/* tp_descr_get */ 0,

pydatastructs/utils/_extensions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@
1616
'graph_utils.cpp'])]
1717

1818
extensions = [
19-
Extension(nodes, sources=nodes_sources),
20-
Extension(graph_utils, sources = graph_utils_sources)
19+
Extension(nodes, sources=nodes_sources, language="c++", extra_compile_args=["-std=c++17", "-stdlib=libc++"]),
20+
Extension(graph_utils, sources = graph_utils_sources, language="c++", extra_compile_args=["-std=c++17", "-stdlib=libc++"])
2121
]

0 commit comments

Comments
 (0)