diff --git a/doc/concepts.qbk b/doc/concepts.qbk index 06661e99..b5fa0a97 100644 --- a/doc/concepts.qbk +++ b/doc/concepts.qbk @@ -37,13 +37,14 @@ A `storage_type` is required to: * be default/copy/move constructable * be copy/move assignable * be equal comparable -* have a nested type `bin_type`, the external type used to represent the bin count (internally it may be a different type) +* have a nested type `element_type`, which represent the bin count +* have a nested type `const_reference`, its const reference version * have a constructor `storage_type(std::size_t n)`, which prepares the storage of `n` bins. * have the following methods and operators: * `std::size_t size() const` * `void increase(std::size_t index)` * `template void add(std::size_t index, const T& x)` - * `bin_type operator[](std::size_t index) const` + * `const_reference operator[](std::size_t index) const` * `storage_type& operator+=(const storage_type& other)` * `storage_type& operator*=(const value_type x)` * optionally, it can have the following method to support weighted increments: diff --git a/include/boost/histogram/axis/any.hpp b/include/boost/histogram/axis/any.hpp index 370e71f6..ec5466da 100644 --- a/include/boost/histogram/axis/any.hpp +++ b/include/boost/histogram/axis/any.hpp @@ -67,10 +67,7 @@ struct index : public static_visitor { throw std::runtime_error(::boost::histogram::detail::cat( "cannot convert value_type ", boost::typeindex::type_id().pretty_name(), - " of ", - boost::typeindex::type_id().pretty_name(), - " to double") - ); + " of ", boost::typeindex::type_id().pretty_name(), " to double")); } }; @@ -78,21 +75,22 @@ struct lower : public static_visitor { int idx; lower(int i) : idx(i) {} template double operator()(const Axis &a) const { - return impl(std::integral_constant::value && - std::is_same>::value) - >(), a); + return impl( + std::integral_constant< + bool, + (std::is_convertible::value && + std::is_same>::value)>(), + a); } template double impl(std::true_type, const Axis &a) const { - return a.lower(idx) ; + return a.lower(idx); } template double impl(std::false_type, const Axis &) const { throw std::runtime_error(::boost::histogram::detail::cat( - "cannot use ", - boost::typeindex::type_id().pretty_name(), + "cannot use ", boost::typeindex::type_id().pretty_name(), " with generic boost::histogram::axis::any interface, use" - " boost::histogram::axis::cast to access underlying axis type") - ); + " boost::histogram::axis::cast to access underlying axis type")); } }; } // namespace detail @@ -157,9 +155,7 @@ public: return apply_visitor(detail::lower(idx), *this); } - bin_type operator[](const int idx) const { - return bin_type(idx, *this); - } + bin_type operator[](const int idx) const { return bin_type(idx, *this); } bool operator==(const any &rhs) const { return base_type::operator==(static_cast(rhs)); diff --git a/include/boost/histogram/axis/axis.hpp b/include/boost/histogram/axis/axis.hpp index 7aec7abd..f5bbc234 100644 --- a/include/boost/histogram/axis/axis.hpp +++ b/include/boost/histogram/axis/axis.hpp @@ -249,7 +249,7 @@ public: x = -std::numeric_limits::infinity(); else if (i > n) x = std::numeric_limits::infinity(); - else { + else { const auto z = value_type(i) / n; x = (1.0 - z) * min_ + z * (min_ + delta_ * n); } @@ -552,7 +552,7 @@ public: } /// Returns the value for the bin index (performs a range check). - inline const value_type& value(int idx) const { + inline const value_type &value(int idx) const { auto it = map_->right.find(idx); if (it == map_->right.end()) throw std::out_of_range("category index out of range"); diff --git a/include/boost/histogram/axis/bin_view.hpp b/include/boost/histogram/axis/bin_view.hpp index 5d672d0d..a304f119 100644 --- a/include/boost/histogram/axis/bin_view.hpp +++ b/include/boost/histogram/axis/bin_view.hpp @@ -16,8 +16,7 @@ namespace axis { template class interval_view { public: - interval_view(int idx, const Axis& axis) : - idx_(idx), axis_(axis) {} + interval_view(int idx, const Axis &axis) : idx_(idx), axis_(axis) {} interval_view(const interval_view &) = default; interval_view &operator=(const interval_view &) = default; @@ -26,8 +25,12 @@ public: int idx() const noexcept { return idx_; } - auto lower() const noexcept -> decltype(std::declval().lower(0)) { return axis_.lower(idx_); } - auto upper() const noexcept -> decltype(std::declval().lower(0)) { return axis_.lower(idx_+1); } + auto lower() const noexcept -> decltype(std::declval().lower(0)) { + return axis_.lower(idx_); + } + auto upper() const noexcept -> decltype(std::declval().lower(0)) { + return axis_.lower(idx_ + 1); + } typename Axis::value_type width() const noexcept { return upper() - lower(); } bool operator==(const interval_view &rhs) const noexcept { @@ -41,13 +44,12 @@ public: private: const int idx_; - const Axis& axis_; + const Axis &axis_; }; template class value_view { public: - value_view(int idx, const Axis& axis) : - idx_(idx), axis_(axis) {} + value_view(int idx, const Axis &axis) : idx_(idx), axis_(axis) {} value_view(const value_view &) = default; value_view &operator=(const value_view &) = default; @@ -56,7 +58,7 @@ public: int idx() const noexcept { return idx_; } - auto value() const -> decltype(std::declval().value(0)) { + auto value() const -> decltype(std::declval().value(0)) { return axis_.value(idx_); } @@ -71,7 +73,7 @@ public: private: const int idx_; - const Axis& axis_; + const Axis &axis_; }; } // namespace axis diff --git a/include/boost/histogram/dynamic_histogram.hpp b/include/boost/histogram/dynamic_histogram.hpp index 87124db2..b23b5330 100644 --- a/include/boost/histogram/dynamic_histogram.hpp +++ b/include/boost/histogram/dynamic_histogram.hpp @@ -58,8 +58,9 @@ class histogram { public: using any_axis_type = axis::any; using axes_type = std::vector; - using bin_type = typename Storage::bin_type; - using bin_iterator = iterator_over; + using element_type = typename Storage::element_type; + using const_reference = typename Storage::const_reference; + using const_iterator = iterator_over; public: histogram() = default; @@ -175,7 +176,8 @@ public: } } - template bin_type bin(Indices &&... indices) const { + template + const_reference bin(Indices &&... indices) const { if (dim() != sizeof...(indices)) throw std::invalid_argument( "value arguments does not match histogram dimension"); @@ -187,7 +189,7 @@ public: } template > - bin_type bin(Iterator begin, Iterator end) const { + const_reference bin(Iterator begin, Iterator end) const { if (dim() != std::distance(begin, end)) throw std::invalid_argument( "iterator range in bin(...) does not match histogram dimension"); @@ -205,8 +207,8 @@ public: std::size_t bincount() const noexcept { return storage_.size(); } /// Sum of all counts in the histogram - bin_type sum() const noexcept { - bin_type result(0); + element_type sum() const noexcept { + element_type result(0); // don't use bincount() here, so sum() still works in a moved-from object for (std::size_t i = 0, n = storage_.size(); i < n; ++i) { result += storage_[i]; @@ -263,9 +265,13 @@ public: return reduce_impl(b); } - bin_iterator begin() const noexcept { return bin_iterator(*this, storage_); } + const_iterator begin() const noexcept { + return const_iterator(*this, storage_, true); + } - bin_iterator end() const noexcept { return bin_iterator(storage_); } + const_iterator end() const noexcept { + return const_iterator(*this, storage_); + } private: axes_type axes_; diff --git a/include/boost/histogram/iterator.hpp b/include/boost/histogram/iterator.hpp index afb24c33..d7b72a1d 100644 --- a/include/boost/histogram/iterator.hpp +++ b/include/boost/histogram/iterator.hpp @@ -7,7 +7,10 @@ #ifndef _BOOST_HISTOGRAM_VALUE_ITERATOR_HPP_ #define _BOOST_HISTOGRAM_VALUE_ITERATOR_HPP_ +#include +#include #include +#include #include #include @@ -15,84 +18,141 @@ namespace boost { namespace histogram { namespace detail { -class multi_index { - struct dim_t { - int idx, size; - std::size_t stride; - }; - struct dim_visitor { - mutable std::size_t stride; - std::vector &dims; - template void operator()(const Axis &a) const { - dims.emplace_back(dim_t{0, a.size(), stride}); - stride *= a.shape(); - } - }; +struct dim_t { + int idx, size; + std::size_t stride; +}; -public: - multi_index() = default; - - template - explicit multi_index(const Histogram &h) : idx_(0) { - dims_.reserve(h.dim()); - h.for_each_axis(dim_visitor{1, dims_}); +std::size_t inc(dim_t *iter, dim_t *end) noexcept { + for (; iter != end; ++iter) { + ++(iter->idx); + if (iter->idx < iter->size) + break; + iter->idx = 0; } + if (iter == end) + return std::numeric_limits::max(); + + std::size_t idx = 0; + for (; iter != end; ++iter) + idx += iter->idx * iter->stride; + return idx; +} + +struct dim_visitor { + mutable std::size_t stride; + mutable dim_t *dims; + template void operator()(const Axis &a) const noexcept { + *dims++ = dim_t{0, a.size(), stride}; + stride *= a.shape(); + } +}; + +class multi_index { +public: int idx(unsigned dim = 0) const noexcept { return dims_[dim].idx; } unsigned dim() const noexcept { return dims_.size(); } protected: - void increment() noexcept { - auto iter = dims_.begin(); - for (; iter != dims_.end(); ++iter) { - ++(iter->idx); - if (iter->idx < iter->size) - break; - iter->idx = 0; - } - if (iter == dims_.end()) - idx_ = std::numeric_limits::max(); - else { - idx_ = 0; - for (; iter != dims_.end(); ++iter) - idx_ += iter->idx * iter->stride; - } + multi_index() = default; + + template + explicit multi_index(const Histogram &h) : idx_(0), dims_(h.dim()) { + h.for_each_axis(dim_visitor{1, dims_.data()}); } - std::size_t idx_ = std::numeric_limits::max(); + void increment() noexcept { + idx_ = inc(dims_.data(), dims_.data() + dims_.size()); + } + + std::size_t idx_; std::vector dims_; }; } // namespace detail -template +template class iterator_over - : public iterator_facade, - typename Storage::bin_type, forward_traversal_tag, - typename Storage::bin_type>, + : public iterator_facade< + iterator_over, typename Storage::element_type, + forward_traversal_tag, typename Storage::const_reference>, public detail::multi_index { public: /// begin iterator - template - iterator_over(const Histogram &h, const Storage &s) - : detail::multi_index(h), s_(s) {} + iterator_over(const Histogram &h, const Storage &s, bool) + : detail::multi_index(h), histogram_(h), storage_(s) {} /// end iterator - explicit iterator_over(const Storage &s) : s_(s) {} + iterator_over(const Histogram &h, const Storage &s) + : histogram_(h), storage_(s) { + idx_ = std::numeric_limits::max(); + } iterator_over(const iterator_over &) = default; iterator_over &operator=(const iterator_over &) = default; -private: - bool equal(const iterator_over &other) const noexcept { - return &s_ == &(other.s_) && idx_ == other.idx_; + template + auto bin(mpl::int_) const + -> decltype(std::declval().axis(mpl::int_())[0]) { + return histogram_.axis(mpl::int_())[dims_[dim].idx]; } - typename Storage::bin_type dereference() const { return s_[idx_]; } - const Storage &s_; +private: + bool equal(const iterator_over &rhs) const noexcept { + return &storage_ == &rhs.storage_ && idx_ == rhs.idx_; + } + typename Storage::const_reference dereference() const { + return storage_[idx_]; + } + + const Histogram &histogram_; + const Storage &storage_; friend class ::boost::iterator_core_access; }; + +template +class iterator_over, Storage> + : public iterator_facade< + iterator_over, Storage>, + typename Storage::element_type, forward_traversal_tag, + typename Storage::const_reference>, + public detail::multi_index { + +public: + /// begin iterator + iterator_over(const dynamic_histogram &h, const Storage &s, + bool) + : detail::multi_index(h), histogram_(h), storage_(s) {} + + /// end iterator + iterator_over(const dynamic_histogram &h, const Storage &s) + : histogram_(h), storage_(s) { + idx_ = std::numeric_limits::max(); + } + + iterator_over(const iterator_over &) = default; + iterator_over &operator=(const iterator_over &) = default; + + const typename dynamic_histogram::any_axis_type & + bin(unsigned dim = 0) const { + return histogram_.axis(dim)[dims_[dim].idx]; + } + +private: + bool equal(const iterator_over &rhs) const noexcept { + return &storage_ == &rhs.storage_ && idx_ == rhs.idx_; + } + typename Storage::const_reference dereference() const { + return storage_[idx_]; + } + + const dynamic_histogram &histogram_; + const Storage &storage_; + friend class ::boost::iterator_core_access; +}; + } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/static_histogram.hpp b/include/boost/histogram/static_histogram.hpp index d5e49de2..046bfe04 100644 --- a/include/boost/histogram/static_histogram.hpp +++ b/include/boost/histogram/static_histogram.hpp @@ -52,8 +52,9 @@ class histogram { public: using axes_type = typename fusion::result_of::as_vector::type; - using bin_type = typename Storage::bin_type; - using bin_iterator = iterator_over; + using element_type = typename Storage::element_type; + using const_reference = typename Storage::const_reference; + using const_iterator = iterator_over; histogram() = default; histogram(const histogram &rhs) = default; @@ -161,7 +162,7 @@ public: } template - bin_type bin(const Indices &... indices) const { + const_reference bin(const Indices &... indices) const { static_assert(sizeof...(indices) == axes_size::value, "number of arguments does not match histogram dimension"); std::size_t idx = 0, stride = 1; @@ -179,8 +180,8 @@ public: std::size_t bincount() const noexcept { return storage_.size(); } /// Sum of all counts in the histogram - bin_type sum() const noexcept { - bin_type result(0); + element_type sum() const noexcept { + element_type result(0); for (std::size_t i = 0, n = storage_.size(); i < n; ++i) result += storage_[i]; return result; @@ -242,9 +243,13 @@ public: return hr; } - bin_iterator begin() const noexcept { return bin_iterator(*this, storage_); } + const_iterator begin() const noexcept { + return const_iterator(*this, storage_, true); + } - bin_iterator end() const noexcept { return bin_iterator(storage_); } + const_iterator end() const noexcept { + return const_iterator(*this, storage_); + } private: axes_type axes_; diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index c6c1bd08..eeee6643 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -388,7 +388,8 @@ class adaptive_storage { using buffer_type = detail::any_array; public: - using bin_type = detail::wcount; + using element_type = detail::wcount; + using const_reference = element_type; explicit adaptive_storage(std::size_t s) : buffer_(detail::array(s)) {} @@ -401,7 +402,7 @@ public: template explicit adaptive_storage(const RHS &rhs) : buffer_(detail::array(rhs.size())) { - using T = typename RHS::bin_type; + using T = typename RHS::element_type; for (std::size_t i = 0, n = rhs.size(); i < n; ++i) { apply_visitor(detail::assign_visitor(buffer_, i, rhs[i]), buffer_); } @@ -413,7 +414,7 @@ public: if (size() != n) { buffer_ = detail::array(n); } - using T = typename RHS::bin_type; + using T = typename RHS::element_type; for (std::size_t i = 0; i < n; ++i) { apply_visitor(detail::assign_visitor(buffer_, i, rhs[i]), buffer_); } @@ -428,12 +429,12 @@ public: apply_visitor(detail::increase_visitor(buffer_, i), buffer_); } - void add(std::size_t i, const bin_type &x) { + void add(std::size_t i, const element_type &x) { if (x.has_trivial_variance()) { apply_visitor(detail::radd_visitor(buffer_, i, x.value()), buffer_); } else { - apply_visitor(detail::radd_visitor(buffer_, i, x), buffer_); + apply_visitor(detail::radd_visitor(buffer_, i, x), buffer_); } } @@ -447,7 +448,7 @@ public: buffer_); } - bin_type operator[](std::size_t i) const { + const_reference operator[](std::size_t i) const { return apply_visitor(detail::bin_visitor(i), buffer_); } @@ -471,7 +472,7 @@ public: template adaptive_storage &operator+=(const RHS &rhs) { for (auto i = 0ul, n = size(); i < n; ++i) apply_visitor( - detail::radd_visitor(buffer_, i, rhs[i]), + detail::radd_visitor(buffer_, i, rhs[i]), buffer_); return *this; } diff --git a/include/boost/histogram/storage/array_storage.hpp b/include/boost/histogram/storage/array_storage.hpp index 0bc82c90..310b7f34 100644 --- a/include/boost/histogram/storage/array_storage.hpp +++ b/include/boost/histogram/storage/array_storage.hpp @@ -24,15 +24,17 @@ namespace histogram { template class array_storage { public: - using bin_type = T; + using element_type = T; + using const_reference = const T &; - explicit array_storage(std::size_t s) : size_(s), array_(new bin_type[s]) { - std::fill(array_.get(), array_.get() + s, bin_type(0)); + explicit array_storage(std::size_t s) + : size_(s), array_(new element_type[s]) { + std::fill(array_.get(), array_.get() + s, element_type(0)); } array_storage() = default; array_storage(const array_storage &other) - : size_(other.size()), array_(new bin_type[other.size()]) { + : size_(other.size()), array_(new element_type[other.size()]) { std::copy(other.array_.get(), other.array_.get() + size_, array_.get()); } array_storage &operator=(const array_storage &other) { @@ -58,14 +60,14 @@ public: explicit array_storage(const S &other) { reset(other.size()); for (std::size_t i = 0; i < size_; ++i) - array_[i] = static_cast(other[i]); + array_[i] = static_cast(other[i]); } template > array_storage &operator=(const S &other) { reset(other.size()); for (std::size_t i = 0; i < size_; ++i) - array_[i] = static_cast(other[i]); + array_[i] = static_cast(other[i]); return *this; } @@ -77,7 +79,7 @@ public: array_[i] += x; } - const bin_type &operator[](std::size_t i) const noexcept { return array_[i]; } + const_reference operator[](std::size_t i) const noexcept { return array_[i]; } template bool operator==(const array_storage &rhs) const noexcept { @@ -100,11 +102,11 @@ public: private: std::size_t size_ = 0; - std::unique_ptr array_; + std::unique_ptr array_; void reset(std::size_t size) { size_ = size; - array_.reset(new bin_type[size]); + array_.reset(new element_type[size]); } template friend class array_storage; diff --git a/src/python/histogram.cpp b/src/python/histogram.cpp index 604c6fd8..1116b227 100644 --- a/src/python/histogram.cpp +++ b/src/python/histogram.cpp @@ -334,21 +334,21 @@ std::string histogram_repr(const pyhistogram &h) { return os.str(); } -double bin_type_value(const pyhistogram::bin_type& b) { +double element_value(const pyhistogram::element_type& b) { return b.value(); } -double bin_type_variance(const pyhistogram::bin_type& b) { +double element_variance(const pyhistogram::element_type& b) { return b.variance(); } void register_histogram() { bp::docstring_options dopt(true, true, false); - bp::class_( - "bin_type", "Holds value and variance of bin count.", bp::no_init) - .add_property("value", bin_type_value) - .add_property("variance", bin_type_variance); + bp::class_( + "element", "Holds value and variance of bin count.", bp::no_init) + .add_property("value", element_value) + .add_property("variance", element_variance); bp::class_>( "histogram", "N-dimensional histogram for real-valued data.", bp::no_init) diff --git a/test/adaptive_storage_test.cpp b/test/adaptive_storage_test.cpp index 4fa94154..f913a013 100644 --- a/test/adaptive_storage_test.cpp +++ b/test/adaptive_storage_test.cpp @@ -388,7 +388,7 @@ int main() { BOOST_TEST_EQ(a[0].variance(), 9); BOOST_TEST_EQ(a[1].value(), 0); BOOST_TEST_EQ(a[1].variance(), 0); - a.add(1, adaptive_storage::bin_type(2, 5)); + a.add(1, adaptive_storage::element_type(2, 5)); BOOST_TEST_EQ(a[0].value(), 3); BOOST_TEST_EQ(a[0].variance(), 9); BOOST_TEST_EQ(a[1].value(), 2); diff --git a/test/axis_test.cpp b/test/axis_test.cpp index b2a46c86..58013db3 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -224,7 +224,8 @@ int main() { test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3); test_axis_iterator(any_axis_type(axis::regular<>(5, 0, 1)), 0, 5); - BOOST_TEST_THROWS(any_axis_type(axis::category<>({A, B, C})).lower(0), std::runtime_error); + BOOST_TEST_THROWS(any_axis_type(axis::category<>({A, B, C})).lower(0), + std::runtime_error); } // any_axis_type_copyable