Commit 4dec09ef authored by Quentin Khan's avatar Quentin Khan

Add NodeIterator base class

parent f81be4b4
#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
#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();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment