Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 4dec09ef authored by Quentin Khan's avatar Quentin Khan
Browse files

Add NodeIterator base class

parent f81be4b4
No related branches found
No related tags found
No related merge requests found
#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();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment