diff --git a/doc/getting_started.qbk b/doc/getting_started.qbk index 1ceea984..81e6ee88 100644 --- a/doc/getting_started.qbk +++ b/doc/getting_started.qbk @@ -41,20 +41,21 @@ int main(int, char**) { iterate over bins, loop excludes under- and overflow bins - index 0_c is a compile-time number, the only way in C++ to make axis(...) to return a different type for each index - - for-loop yields instances of `std::pair`, where - `bin_type` usually is a semi-open interval representing the bin, - whose edges can be accessed with methods `lower()` and `upper()`, - but the [bin type] depends on the axis, look it up in the reference + - for-loop yields instances of `bin_type`, usually is a semi-open + interval representing the bin, whose edges can be accessed with + methods `lower()` and `upper()`, but the choice depends on the + axis type, please look it up in the reference - `bin(index)` method returns the bin counter at index, you can then access its `value() and `variance()` methods; the first returns the - actual count, the second returns a variance estimate of the count + actual count, the second returns a variance estimate of the count; + a bin_type is convertible into an index (see Rationale section for what this means) */ - for (auto it = h.axis(0_c).begin(); it; ++it) { - std::cout << "bin " << it.idx() << " x in [" - << it->lower() << ", " << it->upper() << "): " - << h.bin(it).value() << " +/- " - << std::sqrt(h.bin(it).variance()) + for (auto bin : h.axis(0_c)) { + std::cout << "bin " << bin.idx() << " x in [" + << bin.lower() << ", " << bin.upper() << "): " + << h.bin(bin).value() << " +/- " + << std::sqrt(h.bin(bin).variance()) << std::endl; } @@ -131,11 +132,11 @@ int main() { this is here the case for the category axis */ using cas = bh::axis::category; - for (auto cit = bh::axis::cast(h.axis(0)).begin(); cit; ++cit) { - std::printf("%s\n", cit->c_str()); - for (auto yit = h.axis(2).begin(); yit; ++yit) { // rows - for (auto xit = h.axis(1).begin(); xit; ++xit) { // columns - std::printf("%3.0f ", h.bin(cit, xit, yit).value()); + for (auto cbin : bh::axis::cast(h.axis(0))) { + std::printf("%s\n", cbin.value().c_str()); + for (auto ybin : h.axis(2)) { // rows + for (auto xbin : h.axis(1)) { // columns + std::printf("%3.0f ", h.bin(cbin, xbin, ybin).value()); } std::printf("\n"); } diff --git a/doc/guide.qbk b/doc/guide.qbk index 565f42e1..44dc1140 100644 --- a/doc/guide.qbk +++ b/doc/guide.qbk @@ -374,19 +374,19 @@ int main() { << hr1.sum().value() << std::endl; // prints: 3 3 3 - for (auto yit = h.axis(1_c).begin(); yit; ++yit) { - for (auto xit = h.axis(0_c).begin(); xit; ++xit) { - std::cout << h.bin(xit, yit).value() << " "; + for (auto ybin : h.axis(1_c)) { + for (auto xbin : h.axis(0_c)) { + std::cout << h.bin(xbin, ybin).value() << " "; } std::cout << std::endl; } - for (auto it = hr0.axis().begin(); it; ++it) - std::cout << hr0.bin(it).value() << " "; + for (auto abin : hr0.axis()) + std::cout << hr0.bin(abin).value() << " "; std::cout << std::endl; - for (auto it = hr1.axis().begin(); it; ++it) - std::cout << hr1.bin(it).value() << " "; + for (auto abin : hr1.axis()) + std::cout << hr1.bin(abin).value() << " "; std::cout << std::endl; } `` @@ -769,9 +769,9 @@ int main() { h.fill("1"); h.fill("9"); - for (auto it = h.axis().begin(); it; ++it) { - std::cout << "bin " << it.idx() << " [" << it->lower() << ", " - << it->upper() << ") " << h.bin(it).value() + for (auto bin : h.axis()) { + std::cout << "bin " << bin.idx() << " [" << bin.lower() << ", " + << bin.upper() << ") " << h.bin(bin).value() << std::endl; } diff --git a/include/boost/histogram/axis/any.hpp b/include/boost/histogram/axis/any.hpp index 9552f26a..370e71f6 100644 --- a/include/boost/histogram/axis/any.hpp +++ b/include/boost/histogram/axis/any.hpp @@ -7,7 +7,7 @@ #ifndef _BOOST_HISTOGRAM_AXIS_ANY_HPP_ #define _BOOST_HISTOGRAM_AXIS_ANY_HPP_ -#include +#include #include #include #include @@ -74,26 +74,24 @@ struct index : public static_visitor { } }; -struct eval : public static_visitor> { - template std::function operator()(const Axis &a) const { +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< - typename Axis::bin_type, - interval_view - >::value) + std::is_same>::value) >(), a); } - template std::function impl(std::true_type, const Axis &a) const { - return [&a](int i) -> double { return a[i].lower(); } ; + template double impl(std::true_type, const Axis &a) const { + return a.lower(idx) ; } - template std::function impl(std::false_type, const Axis &) const { + template double impl(std::false_type, const Axis &) const { throw std::runtime_error(::boost::histogram::detail::cat( - "cannot convert bin_type ", - boost::typeindex::type_id().pretty_name(), - " of ", + "cannot use ", boost::typeindex::type_id().pretty_name(), - " to interval_view") + " with generic boost::histogram::axis::any interface, use" + " boost::histogram::axis::cast to access underlying axis type") ); } }; @@ -106,7 +104,7 @@ template class any : public make_variant_over::type { public: using types = typename base_type::types; using value_type = double; - using bin_type = interval_view; + using bin_type = interval_view; using const_iterator = iterator_over; using const_reverse_iterator = reverse_iterator_over; @@ -155,9 +153,12 @@ public: // this only works for axes with compatible bin type // and will throw a runtime_error otherwise - bin_type operator[](const int i) const { - auto eval = apply_visitor(detail::eval(), *this); - return bin_type(i, eval); + double lower(int idx) const { + return apply_visitor(detail::lower(idx), *this); + } + + bin_type operator[](const int idx) const { + return bin_type(idx, *this); } bool operator==(const any &rhs) const { diff --git a/include/boost/histogram/axis/axis.hpp b/include/boost/histogram/axis/axis.hpp index 783bcf27..87f7be79 100644 --- a/include/boost/histogram/axis/axis.hpp +++ b/include/boost/histogram/axis/axis.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -202,7 +202,7 @@ class regular : public axis_base_uoflow>, public: using value_type = RealType; - using bin_type = interval_view; + using bin_type = interval_view; /** Construct axis with n bins over real range [lower, upper). * @@ -241,19 +241,23 @@ public: : -1; } - /// Returns the bin interval. - bin_type operator[](int idx) const noexcept { - return bin_type(idx, [this](int i) { - const auto n = base_type::size(); - if (i < 0) - return this->inverse(-std::numeric_limits::infinity()); - if (i > n) - return this->inverse(std::numeric_limits::infinity()); + /// Returns lower edge of bin. + inline value_type lower(int i) const noexcept { + const auto n = base_type::size(); + value_type x; + if (i < 0) + x = -std::numeric_limits::infinity(); + else if (i > n) + x = std::numeric_limits::infinity(); + else { const auto z = value_type(i) / n; - return this->inverse((1.0 - z) * min_ + z * (min_ + delta_ * n)); - }); + x = (1.0 - z) * min_ + z * (min_ + delta_ * n); + } + return Transform::inverse(x); } + bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } + bool operator==(const regular &o) const noexcept { return base_type::operator==(o) && Transform::operator==(o) && min_ == o.min_ && delta_ == o.delta_; @@ -283,7 +287,7 @@ class circular : public axis_base> { public: using value_type = RealType; - using bin_type = interval_view; + using bin_type = interval_view; /** Constructor for n bins with an optional offset. * @@ -311,12 +315,14 @@ public: return i + (i < 0) * base_type::size(); } - /// Returns the starting edge of the bin. - bin_type operator[](int idx) const { - return bin_type(idx, [this](int i) { - const value_type z = value_type(i) / base_type::size(); - return z * perimeter_ + phase_; - }); + /// Returns lower edge of bin. + inline value_type lower(int i) const noexcept { + const value_type z = value_type(i) / base_type::size(); + return z * perimeter_ + phase_; + } + + inline bin_type operator[](int idx) const noexcept { + return bin_type(idx, *this); } bool operator==(const circular &o) const noexcept { @@ -345,7 +351,7 @@ class variable : public axis_base_uoflow> { public: using value_type = RealType; - using bin_type = interval_view; + using bin_type = interval_view; /** Construct an axis from bin edges. * @@ -395,18 +401,18 @@ public: } /// Returns the starting edge of the bin. - bin_type operator[](int idx) const { - return bin_type(idx, [this](int i) { - if (i < 0) { - return -std::numeric_limits::infinity(); - } - if (i > base_type::size()) { - return std::numeric_limits::infinity(); - } - return x_[i]; - }); + inline value_type lower(int i) const noexcept { + if (i < 0) { + return -std::numeric_limits::infinity(); + } + if (i > base_type::size()) { + return std::numeric_limits::infinity(); + } + return x_[i]; } + bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } + bool operator==(const variable &o) const noexcept { if (!base_type::operator==(o)) { return false; @@ -432,7 +438,7 @@ class integer : public axis_base_uoflow> { public: using value_type = IntType; - using bin_type = interval_view; + using bin_type = interval_view; /** Construct axis over a semi-open integer interval [lower, upper). * @@ -461,19 +467,19 @@ public: return z >= 0 ? (z > base_type::size() ? base_type::size() : z) : -1; } - /// Returns the integer that is mapped to the bin index. - bin_type operator[](int idx) const { - return bin_type(idx, [this](int i) { - if (i < 0) { - return -std::numeric_limits::max(); - } - if (i > base_type::size()) { - return std::numeric_limits::max(); - } - return min_ + i; - }); + /// Returns lower edge of the integral bin. + inline value_type lower(int i) const noexcept { + if (i < 0) { + return -std::numeric_limits::max(); + } + if (i > base_type::size()) { + return std::numeric_limits::max(); + } + return min_ + i; } + bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } + bool operator==(const integer &o) const noexcept { return base_type::operator==(o) && min_ == o.min_; } @@ -498,7 +504,7 @@ template class category : public axis_base> { public: using value_type = T; - using bin_type = T; + using bin_type = value_view; category() = default; category(const category &rhs) @@ -517,7 +523,7 @@ public: * * \param seq sequence of unique values. */ - category(std::initializer_list seq, string_view label = {}) + category(std::initializer_list seq, string_view label = {}) : base_type(seq.size(), label), map_(new map_type()) { int index = 0; for (const auto &x : seq) @@ -546,13 +552,15 @@ public: } /// Returns the value for the bin index (performs a range check). - bin_type operator[](int idx) const { + inline 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"); return it->second; } + bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } + bool operator==(const category &o) const noexcept { return base_type::operator==(o) && std::equal(map_->begin(), map_->end(), o.map_->begin()); diff --git a/include/boost/histogram/axis/bin_view.hpp b/include/boost/histogram/axis/bin_view.hpp new file mode 100644 index 00000000..ce9b6e3b --- /dev/null +++ b/include/boost/histogram/axis/bin_view.hpp @@ -0,0 +1,81 @@ +// 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_AXIS_BIN_VIEW_HPP_ +#define _BOOST_HISTOGRAM_AXIS_BIN_VIEW_HPP_ + +#include +#include + +namespace boost { +namespace histogram { +namespace axis { + +template class interval_view { +public: + interval_view(int idx, const Axis& axis) : + idx_(idx), axis_(axis) {} + + interval_view(const interval_view &) = default; + interval_view &operator=(const interval_view &) = default; + interval_view(interval_view &&) = default; + interval_view &operator=(interval_view &&) = default; + + int idx() const noexcept { return idx_; } + + typename Axis::value_type lower() const noexcept { return axis_.lower(idx_); } + typename Axis::value_type upper() const noexcept { return axis_.lower(idx_+1); } + typename Axis::value_type width() const noexcept { return upper() - lower(); } + + bool operator==(const interval_view &rhs) const noexcept { + return idx_ == rhs.idx_ && axis_ == rhs.axis_; + } + bool operator!=(const interval_view &rhs) const noexcept { + return !operator==(rhs); + } + + explicit operator int() const noexcept { return idx_; } + +private: + const int idx_; + const Axis& axis_; +}; + +template class value_view { +public: + value_view(int idx, const Axis& axis) : + idx_(idx), axis_(axis) {} + + value_view(const value_view &) = default; + value_view &operator=(const value_view &) = default; + value_view(value_view &&) = default; + value_view &operator=(value_view &&) = default; + + int idx() const noexcept { return idx_; } + + typename Axis::value_type value() const { + return axis_.value(idx_); + } + + bool operator==(const value_view &rhs) const noexcept { + return idx_ == rhs.idx_ && axis_ == rhs.axis_; + } + bool operator!=(const value_view &rhs) const noexcept { + return !operator==(rhs); + } + + explicit operator int() const noexcept { return idx_; } + +private: + const int idx_; + const Axis& axis_; +}; + +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/interval.hpp b/include/boost/histogram/axis/interval.hpp deleted file mode 100644 index c5feed58..00000000 --- a/include/boost/histogram/axis/interval.hpp +++ /dev/null @@ -1,46 +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_AXIS_INTERVAL_HPP_ -#define _BOOST_HISTOGRAM_AXIS_INTERVAL_HPP_ - -#include -#include - -namespace boost { -namespace histogram { -namespace axis { - -template class interval_view { -public: - interval_view(int idx, std::function eval) : idx_(idx), eval_(eval) {} - - interval_view(const interval_view &) = default; - interval_view &operator=(const interval_view &) = default; - interval_view(interval_view &&) = default; - interval_view &operator=(interval_view &&) = default; - - T lower() const noexcept { return eval_(idx_); } - T upper() const noexcept { return eval_(idx_ + 1); } - T width() const noexcept { return upper() - lower(); } - - bool operator==(const interval_view &rhs) const noexcept { - return lower() == rhs.lower() && upper() == rhs.upper(); - } - bool operator!=(const interval_view &rhs) const noexcept { - return !operator==(rhs); - } - -private: - const int idx_; - const std::function eval_; -}; - -} // namespace axis -} // namespace histogram -} // namespace boost - -#endif diff --git a/include/boost/histogram/axis/iterator.hpp b/include/boost/histogram/axis/iterator.hpp index 45debfee..e1fa9332 100644 --- a/include/boost/histogram/axis/iterator.hpp +++ b/include/boost/histogram/axis/iterator.hpp @@ -24,10 +24,6 @@ public: iterator_over(const iterator_over &) = default; iterator_over &operator=(const iterator_over &) = default; - operator bool() const noexcept { return idx_ < axis_.size(); } - explicit operator int() const noexcept { return idx_; } - int idx() const noexcept { return idx_; } - protected: void increment() noexcept { ++idx_; } void decrement() noexcept { --idx_; } @@ -57,10 +53,6 @@ public: reverse_iterator_over(const reverse_iterator_over &) = default; reverse_iterator_over &operator=(const reverse_iterator_over &) = default; - operator bool() const noexcept { return idx_ > 0; } - explicit operator int() const noexcept { return idx_; } - int idx() const noexcept { return idx_ - 1; } - protected: void increment() noexcept { --idx_; } void decrement() noexcept { ++idx_; } diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 50a6152f..6be6d7dc 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -10,7 +10,7 @@ #define _BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP_ #include -#include +#include #include #include #include @@ -32,6 +32,12 @@ inline std::ostream &operator<<(std::ostream &os, const interval_view &i) { return os; } +template +inline std::ostream &operator<<(std::ostream &os, const value_view &i) { + os << i.value(); + return os; +} + template inline std::ostream &operator<<(std::ostream &os, const regular &a) { @@ -131,7 +137,7 @@ inline std::ostream &operator<<(std::ostream &os, const category &a) { os << "category("; for (int i = 0; i < a.size(); ++i) { - ::boost::histogram::detail::escape(os, a[i]); + ::boost::histogram::detail::escape(os, a[i].value()); os << (i == (a.size() - 1) ? "" : ", "); } if (!a.label().empty()) { diff --git a/include/boost/histogram/detail/axis_visitor.hpp b/include/boost/histogram/detail/axis_visitor.hpp index 38869368..51429f40 100644 --- a/include/boost/histogram/detail/axis_visitor.hpp +++ b/include/boost/histogram/detail/axis_visitor.hpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/python/axis.cpp b/src/python/axis.cpp index 1467513b..736e5d23 100644 --- a/src/python/axis.cpp +++ b/src/python/axis.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -54,10 +55,19 @@ generic_iterator make_generic_iterator(bp::object self) { return generic_iterator(self); } -template +template +struct axis_value_view_to_python +{ + static PyObject* convert(const bha::value_view &i) + { + return bp::incref(bp::object(i.value()).ptr()); + } +}; + +template struct axis_interval_view_to_python { - static PyObject* convert(const bha::interval_view &i) + static PyObject* convert(const bha::interval_view &i) { return bp::incref(bp::make_tuple(i.lower(), i.upper()).ptr()); } @@ -130,12 +140,23 @@ bp::object category_init(bp::tuple args, bp::dict kwargs) { return self.attr("__init__")(bha::category<>(c.begin(), c.end(), label)); } +template bp::object axis_getitem_impl(std::true_type, const A &a, int i) { + return bp::make_tuple(a.lower(i), a.lower(i+1)); +} + +template bp::object axis_getitem_impl(std::false_type, const A &a, int i) { + return bp::object(a.value(i)); +} + template bp::object axis_getitem(const A &a, int i) { if (i < -1 * a.uoflow() || i >= a.size() + 1 * a.uoflow()) { PyErr_SetString(PyExc_IndexError, "index out of bounds"); bp::throw_error_already_set(); } - return bp::object(a[i]); + return axis_getitem_impl(std::is_same< + typename A::bin_type, bha::interval_view + >(), a, i); + return bp::object(); } template void axis_set_label(T& t, bp::str s) { @@ -159,7 +180,7 @@ template bp::object axis_array_interface(const Axis& axis) { auto a = np::empty(shape, np::dtype::get_builtin()); auto buf = reinterpret_cast(a.get_data()); for (auto i = 0; i < axis.size()+1; ++i) - buf[i] = axis[i].lower(); + buf[i] = axis.lower(i); d["data"] = a; d["version"] = 3; return d; @@ -174,7 +195,7 @@ template <> bp::object axis_array_interface>(const bha::category auto a = np::empty(shape, np::dtype::get_builtin()); auto buf = reinterpret_cast(a.get_data()); for (auto i = 0; i < axis.size(); ++i) - buf[i] = axis[i]; + buf[i] = axis.value(i); d["data"] = a; d["version"] = 3; return d; @@ -251,16 +272,6 @@ void register_axis_types() { using bp::arg; // resolve ambiguity docstring_options dopt(true, true, false); - to_python_converter< - bha::interval_view, - axis_interval_view_to_python - >(); - - to_python_converter< - bha::interval_view, - axis_interval_view_to_python - >(); - class_("generic_iterator", init()) .def("__iter__", &generic_iterator::self) .def("next", &generic_iterator::next); diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 25513468..b2a46c86 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -25,20 +25,17 @@ template void test_axis_iterator(const Axis &a, int begin, int end) { - auto it = a.begin(); - for (; it; ++it) { - BOOST_TEST_EQ(it.idx(), begin); - BOOST_TEST_EQ(*it, a[begin]); + for (auto bin : a) { + BOOST_TEST_EQ(bin.idx(), begin); + BOOST_TEST_EQ(bin, a[begin]); ++begin; } BOOST_TEST_EQ(begin, end); - BOOST_TEST_EQ(it, a.end()); auto rit = a.rbegin(); - for (; rit; ++rit) { - BOOST_TEST_EQ(rit.idx(), --begin); + for (; rit != a.rend(); ++rit) { + BOOST_TEST_EQ(rit->idx(), --begin); BOOST_TEST_EQ(*rit, a[begin]); } - BOOST_TEST_EQ(rit, a.rend()); } int main() { @@ -212,9 +209,9 @@ int main() { BOOST_TEST_EQ(a.index(A), 0); BOOST_TEST_EQ(a.index(B), 1); BOOST_TEST_EQ(a.index(C), 2); - BOOST_TEST_EQ(a[0], A); - BOOST_TEST_EQ(a[1], B); - BOOST_TEST_EQ(a[2], C); + BOOST_TEST_EQ(a.value(0), A); + BOOST_TEST_EQ(a.value(1), B); + BOOST_TEST_EQ(a.value(2), C); } // iterators @@ -227,7 +224,7 @@ 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}))[0], std::runtime_error); + BOOST_TEST_THROWS(any_axis_type(axis::category<>({A, B, C})).lower(0), std::runtime_error); } // any_axis_type_copyable diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 02df5aac..38910e74 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -209,7 +209,7 @@ template void run_tests() { BOOST_TEST_EQ(c.axis().label(), "foo"); // need to cast here for this to work with Type == dynamic_tag auto ca = axis::cast>(c.axis()); - BOOST_TEST_EQ(ca[0], A); + BOOST_TEST_EQ(ca[0].value(), A); } // equal_compare @@ -882,7 +882,7 @@ int main() { { enum { A, B }; auto c = make_dynamic_histogram(axis::category<>({A, B})); - BOOST_TEST_THROWS(c.axis()[0], std::runtime_error); + BOOST_TEST_THROWS(c.axis().lower(0), std::runtime_error); } // reduce