| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- // Copyright 2015-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)
- #ifndef BOOST_HISTOGRAM_HISTOGRAM_HPP
- #define BOOST_HISTOGRAM_HISTOGRAM_HPP
- #include <boost/histogram/detail/axes.hpp>
- #include <boost/histogram/detail/common_type.hpp>
- #include <boost/histogram/detail/linearize.hpp>
- #include <boost/histogram/detail/meta.hpp>
- #include <boost/histogram/fwd.hpp>
- #include <boost/mp11/list.hpp>
- #include <boost/throw_exception.hpp>
- #include <stdexcept>
- #include <tuple>
- #include <type_traits>
- #include <utility>
- namespace boost {
- namespace histogram {
- /** Central class of the histogram library.
- Histogram uses the call operator to insert data, like the
- [Boost.Accumulators](https://www.boost.org/doc/libs/develop/doc/html/accumulators.html).
- Use factory functions (see
- [make_histogram.hpp](histogram/reference.html#header.boost.histogram.make_histogram_hpp)
- and
- [make_profile.hpp](histogram/reference.html#header.boost.histogram.make_profile_hpp)) to
- conveniently create histograms rather than calling the ctors directly.
- Use the [indexed](boost/histogram/indexed.html) range generator to iterate over filled
- histograms, which is convenient and faster than hand-written loops for multi-dimensional
- histograms.
- @tparam Axes std::tuple of axis types OR std::vector of an axis type or axis::variant
- @tparam Storage class that implements the storage interface
- */
- template <class Axes, class Storage>
- class histogram {
- public:
- static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
- static_assert(std::is_same<detail::remove_cvref_t<Storage>, Storage>::value,
- "Storage may not be a reference or const or volatile");
- public:
- using axes_type = Axes;
- using storage_type = Storage;
- using value_type = typename storage_type::value_type;
- // typedefs for boost::range_iterator
- using iterator = typename storage_type::iterator;
- using const_iterator = typename storage_type::const_iterator;
- histogram() = default;
- histogram(const histogram& rhs) = default;
- histogram(histogram&& rhs) = default;
- histogram& operator=(const histogram& rhs) = default;
- histogram& operator=(histogram&& rhs) = default;
- template <class A, class S>
- explicit histogram(histogram<A, S>&& rhs) : storage_(std::move(rhs.storage_)) {
- detail::axes_assign(axes_, rhs.axes_);
- }
- template <class A, class S>
- explicit histogram(const histogram<A, S>& rhs) : storage_(rhs.storage_) {
- detail::axes_assign(axes_, rhs.axes_);
- }
- template <class A, class S>
- histogram& operator=(histogram<A, S>&& rhs) {
- detail::axes_assign(axes_, std::move(rhs.axes_));
- storage_ = std::move(rhs.storage_);
- return *this;
- }
- template <class A, class S>
- histogram& operator=(const histogram<A, S>& rhs) {
- detail::axes_assign(axes_, rhs.axes_);
- storage_ = rhs.storage_;
- return *this;
- }
- template <class A, class S>
- histogram(A&& a, S&& s) : axes_(std::forward<A>(a)), storage_(std::forward<S>(s)) {
- storage_.reset(detail::bincount(axes_));
- }
- template <class A, class = detail::requires_axes<A>>
- explicit histogram(A&& a) : histogram(std::forward<A>(a), storage_type()) {}
- /// Number of axes (dimensions).
- constexpr unsigned rank() const noexcept {
- return static_cast<unsigned>(detail::get_size(axes_));
- }
- /// Total number of bins (including underflow/overflow).
- std::size_t size() const noexcept { return storage_.size(); }
- /// Reset all bins to default initialized values.
- void reset() { storage_.reset(storage_.size()); }
- /// Get N-th axis using a compile-time number.
- /// This version is more efficient than the one accepting a run-time number.
- template <unsigned N = 0>
- decltype(auto) axis(std::integral_constant<unsigned, N> = {}) const {
- detail::axis_index_is_valid(axes_, N);
- return detail::axis_get<N>(axes_);
- }
- /// Get N-th axis with run-time number.
- /// Use version that accepts a compile-time number, if possible.
- decltype(auto) axis(unsigned i) const {
- detail::axis_index_is_valid(axes_, i);
- return detail::axis_get(axes_, i);
- }
- /// Apply unary functor/function to each axis.
- template <class Unary>
- auto for_each_axis(Unary&& unary) const {
- return detail::for_each_axis(axes_, std::forward<Unary>(unary));
- }
- /** Fill histogram with values, an optional weight, and/or a sample.
- Arguments are passed in order to the axis objects. Passing an argument type that is
- not convertible to the value type accepted by the axis or passing the wrong number
- of arguments causes a throw of `std::invalid_argument`.
- __Axis with multiple arguments__
- If the histogram contains an axis which accepts a `std::tuple` of arguments, the
- arguments for that axis need to passed as a `std::tuple`, for example,
- `std::make_tuple(1.2, 2.3)`. If the histogram contains only this axis and no other,
- the arguments can be passed directly.
- __Optional weight__
- An optional weight can be passed as the first or last argument
- with the [weight](boost/histogram/weight.html) helper function. Compilation fails if
- the storage elements do not support weights.
- __Samples__
- If the storage elements accept samples, pass them with the sample helper function
- in addition to the axis arguments, which can be the first or last argument. The
- [sample](boost/histogram/sample.html) helper function can pass one or more arguments to
- the storage element. If samples and weights are used together, they can be passed in
- any order at the beginning or end of the argument list.
- */
- template <class... Ts>
- auto operator()(const Ts&... ts) {
- return operator()(std::make_tuple(ts...));
- }
- /// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`.
- template <class... Ts>
- auto operator()(const std::tuple<Ts...>& t) {
- return detail::fill(storage_, axes_, t);
- }
- /// Access cell value at integral indices.
- /**
- You can pass indices as individual arguments, as a std::tuple of integers, or as an
- interable range of integers. Passing the wrong number of arguments causes a throw of
- std::invalid_argument. Passing an index which is out of bounds causes a throw of
- std::out_of_range.
- */
- template <class... Ts>
- decltype(auto) at(axis::index_type t, Ts... ts) {
- return at(std::forward_as_tuple(t, ts...));
- }
- /// Access cell value at integral indices (read-only).
- /// @copydoc at(axis::index_type t, Ts... ts)
- template <class... Ts>
- decltype(auto) at(axis::index_type t, Ts... ts) const {
- return at(std::forward_as_tuple(t, ts...));
- }
- /// Access cell value at integral indices stored in `std::tuple`.
- /// @copydoc at(axis::index_type t, Ts... ts)
- template <typename... Ts>
- decltype(auto) at(const std::tuple<Ts...>& t) {
- const auto idx = detail::at(axes_, t);
- if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds"));
- return storage_[*idx];
- }
- /// Access cell value at integral indices stored in `std::tuple` (read-only).
- /// @copydoc at(axis::index_type t, Ts... ts)
- template <typename... Ts>
- decltype(auto) at(const std::tuple<Ts...>& t) const {
- const auto idx = detail::at(axes_, t);
- if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds"));
- return storage_[*idx];
- }
- /// Access cell value at integral indices stored in iterable.
- /// @copydoc at(axis::index_type t, Ts... ts)
- template <class Iterable, class = detail::requires_iterable<Iterable>>
- decltype(auto) at(const Iterable& c) {
- const auto idx = detail::at(axes_, c);
- if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds"));
- return storage_[*idx];
- }
- /// Access cell value at integral indices stored in iterable (read-only).
- /// @copydoc at(axis::index_type t, Ts... ts)
- template <class Iterable, class = detail::requires_iterable<Iterable>>
- decltype(auto) at(const Iterable& c) const {
- const auto idx = detail::at(axes_, c);
- if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds"));
- return storage_[*idx];
- }
- /// Access value at index (number for rank = 1, else `std::tuple` or iterable).
- template <class T>
- decltype(auto) operator[](const T& t) {
- return at(t);
- }
- /// @copydoc operator[]
- template <class T>
- decltype(auto) operator[](const T& t) const {
- return at(t);
- }
- /// Equality operator, tests equality for all axes and the storage.
- template <class A, class S>
- bool operator==(const histogram<A, S>& rhs) const noexcept {
- return detail::axes_equal(axes_, rhs.axes_) && storage_ == rhs.storage_;
- }
- /// Negation of the equality operator.
- template <class A, class S>
- bool operator!=(const histogram<A, S>& rhs) const noexcept {
- return !operator==(rhs);
- }
- /// Add values of another histogram.
- template <class A, class S>
- histogram& operator+=(const histogram<A, S>& rhs) {
- static_assert(detail::has_operator_radd<value_type,
- typename histogram<A, S>::value_type>::value,
- "cell value does not support adding value of right-hand-side");
- if (!detail::axes_equal(axes_, rhs.axes_))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = rhs.storage_.begin();
- std::for_each(storage_.begin(), storage_.end(), [&rit](auto&& x) { x += *rit++; });
- return *this;
- }
- /// Subtract values of another histogram.
- template <class A, class S>
- histogram& operator-=(const histogram<A, S>& rhs) {
- static_assert(detail::has_operator_rsub<value_type,
- typename histogram<A, S>::value_type>::value,
- "cell value does not support subtracting value of right-hand-side");
- if (!detail::axes_equal(axes_, rhs.axes_))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = rhs.storage_.begin();
- std::for_each(storage_.begin(), storage_.end(), [&rit](auto&& x) { x -= *rit++; });
- return *this;
- }
- /// Multiply by values of another histogram.
- template <class A, class S>
- histogram& operator*=(const histogram<A, S>& rhs) {
- static_assert(detail::has_operator_rmul<value_type,
- typename histogram<A, S>::value_type>::value,
- "cell value does not support multiplying by value of right-hand-side");
- if (!detail::axes_equal(axes_, rhs.axes_))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = rhs.storage_.begin();
- std::for_each(storage_.begin(), storage_.end(), [&rit](auto&& x) { x *= *rit++; });
- return *this;
- }
- /// Divide by values of another histogram.
- template <class A, class S>
- histogram& operator/=(const histogram<A, S>& rhs) {
- static_assert(detail::has_operator_rdiv<value_type,
- typename histogram<A, S>::value_type>::value,
- "cell value does not support dividing by value of right-hand-side");
- if (!detail::axes_equal(axes_, rhs.axes_))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = rhs.storage_.begin();
- std::for_each(storage_.begin(), storage_.end(), [&rit](auto&& x) { x /= *rit++; });
- return *this;
- }
- /// Multiply all values with a scalar.
- template <class V = value_type,
- class = std::enable_if_t<detail::has_operator_rmul<V, double>::value>>
- histogram& operator*=(const double x) {
- // use special implementation of scaling if available
- detail::static_if<detail::has_operator_rmul<storage_type, double>>(
- [](storage_type& s, auto x) { s *= x; },
- [](storage_type& s, auto x) {
- for (auto&& si : s) si *= x;
- },
- storage_, x);
- return *this;
- }
- /// Divide all values by a scalar.
- template <class V = value_type,
- class = std::enable_if_t<detail::has_operator_rmul<V, double>::value>>
- histogram& operator/=(const double x) {
- return operator*=(1.0 / x);
- }
- /// Return value iterator to the beginning of the histogram.
- iterator begin() noexcept { return storage_.begin(); }
- /// Return value iterator to the end in the histogram.
- iterator end() noexcept { return storage_.end(); }
- /// Return value iterator to the beginning of the histogram (read-only).
- const_iterator begin() const noexcept { return storage_.begin(); }
- /// Return value iterator to the end in the histogram (read-only).
- const_iterator end() const noexcept { return storage_.end(); }
- /// Return value iterator to the beginning of the histogram (read-only).
- const_iterator cbegin() const noexcept { return storage_.begin(); }
- /// Return value iterator to the end in the histogram (read-only).
- const_iterator cend() const noexcept { return storage_.end(); }
- private:
- axes_type axes_;
- storage_type storage_;
- template <class A, class S>
- friend class histogram;
- friend struct unsafe_access;
- };
- template <class A1, class S1, class A2, class S2>
- auto operator+(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r += b;
- }
- template <class A1, class S1, class A2, class S2>
- auto operator*(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r *= b;
- }
- template <class A1, class S1, class A2, class S2>
- auto operator-(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r -= b;
- }
- template <class A1, class S1, class A2, class S2>
- auto operator/(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
- auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
- return r /= b;
- }
- template <class A, class S>
- auto operator*(const histogram<A, S>& h, double x) {
- auto r = histogram<A, detail::common_storage<S, dense_storage<double>>>(h);
- return r *= x;
- }
- template <class A, class S>
- auto operator*(double x, const histogram<A, S>& h) {
- return h * x;
- }
- template <class A, class S>
- auto operator/(const histogram<A, S>& h, double x) {
- return h * (1.0 / x);
- }
- #if __cpp_deduction_guides >= 201606
- template <class Axes>
- histogram(Axes&& axes)->histogram<detail::remove_cvref_t<Axes>, default_storage>;
- template <class Axes, class Storage>
- histogram(Axes&& axes, Storage&& storage)
- ->histogram<detail::remove_cvref_t<Axes>, detail::remove_cvref_t<Storage>>;
- #endif
- /// Helper function to mark argument as weight.
- /// @param t argument to be forward to the histogram.
- template <typename T>
- auto weight(T&& t) noexcept {
- return weight_type<T>{std::forward<T>(t)};
- }
- /// Helper function to mark arguments as sample.
- /// @param ts arguments to be forwarded to the accumulator.
- template <typename... Ts>
- auto sample(Ts&&... ts) noexcept {
- return sample_type<std::tuple<Ts...>>{std::forward_as_tuple(std::forward<Ts>(ts)...)};
- }
- #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- template <class T>
- struct weight_type {
- T value;
- };
- template <class T>
- struct sample_type {
- T value;
- };
- #endif
- } // namespace histogram
- } // namespace boost
- #endif
|