From e05386eafe5d230c2d23048101f54bb6336ea094 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 23 Aug 2025 12:27:14 +0100 Subject: [PATCH] Tree iterator first commit --- include/duckhandy/implem/tree_iterator.inl | 252 +++++++++++++++++++++ include/duckhandy/tree_iterator.hpp | 152 +++++++++++++ test/unit/meson.build | 1 + test/unit/tree_iterator_test.cpp | 136 +++++++++++ 4 files changed, 541 insertions(+) create mode 100644 include/duckhandy/implem/tree_iterator.inl create mode 100644 include/duckhandy/tree_iterator.hpp create mode 100644 test/unit/tree_iterator_test.cpp diff --git a/include/duckhandy/implem/tree_iterator.inl b/include/duckhandy/implem/tree_iterator.inl new file mode 100644 index 0000000..eb2f61b --- /dev/null +++ b/include/duckhandy/implem/tree_iterator.inl @@ -0,0 +1,252 @@ +/* Copyright 2016-2024 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +namespace dhandy { + namespace implem { + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + bool TreeIterator_base

::Exhausted() const { + return m_stack.empty(); + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + TreeIterator_base

::TreeIterator_base (const TreeIterator_base& parOther) : + m_stack(parOther.m_stack) + { + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + template + TreeIterator_base

::TreeIterator_base (const TreeIterator_base& parOther) { + typename TreeIterator_base::StackType otherStackCopy(parOther.m_stack); + std::vector

localCopy; + localCopy.reserve(parOther.m_stack.size()); + while (not otherStackCopy.empty()) { + P convertedItem = otherStackCopy.top(); + localCopy.push_back(convertedItem); + otherStackCopy.pop(); + } + m_stack.reserve(parOther.m_stack.capacity()); + for (typename std::vector

::reverse_iterator itRev = localCopy.rbegin(), itRevEnd = localCopy.rend(); itRev != itRevEnd; ++itRev) { + assert(m_stack.capacity() > m_stack.size()); + m_stack.push(*itRev); + } + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::reference TreeIterator_const_layer::operator* () { + assert(not this->Exhausted()); + return this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_reference TreeIterator_const_layer::operator* () const { + assert(not this->Exhausted()); + return this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::pointer TreeIterator_const_layer::operator-> () { + assert(not this->Exhausted()); + return &this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_pointer TreeIterator_const_layer::operator-> () const { + assert(not this->Exhausted()); + return &this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + N* TreeIterator_const_layer::GetPointer() { + assert(not this->Exhausted()); + return this->m_stack.top(); + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + const N* TreeIterator_const_layer::GetPointer() const { + assert(not this->Exhausted()); + return this->m_stack.top(); + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_reference TreeIterator_const_layer::operator* () const { + assert(not this->Exhausted()); + return this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_pointer TreeIterator_const_layer::operator-> () const { + assert(not this->Exhausted()); + return &this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + const N* TreeIterator_const_layer::GetPointer() const { + assert(not this->Exhausted()); + return this->m_stack.top(); + } + } //namespace implem + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + TreeIterator::TreeIterator() { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + TreeIterator::TreeIterator (const TreeIterator& parOther) : + parent_type(parOther) + { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + template + TreeIterator::TreeIterator (const TreeIterator& parOther) : + parent_type(parOther) + { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + template + TreeIterator::TreeIterator (S parCopyStackBottomUp, size_type parStackLen, size_type parMaxDepthHint) { + assert(parStackLen > 0); + this->m_stack.reserve(std::max(parStackLen, parMaxDepthHint)); + typename StackType::value_type prevNode = *parCopyStackBottomUp; + ++parCopyStackBottomUp; + this->m_stack.push(prevNode); + + for (size_type z = 1; z < parStackLen; ++z) { + typename StackType::value_type currNode = *parCopyStackBottomUp; + if (prevNode->left == currNode) { + assert(this->m_stack.capacity() > this->m_stack.size()); + this->m_stack.push(currNode); + } + else { + //If you get this assertion make sure the iterator you are + //passing in is reversed (ie: from leaf to root) + assert(currNode == prevNode->right); + } + + prevNode = currNode; + ++parCopyStackBottomUp; + } + assert(not this->Exhausted()); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + TreeIterator::TreeIterator (NodeTypePointer parRoot, size_type parMaxDepthHint) { + if (parMaxDepthHint > 0) + this->m_stack.reserve(parMaxDepthHint); + RecurseLeft(parRoot); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + TreeIterator::~TreeIterator() { + } + + ///------------------------------------------------------------------------- + ///Post-increment + ///------------------------------------------------------------------------- + template + TreeIterator TreeIterator::operator++ (int) { + assert(not this->Exhausted()); + TreeIterator retVal = *this; + ++(*this); + return retVal; + } + + ///------------------------------------------------------------------------- + ///Pre-increment + ///------------------------------------------------------------------------- + template + TreeIterator& TreeIterator::operator++() { + assert(not this->Exhausted()); + NodeTypePointer currNode = this->m_stack.top(); +#if defined(ASSERTIONSENABLED) + const size_type stackCapacity = this->m_stack.capacity(); +#endif + this->m_stack.pop(); +#if defined(ASSERTIONSENABLED) + //It shouldn't normally happen, but it's just to make sure + assert(stackCapacity == this->m_stack.capacity()); +#endif + RecurseLeft(currNode->right); + return *this; + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + const TreeIterator& TreeIterator::operator= (const TreeIterator& parOther) { + this->m_stack = parOther.m_stack; + assert(this->m_stack.capacity() >= parOther.m_stack.capacity()); + return *this; + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + bool TreeIterator::operator== (const TreeIterator& parOther) const { + return this->m_stack.size() == parOther.m_stack.size() and (this->m_stack.empty() or parOther.m_stack.top() == this->m_stack.top()); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + void TreeIterator::RecurseLeft (NodeTypePointer parFrom) { + NodeTypePointer currNode = parFrom; + while (NULL != currNode) { + assert(this->m_stack.capacity() > this->m_stack.size()); + this->m_stack.push(currNode); + currNode = currNode->left; + } + } +} //namespace dhandy diff --git a/include/duckhandy/tree_iterator.hpp b/include/duckhandy/tree_iterator.hpp new file mode 100644 index 0000000..40b3e41 --- /dev/null +++ b/include/duckhandy/tree_iterator.hpp @@ -0,0 +1,152 @@ +/* Copyright 2016-2024 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +#ifndef id6109D5EDE99D43C4909F084A231BF2C2 +#define id6109D5EDE99D43C4909F084A231BF2C2 + +#include +#include +#include +#include +#include + +namespace dhandy { + namespace implem { + template + class TreeIteratorStack : public std::stack> { + public: + void reserve (std::size_t size) { + std::stack>::c.reserve(size); + } + + std::size_t capacity() const { + return std::stack>::c.capacity(); + } + private: + }; + + template + class TreeIterator_base { + template friend class TreeIterator_base; + public: + explicit TreeIterator_base ( void ) {} + TreeIterator_base ( const TreeIterator_base& parOther ); + template + explicit TreeIterator_base ( const TreeIterator_base& parOther ); + ~TreeIterator_base ( void ) {} + protected: + typedef TreeIteratorStack

StackType; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + bool Exhausted ( void ) const; + StackType m_stack; + }; + + template + class TreeIterator_const_layer; + template + class TreeIterator_const_layer : protected TreeIterator_base { + template friend class TreeIterator_const_layer; + typedef TreeIterator_base parent_type; + public: + typedef const T* pointer; + typedef const T* const_pointer; + typedef const T& reference; + typedef const T& const_reference; + typedef typename parent_type::size_type size_type; + typedef typename parent_type::difference_type difference_type; + typedef N NodeType; + typedef const N* NodeTypePointer; + enum { IS_CONST = 1 }; + TreeIterator_const_layer ( void ) {} + TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {} + template + explicit TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {} + const_reference operator* ( void ) const; + const_pointer operator-> ( void ) const; + const N* GetPointer ( void ) const; + protected: + typedef typename parent_type::StackType StackType; + }; + template + class TreeIterator_const_layer : protected TreeIterator_base { + template friend class TreeIterator_const_layer; + typedef TreeIterator_base parent_type; + public: + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef typename parent_type::size_type size_type; + typedef typename parent_type::difference_type difference_type; + typedef N NodeType; + typedef N* NodeTypePointer; + enum { IS_CONST = 0 }; + TreeIterator_const_layer ( void ) {} + TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {} + template + explicit TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {} + reference operator* ( void ); + const_reference operator* ( void ) const; + pointer operator-> ( void ); + const_pointer operator-> ( void ) const; + const N* GetPointer ( void ) const; + N* GetPointer ( void ); + protected: + typedef typename parent_type::StackType StackType; + }; + } //namespace implem + + template + class TreeIterator : public implem::TreeIterator_const_layer::value> { + typedef implem::TreeIterator_const_layer::value> parent_type; + typedef typename parent_type::NodeTypePointer NodeTypePointer; + typedef typename parent_type::NodeType NodeType; + typedef typename parent_type::StackType StackType; + public: + typedef T value_type; + typedef std::forward_iterator_tag iterator_category; + typedef typename parent_type::difference_type difference_type; + typedef typename parent_type::size_type size_type; + typedef typename parent_type::pointer pointer; + typedef typename parent_type::const_pointer const_pointer; + typedef typename parent_type::reference reference; + typedef typename parent_type::const_reference const_reference; + + TreeIterator ( void ); + TreeIterator ( const TreeIterator& parOther ); + TreeIterator ( NodeTypePointer parRoot, size_type parMaxDepthHint ); + template + TreeIterator ( S parCopyStackBottomUp, size_type parStackLen, size_type parMaxDepthHint ); + template + TreeIterator ( const TreeIterator& parOther ); + ~TreeIterator ( void ); + + const TreeIterator& operator= ( const TreeIterator& parOther ); + bool operator== ( const TreeIterator& parOther ) const; + bool operator!= ( const TreeIterator& parOther ) const { return not (*this == parOther); } + TreeIterator& operator++ ( void ); //pre + TreeIterator operator++ ( int ); //post + + private: + void RecurseLeft ( NodeTypePointer parFrom ); + }; +} //namespace dhandy + +#include "implem/tree_iterator.inl" + +#endif diff --git a/test/unit/meson.build b/test/unit/meson.build index 5ec1bd0..2373f86 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -10,6 +10,7 @@ unit_test_prog = executable(meson.project_name(), 'version_test.cpp', 'tiger_test.cpp', 'infix_iterator.cpp', + 'tree_iterator_test.cpp', install: false, dependencies: [sprout_dep, catch2_dep], include_directories: [public_incl], diff --git a/test/unit/tree_iterator_test.cpp b/test/unit/tree_iterator_test.cpp new file mode 100644 index 0000000..b0533a4 --- /dev/null +++ b/test/unit/tree_iterator_test.cpp @@ -0,0 +1,136 @@ +/* Copyright 2016-2024 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +#include "catch2/catch_test_macros.hpp" +#include "duckhandy/tree_iterator.hpp" + +namespace { + +enum Letters : unsigned int { + A = 'a', B, C, D, E, F, G, H, I +}; + +struct TestNode { + TestNode (unsigned int val) : content(val) {} + TestNode* left{nullptr}, *right{nullptr}; + unsigned int content{0}; +}; +} //unnamed namespace + +TEST_CASE("Check TreeIterator", "[TreeIterator][containers][iterator]") { + typedef dhandy::TreeIterator TestIterator; + + TestIterator empty; + + { + TestNode root{0xDEADBEEFu}; + TestIterator it{&root, 1}; + CHECK(*it == 0xDEADBEEFu); + + ++it; + CHECK(it == empty); + } + + { + TestNode root{A}; + TestNode l{B}, ll{C}, lr{D}; + TestNode r{E}, rl{F}, rr{G}, rll{H}, rrr{I}; + + root.left = &l; + root.right = &r; + l.left = ≪ + l.right = &lr; + r.left = &rl; + r.right = &rr; + rl.left = &rll; + rr.right = &rrr; + + TestIterator it{&root, 4}; + CHECK(*it == C); //ll + ++it; + CHECK(*it == B); //l + it++; + CHECK(*it == D); //lr + it++; + CHECK(*it == A); //root + ++it; + CHECK(*it == H); //rll + it++; + CHECK(*it == F); //rl + it++; + CHECK(*it == E); //r + ++it; + CHECK(*it == G); //rr + ++it; + CHECK(*it == I); //rrr + CHECK(it != empty); + it++; + CHECK(it == empty); + } + + { + TestNode root{A}; + TestNode l{B}, ll{C}, lll{D}, llll{E}, lllll{F}; + root.left = &l; + l.left = ≪ + ll.left = &lll; + lll.left = &llll; + llll.left = &lllll; + + TestIterator it{&root, 6}; + CHECK(it != empty); + CHECK(*it == F); + ++it; + CHECK(*it == E); + ++it; + CHECK(*it == D); + ++it; + CHECK(*it == C); + ++it; + CHECK(*it == B); + ++it; + CHECK(*it == A); + ++it; + CHECK(it == empty); + } + + { + TestNode root{A}; + TestNode r{B}, rr{C}, rrr{D}, rrrr{E}, rrrrr{F}; + root.right = &r; + r.right = &rr; + rr.right = &rrr; + rrr.right = &rrrr; + rrrr.right = &rrrrr; + + TestIterator it{&root, 6}; + CHECK(it != empty); + CHECK(*it == A); + ++it; + CHECK(*it == B); + ++it; + CHECK(*it == C); + ++it; + CHECK(*it == D); + ++it; + CHECK(*it == E); + ++it; + CHECK(*it == F); + ++it; + CHECK(it == empty); + } +}