#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/compressed_pair.hpp>
-#include <boost/histogram/detail/linearize.hpp>
-#include <boost/histogram/detail/noop_mutex.hpp>
-#include <boost/histogram/detail/static_if.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>
@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");
+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 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;
- histogram(const histogram& rhs)
- : axes_(rhs.axes_), storage_and_mutex_(rhs.storage_and_mutex_.first()) {}
- histogram(histogram&& rhs)
- : axes_(std::move(rhs.axes_))
- , storage_and_mutex_(std::move(rhs.storage_and_mutex_.first())) {}
- histogram& operator=(histogram&& rhs) {
- if (this != &rhs) {
- axes_ = std::move(rhs.axes_);
- storage_and_mutex_.first() = std::move(rhs.storage_and_mutex_.first());
- }
- return *this;
- }
- histogram& operator=(const histogram& rhs) {
- if (this != &rhs) {
- axes_ = rhs.axes_;
- storage_and_mutex_.first() = rhs.storage_and_mutex_.first();
- }
- return *this;
- }
template <class A, class S>
explicit histogram(histogram<A, S>&& rhs)
- : storage_and_mutex_(std::move(unsafe_access::storage(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_and_mutex_(unsafe_access::storage(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)));
- storage_and_mutex_.first() = std::move(unsafe_access::storage(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));
- storage_and_mutex_.first() = unsafe_access::storage(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 S>
- histogram(A&& a, S&& s)
- : axes_(std::forward<A>(a)), storage_and_mutex_(std::forward<S>(s)) {
- storage_and_mutex_.first().reset(detail::bincount(axes_));
+ 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_));
}
- template <class A, class = detail::requires_axes<A>>
- explicit histogram(A&& a) : histogram(std::forward<A>(a), storage_type()) {}
+ 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_and_mutex_.first().size(); }
+ std::size_t size() const noexcept { return storage_.size(); }
/// Reset all bins to default initialized values.
- void reset() { storage_and_mutex_.first().reset(size()); }
+ 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.
/** 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`.
+ 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__
+ __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.
+ 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__
+ __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.
+ 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__
+ __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.
+ 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... Ts>
- iterator operator()(const Ts&... ts) {
- return operator()(std::forward_as_tuple(ts...));
+ 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...>& t) {
- std::lock_guard<mutex_type> guard{storage_and_mutex_.second()};
- return detail::fill(axes_, storage_and_mutex_.first(), t);
+ 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.
}
/// Access cell value at integral indices stored in `std::tuple`.
- template <typename... Indices>
+ 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 (!idx)
+ if (!is_valid(idx))
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- return storage_and_mutex_.first()[*idx];
+ 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 (!idx)
+ if (!is_valid(idx))
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- return storage_and_mutex_.first()[*idx];
+ 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 (!idx)
+ if (!is_valid(idx))
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- return storage_and_mutex_.first()[*idx];
+ 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 (!idx)
+ if (!is_valid(idx))
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
- return storage_and_mutex_.first()[*idx];
+ BOOST_ASSERT(idx < storage_.size());
+ return storage_[idx];
}
/// Access value at index (number for rank = 1, else `std::tuple` or iterable).
/// 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_, unsafe_access::axes(rhs)) &&
- storage_and_mutex_.first() == unsafe_access::storage(rhs);
+ // 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.
}
/// Add values of another histogram.
- template <class A, class S,
- class = std::enable_if_t<detail::has_operator_radd<
- value_type, typename histogram<A, S>::value_type>::value>>
- histogram& operator+=(const histogram<A, S>& rhs) {
+ 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();
- auto& s = storage_and_mutex_.first();
- std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x += *rit++; });
+ for (auto&& x : storage_) x += *rit++;
return *this;
}
/// Subtract values of another histogram.
- template <class A, class S,
- class = std::enable_if_t<detail::has_operator_rsub<
- value_type, typename histogram<A, S>::value_type>::value>>
- histogram& operator-=(const histogram<A, S>& rhs) {
+ 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();
- auto& s = storage_and_mutex_.first();
- std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x -= *rit++; });
+ for (auto&& x : storage_) x -= *rit++;
return *this;
}
/// Multiply by values of another histogram.
- template <class A, class S,
- class = std::enable_if_t<detail::has_operator_rmul<
- value_type, typename histogram<A, S>::value_type>::value>>
- histogram& operator*=(const histogram<A, S>& rhs) {
+ 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();
- auto& s = storage_and_mutex_.first();
- std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x *= *rit++; });
+ for (auto&& x : storage_) x *= *rit++;
return *this;
}
/// Divide by values of another histogram.
- template <class A, class S,
- class = std::enable_if_t<detail::has_operator_rdiv<
- value_type, typename histogram<A, S>::value_type>::value>>
- histogram& operator/=(const histogram<A, S>& rhs) {
+ 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();
- auto& s = storage_and_mutex_.first();
- std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x /= *rit++; });
+ for (auto&& x : storage_) 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) {
+ 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
- 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_and_mutex_.first(), x);
+ 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,
- class = std::enable_if_t<detail::has_operator_rmul<V, double>::value>>
- histogram& operator/=(const double x) {
+ 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_and_mutex_.first().begin(); }
+ iterator begin() noexcept { return storage_.begin(); }
/// Return value iterator to the end in the histogram.
- iterator end() noexcept { return storage_and_mutex_.first().end(); }
+ iterator end() noexcept { return storage_.end(); }
/// Return value iterator to the beginning of the histogram (read-only).
- const_iterator begin() const noexcept { return storage_and_mutex_.first().begin(); }
+ 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_and_mutex_.first().end(); }
+ 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_;
-
- using mutex_type = mp11::mp_if_c<(storage_type::has_threading_support &&
- detail::has_growing_axis<axes_type>::value),
- std::mutex, detail::noop_mutex>;
-
- detail::compressed_pair<storage_type, mutex_type> storage_and_mutex_;
+ storage_type storage_;
+ std::size_t offset_ = 0;
friend struct unsafe_access;
};
#if __cpp_deduction_guides >= 201606
-template <class Axes>
-histogram(Axes&& axes)->histogram<std::decay_t<Axes>, default_storage>;
-
-template <class Axes, class Storage>
-histogram(Axes&& axes, Storage&& storage)
- ->histogram<std::decay_t<Axes>, std::decay_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)};
-}
+template <class... Axes, class = detail::requires_axes<std::tuple<std::decay_t<Axes>...>>>
+histogram(Axes...)->histogram<std::tuple<std::decay_t<Axes>...>>;
-/** Helper function to mark arguments as sample.
+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>>;
- @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)...)};
-}
+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 T>
-struct weight_type {
- T value;
-};
+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>>;
-template <class T>
-struct sample_type {
- T value;
-};
+#endif
} // namespace histogram
} // namespace boost