diff --git a/Src/Adaptive/new/FNodeIterator.hpp b/Src/Adaptive/new/FNodeIterator.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d5034e8a849ef35d6b46697f8727e375df8e1e7c --- /dev/null +++ b/Src/Adaptive/new/FNodeIterator.hpp @@ -0,0 +1,258 @@ +#ifndef _SCALFMM_NODE_ITERATOR_HPP_ +#define _SCALFMM_NODE_ITERATOR_HPP_ + +#include <vector> + +#include "Utils/FConstFuncs.hpp" + +namespace scalfmm { + namespace tests { + struct test_NodeIterator; + } +} + +template<class Derived, class Node> +class FNodeIterator { +public: + + friend struct scalfmm::tests::test_NodeIterator; + + using node_t = Node; + + // Iterator types + using value_type = node_t; + using difference_type = std::ptrdiff_t; + using pointer = node_t*; + using reference = node_t&; + using iterator_category = std::bidirectional_iterator_tag; + + constexpr static std::size_t child_count = node_t::child_count; + enum class IteratorPosition {begin, end, reverse_end}; + +protected: + enum class Direction {forwards, backwards}; + using state_container = std::vector<char>; + using index_t = typename state_container::value_type; + + + /// Root node for the graph walk + /** \warning This may not be the first node of the walk. */ + node_t* _root = nullptr; + /// Current node for the graph walk + node_t* _cur = nullptr; + /** Current state in the walk + * Contains the indexes of the next children to visit per level + * in the currently visited tree branch. + */ + state_container _cur_state; + /// Special past_the_end iterator status + bool _is_end = false; + /// Special past_the_reverse_end iterator status + bool _is_rend = false; + +public: + /// Main constructor + /** + * \param root The graph root node before walking accross it. + * + * \warning This constructor must be completed by the derived class. + * It does not set #_cur nor #_cur_state to the right value. */ + FNodeIterator(node_t* root, IteratorPosition p = IteratorPosition::begin) : + _root(root), + _is_end(p == IteratorPosition::end), + _is_rend(p == IteratorPosition::reverse_end) + { + if(!_is_end && ! _is_rend) { + static_cast<Derived*>(this)->template move_to_boundary<Direction::forwards>(); + } + } + + /// Default constructor + /** Such a constructed iterator is not in a valid state. */ + FNodeIterator() = default; + /// Copy constructor + FNodeIterator(const FNodeIterator& other) = default; + /// Copy assignment + FNodeIterator& operator=(const FNodeIterator& other) = default; + /// Move constructor + FNodeIterator(FNodeIterator&& other) = default; + /// Move assignment + FNodeIterator& operator=(FNodeIterator&& other) = default; + + /// Destructor + ~FNodeIterator() = default; + + /// Swap operation + void swap(FNodeIterator&& other) noexcept { + #define _MY_SWAP_(a) {auto tmp = a; a = other.a; other.a = tmp;} + _MY_SWAP_(_root); + _MY_SWAP_(_cur); + _MY_SWAP_(_is_end); + _MY_SWAP_(_is_rend); + _cur_state.swap(other._cur_state); + #undef _MY_SWAP_ + } + + + /// Dereference operator + node_t& operator*() { + return *_cur; + } + /// Dereference const operator + const node_t& operator*() const { + return *_cur; + } + + /// Pointer dereference operator + node_t* operator->() { + return _cur; + } + /// Pointer const dereference operator + const node_t* operator->() const { + return _cur; + } + + /// Prefix increment + //virtual FNodeIterator& operator++() = 0; + + /// Postfix increment + Derived operator++(int) { + Derived it(static_cast<Derived&>(*this)); + ++static_cast<Derived&>(*this); + return it; + } + + /// Prefix decrement + //virtual FNodeIterator& operator--() = 0; + + /// Postfix decrement + Derived operator--(int) { + Derived it(static_cast<Derived&>(*this)); + --static_cast<Derived&>(*this); + return it; + } + + /// Equality operator + friend bool operator==(const FNodeIterator& lhs, const FNodeIterator& rhs) { + #define _TEST_EQ(name) (lhs.name == rhs.name) + return + _TEST_EQ(_root) && + _TEST_EQ(_cur) && + _TEST_EQ(_is_end) && + _TEST_EQ(_is_rend); + #undef _TEST_EQ + } + + /// Inequality operator + friend bool operator!=(const FNodeIterator& lhs, const FNodeIterator& rhs) { + return ! (lhs == rhs); + } + + friend Derived operator+=(Derived lhs, difference_type n) { + if(n < 0) { + for(; n != 0; ++n, --lhs); + } + if(n > 0) { + for(; n != 0; --n, ++lhs); + } + return lhs; + } + + friend Derived operator-=(const Derived& lhs, difference_type n) { + return operator+=(lhs, -n); + } + + friend Derived operator+(const Derived& lhs, difference_type n) { + return lhs += n; + } + + friend Derived operator-(const Derived& lhs, difference_type n) { + return operator+=(lhs, -n); + } + +protected: + + // Methods used in all subclasses + + template<Direction dir> + void goto_root() { + this->_is_rend = false; + this->_is_end = false; + this->_cur = this->_root; + this->_cur_state.clear(); + this->_cur_state.push_back(first_idx<dir>()); + } + + /** Returns the end flag corresponding to the direction */ + template<Direction dir> + bool is_end() { + return dir == Direction::forwards ? this->_is_end : this->_is_rend; + } + + /** Returns the end flag corresponding to the oposite direction */ + template<Direction dir> + bool is_other_end() { + return dir == Direction::forwards ? this->_is_rend : this->_is_end; + } + + /** Sets the end flag according to the direction */ + template<Direction dir> + void set_end() { + if(dir == Direction::forwards) { + this->_is_end = true; + } else { + this->_is_rend = true; + } + } + + /** Returns the first index to insert in #_cur_state */ + template<Direction dir> + index_t first_idx() const { + return dir == Direction::forwards ? -1 : (index_t)Node::child_count; + } + + + /** Indicates whether the last child of a node has been visited */ + template<Direction dir> + bool at_last_child() const { + if(dir == Direction::forwards) { // Compile time optimisation will remove unused branch + return Node::child_count-1 == this->_cur_state.back(); + } else { + return 0 == this->_cur_state.back(); + } + } + + /** Moves the cursor to its parent + * + * \warning Undefined behaviour when the cursor points to a leaf. + */ + void move_up() { + this->_cur_state.pop_back(); + this->_cur = this->_cur->getParent(); + } + + /** Moves the cursor to the next child to visit + * + * \warning Undefined behaviour when the cursor points to a leaf. + */ + template<Direction dir> + void move_down() { + if(dir == Direction::forwards) { // Compile time optimisation will remove unused branch + ++(this->_cur_state.back()); + } else { + --(this->_cur_state.back()); + } + this->_cur = this->_cur->getChild((std::size_t) this->_cur_state.back()); + this->_cur_state.push_back(first_idx<dir>()); + } + + + +}; + +template<class Derived, class Node> +void swap(FNodeIterator<Derived, Node>& it1, FNodeIterator<Derived, Node>& it2) { + it1.swap(it2); +} + +#endif diff --git a/Tests/noDist/Adaptive/test_FNodeIterator.cpp b/Tests/noDist/Adaptive/test_FNodeIterator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5af464db2cec0fc6ba0f47bdd94a2366fed8f23f --- /dev/null +++ b/Tests/noDist/Adaptive/test_FNodeIterator.cpp @@ -0,0 +1,191 @@ +#include <cassert> + +#include "test_Abstract.hpp" +#include "MockParticle.hpp" + +#include "Adaptive/new/FTree.hpp" +#include "Adaptive/new/FNode.hpp" +#include "Adaptive/new/FNodeIterator.hpp" + +#include "Utils/make_unique.hpp" + +namespace scalfmm { + namespace tests { + + struct test_NodeIterator : public test_Abstract<test_NodeIterator> { + + using Real = double; + constexpr static std::size_t Dim = 3; + + using tree_t = FTree<std::vector<MockParticle<Real, Dim> >, NodeEmptyData >; + using node_t = typename tree_t::node_t; + using box_t = typename tree_t::box_t; + + struct test_iterator : public FNodeIterator<test_iterator, node_t> { + using base_t = FNodeIterator<test_iterator, node_t>; + + int pp = 0; + int mm = 0; + + using base_t::base_t; + using base_t::operator++; + using base_t::operator--; + + template<base_t::Direction> void move_to_boundary() {} + test_iterator& operator++(){++pp; return *this;} + test_iterator& operator--(){++mm; return *this;} + }; + + tree_t* tree = nullptr; + node_t* root = nullptr; + box_t box {{0,0,0}, 100}; + + + public: + + void set_up() { + assert(nullptr == tree); + tree = new tree_t(box); + root = tree->root(); // Allocated by tree constructor + } + + void tear_down() { + delete tree; + tree = nullptr; + root = nullptr; // Deleted by tree destructor + } + + void run_all() { + RUN(test_constructor_default); + RUN(test_constructor_custom); + RUN(test_constructor_copy); + RUN(test_constructor_move); + RUN(test_operator_copy); + RUN(test_operator_move); + RUN(test_operator_dereference); + RUN(test_increment_postfix); + RUN(test_decrement_postfix); + } + + void test_constructor_default() { + test_iterator a; + assert(a._root == nullptr); + assert(a._cur == nullptr); + assert(a._is_end == false); + assert(a._is_rend == false); + } + + void test_constructor_custom() { + test_iterator a(root); + + assert(a._root == root); + assert(a._is_end == false); + assert(a._is_rend == false); + } + + void test_constructor_copy() { + test_iterator a(root); + a._is_end = true; + a._is_rend = true; + test_iterator b(a); + + assert(a._root == b._root ); + assert(a._cur == b._cur ); + assert(a._is_end == b._is_end ); + assert(a._is_rend == b._is_rend ); + assert(a._cur_state == b._cur_state); + } + + void test_constructor_move() { + auto a = std::make_unique<test_iterator>(root); + a->_is_end = true; + a->_is_rend = true; + + auto _root = a->_root; + auto _cur = a->_cur; + auto _cur_state = a->_cur_state; + auto _is_end = a->_is_end; + auto _is_rend = a->_is_rend; + + test_iterator b(std::move(*a)); + + assert(_root == b._root ); + assert(_cur == b._cur ); + assert(_is_end == b._is_end ); + assert(_is_rend == b._is_rend ); + assert(_cur_state == b._cur_state); + } + + void test_operator_copy() { + test_iterator a(root); + a._is_end = true; + a._is_rend = true; + test_iterator b; + b = a; + + assert(a._root == b._root ); + assert(a._cur == b._cur ); + assert(a._is_end == b._is_end ); + assert(a._is_rend == b._is_rend ); + assert(a._cur_state == b._cur_state); + } + + void test_operator_move() { + auto a = std::make_unique<test_iterator>(root); + a->_is_end = true; + a->_is_rend = true; + + auto _root = a->_root; + auto _cur = a->_cur; + auto _cur_state = a->_cur_state; + auto _is_end = a->_is_end; + auto _is_rend = a->_is_rend; + + test_iterator b; + b = std::move(*a); + + assert(_root == b._root ); + assert(_cur == b._cur ); + assert(_is_end == b._is_end ); + assert(_is_rend == b._is_rend ); + assert(_cur_state == b._cur_state); + } + + void test_operator_dereference() { + test_iterator a(root); + a._cur = root; + assert(&(*a) == root); + + const test_iterator b(a); + assert(&(*b) == root); + + auto c = std::make_unique<test_iterator>(a); + assert(&((*c)->getTree()) == tree); + + auto d = std::make_unique<const test_iterator>(a); + assert(&((*d)->getTree()) == tree); + } + + void test_increment_postfix() { + test_iterator a(root); + int plus = (a++).pp; + assert(plus == 0); + assert(a.pp == 1); + } + + void test_decrement_postfix() { + test_iterator a(root); + int minus = (a--).mm; + assert(minus == 0); + assert(a.mm == 1); + } + + + }; + } +} + + +int main() { + scalfmm::tests::test_NodeIterator().run_all(); +}