From 47cb7699396afb971deac2b8aa560278e7f08af7 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 25 May 2021 16:29:33 -0400 Subject: [PATCH 01/68] Import region calculus functions --- CMakeLists.txt | 2 + include/openPMD/RegionCalculus.hpp | 4055 ++++++++++++++++++++++++++++ src/RegionCalculus.cpp | 3 + test/RegionCalculusTest.cpp | 1634 +++++++++++ 4 files changed, 5694 insertions(+) create mode 100644 include/openPMD/RegionCalculus.hpp create mode 100644 src/RegionCalculus.cpp create mode 100644 test/RegionCalculusTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 14ba421302..4741834442 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,6 +347,7 @@ set(CORE_SOURCE src/ReadIterations.cpp src/Record.cpp src/RecordComponent.cpp + src/RegionCalculus.cpp src/Series.cpp src/version.cpp src/WriteIterations.cpp @@ -682,6 +683,7 @@ endif() set(openPMD_TEST_NAMES Core Auxiliary + RegionCalculus SerialIO ParallelIO ) diff --git a/include/openPMD/RegionCalculus.hpp b/include/openPMD/RegionCalculus.hpp new file mode 100644 index 0000000000..81768d7312 --- /dev/null +++ b/include/openPMD/RegionCalculus.hpp @@ -0,0 +1,4055 @@ +#ifndef REGIONCALCULUS_HPP +#define REGIONCALCULUS_HPP + +// #include "Config.hpp" +// #include "Helpers.hpp" + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Whether extra, expensive run-time tests should be enabled. Set to 0 or 1. +#define REGIONCALCULUS_DEBUG 0 + +// Whether the more complex and more efficient tree-based implementation should +// be used. Set to 0 or 1. +#define REGIONCALCULUS_TREE 1 + +namespace RegionCalculus { + +using std::abs; +using std::array; +using std::equal_to; +using std::hash; +using std::is_sorted; +using std::less; +using std::make_pair; +using std::make_tuple; +using std::map; +using std::max; +using std::min; +using std::move; +using std::numeric_limits; +using std::ostream; +using std::pair; +using std::ptrdiff_t; +using std::size_t; +using std::sort; +using std::tuple; +using std::unique; +using std::unique_ptr; +using std::vector; + +//////////////////////////////////////////////////////////////////////////////// +// C++ helper functions +//////////////////////////////////////////////////////////////////////////////// + +// make_unique is only available in C++14 +template +std::unique_ptr make_unique1(Args &&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +// Combine hashes +template size_t hash_combine(size_t seed, const T &x) { + hash h; + // Taken from Boost + return seed ^ + h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2); +} + +namespace detail { +// Abort when called +template struct error { + T operator()(const T &) const { std::abort(); } + T operator()(const T &, const T &) const { std::abort(); } + T operator()(const T &, const T &, const T &) const { std::abort(); } + T &operator()(T &) const { std::abort(); } + T &operator()(T &, const T &) const { std::abort(); } + T &operator()(T &, const T &, const T &) const { std::abort(); } +}; +} // namespace detail + +// Call function object F if T is integral; otherwise, abort +template struct call_if_integral { + typedef typename T::value_type S; + typedef typename std::conditional::value, F, + detail::error>::type type; + constexpr T operator()(const T &x) const { return type()(x); } + constexpr T operator()(const T &x, const T &y) const { return type()(x, y); } + constexpr T operator()(const T &x, const T &y, const T &z) const { + return type()(x, y, z); + } + T &operator()(T &x) const { return type()(x); } + T &operator()(T &x, const T &y) const { return type()(x, y); } + T &operator()(T &x, const T &y, const T &z) const { return type()(x, y, z); } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Numerical functions +//////////////////////////////////////////////////////////////////////////////// + +namespace detail { + +template +typename std::enable_if::value, T>::type abs_wrapper(T x) { + using std::abs; + return abs(x); +} + +template +typename std::enable_if::value, T>::type abs_wrapper(T x) { + return x; +} +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +// Reduction +//////////////////////////////////////////////////////////////////////////////// + +// op is functional +template ::type> +T reduce(Op &&op, vector &xs) { + assert(!xs.empty()); + for (size_t dist = 1; dist < xs.size(); dist *= 2) + for (size_t i = 0; i + dist < xs.size(); i += 2 * dist) + xs[i] = std::forward(op)(move(xs[i]), move(xs[i + dist])); + return move(xs[0]); +} + +// // op is assignment-like +// template ::type> +// T reduce(Op &&op, vector &xs) { +// assert(!xs.empty()); +// for (size_t dist = 1; dist < xs.size(); dist *= 2) +// for (size_t i = 0; i + dist < x.size(); i += 2 * dist) +// std::forward(op)(xs[i], xs[i + dist]); +// return move(xs[0]); +// } + +template +R reduce(F &&f, Op &&op, R &&z, const B &b, const E &e) { + vector rs; + for (auto i = b; i != e; ++i) + rs.push_back(std::forward(f)(*i)); + if (rs.empty()) + return std::forward(z); + return reduce(std::forward(op), rs); +} + +template ::value_type, + typename R1 = typename std::result_of::type, + typename R = typename std::decay::type> +R reduce(F &&f, Op &&op, const I &b, const I &e) { + return reduce(std::forward(f), std::forward(op), R(), b, e); +} + +template ::type, + typename R = typename std::decay::type> +R reduce(F &&f, Op &&op, const C &c) { + return reduce(std::forward(f), std::forward(op), std::begin(c), + std::end(c)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Point +//////////////////////////////////////////////////////////////////////////////// + +namespace detail { +template struct largeint { typedef T type; }; +template <> struct largeint { typedef long long type; }; +template <> struct largeint { typedef long long type; }; +template <> struct largeint { typedef long long type; }; +template <> struct largeint { + typedef unsigned long long type; +}; +template <> struct largeint { typedef unsigned long long type; }; +template <> struct largeint { typedef unsigned long long type; }; +} // namespace detail + +template struct point { + typedef T value_type; + typedef int size_type; + constexpr size_type rank() const { return D; } + array elt; + point() { + for (int d = 0; d < D; ++d) + elt[d] = T(0); + } + point(const array &p) : elt(p) {} + template explicit point(const array &p) { + for (int d = 0; d < D; ++d) + elt[d] = T(p[d]); + } + point(const vector &p) { + assert(p.size() == D); + for (int d = 0; d < D; ++d) + elt[d] = p[d]; + } + template explicit point(const vector &p) { + assert(p.size() == D); + for (int d = 0; d < D; ++d) + elt[d] = T(p[d]); + } + point(const point &p) = default; + point(point &&p) = default; + explicit point(T x) { + for (int d = 0; d < D; ++d) + elt[d] = x; + } + template explicit point(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] = T(p.elt[d]); + } + operator array() const { + array r; + for (int d = 0; d < D; ++d) + r[d] = elt[d]; + return r; + } + template explicit operator array() const { + array r; + for (int d = 0; d < D; ++d) + r[d] = U(elt[d]); + return r; + } + operator vector() const { + vector r(D); + for (int d = 0; d < D; ++d) + r[d] = elt[d]; + return r; + } + template explicit operator vector() const { + vector r(D); + for (int d = 0; d < D; ++d) + r[d] = U(elt[d]); + return r; + } + explicit point(T x0, T x1) { + static_assert(D == 2, ""); + elt[0] = x0; + elt[1] = x1; + } + explicit point(T x0, T x1, T x2) { + static_assert(D == 3, ""); + elt[0] = x0; + elt[1] = x1; + elt[2] = x2; + } + explicit point(T x0, T x1, T x2, T x3) { + static_assert(D == 4, ""); + elt[0] = x0; + elt[1] = x1; + elt[2] = x2; + elt[3] = x3; + } + point &operator=(const point &p) = default; + point &operator=(point &&p) = default; + + // Access and conversion + const T &operator[](int d) const { return elt[d]; } + T &operator[](int d) { return elt[d]; } + point 0 ? D - 1 : 0)> subpoint(int dir) const { + assert(dir >= 0 && dir < D); + point 0 ? D - 1 : 0)> r; + for (int d = 0; d < D - 1; ++d) + r.elt[d] = elt[d + (d >= dir)]; + return r; + } + point superpoint(int dir, T x) const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d + (d >= dir)] = elt[d]; + r.elt[dir] = x; + return r; + } + point reversed() const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = elt[D - 1 - d]; + return r; + } + + // Unary operators + point operator+() const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = +elt[d]; + return r; + } + point operator-() const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = -elt[d]; + return r; + } + + point operator~() const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = ~elt[d]; + return r; + } + point operator!() const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = !elt[d]; + return r; + } + + // Assignment operators + point &operator+=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] += p.elt[d]; + return *this; + } + point &operator-=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] -= p.elt[d]; + return *this; + } + point &operator*=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] *= p.elt[d]; + return *this; + } + point &operator/=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] /= p.elt[d]; + return *this; + } + point &operator%=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] %= p.elt[d]; + return *this; + } + point &operator&=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] &= p.elt[d]; + return *this; + } + point &operator|=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] |= p.elt[d]; + return *this; + } + point &operator^=(const point &p) { + for (int d = 0; d < D; ++d) + elt[d] ^= p.elt[d]; + return *this; + } + + // Binary operators + point operator+(const point &p) const { return point(*this) += p; } + point operator-(const point &p) const { return point(*this) -= p; } + point operator*(const point &p) const { return point(*this) *= p; } + point operator/(const point &p) const { return point(*this) /= p; } + point operator%(const point &p) const { return point(*this) %= p; } + point operator&(const point &p) const { return point(*this) &= p; } + point operator|(const point &p) const { return point(*this) |= p; } + point operator^(const point &p) const { return point(*this) ^= p; } + point operator&&(const point &p) const { + return point(*this) &= point(p); + } + point operator||(const point &p) const { + return point(*this) |= point(p); + } + + // Unary functions + point abs() const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = detail::abs_wrapper(elt[d]); + return r; + } + + // Binary functions + point min(const point &p) const { + using std::min; + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = min(elt[d], p.elt[d]); + return r; + } + point max(const point &p) const { + using std::max; + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = max(elt[d], p.elt[d]); + return r; + } + + // Comparison operators + point operator==(const point &p) const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = elt[d] == p.elt[d]; + return r; + } + point operator!=(const point &p) const { return !(*this == p); } + point operator<(const point &p) const { + point r; + for (int d = 0; d < D; ++d) + r.elt[d] = elt[d] < p.elt[d]; + return r; + } + point operator>(const point &p) const { return p < *this; } + point operator>=(const point &p) const { return !(*this < p); } + point operator<=(const point &p) const { return !(*this > p); } + + bool equal_to(const point &p) const { + std::equal_to> eq; + return eq(elt, p.elt); + } + bool less(const point &p) const { + std::less lt; + // Use Fortran array index ordering, where the highest dimensions are the + // most significand + for (int d = D - 1; d >= 0; --d) { + if (lt(elt[d], p.elt[d])) + return true; + if (lt(p.elt[d], elt[d])) + return false; + } + return false; + } + size_t hash() const { + size_t r = size_t(0xb89a122a8c3f540eULL); + for (int d = 0; d < D; ++d) + r = hash_combine(r, elt[d]); + return r; + } + + // Reductions + bool all() const { + bool r = true; + for (int d = 0; d < D; ++d) + r = r && elt[d]; + return r; + } + bool any() const { + bool r = false; + for (int d = 0; d < D; ++d) + r = r || elt[d]; + return r; + } + T minval() const { + using std::min; + T r = numeric_limits::max(); + for (int d = 0; d < D; ++d) + r = min(r, elt[d]); + return r; + } + T maxval() const { + using std::max; + T r = numeric_limits::min(); + for (int d = 0; d < D; ++d) + r = max(r, elt[d]); + return r; + } + T sum() const { + T r = T(0); + for (int d = 0; d < D; ++d) + r += elt[d]; + return r; + } + typedef typename detail::largeint::type prod_t; + prod_t prod() const { + prod_t r = prod_t(1); + for (int d = 0; d < D; ++d) + r *= elt[d]; + return r; + } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit point(const YAML::Node &node) { + assert(node.Tag() == + "tag:github.com/eschnett/SimulationIO/asdf-cxx/point-1.0.0"); + *this = point(node.as>()); + } + friend void operator>>(const YAML::Node &node, point &p) { p = point(node); } +#endif + + ostream &output(ostream &os) const { + os << "["; + for (int d = 0; d < D; ++d) { + if (d > 0) + os << ","; + os << elt[d]; + } + os << "]"; + return os; + } + friend ostream &operator<<(ostream &os, const point &p) { + return p.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + w << YAML::LocalTag("sio", "point-1.0.0"); + w << YAML::Flow << YAML::BeginSeq; + for (int d = 0; d < D; ++d) + w << elt[d]; + w << YAML::EndSeq; + return w; + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const point &p) { + return p.output(w); + } +#endif +}; + +// Unary functions +template point abs(const point &p) { + return p.abs(); +} + +// Binary functions +template +point min(const point &p, const point &q) { + return p.min(q); +} +template +point max(const point &p, const point &q) { + return p.max(q); +} + +// Reductions +template bool all(const point &p) { return p.all(); } +template bool any(const point &p) { return p.any(); } +template T minval(const point &p) { + return p.minval(); +} +template T maxval(const point &p) { + return p.maxval(); +} +template T sum(const point &p) { return p.sum(); } +template +typename point::prod_t prod(const point &p) { + return p.prod(); +} + +// Function objects +template struct bit_not; +template struct bit_not> { + constexpr point operator()(const point &p) const { return ~p; } +}; + +template struct modulus_eq; +template struct modulus_eq> { + point &operator()(point &p, const point &q) const { + return p %= q; + } +}; +template struct bit_and_eq; +template struct bit_and_eq> { + point &operator()(point &p, const point &q) const { + return p &= q; + } +}; +template struct bit_or_eq; +template struct bit_or_eq> { + point &operator()(point &p, const point &q) const { + return p |= q; + } +}; +template struct bit_xor_eq; +template struct bit_xor_eq> { + point &operator()(point &p, const point &q) const { + return p ^= q; + } +}; + +template struct modulus; +template struct modulus> { + constexpr point operator()(const point &p, + const point &q) const { + return p % q; + } +}; +template struct bit_and; +template struct bit_and> { + constexpr point operator()(const point &p, + const point &q) const { + return p & q; + } +}; +template struct bit_or; +template struct bit_or> { + constexpr point operator()(const point &p, + const point &q) const { + return p | q; + } +}; +template struct bit_xor; +template struct bit_xor> { + constexpr point operator()(const point &p, + const point &q) const { + return p ^ q; + } +}; +} // namespace RegionCalculus + +namespace std { +template struct equal_to> { + bool operator()(const RegionCalculus::point &p, + const RegionCalculus::point &q) const { + return p.equal_to(q); + } +}; +template struct less> { + bool operator()(const RegionCalculus::point &p, + const RegionCalculus::point &q) const { + return p.less(q); + } +}; +template struct hash> { + size_t operator()(const RegionCalculus::point &p) const { + return p.hash(); + } +}; +} // namespace std + +//////////////////////////////////////////////////////////////////////////////// +// Box +//////////////////////////////////////////////////////////////////////////////// + +namespace RegionCalculus { +template struct box; + +template struct box { + constexpr static int D = 0; + + bool m_full; + + box() : m_full(false) {} + box(const box &b) = default; + box(box &&b) = default; + explicit box(bool b) : m_full(b) {} + box(const point &lo, const point &hi) : m_full(true) {} + explicit box(const point &p) : m_full(true) {} + box(const vector &lo, const vector &hi) : m_full(true) {} + box &operator=(const box &p) = default; + box &operator=(box &&p) = default; + template box(const box &b) : m_full(b.m_full) {} + + // Predicates + bool empty() const { return !m_full; } + point lower() const { return point(); } + point upper() const { return point(); } + point shape() const { return point(); } + typedef typename point::prod_t prod_t; + prod_t size() const { return m_full; } + + // Shift and scale operators + box &operator>>=(const point &p) { return *this; } + box &operator<<=(const point &p) { return *this; } + box &operator*=(const point &p) { return *this; } + box operator>>(const point &p) const { return *this; } + box operator<<(const point &p) const { return *this; } + box operator*(const point &p) const { return *this; } + box grow(const point &dlo, const point &dup) const { + return *this; + } + box grow(const point &d) const { return grow(d, d); } + box grow(T n) const { return grow(point(n)); } + box shrink(const point &dlo, const point &dup) const { + return *this; + } + box shrink(const point &d) const { return shrink(d, d); } + box shrink(T n) const { return grow(point(n)); } + + // Comparison operators + bool operator==(const box &b) const { return m_full == b.m_full; } + bool operator!=(const box &b) const { return !(*this == b); } + + bool equal_to(const box &p) const { return m_full == p.m_full; } + bool less(const box &p) const { return m_full < p.m_full; } + size_t hash() const { + std::hash h; + return h(m_full) ^ size_t(0x4a473053c081f0efULL); + } + + // Set comparison operators + bool contains(const point &p) const { return !empty(); } + bool isdisjoint(const box &b) const { return empty() | b.empty(); } + bool operator<=(const box &b) const { return m_full <= b.m_full; } + bool operator>=(const box &b) const { return b <= *this; } + bool operator<(const box &b) const { return *this <= b && *this != b; } + bool operator>(const box &b) const { return b < *this; } + bool issubset(const box &b) const { return *this <= b; } + bool issuperset(const box &b) const { return *this >= b; } + bool is_strict_subset(const box &b) const { return *this < b; } + bool is_strict_superset(const box &b) const { return *this > b; } + + // Set operations + box bounding_box(const box &b) const { return box(m_full | b.m_full); } + + box operator&(const box &b) const { return box(m_full & b.m_full); } + box intersection(const box &b) const { return *this & b; } + + vector operator-(const box &b) const { + if (m_full > b.m_full) + return vector(1, box(true)); + return vector(); + } + vector difference(const box &b) const { return *this - b; } + + vector operator|(const box &b) const { + if (m_full & b.m_full) + return vector(1, box(true)); + return vector(); + } + vector setunion(const box &b) const { return *this | b; } + + vector operator^(const box &b) const { + if (m_full ^ b.m_full) + return vector(1, box(true)); + return vector(); + } + vector symmetric_difference(const box &b) const { return *this ^ b; } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit box(const YAML::Node &node) { + assert(node.Tag() == + "tag:github.com/eschnett/SimulationIO/asdf-cxx/box-1.0.0"); + m_full = node["full"].as(); + } + friend void operator>>(const YAML::Node &node, box &b) { b = box(node); } +#endif + + ostream &output(ostream &os) const { return os << "(" << m_full << ")"; } + friend ostream &operator<<(ostream &os, const box &b) { return b.output(os); } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + w << YAML::LocalTag("sio", "box-1.0.0"); + w << YAML::Flow << YAML::BeginMap; + w << YAML::Key << "full" << YAML::Value << m_full; + w << YAML::EndMap; + return w; + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const box &b) { + return b.output(w); + } +#endif +}; + +template struct box { + point lo, hi; + box() = default; + box(const box &b) = default; + box(box &&b) = default; + box(const point &lo, const point &hi) : lo(lo), hi(hi) {} + explicit box(const point &p) : lo(p), hi(p + point(1)) {} + box(const array &lo, const array &hi) : lo(lo), hi(hi) {} + box(const vector &lo, const vector &hi) : lo(lo), hi(hi) {} + box &operator=(const box &p) = default; + box &operator=(box &&p) = default; + template box(const box &b) : lo(b.lo), hi(b.hi) {} + + // Predicates + bool empty() const { return any(hi <= lo); } + point lower() const { return lo; /* empty() ? point(0) : lo; */ } + point upper() const { return hi; /* empty() ? point(0) : hi; */ } + point shape() const { return max(hi - lo, point(0)); } + typedef typename point::prod_t prod_t; + prod_t size() const { return prod(shape()); } + + // Shift and scale operators + box &operator>>=(const point &p) { + lo += p; + hi += p; + return *this; + } + box &operator<<=(const point &p) { + lo -= p; + hi -= p; + return *this; + } + box &operator*=(const point &p) { + lo *= p; + hi *= p; + return *this; + } + box operator>>(const point &p) const { return box(*this) >>= p; } + box operator<<(const point &p) const { return box(*this) <<= p; } + box operator*(const point &p) const { return box(*this) *= p; } + box grow(const point &dlo, const point &dup) const { + box nb(*this); + if (!empty()) { + nb.lo -= dlo; + nb.hi += dup; + } + return nb; + } + box grow(const point &d) const { return grow(d, d); } + box grow(T n) const { return grow(point(n)); } + box shrink(const point &dlo, const point &dup) const { + return grow(-dlo, -dup); + } + box shrink(const point &d) const { return shrink(d, d); } + box shrink(T n) const { return shrink(point(n)); } + + // Comparison operators + bool operator==(const box &b) const { + if (empty() && b.empty()) + return true; + if (empty() || b.empty()) + return false; + std::equal_to> eq; + return eq(lo, b.lo) && eq(hi, b.hi); + } + bool operator!=(const box &b) const { return !(*this == b); } + + bool equal_to(const box &b) const { + if (empty() && b.empty()) + return true; + if (empty() || b.empty()) + return false; + std::equal_to> eq; + return eq(lo, b.lo) && eq(hi, b.hi); + } + bool less(const box &b) const { + // Empty boxes are less than non-empty ones + if (b.empty()) + return false; + if (empty()) + return true; + std::less> lt; + if (lt(lo, b.lo)) + return true; + if (lt(b.lo, lo)) + return false; + return lt(hi, b.hi); + } + size_t hash() const { + return hash_combine(hash_combine(size_t(0x8ba458a873481993ULL), lo), hi); + } + + // Set comparison operators + bool contains(const point &p) const { + if (empty()) + return false; + return all(p >= lo && p < hi); + } + bool isdisjoint(const box &b) const { return (*this & b).empty(); } + bool operator<=(const box &b) const { + if (empty()) + return true; + if (b.empty()) + return false; + return all(lo >= b.lo && hi <= b.hi); + } + bool operator>=(const box &b) const { return b <= *this; } + bool operator<(const box &b) const { return *this <= b && *this != b; } + bool operator>(const box &b) const { return b < *this; } + bool issubset(const box &b) const { return *this <= b; } + bool issuperset(const box &b) const { return *this >= b; } + bool is_strict_subset(const box &b) const { return *this < b; } + bool is_strict_superset(const box &b) const { return *this > b; } + + // Set operations + box bounding_box(const box &b) const { + if (empty()) + return b; + if (b.empty()) + return *this; + auto r = box(min(lo, b.lo), max(hi, b.hi)); +// Postcondition +#if REGIONCALCULUS_DEBUG + assert(*this <= r && b <= r); +#endif + return r; + } + + box operator&(const box &b) const { + auto nlo = max(lo, b.lo); + auto nhi = min(hi, b.hi); + auto r = box(nlo, nhi); +// Postcondition +#if REGIONCALCULUS_DEBUG + assert(r <= *this && r <= b); +#endif + return r; + } + box intersection(const box &b) const { return *this & b; } + +private: + void split(const point &p, vector &rs) const { + assert(!empty()); +#if REGIONCALCULUS_DEBUG + const auto old_rs_size = rs.size(); +#endif + for (int m = 0; m < (1 << D); ++m) { + point newlo = lo, newhi = hi; + bool is_inside = true; + for (int d = 0; d < D; ++d) { + const bool lohi = m & (1 << d); + if (p.elt[d] > lo.elt[d] && p.elt[d] < hi.elt[d]) { + if (!lohi) + newhi.elt[d] = p.elt[d]; + else + newlo.elt[d] = p.elt[d]; + } else { + is_inside &= !lohi; + } + } + if (is_inside) + rs.push_back(box(newlo, newhi)); + } +#if REGIONCALCULUS_DEBUG + // Postcondition + prod_t vol = prod_t(0); + for (auto i = old_rs_size; i < rs.size(); ++i) { + assert(!rs[i].empty()); + assert(rs[i] <= *this); + vol += rs[i].size(); + } + assert(vol == size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(rs[i].isdisjoint(rs[j])); +#endif + } + +public: + vector operator-(const box &b) const { + if (empty()) + return vector(); + if (b.empty()) + return vector(1, *this); + vector bs1; + split(b.lo, bs1); + vector bs2; + for (const auto &b1 : bs1) + b1.split(b.hi, bs2); + vector rs; + for (const auto &b2 : bs2) { + assert(b2.isdisjoint(b) || b2 <= b); + if (b2.isdisjoint(b)) + rs.push_back(b2); + } +#if REGIONCALCULUS_DEBUG + // Postcondition + prod_t vol = prod_t(0); + for (const auto &r : rs) { + assert(!r.empty()); + assert(r <= *this && !(r <= b)); + vol += r.size(); + } + assert(vol >= max(prod_t(0), size() - b.size()) && vol <= size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(rs[i].isdisjoint(rs[j])); +#endif + return rs; + } + vector difference(const box &b) const { return *this - b; } + + vector operator|(const box &b) const { + auto rs = *this - b; + if (!b.empty()) + rs.push_back(b); +#if REGIONCALCULUS_DEBUG + // Postcondition + prod_t vol = prod_t(0); + for (const auto &r : rs) { + assert(!r.empty()); + assert(r <= *this || r <= b); + vol += r.size(); + } + assert(vol >= size() && vol <= size() + b.size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(rs[i].isdisjoint(rs[j])); +#endif + return rs; + } + vector setunion(const box &b) const { return *this | b; } + + vector operator^(const box &b) const { + auto rs = *this - b; + auto rs1 = b - *this; + // TODO: Avoid this concatenation + rs.insert(rs.end(), rs1.begin(), rs1.end()); +#if REGIONCALCULUS_DEBUG + // Postcondition + prod_t vol = prod_t(0); + for (const auto &r : rs) { + assert(!r.empty()); + assert((r <= *this) ^ (r <= b)); + vol += r.size(); + } + assert(vol >= abs(size() - b.size()) && vol <= size() + b.size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(rs[i].isdisjoint(rs[j])); +#endif + return rs; + } + vector symmetric_difference(const box &b) const { return *this ^ b; } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit box(const YAML::Node &node) { + assert(node.Tag() == + "tag:github.com/eschnett/SimulationIO/asdf-cxx/box-1.0.0"); + lo = point(node["low"]); + hi = point(node["high"]); + } + friend void operator>>(const YAML::Node &node, box &b) { b = box(node); } +#endif + + ostream &output(ostream &os) const { + return os << "(" << lo << ":" << hi << ")"; + } + friend ostream &operator<<(ostream &os, const box &b) { return b.output(os); } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + w << YAML::LocalTag("sio", "box-1.0.0"); + w << YAML::Flow << YAML::BeginMap; + w << YAML::Key << "low" << YAML::Value << lo; + w << YAML::Key << "high" << YAML::Value << hi; + w << YAML::EndMap; + return w; + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const box &b) { + return b.output(w); + } +#endif +}; +} // namespace RegionCalculus + +namespace std { +template struct equal_to> { + bool operator()(const RegionCalculus::box &p, + const RegionCalculus::box &q) const { + return p.equal_to(q); + } +}; +template struct less> { + bool operator()(const RegionCalculus::box &p, + const RegionCalculus::box &q) const { + return p.less(q); + } +}; +template struct hash> { + size_t operator()(const RegionCalculus::box &b) const { + return b.hash(); + } +}; +} // namespace std + +//////////////////////////////////////////////////////////////////////////////// +// Region +//////////////////////////////////////////////////////////////////////////////// + +#if !REGIONCALCULUS_TREE + +namespace RegionCalculus { +template struct region { + vector> boxes; + region() = default; + region(const region &r) = default; + region(region &&r) = default; + + region(const box &b) { + if (!b.empty()) + boxes.push_back(b); +#if REGIONCALCULUS_DEBUG + assert(invariant()); +#endif + } + region(const vector> &bs) : boxes(bs) { + normalize(); + assert(invariant()); + } + region(vector> &&bs) : boxes(move(bs)) { + normalize(); + assert(invariant()); + } + operator vector>() const { return boxes; } + region &operator=(const region &r) = default; + region &operator=(region &&r) = default; + template region(const region &r) { + boxes.reserve(r.boxes.size()); + for (const auto &b : r.boxes) + boxes.push_back(box(b)); + assert(invariant()); + } + +private: + void append(const region &r) { + boxes.insert(boxes.end(), r.boxes.begin(), r.boxes.end()); + } + + // Normalization + void normalize() { sort(boxes.begin(), boxes.end(), std::less>()); } + +public: + // Invariant + bool invariant() const { + for (size_t i = 0; i < boxes.size(); ++i) { + if (boxes[i].empty()) + return false; + for (size_t j = i + 1; j < boxes.size(); ++j) { + if (!boxes[i].isdisjoint(boxes[j])) + return false; + if (!boxes[i].less(boxes[j])) + return false; + } + } + return true; + } + + // Predicates + bool empty() const { return boxes.empty(); } + typedef typename point::prod_t prod_t; + prod_t size() const { + prod_t sz = T(0); + for (const auto &b : boxes) + sz += b.size(); + return sz; + } + + // Shift and scale operators + region operator>>(const point &d) const { + region nr(*this); + for (auto &b : nr.boxes) + b >>= d; + return nr; + } + region operator<<(const point &d) const { return *this >> -d; } + region grow(const point &dlo, const point &dup) const { + // Cannot shrink + assert(all(dlo + dup >= point(T(0)))); + region nr; + for (const auto &b : boxes) + nr = nr | b.grow(dlo, dup); + return nr; + } + region grow(const point &d) const { return grow(d, d); } + region grow(T n) const { return grow(point(n)); } + region shrink(const point &dlo, const point &dup) const { + // Cannot grow + assert(all(dlo + dup >= point(T(0)))); + auto maxdist = maxval(max(abs(dlo), abs(dup))); + auto world = bounding_box(); + region world2 = world.grow(2 * maxdist); + return world2 - (world2 - *this).grow(dlo, dup); + } + region shrink(const point &d) const { return shrink(d, d); } + region shrink(T n) const { return shrink(point(n)); } + + // Set operations + box bounding_box() const { + box r; + for (const auto &b : boxes) + r = r.bounding_box(b); + return r; + } + + region operator&(const box &b) const { + region nr; + for (const auto &rb : boxes) { + auto nb = rb & b; + if (!nb.empty()) + nr.boxes.push_back(nb); + } + nr.normalize(); +#if REGIONCALCULUS_DEBUG + assert(invariant()); +#endif + return nr; + } + region operator&(const region &r) const { + region nr; + for (const auto &b : r.boxes) + nr.append(*this & b); + nr.normalize(); +#if REGIONCALCULUS_DEBUG + assert(invariant()); +#endif + return nr; + } + region &operator&=(const box &b) { return *this = *this & b; } + region &operator&=(const region &r) { return *this = *this & r; } + region intersection(const box &b) const { return *this & b; } + region intersection(const region &r) const { return *this & r; } + + region operator-(const box &b) const { + region nr; + for (const auto &rb : boxes) + nr.append(region(rb - b)); + nr.normalize(); +#if REGIONCALCULUS_DEBUG + assert(invariant()); +#endif + return nr; + } + region operator-(const region &r) const { + region nr = *this; + for (const auto &b : r.boxes) + nr = nr - b; + nr.normalize(); +#if REGIONCALCULUS_DEBUG + assert(invariant()); +#endif + return nr; + } + region &operator-=(const box &b) { return *this = *this - b; } + region &operator-=(const region &r) { return *this = *this - r; } + region difference(const box &b) const { return *this - b; } + region difference(const region &r) const { return *this - r; } + + region operator|(const region &r) const { + region nr = *this - r; + nr.append(r); + nr.normalize(); +#if REGIONCALCULUS_DEBUG + assert(invariant()); +#endif + return nr; + } + region operator|(const box &b) const { return *this | region(b); } + region &operator|=(const box &b) { return *this = *this | b; } + region &operator|=(const region &r) { return *this = *this | r; } + region setunion(const region &r) const { return *this | r; } + region setunion(const box &b) const { return *this | b; } + + region operator^(const region &r) const { + region nr = *this - r; + nr.append(r - *this); + nr.normalize(); +#if REGIONCALCULUS_DEBUG + assert(invariant()); +#endif + return nr; + } + region operator^(const box &b) const { return *this ^ region(b); } + region &operator^=(const box &b) { return *this = *this ^ b; } + region &operator^=(const region &r) { return *this = *this ^ r; } + region symmetric_difference(const region &r) const { return *this ^ r; } + region symmetric_difference(const box &b) const { return *this ^ b; } + + // Set comparison operators + bool contains(const point &p) const { + for (const auto &b : boxes) + if (b.contains(p)) + return true; + return false; + } + bool isdisjoint(const box &b) const { + for (const auto &rb : boxes) + if (!rb.isdisjoint(b)) + return false; + return true; + } + bool isdisjoint(const region &r) const { + for (const auto &b : r.boxes) + if (!isdisjoint(b)) + return false; + return true; + } + + // Comparison operators + bool operator<=(const region &r) const { return (*this - r).empty(); } + bool operator>=(const region &r) const { return r <= *this; } + bool operator<(const region &r) const { + return *this <= r && size() < r.size(); + } + bool operator>(const region &r) const { return r < *this; } + bool issubset(const region &r) const { return *this <= r; } + bool issuperset(const region &r) const { return *this >= r; } + bool is_strict_subset(const region &r) const { return *this < r; } + bool is_strict_superset(const region &r) const { return *this > r; } + bool operator==(const region &r) const { return (*this ^ r).empty(); } + bool operator!=(const region &r) const { return !(*this == r); } + + bool equal_to(const region &r) const { + std::equal_to> eq; + return eq(boxes, r.boxes); + } + bool less(const region &r) const { + std::less> lt; + return lt(boxes, r.boxes); + } + size_t hash() const { + std::hash> h; + return h(boxes) ^ size_t(0x4861d2118c306aefULL); + } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit region(const YAML::Node &node) { + assert(node.Tag() == + "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); + dim = node["dimension"].as(); + assert(dim == D); + boxes = node["boxes"].as>>(); + } +#endif + + ostream &output(ostream &os) const { + os << "{"; + for (size_t i = 0; i < boxes.size(); ++i) { + if (i > 0) + os << ","; + os << boxes[i]; + } + os << "}"; + return os; + } + friend ostream &operator<<(ostream &os, const region &r) { + return r.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + w << YAML::LocalTag("sio", "region-1.0.0"); + w << YAML::Flow << YAML::BeginMap; + w << YAML::Key << "dimension" << YAML::Value << D; + w << YAML::Key << "boxes" << YAML::Value << boxes; + w << YAMLL::EndMap; + return w; + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { + return r.output(w); + } +#endif +}; +} // namespace RegionCalculus + +namespace std { +template struct equal_to> { + bool operator()(const RegionCalculus::region &p, + const RegionCalculus::region &q) const { + return p.equal_to(q); + } +}; +template struct less> { + bool operator()(const RegionCalculus::region &p, + const RegionCalculus::region &q) const { + return p.less(q); + } +}; +template struct hash> { + size_t operator()(const RegionCalculus::region &p) const { + return p.hash(); + } +}; +} // namespace std + +#else // #if REGIONCALCULUS_TREE + +namespace RegionCalculus { +template struct region; + +template struct region { + constexpr static int D = 0; + + bool m_full; + + region() : m_full(false) {} + region(const region &) = default; + region(region &&) = default; + region &operator=(const region &) = default; + region &operator=(region &&) = default; + + explicit region(bool b) : m_full(b) {} + region(const box &b) : m_full(b.m_full) {} + region(const point &p) : m_full(true) {} + region(const vector> &bs) { + m_full = false; + for (const auto &b : bs) + m_full |= !b.empty(); + } + template region(const region &r) : m_full(r.m_full) {} + + // Invariant + bool invariant() const { return true; } + + // Predicates + bool empty() const { return !m_full; } + typedef typename point::prod_t prod_t; + prod_t size() const { return m_full; } + ptrdiff_t chi_size() const { return 1; } + + // Conversion to boxes + operator vector>() const { + if (empty()) + return vector>(); + return vector>(1, box(true)); + } + + // Shift and scale operators + region operator>>(const point &d) const { return *this; } + region operator<<(const point &d) const { return *this; } + region grow(const point &dlo, const point &dup) const { + return *this; + } + region grow(const point &d) const { return grow(d, d); } + region grow(T n) const { return grow(point(n)); } + region shrink(const point &dlo, const point &dup) const { + return *this; + } + region shrink(const point &d) const { return shrink(d, d); } + region shrink(T n) const { return shrink(point(n)); } + + // Set operations + box bounding_box() const { return box(m_full); } + + region operator&(const region &other) const { + return region(m_full & other.m_full); + } + region operator|(const region &other) const { + return region(m_full | other.m_full); + } + region operator^(const region &other) const { + return region(m_full ^ other.m_full); + } + region operator-(const region &other) const { + return region(m_full & !other.m_full); + } + + region &operator^=(const region &other) { return *this = *this ^ other; } + region &operator&=(const region &other) { return *this = *this & other; } + region &operator|=(const region &other) { return *this = *this | other; } + region &operator-=(const region &other) { return *this = *this - other; } + + region intersection(const region &other) const { return *this & other; } + region setunion(const region &other) const { return *this | other; } + region symmetric_difference(const region &other) const { + return *this ^ other; + } + region difference(const region &other) const { return *this - other; } + + // Set comparison operators + bool contains(const point &p) const { return m_full; } + bool isdisjoint(const region &other) const { + return !(m_full & other.m_full); + } + + // Comparison operators + bool operator<=(const region &other) const { return !m_full || other.m_full; } + bool operator>=(const region &other) const { return other <= *this; } + bool operator<(const region &other) const { return !m_full & other.m_full; } + bool operator>(const region &other) const { return other < *this; } + bool issubset(const region &other) const { return *this <= other; } + bool issuperset(const region &other) const { return *this >= other; } + bool is_strict_subset(const region &other) const { return *this < other; } + bool is_strict_superset(const region &other) const { return *this > other; } + bool operator==(const region &other) const { return m_full == other.m_full; } + bool operator!=(const region &other) const { return !(*this == other); } + + bool equal_to(const region &other) const { return m_full == other.m_full; } + bool less(const region &other) const { return m_full < other.m_full; } + size_t hash() const { + std::hash h; + return h(m_full) ^ size_t(0x07da947bfacbea06ULL); + } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit region(const YAML::Node &node) { + assert(node.Tag() == + "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); + auto dim = node["dimension"].as(); + assert(dim == 0); + m_full = node["full"].as(); + } +#endif + + ostream &output(ostream &os) const { + os << "{"; + if (m_full) + os << "(1)"; + os << "}"; + return os; + } + friend ostream &operator<<(ostream &os, const region &r) { + return r.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + w << YAML::LocalTag("sio", "region-1.0.0"); + w << YAML::Flow << YAML::BeginMap; + w << YAML::Key << "dimension" << YAML::Value << 0; + w << YAML::Key << "full" << YAML::Value << m_full; + w << YAML::EndMap; + return w; + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { + return r.output(w); + } +#endif +}; + +template struct region { + constexpr static int D = 1; + + typedef region subregion_t; // This is essentially a bool + typedef vector subregions_t; + subregions_t subregions; + + region() = default; + region(const region &) = default; + region(region &&) = default; + region &operator=(const region &) = default; + region &operator=(region &&) = default; + + region(const box &b) { + if (b.empty()) + return; + subregions = {b.lower()[0], b.upper()[0]}; + assert(invariant()); + } + region(const point &p) : region({p[0], p[0] + 1}) {} + template region(const region &r) { + subregions.reserve(r.subregions.size()); + for (const auto &pos : r.subregions) + subregions.push_back(T(pos)); + } + +private: + template void traverse_subregions(const F &f) const { + subregion_t decoded_subregion; + for (const auto &pos : subregions) { + decoded_subregion ^= subregion_t(true); + f(pos, decoded_subregion); + } + assert(decoded_subregion.empty()); + } + + template + void traverse_subregions(const F &f, const region &other) const { + subregion_t decoded_subregion0, decoded_subregion; + + typedef typename subregions_t::const_iterator subregions_iter_t; + subregions_iter_t iter0 = subregions.begin(); + subregions_iter_t iter1 = other.subregions.begin(); + const subregions_iter_t end0 = subregions.end(); + const subregions_iter_t end1 = other.subregions.end(); + while (iter0 != end0 && iter1 != end1) { + const T next_pos0 = *iter0; + const T next_pos1 = *iter1; + const T pos = min(next_pos0, next_pos1); + const bool active0 = next_pos0 == pos; + const bool active1 = next_pos1 == pos; + decoded_subregion0 ^= subregion_t(active0); + decoded_subregion ^= subregion_t(active1); + f(pos, decoded_subregion0, decoded_subregion); + if (active0) + ++iter0; + if (active1) + ++iter1; + } + for (; iter0 != end0; ++iter0) { + const T pos = *iter0; + decoded_subregion0 ^= subregion_t(true); + f(pos, decoded_subregion0, subregion_t(false)); + } + for (; iter1 != end1; ++iter1) { + const T pos = *iter1; + decoded_subregion ^= subregion_t(true); + f(pos, subregion_t(false), decoded_subregion); + } + assert(decoded_subregion0.empty()); + assert(decoded_subregion.empty()); + } + + template region unary_operator(const F &op) const { + region res; + // res.reserve(subregions.size()); + subregion_t old_decoded_subregion; + traverse_subregions( + [&](const T pos, const subregion_t &decoded_subregion0) { + auto decoded_subregion = op(decoded_subregion0); + auto subregion = decoded_subregion ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.push_back(pos); + old_decoded_subregion = decoded_subregion; + }); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + + template + region binary_operator(const F &op, const region &other) const { + region res; + // res.reserve(subregions.size() + other.subregions.size()); + subregion_t old_decoded_subregion; + traverse_subregions( + [&](const T pos, const subregion_t &decoded_subregion0, + const subregion_t &decoded_subregion1) { + auto decoded_subregion = op(decoded_subregion0, decoded_subregion1); + auto subregion = decoded_subregion ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.push_back(pos); + old_decoded_subregion = decoded_subregion; + }, + other); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + +public: + // Invariant + bool invariant() const { +#if REGIONCALCULUS_DEBUG + const size_t nboxes = subregions.size(); + for (size_t i = 1; i < nboxes; ++i) + if (subregions[i] <= subregions[i - 1]) + return false; + if (chi_size() % 2 != 0) + return false; +#endif + return true; + } + + // Predicates + bool empty() const { return subregions.empty(); } + + typedef typename point::prod_t prod_t; + prod_t size() const { + prod_t total_size = 0; + typedef typename subregions_t::const_iterator subregions_iter_t; + subregions_iter_t iter = subregions.begin(); + const subregions_iter_t end = subregions.end(); + while (iter != end) { + const auto pos0 = *iter++; + const auto pos1 = *iter++; + total_size += pos1 - pos0; + } + return total_size; + } + + ptrdiff_t chi_size() const { return subregions.size(); } + +private: + static vector subregions_from_bounds(vector lbnds, vector ubnds) { + const size_t nboxes = lbnds.size(); + assert(ubnds.size() == nboxes); + vector subregions; + if (nboxes == 0) + return subregions; + sort(lbnds.begin(), lbnds.end()); + sort(ubnds.begin(), ubnds.end()); + size_t nactive = 0; + size_t lpos = 0, upos = 0; + while (lpos < nboxes) { + const auto lbnd = lbnds[lpos]; + assert(upos < nboxes); + const auto ubnd = ubnds[upos]; + // Process lower bounds before upper bounds + if (lbnd <= ubnd) { + if (nactive == 0) + subregions.push_back(lbnd); + ++nactive; + ++lpos; + } else { + assert(nactive > 0); + --nactive; + if (nactive == 0) + subregions.push_back(ubnd); + ++upos; + } + } + assert(nactive > 0); + assert(upos < nboxes); + assert(upos + nactive == nboxes); + subregions.push_back(ubnds[nboxes - 1]); +#if REGIONCALCULUS_DEBUG + { + region reg; + for (size_t i = 0; i < nboxes; ++i) + reg |= region(box(point(lbnds[i]), point(ubnds[i]))); + assert(subregions == reg.subregions); + } +#endif + return subregions; + } + +public: + // Conversion from and to boxes + region(const vector> &boxes) { + vector lbnds, ubnds; + lbnds.reserve(boxes.size()); + ubnds.reserve(boxes.size()); + for (const auto &box : boxes) { + lbnds.push_back(box.lower()[0]); + ubnds.push_back(box.upper()[0]); + } + subregions = subregions_from_bounds(move(lbnds), move(ubnds)); + assert(invariant()); + } + + operator vector>() const { + vector> res; + res.reserve(subregions.size() / 2); + typedef typename subregions_t::const_iterator subregions_iter_t; + subregions_iter_t iter = subregions.begin(); + const subregions_iter_t end = subregions.end(); + while (iter != end) { + const auto pos0 = *iter++; + const auto pos1 = *iter++; + res.emplace_back(box(point(pos0), point(pos1))); + } +#if REGIONCALCULUS_DEBUG + assert(is_sorted(res.begin(), res.end())); + { + region reg; + for (const auto &b : res) { + assert(region(b).isdisjoint(reg)); + reg |= b; + } + assert(reg == *this); + } +#endif + return res; + } + + // Shift and scale operators + region operator>>(const point &d) const { + region nr; + nr.subregions.reserve(subregions.size()); + for (const auto &pos : subregions) + nr.subregions.push_back(pos + d[0]); + return nr; + } + region operator<<(const point &d) const { return *this >> -d; } + region grow(const point &dlo, const point &dup) const { + // Cannot shrink + assert(all(dlo + dup >= point(T(0)))); + vector lbnds, ubnds; + lbnds.reserve(subregions.size()); + ubnds.reserve(subregions.size()); + typedef typename subregions_t::const_iterator subregions_iter_t; + subregions_iter_t iter = subregions.begin(); + const subregions_iter_t end = subregions.end(); + while (iter != end) { + const auto pos0 = *iter++ - dlo[0]; + const auto pos1 = *iter++ + dup[0]; + lbnds.push_back(pos0); + ubnds.push_back(pos1); + } + region nr; + nr.subregions = subregions_from_bounds(move(lbnds), move(ubnds)); + assert(nr.invariant()); +#if REGIONCALCULUS_DEBUG + { + const vector> boxes(*this); + region reg; + for (const auto &box : boxes) + reg |= box.grow(dlo, dup); + assert(nr == reg); + } +#endif + return nr; + } + region grow(const point &d) const { return grow(d, d); } + region grow(T n) const { return grow(point(n)); } + region shrink(const point &dlo, const point &dup) const { + // Cannot grow + assert(all(dlo + dup >= point(T(0)))); + region nr; + typedef typename subregions_t::const_iterator subregions_iter_t; + subregions_iter_t iter = subregions.begin(); + const subregions_iter_t end = subregions.end(); + while (iter != end) { + const auto pos0 = *iter++ + dlo[0]; + const auto pos1 = *iter++ - dup[0]; + if (pos1 > pos0) { + nr.subregions.push_back(pos0); + nr.subregions.push_back(pos1); + } + } + assert(nr.invariant()); +#if REGIONCALCULUS_DEBUG + { + auto world = bounding_box().grow(1); + region reg = + region(world.grow(dup, dlo)) - (region(world) - *this).grow(dup, dlo); + assert(nr == reg); + } +#endif + return nr; + } + region shrink(const point &d) const { return shrink(d, d); } + region shrink(T n) const { return shrink(point(n)); } + + // Set operations + box bounding_box() const { + if (empty()) + return box(); + return box(point(*subregions.begin()), + point(*subregions.rbegin())); + } + + region operator^(const region &other) const { + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 ^ set1; }, + other); + } + + region operator&(const region &other) const { + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 & set1; }, + other); + } + + region operator|(const region &other) const { + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 | set1; }, + other); + } + + region operator-(const region &other) const { + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 - set1; }, + other); + } + + region &operator^=(const region &other) { return *this = *this ^ other; } + region &operator&=(const region &other) { return *this = *this & other; } + region &operator|=(const region &other) { return *this = *this | other; } + region &operator-=(const region &other) { return *this = *this - other; } + + region intersection(const region &other) const { return *this & other; } + region setunion(const region &other) const { return *this | other; } + region symmetric_difference(const region &other) const { + return *this ^ other; + } + region difference(const region &other) const { return *this - other; } + + // Set comparison operators + bool contains(const point &p) const { + if (subregions.empty()) + return false; + if (p[0] < *subregions.begin() || p[0] >= *subregions.rbegin()) + return false; + const auto pos = std::find_if(subregions.begin(), subregions.end(), + [p](T pos) { return p[0] >= pos; }); + return (pos - subregions.begin()) % 2 == 0; + } + bool isdisjoint(const region &other) const { return (*this & other).empty(); } + + // Comparison operators + bool operator<=(const region &other) const { return (*this - other).empty(); } + bool operator>=(const region &other) const { return other <= *this; } + bool operator<(const region &other) const { + return *this != other && *this <= other; + } + bool operator>(const region &other) const { return other < *this; } + bool issubset(const region &other) const { return *this <= other; } + bool issuperset(const region &other) const { return *this >= other; } + bool is_strict_subset(const region &other) const { return *this < other; } + bool is_strict_superset(const region &other) const { return *this > other; } + bool operator==(const region &other) const { + return subregions == other.subregions; + } + bool operator!=(const region &other) const { return !(*this == other); } + + bool equal_to(const region &other) const { + std::equal_to eq; + return eq(subregions, other.subregions); + } + bool less(const region &other) const { + std::less lt; + return lt(subregions, other.subregions); + } + size_t hash() const { + size_t r = size_t(0x725f347c326789eeULL); + for (const auto &pos : subregions) + r = hash_combine(r, pos); + return r; + } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit region(const YAML::Node &node) { + assert(node.Tag() == + "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); + auto dim = node["dimension"].as(); + assert(dim == D); + // const auto &boxes = node["boxes"].as>>(); + vector> boxes; + for (const auto &b : node["boxes"]) + boxes.push_back(box(b)); + *this = region(boxes); + } + friend void operator>>(const YAML::Node &node, region &r) { + r = region(node); + } +#endif + + ostream &output(ostream &os) const { + // os << "{"; + // for (const auto &pos_subregion : subregions) + // os << pos_subregion.first << ":" << pos_subregion.second << ","; + // os << "}"; + os << "{"; + const vector> boxes(*this); + for (size_t i = 0; i < boxes.size(); ++i) { + if (i > 0) + os << ","; + os << boxes[i]; + } + os << "}"; + return os; + } + friend ostream &operator<<(ostream &os, const region &r) { + return r.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + w << YAML::LocalTag("sio", "region-1.0.0"); + w << YAML::Flow << YAML::BeginMap; + w << YAML::Key << "dimension" << YAML::Value << D; + w << YAML::Key << "boxes" << YAML::Value << vector>(*this); + w << YAML::EndMap; + return w; + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { + return r.output(w); + } +#endif +}; + +template struct region { + typedef region subregion_t; + typedef vector> subregions_t; + subregions_t subregions; + + region() = default; + region(const region &) = default; + region(region &&) = default; + region &operator=(const region &) = default; + region &operator=(region &&) = default; + + region(const box &b) { + if (b.empty()) + return; + box subbox(b.lower().subpoint(D - 1), b.upper().subpoint(D - 1)); + subregions = {{b.lower()[D - 1], subregion_t(subbox)}, + {b.upper()[D - 1], subregion_t(subbox)}}; + assert(invariant()); + } + region(const point &p) : region(box(p)) {} + template region(const region &r) { + subregions.reserve(r.subregions.size()); + for (const auto &pos_subregion : r.subregions) { + T pos(pos_subregion.first); + subregion_t subregion(pos_subregion.second); + subregions.emplace_back(make_pair(pos, move(subregion))); + } + } + +private: + template void traverse_subregions(const F &f) const { + subregion_t decoded_subregion; + for (const auto &pos_subregion : subregions) { + const T pos = pos_subregion.first; + const auto &subregion = pos_subregion.second; + decoded_subregion ^= subregion; + f(pos, decoded_subregion); + } + assert(decoded_subregion.empty()); + } + + template + void traverse_subregions(const F &f, const region &other) const { + subregion_t decoded_subregion0, decoded_subregion; + + typedef typename subregions_t::const_iterator subregions_iter_t; + subregions_iter_t iter0 = subregions.begin(); + subregions_iter_t iter1 = other.subregions.begin(); + const subregions_iter_t end0 = subregions.end(); + const subregions_iter_t end1 = other.subregions.end(); + while (iter0 != end0 || iter1 != end1) { + const T next_pos0 = + iter0 != end0 ? iter0->first : numeric_limits::max(); + const T next_pos1 = + iter1 != end1 ? iter1->first : numeric_limits::max(); + const T pos = min(next_pos0, next_pos1); + const bool active0 = next_pos0 == pos; + const bool active1 = next_pos1 == pos; + subregion_t dummy; + const subregion_t &subregion0 = active0 ? iter0->second : dummy; + const subregion_t &subregion = active1 ? iter1->second : dummy; + if (active0) + decoded_subregion0 ^= subregion0; + if (active1) + decoded_subregion ^= subregion; + + f(pos, decoded_subregion0, decoded_subregion); + + if (active0) + ++iter0; + if (active1) + ++iter1; + } + assert(decoded_subregion0.empty()); + assert(decoded_subregion.empty()); + } + + template region unary_operator(const F &op) const { + region res; + subregion_t old_decoded_subregion; + traverse_subregions( + [&](const T pos, const subregion_t &decoded_subregion0) { + auto decoded_subregion = op(decoded_subregion0); + auto subregion = decoded_subregion ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.emplace_back(make_pair(pos, move(subregion))); + old_decoded_subregion = move(decoded_subregion); + }); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + + template + region binary_operator(const F &op, const region &other) const { + region res; + subregion_t old_decoded_subregion; + traverse_subregions( + [&](const T pos, const subregion_t &decoded_subregion0, + const subregion_t &decoded_subregion1) { + auto decoded_subregion = op(decoded_subregion0, decoded_subregion1); + auto subregion = decoded_subregion ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.emplace_back(make_pair(pos, move(subregion))); + old_decoded_subregion = move(decoded_subregion); + }, + other); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + +public: + // Invariant + bool invariant() const { +#if REGIONCALCULUS_DEBUG + for (const auto &pos_subregion : subregions) { + const auto &subregion = pos_subregion.second; + if (subregion.empty() || !subregion.invariant()) + return false; + } + if (chi_size() % 2 != 0) + return false; +#endif + return true; + } + + // Predicates + bool empty() const { return subregions.empty(); } + + typedef typename point::prod_t prod_t; + prod_t size() const { + prod_t total_size = 0; + T old_pos = numeric_limits::min(); // location of last subregion + prod_t old_subregion_size = 0; // number of points in the last subregion + traverse_subregions([&](const T pos, const subregion_t &subregion) { + const prod_t subregion_size = subregion.size(); + total_size += old_subregion_size == 0 + ? 0 + : prod_t(pos - old_pos) * old_subregion_size; + old_pos = pos; + old_subregion_size = subregion_size; + }); + assert(old_subregion_size == 0); + return total_size; + } + + ptrdiff_t chi_size() const { + ptrdiff_t sz = 0; + for (const auto &pos_subregion : subregions) { + const auto &subregion = pos_subregion.second; + sz += subregion.chi_size(); + } + return sz; + } + + // Conversion from and to boxes +private: + static region + region_from_boxes(const typename vector>::const_iterator &begin, + const typename vector>::const_iterator &end) { + auto sz = end - begin; + if (sz == 0) + return region(); + if (sz == 1) + return region(*begin); + const auto mid = begin + sz / 2; + return region_from_boxes(begin, mid) | region_from_boxes(mid, end); + } + +public: + region(const vector> &boxes) { + *this = region_from_boxes(boxes.begin(), boxes.end()); +#if REGIONCALCULUS_DEBUG + { + region reg; + for (const auto &box : boxes) + reg |= region(box); + assert(*this == reg); + } +#endif + } + + operator vector>() const { + vector> res; + map, T> old_subboxes; + traverse_subregions([&](const T pos, const subregion_t &subregion) { + // Convert subregion to boxes + const vector> subboxes1(subregion); + + auto iter0 = old_subboxes.begin(); + auto iter1 = subboxes1.begin(); + const auto end0 = old_subboxes.end(); + const auto end1 = subboxes1.end(); +#if REGIONCALCULUS_DEBUG + assert(is_sorted(iter1, end1)); +#endif + map, T> subboxes; + while (iter0 != end0 || iter1 != end1) { + bool active0 = iter0 != end0; + bool active1 = iter1 != end1; + box dummy; + const box &subbox0 = active0 ? iter0->first : dummy; + const box &subbox1 = active1 ? *iter1 : dummy; + // When both subboxes are active, keep only the first (as determined by + // less<>) + std::equal_to eq; + std::less lt; + if (active0 && active1) { + active0 = !lt(subbox0, subbox1); + active1 = !lt(subbox1, subbox0); + } + + const T old_pos = iter0->second; + if (active0 && active1 && eq(subbox0, subbox1)) { + // The current bbox continues unchanged -- keep it + subboxes[subbox1] = old_pos; + } else { + if (active0) + // The current box changed; finalize it + res.push_back(box(subbox0.lower().superpoint(D - 1, old_pos), + subbox0.upper().superpoint(D - 1, pos))); + if (active1) + // There is a new box; add it + subboxes[subbox1] = pos; + } + + if (active0) + ++iter0; + if (active1) + ++iter1; + } + old_subboxes = move(subboxes); + }); + assert(old_subboxes.empty()); +#if REGIONCALCULUS_DEBUG + assert(is_sorted(res.begin(), res.end())); + { + region reg; + for (const auto &b : res) { + assert(region(b).isdisjoint(reg)); + reg |= b; + } + assert(reg == *this); + } +#endif + return res; + } + + // Shift and scale operators + region operator>>(const point &d) const { + region nr; + nr.subregions.reserve(subregions.size()); + T dx = d[D - 1]; + auto subd = d.subpoint(D - 1); + for (const auto &pos_subregion : subregions) { + const T pos = pos_subregion.first; + const auto &subregion = pos_subregion.second; + nr.subregions.emplace_back(make_pair(pos + dx, subregion >> subd)); + } + assert(nr.invariant()); + return nr; + } + region operator<<(const point &d) const { return *this >> -d; } + region grow(const point &dlo, const point &dup) const { + // Cannot shrink + assert(all(dlo + dup >= point(T(0)))); + // region nr; + // for (const auto &box : vector>(*this)) + // nr |= box.grow(dlo, dup); + // return nr; + return reduce([&](const box &b) { return region(b.grow(dlo, dup)); }, + [](const region &x, const region &y) { return x | y; }, + vector>(*this)); + } + region grow(const point &d) const { return grow(d, d); } + region grow(T n) const { return grow(point(n)); } + region shrink(const point &dlo, const point &dup) const { + // Cannot grow + assert(all(dlo + dup >= point(T(0)))); + auto world = bounding_box().grow(1); + return region(world.grow(dup, dlo)) - + (region(world) - *this).grow(dup, dlo); + } + region shrink(const point &d) const { return shrink(d, d); } + region shrink(T n) const { return shrink(point(n)); } + + // Set operations + box bounding_box() const { + if (empty()) + return box(); + point pmin(numeric_limits::max()), + pmax(numeric_limits::min()); + for (const auto &pos_subregion : subregions) { + const auto &subregion = pos_subregion.second; + auto subbox = subregion.bounding_box(); + pmin = min(pmin, subbox.lower()); + pmax = max(pmax, subbox.upper()); + } + const T xmin = subregions.begin()->first; + const T xmax = subregions.rbegin()->first; + return box(pmin.superpoint(D - 1, xmin), + pmax.superpoint(D - 1, xmax)); + } + + region operator^(const region &other) const { + // TODO: If other is much smaller than this, direct insertion may be faster + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 ^ set1; }, + other); + } + + region operator&(const region &other) const { + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 & set1; }, + other); + } + + region operator|(const region &other) const { + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 | set1; }, + other); + } + + region operator-(const region &other) const { + // return *this & (*this ^ other); + return binary_operator([](const subregion_t &set0, + const subregion_t &set1) { return set0 - set1; }, + other); + } + + region &operator^=(const region &other) { return *this = *this ^ other; } + region &operator&=(const region &other) { return *this = *this & other; } + region &operator|=(const region &other) { return *this = *this | other; } + region &operator-=(const region &other) { return *this = *this - other; } + + region intersection(const region &other) const { return *this & other; } + region setunion(const region &other) const { return *this | other; } + region symmetric_difference(const region &other) const { + return *this ^ other; + } + region difference(const region &other) const { return *this - other; } + + // Set comparison operators + bool contains(const point &p) const { return !isdisjoint(region(p)); } + bool isdisjoint(const region &other) const { return (*this & other).empty(); } + + // Comparison operators + bool operator<=(const region &other) const { return (*this - other).empty(); } + bool operator>=(const region &other) const { return other <= *this; } + bool operator<(const region &other) const { + return *this != other && *this <= other; + } + bool operator>(const region &other) const { return other < *this; } + bool issubset(const region &other) const { return *this <= other; } + bool issuperset(const region &other) const { return *this >= other; } + bool is_strict_subset(const region &other) const { return *this < other; } + bool is_strict_superset(const region &other) const { return *this > other; } + bool operator==(const region &other) const { + return subregions == other.subregions; + } + bool operator!=(const region &other) const { return !(*this == other); } + + bool equal_to(const region &other) const { + std::equal_to eq; + return eq(subregions, other.subregions); + } + bool less(const region &other) const { + std::less lt; + return lt(subregions, other.subregions); + } + size_t hash() const { + size_t r = size_t(0x4eecc6384bcd469dULL); + for (const auto &p : subregions) + r = hash_combine(hash_combine(r, p.first), p.second); + return r; + } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit region(const YAML::Node &node) { + assert(node.Tag() == + "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); + auto dim = node["dimension"].as(); + assert(dim == D); + // const auto &boxes = node["boxes"].as>>(); + vector> boxes; + for (const auto &b : node["boxes"]) + boxes.push_back(box(b)); + *this = region(boxes); + } + friend void operator>>(const YAML::Node &node, region &r) { + r = region(node); + } +#endif + + ostream &output(ostream &os) const { + // os << "{"; + // for (const auto &pos_subregion : subregions) + // os << pos_subregion.first << ":" << pos_subregion.second << ","; + // os << "}"; + os << "{"; + const vector> boxes(*this); + for (size_t i = 0; i < boxes.size(); ++i) { + if (i > 0) + os << ","; + os << boxes[i]; + } + os << "}"; + return os; + } + friend ostream &operator<<(ostream &os, const region &r) { + return r.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + w << YAML::LocalTag("sio", "region-1.0.0"); + w << YAML::Flow << YAML::BeginMap; + w << YAML::Key << "dimension" << YAML::Value << D; + w << YAML::Key << "boxes" << YAML::Value << vector>(*this); + w << YAML::EndMap; + return w; + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { + return r.output(w); + } +#endif +}; +} // namespace RegionCalculus + +namespace std { +template struct equal_to> { + bool operator()(const RegionCalculus::region &p, + const RegionCalculus::region &q) const { + return p.equal_to(q); + } +}; +template struct less> { + bool operator()(const RegionCalculus::region &p, + const RegionCalculus::region &q) const { + return p.less(q); + } +}; +template struct hash> { + size_t operator()(const RegionCalculus::region &p) const { + return p.hash(); + } +}; +} // namespace std + +#endif // #if REGIONCALCULUS_TREE + +//////////////////////////////////////////////////////////////////////////////// +// Dimension-independent wrappers +//////////////////////////////////////////////////////////////////////////////// + +namespace RegionCalculus { + +// Virtual classes + +template struct vpoint { + virtual ~vpoint() {} + + virtual unique_ptr copy() const = 0; + + static unique_ptr make(int d); + static unique_ptr make(int d, T x); + static unique_ptr make(const vector &val); + template static unique_ptr make(const vector &val); + virtual operator vector() const = 0; + template operator vector() const { + auto rT(vector(*this)); + vector rU(rT.size()); + for (size_t i = 0; i < rU.size(); ++i) + rU[i] = U(move(rT[i])); + return rU; + } + template static unique_ptr make(const vpoint &p); + + virtual int rank() const = 0; + + // Access and conversion + virtual T operator[](int d) const = 0; + virtual T &operator[](int d) = 0; + virtual unique_ptr subpoint(int dir) const = 0; + virtual unique_ptr superpoint(int dir, T x) const = 0; + virtual unique_ptr reversed() const = 0; + + // Unary operators + virtual unique_ptr operator+() const = 0; + virtual unique_ptr operator-() const = 0; + virtual unique_ptr operator~() const = 0; + virtual unique_ptr> operator!() const = 0; + + // Assignment operators + virtual vpoint &operator+=(const vpoint &p) = 0; + virtual vpoint &operator-=(const vpoint &p) = 0; + virtual vpoint &operator*=(const vpoint &p) = 0; + virtual vpoint &operator/=(const vpoint &p) = 0; + virtual vpoint &operator%=(const vpoint &p) = 0; + virtual vpoint &operator&=(const vpoint &p) = 0; + virtual vpoint &operator|=(const vpoint &p) = 0; + virtual vpoint &operator^=(const vpoint &p) = 0; + + // Binary operators + virtual unique_ptr operator+(const vpoint &p) const = 0; + virtual unique_ptr operator-(const vpoint &p) const = 0; + virtual unique_ptr operator*(const vpoint &p) const = 0; + virtual unique_ptr operator/(const vpoint &p) const = 0; + virtual unique_ptr operator%(const vpoint &p) const = 0; + virtual unique_ptr operator&(const vpoint &p) const = 0; + virtual unique_ptr operator|(const vpoint &p) const = 0; + virtual unique_ptr operator^(const vpoint &p) const = 0; + virtual unique_ptr> operator&&(const vpoint &p) const = 0; + virtual unique_ptr> operator||(const vpoint &p) const = 0; + + // Unary functions + virtual unique_ptr abs() const = 0; + + // Binary functions + virtual unique_ptr min(const vpoint &p) const = 0; + virtual unique_ptr max(const vpoint &p) const = 0; + + // Comparison operators + virtual unique_ptr> operator==(const vpoint &p) const = 0; + virtual unique_ptr> operator!=(const vpoint &p) const = 0; + virtual unique_ptr> operator<(const vpoint &p) const = 0; + virtual unique_ptr> operator>(const vpoint &p) const = 0; + virtual unique_ptr> operator<=(const vpoint &p) const = 0; + virtual unique_ptr> operator>=(const vpoint &p) const = 0; + + virtual bool equal_to(const vpoint &p) const = 0; + virtual bool less(const vpoint &p) const = 0; + virtual size_t hash() const = 0; + + // Reductions + virtual bool any() const = 0; + virtual bool all() const = 0; + virtual T minval() const = 0; + virtual T maxval() const = 0; + virtual T sum() const = 0; + typedef typename point::prod_t prod_t; + virtual prod_t prod() const = 0; + + // I/O +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + static unique_ptr make(const YAML::Node &node); +#endif + virtual ostream &output(ostream &os) const = 0; + friend ostream &operator<<(ostream &os, const vpoint &p) { + return p.output(os); + } +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + virtual YAML::Emitter &output(YAML::Emitter &w) const = 0; + friend YAML::Emitter &operator<<(YAML::Emitter &w, const vpoint &p) { + return p.output(w); + } +#endif +}; + +template struct vbox { + virtual ~vbox() {} + + virtual unique_ptr copy() const = 0; + + static unique_ptr make(int d); + static unique_ptr make(const vpoint &lo, const vpoint &hi); + template static unique_ptr make(const vbox &p); + + virtual int rank() const = 0; + + // Predicates + virtual bool empty() const = 0; + virtual unique_ptr> shape() const = 0; + virtual unique_ptr> lower() const = 0; + virtual unique_ptr> upper() const = 0; + typedef typename box::prod_t prod_t; + virtual prod_t size() const = 0; + + // Shift and scale operators + virtual vbox &operator>>=(const vpoint &p) = 0; + virtual vbox &operator<<=(const vpoint &p) = 0; + virtual vbox &operator*=(const vpoint &p) = 0; + virtual unique_ptr operator>>(const vpoint &p) const = 0; + virtual unique_ptr operator<<(const vpoint &p) const = 0; + virtual unique_ptr operator*(const vpoint &p) const = 0; + virtual unique_ptr grow(const vpoint &dlo, + const vpoint &dup) const = 0; + virtual unique_ptr grow(const vpoint &d) const = 0; + virtual unique_ptr grow(T n) const = 0; + virtual unique_ptr shrink(const vpoint &dlo, + const vpoint &dup) const = 0; + virtual unique_ptr shrink(const vpoint &d) const = 0; + virtual unique_ptr shrink(T n) const = 0; + + // Comparison operators + virtual bool operator==(const vbox &b) const = 0; + + virtual bool equal_to(const vbox &b) const = 0; + virtual bool less(const vbox &b) const = 0; + virtual size_t hash() const = 0; + + // Set comparison operators + virtual bool contains(const vpoint &p) const = 0; + virtual bool isdisjoint(const vbox &b) const = 0; + virtual bool operator<=(const vbox &b) const = 0; + virtual bool operator<(const vbox &b) const = 0; + + // Set operations + virtual unique_ptr bounding_box(const vbox &b) const = 0; + virtual unique_ptr operator&(const vbox &b) const = 0; + // virtual unique_ptr operator-(const vbox &b) const = 0; + // virtual unique_ptr operator|(const vbox &b) const = 0; + // virtual unique_ptr operator^(const vbox &b) const = 0; + + // I/O +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + static unique_ptr make(const YAML::Node &node); +#endif + virtual ostream &output(ostream &os) const = 0; + friend ostream &operator<<(ostream &os, const vbox &b) { + return b.output(os); + } +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + virtual YAML::Emitter &output(YAML::Emitter &w) const = 0; + friend YAML::Emitter &operator<<(YAML::Emitter &w, const vbox &b) { + return b.output(w); + } +#endif +}; + +template struct vregion { + virtual ~vregion() {} + + virtual unique_ptr copy() const = 0; + + static unique_ptr make(int d); + static unique_ptr make(const vbox &b); + static unique_ptr make(const vector>> &bs); + virtual operator vector>>() const = 0; + template static unique_ptr make(const vregion &p); + + virtual int rank() const = 0; + + // Predicates + virtual bool invariant() const = 0; + virtual bool empty() const = 0; + typedef typename region::prod_t prod_t; + virtual prod_t size() const = 0; + + // Shift and scale operators + virtual unique_ptr operator>>(const vpoint &d) const = 0; + virtual unique_ptr operator<<(const vpoint &d) const = 0; + virtual unique_ptr grow(const vpoint &dlo, + const vpoint &dup) const = 0; + virtual unique_ptr grow(const vpoint &d) const = 0; + virtual unique_ptr grow(T n) const = 0; + virtual unique_ptr shrink(const vpoint &dlo, + const vpoint &dup) const = 0; + virtual unique_ptr shrink(const vpoint &d) const = 0; + virtual unique_ptr shrink(T n) const = 0; + + // Set operations + virtual unique_ptr> bounding_box() const = 0; + virtual unique_ptr operator&(const vbox &b) const = 0; + virtual unique_ptr operator&(const vregion &r) const = 0; + virtual unique_ptr operator-(const vbox &b) const = 0; + virtual unique_ptr operator-(const vregion &r) const = 0; + virtual unique_ptr operator|(const vbox &b) const = 0; + virtual unique_ptr operator|(const vregion &r) const = 0; + virtual unique_ptr operator^(const vbox &b) const = 0; + virtual unique_ptr operator^(const vregion &r) const = 0; + + // Set comparison operators + virtual bool contains(const vpoint &p) const = 0; + virtual bool isdisjoint(const vbox &b) const = 0; + virtual bool isdisjoint(const vregion &r) const = 0; + + // Comparison operators + virtual bool operator<=(const vregion &r) const = 0; + virtual bool operator>=(const vregion &r) const = 0; + virtual bool operator<(const vregion &r) const = 0; + virtual bool operator>(const vregion &r) const = 0; + virtual bool operator==(const vregion &r) const = 0; + virtual bool operator!=(const vregion &r) const = 0; + + virtual bool equal_to(const vregion &r) const = 0; + virtual bool less(const vregion &r) const = 0; + virtual size_t hash() const = 0; + + // I/O +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + static unique_ptr make(const YAML::Node &node); +#endif + virtual ostream &output(ostream &os) const = 0; + friend ostream &operator<<(ostream &os, const vregion &r) { + return r.output(os); + } +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + virtual YAML::Emitter &output(YAML::Emitter &w) const = 0; + friend YAML::Emitter &operator<<(YAML::Emitter &w, const vregion &r) { + return r.output(w); + } +#endif +}; + +//////////////////////////////////////////////////////////////////////////////// + +// Wrapper classes (using pointers) + +template struct wpoint : vpoint { + point val; + + wpoint(const wpoint &p) = default; + wpoint(wpoint &&p) = default; + wpoint &operator=(const wpoint &p) = default; + wpoint &operator=(wpoint &&p) = default; + unique_ptr> copy() const { return make_unique1(*this); } + + wpoint(const point &p) : val(p) {} + + wpoint() : val() {} + wpoint(T x) : val(x) {} + wpoint(const array &p) : val(p) {} + template wpoint(const array &p) : val(p) {} + wpoint(const vector &p) : val(p) {} + template wpoint(const vector &p) : val(p) {} + operator vector() const { return vector(val); } + template operator vector() const { + vector rT(val); + vector rU(rT.size()); + for (size_t i = 0; i < rU.size(); ++i) + rU[i] = U(move(rT[i])); + return rU; + } + template wpoint(const wpoint &p) : val(p.val) {} + + int rank() const { return D; } + + // Access and conversion + T operator[](int d) const { return val[d]; } + T &operator[](int d) { return val[d]; } + unique_ptr> subpoint(int dir) const { + return make_unique1 0 ? D - 1 : 0)>>(val.subpoint(dir)); + } + unique_ptr> superpoint(int dir, T x) const { + // This is intentionally wrong for D >= 4 to avoid infinite recursion to + // ever larger ranks + return make_unique1>(val.superpoint(dir, x)); + } + unique_ptr> reversed() const { + return make_unique1(val.reversed()); + } + + // Unary operators + unique_ptr> operator+() const { return make_unique1(+val); } + unique_ptr> operator-() const { return make_unique1(-val); } + unique_ptr> operator~() const { + return make_unique1( + typename call_if_integral, bit_not>>::type()( + val)); + } + unique_ptr> operator!() const { + return make_unique1>(!val); + } + + // Assignment operators + vpoint &operator+=(const vpoint &p) { + val += dynamic_cast(p).val; + return *this; + } + vpoint &operator-=(const vpoint &p) { + val -= dynamic_cast(p).val; + return *this; + } + vpoint &operator*=(const vpoint &p) { + val *= dynamic_cast(p).val; + return *this; + } + vpoint &operator/=(const vpoint &p) { + val /= dynamic_cast(p).val; + return *this; + } + vpoint &operator%=(const vpoint &p) { + typename call_if_integral, modulus_eq>>::type()( + val, dynamic_cast(p).val); + return *this; + } + vpoint &operator&=(const vpoint &p) { + typename call_if_integral, bit_and_eq>>::type()( + val, dynamic_cast(p).val); + return *this; + } + vpoint &operator|=(const vpoint &p) { + typename call_if_integral, bit_or_eq>>::type()( + val, dynamic_cast(p).val); + return *this; + } + vpoint &operator^=(const vpoint &p) { + typename call_if_integral, bit_xor_eq>>::type()( + val, dynamic_cast(p).val); + return *this; + } + + // Binary operators + unique_ptr> operator+(const vpoint &p) const { + return make_unique1(val + dynamic_cast(p).val); + } + unique_ptr> operator-(const vpoint &p) const { + return make_unique1(val - dynamic_cast(p).val); + } + unique_ptr> operator*(const vpoint &p) const { + return make_unique1(val * dynamic_cast(p).val); + } + unique_ptr> operator/(const vpoint &p) const { + return make_unique1(val / dynamic_cast(p).val); + } + unique_ptr> operator%(const vpoint &p) const { + return make_unique1( + typename call_if_integral, modulus>>::type()( + val, dynamic_cast(p).val)); + } + unique_ptr> operator&(const vpoint &p) const { + return make_unique1( + typename call_if_integral, bit_and>>::type()( + val, dynamic_cast(p).val)); + } + unique_ptr> operator|(const vpoint &p) const { + return make_unique1( + typename call_if_integral, bit_or>>::type()( + val, dynamic_cast(p).val)); + } + unique_ptr> operator^(const vpoint &p) const { + return make_unique1( + typename call_if_integral, bit_xor>>::type()( + val, dynamic_cast(p).val)); + } + unique_ptr> operator&&(const vpoint &p) const { + return make_unique1>(val && + dynamic_cast(p).val); + } + unique_ptr> operator||(const vpoint &p) const { + return make_unique1>(val || + dynamic_cast(p).val); + } + + // Unary functions + unique_ptr> abs() const { return make_unique1(val.abs()); } + + // Binary functions + unique_ptr> min(const vpoint &p) const { + return make_unique1(val.min(dynamic_cast(p).val)); + } + unique_ptr> max(const vpoint &p) const { + return make_unique1(val.max(dynamic_cast(p).val)); + } + + // Comparison operators + unique_ptr> operator==(const vpoint &p) const { + return make_unique1>(val == + dynamic_cast(p).val); + } + unique_ptr> operator!=(const vpoint &p) const { + return make_unique1>(val != + dynamic_cast(p).val); + } + unique_ptr> operator<(const vpoint &p) const { + return make_unique1>(val < + dynamic_cast(p).val); + } + unique_ptr> operator>(const vpoint &p) const { + return make_unique1>(val > + dynamic_cast(p).val); + } + unique_ptr> operator<=(const vpoint &p) const { + return make_unique1>(val <= + dynamic_cast(p).val); + } + unique_ptr> operator>=(const vpoint &p) const { + return make_unique1>(val >= + dynamic_cast(p).val); + } + + bool equal_to(const vpoint &p) const { + return val.equal_to(dynamic_cast(p).val); + } + bool less(const vpoint &p) const { + return val.less(dynamic_cast(p).val); + } + size_t hash() const { return val.hash(); } + + // Reductions + bool any() const { return val.any(); } + bool all() const { return val.all(); } + T minval() const { return val.minval(); } + T maxval() const { return val.maxval(); } + T sum() const { return val.sum(); } + typedef typename vpoint::prod_t prod_t; + prod_t prod() const { return val.prod(); } + + // I/O +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit wpoint(const YAML::Node &node) : val(node) {} +#endif + ostream &output(ostream &os) const { return val.output(os); } + // friend ostream &operator<<(ostream &os, const wpoint &p) { + // return p.output(os); + // } +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { return val.output(w); } + // friend YAML::Emitter &operator<<(YAML::Emitter &w, const wpoint &p) { + // return p.output(w); + // } +#endif +}; + +template struct wbox : vbox { + box val; + + wbox(const wbox &b) = default; + wbox(wbox &&b) = default; + wbox &operator=(const wbox &b) = default; + wbox &operator=(wbox &&b) = default; + unique_ptr> copy() const { return make_unique1(*this); } + + wbox(const box &b) : val(b) {} + + wbox() : val() {} + wbox(const wpoint &lo, const wpoint &hi) : val(lo.val, hi.val) {} + template wbox(const wbox &p) : val(p.val) {} + + int rank() const { return D; } + + // Predicates + bool empty() const { return val.empty(); } + unique_ptr> lower() const { + return make_unique1>(val.lower()); + } + unique_ptr> upper() const { + return make_unique1>(val.upper()); + } + unique_ptr> shape() const { + return make_unique1>(val.shape()); + } + typedef typename vbox::prod_t prod_t; + prod_t size() const { return val.size(); } + + // Shift and scale operators + vbox &operator>>=(const vpoint &p) { + val >>= dynamic_cast &>(p).val; + return *this; + } + vbox &operator<<=(const vpoint &p) { + val <<= dynamic_cast &>(p).val; + return *this; + } + vbox &operator*=(const vpoint &p) { + val *= dynamic_cast &>(p).val; + return *this; + } + unique_ptr> operator>>(const vpoint &p) const { + return make_unique1(val >> dynamic_cast &>(p).val); + } + unique_ptr> operator<<(const vpoint &p) const { + return make_unique1(val << dynamic_cast &>(p).val); + } + unique_ptr> operator*(const vpoint &p) const { + return make_unique1(val * dynamic_cast &>(p).val); + } + unique_ptr> grow(const vpoint &dlo, const vpoint &dup) const { + return make_unique1( + val.grow(dynamic_cast &>(dlo).val, + dynamic_cast &>(dup).val)); + } + unique_ptr> grow(const vpoint &d) const { + return make_unique1( + val.grow(dynamic_cast &>(d).val)); + } + unique_ptr> grow(T n) const { + return make_unique1(val.grow(n)); + } + unique_ptr> shrink(const vpoint &dlo, const vpoint &dup) const { + return make_unique1( + val.shrink(dynamic_cast &>(dlo).val, + dynamic_cast &>(dup).val)); + } + unique_ptr> shrink(const vpoint &d) const { + return make_unique1( + val.shrink(dynamic_cast &>(d).val)); + } + unique_ptr> shrink(T n) const { + return make_unique1(val.shrink(n)); + } + + // Comparison operators + bool operator==(const vbox &b) const { + return rank() == b.rank() && val == dynamic_cast &>(b).val; + } + + bool equal_to(const vbox &b) const { + return val.equal_to(dynamic_cast &>(b).val); + } + bool less(const vbox &b) const { + return val.less(dynamic_cast &>(b).val); + } + size_t hash() const { return val.hash(); } + + // Set comparison operators + bool contains(const vpoint &p) const { + return val.contains(dynamic_cast &>(p).val); + } + bool isdisjoint(const vbox &b) const { + return val.isdisjoint(dynamic_cast(b).val); + } + bool operator<=(const vbox &b) const { + return val <= dynamic_cast(b).val; + } + bool operator<(const vbox &b) const { + return val < dynamic_cast(b).val; + } + + // Set operations + unique_ptr> bounding_box(const vbox &b) const { + return make_unique1( + val.bounding_box(dynamic_cast(b).val)); + } + unique_ptr> operator&(const vbox &b) const { + return make_unique1(val & dynamic_cast(b).val); + } + // unique_ptr> operator-(const vbox &b) const { + // return make_unique1(val - dynamic_cast(b).val); + // } + // unique_ptr> operator|(const vbox &b) const { + // return make_unique1(val | dynamic_cast(b).val); + // } + // unique_ptr> operator^(const vbox &b) const { + // return make_unique1(val ^ dynamic_cast(b).val); + // } + + // I/O +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit wbox(const YAML::Node &node) : val(node) {} +#endif + ostream &output(ostream &os) const { return val.output(os); } + // friend ostream &operator<<(ostream &os, const wbox &b) { + // return b.output(os); + // } +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { return val.output(w); } + // friend YAML::Emitter &operator<<(YAML::Emitter &w, const wbox &b) { + // return b.output(w); + // } +#endif +}; + +template struct wregion : vregion { + region val; + + wregion(const wregion &r) = default; + wregion(wregion &&r) = default; + wregion &operator=(const wregion &r) = default; + wregion &operator=(wregion &&r) = default; + unique_ptr> copy() const { + return unique_ptr>(new wregion(*this)); + } + + wregion(const region &r) : val(r) {} + wregion(region &&r) : val(move(r)) {} + + wregion() = default; + wregion(const wbox &b) : val(b.val) {} + wregion(const vector>> &bs) { + vector> rs; + for (const auto &b : bs) + rs.push_back(dynamic_cast &>(b).val); + val = region(rs); + } + operator vector>>() const { + vector>> bs; + for (const auto &b : vector>(val)) + bs.push_back(make_unique1>(b)); + return bs; + } + template wregion(const wregion &p) : val(p.val) {} + + int rank() const { return D; } + + // Predicates + bool invariant() const { return val.invariant(); } + bool empty() const { return val.empty(); } + typedef typename vregion::prod_t prod_t; + prod_t size() const { return val.size(); } + + // Shift and scale operators + unique_ptr> operator>>(const vpoint &d) const { + return make_unique1(val >> + dynamic_cast &>(d).val); + } + unique_ptr> operator<<(const vpoint &d) const { + return make_unique1(val + << dynamic_cast &>(d).val); + } + unique_ptr> grow(const vpoint &dlo, + const vpoint &dup) const { + return make_unique1( + val.grow(dynamic_cast &>(dlo).val, + dynamic_cast &>(dup).val)); + } + unique_ptr> grow(const vpoint &d) const { + return make_unique1( + val.grow(dynamic_cast &>(d).val)); + } + unique_ptr> grow(T n) const { + return make_unique1(val.grow(n)); + } + unique_ptr> shrink(const vpoint &dlo, + const vpoint &dup) const { + return make_unique1( + val.shrink(dynamic_cast &>(dlo).val, + dynamic_cast &>(dup).val)); + } + unique_ptr> shrink(const vpoint &d) const { + return make_unique1( + val.shrink(dynamic_cast &>(d).val)); + } + unique_ptr> shrink(T n) const { + return make_unique1(val.shrink(n)); + } + + // Set operations + unique_ptr> bounding_box() const { + return make_unique1>(val.bounding_box()); + } + unique_ptr> operator&(const vbox &b) const { + return make_unique1(val & dynamic_cast &>(b).val); + } + unique_ptr> operator&(const vregion &r) const { + return make_unique1(val & dynamic_cast(r).val); + } + unique_ptr> operator-(const vbox &b) const { + return make_unique1(val - dynamic_cast &>(b).val); + } + unique_ptr> operator-(const vregion &r) const { + return make_unique1(val - dynamic_cast(r).val); + } + unique_ptr> operator|(const vbox &b) const { + return make_unique1(val | dynamic_cast &>(b).val); + } + unique_ptr> operator|(const vregion &r) const { + return make_unique1(val | dynamic_cast(r).val); + } + unique_ptr> operator^(const vbox &b) const { + return make_unique1(val ^ dynamic_cast &>(b).val); + } + unique_ptr> operator^(const vregion &r) const { + return make_unique1(val ^ dynamic_cast(r).val); + } + + // Set comparison operators + bool contains(const vpoint &p) const { + return val.contains(dynamic_cast &>(p).val); + } + bool isdisjoint(const vbox &b) const { + return val.isdisjoint(dynamic_cast &>(b).val); + } + bool isdisjoint(const vregion &r) const { + return val.isdisjoint(dynamic_cast(r).val); + } + + // Comparison operators + bool operator<=(const vregion &r) const { + return val <= dynamic_cast(r).val; + } + bool operator>=(const vregion &r) const { + return val >= dynamic_cast(r).val; + } + bool operator<(const vregion &r) const { + return val < dynamic_cast(r).val; + } + bool operator>(const vregion &r) const { + return val > dynamic_cast(r).val; + } + bool operator==(const vregion &r) const { + return rank() == r.rank() && val == dynamic_cast(r).val; + } + bool operator!=(const vregion &r) const { + return rank() != r.rank() || val != dynamic_cast(r).val; + } + + bool equal_to(const vregion &r) const { + return val.equal_to(dynamic_cast(r).val); + } + bool less(const vregion &r) const { + return val.less(dynamic_cast(r).val); + } + size_t hash() const { return val.hash(); } + + // I/O +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit wregion(const YAML::Node &node) : val(node) {} +#endif + ostream &output(ostream &os) const { return val.output(os); } + // friend ostream &operator<<(ostream &os, const wregion &r) { + // return r.output(os); + // } +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { return val.output(w); } + // friend YAML::Emitter &operator<<(YAML::Emitter &w, const wregion &r) { + // return r.output(w); + // } +#endif +}; + +//////////////////////////////////////////////////////////////////////////////// + +// Dispatching functions (replacements for constructors) + +template unique_ptr> vpoint::make(int d) { + switch (d) { + case 0: + return make_unique1>(); + case 1: + return make_unique1>(); + case 2: + return make_unique1>(); + case 3: + return make_unique1>(); + case 4: + return make_unique1>(); + default: + assert(0); + } +} + +template unique_ptr> vpoint::make(int d, T x) { + switch (d) { + case 0: + return make_unique1>(x); + case 1: + return make_unique1>(x); + case 2: + return make_unique1>(x); + case 3: + return make_unique1>(x); + case 4: + return make_unique1>(x); + default: + assert(0); + } +} + +template +unique_ptr> vpoint::make(const vector &val) { + switch (val.size()) { + case 0: + return make_unique1>(val); + case 1: + return make_unique1>(val); + case 2: + return make_unique1>(val); + case 3: + return make_unique1>(val); + case 4: + return make_unique1>(val); + default: + assert(0); + } +} + +template +template +unique_ptr> vpoint::make(const vector &val) { + switch (val.size()) { + case 0: + return make_unique1>(val); + case 1: + return make_unique1>(val); + case 2: + return make_unique1>(val); + case 3: + return make_unique1>(val); + case 4: + return make_unique1>(val); + default: + assert(0); + } +} + +template +template +unique_ptr> vpoint::make(const vpoint &p) { + switch (p.rank()) { + case 0: + return make_unique1>(dynamic_cast &>(p)); + case 1: + return make_unique1>(dynamic_cast &>(p)); + case 2: + return make_unique1>(dynamic_cast &>(p)); + case 3: + return make_unique1>(dynamic_cast &>(p)); + case 4: + return make_unique1>(dynamic_cast &>(p)); + default: + assert(0); + } +} + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX +template +unique_ptr> vpoint::make(const YAML::Node &node) { + const auto &v = node.as>(); + switch (v.size()) { + case 0: + return make_unique1>(v); + case 1: + return make_unique1>(v); + case 2: + return make_unique1>(v); + case 3: + return make_unique1>(v); + case 4: + return make_unique1>(v); + default: + assert(0); + } +} +#endif + +template unique_ptr> vbox::make(int d) { + switch (d) { + case 0: + return make_unique1>(); + case 1: + return make_unique1>(); + case 2: + return make_unique1>(); + case 3: + return make_unique1>(); + case 4: + return make_unique1>(); + default: + assert(0); + } +} + +template +unique_ptr> vbox::make(const vpoint &lo, const vpoint &hi) { + switch (lo.rank()) { + case 0: + return make_unique1>(dynamic_cast &>(lo), + dynamic_cast &>(hi)); + case 1: + return make_unique1>(dynamic_cast &>(lo), + dynamic_cast &>(hi)); + case 2: + return make_unique1>(dynamic_cast &>(lo), + dynamic_cast &>(hi)); + case 3: + return make_unique1>(dynamic_cast &>(lo), + dynamic_cast &>(hi)); + case 4: + return make_unique1>(dynamic_cast &>(lo), + dynamic_cast &>(hi)); + default: + assert(0); + } +} + +template +template +unique_ptr> vbox::make(const vbox &b) { + switch (b.rank()) { + case 0: + return make_unique1>(dynamic_cast &>(b)); + case 1: + return make_unique1>(dynamic_cast &>(b)); + case 2: + return make_unique1>(dynamic_cast &>(b)); + case 3: + return make_unique1>(dynamic_cast &>(b)); + case 4: + return make_unique1>(dynamic_cast &>(b)); + default: + assert(0); + } +} + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX +template +unique_ptr> vbox::make(const YAML::Node &node) { + const auto &full = node["full"]; + if (full.IsDefined()) + // return make_unique1>(node); + return make_unique1>(box(full.as())); + const auto &lo = vpoint::make(node["low"]); + const auto &hi = vpoint::make(node["high"]); + return vbox::make(*lo, *hi); +} +#endif + +template unique_ptr> vregion::make(int d) { + switch (d) { + case 0: + return make_unique1>(); + case 1: + return make_unique1>(); + case 2: + return make_unique1>(); + case 3: + return make_unique1>(); + case 4: + return make_unique1>(); + default: + assert(0); + } +} + +template +unique_ptr> vregion::make(const vbox &b) { + switch (b.rank()) { + case 0: + return make_unique1>(dynamic_cast &>(b)); + case 1: + return make_unique1>(dynamic_cast &>(b)); + case 2: + return make_unique1>(dynamic_cast &>(b)); + case 3: + return make_unique1>(dynamic_cast &>(b)); + case 4: + return make_unique1>(dynamic_cast &>(b)); + default: + assert(0); + } +} + +template +unique_ptr> vregion::make(const vector>> &bs) { + if (bs.empty()) + // Cannot determine rank + return nullptr; + switch (bs[0]->rank()) { + case 0: { + vector> rs; + for (const auto &b : bs) + rs.push_back(dynamic_cast &>(*b).val); + return make_unique1>(rs); + } + case 1: { + vector> rs; + for (const auto &b : bs) + rs.push_back(dynamic_cast &>(*b).val); + return make_unique1>(rs); + } + case 2: { + vector> rs; + for (const auto &b : bs) + rs.push_back(dynamic_cast &>(*b).val); + return make_unique1>(rs); + } + case 3: { + vector> rs; + for (const auto &b : bs) + rs.push_back(dynamic_cast &>(*b).val); + return make_unique1>(rs); + } + case 4: { + vector> rs; + for (const auto &b : bs) + rs.push_back(dynamic_cast &>(*b).val); + return make_unique1>(rs); + } + default: + assert(0); + } +} + +template +template +unique_ptr> vregion::make(const vregion &r) { + switch (r.rank()) { + case 0: + return make_unique1>(dynamic_cast &>(r)); + case 1: + return make_unique1>(dynamic_cast &>(r)); + case 2: + return make_unique1>(dynamic_cast &>(r)); + case 3: + return make_unique1>(dynamic_cast &>(r)); + case 4: + return make_unique1>(dynamic_cast &>(r)); + default: + assert(0); + } +} + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX +template +unique_ptr> vregion::make(const YAML::Node &node) { + auto dim = node["dimension"].as(); + switch (dim) { + case 0: + return make_unique1>(node); + case 1: + return make_unique1>(node); + case 2: + return make_unique1>(node); + case 3: + return make_unique1>(node); + case 4: + return make_unique1>(node); + default: + assert(0); + } +} +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Dimension-independent classes (hiding the pointers) + +template struct dpoint { + unique_ptr> val; + + dpoint() = default; + + dpoint(const dpoint &p) { + if (p.val) + val = p.val->copy(); + } + dpoint(dpoint &&p) = default; + dpoint &operator=(const dpoint &p) { + if (p.val) + val = p.val->copy(); + else + val = nullptr; + return *this; + } + dpoint &operator=(dpoint &&p) = default; + + template + dpoint(const point &p) : val(make_unique1>(p)) {} + dpoint(const vpoint &p) : val(p.copy()) {} + dpoint(const unique_ptr> &val) { + if (val) + this->val = val->copy(); + } + dpoint(unique_ptr> &&val) : val(move(val)) {} + + explicit dpoint(int d) : val(vpoint::make(d)) {} + dpoint(int d, T x) : val(vpoint::make(d, x)) {} + template + dpoint(const array &p) : val(make_unique1>(p)) {} + template + explicit dpoint(const array &p) : val(make_unique1>(p)) {} + dpoint(const vector &p) : val(vpoint::make(p)) {} + template + explicit dpoint(const vector &p) : val(vpoint::make(p)) {} + operator vector() const { return vector(*val); } + template explicit operator vector() const { + return vector(*val); + } + template dpoint(const dpoint &p) { + if (p.val) + val = vpoint::make(*p.val); + } + template operator point() const { + assert(valid()); + assert(rank() == D); + return dynamic_cast *>(val.get())->val; + } + + bool valid() const { return bool(val); } + void reset() { val.reset(); } + int rank() const { return val->rank(); } + + // Access and conversion + T operator[](int d) const { return (*val)[d]; } + T &operator[](int d) { return (*val)[d]; } + dpoint subpoint(int dir) const { return dpoint(val->subpoint(dir)); } + dpoint superpoint(int dir, T x) const { return dpoint(val->subpoint(dir)); } + dpoint reversed() const { return dpoint(val->reversed()); } + + // Unary operators + dpoint operator+() const { return dpoint(+*val); } + dpoint operator-() const { return dpoint(-*val); } + dpoint operator~() const { return dpoint(~*val); } + dpoint operator!() const { return dpoint(!*val); } + + // Assignment operators + dpoint &operator+=(const dpoint &p) { + *val += *p.val; + return *this; + } + dpoint &operator-=(const dpoint &p) { + *val -= *p.val; + return *this; + } + dpoint &operator*=(const dpoint &p) { + *val *= *p.val; + return *this; + } + dpoint &operator/=(const dpoint &p) { + *val /= *p.val; + return *this; + } + dpoint &operator%=(const dpoint &p) { + *val %= *p.val; + return *this; + } + dpoint &operator&=(const dpoint &p) { + *val &= *p.val; + return *this; + } + dpoint &operator|=(const dpoint &p) { + *val |= *p.val; + return *this; + } + dpoint &operator^=(const dpoint &p) { + *val ^= *p.val; + return *this; + } + + // Binary operators + dpoint operator+(const dpoint &p) const { return dpoint(*val + *p.val); } + dpoint operator-(const dpoint &p) const { return dpoint(*val - *p.val); } + dpoint operator*(const dpoint &p) const { return dpoint(*val * *p.val); } + dpoint operator/(const dpoint &p) const { return dpoint(*val / *p.val); } + dpoint operator%(const dpoint &p) const { return dpoint(*val % *p.val); } + dpoint operator&(const dpoint &p) const { return dpoint(*val & *p.val); } + dpoint operator|(const dpoint &p) const { return dpoint(*val | *p.val); } + dpoint operator^(const dpoint &p) const { return dpoint(*val ^ *p.val); } + dpoint operator&&(const dpoint &p) const { + return dpoint(*val && *p.val); + } + dpoint operator||(const dpoint &p) const { + return dpoint(*val || *p.val); + } + + // Unary functions + dpoint abs() const { return dpoint(val->abs()); } + + // Binary functions + dpoint min(const dpoint &p) const { return dpoint(val->min(*p.val)); } + dpoint max(const dpoint &p) const { return dpoint(val->max(*p.val)); } + + // Comparison operators + dpoint operator==(const dpoint &p) const { + return dpoint(*val == *p.val); + } + dpoint operator!=(const dpoint &p) const { + return dpoint(*val != *p.val); + } + dpoint operator<(const dpoint &p) const { + return dpoint(*val < *p.val); + } + dpoint operator>(const dpoint &p) const { + return dpoint(*val > *p.val); + } + dpoint operator<=(const dpoint &p) const { + return dpoint(*val <= *p.val); + } + dpoint operator>=(const dpoint &p) const { + return dpoint(*val >= *p.val); + } + + bool equal_to(const dpoint &p) const { return val->equal_to(*p.val); } + bool less(const dpoint &p) const { return val->less(*p.val); } + size_t hash() const { return val->hash(); } + + // Reductions + bool all() const { return val->all(); } + bool any() const { return val->any(); } + T minval() const { return val->minval(); } + T maxval() const { return val->maxval(); } + T sum() const { return val->sum(); } + typedef typename vpoint::prod_t prod_t; + prod_t prod() const { return val->prod(); } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit dpoint(const YAML::Node &node) : val(vpoint::make(node)) {} +#endif + + ostream &output(ostream &os) const { + if (!val) + return os << "dpoint()"; + return val->output(os); + } + friend ostream &operator<<(ostream &os, const dpoint &p) { + return p.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + if (!val) + return w << YAML::LocalTag("sio", "point-1.0.0"); + return val->output(w); + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const dpoint &p) { + return p.output(w); + } +#endif +}; + +// Unary functions +template dpoint abs(const dpoint &p) { return p.abs(); } + +// Binary functions +template dpoint min(const dpoint &p, const dpoint &q) { + return p.min(q); +} +template dpoint max(const dpoint &p, const dpoint &q) { + return p.max(q); +} + +// Reductions +template bool all(const dpoint &p) { return p.all(); } +template bool any(const dpoint &p) { return p.any(); } +template T minval(const dpoint &p) { return p.minval(); } +template T maxval(const dpoint &p) { return p.maxval(); } +template T sum(const dpoint &p) { return p.sum(); } +template typename dpoint::prod_t prod(const dpoint &p) { + return p.prod(); +} +} // namespace RegionCalculus + +namespace std { +template struct equal_to> { + bool operator()(const RegionCalculus::dpoint &p, + const RegionCalculus::dpoint &q) const { + return p.equal_to(q); + } +}; + +template struct less> { + bool operator()(const RegionCalculus::dpoint &p, + const RegionCalculus::dpoint &q) const { + return p.less(q); + } +}; +template struct hash> { + size_t operator()(const RegionCalculus::dpoint &p) const { + return p.hash(); + } +}; +} // namespace std + +namespace RegionCalculus { +template struct dbox { + unique_ptr> val; + + dbox() = default; + + dbox(const dbox &b) { + if (b.val) + val = b.val->copy(); + } + dbox(dbox &&b) = default; + dbox &operator=(const dbox &b) { + if (b.val) + val = b.val->copy(); + else + val = nullptr; + return *this; + } + dbox &operator=(dbox &&b) = default; + + template + dbox(const box &b) : val(make_unique1>(b)) {} + dbox(const vbox &b) : val(b.copy()) {} + dbox(const unique_ptr> &val) { + if (val) + this->val = val->copy(); + } + dbox(unique_ptr> &&val) : val(move(val)) {} + + explicit dbox(int d) : val(vbox::make(d)) {} + dbox(const dpoint &lo, const dpoint &hi) + : val(vbox::make(*lo.val, *hi.val)) {} + template dbox(const dbox &p) { + if (p.val) + val = vbox::make(*p.val); + } + template operator box() const { + assert(valid()); + assert(rank() == D); + return dynamic_cast *>(val.get())->val; + } + + bool valid() const { return bool(val); } + void reset() { val.reset(); } + int rank() const { return val->rank(); } + + // Predicates + bool empty() const { return val->empty(); } + dpoint lower() const { return dpoint(val->lower()); } + dpoint upper() const { return dpoint(val->upper()); } + dpoint shape() const { return dpoint(val->shape()); } + typedef typename vbox::prod_t prod_t; + prod_t size() const { return val->size(); } + + // Shift and scale operators + dbox &operator>>=(const dpoint &p) { + *val >>= *p.val; + return *this; + } + dbox &operator<<=(const dpoint &p) { + *val <<= *p.val; + return *this; + } + dbox &operator*=(const dpoint &p) { + *val *= *p.val; + return *this; + } + dbox operator>>(const dpoint &p) const { return dbox(*val >> *p.val); } + dbox operator<<(const dpoint &p) const { return dbox(*val << *p.val); } + dbox operator*(const dpoint &p) const { return dbox(*val * *p.val); } + dbox grow(const dpoint &dlo, const dpoint &dup) const { + return dbox(val->grow(*dlo.val, *dup.val)); + } + dbox grow(const dpoint &d) const { return dbox(val->grow(*d)); } + dbox grow(T n) const { return dbox(val->grow(n)); } + dbox shrink(const dpoint &dlo, const dpoint &dup) const { + return dbox(val->shrink(*dlo.val, *dup.val)); + } + dbox shrink(const dpoint &d) const { return dbox(val->shrink(*d)); } + dbox shrink(T n) const { return dbox(val->shrink(n)); } + + // Comparison operators + bool operator==(const dbox &b) const { return *val == *b.val; } + bool operator!=(const dbox &b) const { return !(*this == b); } + bool equal_to(const dbox &b) const { return val->equal_to(*b.val); } + bool less(const dbox &b) const { return val->less(*b.val); } + size_t hash() const { return val->hash(); } + + // Set comparison operators + bool contains(const dpoint &p) const { return val->contains(*p.val); } + bool isdisjoint(const dbox &b) const { return val->isdisjoint(*b.val); } + bool operator<=(const dbox &b) const { return *val <= *b.val; } + bool operator>=(const dbox &b) const { return b <= *this; } + bool operator<(const dbox &b) const { return *val < *b.val; } + bool operator>(const dbox &b) const { return b < *this; } + bool issubset(const dbox &b) const { return *this <= b; } + bool issuperset(const dbox &b) const { return *this >= b; } + bool is_strict_subset(const dbox &b) const { return *this < b; } + bool is_strict_superset(const dbox &b) const { return *this > b; } + + // Set operations + dbox bounding_box(const dbox &b) const { + return dbox(val->bounding_box(*b.val)); + } + dbox operator&(const dbox &b) const { return dbox(*val & *b.val); } + // dbox operator-(const dbox &b) const { return dbox(*val - *b.val); } + // dbox operator|(const dbox &b) const { return dbox(*val | *b.val); } + // dbox operator^(const dbox &b) const { return dbox(*val ^ *b.val); } + dbox intersection(const dbox &b) const { return *this & b; } + // dbox difference(const dbox &b) const { return *this - b; } + // dbox setunion(const dbox &b) const { return *this | b; } + // dbox symmetric_difference(const dbox &b) const { return *this ^ b; } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit dbox(const YAML::Node &node) : val(vbox::make(node)) {} +#endif + + ostream &output(ostream &os) const { + if (!val) + return os << "dbox()"; + return val->output(os); + } + friend ostream &operator<<(ostream &os, const dbox &b) { + return b.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + if (!val) + return w << YAML::LocalTag("sio", "box-1.0.0"); + return val->output(w); + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const dbox &b) { + return b.output(w); + } +#endif +}; +} // namespace RegionCalculus + +namespace std { +template struct equal_to> { + bool operator()(const RegionCalculus::dbox &p, + const RegionCalculus::dbox &q) const { + return p.equal_to(q); + } +}; +template struct less> { + bool operator()(const RegionCalculus::dbox &p, + const RegionCalculus::dbox &q) const { + return p.less(q); + } +}; +template struct hash> { + size_t operator()(const RegionCalculus::dbox &p) const { return p.hash(); } +}; +} // namespace std + +namespace RegionCalculus { +template struct dregion { + unique_ptr> val; + + dregion() = default; + + dregion(const dregion &r) { + if (r.val) + val = r.val->copy(); + } + dregion(dregion &&r) = default; + dregion &operator=(const dregion &r) { + if (r.val) + val = r.val->copy(); + else + val = nullptr; + return *this; + } + dregion &operator=(dregion &&r) = default; + + template + dregion(const region &r) : val(make_unique1>(r)) {} + dregion(const vregion &r) : val(r.copy()) {} + dregion(const unique_ptr> &val) { + if (val) + this->val = val->copy(); + } + dregion(unique_ptr> &&val) : val(move(val)) {} + + explicit dregion(int d) : val(vregion::make(d)) {} + dregion(const dbox &b) : val(vregion::make(*b.val)) {} + dregion(const vector> &bs) { + vector>> rs; + for (const auto &b : bs) + rs.push_back(b.val->copy()); + val = vregion::make(move(rs)); + } + dregion(vector> &&bs) { + vector>> rs; + for (auto &b : bs) + rs.push_back(move(b.val)); + val = vregion::make(move(rs)); + } + operator vector>() const { + vector>> bs(*val); + vector> rs; + for (auto &b : bs) + rs.push_back(dbox(move(b))); + return rs; + } + template dregion(const dregion &p) { + if (p.val) + val = vregion::make(*p.val); + } + template operator region() const { + assert(valid()); + assert(rank() == D); + return dynamic_cast *>(val.get())->val; + } + + bool valid() const { return bool(val); } + void reset() { val.reset(); } + int rank() const { return val->rank(); } + + // Predicates + bool invariant() const { return val->invariant(); } + bool empty() const { return val->empty(); } + typedef typename vregion::prod_t prod_t; + prod_t size() const { return val->size(); } + + // Shift and scale operators + dregion operator>>(const dpoint &d) const { + return dregion(*val >> *d.val); + } + dregion operator<<(const dpoint &d) const { + return dregion(*val << *d.val); + } + dregion grow(const dpoint &dlo, const dpoint &dup) const { + return dregion(val->grow(*dlo, *dup)); + } + dregion grow(const dpoint &d) const { return dregion(val->grow(*d.val)); } + dregion grow(T n) const { return dregion(val->grow(n)); } + dregion shrink(const dpoint &dlo, const dpoint &dup) const { + return dregion(val->shrink(*dlo.val, *dup.val)); + } + dregion shrink(const dpoint &d) const { + return dregion(val->shrink(*d.val)); + } + dregion shrink(T n) const { return dregion(val->shrink(n)); } + + // Set operations + dbox bounding_box() const { return dbox(val->bounding_box()); } + dregion operator&(const dbox &b) const { return dregion(*val & *b.val); } + dregion operator&(const dregion &r) const { return dregion(*val & *r.val); } + dregion operator-(const dbox &b) const { return dregion(*val - *b.val); } + dregion operator-(const dregion &r) const { return dregion(*val - *r.val); } + dregion operator|(const dbox &b) const { return dregion(*val | *b.val); } + dregion operator|(const dregion &r) const { return dregion(*val | *r.val); } + dregion operator^(const dbox &b) const { return dregion(*val ^ *b.val); } + dregion operator^(const dregion &r) const { return dregion(*val ^ *r.val); } + + dregion &operator^=(const dregion &other) { return *this = *this ^ other; } + dregion &operator&=(const dregion &other) { return *this = *this & other; } + dregion &operator|=(const dregion &other) { return *this = *this | other; } + dregion &operator-=(const dregion &other) { return *this = *this - other; } + + dregion intersection(const dbox &b) const { return *this & b; } + dregion intersection(const dregion &r) const { return *this & r; } + dregion difference(const dbox &b) const { return *this - b; } + dregion difference(const dregion &r) const { return *this - r; } + dregion setunion(const dbox &b) const { return *this | b; } + dregion setunion(const dregion &r) const { return *this | r; } + dregion symmetric_difference(const dbox &b) const { return *this ^ b; } + dregion symmetric_difference(const dregion &r) const { return *this ^ r; } + + // Set comparison operators + bool contains(const dpoint &p) const { return val->contains(*p.val); } + bool isdisjoint(const dbox &b) const { return val->isdisjoint(*b.val); } + bool isdisjoint(const dregion &r) const { return val->isdisjoint(*r.val); } + + // Comparison operators + bool operator<=(const dregion &r) const { return *val <= *r.val; } + bool operator>=(const dregion &r) const { return *val >= *r.val; } + bool operator<(const dregion &r) const { return *val < *r.val; } + bool operator>(const dregion &r) const { return *val > *r.val; } + bool issubset(const dregion &r) const { return *this <= r; } + bool issuperset(const dregion &r) const { return *this >= r; } + bool is_strict_subset(const dregion &r) const { return *this < r; } + bool is_strict_superset(const dregion &r) const { return *this > r; } + bool operator==(const dregion &r) const { return *val == *r.val; } + bool operator!=(const dregion &r) const { return *val != *r.val; } + + bool equal_to(const dregion &r) const { return val->equal_to(*r.val); } + bool less(const dregion &r) const { return val->less(*r.val); } + size_t hash() const { return val->hash(); } + + // I/O + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + explicit dregion(const YAML::Node &node) : val(vregion::make(node)) {} +#endif + + ostream &output(ostream &os) const { + if (!val) + return os << "dregion()"; + return val->output(os); + } + friend ostream &operator<<(ostream &os, const dregion &r) { + return r.output(os); + } + +#ifdef SIMULATIONIO_HAVE_ASDF_CXX + YAML::Emitter &output(YAML::Emitter &w) const { + if (!val) + return w << YAML::LocalTag("sio", "region-1.0.0"); + return val->output(w); + } + friend YAML::Emitter &operator<<(YAML::Emitter &w, const dregion &r) { + return r.output(w); + } +#endif +}; + +} // namespace RegionCalculus + +namespace std { +template struct equal_to> { + bool operator()(const RegionCalculus::dregion &p, + const RegionCalculus::dregion &q) const { + return p.equal_to(q); + } +}; +template struct less> { + bool operator()(const RegionCalculus::dregion &p, + const RegionCalculus::dregion &q) const { + return p.less(q); + } +}; +template struct hash> { + size_t operator()(const RegionCalculus::dregion &p) const { + return p.hash(); + } +}; +} // namespace std + +namespace RegionCalculus { +typedef RegionCalculus::dpoint point_t; +typedef RegionCalculus::dbox box_t; +typedef RegionCalculus::dregion region_t; + +} // namespace RegionCalculus + +#endif // REGIONCALCULUS_HPP diff --git a/src/RegionCalculus.cpp b/src/RegionCalculus.cpp new file mode 100644 index 0000000000..651ffc2885 --- /dev/null +++ b/src/RegionCalculus.cpp @@ -0,0 +1,3 @@ +#include "openPMD/RegionCalculus.hpp" + +// We could instantiate some templates here. diff --git a/test/RegionCalculusTest.cpp b/test/RegionCalculusTest.cpp new file mode 100644 index 0000000000..5e722fd3e1 --- /dev/null +++ b/test/RegionCalculusTest.cpp @@ -0,0 +1,1634 @@ +#include "openPMD/RegionCalculus.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +using std::array; +using std::equal_to; +using std::hash; +using std::less; +using std::ostringstream; +using std::set; +using std::vector; + +int irand(int imax) { + static std::mt19937 rng; + return std::uniform_int_distribution<>(0, imax)(rng); +} + +using namespace RegionCalculus; + +TEST(RegionCalculus, point) { + typedef point point; + point p; + EXPECT_FALSE(all(p)); + EXPECT_FALSE(any(p)); + point p0(0); + EXPECT_FALSE(all(p0)); + EXPECT_FALSE(any(p0)); + EXPECT_TRUE(all(p0 == p)); + point p1(::point(true)); + EXPECT_TRUE(all(p1)); + EXPECT_TRUE(any(p1)); + EXPECT_TRUE(all(p1 != p)); + EXPECT_TRUE(all(p1 == +p1)); + EXPECT_TRUE(all(p0 == -p0)); + EXPECT_TRUE(all(p1 == abs(-p1))); + EXPECT_TRUE(all(-p1 == ~p0)); + EXPECT_TRUE(all(!p0)); + point p2(point(2)); + EXPECT_TRUE(equal_to()(p2, point(::point(p2)))); + EXPECT_TRUE(all(p1 + p1 == p2)); + EXPECT_TRUE(all(p2 - p1 == p1)); + EXPECT_TRUE(all(p2 * p1 == p2)); + EXPECT_TRUE(all((p2 & p1) == p0)); + EXPECT_TRUE(all((p2 | p1) == p1 + p2)); + EXPECT_TRUE(all((p2 ^ p1) == p1 + p2)); + EXPECT_TRUE(all(p1 && p2)); + EXPECT_TRUE(all(p1 || p2)); + EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); + EXPECT_EQ(2, minval(p2)); + EXPECT_EQ(2, maxval(p2)); + EXPECT_EQ(3, sum(p1)); + EXPECT_EQ(8, prod(p2)); + point p3; + p3.elt[0] = 0; + p3.elt[1] = 1; + p3.elt[2] = 2; + EXPECT_TRUE(all((p1 + p3) * point(2) - p3 == p3 + p2)); + EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); + EXPECT_TRUE(all(p2 == ::point(::point(2)))); + EXPECT_TRUE(all(p2 == ::point(::point(2)))); + auto v2a = array{2, 2, 2}; + EXPECT_EQ(v2a, (array(::point(2)))); + EXPECT_EQ(v2a, (array(::point(2)))); + auto v2 = vector({2, 2, 2}); + EXPECT_EQ(v2, vector(::point(2))); + EXPECT_EQ(v2, vector(::point(2))); + ostringstream buf; + buf << p3; + EXPECT_EQ("[0,1,2]", buf.str()); +} + +TEST(RegionCalculus, box) { + typedef point point; + typedef box box; + box b; + EXPECT_TRUE(b.empty()); + point p0(0), p1(1); + box b1(box(p0, p1)); + EXPECT_EQ(1, b1.size()); + box b4(p0, point(4)); + box b5 = b4 >> p1; + box b3 = b4 << p1; + box b6 = b3 * point(2); + box b7 = b4.grow(p0, p1); + box b8 = b4.grow(p1, p0); + EXPECT_EQ(b4, (box(::box(b4)))); + EXPECT_TRUE(b4 == b4); + EXPECT_TRUE(b4 != b5); + EXPECT_TRUE(b4.contains(p1)); + EXPECT_FALSE(b5.contains(p0)); + EXPECT_FALSE(b5.isdisjoint(b3)); + EXPECT_TRUE(b1.isdisjoint(b5)); + EXPECT_TRUE(b.isdisjoint(b5)); + EXPECT_TRUE(b1.isdisjoint(b)); + EXPECT_TRUE(b6.issuperset(b3)); + EXPECT_FALSE(b3.issuperset(b6)); + EXPECT_TRUE(b4.issuperset(b4)); + EXPECT_TRUE(b4.issuperset(b)); + EXPECT_FALSE(b.issuperset(b4)); + EXPECT_TRUE(b.issuperset(b)); + EXPECT_TRUE(b7.issuperset(b4)); + EXPECT_TRUE(b7.issuperset(b5)); + EXPECT_TRUE(b8.issuperset(b4)); + EXPECT_TRUE(b8.issuperset(b3)); + EXPECT_FALSE(b4.is_strict_superset(b4)); + EXPECT_TRUE(b4.is_strict_superset(b)); + EXPECT_FALSE(b.is_strict_superset(b4)); + EXPECT_FALSE(b.is_strict_superset(b)); + EXPECT_EQ(box(p1, point(3)), b5.intersection(b3)); + EXPECT_TRUE(b1.intersection(b5).empty()); + EXPECT_TRUE(b.intersection(b5).empty()); + EXPECT_TRUE(b1.intersection(b).empty()); + ostringstream buf; + buf << b4; + EXPECT_EQ("([0,0,0]:[4,4,4])", buf.str()); +} + +#if !REGIONCALCULUS_TREE + +TEST(RegionCalculus, region1) { + typedef point point; + typedef box box; + typedef region region; + region r; + EXPECT_TRUE(r.invariant()); + EXPECT_TRUE(r.empty()); + point p; + point p1(1); + box b; + box b1(p, p1); + region r0(b); + EXPECT_TRUE(r0.invariant()); + EXPECT_TRUE(r0.empty()); + region r1(b1); + EXPECT_TRUE(r1.invariant()); + EXPECT_FALSE(r1.empty()); + EXPECT_TRUE(r == r); + EXPECT_TRUE(r1 == r1); + EXPECT_FALSE(r != r); + EXPECT_TRUE(r != r1); + point p2(2); + box b2(p, p2); + region r2(b2); + EXPECT_EQ(r2, (region(::region(r2)))); + EXPECT_TRUE(r == r.intersection(r1)); + EXPECT_TRUE(r == r1.intersection(r)); + EXPECT_TRUE(r1 == r1.intersection(r2)); + EXPECT_TRUE(r1 == r2.intersection(r1)); + EXPECT_TRUE(r == r.difference(r)); + EXPECT_TRUE(r1 == r1.difference(r)); + EXPECT_TRUE(r == r.difference(r1)); + EXPECT_TRUE(r == r1.difference(r1)); + EXPECT_TRUE(r == r.setunion(r)); + EXPECT_TRUE(r1 == r1.setunion(r)); + EXPECT_TRUE(r1 == r.setunion(r1)); + EXPECT_TRUE(r1 == r1.setunion(r1)); + EXPECT_TRUE(r == r.symmetric_difference(r)); + EXPECT_TRUE(r1 == r1.symmetric_difference(r)); + EXPECT_TRUE(r1 == r.symmetric_difference(r1)); + EXPECT_TRUE(r == r1.symmetric_difference(r1)); + vector r12vals; + r12vals.push_back(b1); + r12vals.push_back(box(p1, p2)); + region r12(r12vals); + vector r12boxes = r12; + EXPECT_EQ(r12vals, r12boxes); + vector rs; + rs.push_back(r); + rs.push_back(r1); + rs.push_back(r2); + box b4(p, point(4)); + region r4(b4); + rs.push_back(r4); + rs.push_back(r12); + rs.push_back(r2.difference(r1)); + rs.push_back(r2.symmetric_difference(r12)); + for (std::size_t i = 0; i < rs.size(); ++i) { + const auto &ri = rs[i]; + auto rgrown = ri.grow(p1); + auto rshrunk = ri.shrink(p1); + EXPECT_TRUE(ri.invariant()); + EXPECT_TRUE(rgrown.invariant()); + EXPECT_TRUE(rshrunk.invariant()); + EXPECT_TRUE(rgrown.issuperset(ri)); + if (ri.empty()) + EXPECT_TRUE(rgrown.empty()); + EXPECT_TRUE(ri.issuperset(rshrunk)); + if (!rshrunk.empty()) + EXPECT_TRUE(rshrunk.grow(p1) == ri); + EXPECT_TRUE(rgrown.shrink(p1) == ri); + for (std::size_t j = 0; j < rs.size(); ++j) { + const auto &rj = rs[j]; + auto rintersection = ri.intersection(rj); + auto rdifference = ri.difference(rj); + auto rsetunion = ri.setunion(rj); + auto rsymmetric_difference = ri.symmetric_difference(rj); + EXPECT_TRUE(rintersection.invariant()); + EXPECT_TRUE(rdifference.invariant()); + EXPECT_TRUE(rsetunion.invariant()); + EXPECT_TRUE(rsymmetric_difference.invariant()); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(rj.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(ri)); + EXPECT_TRUE(rsetunion.issuperset(rj)); + EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); + EXPECT_TRUE(rdifference.isdisjoint(rj)); + EXPECT_TRUE(rsetunion.issuperset(rintersection)); + EXPECT_TRUE(rsetunion.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); + EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); + EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); + EXPECT_TRUE(rintersection == rj.intersection(ri)); + EXPECT_TRUE(rsetunion == rj.setunion(ri)); + EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); + if (ri == rj) { + EXPECT_TRUE(rintersection == ri); + EXPECT_TRUE(rdifference.empty()); + EXPECT_TRUE(rsetunion == ri); + EXPECT_TRUE(rsymmetric_difference.empty()); + } + } + } + ostringstream buf; + buf << r12; + EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); +} + +#else // #if REGIONCALCULUS_TREE + +TEST(RegionCalculus, region) { + typedef point point; + typedef box box; + typedef region region; + region r; + EXPECT_TRUE(r.invariant()); + EXPECT_TRUE(r.empty()); + point p; + point p1(1); + box b; + box b1(p, p1); + region r0(b); + EXPECT_TRUE(r0.invariant()); + EXPECT_TRUE(r0.empty()); + region r1(b1); + EXPECT_TRUE(r1.invariant()); + EXPECT_FALSE(r1.empty()); + EXPECT_TRUE(r == r); + EXPECT_TRUE(r1 == r1); + EXPECT_FALSE(r != r); + EXPECT_TRUE(r != r1); + point p2(2); + box b2(p, p2); + region r2(b2); + EXPECT_EQ(r2, region(::region(r2))); + EXPECT_TRUE(r == r.intersection(r1)); + EXPECT_TRUE(r == r1.intersection(r)); + EXPECT_TRUE(r1 == r1.intersection(r2)); + EXPECT_TRUE(r1 == r2.intersection(r1)); + EXPECT_TRUE(r == r.difference(r)); + EXPECT_TRUE(r1 == r1.difference(r)); + EXPECT_TRUE(r == r.difference(r1)); + EXPECT_TRUE(r == r1.difference(r1)); + EXPECT_TRUE(r == r.setunion(r)); + EXPECT_TRUE(r1 == r1.setunion(r)); + EXPECT_TRUE(r1 == r.setunion(r1)); + EXPECT_TRUE(r1 == r1.setunion(r1)); + EXPECT_TRUE(r == r.symmetric_difference(r)); + EXPECT_TRUE(r1 == r1.symmetric_difference(r)); + EXPECT_TRUE(r1 == r.symmetric_difference(r1)); + EXPECT_TRUE(r == r1.symmetric_difference(r1)); + EXPECT_EQ(0, vector(r).size()); + EXPECT_EQ(1, vector(r1).size()); + EXPECT_EQ(1, vector(r2).size()); + auto r21 = r2 - r1; + vector r21boxes(r21); + EXPECT_EQ(3, r21boxes.size()); + EXPECT_EQ(box(point(1, 0, 0), point(2, 1, 1)), r21boxes.at(0)); + EXPECT_EQ(box(point(0, 1, 0), point(2, 2, 1)), r21boxes.at(1)); + EXPECT_EQ(box(point(0, 0, 1), point(2, 2, 2)), r21boxes.at(2)); + vector r12vals; + r12vals.push_back(b1); + r12vals.push_back(box(p1, p2)); + region r12(r12vals); + vector r12boxes = r12; + EXPECT_EQ(r12vals, r12boxes); + vector rs; + rs.push_back(r); + rs.push_back(r1); + rs.push_back(r2); + box b4(p, point(4)); + region r4(b4); + rs.push_back(r4); + rs.push_back(r12); + rs.push_back(r2.difference(r1)); + rs.push_back(r2.symmetric_difference(r12)); + for (int i = 0; i < 10; ++i) { + vector bs; + for (int n = irand(5); n >= 0; --n) + bs.push_back(box(point(irand(10), irand(10), irand(10)), + point(irand(10), irand(10), irand(10)))); + rs.push_back(region(bs)); + } + for (std::size_t i = 0; i < rs.size(); ++i) { + const auto &ri = rs[i]; + EXPECT_TRUE(ri.invariant()); + const auto ri_size = ri.size(); + for (std::size_t j = 0; j < rs.size(); ++j) { + const auto &rj = rs[j]; + const auto rj_size = rj.size(); + auto rintersection = ri.intersection(rj); + auto rdifference = ri.difference(rj); + auto rsetunion = ri.setunion(rj); + auto rsymmetric_difference = ri.symmetric_difference(rj); + auto rintersection_size = rintersection.size(); + auto rdifference_size = rdifference.size(); + auto rsetunion_size = rsetunion.size(); + auto rsymmetric_difference_size = rsymmetric_difference.size(); + EXPECT_TRUE(rintersection.invariant()); + EXPECT_TRUE(rdifference.invariant()); + EXPECT_TRUE(rsetunion.invariant()); + EXPECT_TRUE(rsymmetric_difference.invariant()); + EXPECT_TRUE(rintersection_size <= ri_size && + rintersection_size <= rj_size); + EXPECT_TRUE(rdifference_size <= ri_size && + rdifference_size >= ri_size - rj_size); + EXPECT_TRUE(rsetunion_size >= ri_size && rsetunion_size >= rj_size && + rsetunion_size <= ri_size + rj_size); + EXPECT_TRUE(rsymmetric_difference_size <= ri_size + rj_size && + rsymmetric_difference_size >= abs(ri_size - rj_size)); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(rj.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(ri)); + EXPECT_TRUE(rsetunion.issuperset(rj)); + EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); + EXPECT_TRUE(rdifference.isdisjoint(rj)); + EXPECT_TRUE(rsetunion.issuperset(rintersection)); + EXPECT_TRUE(rsetunion.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); + EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); + EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); + EXPECT_TRUE(rintersection == rj.intersection(ri)); + EXPECT_TRUE(rsetunion == rj.setunion(ri)); + EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); + for (int n = 0; n < 10; ++n) { + int i = irand(10), j = irand(10), k = irand(10); + point p(i, j, k); + EXPECT_EQ(ri.contains(p) & rj.contains(p), rintersection.contains(p)); + EXPECT_EQ(ri.contains(p) & !rj.contains(p), rdifference.contains(p)); + EXPECT_EQ(ri.contains(p) | rj.contains(p), rsetunion.contains(p)); + EXPECT_EQ(ri.contains(p) ^ rj.contains(p), + rsymmetric_difference.contains(p)); + } + if (ri == rj) { + EXPECT_TRUE(rintersection == ri); + EXPECT_TRUE(rdifference.empty()); + EXPECT_TRUE(rsetunion == ri); + EXPECT_TRUE(rsymmetric_difference.empty()); + } + } + } + ostringstream buf; + buf << r12; + EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); +} + +#endif // #if REGIONCALCULUS_TREE + +TEST(RegionCalculus, region2) { + typedef point point; + typedef box box; + typedef region region; + region r; + EXPECT_TRUE(r.invariant()); + EXPECT_TRUE(r.empty()); + point p; + point p1(1); + box b; + box b1(p, p1); + region r0(b); + EXPECT_TRUE(r0.invariant()); + EXPECT_TRUE(r0.empty()); + region r1(b1); + EXPECT_TRUE(r1.invariant()); + EXPECT_FALSE(r1.empty()); + EXPECT_TRUE(r == r); + EXPECT_TRUE(r1 == r1); + EXPECT_FALSE(r != r); + EXPECT_TRUE(r != r1); + point p2(2); + box b2(p, p2); + region r2(b2); + EXPECT_EQ(r2, (region(::region(r2)))); + EXPECT_TRUE(r == r.intersection(r1)); + EXPECT_TRUE(r == r1.intersection(r)); + EXPECT_TRUE(r1 == r1.intersection(r2)); + EXPECT_TRUE(r1 == r2.intersection(r1)); + EXPECT_TRUE(r == r.difference(r)); + EXPECT_TRUE(r1 == r1.difference(r)); + EXPECT_TRUE(r == r.difference(r1)); + EXPECT_TRUE(r == r1.difference(r1)); + EXPECT_TRUE(r == r.setunion(r)); + EXPECT_TRUE(r1 == r1.setunion(r)); + EXPECT_TRUE(r1 == r.setunion(r1)); + EXPECT_TRUE(r1 == r1.setunion(r1)); + EXPECT_TRUE(r == r.symmetric_difference(r)); + EXPECT_TRUE(r1 == r1.symmetric_difference(r)); + EXPECT_TRUE(r1 == r.symmetric_difference(r1)); + EXPECT_TRUE(r == r1.symmetric_difference(r1)); + vector r12vals; + r12vals.push_back(b1); + r12vals.push_back(box(p1, p2)); + region r12(r12vals); + vector r12boxes = r12; + EXPECT_EQ(r12vals, r12boxes); + vector rs; + rs.push_back(r); + rs.push_back(r1); + rs.push_back(r2); + box b4(p, point(4)); + region r4(b4); + rs.push_back(r4); + rs.push_back(r12); + rs.push_back(r2.difference(r1)); + rs.push_back(r2.symmetric_difference(r12)); + for (std::size_t i = 0; i < rs.size(); ++i) { + const auto &ri = rs[i]; + auto rgrown = ri.grow(p1); + auto rshrunk = ri.shrink(p1); + EXPECT_TRUE(ri.invariant()); + EXPECT_TRUE(rgrown.invariant()); + EXPECT_TRUE(rshrunk.invariant()); + EXPECT_TRUE(rgrown.issuperset(ri)); + if (ri.empty()) + EXPECT_TRUE(rgrown.empty()); + region rgrown_test; + for (int dk = -1; dk <= +1; ++dk) + for (int dj = -1; dj <= +1; ++dj) + for (int di = -1; di <= +1; ++di) { + auto shifted = ri >> point(di, dj, dk); + rgrown_test |= shifted; + } + EXPECT_TRUE(rgrown_test == rgrown); + EXPECT_TRUE(ri.issuperset(rshrunk)); + if (!rshrunk.empty()) + EXPECT_TRUE(rshrunk.grow(p1) == ri); + region rshrunk_test = ri; + for (int dk = -1; dk <= +1; ++dk) + for (int dj = -1; dj <= +1; ++dj) + for (int di = -1; di <= +1; ++di) { + auto shifted = ri >> point(di, dj, dk); + rshrunk_test &= shifted; + } + EXPECT_TRUE(rshrunk_test == rshrunk); + region rgrown_shrunk_test = rgrown; + for (int dk = -1; dk <= +1; ++dk) + for (int dj = -1; dj <= +1; ++dj) + for (int di = -1; di <= +1; ++di) { + auto shifted = rgrown >> point(di, dj, dk); + rgrown_shrunk_test &= shifted; + } + EXPECT_TRUE(rgrown_shrunk_test == ri); + EXPECT_TRUE(rgrown.shrink(p1) == ri); + for (std::size_t j = 0; j < rs.size(); ++j) { + const auto &rj = rs[j]; + auto rintersection = ri.intersection(rj); + auto rdifference = ri.difference(rj); + auto rsetunion = ri.setunion(rj); + auto rsymmetric_difference = ri.symmetric_difference(rj); + EXPECT_TRUE(rintersection.invariant()); + EXPECT_TRUE(rdifference.invariant()); + EXPECT_TRUE(rsetunion.invariant()); + EXPECT_TRUE(rsymmetric_difference.invariant()); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(rj.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(ri)); + EXPECT_TRUE(rsetunion.issuperset(rj)); + EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); + EXPECT_TRUE(rdifference.isdisjoint(rj)); + EXPECT_TRUE(rsetunion.issuperset(rintersection)); + EXPECT_TRUE(rsetunion.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); + EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); + EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); + EXPECT_TRUE(rintersection == rj.intersection(ri)); + EXPECT_TRUE(rsetunion == rj.setunion(ri)); + EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); + if (ri == rj) { + EXPECT_TRUE(rintersection == ri); + EXPECT_TRUE(rdifference.empty()); + EXPECT_TRUE(rsetunion == ri); + EXPECT_TRUE(rsymmetric_difference.empty()); + } + EXPECT_TRUE(rintersection == (ri & rj)); + auto rintersection1 = ri; + rintersection1 &= rj; + EXPECT_TRUE(rintersection == rintersection1); + EXPECT_TRUE(rdifference == (ri - rj)); + auto rdifference1 = ri; + rdifference1 -= rj; + EXPECT_TRUE(rdifference == rdifference1); + EXPECT_TRUE(rsetunion == (ri | rj)); + auto rsetunion1 = ri; + rsetunion1 |= rj; + EXPECT_TRUE(rsetunion == rsetunion1); + EXPECT_TRUE(rsymmetric_difference == (ri ^ rj)); + auto rsymmetric_difference1 = ri; + rsymmetric_difference1 ^= rj; + EXPECT_TRUE(rsymmetric_difference == rsymmetric_difference1); + } + } + ostringstream buf; + buf << r12; + EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); +} + +// Get random set element +template T getelt(const set &xs) { + assert(!xs.empty()); + int n = irand(xs.size() - 1); + auto p = xs.begin(); + for (int i = 0; i < n; ++i) + ++p; + return *p; +} + +template void test_point() { + typedef point P; + + // Create a few points + set

ps; + while (ps.size() < 10) { + array xs; + for (int d = 0; d < D; ++d) + xs[d] = irand(10); + P p(xs); + ps.insert(p); + } + + const int niters = 100; + for (int n = 0; n < niters; ++n) { + auto p1 = getelt(ps); + auto p2 = getelt(ps); + auto p3 = getelt(ps); + std::equal_to

eq; + std::less

lt; + std::hash

h; + auto eq1 = eq(p1, p2); + auto eq2 = eq(p2, p1); + auto eq3 = eq(p2, p3); + auto eq4 = eq(p1, p3); + auto lt1 = lt(p1, p2); + auto lt2 = lt(p2, p1); + auto lt3 = lt(p2, p3); + auto lt4 = lt(p1, p3); + EXPECT_TRUE(eq1 == eq2); // eq is reflexive + EXPECT_FALSE(eq1 && lt1); + EXPECT_FALSE(eq1 && lt2); + EXPECT_FALSE(lt1 && lt2); // lt is irreflexive + EXPECT_EQ(1, eq1 + lt1 + lt2); + if (eq1 && eq3) + EXPECT_TRUE(eq4); // eq is transitive + if (lt1 && lt3) + EXPECT_TRUE(lt4); // lt is transitive + if (eq1) + EXPECT_EQ(h(p1), h(p2)); + if (eq2) + EXPECT_EQ(h(p2), h(p1)); + if (eq3) + EXPECT_EQ(h(p2), h(p3)); + if (eq4) + EXPECT_EQ(h(p1), h(p3)); + if (h(p1) != h(p2)) + EXPECT_FALSE(eq1); + if (h(p2) != h(p1)) + EXPECT_FALSE(eq2); + if (h(p2) != h(p3)) + EXPECT_FALSE(eq3); + if (h(p1) != h(p3)) + EXPECT_FALSE(eq4); + } +} + +template void test_box() { + typedef point P; + typedef box B; + + // Create a few boxes + set bs; + bs.insert(B()); + while (bs.size() < 10) { + array xs, ys; + for (int d = 0; d < D; ++d) { + xs[d] = irand(10); + ys[d] = irand(10); + } + P px(xs), py(ys); + B b{xs, ys}; + bs.insert(b); + } + + const int niters = 100; + for (int n = 0; n < niters; ++n) { + const auto b1 = getelt(bs); + const auto b2 = getelt(bs); + const auto bint = b1 & b2; + const auto bbox = b1.bounding_box(b2); + EXPECT_TRUE(bint <= b1); + EXPECT_TRUE(bint <= b2); + EXPECT_TRUE(b1 <= bbox); + EXPECT_TRUE(b2 <= bbox); + EXPECT_TRUE(bint <= bbox); + bs.insert(bint); + bs.insert(bbox); + + array xs, ys; + for (int d = 0; d < D; ++d) { + xs[d] = irand(5) - 2; + ys[d] = irand(5) - 2; + } + P plo(xs); + P phi(ys); + const auto bshl = b1 << plo; + const auto bshr = b2 >> phi; + const auto bgro = b1.grow(plo, phi); + const auto bred = b2.shrink(plo, phi); + EXPECT_TRUE((bshl >> plo) == b1); + EXPECT_TRUE((bshr << phi) == b2); + if (all(plo + phi >= P(0))) { + EXPECT_TRUE(bgro.size() >= b1.size()); + EXPECT_TRUE(bred.size() <= b2.size()); + EXPECT_TRUE(bgro.shrink(plo, phi) >= b1); + EXPECT_TRUE(bred.grow(plo, phi) <= b2); + } + bs.insert(bshl); + bs.insert(bshr); + bs.insert(bgro); + bs.insert(bred); + } + + for (int n = 0; n < niters; ++n) { + auto b1 = getelt(bs); + auto b2 = getelt(bs); + auto b3 = getelt(bs); + std::equal_to eq; + std::less lt; + std::hash h; + auto eq1 = eq(b1, b2); + auto eq2 = eq(b2, b1); + auto eq3 = eq(b2, b3); + auto eq4 = eq(b1, b3); + auto lt1 = lt(b1, b2); + auto lt2 = lt(b2, b1); + auto lt3 = lt(b2, b3); + auto lt4 = lt(b1, b3); + EXPECT_TRUE(eq1 == eq2); // eq is reflexive + EXPECT_FALSE(eq1 && lt1); + EXPECT_FALSE(eq1 && lt2); + EXPECT_FALSE(lt1 && lt2); // lt is irreflexive + EXPECT_EQ(1, eq1 + lt1 + lt2); + if (eq1 && eq3) + EXPECT_TRUE(eq4); // eq is transitive + if (lt1 && lt3) + EXPECT_TRUE(lt4); // lt is transitive + if (eq1) + EXPECT_EQ(h(b1), h(b2)); + if (eq2) + EXPECT_EQ(h(b2), h(b1)); + if (eq3) + EXPECT_EQ(h(b2), h(b3)); + if (eq4) + EXPECT_EQ(h(b1), h(b3)); + if (h(b1) != h(b2)) + EXPECT_FALSE(eq1); + if (h(b2) != h(b1)) + EXPECT_FALSE(eq2); + if (h(b2) != h(b3)) + EXPECT_FALSE(eq3); + if (h(b1) != h(b3)) + EXPECT_FALSE(eq4); + } +} + +template void test_region() { + typedef point P; + typedef box B; + typedef region R; + + // Create a few regions + set rs; + rs.insert(R()); + while (rs.size() < 10) { + array xs, ys; + for (int d = 0; d < D; ++d) { + xs[d] = irand(10); + ys[d] = irand(10); + } + P px(xs), py(ys); + B b{xs, ys}; + R r(b); + rs.insert(r); + } + + // Perform a few operations + const auto getelt = [](const set &rs) { + assert(!rs.empty()); + int n = irand(rs.size() - 1); + auto p = rs.begin(); + for (int i = 0; i < n; ++i) + ++p; + return *p; + }; + + const int niters = 1000 >> (2 * (D - 1)); + for (int n = 0; n < niters; ++n) { + const auto r1 = getelt(rs); + const auto r2 = getelt(rs); + const auto rint = r1 & r2; + const auto runi = r1 | r2; + const auto rsym = r1 ^ r2; + const auto rdif = r1 - r2; + EXPECT_TRUE(rint <= r1); + EXPECT_TRUE(rint <= r2); + EXPECT_TRUE(r1 <= runi); + EXPECT_TRUE(r2 <= runi); + EXPECT_TRUE(rint <= runi); + EXPECT_TRUE(rsym <= runi); + EXPECT_TRUE(rsym.isdisjoint(rint)); + EXPECT_TRUE(rdif <= r1); + EXPECT_TRUE(rdif.isdisjoint(r2)); + EXPECT_TRUE((rsym | rint) == runi); + rs.insert(rint); + rs.insert(runi); + rs.insert(rsym); + rs.insert(rdif); + + P plo, phi; + while (1) { + array xs, ys; + for (int d = 0; d < D; ++d) { + xs[d] = irand(5) - 2; + ys[d] = irand(5) - 2; + } + plo = P(xs); + phi = P(ys); + if (all(plo + phi >= P(0))) + break; + } + const auto rshl = r1 << plo; + const auto rshr = r2 >> phi; + const auto rgro = r1.grow(plo, phi); + const auto rred = r2.shrink(plo, phi); + EXPECT_TRUE((rshl >> plo) == r1); + EXPECT_TRUE((rshr << phi) == r2); + EXPECT_TRUE(rgro.size() >= r1.size()); + EXPECT_TRUE(rred.size() <= r2.size()); + EXPECT_TRUE(rgro.shrink(plo, phi) >= r1); + EXPECT_TRUE(rred.grow(plo, phi) <= r2); + rs.insert(rshl); + rs.insert(rshr); + rs.insert(rgro); + rs.insert(rred); + } + + for (int n = 0; n < niters; ++n) { + auto r1 = getelt(rs); + auto r2 = getelt(rs); + auto r3 = getelt(rs); + std::equal_to eq; + std::less lt; + std::hash h; + auto eq1 = eq(r1, r2); + auto eq2 = eq(r2, r1); + auto eq3 = eq(r2, r3); + auto eq4 = eq(r1, r3); + auto lt1 = lt(r1, r2); + auto lt2 = lt(r2, r1); + auto lt3 = lt(r2, r3); + auto lt4 = lt(r1, r3); + EXPECT_TRUE(eq1 == eq2); // eq is reflexive + EXPECT_FALSE(eq1 && lt1); + EXPECT_FALSE(eq1 && lt2); + EXPECT_FALSE(lt1 && lt2); // lt is irreflexive + EXPECT_EQ(1, eq1 + lt1 + lt2); + if (eq1 && eq3) + EXPECT_TRUE(eq4); // eq is transitive + if (lt1 && lt3) + EXPECT_TRUE(lt4); // lt is transitive + if (eq1) + EXPECT_EQ(h(r1), h(r2)); + if (eq2) + EXPECT_EQ(h(r2), h(r1)); + if (eq3) + EXPECT_EQ(h(r2), h(r3)); + if (eq4) + EXPECT_EQ(h(r1), h(r3)); + if (h(r1) != h(r2)) + EXPECT_FALSE(eq1); + if (h(r2) != h(r1)) + EXPECT_FALSE(eq2); + if (h(r2) != h(r3)) + EXPECT_FALSE(eq3); + if (h(r1) != h(r3)) + EXPECT_FALSE(eq4); + } +} + +TEST(RegionCalculus, point_1d) { test_point<1>(); } +TEST(RegionCalculus, point_2d) { test_point<2>(); } +TEST(RegionCalculus, point_3d) { test_point<3>(); } +TEST(RegionCalculus, point_4d) { test_point<4>(); } + +TEST(RegionCalculus, box_1d) { test_box<1>(); } +TEST(RegionCalculus, box_2d) { test_box<2>(); } +TEST(RegionCalculus, box_3d) { test_box<3>(); } +TEST(RegionCalculus, box_4d) { test_box<4>(); } + +TEST(RegionCalculus, region_1d) { test_region<1>(); } +TEST(RegionCalculus, region_2d) { test_region<2>(); } +TEST(RegionCalculus, region_3d) { test_region<3>(); } +TEST(RegionCalculus, region_4d) { test_region<4>(); } + +TEST(RegionCalculus, dpoint) { + const int dim = 3; + typedef dpoint dpoint; + dpoint p(dim); + EXPECT_FALSE(all(p)); + EXPECT_FALSE(any(p)); + dpoint p0(dim, 0); + EXPECT_FALSE(all(p0)); + EXPECT_FALSE(any(p0)); + EXPECT_TRUE(all(p0 == p)); + dpoint p1(::dpoint(dim, true)); + EXPECT_TRUE(all(p1)); + EXPECT_TRUE(any(p1)); + EXPECT_TRUE(all(p1 != p)); + EXPECT_TRUE(all(p1 == +p1)); + EXPECT_TRUE(all(p0 == -p0)); + EXPECT_TRUE(all(p1 == abs(-p1))); + EXPECT_TRUE(all(-p1 == ~p0)); + EXPECT_TRUE(all(!p0)); + dpoint p2(dpoint(dim, 2)); + EXPECT_TRUE(equal_to()(p2, dpoint(::dpoint(p2)))); + EXPECT_TRUE(all(p1 + p1 == p2)); + EXPECT_TRUE(all(p2 - p1 == p1)); + EXPECT_TRUE(all(p2 * p1 == p2)); + EXPECT_TRUE(all((p2 & p1) == p0)); + EXPECT_TRUE(all((p2 | p1) == p1 + p2)); + EXPECT_TRUE(all((p2 ^ p1) == p1 + p2)); + EXPECT_TRUE(all(p1 && p2)); + EXPECT_TRUE(all(p1 || p2)); + EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); + EXPECT_EQ(2, minval(p2)); + EXPECT_EQ(2, maxval(p2)); + EXPECT_EQ(3, sum(p1)); + EXPECT_EQ(8, prod(p2)); + vector p3vals(dim); + p3vals[0] = 0; + p3vals[1] = 1; + p3vals[2] = 2; + dpoint p3(p3vals); + EXPECT_TRUE(all((p1 + p3) * dpoint(dim, 2) - p3 == p3 + p2)); + EXPECT_TRUE(all(p3 == ::dpoint(vector{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::dpoint(vector{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); + EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(3, 2)))); + EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(3, 2)))); + auto v2 = vector({2, 2, 2}); + EXPECT_EQ(v2, vector(::dpoint(3, 2))); + EXPECT_EQ(v2, vector(::dpoint(3, 2))); + ostringstream buf; + buf << p3; + EXPECT_EQ("[0,1,2]", buf.str()); +} + +TEST(RegionCalculus, dbox) { + const int dim = 3; + typedef dpoint dpoint; + typedef dbox dbox; + dbox b(dim); + EXPECT_TRUE(b.empty()); + dpoint p0(dim, 0), p1(dim, 1); + dbox b1(dbox(p0, p1)); + EXPECT_EQ(1, b1.size()); + dbox b4(p0, dpoint(dim, 4)); + dbox b5 = b4 >> p1; + dbox b3 = b4 << p1; + dbox b6 = b3 * dpoint(dim, 2); + EXPECT_EQ(b4, dbox(::dbox(b4))); + EXPECT_TRUE(b4 == b4); + EXPECT_TRUE(b4 != b5); + EXPECT_TRUE(b4.contains(p1)); + EXPECT_FALSE(b5.contains(p0)); + EXPECT_FALSE(b5.isdisjoint(b3)); + EXPECT_TRUE(b1.isdisjoint(b5)); + EXPECT_TRUE(b.isdisjoint(b5)); + EXPECT_TRUE(b1.isdisjoint(b)); + EXPECT_TRUE(b6.issuperset(b3)); + EXPECT_FALSE(b3.issuperset(b6)); + EXPECT_TRUE(b4.issuperset(b4)); + EXPECT_TRUE(b4.issuperset(b)); + EXPECT_FALSE(b.issuperset(b4)); + EXPECT_TRUE(b.issuperset(b)); + EXPECT_FALSE(b4.is_strict_superset(b4)); + EXPECT_TRUE(b4.is_strict_superset(b)); + EXPECT_FALSE(b.is_strict_superset(b4)); + EXPECT_FALSE(b.is_strict_superset(b)); + EXPECT_EQ(dbox(p1, dpoint(dim, 3)), b5.intersection(b3)); + EXPECT_TRUE(b1.intersection(b5).empty()); + EXPECT_TRUE(b.intersection(b5).empty()); + EXPECT_TRUE(b1.intersection(b).empty()); + ostringstream buf; + buf << b4; + EXPECT_EQ("([0,0,0]:[4,4,4])", buf.str()); +} + +TEST(RegionCalculus, dregion) { + const int dim = 3; + typedef dpoint dpoint; + typedef dbox dbox; + typedef dregion dregion; + dregion r(dim); + EXPECT_TRUE(r.invariant()); + EXPECT_TRUE(r.empty()); + dpoint p(dim); + dpoint p1(dim, 1); + dbox b(dim); + dbox b1(p, p1); + dregion r0(b); + EXPECT_TRUE(r0.invariant()); + EXPECT_TRUE(r0.empty()); + dregion r1(b1); + EXPECT_TRUE(r1.invariant()); + EXPECT_FALSE(r1.empty()); + EXPECT_TRUE(r == r); + EXPECT_TRUE(r1 == r1); + EXPECT_FALSE(r != r); + EXPECT_TRUE(r != r1); + dpoint p2(dim, 2); + dbox b2(p, p2); + dregion r2(b2); + EXPECT_EQ(r2, dregion(::dregion(r2))); + EXPECT_TRUE(r == r.intersection(r1)); + EXPECT_TRUE(r == r1.intersection(r)); + EXPECT_TRUE(r1 == r1.intersection(r2)); + EXPECT_TRUE(r1 == r2.intersection(r1)); + EXPECT_TRUE(r == r.difference(r)); + EXPECT_TRUE(r1 == r1.difference(r)); + EXPECT_TRUE(r == r.difference(r1)); + EXPECT_TRUE(r == r1.difference(r1)); + EXPECT_TRUE(r == r.setunion(r)); + EXPECT_TRUE(r1 == r1.setunion(r)); + EXPECT_TRUE(r1 == r.setunion(r1)); + EXPECT_TRUE(r1 == r1.setunion(r1)); + EXPECT_TRUE(r == r.symmetric_difference(r)); + EXPECT_TRUE(r1 == r1.symmetric_difference(r)); + EXPECT_TRUE(r1 == r.symmetric_difference(r1)); + EXPECT_TRUE(r == r1.symmetric_difference(r1)); + vector r12vals; + r12vals.push_back(b1); + r12vals.push_back(dbox(p1, p2)); + dregion r12(r12vals); + vector r12boxes = r12; + EXPECT_EQ(r12vals, r12boxes); + vector rs; + rs.push_back(r); + rs.push_back(r1); + rs.push_back(r2); + dbox b4(p, dpoint(dim, 4)); + dregion r4(b4); + rs.push_back(r4); + rs.push_back(r12); + rs.push_back(r2.difference(r1)); + rs.push_back(r2.symmetric_difference(r12)); + for (std::size_t i = 0; i < rs.size(); ++i) { + const auto &ri = rs[i]; + EXPECT_TRUE(ri.invariant()); + for (std::size_t j = 0; j < rs.size(); ++j) { + const auto &rj = rs[j]; + auto rintersection = ri.intersection(rj); + auto rdifference = ri.difference(rj); + auto rsetunion = ri.setunion(rj); + auto rsymmetric_difference = ri.symmetric_difference(rj); + EXPECT_TRUE(rintersection.invariant()); + EXPECT_TRUE(rdifference.invariant()); + EXPECT_TRUE(rsetunion.invariant()); + EXPECT_TRUE(rsymmetric_difference.invariant()); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(rj.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(ri)); + EXPECT_TRUE(rsetunion.issuperset(rj)); + EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); + EXPECT_TRUE(rdifference.isdisjoint(rj)); + EXPECT_TRUE(rsetunion.issuperset(rintersection)); + EXPECT_TRUE(rsetunion.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); + EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); + EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); + EXPECT_TRUE(rintersection == rj.intersection(ri)); + EXPECT_TRUE(rsetunion == rj.setunion(ri)); + EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); + if (ri == rj) { + EXPECT_TRUE(rintersection == ri); + EXPECT_TRUE(rdifference.empty()); + EXPECT_TRUE(rsetunion == ri); + EXPECT_TRUE(rsymmetric_difference.empty()); + } + EXPECT_TRUE(rintersection == (ri & rj)); + auto rintersection1 = ri; + rintersection1 &= rj; + EXPECT_TRUE(rintersection == rintersection1); + EXPECT_TRUE(rdifference == (ri - rj)); + auto rdifference1 = ri; + rdifference1 -= rj; + EXPECT_TRUE(rdifference == rdifference1); + EXPECT_TRUE(rsetunion == (ri | rj)); + auto rsetunion1 = ri; + rsetunion1 |= rj; + EXPECT_TRUE(rsetunion == rsetunion1); + EXPECT_TRUE(rsymmetric_difference == (ri ^ rj)); + auto rsymmetric_difference1 = ri; + rsymmetric_difference1 ^= rj; + EXPECT_TRUE(rsymmetric_difference == rsymmetric_difference1); + } + } + ostringstream buf; + buf << r12; + EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); +} + +TEST(RegionCalculus, dpoint_dim) { + typedef dpoint dpoint; + for (int dim = 0; dim < 4; ++dim) { + dpoint p(dim); + if (dim == 0) + EXPECT_TRUE(all(p)); + else + EXPECT_FALSE(all(p)); + EXPECT_FALSE(any(p)); + dpoint p0(dim, 0); + if (dim == 0) + EXPECT_TRUE(all(p0)); + else + EXPECT_FALSE(all(p0)); + EXPECT_FALSE(any(p0)); + EXPECT_TRUE(all(p0 == p)); + dpoint p1(::dpoint(dim, true)); + EXPECT_TRUE(all(p1)); + if (dim == 0) + EXPECT_FALSE(any(p1)); + else + EXPECT_TRUE(any(p1)); + EXPECT_TRUE(all(p1 != p)); + EXPECT_TRUE(all(p1 == +p1)); + EXPECT_TRUE(all(p0 == -p0)); + EXPECT_TRUE(all(p1 == abs(-p1))); + EXPECT_TRUE(all(-p1 == ~p0)); + EXPECT_TRUE(all(!p0)); + dpoint p2(dpoint(dim, 2)); + EXPECT_TRUE(equal_to()(p2, dpoint(::dpoint(p2)))); + EXPECT_TRUE(all(p1 + p1 == p2)); + EXPECT_TRUE(all(p2 - p1 == p1)); + EXPECT_TRUE(all(p2 * p1 == p2)); + EXPECT_TRUE(all((p2 & p1) == p0)); + EXPECT_TRUE(all((p2 | p1) == p1 + p2)); + EXPECT_TRUE(all((p2 ^ p1) == p1 + p2)); + EXPECT_TRUE(all(p1 && p2)); + EXPECT_TRUE(all(p1 || p2)); + EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); + EXPECT_EQ(dim == 0 ? numeric_limits::max() : 2, minval(p2)); + EXPECT_EQ(dim == 0 ? numeric_limits::min() : 2, maxval(p2)); + EXPECT_EQ(dim, sum(p1)); + EXPECT_EQ(1 << dim, prod(p2)); + vector p3vals(dim); + for (int d = 0; d < dim; ++d) + p3vals[d] = d; + dpoint p3(p3vals); + EXPECT_TRUE(all((p1 + p3) * dpoint(dim, 2) - p3 == p3 + p2)); + vector vi(dim); + for (int d = 0; d < dim; ++d) + vi[d] = d; + EXPECT_TRUE(all(p3 == ::dpoint(vi))); + vector vs(dim); + for (int d = 0; d < dim; ++d) + vs[d] = d; + EXPECT_TRUE(all(p3 == ::dpoint(vs))); + // EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); + // EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); + EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(dim, 2)))); + EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(dim, 2)))); + auto v2 = vector(dim, 2); + EXPECT_EQ(v2, vector(::dpoint(dim, 2))); + EXPECT_EQ(v2, vector(::dpoint(dim, 2))); + ostringstream buf; + buf << p3; + ostringstream goodbuf; + goodbuf << "["; + for (int d = 0; d < dim; ++d) { + if (d > 0) + goodbuf << ","; + goodbuf << d; + } + goodbuf << "]"; + EXPECT_EQ(goodbuf.str(), buf.str()); + } +} + +TEST(RegionCalculus, dbox_dim) { + typedef dpoint dpoint; + typedef dbox dbox; + for (int dim = 0; dim < 4; ++dim) { + dbox b(dim); + EXPECT_TRUE(b.empty()); + dpoint p0(dim, 0), p1(dim, 1); + dbox b1(dbox(p0, p1)); + EXPECT_EQ(1, b1.size()); + dbox b4(p0, dpoint(dim, 4)); + dbox b5 = b4 >> p1; + dbox b3 = b4 << p1; + dbox b6 = b3 * dpoint(dim, 2); + dbox b7 = b4.grow(p0, p1); + dbox b8 = b4.grow(p1, p0); + dbox b9 = b4.shrink(p0, p1); + EXPECT_EQ(b4, dbox(::dbox(b4))); + EXPECT_TRUE(b4 == b4); + if (dim == 0) + EXPECT_FALSE(b4 != b5); + else + EXPECT_TRUE(b4 != b5); + EXPECT_TRUE(b4.contains(p1)); + if (dim == 0) + EXPECT_TRUE(b5.contains(p0)); + else + EXPECT_FALSE(b5.contains(p0)); + EXPECT_FALSE(b5.isdisjoint(b3)); + if (dim == 0) + EXPECT_FALSE(b1.isdisjoint(b5)); + else + EXPECT_TRUE(b1.isdisjoint(b5)); + EXPECT_TRUE(b.isdisjoint(b5)); + EXPECT_TRUE(b1.isdisjoint(b)); + EXPECT_TRUE(b6.issuperset(b3)); + if (dim == 0) + EXPECT_TRUE(b3.issuperset(b6)); + else + EXPECT_FALSE(b3.issuperset(b6)); + EXPECT_TRUE(b4.issuperset(b4)); + EXPECT_TRUE(b4.issuperset(b)); + EXPECT_FALSE(b.issuperset(b4)); + EXPECT_TRUE(b.issuperset(b)); + EXPECT_FALSE(b4.is_strict_superset(b4)); + EXPECT_TRUE(b4.is_strict_superset(b)); + EXPECT_FALSE(b.is_strict_superset(b4)); + EXPECT_FALSE(b.is_strict_superset(b)); + EXPECT_EQ(dbox(p1, dpoint(dim, 3)), b5.intersection(b3)); + if (dim == 0) + EXPECT_FALSE(b1.intersection(b5).empty()); + else + EXPECT_TRUE(b1.intersection(b5).empty()); + EXPECT_TRUE(b.intersection(b5).empty()); + EXPECT_TRUE(b1.intersection(b).empty()); + ostringstream buf; + buf << b4; + ostringstream goodbuf; + goodbuf << "("; + if (dim == 0) { + goodbuf << 1; + } else { + goodbuf << "["; + for (int d = 0; d < dim; ++d) { + if (d > 0) + goodbuf << ","; + goodbuf << 0; + } + goodbuf << "]:["; + for (int d = 0; d < dim; ++d) { + if (d > 0) + goodbuf << ","; + goodbuf << 4; + } + goodbuf << "]"; + } + goodbuf << ")"; + EXPECT_EQ(goodbuf.str(), buf.str()); + } +} + +TEST(RegionCalculus, dregion_dim) { + typedef dpoint dpoint; + typedef dbox dbox; + typedef dregion dregion; + for (int dim = 0; dim < 4; ++dim) { + dregion r(dim); + EXPECT_TRUE(r.invariant()); + EXPECT_TRUE(r.empty()); + dpoint p(dim); + dpoint p1(dim, 1); + dbox b(dim); + dbox b1(p, p1); + dregion r0(b); + EXPECT_TRUE(r0.invariant()); + EXPECT_TRUE(r0.empty()); + dregion r1(b1); + EXPECT_TRUE(r1.invariant()); + EXPECT_FALSE(r1.empty()); + EXPECT_TRUE(r == r); + EXPECT_TRUE(r1 == r1); + EXPECT_FALSE(r != r); + EXPECT_TRUE(r != r1); + dpoint p2(dim, 2); + dbox b2(p, p2); + dregion r2(b2); + EXPECT_EQ(r2, dregion(::dregion(r2))); + EXPECT_TRUE(r == r.intersection(r1)); + EXPECT_TRUE(r == r1.intersection(r)); + EXPECT_TRUE(r1 == r1.intersection(r2)); + EXPECT_TRUE(r1 == r2.intersection(r1)); + EXPECT_TRUE(r == r.difference(r)); + EXPECT_TRUE(r1 == r1.difference(r)); + EXPECT_TRUE(r == r.difference(r1)); + EXPECT_TRUE(r == r1.difference(r1)); + EXPECT_TRUE(r == r.setunion(r)); + EXPECT_TRUE(r1 == r1.setunion(r)); + EXPECT_TRUE(r1 == r.setunion(r1)); + EXPECT_TRUE(r1 == r1.setunion(r1)); + EXPECT_TRUE(r == r.symmetric_difference(r)); + EXPECT_TRUE(r1 == r1.symmetric_difference(r)); + EXPECT_TRUE(r1 == r.symmetric_difference(r1)); + EXPECT_TRUE(r == r1.symmetric_difference(r1)); + vector r12vals; + if (dim == 0) { + r12vals.push_back(b1); + } else if (dim == 1) { + r12vals.push_back(dbox(p, p2)); + } else { + r12vals.push_back(b1); + r12vals.push_back(dbox(p1, p2)); + } + dregion r12(r12vals); + vector r12boxes = r12; + EXPECT_EQ(r12vals, r12boxes); + vector rs; + rs.push_back(r); + rs.push_back(r1); + rs.push_back(r2); + dbox b4(p, dpoint(dim, 4)); + dregion r4(b4); + rs.push_back(r4); + rs.push_back(r12); + rs.push_back(r2.difference(r1)); + rs.push_back(r2.symmetric_difference(r12)); + rs.push_back(r2 >> p1); + rs.push_back(r2 << p1); + rs.push_back(r2.grow(p1)); + rs.push_back(r2.shrink(p1)); + for (std::size_t i = 0; i < rs.size(); ++i) { + const auto &ri = rs[i]; + EXPECT_TRUE(ri.invariant()); + for (std::size_t j = 0; j < rs.size(); ++j) { + const auto &rj = rs[j]; + auto rintersection = ri.intersection(rj); + auto rdifference = ri.difference(rj); + auto rsetunion = ri.setunion(rj); + auto rsymmetric_difference = ri.symmetric_difference(rj); + EXPECT_TRUE(rintersection.invariant()); + EXPECT_TRUE(rdifference.invariant()); + EXPECT_TRUE(rsetunion.invariant()); + EXPECT_TRUE(rsymmetric_difference.invariant()); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(rj.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(ri)); + EXPECT_TRUE(rsetunion.issuperset(rj)); + EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); + EXPECT_TRUE(rdifference.isdisjoint(rj)); + EXPECT_TRUE(rsetunion.issuperset(rintersection)); + EXPECT_TRUE(rsetunion.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); + EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); + EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); + EXPECT_TRUE(rintersection == rj.intersection(ri)); + EXPECT_TRUE(rsetunion == rj.setunion(ri)); + EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); + if (ri == rj) { + EXPECT_TRUE(rintersection == ri); + EXPECT_TRUE(rdifference.empty()); + EXPECT_TRUE(rsetunion == ri); + EXPECT_TRUE(rsymmetric_difference.empty()); + } + } + } + ostringstream buf; + buf << r12; + ostringstream goodbuf; + goodbuf << "{"; + if (dim == 0) { + goodbuf << "(" << true << ")"; + } else { + goodbuf << "(["; + for (int d = 0; d < dim; ++d) { + if (d > 0) + goodbuf << ","; + goodbuf << 0; + } + goodbuf << "]:["; + if (dim > 1) { + for (int d = 0; d < dim; ++d) { + if (d > 0) + goodbuf << ","; + goodbuf << 1; + } + goodbuf << "]),(["; + for (int d = 0; d < dim; ++d) { + if (d > 0) + goodbuf << ","; + goodbuf << 1; + } + goodbuf << "]:["; + } + for (int d = 0; d < dim; ++d) { + if (d > 0) + goodbuf << ","; + goodbuf << 2; + } + goodbuf << "])"; + } + goodbuf << "}"; + EXPECT_EQ(goodbuf.str(), buf.str()); + } +} + +TEST(RegionCalculus, point_double) { + typedef point point; + point p; + point p0(0); + EXPECT_TRUE(all(p0 == p)); + point p1(::point(1)); + EXPECT_TRUE(all(p1 != p)); + EXPECT_TRUE(all(p1 == +p1)); + EXPECT_TRUE(all(p0 == -p0)); + EXPECT_TRUE(all(p1 == abs(-p1))); + point p2(point(2)); + EXPECT_TRUE(equal_to()(p2, point(::point(p2)))); + EXPECT_TRUE(all(p1 + p1 == p2)); + EXPECT_TRUE(all(p2 - p1 == p1)); + EXPECT_TRUE(all(p2 * p1 == p2)); + EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); + EXPECT_EQ(2, minval(p2)); + EXPECT_EQ(2, maxval(p2)); + EXPECT_EQ(3, sum(p1)); + EXPECT_EQ(8, prod(p2)); + point p3; + p3.elt[0] = 0; + p3.elt[1] = 1; + p3.elt[2] = 2; + EXPECT_TRUE(all((p1 + p3) * point(2) - p3 == p3 + p2)); + EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); + EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); + EXPECT_TRUE(all(p2 == ::point(::point(2)))); + EXPECT_TRUE(all(p2 == ::point(::point(2)))); + auto v2 = vector({2, 2, 2}); + EXPECT_EQ(v2, vector(::point(2))); + EXPECT_EQ(v2, vector(::point(2))); + ostringstream buf; + buf << p3; + EXPECT_EQ("[0,1,2]", buf.str()); +} + +TEST(RegionCalculus, box_double) { + typedef point point; + typedef box box; + box b; + EXPECT_TRUE(b.empty()); + point p0(0), p1(1); + box b1(box(p0, p1)); + EXPECT_EQ(1, b1.size()); + box b4(p0, point(4)); + box b5 = b4 >> p1; + box b3 = b4 << p1; + box b6 = b3 * point(2); + box b7 = b4.grow(p0, p1); + box b8 = b4.grow(p1, p0); + box b9 = b4.shrink(p0, p1); + EXPECT_EQ(b4, (box(::box(b4)))); + EXPECT_TRUE(b4 == b4); + EXPECT_TRUE(b4 != b5); + EXPECT_TRUE(b4.contains(p1)); + EXPECT_FALSE(b5.contains(p0)); + EXPECT_FALSE(b5.isdisjoint(b3)); + EXPECT_TRUE(b1.isdisjoint(b5)); + EXPECT_TRUE(b.isdisjoint(b5)); + EXPECT_TRUE(b1.isdisjoint(b)); + EXPECT_TRUE(b6.issuperset(b3)); + EXPECT_FALSE(b3.issuperset(b6)); + EXPECT_TRUE(b4.issuperset(b4)); + EXPECT_TRUE(b4.issuperset(b)); + EXPECT_FALSE(b.issuperset(b4)); + EXPECT_TRUE(b.issuperset(b)); + EXPECT_TRUE(b7.issuperset(b4)); + EXPECT_TRUE(b7.issuperset(b5)); + EXPECT_TRUE(b8.issuperset(b4)); + EXPECT_TRUE(b8.issuperset(b3)); + EXPECT_FALSE(b4.is_strict_superset(b4)); + EXPECT_TRUE(b4.is_strict_superset(b)); + EXPECT_FALSE(b.is_strict_superset(b4)); + EXPECT_FALSE(b.is_strict_superset(b)); + EXPECT_EQ(box(p1, point(3)), b5.intersection(b3)); + EXPECT_TRUE(b1.intersection(b5).empty()); + EXPECT_TRUE(b.intersection(b5).empty()); + EXPECT_TRUE(b1.intersection(b).empty()); + ostringstream buf; + buf << b4; + EXPECT_EQ("([0,0,0]:[4,4,4])", buf.str()); +} + +TEST(RegionCalculus, region_double) { + typedef point point; + typedef box box; + typedef region region; + region r; + EXPECT_TRUE(r.invariant()); + EXPECT_TRUE(r.empty()); + point p; + point p1(1); + box b; + box b1(p, p1); + region r0(b); + EXPECT_TRUE(r0.invariant()); + EXPECT_TRUE(r0.empty()); + region r1(b1); + EXPECT_TRUE(r1.invariant()); + EXPECT_FALSE(r1.empty()); + EXPECT_TRUE(r == r); + EXPECT_TRUE(r1 == r1); + EXPECT_FALSE(r != r); + EXPECT_TRUE(r != r1); + point p2(2); + box b2(p, p2); + region r2(b2); + EXPECT_EQ(r2, (region(::region(r2)))); + EXPECT_TRUE(r == r.intersection(r1)); + EXPECT_TRUE(r == r1.intersection(r)); + EXPECT_TRUE(r1 == r1.intersection(r2)); + EXPECT_TRUE(r1 == r2.intersection(r1)); + EXPECT_TRUE(r == r.difference(r)); + EXPECT_TRUE(r1 == r1.difference(r)); + EXPECT_TRUE(r == r.difference(r1)); + EXPECT_TRUE(r == r1.difference(r1)); + EXPECT_TRUE(r == r.setunion(r)); + EXPECT_TRUE(r1 == r1.setunion(r)); + EXPECT_TRUE(r1 == r.setunion(r1)); + EXPECT_TRUE(r1 == r1.setunion(r1)); + EXPECT_TRUE(r == r.symmetric_difference(r)); + EXPECT_TRUE(r1 == r1.symmetric_difference(r)); + EXPECT_TRUE(r1 == r.symmetric_difference(r1)); + EXPECT_TRUE(r == r1.symmetric_difference(r1)); + vector r12vals; + r12vals.push_back(b1); + r12vals.push_back(box(p1, p2)); + region r12(r12vals); + vector r12boxes = r12; + EXPECT_EQ(r12vals, r12boxes); + vector rs; + rs.push_back(r); + rs.push_back(r1); + rs.push_back(r2); + box b4(p, point(4)); + region r4(b4); + rs.push_back(r4); + rs.push_back(r12); + rs.push_back(r2.difference(r1)); + rs.push_back(r2.symmetric_difference(r12)); + for (std::size_t i = 0; i < rs.size(); ++i) { + const auto &ri = rs[i]; + auto rgrown = ri.grow(p1); + auto rshrunk = ri.shrink(p1); + EXPECT_TRUE(ri.invariant()); + EXPECT_TRUE(rgrown.invariant()); + EXPECT_TRUE(rshrunk.invariant()); + EXPECT_TRUE(rgrown.issuperset(ri)); + if (ri.empty()) + EXPECT_TRUE(rgrown.empty()); + region rgrown_test; + for (int dk = -1; dk <= +1; ++dk) + for (int dj = -1; dj <= +1; ++dj) + for (int di = -1; di <= +1; ++di) { + auto shifted = ri >> point(di, dj, dk); + rgrown_test |= shifted; + } + EXPECT_TRUE(rgrown_test == rgrown); + EXPECT_TRUE(ri.issuperset(rshrunk)); + if (!rshrunk.empty()) + EXPECT_TRUE(rshrunk.grow(p1) == ri); + region rshrunk_test = ri; + for (int dk = -1; dk <= +1; ++dk) + for (int dj = -1; dj <= +1; ++dj) + for (int di = -1; di <= +1; ++di) { + auto shifted = ri >> point(di, dj, dk); + rshrunk_test &= shifted; + } + EXPECT_TRUE(rshrunk_test == rshrunk); + region rgrown_shrunk_test = rgrown; + for (int dk = -1; dk <= +1; ++dk) + for (int dj = -1; dj <= +1; ++dj) + for (int di = -1; di <= +1; ++di) { + auto shifted = rgrown >> point(di, dj, dk); + rgrown_shrunk_test &= shifted; + } + EXPECT_TRUE(rgrown_shrunk_test == ri); + EXPECT_TRUE(rgrown.shrink(p1) == ri); + for (std::size_t j = 0; j < rs.size(); ++j) { + const auto &rj = rs[j]; + auto rintersection = ri.intersection(rj); + auto rdifference = ri.difference(rj); + auto rsetunion = ri.setunion(rj); + auto rsymmetric_difference = ri.symmetric_difference(rj); + EXPECT_TRUE(rintersection.invariant()); + EXPECT_TRUE(rdifference.invariant()); + EXPECT_TRUE(rsetunion.invariant()); + EXPECT_TRUE(rsymmetric_difference.invariant()); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rintersection)); + EXPECT_TRUE(rj.issuperset(rintersection)); + EXPECT_TRUE(ri.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(ri)); + EXPECT_TRUE(rsetunion.issuperset(rj)); + EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); + EXPECT_TRUE(rdifference.isdisjoint(rj)); + EXPECT_TRUE(rsetunion.issuperset(rintersection)); + EXPECT_TRUE(rsetunion.issuperset(rdifference)); + EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); + EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); + EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); + EXPECT_TRUE(rintersection == rj.intersection(ri)); + EXPECT_TRUE(rsetunion == rj.setunion(ri)); + EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); + if (ri == rj) { + EXPECT_TRUE(rintersection == ri); + EXPECT_TRUE(rdifference.empty()); + EXPECT_TRUE(rsetunion == ri); + EXPECT_TRUE(rsymmetric_difference.empty()); + } + } + } + ostringstream buf; + buf << r12; + EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); +} + +TEST(RegionCalculus, double_) { + for (int d = 0; d <= 4; ++d) { + dpoint p(d); + dbox b(d); + dregion r(d); + } +} + +namespace benchmark { +constexpr int D = 3; +typedef double T; +typedef point P; +typedef box B; +typedef region R; +array regs; +} // namespace benchmark + +TEST(RegionCalculus, benchmark_region_setup) { + using namespace benchmark; + + constexpr int niters = 10000; + for (int i = 0; i < 2; ++i) { + vector bs; + bs.reserve(niters); + for (int n = 0; n < niters; ++n) { + array xs, ys; + for (int d = 0; d < D; ++d) { + xs[d] = irand(100); + ys[d] = xs[d] + irand(10); + } + P px(xs), py(ys); + B b{px, py}; + bs.push_back(b); + } + regs[i] = R(bs); + } +} + +TEST(RegionCalculus, benchmark_region_intersection) { + using namespace benchmark; + auto rint = regs[0] & regs[1]; +} +TEST(RegionCalculus, benchmark_region_union) { + using namespace benchmark; + auto runi = regs[0] | regs[1]; +} +TEST(RegionCalculus, benchmark_region_symmetric_difference) { + using namespace benchmark; + auto rsym = regs[0] ^ regs[1]; +} +TEST(RegionCalculus, benchmark_region_difference) { + using namespace benchmark; + auto rdif = regs[0] - regs[1]; +} +TEST(RegionCalculus, benchmark_region_equal) { + using namespace benchmark; + auto req = regs[0] == regs[1]; +} +TEST(RegionCalculus, benchmark_region_subset) { + using namespace benchmark; + auto rle = regs[0] & regs[1]; +} +TEST(RegionCalculus, benchmark_region_strict_subset) { + using namespace benchmark; + auto rlt = regs[0] < regs[1]; +} From 84796f84bebc59df2eb4e162a891f4bd44997847 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 28 May 2021 18:19:18 -0400 Subject: [PATCH 02/68] Add example classes `point` and `ndpoint` --- CMakeLists.txt | 2 + include/openPMD/RegionCalculus2.hpp | 165 ++++++++++++++++++++++++++++ src/RegionCalculus2.cpp | 3 + test/RegionCalculus2Test.cpp | 48 ++++++++ test/RegionCalculusTest.cpp | 4 + 5 files changed, 222 insertions(+) create mode 100644 include/openPMD/RegionCalculus2.hpp create mode 100644 src/RegionCalculus2.cpp create mode 100644 test/RegionCalculus2Test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4741834442..14f78a487a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -348,6 +348,7 @@ set(CORE_SOURCE src/Record.cpp src/RecordComponent.cpp src/RegionCalculus.cpp + src/RegionCalculus2.cpp src/Series.cpp src/version.cpp src/WriteIterations.cpp @@ -684,6 +685,7 @@ set(openPMD_TEST_NAMES Core Auxiliary RegionCalculus + RegionCalculus2 SerialIO ParallelIO ) diff --git a/include/openPMD/RegionCalculus2.hpp b/include/openPMD/RegionCalculus2.hpp new file mode 100644 index 0000000000..601895d31d --- /dev/null +++ b/include/openPMD/RegionCalculus2.hpp @@ -0,0 +1,165 @@ +#ifndef REGIONCALCULUS2_HPP +#define REGIONCALCULUS2_HPP + +#include +#include +#include +#include +#include + +namespace RegionCalculus2 { + +// A D-dimensional point + +template class point { + std::array elts; + +public: + typedef T value_type; + typedef std::size_t size_type; + + constexpr point() : elts{} {} // value initialization + + point(const point &) = default; + point(point &&) = default; + point &operator=(const point &) = default; + point &operator=(point &&) = default; + + constexpr size_type size() const { return D; } + + constexpr const T &operator[](const size_type d) const { return elts[d]; } + constexpr T &operator[](const size_type d) { return elts[d]; } + + friend constexpr point operator+(const point &x, const point &y) { + point r; + for (size_type d = 0; d < D; ++d) + r.elts[d] = x.elts[d] + y.elts[d]; + return r; + } + constexpr point &operator+=(const point &x) { return *this = *this + x; } +}; + +namespace detail { + +// Abstract base helper class + +template class vpoint { +public: + typedef T value_type; + typedef std::size_t size_type; + + virtual std::unique_ptr copy() const = 0; + + virtual ~vpoint() {} + + virtual size_type size() const = 0; + + virtual const T &operator[](const size_type d) const = 0; + virtual T &operator[](const size_type d) = 0; + + virtual vpoint &operator+=(const std::unique_ptr &x) = 0; + virtual std::unique_ptr + operator+(const std::unique_ptr &x) const = 0; +}; + +// Helper class wrapping point + +template class wpoint final : public vpoint { + point p; + +public: + using typename vpoint::value_type; + using typename vpoint::size_type; + + wpoint() : p{} {} + + wpoint(const wpoint &x) = default; + wpoint(wpoint &&) = default; + wpoint &operator=(const wpoint &) = default; + wpoint &operator=(wpoint &&) = default; + + std::unique_ptr> copy() const override { + return std::make_unique(*this); + } + + ~wpoint() override {} + + constexpr size_type size() const override { return p.size(); } + + const T &operator[](const size_type d) const override { return p[d]; } + T &operator[](const size_type d) override { return p[d]; } + + vpoint &operator+=(const std::unique_ptr> &x) override { + p += dynamic_cast(*x).p; + return *this; + } + std::unique_ptr> + operator+(const std::unique_ptr> &x) const override { + auto r = std::make_unique(*this); + *r += x; + return r; + } +}; + +template +std::unique_ptr> make_vpoint(const std::size_t D) { + switch (D) { + case 0: + return std::make_unique>(); + case 1: + return std::make_unique>(); + case 2: + return std::make_unique>(); + case 3: + return std::make_unique>(); + case 4: + return std::make_unique>(); + case 5: + return std::make_unique>(); + default: + abort(); + } +} + +} // namespace detail + +// A point + +template class ndpoint { + template using vpoint = detail::vpoint; + + std::unique_ptr> p; + + ndpoint(std::unique_ptr> p) : p(std::move(p)) {} + +public: + typedef typename vpoint::value_type value_type; + typedef typename vpoint::size_type size_type; + + ndpoint() : p() {} + ndpoint(const size_type D) : p(detail::make_vpoint(D)) {} + + ndpoint(const ndpoint &x) : p(x.p ? x.p->copy() : nullptr) {} + ndpoint(ndpoint &&) = default; + ndpoint &operator=(const ndpoint &) = default; + ndpoint &operator=(ndpoint &&) = default; + + operator bool() const { return bool(p); } + + size_type size() const { return p->size(); } + + const T &operator[](const size_type d) const { return (*p)[d]; } + T &operator[](const size_type d) { return (*p)[d]; } + + ndpoint &operator+=(const ndpoint &x) { + *p += x.p; + return *this; + } + friend ndpoint operator+(const ndpoint &x, const ndpoint &y) { + return ndpoint(x) += y; + } +}; + +} // namespace RegionCalculus2 + +#endif // #ifndef REGIONCALCULUS2_HPP diff --git a/src/RegionCalculus2.cpp b/src/RegionCalculus2.cpp new file mode 100644 index 0000000000..651ffc2885 --- /dev/null +++ b/src/RegionCalculus2.cpp @@ -0,0 +1,3 @@ +#include "openPMD/RegionCalculus.hpp" + +// We could instantiate some templates here. diff --git a/test/RegionCalculus2Test.cpp b/test/RegionCalculus2Test.cpp new file mode 100644 index 0000000000..b0569b4efc --- /dev/null +++ b/test/RegionCalculus2Test.cpp @@ -0,0 +1,48 @@ +#include "openPMD/RegionCalculus2.hpp" + +#include + +#include + +using namespace RegionCalculus2; + +template void test_point() { + point x; + REQUIRE(x.size() == D); + point y; + for (std::size_t d = 0; d < D; ++d) + y[d] = d; + point z = x + y; + for (std::size_t d = 0; d < D; ++d) + REQUIRE(z[d] == d); + z += y; + for (std::size_t d = 0; d < D; ++d) + REQUIRE(z[d] == 2 * d); +} + +TEST_CASE("point", "[auxiliary]") { + test_point(); + test_point(); +} + +template void test_ndpoint(const std::size_t D) { + ndpoint n; + REQUIRE(!n); + ndpoint x(D); + REQUIRE(x); + REQUIRE(x.size() == D); + ndpoint y(D); + for (std::size_t d = 0; d < D; ++d) + y[d] = d; + ndpoint z = x + y; + for (std::size_t d = 0; d < D; ++d) + REQUIRE(z[d] == d); + z += y; + for (std::size_t d = 0; d < D; ++d) + REQUIRE(z[d] == 2 * d); +} + +TEST_CASE("ndpoint", "[auxiliary]") { + test_ndpoint(2); + test_ndpoint(3); +} diff --git a/test/RegionCalculusTest.cpp b/test/RegionCalculusTest.cpp index 5e722fd3e1..a63f7bedb8 100644 --- a/test/RegionCalculusTest.cpp +++ b/test/RegionCalculusTest.cpp @@ -1,5 +1,7 @@ #include "openPMD/RegionCalculus.hpp" +#if 0 + #include #include @@ -1632,3 +1634,5 @@ TEST(RegionCalculus, benchmark_region_strict_subset) { using namespace benchmark; auto rlt = regs[0] < regs[1]; } + +#endif From 7eb8dbee9e5bd8777bce2bdac344afe0bf315386 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 4 Jun 2021 12:09:58 -0400 Subject: [PATCH 03/68] Re-write Point class --- CMakeLists.txt | 5 +- include/openPMD/RegionCalculus2.hpp | 165 --- include/openPMD/regions/Helpers.hpp | 47 + include/openPMD/regions/NDPoint.hpp | 776 ++++++++++++ include/openPMD/regions/Point.hpp | 494 ++++++++ include/openPMD/regions/RegionCalculus.hpp | 1116 +++++++++++++++++ .../RegionCalculus_old.hpp} | 0 include/openPMD/regions/Regions.hpp | 7 + src/RegionCalculus.cpp | 3 - src/RegionCalculus2.cpp | 3 - test/RegionCalculus2Test.cpp | 48 - ...lusTest.cpp => RegionCalculusTest_old.cpp} | 0 test/RegionsTest.cpp | 454 +++++++ 13 files changed, 2895 insertions(+), 223 deletions(-) delete mode 100644 include/openPMD/RegionCalculus2.hpp create mode 100644 include/openPMD/regions/Helpers.hpp create mode 100644 include/openPMD/regions/NDPoint.hpp create mode 100644 include/openPMD/regions/Point.hpp create mode 100644 include/openPMD/regions/RegionCalculus.hpp rename include/openPMD/{RegionCalculus.hpp => regions/RegionCalculus_old.hpp} (100%) create mode 100644 include/openPMD/regions/Regions.hpp delete mode 100644 src/RegionCalculus.cpp delete mode 100644 src/RegionCalculus2.cpp delete mode 100644 test/RegionCalculus2Test.cpp rename test/{RegionCalculusTest.cpp => RegionCalculusTest_old.cpp} (100%) create mode 100644 test/RegionsTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 14f78a487a..32671b44f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,8 +347,6 @@ set(CORE_SOURCE src/ReadIterations.cpp src/Record.cpp src/RecordComponent.cpp - src/RegionCalculus.cpp - src/RegionCalculus2.cpp src/Series.cpp src/version.cpp src/WriteIterations.cpp @@ -684,8 +682,7 @@ endif() set(openPMD_TEST_NAMES Core Auxiliary - RegionCalculus - RegionCalculus2 + Regions SerialIO ParallelIO ) diff --git a/include/openPMD/RegionCalculus2.hpp b/include/openPMD/RegionCalculus2.hpp deleted file mode 100644 index 601895d31d..0000000000 --- a/include/openPMD/RegionCalculus2.hpp +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef REGIONCALCULUS2_HPP -#define REGIONCALCULUS2_HPP - -#include -#include -#include -#include -#include - -namespace RegionCalculus2 { - -// A D-dimensional point - -template class point { - std::array elts; - -public: - typedef T value_type; - typedef std::size_t size_type; - - constexpr point() : elts{} {} // value initialization - - point(const point &) = default; - point(point &&) = default; - point &operator=(const point &) = default; - point &operator=(point &&) = default; - - constexpr size_type size() const { return D; } - - constexpr const T &operator[](const size_type d) const { return elts[d]; } - constexpr T &operator[](const size_type d) { return elts[d]; } - - friend constexpr point operator+(const point &x, const point &y) { - point r; - for (size_type d = 0; d < D; ++d) - r.elts[d] = x.elts[d] + y.elts[d]; - return r; - } - constexpr point &operator+=(const point &x) { return *this = *this + x; } -}; - -namespace detail { - -// Abstract base helper class - -template class vpoint { -public: - typedef T value_type; - typedef std::size_t size_type; - - virtual std::unique_ptr copy() const = 0; - - virtual ~vpoint() {} - - virtual size_type size() const = 0; - - virtual const T &operator[](const size_type d) const = 0; - virtual T &operator[](const size_type d) = 0; - - virtual vpoint &operator+=(const std::unique_ptr &x) = 0; - virtual std::unique_ptr - operator+(const std::unique_ptr &x) const = 0; -}; - -// Helper class wrapping point - -template class wpoint final : public vpoint { - point p; - -public: - using typename vpoint::value_type; - using typename vpoint::size_type; - - wpoint() : p{} {} - - wpoint(const wpoint &x) = default; - wpoint(wpoint &&) = default; - wpoint &operator=(const wpoint &) = default; - wpoint &operator=(wpoint &&) = default; - - std::unique_ptr> copy() const override { - return std::make_unique(*this); - } - - ~wpoint() override {} - - constexpr size_type size() const override { return p.size(); } - - const T &operator[](const size_type d) const override { return p[d]; } - T &operator[](const size_type d) override { return p[d]; } - - vpoint &operator+=(const std::unique_ptr> &x) override { - p += dynamic_cast(*x).p; - return *this; - } - std::unique_ptr> - operator+(const std::unique_ptr> &x) const override { - auto r = std::make_unique(*this); - *r += x; - return r; - } -}; - -template -std::unique_ptr> make_vpoint(const std::size_t D) { - switch (D) { - case 0: - return std::make_unique>(); - case 1: - return std::make_unique>(); - case 2: - return std::make_unique>(); - case 3: - return std::make_unique>(); - case 4: - return std::make_unique>(); - case 5: - return std::make_unique>(); - default: - abort(); - } -} - -} // namespace detail - -// A point - -template class ndpoint { - template using vpoint = detail::vpoint; - - std::unique_ptr> p; - - ndpoint(std::unique_ptr> p) : p(std::move(p)) {} - -public: - typedef typename vpoint::value_type value_type; - typedef typename vpoint::size_type size_type; - - ndpoint() : p() {} - ndpoint(const size_type D) : p(detail::make_vpoint(D)) {} - - ndpoint(const ndpoint &x) : p(x.p ? x.p->copy() : nullptr) {} - ndpoint(ndpoint &&) = default; - ndpoint &operator=(const ndpoint &) = default; - ndpoint &operator=(ndpoint &&) = default; - - operator bool() const { return bool(p); } - - size_type size() const { return p->size(); } - - const T &operator[](const size_type d) const { return (*p)[d]; } - T &operator[](const size_type d) { return (*p)[d]; } - - ndpoint &operator+=(const ndpoint &x) { - *p += x.p; - return *this; - } - friend ndpoint operator+(const ndpoint &x, const ndpoint &y) { - return ndpoint(x) += y; - } -}; - -} // namespace RegionCalculus2 - -#endif // #ifndef REGIONCALCULUS2_HPP diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp new file mode 100644 index 0000000000..da10fd8d63 --- /dev/null +++ b/include/openPMD/regions/Helpers.hpp @@ -0,0 +1,47 @@ +#ifndef REGIONS_HELPERS_HPP +#define REGIONS_HELPERS_HPP + +#include +#include +#include + +namespace openPMD { +namespace Regions { + +namespace detail { + +// Combine hashes +template std::size_t hash_combine(std::size_t seed, const T &x) { + std::hash h; + // Taken from Boost + return seed ^ + h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2); +} + +// Convert a tuple-like type to a std::array +template +constexpr auto array_push(const Tuple &t, std::index_sequence, + const U &x) { + return std::array{std::get(t)..., T(x)}; +} + +// Append an element to a std::array +template +constexpr auto array_push(const std::array &a, const U &e) { + return array_push(a, std::make_index_sequence(), e); +} + +// Construct a std::array from a function +template +constexpr std::array construct_array(const F &f) { + if constexpr (N == 0) + return std::array(); + if constexpr (N > 0) + return array_push(construct_array(f), f(N - 1)); +} +} // namespace detail + +} // namespace Regions +} // namespace openPMD + +#endif // #ifndef REGIONS_HELPERS_HPP diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp new file mode 100644 index 0000000000..29a649606b --- /dev/null +++ b/include/openPMD/regions/NDPoint.hpp @@ -0,0 +1,776 @@ +#ifndef REGIONS_NDPOINT_HPP +#define REGIONS_NDPOINT_HPP + +#include "Helpers.hpp" +#include "Point.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openPMD { +namespace Regions { + +namespace detail { + +// Abstract base helper class + +template class VPoint { +public: + typedef T value_type; + typedef std::size_t size_type; + + virtual std::unique_ptr copy() const = 0; + + virtual ~VPoint() {} + + virtual std::unique_ptr + fmap1(const std::function &f) const = 0; + virtual std::unique_ptr + fmap1(const std::function &f, + const VPoint &x) const = 0; + virtual std::unique_ptr + fmap1(const std::function &f, + const VPoint &x, const VPoint &y) const = 0; + virtual T fold1(const std::function &op, + const T &r) const = 0; + virtual T fold1(const std::function &op, + const T &r, const VPoint &x) const = 0; + + virtual void set_from_vector(const std::vector &vec) = 0; + virtual operator std::vector() const = 0; + + virtual size_type size() const = 0; + + virtual const T &operator[](const size_type d) const = 0; + virtual T &operator[](const size_type d) = 0; + + virtual size_type ndims() const = 0; + + virtual std::unique_ptr operator+() const = 0; + virtual std::unique_ptr operator-() const = 0; + virtual std::unique_ptr operator~() const = 0; + virtual std::unique_ptr> operator!() const = 0; + + virtual std::unique_ptr operator+(const VPoint &x) const = 0; + virtual std::unique_ptr operator-(const VPoint &x) const = 0; + virtual std::unique_ptr operator*(const VPoint &x) const = 0; + virtual std::unique_ptr operator/(const VPoint &x) const = 0; + virtual std::unique_ptr operator%(const VPoint &x) const = 0; + virtual std::unique_ptr operator&(const VPoint &x) const = 0; + virtual std::unique_ptr operator|(const VPoint &x) const = 0; + virtual std::unique_ptr operator^(const VPoint &x) const = 0; + virtual std::unique_ptr> operator&&(const VPoint &x) const = 0; + virtual std::unique_ptr> operator||(const VPoint &x) const = 0; + + virtual std::unique_ptr left_plus(const T &a) const = 0; + virtual std::unique_ptr left_minus(const T &a) const = 0; + virtual std::unique_ptr left_multiplies(const T &a) const = 0; + virtual std::unique_ptr left_divides(const T &a) const = 0; + virtual std::unique_ptr left_modulus(const T &a) const = 0; + virtual std::unique_ptr left_bit_and(const T &a) const = 0; + virtual std::unique_ptr left_bit_or(const T &a) const = 0; + virtual std::unique_ptr left_bit_xor(const T &a) const = 0; + virtual std::unique_ptr> left_logical_and(const T &a) const = 0; + virtual std::unique_ptr> left_logical_or(const T &a) const = 0; + + virtual std::unique_ptr operator+(const T &b) const = 0; + virtual std::unique_ptr operator-(const T &b) const = 0; + virtual std::unique_ptr operator*(const T &b) const = 0; + virtual std::unique_ptr operator/(const T &b) const = 0; + virtual std::unique_ptr operator%(const T &b) const = 0; + virtual std::unique_ptr operator&(const T &b) const = 0; + virtual std::unique_ptr operator|(const T &b) const = 0; + virtual std::unique_ptr operator^(const T &b) const = 0; + virtual std::unique_ptr> operator&&(const T &b) const = 0; + virtual std::unique_ptr> operator||(const T &b) const = 0; + + virtual VPoint &operator+=(const VPoint &x) = 0; + virtual VPoint &operator-=(const VPoint &x) = 0; + virtual VPoint &operator*=(const VPoint &x) = 0; + virtual VPoint &operator/=(const VPoint &x) = 0; + virtual VPoint &operator%=(const VPoint &x) = 0; + virtual VPoint &operator&=(const VPoint &x) = 0; + virtual VPoint &operator|=(const VPoint &x) = 0; + virtual VPoint &operator^=(const VPoint &x) = 0; + + virtual VPoint &operator+=(const T &b) = 0; + virtual VPoint &operator-=(const T &b) = 0; + virtual VPoint &operator*=(const T &b) = 0; + virtual VPoint &operator/=(const T &b) = 0; + virtual VPoint &operator%=(const T &b) = 0; + virtual VPoint &operator&=(const T &b) = 0; + virtual VPoint &operator|=(const T &b) = 0; + virtual VPoint &operator^=(const T &b) = 0; + + virtual std::unique_ptr abs1() const = 0; + virtual std::unique_ptr fabs1() const = 0; + + virtual std::unique_ptr fmax1(const VPoint &x) const = 0; + virtual std::unique_ptr fmin1(const VPoint &x) const = 0; + virtual std::unique_ptr max1(const VPoint &x) const = 0; + virtual std::unique_ptr min1(const VPoint &x) const = 0; + + virtual std::unique_ptr fmax1(const T &b) const = 0; + virtual std::unique_ptr fmin1(const T &b) const = 0; + virtual std::unique_ptr max1(const T &b) const = 0; + virtual std::unique_ptr min1(const T &b) const = 0; + + virtual T all1() const = 0; + virtual T any1() const = 0; + virtual T max_element1() const = 0; + virtual T min_element1() const = 0; + virtual T product1() const = 0; + virtual T sum1() const = 0; + + virtual bool operator==(const VPoint &x) const = 0; + virtual bool operator!=(const VPoint &x) const = 0; + + virtual std::ostream &output(std::ostream &os) const = 0; +}; + +// Helper class wrapping point + +template class WPoint final : public VPoint { + Point p; + +public: + using typename VPoint::value_type; + using typename VPoint::size_type; + + WPoint() : p{} {} + + WPoint(const WPoint &x) = default; + WPoint(WPoint &&) = default; + WPoint &operator=(const WPoint &) = default; + WPoint &operator=(WPoint &&) = default; + + WPoint(const Point &p) : p(p) {} + WPoint(Point &&p) : p(std::move(p)) {} + + std::unique_ptr> copy() const override { + return std::make_unique(*this); + } + + ~WPoint() override {} + + std::unique_ptr> + fmap1(const std::function &f) const override { + return std::make_unique(fmap(f, p)); + } + std::unique_ptr> + fmap1(const std::function &f, + const VPoint &x) const override { + return std::make_unique( + fmap(f, p, dynamic_cast(x).p)); + } + std::unique_ptr> + fmap1(const std::function &f, + const VPoint &x, const VPoint &y) const override { + return std::make_unique(fmap(f, p, + dynamic_cast(x).p, + dynamic_cast(y).p)); + } + T fold1(const std::function &op, + const T &r) const override { + return fold(op, r, p); + } + T fold1(const std::function &op, + const T &r, const VPoint &x) const override { + return fold(op, r, p, dynamic_cast(x).p); + } + + void set_from_vector(const std::vector &vec) override { *this = WPoint(vec); } + operator std::vector() const override { return std::vector(p); } + + constexpr size_type size() const override { return p.size(); } + + const T &operator[](const size_type d) const override { return p[d]; } + T &operator[](const size_type d) override { return p[d]; } + + constexpr size_type ndims() const override { return p.ndims(); } + + std::unique_ptr> operator+() const override { + return std::make_unique(+p); + } + std::unique_ptr> operator-() const override { + return std::make_unique(-p); + } + std::unique_ptr> operator~() const override { + return std::make_unique(~p); + } + std::unique_ptr> operator!() const override { + return std::make_unique>(!p); + } + + std::unique_ptr> operator+(const VPoint &x) const override { + return std::make_unique(p + dynamic_cast(x).p); + } + std::unique_ptr> operator-(const VPoint &x) const override { + return std::make_unique(p - dynamic_cast(x).p); + } + std::unique_ptr> operator*(const VPoint &x) const override { + return std::make_unique(p * dynamic_cast(x).p); + } + std::unique_ptr> operator/(const VPoint &x) const override { + return std::make_unique(p / dynamic_cast(x).p); + } + std::unique_ptr> operator%(const VPoint &x) const override { + return std::make_unique(p % dynamic_cast(x).p); + } + std::unique_ptr> operator&(const VPoint &x) const override { + return std::make_unique(p & dynamic_cast(x).p); + } + std::unique_ptr> operator|(const VPoint &x) const override { + return std::make_unique(p | dynamic_cast(x).p); + } + std::unique_ptr> operator^(const VPoint &x) const override { + return std::make_unique(p ^ dynamic_cast(x).p); + } + std::unique_ptr> operator&&(const VPoint &x) const override { + return std::make_unique>(p && + dynamic_cast(x).p); + } + std::unique_ptr> operator||(const VPoint &x) const override { + return std::make_unique>(p || + dynamic_cast(x).p); + } + + std::unique_ptr> left_plus(const T &a) const override { + return std::make_unique(a + p); + } + std::unique_ptr> left_minus(const T &a) const override { + return std::make_unique(a - p); + } + std::unique_ptr> left_multiplies(const T &a) const override { + return std::make_unique(a * p); + } + std::unique_ptr> left_divides(const T &a) const override { + return std::make_unique(a / p); + } + std::unique_ptr> left_modulus(const T &a) const override { + return std::make_unique(a % p); + } + std::unique_ptr> left_bit_and(const T &a) const override { + return std::make_unique(a & p); + } + std::unique_ptr> left_bit_or(const T &a) const override { + return std::make_unique(a | p); + } + std::unique_ptr> left_bit_xor(const T &a) const override { + return std::make_unique(a ^ p); + } + std::unique_ptr> left_logical_and(const T &a) const override { + return std::make_unique>(a && p); + } + std::unique_ptr> left_logical_or(const T &a) const override { + return std::make_unique>(a || p); + } + + std::unique_ptr> operator+(const T &b) const override { + return std::make_unique(p + b); + } + std::unique_ptr> operator-(const T &b) const override { + return std::make_unique(p - b); + } + std::unique_ptr> operator*(const T &b) const override { + return std::make_unique(p * b); + } + std::unique_ptr> operator/(const T &b) const override { + return std::make_unique(p / b); + } + std::unique_ptr> operator%(const T &b) const override { + return std::make_unique(p % b); + } + std::unique_ptr> operator&(const T &b) const override { + return std::make_unique(p & b); + } + std::unique_ptr> operator|(const T &b) const override { + return std::make_unique(p | b); + } + std::unique_ptr> operator^(const T &b) const override { + return std::make_unique(p ^ b); + } + std::unique_ptr> operator&&(const T &b) const override { + return std::make_unique>(p && b); + } + std::unique_ptr> operator||(const T &b) const override { + return std::make_unique>(p || b); + } + + VPoint &operator+=(const VPoint &x) override { + p += dynamic_cast(x).p; + return *this; + } + VPoint &operator-=(const VPoint &x) override { + p -= dynamic_cast(x).p; + return *this; + } + VPoint &operator*=(const VPoint &x) override { + p *= dynamic_cast(x).p; + return *this; + } + VPoint &operator/=(const VPoint &x) override { + p /= dynamic_cast(x).p; + return *this; + } + VPoint &operator%=(const VPoint &x) override { + p %= dynamic_cast(x).p; + return *this; + } + VPoint &operator&=(const VPoint &x) override { + p &= dynamic_cast(x).p; + return *this; + } + VPoint &operator|=(const VPoint &x) override { + p |= dynamic_cast(x).p; + return *this; + } + VPoint &operator^=(const VPoint &x) override { + p ^= dynamic_cast(x).p; + return *this; + } + + VPoint &operator+=(const T &b) override { + p += b; + return *this; + } + VPoint &operator-=(const T &b) override { + p -= b; + return *this; + } + VPoint &operator*=(const T &b) override { + p *= b; + return *this; + } + VPoint &operator/=(const T &b) override { + p /= b; + return *this; + } + VPoint &operator%=(const T &b) override { + p %= b; + return *this; + } + VPoint &operator&=(const T &b) override { + p &= b; + return *this; + } + VPoint &operator|=(const T &b) override { + p |= b; + return *this; + } + VPoint &operator^=(const T &b) override { + p ^= b; + return *this; + } + + std::unique_ptr> abs1() const override { + return std::make_unique(abs(p)); + } + std::unique_ptr> fabs1() const override { + return std::make_unique(fabs(p)); + } + + std::unique_ptr> fmax1(const VPoint &x) const override { + return std::make_unique(fmax(p, dynamic_cast(x).p)); + } + std::unique_ptr> fmin1(const VPoint &x) const override { + return std::make_unique(fmin(p, dynamic_cast(x).p)); + } + std::unique_ptr> max1(const VPoint &x) const override { + return std::make_unique(max(p, dynamic_cast(x).p)); + } + std::unique_ptr> min1(const VPoint &x) const override { + return std::make_unique(min(p, dynamic_cast(x).p)); + } + + std::unique_ptr> fmax1(const T &b) const override { + return std::make_unique(fmax(p, b)); + } + std::unique_ptr> fmin1(const T &b) const override { + return std::make_unique(fmin(p, b)); + } + std::unique_ptr> max1(const T &b) const override { + return std::make_unique(max(p, b)); + } + std::unique_ptr> min1(const T &b) const override { + return std::make_unique(min(p, b)); + } + + T all1() const override { return all(p); } + T any1() const override { return any(p); } + T max_element1() const override { return max_element(p); } + T min_element1() const override { return min_element(p); } + T product1() const override { return product(p); } + T sum1() const override { return sum(p); } + + bool operator==(const VPoint &x) const override { + return p == dynamic_cast(x).p; + } + bool operator!=(const VPoint &x) const override { + return p != dynamic_cast(x).p; + } + + std::ostream &output(std::ostream &os) const override { return os << p; } +}; + +template +std::unique_ptr> make_VPoint(const std::size_t D) { + switch (D) { + case 0: + return std::make_unique>(); + case 1: + return std::make_unique>(); + case 2: + return std::make_unique>(); + case 3: + return std::make_unique>(); + case 4: + return std::make_unique>(); + case 5: + return std::make_unique>(); + default: + abort(); + } +} + +} // namespace detail + +/** A Point + * + * The dimension (number of component) of the Point is only known at + * run-time. @see Point + * + * Points can represent either Points or distances. Points are + * fixed-size vectors that support arithmetic operations. + */ +template class NDPoint { + + template using VPoint = detail::VPoint; + + template friend class NDPoint; + + std::unique_ptr> p; + + NDPoint(std::unique_ptr> p) : p(std::move(p)) {} + +public: + /** Component type + */ + typedef typename VPoint::value_type value_type; + /** Return type of Point::size() + */ + typedef typename VPoint::size_type size_type; + + /** Create an invalid Point + */ + NDPoint() : p() {} + /** Create a value-initialized Point with D components + */ + NDPoint(const size_type D) : p(detail::make_VPoint(D)) {} + + NDPoint(const NDPoint &x) : p(x.p ? x.p->copy() : nullptr) {} + NDPoint(NDPoint &&) = default; + NDPoint &operator=(const NDPoint &x) { + p = x.p ? x.p->copy() : nullptr; + return *this; + } + NDPoint &operator=(NDPoint &&) = default; + +private: + template + static std::vector convert_vector(const std::vector &vec) { + std::vector res(vec.size()); + for (std::size_t n = 0; n < res.size(); ++n) + res[n] = T(vec[n]); + return res; + } + +public: + template + NDPoint(const NDPoint &x) + : p(x.p ? NDPoint(convert_vector(std::vector(x))) : nullptr) {} + + /** Check whether a Point is valid + * + * A valid Point knows its number of dimensions, and its components + * are initialized. An invalid Point does not know its number of + * dimensions and holds no data, similar to a null Pointer. + * + * Most other member functions must not be called for invalid + * Points. + */ + bool has_value() const { return bool(p); } + + template friend NDPoint fmap(const F &f, const NDPoint &x) { + return NDPoint(x.p->fmap1(f)); + } + template + friend NDPoint fmap(const F &f, const NDPoint &x, const NDPoint &y) { + return NDPoint(x.p->fmap1(f, *y.p)); + } + template + friend NDPoint fmap(const F &f, const NDPoint &x, const NDPoint &y, + const NDPoint &z) { + return NDPoint(x.p->fmap1(f, *y.p, *z.p)); + } + template + friend T fold(const Op &op, const T &r, const NDPoint &x) { + return x.p->fold1(op, r); + } + template + friend T fold(const Op &op, const T &r, const NDPoint &x, const NDPoint &y) { + return x.p->fold1(op, r, *y.p); + } + + NDPoint(const std::vector &vec) : NDPoint(vec.size()) { + p->set_from_vector(vec); + } + operator std::vector() const { return std::vector(p); } + + /** Number of comopnents (same as number of dimensions) + */ + size_type size() const { return p->size(); } + + /** Get a component of a Point + */ + const T &operator[](const size_type d) const { return (*p)[d]; } + /** Get a component of a Point + */ + T &operator[](const size_type d) { return (*p)[d]; } + + /** Number of dimensions (same as number of comopnents) + */ + size_type ndims() const { return p->ndims(); } + + friend NDPoint operator+(const NDPoint &x) { return NDPoint(+*x.p); } + friend NDPoint operator-(const NDPoint &x) { return NDPoint(-*x.p); } + friend NDPoint operator~(const NDPoint &x) { return NDPoint(~*x.p); } + friend NDPoint operator!(const NDPoint &x) { + return NDPoint(!*x.p); + } + + friend NDPoint operator+(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p + *y.p); + } + friend NDPoint operator-(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p - *y.p); + } + friend NDPoint operator*(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p * *y.p); + } + friend NDPoint operator/(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p / *y.p); + } + friend NDPoint operator%(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p % *y.p); + } + friend NDPoint operator&(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p & *y.p); + } + friend NDPoint operator|(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p | *y.p); + } + friend NDPoint operator^(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p ^ *y.p); + } + friend NDPoint operator&&(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p && *y.p); + } + friend NDPoint operator||(const NDPoint &x, const NDPoint &y) { + return NDPoint(*x.p || *y.p); + } + + friend NDPoint operator+(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_plus(a)); + } + friend NDPoint operator-(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_minus(a)); + } + friend NDPoint operator*(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_multiplies(a)); + } + friend NDPoint operator/(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_divides(a)); + } + friend NDPoint operator%(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_modulus(a)); + } + friend NDPoint operator&(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_bit_and(a)); + } + friend NDPoint operator|(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_bit_or(a)); + } + friend NDPoint operator^(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_bit_xor(a)); + } + friend NDPoint operator&&(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_logical_and(a)); + } + friend NDPoint operator||(const T &a, const NDPoint &y) { + return NDPoint(y.p->left_logical_or(a)); + } + + friend NDPoint operator+(const NDPoint &x, const T &b) { + return NDPoint(*x.p + b); + } + friend NDPoint operator-(const NDPoint &x, const T &b) { + return NDPoint(*x.p - b); + } + friend NDPoint operator*(const NDPoint &x, const T &b) { + return NDPoint(*x.p * b); + } + friend NDPoint operator/(const NDPoint &x, const T &b) { + return NDPoint(*x.p / b); + } + friend NDPoint operator%(const NDPoint &x, const T &b) { + return NDPoint(*x.p % b); + } + friend NDPoint operator&(const NDPoint &x, const T &b) { + return NDPoint(*x.p & b); + } + friend NDPoint operator|(const NDPoint &x, const T &b) { + return NDPoint(*x.p | b); + } + friend NDPoint operator^(const NDPoint &x, const T &b) { + return NDPoint(*x.p ^ b); + } + friend NDPoint operator&&(const NDPoint &x, const T &b) { + return NDPoint(*x.p && b); + } + friend NDPoint operator||(const NDPoint &x, const T &b) { + return NDPoint(*x.p || b); + } + + NDPoint &operator+=(const NDPoint &x) { + *p += *x.p; + return *this; + } + NDPoint &operator-=(const NDPoint &x) { + *p -= *x.p; + return *this; + } + NDPoint &operator*=(const NDPoint &x) { + *p *= *x.p; + return *this; + } + NDPoint &operator/=(const NDPoint &x) { + *p /= *x.p; + return *this; + } + NDPoint &operator%=(const NDPoint &x) { + *p %= *x.p; + return *this; + } + NDPoint &operator&=(const NDPoint &x) { + *p &= *x.p; + return *this; + } + NDPoint &operator|=(const NDPoint &x) { + *p |= *x.p; + return *this; + } + NDPoint &operator^=(const NDPoint &x) { + *p ^= *x.p; + return *this; + } + + NDPoint &operator+=(const T &a) { + *p += a; + return *this; + } + NDPoint &operator-=(const T &a) { + *p -= a; + return *this; + } + NDPoint &operator*=(const T &a) { + *p *= a; + return *this; + } + NDPoint &operator/=(const T &a) { + *p /= a; + return *this; + } + NDPoint &operator%=(const T &a) { + *p %= a; + return *this; + } + NDPoint &operator&=(const T &a) { + *p &= a; + return *this; + } + NDPoint &operator|=(const T &a) { + *p |= a; + return *this; + } + NDPoint &operator^=(const T &a) { + *p ^= a; + return *this; + } + + friend NDPoint abs(const NDPoint &x) { return x.p->abs1(); } + friend NDPoint fabs(const NDPoint &x) { return x.p->fabs1(); } + + friend NDPoint fmax(const NDPoint &x, const NDPoint &y) { + return x.p->fmax1(*y.p); + } + friend NDPoint fmin(const NDPoint &x, const NDPoint &y) { + return x.p->fmin1(*y.p); + } + friend NDPoint max(const NDPoint &x, const NDPoint &y) { + return x.p->max1(*y.p); + } + friend NDPoint min(const NDPoint &x, const NDPoint &y) { + return x.p->min1(*y.p); + } + + friend NDPoint fmax(const T &a, const NDPoint &y) { return y.p->fmax1(a); } + friend NDPoint fmin(const T &a, const NDPoint &y) { return y.p->fmin1(a); } + friend NDPoint max(const T &a, const NDPoint &y) { return y.p->max1(a); } + friend NDPoint min(const T &a, const NDPoint &y) { return y.p->min1(a); } + + friend NDPoint fmax(const NDPoint &x, const T &b) { return x.p->fmax1(b); } + friend NDPoint fmin(const NDPoint &x, const T &b) { return x.p->fmin1(b); } + friend NDPoint max(const NDPoint &x, const T &b) { return x.p->max1(b); } + friend NDPoint min(const NDPoint &x, const T &b) { return x.p->min1(b); } + + friend T all(const NDPoint &x) { return x.p->all1(); } + friend T any(const NDPoint &x) { return x.p->any1(); } + friend T max_element(const NDPoint &x) { return x.p->max_element1(); } + friend T min_element(const NDPoint &x) { return x.p->min_element1(); } + friend T product(const NDPoint &x) { return x.p->product1(); } + friend T sum(const NDPoint &x) { return x.p->sum1(); } + + friend bool operator==(const NDPoint &x, const NDPoint &y) { + return *x.p == *y.p; + } + friend bool operator!=(const NDPoint &x, const NDPoint &y) { + return *x.p != *y.p; + } + + /** Output a point + */ + friend std::ostream &operator<<(std::ostream &os, const NDPoint &x) { + if (x.p) + x.p->output(os); + else + os << "[INVALID]"; + return os; + } +}; + +} // namespace Regions +} // namespace openPMD + +#endif // #ifndef REGIONS_NDPOINT_HPP diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp new file mode 100644 index 0000000000..61e69ccb4b --- /dev/null +++ b/include/openPMD/regions/Point.hpp @@ -0,0 +1,494 @@ +#ifndef REGIONS_POINT_HPP +#define REGIONS_POINT_HPP + +#include "Helpers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openPMD { +namespace Regions { + +/** A D-dimensional point + * + * The dimension D needs to be known at compile time. @see NDPoint + * + * Points can represent either points or distances. Points are + * fixed-size vectors that support arithmetic operations. + */ +template class Point { + std::array elts; + +public: + /** Component type + */ + typedef T value_type; + /** Return type of Point::size() + */ + typedef std::size_t size_type; + + /** Create a value-initialized Point + * + * For most types, this initializes all components to zero. + */ + constexpr Point() : elts{} {} // value initialization + + Point(const Point &) = default; + Point(Point &&) = default; + Point &operator=(const Point &) = default; + Point &operator=(Point &&) = default; + + /** Loop over the natural number sequence [0, ..., D-1], and evaluate f for + * each number + */ + template static constexpr void loop(const F &f) { + for (size_type d = 0; d < D; ++d) + f(d); + } + /** Create a new Point by applying a function to the natural number sequence + * [0, ..., D-1] + */ + template static constexpr Point make(const F &f) { + return detail::construct_array(f); + } + + /** Create a point with each component set to the same value a + */ + static constexpr Point pure(const T &a) { + return make([&](size_type) { return a; }); + } + + /** Create a unit Point, where component dir is one, and all other components + * are zero + */ + static constexpr Point unit(size_type dir) { + return make([&](size_type d) { return d == dir; }); + } + + /** Create a point with components set to the natural number + * sequence [0, ..., D-1] + */ + static constexpr Point iota() { + return Point::make([](size_type d) { return d; }); + } + + /** Map a function over all components of one or several Points + * + * Example: + * Point pi, pj; + * Point pk = fmap([](auto i, auto j) { return i+j; }, pi, pj); + * This calculates the component-wise sum of pi and pj, i.e. pi + pj . + */ + template >>> + friend constexpr Point fmap(const F &f, const Point &x, + const Point &...args) { + return Point::make( + [&](size_type d) { return f(x.elts[d], args.elts[d]...); }); + } + /** Map a function over all components of one or several Points, returning + * void + * + * Example: + * Point pi; + * fmap_([](auto& i) { return i*=2; }, pi); + * This doubles each component of pi, the same as pi *= 2 . + */ + template + friend constexpr void fmap_(const F &f, const Point &x, + const Point &...args) { + loop([&](size_type d) { f(x.elts[d], args.elts[d]...); }); + } + + /** Reduce over all components of one or several Points + * + * Example: + * Point pi; + * int s = fold([](auto r, auto i) { return r+i; }, 0, pi); + * This calculates the sum of all components ("horizonal sum") of pi, + * same as sum(pi). + */ + template >>> + friend constexpr R fold(const Op &op, R r, const Point &x, + const Point &...args) { + loop([&](int d) { r = op(r, x[d], args[d]...); }); + return r; + } + + /** Create a point from a point with different component type + */ + template + constexpr Point(const Point &x) + : elts(fmap([](const U &a) { return T(a); }, x)) {} + + /** Create a point from Pointers to first and one past the last element + */ + constexpr Point(const T *begin, const T *end) + : elts((assert(begin + D == end), + make([&](size_type d) { return begin[d]; }))) {} + /** Create a point from an initializer list + * + * Example: Point{1,2,3} + */ + constexpr Point(std::initializer_list lst) + : Point(lst.begin(), lst.end()) {} + /** Create a point from a C-style array + */ + constexpr Point(const T (&arr)[D]) : elts(&arr[0], &arr[D]) {} + /** Create a point from a std::array + */ + constexpr Point(const std::array &arr) : elts(arr) {} + /** Create a point from a std::vector + */ + template > * = nullptr> + constexpr Point(const std::vector &vec) + : Point(&*vec.begin(), &*vec.end()) {} + constexpr Point(const std::vector &vec) + : Point((assert(vec.size() == D), + make([&](size_type d) { return vec[d]; }))) {} + + /** Convert a point to a std::array + */ + operator std::array() const { return elts; } + /** Convert a point to a std::vector + */ + explicit operator std::vector() const { + return std::vector(elts.begin(), elts.end()); + } + + /** Number of components (same as number of dimensions) + */ + constexpr size_type size() const { return D; } + + /** Get a component of a point + */ + constexpr const T &operator[](const size_type d) const { return elts[d]; } + /** Get a component of a point + */ + constexpr T &operator[](const size_type d) { return elts[d]; } + + /** Number of dimensions (same as number of components) + */ + constexpr size_type ndims() const { return D; } + + friend constexpr Point operator+(const Point &x) { + return fmap([](const T &a) { return +a; }, x); + } + friend constexpr Point operator-(const Point &x) { + return fmap([](const T &a) { return -a; }, x); + } + friend constexpr Point operator~(const Point &x) { + if constexpr (std::is_same_v) + return fmap([](const T &a) { return true; }, x); + else if constexpr (std::is_integral_v) + return fmap([](const T &a) { return ~a; }, x); + std::abort(); + } + friend constexpr Point operator!(const Point &x) { + return fmap([](const T &a) { return !a; }, x); + } + + friend constexpr Point operator+(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a + b; }, x, y); + } + friend constexpr Point operator-(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a - b; }, x, y); + } + friend constexpr Point operator*(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a * b; }, x, y); + } + friend constexpr Point operator/(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a / b; }, x, y); + } + friend constexpr Point operator%(const Point &x, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([](const T &a, const T &b) { return a % b; }, x, y); + std::abort(); + } + friend constexpr Point operator&(const Point &x, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([](const T &a, const T &b) { return a & b; }, x, y); + std::abort(); + } + friend constexpr Point operator|(const Point &x, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([](const T &a, const T &b) { return a | b; }, x, y); + std::abort(); + } + friend constexpr Point operator^(const Point &x, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([](const T &a, const T &b) { return a ^ b; }, x, y); + std::abort(); + } + friend constexpr Point operator&&(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a && b; }, x, y); + } + friend constexpr Point operator||(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a || b; }, x, y); + } + + friend constexpr Point operator+(const T &a, const Point &y) { + return fmap([&](const T &b) { return a + b; }, y); + } + friend constexpr Point operator-(const T &a, const Point &y) { + return fmap([&](const T &b) { return a - b; }, y); + } + friend constexpr Point operator*(const T &a, const Point &y) { + return fmap([&](const T &b) { return a * b; }, y); + } + friend constexpr Point operator/(const T &a, const Point &y) { + return fmap([&](const T &b) { return a / b; }, y); + } + friend constexpr Point operator%(const T &a, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([&](const T &b) { return a % b; }, y); + std::abort(); + } + friend constexpr Point operator&(const T &a, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([&](const T &b) { return a & b; }, y); + std::abort(); + } + friend constexpr Point operator|(const T &a, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([&](const T &b) { return a | b; }, y); + std::abort(); + } + friend constexpr Point operator^(const T &a, const Point &y) { + if constexpr (std::is_integral_v) + return fmap([&](const T &b) { return a ^ b; }, y); + std::abort(); + } + friend constexpr Point operator&&(const T &a, const Point &y) { + return fmap([&](const T &b) { return a && b; }, y); + } + friend constexpr Point operator||(const T &a, const Point &y) { + return fmap([&](const T &b) { return a || b; }, y); + } + + friend constexpr Point operator+(const Point &x, const T &b) { + return fmap([&](const T &a) { return a + b; }, x); + } + friend constexpr Point operator-(const Point &x, const T &b) { + return fmap([&](const T &a) { return a - b; }, x); + } + friend constexpr Point operator*(const Point &x, const T &b) { + return fmap([&](const T &a) { return a * b; }, x); + } + friend constexpr Point operator/(const Point &x, const T &b) { + return fmap([&](const T &a) { return a / b; }, x); + } + friend constexpr Point operator%(const Point &x, const T &b) { + if constexpr (std::is_integral_v) + return fmap([&](const T &a) { return a % b; }, x); + std::abort(); + } + friend constexpr Point operator&(const Point &x, const T &b) { + if constexpr (std::is_integral_v) + return fmap([&](const T &a) { return a & b; }, x); + std::abort(); + } + friend constexpr Point operator|(const Point &x, const T &b) { + if constexpr (std::is_integral_v) + return fmap([&](const T &a) { return a | b; }, x); + std::abort(); + } + friend constexpr Point operator^(const Point &x, const T &b) { + if constexpr (std::is_integral_v) + return fmap([&](const T &a) { return a ^ b; }, x); + std::abort(); + } + friend constexpr Point operator&&(const Point &x, const T &b) { + return fmap([&](const T &a) { return a && b; }, x); + } + friend constexpr Point operator||(const Point &x, const T &b) { + return fmap([&](const T &a) { return a || b; }, x); + } + + constexpr Point &operator+=(const Point &x) { return *this = *this + x; } + constexpr Point &operator-=(const Point &x) { return *this = *this - x; } + constexpr Point &operator*=(const Point &x) { return *this = *this * x; } + constexpr Point &operator/=(const Point &x) { return *this = *this / x; } + constexpr Point &operator%=(const Point &x) { return *this = *this % x; } + constexpr Point &operator&=(const Point &x) { return *this = *this & x; } + constexpr Point &operator|=(const Point &x) { return *this = *this | x; } + constexpr Point &operator^=(const Point &x) { return *this = *this ^ x; } + + constexpr Point &operator+=(const T &a) { return *this = *this + a; } + constexpr Point &operator-=(const T &a) { return *this = *this - a; } + constexpr Point &operator*=(const T &a) { return *this = *this * a; } + constexpr Point &operator/=(const T &a) { return *this = *this / a; } + constexpr Point &operator%=(const T &a) { return *this = *this % a; } + constexpr Point &operator&=(const T &a) { return *this = *this & a; } + constexpr Point &operator|=(const T &a) { return *this = *this | a; } + constexpr Point &operator^=(const T &a) { return *this = *this ^ a; } + + friend constexpr Point abs(const Point &x) { + using std::abs; + return fmap( + [&](const T &a) { + if constexpr (std::is_same_v) + return a; + else + return abs(a); + }, + x); + } + friend constexpr Point fabs(const Point &x) { + using std::fabs; + return fmap([&](const T &a) { return fabs(a); }, x); + } + + friend constexpr Point fmax(const Point &x, const Point &y) { + using std::fmax; + return fmap([](const T &a, const T &b) { return fmax(a, b); }, x, y); + } + friend constexpr Point fmin(const Point &x, const Point &y) { + using std::fmin; + return fmap([](const T &a, const T &b) { return fmin(a, b); }, x, y); + } + friend constexpr Point max(const Point &x, const Point &y) { + using std::max; + return fmap([](const T &a, const T &b) { return max(a, b); }, x, y); + } + friend constexpr Point min(const Point &x, const Point &y) { + using std::min; + return fmap([](const T &a, const T &b) { return min(a, b); }, x, y); + } + + friend constexpr Point fmax(const T &a, const Point &y) { + using std::fmax; + return fmap([&](const T &b) { return fmax(a, b); }, y); + } + friend constexpr Point fmin(const T &a, const Point &y) { + using std::fmin; + return fmap([&](const T &b) { return fmin(a, b); }, y); + } + friend constexpr Point max(const T &a, const Point &y) { + using std::max; + return fmap([&](const T &b) { return max(a, b); }, y); + } + friend constexpr Point min(const T &a, const Point &y) { + using std::min; + return fmap([&](const T &b) { return min(a, b); }, y); + } + + friend constexpr Point fmax(const Point &x, const T &b) { + using std::fmax; + return fmap([&](const T &a) { return fmax(a, b); }, x); + } + friend constexpr Point fmin(const Point &x, const T &b) { + using std::fmin; + return fmap([&](const T &a) { return fmin(a, b); }, x); + } + friend constexpr Point max(const Point &x, const T &b) { + using std::max; + return fmap([&](const T &a) { return max(a, b); }, x); + } + friend constexpr Point min(const Point &x, const T &b) { + using std::min; + return fmap([&](const T &a) { return min(a, b); }, x); + } + + friend constexpr bool all(const Point &x) { + return fold([](bool r, const T &a) { return r && a; }, true, x); + } + friend constexpr bool any(const Point &x) { + return fold([](bool r, const T &a) { return r || a; }, false, x); + } + friend constexpr T max_element(const Point &x) { + using std::max; + const T neutral = std::is_floating_point_v + ? -std::numeric_limits::infinity() + : std::numeric_limits::lowest(); + return fold([](const T &r, const T &a) { return max(r, a); }, neutral, x); + } + friend constexpr T min_element(const Point &x) { + using std::min; + const T neutral = std::is_floating_point_v + ? std::numeric_limits::infinity() + : std::numeric_limits::max(); + return fold([](const T &r, const T &a) { return min(r, a); }, neutral, x); + } + friend constexpr T product(const Point &x) { + return fold([](const T &r, const T &a) { return r * a; }, T(1), x); + } + friend constexpr T sum(const Point &x) { + return fold([](const T &r, const T &a) { return r + a; }, T(0), x); + } + + friend constexpr bool operator==(const Point &x, const Point &y) { + return all(fmap([](const T &a, const T &b) { return a == b; }, x, y)); + } + friend constexpr bool operator!=(const Point &x, const Point &y) { + return any(fmap([](const T &a, const T &b) { return a != b; }, x, y)); + } + + /** Output a point + */ + friend std::ostream &operator<<(std::ostream &os, const Point &x) { + os << "["; + for (size_type d = 0; d < D; ++d) { + if (d != 0) + os << ","; + os << x[d]; + } + os << "]"; + return os; + } +}; + +} // namespace Regions +} // namespace openPMD + +namespace std { + +template +struct equal_to> { + constexpr bool operator()(const openPMD::Regions::Point &p, + const openPMD::Regions::Point &q) const { + return openPMD::Regions::Point::all( + openPMD::Regions::Point::fmap( + [](const T &a, const T &b) { return equal_to()(a, b); }, p, q)); + } +}; + +template +struct hash> { + constexpr size_t operator()(const openPMD::Regions::Point &p) const { + return openPMD::Regions::Point::fold( + [](size_t r, const T &b) { + return openPMD::Regions::detail::hash_combine(r, b); + }, + size_t(0xb22da17173243869ULL), p); + } +}; + +template +struct less> { + constexpr bool operator()(const openPMD::Regions::Point &p, + const openPMD::Regions::Point &q) const { + return openPMD::Regions::Point::fold( + [](bool r, const T &a, const T &b) { return r && less()(a, b); }, + true, p, q); + } +}; + +} // namespace std + +#endif // #ifndef REGIONS_POINT_HPP diff --git a/include/openPMD/regions/RegionCalculus.hpp b/include/openPMD/regions/RegionCalculus.hpp new file mode 100644 index 0000000000..fc93110560 --- /dev/null +++ b/include/openPMD/regions/RegionCalculus.hpp @@ -0,0 +1,1116 @@ +#ifndef REGIONCALCULUS2_HPP +#define REGIONCALCULUS2_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openPMD { +namespace RegionCalculus { + +namespace detail { +// Combine hashes +template std::size_t hash_combine(std::size_t seed, const T &x) { + std::hash h; + // Taken from Boost + return seed ^ + h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2); +} +} // namespace detail + +namespace detail { +template +constexpr auto array_push(const Tuple &t, std::index_sequence, + const U &x) { + return std::array{std::get(t)..., T(x)}; +} + +template +constexpr auto array_push(const std::array &a, const U &e) { + return array_push(a, std::make_index_sequence(), e); +} + +template +constexpr std::array construct_array(const F &f) { + if constexpr (N == 0) + return std::array(); + if constexpr (N > 0) + return array_push(construct_array(f), f(N - 1)); +} +} // namespace detail + +/** A D-dimensional point + * + * The dimension D needs to be known at compile time. @see ndpoint + * + * Points can represent either points or distances. Points are + * fixed-size vectors that support arithmetic operations. + */ +template class point { + std::array elts; + +public: + /** Component type + */ + typedef T value_type; + /** Return type of point::size() + */ + typedef std::size_t size_type; + + /** Create a value-initialized point + * + * For most types, this initializes all components to zero. + */ + constexpr point() : elts{} {} // value initialization + + point(const point &) = default; + point(point &&) = default; + point &operator=(const point &) = default; + point &operator=(point &&) = default; + + /** Loop over the natural number sequence [0, ..., D-1], and evaluate f for + * each number + */ + template static constexpr void loop(const F &f) { + for (size_type d = 0; d < D; ++d) + f(d); + } + /** Create a new point by applying a function to the natural number sequence + * [0, ..., D-1] + */ + template static constexpr point make(const F &f) { + return detail::construct_array(f); + } + + /** Create a point with each component set to the same value a + */ + static constexpr point pure(const T &a) { + return make([&](size_type) { return a; }); + } + + /** Create a unit point, where component dir is one, and all other components + * are zero + */ + static constexpr point unit(size_type dir) { + return make([&](size_type d) { return d == dir; }); + } + + /** Create a point with components set to the natural number + * sequence [0, ..., D-1] + */ + static constexpr point iota() { + return point::make([](size_type d) { return d; }); + } + + /** Map a function over all components of one or several points + * + * Example: + * point pi, pj; + * point pk = fmap([](auto i, auto j) { return i+j; }, pi, pj); + * This calculates the component-wise sum of pi and pj, i.e. pi + pj . + */ + template >>> + friend constexpr point fmap(const F &f, const point &x, + const point &...args) { + return point::make( + [&](size_type d) { return f(x.elts[d], args.elts[d]...); }); + } + /** Map a function over all components of one or several points, returning + * void + * + * Example: + * point pi; + * fmap_([](auto& i) { return i*=2; }, pi); + * This doubles each component of pi, the same as pi *= 2 . + */ + template + friend constexpr void fmap_(const F &f, const point &x, + const point &...args) { + loop([&](size_type d) { f(x.elts[d], args.elts[d]...); }); + } + + /** Reduce over all components of one or several points + * + * Example: + * point pi; + * int s = fold([](auto r, auto i) { return r+i; }, 0, pi); + * This calculates the sum of all components ("horizonal sum") of pi, + * same as sum(pi). + */ + template >>> + friend constexpr R fold(const Op &op, R r, const point &x, + const point &...args) { + loop([&](int d) { r = op(r, x[d], args[d]...); }); + return r; + } + + /** Create a point from a point with different component type + */ + template + constexpr point(const point &x) + : elts(fmap([](const U &a) { return T(a); }, x)) {} + + /** Create a point from pointers to first and one past the last element + */ + constexpr point(const T *begin, const T *end) + : elts((assert(begin + D == end), + make([&](size_type d) { return begin[d]; }))) {} + /** Create a point from an initializer list + * + * Example: point{1,2,3} + */ + constexpr point(std::initializer_list lst) + : point(lst.begin(), lst.end()) {} + /** Create a point from a C-style array + */ + constexpr point(const T (&arr)[D]) : elts(&arr[0], &arr[D]) {} + /** Create a point from a std::array + */ + constexpr point(const std::array &arr) : elts(arr) {} + /** Create a point from a std::vector + */ + constexpr point(const std::vector &vec) : point(vec.begin(), vec.end()) {} + + /** Convert a point to a std::array + */ + operator std::array() const { return elts; } + /** Convert a point to a std::vector + */ + explicit operator std::vector() const { + return std::vector(elts.begin(), elts.end()); + } + + /** Number of components (same as number of dimensions) + */ + constexpr size_type size() const { return D; } + + /** Get a component of a point + */ + constexpr const T &operator[](const size_type d) const { return elts[d]; } + /** Get a component of a point + */ + constexpr T &operator[](const size_type d) { return elts[d]; } + + /** Number of dimensions (same as number of components) + */ + constexpr size_type ndims() const { return D; } + + friend constexpr point operator+(const point &x) { + return fmap([](const T &a) { return +a; }, x); + } + friend constexpr point operator-(const point &x) { + return fmap([](const T &a) { return -a; }, x); + } + friend constexpr point operator~(const point &x) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([](const T &a) { return ~a; }, x); + else + return point(); + } + friend constexpr point operator!(const point &x) { + return fmap([](const T &a) { return !a; }, x); + } + + friend constexpr point operator+(const point &x, const point &y) { + return fmap([](const T &a, const T &b) { return a + b; }, x, y); + } + friend constexpr point operator-(const point &x, const point &y) { + return fmap([](const T &a, const T &b) { return a - b; }, x, y); + } + friend constexpr point operator*(const point &x, const point &y) { + return fmap([](const T &a, const T &b) { return a * b; }, x, y); + } + friend constexpr point operator/(const point &x, const point &y) { + return fmap([](const T &a, const T &b) { return a / b; }, x, y); + } + friend constexpr point operator%(const point &x, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([](const T &a, const T &b) { return a % b; }, x, y); + else + return point(); + } + friend constexpr point operator&(const point &x, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([](const T &a, const T &b) { return a & b; }, x, y); + else + return point(); + } + friend constexpr point operator|(const point &x, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([](const T &a, const T &b) { return a | b; }, x, y); + else + return point(); + } + friend constexpr point operator^(const point &x, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([](const T &a, const T &b) { return a ^ b; }, x, y); + else + return point(); + } + friend constexpr point operator&&(const point &x, const point &y) { + return fmap([](const T &a, const T &b) { return a && b; }, x, y); + } + friend constexpr point operator||(const point &x, const point &y) { + return fmap([](const T &a, const T &b) { return a || b; }, x, y); + } + + friend constexpr point operator+(const T &a, const point &y) { + return fmap([&](const T &b) { return a + b; }, y); + } + friend constexpr point operator-(const T &a, const point &y) { + return fmap([&](const T &b) { return a - b; }, y); + } + friend constexpr point operator*(const T &a, const point &y) { + return fmap([&](const T &b) { return a * b; }, y); + } + friend constexpr point operator/(const T &a, const point &y) { + return fmap([&](const T &b) { return a / b; }, y); + } + friend constexpr point operator%(const T &a, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &b) { return a % b; }, y); + else + return point(); + } + friend constexpr point operator&(const T &a, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &b) { return a & b; }, y); + else + return point(); + } + friend constexpr point operator|(const T &a, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &b) { return a | b; }, y); + else + return point(); + } + friend constexpr point operator^(const T &a, const point &y) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &b) { return a ^ b; }, y); + else + return point(); + } + friend constexpr point operator&&(const T &a, const point &y) { + return fmap([&](const T &b) { return a && b; }, y); + } + friend constexpr point operator||(const T &a, const point &y) { + return fmap([&](const T &b) { return a || b; }, y); + } + + friend constexpr point operator+(const point &x, const T &b) { + return fmap([&](const T &a) { return a + b; }, x); + } + friend constexpr point operator-(const point &x, const T &b) { + return fmap([&](const T &a) { return a - b; }, x); + } + friend constexpr point operator*(const point &x, const T &b) { + return fmap([&](const T &a) { return a * b; }, x); + } + friend constexpr point operator/(const point &x, const T &b) { + return fmap([&](const T &a) { return a / b; }, x); + } + friend constexpr point operator%(const point &x, const T &b) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &a) { return a % b; }, x); + else return point(); + } + friend constexpr point operator&(const point &x, const T &b) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &a) { return a & b; }, x); + else + return point(); + } + friend constexpr point operator|(const point &x, const T &b) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &a) { return a | b; }, x); + else + return point(); + } + friend constexpr point operator^(const point &x, const T &b) { + if constexpr (std::is_integral_v && !std::is_same_v) + return fmap([&](const T &a) { return a ^ b; }, x); + else + return point(); + } + friend constexpr point operator&&(const point &x, const T &b) { + return fmap([&](const T &a) { return a && b; }, x); + } + friend constexpr point operator||(const point &x, const T &b) { + return fmap([&](const T &a) { return a || b; }, x); + } + + constexpr point &operator+=(const point &x) { return *this = *this + x; } + constexpr point &operator-=(const point &x) { return *this = *this - x; } + constexpr point &operator*=(const point &x) { return *this = *this * x; } + constexpr point &operator/=(const point &x) { return *this = *this / x; } + constexpr point &operator%=(const point &x) { return *this = *this % x; } + constexpr point &operator&=(const point &x) { return *this = *this & x; } + constexpr point &operator|=(const point &x) { return *this = *this | x; } + constexpr point &operator^=(const point &x) { return *this = *this ^ x; } + + constexpr point &operator+=(const T &a) { return *this = *this + a; } + constexpr point &operator-=(const T &a) { return *this = *this - a; } + constexpr point &operator*=(const T &a) { return *this = *this * a; } + constexpr point &operator/=(const T &a) { return *this = *this / a; } + constexpr point &operator%=(const T &a) { return *this = *this % a; } + constexpr point &operator&=(const T &a) { return *this = *this & a; } + constexpr point &operator|=(const T &a) { return *this = *this | a; } + constexpr point &operator^=(const T &a) { return *this = *this ^ a; } + + friend constexpr point abs(const point &x) { + using std::abs; + return fmap([&](const T &a) { return abs(a); }, x); + } + friend constexpr point fabs(const point &x) { + using std::fabs; + return fmap([&](const T &a) { return fabs(a); }, x); + } + + friend constexpr point fmax(const point &x, const point &y) { + using std::fmax; + return fmap([](const T &a, const T &b) { return fmax(a, b); }, x, y); + } + friend constexpr point fmin(const point &x, const point &y) { + using std::fmin; + return fmap([](const T &a, const T &b) { return fmin(a, b); }, x, y); + } + friend constexpr point max(const point &x, const point &y) { + using std::max; + return fmap([](const T &a, const T &b) { return max(a, b); }, x, y); + } + friend constexpr point min(const point &x, const point &y) { + using std::min; + return fmap([](const T &a, const T &b) { return min(a, b); }, x, y); + } + + friend constexpr point fmax(const T &a, const point &y) { + using std::fmax; + return fmap([&](const T &b) { return fmax(a, b); }, y); + } + friend constexpr point fmin(const T &a, const point &y) { + using std::fmin; + return fmap([&](const T &b) { return fmin(a, b); }, y); + } + friend constexpr point max(const T &a, const point &y) { + using std::max; + return fmap([&](const T &b) { return max(a, b); }, y); + } + friend constexpr point min(const T &a, const point &y) { + using std::min; + return fmap([&](const T &b) { return min(a, b); }, y); + } + + friend constexpr point fmax(const point &x, const T &b) { + using std::fmax; + return fmap([&](const T &a) { return fmax(a, b); }, x); + } + friend constexpr point fmin(const point &x, const T &b) { + using std::fmin; + return fmap([&](const T &a) { return fmin(a, b); }, x); + } + friend constexpr point max(const point &x, const T &b) { + using std::max; + return fmap([&](const T &a) { return max(a, b); }, x); + } + friend constexpr point min(const point &x, const T &b) { + using std::min; + return fmap([&](const T &a) { return min(a, b); }, x); + } + + friend constexpr bool all(const point &x) { + return fold([](bool r, const T &a) { return r && a; }, true, x); + } + friend constexpr bool any(const point &x) { + return fold([](bool r, const T &a) { return r || a; }, false, x); + } + friend constexpr T max_element(const point &x) { + using std::max; + return fold([](const T &r, const T &a) { return max(r, a); }, + std::numeric_limits::lowest(), x); + } + friend constexpr T min_element(const point &x) { + using std::min; + return fold([](const T &r, const T &a) { return min(r, a); }, + std::numeric_limits::max(), x); + } + friend constexpr T product(const point &x) { + return fold([](const T &r, const T &a) { return r * a; }, T(1), x); + } + friend constexpr T sum(const point &x) { + return fold([](const T &r, const T &a) { return r + a; }, T(0), x); + } + + friend constexpr bool operator==(const point &x, const point &y) { + return all(fmap([](const T &a, const T &b) { return a == b; })); + } + friend constexpr bool operator!=(const point &x, const point &y) { + return any(fmap([](const T &a, const T &b) { return a != b; })); + } + + /** Output a point + */ + friend std::ostream &operator<<(std::ostream &os, const point &x) { + os << "["; + for (size_type d = 0; d < D; ++d) { + if (d != 0) + os << ","; + os << x[d]; + } + os << "]"; + return os; + } +}; + +} // namespace RegionCalculus +} // namespace openPMD + +namespace std { + +template +struct equal_to> { + constexpr bool + operator()(const openPMD::RegionCalculus::point &p, + const openPMD::RegionCalculus::point &q) const { + return openPMD::RegionCalculus::point::all( + openPMD::RegionCalculus::point::fmap( + [](const T &a, const T &b) { return equal_to()(a, b); }, p, q)); + } +}; + +template +struct hash> { + constexpr size_t + operator()(const openPMD::RegionCalculus::point &p) const { + return openPMD::RegionCalculus::point::fold( + [](size_t r, const T &b) { + return openPMD::RegionCalculus::detail::hash_combine(r, b); + }, + size_t(0xb22da17173243869ULL), p); + } +}; + +template +struct less> { + constexpr bool + operator()(const openPMD::RegionCalculus::point &p, + const openPMD::RegionCalculus::point &q) const { + return openPMD::RegionCalculus::point::fold( + [](bool r, const T &a, const T &b) { return r && less()(a, b); }, + true, p, q); + } +}; + +} // namespace std + +namespace openPMD { +namespace RegionCalculus { + +namespace detail { + +// Abstract base helper class + +template class vpoint { +public: + typedef T value_type; + typedef std::size_t size_type; + + virtual std::unique_ptr copy() const = 0; + + virtual ~vpoint() {} + + virtual size_type size() const = 0; + + virtual const T &operator[](const size_type d) const = 0; + virtual T &operator[](const size_type d) = 0; + + virtual size_type ndims() const = 0; + + virtual std::unique_ptr operator+() const = 0; + virtual std::unique_ptr operator-() const = 0; + virtual std::unique_ptr operator~() const = 0; + virtual std::unique_ptr> operator!() const = 0; + + virtual std::unique_ptr operator+(const vpoint &x) const = 0; + virtual std::unique_ptr operator-(const vpoint &x) const = 0; + virtual std::unique_ptr operator*(const vpoint &x) const = 0; + virtual std::unique_ptr operator/(const vpoint &x) const = 0; + virtual std::unique_ptr operator%(const vpoint &x) const = 0; + virtual std::unique_ptr operator&(const vpoint &x) const = 0; + virtual std::unique_ptr operator|(const vpoint &x) const = 0; + virtual std::unique_ptr operator^(const vpoint &x) const = 0; + virtual std::unique_ptr> operator&&(const vpoint &x) const = 0; + virtual std::unique_ptr> operator||(const vpoint &x) const = 0; + + virtual std::unique_ptr left_plus(const T &a) const = 0; + virtual std::unique_ptr left_minus(const T &a) const = 0; + virtual std::unique_ptr left_multiplies(const T &a) const = 0; + virtual std::unique_ptr left_divides(const T &a) const = 0; + virtual std::unique_ptr left_modulus(const T &a) const = 0; + virtual std::unique_ptr left_bit_and(const T &a) const = 0; + virtual std::unique_ptr left_bit_or(const T &a) const = 0; + virtual std::unique_ptr left_bit_xor(const T &a) const = 0; + virtual std::unique_ptr> left_logical_and(const T &a) const = 0; + virtual std::unique_ptr> left_logical_or(const T &a) const = 0; + + virtual std::unique_ptr operator+(const T &b) const = 0; + virtual std::unique_ptr operator-(const T &b) const = 0; + virtual std::unique_ptr operator*(const T &b) const = 0; + virtual std::unique_ptr operator/(const T &b) const = 0; + virtual std::unique_ptr operator%(const T &b) const = 0; + virtual std::unique_ptr operator&(const T &b) const = 0; + virtual std::unique_ptr operator|(const T &b) const = 0; + virtual std::unique_ptr operator^(const T &b) const = 0; + virtual std::unique_ptr> operator&&(const T &b) const = 0; + virtual std::unique_ptr> operator||(const T &b) const = 0; + + virtual vpoint &operator+=(const vpoint &x) = 0; + virtual vpoint &operator-=(const vpoint &x) = 0; + virtual vpoint &operator*=(const vpoint &x) = 0; + virtual vpoint &operator/=(const vpoint &x) = 0; + virtual vpoint &operator%=(const vpoint &x) = 0; + virtual vpoint &operator&=(const vpoint &x) = 0; + virtual vpoint &operator|=(const vpoint &x) = 0; + virtual vpoint &operator^=(const vpoint &x) = 0; + + virtual vpoint &operator+=(const T &b) = 0; + virtual vpoint &operator-=(const T &b) = 0; + virtual vpoint &operator*=(const T &b) = 0; + virtual vpoint &operator/=(const T &b) = 0; + virtual vpoint &operator%=(const T &b) = 0; + virtual vpoint &operator&=(const T &b) = 0; + virtual vpoint &operator|=(const T &b) = 0; + virtual vpoint &operator^=(const T &b) = 0; + + virtual std::ostream &output(std::ostream &os) const = 0; +}; + +// Helper class wrapping point + +template class wpoint final : public vpoint { + point p; + +public: + using typename vpoint::value_type; + using typename vpoint::size_type; + + wpoint() : p{} {} + + wpoint(const wpoint &x) = default; + wpoint(wpoint &&) = default; + wpoint &operator=(const wpoint &) = default; + wpoint &operator=(wpoint &&) = default; + + wpoint(const point &p) : p(p) {} + wpoint(point &&p) : p(std::move(p)) {} + + std::unique_ptr> copy() const override { + return std::make_unique(*this); + } + + ~wpoint() override {} + + constexpr size_type size() const override { return p.size(); } + + const T &operator[](const size_type d) const override { return p[d]; } + T &operator[](const size_type d) override { return p[d]; } + + constexpr size_type ndims() const override { return p.ndims(); } + + std::unique_ptr> operator+() const override { + return std::make_unique(+p); + } + std::unique_ptr> operator-() const override { + return std::make_unique(+p); + } + std::unique_ptr> operator~() const override { + return std::make_unique(~p); + } + std::unique_ptr> operator!() const override { + return std::make_unique>(!p); + } + + std::unique_ptr> operator+(const vpoint &x) const override { + return std::make_unique(p + dynamic_cast(x).p); + } + std::unique_ptr> operator-(const vpoint &x) const override { + return std::make_unique(p - dynamic_cast(x).p); + } + std::unique_ptr> operator*(const vpoint &x) const override { + return std::make_unique(p * dynamic_cast(x).p); + } + std::unique_ptr> operator/(const vpoint &x) const override { + return std::make_unique(p / dynamic_cast(x).p); + } + std::unique_ptr> operator%(const vpoint &x) const override { + return std::make_unique(p % dynamic_cast(x).p); + } + std::unique_ptr> operator&(const vpoint &x) const override { + return std::make_unique(p & dynamic_cast(x).p); + } + std::unique_ptr> operator|(const vpoint &x) const override { + return std::make_unique(p | dynamic_cast(x).p); + } + std::unique_ptr> operator^(const vpoint &x) const override { + return std::make_unique(p ^ dynamic_cast(x).p); + } + std::unique_ptr> operator&&(const vpoint &x) const override { + return std::make_unique>(p && + dynamic_cast(x).p); + } + std::unique_ptr> operator||(const vpoint &x) const override { + return std::make_unique>(p || + dynamic_cast(x).p); + } + + std::unique_ptr> left_plus(const T &a) const override { + return std::make_unique(a + p); + } + std::unique_ptr> left_minus(const T &a) const override { + return std::make_unique(a - p); + } + std::unique_ptr> left_multiplies(const T &a) const override { + return std::make_unique(a * p); + } + std::unique_ptr> left_divides(const T &a) const override { + return std::make_unique(a / p); + } + std::unique_ptr> left_modulus(const T &a) const override { + return std::make_unique(a % p); + } + std::unique_ptr> left_bit_and(const T &a) const override { + return std::make_unique(a & p); + } + std::unique_ptr> left_bit_or(const T &a) const override { + return std::make_unique(a | p); + } + std::unique_ptr> left_bit_xor(const T &a) const override { + return std::make_unique(a ^ p); + } + std::unique_ptr> left_logical_and(const T &a) const override { + return std::make_unique>(a && p); + } + std::unique_ptr> left_logical_or(const T &a) const override { + return std::make_unique>(a || p); + } + + std::unique_ptr> operator+(const T &b) const override { + return std::make_unique(p + b); + } + std::unique_ptr> operator-(const T &b) const override { + return std::make_unique(p - b); + } + std::unique_ptr> operator*(const T &b) const override { + return std::make_unique(p * b); + } + std::unique_ptr> operator/(const T &b) const override { + return std::make_unique(p / b); + } + std::unique_ptr> operator%(const T &b) const override { + return std::make_unique(p % b); + } + std::unique_ptr> operator&(const T &b) const override { + return std::make_unique(p & b); + } + std::unique_ptr> operator|(const T &b) const override { + return std::make_unique(p | b); + } + std::unique_ptr> operator^(const T &b) const override { + return std::make_unique(p ^ b); + } + std::unique_ptr> operator&&(const T &b) const override { + return std::make_unique>(p && b); + } + std::unique_ptr> operator||(const T &b) const override { + return std::make_unique>(p || b); + } + + vpoint &operator+=(const vpoint &x) override { + p += dynamic_cast(x).p; + return *this; + } + vpoint &operator-=(const vpoint &x) override { + p -= dynamic_cast(x).p; + return *this; + } + vpoint &operator*=(const vpoint &x) override { + p *= dynamic_cast(x).p; + return *this; + } + vpoint &operator/=(const vpoint &x) override { + p /= dynamic_cast(x).p; + return *this; + } + vpoint &operator%=(const vpoint &x) override { + p %= dynamic_cast(x).p; + return *this; + } + vpoint &operator&=(const vpoint &x) override { + p &= dynamic_cast(x).p; + return *this; + } + vpoint &operator|=(const vpoint &x) override { + p |= dynamic_cast(x).p; + return *this; + } + vpoint &operator^=(const vpoint &x) override { + p ^= dynamic_cast(x).p; + return *this; + } + + vpoint &operator+=(const T &b) override { + p += b; + return *this; + } + vpoint &operator-=(const T &b) override { + p -= b; + return *this; + } + vpoint &operator*=(const T &b) override { + p *= b; + return *this; + } + vpoint &operator/=(const T &b) override { + p /= b; + return *this; + } + vpoint &operator%=(const T &b) override { + p %= b; + return *this; + } + vpoint &operator&=(const T &b) override { + p &= b; + return *this; + } + vpoint &operator|=(const T &b) override { + p |= b; + return *this; + } + vpoint &operator^=(const T &b) override { + p ^= b; + return *this; + } + + std::ostream &output(std::ostream &os) const override { return os << p; } +}; + +template +std::unique_ptr> make_vpoint(const std::size_t D) { + switch (D) { + case 0: + return std::make_unique>(); + case 1: + return std::make_unique>(); + case 2: + return std::make_unique>(); + case 3: + return std::make_unique>(); + case 4: + return std::make_unique>(); + case 5: + return std::make_unique>(); + default: + abort(); + } +} + +} // namespace detail + +/** A point + * + * The dimension (number of component) of the point is only known at + * run-time. @see point + * + * Points can represent either points or distances. Points are + * fixed-size vectors that support arithmetic operations. + */ +template class ndpoint { + + template using vpoint = detail::vpoint; + + template friend class ndpoint; + + std::unique_ptr> p; + + ndpoint(std::unique_ptr> p) : p(std::move(p)) {} + +public: + /** Component type + */ + typedef typename vpoint::value_type value_type; + /** Return type of point::size() + */ + typedef typename vpoint::size_type size_type; + + /** Create an invalid point + */ + ndpoint() : p() {} + /** Create a value-initialized point with D components + */ + ndpoint(const size_type D) : p(detail::make_vpoint(D)) {} + + ndpoint(const ndpoint &x) : p(x.p ? x.p->copy() : nullptr) {} + ndpoint(ndpoint &&) = default; + ndpoint &operator=(const ndpoint &) = default; + ndpoint &operator=(ndpoint &&) = default; + + /** Check whether a point is valid + * + * A valid point knows its number of dimensions, and its components + * are initialized. An invalid point does not know its number of + * dimensions and holds no data, similar to a null pointer. + * + * Most other member functions must not be called for invalid + * points. + */ + operator bool() const { return bool(p); } + + /** Number of comopnents (same as number of dimensions) + */ + size_type size() const { return p->size(); } + + /** Get a component of a point + */ + const T &operator[](const size_type d) const { return (*p)[d]; } + /** Get a component of a point + */ + T &operator[](const size_type d) { return (*p)[d]; } + + /** Number of dimensions (same as number of comopnents) + */ + size_type ndims() const { return p->ndims(); } + + friend ndpoint operator+(const ndpoint &x) { return ndpoint(+*x.p); } + friend ndpoint operator-(const ndpoint &x) { return ndpoint(-*x.p); } + friend ndpoint operator~(const ndpoint &x) { return ndpoint(~*x.p); } + friend ndpoint operator!(const ndpoint &x) { + return ndpoint(!*x.p); + } + + friend ndpoint operator+(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p + *y.p); + } + friend ndpoint operator-(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p - *y.p); + } + friend ndpoint operator*(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p * *y.p); + } + friend ndpoint operator/(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p / *y.p); + } + friend ndpoint operator%(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p % *y.p); + } + friend ndpoint operator&(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p & *y.p); + } + friend ndpoint operator|(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p | *y.p); + } + friend ndpoint operator^(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p ^ *y.p); + } + friend ndpoint operator&&(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p && *y.p); + } + friend ndpoint operator||(const ndpoint &x, const ndpoint &y) { + return ndpoint(*x.p || *y.p); + } + + friend ndpoint operator+(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_plus(a)); + } + friend ndpoint operator-(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_minus(a)); + } + friend ndpoint operator*(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_multiplies(a)); + } + friend ndpoint operator/(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_divides(a)); + } + friend ndpoint operator%(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_modulus(a)); + } + friend ndpoint operator&(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_bit_and(a)); + } + friend ndpoint operator|(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_bit_or(a)); + } + friend ndpoint operator^(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_bit_xor(a)); + } + friend ndpoint operator&&(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_logical_and(a)); + } + friend ndpoint operator||(const T &a, const ndpoint &y) { + return ndpoint(y.p->left_logical_or(a)); + } + + friend ndpoint operator+(const ndpoint &x, const T &b) { + return ndpoint(*x.p + b); + } + friend ndpoint operator-(const ndpoint &x, const T &b) { + return ndpoint(*x.p - b); + } + friend ndpoint operator*(const ndpoint &x, const T &b) { + return ndpoint(*x.p * b); + } + friend ndpoint operator/(const ndpoint &x, const T &b) { + return ndpoint(*x.p / b); + } + friend ndpoint operator%(const ndpoint &x, const T &b) { + return ndpoint(*x.p % b); + } + friend ndpoint operator&(const ndpoint &x, const T &b) { + return ndpoint(*x.p & b); + } + friend ndpoint operator|(const ndpoint &x, const T &b) { + return ndpoint(*x.p | b); + } + friend ndpoint operator^(const ndpoint &x, const T &b) { + return ndpoint(*x.p ^ b); + } + friend ndpoint operator&&(const ndpoint &x, const T &b) { + return ndpoint(*x.p && b); + } + friend ndpoint operator||(const ndpoint &x, const T &b) { + return ndpoint(*x.p || b); + } + + ndpoint &operator+=(const ndpoint &x) { + *p += *x.p; + return *this; + } + ndpoint &operator-=(const ndpoint &x) { + *p -= *x.p; + return *this; + } + ndpoint &operator*=(const ndpoint &x) { + *p *= *x.p; + return *this; + } + ndpoint &operator/=(const ndpoint &x) { + *p /= *x.p; + return *this; + } + ndpoint &operator%=(const ndpoint &x) { + *p %= *x.p; + return *this; + } + ndpoint &operator&=(const ndpoint &x) { + *p &= *x.p; + return *this; + } + ndpoint &operator|=(const ndpoint &x) { + *p |= *x.p; + return *this; + } + ndpoint &operator^=(const ndpoint &x) { + *p ^= *x.p; + return *this; + } + + ndpoint &operator+=(const T &a) { + *p += a; + return *this; + } + ndpoint &operator-=(const T &a) { + *p -= a; + return *this; + } + ndpoint &operator*=(const T &a) { + *p *= a; + return *this; + } + ndpoint &operator/=(const T &a) { + *p /= a; + return *this; + } + ndpoint &operator%=(const T &a) { + *p %= a; + return *this; + } + ndpoint &operator&=(const T &a) { + *p &= a; + return *this; + } + ndpoint &operator|=(const T &a) { + *p |= a; + return *this; + } + ndpoint &operator^=(const T &a) { + *p ^= a; + return *this; + } + + friend ndpoint abs(const ndpoint &x) { return abs(*x.p); } + friend ndpoint fabs(const ndpoint &x) { return fabs(*x.p); } + + friend ndpoint fmax(const ndpoint &x, const ndpoint &y) { + return fmax(*x.p, *y.p); + } + friend ndpoint fmin(const ndpoint &x, const ndpoint &y) { + return fmin(*x.p, *y.p); + } + friend ndpoint max(const ndpoint &x, const ndpoint &y) { + return max(*x.p, *y.p); + } + friend ndpoint min(const ndpoint &x, const ndpoint &y) { + return min(*x.p, *y.p); + } + + friend ndpoint fmax(const T &a, const ndpoint &y) { return fmax(a, *y.p); } + friend ndpoint fmin(const T &a, const ndpoint &y) { return fmin(a, *y.p); } + friend ndpoint max(const T &a, const ndpoint &y) { return max(a, *y.p); } + friend ndpoint min(const T &a, const ndpoint &y) { return min(a, *y.p); } + + friend ndpoint fmax(const ndpoint &x, const T &b) { return fmax(*x.p, b); } + friend ndpoint fmin(const ndpoint &x, const T &b) { return fmin(*x.p, b); } + friend ndpoint max(const ndpoint &x, const T &b) { return max(*x.p, b); } + friend ndpoint min(const ndpoint &x, const T &b) { return min(*x.p, b); } + + friend T all(const ndpoint &x) { return all(*x.p); } + friend T any(const ndpoint &x) { return any(*x.p); } + friend T max_element(const ndpoint &x) { return max_element(*x.p); } + friend T min_element(const ndpoint &x) { return min_element(*x.p); } + friend T product(const ndpoint &x) { return product(*x.p); } + friend T sum(const ndpoint &x) { return sum(*x.p); } + + friend bool operator==(const ndpoint &x, const ndpoint &y) { + return *x.p == *y.p; + } + friend bool operator!=(const ndpoint &x, const ndpoint &y) { + return *x.p != *y.p; + } + + /** Output a point + */ + friend std::ostream &operator<<(std::ostream &os, const ndpoint &x) { + if (x.p) + x.p->output(os); + else + os << "[INVALID]"; + return os; + } +}; + +} // namespace RegionCalculus +} // namespace openPMD + +#endif // #ifndef REGIONCALCULUS2_HPP diff --git a/include/openPMD/RegionCalculus.hpp b/include/openPMD/regions/RegionCalculus_old.hpp similarity index 100% rename from include/openPMD/RegionCalculus.hpp rename to include/openPMD/regions/RegionCalculus_old.hpp diff --git a/include/openPMD/regions/Regions.hpp b/include/openPMD/regions/Regions.hpp new file mode 100644 index 0000000000..6fdaffcdac --- /dev/null +++ b/include/openPMD/regions/Regions.hpp @@ -0,0 +1,7 @@ +#ifndef REGIONS_REGIONS_HPP +#define REGIONS_REGIONS_HPP + +#include "Point.hpp" +#include "NDPoint.hpp" + +#endif // #ifndef REGIONS_REGIONS_HPP diff --git a/src/RegionCalculus.cpp b/src/RegionCalculus.cpp deleted file mode 100644 index 651ffc2885..0000000000 --- a/src/RegionCalculus.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "openPMD/RegionCalculus.hpp" - -// We could instantiate some templates here. diff --git a/src/RegionCalculus2.cpp b/src/RegionCalculus2.cpp deleted file mode 100644 index 651ffc2885..0000000000 --- a/src/RegionCalculus2.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "openPMD/RegionCalculus.hpp" - -// We could instantiate some templates here. diff --git a/test/RegionCalculus2Test.cpp b/test/RegionCalculus2Test.cpp deleted file mode 100644 index b0569b4efc..0000000000 --- a/test/RegionCalculus2Test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "openPMD/RegionCalculus2.hpp" - -#include - -#include - -using namespace RegionCalculus2; - -template void test_point() { - point x; - REQUIRE(x.size() == D); - point y; - for (std::size_t d = 0; d < D; ++d) - y[d] = d; - point z = x + y; - for (std::size_t d = 0; d < D; ++d) - REQUIRE(z[d] == d); - z += y; - for (std::size_t d = 0; d < D; ++d) - REQUIRE(z[d] == 2 * d); -} - -TEST_CASE("point", "[auxiliary]") { - test_point(); - test_point(); -} - -template void test_ndpoint(const std::size_t D) { - ndpoint n; - REQUIRE(!n); - ndpoint x(D); - REQUIRE(x); - REQUIRE(x.size() == D); - ndpoint y(D); - for (std::size_t d = 0; d < D; ++d) - y[d] = d; - ndpoint z = x + y; - for (std::size_t d = 0; d < D; ++d) - REQUIRE(z[d] == d); - z += y; - for (std::size_t d = 0; d < D; ++d) - REQUIRE(z[d] == 2 * d); -} - -TEST_CASE("ndpoint", "[auxiliary]") { - test_ndpoint(2); - test_ndpoint(3); -} diff --git a/test/RegionCalculusTest.cpp b/test/RegionCalculusTest_old.cpp similarity index 100% rename from test/RegionCalculusTest.cpp rename to test/RegionCalculusTest_old.cpp diff --git a/test/RegionsTest.cpp b/test/RegionsTest.cpp new file mode 100644 index 0000000000..fe4124a775 --- /dev/null +++ b/test/RegionsTest.cpp @@ -0,0 +1,454 @@ +#include + +#include + +#include +#include +#include +#include + +using namespace openPMD::Regions; + +template auto maxabs(const T &xs) { + using std::abs, std::max_element; + return max_element(abs(xs)); +} +template bool is_approx(const T &xs, const T &ys) { + return maxabs(xs - ys) <= + 10 * std::numeric_limits::epsilon(); +} + +template void test_Point_bool(const P &p) { + const std::size_t D = p.size(); + using T = typename P::value_type; + + std::mt19937 gen; + std::uniform_int_distribution dist(0, 1); + const auto rand = [&]() { return dist(gen); }; + + P n = p; + REQUIRE(n.size() == D); + for (std::size_t d = 0; d < D; ++d) + REQUIRE(n[d] == 0); + + const P x = fmap([&](auto) { return rand(); }, p); + const P y = fmap([&](auto) { return rand(); }, p); + const P z = fmap([&](auto) { return rand(); }, p); + + const T a = rand(); + const T b = rand(); + + REQUIRE(!any(n)); + REQUIRE(all(!n)); + + REQUIRE((n & x) == n); + REQUIRE((0 & x) == n); + REQUIRE((x & n) == n); + REQUIRE((x & 0) == n); + + REQUIRE((!n & x) == x); + REQUIRE((T(1) & x) == x); + REQUIRE((x & !n) == x); + REQUIRE((x & T(1)) == x); + + REQUIRE((n | x) == x); + REQUIRE((0 | x) == x); + REQUIRE((x | n) == x); + REQUIRE((x | 0) == x); + + REQUIRE((!n | x) == !n); + REQUIRE((T(1) | x) == !n); + REQUIRE((x | !n) == !n); + REQUIRE((x | T(1)) == !n); + + REQUIRE((x & y) == (y & x)); + REQUIRE((x | y) == (y | x)); + + REQUIRE(((x & y) & z) == (x & (y & z))); + REQUIRE(((x | y) | z) == (x | (y | z))); + + REQUIRE((x & (y | z)) == ((y & x) | (x & z))); + REQUIRE((x | (y & z)) == ((y | x) & (x | z))); + + REQUIRE((x & y) == !(!x | !y)); + REQUIRE((x | y) == !(!x & !y)); + + REQUIRE((n ^ x) == x); + REQUIRE((0 ^ x) == x); + REQUIRE((x ^ n) == x); + REQUIRE((x ^ 0) == x); + + REQUIRE((!n ^ x) == !x); + REQUIRE((T(1) ^ x) == !x); + REQUIRE((x ^ !n) == !x); + REQUIRE((x ^ T(1)) == !x); + + REQUIRE((x ^ x) == n); + + REQUIRE((x ^ y) == (y ^ x)); + REQUIRE(((x ^ y) ^ z) == (x ^ (y ^ z))); + + REQUIRE(!(!x) == x); + + REQUIRE((n && x) == n); + REQUIRE((0 && x) == n); + REQUIRE((x && n) == n); + REQUIRE((x && 0) == n); + + REQUIRE((!n && x) == x); + REQUIRE((!T(0) && x) == x); + REQUIRE((x && !n) == x); + REQUIRE((x && !T(0)) == x); + + REQUIRE((n || x) == x); + REQUIRE((0 || x) == x); + REQUIRE((x || n) == x); + REQUIRE((x || 0) == x); + + REQUIRE((!n || x) == !n); + REQUIRE((!T(0) || x) == !n); + REQUIRE((x || !n) == !n); + REQUIRE((x || !T(0)) == !n); + + REQUIRE((x && y) == (y && x)); + REQUIRE((x || y) == (y || x)); + + REQUIRE(((x && y) && z) == (x && (y && z))); + REQUIRE(((x || y) || z) == (x || (y || z))); + + REQUIRE((x && (y || z)) == ((y && x) || (x && z))); + REQUIRE((x || (y && z)) == ((y || x) && (x || z))); + + REQUIRE((x && y) == !(!x || !y)); + REQUIRE((x || y) == !(!x && !y)); + + P t; + t = x; + t &= y; + REQUIRE(t == (x & y)); + t = x; + t |= y; + REQUIRE(t == (x | y)); + t = x; + t ^= y; + REQUIRE(t == (x ^ y)); +} + +template void test_Point_int(const P &p) { + const std::size_t D = p.size(); + using T = typename P::value_type; + + std::mt19937 gen; + std::uniform_int_distribution dist(-1000, 1000); + const auto rand = [&]() { return dist(gen); }; + + P n(p); + REQUIRE(n.size() == D); + for (std::size_t d = 0; d < D; ++d) + REQUIRE(n[d] == 0); + + const P x = fmap([&](auto) { return rand(); }, p); + const P y = fmap([&](auto) { return rand(); }, p); + const P z = fmap([&](auto) { return rand(); }, p); + + const T a = rand(); + const T b = rand(); + + REQUIRE(fmap([](auto i) { return i; }, x) == x); + REQUIRE(fmap([](auto i) { return i + 1; }, + fmap([](auto i) { return 2 * i; }, x)) == + fmap([](auto i) { return 2 * i + 1; }, x)); + + REQUIRE(fmap([](auto i, auto j) { return 2 * i + j; }, x, y) == 2 * x + y); + REQUIRE(fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, + z) == 3 * x + 2 * y + z); + + REQUIRE(fold([](auto i, auto j) { return i + j; }, 0, x) == sum(x)); + REQUIRE(fold([](auto i, auto j, auto k) { return i + j + k; }, 0, x, y) == + sum(x + y)); + + REQUIRE(sum(n) == 0); + REQUIRE(sum(n + 1) == D); + REQUIRE(product(n) == (D == 0 ? 1 : 0)); + REQUIRE(product(n + 1) == 1); + REQUIRE(min_element(n) == (D == 0 ? std::numeric_limits::max() : 0)); + REQUIRE(max_element(n) == (D == 0 ? std::numeric_limits::min() : 0)); + REQUIRE(min_element(n + 1) == (D == 0 ? std::numeric_limits::max() : 1)); + REQUIRE(max_element(n + 1) == (D == 0 ? std::numeric_limits::min() : 1)); + + REQUIRE(+x == x); + REQUIRE(n + x == x); + REQUIRE(T(0) + x == x); + REQUIRE(x + n == x); + REQUIRE(x + T(0) == x); + + REQUIRE(x + y == y + x); + + REQUIRE((x + y) + z == x + (y + z)); + + REQUIRE(-x == -T(1) * x); + REQUIRE(-(-x) == x); + REQUIRE(x - x == n); + + REQUIRE(a * n == n); + REQUIRE(n * a == n); + REQUIRE(T(0) * x == n); + REQUIRE(x * T(0) == n); + REQUIRE(T(1) * x == x); + REQUIRE(x * T(1) == x); + + REQUIRE(a * x == x * a); + + REQUIRE(a * x + b * x == (a + b) * x); + REQUIRE(a * (x + y) == a * x + a * y); + + REQUIRE(x * (y + z) == x * y + x * z); + + if (min_element(abs(y)) != 0) { + REQUIRE(x * y / y == x); + REQUIRE(x / y * y + x % y == x); + } + + REQUIRE(~(~x) == x); + + REQUIRE((n & x) == n); + REQUIRE((0 & x) == n); + REQUIRE((x & n) == n); + REQUIRE((x & 0) == n); + + REQUIRE((~n & x) == x); + REQUIRE((~T(0) & x) == x); + REQUIRE((x & ~n) == x); + REQUIRE((x & ~T(0)) == x); + + REQUIRE((n | x) == x); + REQUIRE((0 | x) == x); + REQUIRE((x | n) == x); + REQUIRE((x | 0) == x); + + REQUIRE((~n | x) == ~n); + REQUIRE((~T(0) | x) == ~n); + REQUIRE((x | ~n) == ~n); + REQUIRE((x | ~T(0)) == ~n); + + REQUIRE((x & y) == (y & x)); + REQUIRE((x | y) == (y | x)); + + REQUIRE(((x & y) & z) == (x & (y & z))); + REQUIRE(((x | y) | z) == (x | (y | z))); + + REQUIRE((x & (y | z)) == ((y & x) | (x & z))); + REQUIRE((x | (y & z)) == ((y | x) & (x | z))); + + REQUIRE((x & y) == ~(~x | ~y)); + REQUIRE((x | y) == ~(~x & ~y)); + + REQUIRE((n ^ x) == x); + REQUIRE((0 ^ x) == x); + REQUIRE((x ^ n) == x); + REQUIRE((x ^ 0) == x); + + REQUIRE((~n ^ x) == ~x); + REQUIRE((~T(0) ^ x) == ~x); + REQUIRE((x ^ ~n) == ~x); + REQUIRE((x ^ ~T(0)) == ~x); + + REQUIRE((x ^ x) == n); + + REQUIRE((x ^ y) == (y ^ x)); + REQUIRE(((x ^ y) ^ z) == (x ^ (y ^ z))); + + P t; + t = x; + t += y; + REQUIRE(t == x + y); + t = x; + t -= y; + REQUIRE(t == x - y); + t = x; + t *= y; + REQUIRE(t == x * y); + t = x; + t /= y; + REQUIRE(t == x / y); + t = x; + t %= y; + REQUIRE(t == x % y); + t = x; + t &= y; + REQUIRE(t == (x & y)); + t = x; + t |= y; + REQUIRE(t == (x | y)); + t = x; + t ^= y; + REQUIRE(t == (x ^ y)); +} + +template void test_Point_float(const P &p) { + const std::size_t D = p.size(); + using T = typename P::value_type; + + std::mt19937 gen; + std::uniform_real_distribution dist(-1.0, 1.0); + const auto rand = [&]() { return dist(gen); }; + + P n(p); + REQUIRE(n.size() == D); + for (std::size_t d = 0; d < D; ++d) + REQUIRE(n[d] == 0); + + const P x = fmap([&](auto) { return rand(); }, p); + const P y = fmap([&](auto) { return rand(); }, p); + const P z = fmap([&](auto) { return rand(); }, p); + + const T a = rand(); + const T b = rand(); + + REQUIRE(fmap([](auto i) { return i; }, x) == x); + REQUIRE(fmap([](auto i) { return i + 1; }, + fmap([](auto i) { return 2 * i; }, x)) == + fmap([](auto i) { return 2 * i + 1; }, x)); + + REQUIRE(fmap([](auto i, auto j) { return 2 * i + j; }, x, y) == 2 * x + y); + REQUIRE(fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, + z) == 3 * x + 2 * y + z); + + REQUIRE(fold([](auto i, auto j) { return i + j; }, T(0), x) == sum(x)); + REQUIRE(fold([](auto i, auto j, auto k) { return i + j + k; }, T(0), x, y) == + sum(x + y)); + + REQUIRE(sum(n) == 0); + REQUIRE(sum(n + 1) == D); + REQUIRE(product(n) == (D == 0 ? 1 : 0)); + REQUIRE(product(n + 1) == 1); + REQUIRE(min_element(n) == (D == 0 ? T(1) / 0 : 0)); + REQUIRE(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); + REQUIRE(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); + REQUIRE(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); + + REQUIRE(+x == x); + REQUIRE(n + x == x); + REQUIRE(T(0) + x == x); + REQUIRE(x + n == x); + REQUIRE(x + T(0) == x); + + REQUIRE(x + y == y + x); + + REQUIRE(is_approx((x + y) + z, x + (y + z))); + + REQUIRE(-x == -T(1) * x); + REQUIRE(-(-x) == x); + REQUIRE(x - x == n); + + REQUIRE(a * n == n); + REQUIRE(n * a == n); + REQUIRE(T(0) * x == n); + REQUIRE(x * T(0) == n); + REQUIRE(T(1) * x == x); + REQUIRE(x * T(1) == x); + + REQUIRE(a * x == x * a); + + if (min_element(abs(y)) != 0) { + REQUIRE(x / x == n + 1); + REQUIRE(1 / (1 / x) == x); + REQUIRE(a / x == a * (1 / x)); + } + if (a != 0) { + REQUIRE(is_approx(x / a, x * (1 / a))); + } + + REQUIRE(is_approx(a * x + b * x, (a + b) * x)); + REQUIRE(is_approx(a * (x + y), a * x + a * y)); + + REQUIRE(is_approx(x * (y + z), x * y + x * z)); + + if (min_element(abs(y)) != 0) { + REQUIRE(is_approx(x * y / y, x)); + } + + P t; + t = x; + t += y; + REQUIRE(t == x + y); + t = x; + t -= y; + REQUIRE(t == x - y); + t = x; + t *= y; + REQUIRE(t == x * y); + t = x; + t /= y; + REQUIRE(t == x / y); +} + +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } + +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} + +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} + +TEST_CASE("NDPoint(0)", "[regions]") { + test_Point_bool(NDPoint(0)); +} +TEST_CASE("NDPoint(1)", "[regions]") { + test_Point_bool(NDPoint(1)); +} +TEST_CASE("NDPoint(2)", "[regions]") { + test_Point_bool(NDPoint(2)); +} +TEST_CASE("NDPoint(3)", "[regions]") { + test_Point_bool(NDPoint(3)); +} + +TEST_CASE("NDPoint(0)); +} +TEST_CASE("NDPoint(1)); +} +TEST_CASE("NDPoint(2)); +} +TEST_CASE("NDPoint(3)); +} + +TEST_CASE("NDPoint(0)); +} +TEST_CASE("NDPoint(1)); +} +TEST_CASE("NDPoint(2)); +} +TEST_CASE("NDPoint(3)); +} From 5d0e233b18deaa9983c966579c4946c223c097d3 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 4 Jun 2021 23:21:56 -0400 Subject: [PATCH 04/68] Implement Regions::Box --- CMakeLists.txt | 3 +- include/openPMD/regions/Box.hpp | 576 ++++++++++++++++++++++++++++ include/openPMD/regions/NDPoint.hpp | 98 ++++- include/openPMD/regions/Point.hpp | 72 +++- include/openPMD/regions/Regions.hpp | 4 + test/RegionsBoxTest.cpp | 250 ++++++++++++ test/RegionsPointTest.cpp | 507 ++++++++++++++++++++++++ test/RegionsTest.cpp | 454 ---------------------- 8 files changed, 1480 insertions(+), 484 deletions(-) create mode 100644 include/openPMD/regions/Box.hpp create mode 100644 test/RegionsBoxTest.cpp create mode 100644 test/RegionsPointTest.cpp delete mode 100644 test/RegionsTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 32671b44f0..bb9880800e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -682,7 +682,8 @@ endif() set(openPMD_TEST_NAMES Core Auxiliary - Regions + RegionsBox + RegionsPoint SerialIO ParallelIO ) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp new file mode 100644 index 0000000000..44e35793a0 --- /dev/null +++ b/include/openPMD/regions/Box.hpp @@ -0,0 +1,576 @@ +#ifndef REGIONS_BOX_HPP +#define REGIONS_BOX_HPP + +#include "Point.hpp" + +#include +#include +#include +#include +#include +#include + +namespace openPMD { +namespace Regions { + +namespace detail { +namespace detail { + +template struct tuple_eq { + template + constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { + typedef std::tuple_element_t T1; + typedef std::tuple_element_t T2; + typedef std::common_type_t T; + const std::equal_to eq; + return tuple_eq()(x, y) && + eq(std::get(x), std::get(y)); + } +}; +template <> struct tuple_eq<0> { + template + constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { + return true; + } +}; + +template struct tuple_cmp { + template + constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { + typedef std::tuple_element_t T1; + typedef std::tuple_element_t T2; + typedef std::common_type_t T; + const int cmp = tuple_cmp()(x, y); + if (cmp != 0) + return cmp; + const std::less lt; + if (lt(std::get(x), std::get(y))) + return -1; + if (lt(std::get(y), std::get(x))) + return +1; + return 0; + } +}; +template <> struct tuple_cmp<0> { + template + constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { + return 0; + } +}; + +} // namespace detail + +template +constexpr bool tuple_eq(const Tuple1 &x, const Tuple2 &y) { + constexpr std::size_t sz = std::tuple_size_v; + static_assert(std::tuple_size_v == sz); + return detail::tuple_eq()(x, y); +} + +template +constexpr int tuple_cmp(const Tuple1 &x, const Tuple2 &y) { + constexpr std::size_t sz = std::tuple_size_v; + static_assert(std::tuple_size_v == sz); + return detail::tuple_cmp()(x, y); +} + +template +constexpr bool tuple_lt(const Tuple1 &x, const Tuple2 &y) { + return tuple_cmp(x, y) < 0; +} + +} // namespace detail + +/** A D-dimensional box + * + * A box is described by two points, its lower bound (inclusive) and + * upper bound (exclusive). If the lower bound not less than the upper + * bound, the box is empty. Empty boxes are fine (similar to an empty + * array). + * + * The dimension D needs to be known at compile time. @see NDBox + */ +template class Box; + +template class Box { + bool is_full; + +public: + constexpr static std::size_t D = 0; + + typedef typename Point::value_type value_type; + typedef typename Point::size_type size_type; + + /** Create empty box + */ + Box() : is_full(false) {} + + Box(const Box &) = default; + Box(Box &&) = default; + Box &operator=(const Box &) = default; + Box &operator=(Box &&) = default; + + template Box(const Box &b) : is_full(b.is_full) {} + + /** Create box from lower (inclusive) and upper (exclusive) bound + */ + Box(const Point &lo, const Point &hi) : is_full(false) {} + /** Create box holding a single point + */ + explicit Box(const Point &p) : is_full(true) {} + + // Predicates + size_type ndims() const { return D; } + bool empty() const { return !is_full; } + Point lower() const { return Point(); } + Point upper() const { return Point(); } + Point shape() const { return Point(); } + size_type size() const { return is_full; } + + // Shift and scale operators + Box &operator>>=(const Point &p) { return *this; } + Box &operator<<=(const Point &p) { return *this; } + Box &operator*=(const Point &p) { return *this; } + Box operator>>(const Point &p) const { return Box(*this); } + Box operator<<(const Point &p) const { return Box(*this); } + Box operator*(const Point &p) const { return Box(*this); } + Box grown(const Point &dlo, const Point &dhi) const { + return *this; + } + Box grown(const Point &d) const { return *this; } + Box grown(const T &d) const { return *this; } + Box shrunk(const Point &dlo, const Point &dhi) const { + return *this; + } + Box shrunk(const Point &d) const { return *this; } + Box shrunk(const T &d) const { return *this; } + + // Comparison operators + friend bool operator==(const Box &b1, const Box &b2) { + return b1.empty() == b2.empty(); + } + friend bool operator!=(const Box &b1, const Box &b2) { return !(b1 == b2); } + + // Set comparison operators + bool contains(const Point &p) const { return !empty(); } + friend bool isdisjoint(const Box &b1, const Box &b2) { + return b1.empty() || b2.empty(); + } + friend bool operator<=(const Box &b1, const Box &b2) { + return !b1.empty() <= !b2.empty(); + } + friend bool operator>=(const Box &b1, const Box &b2) { return b2 <= b1; } + friend bool operator<(const Box &b1, const Box &b2) { + return b1 <= b2 && b1 != b2; + } + friend bool operator>(const Box &b1, const Box &b2) { return b2 < b1; } + bool issubset(const Box &b) const { return *this <= b; } + bool issuperset(const Box &b) const { return *this >= b; } + bool is_strict_subset(const Box &b) const { return *this < b; } + bool is_strict_superset(const Box &b) const { return *this > b; } + + // Set operations + friend Box bounding_box(const Box &b1, const Box &b2) { + return b1.empty() && b2.empty() ? Box() : Box(Point()); + } + + friend Box operator&(const Box &b1, const Box &b2) { + if (!b1.empty() & !b2.empty()) + return Box(Point()); + return Box(); + } + friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } + + friend bool operator==(const Box &b, const std::vector &bs) { + // This assumes that the elements of bs are disjoint + return b.empty() == bs.empty(); + } + friend bool operator==(const std::vector &bs, const Box &b) { + return b == bs; + } + friend bool operator!=(const Box &b, const std::vector &bs) { + return !(b == bs); + } + friend bool operator!=(const std::vector &bs, const Box &b) { + return !(bs == b); + } + + friend std::vector operator-(const Box &b1, const Box &b2) { + if (!b1.empty() > b2.empty()) + return std::vector{Box(Point())}; + return std::vector{}; + } + friend std::vector difference(const Box &b1, const Box &b2) { + return b1 - b2; + } + + friend std::vector operator|(const Box &b1, const Box &b2) { + if (!b1.empty() | !b2.empty()) + return std::vector{Box(Point())}; + return std::vector{}; + } + friend std::vector setunion(const Box &b1, const Box &b2) { + return b1 | b2; + } + + friend std::vector operator^(const Box &b1, const Box &b2) { + if (!b1.empty() ^ !b2.empty()) + return std::vector{Box(Point())}; + return std::vector{}; + } + friend std::vector symmetric_difference(const Box &b1, const Box &b2) { + return b1 ^ b2; + } + + /** Output a box + */ + friend std::ostream &operator<<(std::ostream &os, const Box &b) { + return os << "(" << b.is_full << ")"; + } +}; + +template class Box { + Point lo, hi; + +public: + typedef typename Point::value_type value_type; + typedef typename Point::size_type size_type; + + /** Create empty box + */ + Box() = default; + + Box(const Box &) = default; + Box(Box &&) = default; + Box &operator=(const Box &) = default; + Box &operator=(Box &&) = default; + + template Box(const Box &b) : lo(b.lo), hi(b.hi) {} + + /** Create box from lower (inclusive) and upper (exclusive) bound + */ + Box(const Point &lo, const Point &hi) : lo(lo), hi(hi) {} + /** Create box holding a single point + */ + explicit Box(const Point &p) : lo(p), hi(p + T(1)) {} + + // Predicates + size_type ndims() const { return D; } + bool empty() const { return any(hi <= lo); } + Point lower() const { return lo; } + Point upper() const { return hi; } + Point shape() const { return max(hi - lo, T(0)); } + size_type size() const { return product(shape()); } + + // Shift and scale operators + Box &operator>>=(const Point &p) { + lo += p; + hi += p; + return *this; + } + Box &operator<<=(const Point &p) { + lo -= p; + hi -= p; + return *this; + } + Box &operator*=(const Point &p) { + lo *= p; + hi *= p; + return *this; + } + Box operator>>(const Point &p) const { return Box(*this) >>= p; } + Box operator<<(const Point &p) const { return Box(*this) <<= p; } + Box operator*(const Point &p) const { return Box(*this) *= p; } + Box grown(const Point &dlo, const Point &dup) const { + if (empty()) + return Box(); + Box nb(*this); + nb.lo -= dlo; + nb.hi += dup; + return nb; + } + Box grown(const Point &d) const { return grown(d, d); } + Box grown(const T &d) const { return grown(Point::pure(d)); } + Box shrunk(const Point &dlo, const Point &dup) const { + return grown(-dlo, -dup); + } + Box shrunk(const Point &d) const { return shrunk(d, d); } + Box shrunk(const T &d) const { return shrunk(Point::pure(d)); } + + // Comparison operators + friend bool operator==(const Box &b1, const Box &b2) { + if (b1.empty() && b2.empty()) + return true; + if (b1.empty() || b2.empty()) + return false; + return all(b1.lo == b2.lo && b1.hi == b2.hi); + } + friend bool operator!=(const Box &b1, const Box &b2) { return !(b1 == b2); } + + // Set comparison operators + bool contains(const Point &p) const { + if (empty()) + return false; + return all(p >= lo && p < hi); + } + friend bool isdisjoint(const Box &b1, const Box &b2) { + return (b1 & b2).empty(); + } + friend bool operator<=(const Box &b1, const Box &b2) { + if (b1.empty()) + return true; + if (b2.empty()) + return false; + return all(b1.lo >= b2.lo && b1.hi <= b2.hi); + } + friend bool operator>=(const Box &b1, const Box &b2) { return b2 <= b1; } + friend bool operator<(const Box &b1, const Box &b2) { + return b1 <= b2 && b1 != b2; + } + friend bool operator>(const Box &b1, const Box &b2) { return b2 < b1; } + bool issubset(const Box &b) const { return *this <= b; } + bool issuperset(const Box &b) const { return *this >= b; } + bool is_strict_subset(const Box &b) const { return *this < b; } + bool is_strict_superset(const Box &b) const { return *this > b; } + + // Set operations + friend Box bounding_box(const Box &b1, const Box &b2) { + if (b1.empty()) + return b2; + if (b2.empty()) + return b1; + auto r = Box(min(b1.lo, b2.lo), max(b1.hi, b2.hi)); +// Postcondition +#if REGIONS_DEBUG + assert(b1 <= r && b2 <= r); +#endif + return r; + } + + friend Box operator&(const Box &b1, const Box &b2) { + auto nlo = max(b1.lo, b2.lo); + auto nhi = min(b1.hi, b2.hi); + auto r = Box(nlo, nhi); +// Postcondition +#if REGIONS_DEBUG + assert(r <= b1 && r <= b2); +#endif + return r; + } + friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } + + friend bool operator==(const Box &b, const std::vector &bs) { + // This assumes that the elements of bs are disjoint + size_type sz = 0; + for (const Box &c : bs) + sz += c.size(); + if (b.size() != sz) + return false; + for (const Box &c : bs) + if (!(c <= b)) + return false; + return true; + } + friend bool operator==(const std::vector &bs, const Box &b) { + return b == bs; + } + friend bool operator!=(const Box &b, const std::vector &bs) { + return !(b == bs); + } + friend bool operator!=(const std::vector &bs, const Box &b) { + return !(bs == b); + } + +private: + /** Split a box at a point, creating up to 2^D new boxes + */ + void split(const Point &p, std::vector &rs) const { + assert(!empty()); +#if REGIONS_DEBUG + const auto old_rs_size = rs.size(); +#endif + for (int m = 0; m < (1 << D); ++m) { + Point newlo = lo, newhi = hi; + bool is_inside = true; + for (int d = 0; d < D; ++d) { + const bool lohi = m & (1 << d); + if (p[d] > lo[d] && p[d] < hi[d]) { + if (!lohi) + newhi[d] = p[d]; + else + newlo[d] = p[d]; + } else { + is_inside &= !lohi; + } + } + if (is_inside) + rs.push_back(Box(newlo, newhi)); + } +#if REGIONS_DEBUG + // Postcondition + size_type vol = 0; + for (auto i = old_rs_size; i < rs.size(); ++i) { + assert(!rs[i].empty()); + assert(rs[i] <= *this); + vol += rs[i].size(); + } + assert(vol == size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(rs[i].isdisjoint(rs[j])); +#endif + } + +public: + std::vector operator-(const Box &b) const { + if (empty()) + return std::vector{}; + if (b.empty()) + return std::vector{*this}; + std::vector bs1; + split(b.lo, bs1); + std::vector bs2; + for (const auto &b1 : bs1) + b1.split(b.hi, bs2); + std::vector rs; + for (const auto &b2 : bs2) { + assert(isdisjoint(b2, b) || b2 <= b); + if (isdisjoint(b2, b)) + rs.push_back(b2); + } +#if REGIONS_DEBUG + // Postcondition + size_type vol = 0; + for (const auto &r : rs) { + assert(!r.empty()); + assert(r <= *this && !(r <= b)); + vol += r.size(); + } + assert(vol >= max(size_type(0), size() - b.size()) && vol <= size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(rs[i].isdisjoint(rs[j])); +#endif + return rs; + } + friend std::vector difference(const Box &b1, const Box &b2) { + return b1 - b2; + } + + friend std::vector operator|(const Box &b1, const Box &b2) { + auto rs = b1 - b2; + if (!b2.empty()) + rs.push_back(b2); +#if REGIONS_DEBUG + // Postcondition + size_type vol = 0; + for (const auto &r : rs) { + assert(!r.empty()); + assert(r <= b1 || r <= b2); + vol += r.size(); + } + assert(vol >= b1.size() && vol <= b1.size() + b2.size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(isdisjoint(rs[i], rs[j])); +#endif + return rs; + } + friend std::vector setunion(const Box &b1, const Box &b2) { + return b1 | b2; + } + + friend std::vector operator^(const Box &b1, const Box &b2) { + auto rs = b1 - b2; + auto rs1 = b2 - b1; + // TODO: Avoid this concatenation + rs.insert(rs.end(), rs1.begin(), rs1.end()); +#if REGIONS_DEBUG + // Postcondition + size_type vol = 0; + for (const auto &r : rs) { + assert(!r.empty()); + assert((r <= b1) ^ (r <= b2)); + vol += r.size(); + } + assert(vol >= abs(b1.size() - b2.size()) && vol <= b1size() + b2.size()); + for (size_t i = 0; i < rs.size(); ++i) + for (size_t j = i + 1; j < rs.size(); ++j) + assert(isdisjoint(rs[i], rs[j])); +#endif + return rs; + } + friend std::vector symmetric_difference(const Box &b1, const Box &b2) { + return b1 ^ b2; + } + + /** Output a box + */ + friend std::ostream &operator<<(std::ostream &os, const Box &b) { + return os << "(" << b.lo << ":" << b.hi << ")"; + } +}; + +} // namespace Regions +} // namespace openPMD + +namespace std { + +template +struct equal_to>; + +template struct equal_to> { + constexpr bool operator()(const openPMD::Regions::Box &x, + const openPMD::Regions::Box &y) const { + return x.empty() == y.empty(); + } +}; + +template +struct equal_to> { + constexpr bool operator()(const openPMD::Regions::Box &x, + const openPMD::Regions::Box &y) const { + if (x.empty() && y.empty()) + return true; + if (x.empty() || y.empty()) + return false; + return openPMD::Regions::detail::tuple_eq(make_tuple(x.lower(), x.upper()), + make_tuple(y.lower(), y.upper())); + } +}; + +template struct hash> { + constexpr size_t operator()(const openPMD::Regions::Box &x) const { + if (x.empty()) + return size_t(0xc9df21a36550a048ULL); + hash> h; + return openPMD::Regions::detail::hash_combine(h(x.lower()), h(x.upper())); + } +}; + +template struct less>; + +template struct less> { + constexpr bool operator()(const openPMD::Regions::Box &x, + const openPMD::Regions::Box &y) const { + return !x.empty() < !y.empty(); + } +}; + +template struct less> { + constexpr bool operator()(const openPMD::Regions::Box &x, + const openPMD::Regions::Box &y) const { + if (x.empty() && y.empty()) + return false; + if (x.empty()) + return true; + if (y.empty()) + return false; + return openPMD::Regions::detail::tuple_lt(make_tuple(x.lower(), x.upper()), + make_tuple(y.lower(), y.upper())); + } +}; + +} // namespace std + +#endif // #ifndef REGIONS_BOX_HPP diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 29a649606b..668c985096 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -133,8 +133,16 @@ template class VPoint { virtual T product1() const = 0; virtual T sum1() const = 0; - virtual bool operator==(const VPoint &x) const = 0; - virtual bool operator!=(const VPoint &x) const = 0; + virtual std::unique_ptr operator==(const VPoint &x) const = 0; + virtual std::unique_ptr operator!=(const VPoint &x) const = 0; + virtual std::unique_ptr operator<(const VPoint &x) const = 0; + virtual std::unique_ptr operator>(const VPoint &x) const = 0; + virtual std::unique_ptr operator<=(const VPoint &x) const = 0; + virtual std::unique_ptr operator>=(const VPoint &x) const = 0; + + virtual bool equal_to1(const VPoint &x) const = 0; + virtual std::size_t hash1() const = 0; + virtual bool less1(const VPoint &x) const = 0; virtual std::ostream &output(std::ostream &os) const = 0; }; @@ -190,7 +198,9 @@ template class WPoint final : public VPoint { return fold(op, r, p, dynamic_cast(x).p); } - void set_from_vector(const std::vector &vec) override { *this = WPoint(vec); } + void set_from_vector(const std::vector &vec) override { + *this = WPoint(vec); + } operator std::vector() const override { return std::vector(p); } constexpr size_type size() const override { return p.size(); } @@ -414,11 +424,31 @@ template class WPoint final : public VPoint { T product1() const override { return product(p); } T sum1() const override { return sum(p); } - bool operator==(const VPoint &x) const override { - return p == dynamic_cast(x).p; + std::unique_ptr> operator==(const VPoint &x) const override { + return std::make_unique(p == dynamic_cast(x).p); + } + std::unique_ptr> operator!=(const VPoint &x) const override { + return std::make_unique(p != dynamic_cast(x).p); + } + std::unique_ptr> operator<(const VPoint &x) const override { + return std::make_unique(p < dynamic_cast(x).p); } - bool operator!=(const VPoint &x) const override { - return p != dynamic_cast(x).p; + std::unique_ptr> operator>(const VPoint &x) const override { + return std::make_unique(p > dynamic_cast(x).p); + } + std::unique_ptr> operator<=(const VPoint &x) const override { + return std::make_unique(p <= dynamic_cast(x).p); + } + std::unique_ptr> operator>=(const VPoint &x) const override { + return std::make_unique(p >= dynamic_cast(x).p); + } + + bool equal_to1(const VPoint &x) const override { + return std::equal_to>()(p, dynamic_cast(x).p); + } + std::size_t hash1() const override { return std::hash>()(p); } + bool less1(const VPoint &x) const override { + return std::less>()(p, dynamic_cast(x).p); } std::ostream &output(std::ostream &os) const override { return os << p; } @@ -455,10 +485,12 @@ std::unique_ptr> make_VPoint(const std::size_t D) { * fixed-size vectors that support arithmetic operations. */ template class NDPoint { - template using VPoint = detail::VPoint; template friend class NDPoint; + friend struct std::equal_to; + friend struct std::hash; + friend struct std::less; std::unique_ptr> p; @@ -752,12 +784,24 @@ template class NDPoint { friend T product(const NDPoint &x) { return x.p->product1(); } friend T sum(const NDPoint &x) { return x.p->sum1(); } - friend bool operator==(const NDPoint &x, const NDPoint &y) { + friend NDPoint operator==(const NDPoint &x, const NDPoint &y) { return *x.p == *y.p; } - friend bool operator!=(const NDPoint &x, const NDPoint &y) { + friend NDPoint operator!=(const NDPoint &x, const NDPoint &y) { return *x.p != *y.p; } + friend NDPoint operator<(const NDPoint &x, const NDPoint &y) { + return *x.p < *y.p; + } + friend NDPoint operator>(const NDPoint &x, const NDPoint &y) { + return *x.p > *y.p; + } + friend NDPoint operator<=(const NDPoint &x, const NDPoint &y) { + return *x.p <= *y.p; + } + friend NDPoint operator>=(const NDPoint &x, const NDPoint &y) { + return *x.p >= *y.p; + } /** Output a point */ @@ -773,4 +817,38 @@ template class NDPoint { } // namespace Regions } // namespace openPMD +namespace std { + +template struct equal_to> { + constexpr bool operator()(const openPMD::Regions::NDPoint &x, + const openPMD::Regions::NDPoint &y) const { + if (!x.has_value() && !y.has_value()) + return true; + if (!x.has_value() || !y.has_value()) + return false; + return x.p->equal_to1(*y.p); + } +}; + +template struct hash> { + constexpr size_t operator()(const openPMD::Regions::NDPoint &x) const { + if (!x.has_value()) + return size_t(0xf458b18eca40aef1ULL); + return x.p->hash1(); + } +}; + +template struct less> { + constexpr bool operator()(const openPMD::Regions::NDPoint &x, + const openPMD::Regions::NDPoint &y) const { + if (!x.has_value()) + return true; + if (!y.has_value()) + return false; + return x.p->less1(*y.p); + } +}; + +} // namespace std + #endif // #ifndef REGIONS_NDPOINT_HPP diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index 61e69ccb4b..0ab4c62e26 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -30,6 +30,10 @@ namespace Regions { template class Point { std::array elts; + friend struct std::equal_to; + friend struct std::hash; + friend struct std::less; + public: /** Component type */ @@ -432,11 +436,42 @@ template class Point { return fold([](const T &r, const T &a) { return r + a; }, T(0), x); } - friend constexpr bool operator==(const Point &x, const Point &y) { - return all(fmap([](const T &a, const T &b) { return a == b; }, x, y)); + friend constexpr Point operator==(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a == b; }, x, y); + } + friend constexpr Point operator!=(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a != b; }, x, y); + } + friend constexpr Point operator<(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a < b; }, x, y); + } + friend constexpr Point operator>(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a > b; }, x, y); + } + friend constexpr Point operator<=(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a <= b; }, x, y); + } + friend constexpr Point operator>=(const Point &x, const Point &y) { + return fmap([](const T &a, const T &b) { return a >= b; }, x, y); + } + + friend constexpr Point operator==(const Point &x, const T &b) { + return fmap([&](const T &a) { return a == b; }, x); + } + friend constexpr Point operator!=(const Point &x, const T &b) { + return fmap([&](const T &a) { return a != b; }, x); + } + friend constexpr Point operator<(const Point &x, const T &b) { + return fmap([&](const T &a) { return a < b; }, x); + } + friend constexpr Point operator>(const Point &x, const T &b) { + return fmap([&](const T &a) { return a > b; }, x); + } + friend constexpr Point operator<=(const Point &x, const T &b) { + return fmap([&](const T &a) { return a <= b; }, x); } - friend constexpr bool operator!=(const Point &x, const Point &y) { - return any(fmap([](const T &a, const T &b) { return a != b; }, x, y)); + friend constexpr Point operator>=(const Point &x, const T &b) { + return fmap([&](const T &a) { return a >= b; }, x); } /** Output a point @@ -460,32 +495,31 @@ namespace std { template struct equal_to> { - constexpr bool operator()(const openPMD::Regions::Point &p, - const openPMD::Regions::Point &q) const { - return openPMD::Regions::Point::all( - openPMD::Regions::Point::fmap( - [](const T &a, const T &b) { return equal_to()(a, b); }, p, q)); + constexpr bool operator()(const openPMD::Regions::Point &x, + const openPMD::Regions::Point &y) const { + const equal_to> eq; + return eq(x.elts, y.elts); } }; template struct hash> { - constexpr size_t operator()(const openPMD::Regions::Point &p) const { - return openPMD::Regions::Point::fold( - [](size_t r, const T &b) { - return openPMD::Regions::detail::hash_combine(r, b); + constexpr size_t operator()(const openPMD::Regions::Point &x) const { + const hash h; + return fold( + [&](size_t r, const T &b) { + return openPMD::Regions::detail::hash_combine(r, h(b)); }, - size_t(0xb22da17173243869ULL), p); + size_t(0xb22da17173243869ULL), x); } }; template struct less> { - constexpr bool operator()(const openPMD::Regions::Point &p, - const openPMD::Regions::Point &q) const { - return openPMD::Regions::Point::fold( - [](bool r, const T &a, const T &b) { return r && less()(a, b); }, - true, p, q); + constexpr bool operator()(const openPMD::Regions::Point &x, + const openPMD::Regions::Point &y) const { + const less> lt; + return lt(x.elts, y.elts); } }; diff --git a/include/openPMD/regions/Regions.hpp b/include/openPMD/regions/Regions.hpp index 6fdaffcdac..d0ef5617c3 100644 --- a/include/openPMD/regions/Regions.hpp +++ b/include/openPMD/regions/Regions.hpp @@ -1,7 +1,11 @@ #ifndef REGIONS_REGIONS_HPP #define REGIONS_REGIONS_HPP +#define REGIONS_DEBUG 1 // 0 or 1 + #include "Point.hpp" +#include "Box.hpp" + #include "NDPoint.hpp" #endif // #ifndef REGIONS_REGIONS_HPP diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp new file mode 100644 index 0000000000..902f0569de --- /dev/null +++ b/test/RegionsBoxTest.cpp @@ -0,0 +1,250 @@ +#include + +#include + +#include +#include +#include +#include +#include + +using namespace openPMD::Regions; + +template void test_Box(const B &b) { + REQUIRE(b.empty()); + const std::size_t D = b.ndims(); + using T = typename B::value_type; + const auto p = b.lower(); + typedef std::decay_t P; + const std::equal_to

eqp; + const std::equal_to eqb; + const std::less

ltp; + const std::less ltb; + + std::mt19937 gen; + std::uniform_int_distribution dist0(0, 9); + std::uniform_int_distribution dist(-1000, 1000); + const auto rand = [&]() { return dist(gen); }; + const auto randp = [&]() { return fmap([&](auto) { return rand(); }, p); }; + const auto randb = [&]() { + if (D == 0) { + if (dist0(gen) < 5) + return B(); + else + return B(p); + } + if (dist0(gen) == 0) + return b; + while (1) { + auto lo = randp(); + auto hi = randp(); + auto nb = B(lo, hi); + if (!nb.empty()) + return nb; + } + }; + + for (int iter = 0; iter < 100; ++iter) { + + B N(b); + REQUIRE(N.ndims() == D); + REQUIRE(N.empty()); + for (std::size_t d = 0; d < D; ++d) + REQUIRE(N.lower()[d] >= N.upper()[d]); + + const B X = randb(); + const B Y = randb(); + const B Z = randb(); + + const P n = fmap([](auto) { return T(0); }, p); + REQUIRE(eqp(n + n, n)); + const P x = randp(); + const P y = randp(); + const P z = randp(); + + const T a = rand(); + const T b = rand(); + + REQUIRE(N.empty()); + if (D > 0) { + REQUIRE(X.empty() == all(X.shape() == 0)); + REQUIRE(Y.empty() == all(Y.shape() == 0)); + REQUIRE(Z.empty() == all(Z.shape() == 0)); + } + + REQUIRE(X.empty() == (X.size() == 0)); + REQUIRE(Y.empty() == (Y.size() == 0)); + REQUIRE(Z.empty() == (Z.size() == 0)); + + if (D > 0) { + REQUIRE(X.empty() == all(fmap([](auto a, auto b) { return a >= b; }, + X.lower(), X.upper()))); + REQUIRE(Y.empty() == all(fmap([](auto a, auto b) { return a >= b; }, + Y.lower(), Y.upper()))); + REQUIRE(Z.empty() == all(fmap([](auto a, auto b) { return a >= b; }, + Z.lower(), Z.upper()))); + } + + REQUIRE(eqb(N, N)); + REQUIRE(eqb(X, X)); + REQUIRE(!ltb(N, N)); + REQUIRE(!ltb(X, X)); + if (X.empty()) { + REQUIRE(eqb(N, X)); + REQUIRE(!ltb(N, X)); + } else { + REQUIRE(!eqb(N, X)); + REQUIRE(ltb(N, X)); + } + + REQUIRE(eqb((X >> x) << x, X)); + REQUIRE(eqb(X >> x, X << -x)); + REQUIRE(eqb(X >> x, X << -x)); + REQUIRE(eqb(X >> (x + y), (X >> x) >> y)); + + REQUIRE(eqb((X * x) * y, X * (x * y))); + REQUIRE(eqb((X >> x) * y, (X * y) >> (x * y))); + + REQUIRE(eqb(X.grown(1), X) == (D == 0 || X.empty())); + if (all(x >= 0 && y >= 0)) + REQUIRE(eqb(X.grown(x).grown(y), X.grown(x + y))); + else + REQUIRE((X.grown(x).grown(y).empty() || + eqb(X.grown(x).grown(y), X.grown(x + y))) == true); + if (all(x >= 0)) + REQUIRE(eqb(X.grown(x).grown(-x), X)); + else + REQUIRE((X.grown(x).grown(-x).empty() || eqb(X.grown(x).grown(-x), X)) == + true); + REQUIRE(eqb(X.grown(x), X.grown(x, x))); + REQUIRE(eqb(X.grown(a), X.grown(P::pure(a)))); + + REQUIRE(eqb(X.shrunk(x, y), X.grown(-x, -y))); + REQUIRE(eqb(X.shrunk(x), X.shrunk(x, x))); + REQUIRE(eqb(X.shrunk(a), X.shrunk(P::pure(a)))); + + REQUIRE(N == N); + REQUIRE(X == X); + REQUIRE((N == X) == X.empty()); + REQUIRE(!(N != N)); + REQUIRE(!(X != X)); + REQUIRE((N != X) != (N == X)); + + REQUIRE(X.contains(X.lower()) == !X.empty()); + REQUIRE(X.contains(X.upper() - 1) == !X.empty()); + REQUIRE(X.grown(1).contains(X.upper()) == !X.empty()); + REQUIRE(isdisjoint(X, X) == X.empty()); + + // a <= b means "a implies b" for booleans (yes, it looks wrong) + REQUIRE((X < Y) <= (X <= Y)); + REQUIRE((X > Y) <= (X >= Y)); + REQUIRE((X <= Y) <= (X.empty() || !isdisjoint(X, Y))); + REQUIRE((X >= Y) <= (Y.empty() || !isdisjoint(X, Y))); + REQUIRE(!(X < Y && Y < X)); + REQUIRE((X <= Y && X >= Y) == (X == Y)); + REQUIRE((X < X.grown(1)) == (D > 0 && !X.empty())); + REQUIRE((X.shrunk(1) < X) == (D > 0 && !X.empty())); + + REQUIRE(N <= N); + REQUIRE(!(N < N)); + REQUIRE(N <= X); + REQUIRE((N < X) == !X.empty()); + + const auto BXY = bounding_box(X, Y); + REQUIRE(bounding_box(N, X) == X); + REQUIRE(bounding_box(X, N) == X); + REQUIRE(bounding_box(X, Y) == bounding_box(Y, X)); + REQUIRE(bounding_box(bounding_box(X, Y), Z) == + bounding_box(X, bounding_box(Y, Z))); + + REQUIRE(X <= BXY); + REQUIRE(Y <= BXY); + REQUIRE((X.grown(1) <= BXY && Y.grown(1) <= BXY) == + (D == 0 || BXY.empty())); + REQUIRE( + eqb(bounding_box(X.grown(abs(x)), Y.grown(abs(x))), BXY.grown(abs(x)))); + REQUIRE(eqb(bounding_box(X >> x, Y >> x), BXY >> x)); + REQUIRE(eqb(bounding_box(X * x, Y * x), BXY * x)); + REQUIRE( + eqb(bounding_box(X.grown(abs(x)), Y.grown(abs(x))), BXY.grown(abs(x)))); + + const B E = bounding_box(bounding_box(X, Y), Z).grown(10); + + REQUIRE((N & X) == N); + REQUIRE((X & N) == N); + REQUIRE((E & X) == X); + REQUIRE((X & E) == X); + REQUIRE((X & Y) == (Y & X)); + REQUIRE(((X & Y) & Z) == (X & (Y & Z))); + + REQUIRE((N | X) == X); + REQUIRE((E | X) == E); + REQUIRE((X | E) == E); + // REQUIRE((X | Y) == (Y | X)); + // REQUIRE(((X | Y) | Z) == (X | (Y | Z))); + + // REQUIRE(E - (X & Y) == ((E - X) | (E - Y))); + // REQUIRE(E - (X | Y) == ((E - X) & (E - Y))); + + const B IXY = X & Y; + REQUIRE((IXY <= X && IXY <= Y) == true); + REQUIRE((IXY.grown(1) <= X && IXY.grown(1) <= Y) == + (D == 0 || IXY.empty())); + + const std::vector UXY = X | Y; + for (const auto &U : UXY) + REQUIRE((U <= X || U <= Y) == true); + + const std::vector DXY = X - Y; + for (const auto &D : DXY) + REQUIRE((D <= X || !isdisjoint(D, Y)) == true); + + const std::vector SXY = X ^ Y; + for (const auto &S : SXY) + REQUIRE(((S <= X || S <= Y) && isdisjoint(S, IXY)) == true); + + // REQUIRE(IXY <= UXY); + // REQUIRE(isdisjoint(IXY, SXY)); + // REQUIRE((IXY | SXY) == UXY); + + } // for iter +} + +TEST_CASE("Box", "[regions]") { + test_Box(Box()); +} +TEST_CASE("Box", "[regions]") { + test_Box(Box()); +} +TEST_CASE("Box", "[regions]") { + test_Box(Box()); +} +TEST_CASE("Box", "[regions]") { + test_Box(Box()); +} + +TEST_CASE("Box", "[regions]") { test_Box(Box()); } +TEST_CASE("Box", "[regions]") { test_Box(Box()); } +TEST_CASE("Box", "[regions]") { test_Box(Box()); } +TEST_CASE("Box", "[regions]") { test_Box(Box()); } + +#warning "TODO" +#if 0 +TEST_CASE("NDBox(0)", "[regions]") { + test_Box(NDBox(0)); +} +TEST_CASE("NDBox(1)", "[regions]") { + test_Box(NDBox(1)); +} +TEST_CASE("NDBox(2)", "[regions]") { + test_Box(NDBox(2)); +} +TEST_CASE("NDBox(3)", "[regions]") { + test_Box(NDBox(3)); +} + +TEST_CASE("NDBox(0)", "[regions]") { test_Box(NDBox(0)); } +TEST_CASE("NDBox(1)", "[regions]") { test_Box(NDBox(1)); } +TEST_CASE("NDBox(2)", "[regions]") { test_Box(NDBox(2)); } +TEST_CASE("NDBox(3)", "[regions]") { test_Box(NDBox(3)); } +#endif diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp new file mode 100644 index 0000000000..158145651c --- /dev/null +++ b/test/RegionsPointTest.cpp @@ -0,0 +1,507 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace openPMD::Regions; + +template +auto maxabs(const T &xs) { + if (xs.size() == 0) + return VT(0); + using std::abs, std::max_element; + return max_element(abs(xs)); +} +template > * = nullptr> +auto maxabs(const T &x) { + using std::abs; + return abs(x); +} +template bool is_approx(const T &xs, const T &ys) { + using std::max; + auto m = maxabs(xs - ys); + if (m == 0) + return true; + m /= max(maxabs(xs), maxabs(ys)); + const auto eps = std::numeric_limits::epsilon(); + return m <= 100 * eps; +} + +template void test_Point_bool(const P &p) { + const std::size_t D = p.ndims(); + using T = typename P::value_type; + const std::equal_to

eq; + const std::less

lt; + + std::mt19937 gen; + std::uniform_int_distribution dist(0, 1); + const auto rand = [&]() { return dist(gen); }; + const auto randp = [&]() { return fmap([&](auto) { return rand(); }, p); }; + + for (int iter = 0; iter < 100; ++iter) { + + P n = p; + REQUIRE(n.ndims() == D); + for (std::size_t d = 0; d < D; ++d) + REQUIRE(n[d] == 0); + REQUIRE(n.size() == D); + + const P x = randp(); + const P y = randp(); + const P z = randp(); + + const T a = rand(); + const T b = rand(); + + REQUIRE(eq(n, n)); + REQUIRE(eq(x, x)); + REQUIRE(!lt(n, n)); + REQUIRE(!lt(x, x)); + if (all(x == n)) { + REQUIRE(eq(n, x)); + REQUIRE(!lt(n, x)); + } else { + REQUIRE(!eq(n, x)); + REQUIRE(lt(n, x)); + } + + REQUIRE(!any(n)); + REQUIRE(all(!n)); + + REQUIRE(eq(n & x, n)); + REQUIRE(eq(0 & x, n)); + REQUIRE(eq(x & n, n)); + REQUIRE(eq(x & 0, n)); + + REQUIRE(eq((!n & x), x)); + REQUIRE(eq((T(1) & x), x)); + REQUIRE(eq((x & !n), x)); + REQUIRE(eq((x & T(1)), x)); + + REQUIRE(eq((n | x), x)); + REQUIRE(eq((0 | x), x)); + REQUIRE(eq((x | n), x)); + REQUIRE(eq((x | 0), x)); + + REQUIRE(eq((!n | x), !n)); + REQUIRE(eq((T(1) | x), !n)); + REQUIRE(eq((x | !n), !n)); + REQUIRE(eq((x | T(1)), !n)); + + REQUIRE(eq((x & y), (y & x))); + REQUIRE(eq((x | y), (y | x))); + + REQUIRE(eq(((x & y) & z), (x & (y & z)))); + REQUIRE(eq(((x | y) | z), (x | (y | z)))); + + REQUIRE(eq((x & (y | z)), ((y & x) | (x & z)))); + REQUIRE(eq((x | (y & z)), ((y | x) & (x | z)))); + + REQUIRE(eq((x & y), !(!x | !y))); + REQUIRE(eq((x | y), !(!x & !y))); + + REQUIRE(eq((n ^ x), x)); + REQUIRE(eq((0 ^ x), x)); + REQUIRE(eq((x ^ n), x)); + REQUIRE(eq((x ^ 0), x)); + + REQUIRE(eq((!n ^ x), !x)); + REQUIRE(eq((T(1) ^ x), !x)); + REQUIRE(eq((x ^ !n), !x)); + REQUIRE(eq((x ^ T(1)), !x)); + + REQUIRE(eq((x ^ x), n)); + + REQUIRE(eq((x ^ y), (y ^ x))); + REQUIRE(eq(((x ^ y) ^ z), (x ^ (y ^ z)))); + + REQUIRE(eq(!(!x), x)); + + REQUIRE(eq((n && x), n)); + REQUIRE(eq((0 && x), n)); + REQUIRE(eq((x && n), n)); + REQUIRE(eq((x && 0), n)); + + REQUIRE(eq((!n && x), x)); + REQUIRE(eq((!T(0) && x), x)); + REQUIRE(eq((x && !n), x)); + REQUIRE(eq((x && !T(0)), x)); + + REQUIRE(eq((n || x), x)); + REQUIRE(eq((0 || x), x)); + REQUIRE(eq((x || n), x)); + REQUIRE(eq((x || 0), x)); + + REQUIRE(eq((!n || x), !n)); + REQUIRE(eq((!T(0) || x), !n)); + REQUIRE(eq((x || !n), !n)); + REQUIRE(eq((x || !T(0)), !n)); + + REQUIRE(eq((x && y), (y && x))); + REQUIRE(eq((x || y), (y || x))); + + REQUIRE(eq(((x && y) && z), (x && (y && z)))); + REQUIRE(eq(((x || y) || z), (x || (y || z)))); + + REQUIRE(eq((x && (y || z)), ((y && x) || (x && z)))); + REQUIRE(eq((x || (y && z)), ((y || x) && (x || z)))); + + REQUIRE(eq((x && y), !(!x || !y))); + REQUIRE(eq((x || y), !(!x && !y))); + + P t; + t = x; + t &= y; + REQUIRE(eq(t, (x & y))); + t = x; + t |= y; + REQUIRE(eq(t, (x | y))); + t = x; + t ^= y; + REQUIRE(eq(t, (x ^ y))); + + } // for iter +} + +template void test_Point_int(const P &p) { + const std::size_t D = p.ndims(); + using T = typename P::value_type; + const std::equal_to

eq; + + std::mt19937 gen; + std::uniform_int_distribution dist(-1000, 1000); + const auto rand = [&]() { return dist(gen); }; + const auto randp = [&]() { return fmap([&](auto) { return rand(); }, p); }; + + for (int iter = 0; iter < 100; ++iter) { + + P n(p); + REQUIRE(n.size() == D); + for (std::size_t d = 0; d < D; ++d) + REQUIRE(n[d] == 0); + + const P x = randp(); + const P y = randp(); + const P z = randp(); + + const T a = rand(); + const T b = rand(); + + REQUIRE(eq(fmap([](auto i) { return i; }, x), x)); + REQUIRE(eq(fmap([](auto i) { return i + 1; }, + fmap([](auto i) { return 2 * i; }, x)), + fmap([](auto i) { return 2 * i + 1; }, x))); + + REQUIRE( + eq(fmap([](auto i, auto j) { return 2 * i + j; }, x, y), 2 * x + y)); + REQUIRE(eq( + fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, z), + 3 * x + 2 * y + z)); + + REQUIRE(fold([](auto i, auto j) { return i + j; }, 0, x) == sum(x)); + REQUIRE(fold([](auto i, auto j, auto k) { return i + j + k; }, 0, x, y) == + sum(x + y)); + + REQUIRE(sum(n) == 0); + REQUIRE(sum(n + 1) == D); + REQUIRE(product(n) == (D == 0 ? 1 : 0)); + REQUIRE(product(n + 1) == 1); + REQUIRE(min_element(n) == (D == 0 ? std::numeric_limits::max() : 0)); + REQUIRE(max_element(n) == (D == 0 ? std::numeric_limits::min() : 0)); + REQUIRE(min_element(n + 1) == (D == 0 ? std::numeric_limits::max() : 1)); + REQUIRE(max_element(n + 1) == (D == 0 ? std::numeric_limits::min() : 1)); + + REQUIRE(eq(+x, x)); + REQUIRE(eq(n + x, x)); + REQUIRE(eq(T(0) + x, x)); + REQUIRE(eq(x + n, x)); + REQUIRE(eq(x + T(0), x)); + + REQUIRE(eq(x + y, y + x)); + + REQUIRE(eq((x + y) + z, x + (y + z))); + + REQUIRE(eq(-x, -T(1) * x)); + REQUIRE(eq(-(-x), x)); + REQUIRE(eq(x - x, n)); + + REQUIRE(eq(a * n, n)); + REQUIRE(eq(n * a, n)); + REQUIRE(eq(T(0) * x, n)); + REQUIRE(eq(x * T(0), n)); + REQUIRE(eq(T(1) * x, x)); + REQUIRE(eq(x * T(1), x)); + + REQUIRE(eq(a * x, x * a)); + + REQUIRE(eq(a * x + b * x, (a + b) * x)); + REQUIRE(eq(a * (x + y), a * x + a * y)); + + REQUIRE(eq(x * (y + z), x * y + x * z)); + + if (min_element(abs(y)) != 0) { + REQUIRE(eq(x * y / y, x)); + REQUIRE(eq(x / y * y + x % y, x)); + } + + REQUIRE(eq(~(~x), x)); + + REQUIRE(eq((n & x), n)); + REQUIRE(eq((0 & x), n)); + REQUIRE(eq((x & n), n)); + REQUIRE(eq((x & 0), n)); + + REQUIRE(eq((~n & x), x)); + REQUIRE(eq((~T(0) & x), x)); + REQUIRE(eq((x & ~n), x)); + REQUIRE(eq((x & ~T(0)), x)); + + REQUIRE(eq((n | x), x)); + REQUIRE(eq((0 | x), x)); + REQUIRE(eq((x | n), x)); + REQUIRE(eq((x | 0), x)); + + REQUIRE(eq((~n | x), ~n)); + REQUIRE(eq((~T(0) | x), ~n)); + REQUIRE(eq((x | ~n), ~n)); + REQUIRE(eq((x | ~T(0)), ~n)); + + REQUIRE(eq((x & y), (y & x))); + REQUIRE(eq((x | y), (y | x))); + + REQUIRE(eq(((x & y) & z), (x & (y & z)))); + REQUIRE(eq(((x | y) | z), (x | (y | z)))); + + REQUIRE(eq((x & (y | z)), ((y & x) | (x & z)))); + REQUIRE(eq((x | (y & z)), ((y | x) & (x | z)))); + + REQUIRE(eq((x & y), ~(~x | ~y))); + REQUIRE(eq((x | y), ~(~x & ~y))); + + REQUIRE(eq((n ^ x), x)); + REQUIRE(eq((0 ^ x), x)); + REQUIRE(eq((x ^ n), x)); + REQUIRE(eq((x ^ 0), x)); + + REQUIRE(eq((~n ^ x), ~x)); + REQUIRE(eq((~T(0) ^ x), ~x)); + REQUIRE(eq((x ^ ~n), ~x)); + REQUIRE(eq((x ^ ~T(0)), ~x)); + + REQUIRE(eq((x ^ x), n)); + + REQUIRE(eq((x ^ y), (y ^ x))); + REQUIRE(eq(((x ^ y) ^ z), (x ^ (y ^ z)))); + + P t; + t = x; + t += y; + REQUIRE(eq(t, x + y)); + t = x; + t -= y; + REQUIRE(eq(t, x - y)); + t = x; + t *= y; + REQUIRE(eq(t, x * y)); + t = x; + t /= y; + REQUIRE(eq(t, x / y)); + t = x; + t %= y; + REQUIRE(eq(t, x % y)); + t = x; + t &= y; + REQUIRE(eq(t, (x & y))); + t = x; + t |= y; + REQUIRE(eq(t, (x | y))); + t = x; + t ^= y; + REQUIRE(eq(t, (x ^ y))); + + } // for iter +} + +template void test_Point_float(const P &p) { + const std::size_t D = p.ndims(); + using T = typename P::value_type; + const std::equal_to

eq; + + std::mt19937 gen; + std::uniform_real_distribution dist(-1.0, 1.0); + const auto rand = [&]() { return dist(gen); }; + const auto randp = [&]() { return fmap([&](auto) { return rand(); }, p); }; + + for (int iter = 0; iter < 100; ++iter) { + + P n(p); + REQUIRE(n.size() == D); + for (std::size_t d = 0; d < D; ++d) + REQUIRE(n[d] == 0); + + const P x = randp(); + const P y = randp(); + const P z = randp(); + + const T a = rand(); + const T b = rand(); + + REQUIRE(eq(fmap([](auto i) { return i; }, x), x)); + REQUIRE(eq(fmap([](auto i) { return i + 1; }, + fmap([](auto i) { return 2 * i; }, x)), + fmap([](auto i) { return 2 * i + 1; }, x))); + + REQUIRE( + eq(fmap([](auto i, auto j) { return 2 * i + j; }, x, y), 2 * x + y)); + REQUIRE(eq( + fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, z), + 3 * x + 2 * y + z)); + + REQUIRE(fold([](auto i, auto j) { return i + j; }, T(0), x) == sum(x)); + REQUIRE(is_approx( + fold([](auto i, auto j, auto k) { return i + j + k; }, T(0), x, y), + sum(x + y))); + + REQUIRE(sum(n) == 0); + REQUIRE(sum(n + 1) == D); + REQUIRE(product(n) == (D == 0 ? 1 : 0)); + REQUIRE(product(n + 1) == 1); + REQUIRE(min_element(n) == (D == 0 ? T(1) / 0 : 0)); + REQUIRE(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); + REQUIRE(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); + REQUIRE(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); + + REQUIRE(eq(+x, x)); + REQUIRE(eq(n + x, x)); + REQUIRE(eq(T(0) + x, x)); + REQUIRE(eq(x + n, x)); + REQUIRE(eq(x + T(0), x)); + + REQUIRE(eq(x + y, y + x)); + + REQUIRE(is_approx((x + y) + z, x + (y + z))); + + REQUIRE(eq(-x, -T(1) * x)); + REQUIRE(eq(-(-x), x)); + REQUIRE(eq(x - x, n)); + + REQUIRE(eq(a * n, n)); + REQUIRE(eq(n * a, n)); + REQUIRE(eq(T(0) * x, n)); + REQUIRE(eq(x * T(0), n)); + REQUIRE(eq(T(1) * x, x)); + REQUIRE(eq(x * T(1), x)); + + REQUIRE(eq(a * x, x * a)); + + if (min_element(abs(x)) != 0) { + REQUIRE(eq(x / x, n + 1)); + REQUIRE(is_approx(1 / (1 / x), x)); + REQUIRE(is_approx(a / x, a * (1 / x))); + } + if (a != 0) { + REQUIRE(is_approx(x / a, x * (1 / a))); + } + + REQUIRE(is_approx(a * x + b * x, (a + b) * x)); + REQUIRE(is_approx(a * (x + y), a * x + a * y)); + + REQUIRE(is_approx(x * (y + z), x * y + x * z)); + + if (min_element(abs(y)) != 0) { + REQUIRE(is_approx(x * y / y, x)); + } + + P t; + t = x; + t += y; + REQUIRE(eq(t, x + y)); + t = x; + t -= y; + REQUIRE(eq(t, x - y)); + t = x; + t *= y; + REQUIRE(eq(t, x * y)); + t = x; + t /= y; + REQUIRE(eq(t, x / y)); + + } // for iter +} + +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } +TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } + +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_int(Point()); +} + +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} +TEST_CASE("Point", "[regions]") { + test_Point_float(Point()); +} + +TEST_CASE("NDPoint(0)", "[regions]") { + test_Point_bool(NDPoint(0)); +} +TEST_CASE("NDPoint(1)", "[regions]") { + test_Point_bool(NDPoint(1)); +} +TEST_CASE("NDPoint(2)", "[regions]") { + test_Point_bool(NDPoint(2)); +} +TEST_CASE("NDPoint(3)", "[regions]") { + test_Point_bool(NDPoint(3)); +} + +TEST_CASE("NDPoint(0)", "[regions]") { + test_Point_int(NDPoint(0)); +} +TEST_CASE("NDPoint(1)", "[regions]") { + test_Point_int(NDPoint(1)); +} +TEST_CASE("NDPoint(2)", "[regions]") { + test_Point_int(NDPoint(2)); +} +TEST_CASE("NDPoint(3)", "[regions]") { + test_Point_int(NDPoint(3)); +} + +TEST_CASE("NDPoint(0)", "[regions]") { + test_Point_float(NDPoint(0)); +} +TEST_CASE("NDPoint(1)", "[regions]") { + test_Point_float(NDPoint(1)); +} +TEST_CASE("NDPoint(2)", "[regions]") { + test_Point_float(NDPoint(2)); +} +TEST_CASE("NDPoint(3)", "[regions]") { + test_Point_float(NDPoint(3)); +} diff --git a/test/RegionsTest.cpp b/test/RegionsTest.cpp deleted file mode 100644 index fe4124a775..0000000000 --- a/test/RegionsTest.cpp +++ /dev/null @@ -1,454 +0,0 @@ -#include - -#include - -#include -#include -#include -#include - -using namespace openPMD::Regions; - -template auto maxabs(const T &xs) { - using std::abs, std::max_element; - return max_element(abs(xs)); -} -template bool is_approx(const T &xs, const T &ys) { - return maxabs(xs - ys) <= - 10 * std::numeric_limits::epsilon(); -} - -template void test_Point_bool(const P &p) { - const std::size_t D = p.size(); - using T = typename P::value_type; - - std::mt19937 gen; - std::uniform_int_distribution dist(0, 1); - const auto rand = [&]() { return dist(gen); }; - - P n = p; - REQUIRE(n.size() == D); - for (std::size_t d = 0; d < D; ++d) - REQUIRE(n[d] == 0); - - const P x = fmap([&](auto) { return rand(); }, p); - const P y = fmap([&](auto) { return rand(); }, p); - const P z = fmap([&](auto) { return rand(); }, p); - - const T a = rand(); - const T b = rand(); - - REQUIRE(!any(n)); - REQUIRE(all(!n)); - - REQUIRE((n & x) == n); - REQUIRE((0 & x) == n); - REQUIRE((x & n) == n); - REQUIRE((x & 0) == n); - - REQUIRE((!n & x) == x); - REQUIRE((T(1) & x) == x); - REQUIRE((x & !n) == x); - REQUIRE((x & T(1)) == x); - - REQUIRE((n | x) == x); - REQUIRE((0 | x) == x); - REQUIRE((x | n) == x); - REQUIRE((x | 0) == x); - - REQUIRE((!n | x) == !n); - REQUIRE((T(1) | x) == !n); - REQUIRE((x | !n) == !n); - REQUIRE((x | T(1)) == !n); - - REQUIRE((x & y) == (y & x)); - REQUIRE((x | y) == (y | x)); - - REQUIRE(((x & y) & z) == (x & (y & z))); - REQUIRE(((x | y) | z) == (x | (y | z))); - - REQUIRE((x & (y | z)) == ((y & x) | (x & z))); - REQUIRE((x | (y & z)) == ((y | x) & (x | z))); - - REQUIRE((x & y) == !(!x | !y)); - REQUIRE((x | y) == !(!x & !y)); - - REQUIRE((n ^ x) == x); - REQUIRE((0 ^ x) == x); - REQUIRE((x ^ n) == x); - REQUIRE((x ^ 0) == x); - - REQUIRE((!n ^ x) == !x); - REQUIRE((T(1) ^ x) == !x); - REQUIRE((x ^ !n) == !x); - REQUIRE((x ^ T(1)) == !x); - - REQUIRE((x ^ x) == n); - - REQUIRE((x ^ y) == (y ^ x)); - REQUIRE(((x ^ y) ^ z) == (x ^ (y ^ z))); - - REQUIRE(!(!x) == x); - - REQUIRE((n && x) == n); - REQUIRE((0 && x) == n); - REQUIRE((x && n) == n); - REQUIRE((x && 0) == n); - - REQUIRE((!n && x) == x); - REQUIRE((!T(0) && x) == x); - REQUIRE((x && !n) == x); - REQUIRE((x && !T(0)) == x); - - REQUIRE((n || x) == x); - REQUIRE((0 || x) == x); - REQUIRE((x || n) == x); - REQUIRE((x || 0) == x); - - REQUIRE((!n || x) == !n); - REQUIRE((!T(0) || x) == !n); - REQUIRE((x || !n) == !n); - REQUIRE((x || !T(0)) == !n); - - REQUIRE((x && y) == (y && x)); - REQUIRE((x || y) == (y || x)); - - REQUIRE(((x && y) && z) == (x && (y && z))); - REQUIRE(((x || y) || z) == (x || (y || z))); - - REQUIRE((x && (y || z)) == ((y && x) || (x && z))); - REQUIRE((x || (y && z)) == ((y || x) && (x || z))); - - REQUIRE((x && y) == !(!x || !y)); - REQUIRE((x || y) == !(!x && !y)); - - P t; - t = x; - t &= y; - REQUIRE(t == (x & y)); - t = x; - t |= y; - REQUIRE(t == (x | y)); - t = x; - t ^= y; - REQUIRE(t == (x ^ y)); -} - -template void test_Point_int(const P &p) { - const std::size_t D = p.size(); - using T = typename P::value_type; - - std::mt19937 gen; - std::uniform_int_distribution dist(-1000, 1000); - const auto rand = [&]() { return dist(gen); }; - - P n(p); - REQUIRE(n.size() == D); - for (std::size_t d = 0; d < D; ++d) - REQUIRE(n[d] == 0); - - const P x = fmap([&](auto) { return rand(); }, p); - const P y = fmap([&](auto) { return rand(); }, p); - const P z = fmap([&](auto) { return rand(); }, p); - - const T a = rand(); - const T b = rand(); - - REQUIRE(fmap([](auto i) { return i; }, x) == x); - REQUIRE(fmap([](auto i) { return i + 1; }, - fmap([](auto i) { return 2 * i; }, x)) == - fmap([](auto i) { return 2 * i + 1; }, x)); - - REQUIRE(fmap([](auto i, auto j) { return 2 * i + j; }, x, y) == 2 * x + y); - REQUIRE(fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, - z) == 3 * x + 2 * y + z); - - REQUIRE(fold([](auto i, auto j) { return i + j; }, 0, x) == sum(x)); - REQUIRE(fold([](auto i, auto j, auto k) { return i + j + k; }, 0, x, y) == - sum(x + y)); - - REQUIRE(sum(n) == 0); - REQUIRE(sum(n + 1) == D); - REQUIRE(product(n) == (D == 0 ? 1 : 0)); - REQUIRE(product(n + 1) == 1); - REQUIRE(min_element(n) == (D == 0 ? std::numeric_limits::max() : 0)); - REQUIRE(max_element(n) == (D == 0 ? std::numeric_limits::min() : 0)); - REQUIRE(min_element(n + 1) == (D == 0 ? std::numeric_limits::max() : 1)); - REQUIRE(max_element(n + 1) == (D == 0 ? std::numeric_limits::min() : 1)); - - REQUIRE(+x == x); - REQUIRE(n + x == x); - REQUIRE(T(0) + x == x); - REQUIRE(x + n == x); - REQUIRE(x + T(0) == x); - - REQUIRE(x + y == y + x); - - REQUIRE((x + y) + z == x + (y + z)); - - REQUIRE(-x == -T(1) * x); - REQUIRE(-(-x) == x); - REQUIRE(x - x == n); - - REQUIRE(a * n == n); - REQUIRE(n * a == n); - REQUIRE(T(0) * x == n); - REQUIRE(x * T(0) == n); - REQUIRE(T(1) * x == x); - REQUIRE(x * T(1) == x); - - REQUIRE(a * x == x * a); - - REQUIRE(a * x + b * x == (a + b) * x); - REQUIRE(a * (x + y) == a * x + a * y); - - REQUIRE(x * (y + z) == x * y + x * z); - - if (min_element(abs(y)) != 0) { - REQUIRE(x * y / y == x); - REQUIRE(x / y * y + x % y == x); - } - - REQUIRE(~(~x) == x); - - REQUIRE((n & x) == n); - REQUIRE((0 & x) == n); - REQUIRE((x & n) == n); - REQUIRE((x & 0) == n); - - REQUIRE((~n & x) == x); - REQUIRE((~T(0) & x) == x); - REQUIRE((x & ~n) == x); - REQUIRE((x & ~T(0)) == x); - - REQUIRE((n | x) == x); - REQUIRE((0 | x) == x); - REQUIRE((x | n) == x); - REQUIRE((x | 0) == x); - - REQUIRE((~n | x) == ~n); - REQUIRE((~T(0) | x) == ~n); - REQUIRE((x | ~n) == ~n); - REQUIRE((x | ~T(0)) == ~n); - - REQUIRE((x & y) == (y & x)); - REQUIRE((x | y) == (y | x)); - - REQUIRE(((x & y) & z) == (x & (y & z))); - REQUIRE(((x | y) | z) == (x | (y | z))); - - REQUIRE((x & (y | z)) == ((y & x) | (x & z))); - REQUIRE((x | (y & z)) == ((y | x) & (x | z))); - - REQUIRE((x & y) == ~(~x | ~y)); - REQUIRE((x | y) == ~(~x & ~y)); - - REQUIRE((n ^ x) == x); - REQUIRE((0 ^ x) == x); - REQUIRE((x ^ n) == x); - REQUIRE((x ^ 0) == x); - - REQUIRE((~n ^ x) == ~x); - REQUIRE((~T(0) ^ x) == ~x); - REQUIRE((x ^ ~n) == ~x); - REQUIRE((x ^ ~T(0)) == ~x); - - REQUIRE((x ^ x) == n); - - REQUIRE((x ^ y) == (y ^ x)); - REQUIRE(((x ^ y) ^ z) == (x ^ (y ^ z))); - - P t; - t = x; - t += y; - REQUIRE(t == x + y); - t = x; - t -= y; - REQUIRE(t == x - y); - t = x; - t *= y; - REQUIRE(t == x * y); - t = x; - t /= y; - REQUIRE(t == x / y); - t = x; - t %= y; - REQUIRE(t == x % y); - t = x; - t &= y; - REQUIRE(t == (x & y)); - t = x; - t |= y; - REQUIRE(t == (x | y)); - t = x; - t ^= y; - REQUIRE(t == (x ^ y)); -} - -template void test_Point_float(const P &p) { - const std::size_t D = p.size(); - using T = typename P::value_type; - - std::mt19937 gen; - std::uniform_real_distribution dist(-1.0, 1.0); - const auto rand = [&]() { return dist(gen); }; - - P n(p); - REQUIRE(n.size() == D); - for (std::size_t d = 0; d < D; ++d) - REQUIRE(n[d] == 0); - - const P x = fmap([&](auto) { return rand(); }, p); - const P y = fmap([&](auto) { return rand(); }, p); - const P z = fmap([&](auto) { return rand(); }, p); - - const T a = rand(); - const T b = rand(); - - REQUIRE(fmap([](auto i) { return i; }, x) == x); - REQUIRE(fmap([](auto i) { return i + 1; }, - fmap([](auto i) { return 2 * i; }, x)) == - fmap([](auto i) { return 2 * i + 1; }, x)); - - REQUIRE(fmap([](auto i, auto j) { return 2 * i + j; }, x, y) == 2 * x + y); - REQUIRE(fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, - z) == 3 * x + 2 * y + z); - - REQUIRE(fold([](auto i, auto j) { return i + j; }, T(0), x) == sum(x)); - REQUIRE(fold([](auto i, auto j, auto k) { return i + j + k; }, T(0), x, y) == - sum(x + y)); - - REQUIRE(sum(n) == 0); - REQUIRE(sum(n + 1) == D); - REQUIRE(product(n) == (D == 0 ? 1 : 0)); - REQUIRE(product(n + 1) == 1); - REQUIRE(min_element(n) == (D == 0 ? T(1) / 0 : 0)); - REQUIRE(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); - REQUIRE(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); - REQUIRE(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); - - REQUIRE(+x == x); - REQUIRE(n + x == x); - REQUIRE(T(0) + x == x); - REQUIRE(x + n == x); - REQUIRE(x + T(0) == x); - - REQUIRE(x + y == y + x); - - REQUIRE(is_approx((x + y) + z, x + (y + z))); - - REQUIRE(-x == -T(1) * x); - REQUIRE(-(-x) == x); - REQUIRE(x - x == n); - - REQUIRE(a * n == n); - REQUIRE(n * a == n); - REQUIRE(T(0) * x == n); - REQUIRE(x * T(0) == n); - REQUIRE(T(1) * x == x); - REQUIRE(x * T(1) == x); - - REQUIRE(a * x == x * a); - - if (min_element(abs(y)) != 0) { - REQUIRE(x / x == n + 1); - REQUIRE(1 / (1 / x) == x); - REQUIRE(a / x == a * (1 / x)); - } - if (a != 0) { - REQUIRE(is_approx(x / a, x * (1 / a))); - } - - REQUIRE(is_approx(a * x + b * x, (a + b) * x)); - REQUIRE(is_approx(a * (x + y), a * x + a * y)); - - REQUIRE(is_approx(x * (y + z), x * y + x * z)); - - if (min_element(abs(y)) != 0) { - REQUIRE(is_approx(x * y / y, x)); - } - - P t; - t = x; - t += y; - REQUIRE(t == x + y); - t = x; - t -= y; - REQUIRE(t == x - y); - t = x; - t *= y; - REQUIRE(t == x * y); - t = x; - t /= y; - REQUIRE(t == x / y); -} - -TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } -TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } -TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } -TEST_CASE("Point", "[regions]") { test_Point_bool(Point()); } - -TEST_CASE("Point", "[regions]") { - test_Point_int(Point()); -} -TEST_CASE("Point", "[regions]") { - test_Point_int(Point()); -} -TEST_CASE("Point", "[regions]") { - test_Point_int(Point()); -} -TEST_CASE("Point", "[regions]") { - test_Point_int(Point()); -} - -TEST_CASE("Point", "[regions]") { - test_Point_float(Point()); -} -TEST_CASE("Point", "[regions]") { - test_Point_float(Point()); -} -TEST_CASE("Point", "[regions]") { - test_Point_float(Point()); -} -TEST_CASE("Point", "[regions]") { - test_Point_float(Point()); -} - -TEST_CASE("NDPoint(0)", "[regions]") { - test_Point_bool(NDPoint(0)); -} -TEST_CASE("NDPoint(1)", "[regions]") { - test_Point_bool(NDPoint(1)); -} -TEST_CASE("NDPoint(2)", "[regions]") { - test_Point_bool(NDPoint(2)); -} -TEST_CASE("NDPoint(3)", "[regions]") { - test_Point_bool(NDPoint(3)); -} - -TEST_CASE("NDPoint(0)); -} -TEST_CASE("NDPoint(1)); -} -TEST_CASE("NDPoint(2)); -} -TEST_CASE("NDPoint(3)); -} - -TEST_CASE("NDPoint(0)); -} -TEST_CASE("NDPoint(1)); -} -TEST_CASE("NDPoint(2)); -} -TEST_CASE("NDPoint(3)); -} From 40232dd2a450d0fe1df9fe473bac416420437b75 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sat, 5 Jun 2021 11:54:43 -0400 Subject: [PATCH 05/68] Clean up namespaces --- include/openPMD/regions/Box.hpp | 79 +++------------------------- include/openPMD/regions/Helpers.hpp | 80 +++++++++++++++++++++++++++-- include/openPMD/regions/Point.hpp | 4 +- 3 files changed, 85 insertions(+), 78 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index 44e35793a0..bfd8176d94 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -1,6 +1,7 @@ #ifndef REGIONS_BOX_HPP #define REGIONS_BOX_HPP +#include "Helpers.hpp" #include "Point.hpp" #include @@ -13,74 +14,6 @@ namespace openPMD { namespace Regions { -namespace detail { -namespace detail { - -template struct tuple_eq { - template - constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { - typedef std::tuple_element_t T1; - typedef std::tuple_element_t T2; - typedef std::common_type_t T; - const std::equal_to eq; - return tuple_eq()(x, y) && - eq(std::get(x), std::get(y)); - } -}; -template <> struct tuple_eq<0> { - template - constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { - return true; - } -}; - -template struct tuple_cmp { - template - constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { - typedef std::tuple_element_t T1; - typedef std::tuple_element_t T2; - typedef std::common_type_t T; - const int cmp = tuple_cmp()(x, y); - if (cmp != 0) - return cmp; - const std::less lt; - if (lt(std::get(x), std::get(y))) - return -1; - if (lt(std::get(y), std::get(x))) - return +1; - return 0; - } -}; -template <> struct tuple_cmp<0> { - template - constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { - return 0; - } -}; - -} // namespace detail - -template -constexpr bool tuple_eq(const Tuple1 &x, const Tuple2 &y) { - constexpr std::size_t sz = std::tuple_size_v; - static_assert(std::tuple_size_v == sz); - return detail::tuple_eq()(x, y); -} - -template -constexpr int tuple_cmp(const Tuple1 &x, const Tuple2 &y) { - constexpr std::size_t sz = std::tuple_size_v; - static_assert(std::tuple_size_v == sz); - return detail::tuple_cmp()(x, y); -} - -template -constexpr bool tuple_lt(const Tuple1 &x, const Tuple2 &y) { - return tuple_cmp(x, y) < 0; -} - -} // namespace detail - /** A D-dimensional box * * A box is described by two points, its lower bound (inclusive) and @@ -534,8 +467,8 @@ struct equal_to> { return true; if (x.empty() || y.empty()) return false; - return openPMD::Regions::detail::tuple_eq(make_tuple(x.lower(), x.upper()), - make_tuple(y.lower(), y.upper())); + return openPMD::Regions::helpers::tuple_eq( + make_tuple(x.lower(), x.upper()), make_tuple(y.lower(), y.upper())); } }; @@ -544,7 +477,7 @@ template struct hash> { if (x.empty()) return size_t(0xc9df21a36550a048ULL); hash> h; - return openPMD::Regions::detail::hash_combine(h(x.lower()), h(x.upper())); + return openPMD::Regions::helpers::hash_combine(h(x.lower()), h(x.upper())); } }; @@ -566,8 +499,8 @@ template struct less> { return true; if (y.empty()) return false; - return openPMD::Regions::detail::tuple_lt(make_tuple(x.lower(), x.upper()), - make_tuple(y.lower(), y.upper())); + return openPMD::Regions::helpers::tuple_lt( + make_tuple(x.lower(), x.upper()), make_tuple(y.lower(), y.upper())); } }; diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index da10fd8d63..e836163f61 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -8,9 +8,11 @@ namespace openPMD { namespace Regions { -namespace detail { +namespace helpers { -// Combine hashes +//////////////////////////////////////////////////////////////////////////////// + +// Combine hash values template std::size_t hash_combine(std::size_t seed, const T &x) { std::hash h; // Taken from Boost @@ -18,6 +20,8 @@ template std::size_t hash_combine(std::size_t seed, const T &x) { h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2); } +//////////////////////////////////////////////////////////////////////////////// + // Convert a tuple-like type to a std::array template constexpr auto array_push(const Tuple &t, std::index_sequence, @@ -39,7 +43,77 @@ constexpr std::array construct_array(const F &f) { if constexpr (N > 0) return array_push(construct_array(f), f(N - 1)); } -} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// + +// Compare tuples + +namespace impl { + +template struct tuple_eq { + template + constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { + typedef std::tuple_element_t T1; + typedef std::tuple_element_t T2; + typedef std::common_type_t T; + const std::equal_to eq; + return tuple_eq()(x, y) && + eq(std::get(x), std::get(y)); + } +}; +template <> struct tuple_eq<0> { + template + constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { + return true; + } +}; + +template struct tuple_cmp { + template + constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { + typedef std::tuple_element_t T1; + typedef std::tuple_element_t T2; + typedef std::common_type_t T; + const int cmp = tuple_cmp()(x, y); + if (cmp != 0) + return cmp; + const std::less lt; + if (lt(std::get(x), std::get(y))) + return -1; + if (lt(std::get(y), std::get(x))) + return +1; + return 0; + } +}; +template <> struct tuple_cmp<0> { + template + constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { + return 0; + } +}; + +} // namespace impl + +template +constexpr bool tuple_eq(const Tuple1 &x, const Tuple2 &y) { + constexpr std::size_t sz = std::tuple_size_v; + static_assert(std::tuple_size_v == sz); + return impl::tuple_eq()(x, y); +} + +template +constexpr int tuple_cmp(const Tuple1 &x, const Tuple2 &y) { + constexpr std::size_t sz = std::tuple_size_v; + static_assert(std::tuple_size_v == sz); + return impl::tuple_cmp()(x, y); +} + +template +constexpr bool tuple_lt(const Tuple1 &x, const Tuple2 &y) { + return tuple_cmp(x, y) < 0; +} + +} // namespace helpers } // namespace Regions } // namespace openPMD diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index 0ab4c62e26..fedd159a14 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -64,7 +64,7 @@ template class Point { * [0, ..., D-1] */ template static constexpr Point make(const F &f) { - return detail::construct_array(f); + return helpers::construct_array(f); } /** Create a point with each component set to the same value a @@ -508,7 +508,7 @@ struct hash> { const hash h; return fold( [&](size_t r, const T &b) { - return openPMD::Regions::detail::hash_combine(r, h(b)); + return openPMD::Regions::helpers::hash_combine(r, h(b)); }, size_t(0xb22da17173243869ULL), x); } From 3e398685fee9acd2c6d26f783d4db25cd15327ae Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sun, 6 Jun 2021 11:26:48 -0400 Subject: [PATCH 06/68] First cut at bounding box sets --- CMakeLists.txt | 1 + include/openPMD/regions/Box.hpp | 54 +- include/openPMD/regions/Helpers.hpp | 48 ++ include/openPMD/regions/NDPoint.hpp | 54 +- include/openPMD/regions/Point.hpp | 62 +- include/openPMD/regions/Region.hpp | 1126 +++++++++++++++++++++++++++ include/openPMD/regions/Regions.hpp | 3 +- test/RegionsBoxTest.cpp | 2 +- test/RegionsPointTest.cpp | 22 + test/RegionsRegionTest.cpp | 152 ++++ 10 files changed, 1483 insertions(+), 41 deletions(-) create mode 100644 include/openPMD/regions/Region.hpp create mode 100644 test/RegionsRegionTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bb9880800e..a43be0a1c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -684,6 +684,7 @@ set(openPMD_TEST_NAMES Auxiliary RegionsBox RegionsPoint + RegionsRegion SerialIO ParallelIO ) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index bfd8176d94..fb669b6df4 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,8 @@ template class Box; template class Box { bool is_full; + explicit constexpr Box(bool is_full) : is_full(is_full) {} + public: constexpr static std::size_t D = 0; @@ -36,7 +39,7 @@ template class Box { /** Create empty box */ - Box() : is_full(false) {} + constexpr Box() : is_full(false) {} Box(const Box &) = default; Box(Box &&) = default; @@ -104,14 +107,13 @@ template class Box { // Set operations friend Box bounding_box(const Box &b1, const Box &b2) { - return b1.empty() && b2.empty() ? Box() : Box(Point()); + return Box(!b1.empty() || !b2.empty()); } friend Box operator&(const Box &b1, const Box &b2) { - if (!b1.empty() & !b2.empty()) - return Box(Point()); - return Box(); + return Box(!b1.empty() & !b2.empty()); } + Box &operator|=(const Box &b) { return *this = *this | b; } friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } friend bool operator==(const Box &b, const std::vector &bs) { @@ -162,6 +164,8 @@ template class Box { } }; +//////////////////////////////////////////////////////////////////////////////// + template class Box { Point lo, hi; @@ -350,39 +354,42 @@ template class Box { assert(vol == size()); for (size_t i = 0; i < rs.size(); ++i) for (size_t j = i + 1; j < rs.size(); ++j) - assert(rs[i].isdisjoint(rs[j])); + assert(isdisjoint(rs[i], rs[j])); #endif } public: - std::vector operator-(const Box &b) const { - if (empty()) + friend std::vector operator-(const Box &b1, const Box &b2) { + if (b1.empty()) return std::vector{}; - if (b.empty()) - return std::vector{*this}; - std::vector bs1; - split(b.lo, bs1); - std::vector bs2; - for (const auto &b1 : bs1) - b1.split(b.hi, bs2); + if (b2.empty()) + return std::vector{b1}; + std::vector rs1; + b1.split(b2.lo, rs1); + std::vector rs2; + for (const auto &r : rs1) + r.split(b2.hi, rs2); std::vector rs; - for (const auto &b2 : bs2) { - assert(isdisjoint(b2, b) || b2 <= b); - if (isdisjoint(b2, b)) - rs.push_back(b2); + for (const auto &r : rs2) { +#if REGIONS_DEBUG + assert(isdisjoint(r, b2) || r <= b2); +#endif + if (isdisjoint(r, b2)) + rs.push_back(r); } #if REGIONS_DEBUG // Postcondition size_type vol = 0; for (const auto &r : rs) { assert(!r.empty()); - assert(r <= *this && !(r <= b)); + assert(r <= b1 && !(r <= b2)); vol += r.size(); } - assert(vol >= max(size_type(0), size() - b.size()) && vol <= size()); + using std::max; + assert(vol >= max(size_type(0), b1.size() - b2.size()) && vol <= b1.size()); for (size_t i = 0; i < rs.size(); ++i) for (size_t j = i + 1; j < rs.size(); ++j) - assert(rs[i].isdisjoint(rs[j])); + assert(isdisjoint(rs[i], rs[j])); #endif return rs; } @@ -426,7 +433,8 @@ template class Box { assert((r <= b1) ^ (r <= b2)); vol += r.size(); } - assert(vol >= abs(b1.size() - b2.size()) && vol <= b1size() + b2.size()); + using std::abs; + assert(vol >= abs(b1.size() - b2.size()) && vol <= b1.size() + b2.size()); for (size_t i = 0; i < rs.size(); ++i) for (size_t j = i + 1; j < rs.size(); ++j) assert(isdisjoint(rs[i], rs[j])); diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index e836163f61..91e53f653f 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -8,6 +8,8 @@ namespace openPMD { namespace Regions { +#define REGIONS_DEBUG 1 // 0 or 1 + namespace helpers { //////////////////////////////////////////////////////////////////////////////// @@ -113,6 +115,52 @@ constexpr bool tuple_lt(const Tuple1 &x, const Tuple2 &y) { return tuple_cmp(x, y) < 0; } +//////////////////////////////////////////////////////////////////////////////// + +// Reduce over a std::vector, using bisection to reduce cost + +/** Reduce a non-empty range + */ +template ::type> +T reduce1(const Op &op, std::vector &xs) { + assert(!xs.empty()); + for (std::size_t dist = 1; dist < xs.size(); dist *= 2) + for (std::size_t i = 0; i + dist < xs.size(); i += 2 * dist) + xs[i] = op(std::move(xs[i]), std::move(xs[i + dist])); + return std::move(xs[0]); +} + +/** Mapreduce a range with a given neutral element + */ +template +R mapreduce(const F &f, const Op &op, R z, const B &b, const E &e) { + std::vector rs; + for (auto i = b; i != e; ++i) + rs.push_back(f(*i)); + if (rs.empty()) + return z; + return reduce1(op, rs); +} + +/** Mapreduce a range + */ +template ::value_type, + typename R = std::decay_t>> +R mapreduce(const F &f, const Op &op, const I &b, const I &e) { + return mapreduce(f, op, R(), b, e); +} + +/** Mapreduce a container + */ +template >> +R mapreduce(const F &f, const Op &op, const C &c) { + return mapreduce(f, op, std::begin(c), std::end(c)); +} + } // namespace helpers } // namespace Regions diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 668c985096..06cceadb73 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -21,6 +21,10 @@ namespace openPMD { namespace Regions { +/** Maximum number of dimensions supported by NDPoint + */ +constexpr std::size_t max_ndims = 5; + namespace detail { // Abstract base helper class @@ -52,10 +56,13 @@ template class VPoint { virtual size_type size() const = 0; + virtual size_type ndims() const = 0; virtual const T &operator[](const size_type d) const = 0; virtual T &operator[](const size_type d) = 0; - - virtual size_type ndims() const = 0; + virtual std::unique_ptr erase(const size_type d) const = 0; + virtual std::unique_ptr insert(const size_type d, + const T &a) const = 0; + virtual std::unique_ptr reversed() const = 0; virtual std::unique_ptr operator+() const = 0; virtual std::unique_ptr operator-() const = 0; @@ -205,10 +212,28 @@ template class WPoint final : public VPoint { constexpr size_type size() const override { return p.size(); } + constexpr size_type ndims() const override { return p.ndims(); } + const T &operator[](const size_type d) const override { return p[d]; } T &operator[](const size_type d) override { return p[d]; } - constexpr size_type ndims() const override { return p.ndims(); } + std::unique_ptr> erase(const size_type d) const override { + if constexpr (D == 0) + return std::unique_ptr>(); + else + return std::make_unique>(p.erase(d)); + } + std::unique_ptr> insert(const size_type d, + const T &a) const override { + if constexpr (D == max_ndims) + return std::unique_ptr>(); + else + return std::make_unique>(p.insert(d, a)); + } + + std::unique_ptr> reversed() const override { + return std::make_unique(p.reversed()); + } std::unique_ptr> operator+() const override { return std::make_unique(+p); @@ -574,16 +599,33 @@ template class NDPoint { */ size_type size() const { return p->size(); } + /** Number of dimensions (same as number of comopnents) + */ + size_type ndims() const { return p->ndims(); } + /** Get a component of a Point */ const T &operator[](const size_type d) const { return (*p)[d]; } /** Get a component of a Point */ T &operator[](const size_type d) { return (*p)[d]; } - - /** Number of dimensions (same as number of comopnents) + /** Remove a component from a point + * + * This reduces the dimension of a point by one. */ - size_type ndims() const { return p->ndims(); } + constexpr NDPoint erase(size_type dir) const { + return NDPoint(p->erase(dir)); + } + /** Add a component to a point + * + * This increases the dimension of a point by one. + */ + constexpr NDPoint insert(size_type dir, const T &a) const { + return NDPoint(p->insert(dir, a)); + } + /** Reverse the components of a point + */ + constexpr NDPoint reversed() const { return NDPoint(p->reversed()); } friend NDPoint operator+(const NDPoint &x) { return NDPoint(+*x.p); } friend NDPoint operator-(const NDPoint &x) { return NDPoint(-*x.p); } diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index fedd159a14..f513def0fd 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -40,7 +40,7 @@ template class Point { typedef T value_type; /** Return type of Point::size() */ - typedef std::size_t size_type; + typedef std::ptrdiff_t size_type; /** Create a value-initialized Point * @@ -168,10 +168,10 @@ template class Point { /** Convert a point to a std::array */ - operator std::array() const { return elts; } + constexpr operator std::array() const { return elts; } /** Convert a point to a std::vector */ - explicit operator std::vector() const { + explicit constexpr operator std::vector() const { return std::vector(elts.begin(), elts.end()); } @@ -179,6 +179,10 @@ template class Point { */ constexpr size_type size() const { return D; } + /** Number of dimensions (same as number of components) + */ + constexpr size_type ndims() const { return D; } + /** Get a component of a point */ constexpr const T &operator[](const size_type d) const { return elts[d]; } @@ -186,9 +190,31 @@ template class Point { */ constexpr T &operator[](const size_type d) { return elts[d]; } - /** Number of dimensions (same as number of components) + /** Remove a component from a point + * + * This reduces the dimension of a point by one. */ - constexpr size_type ndims() const { return D; } + constexpr Point 0 ? D - 1 : 0)> erase(size_type dir) const { + assert(dir >= 0 && dir < D); + return Point 0 ? D - 1 : 0)>::make( + [&](size_type d) { return d < dir ? (*this)[d] : (*this)[d + 1]; }); + } + /** Add a component to a point + * + * This increases the dimension of a point by one. + */ + constexpr Point insert(size_type dir, const T &a) const { + assert(dir >= 0 && dir <= D); + return Point::make([&](size_type d) { + return d < dir ? (*this)[d] : d == dir ? a : (*this)[d - 1]; + }); + } + + /** Reverse the components of a point + */ + constexpr Point reversed() const { + return Point::make([&](size_type d) { return (*this)[D - 1 - d]; }); + } friend constexpr Point operator+(const Point &x) { return fmap([](const T &a) { return +a; }, x); @@ -196,11 +222,29 @@ template class Point { friend constexpr Point operator-(const Point &x) { return fmap([](const T &a) { return -a; }, x); } + // friend constexpr Point operator~(const Point &x) { + // if constexpr (std::is_same_v) + // // Special case to avoid compiler warnings + // return fmap([](const T &a) { return true; }, x); + // else if constexpr (std::is_integral_v) + // return fmap([](const T &a) { return ~a; }, x); + // std::abort(); + // } + template && + !std::is_same_v> * = nullptr> + friend constexpr Point operator~(const Point &x) { + return fmap([](const T &a) { return ~a; }, x); + } + template > * = nullptr> + friend constexpr Point operator~(const Point &x) { + // Special case to avoid compiler warnings + return fmap([](const T &a) { return true; }, x); + } + template > * = nullptr> friend constexpr Point operator~(const Point &x) { - if constexpr (std::is_same_v) - return fmap([](const T &a) { return true; }, x); - else if constexpr (std::is_integral_v) - return fmap([](const T &a) { return ~a; }, x); std::abort(); } friend constexpr Point operator!(const Point &x) { diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp new file mode 100644 index 0000000000..22f4e1f6b2 --- /dev/null +++ b/include/openPMD/regions/Region.hpp @@ -0,0 +1,1126 @@ +#ifndef REGIONS_REGION_HPP +#define REGIONS_REGION_HPP + +#include "Box.hpp" +#include "Helpers.hpp" +#include "Point.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace openPMD { +namespace Regions { + +/** A D-dimensional region + * + * A region is an arbitrarily shaped set of points. The internal + * representation is likely based on boxes, and is thus most efficient + * if the region has many axis-aligned boundaries. + * + * The dimension D needs to be known at compile time. @see NDRegion + */ +template class Region; + +//////////////////////////////////////////////////////////////////////////////// + +template class Region { + bool is_full; + + friend class Region; + + explicit constexpr Region(bool is_full) : is_full(is_full) {} + +public: + constexpr static std::size_t D = 0; + + typedef typename Point::value_type value_type; + typedef typename Point::size_type size_type; + + /** Invariant + */ + constexpr bool invariant() const { return true; } + + void check_invariant() const { +#if REGIONS_DEBUG + assert(invariant()); +#endif + } + + /** Create empty region + */ + constexpr Region() : is_full(false) {} + + Region(const Region &) = default; + Region(Region &&) = default; + Region &operator=(const Region &) = default; + Region &operator=(Region &&) = default; + + Region(const Point &p) : is_full(true) {} + Region(const Box &b) : is_full(!b.empty()) {} + + template + Region(const Region ®ion) : is_full(region.is_full) {} + + Region(const std::vector> &bs) { + is_full = false; + for (const auto &b : bs) + is_full |= !b.empty(); + } + operator std::vector>() const { + if (empty()) + return std::vector>{}; + return std::vector>{Box(Point())}; + } + + // Predicates + size_type ndims() const { return D; } + bool empty() const { return !is_full; } + size_type size() const { return is_full; } + size_type nboxes() const { return is_full; } + + // Comparison operators + friend bool operator==(const Region ®ion1, const Region ®ion2) { + return region1.empty() == region2.empty(); + } + friend bool operator!=(const Region ®ion1, const Region ®ion2) { + return !(region1 == region2); + } + friend bool operator==(const Region ®ion1, const Box &box2) { + return region1 == Region(box2); + } + friend bool operator!=(const Region ®ion1, const Box &box2) { + return region1 != Region(box2); + } + friend bool operator==(const Box &box1, const Region ®ion2) { + return Region(box1) == region2; + } + friend bool operator!=(const Box &box1, const Region ®ion2) { + return Region(box1) != region2; + } + + // Shift and scale operators + Region operator>>(const Point &d) const { return *this; } + Region operator<<(const Point &d) const { return *this; } + Region grown(const Point &dlo, const Point &dup) const { + return *this; + } + Region grown(const Point &d) const { return grown(d, d); } + Region grown(T n) const { return grown(Point::pure(n)); } + Region shrunk(const Point &dlo, const Point &dup) const { + return *this; + } + Region shrunk(const Point &d) const { return shrunk(d, d); } + Region shrunk(T n) const { return shrunk(Point::pure(n)); } + + // Set operations + friend Box bounding_box(const Region ®ion) { + if (region.empty()) + return Box(); + return Box(Point()); + } + + friend Region operator&(const Region ®ion1, const Region ®ion2) { + return Region(!region1.empty() & !region2.empty()); + } + friend Region operator|(const Region ®ion1, const Region ®ion2) { + return Region(!region1.empty() | !region2.empty()); + } + friend Region operator^(const Region ®ion1, const Region ®ion2) { + return Region(!region1.empty() ^ !region2.empty()); + } + friend Region operator-(const Region ®ion1, const Region ®ion2) { + return Region(!region1.empty() & region2.empty()); + } + + Region &operator&=(const Region ®ion) { return *this = *this & region; } + Region &operator|=(const Region ®ion) { return *this = *this | region; } + Region &operator^=(const Region ®ion) { return *this = *this ^ region; } + Region &operator-=(const Region ®ion) { return *this = *this - region; } + + friend Region intersection(const Region ®ion1, const Region ®ion2) { + return region1 & region2; + } + friend Region setunion(const Region ®ion1, const Region ®ion2) { + return region1 | region2; + } + friend Region symmetric_difference(const Region ®ion1, + const Region ®ion2) { + return region1 ^ region2; + } + friend Region difference(const Region ®ion1, const Region ®ion2) { + return region1 - region2; + } + + // Set comparison operators + bool contains(const Point &p) const { return !isdisjoint(Region(p)); } + friend bool isdisjoint(const Region ®ion1, const Region ®ion2) { + return (region1 & region2).empty(); + } + + // Comparison operators + friend bool operator<=(const Region ®ion1, const Region ®ion2) { + return (region1 - region2).empty(); + } + friend bool operator>=(const Region ®ion1, const Region ®ion2) { + return region2 <= region1; + } + friend bool operator<(const Region ®ion1, const Region ®ion2) { + return region1 != region2 && region1 <= region2; + } + friend bool operator>(const Region ®ion1, const Region ®ion2) { + return region2 < region1; + } + + bool issubset(const Region ®ion) const { return *this <= region; } + bool issuperset(const Region ®ion) const { return *this >= region; } + bool is_strict_subset(const Region ®ion) const { return *this < region; } + bool is_strict_superset(const Region ®ion) const { return *this > region; } + + friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { + os << "{"; + if (!region.empty()) + os << "(1)"; + os << "}"; + return os; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +template class Region { +public: + constexpr static std::size_t D = 1; + +private: + typedef Region Subregion; // This is essentially a bool + typedef std::vector Subregions; + Subregions subregions; + +public: + typedef typename Point::value_type value_type; + typedef typename Point::size_type size_type; + + /** Invariant + */ + bool invariant() const { + const size_type nboxes = subregions.size(); + for (size_type i = 1; i < nboxes; ++i) + // The subregions must be ordered + if (!(subregions[i - 1] < subregions[i])) { + assert(false); + return false; + } + // There must be an even number of subregions + if (subregions.size() % 2 != 0) { + assert(false); + return false; + } + return true; + } + + void check_invariant() const { +#if REGIONS_DEBUG + assert(invariant()); +#endif + } + + /** Create empty region + */ + Region() = default; + + Region(const Region &) = default; + Region(Region &&) = default; + Region &operator=(const Region &) = default; + Region &operator=(Region &&) = default; + + Region(const Point &p) : Region(Box(p)) {} + Region(const Box &b) { + if (b.empty()) + return; + subregions = {b.lower()[0], b.upper()[0]}; + check_invariant(); + } + + template + Region(const Region ®ion) : subregions(region.subregions.size()) { + std::transform(region.subregions.begin(), region.subregions.end(), + subregions.begin(), + [](const auto &subregion) { return Subregion(subregion); }); + } + +private: + static std::vector subregions_from_bounds(std::vector lbnds, + std::vector ubnds) { + const std::size_t nboxes = lbnds.size(); + assert(ubnds.size() == nboxes); + std::vector subregions; + if (nboxes == 0) + return subregions; + std::sort(lbnds.begin(), lbnds.end()); + std::sort(ubnds.begin(), ubnds.end()); + std::size_t nactive = 0; + std::size_t lpos = 0, upos = 0; + while (lpos < nboxes) { + const auto lbnd = lbnds[lpos]; + assert(upos < nboxes); + const auto ubnd = ubnds[upos]; + // Process lower bounds before upper bounds + if (lbnd <= ubnd) { + if (nactive == 0) + subregions.push_back(lbnd); + ++nactive; + ++lpos; + } else { + assert(nactive > 0); + --nactive; + if (nactive == 0) + subregions.push_back(ubnd); + ++upos; + } + } + assert(nactive > 0); + assert(upos < nboxes); + assert(upos + nactive == nboxes); + subregions.push_back(ubnds[nboxes - 1]); +#if REGIONS_DEBUG +#warning "TODO: turn this into a test case" + { + Region reg; + for (std::size_t i = 0; i < nboxes; ++i) + reg |= Region(Box(Point{lbnds[i]}, Point{ubnds[i]})); + assert(subregions == reg.subregions); + } +#endif + return subregions; + } + +public: + Region(const std::vector> &boxes) { + std::vector lbnds, ubnds; + lbnds.reserve(boxes.size()); + ubnds.reserve(boxes.size()); + for (const auto &box : boxes) { + lbnds.push_back(box.lower()[0]); + ubnds.push_back(box.upper()[0]); + } + subregions = subregions_from_bounds(std::move(lbnds), std::move(ubnds)); + check_invariant(); + } + operator std::vector>() const { + std::vector> res; + res.reserve(subregions.size() / 2); + auto iter = subregions.begin(); + const auto end = subregions.end(); + while (iter != end) { + const auto pos1 = *iter++; + const auto pos2 = *iter++; + res.emplace_back(Box(Point{pos1}, Point{pos2})); + } +#if REGIONS_DEBUG +#warning "TODO: turn this into a test case" + assert(std::is_sorted(res.begin(), res.end())); + { + Region reg; + for (const auto &b : res) { + assert(isdisjoint(Region(b), reg)); + reg |= b; + } + assert(reg == *this); + } +#endif + return res; + } + +private: + template + friend void traverse_subregions(const F &f, const Region ®ion) { + Subregion decoded_subregion; + for (const auto &pos : region.subregions) { + decoded_subregion ^= Subregion(Point()); + f(pos, decoded_subregion); + } + assert(decoded_subregion.empty()); + } + + template + friend void traverse_subregions(const F &f, const Region ®ion1, + const Region ®ion2) { + Subregion decoded_subregion1, decoded_subregion2; + + auto iter1 = region1.subregions.begin(); + auto iter2 = region2.subregions.begin(); + const auto end1 = region1.subregions.end(); + const auto end2 = region2.subregions.end(); + while (iter1 != end1 && iter2 != end2) { + const T next_pos1 = *iter1; + const T next_pos2 = *iter2; + using std::min; + const T pos = min(next_pos1, next_pos2); + const bool active1 = next_pos1 == pos; + const bool active2 = next_pos2 == pos; + decoded_subregion1 ^= Subregion(active1); + decoded_subregion2 ^= Subregion(active2); + f(pos, decoded_subregion1, decoded_subregion2); + if (active1) + ++iter1; + if (active2) + ++iter2; + } + for (; iter1 != end1; ++iter1) { + const T pos = *iter1; + decoded_subregion1 ^= Subregion(Point()); + f(pos, decoded_subregion1, Subregion()); + } + for (; iter2 != end2; ++iter2) { + const T pos = *iter2; + decoded_subregion2 ^= Subregion(Point()); + f(pos, Subregion(), decoded_subregion2); + } + assert(decoded_subregion1.empty()); + assert(decoded_subregion2.empty()); + } + + template + friend Region unary_operator(const F &op, const Region ®ion1) { + Region res; + Subregion old_decoded_subregion; + traverse_subregions( + [&](const T pos, const Subregion &decoded_subregion1) { + auto decoded_subregion_res = op(decoded_subregion1); + auto subregion = decoded_subregion_res ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.push_back(pos); + old_decoded_subregion = decoded_subregion_res; + }, + region1); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + + template + friend Region binary_operator(const F &op, const Region ®ion1, + const Region ®ion2) { + Region res; + Subregion old_decoded_subregion; + traverse_subregions( + [&](const T pos, const Subregion &decoded_subregion1, + const Subregion &decoded_subregion2) { + auto decoded_subregion_res = + op(decoded_subregion1, decoded_subregion2); + auto subregion = decoded_subregion_res ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.push_back(pos); + old_decoded_subregion = decoded_subregion_res; + }, + region1, region2); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + +public: + // Predicates + size_type ndims() const { return D; } + bool empty() const { return subregions.empty(); } + + size_type size() const { + size_type total_size = 0; + auto iter = subregions.begin(); + const auto end = subregions.end(); + while (iter != end) { + const auto pos0 = *iter++; + const auto pos1 = *iter++; + total_size += pos1 - pos0; + } + return total_size; + } + + size_type nboxes() const { return subregions.size(); } + + // Comparison operators + friend bool operator==(const Region ®ion1, const Region ®ion2) { + return region1.subregions == region2.subregions; + } + friend bool operator!=(const Region ®ion1, const Region ®ion2) { + return !(region1 == region2); + } + friend bool operator==(const Region ®ion1, const Box &box2) { + return region1 == Region(box2); + } + friend bool operator!=(const Region ®ion1, const Box &box2) { + return region1 != Region(box2); + } + friend bool operator==(const Box &box1, const Region ®ion2) { + return Region(box1) == region2; + } + friend bool operator!=(const Box &box1, const Region ®ion2) { + return Region(box1) != region2; + } + + // Shift and scale operators + Region operator>>(const Point &d) const { + Region nr; + nr.subregions.reserve(subregions.size()); + for (const auto &pos : subregions) + nr.subregions.push_back(pos + d[0]); + return nr; + } + Region operator<<(const Point &d) const { return *this >> -d; } + +private: + Region grown_(const Point &dlo, const Point &dup) const { + // Cannot shrink + assert(all(dlo + dup >= Point())); + std::vector lbnds, ubnds; + lbnds.reserve(subregions.size()); + ubnds.reserve(subregions.size()); + auto iter = subregions.begin(); + const auto end = subregions.end(); + while (iter != end) { + const auto pos0 = *iter++ - dlo[0]; + const auto pos1 = *iter++ + dup[0]; + lbnds.push_back(pos0); + ubnds.push_back(pos1); + } + Region nr; + nr.subregions = subregions_from_bounds(std::move(lbnds), std::move(ubnds)); + nr.check_invariant(); +#if REGIONS_DEBUG +#warning "TODO: turn this into a test case" + { + const std::vector> boxes(*this); + Region reg; + for (const auto &box : boxes) + reg |= box.grown(dlo, dup); + assert(nr == reg); + } +#endif + return nr; + } + Region shrunk_(const Point &dlo, const Point &dup) const { + // Cannot grow + assert(all(dlo + dup >= Point())); + Region nr; + auto iter = subregions.begin(); + const auto end = subregions.end(); + while (iter != end) { + const auto pos0 = *iter++ + dlo[0]; + const auto pos1 = *iter++ - dup[0]; + if (pos1 > pos0) { + nr.subregions.push_back(pos0); + nr.subregions.push_back(pos1); + } + } + nr.check_invariant(); +#if REGIONS_DEBUG +#warning "TODO: turn this into a test case" + { + auto world = bounding_box(*this).grown(1); + Region reg = Region(world.grown(dup, dlo)) - + (Region(world) - *this).grown(dup, dlo); + assert(nr == reg); + } +#endif + return nr; + } + +public: + Region grown(const Point &dlo, const Point &dup) const { + const Region ®ion0 = *this; + const bool need_shrink = any(dlo + dup < Point()); + Region shrunk_region; + if (need_shrink) { + // Shrink in some directions + const Point ndlo = fmap( + [](auto lo, auto up) { return lo + up < 0 ? lo : T(0); }, dlo, dup); + const Point ndup = fmap( + [](auto lo, auto up) { return lo + up < 0 ? up : T(0); }, dlo, dup); + shrunk_region = region0.shrunk_(-ndlo, -ndup); + } + const Region ®ion1 = need_shrink ? shrunk_region : *this; + const bool need_grow = any(dlo + dup > Point()); + Region grown_region; + if (need_grow) { + // Grow in some direction + const Point ndlo = fmap( + [](auto lo, auto up) { return lo + up > 0 ? lo : T(0); }, dlo, dup); + const Point ndup = fmap( + [](auto lo, auto up) { return lo + up > 0 ? up : T(0); }, dlo, dup); + grown_region = region1.grown_(ndlo, ndup); + } + const Region ®ion2 = need_grow ? grown_region : region1; + return region2; + } + Region grown(const Point &d) const { return grown(d, d); } + Region grown(const T &n) const { return grown(Point::pure(n)); } + Region shrunk(const Point &dlo, const Point &dup) const { + return grown(-dlo, -dup); + } + Region shrunk(const Point &d) const { return shrunk(d, d); } + Region shrunk(T n) const { return shrunk(Point(n)); } + + // Set operations + friend Box bounding_box(const Region ®ion) { + if (region.empty()) + return Box(); + return Box(Point{*region.subregions.begin()}, + Point{*(region.subregions.end() - 1)}); + } + + friend Region operator&(const Region ®ion1, const Region ®ion2) { + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 & set2; }, + region1, region2); + } + friend Region operator|(const Region ®ion1, const Region ®ion2) { + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 | set2; }, + region1, region2); + } + friend Region operator^(const Region ®ion1, const Region ®ion2) { + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 ^ set2; }, + region1, region2); + } + friend Region operator-(const Region ®ion1, const Region ®ion2) { + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 - set2; }, + region1, region2); + } + + Region &operator&=(const Region ®ion) { return *this = *this & region; } + Region &operator|=(const Region ®ion) { return *this = *this | region; } + Region &operator^=(const Region ®ion) { return *this = *this ^ region; } + Region &operator-=(const Region ®ion) { return *this = *this - region; } + + friend Region intersection(const Region ®ion1, const Region ®ion2) { + return region1 & region2; + } + friend Region setunion(const Region ®ion1, const Region ®ion2) { + return region1 | region2; + } + friend Region symmetric_difference(const Region ®ion1, + const Region ®ion2) { + return region1 ^ region2; + } + friend Region difference(const Region ®ion1, const Region ®ion2) { + return region1 - region2; + } + + // Set comparison operators + bool contains(const Point &p) const { return !isdisjoint(Region(p)); } + friend bool isdisjoint(const Region ®ion1, const Region ®ion2) { + return (region1 & region2).empty(); + } + + // Comparison operators + friend bool operator<=(const Region ®ion1, const Region ®ion2) { + return (region1 - region2).empty(); + } + friend bool operator>=(const Region ®ion1, const Region ®ion2) { + return region2 <= region1; + } + friend bool operator<(const Region ®ion1, const Region ®ion2) { + return region1 != region2 && region1 <= region2; + } + friend bool operator>(const Region ®ion1, const Region ®ion2) { + return region2 < region1; + } + + bool issubset(const Region ®ion) const { return *this <= region; } + bool issuperset(const Region ®ion) const { return *this >= region; } + bool is_strict_subset(const Region ®ion) const { return *this < region; } + bool is_strict_superset(const Region ®ion) const { return *this > region; } + + friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { + os << "{"; + const std::vector> boxes(region); + for (std::size_t i = 0; i < boxes.size(); ++i) { + if (i > 0) + os << ","; + os << boxes[i]; + } + os << "}"; + return os; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +template class Region { + typedef Region Subregion; + typedef std::vector> Subregions; + Subregions subregions; + +public: + typedef typename Point::value_type value_type; + typedef typename Point::size_type size_type; + + /** Invariant + */ + bool invariant() const { + for (const auto &pos_subregion : subregions) { + const auto &subregion = pos_subregion.second; + // The subregions must not be empty, and their invariants must hold + if (subregion.empty() || !subregion.invariant()) { + assert(false); + return false; + } + } + // // There must be an even number of subregions + // if (subregions.size() % 2 != 0) { + // assert(false); + // return false; + // } + return true; + } + + void check_invariant() const { +#if REGIONS_DEBUG + assert(invariant()); +#endif + } + + /** Create empty region + */ + Region() = default; + + Region(const Region &) = default; + Region(Region &&) = default; + Region &operator=(const Region &) = default; + Region &operator=(Region &&) = default; + + Region(const Point &p) : Region(Box(p)) {} + Region(const Box &b) { + if (b.empty()) + return; + Box subbox(b.lower().erase(D - 1), b.upper().erase(D - 1)); + subregions = {{b.lower()[D - 1], Subregion(subbox)}, + {b.upper()[D - 1], Subregion(subbox)}}; + check_invariant(); + } + + template + Region(const Region ®ion) : subregions(region.subregions.size()) { + std::transform(region.subregions.begin(), region.subregions.end(), + subregions.begin(), [&](const auto &pos_subregion) { + T pos(pos_subregion.first); + Subregion subregion(pos_subregion.second); + subregions.emplace_back( + std::make_pair(pos, std::move(subregion))); + }); + } + +private: + static Region region_from_boxes( + const typename std::vector>::const_iterator &begin, + const typename std::vector>::const_iterator &end) { + auto sz = end - begin; + if (sz == 0) + return Region(); + if (sz == 1) + return Region(*begin); + const auto mid = begin + sz / 2; + return region_from_boxes(begin, mid) | region_from_boxes(mid, end); + } + +public: + Region(const std::vector> &boxes) { + *this = region_from_boxes(boxes.begin(), boxes.end()); +#if REGIONS_DEBUG +#warning "TODO: turn this into a test case" + { + Region reg; + for (const auto &box : boxes) + reg |= region(box); + assert(*this == reg); + } +#endif + check_invariant(); + } + + operator std::vector>() const { + std::vector> res; + std::map, T> old_subboxes; + traverse_subregions( + [&](const T pos, const Subregion &subregion) { + // Convert subregion to boxes + const std::vector> subboxes1(subregion); + + auto iter0 = old_subboxes.begin(); + auto iter1 = subboxes1.begin(); + const auto end0 = old_subboxes.end(); + const auto end1 = subboxes1.end(); +#if REGIONS_DEBUG + assert(is_sorted(iter1, end1)); +#endif + std::map, T> subboxes; + while (iter0 != end0 || iter1 != end1) { + bool active0 = iter0 != end0; + bool active1 = iter1 != end1; + Box dummy; + const Box &subbox0 = active0 ? iter0->first : dummy; + const Box &subbox1 = active1 ? *iter1 : dummy; + // When both subboxes are active, keep only the first (as determined + // by less<>) + std::equal_to eq; + std::less lt; + if (active0 && active1) { + active0 = !lt(subbox0, subbox1); + active1 = !lt(subbox1, subbox0); + } + + const T old_pos = iter0->second; + if (active0 && active1 && eq(subbox0, subbox1)) { + // The current bbox continues unchanged -- keep it + subboxes[subbox1] = old_pos; + } else { + if (active0) + // The current box changed; finalize it + res.push_back(Box(subbox0.lower().insert(D - 1, old_pos), + subbox0.upper().insert(D - 1, pos))); + if (active1) + // There is a new box; add it + subboxes[subbox1] = pos; + } + + if (active0) + ++iter0; + if (active1) + ++iter1; + } + old_subboxes = std::move(subboxes); + }, + *this); + assert(old_subboxes.empty()); +#if REGIONS_DEBUG +#warning "TODO: turn this into a test case" + assert(std::is_sorted(res.begin(), res.end())); + { + Region reg; + for (const auto &b : res) { + assert(isdisjoint(Region(b), reg)); + reg |= b; + } + assert(reg == *this); + } +#endif + return res; + } + +private: + template + friend void traverse_subregions(const F &f, const Region ®ion) { + Subregion decoded_subregion; + for (const auto &pos_subregion : region.subregions) { + const T pos = pos_subregion.first; + const auto &subregion = pos_subregion.second; + decoded_subregion ^= subregion; + f(pos, decoded_subregion); + } + assert(decoded_subregion.empty()); + } + + template + friend void traverse_subregions(const F &f, const Region ®ion1, + const Region ®ion2) { + Subregion decoded_subregion1, decoded_subregion2; + + auto iter1 = region1.subregions.begin(); + auto iter2 = region2.subregions.begin(); + const auto end1 = region1.subregions.end(); + const auto end2 = region2.subregions.end(); + while (iter1 != end1 || iter2 != end2) { + const T next_pos1 = + iter1 != end1 ? iter1->first : std::numeric_limits::max(); + const T next_pos2 = + iter2 != end2 ? iter2->first : std::numeric_limits::max(); + using std::min; + const T pos = min(next_pos1, next_pos2); + const bool active1 = next_pos1 == pos; + const bool active2 = next_pos2 == pos; + Subregion dummy; + const Subregion &subregion1 = active1 ? iter1->second : dummy; + const Subregion &subregion2 = active2 ? iter2->second : dummy; + if (active1) + decoded_subregion1 ^= subregion1; + if (active2) + decoded_subregion2 ^= subregion2; + + f(pos, decoded_subregion1, decoded_subregion2); + + if (active1) + ++iter1; + if (active2) + ++iter2; + } + assert(decoded_subregion1.empty()); + assert(decoded_subregion2.empty()); + } + + template + friend Region unary_operator(const F &op, const Region ®ion1) { + Region res; + Subregion old_decoded_subregion; + traverse_subregions( + [&](const T pos, const Subregion &decoded_subregion1) { + auto decoded_subregion_res = op(decoded_subregion1); + auto subregion = decoded_subregion_res ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.emplace_back( + std::make_pair(pos, std::move(subregion))); + old_decoded_subregion = std::move(decoded_subregion_res); + }, + region1); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + + template + friend Region binary_operator(const F &op, const Region ®ion1, + const Region ®ion2) { + Region res; + Subregion old_decoded_subregion; + traverse_subregions( + [&](const T pos, const Subregion &decoded_subregion1, + const Subregion &decoded_subregion2) { + auto decoded_subregion_res = + op(decoded_subregion1, decoded_subregion2); + auto subregion = decoded_subregion_res ^ old_decoded_subregion; + if (!subregion.empty()) + res.subregions.emplace_back( + std::make_pair(pos, std::move(subregion))); + old_decoded_subregion = std::move(decoded_subregion_res); + }, + region1, region2); + assert(old_decoded_subregion.empty()); + assert(res.invariant()); + return res; + } + +public: + // Predicates + size_type ndims() const { return D; } + bool empty() const { return subregions.empty(); } + + size_type size() const { + size_type total_size = 0; + T old_pos = std::numeric_limits::min(); // location of last subregion + size_type old_subregion_size = 0; // number of points in the last subregion + traverse_subregions([&](const T pos, const Subregion &subregion) { + const size_type subregion_size = subregion.size(); + total_size += + old_subregion_size == 0 ? 0 : (pos - old_pos) * old_subregion_size; + old_pos = pos; + old_subregion_size = subregion_size; + }); + assert(old_subregion_size == 0); + return total_size; + } + + size_type nboxes() const { + size_type sz = 0; + for (const auto &pos_subregion : subregions) { + const auto &subregion = pos_subregion.second; + sz += subregion.nboxes(); + } + return sz; + } + + // Shift and scale operators + Region operator>>(const Point &d) const { + Region nr; + nr.subregions.reserve(subregions.size()); + const T dx = d[D - 1]; + auto subd = d.subpoint(D - 1); + for (const auto &pos_subregion : subregions) { + const T pos = pos_subregion.first; + const auto &subregion = pos_subregion.second; + nr.subregions.emplace_back(make_pair(pos + dx, subregion >> subd)); + } + nr.check_invariant(); + return nr; + } + Region operator<<(const Point &d) const { return *this >> -d; } + +private: + Region grown_(const Point &dlo, const Point &dup) const { + // Cannot shrink + assert(all(dlo + dup >= Point())); + return helpers::mapreduce( + [&](const Box &b) { return Region(b.grown(dlo, dup)); }, + [](const Region &x, const Region &y) { return x | y; }, + std::vector>(*this)); + } + Region shrunk_(const Point &dlo, const Point &dup) const { + // Cannot grow + assert(all(dlo + dup >= Point())); + auto world = bounding_box(*this).grown(1); + return Region(world.grown(dup, dlo)) - + (Region(world) - *this).grown(dup, dlo); + } + +public: + Region grown(const Point &dlo, const Point &dup) const { + const Region ®ion0 = *this; + const bool need_shrink = any(dlo + dup < Point()); + Region shrunk_region; + if (need_shrink) { + // Shrink in some directions + const Point ndlo = fmap( + [](auto lo, auto up) { return lo + up < 0 ? lo : T(0); }, dlo, dup); + const Point ndup = fmap( + [](auto lo, auto up) { return lo + up < 0 ? up : T(0); }, dlo, dup); + shrunk_region = region0.shrunk_(-ndlo, -ndup); + } + const Region ®ion1 = need_shrink ? shrunk_region : *this; + const bool need_grow = any(dlo + dup > Point()); + Region grown_region; + if (need_grow) { + // Grow in some direction + const Point ndlo = fmap( + [](auto lo, auto up) { return lo + up > 0 ? lo : T(0); }, dlo, dup); + const Point ndup = fmap( + [](auto lo, auto up) { return lo + up > 0 ? up : T(0); }, dlo, dup); + grown_region = region1.grown_(ndlo, ndup); + } + const Region ®ion2 = need_grow ? grown_region : region1; + return region2; + } + Region grown(const Point &d) const { return grown(d, d); } + Region grown(const T &n) const { return grown(Point::pure(n)); } + Region shrunk(const Point &dlo, const Point &dup) const { + return grown(-dlo, -dup); + } + Region shrunk(const Point &d) const { return shrunk(d, d); } + Region shrunk(const T &n) const { return shrunk(Point::pure(n)); } + + // Comparison operators + friend bool operator==(const Region ®ion1, const Region ®ion2) { + return region1.subregions == region2.subregions; + } + friend bool operator!=(const Region ®ion1, const Region ®ion2) { + return !(region1 == region2); + } + friend bool operator==(const Region ®ion1, const Box &box2) { + return region1 == Region(box2); + } + friend bool operator!=(const Region ®ion1, const Box &box2) { + return region1 != Region(box2); + } + friend bool operator==(const Box &box1, const Region ®ion2) { + return Region(box1) == region2; + } + friend bool operator!=(const Box &box1, const Region ®ion2) { + return Region(box1) != region2; + } + + // Set operations + friend Box bounding_box(const Region ®ion) { + if (region.empty()) + return Box(); + auto pmin = Point::pure(std::numeric_limits::max()); + auto pmax = Point::pure(std::numeric_limits::min()); + for (const auto &pos_subregion : region.subregions) { + const auto &subregion = pos_subregion.second; + auto subbox = bounding_box(subregion); + using std::max, std::min; + pmin = min(pmin, subbox.lower()); + pmax = max(pmax, subbox.upper()); + } + const T xmin = region.subregions.begin()->first; + const T xmax = (region.subregions.end() - 1)->first; + return Box(pmin.insert(D - 1, xmin), pmax.insert(D - 1, xmax)); + } + + friend Region operator&(const Region ®ion1, const Region ®ion2) { + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 & set2; }, + region1, region2); + } + friend Region operator|(const Region ®ion1, const Region ®ion2) { + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 | set2; }, + region1, region2); + } + friend Region operator^(const Region ®ion1, const Region ®ion2) { + // TODO: If region2 is much smaller than region1, direct insertion may be + // faster + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 ^ set2; }, + region1, region2); + } + friend Region operator-(const Region ®ion1, const Region ®ion2) { + return binary_operator([](const Subregion &set1, + const Subregion &set2) { return set1 - set2; }, + region1, region2); + } + + Region &operator&=(const Region ®ion) { return *this = *this & region; } + Region &operator|=(const Region ®ion) { return *this = *this | region; } + Region &operator^=(const Region ®ion) { return *this = *this ^ region; } + Region &operator-=(const Region ®ion) { return *this = *this - region; } + + friend Region intersection(const Region ®ion1, const Region ®ion2) { + return region1 & region2; + } + friend Region setunion(const Region ®ion1, const Region ®ion2) { + return region1 | region2; + } + friend Region symmetric_difference(const Region ®ion1, + const Region ®ion2) { + return region1 ^ region2; + } + friend Region difference(const Region ®ion1, const Region ®ion2) { + return region1 - region2; + } + + // Set comparison operators + bool contains(const Point &p) const { return !isdisjoint(Region(p)); } + friend bool isdisjoint(const Region ®ion1, const Region ®ion2) { + return (region1 & region2).empty(); + } + + // Comparison operators + friend bool operator<=(const Region ®ion1, const Region ®ion2) { + return (region1 - region2).empty(); + } + friend bool operator>=(const Region ®ion1, const Region ®ion2) { + return region2 <= region1; + } + friend bool operator<(const Region ®ion1, const Region ®ion2) { + return region1 != region2 && region1 <= region2; + } + friend bool operator>(const Region ®ion1, const Region ®ion2) { + return region2 < region1; + } + + bool issubset(const Region ®ion) const { return *this <= region; } + bool issuperset(const Region ®ion) const { return *this >= region; } + bool is_strict_subset(const Region ®ion) const { return *this < region; } + bool is_strict_superset(const Region ®ion) const { return *this > region; } + + friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { + os << "{"; + const std::vector> boxes(region); + for (std::size_t i = 0; i < boxes.size(); ++i) { + if (i > 0) + os << ","; + os << boxes[i]; + } + os << "}"; + return os; + } +}; + +} // namespace Regions +} // namespace openPMD + +#endif // #ifndef REGIONS_REGION_HPP diff --git a/include/openPMD/regions/Regions.hpp b/include/openPMD/regions/Regions.hpp index d0ef5617c3..76efe0d726 100644 --- a/include/openPMD/regions/Regions.hpp +++ b/include/openPMD/regions/Regions.hpp @@ -1,10 +1,9 @@ #ifndef REGIONS_REGIONS_HPP #define REGIONS_REGIONS_HPP -#define REGIONS_DEBUG 1 // 0 or 1 - #include "Point.hpp" #include "Box.hpp" +#include "Region.hpp" #include "NDPoint.hpp" diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp index 902f0569de..5ef683e00a 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/RegionsBoxTest.cpp @@ -11,7 +11,6 @@ using namespace openPMD::Regions; template void test_Box(const B &b) { - REQUIRE(b.empty()); const std::size_t D = b.ndims(); using T = typename B::value_type; const auto p = b.lower(); @@ -20,6 +19,7 @@ template void test_Box(const B &b) { const std::equal_to eqb; const std::less

ltp; const std::less ltb; + REQUIRE(b.empty()); std::mt19937 gen; std::uniform_int_distribution dist0(0, 9); diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 158145651c..1acc4edbd4 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -353,6 +353,28 @@ template void test_Point_float(const P &p) { const T a = rand(); const T b = rand(); + // remove-insert is no-op + if (D > 0) { + for (std::size_t d = 0; d < D; ++d) { + const auto a1 = x[d]; + const auto x1 = x.erase(d); + REQUIRE(x1.ndims() == D - 1); + const auto x2 = x1.insert(d, a1); + // This conversion is fine for D > 0 + const P x3 = *(const P *)&x2; + REQUIRE(eq(x3, x)); + } + } + // insert-remove is no-op + for (std::size_t d = 0; d <= D; ++d) { + const auto x1 = x.insert(d, a); + REQUIRE(x1.ndims() == D + 1); + REQUIRE(x1[d] == a); + REQUIRE(eq(x1.erase(d), x)); + } + + REQUIRE(eq(x.reversed().reversed(), x)); + REQUIRE(eq(fmap([](auto i) { return i; }, x), x)); REQUIRE(eq(fmap([](auto i) { return i + 1; }, fmap([](auto i) { return 2 * i; }, x)), diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp new file mode 100644 index 0000000000..5ffd0f71e5 --- /dev/null +++ b/test/RegionsRegionTest.cpp @@ -0,0 +1,152 @@ +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace openPMD::Regions; + +template void test_Region(const R &r) { + const std::size_t D = r.ndims(); + using T = typename R::value_type; + const auto b = bounding_box(r); + const auto p = b.lower(); + typedef std::decay_t B; + typedef std::decay_t P; + const std::equal_to

eqp; + const std::equal_to eqb; + const std::equal_to eqr; + const std::less

ltp; + const std::less ltb; + const std::less ltr; + REQUIRE(r.empty()); + REQUIRE(b.empty()); + + std::mt19937 gen; + std::uniform_int_distribution dist0(0, 9); + std::uniform_int_distribution dist(-1000, 1000); + const auto rand = [&]() { return dist(gen); }; + const auto randp = [&]() { return fmap([&](auto) { return rand(); }, p); }; + const auto randb = [&]() { + if (D == 0) { + if (dist0(gen) < 5) + return B(); + else + return B(p); + } + if (dist0(gen) == 0) + return b; + while (1) { + auto lo = randp(); + auto hi = randp(); + auto nb = B(lo, hi); + if (!nb.empty()) + return nb; + } + }; + const auto randr = [&]() { + if (D == 0) { + if (dist0(gen) < 5) + return R(); + else + return R(B(p)); + } + const int nboxes = dist0(gen) / 2; + R r; + for (int n = 0; n < nboxes; ++n) + r |= randb(); + return r; + }; + + for (int iter = 0; iter < 100; ++iter) { + + R N = r; + REQUIRE(N.empty()); + R X = randr(); + R Y = randr(); + R Z = randr(); + + const B E = bounding_box(bounding_box(bounding_box(X), bounding_box(Y)), + bounding_box(Z)) + .grown(10); + + REQUIRE((N & X) == N); + REQUIRE((X & N) == N); + REQUIRE((E & X) == X); + REQUIRE((X & E) == X); + REQUIRE((X & Y) == (Y & X)); + REQUIRE(((X & Y) & Z) == (X & (Y & Z))); + + REQUIRE((N | X) == X); + REQUIRE((E | X) == E); + REQUIRE((X | E) == E); + REQUIRE((X | Y) == (Y | X)); + REQUIRE(((X | Y) | Z) == (X | (Y | Z))); + + REQUIRE(E - (X & Y) == ((E - X) | (E - Y))); + REQUIRE(E - (X | Y) == ((E - X) & (E - Y))); + + const R IXY = X & Y; + REQUIRE((IXY <= X && IXY <= Y) == true); + REQUIRE((IXY.grown(1) <= X && IXY.grown(1) <= Y) == + (D == 0 || IXY.empty())); + + const R UXY = X | Y; + REQUIRE((X <= UXY && Y <= UXY) == true); + + const R DXY = X - Y; + REQUIRE((DXY <= X || !isdisjoint(DXY, Y)) == true); + + const R SXY = X ^ Y; + REQUIRE((SXY <= UXY && isdisjoint(SXY, IXY)) == true); + + REQUIRE(IXY <= UXY); + REQUIRE((IXY | SXY) == UXY); + + } // for iter +} + +TEST_CASE("Region", "[regions]") { + test_Region(Region()); +} +TEST_CASE("Region", "[regions]") { + test_Region(Region()); +} +TEST_CASE("Region", "[regions]") { + test_Region(Region()); +} +TEST_CASE("Region", "[regions]") { + test_Region(Region()); +} + +TEST_CASE("Region", "[regions]") { test_Region(Region()); } +TEST_CASE("Region", "[regions]") { test_Region(Region()); } +TEST_CASE("Region", "[regions]") { test_Region(Region()); } +TEST_CASE("Region", "[regions]") { test_Region(Region()); } + +#warning "TODO" +#if 0 +TEST_CASE("NDRegion(0)", "[regions]") { + test_Region(NDRegion(0)); +} +TEST_CASE("NDRegion(1)", "[regions]") { + test_Region(NDRegion(1)); +} +TEST_CASE("NDRegion(2)", "[regions]") { + test_Region(NDRegion(2)); +} +TEST_CASE("NDRegion(3)", "[regions]") { + test_Region(NDRegion(3)); +} + +TEST_CASE("NDRegion(0)", "[regions]") { test_Region(NDRegion(0)); } +TEST_CASE("NDRegion(1)", "[regions]") { test_Region(NDRegion(1)); } +TEST_CASE("NDRegion(2)", "[regions]") { test_Region(NDRegion(2)); } +TEST_CASE("NDRegion(3)", "[regions]") { test_Region(NDRegion(3)); } +#endif From 83c48f4ebd4d9d4dfb88eeefe4f1f432e74b5e0c Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 13:26:06 -0400 Subject: [PATCH 07/68] More tests (and error corrections) for regions --- include/openPMD/regions/Region.hpp | 121 +- test/RegionCalculusTest_old.cpp | 1638 ---------------------------- test/RegionsRegionTest.cpp | 40 + 3 files changed, 109 insertions(+), 1690 deletions(-) delete mode 100644 test/RegionCalculusTest_old.cpp diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 22f4e1f6b2..c721192a48 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -106,6 +106,10 @@ template class Region { // Shift and scale operators Region operator>>(const Point &d) const { return *this; } Region operator<<(const Point &d) const { return *this; } + Region &operator>>=(const Point &d) { return *this = *this >> d; } + Region &operator<<=(const Point &d) { return *this = *this << d; } + Region operator*(const Point &s) const { return *this; } + Region &operator*=(const Point &s) { return *this = *this * s; } Region grown(const Point &dlo, const Point &dup) const { return *this; } @@ -319,7 +323,7 @@ template class Region { while (iter != end) { const auto pos1 = *iter++; const auto pos2 = *iter++; - res.emplace_back(Box(Point{pos1}, Point{pos2})); + res.emplace_back(Box(Point{pos1}, Point{pos2})); } #if REGIONS_DEBUG #warning "TODO: turn this into a test case" @@ -330,6 +334,16 @@ template class Region { assert(isdisjoint(Region(b), reg)); reg |= b; } + if (!(reg == *this)) { + std::cout << "subregions=["; + for (const auto &sr : subregions) + std::cout << sr << ","; + std::cout << "]\n"; + std::cout << "reg=["; + for (const auto &r : reg.subregions) + std::cout << r << ","; + std::cout << "]\n"; + } assert(reg == *this); } #endif @@ -469,65 +483,41 @@ template class Region { nr.subregions.reserve(subregions.size()); for (const auto &pos : subregions) nr.subregions.push_back(pos + d[0]); + check_invariant(); return nr; } Region operator<<(const Point &d) const { return *this >> -d; } + Region &operator>>=(const Point &d) { return *this = *this >> d; } + Region &operator<<=(const Point &d) { return *this = *this << d; } + Region operator*(const Point &s) const { + if (s[0] == 0) + return empty() ? Region() : Region(Point()); + Region nr; + nr.subregions.reserve(subregions.size()); + for (const auto &pos : subregions) + nr.subregions.push_back(pos * s[0]); + if (s[0] < 0) + std::reverse(nr.subregions.begin(), nr.subregions.end()); + check_invariant(); + return nr; + } + Region &operator*=(const Point &s) { return *this = *this * s; } private: Region grown_(const Point &dlo, const Point &dup) const { // Cannot shrink assert(all(dlo + dup >= Point())); - std::vector lbnds, ubnds; - lbnds.reserve(subregions.size()); - ubnds.reserve(subregions.size()); - auto iter = subregions.begin(); - const auto end = subregions.end(); - while (iter != end) { - const auto pos0 = *iter++ - dlo[0]; - const auto pos1 = *iter++ + dup[0]; - lbnds.push_back(pos0); - ubnds.push_back(pos1); - } - Region nr; - nr.subregions = subregions_from_bounds(std::move(lbnds), std::move(ubnds)); - nr.check_invariant(); -#if REGIONS_DEBUG -#warning "TODO: turn this into a test case" - { - const std::vector> boxes(*this); - Region reg; - for (const auto &box : boxes) - reg |= box.grown(dlo, dup); - assert(nr == reg); - } -#endif - return nr; + return helpers::mapreduce( + [&](const Box &b) { return Region(b.grown(dlo, dup)); }, + [](const Region &x, const Region &y) { return x | y; }, + std::vector>(*this)); } Region shrunk_(const Point &dlo, const Point &dup) const { // Cannot grow assert(all(dlo + dup >= Point())); - Region nr; - auto iter = subregions.begin(); - const auto end = subregions.end(); - while (iter != end) { - const auto pos0 = *iter++ + dlo[0]; - const auto pos1 = *iter++ - dup[0]; - if (pos1 > pos0) { - nr.subregions.push_back(pos0); - nr.subregions.push_back(pos1); - } - } - nr.check_invariant(); -#if REGIONS_DEBUG -#warning "TODO: turn this into a test case" - { - auto world = bounding_box(*this).grown(1); - Region reg = Region(world.grown(dup, dlo)) - - (Region(world) - *this).grown(dup, dlo); - assert(nr == reg); - } -#endif - return nr; + auto world = bounding_box(*this).grown(1); + return Region(world.grown(dup, dlo)) - + (Region(world) - *this).grown(dup, dlo); } public: @@ -879,7 +869,7 @@ template class Region { }, region1); assert(old_decoded_subregion.empty()); - assert(res.invariant()); + res.check_invariant(); return res; } @@ -901,7 +891,7 @@ template class Region { }, region1, region2); assert(old_decoded_subregion.empty()); - assert(res.invariant()); + res.check_invariant(); return res; } @@ -939,16 +929,43 @@ template class Region { Region nr; nr.subregions.reserve(subregions.size()); const T dx = d[D - 1]; - auto subd = d.subpoint(D - 1); + auto subd = d.erase(D - 1); for (const auto &pos_subregion : subregions) { const T pos = pos_subregion.first; const auto &subregion = pos_subregion.second; - nr.subregions.emplace_back(make_pair(pos + dx, subregion >> subd)); + nr.subregions.emplace_back(std::make_pair(pos + dx, subregion >> subd)); } nr.check_invariant(); return nr; } Region operator<<(const Point &d) const { return *this >> -d; } + Region &operator>>=(const Point &d) { return *this = *this >> d; } + Region &operator<<=(const Point &d) { return *this = *this << d; } + Region operator*(const Point &s) const { + if (s[D - 1] == 0) + return empty() ? Region() : Region(Point()); + Region nr; + nr.subregions.reserve(subregions.size()); + const T ds = s[D - 1]; + auto subs = s.erase(D - 1); + for (const auto &pos_subregion : subregions) { + const T pos = pos_subregion.first; + const auto &subregion = pos_subregion.second; + nr.subregions.emplace_back(std::make_pair(pos * ds, subregion * subs)); + } + if (ds < 0) { + std::reverse(nr.subregions.begin(), nr.subregions.end()); + std::transform(nr.subregions.begin(), nr.subregions.end(), + nr.subregions.begin(), [](const auto &pos_subregion) { + const auto &pos = pos_subregion.first; + const auto &subregion = pos_subregion.second; + return std::make_pair(pos + 1, subregion); + }); + } + nr.check_invariant(); + return nr; + } + Region &operator*=(const Point &s) { return *this = *this * s; } private: Region grown_(const Point &dlo, const Point &dup) const { diff --git a/test/RegionCalculusTest_old.cpp b/test/RegionCalculusTest_old.cpp deleted file mode 100644 index a63f7bedb8..0000000000 --- a/test/RegionCalculusTest_old.cpp +++ /dev/null @@ -1,1638 +0,0 @@ -#include "openPMD/RegionCalculus.hpp" - -#if 0 - -#include - -#include -#include -#include -#include -#include -#include -#include - -using std::array; -using std::equal_to; -using std::hash; -using std::less; -using std::ostringstream; -using std::set; -using std::vector; - -int irand(int imax) { - static std::mt19937 rng; - return std::uniform_int_distribution<>(0, imax)(rng); -} - -using namespace RegionCalculus; - -TEST(RegionCalculus, point) { - typedef point point; - point p; - EXPECT_FALSE(all(p)); - EXPECT_FALSE(any(p)); - point p0(0); - EXPECT_FALSE(all(p0)); - EXPECT_FALSE(any(p0)); - EXPECT_TRUE(all(p0 == p)); - point p1(::point(true)); - EXPECT_TRUE(all(p1)); - EXPECT_TRUE(any(p1)); - EXPECT_TRUE(all(p1 != p)); - EXPECT_TRUE(all(p1 == +p1)); - EXPECT_TRUE(all(p0 == -p0)); - EXPECT_TRUE(all(p1 == abs(-p1))); - EXPECT_TRUE(all(-p1 == ~p0)); - EXPECT_TRUE(all(!p0)); - point p2(point(2)); - EXPECT_TRUE(equal_to()(p2, point(::point(p2)))); - EXPECT_TRUE(all(p1 + p1 == p2)); - EXPECT_TRUE(all(p2 - p1 == p1)); - EXPECT_TRUE(all(p2 * p1 == p2)); - EXPECT_TRUE(all((p2 & p1) == p0)); - EXPECT_TRUE(all((p2 | p1) == p1 + p2)); - EXPECT_TRUE(all((p2 ^ p1) == p1 + p2)); - EXPECT_TRUE(all(p1 && p2)); - EXPECT_TRUE(all(p1 || p2)); - EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); - EXPECT_EQ(2, minval(p2)); - EXPECT_EQ(2, maxval(p2)); - EXPECT_EQ(3, sum(p1)); - EXPECT_EQ(8, prod(p2)); - point p3; - p3.elt[0] = 0; - p3.elt[1] = 1; - p3.elt[2] = 2; - EXPECT_TRUE(all((p1 + p3) * point(2) - p3 == p3 + p2)); - EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); - EXPECT_TRUE(all(p2 == ::point(::point(2)))); - EXPECT_TRUE(all(p2 == ::point(::point(2)))); - auto v2a = array{2, 2, 2}; - EXPECT_EQ(v2a, (array(::point(2)))); - EXPECT_EQ(v2a, (array(::point(2)))); - auto v2 = vector({2, 2, 2}); - EXPECT_EQ(v2, vector(::point(2))); - EXPECT_EQ(v2, vector(::point(2))); - ostringstream buf; - buf << p3; - EXPECT_EQ("[0,1,2]", buf.str()); -} - -TEST(RegionCalculus, box) { - typedef point point; - typedef box box; - box b; - EXPECT_TRUE(b.empty()); - point p0(0), p1(1); - box b1(box(p0, p1)); - EXPECT_EQ(1, b1.size()); - box b4(p0, point(4)); - box b5 = b4 >> p1; - box b3 = b4 << p1; - box b6 = b3 * point(2); - box b7 = b4.grow(p0, p1); - box b8 = b4.grow(p1, p0); - EXPECT_EQ(b4, (box(::box(b4)))); - EXPECT_TRUE(b4 == b4); - EXPECT_TRUE(b4 != b5); - EXPECT_TRUE(b4.contains(p1)); - EXPECT_FALSE(b5.contains(p0)); - EXPECT_FALSE(b5.isdisjoint(b3)); - EXPECT_TRUE(b1.isdisjoint(b5)); - EXPECT_TRUE(b.isdisjoint(b5)); - EXPECT_TRUE(b1.isdisjoint(b)); - EXPECT_TRUE(b6.issuperset(b3)); - EXPECT_FALSE(b3.issuperset(b6)); - EXPECT_TRUE(b4.issuperset(b4)); - EXPECT_TRUE(b4.issuperset(b)); - EXPECT_FALSE(b.issuperset(b4)); - EXPECT_TRUE(b.issuperset(b)); - EXPECT_TRUE(b7.issuperset(b4)); - EXPECT_TRUE(b7.issuperset(b5)); - EXPECT_TRUE(b8.issuperset(b4)); - EXPECT_TRUE(b8.issuperset(b3)); - EXPECT_FALSE(b4.is_strict_superset(b4)); - EXPECT_TRUE(b4.is_strict_superset(b)); - EXPECT_FALSE(b.is_strict_superset(b4)); - EXPECT_FALSE(b.is_strict_superset(b)); - EXPECT_EQ(box(p1, point(3)), b5.intersection(b3)); - EXPECT_TRUE(b1.intersection(b5).empty()); - EXPECT_TRUE(b.intersection(b5).empty()); - EXPECT_TRUE(b1.intersection(b).empty()); - ostringstream buf; - buf << b4; - EXPECT_EQ("([0,0,0]:[4,4,4])", buf.str()); -} - -#if !REGIONCALCULUS_TREE - -TEST(RegionCalculus, region1) { - typedef point point; - typedef box box; - typedef region region; - region r; - EXPECT_TRUE(r.invariant()); - EXPECT_TRUE(r.empty()); - point p; - point p1(1); - box b; - box b1(p, p1); - region r0(b); - EXPECT_TRUE(r0.invariant()); - EXPECT_TRUE(r0.empty()); - region r1(b1); - EXPECT_TRUE(r1.invariant()); - EXPECT_FALSE(r1.empty()); - EXPECT_TRUE(r == r); - EXPECT_TRUE(r1 == r1); - EXPECT_FALSE(r != r); - EXPECT_TRUE(r != r1); - point p2(2); - box b2(p, p2); - region r2(b2); - EXPECT_EQ(r2, (region(::region(r2)))); - EXPECT_TRUE(r == r.intersection(r1)); - EXPECT_TRUE(r == r1.intersection(r)); - EXPECT_TRUE(r1 == r1.intersection(r2)); - EXPECT_TRUE(r1 == r2.intersection(r1)); - EXPECT_TRUE(r == r.difference(r)); - EXPECT_TRUE(r1 == r1.difference(r)); - EXPECT_TRUE(r == r.difference(r1)); - EXPECT_TRUE(r == r1.difference(r1)); - EXPECT_TRUE(r == r.setunion(r)); - EXPECT_TRUE(r1 == r1.setunion(r)); - EXPECT_TRUE(r1 == r.setunion(r1)); - EXPECT_TRUE(r1 == r1.setunion(r1)); - EXPECT_TRUE(r == r.symmetric_difference(r)); - EXPECT_TRUE(r1 == r1.symmetric_difference(r)); - EXPECT_TRUE(r1 == r.symmetric_difference(r1)); - EXPECT_TRUE(r == r1.symmetric_difference(r1)); - vector r12vals; - r12vals.push_back(b1); - r12vals.push_back(box(p1, p2)); - region r12(r12vals); - vector r12boxes = r12; - EXPECT_EQ(r12vals, r12boxes); - vector rs; - rs.push_back(r); - rs.push_back(r1); - rs.push_back(r2); - box b4(p, point(4)); - region r4(b4); - rs.push_back(r4); - rs.push_back(r12); - rs.push_back(r2.difference(r1)); - rs.push_back(r2.symmetric_difference(r12)); - for (std::size_t i = 0; i < rs.size(); ++i) { - const auto &ri = rs[i]; - auto rgrown = ri.grow(p1); - auto rshrunk = ri.shrink(p1); - EXPECT_TRUE(ri.invariant()); - EXPECT_TRUE(rgrown.invariant()); - EXPECT_TRUE(rshrunk.invariant()); - EXPECT_TRUE(rgrown.issuperset(ri)); - if (ri.empty()) - EXPECT_TRUE(rgrown.empty()); - EXPECT_TRUE(ri.issuperset(rshrunk)); - if (!rshrunk.empty()) - EXPECT_TRUE(rshrunk.grow(p1) == ri); - EXPECT_TRUE(rgrown.shrink(p1) == ri); - for (std::size_t j = 0; j < rs.size(); ++j) { - const auto &rj = rs[j]; - auto rintersection = ri.intersection(rj); - auto rdifference = ri.difference(rj); - auto rsetunion = ri.setunion(rj); - auto rsymmetric_difference = ri.symmetric_difference(rj); - EXPECT_TRUE(rintersection.invariant()); - EXPECT_TRUE(rdifference.invariant()); - EXPECT_TRUE(rsetunion.invariant()); - EXPECT_TRUE(rsymmetric_difference.invariant()); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(rj.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(ri)); - EXPECT_TRUE(rsetunion.issuperset(rj)); - EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); - EXPECT_TRUE(rdifference.isdisjoint(rj)); - EXPECT_TRUE(rsetunion.issuperset(rintersection)); - EXPECT_TRUE(rsetunion.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); - EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); - EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); - EXPECT_TRUE(rintersection == rj.intersection(ri)); - EXPECT_TRUE(rsetunion == rj.setunion(ri)); - EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); - if (ri == rj) { - EXPECT_TRUE(rintersection == ri); - EXPECT_TRUE(rdifference.empty()); - EXPECT_TRUE(rsetunion == ri); - EXPECT_TRUE(rsymmetric_difference.empty()); - } - } - } - ostringstream buf; - buf << r12; - EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); -} - -#else // #if REGIONCALCULUS_TREE - -TEST(RegionCalculus, region) { - typedef point point; - typedef box box; - typedef region region; - region r; - EXPECT_TRUE(r.invariant()); - EXPECT_TRUE(r.empty()); - point p; - point p1(1); - box b; - box b1(p, p1); - region r0(b); - EXPECT_TRUE(r0.invariant()); - EXPECT_TRUE(r0.empty()); - region r1(b1); - EXPECT_TRUE(r1.invariant()); - EXPECT_FALSE(r1.empty()); - EXPECT_TRUE(r == r); - EXPECT_TRUE(r1 == r1); - EXPECT_FALSE(r != r); - EXPECT_TRUE(r != r1); - point p2(2); - box b2(p, p2); - region r2(b2); - EXPECT_EQ(r2, region(::region(r2))); - EXPECT_TRUE(r == r.intersection(r1)); - EXPECT_TRUE(r == r1.intersection(r)); - EXPECT_TRUE(r1 == r1.intersection(r2)); - EXPECT_TRUE(r1 == r2.intersection(r1)); - EXPECT_TRUE(r == r.difference(r)); - EXPECT_TRUE(r1 == r1.difference(r)); - EXPECT_TRUE(r == r.difference(r1)); - EXPECT_TRUE(r == r1.difference(r1)); - EXPECT_TRUE(r == r.setunion(r)); - EXPECT_TRUE(r1 == r1.setunion(r)); - EXPECT_TRUE(r1 == r.setunion(r1)); - EXPECT_TRUE(r1 == r1.setunion(r1)); - EXPECT_TRUE(r == r.symmetric_difference(r)); - EXPECT_TRUE(r1 == r1.symmetric_difference(r)); - EXPECT_TRUE(r1 == r.symmetric_difference(r1)); - EXPECT_TRUE(r == r1.symmetric_difference(r1)); - EXPECT_EQ(0, vector(r).size()); - EXPECT_EQ(1, vector(r1).size()); - EXPECT_EQ(1, vector(r2).size()); - auto r21 = r2 - r1; - vector r21boxes(r21); - EXPECT_EQ(3, r21boxes.size()); - EXPECT_EQ(box(point(1, 0, 0), point(2, 1, 1)), r21boxes.at(0)); - EXPECT_EQ(box(point(0, 1, 0), point(2, 2, 1)), r21boxes.at(1)); - EXPECT_EQ(box(point(0, 0, 1), point(2, 2, 2)), r21boxes.at(2)); - vector r12vals; - r12vals.push_back(b1); - r12vals.push_back(box(p1, p2)); - region r12(r12vals); - vector r12boxes = r12; - EXPECT_EQ(r12vals, r12boxes); - vector rs; - rs.push_back(r); - rs.push_back(r1); - rs.push_back(r2); - box b4(p, point(4)); - region r4(b4); - rs.push_back(r4); - rs.push_back(r12); - rs.push_back(r2.difference(r1)); - rs.push_back(r2.symmetric_difference(r12)); - for (int i = 0; i < 10; ++i) { - vector bs; - for (int n = irand(5); n >= 0; --n) - bs.push_back(box(point(irand(10), irand(10), irand(10)), - point(irand(10), irand(10), irand(10)))); - rs.push_back(region(bs)); - } - for (std::size_t i = 0; i < rs.size(); ++i) { - const auto &ri = rs[i]; - EXPECT_TRUE(ri.invariant()); - const auto ri_size = ri.size(); - for (std::size_t j = 0; j < rs.size(); ++j) { - const auto &rj = rs[j]; - const auto rj_size = rj.size(); - auto rintersection = ri.intersection(rj); - auto rdifference = ri.difference(rj); - auto rsetunion = ri.setunion(rj); - auto rsymmetric_difference = ri.symmetric_difference(rj); - auto rintersection_size = rintersection.size(); - auto rdifference_size = rdifference.size(); - auto rsetunion_size = rsetunion.size(); - auto rsymmetric_difference_size = rsymmetric_difference.size(); - EXPECT_TRUE(rintersection.invariant()); - EXPECT_TRUE(rdifference.invariant()); - EXPECT_TRUE(rsetunion.invariant()); - EXPECT_TRUE(rsymmetric_difference.invariant()); - EXPECT_TRUE(rintersection_size <= ri_size && - rintersection_size <= rj_size); - EXPECT_TRUE(rdifference_size <= ri_size && - rdifference_size >= ri_size - rj_size); - EXPECT_TRUE(rsetunion_size >= ri_size && rsetunion_size >= rj_size && - rsetunion_size <= ri_size + rj_size); - EXPECT_TRUE(rsymmetric_difference_size <= ri_size + rj_size && - rsymmetric_difference_size >= abs(ri_size - rj_size)); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(rj.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(ri)); - EXPECT_TRUE(rsetunion.issuperset(rj)); - EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); - EXPECT_TRUE(rdifference.isdisjoint(rj)); - EXPECT_TRUE(rsetunion.issuperset(rintersection)); - EXPECT_TRUE(rsetunion.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); - EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); - EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); - EXPECT_TRUE(rintersection == rj.intersection(ri)); - EXPECT_TRUE(rsetunion == rj.setunion(ri)); - EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); - for (int n = 0; n < 10; ++n) { - int i = irand(10), j = irand(10), k = irand(10); - point p(i, j, k); - EXPECT_EQ(ri.contains(p) & rj.contains(p), rintersection.contains(p)); - EXPECT_EQ(ri.contains(p) & !rj.contains(p), rdifference.contains(p)); - EXPECT_EQ(ri.contains(p) | rj.contains(p), rsetunion.contains(p)); - EXPECT_EQ(ri.contains(p) ^ rj.contains(p), - rsymmetric_difference.contains(p)); - } - if (ri == rj) { - EXPECT_TRUE(rintersection == ri); - EXPECT_TRUE(rdifference.empty()); - EXPECT_TRUE(rsetunion == ri); - EXPECT_TRUE(rsymmetric_difference.empty()); - } - } - } - ostringstream buf; - buf << r12; - EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); -} - -#endif // #if REGIONCALCULUS_TREE - -TEST(RegionCalculus, region2) { - typedef point point; - typedef box box; - typedef region region; - region r; - EXPECT_TRUE(r.invariant()); - EXPECT_TRUE(r.empty()); - point p; - point p1(1); - box b; - box b1(p, p1); - region r0(b); - EXPECT_TRUE(r0.invariant()); - EXPECT_TRUE(r0.empty()); - region r1(b1); - EXPECT_TRUE(r1.invariant()); - EXPECT_FALSE(r1.empty()); - EXPECT_TRUE(r == r); - EXPECT_TRUE(r1 == r1); - EXPECT_FALSE(r != r); - EXPECT_TRUE(r != r1); - point p2(2); - box b2(p, p2); - region r2(b2); - EXPECT_EQ(r2, (region(::region(r2)))); - EXPECT_TRUE(r == r.intersection(r1)); - EXPECT_TRUE(r == r1.intersection(r)); - EXPECT_TRUE(r1 == r1.intersection(r2)); - EXPECT_TRUE(r1 == r2.intersection(r1)); - EXPECT_TRUE(r == r.difference(r)); - EXPECT_TRUE(r1 == r1.difference(r)); - EXPECT_TRUE(r == r.difference(r1)); - EXPECT_TRUE(r == r1.difference(r1)); - EXPECT_TRUE(r == r.setunion(r)); - EXPECT_TRUE(r1 == r1.setunion(r)); - EXPECT_TRUE(r1 == r.setunion(r1)); - EXPECT_TRUE(r1 == r1.setunion(r1)); - EXPECT_TRUE(r == r.symmetric_difference(r)); - EXPECT_TRUE(r1 == r1.symmetric_difference(r)); - EXPECT_TRUE(r1 == r.symmetric_difference(r1)); - EXPECT_TRUE(r == r1.symmetric_difference(r1)); - vector r12vals; - r12vals.push_back(b1); - r12vals.push_back(box(p1, p2)); - region r12(r12vals); - vector r12boxes = r12; - EXPECT_EQ(r12vals, r12boxes); - vector rs; - rs.push_back(r); - rs.push_back(r1); - rs.push_back(r2); - box b4(p, point(4)); - region r4(b4); - rs.push_back(r4); - rs.push_back(r12); - rs.push_back(r2.difference(r1)); - rs.push_back(r2.symmetric_difference(r12)); - for (std::size_t i = 0; i < rs.size(); ++i) { - const auto &ri = rs[i]; - auto rgrown = ri.grow(p1); - auto rshrunk = ri.shrink(p1); - EXPECT_TRUE(ri.invariant()); - EXPECT_TRUE(rgrown.invariant()); - EXPECT_TRUE(rshrunk.invariant()); - EXPECT_TRUE(rgrown.issuperset(ri)); - if (ri.empty()) - EXPECT_TRUE(rgrown.empty()); - region rgrown_test; - for (int dk = -1; dk <= +1; ++dk) - for (int dj = -1; dj <= +1; ++dj) - for (int di = -1; di <= +1; ++di) { - auto shifted = ri >> point(di, dj, dk); - rgrown_test |= shifted; - } - EXPECT_TRUE(rgrown_test == rgrown); - EXPECT_TRUE(ri.issuperset(rshrunk)); - if (!rshrunk.empty()) - EXPECT_TRUE(rshrunk.grow(p1) == ri); - region rshrunk_test = ri; - for (int dk = -1; dk <= +1; ++dk) - for (int dj = -1; dj <= +1; ++dj) - for (int di = -1; di <= +1; ++di) { - auto shifted = ri >> point(di, dj, dk); - rshrunk_test &= shifted; - } - EXPECT_TRUE(rshrunk_test == rshrunk); - region rgrown_shrunk_test = rgrown; - for (int dk = -1; dk <= +1; ++dk) - for (int dj = -1; dj <= +1; ++dj) - for (int di = -1; di <= +1; ++di) { - auto shifted = rgrown >> point(di, dj, dk); - rgrown_shrunk_test &= shifted; - } - EXPECT_TRUE(rgrown_shrunk_test == ri); - EXPECT_TRUE(rgrown.shrink(p1) == ri); - for (std::size_t j = 0; j < rs.size(); ++j) { - const auto &rj = rs[j]; - auto rintersection = ri.intersection(rj); - auto rdifference = ri.difference(rj); - auto rsetunion = ri.setunion(rj); - auto rsymmetric_difference = ri.symmetric_difference(rj); - EXPECT_TRUE(rintersection.invariant()); - EXPECT_TRUE(rdifference.invariant()); - EXPECT_TRUE(rsetunion.invariant()); - EXPECT_TRUE(rsymmetric_difference.invariant()); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(rj.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(ri)); - EXPECT_TRUE(rsetunion.issuperset(rj)); - EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); - EXPECT_TRUE(rdifference.isdisjoint(rj)); - EXPECT_TRUE(rsetunion.issuperset(rintersection)); - EXPECT_TRUE(rsetunion.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); - EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); - EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); - EXPECT_TRUE(rintersection == rj.intersection(ri)); - EXPECT_TRUE(rsetunion == rj.setunion(ri)); - EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); - if (ri == rj) { - EXPECT_TRUE(rintersection == ri); - EXPECT_TRUE(rdifference.empty()); - EXPECT_TRUE(rsetunion == ri); - EXPECT_TRUE(rsymmetric_difference.empty()); - } - EXPECT_TRUE(rintersection == (ri & rj)); - auto rintersection1 = ri; - rintersection1 &= rj; - EXPECT_TRUE(rintersection == rintersection1); - EXPECT_TRUE(rdifference == (ri - rj)); - auto rdifference1 = ri; - rdifference1 -= rj; - EXPECT_TRUE(rdifference == rdifference1); - EXPECT_TRUE(rsetunion == (ri | rj)); - auto rsetunion1 = ri; - rsetunion1 |= rj; - EXPECT_TRUE(rsetunion == rsetunion1); - EXPECT_TRUE(rsymmetric_difference == (ri ^ rj)); - auto rsymmetric_difference1 = ri; - rsymmetric_difference1 ^= rj; - EXPECT_TRUE(rsymmetric_difference == rsymmetric_difference1); - } - } - ostringstream buf; - buf << r12; - EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); -} - -// Get random set element -template T getelt(const set &xs) { - assert(!xs.empty()); - int n = irand(xs.size() - 1); - auto p = xs.begin(); - for (int i = 0; i < n; ++i) - ++p; - return *p; -} - -template void test_point() { - typedef point P; - - // Create a few points - set

ps; - while (ps.size() < 10) { - array xs; - for (int d = 0; d < D; ++d) - xs[d] = irand(10); - P p(xs); - ps.insert(p); - } - - const int niters = 100; - for (int n = 0; n < niters; ++n) { - auto p1 = getelt(ps); - auto p2 = getelt(ps); - auto p3 = getelt(ps); - std::equal_to

eq; - std::less

lt; - std::hash

h; - auto eq1 = eq(p1, p2); - auto eq2 = eq(p2, p1); - auto eq3 = eq(p2, p3); - auto eq4 = eq(p1, p3); - auto lt1 = lt(p1, p2); - auto lt2 = lt(p2, p1); - auto lt3 = lt(p2, p3); - auto lt4 = lt(p1, p3); - EXPECT_TRUE(eq1 == eq2); // eq is reflexive - EXPECT_FALSE(eq1 && lt1); - EXPECT_FALSE(eq1 && lt2); - EXPECT_FALSE(lt1 && lt2); // lt is irreflexive - EXPECT_EQ(1, eq1 + lt1 + lt2); - if (eq1 && eq3) - EXPECT_TRUE(eq4); // eq is transitive - if (lt1 && lt3) - EXPECT_TRUE(lt4); // lt is transitive - if (eq1) - EXPECT_EQ(h(p1), h(p2)); - if (eq2) - EXPECT_EQ(h(p2), h(p1)); - if (eq3) - EXPECT_EQ(h(p2), h(p3)); - if (eq4) - EXPECT_EQ(h(p1), h(p3)); - if (h(p1) != h(p2)) - EXPECT_FALSE(eq1); - if (h(p2) != h(p1)) - EXPECT_FALSE(eq2); - if (h(p2) != h(p3)) - EXPECT_FALSE(eq3); - if (h(p1) != h(p3)) - EXPECT_FALSE(eq4); - } -} - -template void test_box() { - typedef point P; - typedef box B; - - // Create a few boxes - set bs; - bs.insert(B()); - while (bs.size() < 10) { - array xs, ys; - for (int d = 0; d < D; ++d) { - xs[d] = irand(10); - ys[d] = irand(10); - } - P px(xs), py(ys); - B b{xs, ys}; - bs.insert(b); - } - - const int niters = 100; - for (int n = 0; n < niters; ++n) { - const auto b1 = getelt(bs); - const auto b2 = getelt(bs); - const auto bint = b1 & b2; - const auto bbox = b1.bounding_box(b2); - EXPECT_TRUE(bint <= b1); - EXPECT_TRUE(bint <= b2); - EXPECT_TRUE(b1 <= bbox); - EXPECT_TRUE(b2 <= bbox); - EXPECT_TRUE(bint <= bbox); - bs.insert(bint); - bs.insert(bbox); - - array xs, ys; - for (int d = 0; d < D; ++d) { - xs[d] = irand(5) - 2; - ys[d] = irand(5) - 2; - } - P plo(xs); - P phi(ys); - const auto bshl = b1 << plo; - const auto bshr = b2 >> phi; - const auto bgro = b1.grow(plo, phi); - const auto bred = b2.shrink(plo, phi); - EXPECT_TRUE((bshl >> plo) == b1); - EXPECT_TRUE((bshr << phi) == b2); - if (all(plo + phi >= P(0))) { - EXPECT_TRUE(bgro.size() >= b1.size()); - EXPECT_TRUE(bred.size() <= b2.size()); - EXPECT_TRUE(bgro.shrink(plo, phi) >= b1); - EXPECT_TRUE(bred.grow(plo, phi) <= b2); - } - bs.insert(bshl); - bs.insert(bshr); - bs.insert(bgro); - bs.insert(bred); - } - - for (int n = 0; n < niters; ++n) { - auto b1 = getelt(bs); - auto b2 = getelt(bs); - auto b3 = getelt(bs); - std::equal_to eq; - std::less lt; - std::hash h; - auto eq1 = eq(b1, b2); - auto eq2 = eq(b2, b1); - auto eq3 = eq(b2, b3); - auto eq4 = eq(b1, b3); - auto lt1 = lt(b1, b2); - auto lt2 = lt(b2, b1); - auto lt3 = lt(b2, b3); - auto lt4 = lt(b1, b3); - EXPECT_TRUE(eq1 == eq2); // eq is reflexive - EXPECT_FALSE(eq1 && lt1); - EXPECT_FALSE(eq1 && lt2); - EXPECT_FALSE(lt1 && lt2); // lt is irreflexive - EXPECT_EQ(1, eq1 + lt1 + lt2); - if (eq1 && eq3) - EXPECT_TRUE(eq4); // eq is transitive - if (lt1 && lt3) - EXPECT_TRUE(lt4); // lt is transitive - if (eq1) - EXPECT_EQ(h(b1), h(b2)); - if (eq2) - EXPECT_EQ(h(b2), h(b1)); - if (eq3) - EXPECT_EQ(h(b2), h(b3)); - if (eq4) - EXPECT_EQ(h(b1), h(b3)); - if (h(b1) != h(b2)) - EXPECT_FALSE(eq1); - if (h(b2) != h(b1)) - EXPECT_FALSE(eq2); - if (h(b2) != h(b3)) - EXPECT_FALSE(eq3); - if (h(b1) != h(b3)) - EXPECT_FALSE(eq4); - } -} - -template void test_region() { - typedef point P; - typedef box B; - typedef region R; - - // Create a few regions - set rs; - rs.insert(R()); - while (rs.size() < 10) { - array xs, ys; - for (int d = 0; d < D; ++d) { - xs[d] = irand(10); - ys[d] = irand(10); - } - P px(xs), py(ys); - B b{xs, ys}; - R r(b); - rs.insert(r); - } - - // Perform a few operations - const auto getelt = [](const set &rs) { - assert(!rs.empty()); - int n = irand(rs.size() - 1); - auto p = rs.begin(); - for (int i = 0; i < n; ++i) - ++p; - return *p; - }; - - const int niters = 1000 >> (2 * (D - 1)); - for (int n = 0; n < niters; ++n) { - const auto r1 = getelt(rs); - const auto r2 = getelt(rs); - const auto rint = r1 & r2; - const auto runi = r1 | r2; - const auto rsym = r1 ^ r2; - const auto rdif = r1 - r2; - EXPECT_TRUE(rint <= r1); - EXPECT_TRUE(rint <= r2); - EXPECT_TRUE(r1 <= runi); - EXPECT_TRUE(r2 <= runi); - EXPECT_TRUE(rint <= runi); - EXPECT_TRUE(rsym <= runi); - EXPECT_TRUE(rsym.isdisjoint(rint)); - EXPECT_TRUE(rdif <= r1); - EXPECT_TRUE(rdif.isdisjoint(r2)); - EXPECT_TRUE((rsym | rint) == runi); - rs.insert(rint); - rs.insert(runi); - rs.insert(rsym); - rs.insert(rdif); - - P plo, phi; - while (1) { - array xs, ys; - for (int d = 0; d < D; ++d) { - xs[d] = irand(5) - 2; - ys[d] = irand(5) - 2; - } - plo = P(xs); - phi = P(ys); - if (all(plo + phi >= P(0))) - break; - } - const auto rshl = r1 << plo; - const auto rshr = r2 >> phi; - const auto rgro = r1.grow(plo, phi); - const auto rred = r2.shrink(plo, phi); - EXPECT_TRUE((rshl >> plo) == r1); - EXPECT_TRUE((rshr << phi) == r2); - EXPECT_TRUE(rgro.size() >= r1.size()); - EXPECT_TRUE(rred.size() <= r2.size()); - EXPECT_TRUE(rgro.shrink(plo, phi) >= r1); - EXPECT_TRUE(rred.grow(plo, phi) <= r2); - rs.insert(rshl); - rs.insert(rshr); - rs.insert(rgro); - rs.insert(rred); - } - - for (int n = 0; n < niters; ++n) { - auto r1 = getelt(rs); - auto r2 = getelt(rs); - auto r3 = getelt(rs); - std::equal_to eq; - std::less lt; - std::hash h; - auto eq1 = eq(r1, r2); - auto eq2 = eq(r2, r1); - auto eq3 = eq(r2, r3); - auto eq4 = eq(r1, r3); - auto lt1 = lt(r1, r2); - auto lt2 = lt(r2, r1); - auto lt3 = lt(r2, r3); - auto lt4 = lt(r1, r3); - EXPECT_TRUE(eq1 == eq2); // eq is reflexive - EXPECT_FALSE(eq1 && lt1); - EXPECT_FALSE(eq1 && lt2); - EXPECT_FALSE(lt1 && lt2); // lt is irreflexive - EXPECT_EQ(1, eq1 + lt1 + lt2); - if (eq1 && eq3) - EXPECT_TRUE(eq4); // eq is transitive - if (lt1 && lt3) - EXPECT_TRUE(lt4); // lt is transitive - if (eq1) - EXPECT_EQ(h(r1), h(r2)); - if (eq2) - EXPECT_EQ(h(r2), h(r1)); - if (eq3) - EXPECT_EQ(h(r2), h(r3)); - if (eq4) - EXPECT_EQ(h(r1), h(r3)); - if (h(r1) != h(r2)) - EXPECT_FALSE(eq1); - if (h(r2) != h(r1)) - EXPECT_FALSE(eq2); - if (h(r2) != h(r3)) - EXPECT_FALSE(eq3); - if (h(r1) != h(r3)) - EXPECT_FALSE(eq4); - } -} - -TEST(RegionCalculus, point_1d) { test_point<1>(); } -TEST(RegionCalculus, point_2d) { test_point<2>(); } -TEST(RegionCalculus, point_3d) { test_point<3>(); } -TEST(RegionCalculus, point_4d) { test_point<4>(); } - -TEST(RegionCalculus, box_1d) { test_box<1>(); } -TEST(RegionCalculus, box_2d) { test_box<2>(); } -TEST(RegionCalculus, box_3d) { test_box<3>(); } -TEST(RegionCalculus, box_4d) { test_box<4>(); } - -TEST(RegionCalculus, region_1d) { test_region<1>(); } -TEST(RegionCalculus, region_2d) { test_region<2>(); } -TEST(RegionCalculus, region_3d) { test_region<3>(); } -TEST(RegionCalculus, region_4d) { test_region<4>(); } - -TEST(RegionCalculus, dpoint) { - const int dim = 3; - typedef dpoint dpoint; - dpoint p(dim); - EXPECT_FALSE(all(p)); - EXPECT_FALSE(any(p)); - dpoint p0(dim, 0); - EXPECT_FALSE(all(p0)); - EXPECT_FALSE(any(p0)); - EXPECT_TRUE(all(p0 == p)); - dpoint p1(::dpoint(dim, true)); - EXPECT_TRUE(all(p1)); - EXPECT_TRUE(any(p1)); - EXPECT_TRUE(all(p1 != p)); - EXPECT_TRUE(all(p1 == +p1)); - EXPECT_TRUE(all(p0 == -p0)); - EXPECT_TRUE(all(p1 == abs(-p1))); - EXPECT_TRUE(all(-p1 == ~p0)); - EXPECT_TRUE(all(!p0)); - dpoint p2(dpoint(dim, 2)); - EXPECT_TRUE(equal_to()(p2, dpoint(::dpoint(p2)))); - EXPECT_TRUE(all(p1 + p1 == p2)); - EXPECT_TRUE(all(p2 - p1 == p1)); - EXPECT_TRUE(all(p2 * p1 == p2)); - EXPECT_TRUE(all((p2 & p1) == p0)); - EXPECT_TRUE(all((p2 | p1) == p1 + p2)); - EXPECT_TRUE(all((p2 ^ p1) == p1 + p2)); - EXPECT_TRUE(all(p1 && p2)); - EXPECT_TRUE(all(p1 || p2)); - EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); - EXPECT_EQ(2, minval(p2)); - EXPECT_EQ(2, maxval(p2)); - EXPECT_EQ(3, sum(p1)); - EXPECT_EQ(8, prod(p2)); - vector p3vals(dim); - p3vals[0] = 0; - p3vals[1] = 1; - p3vals[2] = 2; - dpoint p3(p3vals); - EXPECT_TRUE(all((p1 + p3) * dpoint(dim, 2) - p3 == p3 + p2)); - EXPECT_TRUE(all(p3 == ::dpoint(vector{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::dpoint(vector{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); - EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(3, 2)))); - EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(3, 2)))); - auto v2 = vector({2, 2, 2}); - EXPECT_EQ(v2, vector(::dpoint(3, 2))); - EXPECT_EQ(v2, vector(::dpoint(3, 2))); - ostringstream buf; - buf << p3; - EXPECT_EQ("[0,1,2]", buf.str()); -} - -TEST(RegionCalculus, dbox) { - const int dim = 3; - typedef dpoint dpoint; - typedef dbox dbox; - dbox b(dim); - EXPECT_TRUE(b.empty()); - dpoint p0(dim, 0), p1(dim, 1); - dbox b1(dbox(p0, p1)); - EXPECT_EQ(1, b1.size()); - dbox b4(p0, dpoint(dim, 4)); - dbox b5 = b4 >> p1; - dbox b3 = b4 << p1; - dbox b6 = b3 * dpoint(dim, 2); - EXPECT_EQ(b4, dbox(::dbox(b4))); - EXPECT_TRUE(b4 == b4); - EXPECT_TRUE(b4 != b5); - EXPECT_TRUE(b4.contains(p1)); - EXPECT_FALSE(b5.contains(p0)); - EXPECT_FALSE(b5.isdisjoint(b3)); - EXPECT_TRUE(b1.isdisjoint(b5)); - EXPECT_TRUE(b.isdisjoint(b5)); - EXPECT_TRUE(b1.isdisjoint(b)); - EXPECT_TRUE(b6.issuperset(b3)); - EXPECT_FALSE(b3.issuperset(b6)); - EXPECT_TRUE(b4.issuperset(b4)); - EXPECT_TRUE(b4.issuperset(b)); - EXPECT_FALSE(b.issuperset(b4)); - EXPECT_TRUE(b.issuperset(b)); - EXPECT_FALSE(b4.is_strict_superset(b4)); - EXPECT_TRUE(b4.is_strict_superset(b)); - EXPECT_FALSE(b.is_strict_superset(b4)); - EXPECT_FALSE(b.is_strict_superset(b)); - EXPECT_EQ(dbox(p1, dpoint(dim, 3)), b5.intersection(b3)); - EXPECT_TRUE(b1.intersection(b5).empty()); - EXPECT_TRUE(b.intersection(b5).empty()); - EXPECT_TRUE(b1.intersection(b).empty()); - ostringstream buf; - buf << b4; - EXPECT_EQ("([0,0,0]:[4,4,4])", buf.str()); -} - -TEST(RegionCalculus, dregion) { - const int dim = 3; - typedef dpoint dpoint; - typedef dbox dbox; - typedef dregion dregion; - dregion r(dim); - EXPECT_TRUE(r.invariant()); - EXPECT_TRUE(r.empty()); - dpoint p(dim); - dpoint p1(dim, 1); - dbox b(dim); - dbox b1(p, p1); - dregion r0(b); - EXPECT_TRUE(r0.invariant()); - EXPECT_TRUE(r0.empty()); - dregion r1(b1); - EXPECT_TRUE(r1.invariant()); - EXPECT_FALSE(r1.empty()); - EXPECT_TRUE(r == r); - EXPECT_TRUE(r1 == r1); - EXPECT_FALSE(r != r); - EXPECT_TRUE(r != r1); - dpoint p2(dim, 2); - dbox b2(p, p2); - dregion r2(b2); - EXPECT_EQ(r2, dregion(::dregion(r2))); - EXPECT_TRUE(r == r.intersection(r1)); - EXPECT_TRUE(r == r1.intersection(r)); - EXPECT_TRUE(r1 == r1.intersection(r2)); - EXPECT_TRUE(r1 == r2.intersection(r1)); - EXPECT_TRUE(r == r.difference(r)); - EXPECT_TRUE(r1 == r1.difference(r)); - EXPECT_TRUE(r == r.difference(r1)); - EXPECT_TRUE(r == r1.difference(r1)); - EXPECT_TRUE(r == r.setunion(r)); - EXPECT_TRUE(r1 == r1.setunion(r)); - EXPECT_TRUE(r1 == r.setunion(r1)); - EXPECT_TRUE(r1 == r1.setunion(r1)); - EXPECT_TRUE(r == r.symmetric_difference(r)); - EXPECT_TRUE(r1 == r1.symmetric_difference(r)); - EXPECT_TRUE(r1 == r.symmetric_difference(r1)); - EXPECT_TRUE(r == r1.symmetric_difference(r1)); - vector r12vals; - r12vals.push_back(b1); - r12vals.push_back(dbox(p1, p2)); - dregion r12(r12vals); - vector r12boxes = r12; - EXPECT_EQ(r12vals, r12boxes); - vector rs; - rs.push_back(r); - rs.push_back(r1); - rs.push_back(r2); - dbox b4(p, dpoint(dim, 4)); - dregion r4(b4); - rs.push_back(r4); - rs.push_back(r12); - rs.push_back(r2.difference(r1)); - rs.push_back(r2.symmetric_difference(r12)); - for (std::size_t i = 0; i < rs.size(); ++i) { - const auto &ri = rs[i]; - EXPECT_TRUE(ri.invariant()); - for (std::size_t j = 0; j < rs.size(); ++j) { - const auto &rj = rs[j]; - auto rintersection = ri.intersection(rj); - auto rdifference = ri.difference(rj); - auto rsetunion = ri.setunion(rj); - auto rsymmetric_difference = ri.symmetric_difference(rj); - EXPECT_TRUE(rintersection.invariant()); - EXPECT_TRUE(rdifference.invariant()); - EXPECT_TRUE(rsetunion.invariant()); - EXPECT_TRUE(rsymmetric_difference.invariant()); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(rj.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(ri)); - EXPECT_TRUE(rsetunion.issuperset(rj)); - EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); - EXPECT_TRUE(rdifference.isdisjoint(rj)); - EXPECT_TRUE(rsetunion.issuperset(rintersection)); - EXPECT_TRUE(rsetunion.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); - EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); - EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); - EXPECT_TRUE(rintersection == rj.intersection(ri)); - EXPECT_TRUE(rsetunion == rj.setunion(ri)); - EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); - if (ri == rj) { - EXPECT_TRUE(rintersection == ri); - EXPECT_TRUE(rdifference.empty()); - EXPECT_TRUE(rsetunion == ri); - EXPECT_TRUE(rsymmetric_difference.empty()); - } - EXPECT_TRUE(rintersection == (ri & rj)); - auto rintersection1 = ri; - rintersection1 &= rj; - EXPECT_TRUE(rintersection == rintersection1); - EXPECT_TRUE(rdifference == (ri - rj)); - auto rdifference1 = ri; - rdifference1 -= rj; - EXPECT_TRUE(rdifference == rdifference1); - EXPECT_TRUE(rsetunion == (ri | rj)); - auto rsetunion1 = ri; - rsetunion1 |= rj; - EXPECT_TRUE(rsetunion == rsetunion1); - EXPECT_TRUE(rsymmetric_difference == (ri ^ rj)); - auto rsymmetric_difference1 = ri; - rsymmetric_difference1 ^= rj; - EXPECT_TRUE(rsymmetric_difference == rsymmetric_difference1); - } - } - ostringstream buf; - buf << r12; - EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); -} - -TEST(RegionCalculus, dpoint_dim) { - typedef dpoint dpoint; - for (int dim = 0; dim < 4; ++dim) { - dpoint p(dim); - if (dim == 0) - EXPECT_TRUE(all(p)); - else - EXPECT_FALSE(all(p)); - EXPECT_FALSE(any(p)); - dpoint p0(dim, 0); - if (dim == 0) - EXPECT_TRUE(all(p0)); - else - EXPECT_FALSE(all(p0)); - EXPECT_FALSE(any(p0)); - EXPECT_TRUE(all(p0 == p)); - dpoint p1(::dpoint(dim, true)); - EXPECT_TRUE(all(p1)); - if (dim == 0) - EXPECT_FALSE(any(p1)); - else - EXPECT_TRUE(any(p1)); - EXPECT_TRUE(all(p1 != p)); - EXPECT_TRUE(all(p1 == +p1)); - EXPECT_TRUE(all(p0 == -p0)); - EXPECT_TRUE(all(p1 == abs(-p1))); - EXPECT_TRUE(all(-p1 == ~p0)); - EXPECT_TRUE(all(!p0)); - dpoint p2(dpoint(dim, 2)); - EXPECT_TRUE(equal_to()(p2, dpoint(::dpoint(p2)))); - EXPECT_TRUE(all(p1 + p1 == p2)); - EXPECT_TRUE(all(p2 - p1 == p1)); - EXPECT_TRUE(all(p2 * p1 == p2)); - EXPECT_TRUE(all((p2 & p1) == p0)); - EXPECT_TRUE(all((p2 | p1) == p1 + p2)); - EXPECT_TRUE(all((p2 ^ p1) == p1 + p2)); - EXPECT_TRUE(all(p1 && p2)); - EXPECT_TRUE(all(p1 || p2)); - EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); - EXPECT_EQ(dim == 0 ? numeric_limits::max() : 2, minval(p2)); - EXPECT_EQ(dim == 0 ? numeric_limits::min() : 2, maxval(p2)); - EXPECT_EQ(dim, sum(p1)); - EXPECT_EQ(1 << dim, prod(p2)); - vector p3vals(dim); - for (int d = 0; d < dim; ++d) - p3vals[d] = d; - dpoint p3(p3vals); - EXPECT_TRUE(all((p1 + p3) * dpoint(dim, 2) - p3 == p3 + p2)); - vector vi(dim); - for (int d = 0; d < dim; ++d) - vi[d] = d; - EXPECT_TRUE(all(p3 == ::dpoint(vi))); - vector vs(dim); - for (int d = 0; d < dim; ++d) - vs[d] = d; - EXPECT_TRUE(all(p3 == ::dpoint(vs))); - // EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); - // EXPECT_TRUE(all(p3 == ::dpoint(array{{0, 1, 2}}))); - EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(dim, 2)))); - EXPECT_TRUE(all(p2 == ::dpoint(::dpoint(dim, 2)))); - auto v2 = vector(dim, 2); - EXPECT_EQ(v2, vector(::dpoint(dim, 2))); - EXPECT_EQ(v2, vector(::dpoint(dim, 2))); - ostringstream buf; - buf << p3; - ostringstream goodbuf; - goodbuf << "["; - for (int d = 0; d < dim; ++d) { - if (d > 0) - goodbuf << ","; - goodbuf << d; - } - goodbuf << "]"; - EXPECT_EQ(goodbuf.str(), buf.str()); - } -} - -TEST(RegionCalculus, dbox_dim) { - typedef dpoint dpoint; - typedef dbox dbox; - for (int dim = 0; dim < 4; ++dim) { - dbox b(dim); - EXPECT_TRUE(b.empty()); - dpoint p0(dim, 0), p1(dim, 1); - dbox b1(dbox(p0, p1)); - EXPECT_EQ(1, b1.size()); - dbox b4(p0, dpoint(dim, 4)); - dbox b5 = b4 >> p1; - dbox b3 = b4 << p1; - dbox b6 = b3 * dpoint(dim, 2); - dbox b7 = b4.grow(p0, p1); - dbox b8 = b4.grow(p1, p0); - dbox b9 = b4.shrink(p0, p1); - EXPECT_EQ(b4, dbox(::dbox(b4))); - EXPECT_TRUE(b4 == b4); - if (dim == 0) - EXPECT_FALSE(b4 != b5); - else - EXPECT_TRUE(b4 != b5); - EXPECT_TRUE(b4.contains(p1)); - if (dim == 0) - EXPECT_TRUE(b5.contains(p0)); - else - EXPECT_FALSE(b5.contains(p0)); - EXPECT_FALSE(b5.isdisjoint(b3)); - if (dim == 0) - EXPECT_FALSE(b1.isdisjoint(b5)); - else - EXPECT_TRUE(b1.isdisjoint(b5)); - EXPECT_TRUE(b.isdisjoint(b5)); - EXPECT_TRUE(b1.isdisjoint(b)); - EXPECT_TRUE(b6.issuperset(b3)); - if (dim == 0) - EXPECT_TRUE(b3.issuperset(b6)); - else - EXPECT_FALSE(b3.issuperset(b6)); - EXPECT_TRUE(b4.issuperset(b4)); - EXPECT_TRUE(b4.issuperset(b)); - EXPECT_FALSE(b.issuperset(b4)); - EXPECT_TRUE(b.issuperset(b)); - EXPECT_FALSE(b4.is_strict_superset(b4)); - EXPECT_TRUE(b4.is_strict_superset(b)); - EXPECT_FALSE(b.is_strict_superset(b4)); - EXPECT_FALSE(b.is_strict_superset(b)); - EXPECT_EQ(dbox(p1, dpoint(dim, 3)), b5.intersection(b3)); - if (dim == 0) - EXPECT_FALSE(b1.intersection(b5).empty()); - else - EXPECT_TRUE(b1.intersection(b5).empty()); - EXPECT_TRUE(b.intersection(b5).empty()); - EXPECT_TRUE(b1.intersection(b).empty()); - ostringstream buf; - buf << b4; - ostringstream goodbuf; - goodbuf << "("; - if (dim == 0) { - goodbuf << 1; - } else { - goodbuf << "["; - for (int d = 0; d < dim; ++d) { - if (d > 0) - goodbuf << ","; - goodbuf << 0; - } - goodbuf << "]:["; - for (int d = 0; d < dim; ++d) { - if (d > 0) - goodbuf << ","; - goodbuf << 4; - } - goodbuf << "]"; - } - goodbuf << ")"; - EXPECT_EQ(goodbuf.str(), buf.str()); - } -} - -TEST(RegionCalculus, dregion_dim) { - typedef dpoint dpoint; - typedef dbox dbox; - typedef dregion dregion; - for (int dim = 0; dim < 4; ++dim) { - dregion r(dim); - EXPECT_TRUE(r.invariant()); - EXPECT_TRUE(r.empty()); - dpoint p(dim); - dpoint p1(dim, 1); - dbox b(dim); - dbox b1(p, p1); - dregion r0(b); - EXPECT_TRUE(r0.invariant()); - EXPECT_TRUE(r0.empty()); - dregion r1(b1); - EXPECT_TRUE(r1.invariant()); - EXPECT_FALSE(r1.empty()); - EXPECT_TRUE(r == r); - EXPECT_TRUE(r1 == r1); - EXPECT_FALSE(r != r); - EXPECT_TRUE(r != r1); - dpoint p2(dim, 2); - dbox b2(p, p2); - dregion r2(b2); - EXPECT_EQ(r2, dregion(::dregion(r2))); - EXPECT_TRUE(r == r.intersection(r1)); - EXPECT_TRUE(r == r1.intersection(r)); - EXPECT_TRUE(r1 == r1.intersection(r2)); - EXPECT_TRUE(r1 == r2.intersection(r1)); - EXPECT_TRUE(r == r.difference(r)); - EXPECT_TRUE(r1 == r1.difference(r)); - EXPECT_TRUE(r == r.difference(r1)); - EXPECT_TRUE(r == r1.difference(r1)); - EXPECT_TRUE(r == r.setunion(r)); - EXPECT_TRUE(r1 == r1.setunion(r)); - EXPECT_TRUE(r1 == r.setunion(r1)); - EXPECT_TRUE(r1 == r1.setunion(r1)); - EXPECT_TRUE(r == r.symmetric_difference(r)); - EXPECT_TRUE(r1 == r1.symmetric_difference(r)); - EXPECT_TRUE(r1 == r.symmetric_difference(r1)); - EXPECT_TRUE(r == r1.symmetric_difference(r1)); - vector r12vals; - if (dim == 0) { - r12vals.push_back(b1); - } else if (dim == 1) { - r12vals.push_back(dbox(p, p2)); - } else { - r12vals.push_back(b1); - r12vals.push_back(dbox(p1, p2)); - } - dregion r12(r12vals); - vector r12boxes = r12; - EXPECT_EQ(r12vals, r12boxes); - vector rs; - rs.push_back(r); - rs.push_back(r1); - rs.push_back(r2); - dbox b4(p, dpoint(dim, 4)); - dregion r4(b4); - rs.push_back(r4); - rs.push_back(r12); - rs.push_back(r2.difference(r1)); - rs.push_back(r2.symmetric_difference(r12)); - rs.push_back(r2 >> p1); - rs.push_back(r2 << p1); - rs.push_back(r2.grow(p1)); - rs.push_back(r2.shrink(p1)); - for (std::size_t i = 0; i < rs.size(); ++i) { - const auto &ri = rs[i]; - EXPECT_TRUE(ri.invariant()); - for (std::size_t j = 0; j < rs.size(); ++j) { - const auto &rj = rs[j]; - auto rintersection = ri.intersection(rj); - auto rdifference = ri.difference(rj); - auto rsetunion = ri.setunion(rj); - auto rsymmetric_difference = ri.symmetric_difference(rj); - EXPECT_TRUE(rintersection.invariant()); - EXPECT_TRUE(rdifference.invariant()); - EXPECT_TRUE(rsetunion.invariant()); - EXPECT_TRUE(rsymmetric_difference.invariant()); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(rj.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(ri)); - EXPECT_TRUE(rsetunion.issuperset(rj)); - EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); - EXPECT_TRUE(rdifference.isdisjoint(rj)); - EXPECT_TRUE(rsetunion.issuperset(rintersection)); - EXPECT_TRUE(rsetunion.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); - EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); - EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); - EXPECT_TRUE(rintersection == rj.intersection(ri)); - EXPECT_TRUE(rsetunion == rj.setunion(ri)); - EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); - if (ri == rj) { - EXPECT_TRUE(rintersection == ri); - EXPECT_TRUE(rdifference.empty()); - EXPECT_TRUE(rsetunion == ri); - EXPECT_TRUE(rsymmetric_difference.empty()); - } - } - } - ostringstream buf; - buf << r12; - ostringstream goodbuf; - goodbuf << "{"; - if (dim == 0) { - goodbuf << "(" << true << ")"; - } else { - goodbuf << "(["; - for (int d = 0; d < dim; ++d) { - if (d > 0) - goodbuf << ","; - goodbuf << 0; - } - goodbuf << "]:["; - if (dim > 1) { - for (int d = 0; d < dim; ++d) { - if (d > 0) - goodbuf << ","; - goodbuf << 1; - } - goodbuf << "]),(["; - for (int d = 0; d < dim; ++d) { - if (d > 0) - goodbuf << ","; - goodbuf << 1; - } - goodbuf << "]:["; - } - for (int d = 0; d < dim; ++d) { - if (d > 0) - goodbuf << ","; - goodbuf << 2; - } - goodbuf << "])"; - } - goodbuf << "}"; - EXPECT_EQ(goodbuf.str(), buf.str()); - } -} - -TEST(RegionCalculus, point_double) { - typedef point point; - point p; - point p0(0); - EXPECT_TRUE(all(p0 == p)); - point p1(::point(1)); - EXPECT_TRUE(all(p1 != p)); - EXPECT_TRUE(all(p1 == +p1)); - EXPECT_TRUE(all(p0 == -p0)); - EXPECT_TRUE(all(p1 == abs(-p1))); - point p2(point(2)); - EXPECT_TRUE(equal_to()(p2, point(::point(p2)))); - EXPECT_TRUE(all(p1 + p1 == p2)); - EXPECT_TRUE(all(p2 - p1 == p1)); - EXPECT_TRUE(all(p2 * p1 == p2)); - EXPECT_TRUE(all(p0 <= p1 && p0 < p1 && p1 >= p0 && p1 > p0)); - EXPECT_EQ(2, minval(p2)); - EXPECT_EQ(2, maxval(p2)); - EXPECT_EQ(3, sum(p1)); - EXPECT_EQ(8, prod(p2)); - point p3; - p3.elt[0] = 0; - p3.elt[1] = 1; - p3.elt[2] = 2; - EXPECT_TRUE(all((p1 + p3) * point(2) - p3 == p3 + p2)); - EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::point(vector{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); - EXPECT_TRUE(all(p3 == ::point(array{{0, 1, 2}}))); - EXPECT_TRUE(all(p2 == ::point(::point(2)))); - EXPECT_TRUE(all(p2 == ::point(::point(2)))); - auto v2 = vector({2, 2, 2}); - EXPECT_EQ(v2, vector(::point(2))); - EXPECT_EQ(v2, vector(::point(2))); - ostringstream buf; - buf << p3; - EXPECT_EQ("[0,1,2]", buf.str()); -} - -TEST(RegionCalculus, box_double) { - typedef point point; - typedef box box; - box b; - EXPECT_TRUE(b.empty()); - point p0(0), p1(1); - box b1(box(p0, p1)); - EXPECT_EQ(1, b1.size()); - box b4(p0, point(4)); - box b5 = b4 >> p1; - box b3 = b4 << p1; - box b6 = b3 * point(2); - box b7 = b4.grow(p0, p1); - box b8 = b4.grow(p1, p0); - box b9 = b4.shrink(p0, p1); - EXPECT_EQ(b4, (box(::box(b4)))); - EXPECT_TRUE(b4 == b4); - EXPECT_TRUE(b4 != b5); - EXPECT_TRUE(b4.contains(p1)); - EXPECT_FALSE(b5.contains(p0)); - EXPECT_FALSE(b5.isdisjoint(b3)); - EXPECT_TRUE(b1.isdisjoint(b5)); - EXPECT_TRUE(b.isdisjoint(b5)); - EXPECT_TRUE(b1.isdisjoint(b)); - EXPECT_TRUE(b6.issuperset(b3)); - EXPECT_FALSE(b3.issuperset(b6)); - EXPECT_TRUE(b4.issuperset(b4)); - EXPECT_TRUE(b4.issuperset(b)); - EXPECT_FALSE(b.issuperset(b4)); - EXPECT_TRUE(b.issuperset(b)); - EXPECT_TRUE(b7.issuperset(b4)); - EXPECT_TRUE(b7.issuperset(b5)); - EXPECT_TRUE(b8.issuperset(b4)); - EXPECT_TRUE(b8.issuperset(b3)); - EXPECT_FALSE(b4.is_strict_superset(b4)); - EXPECT_TRUE(b4.is_strict_superset(b)); - EXPECT_FALSE(b.is_strict_superset(b4)); - EXPECT_FALSE(b.is_strict_superset(b)); - EXPECT_EQ(box(p1, point(3)), b5.intersection(b3)); - EXPECT_TRUE(b1.intersection(b5).empty()); - EXPECT_TRUE(b.intersection(b5).empty()); - EXPECT_TRUE(b1.intersection(b).empty()); - ostringstream buf; - buf << b4; - EXPECT_EQ("([0,0,0]:[4,4,4])", buf.str()); -} - -TEST(RegionCalculus, region_double) { - typedef point point; - typedef box box; - typedef region region; - region r; - EXPECT_TRUE(r.invariant()); - EXPECT_TRUE(r.empty()); - point p; - point p1(1); - box b; - box b1(p, p1); - region r0(b); - EXPECT_TRUE(r0.invariant()); - EXPECT_TRUE(r0.empty()); - region r1(b1); - EXPECT_TRUE(r1.invariant()); - EXPECT_FALSE(r1.empty()); - EXPECT_TRUE(r == r); - EXPECT_TRUE(r1 == r1); - EXPECT_FALSE(r != r); - EXPECT_TRUE(r != r1); - point p2(2); - box b2(p, p2); - region r2(b2); - EXPECT_EQ(r2, (region(::region(r2)))); - EXPECT_TRUE(r == r.intersection(r1)); - EXPECT_TRUE(r == r1.intersection(r)); - EXPECT_TRUE(r1 == r1.intersection(r2)); - EXPECT_TRUE(r1 == r2.intersection(r1)); - EXPECT_TRUE(r == r.difference(r)); - EXPECT_TRUE(r1 == r1.difference(r)); - EXPECT_TRUE(r == r.difference(r1)); - EXPECT_TRUE(r == r1.difference(r1)); - EXPECT_TRUE(r == r.setunion(r)); - EXPECT_TRUE(r1 == r1.setunion(r)); - EXPECT_TRUE(r1 == r.setunion(r1)); - EXPECT_TRUE(r1 == r1.setunion(r1)); - EXPECT_TRUE(r == r.symmetric_difference(r)); - EXPECT_TRUE(r1 == r1.symmetric_difference(r)); - EXPECT_TRUE(r1 == r.symmetric_difference(r1)); - EXPECT_TRUE(r == r1.symmetric_difference(r1)); - vector r12vals; - r12vals.push_back(b1); - r12vals.push_back(box(p1, p2)); - region r12(r12vals); - vector r12boxes = r12; - EXPECT_EQ(r12vals, r12boxes); - vector rs; - rs.push_back(r); - rs.push_back(r1); - rs.push_back(r2); - box b4(p, point(4)); - region r4(b4); - rs.push_back(r4); - rs.push_back(r12); - rs.push_back(r2.difference(r1)); - rs.push_back(r2.symmetric_difference(r12)); - for (std::size_t i = 0; i < rs.size(); ++i) { - const auto &ri = rs[i]; - auto rgrown = ri.grow(p1); - auto rshrunk = ri.shrink(p1); - EXPECT_TRUE(ri.invariant()); - EXPECT_TRUE(rgrown.invariant()); - EXPECT_TRUE(rshrunk.invariant()); - EXPECT_TRUE(rgrown.issuperset(ri)); - if (ri.empty()) - EXPECT_TRUE(rgrown.empty()); - region rgrown_test; - for (int dk = -1; dk <= +1; ++dk) - for (int dj = -1; dj <= +1; ++dj) - for (int di = -1; di <= +1; ++di) { - auto shifted = ri >> point(di, dj, dk); - rgrown_test |= shifted; - } - EXPECT_TRUE(rgrown_test == rgrown); - EXPECT_TRUE(ri.issuperset(rshrunk)); - if (!rshrunk.empty()) - EXPECT_TRUE(rshrunk.grow(p1) == ri); - region rshrunk_test = ri; - for (int dk = -1; dk <= +1; ++dk) - for (int dj = -1; dj <= +1; ++dj) - for (int di = -1; di <= +1; ++di) { - auto shifted = ri >> point(di, dj, dk); - rshrunk_test &= shifted; - } - EXPECT_TRUE(rshrunk_test == rshrunk); - region rgrown_shrunk_test = rgrown; - for (int dk = -1; dk <= +1; ++dk) - for (int dj = -1; dj <= +1; ++dj) - for (int di = -1; di <= +1; ++di) { - auto shifted = rgrown >> point(di, dj, dk); - rgrown_shrunk_test &= shifted; - } - EXPECT_TRUE(rgrown_shrunk_test == ri); - EXPECT_TRUE(rgrown.shrink(p1) == ri); - for (std::size_t j = 0; j < rs.size(); ++j) { - const auto &rj = rs[j]; - auto rintersection = ri.intersection(rj); - auto rdifference = ri.difference(rj); - auto rsetunion = ri.setunion(rj); - auto rsymmetric_difference = ri.symmetric_difference(rj); - EXPECT_TRUE(rintersection.invariant()); - EXPECT_TRUE(rdifference.invariant()); - EXPECT_TRUE(rsetunion.invariant()); - EXPECT_TRUE(rsymmetric_difference.invariant()); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rintersection)); - EXPECT_TRUE(rj.issuperset(rintersection)); - EXPECT_TRUE(ri.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(ri)); - EXPECT_TRUE(rsetunion.issuperset(rj)); - EXPECT_TRUE(rintersection.isdisjoint(rsymmetric_difference)); - EXPECT_TRUE(rdifference.isdisjoint(rj)); - EXPECT_TRUE(rsetunion.issuperset(rintersection)); - EXPECT_TRUE(rsetunion.issuperset(rdifference)); - EXPECT_TRUE(rsetunion.issuperset(rsymmetric_difference)); - EXPECT_TRUE(rintersection.setunion(rsymmetric_difference) == rsetunion); - EXPECT_TRUE(rdifference.setunion(rj) == rsetunion); - EXPECT_TRUE(rintersection == rj.intersection(ri)); - EXPECT_TRUE(rsetunion == rj.setunion(ri)); - EXPECT_TRUE(rsymmetric_difference == rj.symmetric_difference(ri)); - if (ri == rj) { - EXPECT_TRUE(rintersection == ri); - EXPECT_TRUE(rdifference.empty()); - EXPECT_TRUE(rsetunion == ri); - EXPECT_TRUE(rsymmetric_difference.empty()); - } - } - } - ostringstream buf; - buf << r12; - EXPECT_EQ("{([0,0,0]:[1,1,1]),([1,1,1]:[2,2,2])}", buf.str()); -} - -TEST(RegionCalculus, double_) { - for (int d = 0; d <= 4; ++d) { - dpoint p(d); - dbox b(d); - dregion r(d); - } -} - -namespace benchmark { -constexpr int D = 3; -typedef double T; -typedef point P; -typedef box B; -typedef region R; -array regs; -} // namespace benchmark - -TEST(RegionCalculus, benchmark_region_setup) { - using namespace benchmark; - - constexpr int niters = 10000; - for (int i = 0; i < 2; ++i) { - vector bs; - bs.reserve(niters); - for (int n = 0; n < niters; ++n) { - array xs, ys; - for (int d = 0; d < D; ++d) { - xs[d] = irand(100); - ys[d] = xs[d] + irand(10); - } - P px(xs), py(ys); - B b{px, py}; - bs.push_back(b); - } - regs[i] = R(bs); - } -} - -TEST(RegionCalculus, benchmark_region_intersection) { - using namespace benchmark; - auto rint = regs[0] & regs[1]; -} -TEST(RegionCalculus, benchmark_region_union) { - using namespace benchmark; - auto runi = regs[0] | regs[1]; -} -TEST(RegionCalculus, benchmark_region_symmetric_difference) { - using namespace benchmark; - auto rsym = regs[0] ^ regs[1]; -} -TEST(RegionCalculus, benchmark_region_difference) { - using namespace benchmark; - auto rdif = regs[0] - regs[1]; -} -TEST(RegionCalculus, benchmark_region_equal) { - using namespace benchmark; - auto req = regs[0] == regs[1]; -} -TEST(RegionCalculus, benchmark_region_subset) { - using namespace benchmark; - auto rle = regs[0] & regs[1]; -} -TEST(RegionCalculus, benchmark_region_strict_subset) { - using namespace benchmark; - auto rlt = regs[0] < regs[1]; -} - -#endif diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index 5ffd0f71e5..1bd32110ef 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -72,6 +72,40 @@ template void test_Region(const R &r) { R Y = randr(); R Z = randr(); + P n = p; + P x = randp(); + P y = randp(); + + REQUIRE(N == N); + REQUIRE(X == X); + REQUIRE((N != X) != X.empty()); + + REQUIRE((X >> n) == X); + REQUIRE((X >> x) == (X << -x)); + REQUIRE(((X >> x) << x) == X); + REQUIRE(((X >> x) >> y) == (X >> (x + y))); + + REQUIRE((X * abs(x)) * abs(y) == X * (abs(x) * abs(y))); + REQUIRE((X >> x) * y == (X * y) >> (x * y)); + + if (X.empty()) { + REQUIRE(X.grown(abs(x)).empty()); + REQUIRE(X.shrunk(abs(x)).empty()); + } else if (all(abs(x) == n)) { + REQUIRE(X.grown(abs(x)) == X); + REQUIRE(X.shrunk(abs(x)) == X); + } else { + REQUIRE(X.grown(abs(x)) > X); + REQUIRE(X.shrunk(abs(x)) < X); + } + + REQUIRE(X.grown(abs(x)).shrunk(abs(x)) >= X); + REQUIRE(X.shrunk(abs(x)).grown(abs(x)) <= X); + + REQUIRE((X.grown(x) >> y) == (X >> y).grown(x)); + + REQUIRE((X * abs(y)).grown(abs(x) * abs(y)) == X.grown(abs(x)) * abs(y)); + const B E = bounding_box(bounding_box(bounding_box(X), bounding_box(Y)), bounding_box(Z)) .grown(10); @@ -92,6 +126,12 @@ template void test_Region(const R &r) { REQUIRE(E - (X & Y) == ((E - X) | (E - Y))); REQUIRE(E - (X | Y) == ((E - X) & (E - Y))); + REQUIRE((N ^ X) == X); + REQUIRE((X ^ N) == X); + REQUIRE((X ^ X) == N); + REQUIRE((X ^ Y) == (Y ^ X)); + REQUIRE(((X ^ Y) ^ Z) == (X ^ (Y ^ Z))); + const R IXY = X & Y; REQUIRE((IXY <= X && IXY <= Y) == true); REQUIRE((IXY.grown(1) <= X && IXY.grown(1) <= Y) == From 11d512df12973ba554689fa7aea62f46024246c5 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 15:13:59 -0400 Subject: [PATCH 08/68] Add example. Use C++17 for regions. --- CMakeLists.txt | 11 +++++++++++ examples/13_regions.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 examples/13_regions.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a43be0a1c7..e9c4f1ca96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -713,6 +713,7 @@ set(openPMD_EXAMPLE_NAMES 10_streaming_write 10_streaming_read 12_span_write + 13_regions ) set(openPMD_PYTHON_EXAMPLE_NAMES 2_read_serial @@ -769,6 +770,11 @@ if(openPMD_BUILD_TESTING) else() target_link_libraries(${testname}Tests PRIVATE CatchMain) endif() + set_target_properties(${examplename} PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) endforeach() endif() @@ -789,6 +795,11 @@ if(openPMD_BUILD_EXAMPLES) else() add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) + set_target_properties(${examplename} PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) endif() endforeach() endif() diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp new file mode 100644 index 0000000000..04a1feacf7 --- /dev/null +++ b/examples/13_regions.cpp @@ -0,0 +1,38 @@ +#include + +#include + +int main() { + using namespace openPMD::Regions; + using namespace std; + + // Two points + Point x{1, 2, 3}, y{4, 5, 6}; + auto z = x + y; + cout << "Points:\n" + << "x: " << x << "\n" + << "y: " << y << "\n"; + + // A box between these points + Box b(x, y); + auto bg = b.grown(1); + cout << "Boxes are spanned between points (inclusive lower, exclusive upper bound):\n" + << "b: " << b << "\n" + << "bg: " << bg << "\n"; + + // Regions + Region r(b); + auto g = Region(bg) - r; + cout << "Regions are sets of non-overlapping boxes:\n" + << "r: " << r << "\n" + << "g: " << g << "\n"; + + { + NDPoint p0(0); + NDPoint p1(1); + NDPoint p2(2); + NDPoint p3(3); + } + + return 0; +} From b4d05e50d8c1b68bd0bd95a15c8867a8f59cf82f Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 15:25:17 -0400 Subject: [PATCH 09/68] Add missing include files --- examples/13_regions.cpp | 11 ++++++----- include/openPMD/regions/Helpers.hpp | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp index 04a1feacf7..678beab86b 100644 --- a/examples/13_regions.cpp +++ b/examples/13_regions.cpp @@ -7,22 +7,23 @@ int main() { using namespace std; // Two points - Point x{1, 2, 3}, y{4, 5, 6}; + Point x{1, 2}, y{4, 5}; auto z = x + y; cout << "Points:\n" << "x: " << x << "\n" << "y: " << y << "\n"; // A box between these points - Box b(x, y); + Box b(x, y); auto bg = b.grown(1); - cout << "Boxes are spanned between points (inclusive lower, exclusive upper bound):\n" + cout << "Boxes are spanned between points (inclusive lower, exclusive upper " + "bound):\n" << "b: " << b << "\n" << "bg: " << bg << "\n"; // Regions - Region r(b); - auto g = Region(bg) - r; + Region r(b); + auto g = Region(bg) - r; cout << "Regions are sets of non-overlapping boxes:\n" << "r: " << r << "\n" << "g: " << g << "\n"; diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 91e53f653f..2fb0957b5e 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -3,7 +3,10 @@ #include #include +#include +#include #include +#include namespace openPMD { namespace Regions { From 860ffc4f1d52a3a52e9d7a3849e5422ac55f901e Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 15:31:55 -0400 Subject: [PATCH 10/68] Correct #include statements --- include/openPMD/regions/Helpers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 2fb0957b5e..660b5b5a03 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include From 6e83f3af7556ebc620466418d02052603a73dcc4 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 15:41:49 -0400 Subject: [PATCH 11/68] Avoid some compiler warnings --- include/openPMD/regions/Box.hpp | 32 +++++++++++++---------------- include/openPMD/regions/Helpers.hpp | 4 ++-- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index fb669b6df4..269e8ee577 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -53,7 +53,7 @@ template class Box { Box(const Point &lo, const Point &hi) : is_full(false) {} /** Create box holding a single point */ - explicit Box(const Point &p) : is_full(true) {} + explicit Box(const Point &) : is_full(true) {} // Predicates size_type ndims() const { return D; } @@ -64,22 +64,18 @@ template class Box { size_type size() const { return is_full; } // Shift and scale operators - Box &operator>>=(const Point &p) { return *this; } - Box &operator<<=(const Point &p) { return *this; } - Box &operator*=(const Point &p) { return *this; } - Box operator>>(const Point &p) const { return Box(*this); } - Box operator<<(const Point &p) const { return Box(*this); } - Box operator*(const Point &p) const { return Box(*this); } - Box grown(const Point &dlo, const Point &dhi) const { - return *this; - } - Box grown(const Point &d) const { return *this; } - Box grown(const T &d) const { return *this; } - Box shrunk(const Point &dlo, const Point &dhi) const { - return *this; - } - Box shrunk(const Point &d) const { return *this; } - Box shrunk(const T &d) const { return *this; } + Box &operator>>=(const Point &) { return *this; } + Box &operator<<=(const Point &) { return *this; } + Box &operator*=(const Point &) { return *this; } + Box operator>>(const Point &) const { return Box(*this); } + Box operator<<(const Point &) const { return Box(*this); } + Box operator*(const Point &) const { return Box(*this); } + Box grown(const Point &, const Point &) const { return *this; } + Box grown(const Point &) const { return *this; } + Box grown(const T &) const { return *this; } + Box shrunk(const Point &, const Point &) const { return *this; } + Box shrunk(const Point &) const { return *this; } + Box shrunk(const T &) const { return *this; } // Comparison operators friend bool operator==(const Box &b1, const Box &b2) { @@ -88,7 +84,7 @@ template class Box { friend bool operator!=(const Box &b1, const Box &b2) { return !(b1 == b2); } // Set comparison operators - bool contains(const Point &p) const { return !empty(); } + bool contains(const Point &) const { return !empty(); } friend bool isdisjoint(const Box &b1, const Box &b2) { return b1.empty() || b2.empty(); } diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 660b5b5a03..bc599252e6 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -68,7 +68,7 @@ template struct tuple_eq { }; template <> struct tuple_eq<0> { template - constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { + constexpr bool operator()(const Tuple1 &, const Tuple2 &) const { return true; } }; @@ -92,7 +92,7 @@ template struct tuple_cmp { }; template <> struct tuple_cmp<0> { template - constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { + constexpr int operator()(const Tuple1 &, const Tuple2 &) const { return 0; } }; From e94bf598369d78c7a8fe51a80cb03ab2759da70f Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 15:44:04 -0400 Subject: [PATCH 12/68] Mark function argument as used --- include/openPMD/regions/Point.hpp | 29 ++++++----------------------- include/openPMD/regions/Region.hpp | 5 ----- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index f513def0fd..af10d330e5 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -141,7 +141,7 @@ template class Point { /** Create a point from Pointers to first and one past the last element */ - constexpr Point(const T *begin, const T *end) + constexpr Point(const T *begin, const T *end __attribute__((__unused__))) : elts((assert(begin + D == end), make([&](size_type d) { return begin[d]; }))) {} /** Create a point from an initializer list @@ -222,29 +222,12 @@ template class Point { friend constexpr Point operator-(const Point &x) { return fmap([](const T &a) { return -a; }, x); } - // friend constexpr Point operator~(const Point &x) { - // if constexpr (std::is_same_v) - // // Special case to avoid compiler warnings - // return fmap([](const T &a) { return true; }, x); - // else if constexpr (std::is_integral_v) - // return fmap([](const T &a) { return ~a; }, x); - // std::abort(); - // } - template && - !std::is_same_v> * = nullptr> - friend constexpr Point operator~(const Point &x) { - return fmap([](const T &a) { return ~a; }, x); - } - template > * = nullptr> - friend constexpr Point operator~(const Point &x) { - // Special case to avoid compiler warnings - return fmap([](const T &a) { return true; }, x); - } - template > * = nullptr> friend constexpr Point operator~(const Point &x) { + if constexpr (std::is_same_v) + // Special case to avoid compiler warnings + return fmap([](const T &) { return true; }, x); + else if constexpr (std::is_integral_v) + return fmap([](const T &a) { return ~a; }, x); std::abort(); } friend constexpr Point operator!(const Point &x) { diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index c721192a48..45e53b871c 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -663,11 +663,6 @@ template class Region { return false; } } - // // There must be an even number of subregions - // if (subregions.size() % 2 != 0) { - // assert(false); - // return false; - // } return true; } From 940d57fcd1c8aba3902540ba307e7e2ab32fa88b Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 15:56:56 -0400 Subject: [PATCH 13/68] Avoid more compiler warnings --- examples/13_regions.cpp | 3 ++- include/openPMD/regions/NDPoint.hpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp index 678beab86b..ae69275791 100644 --- a/examples/13_regions.cpp +++ b/examples/13_regions.cpp @@ -11,7 +11,8 @@ int main() { auto z = x + y; cout << "Points:\n" << "x: " << x << "\n" - << "y: " << y << "\n"; + << "y: " << y << "\n" + << "z: " << z << "\n"; // A box between these points Box b(x, y); diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 06cceadb73..65ec537597 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -210,9 +210,9 @@ template class WPoint final : public VPoint { } operator std::vector() const override { return std::vector(p); } - constexpr size_type size() const override { return p.size(); } + /*constexpr*/ size_type size() const override { return p.size(); } - constexpr size_type ndims() const override { return p.ndims(); } + /*constexpr*/ size_type ndims() const override { return p.ndims(); } const T &operator[](const size_type d) const override { return p[d]; } T &operator[](const size_type d) override { return p[d]; } From 5b9a95c992bcec46d2afd6e9784daa273bb107ec Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 19:33:55 -0400 Subject: [PATCH 14/68] Avoid compiler warnings --- include/openPMD/regions/Box.hpp | 4 ++-- include/openPMD/regions/NDPoint.hpp | 4 ++-- include/openPMD/regions/Region.hpp | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index 269e8ee577..6967473b7c 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -29,7 +29,7 @@ template class Box; template class Box { bool is_full; - explicit constexpr Box(bool is_full) : is_full(is_full) {} + explicit constexpr Box(bool is_full_) : is_full(is_full_) {} public: constexpr static std::size_t D = 0; @@ -182,7 +182,7 @@ template class Box { /** Create box from lower (inclusive) and upper (exclusive) bound */ - Box(const Point &lo, const Point &hi) : lo(lo), hi(hi) {} + Box(const Point &lo_, const Point &hi_) : lo(lo_), hi(hi_) {} /** Create box holding a single point */ explicit Box(const Point &p) : lo(p), hi(p + T(1)) {} diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 65ec537597..5019b16182 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -170,8 +170,8 @@ template class WPoint final : public VPoint { WPoint &operator=(const WPoint &) = default; WPoint &operator=(WPoint &&) = default; - WPoint(const Point &p) : p(p) {} - WPoint(Point &&p) : p(std::move(p)) {} + WPoint(const Point &p_) : p(p_) {} + WPoint(Point &&p_) : p(std::move(p_)) {} std::unique_ptr> copy() const override { return std::make_unique(*this); diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 45e53b871c..18f44c00d7 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -33,7 +33,7 @@ template class Region { friend class Region; - explicit constexpr Region(bool is_full) : is_full(is_full) {} + explicit constexpr Region(bool is_full_) : is_full(is_full_) {} public: constexpr static std::size_t D = 0; @@ -292,7 +292,7 @@ template class Region { assert(upos + nactive == nboxes); subregions.push_back(ubnds[nboxes - 1]); #if REGIONS_DEBUG -#warning "TODO: turn this into a test case" + // TODO: turn this into a test case { Region reg; for (std::size_t i = 0; i < nboxes; ++i) @@ -326,7 +326,7 @@ template class Region { res.emplace_back(Box(Point{pos1}, Point{pos2})); } #if REGIONS_DEBUG -#warning "TODO: turn this into a test case" + // TODO: turn this into a test case assert(std::is_sorted(res.begin(), res.end())); { Region reg; @@ -719,7 +719,7 @@ template class Region { Region(const std::vector> &boxes) { *this = region_from_boxes(boxes.begin(), boxes.end()); #if REGIONS_DEBUG -#warning "TODO: turn this into a test case" + // TODO: turn this into a test case { Region reg; for (const auto &box : boxes) @@ -785,7 +785,7 @@ template class Region { *this); assert(old_subboxes.empty()); #if REGIONS_DEBUG -#warning "TODO: turn this into a test case" + // TODO: turn this into a test case assert(std::is_sorted(res.begin(), res.end())); { Region reg; From 529b4ace85f73b71318d9ca9b9617c7933cd4c49 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 19:54:32 -0400 Subject: [PATCH 15/68] Avoid compiler warnings --- CMakeLists.txt | 20 ++++++++++---------- include/openPMD/regions/Box.hpp | 2 +- include/openPMD/regions/Helpers.hpp | 2 +- include/openPMD/regions/NDPoint.hpp | 2 +- include/openPMD/regions/Point.hpp | 5 +++-- include/openPMD/regions/Region.hpp | 19 ++++++++++--------- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e9c4f1ca96..7b96fa0e09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -770,12 +770,12 @@ if(openPMD_BUILD_TESTING) else() target_link_libraries(${testname}Tests PRIVATE CatchMain) endif() - set_target_properties(${examplename} PROPERTIES - CXX_STANDARD 17 - CXX_EXTENSIONS OFF - CXX_STANDARD_REQUIRED ON - ) endforeach() + set_target_properties(RegionsBoxTests RegionsPointTests RegionsRegionTests PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) endif() if(openPMD_BUILD_CLI_TOOLS) @@ -795,13 +795,13 @@ if(openPMD_BUILD_EXAMPLES) else() add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) - set_target_properties(${examplename} PROPERTIES - CXX_STANDARD 17 - CXX_EXTENSIONS OFF - CXX_STANDARD_REQUIRED ON - ) endif() endforeach() + set_target_properties(13_regions PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) endif() diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index 6967473b7c..7a37a90f04 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -50,7 +50,7 @@ template class Box { /** Create box from lower (inclusive) and upper (exclusive) bound */ - Box(const Point &lo, const Point &hi) : is_full(false) {} + Box(const Point &, const Point &) : is_full(false) {} /** Create box holding a single point */ explicit Box(const Point &) : is_full(true) {} diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index bc599252e6..1a335ce3cb 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -31,7 +31,7 @@ template std::size_t hash_combine(std::size_t seed, const T &x) { template constexpr auto array_push(const Tuple &t, std::index_sequence, const U &x) { - return std::array{std::get(t)..., T(x)}; + return std::array{std::get(t)..., {T(x)}}; } // Append an element to a std::array diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 5019b16182..21882c53c6 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -519,7 +519,7 @@ template class NDPoint { std::unique_ptr> p; - NDPoint(std::unique_ptr> p) : p(std::move(p)) {} + NDPoint(std::unique_ptr> p_) : p(std::move(p_)) {} public: /** Component type diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index af10d330e5..60031cd8f2 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -57,7 +57,7 @@ template class Point { * each number */ template static constexpr void loop(const F &f) { - for (size_type d = 0; d < D; ++d) + for (std::size_t d = 0; d < D; ++d) f(d); } /** Create a new Point by applying a function to the natural number sequence @@ -152,6 +152,7 @@ template class Point { : Point(lst.begin(), lst.end()) {} /** Create a point from a C-style array */ + template * = nullptr> constexpr Point(const T (&arr)[D]) : elts(&arr[0], &arr[D]) {} /** Create a point from a std::array */ @@ -505,7 +506,7 @@ template class Point { */ friend std::ostream &operator<<(std::ostream &os, const Point &x) { os << "["; - for (size_type d = 0; d < D; ++d) { + for (std::size_t d = 0; d < D; ++d) { if (d != 0) os << ","; os << x[d]; diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 18f44c00d7..f03af733f0 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -26,6 +26,9 @@ namespace Regions { */ template class Region; +template class Region; +template class Region; + //////////////////////////////////////////////////////////////////////////////// template class Region { @@ -60,7 +63,7 @@ template class Region { Region &operator=(const Region &) = default; Region &operator=(Region &&) = default; - Region(const Point &p) : is_full(true) {} + Region(const Point &) : is_full(true) {} Region(const Box &b) : is_full(!b.empty()) {} template @@ -104,18 +107,16 @@ template class Region { } // Shift and scale operators - Region operator>>(const Point &d) const { return *this; } - Region operator<<(const Point &d) const { return *this; } + Region operator>>(const Point &) const { return *this; } + Region operator<<(const Point &) const { return *this; } Region &operator>>=(const Point &d) { return *this = *this >> d; } Region &operator<<=(const Point &d) { return *this = *this << d; } - Region operator*(const Point &s) const { return *this; } + Region operator*(const Point &) const { return *this; } Region &operator*=(const Point &s) { return *this = *this * s; } - Region grown(const Point &dlo, const Point &dup) const { - return *this; - } + Region grown(const Point &, const Point &) const { return *this; } Region grown(const Point &d) const { return grown(d, d); } Region grown(T n) const { return grown(Point::pure(n)); } - Region shrunk(const Point &dlo, const Point &dup) const { + Region shrunk(const Point &, const Point &) const { return *this; } Region shrunk(const Point &d) const { return shrunk(d, d); } @@ -430,7 +431,7 @@ template class Region { auto subregion = decoded_subregion_res ^ old_decoded_subregion; if (!subregion.empty()) res.subregions.push_back(pos); - old_decoded_subregion = decoded_subregion_res; + old_decoded_subregion = std::move(decoded_subregion_res); }, region1, region2); assert(old_decoded_subregion.empty()); From 75d5661ec91f112717eac4643f3045b334667320 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 20:00:28 -0400 Subject: [PATCH 16/68] Avoid compiler warnings --- test/RegionsBoxTest.cpp | 2 +- test/RegionsRegionTest.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp index 5ef683e00a..1143c2953a 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/RegionsBoxTest.cpp @@ -228,7 +228,7 @@ TEST_CASE("Box", "[regions]") { test_Box(Box()); } TEST_CASE("Box", "[regions]") { test_Box(Box()); } TEST_CASE("Box", "[regions]") { test_Box(Box()); } -#warning "TODO" +// TODO #if 0 TEST_CASE("NDBox(0)", "[regions]") { test_Box(NDBox(0)); diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index 1bd32110ef..f02135bd81 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -170,7 +170,7 @@ TEST_CASE("Region", "[regions]") { test_Region(Region()); } TEST_CASE("Region", "[regions]") { test_Region(Region()); } TEST_CASE("Region", "[regions]") { test_Region(Region()); } -#warning "TODO" +// TODO #if 0 TEST_CASE("NDRegion(0)", "[regions]") { test_Region(NDRegion(0)); From 01ff022622710d4b3e213830b02a640688c86daa Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 20:20:43 -0400 Subject: [PATCH 17/68] Avoid compiler warnings --- include/openPMD/regions/Helpers.hpp | 5 +++-- include/openPMD/regions/Point.hpp | 2 +- include/openPMD/regions/Region.hpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 1a335ce3cb..cd7112357e 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -2,6 +2,7 @@ #define REGIONS_HELPERS_HPP #include +#include #include #include #include @@ -22,7 +23,7 @@ template std::size_t hash_combine(std::size_t seed, const T &x) { std::hash h; // Taken from Boost return seed ^ - h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2); + (h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2)); } //////////////////////////////////////////////////////////////////////////////// @@ -31,7 +32,7 @@ template std::size_t hash_combine(std::size_t seed, const T &x) { template constexpr auto array_push(const Tuple &t, std::index_sequence, const U &x) { - return std::array{std::get(t)..., {T(x)}}; + return std::array{{std::get(t)..., {T(x)}}}; } // Append an element to a std::array diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index 60031cd8f2..1541871ae1 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -153,7 +153,7 @@ template class Point { /** Create a point from a C-style array */ template * = nullptr> - constexpr Point(const T (&arr)[D]) : elts(&arr[0], &arr[D]) {} + constexpr Point(const T (&arr)[DD]) : elts(&arr[0], &arr[D]) {} /** Create a point from a std::array */ constexpr Point(const std::array &arr) : elts(arr) {} diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index f03af733f0..2cf541c230 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -34,7 +34,7 @@ template class Region; template class Region { bool is_full; - friend class Region; + template friend class Region; explicit constexpr Region(bool is_full_) : is_full(is_full_) {} From b5c01380ab6b68b466828ecdf696141c9777e517 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 20:46:03 -0400 Subject: [PATCH 18/68] Avoid compiler warnings --- include/openPMD/regions/Region.hpp | 9 +++++---- test/RegionsRegionTest.cpp | 6 ------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 2cf541c230..c143bb13d9 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -26,18 +26,19 @@ namespace Regions { */ template class Region; -template class Region; -template class Region; - //////////////////////////////////////////////////////////////////////////////// template class Region { bool is_full; - template friend class Region; + friend class Region; + // This should be private, but GCC 9 seems to ignore the friend + // declaration above +public: explicit constexpr Region(bool is_full_) : is_full(is_full_) {} +private: public: constexpr static std::size_t D = 0; diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index f02135bd81..ff8e868543 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -19,12 +19,6 @@ template void test_Region(const R &r) { const auto p = b.lower(); typedef std::decay_t B; typedef std::decay_t P; - const std::equal_to

eqp; - const std::equal_to eqb; - const std::equal_to eqr; - const std::less

ltp; - const std::less ltb; - const std::less ltr; REQUIRE(r.empty()); REQUIRE(b.empty()); From 1f7632737651cea4a6bf0c0a0bcb02c6186b63ae Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 21:33:48 -0400 Subject: [PATCH 19/68] Avoid compiler warnings --- include/openPMD/regions/Box.hpp | 2 +- include/openPMD/regions/Helpers.hpp | 4 ++-- include/openPMD/regions/NDPoint.hpp | 5 +++-- include/openPMD/regions/Point.hpp | 2 +- test/RegionsBoxTest.cpp | 21 ++++++++++----------- test/RegionsPointTest.cpp | 5 +---- test/RegionsRegionTest.cpp | 6 +++--- 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index 7a37a90f04..e0d1366be0 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -325,7 +325,7 @@ template class Box { for (int m = 0; m < (1 << D); ++m) { Point newlo = lo, newhi = hi; bool is_inside = true; - for (int d = 0; d < D; ++d) { + for (std::size_t d = 0; d < D; ++d) { const bool lohi = m & (1 << d); if (p[d] > lo[d] && p[d] < hi[d]) { if (!lohi) diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index cd7112357e..f26b4d3477 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -32,7 +32,7 @@ template std::size_t hash_combine(std::size_t seed, const T &x) { template constexpr auto array_push(const Tuple &t, std::index_sequence, const U &x) { - return std::array{{std::get(t)..., {T(x)}}}; + return std::array{{std::get(t)..., T(x)}}; } // Append an element to a std::array @@ -46,7 +46,7 @@ template constexpr std::array construct_array(const F &f) { if constexpr (N == 0) return std::array(); - if constexpr (N > 0) + else return array_push(construct_array(f), f(N - 1)); } diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 21882c53c6..5e57e653f2 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -217,13 +217,14 @@ template class WPoint final : public VPoint { const T &operator[](const size_type d) const override { return p[d]; } T &operator[](const size_type d) override { return p[d]; } - std::unique_ptr> erase(const size_type d) const override { + std::unique_ptr> erase(const size_type d + [[maybe_unused]]) const override { if constexpr (D == 0) return std::unique_ptr>(); else return std::make_unique>(p.erase(d)); } - std::unique_ptr> insert(const size_type d, + std::unique_ptr> insert(const size_type d [[maybe_unused]], const T &a) const override { if constexpr (D == max_ndims) return std::unique_ptr>(); diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index 1541871ae1..b1f0b6a0e5 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -141,7 +141,7 @@ template class Point { /** Create a point from Pointers to first and one past the last element */ - constexpr Point(const T *begin, const T *end __attribute__((__unused__))) + constexpr Point(const T *begin, const T *end [[maybe_unused]]) : elts((assert(begin + D == end), make([&](size_type d) { return begin[d]; }))) {} /** Create a point from an initializer list diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp index 1143c2953a..bf87a86f10 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/RegionsBoxTest.cpp @@ -10,16 +10,16 @@ using namespace openPMD::Regions; -template void test_Box(const B &b) { - const std::size_t D = b.ndims(); +template void test_Box(const B &box) { + const std::size_t D = box.ndims(); using T = typename B::value_type; - const auto p = b.lower(); + const auto p = box.lower(); typedef std::decay_t P; const std::equal_to

eqp; const std::equal_to eqb; - const std::less

ltp; + // const std::less

ltp; const std::less ltb; - REQUIRE(b.empty()); + REQUIRE(box.empty()); std::mt19937 gen; std::uniform_int_distribution dist0(0, 9); @@ -34,7 +34,7 @@ template void test_Box(const B &b) { return B(p); } if (dist0(gen) == 0) - return b; + return box; while (1) { auto lo = randp(); auto hi = randp(); @@ -46,8 +46,8 @@ template void test_Box(const B &b) { for (int iter = 0; iter < 100; ++iter) { - B N(b); - REQUIRE(N.ndims() == D); + B N(box); + REQUIRE(N.ndims() == std::ptrdiff_t(D)); REQUIRE(N.empty()); for (std::size_t d = 0; d < D; ++d) REQUIRE(N.lower()[d] >= N.upper()[d]); @@ -60,7 +60,6 @@ template void test_Box(const B &b) { REQUIRE(eqp(n + n, n)); const P x = randp(); const P y = randp(); - const P z = randp(); const T a = rand(); const T b = rand(); @@ -196,8 +195,8 @@ template void test_Box(const B &b) { REQUIRE((U <= X || U <= Y) == true); const std::vector DXY = X - Y; - for (const auto &D : DXY) - REQUIRE((D <= X || !isdisjoint(D, Y)) == true); + for (const auto &DD : DXY) + REQUIRE((DD <= X || !isdisjoint(DD, Y)) == true); const std::vector SXY = X ^ Y; for (const auto &S : SXY) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 1acc4edbd4..5df7e58578 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -57,9 +57,6 @@ template void test_Point_bool(const P &p) { const P y = randp(); const P z = randp(); - const T a = rand(); - const T b = rand(); - REQUIRE(eq(n, n)); REQUIRE(eq(x, x)); REQUIRE(!lt(n, n)); @@ -210,7 +207,7 @@ template void test_Point_int(const P &p) { sum(x + y)); REQUIRE(sum(n) == 0); - REQUIRE(sum(n + 1) == D); + REQUIRE(sum(n + 1) == std::ptrdiff_t(D)); REQUIRE(product(n) == (D == 0 ? 1 : 0)); REQUIRE(product(n + 1) == 1); REQUIRE(min_element(n) == (D == 0 ? std::numeric_limits::max() : 0)); diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index ff8e868543..5be2155dca 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -14,11 +14,11 @@ using namespace openPMD::Regions; template void test_Region(const R &r) { const std::size_t D = r.ndims(); - using T = typename R::value_type; + // using T = typename R::value_type; const auto b = bounding_box(r); const auto p = b.lower(); - typedef std::decay_t B; - typedef std::decay_t P; + using B = std::decay_t; + using P = std::decay_t; REQUIRE(r.empty()); REQUIRE(b.empty()); From e0b85dc7d9680e5114b2c3ad358646ec5c04b568 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 22:09:23 -0400 Subject: [PATCH 20/68] CI: Use GCC 7 instead of GCC 5 --- .../gcc7_py36_ompi_h5_ad1_ad2/spack.yaml | 61 +++++++++++++++++++ .github/workflows/unix.yml | 8 +-- 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 .github/ci/spack-envs/gcc7_py36_ompi_h5_ad1_ad2/spack.yaml diff --git a/.github/ci/spack-envs/gcc7_py36_ompi_h5_ad1_ad2/spack.yaml b/.github/ci/spack-envs/gcc7_py36_ompi_h5_ad1_ad2/spack.yaml new file mode 100644 index 0000000000..191de172ed --- /dev/null +++ b/.github/ci/spack-envs/gcc7_py36_ompi_h5_ad1_ad2/spack.yaml @@ -0,0 +1,61 @@ +# This is a Spack environment file. +# +# Activating and installing this environment will provide all dependencies +# that are needed for full-feature development. +# https//spack.readthedocs.io/en/latest/environments.html#anonymous-environments +# +spack: + specs: + - adios + - adios2 + - hdf5 + - openmpi + + packages: + adios: + variants: ~zfp ~sz ~lz4 ~blosc + adios2: + variants: ~zfp ~sz ~png ~dataman ~python ~fortran ~ssc ~shared ~bzip2 + cmake: + externals: + - spec: "cmake" + prefix: /usr + buildable: False + openmpi: + externals: + - spec: "openmpi" + prefix: /usr + buildable: False + perl: + externals: + - spec: "perl" + prefix: /usr + buildable: False + python: + externals: + - spec: "python" + prefix: /usr + buildable: False + all: + target: ['x86_64'] + variants: ~fortran + compiler: [gcc@7.4.0] + + compilers: + - compiler: + environment: {} + extra_rpaths: [] + flags: {} + modules: [] + operating_system: ubuntu18.04 + paths: + cc: /usr/bin/gcc-7 + cxx: /usr/bin/g++-7 + f77: /usr/bin/gfortran + fc: /usr/bin/gfortran + spec: gcc@7.4.0 + target: x86_64 + + config: + build_jobs: 2 + diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 42a6a4382a..a78e3f44a3 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -207,13 +207,13 @@ jobs: # gcc-4.8.5_py35_nompi_h5 # gcc-7.4.0_py_ompi_h5_ad1_ad2_coveralls - gcc5_py36_pd_dd_ompi_h5_ad1_ad2: + gcc7_py36_pd_dd_ompi_h5_ad1_ad2: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Spack Cache uses: actions/cache@v2 - with: {path: /opt/spack, key: gcc5_py36_ompi_h5_ad1_ad2 } + with: {path: /opt/spack, key: gcc7_py36_ompi_h5_ad1_ad2 } - name: Install run: | sudo apt-get update @@ -226,9 +226,9 @@ jobs: python3 -m pip install -U dask python3 -m pip install -U pyarrow - name: Build - env: {CC: gcc-5, CXX: g++-5, CXXFLAGS: -Werror -Wno-deprecated-declarations} + env: {CC: gcc-7, CXX: g++-7, CXXFLAGS: -Werror -Wno-deprecated-declarations} run: | - eval $(spack env activate --sh .github/ci/spack-envs/gcc5_py36_ompi_h5_ad1_ad2/) + eval $(spack env activate --sh .github/ci/spack-envs/gcc7_py36_ompi_h5_ad1_ad2/) spack install mkdir build && cd build From 483bd2a88e20e355941dcaf0620c7cf004589510 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 22:09:36 -0400 Subject: [PATCH 21/68] Avoid compiler warnings --- include/openPMD/regions/Helpers.hpp | 2 ++ test/RegionsBoxTest.cpp | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index f26b4d3477..294eab145b 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -48,6 +48,8 @@ constexpr std::array construct_array(const F &f) { return std::array(); else return array_push(construct_array(f), f(N - 1)); + // Unreachable statement to appease icc + return {}; } //////////////////////////////////////////////////////////////////////////////// diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp index bf87a86f10..74bf8515bf 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/RegionsBoxTest.cpp @@ -62,7 +62,6 @@ template void test_Box(const B &box) { const P y = randp(); const T a = rand(); - const T b = rand(); REQUIRE(N.empty()); if (D > 0) { @@ -76,11 +75,11 @@ template void test_Box(const B &box) { REQUIRE(Z.empty() == (Z.size() == 0)); if (D > 0) { - REQUIRE(X.empty() == all(fmap([](auto a, auto b) { return a >= b; }, + REQUIRE(X.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, X.lower(), X.upper()))); - REQUIRE(Y.empty() == all(fmap([](auto a, auto b) { return a >= b; }, + REQUIRE(Y.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, Y.lower(), Y.upper()))); - REQUIRE(Z.empty() == all(fmap([](auto a, auto b) { return a >= b; }, + REQUIRE(Z.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, Z.lower(), Z.upper()))); } From 8b3b3ad76605caf60c4843bf85c65127019b0574 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 7 Jun 2021 23:00:55 -0400 Subject: [PATCH 22/68] Correct integer division by zero --- include/openPMD/regions/Helpers.hpp | 2 - include/openPMD/regions/NDPoint.hpp | 87 +++++++++++++++++++++++------ test/RegionsPointTest.cpp | 20 ++++--- test/RegionsRegionTest.cpp | 6 +- 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 294eab145b..f26b4d3477 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -48,8 +48,6 @@ constexpr std::array construct_array(const F &f) { return std::array(); else return array_push(construct_array(f), f(N - 1)); - // Unreachable statement to appease icc - return {}; } //////////////////////////////////////////////////////////////////////////////// diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 5e57e653f2..00d72f7da9 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -140,12 +140,19 @@ template class VPoint { virtual T product1() const = 0; virtual T sum1() const = 0; - virtual std::unique_ptr operator==(const VPoint &x) const = 0; - virtual std::unique_ptr operator!=(const VPoint &x) const = 0; - virtual std::unique_ptr operator<(const VPoint &x) const = 0; - virtual std::unique_ptr operator>(const VPoint &x) const = 0; - virtual std::unique_ptr operator<=(const VPoint &x) const = 0; - virtual std::unique_ptr operator>=(const VPoint &x) const = 0; + virtual std::unique_ptr> operator==(const VPoint &x) const = 0; + virtual std::unique_ptr> operator!=(const VPoint &x) const = 0; + virtual std::unique_ptr> operator<(const VPoint &x) const = 0; + virtual std::unique_ptr> operator>(const VPoint &x) const = 0; + virtual std::unique_ptr> operator<=(const VPoint &x) const = 0; + virtual std::unique_ptr> operator>=(const VPoint &x) const = 0; + + virtual std::unique_ptr> operator==(const T &a) const = 0; + virtual std::unique_ptr> operator!=(const T &a) const = 0; + virtual std::unique_ptr> operator<(const T &a) const = 0; + virtual std::unique_ptr> operator>(const T &a) const = 0; + virtual std::unique_ptr> operator<=(const T &a) const = 0; + virtual std::unique_ptr> operator>=(const T &a) const = 0; virtual bool equal_to1(const VPoint &x) const = 0; virtual std::size_t hash1() const = 0; @@ -450,23 +457,48 @@ template class WPoint final : public VPoint { T product1() const override { return product(p); } T sum1() const override { return sum(p); } - std::unique_ptr> operator==(const VPoint &x) const override { - return std::make_unique(p == dynamic_cast(x).p); + std::unique_ptr> operator==(const VPoint &x) const override { + return std::make_unique>(p == + dynamic_cast(x).p); + } + std::unique_ptr> operator!=(const VPoint &x) const override { + return std::make_unique>(p != + dynamic_cast(x).p); + } + std::unique_ptr> operator<(const VPoint &x) const override { + return std::make_unique>(p < + dynamic_cast(x).p); + } + std::unique_ptr> operator>(const VPoint &x) const override { + return std::make_unique>(p > + dynamic_cast(x).p); + } + std::unique_ptr> operator<=(const VPoint &x) const override { + return std::make_unique>(p <= + dynamic_cast(x).p); + } + std::unique_ptr> operator>=(const VPoint &x) const override { + return std::make_unique>(p >= + dynamic_cast(x).p); + } + + std::unique_ptr> operator==(const T &a) const override { + return std::make_unique>(p == a); } - std::unique_ptr> operator!=(const VPoint &x) const override { - return std::make_unique(p != dynamic_cast(x).p); + std::unique_ptr> operator!=(const T &a) const override { + return std::make_unique>(p != a); } - std::unique_ptr> operator<(const VPoint &x) const override { - return std::make_unique(p < dynamic_cast(x).p); + std::unique_ptr> operator<(const T &a) const override { + return std::make_unique>(p < a); } - std::unique_ptr> operator>(const VPoint &x) const override { - return std::make_unique(p > dynamic_cast(x).p); + std::unique_ptr> operator>(const T &a) const override { + return std::make_unique>(p > a); } - std::unique_ptr> operator<=(const VPoint &x) const override { - return std::make_unique(p <= dynamic_cast(x).p); + std::unique_ptr> operator<=(const T &a) const override { + return std::make_unique>(p <= a); } - std::unique_ptr> operator>=(const VPoint &x) const override { - return std::make_unique(p >= dynamic_cast(x).p); + std::unique_ptr> operator>=(const T &a) const override { + return std::make_unique>(p >= a); } bool equal_to1(const VPoint &x) const override { @@ -846,6 +878,25 @@ template class NDPoint { return *x.p >= *y.p; } + friend NDPoint operator==(const NDPoint &x, const T &b) { + return *x.p == b; + } + friend NDPoint operator!=(const NDPoint &x, const T &b) { + return *x.p != b; + } + friend NDPoint operator<(const NDPoint &x, const T &b) { + return *x.p < b; + } + friend NDPoint operator>(const NDPoint &x, const T &b) { + return *x.p > b; + } + friend NDPoint operator<=(const NDPoint &x, const T &b) { + return *x.p <= b; + } + friend NDPoint operator>=(const NDPoint &x, const T &b) { + return *x.p >= b; + } + /** Output a point */ friend std::ostream &operator<<(std::ostream &os, const NDPoint &x) { diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 5df7e58578..aa292273c8 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -243,7 +243,7 @@ template void test_Point_int(const P &p) { REQUIRE(eq(x * (y + z), x * y + x * z)); - if (min_element(abs(y)) != 0) { + if (all(y != 0)) { REQUIRE(eq(x * y / y, x)); REQUIRE(eq(x / y * y + x % y, x)); } @@ -307,12 +307,14 @@ template void test_Point_int(const P &p) { t = x; t *= y; REQUIRE(eq(t, x * y)); - t = x; - t /= y; - REQUIRE(eq(t, x / y)); - t = x; - t %= y; - REQUIRE(eq(t, x % y)); + if (all(y != 0)) { + t = x; + t /= y; + REQUIRE(eq(t, x / y)); + t = x; + t %= y; + REQUIRE(eq(t, x % y)); + } t = x; t &= y; REQUIRE(eq(t, (x & y))); @@ -420,7 +422,7 @@ template void test_Point_float(const P &p) { REQUIRE(eq(a * x, x * a)); - if (min_element(abs(x)) != 0) { + if (all(x != 0)) { REQUIRE(eq(x / x, n + 1)); REQUIRE(is_approx(1 / (1 / x), x)); REQUIRE(is_approx(a / x, a * (1 / x))); @@ -434,7 +436,7 @@ template void test_Point_float(const P &p) { REQUIRE(is_approx(x * (y + z), x * y + x * z)); - if (min_element(abs(y)) != 0) { + if (all(y != 0)) { REQUIRE(is_approx(x * y / y, x)); } diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index 5be2155dca..026dde03ea 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -52,10 +52,10 @@ template void test_Region(const R &r) { return R(B(p)); } const int nboxes = dist0(gen) / 2; - R r; + R nr; for (int n = 0; n < nboxes; ++n) - r |= randb(); - return r; + nr |= randb(); + return nr; }; for (int iter = 0; iter < 100; ++iter) { From 331b48b02be84e4a5949658556b267c92d3c0779 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 08:59:54 -0400 Subject: [PATCH 23/68] Correct equality and comparison operators --- include/openPMD/regions/Helpers.hpp | 33 +++++++++++++++++ include/openPMD/regions/Region.hpp | 56 +++++++++++++++++++++++++++-- test/RegionsBoxTest.cpp | 9 ++++- test/RegionsPointTest.cpp | 12 +++++++ test/RegionsRegionTest.cpp | 15 +++++++- 5 files changed, 120 insertions(+), 5 deletions(-) diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index f26b4d3477..6ee7fce9ed 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -121,6 +121,39 @@ constexpr bool tuple_lt(const Tuple1 &x, const Tuple2 &y) { //////////////////////////////////////////////////////////////////////////////// +// Compare vectors + +template +bool vector_eq(const Vector1 &x, const Vector2 &y) { + if (x.size() != y.size()) + return false; + typedef typename Vector1::value_type T1; + typedef typename Vector2::value_type T2; + typedef std::common_type_t T; + const std::equal_to eq; + for (std::size_t n = 0; n < x.size(); ++n) + if (!eq(x[n], y[n])) + return false; + return true; +} + +template +bool vector_lt(const Vector1 &x, const Vector2 &y) { + typedef typename Vector1::value_type T1; + typedef typename Vector2::value_type T2; + typedef std::common_type_t T; + const std::less lt; + using std::min; + for (std::size_t n = 0; n < min(x.size(), y.size()); ++n) + if (lt(x[n], y[n])) + return true; + else if (lt(y[n], x[n])) + return false; + return x.size() < y.size(); +} + +//////////////////////////////////////////////////////////////////////////////// + // Reduce over a std::vector, using bisection to reduce cost /** Reduce a non-empty range diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index c143bb13d9..34f4325960 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -29,9 +29,15 @@ template class Region; //////////////////////////////////////////////////////////////////////////////// template class Region { +public: + constexpr static std::size_t D = 0; + +private: bool is_full; - friend class Region; + friend class std::equal_to>; + friend class std::less>; + friend class Region; // This should be private, but GCC 9 seems to ignore the friend // declaration above @@ -40,8 +46,6 @@ template class Region { private: public: - constexpr static std::size_t D = 0; - typedef typename Point::value_type value_type; typedef typename Point::size_type size_type; @@ -207,6 +211,9 @@ template class Region { typedef std::vector Subregions; Subregions subregions; + friend class std::equal_to>; + friend class std::less>; + public: typedef typename Point::value_type value_type; typedef typename Point::size_type size_type; @@ -650,6 +657,9 @@ template class Region { typedef std::vector> Subregions; Subregions subregions; + friend class std::equal_to>; + friend class std::less>; + public: typedef typename Point::value_type value_type; typedef typename Point::size_type size_type; @@ -1137,4 +1147,44 @@ template class Region { } // namespace Regions } // namespace openPMD +namespace std { + +template +struct equal_to>; + +template struct equal_to> { + constexpr bool operator()(const openPMD::Regions::Region &x, + const openPMD::Regions::Region &y) const { + return x.is_full == y.is_full; + } +}; + +template +struct equal_to> { + constexpr bool operator()(const openPMD::Regions::Region &x, + const openPMD::Regions::Region &y) const { + return openPMD::Regions::helpers::vector_eq(x.subregions, y.subregions); + } +}; + +template +struct less>; + +template struct less> { + constexpr bool operator()(const openPMD::Regions::Region &x, + const openPMD::Regions::Region &y) const { + return x.is_full < y.is_full; + } +}; + +template +struct less> { + constexpr bool operator()(const openPMD::Regions::Region &x, + const openPMD::Regions::Region &y) const { + return openPMD::Regions::helpers::vector_lt(x.subregions, y.subregions); + } +}; + +} // namespace std + #endif // #ifndef REGIONS_REGION_HPP diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp index 74bf8515bf..dec7b01ad5 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/RegionsBoxTest.cpp @@ -17,7 +17,6 @@ template void test_Box(const B &box) { typedef std::decay_t P; const std::equal_to

eqp; const std::equal_to eqb; - // const std::less

ltp; const std::less ltb; REQUIRE(box.empty()); @@ -94,6 +93,14 @@ template void test_Box(const B &box) { REQUIRE(!eqb(N, X)); REQUIRE(ltb(N, X)); } + if (eqb(X, Y)) + REQUIRE(ltb(X, Y) + ltb(Y, X) == 0); + else + REQUIRE(ltb(X, Y) + ltb(Y, X) == 1); + if (ltb(X, Y) && ltb(Y, Z)) + REQUIRE(ltb(X, Z)); + if (!ltb(Y, X) && !ltb(Z, Y)) + REQUIRE(!ltb(Z, X)); REQUIRE(eqb((X >> x) << x, X)); REQUIRE(eqb(X >> x, X << -x)); diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index aa292273c8..fdfa1aca38 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -332,6 +332,7 @@ template void test_Point_float(const P &p) { const std::size_t D = p.ndims(); using T = typename P::value_type; const std::equal_to

eq; + const std::less

lt; std::mt19937 gen; std::uniform_real_distribution dist(-1.0, 1.0); @@ -352,6 +353,17 @@ template void test_Point_float(const P &p) { const T a = rand(); const T b = rand(); + REQUIRE(eq(x, x)); + REQUIRE(!lt(x, x)); + if (eq(x, y)) + REQUIRE(lt(x, y) + lt(y, x) == 0); + else + REQUIRE(lt(x, y) + lt(y, x) == 1); + if (lt(x, y) && lt(y, z)) + REQUIRE(lt(x, z)); + if (!lt(y, x) && !lt(z, y)) + REQUIRE(!lt(z, x)); + // remove-insert is no-op if (D > 0) { for (std::size_t d = 0; d < D; ++d) { diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index 026dde03ea..9f1e6867d4 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -14,11 +14,13 @@ using namespace openPMD::Regions; template void test_Region(const R &r) { const std::size_t D = r.ndims(); - // using T = typename R::value_type; + using T = typename R::value_type; const auto b = bounding_box(r); const auto p = b.lower(); using B = std::decay_t; using P = std::decay_t; + const std::equal_to eqr; + const std::less ltr; REQUIRE(r.empty()); REQUIRE(b.empty()); @@ -70,6 +72,17 @@ template void test_Region(const R &r) { P x = randp(); P y = randp(); + REQUIRE(eqr(X, X)); + REQUIRE(!ltr(X, X)); + if (eqr(X, Y)) + REQUIRE(ltr(X, Y) + ltr(Y, X) == 0); + else + REQUIRE(ltr(X, Y) + ltr(Y, X) == 1); + if (ltr(X, Y) && ltr(Y, Z)) + REQUIRE(ltr(X, Z)); + if (!ltr(Y, X) && !ltr(Z, Y)) + REQUIRE(!ltr(Z, X)); + REQUIRE(N == N); REQUIRE(X == X); REQUIRE((N != X) != X.empty()); From f1f3c1163d0419e2d2bd0899a03ff1e17a07f67e Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 09:40:14 -0400 Subject: [PATCH 24/68] cmake: New option for C++17 --- CMakeLists.txt | 71 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b96fa0e09..a2be1cc7d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ openpmd_option(ADIOS1 "ADIOS1 backend (.bp files)" AUTO) openpmd_option(ADIOS2 "ADIOS2 backend (.bp files)" AUTO) openpmd_option(PYTHON "Enable Python bindings" AUTO) +option(openPMD_CXX_STANDARD "Which C++ standard to use (14 or 17)" OFF) option(openPMD_INSTALL "Add installation targets" ON) option(openPMD_HAVE_PKGCONFIG "Generate a .pc file for pkg-config" ON) option(openPMD_USE_INTERNAL_VARIANT "Use internally shipped MPark.Variant" ON) @@ -79,6 +80,17 @@ option(openPMD_USE_INTERNAL_JSON "Use internally shipped nlohmann-json" ON) option(openPMD_USE_INVASIVE_TESTS "Enable unit tests that modify source code" OFF) option(openPMD_USE_VERIFY "Enable internal VERIFY (assert) macro independent of build type" ON) +if(NOT openPMD_CXX_STANDARD) + set(openPMD_CXX_STANDARD 14) +endif() +if(openPMD_CXX_STANDARD STREQUAL 14) + set(openPMD_CXX_STD cxx_std_14) +elseif(openPMD_CXX_STANDARD STREQUAL 17) + set(openPMD_CXX_STD cxx_std_17) +else() + message(FATAL_ERROR "openPMD_CXX_STANDARD must be either 14 or 17; found ${openPMD_CXX_STANDARD}") +endif() + set(CMAKE_CONFIGURATION_TYPES "Release;Debug;MinSizeRel;RelWithDebInfo") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING @@ -396,9 +408,10 @@ add_library(openPMD::openPMD ALIAS openPMD) # properties target_compile_features(openPMD - PUBLIC cxx_std_14 + PUBLIC ${openPMD_CXX_STD} ) set_target_properties(openPMD PROPERTIES + CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -475,10 +488,10 @@ if(openPMD_HAVE_ADIOS1) add_library(openPMD.ADIOS1.Serial SHARED ${IO_ADIOS1_SEQUENTIAL_SOURCE}) add_library(openPMD.ADIOS1.Parallel SHARED ${IO_ADIOS1_SOURCE}) target_compile_features(openPMD.ADIOS1.Serial - PUBLIC cxx_std_14 + PUBLIC ${openPMD_CXX_STD} ) target_compile_features(openPMD.ADIOS1.Parallel - PUBLIC cxx_std_14 + PUBLIC ${openPMD_CXX_STD} ) target_compile_options(openPMD.ADIOS1.Serial PUBLIC ${_msvc_options}) target_compile_options(openPMD.ADIOS1.Parallel PUBLIC ${_msvc_options}) @@ -498,6 +511,7 @@ if(openPMD_HAVE_ADIOS1) endif() set_target_properties(openPMD.ADIOS1.Serial PROPERTIES + CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -525,6 +539,7 @@ if(openPMD_HAVE_ADIOS1) if(openPMD_HAVE_MPI) set_target_properties(openPMD.ADIOS1.Parallel PROPERTIES + CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -682,12 +697,16 @@ endif() set(openPMD_TEST_NAMES Core Auxiliary - RegionsBox - RegionsPoint - RegionsRegion SerialIO ParallelIO ) +if(openPMD_CXX_STANDARD STREQUAL 17) + list(APPEND openPMD_TEST_NAMES + RegionsBox + RegionsPoint + RegionsRegion + ) +endif() # command line tools set(openPMD_CLI_TOOL_NAMES ls @@ -713,8 +732,12 @@ set(openPMD_EXAMPLE_NAMES 10_streaming_write 10_streaming_read 12_span_write - 13_regions ) +if(openPMD_CXX_STANDARD STREQUAL 17) + list(APPEND openPMD_EXAMPLE_NAMES + 13_regions + ) +endif() set(openPMD_PYTHON_EXAMPLE_NAMES 2_read_serial 2a_read_thetaMode_serial @@ -741,9 +764,10 @@ if(openPMD_BUILD_TESTING) test/CatchRunner.cpp) # Always MPI_Init with Serial Fallback add_library(CatchMain ${_openpmd_lib_type} test/CatchMain.cpp) # Serial only - target_compile_features(CatchRunner PUBLIC cxx_std_14) - target_compile_features(CatchMain PUBLIC cxx_std_14) + target_compile_features(CatchRunner PUBLIC ${openPMD_CXX_STD}) + target_compile_features(CatchMain PUBLIC ${openPMD_CXX_STD}) set_target_properties(CatchRunner CatchMain PROPERTIES + CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -770,12 +794,13 @@ if(openPMD_BUILD_TESTING) else() target_link_libraries(${testname}Tests PRIVATE CatchMain) endif() + target_compile_features(${testname}Tests PUBLIC ${openPMD_CXX_STD}) + set_target_properties(${testname}Tests PROPERTIES + CXX_STANDARD ${openPMD_CXX_STANDARD} + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) endforeach() - set_target_properties(RegionsBoxTests RegionsPointTests RegionsRegionTests PROPERTIES - CXX_STANDARD 17 - CXX_EXTENSIONS OFF - CXX_STANDARD_REQUIRED ON - ) endif() if(openPMD_BUILD_CLI_TOOLS) @@ -791,17 +816,24 @@ if(openPMD_BUILD_EXAMPLES) if(openPMD_HAVE_MPI) add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) + target_compile_features(${examplename} PUBLIC ${openPMD_CXX_STD}) + set_target_properties(${examplename} PROPERTIES + CXX_STANDARD ${openPMD_CXX_STANDARD} + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) endif() else() add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) + target_compile_features(${examplename} PUBLIC ${openPMD_CXX_STD}) + set_target_properties(${examplename} PROPERTIES + CXX_STANDARD ${openPMD_CXX_STANDARD} + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) endif() endforeach() - set_target_properties(13_regions PROPERTIES - CXX_STANDARD 17 - CXX_EXTENSIONS OFF - CXX_STANDARD_REQUIRED ON - ) endif() @@ -1323,6 +1355,7 @@ else() message(" Installation: OFF") endif() message("") +message(" C++ standard: ${openPMD_CXX_STD}") message(" Build Type: ${CMAKE_BUILD_TYPE}") if(openPMD_BUILD_SHARED_LIBS) message(" Library: shared") From cf4612121dc3a19bf2f95339e63ee2abcc4b4916 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 09:40:34 -0400 Subject: [PATCH 25/68] Switch back to GCC5 --- .github/workflows/unix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index a78e3f44a3..f48192c378 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -207,7 +207,7 @@ jobs: # gcc-4.8.5_py35_nompi_h5 # gcc-7.4.0_py_ompi_h5_ad1_ad2_coveralls - gcc7_py36_pd_dd_ompi_h5_ad1_ad2: + gcc5_py36_pd_dd_ompi_h5_ad1_ad2: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 From 0751fca63ab9d474d970f732b67c8841da50f58e Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 09:40:46 -0400 Subject: [PATCH 26/68] Avoid compiler warnings --- test/RegionsRegionTest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index 9f1e6867d4..43c7fb7ff7 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -14,7 +14,6 @@ using namespace openPMD::Regions; template void test_Region(const R &r) { const std::size_t D = r.ndims(); - using T = typename R::value_type; const auto b = bounding_box(r); const auto p = b.lower(); using B = std::decay_t; From ac9af25f69ab85704fd1d998b7c3d03a9a82a8b6 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 10:43:31 -0400 Subject: [PATCH 27/68] CI: Enable C++17 where supported --- .github/workflows/unix.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index f48192c378..0e2b50da10 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -23,7 +23,7 @@ jobs: mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_INVASIVE_TESTS=ON + cmake -S .. -B . -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_INVASIVE_TESTS=ON -DopenPMD_CXX_STANDARD=17 cmake --build . --parallel 2 2> clang-tidy.log cat clang-tidy.log if [[ $(wc -m Date: Tue, 8 Jun 2021 11:02:06 -0400 Subject: [PATCH 28/68] Make comparison manifestly type-safe --- test/RegionsPointTest.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index fdfa1aca38..4e6eeeab51 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -34,6 +34,14 @@ template bool is_approx(const T &xs, const T &ys) { return m <= 100 * eps; } +template bool eq_helper(const T &x1, const T &x2) { + std::equal_to eq; + return eq(x1, x2); +} +template bool eq_helper(const T1 &x1, const T2 &x2) { + return false; +} + template void test_Point_bool(const P &p) { const std::size_t D = p.ndims(); using T = typename P::value_type; @@ -371,9 +379,8 @@ template void test_Point_float(const P &p) { const auto x1 = x.erase(d); REQUIRE(x1.ndims() == D - 1); const auto x2 = x1.insert(d, a1); - // This conversion is fine for D > 0 - const P x3 = *(const P *)&x2; - REQUIRE(eq(x3, x)); + REQUIRE(x2.ndims() == D); + REQUIRE(eq_helper(x2, x)); } } // insert-remove is no-op From c7b8cf2942788e2bd5ac8f4113bc58a9e03b8a80 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 11:10:55 -0400 Subject: [PATCH 29/68] Disable C++ on systems where it doesn't work --- .github/workflows/unix.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 0e2b50da10..1bedcaf27e 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -73,13 +73,14 @@ jobs: sudo .github/workflows/dependencies/install_spack - name: Build env: {CC: clang-5.0, CXX: clang++-5.0, CXXFLAGS: -Werror -Wno-deprecated-declarations} + # Clang 5 does not handle GCC 10's STL in C++17-mode run: | eval $(spack env activate --sh .github/ci/spack-envs/clang5_nopy_nompi_h5/) spack install mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=OFF -DopenPMD_USE_HDF5=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DopenPMD_CXX_STANDARD=17 + cmake -S .. -B . -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=OFF -DopenPMD_USE_HDF5=ON -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 ctest --output-on-failure @@ -97,13 +98,14 @@ jobs: sudo .github/workflows/dependencies/install_spack - name: Build env: {CC: clang-5.0, CXX: clang++-5.0, CXXFLAGS: -Werror -Wno-deprecated-declarations} + # Clang 5 does not handle GCC 10's STL in C++17-mode run: | eval $(spack env activate --sh .github/ci/spack-envs/clang5_nopy_ompi_h5_ad1_ad2_bp3/) spack install mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DopenPMD_CXX_STANDARD=17 + cmake -S .. -B . -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 ctest --output-on-failure @@ -126,13 +128,14 @@ jobs: sudo .github/workflows/dependencies/install_spack - name: Build env: {CC: clang-5.0, CXX: clang++-5.0, CXXFLAGS: -Werror -Wno-deprecated-declarations} + # Clang 5 does not handle GCC 10's STL in C++17-mode run: | eval $(spack env activate --sh .github/ci/spack-envs/clang5_nopy_ompi_h5_ad1_ad2_bp3/) spack install mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=OFF -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DopenPMD_CXX_STANDARD=17 + cmake -S .. -B . -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=OFF -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 export OPENPMD2_ADIOS2_SCHEMA=20210209 ctest --output-on-failure @@ -192,7 +195,7 @@ jobs: run: | mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DopenPMD_CXX_STANDARD=17 + cmake -S .. -B . -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 ctest --output-on-failure @@ -270,6 +273,8 @@ jobs: sudo .github/workflows/dependencies/install_icc - name: Build env: {CXXFLAGS: -Werror -Wno-deprecated-declarations} + # C++17 not properly supported (false positives for unused + # arguments and missing return statements with `if constexpr`) run: | set +e; source /opt/intel/oneapi/setvars.sh; set -e mkdir build && cd build @@ -278,8 +283,7 @@ jobs: -DCMAKE_C_COMPILER=$(which icc) \ -DCMAKE_CXX_COMPILER=$(which icpc) \ -DopenPMD_USE_PYTHON=OFF \ - -DopenPMD_USE_MPI=OFF \ - -DopenPMD_CXX_STANDARD=17 + -DopenPMD_USE_MPI=OFF cmake --build . --parallel 2 ctest --output-on-failure From 4b373a69787efdf081a96bc9e82bdafec45501eb Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 11:11:10 -0400 Subject: [PATCH 30/68] Try to avoid comparison failures --- test/RegionsPointTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 4e6eeeab51..e6b2efb1ec 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -413,10 +413,10 @@ template void test_Point_float(const P &p) { REQUIRE(sum(n + 1) == D); REQUIRE(product(n) == (D == 0 ? 1 : 0)); REQUIRE(product(n + 1) == 1); - REQUIRE(min_element(n) == (D == 0 ? T(1) / 0 : 0)); - REQUIRE(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); - REQUIRE(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); - REQUIRE(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); + REQUIRE((min_element(n) == (D == 0 ? T(1) / 0 : 0))==true); + REQUIRE((max_element(n) == (D == 0 ? -T(1) / 0 : 0))==true); + REQUIRE((min_element(n + 1) == (D == 0 ? T(1) / 0 : 1))==true); + REQUIRE((max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1))==true); REQUIRE(eq(+x, x)); REQUIRE(eq(n + x, x)); From bdd1971fc6fb84d421b29b26a1a8c375bb493584 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 11:19:12 -0400 Subject: [PATCH 31/68] Avoid compiler warnings --- test/RegionsPointTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index e6b2efb1ec..e76c634f69 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -38,7 +38,7 @@ template bool eq_helper(const T &x1, const T &x2) { std::equal_to eq; return eq(x1, x2); } -template bool eq_helper(const T1 &x1, const T2 &x2) { +template bool eq_helper(const T1 &, const T2 &) { return false; } From 77358ae566e9bad203286c5746795de0f37fb4bf Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 11:19:27 -0400 Subject: [PATCH 32/68] Try to allow comparing infinities --- test/RegionsPointTest.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index e76c634f69..0ee271e374 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -413,10 +413,12 @@ template void test_Point_float(const P &p) { REQUIRE(sum(n + 1) == D); REQUIRE(product(n) == (D == 0 ? 1 : 0)); REQUIRE(product(n + 1) == 1); - REQUIRE((min_element(n) == (D == 0 ? T(1) / 0 : 0))==true); - REQUIRE((max_element(n) == (D == 0 ? -T(1) / 0 : 0))==true); - REQUIRE((min_element(n + 1) == (D == 0 ? T(1) / 0 : 1))==true); - REQUIRE((max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1))==true); + // We need to allow `inf == inf`, and REQUIRE's standard + // comparison wouldn't, so we compare `== true` instead + REQUIRE((min_element(n) == (D == 0 ? T(1) / 0 : 0)) == true); + REQUIRE((max_element(n) == (D == 0 ? -T(1) / 0 : 0)) == true); + REQUIRE((min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)) == true); + REQUIRE((max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)) == true); REQUIRE(eq(+x, x)); REQUIRE(eq(n + x, x)); From 99a6e6241d96f968b9064b6562c23258a64a0d13 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 12:25:53 -0400 Subject: [PATCH 33/68] CI: Disable C++17 for clang-tidy --- .github/workflows/unix.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 1bedcaf27e..ad97af746a 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -17,13 +17,15 @@ jobs: sudo .github/workflows/dependencies/install_spack - name: Build env: {CC: clang, CXX: clang++, CXXFLAGS: -Wno-deprecated-declarations} + # The main openPMD code base leads to many clang-tidy warnings + # when C++17 is enabled. We cannot enable it yet. run: | eval $(spack env activate --sh .github/ci/spack-envs/clangtidy_nopy_ompi_h5_ad1_ad2/) spack install mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_INVASIVE_TESTS=ON -DopenPMD_CXX_STANDARD=17 + cmake -S .. -B . -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 2> clang-tidy.log cat clang-tidy.log if [[ $(wc -m Date: Tue, 8 Jun 2021 12:26:08 -0400 Subject: [PATCH 34/68] Update catch2 calls --- test/RegionsBoxTest.cpp | 218 +++++++++--------- test/RegionsPointTest.cpp | 446 ++++++++++++++++++------------------- test/RegionsRegionTest.cpp | 107 +++++---- 3 files changed, 382 insertions(+), 389 deletions(-) diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp index dec7b01ad5..e4a98e947d 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/RegionsBoxTest.cpp @@ -18,7 +18,7 @@ template void test_Box(const B &box) { const std::equal_to

eqp; const std::equal_to eqb; const std::less ltb; - REQUIRE(box.empty()); + CHECK(box.empty()); std::mt19937 gen; std::uniform_int_distribution dist0(0, 9); @@ -46,171 +46,169 @@ template void test_Box(const B &box) { for (int iter = 0; iter < 100; ++iter) { B N(box); - REQUIRE(N.ndims() == std::ptrdiff_t(D)); - REQUIRE(N.empty()); + CHECK(N.ndims() == std::ptrdiff_t(D)); + CHECK(N.empty()); for (std::size_t d = 0; d < D; ++d) - REQUIRE(N.lower()[d] >= N.upper()[d]); + CHECK(N.lower()[d] >= N.upper()[d]); const B X = randb(); const B Y = randb(); const B Z = randb(); const P n = fmap([](auto) { return T(0); }, p); - REQUIRE(eqp(n + n, n)); + CHECK(eqp(n + n, n)); const P x = randp(); const P y = randp(); const T a = rand(); - REQUIRE(N.empty()); + CHECK(N.empty()); if (D > 0) { - REQUIRE(X.empty() == all(X.shape() == 0)); - REQUIRE(Y.empty() == all(Y.shape() == 0)); - REQUIRE(Z.empty() == all(Z.shape() == 0)); + CHECK(X.empty() == all(X.shape() == 0)); + CHECK(Y.empty() == all(Y.shape() == 0)); + CHECK(Z.empty() == all(Z.shape() == 0)); } - REQUIRE(X.empty() == (X.size() == 0)); - REQUIRE(Y.empty() == (Y.size() == 0)); - REQUIRE(Z.empty() == (Z.size() == 0)); + CHECK(X.empty() == (X.size() == 0)); + CHECK(Y.empty() == (Y.size() == 0)); + CHECK(Z.empty() == (Z.size() == 0)); if (D > 0) { - REQUIRE(X.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, - X.lower(), X.upper()))); - REQUIRE(Y.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, - Y.lower(), Y.upper()))); - REQUIRE(Z.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, - Z.lower(), Z.upper()))); + CHECK(X.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, + X.lower(), X.upper()))); + CHECK(Y.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, + Y.lower(), Y.upper()))); + CHECK(Z.empty() == all(fmap([](auto lo, auto up) { return lo >= up; }, + Z.lower(), Z.upper()))); } - REQUIRE(eqb(N, N)); - REQUIRE(eqb(X, X)); - REQUIRE(!ltb(N, N)); - REQUIRE(!ltb(X, X)); + CHECK(eqb(N, N)); + CHECK(eqb(X, X)); + CHECK(!ltb(N, N)); + CHECK(!ltb(X, X)); if (X.empty()) { - REQUIRE(eqb(N, X)); - REQUIRE(!ltb(N, X)); + CHECK(eqb(N, X)); + CHECK(!ltb(N, X)); } else { - REQUIRE(!eqb(N, X)); - REQUIRE(ltb(N, X)); + CHECK(!eqb(N, X)); + CHECK(ltb(N, X)); } if (eqb(X, Y)) - REQUIRE(ltb(X, Y) + ltb(Y, X) == 0); + CHECK(ltb(X, Y) + ltb(Y, X) == 0); else - REQUIRE(ltb(X, Y) + ltb(Y, X) == 1); + CHECK(ltb(X, Y) + ltb(Y, X) == 1); if (ltb(X, Y) && ltb(Y, Z)) - REQUIRE(ltb(X, Z)); + CHECK(ltb(X, Z)); if (!ltb(Y, X) && !ltb(Z, Y)) - REQUIRE(!ltb(Z, X)); + CHECK(!ltb(Z, X)); - REQUIRE(eqb((X >> x) << x, X)); - REQUIRE(eqb(X >> x, X << -x)); - REQUIRE(eqb(X >> x, X << -x)); - REQUIRE(eqb(X >> (x + y), (X >> x) >> y)); + CHECK(((X >> x) << x) == X); + CHECK((X >> x) == (X << -x)); + CHECK((X >> x) == (X << -x)); + CHECK((X >> (x + y)) == ((X >> x) >> y)); - REQUIRE(eqb((X * x) * y, X * (x * y))); - REQUIRE(eqb((X >> x) * y, (X * y) >> (x * y))); + CHECK((X * x) * y == X * (x * y)); + CHECK((X >> x) * y == (X * y) >> (x * y)); - REQUIRE(eqb(X.grown(1), X) == (D == 0 || X.empty())); + CHECK((X.grown(1) == X) == (D == 0 || X.empty())); if (all(x >= 0 && y >= 0)) - REQUIRE(eqb(X.grown(x).grown(y), X.grown(x + y))); + CHECK(X.grown(x).grown(y) == X.grown(x + y)); else - REQUIRE((X.grown(x).grown(y).empty() || - eqb(X.grown(x).grown(y), X.grown(x + y))) == true); + CHECK((X.grown(x).grown(y).empty() || + X.grown(x).grown(y) == X.grown(x + y)) == true); if (all(x >= 0)) - REQUIRE(eqb(X.grown(x).grown(-x), X)); + CHECK(X.grown(x).grown(-x) == X); else - REQUIRE((X.grown(x).grown(-x).empty() || eqb(X.grown(x).grown(-x), X)) == - true); - REQUIRE(eqb(X.grown(x), X.grown(x, x))); - REQUIRE(eqb(X.grown(a), X.grown(P::pure(a)))); - - REQUIRE(eqb(X.shrunk(x, y), X.grown(-x, -y))); - REQUIRE(eqb(X.shrunk(x), X.shrunk(x, x))); - REQUIRE(eqb(X.shrunk(a), X.shrunk(P::pure(a)))); - - REQUIRE(N == N); - REQUIRE(X == X); - REQUIRE((N == X) == X.empty()); - REQUIRE(!(N != N)); - REQUIRE(!(X != X)); - REQUIRE((N != X) != (N == X)); - - REQUIRE(X.contains(X.lower()) == !X.empty()); - REQUIRE(X.contains(X.upper() - 1) == !X.empty()); - REQUIRE(X.grown(1).contains(X.upper()) == !X.empty()); - REQUIRE(isdisjoint(X, X) == X.empty()); + CHECK((X.grown(x).grown(-x).empty() || X.grown(x).grown(-x) == X) == + true); + CHECK(X.grown(x) == X.grown(x, x)); + CHECK(X.grown(a) == X.grown(P::pure(a))); + + CHECK(X.shrunk(x, y) == X.grown(-x, -y)); + CHECK(X.shrunk(x) == X.shrunk(x, x)); + CHECK(X.shrunk(a) == X.shrunk(P::pure(a))); + + CHECK(N == N); + CHECK(X == X); + CHECK((N == X) == X.empty()); + CHECK(!(N != N)); + CHECK(!(X != X)); + CHECK((N != X) != (N == X)); + + CHECK(X.contains(X.lower()) == !X.empty()); + CHECK(X.contains(X.upper() - 1) == !X.empty()); + CHECK(X.grown(1).contains(X.upper()) == !X.empty()); + CHECK(isdisjoint(X, X) == X.empty()); // a <= b means "a implies b" for booleans (yes, it looks wrong) - REQUIRE((X < Y) <= (X <= Y)); - REQUIRE((X > Y) <= (X >= Y)); - REQUIRE((X <= Y) <= (X.empty() || !isdisjoint(X, Y))); - REQUIRE((X >= Y) <= (Y.empty() || !isdisjoint(X, Y))); - REQUIRE(!(X < Y && Y < X)); - REQUIRE((X <= Y && X >= Y) == (X == Y)); - REQUIRE((X < X.grown(1)) == (D > 0 && !X.empty())); - REQUIRE((X.shrunk(1) < X) == (D > 0 && !X.empty())); - - REQUIRE(N <= N); - REQUIRE(!(N < N)); - REQUIRE(N <= X); - REQUIRE((N < X) == !X.empty()); + CHECK((X < Y) <= (X <= Y)); + CHECK((X > Y) <= (X >= Y)); + CHECK((X <= Y) <= (X.empty() || !isdisjoint(X, Y))); + CHECK((X >= Y) <= (Y.empty() || !isdisjoint(X, Y))); + CHECK(!(X < Y && Y < X)); + CHECK((X <= Y && X >= Y) == (X == Y)); + CHECK((X < X.grown(1)) == (D > 0 && !X.empty())); + CHECK((X.shrunk(1) < X) == (D > 0 && !X.empty())); + + CHECK(N <= N); + CHECK(!(N < N)); + CHECK(N <= X); + CHECK((N < X) == !X.empty()); const auto BXY = bounding_box(X, Y); - REQUIRE(bounding_box(N, X) == X); - REQUIRE(bounding_box(X, N) == X); - REQUIRE(bounding_box(X, Y) == bounding_box(Y, X)); - REQUIRE(bounding_box(bounding_box(X, Y), Z) == - bounding_box(X, bounding_box(Y, Z))); - - REQUIRE(X <= BXY); - REQUIRE(Y <= BXY); - REQUIRE((X.grown(1) <= BXY && Y.grown(1) <= BXY) == - (D == 0 || BXY.empty())); - REQUIRE( + CHECK(bounding_box(N, X) == X); + CHECK(bounding_box(X, N) == X); + CHECK(bounding_box(X, Y) == bounding_box(Y, X)); + CHECK(bounding_box(bounding_box(X, Y), Z) == + bounding_box(X, bounding_box(Y, Z))); + + CHECK(X <= BXY); + CHECK(Y <= BXY); + CHECK((X.grown(1) <= BXY && Y.grown(1) <= BXY) == (D == 0 || BXY.empty())); + CHECK( eqb(bounding_box(X.grown(abs(x)), Y.grown(abs(x))), BXY.grown(abs(x)))); - REQUIRE(eqb(bounding_box(X >> x, Y >> x), BXY >> x)); - REQUIRE(eqb(bounding_box(X * x, Y * x), BXY * x)); - REQUIRE( + CHECK(eqb(bounding_box(X >> x, Y >> x), BXY >> x)); + CHECK(eqb(bounding_box(X * x, Y * x), BXY * x)); + CHECK( eqb(bounding_box(X.grown(abs(x)), Y.grown(abs(x))), BXY.grown(abs(x)))); const B E = bounding_box(bounding_box(X, Y), Z).grown(10); - REQUIRE((N & X) == N); - REQUIRE((X & N) == N); - REQUIRE((E & X) == X); - REQUIRE((X & E) == X); - REQUIRE((X & Y) == (Y & X)); - REQUIRE(((X & Y) & Z) == (X & (Y & Z))); + CHECK((N & X) == N); + CHECK((X & N) == N); + CHECK((E & X) == X); + CHECK((X & E) == X); + CHECK((X & Y) == (Y & X)); + CHECK(((X & Y) & Z) == (X & (Y & Z))); - REQUIRE((N | X) == X); - REQUIRE((E | X) == E); - REQUIRE((X | E) == E); - // REQUIRE((X | Y) == (Y | X)); - // REQUIRE(((X | Y) | Z) == (X | (Y | Z))); + CHECK((N | X) == X); + CHECK((E | X) == E); + CHECK((X | E) == E); + // CHECK((X | Y) == (Y | X)); + // CHECK(((X | Y) | Z) == (X | (Y | Z))); - // REQUIRE(E - (X & Y) == ((E - X) | (E - Y))); - // REQUIRE(E - (X | Y) == ((E - X) & (E - Y))); + // CHECK(E - (X & Y) == ((E - X) | (E - Y))); + // CHECK(E - (X | Y) == ((E - X) & (E - Y))); const B IXY = X & Y; - REQUIRE((IXY <= X && IXY <= Y) == true); - REQUIRE((IXY.grown(1) <= X && IXY.grown(1) <= Y) == - (D == 0 || IXY.empty())); + CHECK((IXY <= X && IXY <= Y) == true); + CHECK((IXY.grown(1) <= X && IXY.grown(1) <= Y) == (D == 0 || IXY.empty())); const std::vector UXY = X | Y; for (const auto &U : UXY) - REQUIRE((U <= X || U <= Y) == true); + CHECK((U <= X || U <= Y) == true); const std::vector DXY = X - Y; for (const auto &DD : DXY) - REQUIRE((DD <= X || !isdisjoint(DD, Y)) == true); + CHECK((DD <= X || !isdisjoint(DD, Y)) == true); const std::vector SXY = X ^ Y; for (const auto &S : SXY) - REQUIRE(((S <= X || S <= Y) && isdisjoint(S, IXY)) == true); + CHECK(((S <= X || S <= Y) && isdisjoint(S, IXY)) == true); - // REQUIRE(IXY <= UXY); - // REQUIRE(isdisjoint(IXY, SXY)); - // REQUIRE((IXY | SXY) == UXY); + // CHECK(IXY <= UXY); + // CHECK(isdisjoint(IXY, SXY)); + // CHECK((IXY | SXY) == UXY); } // for iter } diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 0ee271e374..30827db80b 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -56,121 +56,121 @@ template void test_Point_bool(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n = p; - REQUIRE(n.ndims() == D); + CHECK(n.ndims() == D); for (std::size_t d = 0; d < D; ++d) - REQUIRE(n[d] == 0); - REQUIRE(n.size() == D); + CHECK(n[d] == 0); + CHECK(n.size() == D); const P x = randp(); const P y = randp(); const P z = randp(); - REQUIRE(eq(n, n)); - REQUIRE(eq(x, x)); - REQUIRE(!lt(n, n)); - REQUIRE(!lt(x, x)); + CHECK(eq(n, n)); + CHECK(eq(x, x)); + CHECK(!lt(n, n)); + CHECK(!lt(x, x)); if (all(x == n)) { - REQUIRE(eq(n, x)); - REQUIRE(!lt(n, x)); + CHECK(eq(n, x)); + CHECK(!lt(n, x)); } else { - REQUIRE(!eq(n, x)); - REQUIRE(lt(n, x)); + CHECK(!eq(n, x)); + CHECK(lt(n, x)); } - REQUIRE(!any(n)); - REQUIRE(all(!n)); + CHECK(!any(n)); + CHECK(all(!n)); - REQUIRE(eq(n & x, n)); - REQUIRE(eq(0 & x, n)); - REQUIRE(eq(x & n, n)); - REQUIRE(eq(x & 0, n)); + CHECK(eq(n & x, n)); + CHECK(eq(0 & x, n)); + CHECK(eq(x & n, n)); + CHECK(eq(x & 0, n)); - REQUIRE(eq((!n & x), x)); - REQUIRE(eq((T(1) & x), x)); - REQUIRE(eq((x & !n), x)); - REQUIRE(eq((x & T(1)), x)); + CHECK(eq((!n & x), x)); + CHECK(eq((T(1) & x), x)); + CHECK(eq((x & !n), x)); + CHECK(eq((x & T(1)), x)); - REQUIRE(eq((n | x), x)); - REQUIRE(eq((0 | x), x)); - REQUIRE(eq((x | n), x)); - REQUIRE(eq((x | 0), x)); + CHECK(eq((n | x), x)); + CHECK(eq((0 | x), x)); + CHECK(eq((x | n), x)); + CHECK(eq((x | 0), x)); - REQUIRE(eq((!n | x), !n)); - REQUIRE(eq((T(1) | x), !n)); - REQUIRE(eq((x | !n), !n)); - REQUIRE(eq((x | T(1)), !n)); + CHECK(eq((!n | x), !n)); + CHECK(eq((T(1) | x), !n)); + CHECK(eq((x | !n), !n)); + CHECK(eq((x | T(1)), !n)); - REQUIRE(eq((x & y), (y & x))); - REQUIRE(eq((x | y), (y | x))); + CHECK(eq((x & y), (y & x))); + CHECK(eq((x | y), (y | x))); - REQUIRE(eq(((x & y) & z), (x & (y & z)))); - REQUIRE(eq(((x | y) | z), (x | (y | z)))); + CHECK(eq(((x & y) & z), (x & (y & z)))); + CHECK(eq(((x | y) | z), (x | (y | z)))); - REQUIRE(eq((x & (y | z)), ((y & x) | (x & z)))); - REQUIRE(eq((x | (y & z)), ((y | x) & (x | z)))); + CHECK(eq((x & (y | z)), ((y & x) | (x & z)))); + CHECK(eq((x | (y & z)), ((y | x) & (x | z)))); - REQUIRE(eq((x & y), !(!x | !y))); - REQUIRE(eq((x | y), !(!x & !y))); + CHECK(eq((x & y), !(!x | !y))); + CHECK(eq((x | y), !(!x & !y))); - REQUIRE(eq((n ^ x), x)); - REQUIRE(eq((0 ^ x), x)); - REQUIRE(eq((x ^ n), x)); - REQUIRE(eq((x ^ 0), x)); + CHECK(eq((n ^ x), x)); + CHECK(eq((0 ^ x), x)); + CHECK(eq((x ^ n), x)); + CHECK(eq((x ^ 0), x)); - REQUIRE(eq((!n ^ x), !x)); - REQUIRE(eq((T(1) ^ x), !x)); - REQUIRE(eq((x ^ !n), !x)); - REQUIRE(eq((x ^ T(1)), !x)); + CHECK(eq((!n ^ x), !x)); + CHECK(eq((T(1) ^ x), !x)); + CHECK(eq((x ^ !n), !x)); + CHECK(eq((x ^ T(1)), !x)); - REQUIRE(eq((x ^ x), n)); + CHECK(eq((x ^ x), n)); - REQUIRE(eq((x ^ y), (y ^ x))); - REQUIRE(eq(((x ^ y) ^ z), (x ^ (y ^ z)))); + CHECK(eq((x ^ y), (y ^ x))); + CHECK(eq(((x ^ y) ^ z), (x ^ (y ^ z)))); - REQUIRE(eq(!(!x), x)); + CHECK(eq(!(!x), x)); - REQUIRE(eq((n && x), n)); - REQUIRE(eq((0 && x), n)); - REQUIRE(eq((x && n), n)); - REQUIRE(eq((x && 0), n)); + CHECK(eq((n && x), n)); + CHECK(eq((0 && x), n)); + CHECK(eq((x && n), n)); + CHECK(eq((x && 0), n)); - REQUIRE(eq((!n && x), x)); - REQUIRE(eq((!T(0) && x), x)); - REQUIRE(eq((x && !n), x)); - REQUIRE(eq((x && !T(0)), x)); + CHECK(eq((!n && x), x)); + CHECK(eq((!T(0) && x), x)); + CHECK(eq((x && !n), x)); + CHECK(eq((x && !T(0)), x)); - REQUIRE(eq((n || x), x)); - REQUIRE(eq((0 || x), x)); - REQUIRE(eq((x || n), x)); - REQUIRE(eq((x || 0), x)); + CHECK(eq((n || x), x)); + CHECK(eq((0 || x), x)); + CHECK(eq((x || n), x)); + CHECK(eq((x || 0), x)); - REQUIRE(eq((!n || x), !n)); - REQUIRE(eq((!T(0) || x), !n)); - REQUIRE(eq((x || !n), !n)); - REQUIRE(eq((x || !T(0)), !n)); + CHECK(eq((!n || x), !n)); + CHECK(eq((!T(0) || x), !n)); + CHECK(eq((x || !n), !n)); + CHECK(eq((x || !T(0)), !n)); - REQUIRE(eq((x && y), (y && x))); - REQUIRE(eq((x || y), (y || x))); + CHECK(eq((x && y), (y && x))); + CHECK(eq((x || y), (y || x))); - REQUIRE(eq(((x && y) && z), (x && (y && z)))); - REQUIRE(eq(((x || y) || z), (x || (y || z)))); + CHECK(eq(((x && y) && z), (x && (y && z)))); + CHECK(eq(((x || y) || z), (x || (y || z)))); - REQUIRE(eq((x && (y || z)), ((y && x) || (x && z)))); - REQUIRE(eq((x || (y && z)), ((y || x) && (x || z)))); + CHECK(eq((x && (y || z)), ((y && x) || (x && z)))); + CHECK(eq((x || (y && z)), ((y || x) && (x || z)))); - REQUIRE(eq((x && y), !(!x || !y))); - REQUIRE(eq((x || y), !(!x && !y))); + CHECK(eq((x && y), !(!x || !y))); + CHECK(eq((x || y), !(!x && !y))); P t; t = x; t &= y; - REQUIRE(eq(t, (x & y))); + CHECK(eq(t, (x & y))); t = x; t |= y; - REQUIRE(eq(t, (x | y))); + CHECK(eq(t, (x | y))); t = x; t ^= y; - REQUIRE(eq(t, (x ^ y))); + CHECK(eq(t, (x ^ y))); } // for iter } @@ -188,9 +188,9 @@ template void test_Point_int(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n(p); - REQUIRE(n.size() == D); + CHECK(n.size() == D); for (std::size_t d = 0; d < D; ++d) - REQUIRE(n[d] == 0); + CHECK(n[d] == 0); const P x = randp(); const P y = randp(); @@ -199,139 +199,138 @@ template void test_Point_int(const P &p) { const T a = rand(); const T b = rand(); - REQUIRE(eq(fmap([](auto i) { return i; }, x), x)); - REQUIRE(eq(fmap([](auto i) { return i + 1; }, - fmap([](auto i) { return 2 * i; }, x)), - fmap([](auto i) { return 2 * i + 1; }, x))); + CHECK(eq(fmap([](auto i) { return i; }, x), x)); + CHECK(eq(fmap([](auto i) { return i + 1; }, + fmap([](auto i) { return 2 * i; }, x)), + fmap([](auto i) { return 2 * i + 1; }, x))); - REQUIRE( - eq(fmap([](auto i, auto j) { return 2 * i + j; }, x, y), 2 * x + y)); - REQUIRE(eq( + CHECK(eq(fmap([](auto i, auto j) { return 2 * i + j; }, x, y), 2 * x + y)); + CHECK(eq( fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, z), 3 * x + 2 * y + z)); - REQUIRE(fold([](auto i, auto j) { return i + j; }, 0, x) == sum(x)); - REQUIRE(fold([](auto i, auto j, auto k) { return i + j + k; }, 0, x, y) == - sum(x + y)); + CHECK(fold([](auto i, auto j) { return i + j; }, 0, x) == sum(x)); + CHECK(fold([](auto i, auto j, auto k) { return i + j + k; }, 0, x, y) == + sum(x + y)); - REQUIRE(sum(n) == 0); - REQUIRE(sum(n + 1) == std::ptrdiff_t(D)); - REQUIRE(product(n) == (D == 0 ? 1 : 0)); - REQUIRE(product(n + 1) == 1); - REQUIRE(min_element(n) == (D == 0 ? std::numeric_limits::max() : 0)); - REQUIRE(max_element(n) == (D == 0 ? std::numeric_limits::min() : 0)); - REQUIRE(min_element(n + 1) == (D == 0 ? std::numeric_limits::max() : 1)); - REQUIRE(max_element(n + 1) == (D == 0 ? std::numeric_limits::min() : 1)); + CHECK(sum(n) == 0); + CHECK(sum(n + 1) == std::ptrdiff_t(D)); + CHECK(product(n) == (D == 0 ? 1 : 0)); + CHECK(product(n + 1) == 1); + CHECK(min_element(n) == (D == 0 ? std::numeric_limits::max() : 0)); + CHECK(max_element(n) == (D == 0 ? std::numeric_limits::min() : 0)); + CHECK(min_element(n + 1) == (D == 0 ? std::numeric_limits::max() : 1)); + CHECK(max_element(n + 1) == (D == 0 ? std::numeric_limits::min() : 1)); - REQUIRE(eq(+x, x)); - REQUIRE(eq(n + x, x)); - REQUIRE(eq(T(0) + x, x)); - REQUIRE(eq(x + n, x)); - REQUIRE(eq(x + T(0), x)); + CHECK(eq(+x, x)); + CHECK(eq(n + x, x)); + CHECK(eq(T(0) + x, x)); + CHECK(eq(x + n, x)); + CHECK(eq(x + T(0), x)); - REQUIRE(eq(x + y, y + x)); + CHECK(eq(x + y, y + x)); - REQUIRE(eq((x + y) + z, x + (y + z))); + CHECK(eq((x + y) + z, x + (y + z))); - REQUIRE(eq(-x, -T(1) * x)); - REQUIRE(eq(-(-x), x)); - REQUIRE(eq(x - x, n)); + CHECK(eq(-x, -T(1) * x)); + CHECK(eq(-(-x), x)); + CHECK(eq(x - x, n)); - REQUIRE(eq(a * n, n)); - REQUIRE(eq(n * a, n)); - REQUIRE(eq(T(0) * x, n)); - REQUIRE(eq(x * T(0), n)); - REQUIRE(eq(T(1) * x, x)); - REQUIRE(eq(x * T(1), x)); + CHECK(eq(a * n, n)); + CHECK(eq(n * a, n)); + CHECK(eq(T(0) * x, n)); + CHECK(eq(x * T(0), n)); + CHECK(eq(T(1) * x, x)); + CHECK(eq(x * T(1), x)); - REQUIRE(eq(a * x, x * a)); + CHECK(eq(a * x, x * a)); - REQUIRE(eq(a * x + b * x, (a + b) * x)); - REQUIRE(eq(a * (x + y), a * x + a * y)); + CHECK(eq(a * x + b * x, (a + b) * x)); + CHECK(eq(a * (x + y), a * x + a * y)); - REQUIRE(eq(x * (y + z), x * y + x * z)); + CHECK(eq(x * (y + z), x * y + x * z)); if (all(y != 0)) { - REQUIRE(eq(x * y / y, x)); - REQUIRE(eq(x / y * y + x % y, x)); + CHECK(eq(x * y / y, x)); + CHECK(eq(x / y * y + x % y, x)); } - REQUIRE(eq(~(~x), x)); + CHECK(eq(~(~x), x)); - REQUIRE(eq((n & x), n)); - REQUIRE(eq((0 & x), n)); - REQUIRE(eq((x & n), n)); - REQUIRE(eq((x & 0), n)); + CHECK(eq((n & x), n)); + CHECK(eq((0 & x), n)); + CHECK(eq((x & n), n)); + CHECK(eq((x & 0), n)); - REQUIRE(eq((~n & x), x)); - REQUIRE(eq((~T(0) & x), x)); - REQUIRE(eq((x & ~n), x)); - REQUIRE(eq((x & ~T(0)), x)); + CHECK(eq((~n & x), x)); + CHECK(eq((~T(0) & x), x)); + CHECK(eq((x & ~n), x)); + CHECK(eq((x & ~T(0)), x)); - REQUIRE(eq((n | x), x)); - REQUIRE(eq((0 | x), x)); - REQUIRE(eq((x | n), x)); - REQUIRE(eq((x | 0), x)); + CHECK(eq((n | x), x)); + CHECK(eq((0 | x), x)); + CHECK(eq((x | n), x)); + CHECK(eq((x | 0), x)); - REQUIRE(eq((~n | x), ~n)); - REQUIRE(eq((~T(0) | x), ~n)); - REQUIRE(eq((x | ~n), ~n)); - REQUIRE(eq((x | ~T(0)), ~n)); + CHECK(eq((~n | x), ~n)); + CHECK(eq((~T(0) | x), ~n)); + CHECK(eq((x | ~n), ~n)); + CHECK(eq((x | ~T(0)), ~n)); - REQUIRE(eq((x & y), (y & x))); - REQUIRE(eq((x | y), (y | x))); + CHECK(eq((x & y), (y & x))); + CHECK(eq((x | y), (y | x))); - REQUIRE(eq(((x & y) & z), (x & (y & z)))); - REQUIRE(eq(((x | y) | z), (x | (y | z)))); + CHECK(eq(((x & y) & z), (x & (y & z)))); + CHECK(eq(((x | y) | z), (x | (y | z)))); - REQUIRE(eq((x & (y | z)), ((y & x) | (x & z)))); - REQUIRE(eq((x | (y & z)), ((y | x) & (x | z)))); + CHECK(eq((x & (y | z)), ((y & x) | (x & z)))); + CHECK(eq((x | (y & z)), ((y | x) & (x | z)))); - REQUIRE(eq((x & y), ~(~x | ~y))); - REQUIRE(eq((x | y), ~(~x & ~y))); + CHECK(eq((x & y), ~(~x | ~y))); + CHECK(eq((x | y), ~(~x & ~y))); - REQUIRE(eq((n ^ x), x)); - REQUIRE(eq((0 ^ x), x)); - REQUIRE(eq((x ^ n), x)); - REQUIRE(eq((x ^ 0), x)); + CHECK(eq((n ^ x), x)); + CHECK(eq((0 ^ x), x)); + CHECK(eq((x ^ n), x)); + CHECK(eq((x ^ 0), x)); - REQUIRE(eq((~n ^ x), ~x)); - REQUIRE(eq((~T(0) ^ x), ~x)); - REQUIRE(eq((x ^ ~n), ~x)); - REQUIRE(eq((x ^ ~T(0)), ~x)); + CHECK(eq((~n ^ x), ~x)); + CHECK(eq((~T(0) ^ x), ~x)); + CHECK(eq((x ^ ~n), ~x)); + CHECK(eq((x ^ ~T(0)), ~x)); - REQUIRE(eq((x ^ x), n)); + CHECK(eq((x ^ x), n)); - REQUIRE(eq((x ^ y), (y ^ x))); - REQUIRE(eq(((x ^ y) ^ z), (x ^ (y ^ z)))); + CHECK(eq((x ^ y), (y ^ x))); + CHECK(eq(((x ^ y) ^ z), (x ^ (y ^ z)))); P t; t = x; t += y; - REQUIRE(eq(t, x + y)); + CHECK(eq(t, x + y)); t = x; t -= y; - REQUIRE(eq(t, x - y)); + CHECK(eq(t, x - y)); t = x; t *= y; - REQUIRE(eq(t, x * y)); + CHECK(eq(t, x * y)); if (all(y != 0)) { t = x; t /= y; - REQUIRE(eq(t, x / y)); + CHECK(eq(t, x / y)); t = x; t %= y; - REQUIRE(eq(t, x % y)); + CHECK(eq(t, x % y)); } t = x; t &= y; - REQUIRE(eq(t, (x & y))); + CHECK(eq(t, (x & y))); t = x; t |= y; - REQUIRE(eq(t, (x | y))); + CHECK(eq(t, (x | y))); t = x; t ^= y; - REQUIRE(eq(t, (x ^ y))); + CHECK(eq(t, (x ^ y))); } // for iter } @@ -350,9 +349,9 @@ template void test_Point_float(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n(p); - REQUIRE(n.size() == D); + CHECK(n.size() == D); for (std::size_t d = 0; d < D; ++d) - REQUIRE(n[d] == 0); + CHECK(n[d] == 0); const P x = randp(); const P y = randp(); @@ -361,119 +360,116 @@ template void test_Point_float(const P &p) { const T a = rand(); const T b = rand(); - REQUIRE(eq(x, x)); - REQUIRE(!lt(x, x)); + CHECK(eq(x, x)); + CHECK(!lt(x, x)); if (eq(x, y)) - REQUIRE(lt(x, y) + lt(y, x) == 0); + CHECK(lt(x, y) + lt(y, x) == 0); else - REQUIRE(lt(x, y) + lt(y, x) == 1); + CHECK(lt(x, y) + lt(y, x) == 1); if (lt(x, y) && lt(y, z)) - REQUIRE(lt(x, z)); + CHECK(lt(x, z)); if (!lt(y, x) && !lt(z, y)) - REQUIRE(!lt(z, x)); + CHECK(!lt(z, x)); // remove-insert is no-op if (D > 0) { for (std::size_t d = 0; d < D; ++d) { const auto a1 = x[d]; const auto x1 = x.erase(d); - REQUIRE(x1.ndims() == D - 1); + CHECK(x1.ndims() == D - 1); const auto x2 = x1.insert(d, a1); - REQUIRE(x2.ndims() == D); - REQUIRE(eq_helper(x2, x)); + CHECK(x2.ndims() == D); + CHECK(eq_helper(x2, x)); } } // insert-remove is no-op for (std::size_t d = 0; d <= D; ++d) { const auto x1 = x.insert(d, a); - REQUIRE(x1.ndims() == D + 1); - REQUIRE(x1[d] == a); - REQUIRE(eq(x1.erase(d), x)); + CHECK(x1.ndims() == D + 1); + CHECK(x1[d] == a); + CHECK(eq(x1.erase(d), x)); } - REQUIRE(eq(x.reversed().reversed(), x)); + CHECK(eq(x.reversed().reversed(), x)); - REQUIRE(eq(fmap([](auto i) { return i; }, x), x)); - REQUIRE(eq(fmap([](auto i) { return i + 1; }, - fmap([](auto i) { return 2 * i; }, x)), - fmap([](auto i) { return 2 * i + 1; }, x))); + CHECK(eq(fmap([](auto i) { return i; }, x), x)); + CHECK(eq(fmap([](auto i) { return i + 1; }, + fmap([](auto i) { return 2 * i; }, x)), + fmap([](auto i) { return 2 * i + 1; }, x))); - REQUIRE( - eq(fmap([](auto i, auto j) { return 2 * i + j; }, x, y), 2 * x + y)); - REQUIRE(eq( + CHECK(eq(fmap([](auto i, auto j) { return 2 * i + j; }, x, y), 2 * x + y)); + CHECK(eq( fmap([](auto i, auto j, auto k) { return 3 * i + 2 * j + k; }, x, y, z), 3 * x + 2 * y + z)); - REQUIRE(fold([](auto i, auto j) { return i + j; }, T(0), x) == sum(x)); - REQUIRE(is_approx( + CHECK(fold([](auto i, auto j) { return i + j; }, T(0), x) == sum(x)); + CHECK(is_approx( fold([](auto i, auto j, auto k) { return i + j + k; }, T(0), x, y), sum(x + y))); - REQUIRE(sum(n) == 0); - REQUIRE(sum(n + 1) == D); - REQUIRE(product(n) == (D == 0 ? 1 : 0)); - REQUIRE(product(n + 1) == 1); - // We need to allow `inf == inf`, and REQUIRE's standard - // comparison wouldn't, so we compare `== true` instead - REQUIRE((min_element(n) == (D == 0 ? T(1) / 0 : 0)) == true); - REQUIRE((max_element(n) == (D == 0 ? -T(1) / 0 : 0)) == true); - REQUIRE((min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)) == true); - REQUIRE((max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)) == true); + CHECK(sum(n) == 0); + CHECK(sum(n + 1) == D); + CHECK(product(n) == (D == 0 ? 1 : 0)); + CHECK(product(n + 1) == 1); + CHECK(min_element(n) == (D == 0 ? T(1) / 0 : 0)); + CHECK(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); + CHECK(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); + CHECK(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); - REQUIRE(eq(+x, x)); - REQUIRE(eq(n + x, x)); - REQUIRE(eq(T(0) + x, x)); - REQUIRE(eq(x + n, x)); - REQUIRE(eq(x + T(0), x)); + CHECK(eq(+x, x)); + CHECK(eq(n + x, x)); + CHECK(eq(T(0) + x, x)); + CHECK(eq(x + n, x)); + CHECK(eq(x + T(0), x)); - REQUIRE(eq(x + y, y + x)); + CHECK(eq(x + y, y + x)); - REQUIRE(is_approx((x + y) + z, x + (y + z))); + CHECK(is_approx((x + y) + z, x + (y + z))); - REQUIRE(eq(-x, -T(1) * x)); - REQUIRE(eq(-(-x), x)); - REQUIRE(eq(x - x, n)); + CHECK(eq(-x, -T(1) * x)); + CHECK(eq(-(-x), x)); + CHECK(eq(x - x, n)); - REQUIRE(eq(a * n, n)); - REQUIRE(eq(n * a, n)); - REQUIRE(eq(T(0) * x, n)); - REQUIRE(eq(x * T(0), n)); - REQUIRE(eq(T(1) * x, x)); - REQUIRE(eq(x * T(1), x)); + CHECK(eq(a * n, n)); + CHECK(eq(n * a, n)); + CHECK(eq(T(0) * x, n)); + CHECK(eq(x * T(0), n)); + CHECK(eq(T(1) * x, x)); + CHECK(eq(x * T(1), x)); - REQUIRE(eq(a * x, x * a)); + CHECK(eq(a * x, x * a)); if (all(x != 0)) { - REQUIRE(eq(x / x, n + 1)); - REQUIRE(is_approx(1 / (1 / x), x)); - REQUIRE(is_approx(a / x, a * (1 / x))); + CHECK(eq(x / x, n + 1)); + CHECK(is_approx(1 / (1 / x), x)); + CHECK(is_approx(a / x, a * (1 / x))); } if (a != 0) { - REQUIRE(is_approx(x / a, x * (1 / a))); + CHECK(is_approx(x / a, x * (1 / a))); } - REQUIRE(is_approx(a * x + b * x, (a + b) * x)); - REQUIRE(is_approx(a * (x + y), a * x + a * y)); + CHECK(is_approx(a * x + b * x, (a + b) * x)); + CHECK(is_approx(a * (x + y), a * x + a * y)); - REQUIRE(is_approx(x * (y + z), x * y + x * z)); + CHECK(is_approx(x * (y + z), x * y + x * z)); if (all(y != 0)) { - REQUIRE(is_approx(x * y / y, x)); + CHECK(is_approx(x * y / y, x)); } P t; t = x; t += y; - REQUIRE(eq(t, x + y)); + CHECK(eq(t, x + y)); t = x; t -= y; - REQUIRE(eq(t, x - y)); + CHECK(eq(t, x - y)); t = x; t *= y; - REQUIRE(eq(t, x * y)); + CHECK(eq(t, x * y)); t = x; t /= y; - REQUIRE(eq(t, x / y)); + CHECK(eq(t, x / y)); } // for iter } diff --git a/test/RegionsRegionTest.cpp b/test/RegionsRegionTest.cpp index 43c7fb7ff7..a6c4fd9766 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionsRegionTest.cpp @@ -20,8 +20,8 @@ template void test_Region(const R &r) { using P = std::decay_t; const std::equal_to eqr; const std::less ltr; - REQUIRE(r.empty()); - REQUIRE(b.empty()); + CHECK(r.empty()); + CHECK(b.empty()); std::mt19937 gen; std::uniform_int_distribution dist0(0, 9); @@ -62,7 +62,7 @@ template void test_Region(const R &r) { for (int iter = 0; iter < 100; ++iter) { R N = r; - REQUIRE(N.empty()); + CHECK(N.empty()); R X = randr(); R Y = randr(); R Z = randr(); @@ -71,89 +71,88 @@ template void test_Region(const R &r) { P x = randp(); P y = randp(); - REQUIRE(eqr(X, X)); - REQUIRE(!ltr(X, X)); + CHECK(eqr(X, X)); + CHECK(!ltr(X, X)); if (eqr(X, Y)) - REQUIRE(ltr(X, Y) + ltr(Y, X) == 0); + CHECK(ltr(X, Y) + ltr(Y, X) == 0); else - REQUIRE(ltr(X, Y) + ltr(Y, X) == 1); + CHECK(ltr(X, Y) + ltr(Y, X) == 1); if (ltr(X, Y) && ltr(Y, Z)) - REQUIRE(ltr(X, Z)); + CHECK(ltr(X, Z)); if (!ltr(Y, X) && !ltr(Z, Y)) - REQUIRE(!ltr(Z, X)); + CHECK(!ltr(Z, X)); - REQUIRE(N == N); - REQUIRE(X == X); - REQUIRE((N != X) != X.empty()); + CHECK(N == N); + CHECK(X == X); + CHECK((N != X) != X.empty()); - REQUIRE((X >> n) == X); - REQUIRE((X >> x) == (X << -x)); - REQUIRE(((X >> x) << x) == X); - REQUIRE(((X >> x) >> y) == (X >> (x + y))); + CHECK((X >> n) == X); + CHECK((X >> x) == (X << -x)); + CHECK(((X >> x) << x) == X); + CHECK(((X >> x) >> y) == (X >> (x + y))); - REQUIRE((X * abs(x)) * abs(y) == X * (abs(x) * abs(y))); - REQUIRE((X >> x) * y == (X * y) >> (x * y)); + CHECK((X * abs(x)) * abs(y) == X * (abs(x) * abs(y))); + CHECK((X >> x) * y == (X * y) >> (x * y)); if (X.empty()) { - REQUIRE(X.grown(abs(x)).empty()); - REQUIRE(X.shrunk(abs(x)).empty()); + CHECK(X.grown(abs(x)).empty()); + CHECK(X.shrunk(abs(x)).empty()); } else if (all(abs(x) == n)) { - REQUIRE(X.grown(abs(x)) == X); - REQUIRE(X.shrunk(abs(x)) == X); + CHECK(X.grown(abs(x)) == X); + CHECK(X.shrunk(abs(x)) == X); } else { - REQUIRE(X.grown(abs(x)) > X); - REQUIRE(X.shrunk(abs(x)) < X); + CHECK(X.grown(abs(x)) > X); + CHECK(X.shrunk(abs(x)) < X); } - REQUIRE(X.grown(abs(x)).shrunk(abs(x)) >= X); - REQUIRE(X.shrunk(abs(x)).grown(abs(x)) <= X); + CHECK(X.grown(abs(x)).shrunk(abs(x)) >= X); + CHECK(X.shrunk(abs(x)).grown(abs(x)) <= X); - REQUIRE((X.grown(x) >> y) == (X >> y).grown(x)); + CHECK((X.grown(x) >> y) == (X >> y).grown(x)); - REQUIRE((X * abs(y)).grown(abs(x) * abs(y)) == X.grown(abs(x)) * abs(y)); + CHECK((X * abs(y)).grown(abs(x) * abs(y)) == X.grown(abs(x)) * abs(y)); const B E = bounding_box(bounding_box(bounding_box(X), bounding_box(Y)), bounding_box(Z)) .grown(10); - REQUIRE((N & X) == N); - REQUIRE((X & N) == N); - REQUIRE((E & X) == X); - REQUIRE((X & E) == X); - REQUIRE((X & Y) == (Y & X)); - REQUIRE(((X & Y) & Z) == (X & (Y & Z))); + CHECK((N & X) == N); + CHECK((X & N) == N); + CHECK((E & X) == X); + CHECK((X & E) == X); + CHECK((X & Y) == (Y & X)); + CHECK(((X & Y) & Z) == (X & (Y & Z))); - REQUIRE((N | X) == X); - REQUIRE((E | X) == E); - REQUIRE((X | E) == E); - REQUIRE((X | Y) == (Y | X)); - REQUIRE(((X | Y) | Z) == (X | (Y | Z))); + CHECK((N | X) == X); + CHECK((E | X) == E); + CHECK((X | E) == E); + CHECK((X | Y) == (Y | X)); + CHECK(((X | Y) | Z) == (X | (Y | Z))); - REQUIRE(E - (X & Y) == ((E - X) | (E - Y))); - REQUIRE(E - (X | Y) == ((E - X) & (E - Y))); + CHECK(E - (X & Y) == ((E - X) | (E - Y))); + CHECK(E - (X | Y) == ((E - X) & (E - Y))); - REQUIRE((N ^ X) == X); - REQUIRE((X ^ N) == X); - REQUIRE((X ^ X) == N); - REQUIRE((X ^ Y) == (Y ^ X)); - REQUIRE(((X ^ Y) ^ Z) == (X ^ (Y ^ Z))); + CHECK((N ^ X) == X); + CHECK((X ^ N) == X); + CHECK((X ^ X) == N); + CHECK((X ^ Y) == (Y ^ X)); + CHECK(((X ^ Y) ^ Z) == (X ^ (Y ^ Z))); const R IXY = X & Y; - REQUIRE((IXY <= X && IXY <= Y) == true); - REQUIRE((IXY.grown(1) <= X && IXY.grown(1) <= Y) == - (D == 0 || IXY.empty())); + CHECK((IXY <= X && IXY <= Y) == true); + CHECK((IXY.grown(1) <= X && IXY.grown(1) <= Y) == (D == 0 || IXY.empty())); const R UXY = X | Y; - REQUIRE((X <= UXY && Y <= UXY) == true); + CHECK((X <= UXY && Y <= UXY) == true); const R DXY = X - Y; - REQUIRE((DXY <= X || !isdisjoint(DXY, Y)) == true); + CHECK((DXY <= X || !isdisjoint(DXY, Y)) == true); const R SXY = X ^ Y; - REQUIRE((SXY <= UXY && isdisjoint(SXY, IXY)) == true); + CHECK((SXY <= UXY && isdisjoint(SXY, IXY)) == true); - REQUIRE(IXY <= UXY); - REQUIRE((IXY | SXY) == UXY); + CHECK(IXY <= UXY); + CHECK((IXY | SXY) == UXY); } // for iter } From 1463411b04154697c3b4b02452b1c1d9108728f2 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 13:23:37 -0400 Subject: [PATCH 35/68] CI: Enable debug info --- .github/workflows/unix.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index ad97af746a..01ca27e5e2 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -25,7 +25,7 @@ jobs: mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_INVASIVE_TESTS=ON + cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 2> clang-tidy.log cat clang-tidy.log if [[ $(wc -m Date: Tue, 8 Jun 2021 13:23:49 -0400 Subject: [PATCH 36/68] Add debug statements --- test/RegionsPointTest.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 30827db80b..3c195d6b73 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -407,6 +407,18 @@ template void test_Point_float(const P &p) { fold([](auto i, auto j, auto k) { return i + j + k; }, T(0), x, y), sum(x + y))); + // Temporary tests + CHECK(0.0 == 0.0); + CHECK(1.0 == 1.0); + CHECK(1.0 / 0.0 == 1.0 / 0.0); + CHECK(-1.0 / 0.0 == -1.0 / 0.0); + CHECK(1.0 / 0.0 != -1.0 / 0.0); + CHECK(0.0 / 0.0 != 0.0 / 0.0); + CHECK(1.0 / 0.0 == std::numeric_limits::infinity()); + CHECK(-1.0 / 0.0 == -std::numeric_limits::infinity()); + CHECK(1.0 / 0.0 == std::numeric_limits::infinity()); + CHECK(-1.0 / 0.0 == -std::numeric_limits::infinity()); + CHECK(sum(n) == 0); CHECK(sum(n + 1) == D); CHECK(product(n) == (D == 0 ? 1 : 0)); From fadb37575742218c6d97c9c7af29f370e0b843aa Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 13:57:31 -0400 Subject: [PATCH 37/68] More debug tests --- test/RegionsPointTest.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 3c195d6b73..9699306749 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -419,14 +419,21 @@ template void test_Point_float(const P &p) { CHECK(1.0 / 0.0 == std::numeric_limits::infinity()); CHECK(-1.0 / 0.0 == -std::numeric_limits::infinity()); + CHECK(min_element(Point()) == (0 == 0 ? T(1) / 0 : 0)); + CHECK(min_element(Point()) == T(1) / 0); + CHECK(min_element(Point()) == T(1) / T(0)); + CHECK(min_element(Point()) == + std::numeric_limits::infinity()); + CHECK(std::numeric_limits::infinity() == (0 == 0 ? T(1) / 0 : 0)); + CHECK(sum(n) == 0); CHECK(sum(n + 1) == D); CHECK(product(n) == (D == 0 ? 1 : 0)); CHECK(product(n + 1) == 1); - CHECK(min_element(n) == (D == 0 ? T(1) / 0 : 0)); - CHECK(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); - CHECK(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); - CHECK(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); + // TODO CHECK(min_element(n) == (D == 0 ? T(1) / 0 : 0)); + // TODO CHECK(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); + // TODO CHECK(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); + // TODO CHECK(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); CHECK(eq(+x, x)); CHECK(eq(n + x, x)); From b145c024873e43cea84b118b3b19cb9f7000a1a4 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 15:05:58 -0400 Subject: [PATCH 38/68] Correct illegal memory access --- include/openPMD/regions/Region.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 34f4325960..67160f948f 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -773,15 +773,17 @@ template class Region { active1 = !lt(subbox1, subbox0); } - const T old_pos = iter0->second; if (active0 && active1 && eq(subbox0, subbox1)) { // The current bbox continues unchanged -- keep it + const T old_pos = iter0->second; subboxes[subbox1] = old_pos; } else { - if (active0) + if (active0) { // The current box changed; finalize it + const T old_pos = iter0->second; res.push_back(Box(subbox0.lower().insert(D - 1, old_pos), subbox0.upper().insert(D - 1, pos))); + } if (active1) // There is a new box; add it subboxes[subbox1] = pos; From 686d14d9e6f67314dcfef0e3ae293d349d00296f Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 15:06:16 -0400 Subject: [PATCH 39/68] Beautify output in example 13 --- examples/13_regions.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp index ae69275791..5773480964 100644 --- a/examples/13_regions.cpp +++ b/examples/13_regions.cpp @@ -10,24 +10,24 @@ int main() { Point x{1, 2}, y{4, 5}; auto z = x + y; cout << "Points:\n" - << "x: " << x << "\n" - << "y: " << y << "\n" - << "z: " << z << "\n"; + << " x: " << x << "\n" + << " y: " << y << "\n" + << " z: " << z << "\n"; // A box between these points Box b(x, y); auto bg = b.grown(1); cout << "Boxes are spanned between points (inclusive lower, exclusive upper " "bound):\n" - << "b: " << b << "\n" - << "bg: " << bg << "\n"; + << " b: " << b << "\n" + << " bg: " << bg << "\n"; // Regions Region r(b); auto g = Region(bg) - r; cout << "Regions are sets of non-overlapping boxes:\n" - << "r: " << r << "\n" - << "g: " << g << "\n"; + << " r: " << r << "\n" + << " g: " << g << "\n"; { NDPoint p0(0); From 2ac1e7fd4f1fc1d19bb1db4b92edb68122a3a451 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 15:06:47 -0400 Subject: [PATCH 40/68] New constructor NDPoint(Point) --- include/openPMD/regions/NDPoint.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 00d72f7da9..65bfcb2ef5 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -180,6 +180,9 @@ template class WPoint final : public VPoint { WPoint(const Point &p_) : p(p_) {} WPoint(Point &&p_) : p(std::move(p_)) {} + WPoint(const std::array &arr) : p(arr) {} + WPoint(std::array &&arr) : p(std::move(arr)) {} + std::unique_ptr> copy() const override { return std::make_unique(*this); } @@ -577,6 +580,19 @@ template class NDPoint { } NDPoint &operator=(NDPoint &&) = default; + template + NDPoint(const Point &p_) + : p(std::make_unique>(p_)) {} + template + NDPoint(Point &&p_) + : p(std::make_unique>(std::move(p_))) {} + template + NDPoint(const std::array &arr) + : p(std::make_unique>(arr)) {} + template + NDPoint(std::array &&arr) + : p(std::make_unique>(std::move(arr))) {} + private: template static std::vector convert_vector(const std::vector &vec) { From 3fb5920ad5c3392f790ca379e35aa98070a37d36 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 8 Jun 2021 15:22:06 -0400 Subject: [PATCH 41/68] Try to appease icpc --- test/RegionsPointTest.cpp | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/test/RegionsPointTest.cpp b/test/RegionsPointTest.cpp index 9699306749..567f683ead 100644 --- a/test/RegionsPointTest.cpp +++ b/test/RegionsPointTest.cpp @@ -407,33 +407,18 @@ template void test_Point_float(const P &p) { fold([](auto i, auto j, auto k) { return i + j + k; }, T(0), x, y), sum(x + y))); - // Temporary tests - CHECK(0.0 == 0.0); - CHECK(1.0 == 1.0); - CHECK(1.0 / 0.0 == 1.0 / 0.0); - CHECK(-1.0 / 0.0 == -1.0 / 0.0); - CHECK(1.0 / 0.0 != -1.0 / 0.0); - CHECK(0.0 / 0.0 != 0.0 / 0.0); - CHECK(1.0 / 0.0 == std::numeric_limits::infinity()); - CHECK(-1.0 / 0.0 == -std::numeric_limits::infinity()); - CHECK(1.0 / 0.0 == std::numeric_limits::infinity()); - CHECK(-1.0 / 0.0 == -std::numeric_limits::infinity()); - - CHECK(min_element(Point()) == (0 == 0 ? T(1) / 0 : 0)); - CHECK(min_element(Point()) == T(1) / 0); - CHECK(min_element(Point()) == T(1) / T(0)); - CHECK(min_element(Point()) == - std::numeric_limits::infinity()); - CHECK(std::numeric_limits::infinity() == (0 == 0 ? T(1) / 0 : 0)); - CHECK(sum(n) == 0); CHECK(sum(n + 1) == D); CHECK(product(n) == (D == 0 ? 1 : 0)); CHECK(product(n + 1) == 1); - // TODO CHECK(min_element(n) == (D == 0 ? T(1) / 0 : 0)); - // TODO CHECK(max_element(n) == (D == 0 ? -T(1) / 0 : 0)); - // TODO CHECK(min_element(n + 1) == (D == 0 ? T(1) / 0 : 1)); - // TODO CHECK(max_element(n + 1) == (D == 0 ? -T(1) / 0 : 1)); + const T inf_zero = D == 0 ? T(1) / 0 : 0; + const T neg_inf_zero = D == 0 ? -T(1) / 0 : 0; + const T inf_one = D == 0 ? T(1) / 0 : 1; + const T neg_inf_one = D == 0 ? -T(1) / 0 : 1; + CHECK(min_element(n) == inf_zero); + CHECK(max_element(n) == neg_inf_zero); + CHECK(min_element(n + 1) == inf_one); + CHECK(max_element(n + 1) == neg_inf_one); CHECK(eq(+x, x)); CHECK(eq(n + x, x)); From fb084cc988ddab00a252cad601ee3c81afa7fdd2 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 14 Jun 2021 16:57:44 -0400 Subject: [PATCH 42/68] Complete regions example --- examples/13_regions.cpp | 269 +++++++++++++++++++++++++--- include/openPMD/regions/NDPoint.hpp | 43 +++++ include/openPMD/regions/Region.hpp | 19 +- 3 files changed, 299 insertions(+), 32 deletions(-) diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp index 5773480964..684fc5b7c1 100644 --- a/examples/13_regions.cpp +++ b/examples/13_regions.cpp @@ -1,40 +1,261 @@ #include +#include +#include +#include #include +#include +#include -int main() { +//////////////////////////////////////////////////////////////////////////////// + +void point_example() { using namespace openPMD::Regions; using namespace std; - // Two points + cout << "\n" + << "Points (with dimension known at compile time):\n"; + + cout << " Define two points:\n"; Point x{1, 2}, y{4, 5}; - auto z = x + y; - cout << "Points:\n" - << " x: " << x << "\n" - << " y: " << y << "\n" - << " z: " << z << "\n"; + cout << " x: " << x << "\n" + << " y: " << y << "\n"; + + cout << " Arithmetic operations:\n"; + auto z = x + 2 * y; + cout << " z: " << z << "\n"; + + cout << " Unit vectors pointing in direction d:\n"; + auto u0 = Point::unit(0); + auto u1 = Point::unit(1); + cout << " u0: " << u0 << "\n" + << " u1: " << u1 << "\n"; + cout << " A points with all elements the same:\n"; + auto p3 = Point::pure(3); + cout << " p3: " << p3 << "\n"; + + cout << " Element-wise operations:\n"; + using std::abs, std::max; + auto mxy1 = max(abs(x), abs(y)); + // Apply arbitrary functions element-wise + auto mxy2 = fmap([](auto a, auto b) { return max(abs(a), abs(b)); }, x, y); + cout << " mxy1: " << mxy1 << "\n" + << " mxy2: " << mxy2 << "\n"; + + cout << " Reduction operations:\n"; + int mx1 = max_element(x); + // Apply arbitrary reduction operations + int mx2 = fold([](auto r, auto a) { return max(r, a); }, 0, x); + cout << " mx1: " << mx1 << "\n" + << " mx2: " << mx2 << "\n"; +} + +//////////////////////////////////////////////////////////////////////////////// + +void ndpoint_example() { + using namespace openPMD::Regions; + using namespace std; + + cout << "\n" + << "NDPoints (with dimension only known at run time):\n"; + + cout << " Define two points:\n"; + NDPoint x{1, 2}, y{4, 5}; + cout << " x: " << x << "\n" + << " y: " << y << "\n"; + + // Arithmetic operations + cout << " Arithmetic operations:\n"; + auto z = x + 2 * y; + cout << " z: " << z << "\n"; + + cout << " Unit vectors pointing in direction d:\n"; + auto u0 = NDPoint::unit(2, 0); + auto u1 = NDPoint::unit(2, 1); + cout << " u0: " << u0 << "\n" + << " u1: " << u1 << "\n"; + cout << " A points with all elements the same:\n"; + auto p3 = NDPoint::pure(2, 3); + cout << " p3: " << p3 << "\n"; + + cout << " Element-wise operations:\n"; + using std::abs, std::max; + auto mxy1 = max(abs(x), abs(y)); + // Apply arbitrary functions element-wise + auto mxy2 = fmap([](auto a, auto b) { return max(abs(a), abs(b)); }, x, y); + cout << " mxy1: " << mxy1 << "\n" + << " mxy2: " << mxy2 << "\n"; + + cout << " Reduction operations:\n"; + int mx1 = max_element(x); + // Apply arbitrary reduction operations + int mx2 = fold([](auto r, auto a) { return max(r, a); }, 0, x); + cout << " mx1: " << mx1 << "\n" + << " mx2: " << mx2 << "\n"; +} + +//////////////////////////////////////////////////////////////////////////////// + +void box_example() { + using namespace openPMD::Regions; + using namespace std; + + cout << "\n" + << "Boxes are spanned between points (inclusive lower, exclusive upper " + "bound):\n"; + + cout << " Define two points:\n"; + Point x{1, 4}, y{2, 5}; + cout << " x:" << x << "\n" + << " y:" << y << "\n"; - // A box between these points + cout << " Define a box between these points:\n"; Box b(x, y); + cout << " b: " << b << " b.empty: " << b.empty() << "\n"; + cout << " Define an empty box:\n"; + Box be; + cout << " be: " << be << " be.empty: " << be.empty() << "\n"; + + cout << " Boxes can be shifted and scaled (e.g. to change resolution):\n"; + Point offset{1, 2}; + auto b1 = b >> offset; + Point scale{2, 2}; + auto b2 = b * scale; + // Boxes can be grown and shrunk (e.g. to add ghost zones) auto bg = b.grown(1); - cout << "Boxes are spanned between points (inclusive lower, exclusive upper " - "bound):\n" - << " b: " << b << "\n" - << " bg: " << bg << "\n"; + auto bs = b.shrunk(1); + cout << " shifted box: " << b1 << "\n" + << " scaled box: " << b2 << "\n" + << " grown box: " << bg << "\n" + << " shrunk box: " << bs << "\n"; + + cout << " The bounding box containing two boxes:\n"; + auto bb = bounding_box(b, b1); + cout << " bounding box: " << bb << "\n"; + + cout << " Boxes can be intersected:\n"; + auto bi = b & b1; + cout << " intersection: " << bi << "\n"; + + cout << " Set tests:\n"; + cout << " b == b1 (equality): " << (b == b1) << "\n" + << " b <= b1 (is-subset-of): " << (b <= b1) << "\n" + << " b < b1 (is-strict-subset_of): " << (b < b1) << "\n"; +} + +//////////////////////////////////////////////////////////////////////////////// + +void region_example() { + using namespace openPMD::Regions; + using namespace std; + + cout << "\n" + << "Regions consists of a set of boxes:\n"; + + cout << " Define two points:\n"; + Point x{1, 4}, y{2, 5}; + cout << " x:" << x << "\n" + << " y:" << y << "\n"; + + cout << " Define a box between these points:\n"; + Box b(x, y); + cout << " b:" << b << "\n"; - // Regions + cout << " Define a region consisting of this box:\n"; Region r(b); - auto g = Region(bg) - r; - cout << "Regions are sets of non-overlapping boxes:\n" - << " r: " << r << "\n" - << " g: " << g << "\n"; - - { - NDPoint p0(0); - NDPoint p1(1); - NDPoint p2(2); - NDPoint p3(3); - } + cout << " r: " << r << " r.empty: " << r.empty() << "\n"; + cout << " Define an empty region:\n"; + Region re; + cout << " re: " << re << " re.empty: " << re.empty() << "\n"; + + cout << " Regions can be shifted and scaled (e.g. to change resolution):\n"; + Point offset{1, 2}; + auto r1 = r >> offset; + Point scale{2, 2}; + auto r2 = r * scale; + // Regions can be grown and shrunk (e.g. to add ghost zones) + auto rg = r.grown(1); + auto rs = r.shrunk(1); + cout << " shifted region: " << r1 << "\n" + << " scaled region: " << r2 << "\n" + << " grown region: " << rg << "\n" + << " shrunk region: " << rs << "\n"; + + cout << " The bounding box containing a region:\n"; + auto bb = bounding_box(r); + cout << " bounding box: " << bb << "\n"; + + cout << " Regions can be treated as sets:\n"; + auto ri = r & r1; + auto ru = r | r1; + auto rd = r - r1; + auto rx = r ^ r1; + cout << " intersection: " << ri << "\n"; + cout << " union: " << ru << "\n"; + cout << " difference: " << rd << "\n"; + cout << " symmetric difference: " << rx << "\n"; + + cout << " Set tests:\n"; + cout << " r == r1 (equality): " << (r == r1) << "\n" + << " r <= r1 (is-subset-of): " << (r <= r1) << "\n" + << " r < r1 (is-strict-subset_of): " << (r < r1) << "\n"; + + cout << " Regions can be converted to a list of boxes:\n"; + cout << " rg - r:\n"; + for (const auto &bx : std::vector>(rg - r)) + cout << " " << bx << "\n"; + +#if !REGIONS_DEBUG + // Don't benchmark the debug version; it uses an N^2 algorithm for self-checks + const int n = 1000; + cout << " Create a list of " << n + << " 3d boxes and convert it to a region:\n"; + std::mt19937 gen; + std::uniform_int_distribution dist(0, 100); + const auto random_point = [&]() { + return Point::make([&](auto) { return dist(gen); }); + }; + const auto random_box = [&]() { + auto p1 = random_point(); + auto p2 = random_point(); + // Sort the points to avoid creating many empty boxes + return Box(min(p1, p2), max(p1, p2)); + }; + using clock = std::chrono::high_resolution_clock; + using timestamp = std::chrono::time_point; + using second = std::chrono::duration>; + const auto gettime = []() { return clock::now(); }; + const auto elapsed = [](timestamp t0, timestamp t1) { + return std::chrono::duration_cast(t1 - t0).count(); + }; + std::vector> boxlist; + for (int i = 0; i < n; ++i) + boxlist.push_back(random_box()); + timestamp t0 = gettime(); + Region reg(boxlist); + timestamp t1 = gettime(); + cout << " reg.size: " << reg.size() << "\n" + << " reg.nboxes: " << reg.nboxes() << "\n" + << " runtime: " << elapsed(t0, t1) << " sec\n"; + cout << " Grow the region by 1 point:\n"; + timestamp t2 = gettime(); + Region reg1 = reg.grown(1); + timestamp t3 = gettime(); + cout << " reg.size: " << reg1.size() << "\n" + << " reg.nboxes: " << reg1.nboxes() << "\n" + << " runtime: " << elapsed(t2, t3) << " sec\n"; +#endif +} + +//////////////////////////////////////////////////////////////////////////////// + +int main() { + point_example(); + ndpoint_example(); + + box_example(); + + region_example(); return 0; } diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 65bfcb2ef5..fbe7beb5d4 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -179,9 +179,11 @@ template class WPoint final : public VPoint { WPoint(const Point &p_) : p(p_) {} WPoint(Point &&p_) : p(std::move(p_)) {} + operator Point() const { return p; } WPoint(const std::array &arr) : p(arr) {} WPoint(std::array &&arr) : p(std::move(arr)) {} + operator std::array() const { return p; } std::unique_ptr> copy() const override { return std::make_unique(*this); @@ -535,6 +537,26 @@ std::unique_ptr> make_VPoint(const std::size_t D) { } } +template +std::unique_ptr> make_VPoint(const std::size_t D, const F &f) { + switch (D) { + case 0: + return std::make_unique>(Point::make(f)); + case 1: + return std::make_unique>(Point::make(f)); + case 2: + return std::make_unique>(Point::make(f)); + case 3: + return std::make_unique>(Point::make(f)); + case 4: + return std::make_unique>(Point::make(f)); + case 5: + return std::make_unique>(Point::make(f)); + default: + abort(); + } +} + } // namespace detail /** A Point @@ -586,12 +608,20 @@ template class NDPoint { template NDPoint(Point &&p_) : p(std::make_unique>(std::move(p_))) {} + template operator Point() const { + return Point(dynamic_cast>(&p)); + } + template NDPoint(const std::array &arr) : p(std::make_unique>(arr)) {} template NDPoint(std::array &&arr) : p(std::make_unique>(std::move(arr))) {} + template operator std::array() const { + return std::array(dynamic_cast>(&p)); + } + NDPoint(std::initializer_list lst) : NDPoint(std::vector(lst)) {} private: template @@ -607,6 +637,19 @@ template class NDPoint { NDPoint(const NDPoint &x) : p(x.p ? NDPoint(convert_vector(std::vector(x))) : nullptr) {} + template static NDPoint make(const size_type D, const F &f) { + return NDPoint(detail::make_VPoint(D, f)); + } + static NDPoint pure(const size_type D, const T &val) { + return make(D, [&](size_type) { return val; }); + } + static NDPoint unit(const size_type D, const size_type dir) { + return make(D, [&](size_type d) { return d == dir; }); + } + static NDPoint iota(const size_type D) { + return make(D, [&](size_type d) { return d; }); + } + /** Check whether a Point is valid * * A valid Point knows its number of dimensions, and its components diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 67160f948f..fdd74a4978 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -735,7 +735,7 @@ template class Region { { Region reg; for (const auto &box : boxes) - reg |= region(box); + reg |= Region(box); assert(*this == reg); } #endif @@ -913,13 +913,16 @@ template class Region { size_type total_size = 0; T old_pos = std::numeric_limits::min(); // location of last subregion size_type old_subregion_size = 0; // number of points in the last subregion - traverse_subregions([&](const T pos, const Subregion &subregion) { - const size_type subregion_size = subregion.size(); - total_size += - old_subregion_size == 0 ? 0 : (pos - old_pos) * old_subregion_size; - old_pos = pos; - old_subregion_size = subregion_size; - }); + traverse_subregions( + [&](const T pos, const Subregion &subregion) { + const size_type subregion_size = subregion.size(); + total_size += old_subregion_size == 0 + ? 0 + : (pos - old_pos) * old_subregion_size; + old_pos = pos; + old_subregion_size = subregion_size; + }, + *this); assert(old_subregion_size == 0); return total_size; } From 6fa5863535b0ad623779d180d5905a84d55fba85 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 18 Jun 2021 10:35:51 -0400 Subject: [PATCH 43/68] Rename set property functions --- include/openPMD/regions/Box.hpp | 16 ++--- include/openPMD/regions/Region.hpp | 24 +++---- .../openPMD/regions/RegionCalculus_old.hpp | 64 +++++++++---------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index e0d1366be0..0bfe25c439 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -96,10 +96,10 @@ template class Box { return b1 <= b2 && b1 != b2; } friend bool operator>(const Box &b1, const Box &b2) { return b2 < b1; } - bool issubset(const Box &b) const { return *this <= b; } - bool issuperset(const Box &b) const { return *this >= b; } - bool is_strict_subset(const Box &b) const { return *this < b; } - bool is_strict_superset(const Box &b) const { return *this > b; } + bool is_subset_of(const Box &b) const { return *this <= b; } + bool is_superset_of(const Box &b) const { return *this >= b; } + bool is_strict_subset_of(const Box &b) const { return *this < b; } + bool is_strict_superset_of(const Box &b) const { return *this > b; } // Set operations friend Box bounding_box(const Box &b1, const Box &b2) { @@ -261,10 +261,10 @@ template class Box { return b1 <= b2 && b1 != b2; } friend bool operator>(const Box &b1, const Box &b2) { return b2 < b1; } - bool issubset(const Box &b) const { return *this <= b; } - bool issuperset(const Box &b) const { return *this >= b; } - bool is_strict_subset(const Box &b) const { return *this < b; } - bool is_strict_superset(const Box &b) const { return *this > b; } + bool is_subset_of(const Box &b) const { return *this <= b; } + bool is_superset_of(const Box &b) const { return *this >= b; } + bool is_strict_subset_of(const Box &b) const { return *this < b; } + bool is_strict_superset_of(const Box &b) const { return *this > b; } // Set operations friend Box bounding_box(const Box &b1, const Box &b2) { diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index fdd74a4978..265e49b700 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -186,10 +186,10 @@ template class Region { return region2 < region1; } - bool issubset(const Region ®ion) const { return *this <= region; } - bool issuperset(const Region ®ion) const { return *this >= region; } - bool is_strict_subset(const Region ®ion) const { return *this < region; } - bool is_strict_superset(const Region ®ion) const { return *this > region; } + bool is_subset_of(const Region ®ion) const { return *this <= region; } + bool is_superset_of(const Region ®ion) const { return *this >= region; } + bool is_strict_subset_of(const Region ®ion) const { return *this < region; } + bool is_strict_superset_of(const Region ®ion) const { return *this > region; } friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { os << "{"; @@ -632,10 +632,10 @@ template class Region { return region2 < region1; } - bool issubset(const Region ®ion) const { return *this <= region; } - bool issuperset(const Region ®ion) const { return *this >= region; } - bool is_strict_subset(const Region ®ion) const { return *this < region; } - bool is_strict_superset(const Region ®ion) const { return *this > region; } + bool is_subset_of(const Region ®ion) const { return *this <= region; } + bool is_superset_of(const Region ®ion) const { return *this >= region; } + bool is_strict_subset_of(const Region ®ion) const { return *this < region; } + bool is_strict_superset_of(const Region ®ion) const { return *this > region; } friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { os << "{"; @@ -1131,10 +1131,10 @@ template class Region { return region2 < region1; } - bool issubset(const Region ®ion) const { return *this <= region; } - bool issuperset(const Region ®ion) const { return *this >= region; } - bool is_strict_subset(const Region ®ion) const { return *this < region; } - bool is_strict_superset(const Region ®ion) const { return *this > region; } + bool is_subset_of(const Region ®ion) const { return *this <= region; } + bool is_superset_of(const Region ®ion) const { return *this >= region; } + bool is_strict_subset_of(const Region ®ion) const { return *this < region; } + bool is_strict_superset_of(const Region ®ion) const { return *this > region; } friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { os << "{"; diff --git a/include/openPMD/regions/RegionCalculus_old.hpp b/include/openPMD/regions/RegionCalculus_old.hpp index 81768d7312..94b2feb219 100644 --- a/include/openPMD/regions/RegionCalculus_old.hpp +++ b/include/openPMD/regions/RegionCalculus_old.hpp @@ -700,10 +700,10 @@ template struct box { bool operator>=(const box &b) const { return b <= *this; } bool operator<(const box &b) const { return *this <= b && *this != b; } bool operator>(const box &b) const { return b < *this; } - bool issubset(const box &b) const { return *this <= b; } - bool issuperset(const box &b) const { return *this >= b; } - bool is_strict_subset(const box &b) const { return *this < b; } - bool is_strict_superset(const box &b) const { return *this > b; } + bool is_subset_of(const box &b) const { return *this <= b; } + bool is_superset_of(const box &b) const { return *this >= b; } + bool is_strict_subset_of(const box &b) const { return *this < b; } + bool is_strict_superset_of(const box &b) const { return *this > b; } // Set operations box bounding_box(const box &b) const { return box(m_full | b.m_full); } @@ -869,10 +869,10 @@ template struct box { bool operator>=(const box &b) const { return b <= *this; } bool operator<(const box &b) const { return *this <= b && *this != b; } bool operator>(const box &b) const { return b < *this; } - bool issubset(const box &b) const { return *this <= b; } - bool issuperset(const box &b) const { return *this >= b; } - bool is_strict_subset(const box &b) const { return *this < b; } - bool is_strict_superset(const box &b) const { return *this > b; } + bool is_subset_of(const box &b) const { return *this <= b; } + bool is_superset_of(const box &b) const { return *this >= b; } + bool is_strict_subset_of(const box &b) const { return *this < b; } + bool is_strict_superset_of(const box &b) const { return *this > b; } // Set operations box bounding_box(const box &b) const { @@ -1287,10 +1287,10 @@ template struct region { return *this <= r && size() < r.size(); } bool operator>(const region &r) const { return r < *this; } - bool issubset(const region &r) const { return *this <= r; } - bool issuperset(const region &r) const { return *this >= r; } - bool is_strict_subset(const region &r) const { return *this < r; } - bool is_strict_superset(const region &r) const { return *this > r; } + bool is_subset_of(const region &r) const { return *this <= r; } + bool is_superset_of(const region &r) const { return *this >= r; } + bool is_strict_subset_of(const region &r) const { return *this < r; } + bool is_strict_superset_of(const region &r) const { return *this > r; } bool operator==(const region &r) const { return (*this ^ r).empty(); } bool operator!=(const region &r) const { return !(*this == r); } @@ -1464,10 +1464,10 @@ template struct region { bool operator>=(const region &other) const { return other <= *this; } bool operator<(const region &other) const { return !m_full & other.m_full; } bool operator>(const region &other) const { return other < *this; } - bool issubset(const region &other) const { return *this <= other; } - bool issuperset(const region &other) const { return *this >= other; } - bool is_strict_subset(const region &other) const { return *this < other; } - bool is_strict_superset(const region &other) const { return *this > other; } + bool is_subset_of(const region &other) const { return *this <= other; } + bool is_superset_of(const region &other) const { return *this >= other; } + bool is_strict_subset_of(const region &other) const { return *this < other; } + bool is_strict_superset_of(const region &other) const { return *this > other; } bool operator==(const region &other) const { return m_full == other.m_full; } bool operator!=(const region &other) const { return !(*this == other); } @@ -1874,10 +1874,10 @@ template struct region { return *this != other && *this <= other; } bool operator>(const region &other) const { return other < *this; } - bool issubset(const region &other) const { return *this <= other; } - bool issuperset(const region &other) const { return *this >= other; } - bool is_strict_subset(const region &other) const { return *this < other; } - bool is_strict_superset(const region &other) const { return *this > other; } + bool is_subset_of(const region &other) const { return *this <= other; } + bool is_superset_of(const region &other) const { return *this >= other; } + bool is_strict_subset_of(const region &other) const { return *this < other; } + bool is_strict_superset_of(const region &other) const { return *this > other; } bool operator==(const region &other) const { return subregions == other.subregions; } @@ -2305,10 +2305,10 @@ template struct region { return *this != other && *this <= other; } bool operator>(const region &other) const { return other < *this; } - bool issubset(const region &other) const { return *this <= other; } - bool issuperset(const region &other) const { return *this >= other; } - bool is_strict_subset(const region &other) const { return *this < other; } - bool is_strict_superset(const region &other) const { return *this > other; } + bool is_subset_of(const region &other) const { return *this <= other; } + bool is_superset_of(const region &other) const { return *this >= other; } + bool is_strict_subset_of(const region &other) const { return *this < other; } + bool is_strict_superset_of(const region &other) const { return *this > other; } bool operator==(const region &other) const { return subregions == other.subregions; } @@ -3796,10 +3796,10 @@ template struct dbox { bool operator>=(const dbox &b) const { return b <= *this; } bool operator<(const dbox &b) const { return *val < *b.val; } bool operator>(const dbox &b) const { return b < *this; } - bool issubset(const dbox &b) const { return *this <= b; } - bool issuperset(const dbox &b) const { return *this >= b; } - bool is_strict_subset(const dbox &b) const { return *this < b; } - bool is_strict_superset(const dbox &b) const { return *this > b; } + bool is_subset_of(const dbox &b) const { return *this <= b; } + bool is_superset_of(const dbox &b) const { return *this >= b; } + bool is_strict_subset_of(const dbox &b) const { return *this < b; } + bool is_strict_superset_of(const dbox &b) const { return *this > b; } // Set operations dbox bounding_box(const dbox &b) const { @@ -3985,10 +3985,10 @@ template struct dregion { bool operator>=(const dregion &r) const { return *val >= *r.val; } bool operator<(const dregion &r) const { return *val < *r.val; } bool operator>(const dregion &r) const { return *val > *r.val; } - bool issubset(const dregion &r) const { return *this <= r; } - bool issuperset(const dregion &r) const { return *this >= r; } - bool is_strict_subset(const dregion &r) const { return *this < r; } - bool is_strict_superset(const dregion &r) const { return *this > r; } + bool is_subset_of(const dregion &r) const { return *this <= r; } + bool is_superset_of(const dregion &r) const { return *this >= r; } + bool is_strict_subset_of(const dregion &r) const { return *this < r; } + bool is_strict_superset_of(const dregion &r) const { return *this > r; } bool operator==(const dregion &r) const { return *val == *r.val; } bool operator!=(const dregion &r) const { return *val != *r.val; } From 3a45617759ab9d79143e679d55c6b49b4732b119 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Wed, 23 Jun 2021 19:52:38 -0400 Subject: [PATCH 44/68] Update example --- examples/13_regions.cpp | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp index 684fc5b7c1..ab4c5dcec9 100644 --- a/examples/13_regions.cpp +++ b/examples/13_regions.cpp @@ -16,10 +16,14 @@ void point_example() { cout << "\n" << "Points (with dimension known at compile time):\n"; - cout << " Define two points:\n"; - Point x{1, 2}, y{4, 5}; - cout << " x: " << x << "\n" - << " y: " << y << "\n"; + cout << " Define a point:\n"; + Point x{1, 2}; + cout << " x: " << x << "\n"; + + cout << " Define a point from a vector:\n"; + vector vec{4, 5}; + Point y(vec); + cout << " y: " << y << "\n"; cout << " Arithmetic operations:\n"; auto z = x + 2 * y; @@ -35,7 +39,7 @@ void point_example() { cout << " p3: " << p3 << "\n"; cout << " Element-wise operations:\n"; - using std::abs, std::max; + // using std::abs, std::max; auto mxy1 = max(abs(x), abs(y)); // Apply arbitrary functions element-wise auto mxy2 = fmap([](auto a, auto b) { return max(abs(a), abs(b)); }, x, y); @@ -59,10 +63,14 @@ void ndpoint_example() { cout << "\n" << "NDPoints (with dimension only known at run time):\n"; - cout << " Define two points:\n"; - NDPoint x{1, 2}, y{4, 5}; - cout << " x: " << x << "\n" - << " y: " << y << "\n"; + cout << " Define a point:\n"; + NDPoint x{1, 2}; + cout << " x: " << x << "\n"; + + cout << " Define a point from a vector:\n"; + vector vec{4, 5}; + NDPoint y(vec); + cout << " y: " << y << "\n"; // Arithmetic operations cout << " Arithmetic operations:\n"; @@ -79,7 +87,7 @@ void ndpoint_example() { cout << " p3: " << p3 << "\n"; cout << " Element-wise operations:\n"; - using std::abs, std::max; + // using std::abs, std::max; auto mxy1 = max(abs(x), abs(y)); // Apply arbitrary functions element-wise auto mxy2 = fmap([](auto a, auto b) { return max(abs(a), abs(b)); }, x, y); @@ -139,8 +147,8 @@ void box_example() { cout << " Set tests:\n"; cout << " b == b1 (equality): " << (b == b1) << "\n" - << " b <= b1 (is-subset-of): " << (b <= b1) << "\n" - << " b < b1 (is-strict-subset_of): " << (b < b1) << "\n"; + << " b <= b1 (is_subset_of): " << (b <= b1) << "\n" + << " b < b1 (is_strict_subset_of): " << (b < b1) << "\n"; } //////////////////////////////////////////////////////////////////////////////// @@ -197,8 +205,8 @@ void region_example() { cout << " Set tests:\n"; cout << " r == r1 (equality): " << (r == r1) << "\n" - << " r <= r1 (is-subset-of): " << (r <= r1) << "\n" - << " r < r1 (is-strict-subset_of): " << (r < r1) << "\n"; + << " r <= r1 (is_subset_of): " << (r <= r1) << "\n" + << " r < r1 (is_strict_subset_of): " << (r < r1) << "\n"; cout << " Regions can be converted to a list of boxes:\n"; cout << " rg - r:\n"; From 2f5018f2212fe1ddbc285f54475346b7e0c52e37 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Wed, 23 Jun 2021 19:54:06 -0400 Subject: [PATCH 45/68] Make region algebra optional --- CMakeLists.txt | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a2be1cc7d5..4a41efb100 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,8 +68,8 @@ openpmd_option(HDF5 "HDF5 backend (.h5 files)" AUTO) openpmd_option(ADIOS1 "ADIOS1 backend (.bp files)" AUTO) openpmd_option(ADIOS2 "ADIOS2 backend (.bp files)" AUTO) openpmd_option(PYTHON "Enable Python bindings" AUTO) +openpmd_option(REGIONS "Enable region algebra (requires C++17)" AUTO) -option(openPMD_CXX_STANDARD "Which C++ standard to use (14 or 17)" OFF) option(openPMD_INSTALL "Add installation targets" ON) option(openPMD_HAVE_PKGCONFIG "Generate a .pc file for pkg-config" ON) option(openPMD_USE_INTERNAL_VARIANT "Use internally shipped MPark.Variant" ON) @@ -80,17 +80,6 @@ option(openPMD_USE_INTERNAL_JSON "Use internally shipped nlohmann-json" ON) option(openPMD_USE_INVASIVE_TESTS "Enable unit tests that modify source code" OFF) option(openPMD_USE_VERIFY "Enable internal VERIFY (assert) macro independent of build type" ON) -if(NOT openPMD_CXX_STANDARD) - set(openPMD_CXX_STANDARD 14) -endif() -if(openPMD_CXX_STANDARD STREQUAL 14) - set(openPMD_CXX_STD cxx_std_14) -elseif(openPMD_CXX_STANDARD STREQUAL 17) - set(openPMD_CXX_STD cxx_std_17) -else() - message(FATAL_ERROR "openPMD_CXX_STANDARD must be either 14 or 17; found ${openPMD_CXX_STANDARD}") -endif() - set(CMAKE_CONFIGURATION_TYPES "Release;Debug;MinSizeRel;RelWithDebInfo") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING @@ -408,10 +397,9 @@ add_library(openPMD::openPMD ALIAS openPMD) # properties target_compile_features(openPMD - PUBLIC ${openPMD_CXX_STD} + PUBLIC cxx_std_14 ) set_target_properties(openPMD PROPERTIES - CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -488,10 +476,10 @@ if(openPMD_HAVE_ADIOS1) add_library(openPMD.ADIOS1.Serial SHARED ${IO_ADIOS1_SEQUENTIAL_SOURCE}) add_library(openPMD.ADIOS1.Parallel SHARED ${IO_ADIOS1_SOURCE}) target_compile_features(openPMD.ADIOS1.Serial - PUBLIC ${openPMD_CXX_STD} + PUBLIC cxx_std_14 ) target_compile_features(openPMD.ADIOS1.Parallel - PUBLIC ${openPMD_CXX_STD} + PUBLIC cxx_std_14 ) target_compile_options(openPMD.ADIOS1.Serial PUBLIC ${_msvc_options}) target_compile_options(openPMD.ADIOS1.Parallel PUBLIC ${_msvc_options}) @@ -511,7 +499,6 @@ if(openPMD_HAVE_ADIOS1) endif() set_target_properties(openPMD.ADIOS1.Serial PROPERTIES - CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -539,7 +526,6 @@ if(openPMD_HAVE_ADIOS1) if(openPMD_HAVE_MPI) set_target_properties(openPMD.ADIOS1.Parallel PROPERTIES - CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -764,10 +750,9 @@ if(openPMD_BUILD_TESTING) test/CatchRunner.cpp) # Always MPI_Init with Serial Fallback add_library(CatchMain ${_openpmd_lib_type} test/CatchMain.cpp) # Serial only - target_compile_features(CatchRunner PUBLIC ${openPMD_CXX_STD}) - target_compile_features(CatchMain PUBLIC ${openPMD_CXX_STD}) + target_compile_features(CatchRunner PUBLIC cxx_std_14) + target_compile_features(CatchMain PUBLIC cxx_std_14) set_target_properties(CatchRunner CatchMain PROPERTIES - CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON @@ -794,9 +779,8 @@ if(openPMD_BUILD_TESTING) else() target_link_libraries(${testname}Tests PRIVATE CatchMain) endif() - target_compile_features(${testname}Tests PUBLIC ${openPMD_CXX_STD}) + target_compile_features(${testname}Tests PUBLIC cxx_std_14) set_target_properties(${testname}Tests PROPERTIES - CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON ) @@ -816,9 +800,8 @@ if(openPMD_BUILD_EXAMPLES) if(openPMD_HAVE_MPI) add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) - target_compile_features(${examplename} PUBLIC ${openPMD_CXX_STD}) + target_compile_features(${examplename} PUBLIC cxx_std_14) set_target_properties(${examplename} PROPERTIES - CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON ) @@ -826,9 +809,8 @@ if(openPMD_BUILD_EXAMPLES) else() add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) - target_compile_features(${examplename} PUBLIC ${openPMD_CXX_STD}) + target_compile_features(${examplename} PUBLIC cxx_std_14) set_target_properties(${examplename} PROPERTIES - CXX_STANDARD ${openPMD_CXX_STANDARD} CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON ) @@ -1355,7 +1337,6 @@ else() message(" Installation: OFF") endif() message("") -message(" C++ standard: ${openPMD_CXX_STD}") message(" Build Type: ${CMAKE_BUILD_TYPE}") if(openPMD_BUILD_SHARED_LIBS) message(" Library: shared") From 84d9108bd203b67fe06528e870adba9c02b4afa4 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Wed, 23 Jun 2021 20:02:49 -0400 Subject: [PATCH 46/68] Require C++17 for the region algebra --- CMakeLists.txt | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a41efb100..f8d75c1dab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -686,7 +686,7 @@ set(openPMD_TEST_NAMES SerialIO ParallelIO ) -if(openPMD_CXX_STANDARD STREQUAL 17) +if(openPMD_HAVE_REGIONS) list(APPEND openPMD_TEST_NAMES RegionsBox RegionsPoint @@ -719,7 +719,7 @@ set(openPMD_EXAMPLE_NAMES 10_streaming_read 12_span_write ) -if(openPMD_CXX_STANDARD STREQUAL 17) +if(openPMD_HAVE_REGIONS) list(APPEND openPMD_EXAMPLE_NAMES 13_regions ) @@ -779,11 +779,13 @@ if(openPMD_BUILD_TESTING) else() target_link_libraries(${testname}Tests PRIVATE CatchMain) endif() - target_compile_features(${testname}Tests PUBLIC cxx_std_14) - set_target_properties(${testname}Tests PROPERTIES - CXX_EXTENSIONS OFF - CXX_STANDARD_REQUIRED ON - ) + if(${testname} MATCHES "Regions.+$") + target_compile_features(${testname}Tests PUBLIC cxx_std_17) + set_target_properties(${testname}Tests PROPERTIES + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) + endif() endforeach() endif() @@ -809,11 +811,13 @@ if(openPMD_BUILD_EXAMPLES) else() add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) - target_compile_features(${examplename} PUBLIC cxx_std_14) - set_target_properties(${examplename} PROPERTIES - CXX_EXTENSIONS OFF - CXX_STANDARD_REQUIRED ON - ) + if(${examplename} MATCHES "13_.+$") + target_compile_features(${examplename} PUBLIC cxx_std_17) + set_target_properties(${examplename} PROPERTIES + CXX_EXTENSIONS OFF + CXX_STANDARD_REQUIRED ON + ) + endif() endif() endforeach() endif() From e9a46cd3cbf51dd8ab0313f6fac658dd31fc31f8 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Wed, 23 Jun 2021 20:07:19 -0400 Subject: [PATCH 47/68] Remove unintended CMakeLists change --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8d75c1dab..70529e424c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -802,11 +802,6 @@ if(openPMD_BUILD_EXAMPLES) if(openPMD_HAVE_MPI) add_executable(${examplename} examples/${examplename}.cpp) target_link_libraries(${examplename} PRIVATE openPMD) - target_compile_features(${examplename} PUBLIC cxx_std_14) - set_target_properties(${examplename} PROPERTIES - CXX_EXTENSIONS OFF - CXX_STANDARD_REQUIRED ON - ) endif() else() add_executable(${examplename} examples/${examplename}.cpp) From c59bc2058c331108d8736fb3efc6239a2d9b366c Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Wed, 23 Jun 2021 20:07:32 -0400 Subject: [PATCH 48/68] CI: Update regions options --- .github/workflows/unix.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 8a68adbd6a..103ccd2408 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -54,7 +54,7 @@ jobs: ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 export LDFLAGS="${LDFLAGS} -fsanitize=address,undefined -shared-libsan" CXXFLAGS="${CXXFLAGS} -fsanitize=address,undefined -shared-libsan" - CXXFLAGS="${CXXFLAGS}" cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_MPI=ON -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_ADIOS1=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DopenPMD_CXX_STANDARD=17 + CXXFLAGS="${CXXFLAGS}" cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_MPI=ON -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_ADIOS1=ON -DopenPMD_USE_REGIONS=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DCMAKE_VERBOSE_MAKEFILE=ON cmake --build . --parallel 2 export ASAN_OPTIONS=detect_stack_use_after_return=1:detect_leaks=1:check_initialization_order=true:strict_init_order=true:detect_stack_use_after_scope=1:fast_unwind_on_malloc=0 export LSAN_OPTIONS=suppressions="$SOURCEPATH/.github/ci/sanitizer/clang/Leak.supp" @@ -163,7 +163,7 @@ jobs: run: | mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_MPI=OFF -DopenPMD_USE_HDF5=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DopenPMD_CXX_STANDARD=17 + cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_MPI=OFF -DopenPMD_USE_HDF5=ON -DopenPMD_USE_REGIONS=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DCMAKE_VERBOSE_MAKEFILE=ON cmake --build . --parallel 2 ctest --output-on-failure @@ -263,7 +263,7 @@ jobs: run: | mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_INVASIVE_TESTS=ON -DopenPMD_CXX_STANDARD=17 + cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=ON -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_REGIONS=ON -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 ctest --output-on-failure @@ -344,12 +344,12 @@ jobs: -DCMAKE_CXX_COMPILER=$(which icpx) \ -DopenPMD_USE_PYTHON=OFF \ -DopenPMD_USE_MPI=OFF \ + -DopenPMD_USE_REGIONS=OFF \ -DCMAKE_C_COMPILER_ID="Clang" \ -DCMAKE_C_COMPILER_VERSION=12.0 \ -DCMAKE_C_STANDARD_COMPUTED_DEFAULT="11" \ -DCMAKE_CXX_COMPILER_ID="Clang" \ -DCMAKE_CXX_COMPILER_VERSION=12.0 \ - -DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT="17" \ - -DopenPMD_CXX_STANDARD=17 + -DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT="17" cmake --build . --parallel 2 ctest --output-on-failure From b5afb43e877bfd4f06e0a93ff8e48007c2a37312 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 19:01:22 -0400 Subject: [PATCH 49/68] Implement NDBox --- examples/13_regions.cpp | 50 +++ include/openPMD/regions/Box.hpp | 3 +- include/openPMD/regions/Helpers.hpp | 16 + include/openPMD/regions/NDBox.hpp | 523 ++++++++++++++++++++++++++++ include/openPMD/regions/NDPoint.hpp | 4 +- include/openPMD/regions/Regions.hpp | 4 +- test/RegionsBoxTest.cpp | 10 +- 7 files changed, 600 insertions(+), 10 deletions(-) create mode 100644 include/openPMD/regions/NDBox.hpp diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp index ab4c5dcec9..88df13ac65 100644 --- a/examples/13_regions.cpp +++ b/examples/13_regions.cpp @@ -153,6 +153,55 @@ void box_example() { //////////////////////////////////////////////////////////////////////////////// +void ndbox_example() { + using namespace openPMD::Regions; + using namespace std; + + cout << "\n" + << "Boxes are spanned between points (inclusive lower, exclusive upper " + "bound):\n"; + + cout << " Define two points:\n"; + NDPoint x{1, 4}, y{2, 5}; + cout << " x:" << x << "\n" + << " y:" << y << "\n"; + + cout << " Define a box between these points:\n"; + NDBox b(x, y); + cout << " b: " << b << " b.empty: " << b.empty() << "\n"; + cout << " Define an empty box:\n"; + NDBox be(2); + cout << " be: " << be << " be.empty: " << be.empty() << "\n"; + + cout << " Boxes can be shifted and scaled (e.g. to change resolution):\n"; + NDPoint offset{1, 2}; + auto b1 = b >> offset; + NDPoint scale{2, 2}; + auto b2 = b * scale; + // Boxes can be grown and shrunk (e.g. to add ghost zones) + auto bg = b.grown(1); + auto bs = b.shrunk(1); + cout << " shifted box: " << b1 << "\n" + << " scaled box: " << b2 << "\n" + << " grown box: " << bg << "\n" + << " shrunk box: " << bs << "\n"; + + cout << " The bounding box containing two boxes:\n"; + auto bb = bounding_box(b, b1); + cout << " bounding box: " << bb << "\n"; + + cout << " Boxes can be intersected:\n"; + auto bi = b & b1; + cout << " intersection: " << bi << "\n"; + + cout << " Set tests:\n"; + cout << " b == b1 (equality): " << (b == b1) << "\n" + << " b <= b1 (is_subset_of): " << (b <= b1) << "\n" + << " b < b1 (is_strict_subset_of): " << (b < b1) << "\n"; +} + +//////////////////////////////////////////////////////////////////////////////// + void region_example() { using namespace openPMD::Regions; using namespace std; @@ -262,6 +311,7 @@ int main() { ndpoint_example(); box_example(); + ndbox_example(); region_example(); diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index 0bfe25c439..6823d813c0 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -109,7 +109,7 @@ template class Box { friend Box operator&(const Box &b1, const Box &b2) { return Box(!b1.empty() & !b2.empty()); } - Box &operator|=(const Box &b) { return *this = *this | b; } + Box &operator&=(const Box &b) { return *this = *this & b; } friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } friend bool operator==(const Box &b, const std::vector &bs) { @@ -290,6 +290,7 @@ template class Box { #endif return r; } + Box &operator&=(const Box &b) { return *this = *this & b; } friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } friend bool operator==(const Box &b, const std::vector &bs) { diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 6ee7fce9ed..80a4ff6225 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -50,6 +50,22 @@ constexpr std::array construct_array(const F &f) { return array_push(construct_array(f), f(N - 1)); } +// Convert a std::vector +template +std::vector convert_vector(const std::vector &vec) { + std::vector res(vec.size()); + for (std::size_t i = 0; i < res.size(); ++i) + res[i] = vec[i]; + return res; +} +template +std::vector convert_vector(std::vector &&vec) { + std::vector res(vec.size()); + for (std::size_t i = 0; i < res.size(); ++i) + res[i] = std::move(vec[i]); + return res; +} + //////////////////////////////////////////////////////////////////////////////// // Compare tuples diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp new file mode 100644 index 0000000000..10f4ee2518 --- /dev/null +++ b/include/openPMD/regions/NDBox.hpp @@ -0,0 +1,523 @@ +#ifndef REGIONS_NDBOX_HPP +#define REGIONS_NDBOX_HPP + +#include "Box.hpp" +#include "Helpers.hpp" +#include "NDPoint.hpp" + +namespace openPMD { +namespace Regions { + +template class NDBox; + +namespace detail { + +// Abstract base helper class + +template class VBox { +public: + typedef T value_type; + typedef std::size_t size_type; + + virtual std::unique_ptr copy() const = 0; + + virtual ~VBox() {} + + virtual size_type ndims() const = 0; + virtual bool empty() const = 0; + virtual NDPoint lower() const = 0; + virtual NDPoint upper() const = 0; + virtual NDPoint shape() const = 0; + virtual size_type size() const = 0; + + virtual VBox &operator>>=(const NDPoint &p) = 0; + virtual VBox &operator<<=(const NDPoint &p) = 0; + virtual VBox &operator*=(const NDPoint &p) = 0; + virtual std::unique_ptr operator>>(const NDPoint &p) const = 0; + virtual std::unique_ptr operator<<(const NDPoint &p) const = 0; + virtual std::unique_ptr operator*(const NDPoint &p) const = 0; + virtual std::unique_ptr grown(const NDPoint &dlo, + const NDPoint &dup) const = 0; + virtual std::unique_ptr grown(const NDPoint &d) const = 0; + virtual std::unique_ptr grown(const T &d) const = 0; + virtual std::unique_ptr shrunk(const NDPoint &dlo, + const NDPoint &dup) const = 0; + virtual std::unique_ptr shrunk(const NDPoint &d) const = 0; + virtual std::unique_ptr shrunk(const T &d) const = 0; + + virtual bool operator==(const VBox &b2) const = 0; + virtual bool operator!=(const VBox &b2) const = 0; + + virtual bool contains(const NDPoint &p) const = 0; + virtual bool isdisjoint1(const VBox &b2) const = 0; + virtual bool operator<=(const VBox &b2) const = 0; + virtual bool operator>=(const VBox &b2) const = 0; + virtual bool operator<(const VBox &b2) const = 0; + virtual bool operator>(const VBox &b2) const = 0; + virtual bool is_subset_of(const VBox &b) const = 0; + virtual bool is_superset_of(const VBox &b) const = 0; + virtual bool is_strict_subset_of(const VBox &b) const = 0; + virtual bool is_strict_superset_of(const VBox &b) const = 0; + + virtual std::unique_ptr bounding_box1(const VBox &b2) const = 0; + + virtual std::unique_ptr operator&(const VBox &b2) const = 0; + virtual VBox &operator&=(const VBox &b) = 0; + virtual std::unique_ptr intersection1(const VBox &b2) const = 0; + + virtual bool operator==(const std::vector> &bs) const = 0; + virtual bool operator!=(const std::vector> &bs) const = 0; + + virtual std::vector> operator-(const VBox &b2) const = 0; + virtual std::vector> difference1(const VBox &b2) const = 0; + + virtual std::vector> operator|(const VBox &b2) const = 0; + virtual std::vector> setunion1(const VBox &b2) const = 0; + + virtual std::vector> operator^(const VBox &b2) const = 0; + virtual std::vector> symmetric_difference1(const VBox &b2) const = 0; + + virtual bool equal_to1(const VBox &x) const = 0; + virtual std::size_t hash1() const = 0; + virtual bool less1(const VBox &x) const = 0; + + virtual std::ostream &output(std::ostream &os) const = 0; +}; + +// Helper class wrapping Box + +template class WBox final : public VBox { + Box b; + +public: + using typename VBox::value_type; + using typename VBox::size_type; + + WBox() : b{} {} + + WBox(const WBox &x) = default; + WBox(WBox &&) = default; + WBox &operator=(const WBox &) = default; + WBox &operator=(WBox &&) = default; + + WBox(const Box &b_) : b(b_) {} + WBox(Box &&b_) : b(std::move(b_)) {} + operator Box() const { return b; } + + WBox(const Point &lo_, const Point &hi_) : b(lo_, hi_) {} + explicit WBox(const Point &p) : b(p) {} + + std::unique_ptr> copy() const override { + return std::make_unique(*this); + } + + ~WBox() override {} + + size_type ndims() const override { return b.ndims(); } + bool empty() const override { return b.empty(); } + NDPoint lower() const override { return b.lower(); } + NDPoint upper() const override { return b.upper(); } + NDPoint shape() const override { return b.shape(); } + size_type size() const override { return b.size(); } + + VBox &operator>>=(const NDPoint &p) override { + b >>= Point(p); + return *this; + } + VBox &operator<<=(const NDPoint &p) override { + b <<= Point(p); + return *this; + } + VBox &operator*=(const NDPoint &p) override { + b *= Point(p); + return *this; + } + std::unique_ptr> operator>>(const NDPoint &p) const override { + return std::make_unique(b >> Point(p)); + } + std::unique_ptr> operator<<(const NDPoint &p) const override { + return std::make_unique(b << Point(p)); + } + std::unique_ptr> operator*(const NDPoint &p) const override { + return std::make_unique(b * Point(p)); + } + std::unique_ptr> grown(const NDPoint &dlo, + const NDPoint &dup) const override { + return std::make_unique(b.grown(Point(dlo), Point(dup))); + } + std::unique_ptr> grown(const NDPoint &d) const override { + return std::make_unique(b.grown(Point(d))); + } + std::unique_ptr> grown(const T &d) const override { + return std::make_unique(b.grown(d)); + } + std::unique_ptr> shrunk(const NDPoint &dlo, + const NDPoint &dup) const override { + return std::make_unique(b.shrunk(Point(dlo), Point(dup))); + } + std::unique_ptr> shrunk(const NDPoint &d) const override { + return std::make_unique(b.shrunk(Point(d))); + } + std::unique_ptr> shrunk(const T &d) const override { + return std::make_unique(b.shrunk(d)); + } + + bool operator==(const VBox &b2) const override { + return b == dynamic_cast(b2).b; + } + bool operator!=(const VBox &b2) const override { + return b != dynamic_cast(b2).b; + } + + bool contains(const NDPoint &p) const override { + return b.contains(Point(p)); + } + bool isdisjoint1(const VBox &b2) const override { + return isdisjoint(b, dynamic_cast(b2).b); + } + bool operator<=(const VBox &b2) const override { + return b <= dynamic_cast(b2).b; + } + bool operator>=(const VBox &b2) const override { + return b >= dynamic_cast(b2).b; + } + bool operator<(const VBox &b2) const override { + return b < dynamic_cast(b2).b; + } + bool operator>(const VBox &b2) const override { + return b > dynamic_cast(b2).b; + } + bool is_subset_of(const VBox &b2) const override { + return b.is_subset_of(dynamic_cast(b2).b); + } + bool is_superset_of(const VBox &b2) const override { + return b.is_superset_of(dynamic_cast(b2).b); + } + bool is_strict_subset_of(const VBox &b2) const override { + return b.is_strict_subset_of(dynamic_cast(b2).b); + } + bool is_strict_superset_of(const VBox &b2) const override { + return b.is_strict_superset_of(dynamic_cast(b2).b); + } + + std::unique_ptr> bounding_box1(const VBox &b2) const override { + return std::make_unique( + bounding_box(b, dynamic_cast(b2).b)); + } + + std::unique_ptr> operator&(const VBox &b2) const override { + return std::make_unique(b & dynamic_cast(b2).b); + } + VBox &operator&=(const VBox &b2) override { + b &= dynamic_cast(b2).b; + return *this; + } + std::unique_ptr> intersection1(const VBox &b2) const override { + return std::make_unique( + intersection(b, dynamic_cast(b2).b)); + } + + bool operator==(const std::vector> &bs) const override { + return b == helpers::convert_vector>(bs); + } + bool operator!=(const std::vector> &bs) const override { + return b != helpers::convert_vector>(bs); + } + + std::vector> operator-(const VBox &b2) const override { + return helpers::convert_vector>(b - + dynamic_cast(b2).b); + } + std::vector> difference1(const VBox &b2) const override { + return helpers::convert_vector>( + difference(b, dynamic_cast(b2).b)); + } + + std::vector> operator|(const VBox &b2) const override { + return helpers::convert_vector>(b | + dynamic_cast(b2).b); + } + std::vector> setunion1(const VBox &b2) const override { + return helpers::convert_vector>( + setunion(b, dynamic_cast(b2).b)); + } + + std::vector> operator^(const VBox &b2) const override { + return helpers::convert_vector>(b ^ + dynamic_cast(b2).b); + } + std::vector> + symmetric_difference1(const VBox &b2) const override { + return helpers::convert_vector>( + symmetric_difference(b, dynamic_cast(b2).b)); + } + + bool equal_to1(const VBox &x) const override { + return std::equal_to>()(b, dynamic_cast(x).b); + } + std::size_t hash1() const override { return std::hash>()(b); } + bool less1(const VBox &x) const override { + return std::less>()(b, dynamic_cast(x).b); + } + + std::ostream &output(std::ostream &os) const override { return os << b; } +}; + +template +std::unique_ptr> make_VBox(const std::size_t D, const Args &...args) { + switch (D) { + case 0: + return std::make_unique>(args...); + case 1: + return std::make_unique>(args...); + case 2: + return std::make_unique>(args...); + case 3: + return std::make_unique>(args...); + case 4: + return std::make_unique>(args...); + case 5: + return std::make_unique>(args...); + default: + abort(); + } +} + +} // namespace detail + +/** A Box + * + * The dimension (number of component) of the Box is only known at + * run-time. @see Box + * + * A box is described by two points, its lower bound (inclusive) and + * upper bound (exclusive). If the lower bound not less than the upper + * bound, the box is empty. Empty boxes are fine (similar to an empty + * array). + */ +template class NDBox { + template using VBox = detail::VBox; + + template friend class NDBox; + friend struct std::equal_to; + friend struct std::hash; + friend struct std::less; + + std::unique_ptr> b; + + NDBox(std::unique_ptr> b_) : b(std::move(b_)) {} + +public: + /** Component type + */ + typedef typename VBox::value_type value_type; + /** Return type of Box::size() + */ + typedef typename VBox::size_type size_type; + + /** Create an invalid Box + */ + NDBox() : b() {} + /** Create a value-initialized Box with D components + */ + NDBox(const size_type D) : b(detail::make_VBox(D)) {} + + NDBox(const NDBox &x) : b(x.b ? x.b->copy() : nullptr) {} + NDBox(NDBox &&) = default; + NDBox &operator=(const NDBox &x) { + b = x.b ? x.b->copy() : nullptr; + return *this; + } + NDBox &operator=(NDBox &&) = default; + + template + NDBox(const Box &b_) : b(std::make_unique>(b_)) {} + template + NDBox(Box &&b_) + : b(std::make_unique>(std::move(b_))) {} + template operator Box() const { + return Box(dynamic_cast &>(*b)); + } + + NDBox(const NDPoint &lo_, const NDPoint &hi_) + : b(detail::make_VBox(lo_.ndims(), lo_, hi_)) {} + explicit NDBox(const NDPoint &p) : b(detail::make_VBox(p.ndims(), p)) {} + + /** Check whether a box is valid + * + * A valid box knows its number of dimensions, and its components + * are initialized. An invalid box does not know its number of + * dimensions and holds no data, similar to a null pointer. + * + * Most other member functions must not be called for invalid + * boxes. + */ + bool has_value() const { return bool(b); } + + size_type ndims() const { return b->ndims(); } + bool empty() const { return b->empty(); } + NDPoint lower() const { return b->lower(); } + NDPoint upper() const { return b->upper(); } + NDPoint shape() const { return b->shape(); } + size_type size() const { return b->size(); } + + NDBox &operator>>=(const NDPoint &p) { + *b >>= p->p; + return *this; + } + NDBox &operator<<=(const NDPoint &p) { + *b <<= p->p; + return *this; + } + NDBox &operator*=(const NDPoint &p) { + *b *= p->p; + return *this; + } + NDBox operator>>(const NDPoint &p) const { return NDBox(*b >> p); } + NDBox operator<<(const NDPoint &p) const { return NDBox(*b << p); } + NDBox operator*(const NDPoint &p) const { return NDBox(*b * p); } + NDBox grown(const NDPoint &dlo, const NDPoint &dup) const { + return NDBox(b->grown(dlo, dup)); + } + NDBox grown(const NDPoint &d) const { return NDBox(b->grown(d)); } + NDBox grown(const T &d) const { return NDBox(b->grown(d)); } + NDBox shrunk(const NDPoint &dlo, const NDPoint &dup) const { + return NDBox(b->shrunk(dlo, dup)); + } + NDBox shrunk(const NDPoint &d) const { return NDBox(b->shrunk(d)); } + NDBox shrunk(const T &d) const { return NDBox(b->shrunk(d)); } + + friend bool operator==(const NDBox &b1, const NDBox &b2) { + return *b1.b == *b2.b; + } + friend bool operator!=(const NDBox &b1, const NDBox &b2) { + return *b1.b != *b2.b; + } + + bool contains(const NDPoint &p) const { return b->contains(p); } + friend bool isdisjoint(const NDBox &b1, const NDBox &b2) { + return b1.b->isdisjoint1(*b2.b); + } + friend bool operator<=(const NDBox &b1, const NDBox &b2) { + return *b1.b <= *b2.b; + } + friend bool operator>=(const NDBox &b1, const NDBox &b2) { + return *b1.b >= *b2.b; + } + friend bool operator<(const NDBox &b1, const NDBox &b2) { + return *b1.b < *b2.b; + } + friend bool operator>(const NDBox &b1, const NDBox &b2) { + return *b1.b > *b2.b; + } + bool is_subset_of(const NDBox &b2) const { return b->is_subset_of(*b2.b); } + bool is_superset_of(const NDBox &b2) const { + return b->is_superset_of(*b2.b); + } + bool is_strict_subset_of(const NDBox &b2) const { + return b->is_strict_subset_of(*b2.b); + } + bool is_strict_superset_of(const NDBox &b2) const { + return b->is_strict_superset_of(*b2.b); + } + + friend NDBox bounding_box(const NDBox &b1, const NDBox &b2) { + return b1.b->bounding_box1(*b2.b); + } + + friend NDBox operator&(const NDBox &b1, const NDBox &b2) { + return *b1.b & *b2.b; + } + NDBox &operator&=(const NDBox &b2) { + *b &= *b2.b; + return *this; + } + friend NDBox intersection(const NDBox &b1, const NDBox &b2) { + return intersection(*b1.b, *b2.b); + } + + friend bool operator==(const NDBox &b, const std::vector &bs) { + return *b.b == bs; + } + friend bool operator==(const std::vector &bs, const NDBox &b) { + return *b.b == bs; + } + friend bool operator!=(const NDBox &b, const std::vector &bs) { + return *b.b != bs; + } + friend bool operator!=(const std::vector &bs, const NDBox &b) { + return *b.b != bs; + } + + friend std::vector operator-(const NDBox &b1, const NDBox &b2) { + + return *b1.b - *b2.b; + } + friend std::vector difference(const NDBox &b1, const NDBox &b2) { + return difference(*b1.b, *b2.b); + } + + friend std::vector operator|(const NDBox &b1, const NDBox &b2) { + return *b1.b | *b2.b; + } + friend std::vector setunion(const NDBox &b1, const NDBox &b2) { + return setunion(*b1.b, *b2.b); + } + + friend std::vector operator^(const NDBox &b1, const NDBox &b2) { + return *b1.b ^ *b2.b; + } + friend std::vector symmetric_difference(const NDBox &b1, + const NDBox &b2) { + return symmetric_difference(*b1.b, *b2.b); + } + + /** Output a box + */ + friend std::ostream &operator<<(std::ostream &os, const NDBox &x) { + if (x.b) + x.b->output(os); + else + os << "(INVALID)"; + return os; + } +}; + +} // namespace Regions +} // namespace openPMD + +namespace std { + +template struct equal_to> { + constexpr bool operator()(const openPMD::Regions::NDBox &x, + const openPMD::Regions::NDBox &y) const { + if (!x.has_value() && !y.has_value()) + return true; + if (!x.has_value() || !y.has_value()) + return false; + return x.b->equal_to1(*y.b); + } +}; + +template struct hash> { + constexpr size_t operator()(const openPMD::Regions::NDBox &x) const { + if (!x.has_value()) + return size_t(0x7e21b87749864bbcULL); + return x.b->hash1(); + } +}; + +template struct less> { + constexpr bool operator()(const openPMD::Regions::NDBox &x, + const openPMD::Regions::NDBox &y) const { + if (!x.has_value()) + return true; + if (!y.has_value()) + return false; + return x.b->less1(*y.b); + } +}; + +} // namespace std + +#endif // #ifndef REGIONS_NDBOX_HPP diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index fbe7beb5d4..b39beb49e3 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -161,7 +161,7 @@ template class VPoint { virtual std::ostream &output(std::ostream &os) const = 0; }; -// Helper class wrapping point +// Helper class wrapping Point template class WPoint final : public VPoint { Point p; @@ -609,7 +609,7 @@ template class NDPoint { NDPoint(Point &&p_) : p(std::make_unique>(std::move(p_))) {} template operator Point() const { - return Point(dynamic_cast>(&p)); + return Point(dynamic_cast&>(*p)); } template diff --git a/include/openPMD/regions/Regions.hpp b/include/openPMD/regions/Regions.hpp index 76efe0d726..62e0433174 100644 --- a/include/openPMD/regions/Regions.hpp +++ b/include/openPMD/regions/Regions.hpp @@ -1,10 +1,12 @@ #ifndef REGIONS_REGIONS_HPP #define REGIONS_REGIONS_HPP -#include "Point.hpp" #include "Box.hpp" +#include "Point.hpp" #include "Region.hpp" +#include "NDBox.hpp" #include "NDPoint.hpp" +//TODO #include "NDRegion.hpp" #endif // #ifndef REGIONS_REGIONS_HPP diff --git a/test/RegionsBoxTest.cpp b/test/RegionsBoxTest.cpp index e4a98e947d..521d86df64 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/RegionsBoxTest.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -28,7 +29,7 @@ template void test_Box(const B &box) { const auto randb = [&]() { if (D == 0) { if (dist0(gen) < 5) - return B(); + return B(p, p); else return B(p); } @@ -122,11 +123,11 @@ template void test_Box(const B &box) { CHECK((X.grown(x).grown(-x).empty() || X.grown(x).grown(-x) == X) == true); CHECK(X.grown(x) == X.grown(x, x)); - CHECK(X.grown(a) == X.grown(P::pure(a))); + CHECK(X.grown(a) == X.grown(fmap([&](int) { return a; }, x))); CHECK(X.shrunk(x, y) == X.grown(-x, -y)); CHECK(X.shrunk(x) == X.shrunk(x, x)); - CHECK(X.shrunk(a) == X.shrunk(P::pure(a))); + CHECK(X.shrunk(a) == X.shrunk(fmap([&](int) { return a; }, x))); CHECK(N == N); CHECK(X == X); @@ -231,8 +232,6 @@ TEST_CASE("Box", "[regions]") { test_Box(Box()); } TEST_CASE("Box", "[regions]") { test_Box(Box()); } TEST_CASE("Box", "[regions]") { test_Box(Box()); } -// TODO -#if 0 TEST_CASE("NDBox(0)", "[regions]") { test_Box(NDBox(0)); } @@ -250,4 +249,3 @@ TEST_CASE("NDBox(0)", "[regions]") { test_Box(NDBox(0)); } TEST_CASE("NDBox(1)", "[regions]") { test_Box(NDBox(1)); } TEST_CASE("NDBox(2)", "[regions]") { test_Box(NDBox(2)); } TEST_CASE("NDBox(3)", "[regions]") { test_Box(NDBox(3)); } -#endif From 514597115886214c0a9b3a65f55e5be6853b739d Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 20:30:00 -0400 Subject: [PATCH 50/68] CMake: Debug regions settings --- .github/workflows/unix.yml | 2 +- CMakeLists.txt | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 0495bd1d9a..0d112c0da9 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -352,7 +352,7 @@ jobs: -DCMAKE_CXX_COMPILER=$(which icpx) \ -DopenPMD_USE_PYTHON=OFF \ -DopenPMD_USE_MPI=OFF \ - -DopenPMD_USE_REGIONS=OFF \ + -DopenPMD_USE_REGIONS=ON \ -DCMAKE_C_COMPILER_ID="Clang" \ -DCMAKE_C_COMPILER_VERSION=12.0 \ -DCMAKE_C_STANDARD_COMPUTED_DEFAULT="11" \ diff --git a/CMakeLists.txt b/CMakeLists.txt index d54b5ea240..813be01d08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,6 +326,26 @@ else() set(openPMD_HAVE_PYTHON FALSE) endif() +# Region algebra +message(STATUS "*** openPMD_USE_REGIONS=${openPMD_USE_REGIONS}") +if(openPMD_USE_REGIONS STREQUAL AUTO) + # Enable regions by default only when building with at least C++17 + if ("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + message(STATUS "*** found C++17 support") + set(openPMD_HAVE_REGIONS TRUE) + else() + message(STATUS "*** found no C++17 support") + set(openPMD_HAVE_REGIONS FALSE) + endif() +elseif(openPMD_USE_REGIONS) + message(STATUS "*** regions have been manually activated") + set(openPMD_HAVE_REGIONS TRUE) +else() + message(STATUS "*** regions have been manually deactivated") + set(openPMD_HAVE_REGIONS FALSE) +endif() +message(STATUS "*** openPMD_USE_REGIONS=${openPMD_USE_REGIONS}") +message(STATUS "*** openPMD_HAVE_REGIONS=${openPMD_HAVE_REGIONS}") # Targets ##################################################################### # From 7d65e4d93af9f11ffb1bd952c846e08e8d17effa Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 20:32:17 -0400 Subject: [PATCH 51/68] CMake: Remove debug output --- CMakeLists.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 813be01d08..09d1bc0801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,25 +327,18 @@ else() endif() # Region algebra -message(STATUS "*** openPMD_USE_REGIONS=${openPMD_USE_REGIONS}") if(openPMD_USE_REGIONS STREQUAL AUTO) # Enable regions by default only when building with at least C++17 if ("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES) - message(STATUS "*** found C++17 support") set(openPMD_HAVE_REGIONS TRUE) else() - message(STATUS "*** found no C++17 support") set(openPMD_HAVE_REGIONS FALSE) endif() elseif(openPMD_USE_REGIONS) - message(STATUS "*** regions have been manually activated") set(openPMD_HAVE_REGIONS TRUE) else() - message(STATUS "*** regions have been manually deactivated") set(openPMD_HAVE_REGIONS FALSE) endif() -message(STATUS "*** openPMD_USE_REGIONS=${openPMD_USE_REGIONS}") -message(STATUS "*** openPMD_HAVE_REGIONS=${openPMD_HAVE_REGIONS}") # Targets ##################################################################### # From 6159f94d1c9fe10c6b0c67b222afd1b6aaa6fcd0 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 22:07:56 -0400 Subject: [PATCH 52/68] Implement NDRegion --- .github/workflows/unix.yml | 3 +- CMakeLists.txt | 6 +- examples/13_regions.cpp | 109 +++- include/openPMD/regions/NDBox.hpp | 18 +- include/openPMD/regions/NDPoint.hpp | 2 +- include/openPMD/regions/NDRegion.hpp | 584 ++++++++++++++++++ include/openPMD/regions/Region.hpp | 38 +- include/openPMD/regions/Regions.hpp | 2 +- test/{RegionsBoxTest.cpp => BoxTest.cpp} | 2 +- test/{RegionsPointTest.cpp => PointTest.cpp} | 0 .../{RegionsRegionTest.cpp => RegionTest.cpp} | 28 +- 11 files changed, 749 insertions(+), 43 deletions(-) create mode 100644 include/openPMD/regions/NDRegion.hpp rename test/{RegionsBoxTest.cpp => BoxTest.cpp} (99%) rename test/{RegionsPointTest.cpp => PointTest.cpp} (100%) rename test/{RegionsRegionTest.cpp => RegionTest.cpp} (90%) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 0d112c0da9..44a1d4dd89 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -352,12 +352,11 @@ jobs: -DCMAKE_CXX_COMPILER=$(which icpx) \ -DopenPMD_USE_PYTHON=OFF \ -DopenPMD_USE_MPI=OFF \ - -DopenPMD_USE_REGIONS=ON \ -DCMAKE_C_COMPILER_ID="Clang" \ -DCMAKE_C_COMPILER_VERSION=12.0 \ -DCMAKE_C_STANDARD_COMPUTED_DEFAULT="11" \ -DCMAKE_CXX_COMPILER_ID="Clang" \ -DCMAKE_CXX_COMPILER_VERSION=12.0 \ - -DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT="17" + -DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT="14" cmake --build . --parallel 2 ctest --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 09d1bc0801..0f4d3fde87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -703,9 +703,9 @@ set(openPMD_TEST_NAMES ) if(openPMD_HAVE_REGIONS) list(APPEND openPMD_TEST_NAMES - RegionsBox - RegionsPoint - RegionsRegion + Box + Point + Region ) endif() # command line tools diff --git a/examples/13_regions.cpp b/examples/13_regions.cpp index 88df13ac65..1eb23312b3 100644 --- a/examples/13_regions.cpp +++ b/examples/13_regions.cpp @@ -207,7 +207,7 @@ void region_example() { using namespace std; cout << "\n" - << "Regions consists of a set of boxes:\n"; + << "Regions consist of a set of boxes:\n"; cout << " Define two points:\n"; Point x{1, 4}, y{2, 5}; @@ -262,9 +262,8 @@ void region_example() { for (const auto &bx : std::vector>(rg - r)) cout << " " << bx << "\n"; -#if !REGIONS_DEBUG // Don't benchmark the debug version; it uses an N^2 algorithm for self-checks - const int n = 1000; + const int n = REGIONS_DEBUG ? 10 : 1000; cout << " Create a list of " << n << " 3d boxes and convert it to a region:\n"; std::mt19937 gen; @@ -301,7 +300,108 @@ void region_example() { cout << " reg.size: " << reg1.size() << "\n" << " reg.nboxes: " << reg1.nboxes() << "\n" << " runtime: " << elapsed(t2, t3) << " sec\n"; -#endif +} + +//////////////////////////////////////////////////////////////////////////////// + +void ndregion_example() { + using namespace openPMD::Regions; + using namespace std; + + cout << "\n" + << "Regions consist of a set of boxes:\n"; + + cout << " Define two points:\n"; + NDPoint x{1, 4}, y{2, 5}; + cout << " x:" << x << "\n" + << " y:" << y << "\n"; + + cout << " Define a box between these points:\n"; + NDBox b(x, y); + cout << " b:" << b << "\n"; + + cout << " Define a region consisting of this box:\n"; + NDRegion r(b); + cout << " r: " << r << " r.empty: " << r.empty() << "\n"; + cout << " Define an empty region:\n"; + NDRegion re(2); + cout << " re: " << re << " re.empty: " << re.empty() << "\n"; + + cout << " Regions can be shifted and scaled (e.g. to change resolution):\n"; + NDPoint offset{1, 2}; + auto r1 = r >> offset; + NDPoint scale{2, 2}; + auto r2 = r * scale; + // Regions can be grown and shrunk (e.g. to add ghost zones) + auto rg = r.grown(1); + auto rs = r.shrunk(1); + cout << " shifted region: " << r1 << "\n" + << " scaled region: " << r2 << "\n" + << " grown region: " << rg << "\n" + << " shrunk region: " << rs << "\n"; + + cout << " The bounding box containing a region:\n"; + auto bb = bounding_box(r); + cout << " bounding box: " << bb << "\n"; + + cout << " Regions can be treated as sets:\n"; + auto ri = r & r1; + auto ru = r | r1; + auto rd = r - r1; + auto rx = r ^ r1; + cout << " intersection: " << ri << "\n"; + cout << " union: " << ru << "\n"; + cout << " difference: " << rd << "\n"; + cout << " symmetric difference: " << rx << "\n"; + + cout << " Set tests:\n"; + cout << " r == r1 (equality): " << (r == r1) << "\n" + << " r <= r1 (is_subset_of): " << (r <= r1) << "\n" + << " r < r1 (is_strict_subset_of): " << (r < r1) << "\n"; + + cout << " Regions can be converted to a list of boxes:\n"; + cout << " rg - r:\n"; + for (const auto &bx : std::vector>(rg - r)) + cout << " " << bx << "\n"; + + // Don't benchmark the debug version; it uses an N^2 algorithm for self-checks + const int n = REGIONS_DEBUG ? 10 : 1000; + cout << " Create a list of " << n + << " 3d boxes and convert it to a region:\n"; + std::mt19937 gen; + std::uniform_int_distribution dist(0, 100); + const auto random_point = [&]() { + return NDPoint::make(3, [&](auto) { return dist(gen); }); + }; + const auto random_box = [&]() { + auto p1 = random_point(); + auto p2 = random_point(); + // Sort the points to avoid creating many empty boxes + return NDBox(min(p1, p2), max(p1, p2)); + }; + using clock = std::chrono::high_resolution_clock; + using timestamp = std::chrono::time_point; + using second = std::chrono::duration>; + const auto gettime = []() { return clock::now(); }; + const auto elapsed = [](timestamp t0, timestamp t1) { + return std::chrono::duration_cast(t1 - t0).count(); + }; + std::vector> boxlist; + for (int i = 0; i < n; ++i) + boxlist.push_back(random_box()); + timestamp t0 = gettime(); + NDRegion reg(3, boxlist); + timestamp t1 = gettime(); + cout << " reg.size: " << reg.size() << "\n" + << " reg.nboxes: " << reg.nboxes() << "\n" + << " runtime: " << elapsed(t0, t1) << " sec\n"; + cout << " Grow the region by 1 point:\n"; + timestamp t2 = gettime(); + NDRegion reg1 = reg.grown(1); + timestamp t3 = gettime(); + cout << " reg.size: " << reg1.size() << "\n" + << " reg.nboxes: " << reg1.nboxes() << "\n" + << " runtime: " << elapsed(t2, t3) << " sec\n"; } //////////////////////////////////////////////////////////////////////////////// @@ -314,6 +414,7 @@ int main() { ndbox_example(); region_example(); + ndregion_example(); return 0; } diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp index 10f4ee2518..dee888bc19 100644 --- a/include/openPMD/regions/NDBox.hpp +++ b/include/openPMD/regions/NDBox.hpp @@ -17,7 +17,7 @@ namespace detail { template class VBox { public: typedef T value_type; - typedef std::size_t size_type; + typedef typename VPoint::size_type size_type; virtual std::unique_ptr copy() const = 0; @@ -436,17 +436,17 @@ template class NDBox { return intersection(*b1.b, *b2.b); } - friend bool operator==(const NDBox &b, const std::vector &bs) { - return *b.b == bs; + friend bool operator==(const NDBox &b1, const std::vector &bs) { + return *b1.b == bs; } - friend bool operator==(const std::vector &bs, const NDBox &b) { - return *b.b == bs; + friend bool operator==(const std::vector &bs, const NDBox &b2) { + return *b2.b == bs; } - friend bool operator!=(const NDBox &b, const std::vector &bs) { - return *b.b != bs; + friend bool operator!=(const NDBox &b1, const std::vector &bs) { + return *b1.b != bs; } - friend bool operator!=(const std::vector &bs, const NDBox &b) { - return *b.b != bs; + friend bool operator!=(const std::vector &bs, const NDBox &b2) { + return *b2.b != bs; } friend std::vector operator-(const NDBox &b1, const NDBox &b2) { diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index b39beb49e3..308fcc6b42 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -32,7 +32,7 @@ namespace detail { template class VPoint { public: typedef T value_type; - typedef std::size_t size_type; + typedef std::ptrdiff_t size_type; virtual std::unique_ptr copy() const = 0; diff --git a/include/openPMD/regions/NDRegion.hpp b/include/openPMD/regions/NDRegion.hpp new file mode 100644 index 0000000000..267f593c1d --- /dev/null +++ b/include/openPMD/regions/NDRegion.hpp @@ -0,0 +1,584 @@ +#ifndef REGIONS_NDREGION_HPP +#define REGIONS_NDREGION_HPP + +#include "Helpers.hpp" +#include "NDBox.hpp" +#include "NDPoint.hpp" +#include "Region.hpp" + +namespace openPMD { +namespace Regions { + +template class NDRegion; + +namespace detail { + +// Abstract base helper class + +template class VRegion { +public: + typedef T value_type; + typedef typename VPoint::size_type size_type; + + virtual std::unique_ptr copy() const = 0; + + virtual operator std::vector>() const = 0; + + virtual ~VRegion() {} + + virtual size_type ndims() const = 0; + virtual bool empty() const = 0; + + virtual size_type size() const = 0; + + virtual size_type nboxes() const = 0; + + virtual std::unique_ptr operator>>(const NDPoint &d) const = 0; + virtual std::unique_ptr operator<<(const NDPoint &d) const = 0; + virtual VRegion &operator>>=(const NDPoint &d) = 0; + virtual VRegion &operator<<=(const NDPoint &d) = 0; + virtual std::unique_ptr operator*(const NDPoint &d) const = 0; + virtual VRegion &operator*=(const NDPoint &d) = 0; + virtual std::unique_ptr grown(const NDPoint &dlo, + const NDPoint &dup) const = 0; + virtual std::unique_ptr grown(const NDPoint &d) const = 0; + virtual std::unique_ptr grown(const T &n) const = 0; + virtual std::unique_ptr shrunk(const NDPoint &dlo, + const NDPoint &dup) const = 0; + virtual std::unique_ptr shrunk(const NDPoint &d) const = 0; + virtual std::unique_ptr shrunk(const T &n) const = 0; + + virtual bool operator==(const VRegion ®ion2) const = 0; + virtual bool operator!=(const VRegion ®ion2) const = 0; + virtual bool operator==(const NDBox &box2) const = 0; + virtual bool operator!=(const NDBox &box2) const = 0; + + virtual NDBox bounding_box1() const = 0; + + virtual std::unique_ptr operator&(const VRegion ®ion2) const = 0; + virtual std::unique_ptr operator|(const VRegion ®ion2) const = 0; + virtual std::unique_ptr operator^(const VRegion ®ion2) const = 0; + virtual std::unique_ptr operator-(const VRegion ®ion2) const = 0; + + virtual VRegion &operator&=(const VRegion ®ion) = 0; + virtual VRegion &operator|=(const VRegion ®ion) = 0; + virtual VRegion &operator^=(const VRegion ®ion) = 0; + virtual VRegion &operator-=(const VRegion ®ion) = 0; + + virtual std::unique_ptr + intersection1(const VRegion ®ion2) const = 0; + virtual std::unique_ptr setunion1(const VRegion ®ion2) const = 0; + virtual std::unique_ptr + symmetric_difference1(const VRegion ®ion2) const = 0; + virtual std::unique_ptr + difference1(const VRegion ®ion2) const = 0; + + virtual bool contains(const NDPoint &p) const = 0; + virtual bool isdisjoint1(const VRegion ®ion2) const = 0; + + virtual bool operator<=(const VRegion ®ion2) const = 0; + virtual bool operator>=(const VRegion ®ion2) const = 0; + virtual bool operator<(const VRegion ®ion2) const = 0; + virtual bool operator>(const VRegion ®ion2) const = 0; + + virtual bool is_subset_of(const VRegion ®ion) const = 0; + virtual bool is_superset_of(const VRegion ®ion) const = 0; + virtual bool is_strict_subset_of(const VRegion ®ion) const = 0; + virtual bool is_strict_superset_of(const VRegion ®ion) const = 0; + + virtual bool equal_to1(const VRegion &x) const = 0; + virtual bool less1(const VRegion &x) const = 0; + + virtual std::ostream &output(std::ostream &os) const = 0; +}; + +// Helper class wrapping Region + +template class WRegion final : public VRegion { + Region r; + +public: + using typename VRegion::value_type; + using typename VRegion::size_type; + + WRegion() : r{} {} + + WRegion(const WRegion &x) = default; + WRegion(WRegion &&) = default; + WRegion &operator=(const WRegion &) = default; + WRegion &operator=(WRegion &&) = default; + + WRegion(const Region &r_) : r(r_) {} + WRegion(Region &&r_) : r(std::move(r_)) {} + operator Region() const { return r; } + + WRegion(const Point &p) : r(p) {} + WRegion(const Box &b) : r(b) {} + WRegion(const std::vector> &boxes) : r(boxes) {} + + WRegion(const NDPoint &p) : r(Point(p)) {} + WRegion(const NDBox &b) : r(Box(b)) {} + WRegion(const std::vector> &boxes) + : r(helpers::convert_vector>(boxes)) {} + + operator std::vector>() const { return std::vector>(r); } + operator std::vector>() const override { + return helpers::convert_vector>(std::vector>(r)); + } + + std::unique_ptr> copy() const override { + return std::make_unique(*this); + } + + ~WRegion() override {} + + size_type ndims() const override { return r.ndims(); } + bool empty() const override { return r.empty(); } + + size_type size() const override { return r.size(); } + + size_type nboxes() const override { return r.nboxes(); } + + std::unique_ptr> operator>>(const NDPoint &d) const override { + return std::make_unique(r >> Point(d)); + } + std::unique_ptr> operator<<(const NDPoint &d) const override { + return std::make_unique(r << Point(d)); + } + VRegion &operator>>=(const NDPoint &d) override { + r >>= Point(d); + return *this; + } + VRegion &operator<<=(const NDPoint &d) override { + r <<= Point(d); + return *this; + } + std::unique_ptr> operator*(const NDPoint &d) const override { + return std::make_unique(r * Point(d)); + } + VRegion &operator*=(const NDPoint &d) override { + r *= Point(d); + return *this; + } + std::unique_ptr> grown(const NDPoint &dlo, + const NDPoint &dup) const override { + return std::make_unique( + r.grown(Point(dlo), Point(dup))); + } + std::unique_ptr> grown(const NDPoint &d) const override { + return std::make_unique(r.grown(Point(d))); + } + std::unique_ptr> grown(const T &n) const override { + return std::make_unique(r.grown(n)); + } + std::unique_ptr> shrunk(const NDPoint &dlo, + const NDPoint &dup) const override { + return std::make_unique( + r.shrunk(Point(dlo), Point(dup))); + } + std::unique_ptr> shrunk(const NDPoint &d) const override { + return std::make_unique(r.shrunk(Point(d))); + } + std::unique_ptr> shrunk(const T &n) const override { + return std::make_unique(r.shrunk(n)); + } + + bool operator==(const VRegion ®ion2) const override { + return r == dynamic_cast(region2); + } + bool operator!=(const VRegion ®ion2) const override { + return r != dynamic_cast(region2); + } + bool operator==(const NDBox &box2) const override { + return r == Box(box2); + } + bool operator!=(const NDBox &box2) const override { + return r != Box(box2); + } + + NDBox bounding_box1() const override { return NDBox(bounding_box(r)); } + + std::unique_ptr> + operator&(const VRegion ®ion2) const override { + return std::make_unique(r & + dynamic_cast(region2)); + } + std::unique_ptr> + operator|(const VRegion ®ion2) const override { + return std::make_unique(r | + dynamic_cast(region2)); + } + std::unique_ptr> + operator^(const VRegion ®ion2) const override { + return std::make_unique(r ^ + dynamic_cast(region2)); + } + std::unique_ptr> + operator-(const VRegion ®ion2) const override { + return std::make_unique(r - + dynamic_cast(region2)); + } + + VRegion &operator&=(const VRegion ®ion) override { + r &= dynamic_cast(region); + return *this; + } + VRegion &operator|=(const VRegion ®ion) override { + r |= dynamic_cast(region); + return *this; + } + VRegion &operator^=(const VRegion ®ion) override { + r ^= dynamic_cast(region); + return *this; + } + VRegion &operator-=(const VRegion ®ion) override { + r -= dynamic_cast(region); + return *this; + } + + std::unique_ptr> + intersection1(const VRegion ®ion2) const override { + return std::make_unique( + intersection(r, dynamic_cast(region2))); + } + std::unique_ptr> + setunion1(const VRegion ®ion2) const override { + return std::make_unique( + setunion(r, dynamic_cast(region2))); + } + std::unique_ptr> + symmetric_difference1(const VRegion ®ion2) const override { + return std::make_unique( + symmetric_difference(r, dynamic_cast(region2))); + } + std::unique_ptr> + difference1(const VRegion ®ion2) const override { + return std::make_unique( + difference(r, dynamic_cast(region2))); + } + + bool contains(const NDPoint &p) const override { + return r.contains(Point(p)); + } + bool isdisjoint1(const VRegion ®ion2) const override { + return isdisjoint(r, dynamic_cast(region2)); + } + + bool operator<=(const VRegion ®ion2) const override { + return r <= dynamic_cast(region2); + } + bool operator>=(const VRegion ®ion2) const override { + return r >= dynamic_cast(region2); + } + bool operator<(const VRegion ®ion2) const override { + return r < dynamic_cast(region2); + } + bool operator>(const VRegion ®ion2) const override { + return r > dynamic_cast(region2); + } + + bool is_subset_of(const VRegion ®ion) const override { + return r.is_subset_of(dynamic_cast(region)); + } + bool is_superset_of(const VRegion ®ion) const override { + return r.is_superset_of(dynamic_cast(region)); + } + bool is_strict_subset_of(const VRegion ®ion) const override { + return r.is_strict_subset_of(dynamic_cast(region)); + } + bool is_strict_superset_of(const VRegion ®ion) const override { + return r.is_strict_superset_of(dynamic_cast(region)); + } + + bool equal_to1(const VRegion &x) const override { + return std::equal_to>()(r, dynamic_cast(x).r); + } + bool less1(const VRegion &x) const override { + return std::less>()(r, dynamic_cast(x).r); + } + + std::ostream &output(std::ostream &os) const override { return os << r; } +}; + +template +std::unique_ptr> make_VRegion(const std::size_t D, + const Args &...args) { + switch (D) { + case 0: + return std::make_unique>(args...); + case 1: + return std::make_unique>(args...); + case 2: + return std::make_unique>(args...); + case 3: + return std::make_unique>(args...); + case 4: + return std::make_unique>(args...); + case 5: + return std::make_unique>(args...); + default: + abort(); + } +} + +} // namespace detail + +/** A Region + * + * The dimension (number of component) of the Region is only known at + * run-time. @see Region + * + * A region is described by two points, its lower bound (inclusive) and + * upper bound (exclusive). If the lower bound not less than the upper + * bound, the region is empty. Empty regiones are fine (similar to an empty + * array). + */ +template class NDRegion { + template using VRegion = detail::VRegion; + + template friend class NDRegion; + friend struct std::equal_to; + friend struct std::less; + + std::unique_ptr> r; + + NDRegion(std::unique_ptr> r_) : r(std::move(r_)) {} + +public: + /** Component type + */ + typedef typename VRegion::value_type value_type; + /** Return type of Region::size() + */ + typedef typename VRegion::size_type size_type; + + /** Create an invalid Region + */ + NDRegion() : r() {} + /** Create a value-initialized Region with D components + */ + NDRegion(const size_type D) : r(detail::make_VRegion(D)) {} + + NDRegion(const NDRegion &x) : r(x.r ? x.r->copy() : nullptr) {} + NDRegion(NDRegion &&) = default; + NDRegion &operator=(const NDRegion &x) { + r = x.r ? x.r->copy() : nullptr; + return *this; + } + NDRegion &operator=(NDRegion &&) = default; + + template + NDRegion(const Region &r_) + : r(std::make_unique>(r_)) {} + template + NDRegion(Region &&r_) + : r(std::make_unique>(std::move(r_))) {} + template operator Region() const { + return Region(dynamic_cast &>(*r)); + } + + template + NDRegion(const Point &p) + : r(std::make_unique>(detail::WRegion(p))) {} + template + NDRegion(const Box &b) + : r(std::make_unique>(detail::WRegion(b))) {} + template + NDRegion(const std::vector> &boxes) + : r(std::make_unique>(detail::WRegion(boxes))) {} + template operator std::vector>() const { + return std::vector>( + dynamic_cast &>(*r)); + } + + NDRegion(const NDPoint &p) : r(detail::make_VRegion(p.ndims(), p)) {} + NDRegion(const NDBox &b) : r(detail::make_VRegion(b.ndims(), b)) {} + NDRegion(const size_type D, const std::vector> &boxes) + : r(detail::make_VRegion(D, boxes)) {} + operator std::vector>() const { return std::vector>(*r); } + + /** Check whether a Region is valid + * + * A valid Region knows its number of dimensions, and its components + * are initialized. An invalid Region does not know its number of + * dimensions and holds no data, similar to a null pointer. + * + * Most other member functions must not be called for invalid + * Regions. + */ + bool has_value() const { return bool(r); } + + size_type ndims() const { return r->ndims(); } + bool empty() const { return r->empty(); } + + size_type size() const { return r->size(); } + + size_type nboxes() const { return r->nboxes(); } + + NDRegion operator>>(const NDPoint &d) const { return NDRegion(*r >> d); } + NDRegion operator<<(const NDPoint &d) const { return NDRegion(*r << d); } + NDRegion &operator>>=(const NDPoint &d) { + *r >>= d; + return *this; + } + NDRegion &operator<<=(const NDPoint &d) { + *r <<= d; + return *this; + } + NDRegion operator*(const NDPoint &d) const { return NDRegion(*r * d); } + NDRegion &operator*=(const NDPoint &d) { + *r *= d; + return *this; + } + + NDRegion grown(const NDPoint &dlo, const NDPoint &dup) const { + return NDRegion(r->grown(dlo, dup)); + } + NDRegion grown(const NDPoint &d) const { return NDRegion(r->grown(d)); } + NDRegion grown(const T &n) const { return NDRegion(r->grown(n)); } + NDRegion shrunk(const NDPoint &dlo, const NDPoint &dup) const { + return NDRegion(r->shrunk(dlo, dup)); + } + NDRegion shrunk(const NDPoint &d) const { return NDRegion(r->shrunk(d)); } + NDRegion shrunk(const T &n) const { return NDRegion(r->shrunk(n)); } + + friend bool operator==(const NDRegion ®ion1, const NDRegion ®ion2) { + return *region1.r == *region2.r; + } + friend bool operator!=(const NDRegion ®ion1, const NDRegion ®ion2) { + return *region1.r != *region2.r; + } + friend bool operator==(const NDRegion ®ion1, const NDBox &box2) { + return *region1.r == box2; + } + friend bool operator!=(const NDRegion ®ion1, const NDBox &box2) { + return *region1.r != box2; + } + friend bool operator==(const NDBox &box1, const NDRegion ®ion2) { + return *region2.r == box1; + } + friend bool operator!=(const NDBox &box1, const NDRegion ®ion2) { + return *region2.r != box1; + } + + friend NDBox bounding_box(const NDRegion ®ion) { + return region.r->bounding_box1(); + } + + friend NDRegion operator&(const NDRegion ®ion1, const NDRegion ®ion2) { + return NDRegion(*region1.r & *region2.r); + } + friend NDRegion operator|(const NDRegion ®ion1, const NDRegion ®ion2) { + return NDRegion(*region1.r | *region2.r); + } + friend NDRegion operator^(const NDRegion ®ion1, const NDRegion ®ion2) { + return NDRegion(*region1.r ^ *region2.r); + } + friend NDRegion operator-(const NDRegion ®ion1, const NDRegion ®ion2) { + return NDRegion(*region1.r - *region2.r); + } + + NDRegion &operator&=(const NDRegion ®ion) { + *r &= *region.r; + return *this; + } + NDRegion &operator|=(const NDRegion ®ion) { + *r |= *region.r; + return *this; + } + NDRegion &operator^=(const NDRegion ®ion) { + *r ^= *region.r; + return *this; + } + NDRegion &operator-=(const NDRegion ®ion) { + *r -= *region.r; + return *this; + } + + friend NDRegion intersection(const NDRegion ®ion1, + const NDRegion ®ion2) { + return NDRegion(intersection(*region1.r, *region2.r)); + } + friend NDRegion setunion(const NDRegion ®ion1, const NDRegion ®ion2) { + return NDRegion(setunion(*region1.r, *region2.r)); + } + friend NDRegion symmetric_difference(const NDRegion ®ion1, + const NDRegion ®ion2) { + return NDRegion(symmetric_difference(*region1.r, *region2.r)); + } + friend NDRegion difference(const NDRegion ®ion1, const NDRegion ®ion2) { + return NDRegion(difference(*region1.r, *region2.r)); + } + + bool contains(const NDPoint &p) const { return r->contains(p); } + friend bool isdisjoint(const NDRegion ®ion1, const NDRegion ®ion2) { + return region1.r->isdisjoint1(*region2.r); + } + + friend bool operator<=(const NDRegion ®ion1, const NDRegion ®ion2) { + return *region1.r <= *region2.r; + } + friend bool operator>=(const NDRegion ®ion1, const NDRegion ®ion2) { + return *region1.r >= *region2.r; + } + friend bool operator<(const NDRegion ®ion1, const NDRegion ®ion2) { + return *region1.r < *region2.r; + } + friend bool operator>(const NDRegion ®ion1, const NDRegion ®ion2) { + return *region1.r > *region2.r; + } + + bool is_subset_of(const NDRegion ®ion) const { + return r->is_subset_of(*region.r); + } + bool is_superset_of(const NDRegion ®ion) const { + return r->is_superset_of(*region.r); + } + bool is_strict_subset_of(const NDRegion ®ion) const { + return r->is_strict_subset_of(*region.r); + } + bool is_strict_superset_of(const NDRegion ®ion) const { + return r->is_strict_superset_of(*region.r); + } + + /** Output a region + */ + friend std::ostream &operator<<(std::ostream &os, const NDRegion &x) { + if (x.r) + x.r->output(os); + else + os << "{INVALID}"; + return os; + } +}; + +} // namespace Regions +} // namespace openPMD + +namespace std { + +template struct equal_to> { + constexpr bool operator()(const openPMD::Regions::NDRegion &x, + const openPMD::Regions::NDRegion &y) const { + if (!x.has_value() && !y.has_value()) + return true; + if (!x.has_value() || !y.has_value()) + return false; + return x.r->equal_to1(*y.r); + } +}; + +template struct less> { + constexpr bool operator()(const openPMD::Regions::NDRegion &x, + const openPMD::Regions::NDRegion &y) const { + if (!x.has_value()) + return true; + if (!y.has_value()) + return false; + return x.r->less1(*y.r); + } +}; + +} // namespace std + +#endif // #ifndef REGIONS_NDREGION_HPP diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 265e49b700..fc5c54eefd 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -167,7 +167,9 @@ template class Region { } // Set comparison operators - bool contains(const Point &p) const { return !isdisjoint(Region(p)); } + bool contains(const Point &p) const { + return !isdisjoint(*this, Region(p)); + } friend bool isdisjoint(const Region ®ion1, const Region ®ion2) { return (region1 & region2).empty(); } @@ -188,8 +190,12 @@ template class Region { bool is_subset_of(const Region ®ion) const { return *this <= region; } bool is_superset_of(const Region ®ion) const { return *this >= region; } - bool is_strict_subset_of(const Region ®ion) const { return *this < region; } - bool is_strict_superset_of(const Region ®ion) const { return *this > region; } + bool is_strict_subset_of(const Region ®ion) const { + return *this < region; + } + bool is_strict_superset_of(const Region ®ion) const { + return *this > region; + } friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { os << "{"; @@ -562,7 +568,7 @@ template class Region { return grown(-dlo, -dup); } Region shrunk(const Point &d) const { return shrunk(d, d); } - Region shrunk(T n) const { return shrunk(Point(n)); } + Region shrunk(T n) const { return shrunk(Point::pure(n)); } // Set operations friend Box bounding_box(const Region ®ion) { @@ -613,7 +619,9 @@ template class Region { } // Set comparison operators - bool contains(const Point &p) const { return !isdisjoint(Region(p)); } + bool contains(const Point &p) const { + return !isdisjoint(*this, Region(p)); + } friend bool isdisjoint(const Region ®ion1, const Region ®ion2) { return (region1 & region2).empty(); } @@ -634,8 +642,12 @@ template class Region { bool is_subset_of(const Region ®ion) const { return *this <= region; } bool is_superset_of(const Region ®ion) const { return *this >= region; } - bool is_strict_subset_of(const Region ®ion) const { return *this < region; } - bool is_strict_superset_of(const Region ®ion) const { return *this > region; } + bool is_strict_subset_of(const Region ®ion) const { + return *this < region; + } + bool is_strict_superset_of(const Region ®ion) const { + return *this > region; + } friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { os << "{"; @@ -1112,7 +1124,9 @@ template class Region { } // Set comparison operators - bool contains(const Point &p) const { return !isdisjoint(Region(p)); } + bool contains(const Point &p) const { + return !isdisjoint(*this, Region(p)); + } friend bool isdisjoint(const Region ®ion1, const Region ®ion2) { return (region1 & region2).empty(); } @@ -1133,8 +1147,12 @@ template class Region { bool is_subset_of(const Region ®ion) const { return *this <= region; } bool is_superset_of(const Region ®ion) const { return *this >= region; } - bool is_strict_subset_of(const Region ®ion) const { return *this < region; } - bool is_strict_superset_of(const Region ®ion) const { return *this > region; } + bool is_strict_subset_of(const Region ®ion) const { + return *this < region; + } + bool is_strict_superset_of(const Region ®ion) const { + return *this > region; + } friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { os << "{"; diff --git a/include/openPMD/regions/Regions.hpp b/include/openPMD/regions/Regions.hpp index 62e0433174..2d08a8158b 100644 --- a/include/openPMD/regions/Regions.hpp +++ b/include/openPMD/regions/Regions.hpp @@ -7,6 +7,6 @@ #include "NDBox.hpp" #include "NDPoint.hpp" -//TODO #include "NDRegion.hpp" +#include "NDRegion.hpp" #endif // #ifndef REGIONS_REGIONS_HPP diff --git a/test/RegionsBoxTest.cpp b/test/BoxTest.cpp similarity index 99% rename from test/RegionsBoxTest.cpp rename to test/BoxTest.cpp index 521d86df64..c88b686bfe 100644 --- a/test/RegionsBoxTest.cpp +++ b/test/BoxTest.cpp @@ -29,7 +29,7 @@ template void test_Box(const B &box) { const auto randb = [&]() { if (D == 0) { if (dist0(gen) < 5) - return B(p, p); + return B(p, p); // empty box else return B(p); } diff --git a/test/RegionsPointTest.cpp b/test/PointTest.cpp similarity index 100% rename from test/RegionsPointTest.cpp rename to test/PointTest.cpp diff --git a/test/RegionsRegionTest.cpp b/test/RegionTest.cpp similarity index 90% rename from test/RegionsRegionTest.cpp rename to test/RegionTest.cpp index a6c4fd9766..141df92618 100644 --- a/test/RegionsRegionTest.cpp +++ b/test/RegionTest.cpp @@ -1,5 +1,4 @@ -#include -#include +#include #include #include @@ -31,7 +30,7 @@ template void test_Region(const R &r) { const auto randb = [&]() { if (D == 0) { if (dist0(gen) < 5) - return B(); + return B(p, p); // empty box else return B(p); } @@ -48,12 +47,12 @@ template void test_Region(const R &r) { const auto randr = [&]() { if (D == 0) { if (dist0(gen) < 5) - return R(); + return R(B(p, p)); // empty region else return R(B(p)); } const int nboxes = dist0(gen) / 2; - R nr; + R nr(B(p, p)); // empty region for (int n = 0; n < nboxes; ++n) nr |= randb(); return nr; @@ -175,8 +174,6 @@ TEST_CASE("Region", "[regions]") { test_Region(Region()); } TEST_CASE("Region", "[regions]") { test_Region(Region()); } TEST_CASE("Region", "[regions]") { test_Region(Region()); } -// TODO -#if 0 TEST_CASE("NDRegion(0)", "[regions]") { test_Region(NDRegion(0)); } @@ -190,8 +187,15 @@ TEST_CASE("NDRegion(3)", "[regions]") { test_Region(NDRegion(3)); } -TEST_CASE("NDRegion(0)", "[regions]") { test_Region(NDRegion(0)); } -TEST_CASE("NDRegion(1)", "[regions]") { test_Region(NDRegion(1)); } -TEST_CASE("NDRegion(2)", "[regions]") { test_Region(NDRegion(2)); } -TEST_CASE("NDRegion(3)", "[regions]") { test_Region(NDRegion(3)); } -#endif +TEST_CASE("NDRegion(0)", "[regions]") { + test_Region(NDRegion(0)); +} +TEST_CASE("NDRegion(1)", "[regions]") { + test_Region(NDRegion(1)); +} +TEST_CASE("NDRegion(2)", "[regions]") { + test_Region(NDRegion(2)); +} +TEST_CASE("NDRegion(3)", "[regions]") { + test_Region(NDRegion(3)); +} From 93cc8d359cb0adfbb352fb0bf5bfd2804d99d338 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 22:24:34 -0400 Subject: [PATCH 53/68] CMake: Correct C++17 tests --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f4d3fde87..f919d8f7af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -794,7 +794,7 @@ if(openPMD_BUILD_TESTING) else() target_link_libraries(${testname}Tests PRIVATE CatchMain) endif() - if(${testname} MATCHES "Regions.+$") + if(${testname} MATCHES "(Point)|(Box)|(Region)") target_compile_features(${testname}Tests PUBLIC cxx_std_17) set_target_properties(${testname}Tests PROPERTIES CXX_EXTENSIONS OFF From aa6b0bed19d7f5919466d8764b501882d2450033 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 22:38:28 -0400 Subject: [PATCH 54/68] CI: Disable regions on icc --- .github/workflows/unix.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 44a1d4dd89..eef0ce6d9e 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -318,8 +318,9 @@ jobs: sudo .github/workflows/dependencies/install_icc - name: Build env: {CXXFLAGS: -Werror -Wno-deprecated-declarations} - # C++17 not properly supported (false positives for unused - # arguments and missing return statements with `if constexpr`) + # C++17 is advertised, but is not properly supported (false + # positives for unused arguments and missing return statements + # with `if constexpr`) run: | set +e; source /opt/intel/oneapi/setvars.sh; set -e mkdir build && cd build @@ -329,7 +330,8 @@ jobs: -DCMAKE_C_COMPILER=$(which icc) \ -DCMAKE_CXX_COMPILER=$(which icpc) \ -DopenPMD_USE_PYTHON=OFF \ - -DopenPMD_USE_MPI=OFF + -DopenPMD_USE_MPI=OFF \ + -DopenPMD_USE_REGIONS=OFF cmake --build . --parallel 2 ctest --output-on-failure From c435db20981ae98a1e3b6788a6b0cb5c210fdb60 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 22:57:55 -0400 Subject: [PATCH 55/68] Tests: Compare ndims with signed integer --- .github/workflows/unix.yml | 2 +- test/PointTest.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index eef0ce6d9e..d8beee948a 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -90,7 +90,7 @@ jobs: mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=OFF -DopenPMD_USE_HDF5=ON -DopenPMD_USE_INVASIVE_TESTS=ON + cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=OFF -DopenPMD_USE_HDF5=ON -DopenPMD_USE_REGIONS=OFF -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 ctest --output-on-failure diff --git a/test/PointTest.cpp b/test/PointTest.cpp index 567f683ead..558ea64673 100644 --- a/test/PointTest.cpp +++ b/test/PointTest.cpp @@ -56,10 +56,10 @@ template void test_Point_bool(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n = p; - CHECK(n.ndims() == D); + CHECK(n.ndims() == std::ptrdiff_t(D)); for (std::size_t d = 0; d < D; ++d) CHECK(n[d] == 0); - CHECK(n.size() == D); + CHECK(n.size() == std::ptrdiff_t(D)); const P x = randp(); const P y = randp(); @@ -188,7 +188,7 @@ template void test_Point_int(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n(p); - CHECK(n.size() == D); + CHECK(n.size() == std::ptrdiff_t(D)); for (std::size_t d = 0; d < D; ++d) CHECK(n[d] == 0); @@ -349,7 +349,7 @@ template void test_Point_float(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n(p); - CHECK(n.size() == D); + CHECK(n.size() == std::ptrdiff_t(D)); for (std::size_t d = 0; d < D; ++d) CHECK(n[d] == 0); @@ -376,16 +376,16 @@ template void test_Point_float(const P &p) { for (std::size_t d = 0; d < D; ++d) { const auto a1 = x[d]; const auto x1 = x.erase(d); - CHECK(x1.ndims() == D - 1); + CHECK(x1.ndims() == std::ptrdiff_t(D) - 1); const auto x2 = x1.insert(d, a1); - CHECK(x2.ndims() == D); + CHECK(x2.ndims() == std::ptrdiff_t(D)); CHECK(eq_helper(x2, x)); } } // insert-remove is no-op for (std::size_t d = 0; d <= D; ++d) { const auto x1 = x.insert(d, a); - CHECK(x1.ndims() == D + 1); + CHECK(x1.ndims() == std::ptrdiff_t(D) + 1); CHECK(x1[d] == a); CHECK(eq(x1.erase(d), x)); } From 93ad888304f6154ec2bf79d78f0d2aee0c67fdf1 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Fri, 25 Jun 2021 23:59:18 -0400 Subject: [PATCH 56/68] CI: Debug --- .github/workflows/unix.yml | 5 +++-- include/openPMD/regions/Point.hpp | 30 ++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index d8beee948a..af80e8957a 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -115,7 +115,7 @@ jobs: mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON + cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=ON -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_REGIONS=OFF -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 ctest --output-on-failure @@ -145,7 +145,7 @@ jobs: mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=OFF -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_INVASIVE_TESTS=ON + cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DopenPMD_USE_PYTHON=OFF -DopenPMD_USE_MPI=ON -DopenPMD_USE_HDF5=ON -DopenPMD_USE_ADIOS1=OFF -DopenPMD_USE_ADIOS2=ON -DopenPMD_USE_REGIONS=OFF -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 export OPENPMD2_ADIOS2_SCHEMA=20210209 ctest --output-on-failure @@ -253,6 +253,7 @@ jobs: -DopenPMD_USE_HDF5=ON \ -DopenPMD_USE_ADIOS1=ON \ -DopenPMD_USE_ADIOS2=ON \ + -DopenPMD_USE_REGIONS=OFF \ -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 ctest --output-on-failure diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index b1f0b6a0e5..fd7e5a590f 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -444,18 +444,28 @@ template class Point { return fold([](bool r, const T &a) { return r || a; }, false, x); } friend constexpr T max_element(const Point &x) { - using std::max; - const T neutral = std::is_floating_point_v - ? -std::numeric_limits::infinity() - : std::numeric_limits::lowest(); - return fold([](const T &r, const T &a) { return max(r, a); }, neutral, x); + if constexpr (std::is_same_v) { + const T neutral = false; + return fold([](const T &r, const T &a) { return r || a; }, neutral, x); + } else { + using std::max; + const T neutral = std::is_floating_point_v + ? -std::numeric_limits::infinity() + : std::numeric_limits::lowest(); + return fold([](const T &r, const T &a) { return max(r, a); }, neutral, x); + } } friend constexpr T min_element(const Point &x) { - using std::min; - const T neutral = std::is_floating_point_v - ? std::numeric_limits::infinity() - : std::numeric_limits::max(); - return fold([](const T &r, const T &a) { return min(r, a); }, neutral, x); + if constexpr (std::is_same_v) { + const T neutral = true; + return fold([](const T &r, const T &a) { return r && a; }, neutral, x); + } else { + using std::min; + const T neutral = std::is_floating_point_v + ? std::numeric_limits::infinity() + : std::numeric_limits::max(); + return fold([](const T &r, const T &a) { return min(r, a); }, neutral, x); + } } friend constexpr T product(const Point &x) { return fold([](const T &r, const T &a) { return r * a; }, T(1), x); From 0c46a717a5e65fb4f05f39f6b6d4ef075798a327 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sat, 26 Jun 2021 00:15:40 -0400 Subject: [PATCH 57/68] CI: Disable regions on Windows --- .appveyor.yml | 2 +- include/openPMD/regions/Point.hpp | 30 ++++++++++-------------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index c10a31646f..e5d066004a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -121,7 +121,7 @@ before_build: # - cmd: if "%TARGET_ARCH%"=="x64" "C:\Program Files (x86)\Microsoft Visual Studio 15.9\VC\vcvarsall.bat" amd64 # CMake configure - - cmd: cmake -G "%OPENPMD_CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_SHARED_LIBS=%SHARED% -DBUILD_TESTING=ON -DCMAKE_INSTALL_PREFIX="%CONDA_INSTALL_LOCN%" -DCMAKE_INSTALL_BINDIR="Library\bin" ".." + - cmd: cmake -G "%OPENPMD_CMAKE_GENERATOR%" -DopenPMD_USE_REGION=OFF -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_SHARED_LIBS=%SHARED% -DBUILD_TESTING=ON -DCMAKE_INSTALL_PREFIX="%CONDA_INSTALL_LOCN%" -DCMAKE_INSTALL_BINDIR="Library\bin" ".." build_script: - cmd: cmake --build . --config %CONFIGURATION% diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index fd7e5a590f..b1f0b6a0e5 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -444,28 +444,18 @@ template class Point { return fold([](bool r, const T &a) { return r || a; }, false, x); } friend constexpr T max_element(const Point &x) { - if constexpr (std::is_same_v) { - const T neutral = false; - return fold([](const T &r, const T &a) { return r || a; }, neutral, x); - } else { - using std::max; - const T neutral = std::is_floating_point_v - ? -std::numeric_limits::infinity() - : std::numeric_limits::lowest(); - return fold([](const T &r, const T &a) { return max(r, a); }, neutral, x); - } + using std::max; + const T neutral = std::is_floating_point_v + ? -std::numeric_limits::infinity() + : std::numeric_limits::lowest(); + return fold([](const T &r, const T &a) { return max(r, a); }, neutral, x); } friend constexpr T min_element(const Point &x) { - if constexpr (std::is_same_v) { - const T neutral = true; - return fold([](const T &r, const T &a) { return r && a; }, neutral, x); - } else { - using std::min; - const T neutral = std::is_floating_point_v - ? std::numeric_limits::infinity() - : std::numeric_limits::max(); - return fold([](const T &r, const T &a) { return min(r, a); }, neutral, x); - } + using std::min; + const T neutral = std::is_floating_point_v + ? std::numeric_limits::infinity() + : std::numeric_limits::max(); + return fold([](const T &r, const T &a) { return min(r, a); }, neutral, x); } friend constexpr T product(const Point &x) { return fold([](const T &r, const T &a) { return r * a; }, T(1), x); From 68d409a87ed41dd5a80fb8f421448c7cca4ce2b6 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sat, 26 Jun 2021 12:17:15 -0400 Subject: [PATCH 58/68] CI: Correct typo --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index e5d066004a..e0aaf575d1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -121,7 +121,7 @@ before_build: # - cmd: if "%TARGET_ARCH%"=="x64" "C:\Program Files (x86)\Microsoft Visual Studio 15.9\VC\vcvarsall.bat" amd64 # CMake configure - - cmd: cmake -G "%OPENPMD_CMAKE_GENERATOR%" -DopenPMD_USE_REGION=OFF -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_SHARED_LIBS=%SHARED% -DBUILD_TESTING=ON -DCMAKE_INSTALL_PREFIX="%CONDA_INSTALL_LOCN%" -DCMAKE_INSTALL_BINDIR="Library\bin" ".." + - cmd: cmake -G "%OPENPMD_CMAKE_GENERATOR%" -DopenPMD_USE_REGIONS=OFF -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_SHARED_LIBS=%SHARED% -DBUILD_TESTING=ON -DCMAKE_INSTALL_PREFIX="%CONDA_INSTALL_LOCN%" -DCMAKE_INSTALL_BINDIR="Library\bin" ".." build_script: - cmd: cmake --build . --config %CONFIGURATION% From 2fd5c9b04d59990b7e90fbc7187ce195ef10ae01 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sat, 26 Jun 2021 22:23:36 -0400 Subject: [PATCH 59/68] Regions: Add comments --- include/openPMD/regions/Box.hpp | 79 +++++++ include/openPMD/regions/NDBox.hpp | 6 + include/openPMD/regions/NDPoint.hpp | 310 ++++++++++++++++++++++++---- include/openPMD/regions/Point.hpp | 231 +++++++++++++++------ 4 files changed, 524 insertions(+), 102 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index 6823d813c0..f42d5aa609 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -188,24 +188,44 @@ template class Box { explicit Box(const Point &p) : lo(p), hi(p + T(1)) {} // Predicates + /** Number of dimensions + */ size_type ndims() const { return D; } + /** Whether a box is empty + */ bool empty() const { return any(hi <= lo); } + /** Lower bound (inclusive) + */ Point lower() const { return lo; } + /** Upper bound (exclusive) + */ Point upper() const { return hi; } + /** Shape, i.e. the "size" in each direction + */ Point shape() const { return max(hi - lo, T(0)); } + /** Size, the total number of contained points + */ size_type size() const { return product(shape()); } // Shift and scale operators + /** Shift a box to the right (upwards). The shift can be negative, which + * shifts left. + */ Box &operator>>=(const Point &p) { lo += p; hi += p; return *this; } + /** Shift a box to the left (downwards). The shift can be negative, which + * shifts right. + */ Box &operator<<=(const Point &p) { lo -= p; hi -= p; return *this; } + /** Scale a box + */ Box &operator*=(const Point &p) { lo *= p; hi *= p; @@ -214,6 +234,12 @@ template class Box { Box operator>>(const Point &p) const { return Box(*this) >>= p; } Box operator<<(const Point &p) const { return Box(*this) <<= p; } Box operator*(const Point &p) const { return Box(*this) *= p; } + /** Grow a box by given amounts in each direction. + * + * The growth can be negative, which shrinks the box. If a box is + * shrunk too much it becomes empty. Growing an empty box always + * results in an empty box. + */ Box grown(const Point &dlo, const Point &dup) const { if (empty()) return Box(); @@ -224,6 +250,12 @@ template class Box { } Box grown(const Point &d) const { return grown(d, d); } Box grown(const T &d) const { return grown(Point::pure(d)); } + /** Grow a box by given amounts in each direction. + * + * The shrinkage can be negative, which grows the box. If a box is + * shrunk too much it becomes empty. Growing an empty box always + * results in an empty box. + */ Box shrunk(const Point &dlo, const Point &dup) const { return grown(-dlo, -dup); } @@ -231,6 +263,10 @@ template class Box { Box shrunk(const T &d) const { return shrunk(Point::pure(d)); } // Comparison operators + /** Compare two boxes + * + * (All empty boxes are equal.) + */ friend bool operator==(const Box &b1, const Box &b2) { if (b1.empty() && b2.empty()) return true; @@ -241,14 +277,21 @@ template class Box { friend bool operator!=(const Box &b1, const Box &b2) { return !(b1 == b2); } // Set comparison operators + /** Check whether a box contains a given point + */ bool contains(const Point &p) const { if (empty()) return false; return all(p >= lo && p < hi); } + /** Check whether two boxes are disjoint, i.e. whether they have no point in + * common + */ friend bool isdisjoint(const Box &b1, const Box &b2) { return (b1 & b2).empty(); } + /** Check whether Box 1 is (completely) contained in Box 2 + */ friend bool operator<=(const Box &b1, const Box &b2) { if (b1.empty()) return true; @@ -256,17 +299,39 @@ template class Box { return false; return all(b1.lo >= b2.lo && b1.hi <= b2.hi); } + /** Check whether Box 1 (completely) contains Box 2 + */ friend bool operator>=(const Box &b1, const Box &b2) { return b2 <= b1; } + /** Check whether Box 1 is (completely) contained in Box 2, and Box + * 2 is larger than Box 1 + */ friend bool operator<(const Box &b1, const Box &b2) { return b1 <= b2 && b1 != b2; } + /** Check whether Box 1 (completely) contains in Box 2, and Box + * 1 is larger than Box 2 + */ friend bool operator>(const Box &b1, const Box &b2) { return b2 < b1; } + /** Check whether a Box is a subset of another Box. This is equivalen to `<=`. + */ bool is_subset_of(const Box &b) const { return *this <= b; } + /** Check whether a Box is a superset of another Box. This is equivalen to + * `>=`. + */ bool is_superset_of(const Box &b) const { return *this >= b; } + /** Check whether a Box is a strict subset of another Box. This is equivalen + * to `<`. + */ bool is_strict_subset_of(const Box &b) const { return *this < b; } + /** Check whether a Box is a strict superset of another Box. This is equivalen + * to `>`. + */ bool is_strict_superset_of(const Box &b) const { return *this > b; } // Set operations + /** Calculate the bounding box of two Boxes. This is the smallest Box that + * contains both Boxes. + */ friend Box bounding_box(const Box &b1, const Box &b2) { if (b1.empty()) return b2; @@ -290,9 +355,22 @@ template class Box { #endif return r; } + /** Calculate the intersection between two Boxes + * + * Other set operations (union, difference, symmetric difference) are not + * supported for Boxes; use Regions instead. + */ Box &operator&=(const Box &b) { return *this = *this & b; } + /** Calculate the intersection between two Boxes + */ friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } +#if 0 + /** Check whether a Box is equal to the union of a set of other, disjoint + * boxes + * + * The other boxes must be disjoint; this is not checked. + */ friend bool operator==(const Box &b, const std::vector &bs) { // This assumes that the elements of bs are disjoint size_type sz = 0; @@ -441,6 +519,7 @@ template class Box { friend std::vector symmetric_difference(const Box &b1, const Box &b2) { return b1 ^ b2; } +#endif /** Output a box */ diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp index dee888bc19..aa9a1a12c9 100644 --- a/include/openPMD/regions/NDBox.hpp +++ b/include/openPMD/regions/NDBox.hpp @@ -65,6 +65,7 @@ template class VBox { virtual VBox &operator&=(const VBox &b) = 0; virtual std::unique_ptr intersection1(const VBox &b2) const = 0; +#if 0 virtual bool operator==(const std::vector> &bs) const = 0; virtual bool operator!=(const std::vector> &bs) const = 0; @@ -76,6 +77,7 @@ template class VBox { virtual std::vector> operator^(const VBox &b2) const = 0; virtual std::vector> symmetric_difference1(const VBox &b2) const = 0; +#endif virtual bool equal_to1(const VBox &x) const = 0; virtual std::size_t hash1() const = 0; @@ -217,6 +219,7 @@ template class WBox final : public VBox { intersection(b, dynamic_cast(b2).b)); } +#if 0 bool operator==(const std::vector> &bs) const override { return b == helpers::convert_vector>(bs); } @@ -251,6 +254,7 @@ template class WBox final : public VBox { return helpers::convert_vector>( symmetric_difference(b, dynamic_cast(b2).b)); } +#endif bool equal_to1(const VBox &x) const override { return std::equal_to>()(b, dynamic_cast(x).b); @@ -436,6 +440,7 @@ template class NDBox { return intersection(*b1.b, *b2.b); } +#if 0 friend bool operator==(const NDBox &b1, const std::vector &bs) { return *b1.b == bs; } @@ -471,6 +476,7 @@ template class NDBox { const NDBox &b2) { return symmetric_difference(*b1.b, *b2.b); } +#endif /** Output a box */ diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 308fcc6b42..9b7cb39512 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -255,7 +255,10 @@ template class WPoint final : public VPoint { return std::make_unique(-p); } std::unique_ptr> operator~() const override { - return std::make_unique(~p); + if constexpr (std::is_integral_v) + return std::make_unique(~p); + else + std::abort(); } std::unique_ptr> operator!() const override { return std::make_unique>(!p); @@ -274,16 +277,28 @@ template class WPoint final : public VPoint { return std::make_unique(p / dynamic_cast(x).p); } std::unique_ptr> operator%(const VPoint &x) const override { - return std::make_unique(p % dynamic_cast(x).p); + if constexpr (std::is_integral_v) + return std::make_unique(p % dynamic_cast(x).p); + else + std::abort(); } std::unique_ptr> operator&(const VPoint &x) const override { - return std::make_unique(p & dynamic_cast(x).p); + if constexpr (std::is_integral_v) + return std::make_unique(p & dynamic_cast(x).p); + else + std::abort(); } std::unique_ptr> operator|(const VPoint &x) const override { - return std::make_unique(p | dynamic_cast(x).p); + if constexpr (std::is_integral_v) + return std::make_unique(p | dynamic_cast(x).p); + else + std::abort(); } std::unique_ptr> operator^(const VPoint &x) const override { - return std::make_unique(p ^ dynamic_cast(x).p); + if constexpr (std::is_integral_v) + return std::make_unique(p ^ dynamic_cast(x).p); + else + std::abort(); } std::unique_ptr> operator&&(const VPoint &x) const override { return std::make_unique>(p && @@ -307,16 +322,28 @@ template class WPoint final : public VPoint { return std::make_unique(a / p); } std::unique_ptr> left_modulus(const T &a) const override { - return std::make_unique(a % p); + if constexpr (std::is_integral_v) + return std::make_unique(a % p); + else + std::abort(); } std::unique_ptr> left_bit_and(const T &a) const override { - return std::make_unique(a & p); + if constexpr (std::is_integral_v) + return std::make_unique(a & p); + else + std::abort(); } std::unique_ptr> left_bit_or(const T &a) const override { - return std::make_unique(a | p); + if constexpr (std::is_integral_v) + return std::make_unique(a | p); + else + std::abort(); } std::unique_ptr> left_bit_xor(const T &a) const override { - return std::make_unique(a ^ p); + if constexpr (std::is_integral_v) + return std::make_unique(a ^ p); + else + std::abort(); } std::unique_ptr> left_logical_and(const T &a) const override { return std::make_unique>(a && p); @@ -338,16 +365,28 @@ template class WPoint final : public VPoint { return std::make_unique(p / b); } std::unique_ptr> operator%(const T &b) const override { - return std::make_unique(p % b); + if constexpr (std::is_integral_v) + return std::make_unique(p % b); + else + std::abort(); } std::unique_ptr> operator&(const T &b) const override { - return std::make_unique(p & b); + if constexpr (std::is_integral_v) + return std::make_unique(p & b); + else + std::abort(); } std::unique_ptr> operator|(const T &b) const override { - return std::make_unique(p | b); + if constexpr (std::is_integral_v) + return std::make_unique(p | b); + else + std::abort(); } std::unique_ptr> operator^(const T &b) const override { - return std::make_unique(p ^ b); + if constexpr (std::is_integral_v) + return std::make_unique(p ^ b); + else + std::abort(); } std::unique_ptr> operator&&(const T &b) const override { return std::make_unique>(p && b); @@ -373,20 +412,36 @@ template class WPoint final : public VPoint { return *this; } VPoint &operator%=(const VPoint &x) override { - p %= dynamic_cast(x).p; - return *this; + if constexpr (std::is_integral_v) { + p %= dynamic_cast(x).p; + return *this; + } else { + std::abort(); + } } VPoint &operator&=(const VPoint &x) override { - p &= dynamic_cast(x).p; - return *this; + if constexpr (std::is_integral_v) { + p &= dynamic_cast(x).p; + return *this; + } else { + std::abort(); + } } VPoint &operator|=(const VPoint &x) override { - p |= dynamic_cast(x).p; - return *this; + if constexpr (std::is_integral_v) { + p |= dynamic_cast(x).p; + return *this; + } else { + std::abort(); + } } VPoint &operator^=(const VPoint &x) override { - p ^= dynamic_cast(x).p; - return *this; + if constexpr (std::is_integral_v) { + p ^= dynamic_cast(x).p; + return *this; + } else { + std::abort(); + } } VPoint &operator+=(const T &b) override { @@ -406,20 +461,36 @@ template class WPoint final : public VPoint { return *this; } VPoint &operator%=(const T &b) override { - p %= b; - return *this; + if constexpr (std::is_integral_v) { + p %= b; + return *this; + } else { + std::abort(); + } } VPoint &operator&=(const T &b) override { - p &= b; - return *this; + if constexpr (std::is_integral_v) { + p &= b; + return *this; + } else { + std::abort(); + } } VPoint &operator|=(const T &b) override { - p |= b; - return *this; + if constexpr (std::is_integral_v) { + p |= b; + return *this; + } else { + std::abort(); + } } VPoint &operator^=(const T &b) override { - p ^= b; - return *this; + if constexpr (std::is_integral_v) { + p ^= b; + return *this; + } else { + std::abort(); + } } std::unique_ptr> abs1() const override { @@ -602,25 +673,41 @@ template class NDPoint { } NDPoint &operator=(NDPoint &&) = default; + /** Create an NDPoint from a Point + */ template NDPoint(const Point &p_) : p(std::make_unique>(p_)) {} template NDPoint(Point &&p_) : p(std::make_unique>(std::move(p_))) {} + /** Convert an NDPoint to a Point + * + * This only works when D == ndims(). + */ template operator Point() const { - return Point(dynamic_cast&>(*p)); + return Point(dynamic_cast &>(*p)); } + /** Create an NDPoint from a std::array + */ template NDPoint(const std::array &arr) : p(std::make_unique>(arr)) {} template NDPoint(std::array &&arr) : p(std::make_unique>(std::move(arr))) {} + /** Convert an NDPoint to a std::array + * + * This only works when D == ndims(). + */ template operator std::array() const { return std::array(dynamic_cast>(&p)); } + /** Create an NDPoint from an initializer list + * + * Example: NDPoint{1, 2} + */ NDPoint(std::initializer_list lst) : NDPoint(std::vector(lst)) {} private: @@ -637,30 +724,51 @@ template class NDPoint { NDPoint(const NDPoint &x) : p(x.p ? NDPoint(convert_vector(std::vector(x))) : nullptr) {} + /** Create an NDPoint from a generating function + * + * Example: NDPoint::make(3, [](int i) { return i+1; }) + * Result: NDPoint{1, 2, 3} + */ template static NDPoint make(const size_type D, const F &f) { return NDPoint(detail::make_VPoint(D, f)); } + /** Create an NDPoint with each component set to the same value `a` + */ static NDPoint pure(const size_type D, const T &val) { return make(D, [&](size_type) { return val; }); } + /** Create a unit NDPoint, where component dir is one, and all other + * components are zero + */ static NDPoint unit(const size_type D, const size_type dir) { return make(D, [&](size_type d) { return d == dir; }); } + /** Create an NDPoint with components set to the natural number + * sequence [0, ..., D-1] + */ static NDPoint iota(const size_type D) { return make(D, [&](size_type d) { return d; }); } - /** Check whether a Point is valid + /** Check whether an NDPoint is valid * - * A valid Point knows its number of dimensions, and its components - * are initialized. An invalid Point does not know its number of - * dimensions and holds no data, similar to a null Pointer. + * A valid NDPoint knows its number of dimensions, and its + * components are initialized. An invalid NDPoint does not know its + * number of dimensions and holds no data, similar to a null + * Pointer. * - * Most other member functions must not be called for invalid - * Points. + * Most other member functions cannot be called for invalid + * NDPoints. */ bool has_value() const { return bool(p); } + /** Map a function over all components of one or several Points + * + * Example: + * NDPoint pi, pj; + * NDPoint pk = fmap([](auto i, auto j) { return i+j; }, pi, pj); + * This calculates the component-wise sum of pi and pj, i.e. pi + pj . + */ template friend NDPoint fmap(const F &f, const NDPoint &x) { return NDPoint(x.p->fmap1(f)); } @@ -673,6 +781,14 @@ template class NDPoint { const NDPoint &z) { return NDPoint(x.p->fmap1(f, *y.p, *z.p)); } + /** Reduce over all components of one or several NDPoints + * + * Example: + * NDPoint p; + * int s = fold([](auto r, auto i) { return r+i; }, 0, p); + * This calculates the sum of all components ("horizonal sum") of p, + * same as sum(p). + */ template friend T fold(const Op &op, const T &r, const NDPoint &x) { return x.p->fold1(op, r); @@ -682,9 +798,13 @@ template class NDPoint { return x.p->fold1(op, r, *y.p); } + /** Create an NDPoint from a std::vector + */ NDPoint(const std::vector &vec) : NDPoint(vec.size()) { p->set_from_vector(vec); } + /** Convert an NDPoint to a std::vector + */ operator std::vector() const { return std::vector(p); } /** Number of comopnents (same as number of dimensions) @@ -719,40 +839,80 @@ template class NDPoint { */ constexpr NDPoint reversed() const { return NDPoint(p->reversed()); } + /** Apply unary plus operator element-wise + */ friend NDPoint operator+(const NDPoint &x) { return NDPoint(+*x.p); } + /** Element-wise negate + */ friend NDPoint operator-(const NDPoint &x) { return NDPoint(-*x.p); } - friend NDPoint operator~(const NDPoint &x) { return NDPoint(~*x.p); } + /** Element-wise bitwise not + */ + template > * = nullptr> + friend NDPoint operator~(const NDPoint &x) { + return NDPoint(~*x.p); + } + /** Element-wise logical not + */ friend NDPoint operator!(const NDPoint &x) { return NDPoint(!*x.p); } + /** Add element-wise + */ friend NDPoint operator+(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p + *y.p); } + /** Subtract element-wise + */ friend NDPoint operator-(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p - *y.p); } + /** Multiply element-wise + */ friend NDPoint operator*(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p * *y.p); } + /** Divide element-wise + */ friend NDPoint operator/(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p / *y.p); } + /** Element-wise modulo + */ + template > * = nullptr> friend NDPoint operator%(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p % *y.p); } + /** Element-wise bitwise and + */ + template > * = nullptr> friend NDPoint operator&(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p & *y.p); } + /** Element-wise bitwise or + */ + template > * = nullptr> friend NDPoint operator|(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p | *y.p); } + /** Element-wise bitwise exclusive or + */ + template > * = nullptr> friend NDPoint operator^(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p ^ *y.p); } + /** Element-wise logical and + */ friend NDPoint operator&&(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p && *y.p); } + /** Element-wise logical or + */ friend NDPoint operator||(const NDPoint &x, const NDPoint &y) { return NDPoint(*x.p || *y.p); } @@ -769,15 +929,23 @@ template class NDPoint { friend NDPoint operator/(const T &a, const NDPoint &y) { return NDPoint(y.p->left_divides(a)); } + template > * = nullptr> friend NDPoint operator%(const T &a, const NDPoint &y) { return NDPoint(y.p->left_modulus(a)); } + template > * = nullptr> friend NDPoint operator&(const T &a, const NDPoint &y) { return NDPoint(y.p->left_bit_and(a)); } + template > * = nullptr> friend NDPoint operator|(const T &a, const NDPoint &y) { return NDPoint(y.p->left_bit_or(a)); } + template > * = nullptr> friend NDPoint operator^(const T &a, const NDPoint &y) { return NDPoint(y.p->left_bit_xor(a)); } @@ -800,15 +968,23 @@ template class NDPoint { friend NDPoint operator/(const NDPoint &x, const T &b) { return NDPoint(*x.p / b); } + template > * = nullptr> friend NDPoint operator%(const NDPoint &x, const T &b) { return NDPoint(*x.p % b); } + template > * = nullptr> friend NDPoint operator&(const NDPoint &x, const T &b) { return NDPoint(*x.p & b); } + template > * = nullptr> friend NDPoint operator|(const NDPoint &x, const T &b) { return NDPoint(*x.p | b); } + template > * = nullptr> friend NDPoint operator^(const NDPoint &x, const T &b) { return NDPoint(*x.p ^ b); } @@ -835,18 +1011,26 @@ template class NDPoint { *p /= *x.p; return *this; } + template > * = nullptr> NDPoint &operator%=(const NDPoint &x) { *p %= *x.p; return *this; } + template > * = nullptr> NDPoint &operator&=(const NDPoint &x) { *p &= *x.p; return *this; } + template > * = nullptr> NDPoint &operator|=(const NDPoint &x) { *p |= *x.p; return *this; } + template > * = nullptr> NDPoint &operator^=(const NDPoint &x) { *p ^= *x.p; return *this; @@ -868,35 +1052,55 @@ template class NDPoint { *p /= a; return *this; } + template > * = nullptr> NDPoint &operator%=(const T &a) { *p %= a; return *this; } + template > * = nullptr> NDPoint &operator&=(const T &a) { *p &= a; return *this; } + template > * = nullptr> NDPoint &operator|=(const T &a) { *p |= a; return *this; } + template > * = nullptr> NDPoint &operator^=(const T &a) { *p ^= a; return *this; } + /** Element-wise absolute value + */ friend NDPoint abs(const NDPoint &x) { return x.p->abs1(); } + /** Element-wise absolute value + */ friend NDPoint fabs(const NDPoint &x) { return x.p->fabs1(); } + /** Element-wise maximum of two points + */ friend NDPoint fmax(const NDPoint &x, const NDPoint &y) { return x.p->fmax1(*y.p); } + /** Element-wise minimum of two points + */ friend NDPoint fmin(const NDPoint &x, const NDPoint &y) { return x.p->fmin1(*y.p); } + /** Element-wise maximum of two points + */ friend NDPoint max(const NDPoint &x, const NDPoint &y) { return x.p->max1(*y.p); } + /** Element-wise minimum of two points + */ friend NDPoint min(const NDPoint &x, const NDPoint &y) { return x.p->min1(*y.p); } @@ -911,28 +1115,52 @@ template class NDPoint { friend NDPoint max(const NDPoint &x, const T &b) { return x.p->max1(b); } friend NDPoint min(const NDPoint &x, const T &b) { return x.p->min1(b); } + /** Return true if all elements are true + */ friend T all(const NDPoint &x) { return x.p->all1(); } + /** Return true if any element is true + */ friend T any(const NDPoint &x) { return x.p->any1(); } + /** Return maximum element + */ friend T max_element(const NDPoint &x) { return x.p->max_element1(); } + /** Return minimum element + */ friend T min_element(const NDPoint &x) { return x.p->min_element1(); } + /** Product of all elements + */ friend T product(const NDPoint &x) { return x.p->product1(); } + /** Sum of all elements + */ friend T sum(const NDPoint &x) { return x.p->sum1(); } + /** Pointwise comparison + */ friend NDPoint operator==(const NDPoint &x, const NDPoint &y) { return *x.p == *y.p; } + /** Pointwise comparison + */ friend NDPoint operator!=(const NDPoint &x, const NDPoint &y) { return *x.p != *y.p; } + /** Pointwise comparison + */ friend NDPoint operator<(const NDPoint &x, const NDPoint &y) { return *x.p < *y.p; } + /** Pointwise comparison + */ friend NDPoint operator>(const NDPoint &x, const NDPoint &y) { return *x.p > *y.p; } + /** Pointwise comparison + */ friend NDPoint operator<=(const NDPoint &x, const NDPoint &y) { return *x.p <= *y.p; } + /** Pointwise comparison + */ friend NDPoint operator>=(const NDPoint &x, const NDPoint &y) { return *x.p >= *y.p; } @@ -956,7 +1184,7 @@ template class NDPoint { return *x.p >= b; } - /** Output a point + /** Output an NDoint */ friend std::ostream &operator<<(std::ostream &os, const NDPoint &x) { if (x.p) @@ -972,6 +1200,8 @@ template class NDPoint { namespace std { +/** Specialization of `equal_to` for NDPoint + */ template struct equal_to> { constexpr bool operator()(const openPMD::Regions::NDPoint &x, const openPMD::Regions::NDPoint &y) const { @@ -983,6 +1213,8 @@ template struct equal_to> { } }; +/** Specialization of `hash` for NDPoint + */ template struct hash> { constexpr size_t operator()(const openPMD::Regions::NDPoint &x) const { if (!x.has_value()) @@ -991,6 +1223,8 @@ template struct hash> { } }; +/** Specialization of `less` for NDPoint + */ template struct less> { constexpr bool operator()(const openPMD::Regions::NDPoint &x, const openPMD::Regions::NDPoint &y) const { diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index b1f0b6a0e5..f996c65d51 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -67,7 +67,7 @@ template class Point { return helpers::construct_array(f); } - /** Create a point with each component set to the same value a + /** Create a Point with each component set to the same value `a` */ static constexpr Point pure(const T &a) { return make([&](size_type) { return a; }); @@ -80,7 +80,7 @@ template class Point { return make([&](size_type d) { return d == dir; }); } - /** Create a point with components set to the natural number + /** Create a Point with components set to the natural number * sequence [0, ..., D-1] */ static constexpr Point iota() { @@ -133,31 +133,31 @@ template class Point { return r; } - /** Create a point from a point with different component type + /** Create a Point from a Point with different component type */ template constexpr Point(const Point &x) : elts(fmap([](const U &a) { return T(a); }, x)) {} - /** Create a point from Pointers to first and one past the last element + /** Create a Point from Pointers to first and one past the last element */ constexpr Point(const T *begin, const T *end [[maybe_unused]]) : elts((assert(begin + D == end), make([&](size_type d) { return begin[d]; }))) {} - /** Create a point from an initializer list + /** Create a Point from an initializer list * * Example: Point{1,2,3} */ constexpr Point(std::initializer_list lst) : Point(lst.begin(), lst.end()) {} - /** Create a point from a C-style array + /** Create a Point from a C-style array */ template * = nullptr> constexpr Point(const T (&arr)[DD]) : elts(&arr[0], &arr[D]) {} - /** Create a point from a std::array + /** Create a Point from a std::array */ constexpr Point(const std::array &arr) : elts(arr) {} - /** Create a point from a std::vector + /** Create a Point from a std::vector */ template > * = nullptr> @@ -167,10 +167,10 @@ template class Point { : Point((assert(vec.size() == D), make([&](size_type d) { return vec[d]; }))) {} - /** Convert a point to a std::array + /** Convert a Point to a std::array */ constexpr operator std::array() const { return elts; } - /** Convert a point to a std::vector + /** Convert a Point to a std::vector */ explicit constexpr operator std::vector() const { return std::vector(elts.begin(), elts.end()); @@ -184,25 +184,25 @@ template class Point { */ constexpr size_type ndims() const { return D; } - /** Get a component of a point + /** Get a component of a Point */ constexpr const T &operator[](const size_type d) const { return elts[d]; } - /** Get a component of a point + /** Get a component of a Point */ constexpr T &operator[](const size_type d) { return elts[d]; } - /** Remove a component from a point + /** Remove a component from a Point * - * This reduces the dimension of a point by one. + * This reduces the dimension of a Point by one. */ constexpr Point 0 ? D - 1 : 0)> erase(size_type dir) const { assert(dir >= 0 && dir < D); return Point 0 ? D - 1 : 0)>::make( [&](size_type d) { return d < dir ? (*this)[d] : (*this)[d + 1]; }); } - /** Add a component to a point + /** Add a component to a Point * - * This increases the dimension of a point by one. + * This increases the dimension of a Point by one. */ constexpr Point insert(size_type dir, const T &a) const { assert(dir >= 0 && dir <= D); @@ -211,65 +211,94 @@ template class Point { }); } - /** Reverse the components of a point + /** Reverse the components of a Point */ constexpr Point reversed() const { return Point::make([&](size_type d) { return (*this)[D - 1 - d]; }); } + /** Apply unary plus operator element-wise + */ friend constexpr Point operator+(const Point &x) { return fmap([](const T &a) { return +a; }, x); } + /** Element-wise negate + */ friend constexpr Point operator-(const Point &x) { return fmap([](const T &a) { return -a; }, x); } + /** Element-wise bitwise not + */ + template > * = nullptr> friend constexpr Point operator~(const Point &x) { if constexpr (std::is_same_v) // Special case to avoid compiler warnings return fmap([](const T &) { return true; }, x); - else if constexpr (std::is_integral_v) + else return fmap([](const T &a) { return ~a; }, x); - std::abort(); } + /** Element-wise logical not + */ friend constexpr Point operator!(const Point &x) { return fmap([](const T &a) { return !a; }, x); } + /** Add element-wise + */ friend constexpr Point operator+(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a + b; }, x, y); } + /** Subtract element-wise + */ friend constexpr Point operator-(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a - b; }, x, y); } + /** Multiply element-wise + */ friend constexpr Point operator*(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a * b; }, x, y); } + /** Divide element-wise + */ friend constexpr Point operator/(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a / b; }, x, y); } + /** Element-wise modulo + */ + template > * = nullptr> friend constexpr Point operator%(const Point &x, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([](const T &a, const T &b) { return a % b; }, x, y); - std::abort(); + return fmap([](const T &a, const T &b) { return a % b; }, x, y); } + /** Element-wise bitwise and + */ + template > * = nullptr> friend constexpr Point operator&(const Point &x, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([](const T &a, const T &b) { return a & b; }, x, y); - std::abort(); + return fmap([](const T &a, const T &b) { return a & b; }, x, y); } + /** Element-wise bitwise or + */ + template > * = nullptr> friend constexpr Point operator|(const Point &x, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([](const T &a, const T &b) { return a | b; }, x, y); - std::abort(); + return fmap([](const T &a, const T &b) { return a | b; }, x, y); } + /** Element-wise bitwise exclusive or + */ + template > * = nullptr> friend constexpr Point operator^(const Point &x, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([](const T &a, const T &b) { return a ^ b; }, x, y); - std::abort(); + return fmap([](const T &a, const T &b) { return a ^ b; }, x, y); } + /** Element-wise logical and + */ friend constexpr Point operator&&(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a && b; }, x, y); } + /** Element-wise logical or + */ friend constexpr Point operator||(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a || b; }, x, y); } @@ -286,25 +315,25 @@ template class Point { friend constexpr Point operator/(const T &a, const Point &y) { return fmap([&](const T &b) { return a / b; }, y); } + template > * = nullptr> friend constexpr Point operator%(const T &a, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([&](const T &b) { return a % b; }, y); - std::abort(); + return fmap([&](const T &b) { return a % b; }, y); } + template > * = nullptr> friend constexpr Point operator&(const T &a, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([&](const T &b) { return a & b; }, y); - std::abort(); + return fmap([&](const T &b) { return a & b; }, y); } + template > * = nullptr> friend constexpr Point operator|(const T &a, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([&](const T &b) { return a | b; }, y); - std::abort(); + return fmap([&](const T &b) { return a | b; }, y); } + template > * = nullptr> friend constexpr Point operator^(const T &a, const Point &y) { - if constexpr (std::is_integral_v) - return fmap([&](const T &b) { return a ^ b; }, y); - std::abort(); + return fmap([&](const T &b) { return a ^ b; }, y); } friend constexpr Point operator&&(const T &a, const Point &y) { return fmap([&](const T &b) { return a && b; }, y); @@ -325,25 +354,25 @@ template class Point { friend constexpr Point operator/(const Point &x, const T &b) { return fmap([&](const T &a) { return a / b; }, x); } + template > * = nullptr> friend constexpr Point operator%(const Point &x, const T &b) { - if constexpr (std::is_integral_v) - return fmap([&](const T &a) { return a % b; }, x); - std::abort(); + return fmap([&](const T &a) { return a % b; }, x); } + template > * = nullptr> friend constexpr Point operator&(const Point &x, const T &b) { - if constexpr (std::is_integral_v) - return fmap([&](const T &a) { return a & b; }, x); - std::abort(); + return fmap([&](const T &a) { return a & b; }, x); } + template > * = nullptr> friend constexpr Point operator|(const Point &x, const T &b) { - if constexpr (std::is_integral_v) - return fmap([&](const T &a) { return a | b; }, x); - std::abort(); + return fmap([&](const T &a) { return a | b; }, x); } + template > * = nullptr> friend constexpr Point operator^(const Point &x, const T &b) { - if constexpr (std::is_integral_v) - return fmap([&](const T &a) { return a ^ b; }, x); - std::abort(); + return fmap([&](const T &a) { return a ^ b; }, x); } friend constexpr Point operator&&(const Point &x, const T &b) { return fmap([&](const T &a) { return a && b; }, x); @@ -356,20 +385,54 @@ template class Point { constexpr Point &operator-=(const Point &x) { return *this = *this - x; } constexpr Point &operator*=(const Point &x) { return *this = *this * x; } constexpr Point &operator/=(const Point &x) { return *this = *this / x; } - constexpr Point &operator%=(const Point &x) { return *this = *this % x; } - constexpr Point &operator&=(const Point &x) { return *this = *this & x; } - constexpr Point &operator|=(const Point &x) { return *this = *this | x; } - constexpr Point &operator^=(const Point &x) { return *this = *this ^ x; } + template > * = nullptr> + constexpr Point &operator%=(const Point &x) { + return *this = *this % x; + } + template > * = nullptr> + constexpr Point &operator&=(const Point &x) { + return *this = *this & x; + } + template > * = nullptr> + constexpr Point &operator|=(const Point &x) { + return *this = *this | x; + } + template > * = nullptr> + constexpr Point &operator^=(const Point &x) { + return *this = *this ^ x; + } constexpr Point &operator+=(const T &a) { return *this = *this + a; } constexpr Point &operator-=(const T &a) { return *this = *this - a; } constexpr Point &operator*=(const T &a) { return *this = *this * a; } constexpr Point &operator/=(const T &a) { return *this = *this / a; } - constexpr Point &operator%=(const T &a) { return *this = *this % a; } - constexpr Point &operator&=(const T &a) { return *this = *this & a; } - constexpr Point &operator|=(const T &a) { return *this = *this | a; } - constexpr Point &operator^=(const T &a) { return *this = *this ^ a; } + template > * = nullptr> + constexpr Point &operator%=(const T &a) { + return *this = *this % a; + } + template > * = nullptr> + constexpr Point &operator&=(const T &a) { + return *this = *this & a; + } + template > * = nullptr> + constexpr Point &operator|=(const T &a) { + return *this = *this | a; + } + template > * = nullptr> + constexpr Point &operator^=(const T &a) { + return *this = *this ^ a; + } + /** Element-wise absolute value + */ friend constexpr Point abs(const Point &x) { using std::abs; return fmap( @@ -381,23 +444,33 @@ template class Point { }, x); } + /** Element-wise absolute value + */ friend constexpr Point fabs(const Point &x) { using std::fabs; return fmap([&](const T &a) { return fabs(a); }, x); } + /** Element-wise maximum of two points + */ friend constexpr Point fmax(const Point &x, const Point &y) { using std::fmax; return fmap([](const T &a, const T &b) { return fmax(a, b); }, x, y); } + /** Element-wise minimum of two points + */ friend constexpr Point fmin(const Point &x, const Point &y) { using std::fmin; return fmap([](const T &a, const T &b) { return fmin(a, b); }, x, y); } + /** Element-wise maximum of two points + */ friend constexpr Point max(const Point &x, const Point &y) { using std::max; return fmap([](const T &a, const T &b) { return max(a, b); }, x, y); } + /** Element-wise minimum of two points + */ friend constexpr Point min(const Point &x, const Point &y) { using std::min; return fmap([](const T &a, const T &b) { return min(a, b); }, x, y); @@ -437,12 +510,18 @@ template class Point { return fmap([&](const T &a) { return min(a, b); }, x); } + /** Return true if all elements are true + */ friend constexpr bool all(const Point &x) { return fold([](bool r, const T &a) { return r && a; }, true, x); } + /** Return true if any element is true + */ friend constexpr bool any(const Point &x) { return fold([](bool r, const T &a) { return r || a; }, false, x); } + /** Return maximum element + */ friend constexpr T max_element(const Point &x) { using std::max; const T neutral = std::is_floating_point_v @@ -450,6 +529,8 @@ template class Point { : std::numeric_limits::lowest(); return fold([](const T &r, const T &a) { return max(r, a); }, neutral, x); } + /** Return minimum element + */ friend constexpr T min_element(const Point &x) { using std::min; const T neutral = std::is_floating_point_v @@ -457,28 +538,44 @@ template class Point { : std::numeric_limits::max(); return fold([](const T &r, const T &a) { return min(r, a); }, neutral, x); } + /** Product of all elements + */ friend constexpr T product(const Point &x) { return fold([](const T &r, const T &a) { return r * a; }, T(1), x); } + /** Sum of all elements + */ friend constexpr T sum(const Point &x) { return fold([](const T &r, const T &a) { return r + a; }, T(0), x); } + /** Pointwise comparison + */ friend constexpr Point operator==(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a == b; }, x, y); } + /** Pointwise comparison + */ friend constexpr Point operator!=(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a != b; }, x, y); } + /** Pointwise comparison + */ friend constexpr Point operator<(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a < b; }, x, y); } + /** Pointwise comparison + */ friend constexpr Point operator>(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a > b; }, x, y); } + /** Pointwise comparison + */ friend constexpr Point operator<=(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a <= b; }, x, y); } + /** Pointwise comparison + */ friend constexpr Point operator>=(const Point &x, const Point &y) { return fmap([](const T &a, const T &b) { return a >= b; }, x, y); } @@ -502,7 +599,7 @@ template class Point { return fmap([&](const T &a) { return a >= b; }, x); } - /** Output a point + /** Output a Point */ friend std::ostream &operator<<(std::ostream &os, const Point &x) { os << "["; @@ -521,6 +618,8 @@ template class Point { namespace std { +/** Specialization of `equal_to` for Point + */ template struct equal_to> { constexpr bool operator()(const openPMD::Regions::Point &x, @@ -530,6 +629,8 @@ struct equal_to> { } }; +/** Specialization of `hash` for Point + */ template struct hash> { constexpr size_t operator()(const openPMD::Regions::Point &x) const { @@ -542,6 +643,8 @@ struct hash> { } }; +/** Specialization of `less` for Point + */ template struct less> { constexpr bool operator()(const openPMD::Regions::Point &x, From a2f07e75309067a0dee66f605a1338d1f85abbff Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sat, 26 Jun 2021 22:25:20 -0400 Subject: [PATCH 60/68] Regions: Remove unused code --- include/openPMD/regions/Box.hpp | 156 ------------------------------ include/openPMD/regions/NDBox.hpp | 89 ----------------- test/BoxTest.cpp | 25 ----- 3 files changed, 270 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index f42d5aa609..fa38a33716 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -365,162 +365,6 @@ template class Box { */ friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } -#if 0 - /** Check whether a Box is equal to the union of a set of other, disjoint - * boxes - * - * The other boxes must be disjoint; this is not checked. - */ - friend bool operator==(const Box &b, const std::vector &bs) { - // This assumes that the elements of bs are disjoint - size_type sz = 0; - for (const Box &c : bs) - sz += c.size(); - if (b.size() != sz) - return false; - for (const Box &c : bs) - if (!(c <= b)) - return false; - return true; - } - friend bool operator==(const std::vector &bs, const Box &b) { - return b == bs; - } - friend bool operator!=(const Box &b, const std::vector &bs) { - return !(b == bs); - } - friend bool operator!=(const std::vector &bs, const Box &b) { - return !(bs == b); - } - -private: - /** Split a box at a point, creating up to 2^D new boxes - */ - void split(const Point &p, std::vector &rs) const { - assert(!empty()); -#if REGIONS_DEBUG - const auto old_rs_size = rs.size(); -#endif - for (int m = 0; m < (1 << D); ++m) { - Point newlo = lo, newhi = hi; - bool is_inside = true; - for (std::size_t d = 0; d < D; ++d) { - const bool lohi = m & (1 << d); - if (p[d] > lo[d] && p[d] < hi[d]) { - if (!lohi) - newhi[d] = p[d]; - else - newlo[d] = p[d]; - } else { - is_inside &= !lohi; - } - } - if (is_inside) - rs.push_back(Box(newlo, newhi)); - } -#if REGIONS_DEBUG - // Postcondition - size_type vol = 0; - for (auto i = old_rs_size; i < rs.size(); ++i) { - assert(!rs[i].empty()); - assert(rs[i] <= *this); - vol += rs[i].size(); - } - assert(vol == size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(isdisjoint(rs[i], rs[j])); -#endif - } - -public: - friend std::vector operator-(const Box &b1, const Box &b2) { - if (b1.empty()) - return std::vector{}; - if (b2.empty()) - return std::vector{b1}; - std::vector rs1; - b1.split(b2.lo, rs1); - std::vector rs2; - for (const auto &r : rs1) - r.split(b2.hi, rs2); - std::vector rs; - for (const auto &r : rs2) { -#if REGIONS_DEBUG - assert(isdisjoint(r, b2) || r <= b2); -#endif - if (isdisjoint(r, b2)) - rs.push_back(r); - } -#if REGIONS_DEBUG - // Postcondition - size_type vol = 0; - for (const auto &r : rs) { - assert(!r.empty()); - assert(r <= b1 && !(r <= b2)); - vol += r.size(); - } - using std::max; - assert(vol >= max(size_type(0), b1.size() - b2.size()) && vol <= b1.size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(isdisjoint(rs[i], rs[j])); -#endif - return rs; - } - friend std::vector difference(const Box &b1, const Box &b2) { - return b1 - b2; - } - - friend std::vector operator|(const Box &b1, const Box &b2) { - auto rs = b1 - b2; - if (!b2.empty()) - rs.push_back(b2); -#if REGIONS_DEBUG - // Postcondition - size_type vol = 0; - for (const auto &r : rs) { - assert(!r.empty()); - assert(r <= b1 || r <= b2); - vol += r.size(); - } - assert(vol >= b1.size() && vol <= b1.size() + b2.size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(isdisjoint(rs[i], rs[j])); -#endif - return rs; - } - friend std::vector setunion(const Box &b1, const Box &b2) { - return b1 | b2; - } - - friend std::vector operator^(const Box &b1, const Box &b2) { - auto rs = b1 - b2; - auto rs1 = b2 - b1; - // TODO: Avoid this concatenation - rs.insert(rs.end(), rs1.begin(), rs1.end()); -#if REGIONS_DEBUG - // Postcondition - size_type vol = 0; - for (const auto &r : rs) { - assert(!r.empty()); - assert((r <= b1) ^ (r <= b2)); - vol += r.size(); - } - using std::abs; - assert(vol >= abs(b1.size() - b2.size()) && vol <= b1.size() + b2.size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(isdisjoint(rs[i], rs[j])); -#endif - return rs; - } - friend std::vector symmetric_difference(const Box &b1, const Box &b2) { - return b1 ^ b2; - } -#endif - /** Output a box */ friend std::ostream &operator<<(std::ostream &os, const Box &b) { diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp index aa9a1a12c9..ded48daeec 100644 --- a/include/openPMD/regions/NDBox.hpp +++ b/include/openPMD/regions/NDBox.hpp @@ -65,20 +65,6 @@ template class VBox { virtual VBox &operator&=(const VBox &b) = 0; virtual std::unique_ptr intersection1(const VBox &b2) const = 0; -#if 0 - virtual bool operator==(const std::vector> &bs) const = 0; - virtual bool operator!=(const std::vector> &bs) const = 0; - - virtual std::vector> operator-(const VBox &b2) const = 0; - virtual std::vector> difference1(const VBox &b2) const = 0; - - virtual std::vector> operator|(const VBox &b2) const = 0; - virtual std::vector> setunion1(const VBox &b2) const = 0; - - virtual std::vector> operator^(const VBox &b2) const = 0; - virtual std::vector> symmetric_difference1(const VBox &b2) const = 0; -#endif - virtual bool equal_to1(const VBox &x) const = 0; virtual std::size_t hash1() const = 0; virtual bool less1(const VBox &x) const = 0; @@ -219,43 +205,6 @@ template class WBox final : public VBox { intersection(b, dynamic_cast(b2).b)); } -#if 0 - bool operator==(const std::vector> &bs) const override { - return b == helpers::convert_vector>(bs); - } - bool operator!=(const std::vector> &bs) const override { - return b != helpers::convert_vector>(bs); - } - - std::vector> operator-(const VBox &b2) const override { - return helpers::convert_vector>(b - - dynamic_cast(b2).b); - } - std::vector> difference1(const VBox &b2) const override { - return helpers::convert_vector>( - difference(b, dynamic_cast(b2).b)); - } - - std::vector> operator|(const VBox &b2) const override { - return helpers::convert_vector>(b | - dynamic_cast(b2).b); - } - std::vector> setunion1(const VBox &b2) const override { - return helpers::convert_vector>( - setunion(b, dynamic_cast(b2).b)); - } - - std::vector> operator^(const VBox &b2) const override { - return helpers::convert_vector>(b ^ - dynamic_cast(b2).b); - } - std::vector> - symmetric_difference1(const VBox &b2) const override { - return helpers::convert_vector>( - symmetric_difference(b, dynamic_cast(b2).b)); - } -#endif - bool equal_to1(const VBox &x) const override { return std::equal_to>()(b, dynamic_cast(x).b); } @@ -440,44 +389,6 @@ template class NDBox { return intersection(*b1.b, *b2.b); } -#if 0 - friend bool operator==(const NDBox &b1, const std::vector &bs) { - return *b1.b == bs; - } - friend bool operator==(const std::vector &bs, const NDBox &b2) { - return *b2.b == bs; - } - friend bool operator!=(const NDBox &b1, const std::vector &bs) { - return *b1.b != bs; - } - friend bool operator!=(const std::vector &bs, const NDBox &b2) { - return *b2.b != bs; - } - - friend std::vector operator-(const NDBox &b1, const NDBox &b2) { - - return *b1.b - *b2.b; - } - friend std::vector difference(const NDBox &b1, const NDBox &b2) { - return difference(*b1.b, *b2.b); - } - - friend std::vector operator|(const NDBox &b1, const NDBox &b2) { - return *b1.b | *b2.b; - } - friend std::vector setunion(const NDBox &b1, const NDBox &b2) { - return setunion(*b1.b, *b2.b); - } - - friend std::vector operator^(const NDBox &b1, const NDBox &b2) { - return *b1.b ^ *b2.b; - } - friend std::vector symmetric_difference(const NDBox &b1, - const NDBox &b2) { - return symmetric_difference(*b1.b, *b2.b); - } -#endif - /** Output a box */ friend std::ostream &operator<<(std::ostream &os, const NDBox &x) { diff --git a/test/BoxTest.cpp b/test/BoxTest.cpp index c88b686bfe..3bb04bc7ed 100644 --- a/test/BoxTest.cpp +++ b/test/BoxTest.cpp @@ -182,35 +182,10 @@ template void test_Box(const B &box) { CHECK((X & Y) == (Y & X)); CHECK(((X & Y) & Z) == (X & (Y & Z))); - CHECK((N | X) == X); - CHECK((E | X) == E); - CHECK((X | E) == E); - // CHECK((X | Y) == (Y | X)); - // CHECK(((X | Y) | Z) == (X | (Y | Z))); - - // CHECK(E - (X & Y) == ((E - X) | (E - Y))); - // CHECK(E - (X | Y) == ((E - X) & (E - Y))); - const B IXY = X & Y; CHECK((IXY <= X && IXY <= Y) == true); CHECK((IXY.grown(1) <= X && IXY.grown(1) <= Y) == (D == 0 || IXY.empty())); - const std::vector UXY = X | Y; - for (const auto &U : UXY) - CHECK((U <= X || U <= Y) == true); - - const std::vector DXY = X - Y; - for (const auto &DD : DXY) - CHECK((DD <= X || !isdisjoint(DD, Y)) == true); - - const std::vector SXY = X ^ Y; - for (const auto &S : SXY) - CHECK(((S <= X || S <= Y) && isdisjoint(S, IXY)) == true); - - // CHECK(IXY <= UXY); - // CHECK(isdisjoint(IXY, SXY)); - // CHECK((IXY | SXY) == UXY); - } // for iter } From 10fe81b547be074446ab61a5425d1a394d58e8cb Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sat, 26 Jun 2021 23:06:24 -0400 Subject: [PATCH 61/68] Regions: Document boxes --- include/openPMD/regions/Box.hpp | 8 +-- include/openPMD/regions/NDBox.hpp | 102 ++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index fa38a33716..4ac9deb044 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -250,7 +250,7 @@ template class Box { } Box grown(const Point &d) const { return grown(d, d); } Box grown(const T &d) const { return grown(Point::pure(d)); } - /** Grow a box by given amounts in each direction. + /** Shrink a box by given amounts in each direction. * * The shrinkage can be negative, which grows the box. If a box is * shrunk too much it becomes empty. Growing an empty box always @@ -319,12 +319,12 @@ template class Box { * `>=`. */ bool is_superset_of(const Box &b) const { return *this >= b; } - /** Check whether a Box is a strict subset of another Box. This is equivalen + /** Check whether a Box is a strict subset of another Box. This is equivalent * to `<`. */ bool is_strict_subset_of(const Box &b) const { return *this < b; } - /** Check whether a Box is a strict superset of another Box. This is equivalen - * to `>`. + /** Check whether a Box is a strict superset of another Box. This is + * equivalent to `>`. */ bool is_strict_superset_of(const Box &b) const { return *this > b; } diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp index ded48daeec..ca8c8bd79c 100644 --- a/include/openPMD/regions/NDBox.hpp +++ b/include/openPMD/regions/NDBox.hpp @@ -283,45 +283,79 @@ template class NDBox { } NDBox &operator=(NDBox &&) = default; + /** Create an NDBox from a Box + */ template NDBox(const Box &b_) : b(std::make_unique>(b_)) {} template NDBox(Box &&b_) : b(std::make_unique>(std::move(b_))) {} + /** Convert an NDBox to a Box + * + * This only works when D == ndims(). + */ template operator Box() const { return Box(dynamic_cast &>(*b)); } + /** Create an NDBox from lower (inclusive) and upper (exclusive) bound + */ NDBox(const NDPoint &lo_, const NDPoint &hi_) : b(detail::make_VBox(lo_.ndims(), lo_, hi_)) {} + /** Create box holding a single point + */ explicit NDBox(const NDPoint &p) : b(detail::make_VBox(p.ndims(), p)) {} - /** Check whether a box is valid + /** Check whether an NDBox is valid * - * A valid box knows its number of dimensions, and its components - * are initialized. An invalid box does not know its number of + * A valid NDBox knows its number of dimensions, and its components + * are initialized. An invalid NDBox does not know its number of * dimensions and holds no data, similar to a null pointer. * - * Most other member functions must not be called for invalid - * boxes. + * Most other member functions must not be called for invalid NDBox. + * + * Note that there is a difference between invalid NDBoxes and empty + * NDBoxes. Invalid NDBoxes are essentially invalid objects that + * cannot be used in a useful manner. Empty NDBoxes are fine, + * similar to empty arrays. */ bool has_value() const { return bool(b); } + /** Number of dimensions + */ size_type ndims() const { return b->ndims(); } + /** Whether a box is empty + */ bool empty() const { return b->empty(); } + /** Lower bound (inclusive) + */ NDPoint lower() const { return b->lower(); } + /** Upper bound (exclusive) + */ NDPoint upper() const { return b->upper(); } + /** Shape, i.e. the "size" in each direction + */ NDPoint shape() const { return b->shape(); } + /** Size, the total number of contained points + */ size_type size() const { return b->size(); } + /** Shift a box to the right (upwards). The shift can be negative, which + * shifts left. + */ NDBox &operator>>=(const NDPoint &p) { *b >>= p->p; return *this; } + /** Shift a box to the left (downwards). The shift can be negative, which + * shifts right. + */ NDBox &operator<<=(const NDPoint &p) { *b <<= p->p; return *this; } + /** Scale a box + */ NDBox &operator*=(const NDPoint &p) { *b *= p->p; return *this; @@ -329,6 +363,12 @@ template class NDBox { NDBox operator>>(const NDPoint &p) const { return NDBox(*b >> p); } NDBox operator<<(const NDPoint &p) const { return NDBox(*b << p); } NDBox operator*(const NDPoint &p) const { return NDBox(*b * p); } + /** Grow a box by given amounts in each direction. + * + * The growth can be negative, which shrinks the box. If a box is + * shrunk too much it becomes empty. Growing an empty box always + * results in an empty box. + */ NDBox grown(const NDPoint &dlo, const NDPoint &dup) const { return NDBox(b->grown(dlo, dup)); } @@ -337,9 +377,19 @@ template class NDBox { NDBox shrunk(const NDPoint &dlo, const NDPoint &dup) const { return NDBox(b->shrunk(dlo, dup)); } + /** Shrink a box by given amounts in each direction. + * + * The shrinkage can be negative, which grows the box. If a box is + * shrunk too much it becomes empty. Growing an empty box always + * results in an empty box. + */ NDBox shrunk(const NDPoint &d) const { return NDBox(b->shrunk(d)); } NDBox shrunk(const T &d) const { return NDBox(b->shrunk(d)); } + /** Compare two boxes + * + * (All empty boxes are equal.) + */ friend bool operator==(const NDBox &b1, const NDBox &b2) { return *b1.b == *b2.b; } @@ -347,37 +397,72 @@ template class NDBox { return *b1.b != *b2.b; } + /** Check whether a box contains a given point + */ bool contains(const NDPoint &p) const { return b->contains(p); } + /** Check whether two boxes are disjoint, i.e. whether they have no point in + * common + */ friend bool isdisjoint(const NDBox &b1, const NDBox &b2) { return b1.b->isdisjoint1(*b2.b); } + /** Check whether Box 1 is (completely) contained in Box 2 + */ friend bool operator<=(const NDBox &b1, const NDBox &b2) { return *b1.b <= *b2.b; } + /** Check whether Box 1 (completely) contains Box 2 + */ friend bool operator>=(const NDBox &b1, const NDBox &b2) { return *b1.b >= *b2.b; } + /** Check whether Box 1 is (completely) contained in Box 2, and Box + * 2 is larger than Box 1 + */ friend bool operator<(const NDBox &b1, const NDBox &b2) { return *b1.b < *b2.b; } + /** Check whether Box 1 (completely) contains in Box 2, and Box + * 1 is larger than Box 2 + */ friend bool operator>(const NDBox &b1, const NDBox &b2) { return *b1.b > *b2.b; } + /** Check whether a Box is a subset of another Box. This is equivalent to + * `<=`. + */ bool is_subset_of(const NDBox &b2) const { return b->is_subset_of(*b2.b); } + /** Check whether a Box is a superset of another Box. This is equivalent to + * `>=`. + */ bool is_superset_of(const NDBox &b2) const { return b->is_superset_of(*b2.b); } + /** Check whether a Box is a strict subset of another Box. This is equivalent + * to `<`. + */ bool is_strict_subset_of(const NDBox &b2) const { return b->is_strict_subset_of(*b2.b); } + /** Check whether a Box is a strict superset of another Box. This is + * equivalent to `>`. + */ bool is_strict_superset_of(const NDBox &b2) const { return b->is_strict_superset_of(*b2.b); } + /** Calculate the bounding box of two Boxes. This is the smallest Box that + * contains both Boxes. + */ friend NDBox bounding_box(const NDBox &b1, const NDBox &b2) { return b1.b->bounding_box1(*b2.b); } + /** Calculate the intersection between two Boxes + * + * Other set operations (union, difference, symmetric difference) are not + * supported for Boxes; use Regions instead. + */ friend NDBox operator&(const NDBox &b1, const NDBox &b2) { return *b1.b & *b2.b; } @@ -385,11 +470,16 @@ template class NDBox { *b &= *b2.b; return *this; } + /** Calculate the intersection between two Boxes + * + * Other set operations (union, difference, symmetric difference) are not + * supported for Boxes; use Regions instead. + */ friend NDBox intersection(const NDBox &b1, const NDBox &b2) { return intersection(*b1.b, *b2.b); } - /** Output a box + /** Output an NDBox */ friend std::ostream &operator<<(std::ostream &os, const NDBox &x) { if (x.b) From 15399b10ba454862c20fcc9bc85baa515f03fb87 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Sun, 27 Jun 2021 12:15:01 -0400 Subject: [PATCH 62/68] Document Regionss --- include/openPMD/regions/Box.hpp | 20 ++--- include/openPMD/regions/NDBox.hpp | 32 ++++---- include/openPMD/regions/NDRegion.hpp | 109 +++++++++++++++++++++++---- include/openPMD/regions/Region.hpp | 89 +++++++++++++++++++++- 4 files changed, 210 insertions(+), 40 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index 4ac9deb044..c3cb21fee4 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -191,7 +191,7 @@ template class Box { /** Number of dimensions */ size_type ndims() const { return D; } - /** Whether a box is empty + /** Whether the Box is empty */ bool empty() const { return any(hi <= lo); } /** Lower bound (inclusive) @@ -208,7 +208,7 @@ template class Box { size_type size() const { return product(shape()); } // Shift and scale operators - /** Shift a box to the right (upwards). The shift can be negative, which + /** Shift a Box to the right (upwards). The shift can be negative, which * shifts left. */ Box &operator>>=(const Point &p) { @@ -216,7 +216,7 @@ template class Box { hi += p; return *this; } - /** Shift a box to the left (downwards). The shift can be negative, which + /** Shift a Box to the left (downwards). The shift can be negative, which * shifts right. */ Box &operator<<=(const Point &p) { @@ -224,7 +224,7 @@ template class Box { hi -= p; return *this; } - /** Scale a box + /** Scale a Box */ Box &operator*=(const Point &p) { lo *= p; @@ -234,9 +234,9 @@ template class Box { Box operator>>(const Point &p) const { return Box(*this) >>= p; } Box operator<<(const Point &p) const { return Box(*this) <<= p; } Box operator*(const Point &p) const { return Box(*this) *= p; } - /** Grow a box by given amounts in each direction. + /** Grow a Box by given amounts in each direction. * - * The growth can be negative, which shrinks the box. If a box is + * The growth can be negative, which shrinks the box. If a Box is * shrunk too much it becomes empty. Growing an empty box always * results in an empty box. */ @@ -250,9 +250,9 @@ template class Box { } Box grown(const Point &d) const { return grown(d, d); } Box grown(const T &d) const { return grown(Point::pure(d)); } - /** Shrink a box by given amounts in each direction. + /** Shrink a Box by given amounts in each direction. * - * The shrinkage can be negative, which grows the box. If a box is + * The shrinkage can be negative, which grows the box. If a Box is * shrunk too much it becomes empty. Growing an empty box always * results in an empty box. */ @@ -277,7 +277,7 @@ template class Box { friend bool operator!=(const Box &b1, const Box &b2) { return !(b1 == b2); } // Set comparison operators - /** Check whether a box contains a given point + /** Check whether a Box contains a given point */ bool contains(const Point &p) const { if (empty()) @@ -365,7 +365,7 @@ template class Box { */ friend Box intersection(const Box &b1, const Box &b2) { return b1 & b2; } - /** Output a box + /** Output a Box */ friend std::ostream &operator<<(std::ostream &os, const Box &b) { return os << "(" << b.lo << ":" << b.hi << ")"; diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp index ca8c8bd79c..e87a80e4df 100644 --- a/include/openPMD/regions/NDBox.hpp +++ b/include/openPMD/regions/NDBox.hpp @@ -238,12 +238,12 @@ std::unique_ptr> make_VBox(const std::size_t D, const Args &...args) { } // namespace detail -/** A Box +/** A NDBox * * The dimension (number of component) of the Box is only known at * run-time. @see Box * - * A box is described by two points, its lower bound (inclusive) and + * a NDBox is described by two points, its lower bound (inclusive) and * upper bound (exclusive). If the lower bound not less than the upper * bound, the box is empty. Empty boxes are fine (similar to an empty * array). @@ -324,7 +324,7 @@ template class NDBox { /** Number of dimensions */ size_type ndims() const { return b->ndims(); } - /** Whether a box is empty + /** Whether the NDBox box is empty */ bool empty() const { return b->empty(); } /** Lower bound (inclusive) @@ -340,21 +340,21 @@ template class NDBox { */ size_type size() const { return b->size(); } - /** Shift a box to the right (upwards). The shift can be negative, which + /** Shift a NDBox to the right (upwards). The shift can be negative, which * shifts left. */ NDBox &operator>>=(const NDPoint &p) { *b >>= p->p; return *this; } - /** Shift a box to the left (downwards). The shift can be negative, which + /** Shift a NDBox to the left (downwards). The shift can be negative, which * shifts right. */ NDBox &operator<<=(const NDPoint &p) { *b <<= p->p; return *this; } - /** Scale a box + /** Scale a NDBox */ NDBox &operator*=(const NDPoint &p) { *b *= p->p; @@ -363,9 +363,9 @@ template class NDBox { NDBox operator>>(const NDPoint &p) const { return NDBox(*b >> p); } NDBox operator<<(const NDPoint &p) const { return NDBox(*b << p); } NDBox operator*(const NDPoint &p) const { return NDBox(*b * p); } - /** Grow a box by given amounts in each direction. + /** Grow a NDBox by given amounts in each direction. * - * The growth can be negative, which shrinks the box. If a box is + * The growth can be negative, which shrinks the box. If a NDBox is * shrunk too much it becomes empty. Growing an empty box always * results in an empty box. */ @@ -377,9 +377,9 @@ template class NDBox { NDBox shrunk(const NDPoint &dlo, const NDPoint &dup) const { return NDBox(b->shrunk(dlo, dup)); } - /** Shrink a box by given amounts in each direction. + /** Shrink a NDBox by given amounts in each direction. * - * The shrinkage can be negative, which grows the box. If a box is + * The shrinkage can be negative, which grows the box. If a NDBox is * shrunk too much it becomes empty. Growing an empty box always * results in an empty box. */ @@ -397,7 +397,7 @@ template class NDBox { return *b1.b != *b2.b; } - /** Check whether a box contains a given point + /** Check whether a NDBox contains a given point */ bool contains(const NDPoint &p) const { return b->contains(p); } /** Check whether two boxes are disjoint, i.e. whether they have no point in @@ -428,23 +428,23 @@ template class NDBox { friend bool operator>(const NDBox &b1, const NDBox &b2) { return *b1.b > *b2.b; } - /** Check whether a Box is a subset of another Box. This is equivalent to + /** Check whether a NDBox is a subset of another Box. This is equivalent to * `<=`. */ bool is_subset_of(const NDBox &b2) const { return b->is_subset_of(*b2.b); } - /** Check whether a Box is a superset of another Box. This is equivalent to + /** Check whether a NDBox is a superset of another Box. This is equivalent to * `>=`. */ bool is_superset_of(const NDBox &b2) const { return b->is_superset_of(*b2.b); } - /** Check whether a Box is a strict subset of another Box. This is equivalent - * to `<`. + /** Check whether a NDBox is a strict subset of another Box. This is + * equivalent to `<`. */ bool is_strict_subset_of(const NDBox &b2) const { return b->is_strict_subset_of(*b2.b); } - /** Check whether a Box is a strict superset of another Box. This is + /** Check whether a NDBox is a strict superset of another Box. This is * equivalent to `>`. */ bool is_strict_superset_of(const NDBox &b2) const { diff --git a/include/openPMD/regions/NDRegion.hpp b/include/openPMD/regions/NDRegion.hpp index 267f593c1d..48cc78c4fb 100644 --- a/include/openPMD/regions/NDRegion.hpp +++ b/include/openPMD/regions/NDRegion.hpp @@ -325,13 +325,12 @@ std::unique_ptr> make_VRegion(const std::size_t D, /** A Region * - * The dimension (number of component) of the Region is only known at - * run-time. @see Region + * The dimension of the NDRegion is only known at run-time. @see + * Region * - * A region is described by two points, its lower bound (inclusive) and - * upper bound (exclusive). If the lower bound not less than the upper - * bound, the region is empty. Empty regiones are fine (similar to an empty - * array). + * A region is an arbitrarily shaped set of points. The internal + * representation is likely based on boxes, and is thus most efficient + * if the region has many axis-aligned boundaries. */ template class NDRegion { template using VRegion = detail::VRegion; @@ -355,7 +354,7 @@ template class NDRegion { /** Create an invalid Region */ NDRegion() : r() {} - /** Create a value-initialized Region with D components + /** Create an empty Region in D dimensions */ NDRegion(const size_type D) : r(detail::make_VRegion(D)) {} @@ -367,12 +366,18 @@ template class NDRegion { } NDRegion &operator=(NDRegion &&) = default; + /** Create an NDRegion from a Region + */ template NDRegion(const Region &r_) : r(std::make_unique>(r_)) {} template NDRegion(Region &&r_) : r(std::make_unique>(std::move(r_))) {} + /** Convert an NDRegion to a Region + * + * This only works when D == ndims(). + */ template operator Region() const { return Region(dynamic_cast &>(*r)); } @@ -391,31 +396,51 @@ template class NDRegion { dynamic_cast &>(*r)); } + /** Create Region containing a single NDPoint + */ NDRegion(const NDPoint &p) : r(detail::make_VRegion(p.ndims(), p)) {} + /** Create Region containina a single NDBox + */ NDRegion(const NDBox &b) : r(detail::make_VRegion(b.ndims(), b)) {} + /** Create a Region from a vector of NDBoxes + */ NDRegion(const size_type D, const std::vector> &boxes) : r(detail::make_VRegion(D, boxes)) {} operator std::vector>() const { return std::vector>(*r); } - /** Check whether a Region is valid + /** Check whether an NDRegion is valid * - * A valid Region knows its number of dimensions, and its components - * are initialized. An invalid Region does not know its number of + * A valid NDRegion knows its number of dimensions, and its components + * are initialized. An invalid NDRegion does not know its number of * dimensions and holds no data, similar to a null pointer. * - * Most other member functions must not be called for invalid - * Regions. + * Most other member functions cannot not be called for invalid + * NDRegions. */ bool has_value() const { return bool(r); } + /** Number of dimensions + */ size_type ndims() const { return r->ndims(); } + /** Whether the Region is empty + */ bool empty() const { return r->empty(); } + /** Size, the total number of contained points + */ size_type size() const { return r->size(); } + /** A measure of the number of vertices defining the Region + */ size_type nboxes() const { return r->nboxes(); } + /** Shift an NDRegion to the right (upwards). The shift can be negative, which + * shifts left. + */ NDRegion operator>>(const NDPoint &d) const { return NDRegion(*r >> d); } + /** Shift an NDRegion to the left (downwards). The shift can be negative, + * which shifts right. + */ NDRegion operator<<(const NDPoint &d) const { return NDRegion(*r << d); } NDRegion &operator>>=(const NDPoint &d) { *r >>= d; @@ -425,26 +450,44 @@ template class NDRegion { *r <<= d; return *this; } + /** Scale an NDRegion + */ NDRegion operator*(const NDPoint &d) const { return NDRegion(*r * d); } NDRegion &operator*=(const NDPoint &d) { *r *= d; return *this; } + /** Grow an NDRegion by given amounts in each direction. + * + * The growth can be negative, which shrinks the NDRegion. If an NDRegion is + * shrunk too much it becomes empty. Growing an empty NDRegion always + * results in an empty NDRegion. + */ NDRegion grown(const NDPoint &dlo, const NDPoint &dup) const { return NDRegion(r->grown(dlo, dup)); } NDRegion grown(const NDPoint &d) const { return NDRegion(r->grown(d)); } NDRegion grown(const T &n) const { return NDRegion(r->grown(n)); } + /** Shrink an NDRegion by given amounts in each direction. + * + * The shrinkage can be negative, which shrinks the NDRegion. If a Region is + * shrunk too much it becomes empty. Growing an empty NDRegion always + * results in an empty NDRegion. + */ NDRegion shrunk(const NDPoint &dlo, const NDPoint &dup) const { return NDRegion(r->shrunk(dlo, dup)); } NDRegion shrunk(const NDPoint &d) const { return NDRegion(r->shrunk(d)); } NDRegion shrunk(const T &n) const { return NDRegion(r->shrunk(n)); } + /** Compare two Regions + */ friend bool operator==(const NDRegion ®ion1, const NDRegion ®ion2) { return *region1.r == *region2.r; } + /** Compare two Regions + */ friend bool operator!=(const NDRegion ®ion1, const NDRegion ®ion2) { return *region1.r != *region2.r; } @@ -461,19 +504,30 @@ template class NDRegion { return *region2.r != box1; } + /** Calculate the bounding box of an NDRegion. This is the smallest NDBox that + * contains the NDRegion. + */ friend NDBox bounding_box(const NDRegion ®ion) { return region.r->bounding_box1(); } + /** Set intersection of two NDRegions + */ friend NDRegion operator&(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(*region1.r & *region2.r); } + /** Set union of two NDRegions + */ friend NDRegion operator|(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(*region1.r | *region2.r); } + /** Symmetric difference of two NDRegions + */ friend NDRegion operator^(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(*region1.r ^ *region2.r); } + /** Set difference of two NDRegions + */ friend NDRegion operator-(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(*region1.r - *region2.r); } @@ -495,53 +549,82 @@ template class NDRegion { return *this; } + /** Set intersection of two NDRegions + */ friend NDRegion intersection(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(intersection(*region1.r, *region2.r)); } + /** Set union of two NDRegions + */ friend NDRegion setunion(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(setunion(*region1.r, *region2.r)); } + /** Symmetric difference of two NDRegions + */ friend NDRegion symmetric_difference(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(symmetric_difference(*region1.r, *region2.r)); } + /** Set difference of two NDRegions + */ friend NDRegion difference(const NDRegion ®ion1, const NDRegion ®ion2) { return NDRegion(difference(*region1.r, *region2.r)); } + /** Whether an NDRegion contains a Point + */ bool contains(const NDPoint &p) const { return r->contains(p); } + /** Whether two NDRegions are disjoint, i.e. whether they have no Point in + * common + */ friend bool isdisjoint(const NDRegion ®ion1, const NDRegion ®ion2) { return region1.r->isdisjoint1(*region2.r); } + /** is subset of + */ friend bool operator<=(const NDRegion ®ion1, const NDRegion ®ion2) { return *region1.r <= *region2.r; } + /** is superset of + */ friend bool operator>=(const NDRegion ®ion1, const NDRegion ®ion2) { return *region1.r >= *region2.r; } + /** is strict subset of + */ friend bool operator<(const NDRegion ®ion1, const NDRegion ®ion2) { return *region1.r < *region2.r; } + /** is strict superset of + */ friend bool operator>(const NDRegion ®ion1, const NDRegion ®ion2) { return *region1.r > *region2.r; } + /** is subset of + */ bool is_subset_of(const NDRegion ®ion) const { return r->is_subset_of(*region.r); } + /** is superset of + */ bool is_superset_of(const NDRegion ®ion) const { return r->is_superset_of(*region.r); } + /** is strict subset of + */ bool is_strict_subset_of(const NDRegion ®ion) const { return r->is_strict_subset_of(*region.r); } + /** is strict superset of + */ bool is_strict_superset_of(const NDRegion ®ion) const { return r->is_strict_superset_of(*region.r); } - /** Output a region + /** Output an NDegion */ friend std::ostream &operator<<(std::ostream &os, const NDRegion &x) { if (x.r) diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index fc5c54eefd..45280001d8 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -690,13 +690,16 @@ template class Region { return true; } + /** Check invariant when in debug mode; abort the code if invariant is not + * met. + */ void check_invariant() const { #if REGIONS_DEBUG assert(invariant()); #endif } - /** Create empty region + /** Create empty Region */ Region() = default; @@ -705,7 +708,11 @@ template class Region { Region &operator=(const Region &) = default; Region &operator=(Region &&) = default; + /** Create Region containing a single Point + */ Region(const Point &p) : Region(Box(p)) {} + /** Create Region containina a single Box + */ Region(const Box &b) { if (b.empty()) return; @@ -740,6 +747,8 @@ template class Region { } public: + /** Create a Region from a vector of Boxes + */ Region(const std::vector> &boxes) { *this = region_from_boxes(boxes.begin(), boxes.end()); #if REGIONS_DEBUG @@ -754,6 +763,10 @@ template class Region { check_invariant(); } + /** Convert a Region to a vector of non-overlapping Boxes + * + * This creates a canonical, reasonably efficient representation. + */ operator std::vector>() const { std::vector> res; std::map, T> old_subboxes; @@ -918,9 +931,15 @@ template class Region { public: // Predicates + /** Number of dimensions + */ size_type ndims() const { return D; } + /** Whether the Region is empty + */ bool empty() const { return subregions.empty(); } + /** Size, the total number of contained points + */ size_type size() const { size_type total_size = 0; T old_pos = std::numeric_limits::min(); // location of last subregion @@ -939,6 +958,8 @@ template class Region { return total_size; } + /** A measure of the number of vertices defining the Region + */ size_type nboxes() const { size_type sz = 0; for (const auto &pos_subregion : subregions) { @@ -949,6 +970,9 @@ template class Region { } // Shift and scale operators + /** Shift a Region to the right (upwards). The shift can be negative, which + * shifts left. + */ Region operator>>(const Point &d) const { Region nr; nr.subregions.reserve(subregions.size()); @@ -962,9 +986,14 @@ template class Region { nr.check_invariant(); return nr; } + /** Shift a Region to the left (downwards). The shift can be negative, which + * shifts right. + */ Region operator<<(const Point &d) const { return *this >> -d; } Region &operator>>=(const Point &d) { return *this = *this >> d; } Region &operator<<=(const Point &d) { return *this = *this << d; } + /** Scale a Region + */ Region operator*(const Point &s) const { if (s[D - 1] == 0) return empty() ? Region() : Region(Point()); @@ -1009,6 +1038,12 @@ template class Region { } public: + /** Grow a Region by given amounts in each direction. + * + * The growth can be negative, which shrinks the Region. If a Region is + * shrunk too much it becomes empty. Growing an empty Region always + * results in an empty Region. + */ Region grown(const Point &dlo, const Point &dup) const { const Region ®ion0 = *this; const bool need_shrink = any(dlo + dup < Point()); @@ -1037,6 +1072,12 @@ template class Region { } Region grown(const Point &d) const { return grown(d, d); } Region grown(const T &n) const { return grown(Point::pure(n)); } + /** Shrink a Region by given amounts in each direction. + * + * The shrinkage can be negative, which shrinks the Region. If a Region is + * shrunk too much it becomes empty. Growing an empty Region always + * results in an empty Region. + */ Region shrunk(const Point &dlo, const Point &dup) const { return grown(-dlo, -dup); } @@ -1044,9 +1085,13 @@ template class Region { Region shrunk(const T &n) const { return shrunk(Point::pure(n)); } // Comparison operators + /** Compare two Regions + */ friend bool operator==(const Region ®ion1, const Region ®ion2) { return region1.subregions == region2.subregions; } + /** Compare two Regions + */ friend bool operator!=(const Region ®ion1, const Region ®ion2) { return !(region1 == region2); } @@ -1064,6 +1109,9 @@ template class Region { } // Set operations + /** Calculate the bounding box of a REgion. This is the smallest Box that + * contains the Region. + */ friend Box bounding_box(const Region ®ion) { if (region.empty()) return Box(); @@ -1081,16 +1129,22 @@ template class Region { return Box(pmin.insert(D - 1, xmin), pmax.insert(D - 1, xmax)); } + /** Set intersection of two Regions + */ friend Region operator&(const Region ®ion1, const Region ®ion2) { return binary_operator([](const Subregion &set1, const Subregion &set2) { return set1 & set2; }, region1, region2); } + /** Set union of two Regions + */ friend Region operator|(const Region ®ion1, const Region ®ion2) { return binary_operator([](const Subregion &set1, const Subregion &set2) { return set1 | set2; }, region1, region2); } + /** Symmetric difference of two Regions + */ friend Region operator^(const Region ®ion1, const Region ®ion2) { // TODO: If region2 is much smaller than region1, direct insertion may be // faster @@ -1098,6 +1152,8 @@ template class Region { const Subregion &set2) { return set1 ^ set2; }, region1, region2); } + /** Set difference of two Regions + */ friend Region operator-(const Region ®ion1, const Region ®ion2) { return binary_operator([](const Subregion &set1, const Subregion &set2) { return set1 - set2; }, @@ -1109,51 +1165,82 @@ template class Region { Region &operator^=(const Region ®ion) { return *this = *this ^ region; } Region &operator-=(const Region ®ion) { return *this = *this - region; } + /** Set intersection of two Regions + */ friend Region intersection(const Region ®ion1, const Region ®ion2) { return region1 & region2; } + /** Set union of two Regions + */ friend Region setunion(const Region ®ion1, const Region ®ion2) { return region1 | region2; } + /** Symmetric difference of two Regions + */ friend Region symmetric_difference(const Region ®ion1, const Region ®ion2) { return region1 ^ region2; } + /** Set difference of two Regions + */ friend Region difference(const Region ®ion1, const Region ®ion2) { return region1 - region2; } // Set comparison operators + /** Whether a Region contains a Point + */ bool contains(const Point &p) const { return !isdisjoint(*this, Region(p)); } + /** Whether two Regions are disjoint, i.e. whether they have no Point in + * common + */ friend bool isdisjoint(const Region ®ion1, const Region ®ion2) { return (region1 & region2).empty(); } // Comparison operators + /** is subset of + */ friend bool operator<=(const Region ®ion1, const Region ®ion2) { return (region1 - region2).empty(); } + /** is superset of + */ friend bool operator>=(const Region ®ion1, const Region ®ion2) { return region2 <= region1; } + /** is strict subset of + */ friend bool operator<(const Region ®ion1, const Region ®ion2) { return region1 != region2 && region1 <= region2; } + /** is strict superset of + */ friend bool operator>(const Region ®ion1, const Region ®ion2) { return region2 < region1; } + /** is subset of + */ bool is_subset_of(const Region ®ion) const { return *this <= region; } + /** is superset of + */ bool is_superset_of(const Region ®ion) const { return *this >= region; } + /** is strict subset of + */ bool is_strict_subset_of(const Region ®ion) const { return *this < region; } + /** is strict superset of + */ bool is_strict_superset_of(const Region ®ion) const { return *this > region; } + /** Output a Region + */ friend std::ostream &operator<<(std::ostream &os, const Region ®ion) { os << "{"; const std::vector> boxes(region); From 8f843cc499618e290d2ccaf34326cdc2eb89db97 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 12 Jul 2021 10:21:32 -0400 Subject: [PATCH 63/68] Remove unwanted files --- include/openPMD/regions/RegionCalculus.hpp | 1116 ----- .../openPMD/regions/RegionCalculus_old.hpp | 4055 ----------------- 2 files changed, 5171 deletions(-) delete mode 100644 include/openPMD/regions/RegionCalculus.hpp delete mode 100644 include/openPMD/regions/RegionCalculus_old.hpp diff --git a/include/openPMD/regions/RegionCalculus.hpp b/include/openPMD/regions/RegionCalculus.hpp deleted file mode 100644 index fc93110560..0000000000 --- a/include/openPMD/regions/RegionCalculus.hpp +++ /dev/null @@ -1,1116 +0,0 @@ -#ifndef REGIONCALCULUS2_HPP -#define REGIONCALCULUS2_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace openPMD { -namespace RegionCalculus { - -namespace detail { -// Combine hashes -template std::size_t hash_combine(std::size_t seed, const T &x) { - std::hash h; - // Taken from Boost - return seed ^ - h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2); -} -} // namespace detail - -namespace detail { -template -constexpr auto array_push(const Tuple &t, std::index_sequence, - const U &x) { - return std::array{std::get(t)..., T(x)}; -} - -template -constexpr auto array_push(const std::array &a, const U &e) { - return array_push(a, std::make_index_sequence(), e); -} - -template -constexpr std::array construct_array(const F &f) { - if constexpr (N == 0) - return std::array(); - if constexpr (N > 0) - return array_push(construct_array(f), f(N - 1)); -} -} // namespace detail - -/** A D-dimensional point - * - * The dimension D needs to be known at compile time. @see ndpoint - * - * Points can represent either points or distances. Points are - * fixed-size vectors that support arithmetic operations. - */ -template class point { - std::array elts; - -public: - /** Component type - */ - typedef T value_type; - /** Return type of point::size() - */ - typedef std::size_t size_type; - - /** Create a value-initialized point - * - * For most types, this initializes all components to zero. - */ - constexpr point() : elts{} {} // value initialization - - point(const point &) = default; - point(point &&) = default; - point &operator=(const point &) = default; - point &operator=(point &&) = default; - - /** Loop over the natural number sequence [0, ..., D-1], and evaluate f for - * each number - */ - template static constexpr void loop(const F &f) { - for (size_type d = 0; d < D; ++d) - f(d); - } - /** Create a new point by applying a function to the natural number sequence - * [0, ..., D-1] - */ - template static constexpr point make(const F &f) { - return detail::construct_array(f); - } - - /** Create a point with each component set to the same value a - */ - static constexpr point pure(const T &a) { - return make([&](size_type) { return a; }); - } - - /** Create a unit point, where component dir is one, and all other components - * are zero - */ - static constexpr point unit(size_type dir) { - return make([&](size_type d) { return d == dir; }); - } - - /** Create a point with components set to the natural number - * sequence [0, ..., D-1] - */ - static constexpr point iota() { - return point::make([](size_type d) { return d; }); - } - - /** Map a function over all components of one or several points - * - * Example: - * point pi, pj; - * point pk = fmap([](auto i, auto j) { return i+j; }, pi, pj); - * This calculates the component-wise sum of pi and pj, i.e. pi + pj . - */ - template >>> - friend constexpr point fmap(const F &f, const point &x, - const point &...args) { - return point::make( - [&](size_type d) { return f(x.elts[d], args.elts[d]...); }); - } - /** Map a function over all components of one or several points, returning - * void - * - * Example: - * point pi; - * fmap_([](auto& i) { return i*=2; }, pi); - * This doubles each component of pi, the same as pi *= 2 . - */ - template - friend constexpr void fmap_(const F &f, const point &x, - const point &...args) { - loop([&](size_type d) { f(x.elts[d], args.elts[d]...); }); - } - - /** Reduce over all components of one or several points - * - * Example: - * point pi; - * int s = fold([](auto r, auto i) { return r+i; }, 0, pi); - * This calculates the sum of all components ("horizonal sum") of pi, - * same as sum(pi). - */ - template >>> - friend constexpr R fold(const Op &op, R r, const point &x, - const point &...args) { - loop([&](int d) { r = op(r, x[d], args[d]...); }); - return r; - } - - /** Create a point from a point with different component type - */ - template - constexpr point(const point &x) - : elts(fmap([](const U &a) { return T(a); }, x)) {} - - /** Create a point from pointers to first and one past the last element - */ - constexpr point(const T *begin, const T *end) - : elts((assert(begin + D == end), - make([&](size_type d) { return begin[d]; }))) {} - /** Create a point from an initializer list - * - * Example: point{1,2,3} - */ - constexpr point(std::initializer_list lst) - : point(lst.begin(), lst.end()) {} - /** Create a point from a C-style array - */ - constexpr point(const T (&arr)[D]) : elts(&arr[0], &arr[D]) {} - /** Create a point from a std::array - */ - constexpr point(const std::array &arr) : elts(arr) {} - /** Create a point from a std::vector - */ - constexpr point(const std::vector &vec) : point(vec.begin(), vec.end()) {} - - /** Convert a point to a std::array - */ - operator std::array() const { return elts; } - /** Convert a point to a std::vector - */ - explicit operator std::vector() const { - return std::vector(elts.begin(), elts.end()); - } - - /** Number of components (same as number of dimensions) - */ - constexpr size_type size() const { return D; } - - /** Get a component of a point - */ - constexpr const T &operator[](const size_type d) const { return elts[d]; } - /** Get a component of a point - */ - constexpr T &operator[](const size_type d) { return elts[d]; } - - /** Number of dimensions (same as number of components) - */ - constexpr size_type ndims() const { return D; } - - friend constexpr point operator+(const point &x) { - return fmap([](const T &a) { return +a; }, x); - } - friend constexpr point operator-(const point &x) { - return fmap([](const T &a) { return -a; }, x); - } - friend constexpr point operator~(const point &x) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([](const T &a) { return ~a; }, x); - else - return point(); - } - friend constexpr point operator!(const point &x) { - return fmap([](const T &a) { return !a; }, x); - } - - friend constexpr point operator+(const point &x, const point &y) { - return fmap([](const T &a, const T &b) { return a + b; }, x, y); - } - friend constexpr point operator-(const point &x, const point &y) { - return fmap([](const T &a, const T &b) { return a - b; }, x, y); - } - friend constexpr point operator*(const point &x, const point &y) { - return fmap([](const T &a, const T &b) { return a * b; }, x, y); - } - friend constexpr point operator/(const point &x, const point &y) { - return fmap([](const T &a, const T &b) { return a / b; }, x, y); - } - friend constexpr point operator%(const point &x, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([](const T &a, const T &b) { return a % b; }, x, y); - else - return point(); - } - friend constexpr point operator&(const point &x, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([](const T &a, const T &b) { return a & b; }, x, y); - else - return point(); - } - friend constexpr point operator|(const point &x, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([](const T &a, const T &b) { return a | b; }, x, y); - else - return point(); - } - friend constexpr point operator^(const point &x, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([](const T &a, const T &b) { return a ^ b; }, x, y); - else - return point(); - } - friend constexpr point operator&&(const point &x, const point &y) { - return fmap([](const T &a, const T &b) { return a && b; }, x, y); - } - friend constexpr point operator||(const point &x, const point &y) { - return fmap([](const T &a, const T &b) { return a || b; }, x, y); - } - - friend constexpr point operator+(const T &a, const point &y) { - return fmap([&](const T &b) { return a + b; }, y); - } - friend constexpr point operator-(const T &a, const point &y) { - return fmap([&](const T &b) { return a - b; }, y); - } - friend constexpr point operator*(const T &a, const point &y) { - return fmap([&](const T &b) { return a * b; }, y); - } - friend constexpr point operator/(const T &a, const point &y) { - return fmap([&](const T &b) { return a / b; }, y); - } - friend constexpr point operator%(const T &a, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &b) { return a % b; }, y); - else - return point(); - } - friend constexpr point operator&(const T &a, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &b) { return a & b; }, y); - else - return point(); - } - friend constexpr point operator|(const T &a, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &b) { return a | b; }, y); - else - return point(); - } - friend constexpr point operator^(const T &a, const point &y) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &b) { return a ^ b; }, y); - else - return point(); - } - friend constexpr point operator&&(const T &a, const point &y) { - return fmap([&](const T &b) { return a && b; }, y); - } - friend constexpr point operator||(const T &a, const point &y) { - return fmap([&](const T &b) { return a || b; }, y); - } - - friend constexpr point operator+(const point &x, const T &b) { - return fmap([&](const T &a) { return a + b; }, x); - } - friend constexpr point operator-(const point &x, const T &b) { - return fmap([&](const T &a) { return a - b; }, x); - } - friend constexpr point operator*(const point &x, const T &b) { - return fmap([&](const T &a) { return a * b; }, x); - } - friend constexpr point operator/(const point &x, const T &b) { - return fmap([&](const T &a) { return a / b; }, x); - } - friend constexpr point operator%(const point &x, const T &b) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &a) { return a % b; }, x); - else return point(); - } - friend constexpr point operator&(const point &x, const T &b) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &a) { return a & b; }, x); - else - return point(); - } - friend constexpr point operator|(const point &x, const T &b) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &a) { return a | b; }, x); - else - return point(); - } - friend constexpr point operator^(const point &x, const T &b) { - if constexpr (std::is_integral_v && !std::is_same_v) - return fmap([&](const T &a) { return a ^ b; }, x); - else - return point(); - } - friend constexpr point operator&&(const point &x, const T &b) { - return fmap([&](const T &a) { return a && b; }, x); - } - friend constexpr point operator||(const point &x, const T &b) { - return fmap([&](const T &a) { return a || b; }, x); - } - - constexpr point &operator+=(const point &x) { return *this = *this + x; } - constexpr point &operator-=(const point &x) { return *this = *this - x; } - constexpr point &operator*=(const point &x) { return *this = *this * x; } - constexpr point &operator/=(const point &x) { return *this = *this / x; } - constexpr point &operator%=(const point &x) { return *this = *this % x; } - constexpr point &operator&=(const point &x) { return *this = *this & x; } - constexpr point &operator|=(const point &x) { return *this = *this | x; } - constexpr point &operator^=(const point &x) { return *this = *this ^ x; } - - constexpr point &operator+=(const T &a) { return *this = *this + a; } - constexpr point &operator-=(const T &a) { return *this = *this - a; } - constexpr point &operator*=(const T &a) { return *this = *this * a; } - constexpr point &operator/=(const T &a) { return *this = *this / a; } - constexpr point &operator%=(const T &a) { return *this = *this % a; } - constexpr point &operator&=(const T &a) { return *this = *this & a; } - constexpr point &operator|=(const T &a) { return *this = *this | a; } - constexpr point &operator^=(const T &a) { return *this = *this ^ a; } - - friend constexpr point abs(const point &x) { - using std::abs; - return fmap([&](const T &a) { return abs(a); }, x); - } - friend constexpr point fabs(const point &x) { - using std::fabs; - return fmap([&](const T &a) { return fabs(a); }, x); - } - - friend constexpr point fmax(const point &x, const point &y) { - using std::fmax; - return fmap([](const T &a, const T &b) { return fmax(a, b); }, x, y); - } - friend constexpr point fmin(const point &x, const point &y) { - using std::fmin; - return fmap([](const T &a, const T &b) { return fmin(a, b); }, x, y); - } - friend constexpr point max(const point &x, const point &y) { - using std::max; - return fmap([](const T &a, const T &b) { return max(a, b); }, x, y); - } - friend constexpr point min(const point &x, const point &y) { - using std::min; - return fmap([](const T &a, const T &b) { return min(a, b); }, x, y); - } - - friend constexpr point fmax(const T &a, const point &y) { - using std::fmax; - return fmap([&](const T &b) { return fmax(a, b); }, y); - } - friend constexpr point fmin(const T &a, const point &y) { - using std::fmin; - return fmap([&](const T &b) { return fmin(a, b); }, y); - } - friend constexpr point max(const T &a, const point &y) { - using std::max; - return fmap([&](const T &b) { return max(a, b); }, y); - } - friend constexpr point min(const T &a, const point &y) { - using std::min; - return fmap([&](const T &b) { return min(a, b); }, y); - } - - friend constexpr point fmax(const point &x, const T &b) { - using std::fmax; - return fmap([&](const T &a) { return fmax(a, b); }, x); - } - friend constexpr point fmin(const point &x, const T &b) { - using std::fmin; - return fmap([&](const T &a) { return fmin(a, b); }, x); - } - friend constexpr point max(const point &x, const T &b) { - using std::max; - return fmap([&](const T &a) { return max(a, b); }, x); - } - friend constexpr point min(const point &x, const T &b) { - using std::min; - return fmap([&](const T &a) { return min(a, b); }, x); - } - - friend constexpr bool all(const point &x) { - return fold([](bool r, const T &a) { return r && a; }, true, x); - } - friend constexpr bool any(const point &x) { - return fold([](bool r, const T &a) { return r || a; }, false, x); - } - friend constexpr T max_element(const point &x) { - using std::max; - return fold([](const T &r, const T &a) { return max(r, a); }, - std::numeric_limits::lowest(), x); - } - friend constexpr T min_element(const point &x) { - using std::min; - return fold([](const T &r, const T &a) { return min(r, a); }, - std::numeric_limits::max(), x); - } - friend constexpr T product(const point &x) { - return fold([](const T &r, const T &a) { return r * a; }, T(1), x); - } - friend constexpr T sum(const point &x) { - return fold([](const T &r, const T &a) { return r + a; }, T(0), x); - } - - friend constexpr bool operator==(const point &x, const point &y) { - return all(fmap([](const T &a, const T &b) { return a == b; })); - } - friend constexpr bool operator!=(const point &x, const point &y) { - return any(fmap([](const T &a, const T &b) { return a != b; })); - } - - /** Output a point - */ - friend std::ostream &operator<<(std::ostream &os, const point &x) { - os << "["; - for (size_type d = 0; d < D; ++d) { - if (d != 0) - os << ","; - os << x[d]; - } - os << "]"; - return os; - } -}; - -} // namespace RegionCalculus -} // namespace openPMD - -namespace std { - -template -struct equal_to> { - constexpr bool - operator()(const openPMD::RegionCalculus::point &p, - const openPMD::RegionCalculus::point &q) const { - return openPMD::RegionCalculus::point::all( - openPMD::RegionCalculus::point::fmap( - [](const T &a, const T &b) { return equal_to()(a, b); }, p, q)); - } -}; - -template -struct hash> { - constexpr size_t - operator()(const openPMD::RegionCalculus::point &p) const { - return openPMD::RegionCalculus::point::fold( - [](size_t r, const T &b) { - return openPMD::RegionCalculus::detail::hash_combine(r, b); - }, - size_t(0xb22da17173243869ULL), p); - } -}; - -template -struct less> { - constexpr bool - operator()(const openPMD::RegionCalculus::point &p, - const openPMD::RegionCalculus::point &q) const { - return openPMD::RegionCalculus::point::fold( - [](bool r, const T &a, const T &b) { return r && less()(a, b); }, - true, p, q); - } -}; - -} // namespace std - -namespace openPMD { -namespace RegionCalculus { - -namespace detail { - -// Abstract base helper class - -template class vpoint { -public: - typedef T value_type; - typedef std::size_t size_type; - - virtual std::unique_ptr copy() const = 0; - - virtual ~vpoint() {} - - virtual size_type size() const = 0; - - virtual const T &operator[](const size_type d) const = 0; - virtual T &operator[](const size_type d) = 0; - - virtual size_type ndims() const = 0; - - virtual std::unique_ptr operator+() const = 0; - virtual std::unique_ptr operator-() const = 0; - virtual std::unique_ptr operator~() const = 0; - virtual std::unique_ptr> operator!() const = 0; - - virtual std::unique_ptr operator+(const vpoint &x) const = 0; - virtual std::unique_ptr operator-(const vpoint &x) const = 0; - virtual std::unique_ptr operator*(const vpoint &x) const = 0; - virtual std::unique_ptr operator/(const vpoint &x) const = 0; - virtual std::unique_ptr operator%(const vpoint &x) const = 0; - virtual std::unique_ptr operator&(const vpoint &x) const = 0; - virtual std::unique_ptr operator|(const vpoint &x) const = 0; - virtual std::unique_ptr operator^(const vpoint &x) const = 0; - virtual std::unique_ptr> operator&&(const vpoint &x) const = 0; - virtual std::unique_ptr> operator||(const vpoint &x) const = 0; - - virtual std::unique_ptr left_plus(const T &a) const = 0; - virtual std::unique_ptr left_minus(const T &a) const = 0; - virtual std::unique_ptr left_multiplies(const T &a) const = 0; - virtual std::unique_ptr left_divides(const T &a) const = 0; - virtual std::unique_ptr left_modulus(const T &a) const = 0; - virtual std::unique_ptr left_bit_and(const T &a) const = 0; - virtual std::unique_ptr left_bit_or(const T &a) const = 0; - virtual std::unique_ptr left_bit_xor(const T &a) const = 0; - virtual std::unique_ptr> left_logical_and(const T &a) const = 0; - virtual std::unique_ptr> left_logical_or(const T &a) const = 0; - - virtual std::unique_ptr operator+(const T &b) const = 0; - virtual std::unique_ptr operator-(const T &b) const = 0; - virtual std::unique_ptr operator*(const T &b) const = 0; - virtual std::unique_ptr operator/(const T &b) const = 0; - virtual std::unique_ptr operator%(const T &b) const = 0; - virtual std::unique_ptr operator&(const T &b) const = 0; - virtual std::unique_ptr operator|(const T &b) const = 0; - virtual std::unique_ptr operator^(const T &b) const = 0; - virtual std::unique_ptr> operator&&(const T &b) const = 0; - virtual std::unique_ptr> operator||(const T &b) const = 0; - - virtual vpoint &operator+=(const vpoint &x) = 0; - virtual vpoint &operator-=(const vpoint &x) = 0; - virtual vpoint &operator*=(const vpoint &x) = 0; - virtual vpoint &operator/=(const vpoint &x) = 0; - virtual vpoint &operator%=(const vpoint &x) = 0; - virtual vpoint &operator&=(const vpoint &x) = 0; - virtual vpoint &operator|=(const vpoint &x) = 0; - virtual vpoint &operator^=(const vpoint &x) = 0; - - virtual vpoint &operator+=(const T &b) = 0; - virtual vpoint &operator-=(const T &b) = 0; - virtual vpoint &operator*=(const T &b) = 0; - virtual vpoint &operator/=(const T &b) = 0; - virtual vpoint &operator%=(const T &b) = 0; - virtual vpoint &operator&=(const T &b) = 0; - virtual vpoint &operator|=(const T &b) = 0; - virtual vpoint &operator^=(const T &b) = 0; - - virtual std::ostream &output(std::ostream &os) const = 0; -}; - -// Helper class wrapping point - -template class wpoint final : public vpoint { - point p; - -public: - using typename vpoint::value_type; - using typename vpoint::size_type; - - wpoint() : p{} {} - - wpoint(const wpoint &x) = default; - wpoint(wpoint &&) = default; - wpoint &operator=(const wpoint &) = default; - wpoint &operator=(wpoint &&) = default; - - wpoint(const point &p) : p(p) {} - wpoint(point &&p) : p(std::move(p)) {} - - std::unique_ptr> copy() const override { - return std::make_unique(*this); - } - - ~wpoint() override {} - - constexpr size_type size() const override { return p.size(); } - - const T &operator[](const size_type d) const override { return p[d]; } - T &operator[](const size_type d) override { return p[d]; } - - constexpr size_type ndims() const override { return p.ndims(); } - - std::unique_ptr> operator+() const override { - return std::make_unique(+p); - } - std::unique_ptr> operator-() const override { - return std::make_unique(+p); - } - std::unique_ptr> operator~() const override { - return std::make_unique(~p); - } - std::unique_ptr> operator!() const override { - return std::make_unique>(!p); - } - - std::unique_ptr> operator+(const vpoint &x) const override { - return std::make_unique(p + dynamic_cast(x).p); - } - std::unique_ptr> operator-(const vpoint &x) const override { - return std::make_unique(p - dynamic_cast(x).p); - } - std::unique_ptr> operator*(const vpoint &x) const override { - return std::make_unique(p * dynamic_cast(x).p); - } - std::unique_ptr> operator/(const vpoint &x) const override { - return std::make_unique(p / dynamic_cast(x).p); - } - std::unique_ptr> operator%(const vpoint &x) const override { - return std::make_unique(p % dynamic_cast(x).p); - } - std::unique_ptr> operator&(const vpoint &x) const override { - return std::make_unique(p & dynamic_cast(x).p); - } - std::unique_ptr> operator|(const vpoint &x) const override { - return std::make_unique(p | dynamic_cast(x).p); - } - std::unique_ptr> operator^(const vpoint &x) const override { - return std::make_unique(p ^ dynamic_cast(x).p); - } - std::unique_ptr> operator&&(const vpoint &x) const override { - return std::make_unique>(p && - dynamic_cast(x).p); - } - std::unique_ptr> operator||(const vpoint &x) const override { - return std::make_unique>(p || - dynamic_cast(x).p); - } - - std::unique_ptr> left_plus(const T &a) const override { - return std::make_unique(a + p); - } - std::unique_ptr> left_minus(const T &a) const override { - return std::make_unique(a - p); - } - std::unique_ptr> left_multiplies(const T &a) const override { - return std::make_unique(a * p); - } - std::unique_ptr> left_divides(const T &a) const override { - return std::make_unique(a / p); - } - std::unique_ptr> left_modulus(const T &a) const override { - return std::make_unique(a % p); - } - std::unique_ptr> left_bit_and(const T &a) const override { - return std::make_unique(a & p); - } - std::unique_ptr> left_bit_or(const T &a) const override { - return std::make_unique(a | p); - } - std::unique_ptr> left_bit_xor(const T &a) const override { - return std::make_unique(a ^ p); - } - std::unique_ptr> left_logical_and(const T &a) const override { - return std::make_unique>(a && p); - } - std::unique_ptr> left_logical_or(const T &a) const override { - return std::make_unique>(a || p); - } - - std::unique_ptr> operator+(const T &b) const override { - return std::make_unique(p + b); - } - std::unique_ptr> operator-(const T &b) const override { - return std::make_unique(p - b); - } - std::unique_ptr> operator*(const T &b) const override { - return std::make_unique(p * b); - } - std::unique_ptr> operator/(const T &b) const override { - return std::make_unique(p / b); - } - std::unique_ptr> operator%(const T &b) const override { - return std::make_unique(p % b); - } - std::unique_ptr> operator&(const T &b) const override { - return std::make_unique(p & b); - } - std::unique_ptr> operator|(const T &b) const override { - return std::make_unique(p | b); - } - std::unique_ptr> operator^(const T &b) const override { - return std::make_unique(p ^ b); - } - std::unique_ptr> operator&&(const T &b) const override { - return std::make_unique>(p && b); - } - std::unique_ptr> operator||(const T &b) const override { - return std::make_unique>(p || b); - } - - vpoint &operator+=(const vpoint &x) override { - p += dynamic_cast(x).p; - return *this; - } - vpoint &operator-=(const vpoint &x) override { - p -= dynamic_cast(x).p; - return *this; - } - vpoint &operator*=(const vpoint &x) override { - p *= dynamic_cast(x).p; - return *this; - } - vpoint &operator/=(const vpoint &x) override { - p /= dynamic_cast(x).p; - return *this; - } - vpoint &operator%=(const vpoint &x) override { - p %= dynamic_cast(x).p; - return *this; - } - vpoint &operator&=(const vpoint &x) override { - p &= dynamic_cast(x).p; - return *this; - } - vpoint &operator|=(const vpoint &x) override { - p |= dynamic_cast(x).p; - return *this; - } - vpoint &operator^=(const vpoint &x) override { - p ^= dynamic_cast(x).p; - return *this; - } - - vpoint &operator+=(const T &b) override { - p += b; - return *this; - } - vpoint &operator-=(const T &b) override { - p -= b; - return *this; - } - vpoint &operator*=(const T &b) override { - p *= b; - return *this; - } - vpoint &operator/=(const T &b) override { - p /= b; - return *this; - } - vpoint &operator%=(const T &b) override { - p %= b; - return *this; - } - vpoint &operator&=(const T &b) override { - p &= b; - return *this; - } - vpoint &operator|=(const T &b) override { - p |= b; - return *this; - } - vpoint &operator^=(const T &b) override { - p ^= b; - return *this; - } - - std::ostream &output(std::ostream &os) const override { return os << p; } -}; - -template -std::unique_ptr> make_vpoint(const std::size_t D) { - switch (D) { - case 0: - return std::make_unique>(); - case 1: - return std::make_unique>(); - case 2: - return std::make_unique>(); - case 3: - return std::make_unique>(); - case 4: - return std::make_unique>(); - case 5: - return std::make_unique>(); - default: - abort(); - } -} - -} // namespace detail - -/** A point - * - * The dimension (number of component) of the point is only known at - * run-time. @see point - * - * Points can represent either points or distances. Points are - * fixed-size vectors that support arithmetic operations. - */ -template class ndpoint { - - template using vpoint = detail::vpoint; - - template friend class ndpoint; - - std::unique_ptr> p; - - ndpoint(std::unique_ptr> p) : p(std::move(p)) {} - -public: - /** Component type - */ - typedef typename vpoint::value_type value_type; - /** Return type of point::size() - */ - typedef typename vpoint::size_type size_type; - - /** Create an invalid point - */ - ndpoint() : p() {} - /** Create a value-initialized point with D components - */ - ndpoint(const size_type D) : p(detail::make_vpoint(D)) {} - - ndpoint(const ndpoint &x) : p(x.p ? x.p->copy() : nullptr) {} - ndpoint(ndpoint &&) = default; - ndpoint &operator=(const ndpoint &) = default; - ndpoint &operator=(ndpoint &&) = default; - - /** Check whether a point is valid - * - * A valid point knows its number of dimensions, and its components - * are initialized. An invalid point does not know its number of - * dimensions and holds no data, similar to a null pointer. - * - * Most other member functions must not be called for invalid - * points. - */ - operator bool() const { return bool(p); } - - /** Number of comopnents (same as number of dimensions) - */ - size_type size() const { return p->size(); } - - /** Get a component of a point - */ - const T &operator[](const size_type d) const { return (*p)[d]; } - /** Get a component of a point - */ - T &operator[](const size_type d) { return (*p)[d]; } - - /** Number of dimensions (same as number of comopnents) - */ - size_type ndims() const { return p->ndims(); } - - friend ndpoint operator+(const ndpoint &x) { return ndpoint(+*x.p); } - friend ndpoint operator-(const ndpoint &x) { return ndpoint(-*x.p); } - friend ndpoint operator~(const ndpoint &x) { return ndpoint(~*x.p); } - friend ndpoint operator!(const ndpoint &x) { - return ndpoint(!*x.p); - } - - friend ndpoint operator+(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p + *y.p); - } - friend ndpoint operator-(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p - *y.p); - } - friend ndpoint operator*(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p * *y.p); - } - friend ndpoint operator/(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p / *y.p); - } - friend ndpoint operator%(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p % *y.p); - } - friend ndpoint operator&(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p & *y.p); - } - friend ndpoint operator|(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p | *y.p); - } - friend ndpoint operator^(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p ^ *y.p); - } - friend ndpoint operator&&(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p && *y.p); - } - friend ndpoint operator||(const ndpoint &x, const ndpoint &y) { - return ndpoint(*x.p || *y.p); - } - - friend ndpoint operator+(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_plus(a)); - } - friend ndpoint operator-(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_minus(a)); - } - friend ndpoint operator*(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_multiplies(a)); - } - friend ndpoint operator/(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_divides(a)); - } - friend ndpoint operator%(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_modulus(a)); - } - friend ndpoint operator&(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_bit_and(a)); - } - friend ndpoint operator|(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_bit_or(a)); - } - friend ndpoint operator^(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_bit_xor(a)); - } - friend ndpoint operator&&(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_logical_and(a)); - } - friend ndpoint operator||(const T &a, const ndpoint &y) { - return ndpoint(y.p->left_logical_or(a)); - } - - friend ndpoint operator+(const ndpoint &x, const T &b) { - return ndpoint(*x.p + b); - } - friend ndpoint operator-(const ndpoint &x, const T &b) { - return ndpoint(*x.p - b); - } - friend ndpoint operator*(const ndpoint &x, const T &b) { - return ndpoint(*x.p * b); - } - friend ndpoint operator/(const ndpoint &x, const T &b) { - return ndpoint(*x.p / b); - } - friend ndpoint operator%(const ndpoint &x, const T &b) { - return ndpoint(*x.p % b); - } - friend ndpoint operator&(const ndpoint &x, const T &b) { - return ndpoint(*x.p & b); - } - friend ndpoint operator|(const ndpoint &x, const T &b) { - return ndpoint(*x.p | b); - } - friend ndpoint operator^(const ndpoint &x, const T &b) { - return ndpoint(*x.p ^ b); - } - friend ndpoint operator&&(const ndpoint &x, const T &b) { - return ndpoint(*x.p && b); - } - friend ndpoint operator||(const ndpoint &x, const T &b) { - return ndpoint(*x.p || b); - } - - ndpoint &operator+=(const ndpoint &x) { - *p += *x.p; - return *this; - } - ndpoint &operator-=(const ndpoint &x) { - *p -= *x.p; - return *this; - } - ndpoint &operator*=(const ndpoint &x) { - *p *= *x.p; - return *this; - } - ndpoint &operator/=(const ndpoint &x) { - *p /= *x.p; - return *this; - } - ndpoint &operator%=(const ndpoint &x) { - *p %= *x.p; - return *this; - } - ndpoint &operator&=(const ndpoint &x) { - *p &= *x.p; - return *this; - } - ndpoint &operator|=(const ndpoint &x) { - *p |= *x.p; - return *this; - } - ndpoint &operator^=(const ndpoint &x) { - *p ^= *x.p; - return *this; - } - - ndpoint &operator+=(const T &a) { - *p += a; - return *this; - } - ndpoint &operator-=(const T &a) { - *p -= a; - return *this; - } - ndpoint &operator*=(const T &a) { - *p *= a; - return *this; - } - ndpoint &operator/=(const T &a) { - *p /= a; - return *this; - } - ndpoint &operator%=(const T &a) { - *p %= a; - return *this; - } - ndpoint &operator&=(const T &a) { - *p &= a; - return *this; - } - ndpoint &operator|=(const T &a) { - *p |= a; - return *this; - } - ndpoint &operator^=(const T &a) { - *p ^= a; - return *this; - } - - friend ndpoint abs(const ndpoint &x) { return abs(*x.p); } - friend ndpoint fabs(const ndpoint &x) { return fabs(*x.p); } - - friend ndpoint fmax(const ndpoint &x, const ndpoint &y) { - return fmax(*x.p, *y.p); - } - friend ndpoint fmin(const ndpoint &x, const ndpoint &y) { - return fmin(*x.p, *y.p); - } - friend ndpoint max(const ndpoint &x, const ndpoint &y) { - return max(*x.p, *y.p); - } - friend ndpoint min(const ndpoint &x, const ndpoint &y) { - return min(*x.p, *y.p); - } - - friend ndpoint fmax(const T &a, const ndpoint &y) { return fmax(a, *y.p); } - friend ndpoint fmin(const T &a, const ndpoint &y) { return fmin(a, *y.p); } - friend ndpoint max(const T &a, const ndpoint &y) { return max(a, *y.p); } - friend ndpoint min(const T &a, const ndpoint &y) { return min(a, *y.p); } - - friend ndpoint fmax(const ndpoint &x, const T &b) { return fmax(*x.p, b); } - friend ndpoint fmin(const ndpoint &x, const T &b) { return fmin(*x.p, b); } - friend ndpoint max(const ndpoint &x, const T &b) { return max(*x.p, b); } - friend ndpoint min(const ndpoint &x, const T &b) { return min(*x.p, b); } - - friend T all(const ndpoint &x) { return all(*x.p); } - friend T any(const ndpoint &x) { return any(*x.p); } - friend T max_element(const ndpoint &x) { return max_element(*x.p); } - friend T min_element(const ndpoint &x) { return min_element(*x.p); } - friend T product(const ndpoint &x) { return product(*x.p); } - friend T sum(const ndpoint &x) { return sum(*x.p); } - - friend bool operator==(const ndpoint &x, const ndpoint &y) { - return *x.p == *y.p; - } - friend bool operator!=(const ndpoint &x, const ndpoint &y) { - return *x.p != *y.p; - } - - /** Output a point - */ - friend std::ostream &operator<<(std::ostream &os, const ndpoint &x) { - if (x.p) - x.p->output(os); - else - os << "[INVALID]"; - return os; - } -}; - -} // namespace RegionCalculus -} // namespace openPMD - -#endif // #ifndef REGIONCALCULUS2_HPP diff --git a/include/openPMD/regions/RegionCalculus_old.hpp b/include/openPMD/regions/RegionCalculus_old.hpp deleted file mode 100644 index 94b2feb219..0000000000 --- a/include/openPMD/regions/RegionCalculus_old.hpp +++ /dev/null @@ -1,4055 +0,0 @@ -#ifndef REGIONCALCULUS_HPP -#define REGIONCALCULUS_HPP - -// #include "Config.hpp" -// #include "Helpers.hpp" - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Whether extra, expensive run-time tests should be enabled. Set to 0 or 1. -#define REGIONCALCULUS_DEBUG 0 - -// Whether the more complex and more efficient tree-based implementation should -// be used. Set to 0 or 1. -#define REGIONCALCULUS_TREE 1 - -namespace RegionCalculus { - -using std::abs; -using std::array; -using std::equal_to; -using std::hash; -using std::is_sorted; -using std::less; -using std::make_pair; -using std::make_tuple; -using std::map; -using std::max; -using std::min; -using std::move; -using std::numeric_limits; -using std::ostream; -using std::pair; -using std::ptrdiff_t; -using std::size_t; -using std::sort; -using std::tuple; -using std::unique; -using std::unique_ptr; -using std::vector; - -//////////////////////////////////////////////////////////////////////////////// -// C++ helper functions -//////////////////////////////////////////////////////////////////////////////// - -// make_unique is only available in C++14 -template -std::unique_ptr make_unique1(Args &&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -// Combine hashes -template size_t hash_combine(size_t seed, const T &x) { - hash h; - // Taken from Boost - return seed ^ - h(x) + size_t(0x00e6052366ac4440eULL) + (seed << 6) + (seed >> 2); -} - -namespace detail { -// Abort when called -template struct error { - T operator()(const T &) const { std::abort(); } - T operator()(const T &, const T &) const { std::abort(); } - T operator()(const T &, const T &, const T &) const { std::abort(); } - T &operator()(T &) const { std::abort(); } - T &operator()(T &, const T &) const { std::abort(); } - T &operator()(T &, const T &, const T &) const { std::abort(); } -}; -} // namespace detail - -// Call function object F if T is integral; otherwise, abort -template struct call_if_integral { - typedef typename T::value_type S; - typedef typename std::conditional::value, F, - detail::error>::type type; - constexpr T operator()(const T &x) const { return type()(x); } - constexpr T operator()(const T &x, const T &y) const { return type()(x, y); } - constexpr T operator()(const T &x, const T &y, const T &z) const { - return type()(x, y, z); - } - T &operator()(T &x) const { return type()(x); } - T &operator()(T &x, const T &y) const { return type()(x, y); } - T &operator()(T &x, const T &y, const T &z) const { return type()(x, y, z); } -}; - -//////////////////////////////////////////////////////////////////////////////// -// Numerical functions -//////////////////////////////////////////////////////////////////////////////// - -namespace detail { - -template -typename std::enable_if::value, T>::type abs_wrapper(T x) { - using std::abs; - return abs(x); -} - -template -typename std::enable_if::value, T>::type abs_wrapper(T x) { - return x; -} -} // namespace detail - -//////////////////////////////////////////////////////////////////////////////// -// Reduction -//////////////////////////////////////////////////////////////////////////////// - -// op is functional -template ::type> -T reduce(Op &&op, vector &xs) { - assert(!xs.empty()); - for (size_t dist = 1; dist < xs.size(); dist *= 2) - for (size_t i = 0; i + dist < xs.size(); i += 2 * dist) - xs[i] = std::forward(op)(move(xs[i]), move(xs[i + dist])); - return move(xs[0]); -} - -// // op is assignment-like -// template ::type> -// T reduce(Op &&op, vector &xs) { -// assert(!xs.empty()); -// for (size_t dist = 1; dist < xs.size(); dist *= 2) -// for (size_t i = 0; i + dist < x.size(); i += 2 * dist) -// std::forward(op)(xs[i], xs[i + dist]); -// return move(xs[0]); -// } - -template -R reduce(F &&f, Op &&op, R &&z, const B &b, const E &e) { - vector rs; - for (auto i = b; i != e; ++i) - rs.push_back(std::forward(f)(*i)); - if (rs.empty()) - return std::forward(z); - return reduce(std::forward(op), rs); -} - -template ::value_type, - typename R1 = typename std::result_of::type, - typename R = typename std::decay::type> -R reduce(F &&f, Op &&op, const I &b, const I &e) { - return reduce(std::forward(f), std::forward(op), R(), b, e); -} - -template ::type, - typename R = typename std::decay::type> -R reduce(F &&f, Op &&op, const C &c) { - return reduce(std::forward(f), std::forward(op), std::begin(c), - std::end(c)); -} - -//////////////////////////////////////////////////////////////////////////////// -// Point -//////////////////////////////////////////////////////////////////////////////// - -namespace detail { -template struct largeint { typedef T type; }; -template <> struct largeint { typedef long long type; }; -template <> struct largeint { typedef long long type; }; -template <> struct largeint { typedef long long type; }; -template <> struct largeint { - typedef unsigned long long type; -}; -template <> struct largeint { typedef unsigned long long type; }; -template <> struct largeint { typedef unsigned long long type; }; -} // namespace detail - -template struct point { - typedef T value_type; - typedef int size_type; - constexpr size_type rank() const { return D; } - array elt; - point() { - for (int d = 0; d < D; ++d) - elt[d] = T(0); - } - point(const array &p) : elt(p) {} - template explicit point(const array &p) { - for (int d = 0; d < D; ++d) - elt[d] = T(p[d]); - } - point(const vector &p) { - assert(p.size() == D); - for (int d = 0; d < D; ++d) - elt[d] = p[d]; - } - template explicit point(const vector &p) { - assert(p.size() == D); - for (int d = 0; d < D; ++d) - elt[d] = T(p[d]); - } - point(const point &p) = default; - point(point &&p) = default; - explicit point(T x) { - for (int d = 0; d < D; ++d) - elt[d] = x; - } - template explicit point(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] = T(p.elt[d]); - } - operator array() const { - array r; - for (int d = 0; d < D; ++d) - r[d] = elt[d]; - return r; - } - template explicit operator array() const { - array r; - for (int d = 0; d < D; ++d) - r[d] = U(elt[d]); - return r; - } - operator vector() const { - vector r(D); - for (int d = 0; d < D; ++d) - r[d] = elt[d]; - return r; - } - template explicit operator vector() const { - vector r(D); - for (int d = 0; d < D; ++d) - r[d] = U(elt[d]); - return r; - } - explicit point(T x0, T x1) { - static_assert(D == 2, ""); - elt[0] = x0; - elt[1] = x1; - } - explicit point(T x0, T x1, T x2) { - static_assert(D == 3, ""); - elt[0] = x0; - elt[1] = x1; - elt[2] = x2; - } - explicit point(T x0, T x1, T x2, T x3) { - static_assert(D == 4, ""); - elt[0] = x0; - elt[1] = x1; - elt[2] = x2; - elt[3] = x3; - } - point &operator=(const point &p) = default; - point &operator=(point &&p) = default; - - // Access and conversion - const T &operator[](int d) const { return elt[d]; } - T &operator[](int d) { return elt[d]; } - point 0 ? D - 1 : 0)> subpoint(int dir) const { - assert(dir >= 0 && dir < D); - point 0 ? D - 1 : 0)> r; - for (int d = 0; d < D - 1; ++d) - r.elt[d] = elt[d + (d >= dir)]; - return r; - } - point superpoint(int dir, T x) const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d + (d >= dir)] = elt[d]; - r.elt[dir] = x; - return r; - } - point reversed() const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = elt[D - 1 - d]; - return r; - } - - // Unary operators - point operator+() const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = +elt[d]; - return r; - } - point operator-() const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = -elt[d]; - return r; - } - - point operator~() const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = ~elt[d]; - return r; - } - point operator!() const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = !elt[d]; - return r; - } - - // Assignment operators - point &operator+=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] += p.elt[d]; - return *this; - } - point &operator-=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] -= p.elt[d]; - return *this; - } - point &operator*=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] *= p.elt[d]; - return *this; - } - point &operator/=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] /= p.elt[d]; - return *this; - } - point &operator%=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] %= p.elt[d]; - return *this; - } - point &operator&=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] &= p.elt[d]; - return *this; - } - point &operator|=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] |= p.elt[d]; - return *this; - } - point &operator^=(const point &p) { - for (int d = 0; d < D; ++d) - elt[d] ^= p.elt[d]; - return *this; - } - - // Binary operators - point operator+(const point &p) const { return point(*this) += p; } - point operator-(const point &p) const { return point(*this) -= p; } - point operator*(const point &p) const { return point(*this) *= p; } - point operator/(const point &p) const { return point(*this) /= p; } - point operator%(const point &p) const { return point(*this) %= p; } - point operator&(const point &p) const { return point(*this) &= p; } - point operator|(const point &p) const { return point(*this) |= p; } - point operator^(const point &p) const { return point(*this) ^= p; } - point operator&&(const point &p) const { - return point(*this) &= point(p); - } - point operator||(const point &p) const { - return point(*this) |= point(p); - } - - // Unary functions - point abs() const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = detail::abs_wrapper(elt[d]); - return r; - } - - // Binary functions - point min(const point &p) const { - using std::min; - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = min(elt[d], p.elt[d]); - return r; - } - point max(const point &p) const { - using std::max; - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = max(elt[d], p.elt[d]); - return r; - } - - // Comparison operators - point operator==(const point &p) const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = elt[d] == p.elt[d]; - return r; - } - point operator!=(const point &p) const { return !(*this == p); } - point operator<(const point &p) const { - point r; - for (int d = 0; d < D; ++d) - r.elt[d] = elt[d] < p.elt[d]; - return r; - } - point operator>(const point &p) const { return p < *this; } - point operator>=(const point &p) const { return !(*this < p); } - point operator<=(const point &p) const { return !(*this > p); } - - bool equal_to(const point &p) const { - std::equal_to> eq; - return eq(elt, p.elt); - } - bool less(const point &p) const { - std::less lt; - // Use Fortran array index ordering, where the highest dimensions are the - // most significand - for (int d = D - 1; d >= 0; --d) { - if (lt(elt[d], p.elt[d])) - return true; - if (lt(p.elt[d], elt[d])) - return false; - } - return false; - } - size_t hash() const { - size_t r = size_t(0xb89a122a8c3f540eULL); - for (int d = 0; d < D; ++d) - r = hash_combine(r, elt[d]); - return r; - } - - // Reductions - bool all() const { - bool r = true; - for (int d = 0; d < D; ++d) - r = r && elt[d]; - return r; - } - bool any() const { - bool r = false; - for (int d = 0; d < D; ++d) - r = r || elt[d]; - return r; - } - T minval() const { - using std::min; - T r = numeric_limits::max(); - for (int d = 0; d < D; ++d) - r = min(r, elt[d]); - return r; - } - T maxval() const { - using std::max; - T r = numeric_limits::min(); - for (int d = 0; d < D; ++d) - r = max(r, elt[d]); - return r; - } - T sum() const { - T r = T(0); - for (int d = 0; d < D; ++d) - r += elt[d]; - return r; - } - typedef typename detail::largeint::type prod_t; - prod_t prod() const { - prod_t r = prod_t(1); - for (int d = 0; d < D; ++d) - r *= elt[d]; - return r; - } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit point(const YAML::Node &node) { - assert(node.Tag() == - "tag:github.com/eschnett/SimulationIO/asdf-cxx/point-1.0.0"); - *this = point(node.as>()); - } - friend void operator>>(const YAML::Node &node, point &p) { p = point(node); } -#endif - - ostream &output(ostream &os) const { - os << "["; - for (int d = 0; d < D; ++d) { - if (d > 0) - os << ","; - os << elt[d]; - } - os << "]"; - return os; - } - friend ostream &operator<<(ostream &os, const point &p) { - return p.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - w << YAML::LocalTag("sio", "point-1.0.0"); - w << YAML::Flow << YAML::BeginSeq; - for (int d = 0; d < D; ++d) - w << elt[d]; - w << YAML::EndSeq; - return w; - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const point &p) { - return p.output(w); - } -#endif -}; - -// Unary functions -template point abs(const point &p) { - return p.abs(); -} - -// Binary functions -template -point min(const point &p, const point &q) { - return p.min(q); -} -template -point max(const point &p, const point &q) { - return p.max(q); -} - -// Reductions -template bool all(const point &p) { return p.all(); } -template bool any(const point &p) { return p.any(); } -template T minval(const point &p) { - return p.minval(); -} -template T maxval(const point &p) { - return p.maxval(); -} -template T sum(const point &p) { return p.sum(); } -template -typename point::prod_t prod(const point &p) { - return p.prod(); -} - -// Function objects -template struct bit_not; -template struct bit_not> { - constexpr point operator()(const point &p) const { return ~p; } -}; - -template struct modulus_eq; -template struct modulus_eq> { - point &operator()(point &p, const point &q) const { - return p %= q; - } -}; -template struct bit_and_eq; -template struct bit_and_eq> { - point &operator()(point &p, const point &q) const { - return p &= q; - } -}; -template struct bit_or_eq; -template struct bit_or_eq> { - point &operator()(point &p, const point &q) const { - return p |= q; - } -}; -template struct bit_xor_eq; -template struct bit_xor_eq> { - point &operator()(point &p, const point &q) const { - return p ^= q; - } -}; - -template struct modulus; -template struct modulus> { - constexpr point operator()(const point &p, - const point &q) const { - return p % q; - } -}; -template struct bit_and; -template struct bit_and> { - constexpr point operator()(const point &p, - const point &q) const { - return p & q; - } -}; -template struct bit_or; -template struct bit_or> { - constexpr point operator()(const point &p, - const point &q) const { - return p | q; - } -}; -template struct bit_xor; -template struct bit_xor> { - constexpr point operator()(const point &p, - const point &q) const { - return p ^ q; - } -}; -} // namespace RegionCalculus - -namespace std { -template struct equal_to> { - bool operator()(const RegionCalculus::point &p, - const RegionCalculus::point &q) const { - return p.equal_to(q); - } -}; -template struct less> { - bool operator()(const RegionCalculus::point &p, - const RegionCalculus::point &q) const { - return p.less(q); - } -}; -template struct hash> { - size_t operator()(const RegionCalculus::point &p) const { - return p.hash(); - } -}; -} // namespace std - -//////////////////////////////////////////////////////////////////////////////// -// Box -//////////////////////////////////////////////////////////////////////////////// - -namespace RegionCalculus { -template struct box; - -template struct box { - constexpr static int D = 0; - - bool m_full; - - box() : m_full(false) {} - box(const box &b) = default; - box(box &&b) = default; - explicit box(bool b) : m_full(b) {} - box(const point &lo, const point &hi) : m_full(true) {} - explicit box(const point &p) : m_full(true) {} - box(const vector &lo, const vector &hi) : m_full(true) {} - box &operator=(const box &p) = default; - box &operator=(box &&p) = default; - template box(const box &b) : m_full(b.m_full) {} - - // Predicates - bool empty() const { return !m_full; } - point lower() const { return point(); } - point upper() const { return point(); } - point shape() const { return point(); } - typedef typename point::prod_t prod_t; - prod_t size() const { return m_full; } - - // Shift and scale operators - box &operator>>=(const point &p) { return *this; } - box &operator<<=(const point &p) { return *this; } - box &operator*=(const point &p) { return *this; } - box operator>>(const point &p) const { return *this; } - box operator<<(const point &p) const { return *this; } - box operator*(const point &p) const { return *this; } - box grow(const point &dlo, const point &dup) const { - return *this; - } - box grow(const point &d) const { return grow(d, d); } - box grow(T n) const { return grow(point(n)); } - box shrink(const point &dlo, const point &dup) const { - return *this; - } - box shrink(const point &d) const { return shrink(d, d); } - box shrink(T n) const { return grow(point(n)); } - - // Comparison operators - bool operator==(const box &b) const { return m_full == b.m_full; } - bool operator!=(const box &b) const { return !(*this == b); } - - bool equal_to(const box &p) const { return m_full == p.m_full; } - bool less(const box &p) const { return m_full < p.m_full; } - size_t hash() const { - std::hash h; - return h(m_full) ^ size_t(0x4a473053c081f0efULL); - } - - // Set comparison operators - bool contains(const point &p) const { return !empty(); } - bool isdisjoint(const box &b) const { return empty() | b.empty(); } - bool operator<=(const box &b) const { return m_full <= b.m_full; } - bool operator>=(const box &b) const { return b <= *this; } - bool operator<(const box &b) const { return *this <= b && *this != b; } - bool operator>(const box &b) const { return b < *this; } - bool is_subset_of(const box &b) const { return *this <= b; } - bool is_superset_of(const box &b) const { return *this >= b; } - bool is_strict_subset_of(const box &b) const { return *this < b; } - bool is_strict_superset_of(const box &b) const { return *this > b; } - - // Set operations - box bounding_box(const box &b) const { return box(m_full | b.m_full); } - - box operator&(const box &b) const { return box(m_full & b.m_full); } - box intersection(const box &b) const { return *this & b; } - - vector operator-(const box &b) const { - if (m_full > b.m_full) - return vector(1, box(true)); - return vector(); - } - vector difference(const box &b) const { return *this - b; } - - vector operator|(const box &b) const { - if (m_full & b.m_full) - return vector(1, box(true)); - return vector(); - } - vector setunion(const box &b) const { return *this | b; } - - vector operator^(const box &b) const { - if (m_full ^ b.m_full) - return vector(1, box(true)); - return vector(); - } - vector symmetric_difference(const box &b) const { return *this ^ b; } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit box(const YAML::Node &node) { - assert(node.Tag() == - "tag:github.com/eschnett/SimulationIO/asdf-cxx/box-1.0.0"); - m_full = node["full"].as(); - } - friend void operator>>(const YAML::Node &node, box &b) { b = box(node); } -#endif - - ostream &output(ostream &os) const { return os << "(" << m_full << ")"; } - friend ostream &operator<<(ostream &os, const box &b) { return b.output(os); } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - w << YAML::LocalTag("sio", "box-1.0.0"); - w << YAML::Flow << YAML::BeginMap; - w << YAML::Key << "full" << YAML::Value << m_full; - w << YAML::EndMap; - return w; - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const box &b) { - return b.output(w); - } -#endif -}; - -template struct box { - point lo, hi; - box() = default; - box(const box &b) = default; - box(box &&b) = default; - box(const point &lo, const point &hi) : lo(lo), hi(hi) {} - explicit box(const point &p) : lo(p), hi(p + point(1)) {} - box(const array &lo, const array &hi) : lo(lo), hi(hi) {} - box(const vector &lo, const vector &hi) : lo(lo), hi(hi) {} - box &operator=(const box &p) = default; - box &operator=(box &&p) = default; - template box(const box &b) : lo(b.lo), hi(b.hi) {} - - // Predicates - bool empty() const { return any(hi <= lo); } - point lower() const { return lo; /* empty() ? point(0) : lo; */ } - point upper() const { return hi; /* empty() ? point(0) : hi; */ } - point shape() const { return max(hi - lo, point(0)); } - typedef typename point::prod_t prod_t; - prod_t size() const { return prod(shape()); } - - // Shift and scale operators - box &operator>>=(const point &p) { - lo += p; - hi += p; - return *this; - } - box &operator<<=(const point &p) { - lo -= p; - hi -= p; - return *this; - } - box &operator*=(const point &p) { - lo *= p; - hi *= p; - return *this; - } - box operator>>(const point &p) const { return box(*this) >>= p; } - box operator<<(const point &p) const { return box(*this) <<= p; } - box operator*(const point &p) const { return box(*this) *= p; } - box grow(const point &dlo, const point &dup) const { - box nb(*this); - if (!empty()) { - nb.lo -= dlo; - nb.hi += dup; - } - return nb; - } - box grow(const point &d) const { return grow(d, d); } - box grow(T n) const { return grow(point(n)); } - box shrink(const point &dlo, const point &dup) const { - return grow(-dlo, -dup); - } - box shrink(const point &d) const { return shrink(d, d); } - box shrink(T n) const { return shrink(point(n)); } - - // Comparison operators - bool operator==(const box &b) const { - if (empty() && b.empty()) - return true; - if (empty() || b.empty()) - return false; - std::equal_to> eq; - return eq(lo, b.lo) && eq(hi, b.hi); - } - bool operator!=(const box &b) const { return !(*this == b); } - - bool equal_to(const box &b) const { - if (empty() && b.empty()) - return true; - if (empty() || b.empty()) - return false; - std::equal_to> eq; - return eq(lo, b.lo) && eq(hi, b.hi); - } - bool less(const box &b) const { - // Empty boxes are less than non-empty ones - if (b.empty()) - return false; - if (empty()) - return true; - std::less> lt; - if (lt(lo, b.lo)) - return true; - if (lt(b.lo, lo)) - return false; - return lt(hi, b.hi); - } - size_t hash() const { - return hash_combine(hash_combine(size_t(0x8ba458a873481993ULL), lo), hi); - } - - // Set comparison operators - bool contains(const point &p) const { - if (empty()) - return false; - return all(p >= lo && p < hi); - } - bool isdisjoint(const box &b) const { return (*this & b).empty(); } - bool operator<=(const box &b) const { - if (empty()) - return true; - if (b.empty()) - return false; - return all(lo >= b.lo && hi <= b.hi); - } - bool operator>=(const box &b) const { return b <= *this; } - bool operator<(const box &b) const { return *this <= b && *this != b; } - bool operator>(const box &b) const { return b < *this; } - bool is_subset_of(const box &b) const { return *this <= b; } - bool is_superset_of(const box &b) const { return *this >= b; } - bool is_strict_subset_of(const box &b) const { return *this < b; } - bool is_strict_superset_of(const box &b) const { return *this > b; } - - // Set operations - box bounding_box(const box &b) const { - if (empty()) - return b; - if (b.empty()) - return *this; - auto r = box(min(lo, b.lo), max(hi, b.hi)); -// Postcondition -#if REGIONCALCULUS_DEBUG - assert(*this <= r && b <= r); -#endif - return r; - } - - box operator&(const box &b) const { - auto nlo = max(lo, b.lo); - auto nhi = min(hi, b.hi); - auto r = box(nlo, nhi); -// Postcondition -#if REGIONCALCULUS_DEBUG - assert(r <= *this && r <= b); -#endif - return r; - } - box intersection(const box &b) const { return *this & b; } - -private: - void split(const point &p, vector &rs) const { - assert(!empty()); -#if REGIONCALCULUS_DEBUG - const auto old_rs_size = rs.size(); -#endif - for (int m = 0; m < (1 << D); ++m) { - point newlo = lo, newhi = hi; - bool is_inside = true; - for (int d = 0; d < D; ++d) { - const bool lohi = m & (1 << d); - if (p.elt[d] > lo.elt[d] && p.elt[d] < hi.elt[d]) { - if (!lohi) - newhi.elt[d] = p.elt[d]; - else - newlo.elt[d] = p.elt[d]; - } else { - is_inside &= !lohi; - } - } - if (is_inside) - rs.push_back(box(newlo, newhi)); - } -#if REGIONCALCULUS_DEBUG - // Postcondition - prod_t vol = prod_t(0); - for (auto i = old_rs_size; i < rs.size(); ++i) { - assert(!rs[i].empty()); - assert(rs[i] <= *this); - vol += rs[i].size(); - } - assert(vol == size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(rs[i].isdisjoint(rs[j])); -#endif - } - -public: - vector operator-(const box &b) const { - if (empty()) - return vector(); - if (b.empty()) - return vector(1, *this); - vector bs1; - split(b.lo, bs1); - vector bs2; - for (const auto &b1 : bs1) - b1.split(b.hi, bs2); - vector rs; - for (const auto &b2 : bs2) { - assert(b2.isdisjoint(b) || b2 <= b); - if (b2.isdisjoint(b)) - rs.push_back(b2); - } -#if REGIONCALCULUS_DEBUG - // Postcondition - prod_t vol = prod_t(0); - for (const auto &r : rs) { - assert(!r.empty()); - assert(r <= *this && !(r <= b)); - vol += r.size(); - } - assert(vol >= max(prod_t(0), size() - b.size()) && vol <= size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(rs[i].isdisjoint(rs[j])); -#endif - return rs; - } - vector difference(const box &b) const { return *this - b; } - - vector operator|(const box &b) const { - auto rs = *this - b; - if (!b.empty()) - rs.push_back(b); -#if REGIONCALCULUS_DEBUG - // Postcondition - prod_t vol = prod_t(0); - for (const auto &r : rs) { - assert(!r.empty()); - assert(r <= *this || r <= b); - vol += r.size(); - } - assert(vol >= size() && vol <= size() + b.size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(rs[i].isdisjoint(rs[j])); -#endif - return rs; - } - vector setunion(const box &b) const { return *this | b; } - - vector operator^(const box &b) const { - auto rs = *this - b; - auto rs1 = b - *this; - // TODO: Avoid this concatenation - rs.insert(rs.end(), rs1.begin(), rs1.end()); -#if REGIONCALCULUS_DEBUG - // Postcondition - prod_t vol = prod_t(0); - for (const auto &r : rs) { - assert(!r.empty()); - assert((r <= *this) ^ (r <= b)); - vol += r.size(); - } - assert(vol >= abs(size() - b.size()) && vol <= size() + b.size()); - for (size_t i = 0; i < rs.size(); ++i) - for (size_t j = i + 1; j < rs.size(); ++j) - assert(rs[i].isdisjoint(rs[j])); -#endif - return rs; - } - vector symmetric_difference(const box &b) const { return *this ^ b; } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit box(const YAML::Node &node) { - assert(node.Tag() == - "tag:github.com/eschnett/SimulationIO/asdf-cxx/box-1.0.0"); - lo = point(node["low"]); - hi = point(node["high"]); - } - friend void operator>>(const YAML::Node &node, box &b) { b = box(node); } -#endif - - ostream &output(ostream &os) const { - return os << "(" << lo << ":" << hi << ")"; - } - friend ostream &operator<<(ostream &os, const box &b) { return b.output(os); } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - w << YAML::LocalTag("sio", "box-1.0.0"); - w << YAML::Flow << YAML::BeginMap; - w << YAML::Key << "low" << YAML::Value << lo; - w << YAML::Key << "high" << YAML::Value << hi; - w << YAML::EndMap; - return w; - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const box &b) { - return b.output(w); - } -#endif -}; -} // namespace RegionCalculus - -namespace std { -template struct equal_to> { - bool operator()(const RegionCalculus::box &p, - const RegionCalculus::box &q) const { - return p.equal_to(q); - } -}; -template struct less> { - bool operator()(const RegionCalculus::box &p, - const RegionCalculus::box &q) const { - return p.less(q); - } -}; -template struct hash> { - size_t operator()(const RegionCalculus::box &b) const { - return b.hash(); - } -}; -} // namespace std - -//////////////////////////////////////////////////////////////////////////////// -// Region -//////////////////////////////////////////////////////////////////////////////// - -#if !REGIONCALCULUS_TREE - -namespace RegionCalculus { -template struct region { - vector> boxes; - region() = default; - region(const region &r) = default; - region(region &&r) = default; - - region(const box &b) { - if (!b.empty()) - boxes.push_back(b); -#if REGIONCALCULUS_DEBUG - assert(invariant()); -#endif - } - region(const vector> &bs) : boxes(bs) { - normalize(); - assert(invariant()); - } - region(vector> &&bs) : boxes(move(bs)) { - normalize(); - assert(invariant()); - } - operator vector>() const { return boxes; } - region &operator=(const region &r) = default; - region &operator=(region &&r) = default; - template region(const region &r) { - boxes.reserve(r.boxes.size()); - for (const auto &b : r.boxes) - boxes.push_back(box(b)); - assert(invariant()); - } - -private: - void append(const region &r) { - boxes.insert(boxes.end(), r.boxes.begin(), r.boxes.end()); - } - - // Normalization - void normalize() { sort(boxes.begin(), boxes.end(), std::less>()); } - -public: - // Invariant - bool invariant() const { - for (size_t i = 0; i < boxes.size(); ++i) { - if (boxes[i].empty()) - return false; - for (size_t j = i + 1; j < boxes.size(); ++j) { - if (!boxes[i].isdisjoint(boxes[j])) - return false; - if (!boxes[i].less(boxes[j])) - return false; - } - } - return true; - } - - // Predicates - bool empty() const { return boxes.empty(); } - typedef typename point::prod_t prod_t; - prod_t size() const { - prod_t sz = T(0); - for (const auto &b : boxes) - sz += b.size(); - return sz; - } - - // Shift and scale operators - region operator>>(const point &d) const { - region nr(*this); - for (auto &b : nr.boxes) - b >>= d; - return nr; - } - region operator<<(const point &d) const { return *this >> -d; } - region grow(const point &dlo, const point &dup) const { - // Cannot shrink - assert(all(dlo + dup >= point(T(0)))); - region nr; - for (const auto &b : boxes) - nr = nr | b.grow(dlo, dup); - return nr; - } - region grow(const point &d) const { return grow(d, d); } - region grow(T n) const { return grow(point(n)); } - region shrink(const point &dlo, const point &dup) const { - // Cannot grow - assert(all(dlo + dup >= point(T(0)))); - auto maxdist = maxval(max(abs(dlo), abs(dup))); - auto world = bounding_box(); - region world2 = world.grow(2 * maxdist); - return world2 - (world2 - *this).grow(dlo, dup); - } - region shrink(const point &d) const { return shrink(d, d); } - region shrink(T n) const { return shrink(point(n)); } - - // Set operations - box bounding_box() const { - box r; - for (const auto &b : boxes) - r = r.bounding_box(b); - return r; - } - - region operator&(const box &b) const { - region nr; - for (const auto &rb : boxes) { - auto nb = rb & b; - if (!nb.empty()) - nr.boxes.push_back(nb); - } - nr.normalize(); -#if REGIONCALCULUS_DEBUG - assert(invariant()); -#endif - return nr; - } - region operator&(const region &r) const { - region nr; - for (const auto &b : r.boxes) - nr.append(*this & b); - nr.normalize(); -#if REGIONCALCULUS_DEBUG - assert(invariant()); -#endif - return nr; - } - region &operator&=(const box &b) { return *this = *this & b; } - region &operator&=(const region &r) { return *this = *this & r; } - region intersection(const box &b) const { return *this & b; } - region intersection(const region &r) const { return *this & r; } - - region operator-(const box &b) const { - region nr; - for (const auto &rb : boxes) - nr.append(region(rb - b)); - nr.normalize(); -#if REGIONCALCULUS_DEBUG - assert(invariant()); -#endif - return nr; - } - region operator-(const region &r) const { - region nr = *this; - for (const auto &b : r.boxes) - nr = nr - b; - nr.normalize(); -#if REGIONCALCULUS_DEBUG - assert(invariant()); -#endif - return nr; - } - region &operator-=(const box &b) { return *this = *this - b; } - region &operator-=(const region &r) { return *this = *this - r; } - region difference(const box &b) const { return *this - b; } - region difference(const region &r) const { return *this - r; } - - region operator|(const region &r) const { - region nr = *this - r; - nr.append(r); - nr.normalize(); -#if REGIONCALCULUS_DEBUG - assert(invariant()); -#endif - return nr; - } - region operator|(const box &b) const { return *this | region(b); } - region &operator|=(const box &b) { return *this = *this | b; } - region &operator|=(const region &r) { return *this = *this | r; } - region setunion(const region &r) const { return *this | r; } - region setunion(const box &b) const { return *this | b; } - - region operator^(const region &r) const { - region nr = *this - r; - nr.append(r - *this); - nr.normalize(); -#if REGIONCALCULUS_DEBUG - assert(invariant()); -#endif - return nr; - } - region operator^(const box &b) const { return *this ^ region(b); } - region &operator^=(const box &b) { return *this = *this ^ b; } - region &operator^=(const region &r) { return *this = *this ^ r; } - region symmetric_difference(const region &r) const { return *this ^ r; } - region symmetric_difference(const box &b) const { return *this ^ b; } - - // Set comparison operators - bool contains(const point &p) const { - for (const auto &b : boxes) - if (b.contains(p)) - return true; - return false; - } - bool isdisjoint(const box &b) const { - for (const auto &rb : boxes) - if (!rb.isdisjoint(b)) - return false; - return true; - } - bool isdisjoint(const region &r) const { - for (const auto &b : r.boxes) - if (!isdisjoint(b)) - return false; - return true; - } - - // Comparison operators - bool operator<=(const region &r) const { return (*this - r).empty(); } - bool operator>=(const region &r) const { return r <= *this; } - bool operator<(const region &r) const { - return *this <= r && size() < r.size(); - } - bool operator>(const region &r) const { return r < *this; } - bool is_subset_of(const region &r) const { return *this <= r; } - bool is_superset_of(const region &r) const { return *this >= r; } - bool is_strict_subset_of(const region &r) const { return *this < r; } - bool is_strict_superset_of(const region &r) const { return *this > r; } - bool operator==(const region &r) const { return (*this ^ r).empty(); } - bool operator!=(const region &r) const { return !(*this == r); } - - bool equal_to(const region &r) const { - std::equal_to> eq; - return eq(boxes, r.boxes); - } - bool less(const region &r) const { - std::less> lt; - return lt(boxes, r.boxes); - } - size_t hash() const { - std::hash> h; - return h(boxes) ^ size_t(0x4861d2118c306aefULL); - } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit region(const YAML::Node &node) { - assert(node.Tag() == - "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); - dim = node["dimension"].as(); - assert(dim == D); - boxes = node["boxes"].as>>(); - } -#endif - - ostream &output(ostream &os) const { - os << "{"; - for (size_t i = 0; i < boxes.size(); ++i) { - if (i > 0) - os << ","; - os << boxes[i]; - } - os << "}"; - return os; - } - friend ostream &operator<<(ostream &os, const region &r) { - return r.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - w << YAML::LocalTag("sio", "region-1.0.0"); - w << YAML::Flow << YAML::BeginMap; - w << YAML::Key << "dimension" << YAML::Value << D; - w << YAML::Key << "boxes" << YAML::Value << boxes; - w << YAMLL::EndMap; - return w; - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { - return r.output(w); - } -#endif -}; -} // namespace RegionCalculus - -namespace std { -template struct equal_to> { - bool operator()(const RegionCalculus::region &p, - const RegionCalculus::region &q) const { - return p.equal_to(q); - } -}; -template struct less> { - bool operator()(const RegionCalculus::region &p, - const RegionCalculus::region &q) const { - return p.less(q); - } -}; -template struct hash> { - size_t operator()(const RegionCalculus::region &p) const { - return p.hash(); - } -}; -} // namespace std - -#else // #if REGIONCALCULUS_TREE - -namespace RegionCalculus { -template struct region; - -template struct region { - constexpr static int D = 0; - - bool m_full; - - region() : m_full(false) {} - region(const region &) = default; - region(region &&) = default; - region &operator=(const region &) = default; - region &operator=(region &&) = default; - - explicit region(bool b) : m_full(b) {} - region(const box &b) : m_full(b.m_full) {} - region(const point &p) : m_full(true) {} - region(const vector> &bs) { - m_full = false; - for (const auto &b : bs) - m_full |= !b.empty(); - } - template region(const region &r) : m_full(r.m_full) {} - - // Invariant - bool invariant() const { return true; } - - // Predicates - bool empty() const { return !m_full; } - typedef typename point::prod_t prod_t; - prod_t size() const { return m_full; } - ptrdiff_t chi_size() const { return 1; } - - // Conversion to boxes - operator vector>() const { - if (empty()) - return vector>(); - return vector>(1, box(true)); - } - - // Shift and scale operators - region operator>>(const point &d) const { return *this; } - region operator<<(const point &d) const { return *this; } - region grow(const point &dlo, const point &dup) const { - return *this; - } - region grow(const point &d) const { return grow(d, d); } - region grow(T n) const { return grow(point(n)); } - region shrink(const point &dlo, const point &dup) const { - return *this; - } - region shrink(const point &d) const { return shrink(d, d); } - region shrink(T n) const { return shrink(point(n)); } - - // Set operations - box bounding_box() const { return box(m_full); } - - region operator&(const region &other) const { - return region(m_full & other.m_full); - } - region operator|(const region &other) const { - return region(m_full | other.m_full); - } - region operator^(const region &other) const { - return region(m_full ^ other.m_full); - } - region operator-(const region &other) const { - return region(m_full & !other.m_full); - } - - region &operator^=(const region &other) { return *this = *this ^ other; } - region &operator&=(const region &other) { return *this = *this & other; } - region &operator|=(const region &other) { return *this = *this | other; } - region &operator-=(const region &other) { return *this = *this - other; } - - region intersection(const region &other) const { return *this & other; } - region setunion(const region &other) const { return *this | other; } - region symmetric_difference(const region &other) const { - return *this ^ other; - } - region difference(const region &other) const { return *this - other; } - - // Set comparison operators - bool contains(const point &p) const { return m_full; } - bool isdisjoint(const region &other) const { - return !(m_full & other.m_full); - } - - // Comparison operators - bool operator<=(const region &other) const { return !m_full || other.m_full; } - bool operator>=(const region &other) const { return other <= *this; } - bool operator<(const region &other) const { return !m_full & other.m_full; } - bool operator>(const region &other) const { return other < *this; } - bool is_subset_of(const region &other) const { return *this <= other; } - bool is_superset_of(const region &other) const { return *this >= other; } - bool is_strict_subset_of(const region &other) const { return *this < other; } - bool is_strict_superset_of(const region &other) const { return *this > other; } - bool operator==(const region &other) const { return m_full == other.m_full; } - bool operator!=(const region &other) const { return !(*this == other); } - - bool equal_to(const region &other) const { return m_full == other.m_full; } - bool less(const region &other) const { return m_full < other.m_full; } - size_t hash() const { - std::hash h; - return h(m_full) ^ size_t(0x07da947bfacbea06ULL); - } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit region(const YAML::Node &node) { - assert(node.Tag() == - "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); - auto dim = node["dimension"].as(); - assert(dim == 0); - m_full = node["full"].as(); - } -#endif - - ostream &output(ostream &os) const { - os << "{"; - if (m_full) - os << "(1)"; - os << "}"; - return os; - } - friend ostream &operator<<(ostream &os, const region &r) { - return r.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - w << YAML::LocalTag("sio", "region-1.0.0"); - w << YAML::Flow << YAML::BeginMap; - w << YAML::Key << "dimension" << YAML::Value << 0; - w << YAML::Key << "full" << YAML::Value << m_full; - w << YAML::EndMap; - return w; - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { - return r.output(w); - } -#endif -}; - -template struct region { - constexpr static int D = 1; - - typedef region subregion_t; // This is essentially a bool - typedef vector subregions_t; - subregions_t subregions; - - region() = default; - region(const region &) = default; - region(region &&) = default; - region &operator=(const region &) = default; - region &operator=(region &&) = default; - - region(const box &b) { - if (b.empty()) - return; - subregions = {b.lower()[0], b.upper()[0]}; - assert(invariant()); - } - region(const point &p) : region({p[0], p[0] + 1}) {} - template region(const region &r) { - subregions.reserve(r.subregions.size()); - for (const auto &pos : r.subregions) - subregions.push_back(T(pos)); - } - -private: - template void traverse_subregions(const F &f) const { - subregion_t decoded_subregion; - for (const auto &pos : subregions) { - decoded_subregion ^= subregion_t(true); - f(pos, decoded_subregion); - } - assert(decoded_subregion.empty()); - } - - template - void traverse_subregions(const F &f, const region &other) const { - subregion_t decoded_subregion0, decoded_subregion; - - typedef typename subregions_t::const_iterator subregions_iter_t; - subregions_iter_t iter0 = subregions.begin(); - subregions_iter_t iter1 = other.subregions.begin(); - const subregions_iter_t end0 = subregions.end(); - const subregions_iter_t end1 = other.subregions.end(); - while (iter0 != end0 && iter1 != end1) { - const T next_pos0 = *iter0; - const T next_pos1 = *iter1; - const T pos = min(next_pos0, next_pos1); - const bool active0 = next_pos0 == pos; - const bool active1 = next_pos1 == pos; - decoded_subregion0 ^= subregion_t(active0); - decoded_subregion ^= subregion_t(active1); - f(pos, decoded_subregion0, decoded_subregion); - if (active0) - ++iter0; - if (active1) - ++iter1; - } - for (; iter0 != end0; ++iter0) { - const T pos = *iter0; - decoded_subregion0 ^= subregion_t(true); - f(pos, decoded_subregion0, subregion_t(false)); - } - for (; iter1 != end1; ++iter1) { - const T pos = *iter1; - decoded_subregion ^= subregion_t(true); - f(pos, subregion_t(false), decoded_subregion); - } - assert(decoded_subregion0.empty()); - assert(decoded_subregion.empty()); - } - - template region unary_operator(const F &op) const { - region res; - // res.reserve(subregions.size()); - subregion_t old_decoded_subregion; - traverse_subregions( - [&](const T pos, const subregion_t &decoded_subregion0) { - auto decoded_subregion = op(decoded_subregion0); - auto subregion = decoded_subregion ^ old_decoded_subregion; - if (!subregion.empty()) - res.subregions.push_back(pos); - old_decoded_subregion = decoded_subregion; - }); - assert(old_decoded_subregion.empty()); - assert(res.invariant()); - return res; - } - - template - region binary_operator(const F &op, const region &other) const { - region res; - // res.reserve(subregions.size() + other.subregions.size()); - subregion_t old_decoded_subregion; - traverse_subregions( - [&](const T pos, const subregion_t &decoded_subregion0, - const subregion_t &decoded_subregion1) { - auto decoded_subregion = op(decoded_subregion0, decoded_subregion1); - auto subregion = decoded_subregion ^ old_decoded_subregion; - if (!subregion.empty()) - res.subregions.push_back(pos); - old_decoded_subregion = decoded_subregion; - }, - other); - assert(old_decoded_subregion.empty()); - assert(res.invariant()); - return res; - } - -public: - // Invariant - bool invariant() const { -#if REGIONCALCULUS_DEBUG - const size_t nboxes = subregions.size(); - for (size_t i = 1; i < nboxes; ++i) - if (subregions[i] <= subregions[i - 1]) - return false; - if (chi_size() % 2 != 0) - return false; -#endif - return true; - } - - // Predicates - bool empty() const { return subregions.empty(); } - - typedef typename point::prod_t prod_t; - prod_t size() const { - prod_t total_size = 0; - typedef typename subregions_t::const_iterator subregions_iter_t; - subregions_iter_t iter = subregions.begin(); - const subregions_iter_t end = subregions.end(); - while (iter != end) { - const auto pos0 = *iter++; - const auto pos1 = *iter++; - total_size += pos1 - pos0; - } - return total_size; - } - - ptrdiff_t chi_size() const { return subregions.size(); } - -private: - static vector subregions_from_bounds(vector lbnds, vector ubnds) { - const size_t nboxes = lbnds.size(); - assert(ubnds.size() == nboxes); - vector subregions; - if (nboxes == 0) - return subregions; - sort(lbnds.begin(), lbnds.end()); - sort(ubnds.begin(), ubnds.end()); - size_t nactive = 0; - size_t lpos = 0, upos = 0; - while (lpos < nboxes) { - const auto lbnd = lbnds[lpos]; - assert(upos < nboxes); - const auto ubnd = ubnds[upos]; - // Process lower bounds before upper bounds - if (lbnd <= ubnd) { - if (nactive == 0) - subregions.push_back(lbnd); - ++nactive; - ++lpos; - } else { - assert(nactive > 0); - --nactive; - if (nactive == 0) - subregions.push_back(ubnd); - ++upos; - } - } - assert(nactive > 0); - assert(upos < nboxes); - assert(upos + nactive == nboxes); - subregions.push_back(ubnds[nboxes - 1]); -#if REGIONCALCULUS_DEBUG - { - region reg; - for (size_t i = 0; i < nboxes; ++i) - reg |= region(box(point(lbnds[i]), point(ubnds[i]))); - assert(subregions == reg.subregions); - } -#endif - return subregions; - } - -public: - // Conversion from and to boxes - region(const vector> &boxes) { - vector lbnds, ubnds; - lbnds.reserve(boxes.size()); - ubnds.reserve(boxes.size()); - for (const auto &box : boxes) { - lbnds.push_back(box.lower()[0]); - ubnds.push_back(box.upper()[0]); - } - subregions = subregions_from_bounds(move(lbnds), move(ubnds)); - assert(invariant()); - } - - operator vector>() const { - vector> res; - res.reserve(subregions.size() / 2); - typedef typename subregions_t::const_iterator subregions_iter_t; - subregions_iter_t iter = subregions.begin(); - const subregions_iter_t end = subregions.end(); - while (iter != end) { - const auto pos0 = *iter++; - const auto pos1 = *iter++; - res.emplace_back(box(point(pos0), point(pos1))); - } -#if REGIONCALCULUS_DEBUG - assert(is_sorted(res.begin(), res.end())); - { - region reg; - for (const auto &b : res) { - assert(region(b).isdisjoint(reg)); - reg |= b; - } - assert(reg == *this); - } -#endif - return res; - } - - // Shift and scale operators - region operator>>(const point &d) const { - region nr; - nr.subregions.reserve(subregions.size()); - for (const auto &pos : subregions) - nr.subregions.push_back(pos + d[0]); - return nr; - } - region operator<<(const point &d) const { return *this >> -d; } - region grow(const point &dlo, const point &dup) const { - // Cannot shrink - assert(all(dlo + dup >= point(T(0)))); - vector lbnds, ubnds; - lbnds.reserve(subregions.size()); - ubnds.reserve(subregions.size()); - typedef typename subregions_t::const_iterator subregions_iter_t; - subregions_iter_t iter = subregions.begin(); - const subregions_iter_t end = subregions.end(); - while (iter != end) { - const auto pos0 = *iter++ - dlo[0]; - const auto pos1 = *iter++ + dup[0]; - lbnds.push_back(pos0); - ubnds.push_back(pos1); - } - region nr; - nr.subregions = subregions_from_bounds(move(lbnds), move(ubnds)); - assert(nr.invariant()); -#if REGIONCALCULUS_DEBUG - { - const vector> boxes(*this); - region reg; - for (const auto &box : boxes) - reg |= box.grow(dlo, dup); - assert(nr == reg); - } -#endif - return nr; - } - region grow(const point &d) const { return grow(d, d); } - region grow(T n) const { return grow(point(n)); } - region shrink(const point &dlo, const point &dup) const { - // Cannot grow - assert(all(dlo + dup >= point(T(0)))); - region nr; - typedef typename subregions_t::const_iterator subregions_iter_t; - subregions_iter_t iter = subregions.begin(); - const subregions_iter_t end = subregions.end(); - while (iter != end) { - const auto pos0 = *iter++ + dlo[0]; - const auto pos1 = *iter++ - dup[0]; - if (pos1 > pos0) { - nr.subregions.push_back(pos0); - nr.subregions.push_back(pos1); - } - } - assert(nr.invariant()); -#if REGIONCALCULUS_DEBUG - { - auto world = bounding_box().grow(1); - region reg = - region(world.grow(dup, dlo)) - (region(world) - *this).grow(dup, dlo); - assert(nr == reg); - } -#endif - return nr; - } - region shrink(const point &d) const { return shrink(d, d); } - region shrink(T n) const { return shrink(point(n)); } - - // Set operations - box bounding_box() const { - if (empty()) - return box(); - return box(point(*subregions.begin()), - point(*subregions.rbegin())); - } - - region operator^(const region &other) const { - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 ^ set1; }, - other); - } - - region operator&(const region &other) const { - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 & set1; }, - other); - } - - region operator|(const region &other) const { - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 | set1; }, - other); - } - - region operator-(const region &other) const { - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 - set1; }, - other); - } - - region &operator^=(const region &other) { return *this = *this ^ other; } - region &operator&=(const region &other) { return *this = *this & other; } - region &operator|=(const region &other) { return *this = *this | other; } - region &operator-=(const region &other) { return *this = *this - other; } - - region intersection(const region &other) const { return *this & other; } - region setunion(const region &other) const { return *this | other; } - region symmetric_difference(const region &other) const { - return *this ^ other; - } - region difference(const region &other) const { return *this - other; } - - // Set comparison operators - bool contains(const point &p) const { - if (subregions.empty()) - return false; - if (p[0] < *subregions.begin() || p[0] >= *subregions.rbegin()) - return false; - const auto pos = std::find_if(subregions.begin(), subregions.end(), - [p](T pos) { return p[0] >= pos; }); - return (pos - subregions.begin()) % 2 == 0; - } - bool isdisjoint(const region &other) const { return (*this & other).empty(); } - - // Comparison operators - bool operator<=(const region &other) const { return (*this - other).empty(); } - bool operator>=(const region &other) const { return other <= *this; } - bool operator<(const region &other) const { - return *this != other && *this <= other; - } - bool operator>(const region &other) const { return other < *this; } - bool is_subset_of(const region &other) const { return *this <= other; } - bool is_superset_of(const region &other) const { return *this >= other; } - bool is_strict_subset_of(const region &other) const { return *this < other; } - bool is_strict_superset_of(const region &other) const { return *this > other; } - bool operator==(const region &other) const { - return subregions == other.subregions; - } - bool operator!=(const region &other) const { return !(*this == other); } - - bool equal_to(const region &other) const { - std::equal_to eq; - return eq(subregions, other.subregions); - } - bool less(const region &other) const { - std::less lt; - return lt(subregions, other.subregions); - } - size_t hash() const { - size_t r = size_t(0x725f347c326789eeULL); - for (const auto &pos : subregions) - r = hash_combine(r, pos); - return r; - } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit region(const YAML::Node &node) { - assert(node.Tag() == - "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); - auto dim = node["dimension"].as(); - assert(dim == D); - // const auto &boxes = node["boxes"].as>>(); - vector> boxes; - for (const auto &b : node["boxes"]) - boxes.push_back(box(b)); - *this = region(boxes); - } - friend void operator>>(const YAML::Node &node, region &r) { - r = region(node); - } -#endif - - ostream &output(ostream &os) const { - // os << "{"; - // for (const auto &pos_subregion : subregions) - // os << pos_subregion.first << ":" << pos_subregion.second << ","; - // os << "}"; - os << "{"; - const vector> boxes(*this); - for (size_t i = 0; i < boxes.size(); ++i) { - if (i > 0) - os << ","; - os << boxes[i]; - } - os << "}"; - return os; - } - friend ostream &operator<<(ostream &os, const region &r) { - return r.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - w << YAML::LocalTag("sio", "region-1.0.0"); - w << YAML::Flow << YAML::BeginMap; - w << YAML::Key << "dimension" << YAML::Value << D; - w << YAML::Key << "boxes" << YAML::Value << vector>(*this); - w << YAML::EndMap; - return w; - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { - return r.output(w); - } -#endif -}; - -template struct region { - typedef region subregion_t; - typedef vector> subregions_t; - subregions_t subregions; - - region() = default; - region(const region &) = default; - region(region &&) = default; - region &operator=(const region &) = default; - region &operator=(region &&) = default; - - region(const box &b) { - if (b.empty()) - return; - box subbox(b.lower().subpoint(D - 1), b.upper().subpoint(D - 1)); - subregions = {{b.lower()[D - 1], subregion_t(subbox)}, - {b.upper()[D - 1], subregion_t(subbox)}}; - assert(invariant()); - } - region(const point &p) : region(box(p)) {} - template region(const region &r) { - subregions.reserve(r.subregions.size()); - for (const auto &pos_subregion : r.subregions) { - T pos(pos_subregion.first); - subregion_t subregion(pos_subregion.second); - subregions.emplace_back(make_pair(pos, move(subregion))); - } - } - -private: - template void traverse_subregions(const F &f) const { - subregion_t decoded_subregion; - for (const auto &pos_subregion : subregions) { - const T pos = pos_subregion.first; - const auto &subregion = pos_subregion.second; - decoded_subregion ^= subregion; - f(pos, decoded_subregion); - } - assert(decoded_subregion.empty()); - } - - template - void traverse_subregions(const F &f, const region &other) const { - subregion_t decoded_subregion0, decoded_subregion; - - typedef typename subregions_t::const_iterator subregions_iter_t; - subregions_iter_t iter0 = subregions.begin(); - subregions_iter_t iter1 = other.subregions.begin(); - const subregions_iter_t end0 = subregions.end(); - const subregions_iter_t end1 = other.subregions.end(); - while (iter0 != end0 || iter1 != end1) { - const T next_pos0 = - iter0 != end0 ? iter0->first : numeric_limits::max(); - const T next_pos1 = - iter1 != end1 ? iter1->first : numeric_limits::max(); - const T pos = min(next_pos0, next_pos1); - const bool active0 = next_pos0 == pos; - const bool active1 = next_pos1 == pos; - subregion_t dummy; - const subregion_t &subregion0 = active0 ? iter0->second : dummy; - const subregion_t &subregion = active1 ? iter1->second : dummy; - if (active0) - decoded_subregion0 ^= subregion0; - if (active1) - decoded_subregion ^= subregion; - - f(pos, decoded_subregion0, decoded_subregion); - - if (active0) - ++iter0; - if (active1) - ++iter1; - } - assert(decoded_subregion0.empty()); - assert(decoded_subregion.empty()); - } - - template region unary_operator(const F &op) const { - region res; - subregion_t old_decoded_subregion; - traverse_subregions( - [&](const T pos, const subregion_t &decoded_subregion0) { - auto decoded_subregion = op(decoded_subregion0); - auto subregion = decoded_subregion ^ old_decoded_subregion; - if (!subregion.empty()) - res.subregions.emplace_back(make_pair(pos, move(subregion))); - old_decoded_subregion = move(decoded_subregion); - }); - assert(old_decoded_subregion.empty()); - assert(res.invariant()); - return res; - } - - template - region binary_operator(const F &op, const region &other) const { - region res; - subregion_t old_decoded_subregion; - traverse_subregions( - [&](const T pos, const subregion_t &decoded_subregion0, - const subregion_t &decoded_subregion1) { - auto decoded_subregion = op(decoded_subregion0, decoded_subregion1); - auto subregion = decoded_subregion ^ old_decoded_subregion; - if (!subregion.empty()) - res.subregions.emplace_back(make_pair(pos, move(subregion))); - old_decoded_subregion = move(decoded_subregion); - }, - other); - assert(old_decoded_subregion.empty()); - assert(res.invariant()); - return res; - } - -public: - // Invariant - bool invariant() const { -#if REGIONCALCULUS_DEBUG - for (const auto &pos_subregion : subregions) { - const auto &subregion = pos_subregion.second; - if (subregion.empty() || !subregion.invariant()) - return false; - } - if (chi_size() % 2 != 0) - return false; -#endif - return true; - } - - // Predicates - bool empty() const { return subregions.empty(); } - - typedef typename point::prod_t prod_t; - prod_t size() const { - prod_t total_size = 0; - T old_pos = numeric_limits::min(); // location of last subregion - prod_t old_subregion_size = 0; // number of points in the last subregion - traverse_subregions([&](const T pos, const subregion_t &subregion) { - const prod_t subregion_size = subregion.size(); - total_size += old_subregion_size == 0 - ? 0 - : prod_t(pos - old_pos) * old_subregion_size; - old_pos = pos; - old_subregion_size = subregion_size; - }); - assert(old_subregion_size == 0); - return total_size; - } - - ptrdiff_t chi_size() const { - ptrdiff_t sz = 0; - for (const auto &pos_subregion : subregions) { - const auto &subregion = pos_subregion.second; - sz += subregion.chi_size(); - } - return sz; - } - - // Conversion from and to boxes -private: - static region - region_from_boxes(const typename vector>::const_iterator &begin, - const typename vector>::const_iterator &end) { - auto sz = end - begin; - if (sz == 0) - return region(); - if (sz == 1) - return region(*begin); - const auto mid = begin + sz / 2; - return region_from_boxes(begin, mid) | region_from_boxes(mid, end); - } - -public: - region(const vector> &boxes) { - *this = region_from_boxes(boxes.begin(), boxes.end()); -#if REGIONCALCULUS_DEBUG - { - region reg; - for (const auto &box : boxes) - reg |= region(box); - assert(*this == reg); - } -#endif - } - - operator vector>() const { - vector> res; - map, T> old_subboxes; - traverse_subregions([&](const T pos, const subregion_t &subregion) { - // Convert subregion to boxes - const vector> subboxes1(subregion); - - auto iter0 = old_subboxes.begin(); - auto iter1 = subboxes1.begin(); - const auto end0 = old_subboxes.end(); - const auto end1 = subboxes1.end(); -#if REGIONCALCULUS_DEBUG - assert(is_sorted(iter1, end1)); -#endif - map, T> subboxes; - while (iter0 != end0 || iter1 != end1) { - bool active0 = iter0 != end0; - bool active1 = iter1 != end1; - box dummy; - const box &subbox0 = active0 ? iter0->first : dummy; - const box &subbox1 = active1 ? *iter1 : dummy; - // When both subboxes are active, keep only the first (as determined by - // less<>) - std::equal_to eq; - std::less lt; - if (active0 && active1) { - active0 = !lt(subbox0, subbox1); - active1 = !lt(subbox1, subbox0); - } - - const T old_pos = iter0->second; - if (active0 && active1 && eq(subbox0, subbox1)) { - // The current bbox continues unchanged -- keep it - subboxes[subbox1] = old_pos; - } else { - if (active0) - // The current box changed; finalize it - res.push_back(box(subbox0.lower().superpoint(D - 1, old_pos), - subbox0.upper().superpoint(D - 1, pos))); - if (active1) - // There is a new box; add it - subboxes[subbox1] = pos; - } - - if (active0) - ++iter0; - if (active1) - ++iter1; - } - old_subboxes = move(subboxes); - }); - assert(old_subboxes.empty()); -#if REGIONCALCULUS_DEBUG - assert(is_sorted(res.begin(), res.end())); - { - region reg; - for (const auto &b : res) { - assert(region(b).isdisjoint(reg)); - reg |= b; - } - assert(reg == *this); - } -#endif - return res; - } - - // Shift and scale operators - region operator>>(const point &d) const { - region nr; - nr.subregions.reserve(subregions.size()); - T dx = d[D - 1]; - auto subd = d.subpoint(D - 1); - for (const auto &pos_subregion : subregions) { - const T pos = pos_subregion.first; - const auto &subregion = pos_subregion.second; - nr.subregions.emplace_back(make_pair(pos + dx, subregion >> subd)); - } - assert(nr.invariant()); - return nr; - } - region operator<<(const point &d) const { return *this >> -d; } - region grow(const point &dlo, const point &dup) const { - // Cannot shrink - assert(all(dlo + dup >= point(T(0)))); - // region nr; - // for (const auto &box : vector>(*this)) - // nr |= box.grow(dlo, dup); - // return nr; - return reduce([&](const box &b) { return region(b.grow(dlo, dup)); }, - [](const region &x, const region &y) { return x | y; }, - vector>(*this)); - } - region grow(const point &d) const { return grow(d, d); } - region grow(T n) const { return grow(point(n)); } - region shrink(const point &dlo, const point &dup) const { - // Cannot grow - assert(all(dlo + dup >= point(T(0)))); - auto world = bounding_box().grow(1); - return region(world.grow(dup, dlo)) - - (region(world) - *this).grow(dup, dlo); - } - region shrink(const point &d) const { return shrink(d, d); } - region shrink(T n) const { return shrink(point(n)); } - - // Set operations - box bounding_box() const { - if (empty()) - return box(); - point pmin(numeric_limits::max()), - pmax(numeric_limits::min()); - for (const auto &pos_subregion : subregions) { - const auto &subregion = pos_subregion.second; - auto subbox = subregion.bounding_box(); - pmin = min(pmin, subbox.lower()); - pmax = max(pmax, subbox.upper()); - } - const T xmin = subregions.begin()->first; - const T xmax = subregions.rbegin()->first; - return box(pmin.superpoint(D - 1, xmin), - pmax.superpoint(D - 1, xmax)); - } - - region operator^(const region &other) const { - // TODO: If other is much smaller than this, direct insertion may be faster - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 ^ set1; }, - other); - } - - region operator&(const region &other) const { - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 & set1; }, - other); - } - - region operator|(const region &other) const { - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 | set1; }, - other); - } - - region operator-(const region &other) const { - // return *this & (*this ^ other); - return binary_operator([](const subregion_t &set0, - const subregion_t &set1) { return set0 - set1; }, - other); - } - - region &operator^=(const region &other) { return *this = *this ^ other; } - region &operator&=(const region &other) { return *this = *this & other; } - region &operator|=(const region &other) { return *this = *this | other; } - region &operator-=(const region &other) { return *this = *this - other; } - - region intersection(const region &other) const { return *this & other; } - region setunion(const region &other) const { return *this | other; } - region symmetric_difference(const region &other) const { - return *this ^ other; - } - region difference(const region &other) const { return *this - other; } - - // Set comparison operators - bool contains(const point &p) const { return !isdisjoint(region(p)); } - bool isdisjoint(const region &other) const { return (*this & other).empty(); } - - // Comparison operators - bool operator<=(const region &other) const { return (*this - other).empty(); } - bool operator>=(const region &other) const { return other <= *this; } - bool operator<(const region &other) const { - return *this != other && *this <= other; - } - bool operator>(const region &other) const { return other < *this; } - bool is_subset_of(const region &other) const { return *this <= other; } - bool is_superset_of(const region &other) const { return *this >= other; } - bool is_strict_subset_of(const region &other) const { return *this < other; } - bool is_strict_superset_of(const region &other) const { return *this > other; } - bool operator==(const region &other) const { - return subregions == other.subregions; - } - bool operator!=(const region &other) const { return !(*this == other); } - - bool equal_to(const region &other) const { - std::equal_to eq; - return eq(subregions, other.subregions); - } - bool less(const region &other) const { - std::less lt; - return lt(subregions, other.subregions); - } - size_t hash() const { - size_t r = size_t(0x4eecc6384bcd469dULL); - for (const auto &p : subregions) - r = hash_combine(hash_combine(r, p.first), p.second); - return r; - } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit region(const YAML::Node &node) { - assert(node.Tag() == - "tag:github.com/eschnett/SimulationIO/asdf-cxx/region-1.0.0"); - auto dim = node["dimension"].as(); - assert(dim == D); - // const auto &boxes = node["boxes"].as>>(); - vector> boxes; - for (const auto &b : node["boxes"]) - boxes.push_back(box(b)); - *this = region(boxes); - } - friend void operator>>(const YAML::Node &node, region &r) { - r = region(node); - } -#endif - - ostream &output(ostream &os) const { - // os << "{"; - // for (const auto &pos_subregion : subregions) - // os << pos_subregion.first << ":" << pos_subregion.second << ","; - // os << "}"; - os << "{"; - const vector> boxes(*this); - for (size_t i = 0; i < boxes.size(); ++i) { - if (i > 0) - os << ","; - os << boxes[i]; - } - os << "}"; - return os; - } - friend ostream &operator<<(ostream &os, const region &r) { - return r.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - w << YAML::LocalTag("sio", "region-1.0.0"); - w << YAML::Flow << YAML::BeginMap; - w << YAML::Key << "dimension" << YAML::Value << D; - w << YAML::Key << "boxes" << YAML::Value << vector>(*this); - w << YAML::EndMap; - return w; - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const region &r) { - return r.output(w); - } -#endif -}; -} // namespace RegionCalculus - -namespace std { -template struct equal_to> { - bool operator()(const RegionCalculus::region &p, - const RegionCalculus::region &q) const { - return p.equal_to(q); - } -}; -template struct less> { - bool operator()(const RegionCalculus::region &p, - const RegionCalculus::region &q) const { - return p.less(q); - } -}; -template struct hash> { - size_t operator()(const RegionCalculus::region &p) const { - return p.hash(); - } -}; -} // namespace std - -#endif // #if REGIONCALCULUS_TREE - -//////////////////////////////////////////////////////////////////////////////// -// Dimension-independent wrappers -//////////////////////////////////////////////////////////////////////////////// - -namespace RegionCalculus { - -// Virtual classes - -template struct vpoint { - virtual ~vpoint() {} - - virtual unique_ptr copy() const = 0; - - static unique_ptr make(int d); - static unique_ptr make(int d, T x); - static unique_ptr make(const vector &val); - template static unique_ptr make(const vector &val); - virtual operator vector() const = 0; - template operator vector() const { - auto rT(vector(*this)); - vector rU(rT.size()); - for (size_t i = 0; i < rU.size(); ++i) - rU[i] = U(move(rT[i])); - return rU; - } - template static unique_ptr make(const vpoint &p); - - virtual int rank() const = 0; - - // Access and conversion - virtual T operator[](int d) const = 0; - virtual T &operator[](int d) = 0; - virtual unique_ptr subpoint(int dir) const = 0; - virtual unique_ptr superpoint(int dir, T x) const = 0; - virtual unique_ptr reversed() const = 0; - - // Unary operators - virtual unique_ptr operator+() const = 0; - virtual unique_ptr operator-() const = 0; - virtual unique_ptr operator~() const = 0; - virtual unique_ptr> operator!() const = 0; - - // Assignment operators - virtual vpoint &operator+=(const vpoint &p) = 0; - virtual vpoint &operator-=(const vpoint &p) = 0; - virtual vpoint &operator*=(const vpoint &p) = 0; - virtual vpoint &operator/=(const vpoint &p) = 0; - virtual vpoint &operator%=(const vpoint &p) = 0; - virtual vpoint &operator&=(const vpoint &p) = 0; - virtual vpoint &operator|=(const vpoint &p) = 0; - virtual vpoint &operator^=(const vpoint &p) = 0; - - // Binary operators - virtual unique_ptr operator+(const vpoint &p) const = 0; - virtual unique_ptr operator-(const vpoint &p) const = 0; - virtual unique_ptr operator*(const vpoint &p) const = 0; - virtual unique_ptr operator/(const vpoint &p) const = 0; - virtual unique_ptr operator%(const vpoint &p) const = 0; - virtual unique_ptr operator&(const vpoint &p) const = 0; - virtual unique_ptr operator|(const vpoint &p) const = 0; - virtual unique_ptr operator^(const vpoint &p) const = 0; - virtual unique_ptr> operator&&(const vpoint &p) const = 0; - virtual unique_ptr> operator||(const vpoint &p) const = 0; - - // Unary functions - virtual unique_ptr abs() const = 0; - - // Binary functions - virtual unique_ptr min(const vpoint &p) const = 0; - virtual unique_ptr max(const vpoint &p) const = 0; - - // Comparison operators - virtual unique_ptr> operator==(const vpoint &p) const = 0; - virtual unique_ptr> operator!=(const vpoint &p) const = 0; - virtual unique_ptr> operator<(const vpoint &p) const = 0; - virtual unique_ptr> operator>(const vpoint &p) const = 0; - virtual unique_ptr> operator<=(const vpoint &p) const = 0; - virtual unique_ptr> operator>=(const vpoint &p) const = 0; - - virtual bool equal_to(const vpoint &p) const = 0; - virtual bool less(const vpoint &p) const = 0; - virtual size_t hash() const = 0; - - // Reductions - virtual bool any() const = 0; - virtual bool all() const = 0; - virtual T minval() const = 0; - virtual T maxval() const = 0; - virtual T sum() const = 0; - typedef typename point::prod_t prod_t; - virtual prod_t prod() const = 0; - - // I/O -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - static unique_ptr make(const YAML::Node &node); -#endif - virtual ostream &output(ostream &os) const = 0; - friend ostream &operator<<(ostream &os, const vpoint &p) { - return p.output(os); - } -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - virtual YAML::Emitter &output(YAML::Emitter &w) const = 0; - friend YAML::Emitter &operator<<(YAML::Emitter &w, const vpoint &p) { - return p.output(w); - } -#endif -}; - -template struct vbox { - virtual ~vbox() {} - - virtual unique_ptr copy() const = 0; - - static unique_ptr make(int d); - static unique_ptr make(const vpoint &lo, const vpoint &hi); - template static unique_ptr make(const vbox &p); - - virtual int rank() const = 0; - - // Predicates - virtual bool empty() const = 0; - virtual unique_ptr> shape() const = 0; - virtual unique_ptr> lower() const = 0; - virtual unique_ptr> upper() const = 0; - typedef typename box::prod_t prod_t; - virtual prod_t size() const = 0; - - // Shift and scale operators - virtual vbox &operator>>=(const vpoint &p) = 0; - virtual vbox &operator<<=(const vpoint &p) = 0; - virtual vbox &operator*=(const vpoint &p) = 0; - virtual unique_ptr operator>>(const vpoint &p) const = 0; - virtual unique_ptr operator<<(const vpoint &p) const = 0; - virtual unique_ptr operator*(const vpoint &p) const = 0; - virtual unique_ptr grow(const vpoint &dlo, - const vpoint &dup) const = 0; - virtual unique_ptr grow(const vpoint &d) const = 0; - virtual unique_ptr grow(T n) const = 0; - virtual unique_ptr shrink(const vpoint &dlo, - const vpoint &dup) const = 0; - virtual unique_ptr shrink(const vpoint &d) const = 0; - virtual unique_ptr shrink(T n) const = 0; - - // Comparison operators - virtual bool operator==(const vbox &b) const = 0; - - virtual bool equal_to(const vbox &b) const = 0; - virtual bool less(const vbox &b) const = 0; - virtual size_t hash() const = 0; - - // Set comparison operators - virtual bool contains(const vpoint &p) const = 0; - virtual bool isdisjoint(const vbox &b) const = 0; - virtual bool operator<=(const vbox &b) const = 0; - virtual bool operator<(const vbox &b) const = 0; - - // Set operations - virtual unique_ptr bounding_box(const vbox &b) const = 0; - virtual unique_ptr operator&(const vbox &b) const = 0; - // virtual unique_ptr operator-(const vbox &b) const = 0; - // virtual unique_ptr operator|(const vbox &b) const = 0; - // virtual unique_ptr operator^(const vbox &b) const = 0; - - // I/O -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - static unique_ptr make(const YAML::Node &node); -#endif - virtual ostream &output(ostream &os) const = 0; - friend ostream &operator<<(ostream &os, const vbox &b) { - return b.output(os); - } -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - virtual YAML::Emitter &output(YAML::Emitter &w) const = 0; - friend YAML::Emitter &operator<<(YAML::Emitter &w, const vbox &b) { - return b.output(w); - } -#endif -}; - -template struct vregion { - virtual ~vregion() {} - - virtual unique_ptr copy() const = 0; - - static unique_ptr make(int d); - static unique_ptr make(const vbox &b); - static unique_ptr make(const vector>> &bs); - virtual operator vector>>() const = 0; - template static unique_ptr make(const vregion &p); - - virtual int rank() const = 0; - - // Predicates - virtual bool invariant() const = 0; - virtual bool empty() const = 0; - typedef typename region::prod_t prod_t; - virtual prod_t size() const = 0; - - // Shift and scale operators - virtual unique_ptr operator>>(const vpoint &d) const = 0; - virtual unique_ptr operator<<(const vpoint &d) const = 0; - virtual unique_ptr grow(const vpoint &dlo, - const vpoint &dup) const = 0; - virtual unique_ptr grow(const vpoint &d) const = 0; - virtual unique_ptr grow(T n) const = 0; - virtual unique_ptr shrink(const vpoint &dlo, - const vpoint &dup) const = 0; - virtual unique_ptr shrink(const vpoint &d) const = 0; - virtual unique_ptr shrink(T n) const = 0; - - // Set operations - virtual unique_ptr> bounding_box() const = 0; - virtual unique_ptr operator&(const vbox &b) const = 0; - virtual unique_ptr operator&(const vregion &r) const = 0; - virtual unique_ptr operator-(const vbox &b) const = 0; - virtual unique_ptr operator-(const vregion &r) const = 0; - virtual unique_ptr operator|(const vbox &b) const = 0; - virtual unique_ptr operator|(const vregion &r) const = 0; - virtual unique_ptr operator^(const vbox &b) const = 0; - virtual unique_ptr operator^(const vregion &r) const = 0; - - // Set comparison operators - virtual bool contains(const vpoint &p) const = 0; - virtual bool isdisjoint(const vbox &b) const = 0; - virtual bool isdisjoint(const vregion &r) const = 0; - - // Comparison operators - virtual bool operator<=(const vregion &r) const = 0; - virtual bool operator>=(const vregion &r) const = 0; - virtual bool operator<(const vregion &r) const = 0; - virtual bool operator>(const vregion &r) const = 0; - virtual bool operator==(const vregion &r) const = 0; - virtual bool operator!=(const vregion &r) const = 0; - - virtual bool equal_to(const vregion &r) const = 0; - virtual bool less(const vregion &r) const = 0; - virtual size_t hash() const = 0; - - // I/O -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - static unique_ptr make(const YAML::Node &node); -#endif - virtual ostream &output(ostream &os) const = 0; - friend ostream &operator<<(ostream &os, const vregion &r) { - return r.output(os); - } -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - virtual YAML::Emitter &output(YAML::Emitter &w) const = 0; - friend YAML::Emitter &operator<<(YAML::Emitter &w, const vregion &r) { - return r.output(w); - } -#endif -}; - -//////////////////////////////////////////////////////////////////////////////// - -// Wrapper classes (using pointers) - -template struct wpoint : vpoint { - point val; - - wpoint(const wpoint &p) = default; - wpoint(wpoint &&p) = default; - wpoint &operator=(const wpoint &p) = default; - wpoint &operator=(wpoint &&p) = default; - unique_ptr> copy() const { return make_unique1(*this); } - - wpoint(const point &p) : val(p) {} - - wpoint() : val() {} - wpoint(T x) : val(x) {} - wpoint(const array &p) : val(p) {} - template wpoint(const array &p) : val(p) {} - wpoint(const vector &p) : val(p) {} - template wpoint(const vector &p) : val(p) {} - operator vector() const { return vector(val); } - template operator vector() const { - vector rT(val); - vector rU(rT.size()); - for (size_t i = 0; i < rU.size(); ++i) - rU[i] = U(move(rT[i])); - return rU; - } - template wpoint(const wpoint &p) : val(p.val) {} - - int rank() const { return D; } - - // Access and conversion - T operator[](int d) const { return val[d]; } - T &operator[](int d) { return val[d]; } - unique_ptr> subpoint(int dir) const { - return make_unique1 0 ? D - 1 : 0)>>(val.subpoint(dir)); - } - unique_ptr> superpoint(int dir, T x) const { - // This is intentionally wrong for D >= 4 to avoid infinite recursion to - // ever larger ranks - return make_unique1>(val.superpoint(dir, x)); - } - unique_ptr> reversed() const { - return make_unique1(val.reversed()); - } - - // Unary operators - unique_ptr> operator+() const { return make_unique1(+val); } - unique_ptr> operator-() const { return make_unique1(-val); } - unique_ptr> operator~() const { - return make_unique1( - typename call_if_integral, bit_not>>::type()( - val)); - } - unique_ptr> operator!() const { - return make_unique1>(!val); - } - - // Assignment operators - vpoint &operator+=(const vpoint &p) { - val += dynamic_cast(p).val; - return *this; - } - vpoint &operator-=(const vpoint &p) { - val -= dynamic_cast(p).val; - return *this; - } - vpoint &operator*=(const vpoint &p) { - val *= dynamic_cast(p).val; - return *this; - } - vpoint &operator/=(const vpoint &p) { - val /= dynamic_cast(p).val; - return *this; - } - vpoint &operator%=(const vpoint &p) { - typename call_if_integral, modulus_eq>>::type()( - val, dynamic_cast(p).val); - return *this; - } - vpoint &operator&=(const vpoint &p) { - typename call_if_integral, bit_and_eq>>::type()( - val, dynamic_cast(p).val); - return *this; - } - vpoint &operator|=(const vpoint &p) { - typename call_if_integral, bit_or_eq>>::type()( - val, dynamic_cast(p).val); - return *this; - } - vpoint &operator^=(const vpoint &p) { - typename call_if_integral, bit_xor_eq>>::type()( - val, dynamic_cast(p).val); - return *this; - } - - // Binary operators - unique_ptr> operator+(const vpoint &p) const { - return make_unique1(val + dynamic_cast(p).val); - } - unique_ptr> operator-(const vpoint &p) const { - return make_unique1(val - dynamic_cast(p).val); - } - unique_ptr> operator*(const vpoint &p) const { - return make_unique1(val * dynamic_cast(p).val); - } - unique_ptr> operator/(const vpoint &p) const { - return make_unique1(val / dynamic_cast(p).val); - } - unique_ptr> operator%(const vpoint &p) const { - return make_unique1( - typename call_if_integral, modulus>>::type()( - val, dynamic_cast(p).val)); - } - unique_ptr> operator&(const vpoint &p) const { - return make_unique1( - typename call_if_integral, bit_and>>::type()( - val, dynamic_cast(p).val)); - } - unique_ptr> operator|(const vpoint &p) const { - return make_unique1( - typename call_if_integral, bit_or>>::type()( - val, dynamic_cast(p).val)); - } - unique_ptr> operator^(const vpoint &p) const { - return make_unique1( - typename call_if_integral, bit_xor>>::type()( - val, dynamic_cast(p).val)); - } - unique_ptr> operator&&(const vpoint &p) const { - return make_unique1>(val && - dynamic_cast(p).val); - } - unique_ptr> operator||(const vpoint &p) const { - return make_unique1>(val || - dynamic_cast(p).val); - } - - // Unary functions - unique_ptr> abs() const { return make_unique1(val.abs()); } - - // Binary functions - unique_ptr> min(const vpoint &p) const { - return make_unique1(val.min(dynamic_cast(p).val)); - } - unique_ptr> max(const vpoint &p) const { - return make_unique1(val.max(dynamic_cast(p).val)); - } - - // Comparison operators - unique_ptr> operator==(const vpoint &p) const { - return make_unique1>(val == - dynamic_cast(p).val); - } - unique_ptr> operator!=(const vpoint &p) const { - return make_unique1>(val != - dynamic_cast(p).val); - } - unique_ptr> operator<(const vpoint &p) const { - return make_unique1>(val < - dynamic_cast(p).val); - } - unique_ptr> operator>(const vpoint &p) const { - return make_unique1>(val > - dynamic_cast(p).val); - } - unique_ptr> operator<=(const vpoint &p) const { - return make_unique1>(val <= - dynamic_cast(p).val); - } - unique_ptr> operator>=(const vpoint &p) const { - return make_unique1>(val >= - dynamic_cast(p).val); - } - - bool equal_to(const vpoint &p) const { - return val.equal_to(dynamic_cast(p).val); - } - bool less(const vpoint &p) const { - return val.less(dynamic_cast(p).val); - } - size_t hash() const { return val.hash(); } - - // Reductions - bool any() const { return val.any(); } - bool all() const { return val.all(); } - T minval() const { return val.minval(); } - T maxval() const { return val.maxval(); } - T sum() const { return val.sum(); } - typedef typename vpoint::prod_t prod_t; - prod_t prod() const { return val.prod(); } - - // I/O -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit wpoint(const YAML::Node &node) : val(node) {} -#endif - ostream &output(ostream &os) const { return val.output(os); } - // friend ostream &operator<<(ostream &os, const wpoint &p) { - // return p.output(os); - // } -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { return val.output(w); } - // friend YAML::Emitter &operator<<(YAML::Emitter &w, const wpoint &p) { - // return p.output(w); - // } -#endif -}; - -template struct wbox : vbox { - box val; - - wbox(const wbox &b) = default; - wbox(wbox &&b) = default; - wbox &operator=(const wbox &b) = default; - wbox &operator=(wbox &&b) = default; - unique_ptr> copy() const { return make_unique1(*this); } - - wbox(const box &b) : val(b) {} - - wbox() : val() {} - wbox(const wpoint &lo, const wpoint &hi) : val(lo.val, hi.val) {} - template wbox(const wbox &p) : val(p.val) {} - - int rank() const { return D; } - - // Predicates - bool empty() const { return val.empty(); } - unique_ptr> lower() const { - return make_unique1>(val.lower()); - } - unique_ptr> upper() const { - return make_unique1>(val.upper()); - } - unique_ptr> shape() const { - return make_unique1>(val.shape()); - } - typedef typename vbox::prod_t prod_t; - prod_t size() const { return val.size(); } - - // Shift and scale operators - vbox &operator>>=(const vpoint &p) { - val >>= dynamic_cast &>(p).val; - return *this; - } - vbox &operator<<=(const vpoint &p) { - val <<= dynamic_cast &>(p).val; - return *this; - } - vbox &operator*=(const vpoint &p) { - val *= dynamic_cast &>(p).val; - return *this; - } - unique_ptr> operator>>(const vpoint &p) const { - return make_unique1(val >> dynamic_cast &>(p).val); - } - unique_ptr> operator<<(const vpoint &p) const { - return make_unique1(val << dynamic_cast &>(p).val); - } - unique_ptr> operator*(const vpoint &p) const { - return make_unique1(val * dynamic_cast &>(p).val); - } - unique_ptr> grow(const vpoint &dlo, const vpoint &dup) const { - return make_unique1( - val.grow(dynamic_cast &>(dlo).val, - dynamic_cast &>(dup).val)); - } - unique_ptr> grow(const vpoint &d) const { - return make_unique1( - val.grow(dynamic_cast &>(d).val)); - } - unique_ptr> grow(T n) const { - return make_unique1(val.grow(n)); - } - unique_ptr> shrink(const vpoint &dlo, const vpoint &dup) const { - return make_unique1( - val.shrink(dynamic_cast &>(dlo).val, - dynamic_cast &>(dup).val)); - } - unique_ptr> shrink(const vpoint &d) const { - return make_unique1( - val.shrink(dynamic_cast &>(d).val)); - } - unique_ptr> shrink(T n) const { - return make_unique1(val.shrink(n)); - } - - // Comparison operators - bool operator==(const vbox &b) const { - return rank() == b.rank() && val == dynamic_cast &>(b).val; - } - - bool equal_to(const vbox &b) const { - return val.equal_to(dynamic_cast &>(b).val); - } - bool less(const vbox &b) const { - return val.less(dynamic_cast &>(b).val); - } - size_t hash() const { return val.hash(); } - - // Set comparison operators - bool contains(const vpoint &p) const { - return val.contains(dynamic_cast &>(p).val); - } - bool isdisjoint(const vbox &b) const { - return val.isdisjoint(dynamic_cast(b).val); - } - bool operator<=(const vbox &b) const { - return val <= dynamic_cast(b).val; - } - bool operator<(const vbox &b) const { - return val < dynamic_cast(b).val; - } - - // Set operations - unique_ptr> bounding_box(const vbox &b) const { - return make_unique1( - val.bounding_box(dynamic_cast(b).val)); - } - unique_ptr> operator&(const vbox &b) const { - return make_unique1(val & dynamic_cast(b).val); - } - // unique_ptr> operator-(const vbox &b) const { - // return make_unique1(val - dynamic_cast(b).val); - // } - // unique_ptr> operator|(const vbox &b) const { - // return make_unique1(val | dynamic_cast(b).val); - // } - // unique_ptr> operator^(const vbox &b) const { - // return make_unique1(val ^ dynamic_cast(b).val); - // } - - // I/O -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit wbox(const YAML::Node &node) : val(node) {} -#endif - ostream &output(ostream &os) const { return val.output(os); } - // friend ostream &operator<<(ostream &os, const wbox &b) { - // return b.output(os); - // } -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { return val.output(w); } - // friend YAML::Emitter &operator<<(YAML::Emitter &w, const wbox &b) { - // return b.output(w); - // } -#endif -}; - -template struct wregion : vregion { - region val; - - wregion(const wregion &r) = default; - wregion(wregion &&r) = default; - wregion &operator=(const wregion &r) = default; - wregion &operator=(wregion &&r) = default; - unique_ptr> copy() const { - return unique_ptr>(new wregion(*this)); - } - - wregion(const region &r) : val(r) {} - wregion(region &&r) : val(move(r)) {} - - wregion() = default; - wregion(const wbox &b) : val(b.val) {} - wregion(const vector>> &bs) { - vector> rs; - for (const auto &b : bs) - rs.push_back(dynamic_cast &>(b).val); - val = region(rs); - } - operator vector>>() const { - vector>> bs; - for (const auto &b : vector>(val)) - bs.push_back(make_unique1>(b)); - return bs; - } - template wregion(const wregion &p) : val(p.val) {} - - int rank() const { return D; } - - // Predicates - bool invariant() const { return val.invariant(); } - bool empty() const { return val.empty(); } - typedef typename vregion::prod_t prod_t; - prod_t size() const { return val.size(); } - - // Shift and scale operators - unique_ptr> operator>>(const vpoint &d) const { - return make_unique1(val >> - dynamic_cast &>(d).val); - } - unique_ptr> operator<<(const vpoint &d) const { - return make_unique1(val - << dynamic_cast &>(d).val); - } - unique_ptr> grow(const vpoint &dlo, - const vpoint &dup) const { - return make_unique1( - val.grow(dynamic_cast &>(dlo).val, - dynamic_cast &>(dup).val)); - } - unique_ptr> grow(const vpoint &d) const { - return make_unique1( - val.grow(dynamic_cast &>(d).val)); - } - unique_ptr> grow(T n) const { - return make_unique1(val.grow(n)); - } - unique_ptr> shrink(const vpoint &dlo, - const vpoint &dup) const { - return make_unique1( - val.shrink(dynamic_cast &>(dlo).val, - dynamic_cast &>(dup).val)); - } - unique_ptr> shrink(const vpoint &d) const { - return make_unique1( - val.shrink(dynamic_cast &>(d).val)); - } - unique_ptr> shrink(T n) const { - return make_unique1(val.shrink(n)); - } - - // Set operations - unique_ptr> bounding_box() const { - return make_unique1>(val.bounding_box()); - } - unique_ptr> operator&(const vbox &b) const { - return make_unique1(val & dynamic_cast &>(b).val); - } - unique_ptr> operator&(const vregion &r) const { - return make_unique1(val & dynamic_cast(r).val); - } - unique_ptr> operator-(const vbox &b) const { - return make_unique1(val - dynamic_cast &>(b).val); - } - unique_ptr> operator-(const vregion &r) const { - return make_unique1(val - dynamic_cast(r).val); - } - unique_ptr> operator|(const vbox &b) const { - return make_unique1(val | dynamic_cast &>(b).val); - } - unique_ptr> operator|(const vregion &r) const { - return make_unique1(val | dynamic_cast(r).val); - } - unique_ptr> operator^(const vbox &b) const { - return make_unique1(val ^ dynamic_cast &>(b).val); - } - unique_ptr> operator^(const vregion &r) const { - return make_unique1(val ^ dynamic_cast(r).val); - } - - // Set comparison operators - bool contains(const vpoint &p) const { - return val.contains(dynamic_cast &>(p).val); - } - bool isdisjoint(const vbox &b) const { - return val.isdisjoint(dynamic_cast &>(b).val); - } - bool isdisjoint(const vregion &r) const { - return val.isdisjoint(dynamic_cast(r).val); - } - - // Comparison operators - bool operator<=(const vregion &r) const { - return val <= dynamic_cast(r).val; - } - bool operator>=(const vregion &r) const { - return val >= dynamic_cast(r).val; - } - bool operator<(const vregion &r) const { - return val < dynamic_cast(r).val; - } - bool operator>(const vregion &r) const { - return val > dynamic_cast(r).val; - } - bool operator==(const vregion &r) const { - return rank() == r.rank() && val == dynamic_cast(r).val; - } - bool operator!=(const vregion &r) const { - return rank() != r.rank() || val != dynamic_cast(r).val; - } - - bool equal_to(const vregion &r) const { - return val.equal_to(dynamic_cast(r).val); - } - bool less(const vregion &r) const { - return val.less(dynamic_cast(r).val); - } - size_t hash() const { return val.hash(); } - - // I/O -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit wregion(const YAML::Node &node) : val(node) {} -#endif - ostream &output(ostream &os) const { return val.output(os); } - // friend ostream &operator<<(ostream &os, const wregion &r) { - // return r.output(os); - // } -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { return val.output(w); } - // friend YAML::Emitter &operator<<(YAML::Emitter &w, const wregion &r) { - // return r.output(w); - // } -#endif -}; - -//////////////////////////////////////////////////////////////////////////////// - -// Dispatching functions (replacements for constructors) - -template unique_ptr> vpoint::make(int d) { - switch (d) { - case 0: - return make_unique1>(); - case 1: - return make_unique1>(); - case 2: - return make_unique1>(); - case 3: - return make_unique1>(); - case 4: - return make_unique1>(); - default: - assert(0); - } -} - -template unique_ptr> vpoint::make(int d, T x) { - switch (d) { - case 0: - return make_unique1>(x); - case 1: - return make_unique1>(x); - case 2: - return make_unique1>(x); - case 3: - return make_unique1>(x); - case 4: - return make_unique1>(x); - default: - assert(0); - } -} - -template -unique_ptr> vpoint::make(const vector &val) { - switch (val.size()) { - case 0: - return make_unique1>(val); - case 1: - return make_unique1>(val); - case 2: - return make_unique1>(val); - case 3: - return make_unique1>(val); - case 4: - return make_unique1>(val); - default: - assert(0); - } -} - -template -template -unique_ptr> vpoint::make(const vector &val) { - switch (val.size()) { - case 0: - return make_unique1>(val); - case 1: - return make_unique1>(val); - case 2: - return make_unique1>(val); - case 3: - return make_unique1>(val); - case 4: - return make_unique1>(val); - default: - assert(0); - } -} - -template -template -unique_ptr> vpoint::make(const vpoint &p) { - switch (p.rank()) { - case 0: - return make_unique1>(dynamic_cast &>(p)); - case 1: - return make_unique1>(dynamic_cast &>(p)); - case 2: - return make_unique1>(dynamic_cast &>(p)); - case 3: - return make_unique1>(dynamic_cast &>(p)); - case 4: - return make_unique1>(dynamic_cast &>(p)); - default: - assert(0); - } -} - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX -template -unique_ptr> vpoint::make(const YAML::Node &node) { - const auto &v = node.as>(); - switch (v.size()) { - case 0: - return make_unique1>(v); - case 1: - return make_unique1>(v); - case 2: - return make_unique1>(v); - case 3: - return make_unique1>(v); - case 4: - return make_unique1>(v); - default: - assert(0); - } -} -#endif - -template unique_ptr> vbox::make(int d) { - switch (d) { - case 0: - return make_unique1>(); - case 1: - return make_unique1>(); - case 2: - return make_unique1>(); - case 3: - return make_unique1>(); - case 4: - return make_unique1>(); - default: - assert(0); - } -} - -template -unique_ptr> vbox::make(const vpoint &lo, const vpoint &hi) { - switch (lo.rank()) { - case 0: - return make_unique1>(dynamic_cast &>(lo), - dynamic_cast &>(hi)); - case 1: - return make_unique1>(dynamic_cast &>(lo), - dynamic_cast &>(hi)); - case 2: - return make_unique1>(dynamic_cast &>(lo), - dynamic_cast &>(hi)); - case 3: - return make_unique1>(dynamic_cast &>(lo), - dynamic_cast &>(hi)); - case 4: - return make_unique1>(dynamic_cast &>(lo), - dynamic_cast &>(hi)); - default: - assert(0); - } -} - -template -template -unique_ptr> vbox::make(const vbox &b) { - switch (b.rank()) { - case 0: - return make_unique1>(dynamic_cast &>(b)); - case 1: - return make_unique1>(dynamic_cast &>(b)); - case 2: - return make_unique1>(dynamic_cast &>(b)); - case 3: - return make_unique1>(dynamic_cast &>(b)); - case 4: - return make_unique1>(dynamic_cast &>(b)); - default: - assert(0); - } -} - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX -template -unique_ptr> vbox::make(const YAML::Node &node) { - const auto &full = node["full"]; - if (full.IsDefined()) - // return make_unique1>(node); - return make_unique1>(box(full.as())); - const auto &lo = vpoint::make(node["low"]); - const auto &hi = vpoint::make(node["high"]); - return vbox::make(*lo, *hi); -} -#endif - -template unique_ptr> vregion::make(int d) { - switch (d) { - case 0: - return make_unique1>(); - case 1: - return make_unique1>(); - case 2: - return make_unique1>(); - case 3: - return make_unique1>(); - case 4: - return make_unique1>(); - default: - assert(0); - } -} - -template -unique_ptr> vregion::make(const vbox &b) { - switch (b.rank()) { - case 0: - return make_unique1>(dynamic_cast &>(b)); - case 1: - return make_unique1>(dynamic_cast &>(b)); - case 2: - return make_unique1>(dynamic_cast &>(b)); - case 3: - return make_unique1>(dynamic_cast &>(b)); - case 4: - return make_unique1>(dynamic_cast &>(b)); - default: - assert(0); - } -} - -template -unique_ptr> vregion::make(const vector>> &bs) { - if (bs.empty()) - // Cannot determine rank - return nullptr; - switch (bs[0]->rank()) { - case 0: { - vector> rs; - for (const auto &b : bs) - rs.push_back(dynamic_cast &>(*b).val); - return make_unique1>(rs); - } - case 1: { - vector> rs; - for (const auto &b : bs) - rs.push_back(dynamic_cast &>(*b).val); - return make_unique1>(rs); - } - case 2: { - vector> rs; - for (const auto &b : bs) - rs.push_back(dynamic_cast &>(*b).val); - return make_unique1>(rs); - } - case 3: { - vector> rs; - for (const auto &b : bs) - rs.push_back(dynamic_cast &>(*b).val); - return make_unique1>(rs); - } - case 4: { - vector> rs; - for (const auto &b : bs) - rs.push_back(dynamic_cast &>(*b).val); - return make_unique1>(rs); - } - default: - assert(0); - } -} - -template -template -unique_ptr> vregion::make(const vregion &r) { - switch (r.rank()) { - case 0: - return make_unique1>(dynamic_cast &>(r)); - case 1: - return make_unique1>(dynamic_cast &>(r)); - case 2: - return make_unique1>(dynamic_cast &>(r)); - case 3: - return make_unique1>(dynamic_cast &>(r)); - case 4: - return make_unique1>(dynamic_cast &>(r)); - default: - assert(0); - } -} - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX -template -unique_ptr> vregion::make(const YAML::Node &node) { - auto dim = node["dimension"].as(); - switch (dim) { - case 0: - return make_unique1>(node); - case 1: - return make_unique1>(node); - case 2: - return make_unique1>(node); - case 3: - return make_unique1>(node); - case 4: - return make_unique1>(node); - default: - assert(0); - } -} -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Dimension-independent classes (hiding the pointers) - -template struct dpoint { - unique_ptr> val; - - dpoint() = default; - - dpoint(const dpoint &p) { - if (p.val) - val = p.val->copy(); - } - dpoint(dpoint &&p) = default; - dpoint &operator=(const dpoint &p) { - if (p.val) - val = p.val->copy(); - else - val = nullptr; - return *this; - } - dpoint &operator=(dpoint &&p) = default; - - template - dpoint(const point &p) : val(make_unique1>(p)) {} - dpoint(const vpoint &p) : val(p.copy()) {} - dpoint(const unique_ptr> &val) { - if (val) - this->val = val->copy(); - } - dpoint(unique_ptr> &&val) : val(move(val)) {} - - explicit dpoint(int d) : val(vpoint::make(d)) {} - dpoint(int d, T x) : val(vpoint::make(d, x)) {} - template - dpoint(const array &p) : val(make_unique1>(p)) {} - template - explicit dpoint(const array &p) : val(make_unique1>(p)) {} - dpoint(const vector &p) : val(vpoint::make(p)) {} - template - explicit dpoint(const vector &p) : val(vpoint::make(p)) {} - operator vector() const { return vector(*val); } - template explicit operator vector() const { - return vector(*val); - } - template dpoint(const dpoint &p) { - if (p.val) - val = vpoint::make(*p.val); - } - template operator point() const { - assert(valid()); - assert(rank() == D); - return dynamic_cast *>(val.get())->val; - } - - bool valid() const { return bool(val); } - void reset() { val.reset(); } - int rank() const { return val->rank(); } - - // Access and conversion - T operator[](int d) const { return (*val)[d]; } - T &operator[](int d) { return (*val)[d]; } - dpoint subpoint(int dir) const { return dpoint(val->subpoint(dir)); } - dpoint superpoint(int dir, T x) const { return dpoint(val->subpoint(dir)); } - dpoint reversed() const { return dpoint(val->reversed()); } - - // Unary operators - dpoint operator+() const { return dpoint(+*val); } - dpoint operator-() const { return dpoint(-*val); } - dpoint operator~() const { return dpoint(~*val); } - dpoint operator!() const { return dpoint(!*val); } - - // Assignment operators - dpoint &operator+=(const dpoint &p) { - *val += *p.val; - return *this; - } - dpoint &operator-=(const dpoint &p) { - *val -= *p.val; - return *this; - } - dpoint &operator*=(const dpoint &p) { - *val *= *p.val; - return *this; - } - dpoint &operator/=(const dpoint &p) { - *val /= *p.val; - return *this; - } - dpoint &operator%=(const dpoint &p) { - *val %= *p.val; - return *this; - } - dpoint &operator&=(const dpoint &p) { - *val &= *p.val; - return *this; - } - dpoint &operator|=(const dpoint &p) { - *val |= *p.val; - return *this; - } - dpoint &operator^=(const dpoint &p) { - *val ^= *p.val; - return *this; - } - - // Binary operators - dpoint operator+(const dpoint &p) const { return dpoint(*val + *p.val); } - dpoint operator-(const dpoint &p) const { return dpoint(*val - *p.val); } - dpoint operator*(const dpoint &p) const { return dpoint(*val * *p.val); } - dpoint operator/(const dpoint &p) const { return dpoint(*val / *p.val); } - dpoint operator%(const dpoint &p) const { return dpoint(*val % *p.val); } - dpoint operator&(const dpoint &p) const { return dpoint(*val & *p.val); } - dpoint operator|(const dpoint &p) const { return dpoint(*val | *p.val); } - dpoint operator^(const dpoint &p) const { return dpoint(*val ^ *p.val); } - dpoint operator&&(const dpoint &p) const { - return dpoint(*val && *p.val); - } - dpoint operator||(const dpoint &p) const { - return dpoint(*val || *p.val); - } - - // Unary functions - dpoint abs() const { return dpoint(val->abs()); } - - // Binary functions - dpoint min(const dpoint &p) const { return dpoint(val->min(*p.val)); } - dpoint max(const dpoint &p) const { return dpoint(val->max(*p.val)); } - - // Comparison operators - dpoint operator==(const dpoint &p) const { - return dpoint(*val == *p.val); - } - dpoint operator!=(const dpoint &p) const { - return dpoint(*val != *p.val); - } - dpoint operator<(const dpoint &p) const { - return dpoint(*val < *p.val); - } - dpoint operator>(const dpoint &p) const { - return dpoint(*val > *p.val); - } - dpoint operator<=(const dpoint &p) const { - return dpoint(*val <= *p.val); - } - dpoint operator>=(const dpoint &p) const { - return dpoint(*val >= *p.val); - } - - bool equal_to(const dpoint &p) const { return val->equal_to(*p.val); } - bool less(const dpoint &p) const { return val->less(*p.val); } - size_t hash() const { return val->hash(); } - - // Reductions - bool all() const { return val->all(); } - bool any() const { return val->any(); } - T minval() const { return val->minval(); } - T maxval() const { return val->maxval(); } - T sum() const { return val->sum(); } - typedef typename vpoint::prod_t prod_t; - prod_t prod() const { return val->prod(); } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit dpoint(const YAML::Node &node) : val(vpoint::make(node)) {} -#endif - - ostream &output(ostream &os) const { - if (!val) - return os << "dpoint()"; - return val->output(os); - } - friend ostream &operator<<(ostream &os, const dpoint &p) { - return p.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - if (!val) - return w << YAML::LocalTag("sio", "point-1.0.0"); - return val->output(w); - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const dpoint &p) { - return p.output(w); - } -#endif -}; - -// Unary functions -template dpoint abs(const dpoint &p) { return p.abs(); } - -// Binary functions -template dpoint min(const dpoint &p, const dpoint &q) { - return p.min(q); -} -template dpoint max(const dpoint &p, const dpoint &q) { - return p.max(q); -} - -// Reductions -template bool all(const dpoint &p) { return p.all(); } -template bool any(const dpoint &p) { return p.any(); } -template T minval(const dpoint &p) { return p.minval(); } -template T maxval(const dpoint &p) { return p.maxval(); } -template T sum(const dpoint &p) { return p.sum(); } -template typename dpoint::prod_t prod(const dpoint &p) { - return p.prod(); -} -} // namespace RegionCalculus - -namespace std { -template struct equal_to> { - bool operator()(const RegionCalculus::dpoint &p, - const RegionCalculus::dpoint &q) const { - return p.equal_to(q); - } -}; - -template struct less> { - bool operator()(const RegionCalculus::dpoint &p, - const RegionCalculus::dpoint &q) const { - return p.less(q); - } -}; -template struct hash> { - size_t operator()(const RegionCalculus::dpoint &p) const { - return p.hash(); - } -}; -} // namespace std - -namespace RegionCalculus { -template struct dbox { - unique_ptr> val; - - dbox() = default; - - dbox(const dbox &b) { - if (b.val) - val = b.val->copy(); - } - dbox(dbox &&b) = default; - dbox &operator=(const dbox &b) { - if (b.val) - val = b.val->copy(); - else - val = nullptr; - return *this; - } - dbox &operator=(dbox &&b) = default; - - template - dbox(const box &b) : val(make_unique1>(b)) {} - dbox(const vbox &b) : val(b.copy()) {} - dbox(const unique_ptr> &val) { - if (val) - this->val = val->copy(); - } - dbox(unique_ptr> &&val) : val(move(val)) {} - - explicit dbox(int d) : val(vbox::make(d)) {} - dbox(const dpoint &lo, const dpoint &hi) - : val(vbox::make(*lo.val, *hi.val)) {} - template dbox(const dbox &p) { - if (p.val) - val = vbox::make(*p.val); - } - template operator box() const { - assert(valid()); - assert(rank() == D); - return dynamic_cast *>(val.get())->val; - } - - bool valid() const { return bool(val); } - void reset() { val.reset(); } - int rank() const { return val->rank(); } - - // Predicates - bool empty() const { return val->empty(); } - dpoint lower() const { return dpoint(val->lower()); } - dpoint upper() const { return dpoint(val->upper()); } - dpoint shape() const { return dpoint(val->shape()); } - typedef typename vbox::prod_t prod_t; - prod_t size() const { return val->size(); } - - // Shift and scale operators - dbox &operator>>=(const dpoint &p) { - *val >>= *p.val; - return *this; - } - dbox &operator<<=(const dpoint &p) { - *val <<= *p.val; - return *this; - } - dbox &operator*=(const dpoint &p) { - *val *= *p.val; - return *this; - } - dbox operator>>(const dpoint &p) const { return dbox(*val >> *p.val); } - dbox operator<<(const dpoint &p) const { return dbox(*val << *p.val); } - dbox operator*(const dpoint &p) const { return dbox(*val * *p.val); } - dbox grow(const dpoint &dlo, const dpoint &dup) const { - return dbox(val->grow(*dlo.val, *dup.val)); - } - dbox grow(const dpoint &d) const { return dbox(val->grow(*d)); } - dbox grow(T n) const { return dbox(val->grow(n)); } - dbox shrink(const dpoint &dlo, const dpoint &dup) const { - return dbox(val->shrink(*dlo.val, *dup.val)); - } - dbox shrink(const dpoint &d) const { return dbox(val->shrink(*d)); } - dbox shrink(T n) const { return dbox(val->shrink(n)); } - - // Comparison operators - bool operator==(const dbox &b) const { return *val == *b.val; } - bool operator!=(const dbox &b) const { return !(*this == b); } - bool equal_to(const dbox &b) const { return val->equal_to(*b.val); } - bool less(const dbox &b) const { return val->less(*b.val); } - size_t hash() const { return val->hash(); } - - // Set comparison operators - bool contains(const dpoint &p) const { return val->contains(*p.val); } - bool isdisjoint(const dbox &b) const { return val->isdisjoint(*b.val); } - bool operator<=(const dbox &b) const { return *val <= *b.val; } - bool operator>=(const dbox &b) const { return b <= *this; } - bool operator<(const dbox &b) const { return *val < *b.val; } - bool operator>(const dbox &b) const { return b < *this; } - bool is_subset_of(const dbox &b) const { return *this <= b; } - bool is_superset_of(const dbox &b) const { return *this >= b; } - bool is_strict_subset_of(const dbox &b) const { return *this < b; } - bool is_strict_superset_of(const dbox &b) const { return *this > b; } - - // Set operations - dbox bounding_box(const dbox &b) const { - return dbox(val->bounding_box(*b.val)); - } - dbox operator&(const dbox &b) const { return dbox(*val & *b.val); } - // dbox operator-(const dbox &b) const { return dbox(*val - *b.val); } - // dbox operator|(const dbox &b) const { return dbox(*val | *b.val); } - // dbox operator^(const dbox &b) const { return dbox(*val ^ *b.val); } - dbox intersection(const dbox &b) const { return *this & b; } - // dbox difference(const dbox &b) const { return *this - b; } - // dbox setunion(const dbox &b) const { return *this | b; } - // dbox symmetric_difference(const dbox &b) const { return *this ^ b; } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit dbox(const YAML::Node &node) : val(vbox::make(node)) {} -#endif - - ostream &output(ostream &os) const { - if (!val) - return os << "dbox()"; - return val->output(os); - } - friend ostream &operator<<(ostream &os, const dbox &b) { - return b.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - if (!val) - return w << YAML::LocalTag("sio", "box-1.0.0"); - return val->output(w); - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const dbox &b) { - return b.output(w); - } -#endif -}; -} // namespace RegionCalculus - -namespace std { -template struct equal_to> { - bool operator()(const RegionCalculus::dbox &p, - const RegionCalculus::dbox &q) const { - return p.equal_to(q); - } -}; -template struct less> { - bool operator()(const RegionCalculus::dbox &p, - const RegionCalculus::dbox &q) const { - return p.less(q); - } -}; -template struct hash> { - size_t operator()(const RegionCalculus::dbox &p) const { return p.hash(); } -}; -} // namespace std - -namespace RegionCalculus { -template struct dregion { - unique_ptr> val; - - dregion() = default; - - dregion(const dregion &r) { - if (r.val) - val = r.val->copy(); - } - dregion(dregion &&r) = default; - dregion &operator=(const dregion &r) { - if (r.val) - val = r.val->copy(); - else - val = nullptr; - return *this; - } - dregion &operator=(dregion &&r) = default; - - template - dregion(const region &r) : val(make_unique1>(r)) {} - dregion(const vregion &r) : val(r.copy()) {} - dregion(const unique_ptr> &val) { - if (val) - this->val = val->copy(); - } - dregion(unique_ptr> &&val) : val(move(val)) {} - - explicit dregion(int d) : val(vregion::make(d)) {} - dregion(const dbox &b) : val(vregion::make(*b.val)) {} - dregion(const vector> &bs) { - vector>> rs; - for (const auto &b : bs) - rs.push_back(b.val->copy()); - val = vregion::make(move(rs)); - } - dregion(vector> &&bs) { - vector>> rs; - for (auto &b : bs) - rs.push_back(move(b.val)); - val = vregion::make(move(rs)); - } - operator vector>() const { - vector>> bs(*val); - vector> rs; - for (auto &b : bs) - rs.push_back(dbox(move(b))); - return rs; - } - template dregion(const dregion &p) { - if (p.val) - val = vregion::make(*p.val); - } - template operator region() const { - assert(valid()); - assert(rank() == D); - return dynamic_cast *>(val.get())->val; - } - - bool valid() const { return bool(val); } - void reset() { val.reset(); } - int rank() const { return val->rank(); } - - // Predicates - bool invariant() const { return val->invariant(); } - bool empty() const { return val->empty(); } - typedef typename vregion::prod_t prod_t; - prod_t size() const { return val->size(); } - - // Shift and scale operators - dregion operator>>(const dpoint &d) const { - return dregion(*val >> *d.val); - } - dregion operator<<(const dpoint &d) const { - return dregion(*val << *d.val); - } - dregion grow(const dpoint &dlo, const dpoint &dup) const { - return dregion(val->grow(*dlo, *dup)); - } - dregion grow(const dpoint &d) const { return dregion(val->grow(*d.val)); } - dregion grow(T n) const { return dregion(val->grow(n)); } - dregion shrink(const dpoint &dlo, const dpoint &dup) const { - return dregion(val->shrink(*dlo.val, *dup.val)); - } - dregion shrink(const dpoint &d) const { - return dregion(val->shrink(*d.val)); - } - dregion shrink(T n) const { return dregion(val->shrink(n)); } - - // Set operations - dbox bounding_box() const { return dbox(val->bounding_box()); } - dregion operator&(const dbox &b) const { return dregion(*val & *b.val); } - dregion operator&(const dregion &r) const { return dregion(*val & *r.val); } - dregion operator-(const dbox &b) const { return dregion(*val - *b.val); } - dregion operator-(const dregion &r) const { return dregion(*val - *r.val); } - dregion operator|(const dbox &b) const { return dregion(*val | *b.val); } - dregion operator|(const dregion &r) const { return dregion(*val | *r.val); } - dregion operator^(const dbox &b) const { return dregion(*val ^ *b.val); } - dregion operator^(const dregion &r) const { return dregion(*val ^ *r.val); } - - dregion &operator^=(const dregion &other) { return *this = *this ^ other; } - dregion &operator&=(const dregion &other) { return *this = *this & other; } - dregion &operator|=(const dregion &other) { return *this = *this | other; } - dregion &operator-=(const dregion &other) { return *this = *this - other; } - - dregion intersection(const dbox &b) const { return *this & b; } - dregion intersection(const dregion &r) const { return *this & r; } - dregion difference(const dbox &b) const { return *this - b; } - dregion difference(const dregion &r) const { return *this - r; } - dregion setunion(const dbox &b) const { return *this | b; } - dregion setunion(const dregion &r) const { return *this | r; } - dregion symmetric_difference(const dbox &b) const { return *this ^ b; } - dregion symmetric_difference(const dregion &r) const { return *this ^ r; } - - // Set comparison operators - bool contains(const dpoint &p) const { return val->contains(*p.val); } - bool isdisjoint(const dbox &b) const { return val->isdisjoint(*b.val); } - bool isdisjoint(const dregion &r) const { return val->isdisjoint(*r.val); } - - // Comparison operators - bool operator<=(const dregion &r) const { return *val <= *r.val; } - bool operator>=(const dregion &r) const { return *val >= *r.val; } - bool operator<(const dregion &r) const { return *val < *r.val; } - bool operator>(const dregion &r) const { return *val > *r.val; } - bool is_subset_of(const dregion &r) const { return *this <= r; } - bool is_superset_of(const dregion &r) const { return *this >= r; } - bool is_strict_subset_of(const dregion &r) const { return *this < r; } - bool is_strict_superset_of(const dregion &r) const { return *this > r; } - bool operator==(const dregion &r) const { return *val == *r.val; } - bool operator!=(const dregion &r) const { return *val != *r.val; } - - bool equal_to(const dregion &r) const { return val->equal_to(*r.val); } - bool less(const dregion &r) const { return val->less(*r.val); } - size_t hash() const { return val->hash(); } - - // I/O - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - explicit dregion(const YAML::Node &node) : val(vregion::make(node)) {} -#endif - - ostream &output(ostream &os) const { - if (!val) - return os << "dregion()"; - return val->output(os); - } - friend ostream &operator<<(ostream &os, const dregion &r) { - return r.output(os); - } - -#ifdef SIMULATIONIO_HAVE_ASDF_CXX - YAML::Emitter &output(YAML::Emitter &w) const { - if (!val) - return w << YAML::LocalTag("sio", "region-1.0.0"); - return val->output(w); - } - friend YAML::Emitter &operator<<(YAML::Emitter &w, const dregion &r) { - return r.output(w); - } -#endif -}; - -} // namespace RegionCalculus - -namespace std { -template struct equal_to> { - bool operator()(const RegionCalculus::dregion &p, - const RegionCalculus::dregion &q) const { - return p.equal_to(q); - } -}; -template struct less> { - bool operator()(const RegionCalculus::dregion &p, - const RegionCalculus::dregion &q) const { - return p.less(q); - } -}; -template struct hash> { - size_t operator()(const RegionCalculus::dregion &p) const { - return p.hash(); - } -}; -} // namespace std - -namespace RegionCalculus { -typedef RegionCalculus::dpoint point_t; -typedef RegionCalculus::dbox box_t; -typedef RegionCalculus::dregion region_t; - -} // namespace RegionCalculus - -#endif // REGIONCALCULUS_HPP From 092569faf6b1de41c6362edf57e9eed585338506 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 12 Jul 2021 10:22:49 -0400 Subject: [PATCH 64/68] Use `#pragma once` --- include/openPMD/regions/Box.hpp | 5 +---- include/openPMD/regions/Helpers.hpp | 5 +---- include/openPMD/regions/NDBox.hpp | 5 +---- include/openPMD/regions/NDPoint.hpp | 5 +---- include/openPMD/regions/NDRegion.hpp | 5 +---- include/openPMD/regions/Point.hpp | 5 +---- include/openPMD/regions/Region.hpp | 5 +---- include/openPMD/regions/Regions.hpp | 5 +---- 8 files changed, 8 insertions(+), 32 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index c3cb21fee4..d97b06eb5d 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_BOX_HPP -#define REGIONS_BOX_HPP +#pragma once #include "Helpers.hpp" #include "Point.hpp" @@ -433,5 +432,3 @@ template struct less> { }; } // namespace std - -#endif // #ifndef REGIONS_BOX_HPP diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 80a4ff6225..520b35f82b 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_HELPERS_HPP -#define REGIONS_HELPERS_HPP +#pragma once #include #include @@ -218,5 +217,3 @@ R mapreduce(const F &f, const Op &op, const C &c) { } // namespace Regions } // namespace openPMD - -#endif // #ifndef REGIONS_HELPERS_HPP diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp index e87a80e4df..ba0d2f7915 100644 --- a/include/openPMD/regions/NDBox.hpp +++ b/include/openPMD/regions/NDBox.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_NDBOX_HPP -#define REGIONS_NDBOX_HPP +#pragma once #include "Box.hpp" #include "Helpers.hpp" @@ -526,5 +525,3 @@ template struct less> { }; } // namespace std - -#endif // #ifndef REGIONS_NDBOX_HPP diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 9b7cb39512..ccef3c7638 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_NDPOINT_HPP -#define REGIONS_NDPOINT_HPP +#pragma once #include "Helpers.hpp" #include "Point.hpp" @@ -1237,5 +1236,3 @@ template struct less> { }; } // namespace std - -#endif // #ifndef REGIONS_NDPOINT_HPP diff --git a/include/openPMD/regions/NDRegion.hpp b/include/openPMD/regions/NDRegion.hpp index 48cc78c4fb..7f5f3f445b 100644 --- a/include/openPMD/regions/NDRegion.hpp +++ b/include/openPMD/regions/NDRegion.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_NDREGION_HPP -#define REGIONS_NDREGION_HPP +#pragma once #include "Helpers.hpp" #include "NDBox.hpp" @@ -663,5 +662,3 @@ template struct less> { }; } // namespace std - -#endif // #ifndef REGIONS_NDREGION_HPP diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index f996c65d51..0a5b9257a8 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_POINT_HPP -#define REGIONS_POINT_HPP +#pragma once #include "Helpers.hpp" @@ -655,5 +654,3 @@ struct less> { }; } // namespace std - -#endif // #ifndef REGIONS_POINT_HPP diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index 45280001d8..f2292ea1b3 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_REGION_HPP -#define REGIONS_REGION_HPP +#pragma once #include "Box.hpp" #include "Helpers.hpp" @@ -1296,5 +1295,3 @@ struct less> { }; } // namespace std - -#endif // #ifndef REGIONS_REGION_HPP diff --git a/include/openPMD/regions/Regions.hpp b/include/openPMD/regions/Regions.hpp index 2d08a8158b..fe4c9bfb2c 100644 --- a/include/openPMD/regions/Regions.hpp +++ b/include/openPMD/regions/Regions.hpp @@ -1,5 +1,4 @@ -#ifndef REGIONS_REGIONS_HPP -#define REGIONS_REGIONS_HPP +#pragma once #include "Box.hpp" #include "Point.hpp" @@ -8,5 +7,3 @@ #include "NDBox.hpp" #include "NDPoint.hpp" #include "NDRegion.hpp" - -#endif // #ifndef REGIONS_REGIONS_HPP From d617383193cd885193b3afe8e41eb102e715cdaa Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 12 Jul 2021 10:30:28 -0400 Subject: [PATCH 65/68] Abort when openPMD_USE_REGIONS is requested, but C++17 is not supported --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 244f4e22b5..a7fb363e92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -370,7 +370,7 @@ if(openPMD_USE_REGIONS STREQUAL AUTO) if ("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES) set(openPMD_HAVE_REGIONS TRUE) else() - set(openPMD_HAVE_REGIONS FALSE) + message(FATAL_ERROR "openPMD_USE_REGIONS requested, but C++17 not supported by platform") endif() elseif(openPMD_USE_REGIONS) set(openPMD_HAVE_REGIONS TRUE) From 36551f2c61c39e0dd29e4b5944afec19f73f5a8b Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 12 Jul 2021 10:41:51 -0400 Subject: [PATCH 66/68] Use `using` instead of `typedef` --- include/openPMD/regions/Box.hpp | 8 ++++---- include/openPMD/regions/Helpers.hpp | 24 ++++++++++++------------ include/openPMD/regions/NDBox.hpp | 8 ++++---- include/openPMD/regions/NDPoint.hpp | 8 ++++---- include/openPMD/regions/NDRegion.hpp | 8 ++++---- include/openPMD/regions/Point.hpp | 4 ++-- include/openPMD/regions/Region.hpp | 20 ++++++++++---------- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/include/openPMD/regions/Box.hpp b/include/openPMD/regions/Box.hpp index d97b06eb5d..bcc7e762c6 100644 --- a/include/openPMD/regions/Box.hpp +++ b/include/openPMD/regions/Box.hpp @@ -33,8 +33,8 @@ template class Box { public: constexpr static std::size_t D = 0; - typedef typename Point::value_type value_type; - typedef typename Point::size_type size_type; + using value_type = typename Point::value_type; + using size_type = typename Point::size_type; /** Create empty box */ @@ -165,8 +165,8 @@ template class Box { Point lo, hi; public: - typedef typename Point::value_type value_type; - typedef typename Point::size_type size_type; + using value_type = typename Point::value_type; + using size_type = typename Point::size_type; /** Create empty box */ diff --git a/include/openPMD/regions/Helpers.hpp b/include/openPMD/regions/Helpers.hpp index 520b35f82b..84541e91db 100644 --- a/include/openPMD/regions/Helpers.hpp +++ b/include/openPMD/regions/Helpers.hpp @@ -74,9 +74,9 @@ namespace impl { template struct tuple_eq { template constexpr bool operator()(const Tuple1 &x, const Tuple2 &y) const { - typedef std::tuple_element_t T1; - typedef std::tuple_element_t T2; - typedef std::common_type_t T; + using T1 = std::tuple_element_t; + using T2 = std::tuple_element_t; + using T = std::common_type_t; const std::equal_to eq; return tuple_eq()(x, y) && eq(std::get(x), std::get(y)); @@ -92,9 +92,9 @@ template <> struct tuple_eq<0> { template struct tuple_cmp { template constexpr int operator()(const Tuple1 &x, const Tuple2 &y) const { - typedef std::tuple_element_t T1; - typedef std::tuple_element_t T2; - typedef std::common_type_t T; + using T1 = std::tuple_element_t; + using T2 = std::tuple_element_t; + using T = std::common_type_t; const int cmp = tuple_cmp()(x, y); if (cmp != 0) return cmp; @@ -142,9 +142,9 @@ template bool vector_eq(const Vector1 &x, const Vector2 &y) { if (x.size() != y.size()) return false; - typedef typename Vector1::value_type T1; - typedef typename Vector2::value_type T2; - typedef std::common_type_t T; + using T1 = typename Vector1::value_type; + using T2 = typename Vector2::value_type; + using T = std::common_type_t; const std::equal_to eq; for (std::size_t n = 0; n < x.size(); ++n) if (!eq(x[n], y[n])) @@ -154,9 +154,9 @@ bool vector_eq(const Vector1 &x, const Vector2 &y) { template bool vector_lt(const Vector1 &x, const Vector2 &y) { - typedef typename Vector1::value_type T1; - typedef typename Vector2::value_type T2; - typedef std::common_type_t T; + using T1 = typename Vector1::value_type; + using T2 = typename Vector2::value_type; + using T = std::common_type_t; const std::less lt; using std::min; for (std::size_t n = 0; n < min(x.size(), y.size()); ++n) diff --git a/include/openPMD/regions/NDBox.hpp b/include/openPMD/regions/NDBox.hpp index ba0d2f7915..d4e2883baf 100644 --- a/include/openPMD/regions/NDBox.hpp +++ b/include/openPMD/regions/NDBox.hpp @@ -15,8 +15,8 @@ namespace detail { template class VBox { public: - typedef T value_type; - typedef typename VPoint::size_type size_type; + using value_type = T; + using size_type = typename VPoint::size_type; virtual std::unique_ptr copy() const = 0; @@ -262,10 +262,10 @@ template class NDBox { public: /** Component type */ - typedef typename VBox::value_type value_type; + using value_type = typename VBox::value_type; /** Return type of Box::size() */ - typedef typename VBox::size_type size_type; + using size_type = typename VBox::size_type; /** Create an invalid Box */ diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index ccef3c7638..5cc3726d20 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -30,8 +30,8 @@ namespace detail { template class VPoint { public: - typedef T value_type; - typedef std::ptrdiff_t size_type; + using value_type = T; + using size_type = std::ptrdiff_t; virtual std::unique_ptr copy() const = 0; @@ -652,10 +652,10 @@ template class NDPoint { public: /** Component type */ - typedef typename VPoint::value_type value_type; + using value_type = typename VPoint::value_type; /** Return type of Point::size() */ - typedef typename VPoint::size_type size_type; + using size_type = typename VPoint::size_type; /** Create an invalid Point */ diff --git a/include/openPMD/regions/NDRegion.hpp b/include/openPMD/regions/NDRegion.hpp index 7f5f3f445b..78b963d793 100644 --- a/include/openPMD/regions/NDRegion.hpp +++ b/include/openPMD/regions/NDRegion.hpp @@ -16,8 +16,8 @@ namespace detail { template class VRegion { public: - typedef T value_type; - typedef typename VPoint::size_type size_type; + using value_type = T; + using size_type = typename VPoint::size_type; virtual std::unique_ptr copy() const = 0; @@ -345,10 +345,10 @@ template class NDRegion { public: /** Component type */ - typedef typename VRegion::value_type value_type; + using value_type = typename VRegion::value_type; /** Return type of Region::size() */ - typedef typename VRegion::size_type size_type; + using size_type = typename VRegion::size_type; /** Create an invalid Region */ diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index 0a5b9257a8..387b546c9f 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -36,10 +36,10 @@ template class Point { public: /** Component type */ - typedef T value_type; + using value_type = T; /** Return type of Point::size() */ - typedef std::ptrdiff_t size_type; + using size_type = std::ptrdiff_t; /** Create a value-initialized Point * diff --git a/include/openPMD/regions/Region.hpp b/include/openPMD/regions/Region.hpp index f2292ea1b3..a7ef816308 100644 --- a/include/openPMD/regions/Region.hpp +++ b/include/openPMD/regions/Region.hpp @@ -45,8 +45,8 @@ template class Region { private: public: - typedef typename Point::value_type value_type; - typedef typename Point::size_type size_type; + using value_type = typename Point::value_type; + using size_type = typename Point::size_type; /** Invariant */ @@ -212,16 +212,16 @@ template class Region { constexpr static std::size_t D = 1; private: - typedef Region Subregion; // This is essentially a bool - typedef std::vector Subregions; + using Subregion = Region; // This is essentially a bool + using Subregions = std::vector; Subregions subregions; friend class std::equal_to>; friend class std::less>; public: - typedef typename Point::value_type value_type; - typedef typename Point::size_type size_type; + using value_type = typename Point::value_type; + using size_type = typename Point::size_type; /** Invariant */ @@ -664,16 +664,16 @@ template class Region { //////////////////////////////////////////////////////////////////////////////// template class Region { - typedef Region Subregion; - typedef std::vector> Subregions; + using Subregion = Region; + using Subregions = std::vector>; Subregions subregions; friend class std::equal_to>; friend class std::less>; public: - typedef typename Point::value_type value_type; - typedef typename Point::size_type size_type; + using value_type = typename Point::value_type; + using size_type = typename Point::size_type; /** Invariant */ From 23f2133fa9b1cc5116aacc8d193cf711aa2dcd83 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 12 Jul 2021 10:52:16 -0400 Subject: [PATCH 67/68] cmake: Disable Regions by default --- .github/workflows/unix.yml | 18 +++++++++--------- CMakeLists.txt | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index c1f26066eb..b7c0ea895f 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -25,7 +25,7 @@ jobs: mkdir build && cd build ../share/openPMD/download_samples.sh && chmod u-w samples/git-sample/*.h5 - cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_INVASIVE_TESTS=ON + cmake -S .. -B . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-system-headers=0" -DopenPMD_USE_REGIONS=ON -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build . --parallel 2 2> clang-tidy.log cat clang-tidy.log if [[ $(wc -m Date: Tue, 13 Jul 2021 16:22:25 +0200 Subject: [PATCH 68/68] Don't use std::ptrdiff type --- include/openPMD/regions/NDPoint.hpp | 2 +- include/openPMD/regions/Point.hpp | 2 +- test/BoxTest.cpp | 2 +- test/PointTest.cpp | 16 ++++++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/openPMD/regions/NDPoint.hpp b/include/openPMD/regions/NDPoint.hpp index 5cc3726d20..b85bb85009 100644 --- a/include/openPMD/regions/NDPoint.hpp +++ b/include/openPMD/regions/NDPoint.hpp @@ -31,7 +31,7 @@ namespace detail { template class VPoint { public: using value_type = T; - using size_type = std::ptrdiff_t; + using size_type = std::size_t; virtual std::unique_ptr copy() const = 0; diff --git a/include/openPMD/regions/Point.hpp b/include/openPMD/regions/Point.hpp index 387b546c9f..70e2dc449d 100644 --- a/include/openPMD/regions/Point.hpp +++ b/include/openPMD/regions/Point.hpp @@ -39,7 +39,7 @@ template class Point { using value_type = T; /** Return type of Point::size() */ - using size_type = std::ptrdiff_t; + using size_type = std::size_t; /** Create a value-initialized Point * diff --git a/test/BoxTest.cpp b/test/BoxTest.cpp index 3bb04bc7ed..c739a12fa7 100644 --- a/test/BoxTest.cpp +++ b/test/BoxTest.cpp @@ -47,7 +47,7 @@ template void test_Box(const B &box) { for (int iter = 0; iter < 100; ++iter) { B N(box); - CHECK(N.ndims() == std::ptrdiff_t(D)); + CHECK(N.ndims() == D); CHECK(N.empty()); for (std::size_t d = 0; d < D; ++d) CHECK(N.lower()[d] >= N.upper()[d]); diff --git a/test/PointTest.cpp b/test/PointTest.cpp index 558ea64673..28bb697229 100644 --- a/test/PointTest.cpp +++ b/test/PointTest.cpp @@ -56,10 +56,10 @@ template void test_Point_bool(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n = p; - CHECK(n.ndims() == std::ptrdiff_t(D)); + CHECK(n.ndims() == D); for (std::size_t d = 0; d < D; ++d) CHECK(n[d] == 0); - CHECK(n.size() == std::ptrdiff_t(D)); + CHECK(n.size() == D); const P x = randp(); const P y = randp(); @@ -188,7 +188,7 @@ template void test_Point_int(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n(p); - CHECK(n.size() == std::ptrdiff_t(D)); + CHECK(n.size() == D); for (std::size_t d = 0; d < D; ++d) CHECK(n[d] == 0); @@ -214,7 +214,7 @@ template void test_Point_int(const P &p) { sum(x + y)); CHECK(sum(n) == 0); - CHECK(sum(n + 1) == std::ptrdiff_t(D)); + CHECK(sum(n + 1) == T(D)); CHECK(product(n) == (D == 0 ? 1 : 0)); CHECK(product(n + 1) == 1); CHECK(min_element(n) == (D == 0 ? std::numeric_limits::max() : 0)); @@ -349,7 +349,7 @@ template void test_Point_float(const P &p) { for (int iter = 0; iter < 100; ++iter) { P n(p); - CHECK(n.size() == std::ptrdiff_t(D)); + CHECK(n.size() == D); for (std::size_t d = 0; d < D; ++d) CHECK(n[d] == 0); @@ -376,16 +376,16 @@ template void test_Point_float(const P &p) { for (std::size_t d = 0; d < D; ++d) { const auto a1 = x[d]; const auto x1 = x.erase(d); - CHECK(x1.ndims() == std::ptrdiff_t(D) - 1); + CHECK(x1.ndims() == D - 1); const auto x2 = x1.insert(d, a1); - CHECK(x2.ndims() == std::ptrdiff_t(D)); + CHECK(x2.ndims() == D); CHECK(eq_helper(x2, x)); } } // insert-remove is no-op for (std::size_t d = 0; d <= D; ++d) { const auto x1 = x.insert(d, a); - CHECK(x1.ndims() == std::ptrdiff_t(D) + 1); + CHECK(x1.ndims() == D + 1); CHECK(x1[d] == a); CHECK(eq(x1.erase(d), x)); }