wip, histogram_test fails

This commit is contained in:
Hans Dembinski 2018-10-28 23:05:43 +01:00
parent c6e24e4747
commit a17a6c677c
29 changed files with 535 additions and 416 deletions

View File

@ -80,7 +80,7 @@ function(compiled_test SRC)
endfunction()
compiled_test(test/adaptive_storage_test.cpp)
compiled_test(test/array_storage_test.cpp)
compiled_test(test/storage_adaptor_test.cpp)
compiled_test(test/axis_regular_test.cpp)
compiled_test(test/axis_circular_test.cpp)
compiled_test(test/axis_variable_test.cpp)

View File

@ -12,13 +12,13 @@ There are two bars per benchmarked histogram. The upper is for random data from
[[root] [[@https://root.cern.ch ROOT classes] (`TH1I` for 1D, `TH3I` for 3D and `THnI` for 6D)]]
[[py:numpy] [numpy functions ([python]`numpy.histogram` for 1D, `numpy.histogramdd` for 2D, 3D, and 6D)]]
[[gsl] [[@https://www.gnu.org/software/gsl/doc/html/histogram.html GSL histograms] for 1D and 2D]]
[[hs_ss] [[funcref boost::histogram::make_static_histogram static histogram] with [classref boost::histogram::array_storage<int>]]]
[[hs_sd] [[funcref boost::histogram::make_static_histogram static histogram] with [classref boost::histogram::adaptive_storage]]]
[[hd_ss] [[funcref boost::histogram::make_dynamic_histogram dynamic histogram] with [classref boost::histogram::array_storage<int>]]]
[[hd_sd] [[funcref boost::histogram::make_dynamic_histogram dynamic histogram] with [classref boost::histogram::adaptive_storage]]]
[[hs_ss] [[funcref boost::histogram::make_static_histogram static histogram] with [classref std::vector<int>]]]
[[hs_sd] [[funcref boost::histogram::make_static_histogram static histogram] with [classref boost::histogram::default_storage]]]
[[hd_ss] [[funcref boost::histogram::make_dynamic_histogram dynamic histogram] with [classref std::vector<int>]]]
[[hd_sd] [[funcref boost::histogram::make_dynamic_histogram dynamic histogram] with [classref boost::histogram::default_storage]]]
]
A [classref boost::histogram::make_static_histogram static histogram] is always faster than [classref boost::histogram::make_dynamic_histogram dynamic histogram] and safer to use, as more checks are done at compile time. [classref boost::histogram::adaptive_storage] is faster than [classref boost::histogram::array_storage] for histograms with many bins, because it uses the cache more effectively due to its smaller memory consumption per bin. If the number of bins is small, it is slower because of overhead of handling memory in a dynamic way.
A [classref boost::histogram::make_static_histogram static histogram] is always faster than [classref boost::histogram::make_dynamic_histogram dynamic histogram] and safer to use, as more checks are done at compile time. [classref boost::histogram::adaptive_storage] is faster than [classref boost::histogram::default_storage] for histograms with many bins, because it uses the cache more effectively due to its smaller memory consumption per bin. If the number of bins is small, it is slower because of overhead of handling memory in a dynamic way.
The histograms in this library are mostly faster than the competition, in some cases by a factor of 2. Simultaneously they are more flexible, since binning strategies and the counter management can be customised.

View File

@ -42,8 +42,8 @@ int main() {
/*
iterate over bins with a fancy histogram iterator
- order in which bins are iterated over is an implementation detail
- iterator dereferences to histogram::element_type, which is defined by
its storage class; by default this is a double
- iterator dereferences to histogram::const_reference, which is defined by
its storage class; for the default storage it is actually a plain double
- idx(N) method returns the index of the N-th axis
- bin(N_c) method returns current bin of N-th axis; the suffx _c turns
the argument into a compile-time number, which is needed to return

View File

@ -1,16 +1,19 @@
//[ guide_custom_storage
#include <array>
#include <boost/histogram.hpp>
#include <boost/histogram/storage/array_storage.hpp>
#include <vector>
namespace bh = boost::histogram;
int main() {
// create static histogram with array_storage, using int as counter type
auto h = bh::make_histogram_with(bh::array_storage<int>(),
bh::axis::regular<>(10, 0, 1));
// create static histogram with vector<int> as counter storage
auto h1 = bh::make_histogram_with(std::vector<int>(), bh::axis::regular<>(10, 0, 1));
// do something with h
// create static histogram with array<int, 12> as counter storage (no allocation!)
auto h2 = bh::make_histogram_with(std::array<int, 12>(), bh::axis::regular<>(10, 0, 1));
// do something with h1 and h2
}
//]

View File

@ -2,6 +2,7 @@
#include <boost/histogram.hpp>
#include <cassert>
#include <vector>
namespace bh = boost::histogram;
@ -34,7 +35,7 @@ int main() {
assert(h4 != h5 && h4 == 4 * h5);
// note special effect of multiplication on weight_counter variance
auto h = bh::make_histogram_with(bh::array_storage<bh::weight_counter<double>>(),
auto h = bh::make_histogram_with(std::vector<bh::weight_counter<double>>(),
bh::axis::regular<>(2, -1, 1));
h(-0.5); // counts are: 1 0

View File

@ -8,9 +8,8 @@ namespace bh = boost::histogram;
// example of a generic function for histograms, this one sums all entries
template <typename... Ts>
typename bh::histogram<Ts...>::element_type sum(
const bh::histogram<Ts...>& h) {
auto result = typename bh::histogram<Ts...>::element_type(0);
auto sum(const bh::histogram<Ts...>& h) {
auto result = typename bh::histogram<Ts...>::value_type(0);
for (auto x : h) result += x;
return result;
}

View File

@ -1,16 +1,14 @@
//[ guide_make_dynamic_histogram
#include <boost/histogram.hpp>
#include <vector>
#include <cassert>
#include <vector>
namespace bh = boost::histogram;
int main() {
// create vector of axes, axis::any is a polymorphic axis type
auto v = std::vector<bh::axis::variant<
bh::axis::regular<>, bh::axis::integer<>
>>();
auto v = std::vector<bh::axis::variant<bh::axis::regular<>, bh::axis::integer<>>>();
v.push_back(bh::axis::regular<>(100, -1, 1));
v.push_back(bh::axis::integer<>(1, 7));
@ -18,7 +16,7 @@ int main() {
auto h = bh::make_histogram(v.begin(), v.end());
assert(h.rank() == 2);
// create dynamic histogram by moving the vector (this avoids copies)
// create dynamic histogram by moving the vector (avoids copies)
auto h2 = bh::make_histogram(std::move(v));
assert(h2.rank() == 2);
}

View File

@ -7,6 +7,7 @@
#ifndef BOOST_HISTOGRAM_HPP
#define BOOST_HISTOGRAM_HPP
#include <boost/histogram/adaptive_storage.hpp>
#include <boost/histogram/axis/category.hpp>
#include <boost/histogram/axis/circular.hpp>
#include <boost/histogram/axis/integer.hpp>
@ -15,9 +16,8 @@
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/literals.hpp>
#include <boost/histogram/storage/adaptive_storage.hpp>
#include <boost/histogram/storage/array_storage.hpp>
#include <boost/histogram/storage/weight_counter.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/weight_counter.hpp>
/**
* \file boost/histogram.hpp

View File

@ -4,8 +4,8 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_STORAGE_ADAPTIVE_HPP
#define BOOST_HISTOGRAM_STORAGE_ADAPTIVE_HPP
#ifndef BOOST_HISTOGRAM_ADAPTIVE_STORAGE_HPP
#define BOOST_HISTOGRAM_ADAPTIVE_STORAGE_HPP
#include <algorithm>
#include <boost/assert.hpp>
@ -13,6 +13,7 @@
#include <boost/cstdint.hpp>
#include <boost/histogram/detail/buffer.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/weight.hpp>
#include <boost/mp11.hpp>
#ifdef __clang__
@ -34,7 +35,7 @@ namespace histogram {
namespace detail {
template <typename T>
bool safe_increase(T& t) {
bool safe_increment(T& t) {
if (t < std::numeric_limits<T>::max()) {
++t;
return true;
@ -98,7 +99,7 @@ struct adaptive_storage {
"adaptive_storage requires allocator with trivial pointer type");
using allocator_type = Allocator;
using element_type = double;
using value_type = double;
using const_reference = double;
using mp_int = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<
@ -208,8 +209,9 @@ struct adaptive_storage {
return *this;
}
template <typename S, typename = detail::requires_storage<S>>
explicit adaptive_storage(const S& s) : buffer(s.size(), s.get_allocator()) {
template <typename T>
adaptive_storage(const storage_adaptor<T>& s) : buffer(s.size()) {
// TODO: select appropriate buffer for T
buffer.set(buffer.template create<double>());
auto it = static_cast<double*>(buffer.ptr);
const auto end = it + size();
@ -217,14 +219,17 @@ struct adaptive_storage {
while (it != end) *it++ = s[i++];
}
template <typename S, typename = detail::requires_storage<S>>
adaptive_storage& operator=(const S& s) {
// no check for self-assign needed, since S is different type
template <typename T>
adaptive_storage& operator=(const storage_adaptor<T>& s) {
// no check for self-assign needed, since argument is different type
// TODO: select appropriate buffer for T
apply(destroyer(), buffer);
buffer.alloc = s.get_allocator();
buffer.size = s.size();
buffer.set(buffer.template create<void>());
for (std::size_t i = 0; i < size(); ++i) { add(i, s[i]); }
buffer.set(buffer.template create<double>());
auto it = static_cast<double*>(buffer.ptr);
const auto end = it + size();
std::size_t i = 0;
while (it != end) *it++ = s[i++];
return *this;
}
@ -242,19 +247,19 @@ struct adaptive_storage {
std::size_t size() const { return buffer.size; }
void increase(std::size_t i) {
void operator()(std::size_t i) {
BOOST_ASSERT(i < size());
apply(increaser(), buffer, i);
apply(incrementor(), buffer, i);
}
template <typename T>
void add(std::size_t i, const T& x) {
void operator()(std::size_t i, const T& x) {
BOOST_ASSERT(i < size());
apply(adder(), buffer, i, x);
}
template <typename T>
void add(std::size_t i, const weight_type<T>& x) {
void operator()(std::size_t i, const weight_type<T>& x) {
BOOST_ASSERT(i < size());
apply(adder(), buffer, i, x.value);
}
@ -295,7 +300,7 @@ struct adaptive_storage {
template <typename S>
adaptive_storage& operator+=(const S& o) {
BOOST_ASSERT(o.size() == size());
for (std::size_t i = 0; i < size(); ++i) add(i, o[i]);
for (std::size_t i = 0; i < size(); ++i) (*this)(i, o[i]);
return *this;
}
@ -345,10 +350,10 @@ struct adaptive_storage {
}
};
struct increaser {
struct incrementor {
template <typename T, typename Buffer>
void operator()(T* tp, Buffer& b, std::size_t i) {
if (!detail::safe_increase(tp[i])) {
if (!detail::safe_increment(tp[i])) {
using U = mp11::mp_at_c<types, (type_index<T>() + 1)>;
U* ptr = b.template create<U>(tp);
destroyer()(tp, b);
@ -538,7 +543,6 @@ struct adaptive_storage {
buffer_type buffer;
};
} // namespace histogram
} // namespace boost

View File

@ -15,22 +15,22 @@ namespace histogram {
namespace axis {
template <typename Axis>
class iterator_over
: public iterator_facade<iterator_over<Axis>, decltype(std::declval<Axis&>()[0]),
class iterator
: public iterator_facade<iterator<Axis>, decltype(std::declval<Axis&>()[0]),
random_access_traversal_tag,
decltype(std::declval<Axis&>()[0]), int> {
public:
explicit iterator_over(const Axis& axis, int idx) : axis_(axis), idx_(idx) {}
explicit iterator(const Axis& axis, int idx) : axis_(axis), idx_(idx) {}
iterator_over(const iterator_over&) = default;
iterator_over& operator=(const iterator_over&) = default;
iterator(const iterator&) = default;
iterator& operator=(const iterator&) = default;
protected:
void increment() noexcept { ++idx_; }
void decrement() noexcept { --idx_; }
void advance(int n) noexcept { idx_ += n; }
int distance_to(const iterator_over& other) const noexcept { return other.idx_ - idx_; }
bool equal(const iterator_over& other) const noexcept {
int distance_to(const iterator& other) const noexcept { return other.idx_ - idx_; }
bool equal(const iterator& other) const noexcept {
return &axis_ == &other.axis_ && idx_ == other.idx_;
}
decltype(std::declval<Axis&>()[0]) dereference() const { return axis_[idx_]; }
@ -45,7 +45,7 @@ protected:
template <typename Derived>
class iterator_mixin {
public:
using const_iterator = iterator_over<Derived>;
using const_iterator = iterator<Derived>;
using const_reverse_iterator = boost::reverse_iterator<const_iterator>;
const_iterator begin() const noexcept {

View File

@ -53,7 +53,7 @@ void stream_metadata(OStream& os, const T& t) {
if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); }
},
[&os](const auto&) {
using U = detail::rm_cvref<T>;
using U = detail::unqual<T>;
os << ", metadata=" << boost::core::demangled_name(BOOST_CORE_TYPEID(U));
},
t);

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Hans Dembinski
// Copyright 2018 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
@ -7,38 +7,38 @@
#ifndef BOOST_HISTOGRAM_AXIS_TRAITS_HPP
#define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram_fwd.hpp>
namespace boost {
namespace histogram {
namespace axis {
namespace traits {
template <typename T>
using args = detail::args_type<decltype(&T::operator())>;
template <typename T>
using args = detail::args_type<decltype(&T::operator())>;
template <typename T>
decltype(auto) metadata(T&& t) noexcept {
return detail::static_if<detail::has_method_metadata<T>>(
template <typename T>
decltype(auto) metadata(T&& t) noexcept {
return detail::static_if<detail::has_method_metadata<T>>(
[](auto&& x) -> decltype(auto) { return x.metadata(); },
[](auto&&) -> detail::copy_qualifiers<T, axis::empty_metadata_type> {
static axis::empty_metadata_type m; return m;
[](T &&) -> detail::copy_qualifiers<T, axis::empty_metadata_type> {
static axis::empty_metadata_type m;
return m;
},
t);
}
std::forward<T>(t));
}
template <typename T>
option_type options(const T& t) noexcept {
return detail::static_if<detail::has_method_options<T>>(
template <typename T>
option_type options(const T& t) noexcept {
return detail::static_if<detail::has_method_options<T>>(
[](const auto& x) { return x.options(); },
[](const T&) { return axis::option_type::none; },
t);
}
[](const T&) { return axis::option_type::none; }, t);
}
template <typename T>
unsigned extend(const T& t) noexcept {
return t.size() + static_cast<unsigned>(options(t));
}
template <typename T>
unsigned extend(const T& t) noexcept {
return t.size() + static_cast<unsigned>(options(t));
}
} // namespace traits
} // namespace axis
} // namespace histogram

View File

@ -55,11 +55,11 @@ class variant : private boost::variant<Ts...>, public iterator_mixin<variant<Ts.
using base_type = boost::variant<Ts...>;
using first_bounded_type = mp11::mp_first<base_type>;
using metadata_type =
detail::rm_cvref<decltype(traits::metadata(std::declval<first_bounded_type&>()))>;
detail::unqual<decltype(traits::metadata(std::declval<first_bounded_type&>()))>;
template <typename T>
using requires_bounded_type =
mp11::mp_if<mp11::mp_contains<base_type, detail::rm_cvref<T>>, void>;
mp11::mp_if<mp11::mp_contains<base_type, detail::unqual<T>>, void>;
public:
variant() = default;
@ -86,7 +86,7 @@ public:
variant& operator=(const variant<Us...>& u) {
visit(
[this](const auto& u) {
using U = detail::rm_cvref<decltype(u)>;
using U = detail::unqual<decltype(u)>;
detail::static_if<mp11::mp_contains<base_type, U>>(
[this](const auto& u) { this->operator=(u); },
[](const auto&) {
@ -155,7 +155,7 @@ public:
auto&& args = std::forward_as_tuple(std::forward<Us>(x)...);
return visit(
[&args](const auto& a) {
using A = detail::rm_cvref<decltype(a)>;
using A = detail::unqual<decltype(a)>;
using args_t = std::tuple<Us...>;
using expected_args_t = axis::traits::args<A>;
return detail::static_if<std::is_convertible<args_t, expected_args_t>>(
@ -189,10 +189,10 @@ public:
double value(double idx) const {
return visit(
[idx](const auto& a) {
using T = detail::rm_cvref<decltype(a)>;
using T = detail::unqual<decltype(a)>;
return detail::static_if<detail::has_method_value<T>>(
[idx](const auto& a) -> double {
using T = detail::rm_cvref<decltype(a)>;
using T = detail::unqual<decltype(a)>;
using U = detail::return_type<decltype(&T::value)>;
return detail::static_if<std::is_convertible<U, double>>(
[idx](const auto& a) -> double {
@ -278,7 +278,7 @@ auto visit(Functor&& f, Variant&& v) -> detail::visitor_return_type<Functor, Var
return boost::apply_visitor(
detail::functor_wrapper<Functor, R>(f),
static_cast<detail::copy_qualifiers<Variant,
typename detail::rm_cvref<Variant>::base_type>>(
typename detail::unqual<Variant>::base_type>>(
v));
}
@ -287,7 +287,7 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
const variant<Ts...>& v) {
visit(
[&os](const auto& x) {
using T = detail::rm_cvref<decltype(x)>;
using T = detail::unqual<decltype(x)>;
detail::static_if<detail::is_streamable<T>>(
[&os](const auto& x) { os << x; },
[](const auto&) {
@ -327,8 +327,8 @@ const T* get(const variant<Us...>* v) {
}
// pass-through if T is an axis instead of a variant
template <typename T, typename U, typename = detail::requires_axis<detail::rm_cvref<U>>,
typename = detail::requires_same<T, detail::rm_cvref<U>>>
template <typename T, typename U, typename = detail::requires_axis<detail::unqual<U>>,
typename = detail::requires_same<T, detail::unqual<U>>>
U get(U&& u) {
return std::forward<U>(u);
}

View File

@ -23,13 +23,13 @@ namespace histogram {
namespace detail {
template <class T>
using rm_cvref = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
using unqual = std::remove_cv_t<std::remove_reference_t<T>>;
template <class T>
using mp_size = mp11::mp_size<rm_cvref<T>>;
using mp_size = mp11::mp_size<unqual<T>>;
template <typename T, unsigned N>
using mp_at_c = mp11::mp_at_c<rm_cvref<T>, N>;
using mp_at_c = mp11::mp_at_c<unqual<T>, N>;
template <typename T1, typename T2>
using copy_qualifiers = mp11::mp_if<
@ -46,11 +46,10 @@ template <typename L>
using mp_last = mp11::mp_at_c<L, (mp_size<L>::value - 1)>;
template <typename T>
using container_element_type = mp11::mp_first<rm_cvref<T>>;
using container_value_type = mp11::mp_first<unqual<T>>;
template <typename T>
using iterator_value_type =
typename std::iterator_traits<T>::value_type;
using iterator_value_type = typename std::iterator_traits<T>::value_type;
template <typename T>
using return_type = typename boost::callable_traits::return_type<T>::type;
@ -97,24 +96,31 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support,
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, (std::declval<T&>().value(0)));
BOOST_HISTOGRAM_MAKE_SFINAE(
has_method_options, (static_cast<axis::option_type>(std::declval<T&>().options())));
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, &T::options);
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval<T&>().metadata()));
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, &T::metadata);
// resize has two overloads, trying to get pmf in this case always fails
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_resize, (std::declval<T&>().resize(0)));
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_size, &T::size);
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_clear, &T::clear);
BOOST_HISTOGRAM_MAKE_SFINAE(is_indexable, (std::declval<T&>()[0]));
BOOST_HISTOGRAM_MAKE_SFINAE(is_transform, (&T::forward, &T::inverse));
BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container,
(std::declval<T&>()[0], std::declval<T&>().size()));
BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container, (std::declval<T&>()[0], &T::size,
std::begin(std::declval<T&>()),
std::end(std::declval<T&>())));
BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, (std::get<0>(std::declval<T&>())));
BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, (static_cast<int>(std::declval<T&>())));
BOOST_HISTOGRAM_MAKE_SFINAE(is_equal_comparable,
(std::declval<T&>() == std::declval<T&>()));
BOOST_HISTOGRAM_MAKE_SFINAE(is_axis, (std::declval<T&>().size(), &T::operator()));
BOOST_HISTOGRAM_MAKE_SFINAE(is_axis, (&T::size, &T::operator()));
BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable, (std::begin(std::declval<T&>()),
std::end(std::declval<T&>())));
@ -122,6 +128,8 @@ BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable, (std::begin(std::declval<T&>()),
BOOST_HISTOGRAM_MAKE_SFINAE(is_streamable,
(std::declval<std::ostream&>() << std::declval<T&>()));
BOOST_HISTOGRAM_MAKE_SFINAE(is_callable, (std::declval<T&>()()));
namespace {
template <typename T>
struct is_axis_variant_impl : std::false_type {};
@ -136,9 +144,9 @@ using is_axis_variant = typename is_axis_variant_impl<T>::type;
template <typename T>
using is_axis_or_axis_variant = mp11::mp_or<is_axis<T>, is_axis_variant<T>>;
template <typename T>
using is_axis_vector = mp11::mp_all<is_random_access_container<T>,
is_axis_or_axis_variant<container_element_type<rm_cvref<T>>>>;
template <typename T, typename U = container_value_type<T>>
using is_axis_vector =
mp11::mp_all<is_random_access_container<unqual<T>>, is_axis_or_axis_variant<U>>;
struct static_container_tag {};
struct iterable_container_tag {};
@ -169,11 +177,6 @@ std::vector<bool> bool_mask(unsigned n, bool v) {
}
// poor-mans concept checks
template <typename T,
typename = decltype(std::declval<T&>().size(), std::declval<T&>().increase(0),
std::declval<T&>()[0])>
struct requires_storage {};
template <typename T, typename = decltype(*std::declval<T&>(), ++std::declval<T&>())>
struct requires_iterator {};
@ -186,15 +189,10 @@ struct requires_static_container {};
template <typename T, typename = mp11::mp_if<is_axis<T>, void>>
struct requires_axis {};
template <typename T,
typename =
mp11::mp_if_c<(is_axis<T>::value || is_axis_variant<T>::value), void>>
template <typename T, typename = mp11::mp_if<is_axis_or_axis_variant<T>, void>>
struct requires_axis_or_axis_variant {};
template <typename T, typename U = container_element_type<T>,
typename = mp11::mp_if_c<(is_random_access_container<T>::value &&
(is_axis<U>::value || is_axis_variant<U>::value)),
void>>
template <typename T, typename = mp11::mp_if<is_axis_vector<T>, void>>
struct requires_axis_vector {};
template <typename T, typename U, typename = mp11::mp_if<std::is_same<T, U>, void>>

View File

@ -15,8 +15,7 @@
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/iterator.hpp>
#include <boost/histogram/storage/adaptive_storage.hpp>
#include <boost/histogram/storage/weight_counter.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/mp11.hpp>
#include <functional>
#include <tuple>
@ -26,16 +25,17 @@
namespace boost {
namespace histogram {
template <typename Axes, typename Storage>
template <typename Axes, typename Container>
class histogram {
static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
public:
using axes_type = Axes;
using storage_type = Storage;
using element_type = typename storage_type::element_type;
using container_type = Container;
using storage_type = storage_adaptor<Container>;
using value_type = typename storage_type::value_type;
using const_reference = typename storage_type::const_reference;
using const_iterator = iterator_over<histogram>;
using const_iterator = iterator<histogram>;
histogram() = default;
histogram(const histogram& rhs) = default;
@ -43,40 +43,40 @@ public:
histogram& operator=(const histogram& rhs) = default;
histogram& operator=(histogram&& rhs) = default;
template <typename A, typename S>
explicit histogram(const histogram<A, S>& rhs) : storage_(rhs.storage_) {
template <typename A, typename C>
explicit histogram(const histogram<A, C>& rhs) : storage_(rhs.storage_) {
detail::axes_assign(axes_, rhs.axes_);
}
template <typename A, typename S>
histogram& operator=(const histogram<A, S>& rhs) {
template <typename A, typename C>
histogram& operator=(const histogram<A, C>& rhs) {
storage_ = rhs.storage_;
detail::axes_assign(axes_, rhs.axes_);
return *this;
}
explicit histogram(const axes_type& a, const storage_type& s = storage_type())
: axes_(a), storage_(s) {
explicit histogram(const axes_type& a, container_type c = container_type())
: axes_(a), storage_(std::move(c)) {
storage_.reset(detail::bincount(axes_));
}
explicit histogram(axes_type&& a, storage_type&& s = storage_type())
: axes_(std::move(a)), storage_(std::move(s)) {
explicit histogram(axes_type&& a, container_type c = container_type())
: axes_(std::move(a)), storage_(std::move(c)) {
storage_.reset(detail::bincount(axes_));
}
template <typename A, typename S>
bool operator==(const histogram<A, S>& rhs) const noexcept {
template <typename A, typename C>
bool operator==(const histogram<A, C>& rhs) const noexcept {
return detail::axes_equal(axes_, rhs.axes_) && storage_ == rhs.storage_;
}
template <typename A, typename S>
bool operator!=(const histogram<A, S>& rhs) const noexcept {
template <typename A, typename C>
bool operator!=(const histogram<A, C>& rhs) const noexcept {
return !operator==(rhs);
}
template <typename A, typename S>
histogram& operator+=(const histogram<A, S>& rhs) {
template <typename A, typename C>
histogram& operator+=(const histogram<A, C>& rhs) {
if (!detail::axes_equal(axes_, rhs.axes_))
throw std::invalid_argument("axes of histograms differ");
storage_ += rhs.storage_;
@ -147,29 +147,29 @@ public:
void operator()(const Ts&... ts) {
// case with one argument needs special treatment, specialized below
const auto index = detail::call_impl(detail::no_container_tag(), axes_, ts...);
if (index) storage_.increase(*index);
if (index) storage_(*index);
}
template <typename T>
void operator()(const T& t) {
// check whether we need to unpack argument
const auto index = detail::call_impl(detail::classify_container<T>(), axes_, t);
if (index) storage_.increase(*index);
if (index) storage_(*index);
}
/// Fill histogram with a weight and a value tuple
template <typename U, typename... Ts>
void operator()(weight_type<U>&& w, const Ts&... ts) {
void operator()(const weight_type<U>& w, const Ts&... ts) {
// case with one argument needs special treatment, specialized below
const auto index = detail::call_impl(detail::no_container_tag(), axes_, ts...);
if (index) storage_.add(*index, w);
if (index) storage_(*index, w);
}
template <typename U, typename T>
void operator()(weight_type<U>&& w, const T& t) {
void operator()(const weight_type<U>& w, const T& t) {
// check whether we need to unpack argument
const auto index = detail::call_impl(detail::classify_container<T>(), axes_, t);
if (index) storage_.add(*index, w);
if (index) storage_(*index, w);
}
/// Access bin counter at indices
@ -181,6 +181,7 @@ public:
return storage_[index];
}
/// Access bin counter at index (specialization for 1D)
template <typename T>
const_reference at(const T& t) const {
// check whether we need to unpack argument;
@ -204,12 +205,12 @@ public:
using sub_axes_type = detail::sub_axes<axes_type, N, Ns...>;
using HR = histogram<sub_axes_type, storage_type>;
auto sub_axes = detail::make_sub_axes(axes_, N(), Ns()...);
auto hr = HR(std::move(sub_axes), storage_type(storage_.get_allocator()));
auto hr = HR(std::move(sub_axes), storage_type());
const auto b = detail::bool_mask<N, Ns...>(rank(), true);
std::vector<unsigned> shape(rank());
for_each_axis(detail::shape_collector(shape.begin()));
detail::index_mapper m(shape, b);
do { hr.storage_.add(m.second, storage_[m.first]); } while (m.next());
do { hr.storage_(m.second, storage_[m.first]); } while (m.next());
return hr;
}
@ -234,64 +235,59 @@ public:
std::vector<unsigned> shape(rank());
for_each_axis(detail::shape_collector(shape.begin()));
detail::index_mapper m(shape, b);
do { hr.storage_.add(m.second, storage_[m.first]); } while (m.next());
do { hr.storage_(m.second, storage_[m.first]); } while (m.next());
return hr;
}
const_iterator begin() const noexcept { return const_iterator(*this, 0); }
auto begin() const noexcept { return const_iterator(*this, 0); }
const_iterator end() const noexcept { return const_iterator(*this, size()); }
auto end() const noexcept { return const_iterator(*this, size()); }
template <typename Archive>
void serialize(Archive&, unsigned);
private:
axes_type axes_;
Storage storage_;
storage_type storage_;
template <typename A, typename S>
friend class histogram;
template <typename H>
friend class iterator_over;
friend class iterator;
};
/// static type factory with custom storage type
template <typename S, typename T, typename... Ts, typename = detail::requires_axis<T>>
histogram<std::tuple<detail::rm_cvref<T>, detail::rm_cvref<Ts>...>, detail::rm_cvref<S>>
make_histogram_with(S&& s, T&& axis0, Ts&&... axis) {
auto make_histogram_with(S&& s, T&& axis0, Ts&&... axis) {
auto axes = std::make_tuple(std::forward<T>(axis0), std::forward<Ts>(axis)...);
return histogram<decltype(axes), detail::rm_cvref<S>>(std::move(axes),
std::forward<S>(s));
return histogram<decltype(axes), detail::unqual<S>>(std::move(axes),
std::forward<S>(s));
}
/// static type factory with standard storage type
template <typename T, typename... Ts, typename = detail::requires_axis<T>>
auto make_histogram(T&& axis0, Ts&&... axis)
-> decltype(make_histogram_with(default_storage(), std::forward<T>(axis0),
std::forward<Ts>(axis)...)) {
auto make_histogram(T&& axis0, Ts&&... axis) {
return make_histogram_with(default_storage(), std::forward<T>(axis0),
std::forward<Ts>(axis)...);
}
/// dynamic type factory from vector-like with custom storage type
template <typename S, typename T, typename = detail::requires_axis_vector<T>>
histogram<detail::rm_cvref<T>, detail::rm_cvref<S>> make_histogram_with(S&& s, T&& t) {
return histogram<detail::rm_cvref<T>, detail::rm_cvref<S>>(std::forward<T>(t),
std::forward<S>(s));
auto make_histogram_with(S&& s, T&& t) {
return histogram<detail::unqual<T>, detail::unqual<S>>(std::forward<T>(t),
std::forward<S>(s));
}
/// dynamic type factory from vector-like with standard storage type
template <typename T, typename = detail::requires_axis_vector<T>>
auto make_histogram(T&& t)
-> decltype(make_histogram_with(default_storage(), std::forward<T>(t))) {
auto make_histogram(T&& t) {
return make_histogram_with(default_storage(), std::forward<T>(t));
}
/// dynamic type factory from iterator range with custom storage type
template <typename Storage, typename Iterator,
typename = detail::requires_iterator<Iterator>>
histogram<std::vector<detail::iterator_value_type<Iterator>>, detail::rm_cvref<Storage>>
make_histogram_with(Storage&& s, Iterator begin, Iterator end) {
auto make_histogram_with(Storage&& s, Iterator begin, Iterator end) {
using T = detail::iterator_value_type<Iterator>;
auto axes = std::vector<T>(begin, end);
return make_histogram_with(std::forward<Storage>(s), std::move(axes));
@ -299,8 +295,7 @@ make_histogram_with(Storage&& s, Iterator begin, Iterator end) {
/// dynamic type factory from iterator range with standard storage type
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
auto make_histogram(Iterator begin, Iterator end)
-> decltype(make_histogram_with(default_storage(), begin, end)) {
auto make_histogram(Iterator begin, Iterator end) {
return make_histogram_with(default_storage(), begin, end);
}
} // namespace histogram

View File

@ -54,12 +54,12 @@ template <typename... Ts>
class variant;
} // namespace axis
template <typename T>
struct storage_adaptor;
template <typename Allocator = std::allocator<char>>
struct adaptive_storage;
template <typename T, typename Allocator = std::allocator<T>>
struct array_storage;
using default_storage = adaptive_storage<>;
template <class Axes, class Storage = default_storage>

View File

@ -17,16 +17,16 @@ namespace histogram {
/// Fancy iterator over histogram bins with access multi-dimensional index.
template <typename Histogram>
class iterator_over
: public iterator_facade<iterator_over<Histogram>, typename Histogram::element_type,
class iterator
: public iterator_facade<iterator<Histogram>, typename Histogram::value_type,
random_access_traversal_tag,
typename Histogram::const_reference> {
public:
iterator_over(const Histogram& h, std::size_t idx) : histogram_(h), idx_(idx) {}
iterator(const Histogram& h, std::size_t idx) : histogram_(h), idx_(idx) {}
iterator_over(const iterator_over& o) : histogram_(o.histogram_), idx_(o.idx_) {}
iterator(const iterator& o) : histogram_(o.histogram_), idx_(o.idx_) {}
iterator_over& operator=(const iterator_over& o) {
iterator& operator=(const iterator& o) {
histogram_ = o.histogram_;
idx_ = o.idx_;
cache_.reset();
@ -56,7 +56,7 @@ public:
}
private:
bool equal(const iterator_over& rhs) const noexcept {
bool equal(const iterator& rhs) const noexcept {
return &histogram_ == &rhs.histogram_ && idx_ == rhs.idx_;
}

View File

@ -9,7 +9,7 @@
#include <boost/histogram/axis/ostream_operators.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/storage/weight_counter.hpp>
#include <boost/histogram/weight_counter.hpp>
#include <ostream>
namespace boost {

View File

@ -1,111 +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_STORAGE_ARRAY_HPP
#define BOOST_HISTOGRAM_STORAGE_ARRAY_HPP
#include <algorithm>
#include <boost/assert.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/storage/weight_counter.hpp>
#include <cstddef>
#include <memory>
#include <vector>
namespace boost {
namespace histogram {
template <typename T, typename Allocator>
struct array_storage {
using allocator_type = Allocator;
using element_type = T;
using const_reference = const T&;
using buffer_type = std::vector<element_type, allocator_type>;
array_storage(const array_storage&) = default;
array_storage& operator=(const array_storage&) = default;
array_storage(array_storage&&) = default;
array_storage& operator=(array_storage&&) = default;
template <typename S, typename = detail::requires_storage<S>>
explicit array_storage(const S& o) : buffer(o.get_allocator()) {
buffer.reserve(o.size());
for (std::size_t i = 0; i < o.size(); ++i)
buffer.emplace_back(static_cast<element_type>(o[i]));
}
template <typename S, typename = detail::requires_storage<S>>
array_storage& operator=(const S& o) {
buffer = buffer_type(o.get_allocator());
buffer.reserve(o.size());
for (std::size_t i = 0; i < o.size(); ++i)
buffer.emplace_back(static_cast<element_type>(o[i]));
return *this;
}
explicit array_storage(const allocator_type& a = allocator_type()) : buffer(a) {}
allocator_type get_allocator() const { return buffer.get_allocator(); }
void reset(std::size_t s) {
if (s == size()) {
std::fill(buffer.begin(), buffer.end(), element_type(0));
} else {
buffer = buffer_type(s, element_type(0), buffer.get_allocator());
}
}
std::size_t size() const noexcept { return buffer.size(); }
void increase(std::size_t i) noexcept {
BOOST_ASSERT(i < size());
++buffer[i];
}
template <typename U>
void add(std::size_t i, const U& x) noexcept {
BOOST_ASSERT(i < size());
buffer[i] += x;
}
const_reference operator[](std::size_t i) const noexcept {
BOOST_ASSERT(i < size());
return buffer[i];
}
template <typename... Ts>
bool operator==(const array_storage<Ts...>& rhs) const noexcept {
if (size() != rhs.size()) return false;
return std::equal(buffer.begin(), buffer.end(), rhs.buffer.begin());
}
template <typename U>
bool operator==(const U& rhs) const noexcept {
if (size() != rhs.size()) return false;
for (std::size_t i = 0; i < size(); ++i)
if (!(buffer[i] == rhs[i])) return false;
return true;
}
template <typename S>
array_storage& operator+=(const S& rhs) noexcept {
for (std::size_t i = 0; i < size(); ++i) add(i, rhs[i]);
return *this;
}
array_storage& operator*=(const double x) noexcept {
for (std::size_t i = 0; i < size(); ++i) buffer[i] *= x;
return *this;
}
buffer_type buffer;
};
} // namespace histogram
} // namespace boost
#endif

View File

@ -0,0 +1,161 @@
// Copyright 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_STORAGE_ADAPTOR_HPP
#define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
#include <algorithm>
#include <array>
#include <boost/assert.hpp>
#include <boost/histogram/adaptive_storage.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <map>
#include <stdexcept>
#include <type_traits>
#include <vector>
namespace boost {
namespace histogram {
namespace detail {
template <typename T>
void increment_element_impl(std::true_type, T& t) {
t();
}
template <typename T>
void increment_element_impl(std::false_type, T& t) {
++t;
}
template <typename T>
void increment_element(T& t) {
increment_element_impl(is_callable<T>(), t);
}
template <typename HasResize, typename HasClear, typename T>
struct storage_reset_impl;
template <typename T>
struct storage_reset_impl<std::true_type, std::true_type, T> : T {
using T::T;
storage_reset_impl(const T& t) : T(t) {}
storage_reset_impl(T&& t) : T(std::move(t)) {}
void reset(T& t, std::size_t n) {
t.resize(n);
std::fill(t.begin(), t.end(), typename T::value_type());
}
};
template <typename HasClear, typename T>
struct storage_reset_impl<std::false_type, HasClear, T> : T {
using T::T;
storage_reset_impl(const T& t) : T(t) {}
storage_reset_impl(T&& t) : T(std::move(t)) {}
void reset(T& t, std::size_t n) {
if (n > this->max_size()) // for std::array
throw std::runtime_error(
detail::cat("size ", n, " exceeds maximum capacity ", t.max_size()));
// map-like has clear(), std::array does not
static_if<HasClear>(
[](auto& t) { t.clear(); },
[n](auto& t) { std::fill_n(t.begin(), n, typename T::value_type()); }, t);
size_ = n;
}
std::size_t size_ = 0;
std::size_t size() const { return size_; }
};
template <typename T>
using storage_reset = storage_reset_impl<has_method_resize<T>, has_method_clear<T>, T>;
} // namespace detail
/// generic implementation for std::array, vector-like, and map-like containers
template <typename T>
struct storage_adaptor : detail::storage_reset<T> {
using base_type = detail::storage_reset<T>;
using base_type::base_type;
using value_type = typename T::value_type;
using const_reference = typename T::const_reference;
storage_adaptor() = default;
storage_adaptor(const storage_adaptor&) = default;
storage_adaptor& operator=(const storage_adaptor&) = default;
storage_adaptor(storage_adaptor&&) = default;
storage_adaptor& operator=(storage_adaptor&&) = default;
storage_adaptor(const T& t) : base_type(t) {}
storage_adaptor(T&& t) : base_type(std::move(t)) {}
template <typename U>
storage_adaptor(const storage_adaptor<U>& rhs) {
reset(rhs.size());
(*this) = rhs;
}
template <typename U>
storage_adaptor& operator=(const storage_adaptor<U>& rhs) {
reset(rhs.size());
(*this) = rhs;
return *this;
}
void reset(std::size_t n) { detail::storage_reset<T>::reset(*this, n); }
void operator()(std::size_t i) {
BOOST_ASSERT(i < this->size());
detail::increment_element((*this)[i]);
}
template <typename U>
void operator()(std::size_t i, U&& u) {
BOOST_ASSERT(i < this->size());
(*this)[i] += std::forward<U>(u);
}
// precondition: storages have equal size
template <typename U>
storage_adaptor& operator+=(const storage_adaptor<U>& u) {
const auto n = this->size();
BOOST_ASSERT_MSG(n == u.size(), "sizes must be equal");
for (std::size_t i = 0; i < n; ++i) (*this)(i, u[i]);
return *this;
}
storage_adaptor& operator*=(const double x) {
for (std::size_t i = 0, n = this->size(); i < n; ++i) (*this)[i] *= x;
return *this;
}
storage_adaptor& operator/=(const double x) { return operator*=(1.0 / x); }
template <typename U>
bool operator==(const storage_adaptor<U>& u) const {
const auto n = this->size();
if (n != u.size()) return false;
for (std::size_t i = 0; i < n; ++i)
if (!((*this)[i] == u[i])) return false;
return true;
}
};
template <typename A>
struct storage_adaptor<adaptive_storage<A>> : adaptive_storage<A> {
using base_type = adaptive_storage<A>;
using value_type = typename base_type::value_type;
using const_reference = typename base_type::const_reference;
using base_type::base_type;
storage_adaptor(const adaptive_storage<A>& t) : base_type(t) {}
storage_adaptor(adaptive_storage<A>&& t) : base_type(std::move(t)) {}
};
} // namespace histogram
} // namespace boost
#endif

View File

@ -35,6 +35,13 @@ public:
return *this;
}
template <typename T>
weight_counter& operator+=(const weight_type<T>& rhs) {
w += rhs.value;
w2 += rhs.value * rhs.value;
return *this;
}
// TODO: explain why this is needed
weight_counter& operator+=(const RealType& x) noexcept {
w += x;
@ -49,14 +56,6 @@ public:
return *this;
}
template <typename T>
weight_counter& operator+=(const weight_type<T>& rhs) {
const auto x = static_cast<RealType>(rhs.value);
w += x;
w2 += x * x;
return *this;
}
weight_counter& operator*=(const RealType& x) noexcept {
w *= x;
w2 *= x * x;

View File

@ -5,16 +5,17 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/storage/adaptive_storage.hpp>
#include <boost/histogram/storage/array_storage.hpp>
#include <boost/histogram/adaptive_storage.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <limits>
#include <memory>
#include <sstream>
#include <vector>
namespace bh = boost::histogram;
using adaptive_storage_type = bh::adaptive_storage<std::allocator<char>>;
using adaptive_storage_type = bh::storage_adaptor<bh::adaptive_storage<>>;
template <typename T>
using array_storage = bh::array_storage<T, std::allocator<T>>;
using array_storage = bh::storage_adaptor<std::vector<T>>;
using bh::weight;
template <typename T>
@ -35,11 +36,11 @@ void copy_impl() {
const auto b = prepare<T>(1);
auto a(b);
BOOST_TEST(a == b);
a.increase(0);
a(0);
BOOST_TEST(!(a == b));
a = b;
BOOST_TEST(a == b);
a.increase(0);
a(0);
BOOST_TEST(!(a == b));
a = b;
a = prepare<T>(2);
@ -54,7 +55,7 @@ void equal_1_impl() {
auto b = prepare(1, T(0));
BOOST_TEST_EQ(a[0], 0.0);
BOOST_TEST(a == b);
b.increase(0);
b(0);
BOOST_TEST(!(a == b));
}
@ -71,10 +72,10 @@ void equal_1_impl<void>() {
BOOST_TEST(d == a);
BOOST_TEST(!(a == c));
BOOST_TEST(!(c == a));
b.increase(0);
b(0);
BOOST_TEST(!(a == b));
BOOST_TEST(!(b == a));
d.increase(0);
d(0);
BOOST_TEST(!(a == d));
BOOST_TEST(!(d == a));
}
@ -85,7 +86,7 @@ void equal_2_impl() {
array_storage<U> b;
b.reset(1);
BOOST_TEST(a == b);
b.increase(0);
b(0);
BOOST_TEST(!(a == b));
}
@ -96,11 +97,11 @@ void increase_and_grow_impl() {
auto n = s;
auto n2 = s;
n.increase(0);
n(0);
auto x = prepare<void>(2);
x.increase(0);
n2.add(0, x[0]);
x(0);
n2(0, x[0]);
double v = tmax;
++v;
@ -115,7 +116,7 @@ void increase_and_grow_impl<void>() {
auto s = prepare<void>(2);
BOOST_TEST_EQ(s[0], 0);
BOOST_TEST_EQ(s[1], 0);
s.increase(0);
s(0);
BOOST_TEST_EQ(s[0], 1);
BOOST_TEST_EQ(s[1], 0);
}
@ -125,31 +126,31 @@ void convert_array_storage_impl() {
const auto aref = prepare(1, T(0));
array_storage<uint8_t> s;
s.reset(1);
s.increase(0);
s(0);
auto a = aref;
a = s;
BOOST_TEST_EQ(a[0], 1.0);
BOOST_TEST(a == s);
a.increase(0);
a(0);
BOOST_TEST(!(a == s));
adaptive_storage_type b(s);
BOOST_TEST_EQ(b[0], 1.0);
BOOST_TEST(b == s);
b.increase(0);
b(0);
BOOST_TEST(!(b == s));
auto c = aref;
c.add(0, s[0]);
c(0, s[0]);
BOOST_TEST_EQ(c[0], 1.0);
BOOST_TEST(c == s);
BOOST_TEST(s == c);
array_storage<float> t;
t.reset(1);
t.increase(0);
while (t[0] < 1e20) t.add(0, t[0]);
t(0);
while (t[0] < 1e20) t(0, t[0]);
auto d = aref;
d = t;
BOOST_TEST(d == t);
@ -158,24 +159,24 @@ void convert_array_storage_impl() {
e = s;
BOOST_TEST_EQ(e[0], 1.0);
BOOST_TEST(e == s);
e.increase(0);
e(0);
BOOST_TEST(!(e == s));
adaptive_storage_type f(s);
BOOST_TEST_EQ(f[0], 1.0);
BOOST_TEST(f == s);
f.increase(0);
f(0);
BOOST_TEST(!(f == s));
auto g = aref;
g.add(0, s[0]);
g(0, s[0]);
BOOST_TEST_EQ(g[0], 1.0);
BOOST_TEST(g == s);
BOOST_TEST(s == g);
array_storage<uint8_t> u;
u.reset(2);
u.increase(0);
u(0);
auto h = aref;
BOOST_TEST(!(h == u));
h = u;
@ -188,24 +189,24 @@ void convert_array_storage_impl<void>() {
BOOST_TEST_EQ(aref[0], 0.0);
array_storage<uint8_t> s;
s.reset(1);
s.increase(0);
s(0);
auto a = aref;
a = s;
BOOST_TEST_EQ(a[0], 1.0);
BOOST_TEST(a == s);
a.increase(0);
a(0);
BOOST_TEST(!(a == s));
auto c = aref;
c.add(0, s[0]);
c(0, s[0]);
BOOST_TEST_EQ(c[0], 1.0);
BOOST_TEST(c == s);
BOOST_TEST(s == c);
array_storage<uint8_t> t;
t.reset(2);
t.increase(0);
t(0);
auto d = aref;
BOOST_TEST(!(d == t));
}
@ -219,8 +220,8 @@ void add_impl() {
BOOST_TEST_EQ(a[0], 0);
BOOST_TEST_EQ(a[1], 0);
} else {
b.increase(0);
b.increase(0);
b(0);
b(0);
a += b;
BOOST_TEST_EQ(a[0], 2);
BOOST_TEST_EQ(a[1], 0);
@ -242,10 +243,10 @@ int main() {
// low-level tools
{
uint8_t c = 0;
BOOST_TEST_EQ(bh::detail::safe_increase(c), true);
BOOST_TEST_EQ(bh::detail::safe_increment(c), true);
BOOST_TEST_EQ(c, 1);
c = 255;
BOOST_TEST_EQ(bh::detail::safe_increase(c), false);
BOOST_TEST_EQ(bh::detail::safe_increment(c), false);
BOOST_TEST_EQ(c, 255);
BOOST_TEST_EQ(bh::detail::safe_assign(c, 255), true);
BOOST_TEST_EQ(bh::detail::safe_assign(c, 256), false);
@ -301,7 +302,7 @@ int main() {
auto a = prepare<double>(1);
auto b = prepare<adaptive_storage_type::mp_int>(1);
BOOST_TEST(a == b);
a.increase(0);
a(0);
BOOST_TEST_NOT(a == b);
}
@ -317,7 +318,7 @@ int main() {
auto a = prepare<adaptive_storage_type::mp_int>(2, 1);
BOOST_TEST_EQ(a[0], 1);
BOOST_TEST_EQ(a[1], 0);
a.increase(0);
a(0);
BOOST_TEST_EQ(a[0], 2);
BOOST_TEST_EQ(a[1], 0);
}
@ -338,24 +339,24 @@ int main() {
auto a = prepare<void>(1);
a += a;
BOOST_TEST_EQ(a[0], 0);
a.increase(0);
a(0);
double x = 1;
auto b = prepare<void>(1);
b.increase(0);
b(0);
BOOST_TEST_EQ(b[0], x);
for (unsigned i = 0; i < 80; ++i) {
x += x;
a.add(0, a[0]);
a(0, a[0]);
b += b;
BOOST_TEST_EQ(a[0], x);
BOOST_TEST_EQ(b[0], x);
auto c = prepare<void>(1);
c.add(0, a[0]);
c(0, a[0]);
BOOST_TEST_EQ(c[0], x);
c.add(0, weight(0));
c(0, weight(0));
BOOST_TEST_EQ(c[0], x);
auto d = prepare<void>(1);
d.add(0, weight(x));
d(0, weight(x));
BOOST_TEST_EQ(d[0], x);
}
}
@ -366,11 +367,11 @@ int main() {
a *= 2;
BOOST_TEST_EQ(a[0], 0);
BOOST_TEST_EQ(a[1], 0);
a.increase(0);
a(0);
a *= 3;
BOOST_TEST_EQ(a[0], 3);
BOOST_TEST_EQ(a[1], 0);
a.add(1, 2);
a(1, 2);
BOOST_TEST_EQ(a[0], 3);
BOOST_TEST_EQ(a[1], 2);
a *= 3;

View File

@ -103,7 +103,7 @@ int main() {
// axis::variant streamable
{
auto test = [](auto&& a, const char* ref) {
using T = detail::rm_cvref<decltype(a)>;
using T = detail::unqual<decltype(a)>;
axis::variant<T> axis(std::move(a));
std::ostringstream os;
os << axis;

View File

@ -11,5 +11,6 @@ using namespace boost::histogram;
int main() {
std::vector<axis::variant<axis::integer<>>> v(1, axis::integer<>(0, 2));
auto a = make_histogram(v);
a(std::vector<int>(1)); // fails, because tuple is intentionally not unpacked
auto i = std::vector<int>(1);
a(i); // fails, because tuple is intentionally not unpacked
}

View File

@ -10,9 +10,9 @@
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/literals.hpp>
#include <boost/histogram/ostream_operators.hpp>
#include <boost/histogram/storage/adaptive_storage.hpp>
#include <boost/histogram/storage/array_storage.hpp>
#include <boost/histogram/storage/weight_counter.hpp>
#include <boost/histogram/adaptive_storage.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/weight_counter.hpp>
#include <cstdlib>
#include <limits>
#include <numeric>
@ -38,7 +38,7 @@ int main() {
BOOST_TEST_EQ(h.axis(0), v[0]);
BOOST_TEST_EQ(h.axis(1), v[1]);
auto h2 = make_histogram_with(array_storage<int>(), v.begin(), v.end());
auto h2 = make_histogram_with(std::vector<int>(), v.begin(), v.end());
BOOST_TEST_EQ(h2.rank(), 2);
BOOST_TEST_EQ(h2.axis(0), v[0]);
BOOST_TEST_EQ(h2.axis(1), v[1]);
@ -88,7 +88,7 @@ int main() {
// histogram iterator
{
auto h =
make_s(dynamic_tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 3));
make_s(dynamic_tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 3));
const auto& a = h.axis();
h(weight(2), 0);
h(1);

View File

@ -9,9 +9,9 @@
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/literals.hpp>
#include <boost/histogram/ostream_operators.hpp>
#include <boost/histogram/storage/adaptive_storage.hpp>
#include <boost/histogram/storage/array_storage.hpp>
#include <boost/histogram/storage/weight_counter.hpp>
#include <boost/histogram/adaptive_storage.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/weight_counter.hpp>
#include <sstream>
#include <stdexcept>
#include <tuple>
@ -43,7 +43,7 @@ void run_tests() {
BOOST_TEST_EQ(h.size(), 5);
BOOST_TEST_EQ(h.axis(0_c).size(), 3);
BOOST_TEST_EQ(h.axis().size(), 3);
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1});
auto h2 = make_s(Tag(), std::vector<unsigned>(), axis::regular<>{3, -1, 1});
BOOST_TEST_EQ(h2, h);
}
@ -54,7 +54,7 @@ void run_tests() {
BOOST_TEST_EQ(h.size(), 30);
BOOST_TEST_EQ(h.axis(0_c).size(), 3);
BOOST_TEST_EQ(h.axis(1_c).size(), 4);
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
auto h2 = make_s(Tag(), std::vector<unsigned>(), axis::regular<>{3, -1, 1},
axis::integer<>{-1, 3});
BOOST_TEST_EQ(h2, h);
}
@ -65,7 +65,7 @@ void run_tests() {
axis::circular<>{2});
BOOST_TEST_EQ(h.rank(), 3);
BOOST_TEST_EQ(h.size(), 5 * 5 * 3);
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
auto h2 = make_s(Tag(), std::vector<unsigned>(), axis::regular<>{3, -1, 1},
axis::integer<>{-1, 2}, axis::circular<>{2});
BOOST_TEST_EQ(h2, h);
}
@ -77,7 +77,7 @@ void run_tests() {
BOOST_TEST_EQ(h.rank(), 4);
BOOST_TEST_EQ(h.size(), 5 * 5 * 3 * 4);
auto h2 =
make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
make_s(Tag(), std::vector<unsigned>(), axis::regular<>{3, -1, 1},
axis::integer<>{-1, 2}, axis::circular<>{2}, axis::variable<>{-1, 0, 1});
BOOST_TEST_EQ(h2, h);
}
@ -89,7 +89,7 @@ void run_tests() {
axis::category<>{{3, 1, 2}});
BOOST_TEST_EQ(h.rank(), 5);
BOOST_TEST_EQ(h.size(), 5 * 5 * 3 * 4 * 4);
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
auto h2 = make_s(Tag(), std::vector<unsigned>(), axis::regular<>{3, -1, 1},
axis::integer<>{-1, 2}, axis::circular<>{2},
axis::variable<>{-1, 0, 1}, axis::category<>{{3, 1, 2}});
BOOST_TEST_EQ(h2, h);
@ -102,7 +102,7 @@ void run_tests() {
auto h2 = decltype(h)(h);
BOOST_TEST(h2 == h);
auto h3 =
histogram<std::tuple<axis::integer<>, axis::integer<>>, array_storage<unsigned>>(
histogram<std::tuple<axis::integer<>, axis::integer<>>, std::vector<unsigned>>(
h);
BOOST_TEST_EQ(h3, h);
}
@ -119,7 +119,7 @@ void run_tests() {
h2 = h2;
BOOST_TEST_EQ(h, h2);
auto h3 = histogram<std::tuple<axis::integer<>, axis::integer<>>,
array_storage<unsigned>>();
std::vector<unsigned>>();
h3 = h;
BOOST_TEST_EQ(h, h3);
}
@ -253,7 +253,7 @@ void run_tests() {
// d1w
{
auto h = make_s(Tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 2));
auto h = make_s(Tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 2));
h(-1);
h(0);
h(weight(0.5), 0);
@ -307,7 +307,7 @@ void run_tests() {
// d2w
{
auto h = make_s(Tag(), array_storage<weight_counter<>>(), axis::regular<>(2, -1, 1),
auto h = make_s(Tag(), std::vector<weight_counter<>>(), axis::regular<>(2, -1, 1),
axis::integer<>(-1, 2, {}, axis::option_type::none));
h(-1, 0); // -> 0, 1
h(weight(10), -1, -1); // -> 0, 0
@ -352,7 +352,7 @@ void run_tests() {
// d3w
{
auto h = make_s(Tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 3),
auto h = make_s(Tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 3),
axis::integer<>(0, 4), axis::integer<>(0, 5));
for (auto i = 0u; i < h.axis(0_c).size(); ++i) {
for (auto j = 0u; j < h.axis(1_c).size(); ++j) {
@ -373,7 +373,7 @@ void run_tests() {
// add_1
{
auto a = make(Tag(), axis::integer<>(0, 2));
auto b = make_s(Tag(), array_storage<unsigned>(), axis::integer<>(0, 2));
auto b = make_s(Tag(), std::vector<unsigned>(), axis::integer<>(0, 2));
a(0); // 1 0
b(1); // 0 1
auto a2 = a;
@ -395,8 +395,8 @@ void run_tests() {
// add_2
{
auto a = make_s(Tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 2));
auto b = make_s(Tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 2));
auto a = make_s(Tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 2));
auto b = make_s(Tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 2));
a(0);
BOOST_TEST_EQ(a.at(0).variance(), 1);
@ -422,8 +422,8 @@ void run_tests() {
// add_3
{
auto a = make_s(Tag(), array_storage<char>(), axis::integer<>(-1, 2));
auto b = make_s(Tag(), array_storage<unsigned>(), axis::integer<>(-1, 2));
auto a = make_s(Tag(), std::vector<char>(), axis::integer<>(-1, 2));
auto b = make_s(Tag(), std::vector<unsigned>(), axis::integer<>(-1, 2));
a(-1);
b(1);
auto c = a;
@ -652,7 +652,7 @@ void run_tests() {
// histogram iterator 1D
{
auto h = make_s(Tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 3));
auto h = make_s(Tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 3));
const auto& a = h.axis();
h(weight(2), 0);
h(1);
@ -686,7 +686,7 @@ void run_tests() {
// histogram iterator 2D
{
auto h = make_s(Tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 1),
auto h = make_s(Tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 1),
axis::integer<>(2, 4, "", axis::option_type::none));
const auto& a0 = h.axis(0_c);
const auto& a1 = h.axis(1_c);
@ -748,7 +748,7 @@ void run_tests() {
// using STL containers
{
auto h = make_s(Tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 2),
auto h = make_s(Tag(), std::vector<weight_counter<>>(), axis::integer<>(0, 2),
axis::regular<>(2, 2, 4));
// vector in
h(std::vector<int>({0, 2}));
@ -806,12 +806,12 @@ void run_tests() {
tracing_allocator_db db;
{
tracing_allocator<char> a(db);
auto h = make_s(Tag(), array_storage<int, tracing_allocator<int>>(a),
auto h = make_s(Tag(), std::vector<int, tracing_allocator<int>>(a),
axis::integer<>(0, 1000));
h(0);
}
// int allocation for array_storage
// int allocation for std::vector
BOOST_TEST_EQ(db[typeid(int)].first, db[typeid(int)].second);
BOOST_TEST_EQ(db[typeid(int)].first, 1002u);

View File

@ -4,14 +4,17 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <array>
#include <boost/core/lightweight_test.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/literals.hpp>
#include <boost/mp11.hpp>
#include <iterator>
#include <map>
#include <tuple>
#include <type_traits>
#include <utility>
@ -77,18 +80,13 @@ int main() {
// has_method_options
{
struct NotOptions {};
struct A {};
struct B {
NotOptions options();
};
struct C {
bh::axis::option_type options();
};
BOOST_TEST_TRAIT_FALSE((has_method_options<A>));
BOOST_TEST_TRAIT_FALSE((has_method_options<B>));
BOOST_TEST_TRAIT_TRUE((has_method_options<C>));
BOOST_TEST_TRAIT_TRUE((has_method_options<B>));
}
// has_method_metadata
@ -102,6 +100,54 @@ int main() {
BOOST_TEST_TRAIT_TRUE((has_method_metadata<B>));
}
// has_method_resize
{
struct A {};
using B = std::vector<int>;
using C = std::map<int, int>;
BOOST_TEST_TRAIT_FALSE((has_method_resize<A>));
BOOST_TEST_TRAIT_TRUE((has_method_resize<B>));
BOOST_TEST_TRAIT_FALSE((has_method_resize<C>));
}
// has_method_size
{
struct A {};
using B = std::vector<int>;
using C = std::map<int, int>;
BOOST_TEST_TRAIT_FALSE((has_method_size<A>));
BOOST_TEST_TRAIT_TRUE((has_method_size<B>));
BOOST_TEST_TRAIT_TRUE((has_method_size<C>));
}
// has_method_clear
{
struct A {};
using B = std::vector<int>;
using C = std::map<int, int>;
using D = std::array<int, 10>;
BOOST_TEST_TRAIT_FALSE((has_method_clear<A>));
BOOST_TEST_TRAIT_TRUE((has_method_clear<B>));
BOOST_TEST_TRAIT_TRUE((has_method_clear<C>));
BOOST_TEST_TRAIT_FALSE((has_method_clear<D>));
}
// is_indexable
{
struct A {};
using B = std::vector<int>;
using C = std::map<int, int>;
using D = std::map<A, int>;
BOOST_TEST_TRAIT_FALSE((is_indexable<A>));
BOOST_TEST_TRAIT_TRUE((is_indexable<B>));
BOOST_TEST_TRAIT_TRUE((is_indexable<C>));
BOOST_TEST_TRAIT_FALSE((is_indexable<D>));
}
// is_transform
{
struct A {};
@ -114,6 +160,10 @@ int main() {
BOOST_TEST_TRAIT_TRUE((is_transform<B>));
}
{
}
// is_equal_comparable
{
struct A {};
@ -214,7 +264,7 @@ int main() {
BOOST_TEST_EQ(v2, std::vector<bool>({false, true, false, true}));
}
// rm_cvref
// unqual
{
using T1 = int;
using T2 = int&&;
@ -224,14 +274,14 @@ int main() {
using T6 = volatile int&&;
using T7 = volatile const int;
using T8 = volatile const int&;
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T1>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T2>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T3>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T4>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T5>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T6>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T7>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T8>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T1>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T2>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T3>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T4>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T5>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T6>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T7>, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<unqual<T8>, int>));
}
// mp_size
@ -270,12 +320,12 @@ int main() {
BOOST_TEST_TRAIT_TRUE((std::is_same<mp_last<L>, long>));
}
// container_element_type
// container_value_type
{
using T1 = std::vector<int>;
using U1 = container_element_type<T1>;
using U1 = container_value_type<T1>;
using T2 = const std::vector<const int>&;
using U2 = container_element_type<T2>;
using U2 = container_value_type<T2>;
BOOST_TEST_TRAIT_TRUE((std::is_same<U1, int>));
BOOST_TEST_TRAIT_TRUE((std::is_same<U2, const int>));
}
@ -334,10 +384,17 @@ int main() {
using B = std::vector<bh::axis::variant<bh::axis::regular<>>>;
using C = std::vector<int>;
using D = bh::axis::regular<>;
using E = const std::vector<bh::axis::variant<bh::axis::integer<>>>;
using F = const std::vector<bh::axis::variant<bh::axis::integer<>>>&;
auto v = std::vector<bh::axis::variant<bh::axis::regular<>, bh::axis::integer<>>>();
BOOST_TEST_TRAIT_TRUE((is_axis_vector<A>));
BOOST_TEST_TRAIT_TRUE((is_axis_vector<B>));
BOOST_TEST_TRAIT_FALSE((is_axis_vector<C>));
BOOST_TEST_TRAIT_FALSE((is_axis_vector<D>));
BOOST_TEST_TRAIT_TRUE((is_axis_vector<E>));
BOOST_TEST_TRAIT_TRUE((is_axis_vector<std::remove_reference_t<F>>));
BOOST_TEST_TRAIT_TRUE((is_axis_vector<decltype(v)>));
BOOST_TEST_TRAIT_TRUE((is_axis_vector<decltype(std::move(v))>));
}
return boost::report_errors();

View File

@ -6,39 +6,52 @@
#include <array>
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/adaptive_storage.hpp>
#include <boost/histogram/histogram_fwd.hpp>
#include <boost/histogram/storage/array_storage.hpp>
#include <boost/histogram/storage/weight_counter.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/weight_counter.hpp>
#include <limits>
#include <vector>
using namespace boost::histogram;
template <typename T>
using vector_storage = storage_adaptor<std::vector<T>>;
template <typename T>
using array_storage = storage_adaptor<std::array<100, T>>;
int main() {
using namespace boost::histogram;
// ctor and reset
// ctor, copy, move
{
array_storage<unsigned> a;
BOOST_TEST_EQ(a.size(), 0);
a.reset(1);
BOOST_TEST_EQ(a.size(), 1);
a.increase(0);
BOOST_TEST_EQ(a[0], 1);
a.reset(1);
BOOST_TEST_EQ(a[0], 0);
vector_storage<unsigned> a;
vector_storage<unsigned> b(a);
vector_storage<unsigned> c;
c = a;
vector_storage<unsigned> d(std::move(a));
vector_storage<unsigned> e;
e = std::move(b);
vector_storage<int> f(e);
vector_storage<unsigned> g(std::vector<unsigned>(10, 0));
array_storage<unsigned> h;
h = g;
array_storage<unsigned> i(g);
}
// increase
// increment and reset
{
array_storage<unsigned> a, b;
vector_storage<unsigned> a, b;
a.reset(1);
b.reset(1);
array_storage<unsigned char> c, d;
vector_storage<unsigned char> c, d;
c.reset(1);
d.reset(2);
a.increase(0);
b.increase(0);
c.increase(0);
c.increase(0);
d.increase(0);
a.increment(0);
b.increment(0);
c.increment(0);
c.increment(0);
d.increment(0);
d.add(1, 5);
d.add(0, 2);
BOOST_TEST_EQ(a[0], 1);
@ -54,9 +67,9 @@ int main() {
// multiply
{
array_storage<double> a;
vector_storage<double> a;
a.reset(2);
a.increase(0);
a.increment(0);
a *= 3;
BOOST_TEST_EQ(a[0], 3);
BOOST_TEST_EQ(a[1], 0);
@ -70,9 +83,9 @@ int main() {
// copy
{
array_storage<unsigned> a;
vector_storage<unsigned> a;
a.reset(1);
a.increase(0);
a.increment(0);
decltype(a) b;
b.reset(2);
BOOST_TEST(!(a == b));
@ -86,7 +99,7 @@ int main() {
BOOST_TEST_EQ(c.size(), 1);
BOOST_TEST_EQ(c[0], 1);
array_storage<unsigned char> d;
vector_storage<unsigned char> d;
d.reset(1);
BOOST_TEST(!(a == d));
d = a;
@ -97,9 +110,9 @@ int main() {
// move
{
array_storage<unsigned> a;
vector_storage<unsigned> a;
a.reset(1);
a.increase(0);
a.increment(0);
decltype(a) b;
BOOST_TEST(!(a == b));
b = std::move(a);
@ -114,9 +127,9 @@ int main() {
// with weight_counter
{
array_storage<weight_counter<double>> a;
vector_storage<weight_counter<double>> a;
a.reset(1);
a.increase(0);
a.increment(0);
a.add(0, 1);
a.add(0, weight_counter<double>(1, 0));
BOOST_TEST_EQ(a[0].value(), 3);

View File

@ -47,14 +47,14 @@ ostream& operator<<(ostream& os, const tuple<Ts...>& t) {
namespace boost {
namespace histogram {
template <typename Histogram>
typename Histogram::element_type sum(const Histogram& h) {
return std::accumulate(h.begin(), h.end(), typename Histogram::element_type(0));
auto sum(const Histogram& h) {
return std::accumulate(h.begin(), h.end(), typename Histogram::value_type(0));
}
template <typename... Ts>
std::vector<axis::variant<detail::rm_cvref<Ts>...>>
std::vector<axis::variant<detail::unqual<Ts>...>>
make_axis_vector(Ts&& ... ts) {
using T = axis::variant<detail::rm_cvref<Ts>...>;
using T = axis::variant<detail::unqual<Ts>...>;
return std::vector<T>({T(std::forward<Ts>(ts))...});
}