|
| 1 | +#ifndef ADJACENCY_LIST_GRAPH_NODE_HPP |
| 2 | +#define ADJACENCY_LIST_GRAPH_NODE_HPP |
| 3 | + |
| 4 | +#define PY_SSIZE_T_CLEAN |
| 5 | +#include <Python.h> |
| 6 | +#include <string> |
| 7 | +#include <unordered_map> |
| 8 | +#include "GraphNode.hpp" |
| 9 | + |
| 10 | +extern PyTypeObject AdjacencyListGraphNodeType; |
| 11 | + |
| 12 | +typedef struct { |
| 13 | + PyObject_HEAD |
| 14 | + std::string name; |
| 15 | + PyObject* data; |
| 16 | + std::unordered_map<std::string, PyObject*> adjacent; |
| 17 | +} AdjacencyListGraphNode; |
| 18 | + |
| 19 | +static void AdjacencyListGraphNode_dealloc(AdjacencyListGraphNode* self) { |
| 20 | + Py_XDECREF(self->data); |
| 21 | + for (auto& pair : self->adjacent) { |
| 22 | + Py_XDECREF(pair.second); |
| 23 | + } |
| 24 | + self->adjacent.~unordered_map<std::string, PyObject*>(); |
| 25 | + Py_TYPE(self)->tp_free(reinterpret_cast<PyTypeObject*>(self)); |
| 26 | +} |
| 27 | + |
| 28 | +static PyObject* AdjacencyListGraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { |
| 29 | + AdjacencyListGraphNode* self; |
| 30 | + self = reinterpret_cast<AdjacencyListGraphNode*>(type)->tp_alloc(type, 0); |
| 31 | + if (!self) return NULL; |
| 32 | + new (&self->adjacent) std::unordered_map<std::string, PyObject*>(); |
| 33 | + |
| 34 | + static char* kwlist[] = { "name", "data", "adjacency_list", NULL }; |
| 35 | + const char* name; |
| 36 | + PyObject* data = Py_None; |
| 37 | + PyObject* adjacency_list = Py_None; |
| 38 | + |
| 39 | + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OO", kwlist, &name, &data, &adjacency_list)) { |
| 40 | + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (str, object, list)"); |
| 41 | + return NULL; |
| 42 | + } |
| 43 | + |
| 44 | + self->name = std::string(name); |
| 45 | + Py_INCREF(data); |
| 46 | + self->data = data; |
| 47 | + |
| 48 | + if (PyList_Check(adjacency_list)) { |
| 49 | + Py_ssize_t size = PyList_Size(adjacency_list); |
| 50 | + for (Py_ssize_t i = 0; i < size; i++) { |
| 51 | + PyObject* node = PyList_GetItem(adjacency_list, i); |
| 52 | + |
| 53 | + |
| 54 | + if (PyType_Ready(&AdjacencyListGraphNodeType) < 0) { |
| 55 | + PyErr_SetString(PyExc_RuntimeError, "Failed to initialize AdjacencyListGraphNodeType"); |
| 56 | + return NULL; |
| 57 | + } |
| 58 | + |
| 59 | + if (!PyObject_IsInstance(node, (PyObject*)&AdjacencyListGraphNodeType)) { |
| 60 | + PyErr_SetString(PyExc_TypeError, "Adjacency list must contain only AdjacencyListGraphNode instances"); |
| 61 | + return NULL; |
| 62 | + } |
| 63 | + |
| 64 | + const char* adj_name = (reinterpret_cast<AdjacencyListGraphNode*>(node))->name.c_str(); |
| 65 | + std::string str = std::string(adj_name); |
| 66 | + Py_INCREF(node); |
| 67 | + self->adjacent[str] = node; |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + return reinterpret_cast<PyObject*>(self); |
| 72 | +} |
| 73 | + |
| 74 | +static PyObject* AdjacencyListGraphNode_add_adjacent_node(AdjacencyListGraphNode* self, PyObject* args) { |
| 75 | + const char* name; |
| 76 | + PyObject* data = Py_None; |
| 77 | + if (!PyArg_ParseTuple(args, "s|O", &name, &data)) { |
| 78 | + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (str, object)"); |
| 79 | + return NULL; |
| 80 | + } |
| 81 | + |
| 82 | + if (self->adjacent.find(name) == self->adjacent.end()) { |
| 83 | + PyObject* new_node = PyObject_CallFunction(reinterpret_cast<PyObject*>(&AdjacencyListGraphNodeType), "sO", name, data); |
| 84 | + if (!new_node) { |
| 85 | + PyErr_SetString(PyExc_RuntimeError, "Failed to create adjacent node"); |
| 86 | + return NULL; |
| 87 | + } |
| 88 | + self->adjacent[name] = new_node; |
| 89 | + Py_INCREF(new_node); |
| 90 | + } else { |
| 91 | + Py_XDECREF(self->adjacent[name]); |
| 92 | + Py_INCREF(data); |
| 93 | + self->adjacent[name] = data; |
| 94 | + } |
| 95 | + Py_RETURN_NONE; |
| 96 | +} |
| 97 | + |
| 98 | +static PyObject* AdjacencyListGraphNode_remove_adjacent_node(AdjacencyListGraphNode* self, PyObject* args) { |
| 99 | + const char* name; |
| 100 | + if (!PyArg_ParseTuple(args, "s", &name)) { |
| 101 | + PyErr_SetString(PyExc_ValueError, "Invalid arguments: Expected (str)"); |
| 102 | + return NULL; |
| 103 | + } |
| 104 | + |
| 105 | + auto it = self->adjacent.find(name); |
| 106 | + if (it != self->adjacent.end()) { |
| 107 | + Py_XDECREF(it->second); |
| 108 | + self->adjacent.erase(it); |
| 109 | + Py_RETURN_NONE; |
| 110 | + } else { |
| 111 | + PyErr_SetString(PyExc_ValueError, "Node is not adjacent"); |
| 112 | + return NULL; |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +static PyObject* AdjacencyListGraphNode_get_name(AdjacencyListGraphNode* self, void* closure) { |
| 117 | + return PyUnicode_FromString(self->name.c_str()); |
| 118 | +} |
| 119 | + |
| 120 | +static int AdjacencyListGraphNode_set_name(AdjacencyListGraphNode* self, PyObject* value, void* closure) { |
| 121 | + if (!PyUnicode_Check(value)) { |
| 122 | + PyErr_SetString(PyExc_TypeError, "name must be a string"); |
| 123 | + return -1; |
| 124 | + } |
| 125 | + self->name = PyUnicode_AsUTF8(value); |
| 126 | + return 0; |
| 127 | +} |
| 128 | + |
| 129 | +static PyObject* AdjacencyListGraphNode_get_data(AdjacencyListGraphNode* self, void* closure) { |
| 130 | + Py_INCREF(self->data); |
| 131 | + return self->data; |
| 132 | +} |
| 133 | + |
| 134 | +static int AdjacencyListGraphNode_set_data(AdjacencyListGraphNode* self, PyObject* value, void* closure) { |
| 135 | + Py_XDECREF(self->data); |
| 136 | + Py_INCREF(value); |
| 137 | + self->data = value; |
| 138 | + return 0; |
| 139 | +} |
| 140 | + |
| 141 | +static PyObject* AdjacencyListGraphNode_get_adjacent(AdjacencyListGraphNode* self, void* closure) { |
| 142 | + PyObject* py_dict = PyDict_New(); |
| 143 | + if (!py_dict) return NULL; |
| 144 | + |
| 145 | + for (const auto& pair : self->adjacent) { |
| 146 | + PyDict_SetItemString(py_dict, pair.first.c_str(), pair.second); |
| 147 | + } |
| 148 | + |
| 149 | + return py_dict; |
| 150 | +} |
| 151 | + |
| 152 | +static int AdjacencyListGraphNode_set_adjacent(AdjacencyListGraphNode* self, PyObject* value, void* closure) { |
| 153 | + if (!PyDict_Check(value)) { |
| 154 | + PyErr_SetString(PyExc_TypeError, "adjacent must be a dictionary"); |
| 155 | + return -1; |
| 156 | + } |
| 157 | + |
| 158 | + self->adjacent.clear(); |
| 159 | + |
| 160 | + PyObject *key, *val; |
| 161 | + Py_ssize_t pos = 0; |
| 162 | + while (PyDict_Next(value, &pos, &key, &val)) { |
| 163 | + if (!PyUnicode_Check(key) || !PyObject_IsInstance(val, (PyObject*)&AdjacencyListGraphNodeType)) { |
| 164 | + PyErr_SetString(PyExc_TypeError, "Keys must be strings and values must be AdjacencyListGraphNode instances"); |
| 165 | + return -1; |
| 166 | + } |
| 167 | + |
| 168 | + const char* key_str = PyUnicode_AsUTF8(key); |
| 169 | + Py_INCREF(val); |
| 170 | + self->adjacent[key_str] = val; |
| 171 | + } |
| 172 | + |
| 173 | + return 0; |
| 174 | +} |
| 175 | + |
| 176 | +static PyGetSetDef AdjacencyListGraphNode_getsetters[] = { |
| 177 | + {"name", (getter)AdjacencyListGraphNode_get_name, (setter)AdjacencyListGraphNode_set_name, "Get or set node name", NULL}, |
| 178 | + {"data", (getter)AdjacencyListGraphNode_get_data, (setter)AdjacencyListGraphNode_set_data, "Get or set node data", NULL}, |
| 179 | + {"adjacent", (getter)AdjacencyListGraphNode_get_adjacent, (setter)AdjacencyListGraphNode_set_adjacent, "Get or set adjacent nodes", NULL}, |
| 180 | + {NULL} |
| 181 | +}; |
| 182 | + |
| 183 | + |
| 184 | +static PyMethodDef AdjacencyListGraphNode_methods[] = { |
| 185 | + {"add_adjacent_node", (PyCFunction)AdjacencyListGraphNode_add_adjacent_node, METH_VARARGS, "Add adjacent node"}, |
| 186 | + {"remove_adjacent_node", (PyCFunction)AdjacencyListGraphNode_remove_adjacent_node, METH_VARARGS, "Remove adjacent node"}, |
| 187 | + {NULL} |
| 188 | +}; |
| 189 | + |
| 190 | +PyTypeObject AdjacencyListGraphNodeType = { |
| 191 | + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "AdjacencyListGraphNode", |
| 192 | + /* tp_basicsize */ sizeof(AdjacencyListGraphNode), |
| 193 | + /* tp_itemsize */ 0, |
| 194 | + /* tp_dealloc */ (destructor)AdjacencyListGraphNode_dealloc, |
| 195 | + /* tp_print */ 0, |
| 196 | + /* tp_getattr */ 0, |
| 197 | + /* tp_setattr */ 0, |
| 198 | + /* tp_reserved */ 0, |
| 199 | + /* tp_repr */ 0, |
| 200 | + /* tp_as_number */ 0, |
| 201 | + /* tp_as_sequence */ 0, |
| 202 | + /* tp_as_mapping */ 0, |
| 203 | + /* tp_hash */ 0, |
| 204 | + /* tp_call */ 0, |
| 205 | + /* tp_str */ 0, |
| 206 | + /* tp_getattro */ 0, |
| 207 | + /* tp_setattro */ 0, |
| 208 | + /* tp_as_buffer */ 0, |
| 209 | + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, |
| 210 | + /* tp_doc */ "Node Data Structure for an Adjacency List Graph", |
| 211 | + /* tp_traverse */ 0, |
| 212 | + /* tp_clear */ 0, |
| 213 | + /* tp_richcompare */ 0, |
| 214 | + /* tp_weaklistoffset */ 0, |
| 215 | + /* tp_iter */ 0, |
| 216 | + /* tp_iternext */ 0, |
| 217 | + /* tp_methods */ AdjacencyListGraphNode_methods, |
| 218 | + /* tp_members */ 0, |
| 219 | + /* tp_getset */ AdjacencyListGraphNode_getsetters, |
| 220 | + /* tp_base */ &GraphNodeType, |
| 221 | + /* tp_dict */ 0, |
| 222 | + /* tp_descr_get */ 0, |
| 223 | + /* tp_descr_set */ 0, |
| 224 | + /* tp_dictoffset */ 0, |
| 225 | + /* tp_init */ 0, |
| 226 | + /* tp_alloc */ 0, |
| 227 | + /* tp_new */ AdjacencyListGraphNode_new, |
| 228 | +}; |
| 229 | + |
| 230 | + |
| 231 | +#endif |
0 commit comments