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();
+}