From 719e806d2fd5a2a2239f92577b47d4f64249a49e Mon Sep 17 00:00:00 2001
From: Quentin Khan <quentin.khan@inria.fr>
Date: Mon, 25 Jan 2016 10:27:22 +0100
Subject: [PATCH] Add FBox and tests

FBox is a class that represents a N dimensions interval. To do so, it
keeps the maximum and minimum corners of the interval.
---
 Src/Adaptive/new/FBox.hpp           | 199 +++++++++++++++++++++++++++
 Tests/noDist/Adaptive/test_FBox.cpp | 204 ++++++++++++++++++++++++++++
 2 files changed, 403 insertions(+)
 create mode 100644 Src/Adaptive/new/FBox.hpp
 create mode 100644 Tests/noDist/Adaptive/test_FBox.cpp

diff --git a/Src/Adaptive/new/FBox.hpp b/Src/Adaptive/new/FBox.hpp
new file mode 100644
index 000000000..71146022a
--- /dev/null
+++ b/Src/Adaptive/new/FBox.hpp
@@ -0,0 +1,199 @@
+#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
diff --git a/Tests/noDist/Adaptive/test_FBox.cpp b/Tests/noDist/Adaptive/test_FBox.cpp
new file mode 100644
index 000000000..154995605
--- /dev/null
+++ b/Tests/noDist/Adaptive/test_FBox.cpp
@@ -0,0 +1,204 @@
+#include <cassert>
+#include <cmath>
+#include <iostream>
+
+#include "test_Abstract.hpp"
+
+#include "Utils/FPoint.hpp"
+#include "Utils/FConstFuncs.hpp"
+#include "Adaptive/new/FBox.hpp"
+
+namespace scalfmm {
+    namespace tests {
+
+
+        struct test_Box : public test_Abstract<test_Box> {
+
+            constexpr static const std::size_t Dim = 3;
+            using position_t = FPoint<double, Dim>;
+            using box_t      = FBox<position_t>;
+
+            void run_all() {
+                RUN(test_constructor_1       );
+                RUN(test_constructor_2       );
+                RUN(test_constructor_3       );
+                RUN(test_constructor_4       );
+                RUN(test_constructor_5       );
+                RUN(test_assign_copy_operator);
+                RUN(test_assign_move_operator);
+                RUN(test_center              );
+                RUN(test_corner              );
+                RUN(test_contains            );
+            }
+
+
+            /// Test default constructor
+            void test_constructor_1() {
+                box_t b;
+                position_t p {0,0,0};
+                assert(b.c1 == p);
+                assert(b.c2 == p);
+            }
+
+            /// Test constructor with minimum and maximum corners
+            void test_constructor_2() {
+                position_t p1 = {0,0,0};
+                position_t p2 = {100,100,100};
+
+                box_t b(p1, p2);
+                assert(b.c1 == p1);
+                assert(b.c2 == p2);
+            }
+
+            /// Test constructor with initializer lists
+            void test_constructor_3() {
+                position_t p {0,0,0};
+                box_t d({0,0,0},{0,0,0});
+
+                assert(d.c1 == p);
+                assert(d.c2 == p);
+            }
+
+            /// Test copy constructor
+            void test_constructor_4() {
+                position_t p1 = {0,0,0};
+                position_t p2 = {100,100,100};
+                box_t c(p1, p2);
+
+                box_t e(c);
+                assert(c.c1 == e.c1);
+                assert(c.c2 == e.c2);
+
+                // test deep copy
+                c.set(p2, p1 + position_t{-1,0,0});
+
+                assert(c.c1 != e.c1);
+                assert(c.c2 == e.c2);
+            }
+
+            void test_constructor_5() {
+                position_t p1 = {0,100,0};
+                position_t p2 = {100,0,100};
+                box_t c(p1, p2);
+
+                for(std::size_t i = 0; i < Dim; ++i) {
+                    assert(Ffeq(c.c1[i], std::fmin(p1[i], p2[i])));
+                    assert(Ffeq(c.c2[i], std::fmax(p1[i], p2[i])));
+                }
+            }
+
+            void test_assign_copy_operator() {
+                position_t p1 = {0,0,0};
+                position_t p2 = {100,100,100};
+                box_t a(p1, p2);
+
+                box_t b;
+                b = a;
+                assert(a.c1 == b.c1);
+                assert(a.c2 == b.c2);
+                assert(a.center() == b.center());
+            }
+
+            void test_assign_move_operator() {
+                position_t p1 = {0,0,0};
+                position_t p2 = {100,100,100};
+                box_t a(p1, p2);
+
+                box_t b;
+                b = std::move(a);
+                assert(a.c1 == b.c1);
+                assert(a.c2 == b.c2);
+            }
+
+
+            void test_center() {
+                position_t p1 = {0,10,50};
+                position_t p2 = {100,100,100};
+                position_t p3 = {50,55,75};
+
+                box_t b{p1, p2};
+
+                assert(b.center() == p3);
+
+                // Test center after copy
+                box_t c(b);
+                assert(c.center() == p3);
+
+                b.set({20,30,40}, {120, 130, 140});
+                assert(b.center() == position_t(70, 80, 90));
+            }
+
+            void test_corner() {
+                position_t p0 = {  0,  0,  0};
+                position_t p1 = {  0,  0,100};
+                position_t p2 = {  0,100,  0};
+                position_t p3 = {  0,100,100};
+                position_t p4 = {100,  0,  0};
+                position_t p5 = {100,  0,100};
+                position_t p6 = {100,100,  0};
+                position_t p7 = {100,100,100};
+
+                box_t b = {p0, p7};
+
+                assert(b.corner(0) == p0);
+                assert(b.corner(1) == p1);
+                assert(b.corner(2) == p2);
+                assert(b.corner(3) == p3);
+                assert(b.corner(4) == p4);
+                assert(b.corner(5) == p5);
+                assert(b.corner(6) == p6);
+                assert(b.corner(7) == p7);
+
+                b.corner(0, {1,1,1});
+                assert(b.corner(0) == b.c1);
+                assert(b.corner(0) == position_t(1,1,1));
+                b.corner(0, {0,0,0});
+
+                b.corner(2, {20, 80, 30});
+                assert(b.c1 == position_t(20, 0, 30));
+                assert(b.c2 == position_t(100, 80, 100));
+
+                b.corner(5, {0, 20, 25});
+                assert(b.c1 == position_t(0, 20, 25));
+                assert(b.c2 == position_t(20, 80, 30));
+            }
+
+            struct DummyObject {
+                position_t p;
+                position_t position() const {return p;}
+            };
+
+            void test_contains() {
+                box_t b = {{0,0,0}, {100,100,100}};
+
+                assert(b.contains(b.c1));
+                assert(b.contains(b.c2));
+                assert(b.contains({50,50,50}));
+                assert(! b.contains({150,50,50}));
+                assert(! b.contains({50,150,50}));
+                assert(! b.contains({50,50,150}));
+                assert(! b.contains({50,150,150}));
+                assert(! b.contains({150,150,50}));
+                assert(! b.contains({150,50,150}));
+                assert(! b.contains({150,150,150}));
+
+                assert(b.contains(DummyObject  {{ 50, 50, 50}}));
+                assert(! b.contains(DummyObject{{150, 50, 50}}));
+                assert(! b.contains(DummyObject{{ 50,150, 50}}));
+                assert(! b.contains(DummyObject{{ 50, 50,150}}));
+                assert(! b.contains(DummyObject{{ 50,150,150}}));
+                assert(! b.contains(DummyObject{{150,150, 50}}));
+                assert(! b.contains(DummyObject{{150, 50,150}}));
+                assert(! b.contains(DummyObject{{150,150,150}}));
+
+
+            }
+
+        };
+
+    }
+}
+
+int main() {
+    scalfmm::tests::test_Box().run_all();
+}
-- 
GitLab