From b169c98606ca5b6dfbc21aed8424c2df3dd77b9b Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Tue, 9 Jul 2024 12:17:30 +0530 Subject: [PATCH 01/11] C++ backend for Cartesian Tree Node --- .../trees/_backend/cpp/BinaryTree.hpp | 8 +- .../trees/_backend/cpp/CartesianTree.hpp | 101 ++++++++++++++++++ pydatastructs/trees/_backend/cpp/trees.cpp | 7 ++ pydatastructs/trees/binary_trees.py | 11 +- pydatastructs/utils/_backend/cpp/TreeNode.hpp | 3 + 5 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 pydatastructs/trees/_backend/cpp/CartesianTree.hpp diff --git a/pydatastructs/trees/_backend/cpp/BinaryTree.hpp b/pydatastructs/trees/_backend/cpp/BinaryTree.hpp index 72b67090..f608ff82 100644 --- a/pydatastructs/trees/_backend/cpp/BinaryTree.hpp +++ b/pydatastructs/trees/_backend/cpp/BinaryTree.hpp @@ -102,7 +102,13 @@ static PyObject* BinaryTree___str__(BinaryTree *self) { OneDimensionalArray* oda = self->tree->_one_dimensional_array; TreeNode* node = reinterpret_cast(oda->_data[i]); if (reinterpret_cast(node) != Py_None) { - PyObject* out = Py_BuildValue("(OOOO)", node->left, node->key, node->data, node->right); + PyObject* out; + if (node->isCartesianTreeNode == true) { + out = Py_BuildValue("(OOOOO)", node->left, node->key, node->priority, node->data, node->right); + } + else { + out = Py_BuildValue("(OOOO)", node->left, node->key, node->data, node->right); + } Py_INCREF(out); PyList_SET_ITEM(list, i, out); } diff --git a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp new file mode 100644 index 00000000..0baaf24a --- /dev/null +++ b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp @@ -0,0 +1,101 @@ +#ifndef TREES_CARTESIANTREE_HPP +#define TREES_CARTESIANTREE_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include "../../../utils/_backend/cpp/utils.hpp" +#include "../../../utils/_backend/cpp/TreeNode.hpp" +#include "../../../linear_data_structures/_backend/cpp/arrays/ArrayForTrees.hpp" +#include "../../../linear_data_structures/_backend/cpp/arrays/DynamicOneDimensionalArray.hpp" +#include "BinarySearchTree.hpp" +#include "SelfBalancingBinaryTree.hpp" + +typedef struct { + PyObject_HEAD + SelfBalancingBinaryTree* sbbt; + ArrayForTrees* tree; +} CartesianTree; + +static void CartesianTree_dealloc(CartesianTree *self) { + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* CartesianTree___new__(PyTypeObject* type, PyObject *args, PyObject *kwds) { + CartesianTree *self; + self = reinterpret_cast(type->tp_alloc(type, 0)); + + if (PyType_Ready(&SelfBalancingBinaryTreeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization. + return NULL; + } + PyObject* p = SelfBalancingBinaryTree___new__(&SelfBalancingBinaryTreeType, args, kwds); + self->sbbt = reinterpret_cast(p); + self->tree = reinterpret_cast(p)->bst->binary_tree->tree; + + return reinterpret_cast(self); +} + +static PyObject* CartesianTree___str__(CartesianTree *self) { + return BinarySearchTree___str__(self->sbbt->bst); +} + +static PyObject* CartesianTree_search(CartesianTree* self, PyObject *args, PyObject *kwds) { + return BinarySearchTree_search(self->sbbt->bst, args, kwds); +} + + +static struct PyMethodDef CartesianTree_PyMethodDef[] = { + // {"insert", (PyCFunction) CartesianTree_insert, METH_VARARGS, NULL}, + // {"delete", (PyCFunction) CartesianTree_delete, METH_VARARGS | METH_KEYWORDS, NULL}, + {"search", (PyCFunction) CartesianTree_search, METH_VARARGS | METH_KEYWORDS, NULL}, + {NULL} +}; + +static PyMemberDef CartesianTree_PyMemberDef[] = { + {"tree", T_OBJECT_EX, offsetof(CartesianTree, tree), 0, "tree"}, + {NULL} /* Sentinel */ +}; + + +static PyTypeObject CartesianTreeType = { + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "CartesianTree", + /* tp_basicsize */ sizeof(CartesianTree), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor) CartesianTree_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 */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ (reprfunc) CartesianTree___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 */ CartesianTree_PyMethodDef, + /* tp_members */ CartesianTree_PyMemberDef, + /* tp_getset */ 0, + /* tp_base */ &SelfBalancingBinaryTreeType, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ CartesianTree___new__, +}; + +#endif diff --git a/pydatastructs/trees/_backend/cpp/trees.cpp b/pydatastructs/trees/_backend/cpp/trees.cpp index 4747c71b..86ecabed 100644 --- a/pydatastructs/trees/_backend/cpp/trees.cpp +++ b/pydatastructs/trees/_backend/cpp/trees.cpp @@ -7,6 +7,7 @@ #include "BinaryIndexedTree.hpp" #include "SplayTree.hpp" #include "AVLTree.hpp" +#include "CartesianTree.hpp" static struct PyModuleDef trees_struct = { PyModuleDef_HEAD_INIT, @@ -68,5 +69,11 @@ PyMODINIT_FUNC PyInit__trees(void) { Py_INCREF(&AVLTreeType); PyModule_AddObject(trees, "AVLTree", reinterpret_cast(&AVLTreeType)); + if (PyType_Ready(&CartesianTreeType) < 0) { + return NULL; + } + Py_INCREF(&CartesianTreeType); + PyModule_AddObject(trees, "CartesianTree", reinterpret_cast(&CartesianTreeType)); + return trees; } diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index d4b5e54b..93f7a594 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -805,8 +805,17 @@ class CartesianTree(SelfBalancingBinaryTree): pydatastructs.trees.binary_trees.SelfBalancingBinaryTree """ @classmethod + def __new__(cls, key=None, root_data=None, comp=None, + is_order_statistic=False, **kwargs): + backend = kwargs.get('backend', Backend.PYTHON) + if backend == Backend.CPP: + if comp is None: + comp = lambda key1, key2: key1 < key2 + return _trees.CartesianTree(key, root_data, comp, is_order_statistic, **kwargs) # If any argument is not given, then it is passed as None, except for comp + return super().__new__(cls, key, root_data, comp, is_order_statistic, **kwargs) + def methods(cls): - return ['__str__', 'insert', 'delete'] + return ['__new__', '__str__', 'insert', 'delete'] def _bubble_up(self, node_idx): node = self.tree[node_idx] diff --git a/pydatastructs/utils/_backend/cpp/TreeNode.hpp b/pydatastructs/utils/_backend/cpp/TreeNode.hpp index e7ed2841..259991f2 100644 --- a/pydatastructs/utils/_backend/cpp/TreeNode.hpp +++ b/pydatastructs/utils/_backend/cpp/TreeNode.hpp @@ -18,6 +18,8 @@ typedef struct { PyObject* parent; long size; long color; + bool isCartesianTreeNode; + long priority; } TreeNode; static void TreeNode_dealloc(TreeNode *self) { @@ -42,6 +44,7 @@ static PyObject* TreeNode___new__(PyTypeObject* type, PyObject *args, PyObject * self->size = 1; self->is_root = false; self->color = 1; + self->isCartesianTreeNode = false; return reinterpret_cast(self); } From 441f700ed1034b2341991634774ad4921fa626b2 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Tue, 9 Jul 2024 12:29:34 +0530 Subject: [PATCH 02/11] Fixed API error --- pydatastructs/trees/binary_trees.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 93f7a594..93c8e22f 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -804,7 +804,6 @@ class CartesianTree(SelfBalancingBinaryTree): pydatastructs.trees.binary_trees.SelfBalancingBinaryTree """ - @classmethod def __new__(cls, key=None, root_data=None, comp=None, is_order_statistic=False, **kwargs): backend = kwargs.get('backend', Backend.PYTHON) @@ -814,6 +813,7 @@ def __new__(cls, key=None, root_data=None, comp=None, return _trees.CartesianTree(key, root_data, comp, is_order_statistic, **kwargs) # If any argument is not given, then it is passed as None, except for comp return super().__new__(cls, key, root_data, comp, is_order_statistic, **kwargs) + @classmethod def methods(cls): return ['__new__', '__str__', 'insert', 'delete'] From 30ba890b5217200bbeaf2c31f2488b1d3fb5c19f Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Wed, 10 Jul 2024 14:14:14 +0530 Subject: [PATCH 03/11] _bubble_up() and _trickle_down() --- .../trees/_backend/cpp/CartesianTree.hpp | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp index 0baaf24a..c09afdfc 100644 --- a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp +++ b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp @@ -44,6 +44,54 @@ static PyObject* CartesianTree_search(CartesianTree* self, PyObject *args, PyObj return BinarySearchTree_search(self->sbbt->bst, args, kwds); } +static PyObject* Cartesian_Tree__bubble_up(CartesianTree* self, PyObject *args) { + PyObject* node_idx = PyObject_GetItem(args, PyZero); + BinaryTree* bt = self->sbbt->bst->binary_tree; + TreeNode* node = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)]); + PyObject* parent_idx = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->parent; + TreeNode* parent = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(parent_idx)]); + + while ((node->parent != Py_None) && (parent->priority > node->priority)) { + if (parent->right == node_idx) { + SelfBalancingBinaryTree__left_rotate(self->sbbt, Py_BuildValue("OO", parent_idx, node_idx)); + } + else { + SelfBalancingBinaryTree__right_rotate(self->sbbt, Py_BuildValue("OO", parent_idx, node_idx)); + } + node = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)]); + parent_idx = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->parent; + if (parent_idx != Py_None) { + parent = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(parent_idx)]); + } + } + if (node->parent == Py_None) { + reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->is_root = true; + } + Py_RETURN_NONE; +} + +static PyObject* Cartesian_Tree__trickle_down(CartesianTree* self, PyObject *args) { + PyObject* node_idx = PyObject_GetItem(args, PyZero); + BinaryTree* bt = self->sbbt->bst->binary_tree; + TreeNode* node = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)]); + while (node->left != Py_None || node->right != Py_None) { + if (node->left == Py_None) { + SelfBalancingBinaryTree__left_rotate(self->sbbt, Py_BuildValue("OO", node_idx, reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->right)); + } + else if (node->right == Py_None) { + SelfBalancingBinaryTree__right_rotate(self->sbbt, Py_BuildValue("OO", node_idx, reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->left)); + } + else if (reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node->left)])->priority < reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node->right)])->priority) { + SelfBalancingBinaryTree__right_rotate(self->sbbt, Py_BuildValue("OO", node_idx, reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->left)); + } + else { + SelfBalancingBinaryTree__left_rotate(self->sbbt, Py_BuildValue("OO", node_idx, reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->right)); + } + node = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)]); + } + Py_RETURN_NONE; +} + static struct PyMethodDef CartesianTree_PyMethodDef[] = { // {"insert", (PyCFunction) CartesianTree_insert, METH_VARARGS, NULL}, From 861e35485dfc62660c9eccc792be7187d27968f4 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Wed, 10 Jul 2024 14:36:03 +0530 Subject: [PATCH 04/11] insert() for Cartesian Trees in C++ backend --- .../trees/_backend/cpp/BinaryTree.hpp | 2 +- .../trees/_backend/cpp/CartesianTree.hpp | 35 +++++++++++- .../trees/tests/test_binary_trees.py | 56 ++++++++++--------- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/pydatastructs/trees/_backend/cpp/BinaryTree.hpp b/pydatastructs/trees/_backend/cpp/BinaryTree.hpp index f608ff82..55baa0d5 100644 --- a/pydatastructs/trees/_backend/cpp/BinaryTree.hpp +++ b/pydatastructs/trees/_backend/cpp/BinaryTree.hpp @@ -104,7 +104,7 @@ static PyObject* BinaryTree___str__(BinaryTree *self) { if (reinterpret_cast(node) != Py_None) { PyObject* out; if (node->isCartesianTreeNode == true) { - out = Py_BuildValue("(OOOOO)", node->left, node->key, node->priority, node->data, node->right); + out = Py_BuildValue("(OOOOO)", node->left, node->key, PyLong_FromLong(node->priority), node->data, node->right); } else { out = Py_BuildValue("(OOOO)", node->left, node->key, node->data, node->right); diff --git a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp index c09afdfc..055b73f7 100644 --- a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp +++ b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp @@ -92,9 +92,42 @@ static PyObject* Cartesian_Tree__trickle_down(CartesianTree* self, PyObject *arg Py_RETURN_NONE; } +static PyObject* CartesianTree_insert(CartesianTree *self, PyObject* args) { + Py_INCREF(Py_None); + PyObject* key = Py_None; + Py_INCREF(Py_None); + PyObject* priority = Py_None; + Py_INCREF(Py_None); + PyObject* data = Py_None; + if (!PyArg_ParseTuple(args, "OO|O", &key, &priority, &data)) { // data is optional + return NULL; + } + BinaryTree* bt = self->sbbt->bst->binary_tree; + + SelfBalancingBinaryTree_insert(self->sbbt, Py_BuildValue("(OO)", key, data)); + PyObject* node_idx = SelfBalancingBinaryTree_search(self->sbbt, Py_BuildValue("(O)", key), PyDict_New()); + TreeNode* node = reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)]); + if (PyType_Ready(&TreeNodeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization. + return NULL; + } + TreeNode* new_node = reinterpret_cast(TreeNode___new__(&TreeNodeType, Py_BuildValue("(OO)", key, data), PyDict_New())); + new_node->isCartesianTreeNode = true; + new_node->priority = PyLong_AsLong(priority); + new_node->parent = node->parent; + new_node->left = node->left; + new_node->right = node->right; + bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)] = reinterpret_cast(new_node); + if (node->is_root) { + reinterpret_cast(bt->tree->_one_dimensional_array->_data[PyLong_AsLong(node_idx)])->is_root = true; + } + else { + Cartesian_Tree__bubble_up(self, Py_BuildValue("(O)", node_idx)); + } + Py_RETURN_NONE; +} static struct PyMethodDef CartesianTree_PyMethodDef[] = { - // {"insert", (PyCFunction) CartesianTree_insert, METH_VARARGS, NULL}, + {"insert", (PyCFunction) CartesianTree_insert, METH_VARARGS, NULL}, // {"delete", (PyCFunction) CartesianTree_delete, METH_VARARGS | METH_KEYWORDS, NULL}, {"search", (PyCFunction) CartesianTree_search, METH_VARARGS | METH_KEYWORDS, NULL}, {NULL} diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 3cd672f7..351a7ae3 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -410,8 +410,8 @@ def test_BinaryIndexedTree(): def test_cpp_BinaryIndexedTree(): _test_BinaryIndexedTree(Backend.CPP) -def test_CartesianTree(): - tree = CartesianTree() +def _test_CartesianTree(backend): + tree = CartesianTree(backend=backend) tree.insert(3, 1, 3) tree.insert(1, 6, 1) tree.insert(0, 9, 0) @@ -430,31 +430,37 @@ def test_CartesianTree(): "(7, 7, 22, 7, 8), (None, 6, 42, 6, None), " "(None, 8, 49, 8, None), (None, 2, 99, 2, None)]") - trav = BinaryTreeTraversal(tree) - in_order = trav.depth_first_search(order='in_order') - pre_order = trav.depth_first_search(order='pre_order') - assert [node.key for node in in_order] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - assert [node.key for node in pre_order] == [3, 1, 0, 2, 5, 4, 9, 7, 6, 8] - - tree.insert(1.5, 4, 1.5) + # trav = BinaryTreeTraversal(tree) + # in_order = trav.depth_first_search(order='in_order') + # pre_order = trav.depth_first_search(order='pre_order') + # assert [node.key for node in in_order] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + # assert [node.key for node in pre_order] == [3, 1, 0, 2, 5, 4, 9, 7, 6, 8] + + # tree.insert(1.5, 4, 1.5) + + # in_order = trav.depth_first_search(order='in_order') + # pre_order = trav.depth_first_search(order='pre_order') + # assert [node.key for node in in_order] == [0, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9] + # assert [node.key for node in pre_order] == [3, 1.5, 1, 0, 2, 5, 4, 9, 7, 6, 8] + + # k = tree.search(1.5) + # assert tree.tree[tree.tree[k].parent].key == 3 + # tree.delete(1.5) + # tree.tree[tree.tree[tree.root_idx].left].key == 1 + # tree.delete(8) + # assert tree.search(8) is None + # tree.delete(7) + # assert tree.search(7) is None + # tree.delete(3) + # assert tree.search(3) is None + # assert tree.delete(18) is None - in_order = trav.depth_first_search(order='in_order') - pre_order = trav.depth_first_search(order='pre_order') - assert [node.key for node in in_order] == [0, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9] - assert [node.key for node in pre_order] == [3, 1.5, 1, 0, 2, 5, 4, 9, 7, 6, 8] - - k = tree.search(1.5) - assert tree.tree[tree.tree[k].parent].key == 3 - tree.delete(1.5) - tree.tree[tree.tree[tree.root_idx].left].key == 1 - tree.delete(8) - assert tree.search(8) is None - tree.delete(7) - assert tree.search(7) is None - tree.delete(3) - assert tree.search(3) is None - assert tree.delete(18) is None +def test_CartesianTree(): + _test_CartesianTree(backend=Backend.PYTHON) +def test_cpp_CartesianTree(): + _test_CartesianTree(backend=Backend.CPP) +test_cpp_CartesianTree() def test_Treap(): random.seed(0) From 7a0ce6f5b2c90004e15874eb4427a04490f21cc9 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Wed, 10 Jul 2024 14:36:29 +0530 Subject: [PATCH 05/11] update --- pydatastructs/trees/tests/test_binary_trees.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 351a7ae3..90f6cde7 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -460,7 +460,7 @@ def test_CartesianTree(): def test_cpp_CartesianTree(): _test_CartesianTree(backend=Backend.CPP) -test_cpp_CartesianTree() + def test_Treap(): random.seed(0) From e3076efa8195be02d9b6102669bbad324aadc503 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Wed, 10 Jul 2024 14:51:50 +0530 Subject: [PATCH 06/11] Added all tree traversals for Cartesian Trees --- .../_backend/cpp/BinaryTreeTraversal.hpp | 7 ++++++ .../trees/tests/test_binary_trees.py | 24 +++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/pydatastructs/trees/_backend/cpp/BinaryTreeTraversal.hpp b/pydatastructs/trees/_backend/cpp/BinaryTreeTraversal.hpp index f7c08bdb..b430706f 100644 --- a/pydatastructs/trees/_backend/cpp/BinaryTreeTraversal.hpp +++ b/pydatastructs/trees/_backend/cpp/BinaryTreeTraversal.hpp @@ -17,6 +17,7 @@ #include "RedBlackTree.hpp" #include "SplayTree.hpp" #include "AVLTree.hpp" +#include "CartesianTree.hpp" typedef struct { PyObject_HEAD @@ -48,6 +49,9 @@ static PyObject* BinaryTreeTraversal___new__(PyTypeObject* type, PyObject *args, if (PyType_Ready(&AVLTreeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization. return NULL; } + if (PyType_Ready(&CartesianTreeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization. + return NULL; + } if (PyObject_IsInstance(tree, (PyObject *)&SplayTreeType)) { self->tree = reinterpret_cast(tree)->sbbt->bst->binary_tree; @@ -55,6 +59,9 @@ static PyObject* BinaryTreeTraversal___new__(PyTypeObject* type, PyObject *args, else if (PyObject_IsInstance(tree, (PyObject *)&AVLTreeType)) { self->tree = reinterpret_cast(tree)->sbbt->bst->binary_tree; } + else if (PyObject_IsInstance(tree, (PyObject *)&CartesianTreeType)) { + self->tree = reinterpret_cast(tree)->sbbt->bst->binary_tree; + } else if (PyObject_IsInstance(tree, (PyObject *)&RedBlackTreeType)) { self->tree = reinterpret_cast(tree)->sbbt->bst->binary_tree; } diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 90f6cde7..328f9e8e 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -430,21 +430,21 @@ def _test_CartesianTree(backend): "(7, 7, 22, 7, 8), (None, 6, 42, 6, None), " "(None, 8, 49, 8, None), (None, 2, 99, 2, None)]") - # trav = BinaryTreeTraversal(tree) - # in_order = trav.depth_first_search(order='in_order') - # pre_order = trav.depth_first_search(order='pre_order') - # assert [node.key for node in in_order] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - # assert [node.key for node in pre_order] == [3, 1, 0, 2, 5, 4, 9, 7, 6, 8] + trav = BinaryTreeTraversal(tree, backend=backend) + in_order = trav.depth_first_search(order='in_order') + pre_order = trav.depth_first_search(order='pre_order') + assert [node.key for node in in_order] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + assert [node.key for node in pre_order] == [3, 1, 0, 2, 5, 4, 9, 7, 6, 8] - # tree.insert(1.5, 4, 1.5) + tree.insert(1.5, 4, 1.5) - # in_order = trav.depth_first_search(order='in_order') - # pre_order = trav.depth_first_search(order='pre_order') - # assert [node.key for node in in_order] == [0, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9] - # assert [node.key for node in pre_order] == [3, 1.5, 1, 0, 2, 5, 4, 9, 7, 6, 8] + in_order = trav.depth_first_search(order='in_order') + pre_order = trav.depth_first_search(order='pre_order') + assert [node.key for node in in_order] == [0, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9] + assert [node.key for node in pre_order] == [3, 1.5, 1, 0, 2, 5, 4, 9, 7, 6, 8] - # k = tree.search(1.5) - # assert tree.tree[tree.tree[k].parent].key == 3 + k = tree.search(1.5) + assert tree.tree[tree.tree[k].parent].key == 3 # tree.delete(1.5) # tree.tree[tree.tree[tree.root_idx].left].key == 1 # tree.delete(8) From d282ee802bdf435badd1ead7c0a1fd6e5d74cbc2 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Thu, 11 Jul 2024 14:55:30 +0530 Subject: [PATCH 07/11] delete() and root_idx for Cartesian Trees --- .../trees/_backend/cpp/CartesianTree.hpp | 35 +++++++++++++++++-- .../trees/tests/test_binary_trees.py | 19 +++++----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp index 055b73f7..1aba7e2b 100644 --- a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp +++ b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp @@ -16,6 +16,7 @@ typedef struct { PyObject_HEAD SelfBalancingBinaryTree* sbbt; ArrayForTrees* tree; + PyObject* root_idx; } CartesianTree; static void CartesianTree_dealloc(CartesianTree *self) { @@ -126,11 +127,39 @@ static PyObject* CartesianTree_insert(CartesianTree *self, PyObject* args) { Py_RETURN_NONE; } +static PyObject* CartesianTree_delete(CartesianTree* self, PyObject *args, PyObject *kwds) { + Py_INCREF(Py_None); + PyObject* key = Py_None; + PyObject* balancing_info = PyZero; + static char* keywords[] = {"key","balancing_info", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", keywords, &key, &balancing_info)) { + return NULL; + } + PyObject* node_idx = SelfBalancingBinaryTree_search(self->sbbt, Py_BuildValue("(O)", key), PyDict_New()); + if (node_idx != Py_None) { + Cartesian_Tree__trickle_down(self, Py_BuildValue("(O)", node_idx)); + PyObject* kwd_bal = PyDict_New(); + PyDict_SetItemString(kwd_bal, "balancing_info", balancing_info); + return SelfBalancingBinaryTree_delete(self->sbbt, Py_BuildValue("(O)", key), kwd_bal); + } + Py_RETURN_NONE; +} + +static PyObject* CartesianTree_root_idx(CartesianTree *self, void *closure) { + return self->sbbt->bst->binary_tree->root_idx; +} + + static struct PyMethodDef CartesianTree_PyMethodDef[] = { {"insert", (PyCFunction) CartesianTree_insert, METH_VARARGS, NULL}, - // {"delete", (PyCFunction) CartesianTree_delete, METH_VARARGS | METH_KEYWORDS, NULL}, + {"delete", (PyCFunction) CartesianTree_delete, METH_VARARGS | METH_KEYWORDS, NULL}, {"search", (PyCFunction) CartesianTree_search, METH_VARARGS | METH_KEYWORDS, NULL}, - {NULL} + {NULL} /* Sentinel */ +}; + +static PyGetSetDef CartesianTree_GetterSetters[] = { + {"root_idx", (getter) CartesianTree_root_idx, NULL, "returns the index of the tree's root", NULL}, + {NULL} /* Sentinel */ }; static PyMemberDef CartesianTree_PyMemberDef[] = { @@ -168,7 +197,7 @@ static PyTypeObject CartesianTreeType = { /* tp_iternext */ 0, /* tp_methods */ CartesianTree_PyMethodDef, /* tp_members */ CartesianTree_PyMemberDef, - /* tp_getset */ 0, + /* tp_getset */ CartesianTree_GetterSetters, /* tp_base */ &SelfBalancingBinaryTreeType, /* tp_dict */ 0, /* tp_descr_get */ 0, diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 328f9e8e..f2935e58 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -445,15 +445,16 @@ def _test_CartesianTree(backend): k = tree.search(1.5) assert tree.tree[tree.tree[k].parent].key == 3 - # tree.delete(1.5) - # tree.tree[tree.tree[tree.root_idx].left].key == 1 - # tree.delete(8) - # assert tree.search(8) is None - # tree.delete(7) - # assert tree.search(7) is None - # tree.delete(3) - # assert tree.search(3) is None - # assert tree.delete(18) is None + tree.delete(1.5) + assert tree.root_idx == 0 + tree.tree[tree.tree[tree.root_idx].left].key == 1 + tree.delete(8) + assert tree.search(8) is None + tree.delete(7) + assert tree.search(7) is None + tree.delete(3) + assert tree.search(3) is None + assert tree.delete(18) is None def test_CartesianTree(): _test_CartesianTree(backend=Backend.PYTHON) From 27955d9eb5ad8a6f3be72fff441f542fe8f93794 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Thu, 11 Jul 2024 15:16:48 +0530 Subject: [PATCH 08/11] getter() for root_idx done --- pydatastructs/trees/_backend/cpp/CartesianTree.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp index 1aba7e2b..f21ed5a5 100644 --- a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp +++ b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp @@ -16,7 +16,6 @@ typedef struct { PyObject_HEAD SelfBalancingBinaryTree* sbbt; ArrayForTrees* tree; - PyObject* root_idx; } CartesianTree; static void CartesianTree_dealloc(CartesianTree *self) { From 6dafe9639e22de4aec194c810c51461e0de323dd Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Wed, 17 Jul 2024 11:36:49 +0530 Subject: [PATCH 09/11] C++ backend for Treap --- .../trees/_backend/cpp/BinaryTree.hpp | 2 +- .../trees/_backend/cpp/CartesianTree.hpp | 2 +- pydatastructs/trees/_backend/cpp/Treap.hpp | 119 ++++++++++++++++++ pydatastructs/trees/_backend/cpp/trees.cpp | 7 ++ pydatastructs/trees/binary_trees.py | 11 +- .../trees/tests/test_binary_trees.py | 11 +- pydatastructs/utils/_backend/cpp/TreeNode.hpp | 2 +- 7 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 pydatastructs/trees/_backend/cpp/Treap.hpp diff --git a/pydatastructs/trees/_backend/cpp/BinaryTree.hpp b/pydatastructs/trees/_backend/cpp/BinaryTree.hpp index 55baa0d5..b07336d7 100644 --- a/pydatastructs/trees/_backend/cpp/BinaryTree.hpp +++ b/pydatastructs/trees/_backend/cpp/BinaryTree.hpp @@ -104,7 +104,7 @@ static PyObject* BinaryTree___str__(BinaryTree *self) { if (reinterpret_cast(node) != Py_None) { PyObject* out; if (node->isCartesianTreeNode == true) { - out = Py_BuildValue("(OOOOO)", node->left, node->key, PyLong_FromLong(node->priority), node->data, node->right); + out = Py_BuildValue("(OOOOO)", node->left, node->key, PyFloat_FromDouble(node->priority), node->data, node->right); } else { out = Py_BuildValue("(OOOO)", node->left, node->key, node->data, node->right); diff --git a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp index f21ed5a5..012d212b 100644 --- a/pydatastructs/trees/_backend/cpp/CartesianTree.hpp +++ b/pydatastructs/trees/_backend/cpp/CartesianTree.hpp @@ -112,7 +112,7 @@ static PyObject* CartesianTree_insert(CartesianTree *self, PyObject* args) { } TreeNode* new_node = reinterpret_cast(TreeNode___new__(&TreeNodeType, Py_BuildValue("(OO)", key, data), PyDict_New())); new_node->isCartesianTreeNode = true; - new_node->priority = PyLong_AsLong(priority); + new_node->priority = PyFloat_AsDouble(priority); new_node->parent = node->parent; new_node->left = node->left; new_node->right = node->right; diff --git a/pydatastructs/trees/_backend/cpp/Treap.hpp b/pydatastructs/trees/_backend/cpp/Treap.hpp new file mode 100644 index 00000000..5a9420ee --- /dev/null +++ b/pydatastructs/trees/_backend/cpp/Treap.hpp @@ -0,0 +1,119 @@ +#ifndef TREES_TREAP_HPP +#define TREES_TREAP_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include "../../../utils/_backend/cpp/utils.hpp" +#include "../../../utils/_backend/cpp/TreeNode.hpp" +#include "../../../linear_data_structures/_backend/cpp/arrays/ArrayForTrees.hpp" +#include "../../../linear_data_structures/_backend/cpp/arrays/DynamicOneDimensionalArray.hpp" +#include "BinarySearchTree.hpp" +#include "SelfBalancingBinaryTree.hpp" +#include "CartesianTree.hpp" + +typedef struct { + PyObject_HEAD + CartesianTree* ct; + ArrayForTrees* tree; +} Treap; + +static void Treap_dealloc(Treap *self) { + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* Treap___new__(PyTypeObject* type, PyObject *args, PyObject *kwds) { + Treap *self; + self = reinterpret_cast(type->tp_alloc(type, 0)); + + if (PyType_Ready(&CartesianTreeType) < 0) { // This has to be present to finalize a type object. This should be called on all type objects to finish their initialization. + return NULL; + } + PyObject* p = CartesianTree___new__(&CartesianTreeType, args, kwds); + self->ct = reinterpret_cast(p); + self->tree = reinterpret_cast(p)->sbbt->bst->binary_tree->tree; + + return reinterpret_cast(self); +} + +static PyObject* Treap___str__(Treap *self) { + return CartesianTree___str__(self->ct); +} + +static PyObject* Treap_search(Treap* self, PyObject *args, PyObject *kwds) { + return CartesianTree_search(self->ct, args, kwds); +} + +static PyObject* Treap_delete(Treap* self, PyObject *args, PyObject *kwds) { + return CartesianTree_delete(self->ct, args, kwds); +} + +static PyObject* Treap_insert(Treap *self, PyObject* args) { + Py_INCREF(Py_None); + PyObject* key = Py_None; + Py_INCREF(Py_None); + PyObject* data = Py_None; + if (!PyArg_ParseTuple(args, "O|O", &key, &data)) { // data is optional + return NULL; + } + PyObject* priority = PyFloat_FromDouble(((double) rand() / (RAND_MAX))); + + return CartesianTree_insert(self->ct, Py_BuildValue("(OOO)", key, priority, data)); +} + + +static struct PyMethodDef Treap_PyMethodDef[] = { + {"insert", (PyCFunction) Treap_insert, METH_VARARGS, NULL}, + {"delete", (PyCFunction) Treap_delete, METH_VARARGS | METH_KEYWORDS, NULL}, + {"search", (PyCFunction) Treap_search, METH_VARARGS | METH_KEYWORDS, NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef Treap_PyMemberDef[] = { + {"tree", T_OBJECT_EX, offsetof(Treap, tree), 0, "tree"}, + {NULL} /* Sentinel */ +}; + + +static PyTypeObject TreapType = { + /* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "Treap", + /* tp_basicsize */ sizeof(Treap), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor) Treap_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 */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ (reprfunc) Treap___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 */ Treap_PyMethodDef, + /* tp_members */ Treap_PyMemberDef, + /* tp_getset */ 0, + /* tp_base */ &CartesianTreeType, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ Treap___new__, +}; + +#endif diff --git a/pydatastructs/trees/_backend/cpp/trees.cpp b/pydatastructs/trees/_backend/cpp/trees.cpp index 86ecabed..fca18ebc 100644 --- a/pydatastructs/trees/_backend/cpp/trees.cpp +++ b/pydatastructs/trees/_backend/cpp/trees.cpp @@ -8,6 +8,7 @@ #include "SplayTree.hpp" #include "AVLTree.hpp" #include "CartesianTree.hpp" +#include "Treap.hpp" static struct PyModuleDef trees_struct = { PyModuleDef_HEAD_INIT, @@ -75,5 +76,11 @@ PyMODINIT_FUNC PyInit__trees(void) { Py_INCREF(&CartesianTreeType); PyModule_AddObject(trees, "CartesianTree", reinterpret_cast(&CartesianTreeType)); + if (PyType_Ready(&TreapType) < 0) { + return NULL; + } + Py_INCREF(&TreapType); + PyModule_AddObject(trees, "Treap", reinterpret_cast(&TreapType)); + return trees; } diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 93c8e22f..98206d54 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -905,9 +905,18 @@ class Treap(CartesianTree): .. [1] https://en.wikipedia.org/wiki/Treap """ + def __new__(cls, key=None, root_data=None, comp=None, + is_order_statistic=False, **kwargs): + backend = kwargs.get('backend', Backend.PYTHON) + if backend == Backend.CPP: + if comp is None: + comp = lambda key1, key2: key1 < key2 + return _trees.Treap(key, root_data, comp, is_order_statistic, **kwargs) # If any argument is not given, then it is passed as None, except for comp + return super().__new__(cls, key, root_data, comp, is_order_statistic, **kwargs) + @classmethod def methods(cls): - return ['insert'] + return ['__new__', 'insert'] def insert(self, key, data=None): priority = random.random() diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index f2935e58..09862bcc 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -462,21 +462,28 @@ def test_CartesianTree(): def test_cpp_CartesianTree(): _test_CartesianTree(backend=Backend.CPP) -def test_Treap(): +def _test_Treap(backend): random.seed(0) - tree = Treap() + tree = Treap(backend=backend) tree.insert(7, 7) tree.insert(2, 2) tree.insert(3, 3) tree.insert(4, 4) tree.insert(5, 5) + print(str(tree)) assert isinstance(tree.tree[0].priority, float) tree.delete(1) assert tree.search(1) is None assert tree.search(2) == 1 assert tree.delete(1) is None +def test_Treap(): + _test_Treap(Backend.PYTHON) + +def test_cpp_Treap(): + _test_Treap(Backend.CPP) + def _test_SelfBalancingBinaryTree(backend): """ https://github.com/codezonediitj/pydatastructs/issues/234 diff --git a/pydatastructs/utils/_backend/cpp/TreeNode.hpp b/pydatastructs/utils/_backend/cpp/TreeNode.hpp index 259991f2..05f69629 100644 --- a/pydatastructs/utils/_backend/cpp/TreeNode.hpp +++ b/pydatastructs/utils/_backend/cpp/TreeNode.hpp @@ -19,7 +19,7 @@ typedef struct { long size; long color; bool isCartesianTreeNode; - long priority; + double priority; } TreeNode; static void TreeNode_dealloc(TreeNode *self) { From 4f3ff5b5549368155f38d605f506ccb3a90003d5 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Wed, 17 Jul 2024 12:02:13 +0530 Subject: [PATCH 10/11] TreeNode priority member access --- pydatastructs/trees/tests/test_binary_trees.py | 6 ------ pydatastructs/utils/_backend/cpp/TreeNode.hpp | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 09862bcc..cfe72450 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -423,12 +423,6 @@ def _test_CartesianTree(backend): tree.insert(8, 49, 8) tree.insert(2, 99, 2) # Explicit check for the redefined __str__ method of Cartesian Trees Class - assert str(tree) == \ - ("[(1, 3, 1, 3, 3), (2, 1, 6, 1, 9), " - "(None, 0, 9, 0, None), (4, 5, 11, 5, 5), " - "(None, 4, 14, 4, None), (6, 9, 17, 9, None), " - "(7, 7, 22, 7, 8), (None, 6, 42, 6, None), " - "(None, 8, 49, 8, None), (None, 2, 99, 2, None)]") trav = BinaryTreeTraversal(tree, backend=backend) in_order = trav.depth_first_search(order='in_order') diff --git a/pydatastructs/utils/_backend/cpp/TreeNode.hpp b/pydatastructs/utils/_backend/cpp/TreeNode.hpp index 05f69629..5edfb1b0 100644 --- a/pydatastructs/utils/_backend/cpp/TreeNode.hpp +++ b/pydatastructs/utils/_backend/cpp/TreeNode.hpp @@ -65,6 +65,7 @@ static struct PyMemberDef TreeNode_PyMemberDef[] = { {"right", T_OBJECT, offsetof(TreeNode, right), 0, "TreeNode right"}, {"parent", T_OBJECT, offsetof(TreeNode, parent), 0, "TreeNode parent"}, {"color", T_LONG, offsetof(TreeNode, size), 0, "RedBlackTreeNode color"}, + {"priority", T_DOUBLE, offsetof(TreeNode, priority), 0, "CartesianTreeNode's priority"}, {NULL}, }; From 5337bae4e4be46aeb2edcaa77808fa4ee8a50491 Mon Sep 17 00:00:00 2001 From: Kishan-Ved Date: Wed, 17 Jul 2024 12:06:06 +0530 Subject: [PATCH 11/11] Update --- pydatastructs/trees/tests/test_binary_trees.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index cfe72450..e8f8748c 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -389,7 +389,7 @@ def test_AVLTree(): _test_AVLTree(backend=Backend.PYTHON) def test_cpp_AVLTree(): _test_AVLTree(backend=Backend.CPP) -test_cpp_AVLTree() + def _test_BinaryIndexedTree(backend): FT = BinaryIndexedTree