From 08a09235a41509bbb2d42545ebfc3283052e99a3 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sun, 25 Nov 2018 22:35:26 +0100 Subject: [PATCH] changed axis::option_type to compile-time option, replaced fancy histogram iterator with indexed range adaptor, cleanup of various headers --- CMakeLists.txt | 1 + examples/getting_started_listing_01.cpp | 33 ++- examples/guide_access_bin_counts.cpp | 50 +++-- examples/guide_axis_with_uoflow_off.cpp | 4 +- examples/guide_histogram_streaming.cpp | 23 +- include/boost/histogram.hpp | 4 + include/boost/histogram/adaptive_storage.hpp | 35 ++- include/boost/histogram/algorithm/reduce.hpp | 6 +- include/boost/histogram/algorithm/sum.hpp | 12 +- include/boost/histogram/axis/base.hpp | 31 +-- include/boost/histogram/axis/category.hpp | 23 +- include/boost/histogram/axis/circular.hpp | 19 +- include/boost/histogram/axis/integer.hpp | 26 +-- include/boost/histogram/axis/iterator.hpp | 10 +- .../histogram/axis/ostream_operators.hpp | 36 ++- include/boost/histogram/axis/regular.hpp | 22 +- include/boost/histogram/axis/traits.hpp | 32 ++- include/boost/histogram/axis/variable.hpp | 34 +-- include/boost/histogram/detail/axes.hpp | 1 - .../boost/histogram/detail/index_cache.hpp | 78 ------- include/boost/histogram/detail/meta.hpp | 30 +++ include/boost/histogram/histogram.hpp | 24 +- include/boost/histogram/histogram_fwd.hpp | 48 +++- include/boost/histogram/indexed.hpp | 106 +++++++++ include/boost/histogram/iterator.hpp | 75 ------- include/boost/histogram/ostream_operators.hpp | 2 - include/boost/histogram/serialization.hpp | 27 ++- include/boost/histogram/storage_adaptor.hpp | 211 +++++++++++------- test/algorithm_sum_test.cpp | 16 ++ test/axis_category_test.cpp | 2 +- test/axis_regular_test.cpp | 6 +- test/axis_variant_test.cpp | 30 +-- test/histogram_dynamic_test.cpp | 5 + test/histogram_mixed_test.cpp | 3 + test/histogram_serialization_test.cpp | 5 +- test/histogram_test.cpp | 132 ++--------- test/indexed_test.cpp | 95 ++++++++ test/meta_test.cpp | 8 +- test/storage_adaptor_test.cpp | 23 +- test/utility_histogram.hpp | 3 - 40 files changed, 720 insertions(+), 611 deletions(-) delete mode 100644 include/boost/histogram/detail/index_cache.hpp create mode 100644 include/boost/histogram/indexed.hpp delete mode 100644 include/boost/histogram/iterator.hpp create mode 100644 test/indexed_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c70f736f..9366497d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,7 @@ compiled_test(test/detail_test.cpp) compiled_test(test/histogram_dynamic_test.cpp) compiled_test(test/histogram_mixed_test.cpp) compiled_test(test/histogram_test.cpp) +compiled_test(test/indexed_test.cpp) compiled_test(test/internal_accumulators_test.cpp) compiled_test(test/meta_test.cpp) compiled_test(test/storage_adaptor_test.cpp) diff --git a/examples/getting_started_listing_01.cpp b/examples/getting_started_listing_01.cpp index a9c0e4bc..63f82e92 100644 --- a/examples/getting_started_listing_01.cpp +++ b/examples/getting_started_listing_01.cpp @@ -6,11 +6,11 @@ //[ getting_started_listing_01 -#include #include +#include +#include #include #include -#include int main() { using namespace boost::histogram; @@ -63,25 +63,24 @@ int main() { */ std::ostringstream os; os.setf(std::ios_base::fixed); - for (auto it = h.begin(); it != h.end(); ++it) { - const auto bin = it.bin(0); - os << "bin " << std::setw(2) << it.idx(0) << " [" << std::setprecision(1) - << std::setw(4) << bin.lower() << ", " << std::setw(4) - << bin.upper() << "): " << *it << "\n"; + for (auto b : indexed(h)) { + const auto idx = b.first[0]; + const auto interval = h.axis()[idx]; + os << "bin " << std::setw(2) << idx << " [" << std::setprecision(1) << std::setw(4) + << interval.lower() << ", " << std::setw(4) << interval.upper() + << "): " << b.second << "\n"; } std::cout << os.str() << std::endl; - assert(os.str() == - "bin 0 [-1.0, -0.5): 1.0\n" - "bin 1 [-0.5, -0.0): 1.0\n" - "bin 2 [-0.0, 0.5): 2.0\n" - "bin 3 [ 0.5, 1.0): 0.0\n" - "bin 4 [ 1.0, 1.5): 1.0\n" - "bin 5 [ 1.5, 2.0): 1.0\n" - "bin 6 [ 2.0, inf): 2.0\n" - "bin -1 [-inf, -1.0): 1.0\n" - ); + assert(os.str() == "bin 0 [-1.0, -0.5): 1.0\n" + "bin 1 [-0.5, -0.0): 1.0\n" + "bin 2 [-0.0, 0.5): 2.0\n" + "bin 3 [ 0.5, 1.0): 0.0\n" + "bin 4 [ 1.0, 1.5): 1.0\n" + "bin 5 [ 1.5, 2.0): 1.0\n" + "bin 6 [ 2.0, inf): 2.0\n" + "bin -1 [-inf, -1.0): 1.0\n"); // note how under- and overflow bins appear at the end } diff --git a/examples/guide_access_bin_counts.cpp b/examples/guide_access_bin_counts.cpp index c2db0d7e..1bde59ba 100644 --- a/examples/guide_access_bin_counts.cpp +++ b/examples/guide_access_bin_counts.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include // for std::accumulate namespace bh = boost::histogram; @@ -33,34 +33,38 @@ int main() { auto idx = std::make_tuple(0, 1); assert(h.at(idx) == 2); - // histogram also provides bin iterators + // histogram has bin iterators which iterate over all bin values including + // underflow/overflow; it works with STL algorithms auto sum = std::accumulate(h.begin(), h.end(), 0.0); assert(sum == 10); - // bin iterators are fancy iterators with extra methods - // (note: iteration order is an implementation detail, don't rely on it) + // often you need the multi-dimensional bin index in addition to the bin value, which + // indexded(...) provides; it creates a range object, which provides a pair of the index + // and the value (note: iteration order is an implementation detail, don't rely on it) std::ostringstream os; - for (auto it = h.begin(), end = h.end(); it != end; ++it) { - os << std::setw(2) << it.idx(0) << " " << std::setw(2) << it.idx(1) << ": " << *it; + for (auto b : indexed(h)) { + os << std::setw(2) << b.first[0] << " " << std::setw(2) << b.first[1] << ": " + << b.second << "\n"; } - assert(os.str() == - " 0 0: 1" - " 1 0: 3" - " 2 0: 0" - "-1 0: 0" - " 0 1: 2" - " 1 1: 4" - " 2 1: 0" - "-1 1: 0" - " 0 2: 0" - " 1 2: 0" - " 2 2: 0" - "-1 2: 0" - " 0 -1: 0" - " 1 -1: 0" - " 2 -1: 0" - "-1 -1: 0"); + std::cout << os.str() << std::flush; + + assert(os.str() == " 0 0: 1\n" + " 1 0: 3\n" + " 2 0: 0\n" + "-1 0: 0\n" + " 0 1: 2\n" + " 1 1: 4\n" + " 2 1: 0\n" + "-1 1: 0\n" + " 0 2: 0\n" + " 1 2: 0\n" + " 2 2: 0\n" + "-1 2: 0\n" + " 0 -1: 0\n" + " 1 -1: 0\n" + " 2 -1: 0\n" + "-1 -1: 0\n"); } //] diff --git a/examples/guide_axis_with_uoflow_off.cpp b/examples/guide_axis_with_uoflow_off.cpp index 9a5860c5..fed586c3 100644 --- a/examples/guide_axis_with_uoflow_off.cpp +++ b/examples/guide_axis_with_uoflow_off.cpp @@ -7,14 +7,14 @@ //[ guide_axis_with_uoflow_off #include +#include namespace bh = boost::histogram; int main() { // create a 1d-histogram for dice throws with integer values from 1 to 6 auto h = bh::make_histogram( - bh::axis::integer<>(1, 7, "eyes", bh::axis::option_type::none) - ); + bh::axis::integer(1, 7, "eyes")); // do something with h } diff --git a/examples/guide_histogram_streaming.cpp b/examples/guide_histogram_streaming.cpp index 6846f343..c101c1fa 100644 --- a/examples/guide_histogram_streaming.cpp +++ b/examples/guide_histogram_streaming.cpp @@ -7,9 +7,12 @@ //[ guide_histogram_streaming #include +#include #include #include +#include #include +#include namespace bh = boost::histogram; @@ -20,7 +23,8 @@ int main() { axis::regular<>(2, -1, 1), axis::regular>(2, 1, 10, "axis 1"), axis::circular(4, 0.1, 1.0), // axis without metadata - axis::variable<>({-1, 0, 1}, "axis 3", axis::option_type::none), + axis::variable, std::string, axis::option_type::none>( + {-1, 0, 1}, "axis 3"), axis::category<>({2, 1, 3}, "axis 4"), axis::integer<>(-1, 1, "axis 5")); std::ostringstream os; @@ -28,15 +32,14 @@ int main() { std::cout << os.str() << std::endl; - assert(os.str() == - "histogram(\n" - " regular(2, -1, 1, options=underflow_and_overflow),\n" - " regular_log(2, 1, 10, metadata=\"axis 1\", options=underflow_and_overflow),\n" - " circular(4, 0.1, 1.1, options=overflow),\n" - " variable(-1, 0, 1, metadata=\"axis 3\", options=none),\n" - " category(2, 1, 3, metadata=\"axis 4\", options=overflow),\n" - " integer(-1, 1, metadata=\"axis 5\", options=underflow_and_overflow),\n" - ")"); + assert(os.str() == "histogram(\n" + " regular(2, -1, 1, options=uoflow),\n" + " regular_log(2, 1, 10, metadata=\"axis 1\", options=uoflow),\n" + " circular(4, 0.1, 1.1, options=overflow),\n" + " variable(-1, 0, 1, metadata=\"axis 3\", options=none),\n" + " category(2, 1, 3, metadata=\"axis 4\", options=overflow),\n" + " integer(-1, 1, metadata=\"axis 5\", options=uoflow),\n" + ")"); } //] diff --git a/include/boost/histogram.hpp b/include/boost/histogram.hpp index cf36de8b..c18a41d0 100644 --- a/include/boost/histogram.hpp +++ b/include/boost/histogram.hpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,8 +22,10 @@ #include #include #include +#include #include #include +#include #include #include diff --git a/include/boost/histogram/adaptive_storage.hpp b/include/boost/histogram/adaptive_storage.hpp index a455d332..8aff669e 100644 --- a/include/boost/histogram/adaptive_storage.hpp +++ b/include/boost/histogram/adaptive_storage.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #if defined BOOST_CLANG #pragma clang diagnostic push @@ -102,7 +103,6 @@ struct adaptive_storage { struct storage_tag {}; using allocator_type = Allocator; using value_type = double; - using const_reference = double; using mp_int = boost::multiprecision::number; + class const_iterator + : public boost::iterator_facade { + public: + const_iterator(const adaptive_storage& parent, std::size_t idx) noexcept + : parent_(parent), idx_(idx) {} + + protected: + void increment() noexcept { ++idx_; } + void decrement() noexcept { --idx_; } + void advance(std::ptrdiff_t n) noexcept { idx_ += n; } + std::ptrdiff_t distance_to(const_iterator rhs) const noexcept { + return rhs.idx_ - idx_; + } + bool equal(const_iterator rhs) const noexcept { + return &parent_ == &rhs.parent_ && idx_ == rhs.idx_; + } + value_type dereference() const { return parent_[idx_]; } + + friend class ::boost::iterator_core_access; + + private: + const adaptive_storage& parent_; + std::size_t idx_; + }; + template static constexpr char type_index() { return static_cast(mp11::mp_find::value); @@ -265,12 +291,12 @@ struct adaptive_storage { } template - void add(std::size_t i, const T& x) { + void add(std::size_t i, T&& x) { BOOST_ASSERT(i < size()); apply(adder(), buffer, i, x); } - const_reference operator[](std::size_t i) const { return apply(getter(), buffer, i); } + double operator[](std::size_t i) const { return apply(getter(), buffer, i); } bool operator==(const adaptive_storage& o) const { if (size() != o.size()) return false; @@ -316,6 +342,9 @@ struct adaptive_storage { return *this; } + const_iterator begin() const { return {*this, 0}; } + const_iterator end() const { return {*this, size()}; } + // used by unit tests, not part of generic storage interface template adaptive_storage(std::size_t s, const T* p, const allocator_type& a = allocator_type()) diff --git a/include/boost/histogram/algorithm/reduce.hpp b/include/boost/histogram/algorithm/reduce.hpp index 9d45128e..fe02441d 100644 --- a/include/boost/histogram/algorithm/reduce.hpp +++ b/include/boost/histogram/algorithm/reduce.hpp @@ -91,11 +91,15 @@ histogram reduce(const histogram& h, const C& c) { stride[0] *= n; auto set_flow = [im_iter](int i, const auto& a) { switch (axis::traits::options(a)) { + case axis::option_type::underflow: + im_iter->overflow[i] = -1; + im_iter->underflow[i] = a.size(); + break; case axis::option_type::overflow: im_iter->overflow[i] = a.size(); im_iter->underflow[i] = -1; break; - case axis::option_type::underflow_and_overflow: + case axis::option_type::uoflow: im_iter->overflow[i] = a.size(); im_iter->underflow[i] = a.size() + 1; break; diff --git a/include/boost/histogram/algorithm/sum.hpp b/include/boost/histogram/algorithm/sum.hpp index 74a70a1f..e290d454 100644 --- a/include/boost/histogram/algorithm/sum.hpp +++ b/include/boost/histogram/algorithm/sum.hpp @@ -15,11 +15,13 @@ namespace boost { namespace histogram { namespace algorithm { -template < - typename A, typename S, typename T = typename histogram::value_type, - typename ReturnType = std::conditional_t::value, double, T>, - typename InternalSum = - std::conditional_t::value, accumulators::sum, T>> +template ::value_type>::value, double, + typename histogram::value_type>, + typename InternalSum = std::conditional_t< + std::is_arithmetic::value_type>::value, + accumulators::sum, typename histogram::value_type>> ReturnType sum(const histogram& h) { InternalSum sum; for (auto x : h) sum += x; diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index e1854cd3..3aa6128d 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,15 +20,15 @@ namespace histogram { namespace axis { /// Base class for all axes -template +template class base { +public: using metadata_type = MetaData; -public: /// Returns the number of bins, without extra bins. unsigned size() const noexcept { return size_meta_.first(); } /// Returns the options. - option_type options() const noexcept { return opt_; } + constexpr option_type options() const noexcept { return Options; } /// Returns the metadata. metadata_type& metadata() noexcept { return size_meta_.second(); } /// Returns the metadata (const version). @@ -37,36 +38,25 @@ public: { using std::swap; swap(a.size_meta_, b.size_meta_); - swap(a.opt_, b.opt_); } template void serialize(Archive&, unsigned); protected: - base(unsigned n, metadata_type m, option_type opt) - : size_meta_(n, std::move(m)), opt_(opt) { + base(unsigned n, metadata_type m) : size_meta_(n, std::move(m)) { if (size() == 0) { throw std::invalid_argument("bins > 0 required"); } - const auto max_index = - static_cast(std::numeric_limits::max() - static_cast(opt_)); + const auto max_index = static_cast(std::numeric_limits::max() - + (options() & option_type::underflow) - + (options() & option_type::overflow)); if (size() > max_index) throw std::invalid_argument(detail::cat("bins <= ", max_index, " required")); } - base() : size_meta_(0), opt_(option_type::none) {} - base(const base&) = default; - base& operator=(const base&) = default; - base(base&& rhs) : size_meta_(std::move(rhs.size_meta_)), opt_(rhs.opt_) {} - base& operator=(base&& rhs) { - if (this != &rhs) { - size_meta_ = std::move(rhs.size_meta_); - opt_ = rhs.opt_; - } - return *this; - } + base() : size_meta_(0) {} bool operator==(const base& rhs) const noexcept { - return size() == rhs.size() && opt_ == rhs.opt_ && + return size() == rhs.size() && detail::static_if>( [&rhs](const auto& m) { return m == rhs.metadata(); }, [](const auto&) { return true; }, metadata()); @@ -74,7 +64,6 @@ protected: private: detail::compressed_pair size_meta_; - option_type opt_; }; // namespace axis } // namespace axis diff --git a/include/boost/histogram/axis/category.hpp b/include/boost/histogram/axis/category.hpp index eae0024d..dfe7da35 100644 --- a/include/boost/histogram/axis/category.hpp +++ b/include/boost/histogram/axis/category.hpp @@ -32,10 +32,12 @@ namespace axis { * Binning is a O(n) operation for n values in the worst case and O(1) in * the best case. The value types must be equal-comparable. */ -template -class category : public base, - public iterator_mixin> { - using base_type = base; +template +class category : public base, + public iterator_mixin> { + static_assert(!(Options & option_type::underflow), + "category axis cannot have underflow"); + using base_type = base; using metadata_type = MetaData; using value_type = T; using allocator_type = Allocator; @@ -51,8 +53,8 @@ public: */ template > category(It begin, It end, metadata_type m = metadata_type(), - option_type o = option_type::overflow, allocator_type a = allocator_type()) - : base_type(std::distance(begin, end), std::move(m), o), x_(nullptr, std::move(a)) { + allocator_type a = allocator_type()) + : base_type(std::distance(begin, end), std::move(m)), x_(nullptr, std::move(a)) { x_.first() = detail::create_buffer_from_iter(x_.second(), base_type::size(), begin); } @@ -65,9 +67,8 @@ public: */ template > category(const C& iterable, metadata_type m = metadata_type(), - option_type o = option_type::overflow, allocator_type a = allocator_type()) - : category(std::begin(iterable), std::end(iterable), std::move(m), o, - std::move(a)) {} + allocator_type a = allocator_type()) + : category(std::begin(iterable), std::end(iterable), std::move(m), std::move(a)) {} /** Construct axis from an initializer list of unique values. * @@ -78,8 +79,8 @@ public: */ template category(std::initializer_list l, metadata_type m = metadata_type(), - option_type o = option_type::overflow, allocator_type a = allocator_type()) - : category(l.begin(), l.end(), std::move(m), o, std::move(a)) {} + allocator_type a = allocator_type()) + : category(l.begin(), l.end(), std::move(m), std::move(a)) {} category() : x_(nullptr) {} diff --git a/include/boost/histogram/axis/circular.hpp b/include/boost/histogram/axis/circular.hpp index 775e50f1..ef5b0f84 100644 --- a/include/boost/histogram/axis/circular.hpp +++ b/include/boost/histogram/axis/circular.hpp @@ -31,10 +31,12 @@ constexpr double two_pi = 6.283185307179586; * It has no underflow bin and the overflow bin merely counts special * values like NaN and infinity. Binning is a O(1) operation. */ -template -class circular : public base, - public iterator_mixin> { - using base_type = base; +template +class circular : public base, + public iterator_mixin> { + static_assert(!(Options & option_type::underflow), + "circular axis cannot have underflow"); + using base_type = base; using value_type = RealType; using metadata_type = MetaData; @@ -48,18 +50,15 @@ public: * \param options extra bin options. */ circular(unsigned n, RealType phase = 0, RealType perimeter = two_pi, - MetaData m = MetaData(), option_type o = option_type::overflow) - : base_type(n, std::move(m), - o == option_type::underflow_and_overflow ? option_type::overflow : o) - , phase_(phase) - , delta_(perimeter / n) { + MetaData m = MetaData()) + : base_type(n, std::move(m)), phase_(phase), delta_(perimeter / n) { if (!std::isfinite(phase) || !(perimeter > 0)) throw std::invalid_argument("invalid phase or perimeter"); } /// Constructor used by algorithm::reduce to shrink and rebin (not for users). circular(const circular& src, unsigned begin, unsigned end, unsigned merge) - : base_type(src.size() / merge, src.metadata(), src.options()) + : base_type(src.size() / merge, src.metadata()) , phase_(src.phase_) , delta_(src.delta_ * merge) { if (!(begin == 0 && end == src.size())) diff --git a/include/boost/histogram/axis/integer.hpp b/include/boost/histogram/axis/integer.hpp index 37af2185..5c8f8b35 100644 --- a/include/boost/histogram/axis/integer.hpp +++ b/include/boost/histogram/axis/integer.hpp @@ -27,9 +27,10 @@ namespace axis { * Binning is a O(1) operation. This axis operates * faster than a regular axis. */ -template -class integer : public base, public iterator_mixin> { - using base_type = base; +template +class integer : public base, + public iterator_mixin> { + using base_type = base; using value_type = IntType; using metadata_type = MetaData; using bin_view = @@ -45,15 +46,14 @@ public: * \param metadata description of the axis. * \param options extra bin options. */ - integer(value_type start, value_type stop, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow) + integer(value_type start, value_type stop, metadata_type m = metadata_type()) : base_type(static_cast(stop - start > 0 ? stop - start : 0), - std::move(m), o) + std::move(m)) , min_(start) {} /// Constructor used by algorithm::reduce to shrink and rebin. integer(const integer& src, unsigned begin, unsigned end, unsigned merge) - : base_type(end - begin, src.metadata(), src.options()), min_(src.min_ + begin) { + : base_type(end - begin, src.metadata()), min_(src.min_ + begin) { if (merge > 1) { throw std::invalid_argument("cannot merge bins for integer axis"); } } @@ -68,16 +68,8 @@ public: /// Returns axis value for index. value_type value(index_type i) const noexcept { - if (i < 0) { - return detail::static_if>( - [](auto) { return std::numeric_limits::min(); }, - [](auto) { return -std::numeric_limits::infinity(); }, 0); - } - if (i > static_cast(base_type::size())) { - return detail::static_if>( - [](auto) { return std::numeric_limits::max(); }, - [](auto) { return std::numeric_limits::infinity(); }, 0); - } + if (i < 0) { return detail::lowest(); } + if (i > static_cast(base_type::size())) { return detail::highest(); } return min_ + i; } diff --git a/include/boost/histogram/axis/iterator.hpp b/include/boost/histogram/axis/iterator.hpp index 90c88bd0..956f3c6a 100644 --- a/include/boost/histogram/axis/iterator.hpp +++ b/include/boost/histogram/axis/iterator.hpp @@ -16,15 +16,12 @@ namespace axis { template class iterator - : public iterator_facade, decltype(std::declval()[0]), - random_access_traversal_tag, - decltype(std::declval()[0]), int> { + : public boost::iterator_facade, decltype(std::declval()[0]), + boost::random_access_traversal_tag, + decltype(std::declval()[0]), int> { public: explicit iterator(const Axis& axis, int idx) : axis_(axis), idx_(idx) {} - iterator(const iterator&) = default; - iterator& operator=(const iterator&) = default; - protected: void increment() noexcept { ++idx_; } void decrement() noexcept { --idx_; } @@ -37,6 +34,7 @@ protected: friend class ::boost::iterator_core_access; +private: const Axis& axis_; int idx_; }; diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 7d04a79f..3eda3a96 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -10,15 +10,12 @@ #define BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP #include -#include -#include -#include #include -#include +#include #include -#include -#include +#include #include +#include #include #include #include @@ -91,8 +88,9 @@ std::basic_ostream& operator<<(std::basic_ostream& os, const axis::option_type o) { switch (o) { case axis::option_type::none: os << "none"; break; + case axis::option_type::underflow: os << "underflow"; break; case axis::option_type::overflow: os << "overflow"; break; - case axis::option_type::underflow_and_overflow: os << "underflow_and_overflow"; break; + case axis::option_type::uoflow: os << "uoflow"; break; } return os; } @@ -126,9 +124,9 @@ std::basic_ostream& operator<<(std::basic_ostream& os, return os; } -template +template std::basic_ostream& operator<<(std::basic_ostream& os, - const regular& a) { + const regular& a) { os << "regular" << detail::to_string(a.transform()) << "(" << a.size() << ", " << a.value(0) << ", " << a.value(a.size()); detail::stream_metadata(os, a.metadata()); @@ -138,9 +136,9 @@ std::basic_ostream& operator<<(std::basic_ostream& os, return os; } -template +template std::basic_ostream& operator<<(std::basic_ostream& os, - const circular& a) { + const circular& a) { os << "circular(" << a.size() << ", " << a.value(0) << ", " << a.value(a.size()); detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); @@ -148,20 +146,20 @@ std::basic_ostream& operator<<(std::basic_ostream& os, return os; } -template +template std::basic_ostream& operator<<(std::basic_ostream& os, - const variable& a) { + const variable& a) { os << "variable(" << a.value(0); - for (unsigned i = 1; i <= a.size(); ++i) { os << ", " << a.value(i); } + for (int i = 1, n = a.size(); i <= n; ++i) { os << ", " << a.value(i); } detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); os << ")"; return os; } -template +template std::basic_ostream& operator<<(std::basic_ostream& os, - const integer& a) { + const integer& a) { os << "integer(" << a.value(0) << ", " << a.value(a.size()); detail::stream_metadata(os, a.metadata()); detail::stream_options(os, a.options()); @@ -169,11 +167,11 @@ std::basic_ostream& operator<<(std::basic_ostream& os, return os; } -template +template std::basic_ostream& operator<<(std::basic_ostream& os, - const category& a) { + const category& a) { os << "category("; - for (unsigned i = 0; i < a.size(); ++i) { + for (unsigned i = 0, n = a.size(); i < n; ++i) { detail::stream_value(os, a.value(i)); os << (i == (a.size() - 1) ? "" : ", "); } diff --git a/include/boost/histogram/axis/regular.hpp b/include/boost/histogram/axis/regular.hpp index a9d56fa6..d57e19c8 100644 --- a/include/boost/histogram/axis/regular.hpp +++ b/include/boost/histogram/axis/regular.hpp @@ -23,6 +23,7 @@ namespace boost { namespace histogram { namespace axis { namespace transform { + template struct identity { static T forward(T x) { return x; } @@ -68,6 +69,7 @@ struct unit { T forward(Q x) const { return x / U(); } Q inverse(T x) const { return x * U(); } }; + } // namespace transform /** Axis for equidistant intervals on the real line. @@ -75,11 +77,11 @@ struct unit { * The most common binning strategy. * Very fast. Binning is a O(1) operation. */ -template -class regular : public base, - public iterator_mixin>, +template +class regular : public base, + public iterator_mixin>, protected Transform { - using base_type = base; + using base_type = base; using transform_type = Transform; using external_type = detail::return_type; using internal_type = detail::return_type; @@ -98,8 +100,8 @@ public: * \param options extra bin options. */ regular(transform_type trans, unsigned n, external_type start, external_type stop, - metadata_type m = {}, option_type o = option_type::underflow_and_overflow) - : base_type(n, std::move(m), o) + metadata_type m = {}) + : base_type(n, std::move(m)) , transform_type(std::move(trans)) , min_(this->forward(start)) , delta_((this->forward(stop) - min_) / base_type::size()) { @@ -117,13 +119,12 @@ public: * \param metadata description of the axis. * \param options extra bin options. */ - regular(unsigned n, external_type start, external_type stop, metadata_type m = {}, - option_type o = option_type::underflow_and_overflow) - : regular({}, n, start, stop, std::move(m), o) {} + regular(unsigned n, external_type start, external_type stop, metadata_type m = {}) + : regular({}, n, start, stop, std::move(m)) {} /// Constructor used by algorithm::reduce to shrink and rebin (not for users). regular(const regular& src, unsigned begin, unsigned end, unsigned merge) - : base_type((end - begin) / merge, src.metadata(), src.options()) + : base_type((end - begin) / merge, src.metadata()) , transform_type(src.transform()) , min_(this->forward(src.value(begin))) , delta_(src.delta_ * merge) { @@ -179,6 +180,7 @@ public: private: internal_type min_, delta_; }; + } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index 94c3b3a5..9d37ab08 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -13,6 +13,18 @@ namespace boost { namespace histogram { +namespace detail { +template +constexpr axis::option_type options_impl(const T&, std::false_type) { + return axis::option_type::none; +} + +template +axis::option_type options_impl(const T& t, std::true_type) { + return t.options(); +} +} // namespace detail + namespace axis { namespace traits { template @@ -28,15 +40,27 @@ decltype(auto) metadata(T&& t) noexcept { template option_type options(const T& t) noexcept { - return detail::static_if>( - [](const auto& x) { return x.options(); }, - [](const T&) { return axis::option_type::none; }, t); + return detail::options_impl(t, detail::has_method_options()); +} + +template +int underflow_index(const T& t) noexcept { + const auto opt = options(t); + return opt & option_type::underflow ? t.size() + (opt & option_type::overflow) : -1; +} + +template +int overflow_index(const T& t) noexcept { + const auto opt = options(t); + return opt & option_type::overflow ? t.size() : -1; } template unsigned extend(const T& t) noexcept { - return t.size() + static_cast(options(t)); + const auto opt = options(t); + return t.size() + (opt & option_type::underflow) + (opt & option_type::overflow); } + } // namespace traits } // namespace axis } // namespace histogram diff --git a/include/boost/histogram/axis/variable.hpp b/include/boost/histogram/axis/variable.hpp index 53f879ae..bbde4877 100644 --- a/include/boost/histogram/axis/variable.hpp +++ b/include/boost/histogram/axis/variable.hpp @@ -31,10 +31,10 @@ namespace axis { * Binning is a O(log(N)) operation. If speed matters and the problem * domain allows it, prefer a regular axis, possibly with a transform. */ -template -class variable : public base, - public iterator_mixin> { - using base_type = base; +template +class variable : public base, + public iterator_mixin> { + using base_type = base; using allocator_type = Allocator; using metadata_type = MetaData; using value_type = RealType; @@ -50,9 +50,8 @@ public: */ template > variable(It begin, It end, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow, allocator_type a = allocator_type()) - : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o) + : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m)) , x_(nullptr, std::move(a)) { using AT = std::allocator_traits; x_.first() = AT::allocate(x_.second(), nx()); @@ -89,10 +88,8 @@ public: */ template > variable(const U& iterable, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow, allocator_type a = allocator_type()) - : variable(std::begin(iterable), std::end(iterable), std::move(m), o, - std::move(a)) {} + : variable(std::begin(iterable), std::end(iterable), std::move(m), std::move(a)) {} /** Construct variable axis from initializer list of bin edges. * @@ -103,13 +100,12 @@ public: */ template variable(const std::initializer_list& l, metadata_type m = metadata_type(), - option_type o = option_type::underflow_and_overflow, allocator_type a = allocator_type()) - : variable(l.begin(), l.end(), std::move(m), o, std::move(a)) {} + : variable(l.begin(), l.end(), std::move(m), std::move(a)) {} /// Constructor used by algorithm::reduce to shrink and rebin (not for users). variable(const variable& src, unsigned begin, unsigned end, unsigned merge) - : base_type((end - begin) / merge, src.metadata(), src.options()), x_(src.x_) { + : base_type((end - begin) / merge, src.metadata()), x_(src.x_) { BOOST_ASSERT((end - begin) % merge == 0); using It = const detail::unqual*; struct skip_iterator { @@ -172,11 +168,15 @@ public: /// Returns axis value for fractional index. value_type value(value_type i) const noexcept { - if (i < 0) { return -std::numeric_limits::infinity(); } - if (i > base_type::size()) { return std::numeric_limits::infinity(); } - value_type z; - const auto k = static_cast(std::modf(i, &z)); - return (1.0 - z) * x_.first()[k] + z * x_.first()[k + 1]; + if (i < 0) { return detail::lowest(); } + if (i > static_cast(base_type::size())) { return detail::highest(); } + return detail::static_if>( + [this](auto i) -> value_type { + decltype(i) z; + const auto k = static_cast(std::modf(i, &z)); + return (1.0 - z) * x_.first()[k] + z * x_.first()[k + 1]; + }, + [this](auto i) -> value_type { return x_.first()[i]; }, i); } auto operator[](int idx) const noexcept { diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index 74c19fe8..82e9ff19 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/include/boost/histogram/detail/index_cache.hpp b/include/boost/histogram/detail/index_cache.hpp deleted file mode 100644 index 25884ace..00000000 --- a/include/boost/histogram/detail/index_cache.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2015-2017 Hans Dembinski -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt -// or copy at http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_HISTOGRAM_DETAIL_INDEX_CACHE_HPP -#define BOOST_HISTOGRAM_DETAIL_INDEX_CACHE_HPP - -#include -#include -#include - -namespace boost { -namespace histogram { -namespace detail { - -struct state_t { - unsigned dim; - std::size_t idx; -}; - -struct dim_t { - int idx, size; - std::size_t stride; -}; - -union block_t { - state_t state; - dim_t dim; -}; - -struct index_cache : public std::unique_ptr { - using ptr_t = std::unique_ptr; - - struct dim_visitor { - mutable std::size_t stride; - mutable block_t* b; - template - void operator()(const T& a) const noexcept { - b->dim = dim_t{0, static_cast(a.size()), stride}; - ++b; - stride *= axis::traits::extend(a); - } - }; - - template - void set(const H& h) { - if (!(*this) || h.rank() != ptr_t::get()->state.dim) { - ptr_t::reset(new block_t[h.rank() + 1]); - ptr_t::get()->state.dim = h.rank(); - ptr_t::get()->state.idx = 0; - } - h.for_each_axis(dim_visitor{1, ptr_t::get() + 1}); - } - - void set_idx(std::size_t idx) { - auto& s = ptr_t::get()->state; - if (idx == s.idx) return; - s.idx = idx; - auto d = s.dim; - auto b = (ptr_t::get() + 1) + d; - while ((--b, --d)) { - b->dim.idx = idx / b->dim.stride; - idx -= b->dim.idx * b->dim.stride; - b->dim.idx -= (b->dim.idx > b->dim.size) * (b->dim.size + 2); - } - b->dim.idx = idx; - b->dim.idx -= (b->dim.idx > b->dim.size) * (b->dim.size + 2); - } - - int get(unsigned d) const { return (ptr_t::get() + 1 + d)->dim.idx; } -}; -} -} -} - -#endif diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index e34a5dbb..2ba63109 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -93,6 +93,36 @@ constexpr decltype(auto) static_if(Ts&&... ts) { return static_if_c(std::forward(ts)...); } +template +constexpr T lowest() { + return std::numeric_limits::lowest(); +} + +template <> +constexpr double lowest() { + return -std::numeric_limits::infinity(); +} + +template <> +constexpr float lowest() { + return -std::numeric_limits::infinity(); +} + +template +constexpr T highest() { + return std::numeric_limits::max(); +} + +template <> +constexpr double highest() { + return std::numeric_limits::infinity(); +} + +template <> +constexpr float highest() { + return std::numeric_limits::infinity(); +} + template struct sub_tuple_impl { template diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index 70222c8b..eb6f85ed 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -31,8 +30,6 @@ public: using axes_type = Axes; using storage_type = Storage; using value_type = typename storage_type::value_type; - using const_reference = typename storage_type::const_reference; - using const_iterator = iterator; histogram() = default; histogram(const histogram& rhs) = default; @@ -137,14 +134,13 @@ public: detail::for_each_axis(axes_, std::forward(unary)); } - /// Fill histogram with value tuple and optional weight + /// Fill histogram with values and optional weight or sample template void operator()(const Ts&... ts) { - // case with one argument needs special treatment, specialized below operator()(std::forward_as_tuple(ts...)); } - /// Fill histogram with value tuple and optional weight + /// Fill histogram with value tuple and optional weight or sample template void operator()(const std::tuple& t) { detail::fill_impl(storage_, axes_, t); @@ -152,28 +148,26 @@ public: /// Access bin counter at indices template - const_reference at(const Ts&... ts) const { - // case with one argument is ambiguous, is specialized below + decltype(auto) at(const Ts&... ts) const { return at(std::forward_as_tuple(ts...)); } - /// Access bin counter at index (specialization for 1D) + /// Access bin counter at index tuple template - const_reference at(const std::tuple& t) const { + decltype(auto) at(const std::tuple& t) const { const auto idx = detail::at_impl(axes_, t); if (!idx) throw std::out_of_range("indices out of bounds"); return storage_[*idx]; } - /// Access bin counter at index + /// Access bin counter at index (for 1D histograms and passing index tuple) template - const_reference operator[](const T& t) const { + decltype(auto) operator[](const T& t) const { return at(t); } - auto begin() const noexcept { return const_iterator(*this, 0); } - - auto end() const noexcept { return const_iterator(*this, size()); } + auto begin() const noexcept { return storage_.begin(); } + auto end() const noexcept { return storage_.end(); } template void serialize(Archive&, unsigned); diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 2c0cb72c..b2cffd5b 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -8,7 +8,11 @@ #define BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP #include -#include +#include // for string and new_allocator + +// Why boost containers as defaults and not std containers? +// - std::vector does not work with incomplete types, but boost::container::vector does +// - std::string is very large on MSVC, boost::container::string is small everywhere namespace boost { namespace histogram { @@ -17,21 +21,36 @@ namespace axis { /* Most of the histogram code is generic and works for any number of axes. Buffers with a * fixed maximum capacity are used in some places, which have a size equal to the rank of * a histogram. The buffers are statically allocated to improve performance, which means - * that they need a preset maximum capacity. 48 seems like a safe upper limit for the rank + * that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank * (you can nevertheless increase it here if necessary): the simplest non-trivial axis has * 2 bins; even if counters are used which need only a byte of storage per bin, this still - * corresponds to 256 TB of storage. + * corresponds to 4 GB of storage. */ -BOOST_ATTRIBUTE_UNUSED static constexpr unsigned limit = 48; +BOOST_ATTRIBUTE_UNUSED static constexpr unsigned limit = +#ifdef BOOST_HISTOGRAM_AXES_LIMIT + BOOST_HISTOGRAM_AXES_LIMIT; +#else + 32; +#endif -struct null_type {}; /// empty meta data type +/// empty metadata type +struct null_type {}; enum class option_type { none = 0, - overflow = 1, - underflow_and_overflow = 2, + underflow = 1, + overflow = 2, + uoflow = 3, }; +constexpr inline option_type operator|(option_type a, option_type b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +constexpr inline bool operator&(option_type a, option_type b) { + return static_cast(a) & static_cast(b); +} + namespace transform { template struct identity; @@ -44,22 +63,27 @@ struct pow; } // namespace transform template , - typename MetaData = boost::container::string> + typename MetaData = boost::container::string, + option_type Options = option_type::uoflow> class regular; -template +template class circular; template , - typename MetaData = boost::container::string> + typename MetaData = boost::container::string, + option_type Options = option_type::uoflow> class variable; -template +template class integer; template , - typename MetaData = boost::container::string> + typename MetaData = boost::container::string, + option_type Options = option_type::overflow> class category; template diff --git a/include/boost/histogram/indexed.hpp b/include/boost/histogram/indexed.hpp new file mode 100644 index 00000000..2c8d91d7 --- /dev/null +++ b/include/boost/histogram/indexed.hpp @@ -0,0 +1,106 @@ +// Copyright 2015-2016 Hans Dembinski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_HISTOGRAM_INDEXED_HPP +#define BOOST_HISTOGRAM_INDEXED_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { + +/// Range over histogram bins with multi-dimensional index. +template +class indexed_type { + using storage_type = typename Histogram::storage_type; + using index_type = boost::container::static_vector; + struct stride_t { + std::size_t stride; + int underflow; + }; + using strides_type = boost::container::static_vector; + +public: + using value_type = + std::pair()[0])>; + + class const_iterator + : public boost::iterator_facade { + public: + const_iterator(const indexed_type& parent, std::size_t idx) noexcept + : parent_(parent), idx_(idx) {} + + protected: + void increment() noexcept { ++idx_; } + void decrement() noexcept { --idx_; } + void advance(std::ptrdiff_t n) noexcept { idx_ += n; } + std::ptrdiff_t distance_to(const_iterator rhs) const noexcept { + return rhs.idx_ - idx_; + } + bool equal(const_iterator rhs) const noexcept { + return &parent_ == &rhs.parent_ && idx_ == rhs.idx_; + } + value_type dereference() const noexcept { + auto sit = parent_.strides_.end(); + auto iit = parent_.index_.end(); + auto idx = idx_; + while (sit != parent_.strides_.begin()) { + --sit; + --iit; + *iit = idx / sit->stride; + idx %= sit->stride; + if (*iit == sit->underflow) *iit = -1; + } + return {parent_.index_, parent_.storage_[idx_]}; + } + + friend class ::boost::iterator_core_access; + + private: + const indexed_type& parent_; + std::size_t idx_; + }; + + indexed_type(const Histogram& h) + : storage_(unsafe_access::storage(h)), strides_(h.rank()), index_(h.rank()) { + auto it = strides_.begin(); + std::size_t s = 1; + h.for_each_axis([&](const auto& a) { + it->stride = s; + s *= axis::traits::extend(a); + it->underflow = axis::traits::underflow_index(a); + ++it; + }); + } + + indexed_type(const indexed_type&) = default; + indexed_type& operator=(const indexed_type& rhs) = default; + + const_iterator begin() const { return {*this, 0}; } + const_iterator end() const { return {*this, storage_.size()}; } + +private: + const storage_type& storage_; + strides_type strides_; + mutable index_type index_; +}; + +template +indexed_type> indexed(Histogram&& h) { + return {std::forward(h)}; +} + +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/iterator.hpp b/include/boost/histogram/iterator.hpp deleted file mode 100644 index d74d79ad..00000000 --- a/include/boost/histogram/iterator.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2015-2016 Hans Dembinski -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt -// or copy at http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_HISTOGRAM_VALUE_ITERATOR_HPP -#define BOOST_HISTOGRAM_VALUE_ITERATOR_HPP - -#include -#include -#include -#include - -namespace boost { -namespace histogram { - -/// Fancy iterator over histogram bins with access multi-dimensional index. -template -class iterator - : public iterator_facade, typename Histogram::value_type, - random_access_traversal_tag, - typename Histogram::const_reference> { -public: - iterator(const Histogram& h, std::size_t idx) : histogram_(h), idx_(idx) {} - - iterator(const iterator& o) : histogram_(o.histogram_), idx_(o.idx_) {} - - iterator& operator=(const iterator& o) { - histogram_ = o.histogram_; - idx_ = o.idx_; - cache_.reset(); - } - - std::size_t rank() const noexcept { return histogram_.rank(); } - - int idx(std::size_t dim = 0) const noexcept { - if (!cache_) { cache_.set(histogram_); } - cache_.set_idx(idx_); - return cache_.get(dim); - } - - decltype(auto) bin() const { return histogram_.axis()[idx()]; } - - template - decltype(auto) bin(mp11::mp_size_t) const { - return histogram_.axis(mp11::mp_size_t())[idx(I)]; - } - - decltype(auto) bin(std::size_t dim) const { return histogram_.axis(dim)[idx(dim)]; } - -private: - bool equal(const iterator& rhs) const noexcept { - return &histogram_ == &rhs.histogram_ && idx_ == rhs.idx_; - } - - void increment() noexcept { ++idx_; } - void decrement() noexcept { --idx_; } - - void advance(int n) noexcept { idx_ += n; } - - decltype(auto) dereference() const noexcept { - return unsafe_access::storage(histogram_)[idx_]; - } - - const Histogram& histogram_; - std::size_t idx_; - mutable detail::index_cache cache_; - friend class ::boost::iterator_core_access; -}; - -} // namespace histogram -} // namespace boost - -#endif diff --git a/include/boost/histogram/ostream_operators.hpp b/include/boost/histogram/ostream_operators.hpp index cf012684..c88b6cda 100644 --- a/include/boost/histogram/ostream_operators.hpp +++ b/include/boost/histogram/ostream_operators.hpp @@ -7,8 +7,6 @@ #ifndef BOOST_HISTOGRAM_OSTREAM_OPERATORS_HPP #define BOOST_HISTOGRAM_OSTREAM_OPERATORS_HPP -#include -#include #include #include diff --git a/include/boost/histogram/serialization.hpp b/include/boost/histogram/serialization.hpp index 82806b14..ed172091 100644 --- a/include/boost/histogram/serialization.hpp +++ b/include/boost/histogram/serialization.hpp @@ -112,7 +112,7 @@ void serialize(Archive& ar, storage_adaptor& s, unsigned /* version */) { auto size = s.size(); ar& size; if (Archive::is_loading::value) { s.reset(size); } - ar& static_cast(s); + ar& s.container_; } namespace detail { @@ -153,12 +153,11 @@ namespace axis { template void serialize(Archive&, null_type&, unsigned /* version */) {} // noop -template +template template -void base::serialize(Archive& ar, unsigned /* version */) { +void base::serialize(Archive& ar, unsigned /* version */) { ar& size_meta_.first(); ar& size_meta_.second(); - ar& opt_; } template @@ -167,26 +166,26 @@ void transform::pow::serialize(Archive& ar, unsigned /* version */) { ar& power; } -template +template template -void regular::serialize(Archive& ar, unsigned /* version */) { +void regular::serialize(Archive& ar, unsigned /* version */) { ar& static_cast(*this); ar& static_cast(*this); ar& min_; ar& delta_; } -template +template template -void circular::serialize(Archive& ar, unsigned /* version */) { +void circular::serialize(Archive& ar, unsigned /* version */) { ar& static_cast(*this); ar& phase_; ar& delta_; } -template +template template -void variable::serialize(Archive& ar, unsigned /* version */) { +void variable::serialize(Archive& ar, unsigned /* version */) { // destroy must happen before base serialization with old size if (Archive::is_loading::value) detail::destroy_buffer(x_.second(), x_.first(), nx()); ar& static_cast(*this); @@ -195,16 +194,16 @@ void variable::serialize(Archive& ar, unsigned /* version */) { ar& boost::serialization::make_array(x_.first(), nx()); } -template +template template -void integer::serialize(Archive& ar, unsigned /* version */) { +void integer::serialize(Archive& ar, unsigned /* version */) { ar& static_cast(*this); ar& min_; } -template +template template -void category::serialize(Archive& ar, unsigned /* version */) { +void category::serialize(Archive& ar, unsigned /* version */) { // destroy must happen before base serialization with old size if (Archive::is_loading::value) detail::destroy_buffer(x_.second(), x_.first(), base_type::size()); diff --git a/include/boost/histogram/storage_adaptor.hpp b/include/boost/histogram/storage_adaptor.hpp index cda6bd37..a950bf07 100644 --- a/include/boost/histogram/storage_adaptor.hpp +++ b/include/boost/histogram/storage_adaptor.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -88,155 +89,201 @@ using element_adaptor = template struct ERROR_type_passed_to_storage_adaptor_not_recognized {}; -template -struct vector_augmentation : T { - using value_type = typename T::value_type; +template +struct vector_impl { + using value_type = typename C::value_type; - vector_augmentation(T&& t) : T(std::move(t)) {} - vector_augmentation(const T& t) : T(t) {} + vector_impl(C&& c) : container_(std::move(c)) {} + vector_impl(const C& c) : container_(c) {} - vector_augmentation() = default; - vector_augmentation(vector_augmentation&&) = default; - vector_augmentation(const vector_augmentation&) = default; - vector_augmentation& operator=(vector_augmentation&&) = default; - vector_augmentation& operator=(const vector_augmentation&) = default; + vector_impl() = default; + vector_impl(vector_impl&&) = default; + vector_impl(const vector_impl&) = default; + vector_impl& operator=(vector_impl&&) = default; + vector_impl& operator=(const vector_impl&) = default; void reset(std::size_t n) { - this->resize(n); - std::fill(this->begin(), this->end(), value_type()); + container_.resize(n); + std::fill(container_.begin(), container_.end(), value_type()); } + const value_type& operator[](std::size_t i) const noexcept { return container_[i]; } + template void add(std::size_t i, U&& u) { - T::operator[](i) += std::forward(u); + container_[i] += std::forward(u); } - template - void set_impl(std::size_t i, U&& u) { - T::operator[](i) = std::forward(u); + void set(std::size_t i, U&& u) { + container_[i] = std::forward(u); } - void mul_impl(std::size_t i, double x) { T::operator[](i) *= x; } + std::size_t size() const noexcept { return container_.size(); } + + decltype(auto) begin() const noexcept { return container_.begin(); } + decltype(auto) end() const noexcept { return container_.end(); } + + decltype(auto) get_allocator() const noexcept { return container_.get_allocator(); } + + value_type& ref(std::size_t i) noexcept { return container_[i]; } + void mul(std::size_t i, double x) { container_[i] *= x; } + + C container_; }; -template -struct array_augmentation : T { - using value_type = typename T::value_type; +template +struct array_impl { + using value_type = typename C::value_type; - array_augmentation(T&& t) : T(std::move(t)) {} - array_augmentation(const T& t) : T(t) {} + array_impl(C&& c) : container_(std::move(c)) {} + array_impl(const C& c) : container_(c) {} - array_augmentation() = default; - array_augmentation(array_augmentation&&) = default; - array_augmentation(const array_augmentation&) = default; - array_augmentation& operator=(array_augmentation&&) = default; - array_augmentation& operator=(const array_augmentation&) = default; + array_impl() = default; + array_impl(array_impl&&) = default; + array_impl(const array_impl&) = default; + array_impl& operator=(array_impl&&) = default; + array_impl& operator=(const array_impl&) = default; void reset(std::size_t n) { - if (n > this->max_size()) // for std::array + if (n > container_.max_size()) // for std::array throw std::runtime_error( - detail::cat("size ", n, " exceeds maximum capacity ", this->max_size())); - std::fill_n(this->begin(), n, value_type()); + detail::cat("size ", n, " exceeds maximum capacity ", container_.max_size())); + std::fill_n(container_.begin(), n, value_type()); size_ = n; } + const value_type& operator[](std::size_t i) const noexcept { return container_[i]; } + template void add(std::size_t i, U&& u) { - T::operator[](i) += std::forward(u); + container_[i] += std::forward(u); + } + template + void set(std::size_t i, U&& u) { + container_[i] = std::forward(u); } std::size_t size() const { return size_; } - template - void set_impl(std::size_t i, U&& u) { - T::operator[](i) = std::forward(u); - } + decltype(auto) begin() const noexcept { return container_.begin(); } + decltype(auto) end() const noexcept { return container_.begin() + size_; } - void mul_impl(std::size_t i, double x) { T::operator[](i) *= x; } + value_type& ref(std::size_t i) { return container_[i]; } + void mul(std::size_t i, double x) { container_[i] *= x; } -private: + C container_; std::size_t size_ = 0; }; -template -struct map_augmentation : T { - static_assert(std::is_same::value, +template +struct map_impl { + static_assert(std::is_same::value, "map must use std::size_t as key_type"); - using value_type = typename T::mapped_type; - map_augmentation(T&& t) : T(std::move(t)) {} - map_augmentation(const T& t) : T(t) {} + using value_type = typename C::mapped_type; - map_augmentation() = default; - map_augmentation(map_augmentation&&) = default; - map_augmentation(const map_augmentation&) = default; - map_augmentation& operator=(map_augmentation&&) = default; - map_augmentation& operator=(const map_augmentation&) = default; + class const_iterator : public boost::iterator_facade { + public: + const_iterator(const map_impl& parent, std::size_t idx) noexcept + : parent_(parent), idx_(idx) {} + + protected: + void increment() noexcept { ++idx_; } + void decrement() noexcept { --idx_; } + void advance(std::ptrdiff_t n) noexcept { idx_ += n; } + std::ptrdiff_t distance_to(const_iterator rhs) const noexcept { + return rhs.idx_ - idx_; + } + bool equal(const_iterator rhs) const noexcept { + return &parent_ == &rhs.parent_ && idx_ == rhs.idx_; + } + const value_type& dereference() const { return parent_[idx_]; } + + friend class ::boost::iterator_core_access; + + private: + const map_impl& parent_; + std::size_t idx_; + }; + + map_impl(C&& c) : container_(std::move(c)) {} + map_impl(const C& c) : container_(c) {} + + map_impl() = default; + map_impl(map_impl&&) = default; + map_impl(const map_impl&) = default; + map_impl& operator=(map_impl&&) = default; + map_impl& operator=(const map_impl&) = default; void reset(std::size_t n) { - this->clear(); + container_.clear(); size_ = n; } - value_type& operator[](std::size_t i) { return T::operator[](i); } - - const value_type& operator[](std::size_t i) const { - auto it = this->find(i); + const value_type& operator[](std::size_t i) const noexcept { + auto it = container_.find(i); static auto null = value_type(); - return it == this->end() ? null : it->second; + return it == container_.end() ? null : it->second; } template void add(std::size_t i, U&& u) { if (u == value_type()) return; - auto it = this->find(i); - if (it != this->end()) + auto it = container_.find(i); + if (it != container_.end()) it->second += std::forward(u); else - T::operator[](i) = std::forward(u); - } - - std::size_t size() const { return size_; } - - void mul_impl(std::size_t i, double x) { - auto it = this->find(i); - if (it != this->end()) it->second *= x; + container_[i] = std::forward(u); } template - void set_impl(std::size_t i, U&& u) { - auto it = this->find(i); + void set(std::size_t i, U&& u) { + auto it = container_.find(i); if (u == value_type()) { - if (it != this->end()) this->erase(it); + if (it != container_.end()) container_.erase(it); } else { - if (it != this->end()) + if (it != container_.end()) it->second = std::forward(u); else - T::operator[](i) = std::forward(u); + container_[i] = std::forward(u); } } -private: + std::size_t size() const noexcept { return size_; } + + const_iterator begin() const noexcept { return {*this, 0}; } + const_iterator end() const noexcept { return {*this, size_}; } + + decltype(auto) get_allocator() const noexcept { return container_.get_allocator(); } + + value_type& ref(std::size_t i) { return container_[i]; } + void mul(std::size_t i, double x) { + auto it = container_.find(i); + if (it != container_.end()) it->second *= x; + } + + C container_; std::size_t size_ = 0; }; template -using storage_augmentation = mp11::mp_if< - is_vector_like, vector_augmentation, - mp11::mp_if, array_augmentation, - mp11::mp_if, map_augmentation, +using storage_adaptor_impl = mp11::mp_if< + is_vector_like, vector_impl, + mp11::mp_if, array_impl, + mp11::mp_if, map_impl, ERROR_type_passed_to_storage_adaptor_not_recognized>>>; } // namespace detail /// generic implementation for std::array, vector-like, and map-like containers template -struct storage_adaptor : detail::storage_augmentation { +struct storage_adaptor : detail::storage_adaptor_impl { struct storage_tag {}; - using base_type = detail::storage_augmentation; + using base_type = detail::storage_adaptor_impl; using value_type = typename base_type::value_type; using element_adaptor = detail::element_adaptor; - using const_reference = const value_type&; storage_adaptor() = default; storage_adaptor(const storage_adaptor&) = default; @@ -255,34 +302,34 @@ struct storage_adaptor : detail::storage_augmentation { template > storage_adaptor& operator=(const U& rhs) { this->reset(rhs.size()); - for (std::size_t i = 0, n = this->size(); i < n; ++i) this->set_impl(i, rhs[i]); + for (std::size_t i = 0, n = this->size(); i < n; ++i) this->set(i, rhs[i]); return *this; } template void operator()(std::size_t i, Us&&... us) { BOOST_ASSERT(i < this->size()); - element_adaptor::forward((*this)[i], std::forward(us)...); + element_adaptor::forward(this->ref(i), std::forward(us)...); } // precondition: storages have equal size template > storage_adaptor& operator+=(const U& rhs) { const auto n = this->size(); - BOOST_ASSERT_MSG(n == rhs.size(), "sizes must be equal"); + BOOST_ASSERT(n == rhs.size()); for (std::size_t i = 0; i < n; ++i) this->add(i, rhs[i]); return *this; } storage_adaptor& operator*=(const double x) { - for (std::size_t i = 0, n = this->size(); i < n; ++i) this->mul_impl(i, x); + for (std::size_t i = 0, n = this->size(); i < n; ++i) this->mul(i, x); return *this; } storage_adaptor& operator/=(const double x) { return operator*=(1.0 / x); } template - bool operator==(const U& u) const { + bool operator==(const U& u) const noexcept { const auto n = this->size(); if (n != u.size()) return false; for (std::size_t i = 0; i < n; ++i) diff --git a/test/algorithm_sum_test.cpp b/test/algorithm_sum_test.cpp index 3dfc8bc0..b480948e 100644 --- a/test/algorithm_sum_test.cpp +++ b/test/algorithm_sum_test.cpp @@ -4,9 +4,14 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) +#include #include #include +#include #include +#include +#include +#include #include "utility_histogram.hpp" using namespace boost::histogram; @@ -33,6 +38,17 @@ void run_tests() { auto h4 = make_s(Tag(), std::unordered_map(), ax); for (unsigned i = 0; i < 100; ++i) h4(i); BOOST_TEST_EQ(sum(h4), 100); + + auto h5 = + make_s(Tag(), std::vector>(), axis::integer<>(0, 1), + axis::integer(2, 4)); + h5(weight(2), 0, 2); + h5(-1, 2); + h5(1, 3); + + const auto v = algorithm::sum(h5); + BOOST_TEST_EQ(v.value(), 4); + BOOST_TEST_EQ(v.variance(), 6); } int main() { diff --git a/test/axis_category_test.cpp b/test/axis_category_test.cpp index d19a062b..483d2cf7 100644 --- a/test/axis_category_test.cpp +++ b/test/axis_category_test.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/axis_regular_test.cpp b/test/axis_regular_test.cpp index 929ac05b..4b711a1e 100644 --- a/test/axis_regular_test.cpp +++ b/test/axis_regular_test.cpp @@ -136,9 +136,11 @@ int main() { // iterators { - test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5); + using tr = axis::transform::identity<>; test_axis_iterator( - axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5); + axis::regular(5, 0, 1), 0, 5); + test_axis_iterator( + axis::regular(5, 0, 1), 0, 5); } // bin_type streamable diff --git a/test/axis_variant_test.cpp b/test/axis_variant_test.cpp index aff2b249..b22a2429 100644 --- a/test/axis_variant_test.cpp +++ b/test/axis_variant_test.cpp @@ -39,7 +39,7 @@ int main() { BOOST_TEST_EQ(a[-10].lower(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(a[a.size() + 10].upper(), std::numeric_limits::infinity()); BOOST_TEST_EQ(a.metadata(), "int"); - BOOST_TEST_EQ(a.options(), axis::option_type::underflow_and_overflow); + BOOST_TEST_EQ(a.options(), axis::option_type::uoflow); a = axis::category({"A", "B"}, "cat"); BOOST_TEST_EQ(a("A"), 0); @@ -50,8 +50,8 @@ int main() { // axis::variant with reference { - auto a = axis::integer(0, 3, {}, axis::option_type::none); - using V = axis::variant&>; + auto a = axis::integer(0, 3); + using V = axis::variant; V v(a); BOOST_TEST_EQ(v.size(), 3); BOOST_TEST_EQ(v[0], a[0]); @@ -121,17 +121,20 @@ int main() { struct user_defined {}; namespace tr = axis::transform; + using M = boost::container::string; test(axis::regular<>(2, -1, 1, "regular1"), - "regular(2, -1, 1, metadata=\"regular1\", options=underflow_and_overflow)"); - test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), + "regular(2, -1, 1, metadata=\"regular1\", options=uoflow)"); + test(axis::regular, M, axis::option_type::none>(2, 1, 10, "regular2"), "regular_log(2, 1, 10, metadata=\"regular2\", options=none)"); - test(axis::regular>(1.5, 2, 1, 10, "regular3", axis::option_type::overflow), + test(axis::regular, M, axis::option_type::overflow>(1.5, 2, 1, 10, + "regular3"), "regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=1.5)"); - test(axis::regular>(-1.5, 2, 1, 10, "regular4", axis::option_type::none), + test(axis::regular, M, axis::option_type::none>(-1.5, 2, 1, 10, "regular4"), "regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-1.5)"); test(axis::circular(4, 0.1, 1.0), "circular(4, 0.1, 1.1, options=overflow)"); - test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none), + test(axis::variable, M, + axis::option_type::none>({-1, 0, 1}, "variable"), "variable(-1, 0, 1, metadata=\"variable\", options=none)"); test(axis::category<>({0, 1, 2}, "category"), "category(0, 1, 2, metadata=\"category\", options=overflow)"); @@ -140,8 +143,7 @@ int main() { const auto ref = detail::cat( "integer(-1, 1, metadata=", boost::core::demangled_name(BOOST_CORE_TYPEID(user_defined)), ", options=none)"); - test(axis::integer(-1, 1, {}, axis::option_type::none), - ref.c_str()); + test(axis::integer(-1, 1), ref.c_str()); } // bin_type operator<< @@ -164,8 +166,7 @@ int main() { axis::integer<>>; std::vector axes; axes.push_back(axis::regular<>{2, -1, 1}); - axes.push_back(axis::regular>( - 0.5, 2, 1, 4, "", axis::option_type::underflow_and_overflow)); + axes.push_back(axis::regular>(0.5, 2, 1, 4)); axes.push_back(axis::circular<>{4}); axes.push_back(axis::variable<>{-1, 0, 1}); axes.push_back(axis::category<>({A, B, C})); @@ -219,10 +220,9 @@ int main() { axes.reserve(5); axes.emplace_back(T1(1, 0, 1, M(3, 'c', a))); axes.emplace_back(T2(2)); - axes.emplace_back( - T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a)); + axes.emplace_back(T3({0., 1., 2.}, {}, a)); axes.emplace_back(T4(0, 4)); - axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); + axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, a)); } // 5 axis::variant objects BOOST_TEST_EQ(db.at().first, db.at().second); diff --git a/test/histogram_dynamic_test.cpp b/test/histogram_dynamic_test.cpp index 2dcbf284..8a1491f7 100644 --- a/test/histogram_dynamic_test.cpp +++ b/test/histogram_dynamic_test.cpp @@ -7,6 +7,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include diff --git a/test/histogram_mixed_test.cpp b/test/histogram_mixed_test.cpp index 6fac5507..08a5259b 100644 --- a/test/histogram_mixed_test.cpp +++ b/test/histogram_mixed_test.cpp @@ -6,6 +6,9 @@ #include #include +#include +#include +#include #include #include #include diff --git a/test/histogram_serialization_test.cpp b/test/histogram_serialization_test.cpp index c0328ce9..4bea9077 100644 --- a/test/histogram_serialization_test.cpp +++ b/test/histogram_serialization_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -23,8 +24,8 @@ void run_tests() { auto a = make(Tag(), axis::regular<>(3, -1, 1, "axis 0"), axis::circular<>(4, 0.0, 1.0, "axis 1"), axis::regular>(3, 1, 100, "axis 2"), - axis::regular>(0.5, 3, 1, 100, "axis 3", - axis::option_type::overflow), + axis::regular, boost::container::string, + axis::option_type::overflow>(0.5, 3, 1, 100, "axis 3"), axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"), axis::category<>{3, 1, 2}, axis::integer(0, 2)); a(0.5, 0.2, 20, 20, 0.25, 1, 1); diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 7e191241..bbab3651 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -11,6 +11,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -236,7 +242,8 @@ void run_tests() { // d1_2 { - auto h = make(Tag(), axis::integer<>(0, 2, "", axis::option_type::none)); + auto h = + make(Tag(), axis::integer(0, 2)); h(0); h(-0); h(-1); @@ -336,7 +343,7 @@ void run_tests() { // d2 { auto h = make(Tag(), axis::regular<>(2, -1, 1), - axis::integer<>(-1, 2, {}, axis::option_type::none)); + axis::integer(-1, 2)); h(-1, -1); h(-1, 0); h(-1, -10); @@ -368,7 +375,7 @@ void run_tests() { { auto h = make_s(Tag(), std::vector>(), axis::regular<>(2, -1, 1), - axis::integer<>(-1, 2, {}, axis::option_type::none)); + axis::integer(-1, 2)); h(-1, 0); // -> 0, 1 h(weight(10), -1, -1); // -> 0, 0 h(weight(5), -1, -10); // is ignored @@ -572,18 +579,17 @@ void run_tests() { auto a = make(Tag(), axis::regular<>(3, -1, 1, "r"), axis::integer<>(0, 2, "i")); std::ostringstream os; os << a; - BOOST_TEST_EQ( - os.str(), - std::string( - "histogram(\n" - " regular(3, -1, 1, metadata=\"r\", options=underflow_and_overflow),\n" - " integer(0, 2, metadata=\"i\", options=underflow_and_overflow),\n" - ")")); + BOOST_TEST_EQ(os.str(), + std::string("histogram(\n" + " regular(3, -1, 1, metadata=\"r\", options=uoflow),\n" + " integer(0, 2, metadata=\"i\", options=uoflow),\n" + ")")); } // histogram_reset { - auto h = make(Tag(), axis::integer<>(0, 2, {}, axis::option_type::none)); + auto h = + make(Tag(), axis::integer(0, 2)); h(0); h(1); BOOST_TEST_EQ(h.at(0), 1); @@ -625,110 +631,6 @@ void run_tests() { BOOST_TEST_EQ(h.at(1, 0, 0), 1); } - // histogram iterator 1D - { - auto h = - make_s(Tag(), std::vector>(), axis::integer<>(0, 3)); - const auto& a = h.axis(); - h(weight(2), 0); - h(1); - h(1); - - auto it = h.begin(); - BOOST_TEST_EQ(it.rank(), 1); - - BOOST_TEST_EQ(it.idx(), 0); - BOOST_TEST_EQ(it.bin(), a[0]); - BOOST_TEST_EQ(it.bin(0), a[0]); - BOOST_TEST_EQ(it->value(), 2); - BOOST_TEST_EQ(it->variance(), 4); - ++it; - BOOST_TEST_EQ(it.idx(), 1); - BOOST_TEST_EQ(it.bin(), a[1]); - BOOST_TEST_EQ(it.bin(0), a[1]); - BOOST_TEST_EQ(it->value(), 2); - ++it; - BOOST_TEST_EQ(it.idx(), 2); - BOOST_TEST_EQ(it.bin(), a[2]); - BOOST_TEST_EQ(it.bin(0), a[2]); - BOOST_TEST_EQ(it->value(), 0); - ++it; - BOOST_TEST_EQ(it.idx(), 3); - BOOST_TEST_EQ(it.bin(), a[3]); - BOOST_TEST_EQ(it.bin(0), a[3]); - BOOST_TEST_EQ(it->value(), 0); - ++it; - BOOST_TEST_EQ(it.idx(), -1); - BOOST_TEST_EQ(it.bin(), a[-1]); - BOOST_TEST_EQ(it.bin(0), a[-1]); - BOOST_TEST_EQ(it->value(), 0); - ++it; - BOOST_TEST(it == h.end()); - } - - // histogram iterator 2D - { - auto h = - make_s(Tag(), std::vector>(), axis::integer<>(0, 1), - axis::integer<>(2, 4, "", axis::option_type::none)); - const auto& a0 = h.axis(0_c); - const auto& a1 = h.axis(1_c); - h(weight(2), 0, 2); - h(-1, 2); - h(1, 3); - - auto it = h.begin(); - BOOST_TEST_EQ(it.rank(), 2); - - BOOST_TEST_EQ(it.idx(0), 0); - BOOST_TEST_EQ(it.idx(1), 0); - BOOST_TEST_EQ(it.bin(0_c), a0[0]); - BOOST_TEST_EQ(it.bin(1_c), a1[0]); - BOOST_TEST_EQ(it->value(), 2); - BOOST_TEST_EQ(it->variance(), 4); - ++it; - BOOST_TEST_EQ(it.idx(0), 1); - BOOST_TEST_EQ(it.idx(1), 0); - BOOST_TEST_EQ(it.bin(0_c), a0[1]); - BOOST_TEST_EQ(it.bin(1_c), a1[0]); - BOOST_TEST_EQ(it->value(), 0); - BOOST_TEST_EQ(it->variance(), 0); - ++it; - BOOST_TEST_EQ(it.idx(0), -1); - BOOST_TEST_EQ(it.idx(1), 0); - BOOST_TEST_EQ(it.bin(0_c), a0[-1]); - BOOST_TEST_EQ(it.bin(1_c), a1[0]); - BOOST_TEST_EQ(it->value(), 1); - BOOST_TEST_EQ(it->variance(), 1); - ++it; - BOOST_TEST_EQ(it.idx(0), 0); - BOOST_TEST_EQ(it.idx(1), 1); - BOOST_TEST_EQ(it.bin(0_c), a0[0]); - BOOST_TEST_EQ(it.bin(1_c), a1[1]); - BOOST_TEST_EQ(it->value(), 0); - BOOST_TEST_EQ(it->variance(), 0); - ++it; - BOOST_TEST_EQ(it.idx(0), 1); - BOOST_TEST_EQ(it.idx(1), 1); - BOOST_TEST_EQ(it.bin(0_c), a0[1]); - BOOST_TEST_EQ(it.bin(1_c), a1[1]); - BOOST_TEST_EQ(it->value(), 1); - BOOST_TEST_EQ(it->variance(), 1); - ++it; - BOOST_TEST_EQ(it.idx(0), -1); - BOOST_TEST_EQ(it.idx(1), 1); - BOOST_TEST_EQ(it.bin(0_c), a0[-1]); - BOOST_TEST_EQ(it.bin(1_c), a1[1]); - BOOST_TEST_EQ(it->value(), 0); - BOOST_TEST_EQ(it->variance(), 0); - ++it; - BOOST_TEST(it == h.end()); - - auto v = algorithm::sum(h); - BOOST_TEST_EQ(v.value(), 4); - BOOST_TEST_EQ(v.variance(), 6); - } - // using static containers { auto h = make_s(Tag(), std::vector>(), diff --git a/test/indexed_test.cpp b/test/indexed_test.cpp new file mode 100644 index 00000000..61c5bc04 --- /dev/null +++ b/test/indexed_test.cpp @@ -0,0 +1,95 @@ +// Copyright 2018 Hans Dembinski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +template +void run_tests() { + // histogram iterator 1D + { + auto h = make(Tag(), axis::integer<>(0, 3)); + h(weight(2), 0); + h(1); + h(1); + + auto ind = indexed(h); + auto it = ind.begin(); + BOOST_TEST_EQ(it->first.size(), 1); + + BOOST_TEST_EQ(it->first[0], 0); + BOOST_TEST_EQ(it->second, 2); + ++it; + BOOST_TEST_EQ(it->first[0], 1); + BOOST_TEST_EQ(it->second, 2); + ++it; + BOOST_TEST_EQ(it->first[0], 2); + BOOST_TEST_EQ(it->second, 0); + ++it; + BOOST_TEST_EQ(it->first[0], 3); + BOOST_TEST_EQ(it->second, 0); + ++it; + BOOST_TEST_EQ(it->first[0], -1); + BOOST_TEST_EQ(it->second, 0); + ++it; + BOOST_TEST(it == ind.end()); + } + + // histogram iterator 2D + { + auto h = make_s(Tag(), std::vector(), axis::integer<>(0, 1), + axis::integer(2, 4)); + h(weight(2), 0, 2); + h(-1, 2); + h(1, 3); + + BOOST_TEST_EQ(axis::traits::extend(h.axis(0)), 3); + BOOST_TEST_EQ(axis::traits::extend(h.axis(1)), 2); + + auto ind = indexed(h); + auto it = ind.begin(); + BOOST_TEST_EQ(it->first.size(), 2); + + BOOST_TEST_EQ(it->first[0], 0); + BOOST_TEST_EQ(it->first[1], 0); + BOOST_TEST_EQ(it->second, 2); + ++it; + BOOST_TEST_EQ(it->first[0], 1); + BOOST_TEST_EQ(it->first[1], 0); + BOOST_TEST_EQ(it->second, 0); + ++it; + BOOST_TEST_EQ(it->first[0], -1); + BOOST_TEST_EQ(it->first[1], 0); + BOOST_TEST_EQ(it->second, 1); + ++it; + BOOST_TEST_EQ(it->first[0], 0); + BOOST_TEST_EQ(it->first[1], 1); + BOOST_TEST_EQ(it->second, 0); + ++it; + BOOST_TEST_EQ(it->first[0], 1); + BOOST_TEST_EQ(it->first[1], 1); + BOOST_TEST_EQ(it->second, 1); + ++it; + BOOST_TEST_EQ(it->first[0], -1); + BOOST_TEST_EQ(it->first[1], 1); + BOOST_TEST_EQ(it->second, 0); + ++it; + BOOST_TEST(it == ind.end()); + } +} + +int main() { + run_tests(); + run_tests(); + + return boost::report_errors(); +} diff --git a/test/meta_test.cpp b/test/meta_test.cpp index 8a498633..a7f87514 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -400,16 +400,14 @@ int main() { using A = std::vector>; using B = std::vector>>; using C = std::vector; - using D = bh::axis::regular<>; - using E = const std::vector>>; - using F = const std::vector>>&; + using D = const std::vector>>; + using E = const std::vector>>&; auto v = std::vector, bh::axis::integer<>>>(); BOOST_TEST_TRAIT_TRUE((is_axis_vector)); BOOST_TEST_TRAIT_TRUE((is_axis_vector)); BOOST_TEST_TRAIT_FALSE((is_axis_vector)); - BOOST_TEST_TRAIT_FALSE((is_axis_vector)); + BOOST_TEST_TRAIT_TRUE((is_axis_vector)); BOOST_TEST_TRAIT_TRUE((is_axis_vector)); - BOOST_TEST_TRAIT_TRUE((is_axis_vector)); BOOST_TEST_TRAIT_TRUE((is_axis_vector)); BOOST_TEST_TRAIT_TRUE((is_axis_vector)); } diff --git a/test/storage_adaptor_test.cpp b/test/storage_adaptor_test.cpp index 00ca48f2..421f69b7 100644 --- a/test/storage_adaptor_test.cpp +++ b/test/storage_adaptor_test.cpp @@ -56,7 +56,7 @@ void tests() { a.add(1, 5); BOOST_TEST_EQ(a[0], 3); BOOST_TEST_EQ(a[1], 5); - a[1] = 9; + a.set(1, 9); BOOST_TEST_EQ(a[0], 3); BOOST_TEST_EQ(a[1], 9); a.reset(0); @@ -243,27 +243,24 @@ int main() { const auto baseline = db.sum.first; a.reset(10); BOOST_TEST_EQ(db.sum.first, baseline); // nothing allocated yet - // query on const reference does not allocate - BOOST_TEST_EQ(static_cast(a)[0], 0); - // query on writeable reference allocates - BOOST_TEST_EQ(a[9], 0); - BOOST_TEST_EQ(db.sum.first, baseline + 1); - - a(5); + // queries do not allocate BOOST_TEST_EQ(a[0], 0); - BOOST_TEST_EQ(a[5], 1); BOOST_TEST_EQ(a[9], 0); - BOOST_TEST_EQ(db.sum.first, baseline + 3); - a *= 2; + BOOST_TEST_EQ(db.sum.first, baseline); + + a(5); // causes one allocation + BOOST_TEST_EQ(a[5], 1); + BOOST_TEST_EQ(db.sum.first, baseline + 1); + a *= 2; // no additional allocations from multiplication BOOST_TEST_EQ(a[5], 2); - BOOST_TEST_EQ(db.sum.first, baseline + 3); + BOOST_TEST_EQ(db.sum.first, baseline + 1); auto b = storage_adaptor>(); b.reset(5); b(2); a = b; // only one new allocation for non-zero value - BOOST_TEST_EQ(db.sum.first, baseline + 4); + BOOST_TEST_EQ(db.sum.first, baseline + 2); } return boost::report_errors(); diff --git a/test/utility_histogram.hpp b/test/utility_histogram.hpp index 965062cf..33e3b163 100644 --- a/test/utility_histogram.hpp +++ b/test/utility_histogram.hpp @@ -9,10 +9,7 @@ #include #include -#include -#include #include -#include #include namespace boost {