diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index dd3795d3..4c2c7700 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -36,7 +36,8 @@ template ().size(), struct requires_storage {}; template struct has_variance_support { - template ().variance())> + template ().value(), + std::declval().variance())> struct SFINAE {}; template static std::true_type Test(SFINAE *); template static std::false_type Test(...); @@ -46,11 +47,6 @@ template struct has_variance_support { template using has_variance_support_t = typename has_variance_support::type; -template -using requires_variance_support = - typename std::enable_if::value, - ReturnValue>::type; - template (), ++std::declval())> struct is_iterator {}; @@ -59,15 +55,14 @@ template ()), std::end(std::declval()))> struct is_sequence {}; -template struct combine { - using type = - typename mpl::copy_if>, - mpl::back_inserter>::type; -}; +template struct union_ : + mpl::copy_if>, + mpl::back_inserter> +{}; template -using combine_t = typename combine::type; +using union_t = typename union_::type; struct bool_mask_op { std::vector &b; @@ -97,12 +92,12 @@ void axes_assign_subset(Axes1 &axes1, const Axes &axes) { } template -using unique_sorted = +using unique_sorted_t = typename mpl::unique::type, std::is_same>::type; template -using axes_select = +using axes_select_t = typename mpl::transform>::type; } // namespace detail diff --git a/include/boost/histogram/dynamic_histogram.hpp b/include/boost/histogram/dynamic_histogram.hpp index c7482442..31491380 100644 --- a/include/boost/histogram/dynamic_histogram.hpp +++ b/include/boost/histogram/dynamic_histogram.hpp @@ -349,7 +349,7 @@ private: } template void impl(std::false_type, const Axis &) const { - throw std::runtime_error( + throw std::invalid_argument( detail::cat("fill argument not convertible to axis value type: ", boost::typeindex::type_id().pretty_name(), ", ", boost::typeindex::type_id().pretty_name())); @@ -417,50 +417,50 @@ private: }; template -histogram>> +histogram>> make_dynamic_histogram(Axes &&... axes) { return histogram>>( + detail::union_t>>( std::forward(axes)...); } template -histogram>, +histogram>, array_storage>> make_dynamic_weighted_histogram(Axes &&... axes) { return histogram>, + detail::union_t>, array_storage> >(std::forward(axes)...); } template -histogram>, +histogram>, Storage> make_dynamic_histogram_with(Axes &&... axes) { return histogram>, + detail::union_t>, Storage>(std::forward(axes)...); } template > -histogram> make_dynamic_histogram(Iterator begin, Iterator end) { return histogram< dynamic_tag, - detail::combine_t>( + detail::union_t>( begin, end); } template > -histogram, array_storage>> make_dynamic_weighted_histogram(Iterator begin, Iterator end) { return histogram< dynamic_tag, - detail::combine_t, + detail::union_t, array_storage>>( begin, end); } @@ -469,12 +469,12 @@ template > histogram< dynamic_tag, - detail::combine_t, + detail::union_t, Storage> make_dynamic_histogram_with(Iterator begin, Iterator end) { return histogram< dynamic_tag, - detail::combine_t, + detail::union_t, Storage>(begin, end); } diff --git a/include/boost/histogram/static_histogram.hpp b/include/boost/histogram/static_histogram.hpp index 642f39d1..010d0894 100644 --- a/include/boost/histogram/static_histogram.hpp +++ b/include/boost/histogram/static_histogram.hpp @@ -226,11 +226,11 @@ public: /// Returns a lower-dimensional histogram template auto reduce_to(mpl::int_, Rest...) const -> histogram< - static_tag, detail::axes_select, Rest...>>, + static_tag, detail::axes_select_t, Rest...>>, Storage> { using HR = histogram, Rest...>>, + detail::axes_select_t, Rest...>>, Storage>; typename HR::axes_type axes; detail::axes_assign_subset, Rest...>>(axes, axes_); diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index d5cccb67..0627098d 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -221,33 +221,18 @@ struct increase_visitor : public static_visitor { void operator()(array &lhs) const { ++lhs[idx]; } }; -struct value_visitor : public static_visitor { +struct bin_visitor : public static_visitor { const std::size_t idx; - value_visitor(const std::size_t i) : idx(i) {} + bin_visitor(const std::size_t i) : idx(i) {} - template double operator()(const Array &b) const { - return static_cast(b[idx]); + template wcount operator()(const Array &b) const { + return wcount(static_cast(b[idx])); } - double operator()(const array & /*b*/) const { return 0; } + wcount operator()(const array & /*b*/) const { return wcount(0.0); } - double operator()(const array &b) const { - return b[idx].value(); - } -}; - -struct variance_visitor : public static_visitor { - const std::size_t idx; - variance_visitor(const std::size_t i) : idx(i) {} - - template double operator()(const Array &b) const { - return static_cast(b[idx]); - } - - double operator()(const array & /*b*/) const { return 0; } - - double operator()(const array &b) const { - return b[idx].variance(); + wcount operator()(const array &b) const { + return b[idx]; } }; @@ -422,23 +407,22 @@ public: explicit adaptive_storage(const RHS &rhs) : buffer_(detail::array(rhs.size())) { using T = typename RHS::bin_type; - for (auto i = 0ul, n = rhs.size(); i < n; ++i) { + for (std::size_t i = 0, n = rhs.size(); i < n; ++i) { apply_visitor(detail::assign_visitor(buffer_, i, rhs[i]), buffer_); } } template adaptive_storage &operator=(const RHS &rhs) { + // no check for self-assign needed, default operator above is better match + const auto n = rhs.size(); + if (size() != n) { + buffer_ = detail::array(n); + } using T = typename RHS::bin_type; - if (static_cast(this) != static_cast(&rhs)) { - const auto n = rhs.size(); - if (size() != n) { - buffer_ = detail::array(n); - } - for (auto i = 0ul; i < n; ++i) { - apply_visitor(detail::assign_visitor(buffer_, i, rhs[i]), - buffer_); - } + for (std::size_t i = 0; i < n; ++i) { + apply_visitor(detail::assign_visitor(buffer_, i, rhs[i]), + buffer_); } return *this; } @@ -451,6 +435,14 @@ public: apply_visitor(detail::increase_visitor(buffer_, i), buffer_); } + void add(std::size_t i, const bin_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_); + } + } + template void add(std::size_t i, const T &t) { apply_visitor(detail::radd_visitor(buffer_, i, t), buffer_); } @@ -460,18 +452,8 @@ public: buffer_); } - void add(std::size_t i, const bin_type& x) { - if (x.value() == x.variance()) { - apply_visitor(detail::radd_visitor(buffer_, i, x.value()), buffer_); - } else { - apply_visitor(detail::radd_visitor(buffer_, i, x), buffer_); - } - } - bin_type operator[](std::size_t i) const { - // TODO optimize this with a dedicated visitor - return {apply_visitor(detail::value_visitor(i), buffer_), - apply_visitor(detail::variance_visitor(i), buffer_)}; + return apply_visitor(detail::bin_visitor(i), buffer_); } bool operator==(const adaptive_storage &rhs) const { diff --git a/include/boost/histogram/storage/array_storage.hpp b/include/boost/histogram/storage/array_storage.hpp index 43460a0f..b050911b 100644 --- a/include/boost/histogram/storage/array_storage.hpp +++ b/include/boost/histogram/storage/array_storage.hpp @@ -8,10 +8,9 @@ #define _BOOST_HISTOGRAM_STORAGE_ARRAY_HPP_ #include -#include +#include #include #include -#include // forward declaration for serialization namespace boost { @@ -55,16 +54,14 @@ public: template explicit array_storage(const S &other) { reset(other.size()); - for (decltype(size_) i = 0u; i < size_; ++i) { + for (std::size_t i = 0; i < size_; ++i) array_[i] = static_cast(other[i]); - } } template array_storage &operator=(const S &other) { reset(other.size()); - for (decltype(size_) i = 0u; i < size_; ++i) { + for (std::size_t i = 0; i < size_; ++i) array_[i] = static_cast(other[i]); - } return *this; } diff --git a/include/boost/histogram/storage/weight_counter.hpp b/include/boost/histogram/storage/weight_counter.hpp index b24dda53..49aa68ce 100644 --- a/include/boost/histogram/storage/weight_counter.hpp +++ b/include/boost/histogram/storage/weight_counter.hpp @@ -8,6 +8,7 @@ #define _BOOST_HISTOGRAM_STORAGE_WEIGHT_COUNTER_HPP_ #include +#include namespace boost { @@ -27,17 +28,7 @@ public: weight_counter &operator=(const weight_counter &) = default; weight_counter &operator=(weight_counter &&) = default; - weight_counter(RealType value, RealType variance) : w(value), w2(variance) {} - - template - explicit weight_counter(const T &t) - : w(static_cast(t)), w2(static_cast(t)) {} - - template weight_counter &operator=(const T &t) { - w = static_cast(t); - w2 = static_cast(t); - return *this; - } + weight_counter(const RealType& value, const RealType& variance) : w(value), w2(variance) {} weight_counter &operator++() { ++w; @@ -45,27 +36,28 @@ public: return *this; } - template - weight_counter &operator+=(const weight_counter &rhs) { - w += rhs.w; - w2 += rhs.w2; - return *this; - } - - template - weight_counter &operator+=(const detail::weight_t &rhs) { - w += rhs.value; - w2 += rhs.value * rhs.value; - return *this; - } - weight_counter &operator+=(const RealType &x) { w += x; w2 += x; return *this; } - weight_counter &operator*=(const RealType x) { + template + weight_counter &operator+=(const weight_counter &rhs) { + w += static_cast(rhs.w); + w2 += static_cast(rhs.w2); + return *this; + } + + template + weight_counter &operator+=(const detail::weight_t &rhs) { + const auto x = static_cast(rhs.value); + w += x; + w2 += x * x; + return *this; + } + + weight_counter &operator*=(const RealType &x) { w *= x; w2 *= x * x; return *this; @@ -77,11 +69,11 @@ public: bool operator!=(const weight_counter &rhs) const { return !operator==(rhs); } - template bool operator==(const weight_counter &rhs) const { + template bool operator==(const weight_counter &rhs) const { return w == rhs.w && w2 == rhs.w2; } - template bool operator!=(const weight_counter &rhs) const { + template bool operator!=(const weight_counter &rhs) const { return !operator==(rhs); } @@ -93,9 +85,27 @@ public: return !operator==(rhs); } - RealType value() const noexcept { return w; } - RealType variance() const noexcept { return w2; } - template explicit operator T() const noexcept { return w; } + const RealType& value() const noexcept { return w; } + const RealType& variance() const noexcept { return w2; } + + bool has_trivial_variance() const noexcept { return w == w2; } + + // conversion + template explicit weight_counter(const T &t) : w(static_cast(t)), w2(w) {} + explicit operator RealType() const { + if (!has_trivial_variance()) + throw std::logic_error("cannot convert weight_counter to RealType, value and variance differ"); + return w; + } + template explicit operator T() const { + if (!has_trivial_variance()) + throw std::logic_error("cannot convert weight_counter to RealType, value and variance differ"); + return w; + } + template weight_counter &operator=(const T &x) { + w = w2 = static_cast(x); + return *this; + } private: friend class ::boost::serialization::access; diff --git a/src/python/histogram.cpp b/src/python/histogram.cpp index 65ed16e7..604c6fd8 100644 --- a/src/python/histogram.cpp +++ b/src/python/histogram.cpp @@ -334,14 +334,21 @@ std::string histogram_repr(const pyhistogram &h) { return os.str(); } +double bin_type_value(const pyhistogram::bin_type& b) { + return b.value(); +} + +double bin_type_variance(const pyhistogram::bin_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", &pyhistogram::bin_type::value) - .add_property("variance", &pyhistogram::bin_type::variance) - ; + .add_property("value", bin_type_value) + .add_property("variance", bin_type_variance); bp::class_>( "histogram", "N-dimensional histogram for real-valued data.", bp::no_init) diff --git a/test/meta_test.cpp b/test/meta_test.cpp index b6cb9cca..e8e02377 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include using namespace boost; @@ -9,25 +10,50 @@ using namespace boost::mpl; using namespace boost::histogram::detail; int main() { + // unique_sorted { typedef vector_c numbers; typedef vector_c expected; - using result = unique_sorted; + using result = unique_sorted_t; BOOST_MPL_ASSERT((equal>)); } - struct no_variance_method { - using bin_type = int; - }; + // union + { + typedef vector main_vector; + typedef vector aux_vector; + using result = union_t; - struct variance_method { - struct bin_type { double variance() const; }; - }; + typedef vector expected; + BOOST_MPL_ASSERT((equal>)); + } - BOOST_TEST_EQ(typename has_variance_support::type(), - false); - BOOST_TEST_EQ(typename has_variance_support::type(), true); + // has_variance_support + { + struct no_methods {}; + + struct value_method { + const double& value() const; + }; + + struct variance_method { + const double& variance() const; + }; + + struct value_and_variance_methods { + const double& value() const; + const double& variance() const; + }; + + BOOST_TEST_EQ(typename has_variance_support::type(), + false); + BOOST_TEST_EQ(typename has_variance_support::type(), + false); + BOOST_TEST_EQ(typename has_variance_support::type(), + false); + BOOST_TEST_EQ(typename has_variance_support::type(), true); + } return boost::report_errors(); } \ No newline at end of file