From 4de2fe90015b26a8ddec349079cba05addc0b057 Mon Sep 17 00:00:00 2001 From: Daniil Bogdanov Date: Sat, 27 Dec 2025 14:20:05 +0300 Subject: [PATCH] Add tests and CI for tree application --- .clang-format | 6 ++ .github/workflows/ci.yml | 42 ++++++++ AVLTree.h | 68 ++++++------- Canvas.h | 15 ++- RedBlackTree.h | 60 ++++++------ SplayTree.h | 64 ++++++------ Vertex.cpp | 22 ++--- Vertex.h | 14 +-- main.cpp | 5 +- mainwindow.cpp | 101 ++++++++++--------- mainwindow.h | 44 +++++---- node.cpp | 8 +- node.h | 14 +-- tests/test_trees.cpp | 206 +++++++++++++++++++++++++++++++++++++++ tests/tests.pro | 28 ++++++ tree.cpp | 6 +- tree.h | 12 +-- 17 files changed, 499 insertions(+), 216 deletions(-) create mode 100644 .clang-format create mode 100644 .github/workflows/ci.yml create mode 100644 tests/test_trees.cpp create mode 100644 tests/tests.pro diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ba18c40 --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 100 +PointerAlignment: Left +DerivePointerAlignment: false +ReflowComments: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7532174 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Qt + run: sudo apt-get update && sudo apt-get install -y qtbase5-dev qttools5-dev-tools qtdeclarative5-dev + - name: Build application + run: | + qmake Trees.pro + make -j2 + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install clang-format + run: sudo apt-get update && sudo apt-get install -y clang-format + - name: Verify formatting + run: clang-format --dry-run --Werror $(find . -name "*.h" -o -name "*.cpp") + + tests: + runs-on: ubuntu-latest + env: + QT_QPA_PLATFORM: offscreen + steps: + - uses: actions/checkout@v4 + - name: Install Qt and tools + run: sudo apt-get update && sudo apt-get install -y qtbase5-dev qttools5-dev-tools qtdeclarative5-dev clang-format + - name: Build tests + run: | + qmake tests/tests.pro -o tests/Makefile + make -C tests -j2 + - name: Run tests + run: ./tests/trees_tests diff --git a/AVLTree.h b/AVLTree.h index e8ae71b..878e49f 100644 --- a/AVLTree.h +++ b/AVLTree.h @@ -2,15 +2,16 @@ #define AVLTREE_H #include + #include #include class AVLNode : public Node { -public: + public: int key; int h; - AVLNode *l; - AVLNode *r; + AVLNode* l; + AVLNode* r; AVLNode() { key = 0; @@ -20,17 +21,17 @@ class AVLNode : public Node { } ~AVLNode() {} - AVLNode *getL() override { return this->l; } - AVLNode *getR() override { return this->r; } + AVLNode* getL() override { return this->l; } + AVLNode* getR() override { return this->r; } int getKey() override { return this->key; } int getType() override { return TTAVL; } }; class AVLTree : public Tree { -public: + public: AVLTree() { root = nullptr; } ~AVLTree() {} - AVLNode *getRoot() override { return root; } + AVLNode* getRoot() override { return root; } void add(int key) override { add(key, this->root); } void drop(int key) override { drop(key, this->root); } void clear() override { @@ -38,10 +39,10 @@ class AVLTree : public Tree { root = nullptr; } -private: - AVLNode *root; + private: + AVLNode* root; - void add(int key, AVLNode *&p) { + void add(int key, AVLNode*& p) { if (p == nullptr) { p = new AVLNode(); p->key = key; @@ -61,7 +62,7 @@ class AVLTree : public Tree { updateHeight(p); } - void drop(int key, AVLNode *&p) { + void drop(int key, AVLNode*& p) { if (p == nullptr) return; if (p->key == key) { if (p->l == nullptr && p->r == nullptr) { @@ -77,7 +78,7 @@ class AVLTree : public Tree { p->r = nullptr; } else { if (p->r->l == nullptr) { - AVLNode **q = &p->r; + AVLNode** q = &p->r; std::swap((*q)->key, p->key); if ((*q)->r != nullptr) { (*q)->key = (*q)->r->key; @@ -90,9 +91,9 @@ class AVLTree : public Tree { int nb = needBalance(p); if (nb) balance(p, nb); } else { - std::stack s; - AVLNode *q = p->r; - AVLNode **z = nullptr; + std::stack s; + AVLNode* q = p->r; + AVLNode** z = nullptr; while (q->l != nullptr) { if (q->l->l == nullptr) { z = &q->l; @@ -111,7 +112,7 @@ class AVLTree : public Tree { } int nb; while (!s.empty()) { - AVLNode *u = s.top(); + AVLNode* u = s.top(); s.pop(); nb = needBalance(u); if (nb) balance(u, nb); @@ -130,14 +131,14 @@ class AVLTree : public Tree { if (nb) balance(p, nb); } - void clear(AVLNode *p) { + void clear(AVLNode* p) { if (p == nullptr) return; clear(p->l); clear(p->r); delete p; } - int needBalance(AVLNode *elem) { + int needBalance(AVLNode* elem) { if (elem == nullptr) throw -1; if (elem->l == nullptr && elem->r == nullptr) { return 0; @@ -165,7 +166,7 @@ class AVLTree : public Tree { } } - void balance(AVLNode *&p, int troubleSide) { + void balance(AVLNode*& p, int troubleSide) { if (troubleSide == -1) { int k = compareHeigth(p->l); if (k == -1) { @@ -183,17 +184,16 @@ class AVLTree : public Tree { } } - void updateHeight(AVLNode *&p) { + void updateHeight(AVLNode*& p) { if (p == nullptr) throw -1; if (p->l == nullptr && p->r == nullptr) { p->h = 1; return; } - p->h = std::max(p->l == nullptr ? 0 : p->l->h, - p->r == nullptr ? 0 : p->r->h) + 1; + p->h = std::max(p->l == nullptr ? 0 : p->l->h, p->r == nullptr ? 0 : p->r->h) + 1; } - int compareHeigth(AVLNode *&p) { + int compareHeigth(AVLNode*& p) { if (p == nullptr) throw -1; if (p->l == nullptr && p->r == nullptr) { return -1; @@ -207,8 +207,8 @@ class AVLTree : public Tree { return ((p->l->h) >= (p->r->h) ? -1 : 1); } - void smallRightTurn(AVLNode *&p) { - AVLNode *q = p->l; + void smallRightTurn(AVLNode*& p) { + AVLNode* q = p->l; p->l = q->r; q->r = p; updateHeight(p); @@ -216,9 +216,9 @@ class AVLTree : public Tree { p = q; } - void bigRightTurn(AVLNode *&p) { - AVLNode *q = p->l; - AVLNode *r = q->r; + void bigRightTurn(AVLNode*& p) { + AVLNode* q = p->l; + AVLNode* r = q->r; p->l = r->r; q->r = r->l; updateHeight(p); @@ -229,8 +229,8 @@ class AVLTree : public Tree { p = r; } - void smallLeftTurn(AVLNode *&p) { - AVLNode *q = p->r; + void smallLeftTurn(AVLNode*& p) { + AVLNode* q = p->r; p->r = q->l; q->l = p; updateHeight(p); @@ -238,9 +238,9 @@ class AVLTree : public Tree { p = q; } - void bigLeftTurn(AVLNode *&p) { - AVLNode *q = p->r; - AVLNode *r = q->l; + void bigLeftTurn(AVLNode*& p) { + AVLNode* q = p->r; + AVLNode* r = q->l; p->r = r->l; q->l = r->r; updateHeight(p); @@ -252,4 +252,4 @@ class AVLTree : public Tree { } }; -#endif // AVLTREE_H +#endif // AVLTREE_H diff --git a/Canvas.h b/Canvas.h index 08870be..3cbd431 100644 --- a/Canvas.h +++ b/Canvas.h @@ -2,16 +2,13 @@ #define CANVAS_H #include - -class Canvas : public QGraphicsView{ +class Canvas : public QGraphicsView { Q_OBJECT -public: - explicit Canvas(int h, int w, QWidget *parent = 0); -protected: + public: + explicit Canvas(int h, int w, QWidget* parent = 0); + + protected: QGraphicsScene m_scene; }; - - - -#endif // CANVAS_H +#endif // CANVAS_H diff --git a/RedBlackTree.h b/RedBlackTree.h index 2215aec..4e95453 100644 --- a/RedBlackTree.h +++ b/RedBlackTree.h @@ -4,21 +4,21 @@ #include class RBNode : public Node { -public: + public: int key = 0; int h = 0; bool b = true; - RBNode *l = nullptr; - RBNode *r = nullptr; - RBNode *p = nullptr; + RBNode* l = nullptr; + RBNode* r = nullptr; + RBNode* p = nullptr; - RBNode(){}; - RBNode(int key, int h, bool b, RBNode *l, RBNode *r, RBNode *p) - : key(key), h(h), b(b), l(l), r(r), p(p) {} - ~RBNode(){}; + RBNode() {}; + RBNode(int key, int h, bool b, RBNode* l, RBNode* r, RBNode* p) + : key(key), h(h), b(b), l(l), r(r), p(p) {} + ~RBNode() {}; - RBNode *getL() override { return this->l; } - RBNode *getR() override { return this->r; } + RBNode* getL() override { return this->l; } + RBNode* getR() override { return this->r; } int getKey() override { return this->key; } int getType() override { return TTRB; } }; @@ -26,7 +26,7 @@ class RBNode : public Node { class RedBlackTree : public Tree { #define NIL &sentinel -public: + public: void add(int data) override { RBNode *current, *parent, *x; current = root; @@ -55,7 +55,7 @@ class RedBlackTree : public Tree { } void drop(int k) override { - RBNode *tmp = findNode(k); + RBNode* tmp = findNode(k); if (tmp != nullptr) { dropNode(tmp); } @@ -66,16 +66,16 @@ class RedBlackTree : public Tree { root = NIL; } - RBNode *getRoot() override { return root; } + RBNode* getRoot() override { return root; } -private: + private: RBNode sentinel = {0, 0, true, NIL, NIL, NIL}; - RBNode *root = NIL; + RBNode* root = NIL; - void afterAdd(RBNode *x) { + void afterAdd(RBNode* x) { while (x != root && x->p->b == 0) { if (x->p == x->p->p->l) { - RBNode *y = x->p->p->r; + RBNode* y = x->p->p->r; if (y->b == 0) { x->p->b = 1; y->b = 1; @@ -91,7 +91,7 @@ class RedBlackTree : public Tree { rightTurn(x->p->p); } } else { - RBNode *y = x->p->p->l; + RBNode* y = x->p->p->l; if (y->b == 0) { x->p->b = 1; y->b = 1; @@ -111,7 +111,7 @@ class RedBlackTree : public Tree { root->b = 1; } - void clear(RBNode *p) { + void clear(RBNode* p) { if (p == nullptr) return; if (p == p->l && p == p->r) return; clear(p->r); @@ -119,8 +119,8 @@ class RedBlackTree : public Tree { delete p; } - RBNode *findNode(int data) { - RBNode *current = root; + RBNode* findNode(int data) { + RBNode* current = root; while (current != NIL) { if (data == current->key) { return current; @@ -131,7 +131,7 @@ class RedBlackTree : public Tree { return nullptr; } - void dropNode(RBNode *z) { + void dropNode(RBNode* z) { RBNode *x, *y; if (!z || z == NIL) return; if (z->l == NIL || z->r == NIL) { @@ -160,10 +160,10 @@ class RedBlackTree : public Tree { delete y; } - void afterDrop(RBNode *x) { + void afterDrop(RBNode* x) { while (x != root && x->b == 1) { if (x == x->p->l) { - RBNode *w = x->p->r; + RBNode* w = x->p->r; if (w->b == 0) { w->b = 1; x->p->b = 0; @@ -187,7 +187,7 @@ class RedBlackTree : public Tree { x = root; } } else { - RBNode *w = x->p->l; + RBNode* w = x->p->l; if (w->b == 0) { w->b = 1; x->p->b = 0; @@ -215,8 +215,8 @@ class RedBlackTree : public Tree { x->b = 1; } - void leftTurn(RBNode *x) { - RBNode *y = x->r; + void leftTurn(RBNode* x) { + RBNode* y = x->r; x->r = y->l; if (y->l != NIL) y->l->p = x; if (y != NIL) y->p = x->p; @@ -233,8 +233,8 @@ class RedBlackTree : public Tree { if (x != NIL) x->p = y; } - void rightTurn(RBNode *x) { - RBNode *y = x->l; + void rightTurn(RBNode* x) { + RBNode* y = x->l; x->l = y->r; if (y->r != NIL) y->r->p = x; if (y != NIL) y->p = x->p; @@ -252,4 +252,4 @@ class RedBlackTree : public Tree { } }; -#endif // REDBLACKTREE_H +#endif // REDBLACKTREE_H diff --git a/SplayTree.h b/SplayTree.h index 7389043..aba31d8 100644 --- a/SplayTree.h +++ b/SplayTree.h @@ -4,53 +4,53 @@ #include class SNode : public Node { - public: + public: int key; - SNode *l; - SNode *r; + SNode* l; + SNode* r; - SNode(){}; - ~SNode(){}; + SNode() {}; + ~SNode() {}; - SNode *getL() override { return this->l; } - SNode *getR() override { return this->r; } + SNode* getL() override { return this->l; } + SNode* getR() override { return this->r; } int getKey() override { return this->key; } int getType() override { return TTSPLAY; } }; class SplayTree : public Tree { -public: - SNode *getRoot() override { return root; } - void add(int k) override { root = add(k, root); } - void drop(int k) override { root = drop(k, root); } - void clear() override { - clear(root); - root = nullptr; - } + public: + SNode* getRoot() override { return root; } + void add(int k) override { root = add(k, root); } + void drop(int k) override { root = drop(k, root); } + void clear() override { + clear(root); + root = nullptr; + } -private: - SNode *root = nullptr; + private: + SNode* root = nullptr; - SNode *RRTurn(SNode *q) { - SNode *p = q->l; + SNode* RRTurn(SNode* q) { + SNode* p = q->l; q->l = p->r; p->r = q; return p; } - SNode *LLTurn(SNode *q) { - SNode *p = q->r; + SNode* LLTurn(SNode* q) { + SNode* p = q->r; q->r = p->l; p->l = q; return p; } - SNode *Splay(int k, SNode *p) { + SNode* Splay(int k, SNode* p) { if (!p) return nullptr; SNode q; q.l = q.r = nullptr; - SNode *LTMax = &q; - SNode *RTMin = &q; + SNode* LTMax = &q; + SNode* RTMin = &q; while (true) { if (k < p->key) { if (!p->l) break; @@ -83,15 +83,15 @@ class SplayTree : public Tree { return p; } - SNode *NN(int k) { - SNode *p = new SNode(); + SNode* NN(int k) { + SNode* p = new SNode(); p->key = k; p->l = p->r = nullptr; return p; } - SNode *add(int k, SNode *p) { - static SNode *q = nullptr; + SNode* add(int k, SNode* p) { + static SNode* q = nullptr; if (!q) { q = NN(k); } else { @@ -120,9 +120,9 @@ class SplayTree : public Tree { return p; } - SNode *drop(int k, SNode *p) { + SNode* drop(int k, SNode* p) { if (!p) return nullptr; - SNode *q; + SNode* q; p = Splay(k, p); if (k != p->key) { return p; @@ -140,11 +140,11 @@ class SplayTree : public Tree { } } - void clear(SNode *p) { + void clear(SNode* p) { if (p == nullptr) return; clear(p->l); clear(p->r); delete p; } }; -#endif // SPLAYTREE_H +#endif // SPLAYTREE_H diff --git a/Vertex.cpp b/Vertex.cpp index c8e5207..fef681d 100644 --- a/Vertex.cpp +++ b/Vertex.cpp @@ -1,24 +1,22 @@ #include "Vertex.h" -Vertex::Vertex(int k, QString colorString, bool isNull) : QGraphicsItem(), key(k), isNull(isNull){ +Vertex::Vertex(int k, QString colorString, bool isNull) : QGraphicsItem(), key(k), isNull(isNull) { if (colorString == "avl") { color = QColor::fromRgb(50, 205, 50); } else if (colorString == "red") { color = Qt::red; } else if (colorString == "black") { color = Qt::black; - } else if (colorString == "splay"){ + } else if (colorString == "splay") { color = QColor::fromRgb(106, 90, 205); } } -Vertex::~Vertex(){} +Vertex::~Vertex() {} -QRectF Vertex::boundingRect() const { - return QRectF(0, 0, 50, 50); -} +QRectF Vertex::boundingRect() const { return QRectF(0, 0, 50, 50); } -void Vertex::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { +void Vertex::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) @@ -35,26 +33,26 @@ void Vertex::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW QFont f; QString s = QString::number(key); int sz = s.length() - 1; - int x = -4 - 4*sz, y = 5; + int x = -4 - 4 * sz, y = 5; f.setFamily("Trebuchet MS"); f.setPixelSize(15); if (sz > 8) { f.setPixelSize(8); - x = -2*sz - 3; + x = -2 * sz - 3; y = 3; } else if (sz > 7) { f.setPixelSize(10); - x = -2*sz - 6; + x = -2 * sz - 6; y = 4; } else if (sz > 5) { f.setPixelSize(12); - x = -3*sz - 3; + x = -3 * sz - 3; } painter->setFont(f); painter->setPen(pen); - painter->drawText(QPoint(x,y), QString::number(key)); + painter->drawText(QPoint(x, y), QString::number(key)); } } diff --git a/Vertex.h b/Vertex.h index 6af0984..1a4dc88 100644 --- a/Vertex.h +++ b/Vertex.h @@ -4,20 +4,20 @@ #include #include -class Vertex : public QGraphicsItem{ - -public: +class Vertex : public QGraphicsItem { + public: Vertex(int k, QString colorString = "default", bool isNull = false); ~Vertex(); -protected: + protected: QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, + QWidget* widget = nullptr) override; -private: + private: int key; QColor color; bool isNull; }; -#endif // VERTEX_H +#endif // VERTEX_H diff --git a/main.cpp b/main.cpp index 62f93d8..f701578 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,8 @@ -#include "mainwindow.h" #include -int main(int argc, char *argv[]) { +#include "mainwindow.h" + +int main(int argc, char* argv[]) { QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication a(argc, argv); MainWindow w; diff --git a/mainwindow.cpp b/mainwindow.cpp index 8537312..846e27b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,12 +1,16 @@ #include "mainwindow.h" -#include "ui_mainwindow.h" -#include + #include +#include + +#include "ui_mainwindow.h" -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), ui(new Ui::MainWindow) { +MainWindow::MainWindow(QWidget* parent, bool showOnStart) + : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); - this->showMaximized(); + if (showOnStart) { + this->showMaximized(); + } Scene = new QGraphicsScene(); Scene->setStickyFocus(true); @@ -26,90 +30,89 @@ MainWindow::MainWindow(QWidget *parent) : tree = new AVLTree(); } -MainWindow::~MainWindow() { - delete ui; -} +MainWindow::~MainWindow() { delete ui; } + +Tree* MainWindow::currentTree() const { return tree; } -template void MainWindow::presketch(T *p) { +template +void MainWindow::presketch(T* p) { Scene->clear(); delete Scene; Scene = new QGraphicsScene(); ui->treeView->setScene(Scene); if (p == nullptr) return; if (p->getType() == TTAVL) { - AVLNode *q = dynamic_cast (p); + AVLNode* q = dynamic_cast(p); RightEnd = sketch(q, 0, -RightEnd).first; } else if (p->getType() == TTRB) { - RBNode *q = dynamic_cast (p); + RBNode* q = dynamic_cast(p); RightEnd = sketch(q, 0, -RightEnd).first; } else if (p->getType() == TTSPLAY) { - SNode *q = dynamic_cast (p); + SNode* q = dynamic_cast(p); RightEnd = sketch(q, 0, -RightEnd).first; } } -QPair MainWindow::sketch(RBNode *p, int h, int y) { +QPair MainWindow::sketch(RBNode* p, int h, int y) { if ((p == p->l && p == p->r) || p == nullptr) { - QGraphicsItem *item = new Vertex(0, "black", true); + QGraphicsItem* item = new Vertex(0, "black", true); Scene->addItem(item); item->setPos(v(y), v(h)); return {y, y}; } - QPair lnr = sketch(p->l, h-3*R, y); - Scene->addLine(lnr.first+2*R, v(h), lnr.second, h-2*R); - QPair nr = sketch(p->r, h-3*R, lnr.first+4*R); - Scene->addLine(lnr.first+2*R, v(h), nr.second, h-2*R); - QGraphicsItem *item = new Vertex(p->key, (p->b ? "black" : "red")); + QPair lnr = sketch(p->l, h - 3 * R, y); + Scene->addLine(lnr.first + 2 * R, v(h), lnr.second, h - 2 * R); + QPair nr = sketch(p->r, h - 3 * R, lnr.first + 4 * R); + Scene->addLine(lnr.first + 2 * R, v(h), nr.second, h - 2 * R); + QGraphicsItem* item = new Vertex(p->key, (p->b ? "black" : "red")); Scene->addItem(item); - item->setPos(v(lnr.first+2*R), v(h)); - return {nr.first, lnr.first+2*R}; + item->setPos(v(lnr.first + 2 * R), v(h)); + return {nr.first, lnr.first + 2 * R}; } -QPair MainWindow::sketch(AVLNode *p, int h, int y) { +QPair MainWindow::sketch(AVLNode* p, int h, int y) { if (p == nullptr) return {y, y}; - QPair lnr = sketch(p->l, h-3*R, y); + QPair lnr = sketch(p->l, h - 3 * R, y); if (p->l) { - Scene->addLine(lnr.first+2*R, v(h), lnr.second, h-2*R); + Scene->addLine(lnr.first + 2 * R, v(h), lnr.second, h - 2 * R); } - QPair nr = sketch(p->r, h-3*R, lnr.first+2*R); + QPair nr = sketch(p->r, h - 3 * R, lnr.first + 2 * R); if (p->r) { - Scene->addLine(lnr.first+2*R, v(h), nr.second, h-2*R); + Scene->addLine(lnr.first + 2 * R, v(h), nr.second, h - 2 * R); } - QGraphicsItem *item = new Vertex(p->key, "avl"); + QGraphicsItem* item = new Vertex(p->key, "avl"); Scene->addItem(item); - item->setPos(v(lnr.first+2*R), v(h)); - return {nr.first, lnr.first+2*R}; + item->setPos(v(lnr.first + 2 * R), v(h)); + return {nr.first, lnr.first + 2 * R}; } -QPair MainWindow::sketch(SNode *p, int h, int y) { +QPair MainWindow::sketch(SNode* p, int h, int y) { if (p == nullptr) return {y, y}; - QPair lnr = sketch(p->l, h-3*R, y); + QPair lnr = sketch(p->l, h - 3 * R, y); if (p->l) { - Scene->addLine(lnr.first+2*R, v(h), lnr.second, h-2*R); + Scene->addLine(lnr.first + 2 * R, v(h), lnr.second, h - 2 * R); } - QPair nr = sketch(p->r, h-3*R, lnr.first+2*R); + QPair nr = sketch(p->r, h - 3 * R, lnr.first + 2 * R); if (p->r) { - Scene->addLine(lnr.first+2*R, v(h), nr.second, h-2*R); + Scene->addLine(lnr.first + 2 * R, v(h), nr.second, h - 2 * R); } - QGraphicsItem *item = new Vertex(p->key, "splay"); + QGraphicsItem* item = new Vertex(p->key, "splay"); Scene->addItem(item); - item->setPos(lnr.first+R, v(h)); - return {nr.first, lnr.first+2*R}; + item->setPos(lnr.first + R, v(h)); + return {nr.first, lnr.first + 2 * R}; } -int MainWindow::v(int x) { - return x - R; -} +int MainWindow::v(int x) { return x - R; } -void MainWindow::toAnother(Tree *targetTree) { +void MainWindow::toAnother(Tree* targetTree) { copyTree(tree->getRoot(), targetTree); tree->clear(); tree = targetTree; presketch(tree->getRoot()); } -void MainWindow::copyTree(Node *p, Tree *targetTree) { +void MainWindow::copyTree(Node* p, Tree* targetTree) { if (p == nullptr || (p == p->getL() && p == p->getR())) return; copyTree(p->getL(), targetTree); copyTree(p->getR(), targetTree); @@ -142,26 +145,22 @@ void MainWindow::on_btn_del_clicked() { presketch(tree->getRoot()); } -void MainWindow::on_btn_z_plus_clicked() { - ui->treeView->scale(1.1, 1.1); -} +void MainWindow::on_btn_z_plus_clicked() { ui->treeView->scale(1.1, 1.1); } -void MainWindow::on_btn_z_minus_clicked() { - ui->treeView->scale(1/1.1, 1/1.1); -} +void MainWindow::on_btn_z_minus_clicked() { ui->treeView->scale(1 / 1.1, 1 / 1.1); } void MainWindow::on_rbtn_AVL_clicked() { - AVLTree *newTree = new AVLTree(); + AVLTree* newTree = new AVLTree(); toAnother(newTree); } void MainWindow::on_rbtn_RB_clicked() { - RedBlackTree *newTree = new RedBlackTree(); + RedBlackTree* newTree = new RedBlackTree(); toAnother(newTree); } void MainWindow::on_rbtn_S_clicked() { - SplayTree *newTree = new SplayTree(); + SplayTree* newTree = new SplayTree(); toAnother(newTree); } diff --git a/mainwindow.h b/mainwindow.h index aaa6c65..d69ad43 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -2,33 +2,39 @@ #define MAINWINDOW_H #include +#include + +#include "AVLTree.h" #include "RedBlackTree.h" #include "SplayTree.h" -#include "AVLTree.h" #include "Vertex.h" QT_BEGIN_NAMESPACE -namespace Ui { class MainWindow; } +namespace Ui { +class MainWindow; +} QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT -public: - MainWindow(QWidget *parent = nullptr); + public: + MainWindow(QWidget* parent = nullptr, bool showOnStart = true); ~MainWindow(); - template void presketch(T *p); - void copyTree(Node *p, Tree *targetTree); - void toAnother(Tree *targetTree); + template + void presketch(T* p); + void copyTree(Node* p, Tree* targetTree); + void toAnother(Tree* targetTree); + Tree* currentTree() const; - QPair sketch(AVLNode *p, int h, int y); - QPair sketch(RBNode *p, int h, int y); - QPair sketch(SNode *p, int h, int y); + QPair sketch(AVLNode* p, int h, int y); + QPair sketch(RBNode* p, int h, int y); + QPair sketch(SNode* p, int h, int y); - int v(int x); + int v(int x); -private slots: + private slots: void on_btn_add_clicked(); void on_btn_del_clicked(); void on_btn_z_plus_clicked(); @@ -39,16 +45,16 @@ private slots: void on_btnClear_clicked(); void on_btnTest_clicked(); -private: - //constants + private: + // constants const int R = 25; const int TestSize = 100; - const int TestDelay = 25; //ms + const int TestDelay = 25; // ms int RightEnd = 0; - Ui::MainWindow *ui; - QGraphicsScene *Scene; - Tree *tree; + Ui::MainWindow* ui; + QGraphicsScene* Scene; + Tree* tree; }; -#endif // MAINWINDOW_H +#endif // MAINWINDOW_H diff --git a/node.cpp b/node.cpp index fe62124..916a191 100644 --- a/node.cpp +++ b/node.cpp @@ -1,8 +1,8 @@ #include -Node::Node() {} -Node::~Node() {} +Node::Node() : l(nullptr), r(nullptr), key(0) {} +Node::~Node() = default; int Node::getType() { return -1; } -Node *Node::getL() { return this->l; } -Node *Node::getR() { return this->r; } +Node* Node::getL() { return this->l; } +Node* Node::getR() { return this->r; } int Node::getKey() { return this->key; } diff --git a/node.h b/node.h index d5d6f58..d034971 100644 --- a/node.h +++ b/node.h @@ -2,19 +2,19 @@ #define NODE_H class Node { -public: + public: Node(); virtual ~Node(); virtual int getType(); - virtual Node *getL(); - virtual Node *getR(); + virtual Node* getL(); + virtual Node* getR(); virtual int getKey(); -private: - Node *l; - Node *r; + private: + Node* l; + Node* r; int key; }; -#endif // NODE_H +#endif // NODE_H diff --git a/tests/test_trees.cpp b/tests/test_trees.cpp new file mode 100644 index 0000000..68deb4c --- /dev/null +++ b/tests/test_trees.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include + +#include "../AVLTree.h" +#include "../RedBlackTree.h" +#include "../SplayTree.h" +#include "../mainwindow.h" + +namespace { +bool isNil(RBNode* node) { return node && node == node->l && node == node->r; } + +RBNode* findNil(RBNode* node) { + if (!node) { + return nullptr; + } + if (isNil(node)) { + return node; + } + RBNode* candidate = findNil(node->l); + if (candidate) { + return candidate; + } + return findNil(node->r); +} + +void collectKeys(Node* node, QVector& keys) { + if (!node) { + return; + } + if (node->getType() == TTRB) { + RBNode* rbNode = dynamic_cast(node); + if (isNil(rbNode)) { + return; + } + collectKeys(rbNode->l, keys); + keys.append(rbNode->key); + collectKeys(rbNode->r, keys); + return; + } + collectKeys(node->getL(), keys); + keys.append(node->getKey()); + collectKeys(node->getR(), keys); +} + +int verifyAvlHeights(AVLNode* node) { + if (!node) { + return 0; + } + int leftHeight = verifyAvlHeights(node->l); + int rightHeight = verifyAvlHeights(node->r); + int expectedHeight = std::max(leftHeight, rightHeight) + 1; + QCOMPARE(node->h, expectedHeight); + QVERIFY(std::abs(leftHeight - rightHeight) <= 1); + return expectedHeight; +} + +void verifyRbProperties(RBNode* node, RBNode* nil, std::optional min, std::optional max, + int currentBlack, int expectedBlack) { + if (!node || node == nil) { + QCOMPARE(currentBlack + 1, expectedBlack); + return; + } + + if (min.has_value()) { + QVERIFY(node->key > min.value()); + } + if (max.has_value()) { + QVERIFY(node->key < max.value()); + } + + if (node->b == 0) { + QVERIFY(node->l == nil || node->l->b == 1); + QVERIFY(node->r == nil || node->r->b == 1); + } + + int nextBlack = currentBlack + (node->b ? 1 : 0); + verifyRbProperties(node->l, nil, min, node->key, nextBlack, expectedBlack); + verifyRbProperties(node->r, nil, node->key, max, nextBlack, expectedBlack); +} + +int expectedBlackHeight(RBNode* root, RBNode* nil) { + int blackHeight = 1; // account for NIL leaf + RBNode* current = root; + while (current && current != nil) { + if (current->b) { + ++blackHeight; + } + current = current->l; + } + return blackHeight; +} +} // namespace + +class TreesTest : public QObject { + Q_OBJECT + + private slots: + void testAvlInsertionBalance(); + void testAvlDeletionBalance(); + void testRedBlackPropertiesAfterOperations(); + void testSplayRootAfterAccess(); + void testApplicationCopyAndConvert(); +}; + +void TreesTest::testAvlInsertionBalance() { + AVLTree tree; + const QList values{10, 20, 30, 40, 50, 25}; + for (int value : values) { + tree.add(value); + } + + QVector keys; + collectKeys(tree.getRoot(), keys); + QVERIFY(std::is_sorted(keys.begin(), keys.end())); + verifyAvlHeights(tree.getRoot()); +} + +void TreesTest::testAvlDeletionBalance() { + AVLTree tree; + const QList values{1, 2, 3, 4, 5, 6, 7}; + for (int value : values) { + tree.add(value); + } + + tree.drop(4); + tree.drop(1); + + QVector keys; + collectKeys(tree.getRoot(), keys); + QVERIFY(std::is_sorted(keys.begin(), keys.end())); + QVERIFY(!keys.contains(4)); + QVERIFY(!keys.contains(1)); + verifyAvlHeights(tree.getRoot()); +} + +void TreesTest::testRedBlackPropertiesAfterOperations() { + RedBlackTree tree; + const QList values{7, 3, 18, 10, 22, 8, 11, 26}; + for (int value : values) { + tree.add(value); + } + + tree.drop(18); + QVector keys; + collectKeys(tree.getRoot(), keys); + QVERIFY(std::is_sorted(keys.begin(), keys.end())); + QVERIFY(!keys.contains(18)); + + RBNode* nil = findNil(tree.getRoot()); + QVERIFY(nil != nullptr); + QVERIFY(tree.getRoot()->b == 1); + int blackHeight = expectedBlackHeight(tree.getRoot(), nil); + verifyRbProperties(tree.getRoot(), nil, std::nullopt, std::nullopt, 0, blackHeight); +} + +void TreesTest::testSplayRootAfterAccess() { + SplayTree tree; + const QList values{5, 9, 1, 7, 3}; + for (int value : values) { + tree.add(value); + QCOMPARE(tree.getRoot()->key, value); + } + + tree.drop(7); + QVector keys; + collectKeys(tree.getRoot(), keys); + QVERIFY(std::is_sorted(keys.begin(), keys.end())); + QVERIFY(!keys.contains(7)); +} + +void TreesTest::testApplicationCopyAndConvert() { + MainWindow window(nullptr, false); + Tree* initialTree = window.currentTree(); + const QList values{15, 2, 9, 30, 25}; + for (int value : values) { + initialTree->add(value); + } + + RedBlackTree rbTree; + window.copyTree(initialTree->getRoot(), &rbTree); + + QVector originalKeys; + collectKeys(initialTree->getRoot(), originalKeys); + QVector copiedKeys; + collectKeys(rbTree.getRoot(), copiedKeys); + QCOMPARE(copiedKeys, originalKeys); + + auto* splayTree = new SplayTree(); + window.toAnother(splayTree); + QVector convertedKeys; + collectKeys(window.currentTree()->getRoot(), convertedKeys); + QCOMPARE(convertedKeys, originalKeys); + QCOMPARE(window.currentTree()->getRoot()->getType(), TTSPLAY); +} + +int main(int argc, char** argv) { + qputenv("QT_QPA_PLATFORM", QByteArray("offscreen")); + QApplication app(argc, argv); + TreesTest tc; + return QTest::qExec(&tc, argc, argv); +} + +#include "test_trees.moc" diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..4625e88 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,28 @@ +QT += core gui testlib widgets +CONFIG += qt warn_on console c++11 +TEMPLATE = app +TARGET = trees_tests + +INCLUDEPATH += .. + +SOURCES += \ + test_trees.cpp \ + ../mainwindow.cpp \ + ../node.cpp \ + ../tree.cpp \ + ../Vertex.cpp + +HEADERS += \ + ../AVLTree.h \ + ../RedBlackTree.h \ + ../SplayTree.h \ + ../mainwindow.h \ + ../node.h \ + ../tree.h \ + ../Vertex.h + +FORMS += \ + ../mainwindow.ui + +RESOURCES += \ + ../src.qrc diff --git a/tree.cpp b/tree.cpp index aac3c8e..e293d2c 100644 --- a/tree.cpp +++ b/tree.cpp @@ -1,8 +1,8 @@ #include -Tree::Tree() {} -Tree::~Tree() {} +Tree::Tree() : root(nullptr) {} +Tree::~Tree() = default; void Tree::add(int) {} void Tree::drop(int) {} void Tree::clear() {} -Node *Tree::getRoot() { return this->root; } +Node* Tree::getRoot() { return this->root; } diff --git a/tree.h b/tree.h index dfa85f3..8b49040 100644 --- a/tree.h +++ b/tree.h @@ -4,23 +4,23 @@ #include "node.h" enum { - //TT - Tree Type + // TT - Tree Type TTAVL = 1, TTRB = 2, TTSPLAY = 3 }; class Tree { -public: + public: Tree(); virtual ~Tree(); - virtual Node *getRoot(); + virtual Node* getRoot(); virtual void add(int); virtual void drop(int); virtual void clear(); -private: - Node *root; + private: + Node* root; }; -#endif // TREE_H +#endif // TREE_H