123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- // 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/accumulator_traits.hpp>
- #include <boost/histogram/detail/argument_traits.hpp>
- #include <boost/histogram/detail/at.hpp>
- #include <boost/histogram/detail/axes.hpp>
- #include <boost/histogram/detail/common_type.hpp>
- #include <boost/histogram/detail/fill.hpp>
- #include <boost/histogram/detail/fill_n.hpp>
- #include <boost/histogram/detail/mutex_base.hpp>
- #include <boost/histogram/detail/non_member_container_access.hpp>
- #include <boost/histogram/detail/span.hpp>
- #include <boost/histogram/fwd.hpp>
- #include <boost/histogram/sample.hpp>
- #include <boost/histogram/storage_adaptor.hpp>
- #include <boost/histogram/unsafe_access.hpp>
- #include <boost/histogram/weight.hpp>
- #include <boost/mp11/integral.hpp>
- #include <boost/mp11/list.hpp>
- #include <boost/mp11/tuple.hpp>
- #include <boost/throw_exception.hpp>
- #include <mutex>
- #include <stdexcept>
- #include <tuple>
- #include <type_traits>
- #include <utility>
- #include <vector>
- 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 : detail::mutex_base<Axes, Storage> {
- static_assert(std::is_same<std::decay_t<Storage>, Storage>::value,
- "Storage may not be a reference or const or volatile");
- static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
- 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;
- private:
- using mutex_base = typename detail::mutex_base<axes_type, storage_type>;
- public:
- histogram() = default;
- template <class A, class S>
- explicit histogram(histogram<A, S>&& rhs)
- : storage_(std::move(unsafe_access::storage(rhs)))
- , offset_(unsafe_access::offset(rhs)) {
- detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
- detail::throw_if_axes_is_too_large(axes_);
- }
- template <class A, class S>
- explicit histogram(const histogram<A, S>& rhs)
- : storage_(unsafe_access::storage(rhs)), offset_(unsafe_access::offset(rhs)) {
- detail::axes_assign(axes_, unsafe_access::axes(rhs));
- detail::throw_if_axes_is_too_large(axes_);
- }
- template <class A, class S>
- histogram& operator=(histogram<A, S>&& rhs) {
- detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
- detail::throw_if_axes_is_too_large(axes_);
- storage_ = std::move(unsafe_access::storage(rhs));
- offset_ = unsafe_access::offset(rhs);
- return *this;
- }
- template <class A, class S>
- histogram& operator=(const histogram<A, S>& rhs) {
- detail::axes_assign(axes_, unsafe_access::axes(rhs));
- detail::throw_if_axes_is_too_large(axes_);
- storage_ = unsafe_access::storage(rhs);
- offset_ = unsafe_access::offset(rhs);
- return *this;
- }
- template <class A, class = detail::requires_axes<A>>
- histogram(A&& a, Storage s)
- : axes_(std::forward<A>(a))
- , storage_(std::move(s))
- , offset_(detail::offset(axes_)) {
- detail::throw_if_axes_is_too_large(axes_);
- storage_.reset(detail::bincount(axes_));
- }
- explicit histogram(Axes axes) : histogram(axes, storage_type()) {}
- template <class... As, class = detail::requires_axes<std::tuple<std::decay_t<As>...>>>
- explicit histogram(As&&... as)
- : histogram(std::tuple<std::decay_t<As>...>(std::forward<As>(as)...),
- storage_type()) {}
- /// Number of axes (dimensions).
- constexpr unsigned rank() const noexcept { return detail::axes_rank(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(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.
- /// Prefer the version that accepts a compile-time number, if you can use it.
- 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`.
- __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.
- __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.
- */
- template <class Arg0, class... Args>
- std::enable_if_t<(detail::is_tuple<Arg0>::value == false || sizeof...(Args) > 0),
- iterator>
- operator()(const Arg0& arg0, const Args&... args) {
- return operator()(std::forward_as_tuple(arg0, args...));
- }
- /// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`.
- template <class... Ts>
- iterator operator()(const std::tuple<Ts...>& args) {
- using arg_traits = detail::argument_traits<std::decay_t<Ts>...>;
- using acc_traits = detail::accumulator_traits<value_type>;
- constexpr bool weight_valid =
- arg_traits::wpos::value == -1 || acc_traits::wsupport::value;
- static_assert(weight_valid, "error: accumulator does not support weights");
- detail::sample_args_passed_vs_expected<typename arg_traits::sargs,
- typename acc_traits::args>();
- constexpr bool sample_valid =
- std::is_convertible<typename arg_traits::sargs, typename acc_traits::args>::value;
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- return detail::fill(mp11::mp_bool<(weight_valid && sample_valid)>{}, arg_traits{},
- offset_, storage_, axes_, args);
- }
- /** Fill histogram with several values at once.
- The argument must be an iterable with a size that matches the
- rank of the histogram. The element of an iterable may be 1) a value or 2) an iterable
- with contiguous storage over values or 3) a variant of 1) and 2). Sub-iterables must
- have the same length.
- Values are passed to the corresponding histogram axis in order. If a single value is
- passed together with an iterable of values, the single value is treated like an
- iterable with matching length of copies of this value.
- If the histogram has only one axis, an iterable of values may be passed directly.
- @param args iterable as explained in the long description.
- */
- template <class Iterable, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args) {
- using acc_traits = detail::accumulator_traits<value_type>;
- constexpr unsigned n_sample_args_expected =
- std::tuple_size<typename acc_traits::args>::value;
- static_assert(n_sample_args_expected == 0,
- "sample argument is missing but required by accumulator");
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- detail::fill_n(mp11::mp_bool<(n_sample_args_expected == 0)>{}, offset_, storage_,
- axes_, detail::make_span(args));
- }
- /** Fill histogram with several values and weights at once.
- @param args iterable of values.
- @param weights single weight or an iterable of weights.
- */
- template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const weight_type<T>& weights) {
- using acc_traits = detail::accumulator_traits<value_type>;
- constexpr bool weight_valid = acc_traits::wsupport::value;
- static_assert(weight_valid, "error: accumulator does not support weights");
- detail::sample_args_passed_vs_expected<std::tuple<>, typename acc_traits::args>();
- constexpr bool sample_valid =
- std::is_convertible<std::tuple<>, typename acc_traits::args>::value;
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_, storage_,
- axes_, detail::make_span(args),
- weight(detail::to_ptr_size(weights.value)));
- }
- /** Fill histogram with several values and weights at once.
- @param weights single weight or an iterable of weights.
- @param args iterable of values.
- */
- template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
- void fill(const weight_type<T>& weights, const Iterable& args) {
- fill(args, weights);
- }
- /** Fill histogram with several values and samples at once.
- @param args iterable of values.
- @param samples single sample or an iterable of samples.
- */
- template <class Iterable, class... Ts, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const sample_type<std::tuple<Ts...>>& samples) {
- using acc_traits = detail::accumulator_traits<value_type>;
- using sample_args_passed =
- std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
- detail::sample_args_passed_vs_expected<sample_args_passed,
- typename acc_traits::args>();
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- mp11::tuple_apply(
- [&](const auto&... sargs) {
- constexpr bool sample_valid =
- std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
- detail::fill_n(mp11::mp_bool<(sample_valid)>{}, offset_, storage_, axes_,
- detail::make_span(args), detail::to_ptr_size(sargs)...);
- },
- samples.value);
- }
- /** Fill histogram with several values and samples at once.
- @param samples single sample or an iterable of samples.
- @param args iterable of values.
- */
- template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
- void fill(const sample_type<T>& samples, const Iterable& args) {
- fill(args, samples);
- }
- template <class Iterable, class T, class... Ts,
- class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const weight_type<T>& weights,
- const sample_type<std::tuple<Ts...>>& samples) {
- using acc_traits = detail::accumulator_traits<value_type>;
- using sample_args_passed =
- std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
- detail::sample_args_passed_vs_expected<sample_args_passed,
- typename acc_traits::args>();
- std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
- mp11::tuple_apply(
- [&](const auto&... sargs) {
- constexpr bool weight_valid = acc_traits::wsupport::value;
- static_assert(weight_valid, "error: accumulator does not support weights");
- constexpr bool sample_valid =
- std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
- detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_,
- storage_, axes_, detail::make_span(args),
- weight(detail::to_ptr_size(weights.value)),
- detail::to_ptr_size(sargs)...);
- },
- samples.value);
- }
- template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
- void fill(const sample_type<T>& samples, const weight_type<U>& weights,
- const Iterable& args) {
- fill(args, weights, samples);
- }
- template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
- void fill(const weight_type<T>& weights, const sample_type<U>& samples,
- const Iterable& args) {
- fill(args, weights, samples);
- }
- template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
- void fill(const Iterable& args, const sample_type<T>& samples,
- const weight_type<U>& weights) {
- fill(args, weights, samples);
- }
- /** 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.
- @param i index of first axis.
- @param is indices of second, third, ... axes.
- @returns reference to cell value.
- */
- template <class... Indices>
- decltype(auto) at(axis::index_type i, Indices... is) {
- return at(std::forward_as_tuple(i, is...));
- }
- /// Access cell value at integral indices (read-only).
- template <class... Indices>
- decltype(auto) at(axis::index_type i, Indices... is) const {
- return at(std::forward_as_tuple(i, is...));
- }
- /// Access cell value at integral indices stored in `std::tuple`.
- template <class... Indices>
- decltype(auto) at(const std::tuple<Indices...>& is) {
- if (rank() != sizeof...(Indices))
- BOOST_THROW_EXCEPTION(
- std::invalid_argument("number of arguments != histogram rank"));
- const auto idx = detail::at(axes_, is);
- if (!is_valid(idx))
- BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- BOOST_ASSERT(idx < storage_.size());
- return storage_[idx];
- }
- /// Access cell value at integral indices stored in `std::tuple` (read-only).
- template <typename... Indices>
- decltype(auto) at(const std::tuple<Indices...>& is) const {
- if (rank() != sizeof...(Indices))
- BOOST_THROW_EXCEPTION(
- std::invalid_argument("number of arguments != histogram rank"));
- const auto idx = detail::at(axes_, is);
- if (!is_valid(idx))
- BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- BOOST_ASSERT(idx < storage_.size());
- return storage_[idx];
- }
- /// Access cell value at integral indices stored in iterable.
- template <class Iterable, class = detail::requires_iterable<Iterable>>
- decltype(auto) at(const Iterable& is) {
- if (rank() != detail::axes_rank(is))
- BOOST_THROW_EXCEPTION(
- std::invalid_argument("number of arguments != histogram rank"));
- const auto idx = detail::at(axes_, is);
- if (!is_valid(idx))
- BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- BOOST_ASSERT(idx < storage_.size());
- return storage_[idx];
- }
- /// Access cell value at integral indices stored in iterable (read-only).
- template <class Iterable, class = detail::requires_iterable<Iterable>>
- decltype(auto) at(const Iterable& is) const {
- if (rank() != detail::axes_rank(is))
- BOOST_THROW_EXCEPTION(
- std::invalid_argument("number of arguments != histogram rank"));
- const auto idx = detail::at(axes_, is);
- if (!is_valid(idx))
- BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- BOOST_ASSERT(idx < storage_.size());
- return storage_[idx];
- }
- /// Access value at index (number for rank = 1, else `std::tuple` or iterable).
- template <class Indices>
- decltype(auto) operator[](const Indices& is) {
- return at(is);
- }
- /// Access value at index (read-only).
- template <class Indices>
- decltype(auto) operator[](const Indices& is) const {
- return at(is);
- }
- /// Equality operator, tests equality for all axes and the storage.
- template <class A, class S>
- bool operator==(const histogram<A, S>& rhs) const noexcept {
- // testing offset is redundant, but offers fast return if it fails
- return offset_ == unsafe_access::offset(rhs) &&
- detail::axes_equal(axes_, unsafe_access::axes(rhs)) &&
- storage_ == unsafe_access::storage(rhs);
- }
- /// 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>
- std::enable_if_t<
- detail::has_operator_radd<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- operator+=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x += *rit++;
- return *this;
- }
- /// Subtract values of another histogram.
- template <class A, class S>
- std::enable_if_t<
- detail::has_operator_rsub<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- operator-=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x -= *rit++;
- return *this;
- }
- /// Multiply by values of another histogram.
- template <class A, class S>
- std::enable_if_t<
- detail::has_operator_rmul<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- operator*=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x *= *rit++;
- return *this;
- }
- /// Divide by values of another histogram.
- template <class A, class S>
- std::enable_if_t<
- detail::has_operator_rdiv<value_type, typename histogram<A, S>::value_type>::value,
- histogram&>
- operator/=(const histogram<A, S>& rhs) {
- if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
- BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
- auto rit = unsafe_access::storage(rhs).begin();
- for (auto&& x : storage_) x /= *rit++;
- return *this;
- }
- /// Multiply all values with a scalar.
- template <class V = value_type>
- std::enable_if_t<(detail::has_operator_rmul<V, double>::value &&
- detail::has_operator_rmul<storage_type, double>::value == true),
- histogram&>
- operator*=(const double x) {
- // use special implementation of scaling if available
- storage_ *= x;
- return *this;
- }
- /// Multiply all values with a scalar.
- template <class V = value_type>
- std::enable_if_t<(detail::has_operator_rmul<V, double>::value &&
- detail::has_operator_rmul<storage_type, double>::value == false),
- histogram&>
- operator*=(const double x) {
- // generic implementation of scaling
- for (auto&& si : storage_) si *= x;
- return *this;
- }
- /// Divide all values by a scalar.
- template <class V = value_type>
- 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 begin(); }
- /// Return value iterator to the end in the histogram (read-only).
- const_iterator cend() const noexcept { return end(); }
- template <class Archive>
- void serialize(Archive& ar, unsigned /* version */) {
- detail::axes_serialize(ar, axes_);
- ar& make_nvp("storage", storage_);
- if (Archive::is_loading::value) {
- offset_ = detail::offset(axes_);
- detail::throw_if_axes_is_too_large(axes_);
- }
- }
- private:
- axes_type axes_;
- storage_type storage_;
- std::size_t offset_ = 0;
- friend struct unsafe_access;
- };
- /**
- Pairwise add cells of two histograms and return histogram with the sum.
- The returned histogram type is the most efficient and safest one constructible from the
- inputs, if they are not the same type. If one histogram has a tuple axis, the result has
- a tuple axis. The chosen storage is the one with the larger dynamic range.
- */
- 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;
- }
- /** Pairwise multiply cells of two histograms and return histogram with the product.
- For notes on the returned histogram type, see operator+.
- */
- 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;
- }
- /** Pairwise subtract cells of two histograms and return histogram with the difference.
- For notes on the returned histogram type, see operator+.
- */
- 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;
- }
- /** Pairwise divide cells of two histograms and return histogram with the quotient.
- For notes on the returned histogram type, see operator+.
- */
- 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;
- }
- /** Multiply all cells of the histogram by a number and return a new histogram.
- If the original histogram has integer cells, the result has double cells.
- */
- 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;
- }
- /** Multiply all cells of the histogram by a number and return a new histogram.
- If the original histogram has integer cells, the result has double cells.
- */
- template <class A, class S>
- auto operator*(double x, const histogram<A, S>& h) {
- return h * x;
- }
- /** Divide all cells of the histogram by a number and return a new histogram.
- If the original histogram has integer cells, the result has double cells.
- */
- 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, class = detail::requires_axes<std::tuple<std::decay_t<Axes>...>>>
- histogram(Axes...)->histogram<std::tuple<std::decay_t<Axes>...>>;
- template <class... Axes, class S, class = detail::requires_storage_or_adaptible<S>>
- histogram(std::tuple<Axes...>, S)
- ->histogram<std::tuple<Axes...>, std::conditional_t<detail::is_adaptible<S>::value,
- storage_adaptor<S>, S>>;
- template <class Iterable, class = detail::requires_iterable<Iterable>,
- class = detail::requires_any_axis<typename Iterable::value_type>>
- histogram(Iterable)->histogram<std::vector<typename Iterable::value_type>>;
- template <class Iterable, class S, class = detail::requires_iterable<Iterable>,
- class = detail::requires_any_axis<typename Iterable::value_type>,
- class = detail::requires_storage_or_adaptible<S>>
- histogram(Iterable, S)
- ->histogram<
- std::vector<typename Iterable::value_type>,
- std::conditional_t<detail::is_adaptible<S>::value, storage_adaptor<S>, S>>;
- #endif
- } // namespace histogram
- } // namespace boost
- #endif
|