Commit c4f3a212 authored by Quentin Khan's avatar Quentin Khan

Development ground for new adaptive FMM

parents fe33c301 eb5517fc
#ifndef SCALFMM_BOX_HPP_
#define SCALFMM_BOX_HPP_
#include <ostream>
#include "FZCurve.hpp"
/* Implements a N dimensions box
*
* \author Quentin Khan <quentin.khan@inria.fr>
*
* The box is described by two opposite corners : the maximum and
* the minimum one. All the class transformations maintain this
* predicate.
*
* \tparam Real Floating number representation.
* \tparam Dim Space dimension count.
* \tparam SpaceFillingCurve A templatize implementation of a space filling curve
*
*/
template<class _Position, template<std::size_t> class SpaceFillingCurve = FZCurve>
class FBox {
public:
/// Position type
using position_t = _Position;
/// Floating number representation
using Real = typename position_t::Real;
/// Space dimension
constexpr static const std::size_t Dim = position_t::Dim;
/// Space filling curve type
using space_filling_curve_t = SpaceFillingCurve<Dim>;
private:
position_t _c1; ///< Minimum corner
position_t _c2; ///< Maximum corner
position_t _center; ///< Center
/** Rearranges the corners to ensure the maximum-minimum predicate */
void rearrange_corners () {
for(std::size_t i = 0; i < Dim; ++i){
if(_c1[i] > _c2[i]) {
std::swap(_c1[i], _c2[i]);
}
}
_center = (_c1 + _c2) / 2;
}
public:
/// Accessor for the minimum corner
const position_t& c1 = _c1;
/// Accessor for the maximum corner
const position_t& c2 = _c2;
/** Builds an empty box at the origin */
FBox() = default;
/** Copies an existing box */
FBox(const FBox& other) : _c1(other._c1), _c2(other._c2), _center(other._center) {}
/** Copies an other box */
FBox& operator=(const FBox& other) {
this->_c1 = other._c1;
this->_c2 = other._c2;
this->_center = other._center;
return *this;
}
/** Builds a cube from the lower corner and its side length
*
* \param min_corner The lowest corner
* \param side_length The cube's side length
**/
FBox(const position_t& min_corner, Real side_length) :
_c1(min_corner),
_c2(min_corner)
{
if(side_length < 0) {
side_length = - side_length;
}
for(auto&& d : _c2) {
d += side_length;
}
_center = (_c1 + _c2) / 2;
}
/** Builds a box from two corners
*
* The maximum and minimum corners are deduced from the given corners.
*
* \param corner_1 The first corner.
* \param corner_2 The second corner.
*/
FBox(const position_t& corner_1, const position_t& corner_2) : _c1(corner_1), _c2(corner_2) {
rearrange_corners();
}
/** Changes the box corners
*
* The maximum and minimum corners are deduced from the given corners.
*
* \param corner_1 The first corner.
* \param corner_2 The second corner.
*/
void set(const position_t& corner_1, const position_t& corner_2) {
_c1 = corner_1;
_c2 = corner_2;
rearrange_corners();
}
/** Checks whether a position is within the box bounds
*
* \param p The position to check.
*/
bool contains(const position_t& p) const {
for(std::size_t i = 0; i < Dim; i++) {
if(p[i] < _c1[i] || p[i] > _c2[i]) {
return false;
}
}
return true;
}
/** Checks whether an object's position is within the box bounds
*
* The object must implement a `position_t position() const;` method.
*
* \tparam T The object type.
* \param obj The object which position to check.
*/
template<class T>
bool contains(const T& obj) const {
return contains(obj.position());
}
/** Accessor for the box center */
const position_t& center() const {
return _center;
}
/** Accessor for the box corners
*
* The corners are numbered using a space filling curve.
*
* \param idx The corner index.
* \return The idx'th corner.
*/
position_t corner(std::size_t idx) const {
position_t c;
std::size_t i = 0;
for(bool choice : space_filling_curve_t::position(idx)) {
c[i] = choice ? _c2[i] : _c1[i];
++i;
}
return c;
}
/** Setter for the corners
*
* Moves a corner to a new position and modifies the relevant other
* ones. The corners are numbered using a space filling curve.
*
* \param idx The moved corner index.
* \param pos The new corner position.
*/
void corner(std::size_t idx, const position_t& pos) {
std::size_t i = 0;
for(bool choice : space_filling_curve_t::position(idx)) {
if(choice) {
_c2[i] = pos[i];
} else {
_c1[i] = pos[i];
}
++i;
}
rearrange_corners();
}
/** Sums the corners of two boxes */
FBox operator+(const FBox& other) const {
return FBox(_c1 + other._c1, _c2 + other._c2);
}
/** Tests two boxes equality */
bool operator==(const FBox& other) const {
return c1 == other.c1 && c2 == other.c2;
}
/** Tests two boxes inequality */
bool operator!=(const FBox& other) const {
return ! this->operator==(other);
}
friend std::ostream& operator<<(std::ostream& os, const FBox& box) {
return os << "[" << box.c1 << "," << box.c2 << "]";
}
};
#endif
#ifndef _SCALFMM_IN_ORDER_NODE_ITERATOR_HPP_
#define _SCALFMM_IN_ORDER_NODE_ITERATOR_HPP_
#include "FNodeIterator.hpp"
namespace scalfmm {
namespace tests {
struct test_InOrderNodeIterator;
}
}
template<class Node>
class FInOrderNodeIterator : public FNodeIterator<FInOrderNodeIterator<Node>, Node> {
using base_t = FNodeIterator<FInOrderNodeIterator<Node>, Node>;
using Direction = typename base_t::Direction;
friend scalfmm::tests::test_InOrderNodeIterator;
friend base_t;
public:
using base_t::base_t;
using base_t::operator++;
using base_t::operator--;
FInOrderNodeIterator& operator++() {
return move_iterator<Direction::forwards>();
}
FInOrderNodeIterator& operator--() {
return move_iterator<Direction::backwards>();
}
private:
/** Indicates the index of the middle child */
template<Direction dir>
typename base_t::index_t mid_idx() const {
if(dir == Direction::forwards) { // Compile time optimisation will remove unused branch
return (base_t::child_count/2)-1;
} else {
return (base_t::child_count/2);
}
}
/** Moves the cursor to the first node
*
* Resets the iterator to a valid state.
*/
template<Direction dir>
FInOrderNodeIterator& move_to_boundary() {
base_t::template goto_root<dir>();
while(! this->_cur->is_leaf()) {
base_t::template move_down<dir>();
}
return *this;
}
template<Direction dir>
FInOrderNodeIterator& leaf_next() {
// Leave current leaf
if(!this->_cur_state.empty()) {
base_t::move_up();
}
// If we visited half the parent's children, stay on the parent
if (!this->_cur_state.empty() && mid_idx<dir>() == this->_cur_state.back()) {
return *this;
}
// If we visited all the children of the node, we move up
while(!this->_cur_state.empty() && base_t::template at_last_child<dir>()) {
base_t::move_up();
}
// Moving too far up means we got to the end of the tree
if(this->_cur_state.empty()) {
base_t::template set_end<dir>();
return *this;
}
// Once we moved up enough, we move down to the next leaf
while(! this->_cur->is_leaf()) {
base_t::template move_down<dir>();
}
return *this;
}
template<Direction dir>
FInOrderNodeIterator& node_next() {
// If we are on an internal node, we move down to the children
if(dir == Direction::forwards) { // Compile time optimisation will remove unused branch
this->_cur_state.back() = static_cast<char>(mid_idx<dir>()+1);
} else {
this->_cur_state.back() = static_cast<char>(mid_idx<dir>()-1);
}
this->_cur = this->_cur->getChild((std::size_t)this->_cur_state.back());
this->_cur_state.push_back(base_t::template first_idx<dir>());
while(! this->_cur->is_leaf()) {
base_t::template move_down<dir>();
}
return *this;
}
template<Direction dir>
FInOrderNodeIterator& move_iterator() {
// If before the beginning, move to the first node
if(base_t::template is_other_end<dir>()) {
return move_to_boundary<dir>();
}
// Don't move if already past the end
if(base_t::template is_end<dir>()) {
return *this;
}
if(this->_cur->is_leaf()) {
return leaf_next<dir>();
} else {
return node_next<dir>();
}
}
};
#endif
This diff is collapsed.
#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
#ifndef _SCALFMM_NODE_ITERATOR_BOX_
#define _SCALFMM_NODE_ITERATOR_BOX_
#include <iterator>
template <class Node, template <class> class NodeIterator>
class FNodeIteratorBox {
using node_t = Node;
public:
using value_type = node_t;
using reference = node_t&;
using const_reference = const node_t&;
using iterator = NodeIterator<node_t>;
using const_iterator = NodeIterator<const node_t>;
using difference_type = typename iterator::difference_type;
using size_type = std::size_t;
node_t* _root = nullptr;
FNodeIteratorBox(node_t* root) : _root(root) { }
iterator begin() {
return iterator(_root);
}
iterator end() {
return iterator(_root, iterator::IteratorPosition::end);
}
};
#endif
#ifndef _SCALFMM____ORDER_NODE_ITERATOR_HPP_
#define _SCALFMM____ORDER_NODE_ITERATOR_HPP_
#include "FNodeIterator.hpp"
namespace scalfmm {
namespace tests {
struct test_PostOrderNodeIterator;
struct test_PreOrderNodeIterator;
}
}
namespace scalfmm_adaptive_iterator_impl {
enum class NodeIteratorOrder {preorder, postorder};
template<class Node, NodeIteratorOrder it_type>
class __OrderNodeIterator : public FNodeIterator<__OrderNodeIterator<Node, it_type>, Node> {
friend scalfmm::tests::test_PostOrderNodeIterator;
friend scalfmm::tests::test_PreOrderNodeIterator;
using base_t = FNodeIterator<__OrderNodeIterator<Node, it_type>, Node>;
using Direction = typename base_t::Direction;
public:
friend base_t;
using base_t::base_t;
using base_t::operator++;
using base_t::operator--;
__OrderNodeIterator& operator++() {
if(it_type == NodeIteratorOrder::preorder) {
return move_iterator_prefix<Direction::forwards>();
} else {
return move_iterator_postfix<Direction::forwards>();
}
}
__OrderNodeIterator& operator--() {
if(it_type == NodeIteratorOrder::preorder) {
return move_iterator_postfix<Direction::backwards>();
} else {
return move_iterator_prefix<Direction::backwards>();
}
}
protected:
/** Moves the cursor to the first node
*
* Resets the iterator to a valid state.
*/
template<Direction dir>
__OrderNodeIterator& move_to_boundary() {
base_t::template goto_root<dir>();
if( (it_type == NodeIteratorOrder::preorder
&& dir == Direction::backwards)
|| (it_type == NodeIteratorOrder::postorder
&& dir == Direction::forwards) )
{
while(! this->_cur->is_leaf()) {
base_t::template move_down<dir>();
}
}
return *this;
}
////////////// POSTORDER TRAVERSAL /////////////////
template<Direction dir>
__OrderNodeIterator& move_iterator_postfix() {
if(base_t::template is_other_end<dir>()) {
return move_to_boundary<dir>();
}
if(base_t::template is_end<dir>()) {
return *this;
}
return next_postfix<dir>();
}
template<Direction dir>
__OrderNodeIterator& next_postfix() {
// Leave current leaf
if(!this->_cur_state.empty()) {
base_t::move_up();
}
// If we visited all the children of the node, we move up
if(!this->_cur_state.empty() && base_t::template at_last_child<dir>()) {
if(dir == Direction::backwards) {
--(this->_cur_state.back());
} else {
++(this->_cur_state.back());