add boost on mac

This commit is contained in:
Bassem Girgis
2019-08-10 16:38:17 -05:00
parent 861b918727
commit be945cb63b
14105 changed files with 2714968 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
// 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_ACCUMULATORS_MEAN_HPP
#define BOOST_HISTOGRAM_ACCUMULATORS_MEAN_HPP
#include <boost/histogram/fwd.hpp>
#include <cstddef>
#include <type_traits>
namespace boost {
namespace histogram {
namespace accumulators {
/** Calculates mean and variance of sample.
Uses Welfords's incremental algorithm to improve the numerical
stability of mean and variance computation.
*/
template <class RealType>
class mean {
public:
mean() = default;
mean(const std::size_t n, const RealType& mean, const RealType& variance)
: sum_(n), mean_(mean), sum_of_deltas_squared_(variance * (sum_ - 1)) {}
void operator()(const RealType& x) {
sum_ += 1;
const auto delta = x - mean_;
mean_ += delta / sum_;
sum_of_deltas_squared_ += delta * (x - mean_);
}
template <class T>
mean& operator+=(const mean<T>& rhs) {
const auto tmp = mean_ * sum_ + static_cast<RealType>(rhs.mean_ * rhs.sum_);
sum_ += rhs.sum_;
mean_ = tmp / sum_;
sum_of_deltas_squared_ += static_cast<RealType>(rhs.sum_of_deltas_squared_);
return *this;
}
mean& operator*=(const RealType& s) {
mean_ *= s;
sum_of_deltas_squared_ *= s * s;
return *this;
}
template <class T>
bool operator==(const mean<T>& rhs) const noexcept {
return sum_ == rhs.sum_ && mean_ == rhs.mean_ &&
sum_of_deltas_squared_ == rhs.sum_of_deltas_squared_;
}
template <class T>
bool operator!=(const mean<T>& rhs) const noexcept {
return !operator==(rhs);
}
std::size_t count() const noexcept { return sum_; }
const RealType& value() const noexcept { return mean_; }
RealType variance() const { return sum_of_deltas_squared_ / (sum_ - 1); }
template <class Archive>
void serialize(Archive&, unsigned /* version */);
private:
std::size_t sum_ = 0;
RealType mean_ = 0, sum_of_deltas_squared_ = 0;
};
} // namespace accumulators
} // namespace histogram
} // namespace boost
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace std {
template <class T, class U>
/// Specialization for boost::histogram::accumulators::mean.
struct common_type<boost::histogram::accumulators::mean<T>,
boost::histogram::accumulators::mean<U>> {
using type = boost::histogram::accumulators::mean<common_type_t<T, U>>;
};
} // namespace std
#endif
#endif

View File

@@ -0,0 +1,52 @@
// 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_ACCUMULATORS_OSTREAM_HPP
#define BOOST_HISTOGRAM_ACCUMULATORS_OSTREAM_HPP
#include <boost/histogram/fwd.hpp>
#include <iosfwd>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace boost {
namespace histogram {
namespace accumulators {
template <typename CharT, typename Traits, typename W>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const sum<W>& x) {
os << "sum(" << x.large() << " + " << x.small() << ")";
return os;
}
template <typename CharT, typename Traits, typename W>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const weighted_sum<W>& x) {
os << "weighted_sum(" << x.value() << ", " << x.variance() << ")";
return os;
}
template <typename CharT, typename Traits, typename W>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const mean<W>& x) {
os << "mean(" << x.count() << ", " << x.value() << ", " << x.variance() << ")";
return os;
}
template <typename CharT, typename Traits, typename W>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const weighted_mean<W>& x) {
os << "weighted_mean(" << x.sum_of_weights() << ", " << x.value() << ", "
<< x.variance() << ")";
return os;
}
} // namespace accumulators
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
#endif

View File

@@ -0,0 +1,107 @@
// 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_ACCUMULATORS_SUM_HPP
#define BOOST_HISTOGRAM_ACCUMULATORS_SUM_HPP
#include <boost/histogram/fwd.hpp>
#include <cmath>
#include <type_traits>
namespace boost {
namespace histogram {
namespace accumulators {
/**
Uses Neumaier algorithm to compute accurate sums.
The algorithm uses memory for two floats and is three to
five times slower compared to a simple floating point
number used to accumulate a sum, but the relative error
of the sum is at the level of the machine precision,
independent of the number of samples.
A. Neumaier, Zeitschrift fuer Angewandte Mathematik
und Mechanik 54 (1974) 39-51.
*/
template <typename RealType>
class sum {
public:
sum() = default;
/// Initialize sum to value
explicit sum(const RealType& value) noexcept : large_(value) {}
/// Set sum to value
sum& operator=(const RealType& value) noexcept {
large_ = value;
small_ = 0;
return *this;
}
/// Increment sum by one
sum& operator++() { return operator+=(1); }
/// Increment sum by value
sum& operator+=(const RealType& value) {
auto temp = large_ + value; // prevent optimization
if (std::abs(large_) >= std::abs(value))
small_ += (large_ - temp) + value;
else
small_ += (value - temp) + large_;
large_ = temp;
return *this;
}
/// Scale by value
sum& operator*=(const RealType& value) {
large_ *= value;
small_ *= value;
return *this;
}
template <class T>
bool operator==(const sum<T>& rhs) const noexcept {
return large_ == rhs.large_ && small_ == rhs.small_;
}
template <class T>
bool operator!=(const T& rhs) const noexcept {
return !operator==(rhs);
}
/// Return large part of the sum.
const RealType& large() const { return large_; }
/// Return small part of the sum.
const RealType& small() const { return small_; }
// allow implicit conversion to RealType
operator RealType() const { return large_ + small_; }
template <class Archive>
void serialize(Archive&, unsigned /* version */);
private:
RealType large_ = RealType();
RealType small_ = RealType();
};
} // namespace accumulators
} // namespace histogram
} // namespace boost
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace std {
template <class T, class U>
struct common_type<boost::histogram::accumulators::sum<T>,
boost::histogram::accumulators::sum<U>> {
using type = boost::histogram::accumulators::sum<common_type_t<T, U>>;
};
} // namespace std
#endif
#endif

View File

@@ -0,0 +1,106 @@
// 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_ACCUMULATORS_WEIGHTED_MEAN_HPP
#define BOOST_HISTOGRAM_ACCUMULATORS_WEIGHTED_MEAN_HPP
#include <boost/histogram/fwd.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace accumulators {
/**
Calculates mean and variance of weighted sample.
Uses West's incremental algorithm to improve numerical stability
of mean and variance computation.
*/
template <typename RealType>
class weighted_mean {
public:
weighted_mean() = default;
weighted_mean(const RealType& wsum, const RealType& wsum2, const RealType& mean,
const RealType& variance)
: sum_of_weights_(wsum)
, sum_of_weights_squared_(wsum2)
, weighted_mean_(mean)
, sum_of_weighted_deltas_squared_(
variance * (sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_)) {}
void operator()(const RealType& x) { operator()(1, x); }
void operator()(const RealType& w, const RealType& x) {
sum_of_weights_ += w;
sum_of_weights_squared_ += w * w;
const auto delta = x - weighted_mean_;
weighted_mean_ += w * delta / sum_of_weights_;
sum_of_weighted_deltas_squared_ += w * delta * (x - weighted_mean_);
}
template <typename T>
weighted_mean& operator+=(const weighted_mean<T>& rhs) {
const auto tmp = weighted_mean_ * sum_of_weights_ +
static_cast<RealType>(rhs.weighted_mean_ * rhs.sum_of_weights_);
sum_of_weights_ += static_cast<RealType>(rhs.sum_of_weights_);
sum_of_weights_squared_ += static_cast<RealType>(rhs.sum_of_weights_squared_);
weighted_mean_ = tmp / sum_of_weights_;
sum_of_weighted_deltas_squared_ +=
static_cast<RealType>(rhs.sum_of_weighted_deltas_squared_);
return *this;
}
weighted_mean& operator*=(const RealType& s) {
weighted_mean_ *= s;
sum_of_weighted_deltas_squared_ *= s * s;
return *this;
}
template <typename T>
bool operator==(const weighted_mean<T>& rhs) const noexcept {
return sum_of_weights_ == rhs.sum_of_weights_ &&
sum_of_weights_squared_ == rhs.sum_of_weights_squared_ &&
weighted_mean_ == rhs.weighted_mean_ &&
sum_of_weighted_deltas_squared_ == rhs.sum_of_weighted_deltas_squared_;
}
template <typename T>
bool operator!=(const T& rhs) const noexcept {
return !operator==(rhs);
}
const RealType& sum_of_weights() const noexcept { return sum_of_weights_; }
const RealType& value() const noexcept { return weighted_mean_; }
RealType variance() const {
return sum_of_weighted_deltas_squared_ /
(sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_);
}
template <class Archive>
void serialize(Archive&, unsigned /* version */);
private:
RealType sum_of_weights_ = RealType(), sum_of_weights_squared_ = RealType(),
weighted_mean_ = RealType(), sum_of_weighted_deltas_squared_ = RealType();
};
} // namespace accumulators
} // namespace histogram
} // namespace boost
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace std {
template <class T, class U>
/// Specialization for boost::histogram::accumulators::weighted_mean.
struct common_type<boost::histogram::accumulators::weighted_mean<T>,
boost::histogram::accumulators::weighted_mean<U>> {
using type = boost::histogram::accumulators::weighted_mean<common_type_t<T, U>>;
};
} // namespace std
#endif
#endif

View File

@@ -0,0 +1,112 @@
// 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_ACCUMULATORS_WEIGHTED_SUM_HPP
#define BOOST_HISTOGRAM_ACCUMULATORS_WEIGHTED_SUM_HPP
#include <boost/histogram/fwd.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace accumulators {
/// Holds sum of weights and its variance estimate
template <typename RealType>
class weighted_sum {
public:
weighted_sum() = default;
explicit weighted_sum(const RealType& value) noexcept
: sum_of_weights_(value), sum_of_weights_squared_(value) {}
weighted_sum(const RealType& value, const RealType& variance) noexcept
: sum_of_weights_(value), sum_of_weights_squared_(variance) {}
/// Increment by one.
weighted_sum& operator++() { return operator+=(1); }
/// Increment by value.
template <typename T>
weighted_sum& operator+=(const T& value) {
sum_of_weights_ += value;
sum_of_weights_squared_ += value * value;
return *this;
}
/// Added another weighted sum.
template <typename T>
weighted_sum& operator+=(const weighted_sum<T>& rhs) {
sum_of_weights_ += static_cast<RealType>(rhs.sum_of_weights_);
sum_of_weights_squared_ += static_cast<RealType>(rhs.sum_of_weights_squared_);
return *this;
}
/// Scale by value.
weighted_sum& operator*=(const RealType& x) {
sum_of_weights_ *= x;
sum_of_weights_squared_ *= x * x;
return *this;
}
bool operator==(const RealType& rhs) const noexcept {
return sum_of_weights_ == rhs && sum_of_weights_squared_ == rhs;
}
template <typename T>
bool operator==(const weighted_sum<T>& rhs) const noexcept {
return sum_of_weights_ == rhs.sum_of_weights_ &&
sum_of_weights_squared_ == rhs.sum_of_weights_squared_;
}
template <typename T>
bool operator!=(const T& rhs) const noexcept {
return !operator==(rhs);
}
/// Return value of the sum.
const RealType& value() const noexcept { return sum_of_weights_; }
/// Return estimated variance of the sum.
const RealType& variance() const noexcept { return sum_of_weights_squared_; }
// lossy conversion must be explicit
template <class T>
explicit operator T() const {
return static_cast<T>(sum_of_weights_);
}
template <class Archive>
void serialize(Archive&, unsigned /* version */);
private:
RealType sum_of_weights_ = RealType();
RealType sum_of_weights_squared_ = RealType();
};
} // namespace accumulators
} // namespace histogram
} // namespace boost
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace std {
template <class T, class U>
struct common_type<boost::histogram::accumulators::weighted_sum<T>,
boost::histogram::accumulators::weighted_sum<U>> {
using type = boost::histogram::accumulators::weighted_sum<common_type_t<T, U>>;
};
template <class T, class U>
struct common_type<boost::histogram::accumulators::weighted_sum<T>, U> {
using type = boost::histogram::accumulators::weighted_sum<common_type_t<T, U>>;
};
template <class T, class U>
struct common_type<T, boost::histogram::accumulators::weighted_sum<U>> {
using type = boost::histogram::accumulators::weighted_sum<common_type_t<T, U>>;
};
} // namespace std
#endif
#endif

View File

@@ -0,0 +1,98 @@
// 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_ALGORITHM_PROJECT_HPP
#define BOOST_HISTOGRAM_ALGORITHM_PROJECT_HPP
#include <algorithm>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/set.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <type_traits>
namespace boost {
namespace histogram {
namespace algorithm {
/**
Returns a lower-dimensional histogram, summing over removed axes.
Arguments are the source histogram and compile-time numbers, the remaining indices of
the axes. Returns a new histogram which only contains the subset of axes. The source
histogram is summed over the removed axes.
*/
template <class A, class S, unsigned N, typename... Ns>
auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N>, Ns...) {
using LN = mp11::mp_list<std::integral_constant<unsigned, N>, Ns...>;
static_assert(mp11::mp_is_set<LN>::value, "indices must be unique");
const auto& old_axes = unsafe_access::axes(h);
auto axes = detail::static_if<detail::is_tuple<A>>(
[&](const auto& old_axes) {
return std::make_tuple(std::get<N>(old_axes), std::get<Ns::value>(old_axes)...);
},
[&](const auto& old_axes) {
return detail::remove_cvref_t<decltype(old_axes)>(
{old_axes[N], old_axes[Ns::value]...});
},
old_axes);
const auto& old_storage = unsafe_access::storage(h);
using A2 = decltype(axes);
auto result = histogram<A2, S>(std::move(axes), detail::make_default(old_storage));
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
for (auto x : indexed(h, coverage::all)) {
auto i = idx.begin();
mp11::mp_for_each<LN>([&i, &x](auto J) { *i++ = x.index(J); });
result.at(idx) += *x;
}
return result;
}
/**
Returns a lower-dimensional histogram, summing over removed axes.
This version accepts a source histogram and an iterable range containing the remaining
indices.
*/
template <class A, class S, class Iterable, class = detail::requires_iterable<Iterable>>
auto project(const histogram<A, S>& h, const Iterable& c) {
static_assert(detail::is_sequence_of_any_axis<A>::value,
"this version of project requires histogram with non-static axes");
const auto& old_axes = unsafe_access::axes(h);
auto axes = detail::make_default(old_axes);
axes.reserve(c.size());
auto seen = detail::make_stack_buffer<bool>(old_axes, false);
for (auto d : c) {
if (seen[d]) BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
seen[d] = true;
axes.emplace_back(old_axes[d]);
}
const auto& old_storage = unsafe_access::storage(h);
auto result = histogram<A, S>(std::move(axes), detail::make_default(old_storage));
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
for (auto x : indexed(h, coverage::all)) {
auto i = idx.begin();
for (auto d : c) *i++ = x.index(d);
result.at(idx) += *x;
}
return result;
}
} // namespace algorithm
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,222 @@
// 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_ALGORITHM_REDUCE_HPP
#define BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP
#include <boost/assert.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/indexed.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <type_traits>
namespace boost {
namespace histogram {
namespace algorithm {
/**
Option type returned by the helper functions shrink_and_rebin(), shrink(), rebin().
*/
struct reduce_option {
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
unsigned iaxis = 0;
double lower, upper;
unsigned merge = 0;
reduce_option() noexcept = default;
reduce_option(unsigned i, double l, double u, unsigned m)
: iaxis(i), lower(l), upper(u), merge(m) {
if (lower == upper)
BOOST_THROW_EXCEPTION(std::invalid_argument("lower != upper required"));
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
}
#endif
};
/**
Shrink and rebin option.
@param iaxis which axis to operate on.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
@param merge how many adjacent bins to merge into one.
*/
inline auto shrink_and_rebin(unsigned iaxis, double lower, double upper, unsigned merge) {
return reduce_option{iaxis, lower, upper, merge};
}
/**
Shrink option.
@param iaxis which axis to operate on.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
*/
inline auto shrink(unsigned iaxis, double lower, double upper) {
return reduce_option{iaxis, lower, upper, 1};
}
/**
Rebin option.
@param iaxis which axis to operate on.
@param merge how many adjacent bins to merge into one.
*/
inline auto rebin(unsigned iaxis, unsigned merge) {
return reduce_option{iaxis, std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), merge};
}
/**
Convenience overload for single axis.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
@param merge how many adjacent bins to merge into one.
*/
inline auto shrink_and_rebin(double lower, double upper, unsigned merge) {
return shrink_and_rebin(0, lower, upper, merge);
}
/**
Convenience overload for single axis.
@param lower lowest bound that should be kept.
@param upper highest bound that should be kept. If upper is inside bin interval, the
whole interval is removed.
*/
inline auto shrink(double lower, double upper) { return shrink(0, lower, upper); }
/**
Convenience overload for single axis.
@param merge how many adjacent bins to merge into one.
*/
inline auto rebin(unsigned merge) { return rebin(0, merge); }
/**
Shrink and/or rebin axes of a histogram.
Returns the reduced copy of the histogram.
@param hist original histogram.
@param options iterable sequence of reduce_options, generated by shrink_and_rebin(),
shrink(), and rebin().
*/
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
template <class Histogram, class Iterable, class = detail::requires_iterable<Iterable>>
#else
template <class Histogram, class Iterable>
#endif
decltype(auto) reduce(const Histogram& hist, const Iterable& options) {
const auto& old_axes = unsafe_access::axes(hist);
struct option_item : reduce_option {
int begin, end;
};
auto opts = detail::make_stack_buffer<option_item>(old_axes);
for (const auto& o : options) {
auto& oi = opts[o.iaxis];
if (oi.merge > 0) // did we already set the option for this axis?
BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
oi.lower = o.lower;
oi.upper = o.upper;
oi.merge = o.merge;
}
// make new axes container with default-constructed axis instances
auto axes = detail::make_default(old_axes);
detail::static_if<detail::is_tuple<decltype(axes)>>(
[](auto&, const auto&) {},
[](auto& axes, const auto& old_axes) {
axes.reserve(old_axes.size());
for (const auto& a : old_axes) axes.emplace_back(detail::make_default(a));
},
axes, old_axes);
// override default-constructed axis instances with modified instances
unsigned iaxis = 0;
hist.for_each_axis([&](const auto& a) {
using T = detail::remove_cvref_t<decltype(a)>;
auto& o = opts[iaxis];
o.begin = 0;
o.end = a.size();
if (o.merge > 0) { // option is set?
if (o.lower < o.upper) {
while (o.begin != o.end && a.value(o.begin) < o.lower) ++o.begin;
while (o.end != o.begin && a.value(o.end - 1) >= o.upper) --o.end;
} else if (o.lower > o.upper) {
// for inverted axis::regular
while (o.begin != o.end && a.value(o.begin) > o.lower) ++o.begin;
while (o.end != o.begin && a.value(o.end - 1) <= o.upper) --o.end;
}
o.end -= (o.end - o.begin) % o.merge;
auto a2 = T(a, o.begin, o.end, o.merge);
axis::get<T>(detail::axis_get(axes, iaxis)) = a2;
} else {
o.merge = 1;
axis::get<T>(detail::axis_get(axes, iaxis)) = a;
}
++iaxis;
});
auto storage = detail::make_default(unsafe_access::storage(hist));
auto result = Histogram(std::move(axes), std::move(storage));
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
for (auto x : indexed(hist, coverage::all)) {
auto i = idx.begin();
auto o = opts.begin();
for (auto j : x.indices()) {
*i = (j - o->begin);
if (*i <= -1)
*i = -1;
else {
*i /= o->merge;
const int end = (o->end - o->begin) / o->merge;
if (*i > end) *i = end;
}
++i;
++o;
}
result.at(idx) += *x;
}
return result;
}
/**
Shrink and/or rebin axes of a histogram.
Returns the modified copy.
@param hist original histogram.
@param opt reduce option generated by shrink_and_rebin(), shrink(), and rebin().
@param opts more reduce_options.
*/
template <class Histogram, class... Ts>
decltype(auto) reduce(const Histogram& hist, const reduce_option& opt, Ts&&... opts) {
// this must be in one line, because any of the ts could be a temporary
return reduce(hist, std::initializer_list<reduce_option>{opt, opts...});
}
} // namespace algorithm
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,41 @@
// 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_ALGORITHM_SUM_HPP
#define BOOST_HISTOGRAM_ALGORITHM_SUM_HPP
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/utility.hpp>
#include <numeric>
#include <type_traits>
namespace boost {
namespace histogram {
namespace algorithm {
/** Compute the sum over all histogram cells, including underflow/overflow bins.
If the value type of the histogram is an integral or floating point type,
boost::accumulators::sum<double> is used to compute the sum, else the original value
type is used. Compilation fails, if the value type does not support operator+=.
Return type is double if the value type of the histogram is integral or floating point,
and the original value type otherwise.
*/
template <class A, class S>
auto sum(const histogram<A, S>& h) {
using T = typename histogram<A, S>::value_type;
using Sum = mp11::mp_if<std::is_arithmetic<T>, accumulators::sum<double>, T>;
Sum sum;
for (auto x : h) sum += x;
using R = mp11::mp_if<std::is_arithmetic<T>, double, T>;
return static_cast<R>(sum);
}
} // namespace algorithm
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,16 @@
// 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_AXIS_HPP
#define BOOST_HISTOGRAM_AXIS_HPP
#include <boost/histogram/axis/category.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/axis/variable.hpp>
#include <boost/histogram/axis/variant.hpp>
#endif

View File

@@ -0,0 +1,184 @@
// 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_AXIS_CATEGORY_HPP
#define BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
#include <algorithm>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace boost {
namespace histogram {
namespace axis {
/**
Maps at a set of unique values to bin indices.
The axis maps a set of values to bins, following the order of arguments in the
constructor. The optional overflow bin for this axis counts input values that
are not part of the set. Binning has O(N) complexity, but with a very small
factor. For small N (the typical use case) it beats other kinds of lookup.
@tparam Value input value type, must be equal-comparable.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management.
The options `underflow` and `circular` are not allowed. The options `growth`
and `overflow` are mutually exclusive.
*/
template <class Value, class MetaData, class Options, class Allocator>
class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>> {
using value_type = Value;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type = detail::replace_default<Options, option::overflow_t>;
static_assert(!options_type::test(option::underflow),
"category axis cannot have underflow");
static_assert(!options_type::test(option::circular),
"category axis cannot be circular");
static_assert(!options_type::test(option::growth) ||
!options_type::test(option::overflow),
"growing category axis cannot have overflow");
using allocator_type = Allocator;
using vector_type = std::vector<value_type, allocator_type>;
public:
explicit category(allocator_type alloc = {}) : vec_meta_(vector_type(alloc)) {}
/** Construct from iterator range of unique values.
*
* \param begin begin of category range of unique values.
* \param end end of category range of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
: vec_meta_(vector_type(begin, end, alloc), std::move(meta)) {
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
}
/** Construct axis from iterable sequence of unique values.
*
* \param iterable sequence of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class C, class = detail::requires_iterable<C>>
category(const C& iterable, metadata_type meta = {}, allocator_type alloc = {})
: category(std::begin(iterable), std::end(iterable), std::move(meta),
std::move(alloc)) {}
/** Construct axis from an initializer list of unique values.
*
* \param list `std::initializer_list` of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class U>
category(std::initializer_list<U> list, metadata_type meta = {},
allocator_type alloc = {})
: category(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
/// Constructor used by algorithm::reduce to shrink and rebin.
category(const category& src, index_type begin, index_type end, unsigned merge)
: category(src.vec_meta_.first().begin() + begin,
src.vec_meta_.first().begin() + end, src.metadata()) {
if (merge > 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for category axis"));
}
/// Return index for value argument.
index_type index(const value_type& x) const noexcept {
const auto beg = vec_meta_.first().begin();
const auto end = vec_meta_.first().end();
return static_cast<index_type>(std::distance(beg, std::find(beg, end, x)));
}
/// Returns index and shift (if axis has grown) for the passed argument.
auto update(const value_type& x) {
const auto i = index(x);
if (i < size()) return std::make_pair(i, 0);
vec_meta_.first().emplace_back(x);
return std::make_pair(i, -1);
}
/// Return value for index argument.
/// Throws `std::out_of_range` if the index is out of bounds.
decltype(auto) value(index_type idx) const {
if (idx < 0 || idx >= size())
BOOST_THROW_EXCEPTION(std::out_of_range("category index out of range"));
return vec_meta_.first()[idx];
}
/// Return value for index argument.
decltype(auto) bin(index_type idx) const noexcept { return value(idx); }
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept {
return static_cast<index_type>(vec_meta_.first().size());
}
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return vec_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return vec_meta_.second(); }
template <class V, class M, class O, class A>
bool operator==(const category<V, M, O, A>& o) const noexcept {
const auto& a = vec_meta_.first();
const auto& b = o.vec_meta_.first();
return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
detail::relaxed_equal(metadata(), o.metadata());
}
template <class V, class M, class O, class A>
bool operator!=(const category<V, M, O, A>& o) const noexcept {
return !operator==(o);
}
auto get_allocator() const { return vec_meta_.first().get_allocator(); }
template <class Archive>
void serialize(Archive&, unsigned);
private:
detail::compressed_pair<vector_type, metadata_type> vec_meta_;
template <class V, class M, class O, class A>
friend class category;
};
#if __cpp_deduction_guides >= 201606
template <class T>
category(std::initializer_list<T>)->category<T>;
category(std::initializer_list<const char*>)->category<std::string>;
template <class T>
category(std::initializer_list<T>, const char*)->category<T>;
template <class T, class M>
category(std::initializer_list<T>, const M&)->category<T, M>;
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,194 @@
// 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_AXIS_INTEGER_HPP
#define BOOST_HISTOGRAM_AXIS_INTEGER_HPP
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
/**
Axis for an interval of integer values with unit steps.
Binning is a O(1) operation. This axis bins faster than a regular axis.
@tparam Value input value type. Must be integer or floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option (all values allowed).
*/
template <class Value, class MetaData, class Options>
class integer : public iterator_mixin<integer<Value, MetaData, Options>> {
static_assert(std::is_integral<Value>::value || std::is_floating_point<Value>::value,
"integer axis requires type floating point or integral type");
using value_type = Value;
using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
index_type, real_index_type>;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type =
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
static_assert(!options_type::test(option::circular) ||
std::is_floating_point<value_type>::value ||
!options_type::test(option::overflow),
"integer axis with integral type cannot have overflow");
public:
constexpr integer() = default;
/** Construct over semi-open integer interval [start, stop).
*
* \param start first integer of covered range.
* \param stop one past last integer of covered range.
* \param meta description of the axis.
*/
integer(value_type start, value_type stop, metadata_type meta = {})
: size_meta_(static_cast<index_type>(stop - start), std::move(meta)), min_(start) {
if (stop <= start) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
}
/// Constructor used by algorithm::reduce to shrink and rebin.
integer(const integer& src, index_type begin, index_type end, unsigned merge)
: integer(src.value(begin), src.value(end), src.metadata()) {
if (merge > 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for integer axis"));
if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
}
/// Return index for value argument.
index_type index(value_type x) const noexcept {
return index_impl(std::is_floating_point<value_type>(), x);
}
/// Returns index and shift (if axis has grown) for the passed argument.
auto update(value_type x) noexcept {
auto impl = [this](long x) {
const auto i = x - min_;
if (i >= 0) {
const auto k = static_cast<axis::index_type>(i);
if (k < size()) return std::make_pair(k, 0);
const auto n = k - size() + 1;
size_meta_.first() += n;
return std::make_pair(k, -n);
}
const auto k = static_cast<axis::index_type>(
detail::static_if<std::is_floating_point<value_type>>(
[](auto x) { return std::floor(x); }, [](auto x) { return x; }, i));
min_ += k;
size_meta_.first() -= k;
return std::make_pair(0, -k);
};
return detail::static_if<std::is_floating_point<value_type>>(
[this, impl](auto x) {
if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x)));
// this->size() is workaround for gcc-5 bug
return std::make_pair(x < 0 ? -1 : this->size(), 0);
},
impl, x);
}
/// Return value for index argument.
value_type value(local_index_type i) const noexcept {
if (!options_type::test(option::circular)) {
if (i < 0) return detail::lowest<value_type>();
if (i > size()) { return detail::highest<value_type>(); }
}
return min_ + i;
}
/// Return bin for index argument.
decltype(auto) bin(index_type idx) const noexcept {
return detail::static_if<std::is_floating_point<local_index_type>>(
[this](auto idx) { return interval_view<integer>(*this, idx); },
[this](auto idx) { return this->value(idx); }, idx);
}
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept { return size_meta_.first(); }
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return size_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
template <class V, class M, class O>
bool operator==(const integer<V, M, O>& o) const noexcept {
return size() == o.size() && detail::relaxed_equal(metadata(), o.metadata()) &&
min_ == o.min_;
}
template <class V, class M, class O>
bool operator!=(const integer<V, M, O>& o) const noexcept {
return !operator==(o);
}
template <class Archive>
void serialize(Archive&, unsigned);
private:
index_type index_impl(std::false_type, int x) const noexcept {
const auto z = x - min_;
if (options_type::test(option::circular))
return static_cast<index_type>(z - std::floor(float(z) / size()) * size());
if (z < size()) return z >= 0 ? z : -1;
return size();
}
template <typename T>
index_type index_impl(std::true_type, T x) const noexcept {
// need to handle NaN, cannot simply cast to int and call int-implementation
const auto z = x - min_;
if (options_type::test(option::circular)) {
if (std::isfinite(z))
return static_cast<index_type>(std::floor(z) - std::floor(z / size()) * size());
} else if (z < size()) {
return z >= 0 ? static_cast<index_type>(z) : -1;
}
return size();
}
detail::compressed_pair<index_type, metadata_type> size_meta_{0};
value_type min_{0};
template <class V, class M, class O>
friend class integer;
};
#if __cpp_deduction_guides >= 201606
template <class T>
integer(T, T)->integer<detail::convert_integer<T, index_type>>;
template <class T>
integer(T, T, const char*)->integer<detail::convert_integer<T, index_type>>;
template <class T, class M>
integer(T, T, M)->integer<detail::convert_integer<T, index_type>, M>;
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,54 @@
// Copyright 2015-2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
#define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
namespace boost {
namespace histogram {
namespace axis {
/**
Lightweight bin view.
Represents the current bin interval.
*/
template <typename Axis>
class interval_view {
public:
interval_view(const Axis& axis, int idx) : axis_(axis), idx_(idx) {}
// avoid viewing a temporary that goes out of scope
interval_view(Axis&& axis, int idx) = delete;
/// Return lower edge of bin.
decltype(auto) lower() const noexcept { return axis_.value(idx_); }
/// Return upper edge of bin.
decltype(auto) upper() const noexcept { return axis_.value(idx_ + 1); }
/// Return center of bin.
decltype(auto) center() const noexcept { return axis_.value(idx_ + 0.5); }
/// Return width of bin.
decltype(auto) width() const noexcept { return upper() - lower(); }
template <typename BinType>
bool operator==(const BinType& rhs) const noexcept {
return lower() == rhs.lower() && upper() == rhs.upper();
}
template <typename BinType>
bool operator!=(const BinType& rhs) const noexcept {
return !operator==(rhs);
}
private:
const Axis& axis_;
const int idx_;
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,74 @@
// Copyright 2015-2017 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_ITERATOR_HPP
#define BOOST_HISTOGRAM_AXIS_ITERATOR_HPP
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/reverse_iterator.hpp>
namespace boost {
namespace histogram {
namespace axis {
template <typename Axis>
class iterator
: public boost::iterator_adaptor<iterator<Axis>, int,
decltype(std::declval<const Axis&>().bin(0)),
std::random_access_iterator_tag,
decltype(std::declval<const Axis&>().bin(0)), int> {
public:
explicit iterator(const Axis& axis, int idx)
: iterator::iterator_adaptor_(idx), axis_(axis) {}
protected:
bool equal(const iterator& other) const noexcept {
return &axis_ == &other.axis_ && this->base() == other.base();
}
decltype(auto) dereference() const { return axis_.bin(this->base()); }
private:
const Axis& axis_;
friend class boost::iterator_core_access;
};
/// Uses CRTP to inject iterator logic into Derived.
template <typename Derived>
class iterator_mixin {
public:
using const_iterator = iterator<Derived>;
using const_reverse_iterator = boost::reverse_iterator<const_iterator>;
/// Bin iterator to beginning of the axis (read-only).
const_iterator begin() const noexcept {
return const_iterator(*static_cast<const Derived*>(this), 0);
}
/// Bin iterator to the end of the axis (read-only).
const_iterator end() const noexcept {
return const_iterator(*static_cast<const Derived*>(this),
static_cast<const Derived*>(this)->size());
}
/// Reverse bin iterator to the last entry of the axis (read-only).
const_reverse_iterator rbegin() const noexcept {
return boost::make_reverse_iterator(end());
}
/// Reverse bin iterator to the end (read-only).
const_reverse_iterator rend() const noexcept {
return boost::make_reverse_iterator(begin());
}
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,82 @@
// Copyright 2015-2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_OPTION_HPP
#define BOOST_HISTOGRAM_AXIS_OPTION_HPP
#include <boost/mp11.hpp>
#include <type_traits>
/**
\file option.hpp Options for builtin axis types.
Options `circular` and `growth` are mutually exclusive.
Options `circular` and `underflow` are mutually exclusive.
*/
namespace boost {
namespace histogram {
namespace axis {
namespace option {
/// Holder of axis options.
template <unsigned Bits>
struct bitset : std::integral_constant<unsigned, Bits> {
/// Returns true if all option flags in the argument are set and false otherwise.
template <unsigned B>
static constexpr auto test(bitset<B>) {
return std::integral_constant<bool, static_cast<bool>(Bits & B)>{};
}
};
/// Set union of the axis option arguments.
template <unsigned B1, unsigned B2>
constexpr auto operator|(bitset<B1>, bitset<B2>) {
return bitset<(B1 | B2)>{};
}
/// Set intersection of the option arguments.
template <unsigned B1, unsigned B2>
constexpr auto operator&(bitset<B1>, bitset<B2>) {
return bitset<(B1 & B2)>{};
}
/// Set difference of the option arguments.
template <unsigned B1, unsigned B2>
constexpr auto operator-(bitset<B1>, bitset<B2>) {
return bitset<(B1 & ~B2)>{};
}
/**
Single option flag.
@tparam Pos position of the bit in the set.
*/
template <unsigned Pos>
struct bit : bitset<(1 << Pos)> {};
/// All options off.
using none_t = bitset<0>;
constexpr none_t none{}; ///< Instance of `none_t`.
/// Axis has an underflow bin. Mutually exclusive with `circular`.
using underflow_t = bit<0>;
constexpr underflow_t underflow{}; ///< Instance of `underflow_t`.
/// Axis has overflow bin.
using overflow_t = bit<1>;
constexpr overflow_t overflow{}; ///< Instance of `overflow_t`.
/// Axis is circular. Mutually exclusive with `growth` and `underflow`.
using circular_t = bit<2>;
constexpr circular_t circular{}; ///< Instance of `circular_t`.
/// Axis can grow. Mutually exclusive with `circular`.
using growth_t = bit<3>;
constexpr growth_t growth{}; ///< Instance of `growth_t`.
} // namespace option
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,193 @@
// 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)
//
// String representations here evaluate correctly in Python.
#ifndef BOOST_HISTOGRAM_AXIS_OSTREAM_HPP
#define BOOST_HISTOGRAM_AXIS_OSTREAM_HPP
#include <boost/assert.hpp>
#include <boost/core/typeinfo.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <iomanip>
#include <iosfwd>
#include <stdexcept>
#include <type_traits>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace boost {
namespace histogram {
namespace detail {
inline const char* axis_suffix(const axis::transform::id&) { return ""; }
inline const char* axis_suffix(const axis::transform::log&) { return "_log"; }
inline const char* axis_suffix(const axis::transform::sqrt&) { return "_sqrt"; }
inline const char* axis_suffix(const axis::transform::pow&) { return "_pow"; }
template <class OStream, class T>
void stream_metadata(OStream& os, const T& t) {
detail::static_if<detail::is_streamable<T>>(
[&os](const auto& t) {
std::ostringstream oss;
oss << t;
if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); }
},
[&os](const auto&) {
os << ", metadata=" << boost::core::demangled_name(BOOST_CORE_TYPEID(T));
},
t);
}
template <class OStream>
void stream_options(OStream& os, const unsigned bits) {
os << ", options=";
bool first = true;
#define BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(x) \
if (bits & axis::option::x) { \
if (first) \
first = false; \
else { \
os << " | "; \
} \
os << #x; \
}
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(underflow);
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(overflow);
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(circular);
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(growth);
#undef BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM
if (first) os << "none";
}
template <class OStream, class T>
void stream_transform(OStream&, const T&) {}
template <class OStream>
void stream_transform(OStream& os, const axis::transform::pow& t) {
os << ", power=" << t.power;
}
template <class OStream, class T>
void stream_value(OStream& os, const T& t) {
os << t;
}
template <class OStream, class... Ts>
void stream_value(OStream& os, const std::basic_string<Ts...>& t) {
os << std::quoted(t);
}
} // namespace detail
namespace axis {
template <class T>
class polymorphic_bin;
template <class... Ts>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const null_type&) {
return os; // do nothing
}
template <class... Ts, class U>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const interval_view<U>& i) {
os << "[" << i.lower() << ", " << i.upper() << ")";
return os;
}
template <class... Ts, class U>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const polymorphic_bin<U>& i) {
if (i.is_discrete())
os << static_cast<double>(i);
else
os << "[" << i.lower() << ", " << i.upper() << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const regular<Us...>& a) {
os << "regular" << detail::axis_suffix(a.transform()) << "(" << a.size() << ", "
<< a.value(0) << ", " << a.value(a.size());
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
detail::stream_transform(os, a.transform());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const integer<Us...>& a) {
os << "integer(" << a.value(0) << ", " << a.value(a.size());
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const variable<Us...>& a) {
os << "variable(" << a.value(0);
for (index_type i = 1, n = a.size(); i <= n; ++i) { os << ", " << a.value(i); }
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const category<Us...>& a) {
os << "category(";
for (index_type i = 0, n = a.size(); i < n; ++i) {
detail::stream_value(os, a.value(i));
os << (i == (a.size() - 1) ? "" : ", ");
}
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const variant<Us...>& v) {
visit(
[&os](const auto& x) {
using A = detail::remove_cvref_t<decltype(x)>;
detail::static_if<detail::is_streamable<A>>(
[&os](const auto& x) { os << x; },
[](const auto&) {
BOOST_THROW_EXCEPTION(std::runtime_error(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(A)),
" is not streamable")));
},
x);
},
v);
return os;
}
} // namespace axis
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
#endif

View File

@@ -0,0 +1,88 @@
// 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_AXIS_POLYMORPHIC_BIN_HPP
#define BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_HPP
#include <boost/histogram/detail/meta.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace axis {
/**
Holds the bin data of a axis::variant.
The interface is a superset of the `value_bin_view` and `interval_bin_view`
classes. The methods value() and lower() return the same number. For a value bin,
upper() and lower() and center() return the same number, width() returns zero.
This is not a view like interval_bin_view or value_bin_view for two reasons.
- Sequential calls to lower() and upper() would have to each loop through
the variant types. This is likely to be slower than filling all the data in
one loop.
- polymorphic_bin may be created from a temporary instance of axis::variant,
like in the call histogram::axis(0). Storing a reference to the axis would
result in a dangling reference. Rather than specialing the code to handle
this, it seems easier to just use a value instead of a view here.
*/
template <typename RealType>
class polymorphic_bin {
using value_type = RealType;
public:
polymorphic_bin(value_type lower, value_type upper)
: lower_or_value_(lower), upper_(upper) {}
/// Implicitly convert to bin value (for axis with discrete values).
operator const value_type&() const noexcept { return lower_or_value_; }
/// Return lower edge of bin.
value_type lower() const noexcept { return lower_or_value_; }
/// Return upper edge of bin.
value_type upper() const noexcept { return upper_; }
/// Return center of bin.
value_type center() const noexcept { return 0.5 * (lower() + upper()); }
/// Return width of bin.
value_type width() const noexcept { return upper() - lower(); }
template <typename BinType>
bool operator==(const BinType& rhs) const noexcept {
return equal_impl(detail::has_method_lower<BinType>(), rhs);
}
template <typename BinType>
bool operator!=(const BinType& rhs) const noexcept {
return !operator==(rhs);
}
/// Return true if bin is discrete.
bool is_discrete() const noexcept { return lower_or_value_ == upper_; }
private:
bool equal_impl(std::true_type, const polymorphic_bin& rhs) const noexcept {
return lower_or_value_ == rhs.lower_or_value_ && upper_ == rhs.upper_;
}
template <typename BinType>
bool equal_impl(std::true_type, const BinType& rhs) const noexcept {
return lower() == rhs.lower() && upper() == rhs.upper();
}
template <typename BinType>
bool equal_impl(std::false_type, const BinType& rhs) const noexcept {
return is_discrete() && static_cast<value_type>(*this) == rhs;
}
const value_type lower_or_value_, upper_;
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,359 @@
// 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_AXIS_REGULAR_HPP
#define BOOST_HISTOGRAM_AXIS_REGULAR_HPP
#include <boost/assert.hpp>
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
namespace transform {
/// Identity transform for equidistant bins.
struct id {
/// Pass-through.
template <typename T>
static T forward(T&& x) noexcept {
return std::forward<T>(x);
}
/// Pass-through.
template <typename T>
static T inverse(T&& x) noexcept {
return std::forward<T>(x);
}
};
/// Log transform for equidistant bins in log-space.
struct log {
/// Returns log(x) of external value x.
template <typename T>
static T forward(T x) {
return std::log(x);
}
/// Returns exp(x) for internal value x.
template <typename T>
static T inverse(T x) {
return std::exp(x);
}
};
/// Sqrt transform for equidistant bins in sqrt-space.
struct sqrt {
/// Returns sqrt(x) of external value x.
template <typename T>
static T forward(T x) {
return std::sqrt(x);
}
/// Returns x^2 of internal value x.
template <typename T>
static T inverse(T x) {
return x * x;
}
};
/// Pow transform for equidistant bins in pow-space.
struct pow {
double power = 1; /**< power index */
/// Make transform with index p.
explicit pow(double p) : power(p) {}
pow() = default;
/// Returns pow(x, power) of external value x.
template <typename T>
auto forward(T x) const {
return std::pow(x, power);
}
/// Returns pow(x, 1/power) of external value x.
template <typename T>
auto inverse(T x) const {
return std::pow(x, 1.0 / power);
}
bool operator==(const pow& o) const noexcept { return power == o.power; }
};
} // namespace transform
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
// Type envelope to mark value as step size
template <typename T>
struct step_type {
T value;
};
#endif
/**
Helper function to mark argument as step size.
*/
template <typename T>
auto step(T&& t) {
return step_type<T&&>{std::forward<T>(t)};
}
/**
Axis for equidistant intervals on the real line.
The most common binning strategy. Very fast. Binning is a O(1) operation.
@tparam Value input value type, must be floating point.
@tparam Transform builtin or user-defined transform type.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option (all values allowed).
*/
template <class Value, class Transform, class MetaData, class Options>
class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
protected detail::replace_default<Transform, transform::id> {
using value_type = Value;
using transform_type = detail::replace_default<Transform, transform::id>;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type =
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
using unit_type = detail::get_unit_type<value_type>;
using internal_value_type = detail::get_scale_type<value_type>;
static_assert(std::is_floating_point<internal_value_type>::value,
"variable axis requires floating point type");
public:
constexpr regular() = default;
/** Construct n bins over real transformed range [start, stop).
*
* @param trans transform instance to use.
* @param n number of bins.
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
*/
regular(transform_type trans, unsigned n, value_type start, value_type stop,
metadata_type meta = {})
: transform_type(std::move(trans))
, size_meta_(static_cast<index_type>(n), std::move(meta))
, min_(this->forward(detail::get_scale(start)))
, delta_(this->forward(detail::get_scale(stop)) - min_) {
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
if (!std::isfinite(min_) || !std::isfinite(delta_))
BOOST_THROW_EXCEPTION(
std::invalid_argument("forward transform of start or stop invalid"));
if (delta_ == 0)
BOOST_THROW_EXCEPTION(std::invalid_argument("range of axis is zero"));
}
/** Construct n bins over real range [start, stop).
*
* @param n number of bins.
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
*/
regular(unsigned n, value_type start, value_type stop, metadata_type meta = {})
: regular({}, n, start, stop, std::move(meta)) {}
/** Construct bins with the given step size over real transformed range
* [start, stop).
*
* @param trans transform instance to use.
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
* (start + n * step).
*/
template <class T>
regular(transform_type trans, const step_type<T>& step, value_type start,
value_type stop, metadata_type meta = {})
: regular(trans, static_cast<index_type>(std::abs(stop - start) / step.value),
start,
start + static_cast<index_type>(std::abs(stop - start) / step.value) *
step.value,
std::move(meta)) {}
/** Construct bins with the given step size over real range [start, stop).
*
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
* (start + n * step).
*/
template <class T>
regular(const step_type<T>& step, value_type start, value_type stop,
metadata_type meta = {})
: regular({}, step, start, stop, std::move(meta)) {}
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
regular(const regular& src, index_type begin, index_type end, unsigned merge)
: regular(src.transform(), (end - begin) / merge, src.value(begin), src.value(end),
src.metadata()) {
BOOST_ASSERT((end - begin) % merge == 0);
if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
}
/// Return instance of the transform type.
const transform_type& transform() const noexcept { return *this; }
/// Return index for value argument.
index_type index(value_type x) const noexcept {
// Runs in hot loop, please measure impact of changes
auto z = (this->forward(x / unit_type{}) - min_) / delta_;
if (options_type::test(option::circular)) {
if (std::isfinite(z)) {
z -= std::floor(z);
return static_cast<index_type>(z * size());
}
} else {
if (z < 1) {
if (z >= 0)
return static_cast<index_type>(z * size());
else
return -1;
}
}
return size(); // also returned if x is NaN
}
/// Returns index and shift (if axis has grown) for the passed argument.
auto update(value_type x) noexcept {
BOOST_ASSERT(options_type::test(option::growth));
const auto z = (this->forward(x / unit_type{}) - min_) / delta_;
if (z < 1) { // don't use i here!
if (z >= 0) {
const auto i = static_cast<axis::index_type>(z * size());
return std::make_pair(i, 0);
}
if (z != -std::numeric_limits<internal_value_type>::infinity()) {
const auto stop = min_ + delta_;
const auto i = static_cast<axis::index_type>(std::floor(z * size()));
min_ += i * (delta_ / size());
delta_ = stop - min_;
size_meta_.first() -= i;
return std::make_pair(0, -i);
}
// z is -infinity
return std::make_pair(-1, 0);
}
// z either beyond range, infinite, or NaN
if (z < std::numeric_limits<internal_value_type>::infinity()) {
const auto i = static_cast<axis::index_type>(z * size());
const auto n = i - size() + 1;
delta_ /= size();
delta_ *= size() + n;
size_meta_.first() += n;
return std::make_pair(i, -n);
}
// z either infinite or NaN
return std::make_pair(size(), 0);
}
/// Return value for fractional index argument.
value_type value(real_index_type i) const noexcept {
auto z = i / size();
if (!options_type::test(option::circular) && z < 0.0)
z = -std::numeric_limits<internal_value_type>::infinity() * delta_;
else if (options_type::test(option::circular) || z <= 1.0)
z = (1.0 - z) * min_ + z * (min_ + delta_);
else {
z = std::numeric_limits<internal_value_type>::infinity() * delta_;
}
return static_cast<value_type>(this->inverse(z) * unit_type());
}
/// Return bin for index argument.
decltype(auto) bin(index_type idx) const noexcept {
return interval_view<regular>(*this, idx);
}
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept { return size_meta_.first(); }
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return size_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
template <class V, class T, class M, class O>
bool operator==(const regular<V, T, M, O>& o) const noexcept {
return detail::relaxed_equal(transform(), o.transform()) && size() == o.size() &&
detail::relaxed_equal(metadata(), o.metadata()) && min_ == o.min_ &&
delta_ == o.delta_;
}
template <class V, class T, class M, class O>
bool operator!=(const regular<V, T, M, O>& o) const noexcept {
return !operator==(o);
}
template <class Archive>
void serialize(Archive&, unsigned);
private:
detail::compressed_pair<index_type, metadata_type> size_meta_{0};
internal_value_type min_{0}, delta_{1};
template <class V, class T, class M, class O>
friend class regular;
};
#if __cpp_deduction_guides >= 201606
template <class T>
regular(unsigned, T, T)->regular<detail::convert_integer<T, double>>;
template <class T>
regular(unsigned, T, T, const char*)->regular<detail::convert_integer<T, double>>;
template <class T, class M>
regular(unsigned, T, T, M)->regular<detail::convert_integer<T, double>, transform::id, M>;
template <class Tr, class T>
regular(Tr, unsigned, T, T)->regular<detail::convert_integer<T, double>, Tr>;
template <class Tr, class T>
regular(Tr, unsigned, T, T, const char*)->regular<detail::convert_integer<T, double>, Tr>;
template <class Tr, class T, class M>
regular(Tr, unsigned, T, T, M)->regular<detail::convert_integer<T, double>, Tr, M>;
#endif
template <class Value = double, class MetaData = use_default, class Options = use_default>
using circular = regular<Value, transform::id, MetaData,
decltype(detail::replace_default<Options, option::overflow_t>{} |
option::circular)>;
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,283 @@
// 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_AXIS_TRAITS_HPP
#define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
#include <boost/core/typeinfo.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
using static_options_impl = axis::option::bitset<T::options()>;
template <class FIntArg, class FDoubleArg, class T>
decltype(auto) value_method_switch(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
return static_if<has_method_value<T>>(
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) {
using A = remove_cvref_t<decltype(t)>;
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
},
[](FIntArg&&, FDoubleArg&&, const auto& t) -> double {
using A = remove_cvref_t<decltype(t)>;
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
boost::core::demangled_name(BOOST_CORE_TYPEID(A)), " has no value method")));
#ifndef _MSC_VER // msvc warns about unreachable return
return double{};
#endif
},
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
}
template <class R1, class R2, class FIntArg, class FDoubleArg, class T>
R2 value_method_switch_with_return_type(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
return static_if<has_method_value_with_convertible_return_type<T, R1>>(
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) -> R2 {
using A = remove_cvref_t<decltype(t)>;
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
},
[](FIntArg&&, FDoubleArg&&, const auto&) -> R2 {
BOOST_THROW_EXCEPTION(std::runtime_error(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
" has no value method or return type is not convertible to ",
boost::core::demangled_name(BOOST_CORE_TYPEID(R1)))));
#ifndef _MSC_VER // msvc warns about unreachable return
// conjure a value out of thin air to satisfy syntactic requirement
return *reinterpret_cast<R2*>(0);
#endif
},
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
}
} // namespace detail
namespace axis {
namespace traits {
/** Returns reference to metadata of an axis.
If the expression x.metadata() for an axis instance `x` (maybe const) is valid, return
the result. Otherwise, return a reference to a static instance of
boost::histogram::axis::null_type.
@param axis any axis instance
*/
template <class Axis>
decltype(auto) metadata(Axis&& axis) noexcept {
return detail::static_if<
detail::has_method_metadata<const detail::remove_cvref_t<Axis>>>(
[](auto&& a) -> decltype(auto) { return a.metadata(); },
[](auto &&) -> detail::copy_qualifiers<Axis, null_type> {
static null_type m;
return m;
},
std::forward<Axis>(axis));
}
/** Generates static axis option type for axis type.
WARNING: Doxygen does not render the synopsis correctly. This is a templated using
directive, which accepts axis type and returns boost::histogram::axis::option::bitset.
If Axis::options() is valid and constexpr, return the corresponding option type.
Otherwise, return boost::histogram::axis::option::growth_t, if the axis has a method
`update`, else return boost::histogram::axis::option::none_t.
@tparam Axis axis type
*/
template <class Axis>
using static_options = detail::mp_eval_or<
mp11::mp_if<detail::has_method_update<detail::remove_cvref_t<Axis>>, option::growth_t,
option::none_t>,
detail::static_options_impl, detail::remove_cvref_t<Axis>>;
/** Returns axis options as unsigned integer.
If axis.options() is valid, return the result. Otherwise, return
boost::histogram::axis::traits::static_options<decltype(axis)>::value.
@param axis any axis instance
*/
template <class Axis>
constexpr unsigned options(const Axis& axis) noexcept {
// cannot reuse static_options here, because this should also work for axis::variant
return detail::static_if<detail::has_method_options<Axis>>(
[](const auto& a) { return a.options(); },
[](const auto&) { return static_options<Axis>::value; }, axis);
}
/** Returns axis size plus any extra bins for under- and overflow.
@param axis any axis instance
*/
template <class Axis>
constexpr index_type extent(const Axis& axis) noexcept {
const auto opt = options(axis);
return axis.size() + (opt & option::underflow ? 1 : 0) +
(opt & option::overflow ? 1 : 0);
}
/** Returns axis value for index.
If the axis has no `value` method, throw std::runtime_error. If the method exists and
accepts a floating point index, pass the index and return the result. If the method
exists but accepts only integer indices, cast the floating point index to int, pass this
index and return the result.
@param axis any axis instance
@param index floating point axis index
*/
template <class Axis>
decltype(auto) value(const Axis& axis, real_index_type index) {
return detail::value_method_switch(
[index](const auto& a) { return a.value(static_cast<int>(index)); },
[index](const auto& a) { return a.value(index); }, axis);
}
/** Returns axis value for index if it is convertible to target type or throws.
Like boost::histogram::axis::traits::value, but converts the result into the requested
return type. If the conversion is not possible, throws std::runtime_error.
@tparam Result requested return type
@tparam Axis axis type
@param axis any axis instance
@param index floating point axis index
*/
template <class Result, class Axis>
Result value_as(const Axis& axis, real_index_type index) {
return detail::value_method_switch_with_return_type<Result, Result>(
[index](const auto& a) {
return static_cast<Result>(a.value(static_cast<int>(index)));
},
[index](const auto& a) { return static_cast<Result>(a.value(index)); }, axis);
}
/** Returns axis index for value.
Throws std::invalid_argument if the value argument is not implicitly convertible.
@param axis any axis instance
@param value argument to be passed to `index` method
*/
template <class Axis, class U>
auto index(const Axis& axis, const U& value) {
using V = detail::arg_type<decltype(&Axis::index)>;
return detail::static_if<std::is_convertible<U, V>>(
[&value](const auto& axis) {
using A = detail::remove_cvref_t<decltype(axis)>;
using V2 = detail::arg_type<decltype(&A::index)>;
return axis.index(static_cast<V2>(value));
},
[](const Axis&) -> index_type {
BOOST_THROW_EXCEPTION(std::invalid_argument(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(Axis)),
": cannot convert argument of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " to ",
boost::core::demangled_name(BOOST_CORE_TYPEID(V)))));
#ifndef _MSC_VER // msvc warns about unreachable return
return index_type{};
#endif
},
axis);
}
/// @copydoc index(const Axis&, const U& value)
template <class... Ts, class U>
auto index(const variant<Ts...>& axis, const U& value) {
return axis.index(value);
}
/** Returns pair of axis index and shift for the value argument.
Throws `std::invalid_argument` if the value argument is not implicitly convertible to
the argument expected by the `index` method. If the result of
boost::histogram::axis::traits::static_options<decltype(axis)> has the growth flag set,
call `update` method with the argument and return the result. Otherwise, call `index`
and return the pair of the result and a zero shift.
@param axis any axis instance
@param value argument to be passed to `update` or `index` method
*/
template <class Axis, class U>
std::pair<int, int> update(Axis& axis, const U& value) {
using V = detail::arg_type<decltype(&Axis::index)>;
return detail::static_if<std::is_convertible<U, V>>(
[&value](auto& a) {
using A = detail::remove_cvref_t<decltype(a)>;
return detail::static_if_c<static_options<A>::test(option::growth)>(
[&value](auto& a) { return a.update(value); },
[&value](auto& a) { return std::make_pair(a.index(value), 0); }, a);
},
[](Axis&) -> std::pair<index_type, index_type> {
BOOST_THROW_EXCEPTION(std::invalid_argument(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(Axis)),
": cannot convert argument of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " to ",
boost::core::demangled_name(BOOST_CORE_TYPEID(V)))));
#ifndef _MSC_VER // msvc warns about unreachable return
return std::make_pair(index_type{}, index_type{});
#endif
},
axis);
}
/// @copydoc update(Axis& axis, const U& value)
template <class... Ts, class U>
auto update(variant<Ts...>& axis, const U& value) {
return axis.update(value);
}
/** Returns bin width at axis index.
If the axis has no `value` method, throw std::runtime_error. If the method exists and
accepts a floating point index, return the result of `axis.value(index + 1) -
axis.value(index)`. If the method exists but accepts only integer indices, return 0.
@param axis any axis instance
@param index bin index
*/
template <class Axis>
decltype(auto) width(const Axis& axis, index_type index) {
return detail::value_method_switch(
[](const auto&) { return 0; },
[index](const auto& a) { return a.value(index + 1) - a.value(index); }, axis);
}
/** Returns bin width at axis index.
Like boost::histogram::axis::traits::width, but converts the result into the requested
return type. If the conversion is not possible, throw std::runtime_error.
@param axis any axis instance
@param index bin index
*/
template <class Result, class Axis>
Result width_as(const Axis& axis, index_type index) {
return detail::value_method_switch_with_return_type<Result, Result>(
[](const auto&) { return Result{}; },
[index](const auto& a) {
return static_cast<Result>(a.value(index + 1) - a.value(index));
},
axis);
}
} // namespace traits
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,242 @@
// 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_AXIS_VARIABLE_HPP
#define BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
#include <algorithm>
#include <boost/assert.hpp>
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace boost {
namespace histogram {
namespace axis {
/**
Axis for non-equidistant bins on the real line.
Binning is a O(log(N)) operation. If speed matters and the problem domain
allows it, prefer a regular axis, possibly with a transform.
@tparam Value input value type, must be floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option (all values allowed).
@tparam Allocator allocator to use for dynamic memory management.
*/
template <class Value, class MetaData, class Options, class Allocator>
class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>> {
static_assert(std::is_floating_point<Value>::value,
"variable axis requires floating point type");
using value_type = Value;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type =
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
using allocator_type = Allocator;
using vec_type = std::vector<Value, allocator_type>;
public:
explicit variable(allocator_type alloc = {}) : vec_meta_(std::move(alloc)) {}
/** Construct from iterator range of bin edges.
*
* \param begin begin of edge sequence.
* \param end end of edge sequence.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class It, class = detail::requires_iterator<It>>
variable(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
: vec_meta_(vec_type(std::move(alloc)), std::move(meta)) {
if (std::distance(begin, end) <= 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
auto& v = vec_meta_.first();
v.reserve(std::distance(begin, end));
v.emplace_back(*begin++);
while (begin != end) {
if (*begin <= v.back())
BOOST_THROW_EXCEPTION(
std::invalid_argument("input sequence must be strictly ascending"));
v.emplace_back(*begin++);
}
}
/** Construct variable axis from iterable range of bin edges.
*
* \param iterable iterable range of bin edges.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class U, class = detail::requires_iterable<U>>
variable(const U& iterable, metadata_type meta = {}, allocator_type alloc = {})
: variable(std::begin(iterable), std::end(iterable), std::move(meta),
std::move(alloc)) {}
/** Construct variable axis from initializer list of bin edges.
*
* @param list `std::initializer_list` of bin edges.
* @param meta description of the axis.
* @param alloc allocator instance to use.
*/
template <class U>
variable(std::initializer_list<U> list, metadata_type meta = {},
allocator_type alloc = {})
: variable(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
variable(const variable& src, index_type begin, index_type end, unsigned merge)
: vec_meta_(vec_type(src.get_allocator()), src.metadata()) {
BOOST_ASSERT((end - begin) % merge == 0);
if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
auto& vec = vec_meta_.first();
vec.reserve((end - begin) / merge);
const auto beg = src.vec_meta_.first().begin();
for (index_type i = begin; i <= end; i += merge) vec.emplace_back(*(beg + i));
}
/// Return index for value argument.
index_type index(value_type x) const noexcept {
const auto& v = vec_meta_.first();
if (options_type::test(option::circular)) {
const auto a = v[0];
const auto b = v[size()];
x -= std::floor((x - a) / (b - a)) * (b - a);
}
return static_cast<index_type>(std::upper_bound(v.begin(), v.end(), x) - v.begin() -
1);
}
auto update(value_type x) noexcept {
const auto i = index(x);
if (std::isfinite(x)) {
auto& vec = vec_meta_.first();
if (0 <= i) {
if (i < size()) return std::make_pair(i, 0);
const auto d = value(size()) - value(size() - 0.5);
x = std::nextafter(x, std::numeric_limits<value_type>::max());
x = std::max(x, vec.back() + d);
vec.push_back(x);
return std::make_pair(i, -1);
}
const auto d = value(0.5) - value(0);
x = std::min(x, value(0) - d);
vec.insert(vec.begin(), x);
return std::make_pair(0, -i);
}
return std::make_pair(x < 0 ? -1 : size(), 0);
}
/// Return value for fractional index argument.
value_type value(real_index_type i) const noexcept {
const auto& v = vec_meta_.first();
if (options_type::test(option::circular)) {
auto shift = std::floor(i / size());
i -= shift * size();
double z;
const auto k = static_cast<index_type>(std::modf(i, &z));
const auto a = v[0];
const auto b = v[size()];
return (1.0 - z) * v[k] + z * v[k + 1] + shift * (b - a);
}
if (i < 0) return detail::lowest<value_type>();
if (i == size()) return v.back();
if (i > size()) return detail::highest<value_type>();
const auto k = static_cast<index_type>(i); // precond: i >= 0
const real_index_type z = i - k;
return (1.0 - z) * v[k] + z * v[k + 1];
}
/// Return bin for index argument.
auto bin(index_type idx) const noexcept { return interval_view<variable>(*this, idx); }
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept {
return static_cast<index_type>(vec_meta_.first().size()) - 1;
}
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return vec_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return vec_meta_.second(); }
template <class V, class M, class O, class A>
bool operator==(const variable<V, M, O, A>& o) const noexcept {
const auto& a = vec_meta_.first();
const auto& b = o.vec_meta_.first();
return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
detail::relaxed_equal(metadata(), o.metadata());
}
template <class V, class M, class O, class A>
bool operator!=(const variable<V, M, O, A>& o) const noexcept {
return !operator==(o);
}
/// Return allocator instance.
auto get_allocator() const { return vec_meta_.first().get_allocator(); }
template <class Archive>
void serialize(Archive&, unsigned);
private:
detail::compressed_pair<vec_type, metadata_type> vec_meta_;
template <class V, class M, class O, class A>
friend class variable;
};
#if __cpp_deduction_guides >= 201606
template <class U, class T = detail::convert_integer<U, double>>
variable(std::initializer_list<U>)->variable<T>;
template <class U, class T = detail::convert_integer<U, double>>
variable(std::initializer_list<U>, const char*)->variable<T>;
template <class U, class M, class T = detail::convert_integer<U, double>>
variable(std::initializer_list<U>, M)->variable<T, M>;
template <
class Iterable,
class T = detail::convert_integer<
detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
variable(Iterable)->variable<T>;
template <
class Iterable,
class T = detail::convert_integer<
detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
variable(Iterable, const char*)->variable<T>;
template <
class Iterable, class M,
class T = detail::convert_integer<
detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
variable(Iterable, M)->variable<T, M>;
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,299 @@
// Copyright 2015-2017 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_VARIANT_HPP
#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP
#include <boost/core/typeinfo.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/polymorphic_bin.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/list.hpp>
#include <boost/throw_exception.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/get.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/variant/variant.hpp>
#include <ostream>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
template <class T, class... Us>
T* get_if(variant<Us...>* v);
template <class T, class... Us>
const T* get_if(const variant<Us...>* v);
/// Polymorphic axis type
template <class... Ts>
class variant : public iterator_mixin<variant<Ts...>> {
using impl_type = boost::variant<Ts...>;
using raw_types = mp11::mp_transform<detail::remove_cvref_t, impl_type>;
template <class T>
using is_bounded_type = mp11::mp_contains<raw_types, detail::remove_cvref_t<T>>;
template <typename T>
using requires_bounded_type = std::enable_if_t<is_bounded_type<T>::value>;
// maybe metadata_type or const metadata_type, if bounded type is const
using metadata_type = std::remove_reference_t<decltype(
traits::metadata(std::declval<mp11::mp_first<impl_type>>()))>;
public:
// cannot import ctors with using directive, it breaks gcc and msvc
variant() = default;
variant(const variant&) = default;
variant& operator=(const variant&) = default;
variant(variant&&) = default;
variant& operator=(variant&&) = default;
template <typename T, typename = requires_bounded_type<T>>
variant(T&& t) : impl(std::forward<T>(t)) {}
template <typename T, typename = requires_bounded_type<T>>
variant& operator=(T&& t) {
impl = std::forward<T>(t);
return *this;
}
template <class... Us>
variant(const variant<Us...>& u) {
this->operator=(u);
}
template <class... Us>
variant& operator=(const variant<Us...>& u) {
visit(
[this](const auto& u) {
using U = detail::remove_cvref_t<decltype(u)>;
detail::static_if<is_bounded_type<U>>(
[this](const auto& u) { this->operator=(u); },
[](const auto&) {
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
" is not convertible to a bounded type of ",
boost::core::demangled_name(BOOST_CORE_TYPEID(variant)))));
},
u);
},
u);
return *this;
}
/// Return size of axis.
index_type size() const {
return visit([](const auto& x) { return x.size(); }, *this);
}
/// Return options of axis or option::none_t if axis has no options.
unsigned options() const {
return visit([](const auto& x) { return axis::traits::options(x); }, *this);
}
/// Return reference to const metadata or instance of null_type if axis has no
/// metadata.
const metadata_type& metadata() const {
return visit(
[](const auto& a) -> const metadata_type& {
using M = decltype(traits::metadata(a));
return detail::static_if<std::is_same<M, const metadata_type&>>(
[](const auto& a) -> const metadata_type& { return traits::metadata(a); },
[](const auto&) -> const metadata_type& {
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
"cannot return metadata of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(M)),
" through axis::variant interface which uses type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type)),
"; use boost::histogram::axis::get to obtain a reference "
"of this axis type")));
},
a);
},
*this);
}
/// Return reference to metadata or instance of null_type if axis has no
/// metadata.
metadata_type& metadata() {
return visit(
[](auto&& a) -> metadata_type& {
using M = decltype(traits::metadata(a));
return detail::static_if<std::is_same<M, metadata_type&>>(
[](auto& a) -> metadata_type& { return traits::metadata(a); },
[](auto&) -> metadata_type& {
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
"cannot return metadata of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(M)),
" through axis::variant interface which uses type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type)),
"; use boost::histogram::axis::get to obtain a reference "
"of this axis type")));
},
a);
},
*this);
}
/// Return index for value argument.
/// Throws std::invalid_argument if axis has incompatible call signature.
template <class U>
index_type index(const U& u) const {
return visit([&u](const auto& a) { return traits::index(a, u); }, *this);
}
/// Return value for index argument.
/// Only works for axes with value method that returns something convertible
/// to double and will throw a runtime_error otherwise, see
/// axis::traits::value().
double value(real_index_type idx) const {
return visit([idx](const auto& a) { return traits::value_as<double>(a, idx); },
*this);
}
/// Return bin for index argument.
/// Only works for axes with value method that returns something convertible
/// to double and will throw a runtime_error otherwise, see
/// axis::traits::value().
auto bin(index_type idx) const {
return visit(
[idx](const auto& a) {
return detail::value_method_switch_with_return_type<double,
polymorphic_bin<double>>(
[idx](const auto& a) { // axis is discrete
const auto x = a.value(idx);
return polymorphic_bin<double>(x, x);
},
[idx](const auto& a) { // axis is continuous
return polymorphic_bin<double>(a.value(idx), a.value(idx + 1));
},
a);
},
*this);
}
template <class... Us>
bool operator==(const variant<Us...>& u) const {
return visit([&u](const auto& x) { return u == x; }, *this);
}
template <class T>
bool operator==(const T& t) const {
// boost::variant::operator==(T) implemented only to fail, cannot use it
auto tp = get_if<T>(this);
return tp && detail::relaxed_equal(*tp, t);
}
template <class T>
bool operator!=(const T& t) const {
return !operator==(t);
}
template <class Archive>
void serialize(Archive& ar, unsigned);
template <class Visitor, class Variant>
friend auto visit(Visitor&&, Variant &&)
-> detail::visitor_return_type<Visitor, Variant>;
template <class T, class... Us>
friend T& get(variant<Us...>& v);
template <class T, class... Us>
friend const T& get(const variant<Us...>& v);
template <class T, class... Us>
friend T&& get(variant<Us...>&& v);
template <class T, class... Us>
friend T* get_if(variant<Us...>* v);
template <class T, class... Us>
friend const T* get_if(const variant<Us...>* v);
private:
boost::variant<Ts...> impl;
};
/// Apply visitor to variant.
template <class Visitor, class Variant>
auto visit(Visitor&& vis, Variant&& var)
-> detail::visitor_return_type<Visitor, Variant> {
return boost::apply_visitor(std::forward<Visitor>(vis), var.impl);
}
/// Return lvalue reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
T& get(variant<Us...>& v) {
return boost::get<T>(v.impl);
}
/// Return rvalue reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
T&& get(variant<Us...>&& v) {
return boost::get<T>(std::move(v.impl));
}
/// Return const reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
const T& get(const variant<Us...>& v) {
return boost::get<T>(v.impl);
}
/// Returns pointer to T in variant or null pointer if type does not match.
template <class T, class... Us>
T* get_if(variant<Us...>* v) {
return boost::relaxed_get<T>(&(v->impl));
}
/// Returns pointer to const T in variant or null pointer if type does not
/// match.
template <class T, class... Us>
const T* get_if(const variant<Us...>* v) {
return boost::relaxed_get<T>(&(v->impl));
}
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
// pass-through version of get for generic programming
template <class T, class U>
decltype(auto) get(U&& u) {
return static_cast<detail::copy_qualifiers<U, T>>(u);
}
// pass-through version of get_if for generic programming
template <class T, class U>
T* get_if(U* u) {
return std::is_same<T, detail::remove_cvref_t<U>>::value ? reinterpret_cast<T*>(u)
: nullptr;
}
// pass-through version of get_if for generic programming
template <class T, class U>
const T* get_if(const U* u) {
return std::is_same<T, detail::remove_cvref_t<U>>::value ? reinterpret_cast<const T*>(u)
: nullptr;
}
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,16 @@
// 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_DETAIL_ATTRIBUTE_HPP
#define BOOST_HISTOGRAM_DETAIL_ATTRIBUTE_HPP
#if __cplusplus >= 201703L
#define BOOST_HISTOGRAM_DETAIL_NODISCARD [[nodiscard]]
#else
#define BOOST_HISTOGRAM_DETAIL_NODISCARD
#endif
#endif

View File

@@ -0,0 +1,176 @@
// 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_DETAIL_AXES_HPP
#define BOOST_HISTOGRAM_DETAIL_AXES_HPP
#include <boost/assert.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <tuple>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <unsigned N, class... Ts>
decltype(auto) axis_get(std::tuple<Ts...>& axes) {
return std::get<N>(axes);
}
template <unsigned N, class... Ts>
decltype(auto) axis_get(const std::tuple<Ts...>& axes) {
return std::get<N>(axes);
}
template <unsigned N, class T>
decltype(auto) axis_get(T& axes) {
return axes[N];
}
template <unsigned N, class T>
decltype(auto) axis_get(const T& axes) {
return axes[N];
}
template <class... Ts>
decltype(auto) axis_get(std::tuple<Ts...>& axes, unsigned i) {
return mp11::mp_with_index<sizeof...(Ts)>(
i, [&](auto I) { return axis::variant<Ts&...>(std::get<I>(axes)); });
}
template <class... Ts>
decltype(auto) axis_get(const std::tuple<Ts...>& axes, unsigned i) {
return mp11::mp_with_index<sizeof...(Ts)>(
i, [&](auto I) { return axis::variant<const Ts&...>(std::get<I>(axes)); });
}
template <class T>
decltype(auto) axis_get(T& axes, unsigned i) {
return axes.at(i);
}
template <class T>
decltype(auto) axis_get(const T& axes, unsigned i) {
return axes.at(i);
}
template <class... Ts, class... Us>
bool axes_equal(const std::tuple<Ts...>& ts, const std::tuple<Us...>& us) {
return static_if<std::is_same<mp11::mp_list<Ts...>, mp11::mp_list<Us...>>>(
[](const auto& ts, const auto& us) {
using N = mp11::mp_size<remove_cvref_t<decltype(ts)>>;
bool equal = true;
mp11::mp_for_each<mp11::mp_iota<N>>(
[&](auto I) { equal &= relaxed_equal(std::get<I>(ts), std::get<I>(us)); });
return equal;
},
[](const auto&, const auto&) { return false; }, ts, us);
}
template <class... Ts, class U>
bool axes_equal(const std::tuple<Ts...>& t, const U& u) {
if (sizeof...(Ts) != u.size()) return false;
bool equal = true;
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Ts)>>(
[&](auto I) { equal &= u[I] == std::get<I>(t); });
return equal;
}
template <class T, class... Us>
bool axes_equal(const T& t, const std::tuple<Us...>& u) {
return axes_equal(u, t);
}
template <class T, class U>
bool axes_equal(const T& t, const U& u) {
if (t.size() != u.size()) return false;
return std::equal(t.begin(), t.end(), u.begin());
}
template <class... Ts, class... Us>
void axes_assign(std::tuple<Ts...>& t, const std::tuple<Us...>& u) {
static_if<std::is_same<mp11::mp_list<Ts...>, mp11::mp_list<Us...>>>(
[](auto& a, const auto& b) { a = b; },
[](auto&, const auto&) {
BOOST_THROW_EXCEPTION(
std::invalid_argument("cannot assign axes, types do not match"));
},
t, u);
}
template <class... Ts, class U>
void axes_assign(std::tuple<Ts...>& t, const U& u) {
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Ts)>>([&](auto I) {
using T = mp11::mp_at_c<std::tuple<Ts...>, I>;
std::get<I>(t) = axis::get<T>(u[I]);
});
}
template <class T, class... Us>
void axes_assign(T& t, const std::tuple<Us...>& u) {
// resize instead of reserve, because t may not be empty and we want exact capacity
t.resize(sizeof...(Us));
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>(
[&](auto I) { t[I] = std::get<I>(u); });
}
template <typename T, typename U>
void axes_assign(T& t, const U& u) {
t.assign(u.begin(), u.end());
}
template <typename T>
void axis_index_is_valid(const T& axes, const unsigned N) {
BOOST_ASSERT_MSG(N < get_size(axes), "index out of range");
}
template <typename F, typename T>
void for_each_axis_impl(std::true_type, const T& axes, F&& f) {
for (const auto& x : axes) { axis::visit(std::forward<F>(f), x); }
}
template <typename F, typename T>
void for_each_axis_impl(std::false_type, const T& axes, F&& f) {
for (const auto& x : axes) std::forward<F>(f)(x);
}
template <typename F, typename T>
void for_each_axis(const T& axes, F&& f) {
using U = mp11::mp_first<T>;
for_each_axis_impl(is_axis_variant<U>(), axes, std::forward<F>(f));
}
template <typename F, typename... Ts>
void for_each_axis(const std::tuple<Ts...>& axes, F&& f) {
mp11::tuple_for_each(axes, std::forward<F>(f));
}
template <typename T>
std::size_t bincount(const T& axes) {
std::size_t n = 1;
for_each_axis(axes, [&n](const auto& a) {
const auto old = n;
const auto s = axis::traits::extent(a);
n *= s;
if (s > 0 && n < old) BOOST_THROW_EXCEPTION(std::overflow_error("bincount overflow"));
});
return n;
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,34 @@
// 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_DETAIL_CAT_HPP
#define BOOST_HISTOGRAM_DETAIL_CAT_HPP
#include <boost/config.hpp>
#include <sstream>
namespace boost {
namespace histogram {
namespace detail {
BOOST_ATTRIBUTE_UNUSED inline void cat_impl(std::ostringstream&) {}
template <typename T, typename... Ts>
void cat_impl(std::ostringstream& os, const T& t, const Ts&... ts) {
os << t;
cat_impl(os, ts...);
}
template <typename... Ts>
std::string cat(const Ts&... args) {
std::ostringstream os;
cat_impl(os, args...);
return os.str();
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,78 @@
// 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_DETAIL_COMMON_TYPE_HPP
#define BOOST_HISTOGRAM_DETAIL_COMMON_TYPE_HPP
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
#include <tuple>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
// clang-format off
template <class T, class U>
using common_axes = mp11::mp_cond<
is_tuple<T>, T,
is_tuple<U>, U,
is_sequence_of_axis<T>, T,
is_sequence_of_axis<U>, U,
std::true_type, T
>;
template <class T, class U>
using common_container = mp11::mp_cond<
is_array_like<T>, T,
is_array_like<U>, U,
is_vector_like<T>, T,
is_vector_like<U>, U,
std::true_type, T
>;
// clang-format on
template <class T>
using type_score = mp11::mp_size_t<((!std::is_pod<T>::value) * 1000 +
std::is_floating_point<T>::value * 50 + sizeof(T))>;
template <class T, class U>
struct common_storage_impl;
template <class T, class U>
struct common_storage_impl<storage_adaptor<T>, storage_adaptor<U>> {
using type =
mp11::mp_if_c<(type_score<typename storage_adaptor<T>::value_type>::value >=
type_score<typename storage_adaptor<U>::value_type>::value),
storage_adaptor<T>, storage_adaptor<U>>;
};
template <class T, class A>
struct common_storage_impl<storage_adaptor<T>, unlimited_storage<A>> {
using type =
mp11::mp_if_c<(type_score<typename storage_adaptor<T>::value_type>::value >=
type_score<typename unlimited_storage<A>::value_type>::value),
storage_adaptor<T>, unlimited_storage<A>>;
};
template <class C, class A>
struct common_storage_impl<unlimited_storage<A>, storage_adaptor<C>>
: common_storage_impl<storage_adaptor<C>, unlimited_storage<A>> {};
template <class A1, class A2>
struct common_storage_impl<unlimited_storage<A1>, unlimited_storage<A2>> {
using type = unlimited_storage<A1>;
};
template <class A, class B>
using common_storage = typename common_storage_impl<A, B>::type;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,74 @@
// 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_DETAIL_COMPRESSED_PAIR_HPP
#define BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
template <typename T1, typename T2, bool B>
class compressed_pair_impl;
template <typename T1, typename T2>
class compressed_pair_impl<T1, T2, true> : protected T2 {
public:
template <typename U1, typename U2>
compressed_pair_impl(U1&& u1, U2&& u2)
: T2(std::forward<U2>(u2)), first_(std::forward<U1>(u1)) {}
template <typename U1>
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
compressed_pair_impl() = default;
T1& first() { return first_; }
T2& second() { return static_cast<T2&>(*this); }
const T1& first() const { return first_; }
const T2& second() const { return static_cast<const T2&>(*this); }
private:
T1 first_;
};
template <typename T1, typename T2>
class compressed_pair_impl<T1, T2, false> {
public:
template <typename U1, typename U2>
compressed_pair_impl(U1&& u1, U2&& u2)
: first_(std::forward<U1>(u1)), second_(std::forward<U2>(u2)) {}
template <typename U1>
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
compressed_pair_impl() = default;
T1& first() { return first_; }
T2& second() { return second_; }
const T1& first() const { return first_; }
const T2& second() const { return second_; }
private:
T1 first_;
T2 second_;
};
template <typename... Ts>
void swap(compressed_pair_impl<Ts...>& a, compressed_pair_impl<Ts...>& b) {
using std::swap;
swap(a.first(), b.first());
swap(a.second(), b.second());
}
template <typename T1, typename T2>
using compressed_pair =
compressed_pair_impl<T1, T2, (!std::is_final<T2>::value && std::is_empty<T2>::value)>;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,337 @@
// 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_DETAIL_LINEARIZE_HPP
#define BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
#include <algorithm>
#include <boost/assert.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <tuple>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
struct is_accumulator_set : std::false_type {};
template <class T>
using has_underflow =
decltype(axis::traits::static_options<T>::test(axis::option::underflow));
template <class T>
struct is_growing
: decltype(axis::traits::static_options<T>::test(axis::option::growth)) {};
template <class... Ts>
struct is_growing<std::tuple<Ts...>> : mp11::mp_or<is_growing<Ts>...> {};
template <class... Ts>
struct is_growing<axis::variant<Ts...>> : mp11::mp_or<is_growing<Ts>...> {};
template <class T>
using has_growing_axis =
mp11::mp_if<is_vector_like<T>, is_growing<mp11::mp_first<T>>, is_growing<T>>;
/// Index with an invalid state
struct optional_index {
std::size_t idx = 0;
std::size_t stride = 1;
operator bool() const { return stride > 0; }
std::size_t operator*() const { return idx; }
};
inline void linearize(optional_index& out, const axis::index_type extent,
const axis::index_type j) noexcept {
// j is internal index shifted by +1 if axis has underflow bin
out.idx += j * out.stride;
// set stride to 0, if j is invalid
out.stride *= (0 <= j && j < extent) * extent;
}
// for non-growing axis
template <class Axis, class Value>
void linearize_value(optional_index& o, const Axis& a, const Value& v) {
using B = decltype(axis::traits::static_options<Axis>::test(axis::option::underflow));
const auto j = axis::traits::index(a, v) + B::value;
linearize(o, axis::traits::extent(a), j);
}
// for variant that does not contain any growing axis
template <class... Ts, class Value>
void linearize_value(optional_index& o, const axis::variant<Ts...>& a, const Value& v) {
axis::visit([&o, &v](const auto& a) { linearize_value(o, a, v); }, a);
}
// for growing axis
template <class Axis, class Value>
void linearize_value(optional_index& o, axis::index_type& s, Axis& a, const Value& v) {
axis::index_type j;
std::tie(j, s) = axis::traits::update(a, v);
j += has_underflow<Axis>::value;
linearize(o, axis::traits::extent(a), j);
}
// for variant which contains at least one growing axis
template <class... Ts, class Value>
void linearize_value(optional_index& o, axis::index_type& s, axis::variant<Ts...>& a,
const Value& v) {
axis::visit([&o, &s, &v](auto&& a) { linearize_value(o, s, a, v); }, a);
}
template <class A>
void linearize_index(optional_index& out, const A& axis, const axis::index_type j) {
// A may be axis or variant, cannot use static option detection here
const auto opt = axis::traits::options(axis);
const auto shift = opt & axis::option::underflow ? 1 : 0;
const auto n = axis.size() + (opt & axis::option::overflow ? 1 : 0);
linearize(out, n + shift, j + shift);
}
template <class S, class A, class T>
void maybe_replace_storage(S& storage, const A& axes, const T& shifts) {
bool update_needed = false;
auto sit = shifts;
for_each_axis(axes, [&](const auto&) { update_needed |= (*sit++ != 0); });
if (!update_needed) return;
struct item {
axis::index_type idx, old_extent;
std::size_t new_stride;
} data[buffer_size<A>::value];
sit = shifts;
auto dit = data;
std::size_t s = 1;
for_each_axis(axes, [&](const auto& a) {
const auto n = axis::traits::extent(a);
*dit++ = {0, n - std::abs(*sit++), s};
s *= n;
});
auto new_storage = make_default(storage);
new_storage.reset(detail::bincount(axes));
const auto dlast = data + get_size(axes) - 1;
for (const auto& x : storage) {
auto ns = new_storage.begin();
sit = shifts;
dit = data;
for_each_axis(axes, [&](const auto& a) {
using opt = axis::traits::static_options<decltype(a)>;
if (opt::test(axis::option::underflow)) {
if (dit->idx == 0) {
// axis has underflow and we are in the underflow bin:
// keep storage pointer unchanged
++dit;
++sit;
return;
}
}
if (opt::test(axis::option::overflow)) {
if (dit->idx == dit->old_extent - 1) {
// axis has overflow and we are in the overflow bin:
// move storage pointer to corresponding overflow bin position
ns += (axis::traits::extent(a) - 1) * dit->new_stride;
++dit;
++sit;
return;
}
}
// we are in a normal bin:
// move storage pointer to index position, apply positive shifts
ns += (dit->idx + std::max(*sit, 0)) * dit->new_stride;
++dit;
++sit;
});
// assign old value to new location
*ns = x;
// advance multi-dimensional index
dit = data;
++dit->idx;
while (dit != dlast && dit->idx == dit->old_extent) {
dit->idx = 0;
++(++dit)->idx;
}
}
storage = std::move(new_storage);
}
// special case: if histogram::operator()(tuple(1, 2)) is called on 1d histogram
// with axis that accepts 2d tuple, this should not fail
// - solution is to forward tuples of size > 1 directly to axis for 1d
// histograms
// - has nice side-effect of making histogram::operator(1, 2) work as well
// - cannot detect call signature of axis at compile-time in all configurations
// (axis::variant provides generic call interface and hides concrete
// interface), so we throw at runtime if incompatible argument is passed (e.g.
// 3d tuple)
// histogram has only non-growing axes
template <unsigned I, unsigned N, class S, class T, class U>
optional_index args_to_index(std::false_type, S&, const T& axes, const U& args) {
optional_index idx;
const auto rank = get_size(axes);
if (rank == 1 && N > 1)
linearize_value(idx, axis_get<0>(axes), tuple_slice<I, N>(args));
else {
if (rank != N)
BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments != histogram rank"));
constexpr unsigned M = buffer_size<remove_cvref_t<decltype(axes)>>::value;
mp11::mp_for_each<mp11::mp_iota_c<(N < M ? N : M)>>([&](auto J) {
linearize_value(idx, axis_get<J>(axes), std::get<(J + I)>(args));
});
}
return idx;
}
// histogram has growing axes
template <unsigned I, unsigned N, class S, class T, class U>
optional_index args_to_index(std::true_type, S& storage, T& axes, const U& args) {
optional_index idx;
axis::index_type shifts[buffer_size<T>::value];
const auto rank = get_size(axes);
if (rank == 1 && N > 1)
linearize_value(idx, shifts[0], axis_get<0>(axes), tuple_slice<I, N>(args));
else {
if (rank != N)
BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments != histogram rank"));
constexpr unsigned M = buffer_size<remove_cvref_t<decltype(axes)>>::value;
mp11::mp_for_each<mp11::mp_iota_c<(N < M ? N : M)>>([&](auto J) {
linearize_value(idx, shifts[J], axis_get<J>(axes), std::get<(J + I)>(args));
});
}
maybe_replace_storage(storage, axes, shifts);
return idx;
}
template <typename U>
constexpr auto weight_sample_indices() {
if (is_weight<U>::value) return std::make_pair(0, -1);
if (is_sample<U>::value) return std::make_pair(-1, 0);
return std::make_pair(-1, -1);
}
template <typename U0, typename U1, typename... Us>
constexpr auto weight_sample_indices() {
using L = mp11::mp_list<U0, U1, Us...>;
const int n = sizeof...(Us) + 1;
if (is_weight<mp11::mp_at_c<L, 0>>::value) {
if (is_sample<mp11::mp_at_c<L, 1>>::value) return std::make_pair(0, 1);
if (is_sample<mp11::mp_at_c<L, n>>::value) return std::make_pair(0, n);
return std::make_pair(0, -1);
}
if (is_sample<mp11::mp_at_c<L, 0>>::value) {
if (is_weight<mp11::mp_at_c<L, 1>>::value) return std::make_pair(1, 0);
if (is_weight<mp11::mp_at_c<L, n>>::value) return std::make_pair(n, 0);
return std::make_pair(-1, 0);
}
if (is_weight<mp11::mp_at_c<L, n>>::value) {
// 0, n already covered
if (is_sample<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n, n - 1);
return std::make_pair(n, -1);
}
if (is_sample<mp11::mp_at_c<L, n>>::value) {
// n, 0 already covered
if (is_weight<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n - 1, n);
return std::make_pair(-1, n);
}
return std::make_pair(-1, -1);
}
template <class T, class U>
void fill_storage(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, U&&) {
static_if<is_incrementable<remove_cvref_t<T>>>(
[](auto&& t) { ++t; }, [](auto&& t) { t(); }, std::forward<T>(t));
}
template <class IW, class T, class U>
void fill_storage(IW, mp11::mp_int<-1>, T&& t, U&& args) {
static_if<is_incrementable<remove_cvref_t<T>>>(
[](auto&& t, const auto& w) { t += w; },
[](auto&& t, const auto& w) {
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
static_if<is_accumulator_set<remove_cvref_t<T>>>(
[w](auto&& t) { t(::boost::accumulators::weight = w); },
[w](auto&& t) { t(w); }, t);
#else
t(w);
#endif
},
std::forward<T>(t), std::get<IW::value>(args).value);
}
template <class IS, class T, class U>
void fill_storage(mp11::mp_int<-1>, IS, T&& t, U&& args) {
mp11::tuple_apply([&t](auto&&... args) { t(args...); },
std::get<IS::value>(args).value);
}
template <class IW, class IS, class T, class U>
void fill_storage(IW, IS, T&& t, U&& args) {
mp11::tuple_apply(
[&](auto&&... args2) { t(std::get<IW::value>(args).value, args2...); },
std::get<IS::value>(args).value);
}
template <class S, class A, class... Us>
auto fill(S& storage, A& axes, const std::tuple<Us...>& args) {
constexpr auto iws = weight_sample_indices<Us...>();
constexpr unsigned n = sizeof...(Us) - (iws.first > -1) - (iws.second > -1);
constexpr unsigned i = (iws.first == 0 || iws.second == 0)
? (iws.first == 1 || iws.second == 1 ? 2 : 1)
: 0;
optional_index idx = args_to_index<i, n>(has_growing_axis<A>(), storage, axes, args);
if (idx) {
fill_storage(mp11::mp_int<iws.first>(), mp11::mp_int<iws.second>(), storage[*idx],
args);
return storage.begin() + *idx;
}
return storage.end();
}
template <typename A, typename... Us>
optional_index at(const A& axes, const std::tuple<Us...>& args) {
if (get_size(axes) != sizeof...(Us))
BOOST_THROW_EXCEPTION(std::invalid_argument("number of arguments != histogram rank"));
optional_index idx;
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>([&](auto I) {
// axes_get works with static and dynamic axes
linearize_index(idx, axis_get<I>(axes),
static_cast<axis::index_type>(std::get<I>(args)));
});
return idx;
}
template <typename A, typename U>
optional_index at(const A& axes, const U& args) {
if (get_size(axes) != get_size(args))
BOOST_THROW_EXCEPTION(std::invalid_argument("number of arguments != histogram rank"));
optional_index idx;
using std::begin;
auto it = begin(args);
for_each_axis(axes, [&](const auto& a) {
linearize_index(idx, a, static_cast<axis::index_type>(*it++));
});
return idx;
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,436 @@
// 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_DETAIL_META_HPP
#define BOOST_HISTOGRAM_DETAIL_META_HPP
/* Most of the histogram code is generic and works for any number of axes. Buffers with a
* fixed maximum capacity are used in some places, which have a size equal to the rank of
* a histogram. The buffers are statically allocated to improve performance, which means
* that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank
* (you can nevertheless increase it here if necessary): the simplest non-trivial axis has
* 2 bins; even if counters are used which need only a byte of storage per bin, this still
* corresponds to 4 GB of storage.
*/
#ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT
#define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32
#endif
#include <boost/config/workaround.hpp>
#if BOOST_WORKAROUND(BOOST_GCC, >= 60000)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnoexcept-type"
#endif
#include <boost/callable_traits/args.hpp>
#include <boost/callable_traits/return_type.hpp>
#if BOOST_WORKAROUND(BOOST_GCC, >= 60000)
#pragma GCC diagnostic pop
#endif
#include <array>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
#include <functional>
#include <iterator>
#include <limits>
#include <tuple>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <class T, class U>
using convert_integer = mp11::mp_if<std::is_integral<remove_cvref_t<T>>, U, T>;
// to be replaced by official version from mp11
template <class E, template <class...> class F, class... Ts>
using mp_eval_or = mp11::mp_eval_if_c<!(mp11::mp_valid<F, Ts...>::value), E, F, Ts...>;
template <class T1, class T2>
using copy_qualifiers = mp11::mp_if<
std::is_rvalue_reference<T1>, T2&&,
mp11::mp_if<std::is_lvalue_reference<T1>,
mp11::mp_if<std::is_const<typename std::remove_reference<T1>::type>,
const T2&, T2&>,
mp11::mp_if<std::is_const<T1>, const T2, T2>>>;
template <class L>
using mp_last = mp11::mp_at_c<L, (mp11::mp_size<L>::value - 1)>;
template <class T, class Args = boost::callable_traits::args_t<T>>
using args_type =
mp11::mp_if<std::is_member_function_pointer<T>, mp11::mp_pop_front<Args>, Args>;
template <class T, std::size_t N = 0>
using arg_type = typename mp11::mp_at_c<args_type<T>, N>;
template <class T>
using return_type = typename boost::callable_traits::return_type<T>::type;
template <class F, class V,
class T = copy_qualifiers<V, mp11::mp_first<remove_cvref_t<V>>>>
using visitor_return_type = decltype(std::declval<F>()(std::declval<T>()));
template <bool B, typename T, typename F, typename... Ts>
constexpr decltype(auto) static_if_c(T&& t, F&& f, Ts&&... ts) {
return std::get<(B ? 0 : 1)>(std::forward_as_tuple(
std::forward<T>(t), std::forward<F>(f)))(std::forward<Ts>(ts)...);
}
template <typename B, typename... Ts>
constexpr decltype(auto) static_if(Ts&&... ts) {
return static_if_c<B::value>(std::forward<Ts>(ts)...);
}
template <typename T>
constexpr T lowest() {
return std::numeric_limits<T>::lowest();
}
template <>
constexpr double lowest() {
return -std::numeric_limits<double>::infinity();
}
template <>
constexpr float lowest() {
return -std::numeric_limits<float>::infinity();
}
template <typename T>
constexpr T highest() {
return std::numeric_limits<T>::max();
}
template <>
constexpr double highest() {
return std::numeric_limits<double>::infinity();
}
template <>
constexpr float highest() {
return std::numeric_limits<float>::infinity();
}
template <std::size_t I, class T, std::size_t... N>
decltype(auto) tuple_slice_impl(T&& t, mp11::index_sequence<N...>) {
return std::forward_as_tuple(std::get<(N + I)>(std::forward<T>(t))...);
}
template <std::size_t I, std::size_t N, class T>
decltype(auto) tuple_slice(T&& t) {
static_assert(I + N <= mp11::mp_size<remove_cvref_t<T>>::value,
"I and N must describe a slice");
return tuple_slice_impl<I>(std::forward<T>(t), mp11::make_index_sequence<N>{});
}
#define BOOST_HISTOGRAM_DETECT(name, cond) \
template <class T, class = decltype(cond)> \
struct name##_impl {}; \
template <class T> \
using name = typename mp11::mp_valid<name##_impl, T>
#define BOOST_HISTOGRAM_DETECT_BINARY(name, cond) \
template <class T, class U, class = decltype(cond)> \
struct name##_impl {}; \
template <class T, class U = T> \
using name = typename mp11::mp_valid<name##_impl, T, U>
BOOST_HISTOGRAM_DETECT(has_method_metadata, (std::declval<T&>().metadata()));
// resize has two overloads, trying to get pmf in this case always fails
BOOST_HISTOGRAM_DETECT(has_method_resize, (std::declval<T&>().resize(0)));
BOOST_HISTOGRAM_DETECT(has_method_size, &T::size);
BOOST_HISTOGRAM_DETECT(has_method_clear, &T::clear);
BOOST_HISTOGRAM_DETECT(has_method_lower, &T::lower);
BOOST_HISTOGRAM_DETECT(has_method_value, &T::value);
BOOST_HISTOGRAM_DETECT(has_method_update, (&T::update));
BOOST_HISTOGRAM_DETECT(has_method_reset, (std::declval<T>().reset(0)));
template <typename T>
using get_value_method_return_type_impl = decltype(std::declval<T&>().value(0));
template <typename T, typename R>
using has_method_value_with_convertible_return_type =
typename std::is_convertible<mp_eval_or<void, get_value_method_return_type_impl, T>,
R>::type;
BOOST_HISTOGRAM_DETECT(has_method_options, (&T::options));
BOOST_HISTOGRAM_DETECT(has_allocator, &T::get_allocator);
BOOST_HISTOGRAM_DETECT(is_indexable, (std::declval<T&>()[0]));
BOOST_HISTOGRAM_DETECT(is_transform, (&T::forward, &T::inverse));
BOOST_HISTOGRAM_DETECT(is_indexable_container,
(std::declval<T>()[0], &T::size, std::begin(std::declval<T>()),
std::end(std::declval<T>())));
BOOST_HISTOGRAM_DETECT(is_vector_like,
(std::declval<T>()[0], &T::size, std::declval<T>().resize(0),
std::begin(std::declval<T>()), std::end(std::declval<T>())));
BOOST_HISTOGRAM_DETECT(is_array_like,
(std::declval<T>()[0], &T::size, std::tuple_size<T>::value,
std::begin(std::declval<T>()), std::end(std::declval<T>())));
BOOST_HISTOGRAM_DETECT(is_map_like,
(std::declval<typename T::key_type>(),
std::declval<typename T::mapped_type>(),
std::begin(std::declval<T>()), std::end(std::declval<T>())));
// ok: is_axis is false for axis::variant, operator() is templated
BOOST_HISTOGRAM_DETECT(is_axis, (&T::size, &T::index));
BOOST_HISTOGRAM_DETECT(is_iterable,
(std::begin(std::declval<T&>()), std::end(std::declval<T&>())));
BOOST_HISTOGRAM_DETECT(is_iterator,
(typename std::iterator_traits<T>::iterator_category()));
BOOST_HISTOGRAM_DETECT(is_streamable,
(std::declval<std::ostream&>() << std::declval<T&>()));
BOOST_HISTOGRAM_DETECT(is_incrementable, (++std::declval<T&>()));
BOOST_HISTOGRAM_DETECT(has_operator_preincrement, (++std::declval<T&>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_equal,
(std::declval<const T&>() == std::declval<const U&>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_radd,
(std::declval<T&>() += std::declval<U&>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rsub,
(std::declval<T&>() -= std::declval<U&>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rmul,
(std::declval<T&>() *= std::declval<U&>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rdiv,
(std::declval<T&>() /= std::declval<U&>()));
template <typename T>
using is_storage =
mp11::mp_bool<(is_indexable_container<T>::value && has_method_reset<T>::value)>;
template <typename T>
struct is_tuple_impl : std::false_type {};
template <typename... Ts>
struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
template <typename T>
using is_tuple = typename is_tuple_impl<T>::type;
template <typename T>
struct is_axis_variant_impl : std::false_type {};
template <typename... Ts>
struct is_axis_variant_impl<axis::variant<Ts...>> : std::true_type {};
template <typename T>
using is_axis_variant = typename is_axis_variant_impl<T>::type;
template <typename T>
using is_any_axis = mp11::mp_or<is_axis<T>, is_axis_variant<T>>;
template <typename T>
using is_sequence_of_axis = mp11::mp_and<is_iterable<T>, is_axis<mp11::mp_first<T>>>;
template <typename T>
using is_sequence_of_axis_variant =
mp11::mp_and<is_iterable<T>, is_axis_variant<mp11::mp_first<T>>>;
template <typename T>
using is_sequence_of_any_axis =
mp11::mp_and<is_iterable<T>, is_any_axis<mp11::mp_first<T>>>;
template <typename T>
struct is_weight_impl : std::false_type {};
template <typename T>
struct is_weight_impl<weight_type<T>> : std::true_type {};
template <typename T>
using is_weight = is_weight_impl<remove_cvref_t<T>>;
template <typename T>
struct is_sample_impl : std::false_type {};
template <typename T>
struct is_sample_impl<sample_type<T>> : std::true_type {};
template <typename T>
using is_sample = is_sample_impl<remove_cvref_t<T>>;
// poor-mans concept checks
template <class T, class = std::enable_if_t<is_iterator<remove_cvref_t<T>>::value>>
struct requires_iterator {};
template <class T, class = std::enable_if_t<is_iterable<remove_cvref_t<T>>::value>>
struct requires_iterable {};
template <class T, class = std::enable_if_t<is_axis<remove_cvref_t<T>>::value>>
struct requires_axis {};
template <class T, class = std::enable_if_t<is_any_axis<remove_cvref_t<T>>::value>>
struct requires_any_axis {};
template <class T,
class = std::enable_if_t<is_sequence_of_axis<remove_cvref_t<T>>::value>>
struct requires_sequence_of_axis {};
template <class T,
class = std::enable_if_t<is_sequence_of_axis_variant<remove_cvref_t<T>>::value>>
struct requires_sequence_of_axis_variant {};
template <class T,
class = std::enable_if_t<is_sequence_of_any_axis<remove_cvref_t<T>>::value>>
struct requires_sequence_of_any_axis {};
template <class T,
class = std::enable_if_t<is_any_axis<mp11::mp_first<remove_cvref_t<T>>>::value>>
struct requires_axes {};
template <class T, class U, class = std::enable_if_t<std::is_convertible<T, U>::value>>
struct requires_convertible {};
template <class T>
auto make_default(const T& t) {
return static_if<has_allocator<T>>([](const auto& t) { return T(t.get_allocator()); },
[](const auto&) { return T(); }, t);
}
template <class T>
using tuple_size_t = typename std::tuple_size<T>::type;
template <class T>
constexpr std::size_t get_size_impl(std::true_type, const T&) noexcept {
return std::tuple_size<T>::value;
}
template <class T>
std::size_t get_size_impl(std::false_type, const T& t) noexcept {
using std::begin;
using std::end;
return static_cast<std::size_t>(std::distance(begin(t), end(t)));
}
template <class T>
std::size_t get_size(const T& t) noexcept {
return get_size_impl(mp11::mp_valid<tuple_size_t, T>(), t);
}
template <class T>
using buffer_size =
mp_eval_or<std::integral_constant<std::size_t, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>,
tuple_size_t, T>;
template <class T, std::size_t N>
class sub_array : public std::array<T, N> {
public:
explicit sub_array(std::size_t s) : size_(s) {}
sub_array(std::size_t s, T value) : size_(s) { std::array<T, N>::fill(value); }
// need to override both versions of std::array
auto end() noexcept { return std::array<T, N>::begin() + size_; }
auto end() const noexcept { return std::array<T, N>::begin() + size_; }
auto size() const noexcept { return size_; }
private:
std::size_t size_ = N;
};
template <class U, class T>
using stack_buffer = sub_array<U, buffer_size<T>::value>;
template <class U, class T, class... Ts>
auto make_stack_buffer(const T& t, Ts&&... ts) {
return stack_buffer<U, T>(get_size(t), std::forward<Ts>(ts)...);
}
template <class T>
constexpr bool relaxed_equal(const T& a, const T& b) noexcept {
return static_if<has_operator_equal<T>>(
[](const auto& a, const auto& b) { return a == b; },
[](const auto&, const auto&) { return true; }, a, b);
}
template <class T>
using get_scale_type_helper = typename T::value_type;
template <class T>
using get_scale_type = mp_eval_or<T, detail::get_scale_type_helper, T>;
struct one_unit {};
template <class T>
T operator*(T&& t, const one_unit&) {
return std::forward<T>(t);
}
template <class T>
T operator/(T&& t, const one_unit&) {
return std::forward<T>(t);
}
template <class T>
using get_unit_type_helper = typename T::unit_type;
template <class T>
using get_unit_type = mp_eval_or<one_unit, detail::get_unit_type_helper, T>;
template <class T, class R = get_scale_type<T>>
R get_scale(const T& t) {
return t / get_unit_type<T>();
}
template <class T, class Default>
using replace_default = mp11::mp_if<std::is_same<T, use_default>, Default, T>;
template <class T, class U>
using is_convertible_helper =
mp11::mp_apply<mp11::mp_all, mp11::mp_transform<std::is_convertible, T, U>>;
template <class T, class U>
using is_convertible = mp_eval_or<std::false_type, is_convertible_helper, T, U>;
template <class T>
auto make_unsigned_impl(std::true_type, const T t) noexcept {
return static_cast<typename std::make_unsigned<T>::type>(t);
}
template <class T>
auto make_unsigned_impl(std::false_type, const T t) noexcept {
return t;
}
template <class T>
auto make_unsigned(const T t) noexcept {
return make_unsigned_impl(std::is_integral<T>{}, t);
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,122 @@
// 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_FWD_HPP
#define BOOST_HISTOGRAM_FWD_HPP
/**
\file boost/histogram/fwd.hpp
Forward declarations, tag types and type aliases.
*/
#include <boost/core/use_default.hpp>
#include <boost/histogram/detail/attribute.hpp> // BOOST_HISTOGRAM_DETAIL_NODISCARD
#include <string>
#include <vector>
namespace boost {
namespace histogram {
/// Tag type to indicate use of a default type
using boost::use_default;
namespace axis {
/// Integral type for axis indices
using index_type = int;
/// Real type for axis indices
using real_index_type = double;
/// Empty metadata type
struct null_type {};
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace transform {
struct id;
struct log;
struct sqrt;
struct pow;
} // namespace transform
template <class Value = double, class Transform = use_default,
class MetaData = use_default, class Options = use_default>
class regular;
template <class Value = int, class MetaData = use_default, class Options = use_default>
class integer;
template <class Value = double, class MetaData = use_default, class Options = use_default,
class Allocator = std::allocator<Value>>
class variable;
template <class Value = int, class MetaData = use_default, class Options = use_default,
class Allocator = std::allocator<Value>>
class category;
template <class... Ts>
class variant;
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
} // namespace axis
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
template <class T>
struct weight_type;
template <class T>
struct sample_type;
namespace accumulators {
template <class Value = double>
class sum;
template <class Value = double>
class weighted_sum;
template <class Value = double>
class mean;
template <class Value = double>
class weighted_mean;
} // namespace accumulators
struct unsafe_access;
template <class Allocator = std::allocator<char>>
class unlimited_storage;
template <class T>
class storage_adaptor;
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
/// Vector-like storage for fast zero-overhead access to cells
template <class T, class A = std::allocator<T>>
using dense_storage = storage_adaptor<std::vector<T, A>>;
/// Default storage, optimized for unweighted histograms
using default_storage = unlimited_storage<>;
/// Dense storage which tracks sums of weights and a variance estimate
using weight_storage = dense_storage<accumulators::weighted_sum<>>;
/// Dense storage which tracks means of samples in each cell
using profile_storage = dense_storage<accumulators::mean<>>;
/// Dense storage which tracks means of weighted samples in each cell
using weighted_profile_storage = dense_storage<accumulators::weighted_mean<>>;
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
template <class Axes, class Storage = default_storage>
class BOOST_HISTOGRAM_DETAIL_NODISCARD histogram;
#endif
} // namespace histogram
} // namespace boost
#endif

View File

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

View File

@@ -0,0 +1,220 @@
// Copyright 2015-2016 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_INDEXED_HPP
#define BOOST_HISTOGRAM_INDEXED_HPP
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/detail/attribute.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
/**
Coverage mode of the indexed range generator.
Defines options for the iteration strategy.
*/
enum class coverage {
inner, /*!< iterate over inner bins, exclude underflow and overflow */
all, /*!< iterate over all bins, including underflow and overflow */
};
/// Range over histogram bins with multi-dimensional index.
template <class Histogram>
class BOOST_HISTOGRAM_DETAIL_NODISCARD indexed_range {
using max_dim = mp11::mp_size_t<
detail::buffer_size<typename detail::remove_cvref_t<Histogram>::axes_type>::value>;
using value_iterator = decltype(std::declval<Histogram>().begin());
struct cache_item {
int idx, begin, end, extent;
};
public:
/**
Pointer-like class to access value and index of current cell.
Its methods allow one to query the current indices and bins. Furthermore, it acts like
a pointer to the cell value.
*/
class accessor {
public:
/// Array-like view into the current multi-dimensional index.
class index_view {
public:
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
class index_iterator
: public boost::iterator_adaptor<index_iterator, const cache_item*> {
public:
index_iterator(const cache_item* i) : index_iterator::iterator_adaptor_(i) {}
decltype(auto) operator*() const noexcept { return index_iterator::base()->idx; }
};
#endif
auto begin() const noexcept { return index_iterator(begin_); }
auto end() const noexcept { return index_iterator(end_); }
auto size() const noexcept { return static_cast<std::size_t>(end_ - begin_); }
int operator[](unsigned d) const noexcept { return begin_[d].idx; }
int at(unsigned d) const { return begin_[d].idx; }
private:
index_view(const cache_item* b, const cache_item* e) : begin_(b), end_(e) {}
const cache_item *begin_, *end_;
friend class accessor;
};
/// Returns the cell value.
decltype(auto) operator*() const noexcept { return *iter_; }
/// Returns the cell value.
decltype(auto) get() const noexcept { return *iter_; }
/// Access fields and methods of the cell object.
decltype(auto) operator-> () const noexcept { return iter_; }
/// Access current index.
/// @param d axis dimension.
int index(unsigned d = 0) const noexcept { return parent_.cache_[d].idx; }
/// Access indices as an iterable range.
auto indices() const noexcept {
return index_view(parent_.cache_, parent_.cache_ + parent_.hist_.rank());
}
/// Access current bin.
/// @tparam N axis dimension.
template <unsigned N = 0>
decltype(auto) bin(std::integral_constant<unsigned, N> = {}) const {
return parent_.hist_.axis(std::integral_constant<unsigned, N>()).bin(index(N));
}
/// Access current bin.
/// @param d axis dimension.
decltype(auto) bin(unsigned d) const { return parent_.hist_.axis(d).bin(index(d)); }
/**
Computes density in current cell.
The density is computed as the cell value divided by the product of bin widths. Axes
without bin widths, like axis::category, are treated as having unit bin with.
*/
double density() const {
double x = 1;
auto it = parent_.cache_;
parent_.hist_.for_each_axis([&](const auto& a) {
const auto w = axis::traits::width_as<double>(a, it++->idx);
x *= w ? w : 1;
});
return *iter_ / x;
}
private:
accessor(indexed_range& p, value_iterator i) : parent_(p), iter_(i) {}
indexed_range& parent_;
value_iterator iter_;
friend class indexed_range;
};
class range_iterator
: public boost::iterator_adaptor<range_iterator, value_iterator, accessor,
std::forward_iterator_tag, accessor> {
public:
accessor operator*() const noexcept { return {*parent_, range_iterator::base()}; }
private:
range_iterator(indexed_range* p, value_iterator i) noexcept
: range_iterator::iterator_adaptor_(i), parent_(p) {}
void increment() noexcept {
std::size_t stride = 1;
auto c = parent_->cache_;
++c->idx;
++range_iterator::base_reference();
while (c->idx == c->end && (c != (parent_->cache_ + parent_->hist_.rank() - 1))) {
c->idx = c->begin;
range_iterator::base_reference() -= (c->end - c->begin) * stride;
stride *= c->extent;
++c;
++c->idx;
range_iterator::base_reference() += stride;
}
}
mutable indexed_range* parent_;
friend class boost::iterator_core_access;
friend class indexed_range;
};
indexed_range(Histogram& h, coverage c)
: hist_(h), cover_all_(c == coverage::all), begin_(hist_.begin()), end_(begin_) {
auto ca = cache_;
const auto clast = ca + hist_.rank() - 1;
std::size_t stride = 1;
h.for_each_axis([&, this](const auto& a) {
using opt = axis::traits::static_options<decltype(a)>;
constexpr int under = opt::test(axis::option::underflow);
constexpr int over = opt::test(axis::option::overflow);
const auto size = a.size();
ca->extent = size + under + over;
// -1 if underflow and cover all, else 0
ca->begin = cover_all_ ? -under : 0;
// size + 1 if overflow and cover all, else size
ca->end = cover_all_ ? size + over : size;
ca->idx = ca->begin;
begin_ += (ca->begin + under) * stride;
if (ca < clast)
end_ += (ca->begin + under) * stride;
else
end_ += (ca->end + under) * stride;
stride *= ca->extent;
++ca;
});
}
range_iterator begin() noexcept { return {this, begin_}; }
range_iterator end() noexcept { return {nullptr, end_}; }
private:
Histogram& hist_;
const bool cover_all_;
value_iterator begin_, end_;
mutable cache_item cache_[max_dim::value];
};
/**
Generates a range over the histogram entries.
Use this in a range-based for loop:
```
for (auto x : indexed(hist)) { ... }
```
The iterators dereference to an indexed_range::accessor, which has methods to query the
current indices and bins, and acts like a pointer to the cell value.
@param hist Reference to the histogram.
@param cov Iterate over all or only inner bins (optional, default: inner).
*/
template <typename Histogram>
auto indexed(Histogram&& hist, coverage cov = coverage::inner) {
return indexed_range<std::remove_reference_t<Histogram>>{std::forward<Histogram>(hist),
cov};
}
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,33 @@
// 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_LITERALS_HPP
#define BOOST_HISTOGRAM_LITERALS_HPP
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
constexpr unsigned parse_number(unsigned n) { return n; }
template <class... Rest>
constexpr unsigned parse_number(unsigned n, char f, Rest... rest) {
return parse_number(10u * n + static_cast<unsigned>(f - '0'), rest...);
}
} // namespace detail
namespace literals {
/// Suffix operator to generate literal compile-time numbers, 0_c, 12_c, etc.
template <char... digits>
auto operator"" _c() {
return std::integral_constant<unsigned, detail::parse_number(0, digits...)>();
}
} // namespace literals
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,133 @@
// 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_MAKE_HISTOGRAM_HPP
#define BOOST_HISTOGRAM_MAKE_HISTOGRAM_HPP
/**
\file boost/histogram/make_histogram.hpp
Collection of factory functions to conveniently create histograms.
*/
#include <boost/histogram/accumulators/weighted_sum.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/unlimited_storage.hpp> // = default_storage
#include <boost/mp11/utility.hpp>
#include <tuple>
#include <vector>
namespace boost {
namespace histogram {
/**
Make histogram from compile-time axis configuration and custom storage.
@param storage Storage or container with standard interface (any vector, array, or map).
@param axis First axis instance.
@param axes Other axis instances.
*/
template <class Storage, class Axis, class... Axes, class = detail::requires_axis<Axis>>
auto make_histogram_with(Storage&& storage, Axis&& axis, Axes&&... axes) {
auto a = std::make_tuple(std::forward<Axis>(axis), std::forward<Axes>(axes)...);
using U = detail::remove_cvref_t<Storage>;
using S = mp11::mp_if<detail::is_storage<U>, U, storage_adaptor<U>>;
return histogram<decltype(a), S>(std::move(a), S(std::forward<Storage>(storage)));
}
/**
Make histogram from compile-time axis configuration and default storage.
@param axis First axis instance.
@param axes Other axis instances.
*/
template <class Axis, class... Axes, class = detail::requires_axis<Axis>>
auto make_histogram(Axis&& axis, Axes&&... axes) {
return make_histogram_with(default_storage(), std::forward<Axis>(axis),
std::forward<Axes>(axes)...);
}
/**
Make histogram from compile-time axis configuration and weight-counting storage.
@param axis First axis instance.
@param axes Other axis instances.
*/
template <class Axis, class... Axes, class = detail::requires_axis<Axis>>
auto make_weighted_histogram(Axis&& axis, Axes&&... axes) {
return make_histogram_with(weight_storage(), std::forward<Axis>(axis),
std::forward<Axes>(axes)...);
}
/**
Make histogram from iterable range and custom storage.
@param storage Storage or container with standard interface (any vector, array, or map).
@param iterable Iterable range of axis objects.
*/
template <class Storage, class Iterable,
class = detail::requires_sequence_of_any_axis<Iterable>>
auto make_histogram_with(Storage&& storage, Iterable&& iterable) {
using U = detail::remove_cvref_t<Storage>;
using S = mp11::mp_if<detail::is_storage<U>, U, storage_adaptor<U>>;
using It = detail::remove_cvref_t<Iterable>;
using A = mp11::mp_if<detail::is_indexable_container<It>, It,
std::vector<mp11::mp_first<It>>>;
return histogram<A, S>(std::forward<Iterable>(iterable),
S(std::forward<Storage>(storage)));
}
/**
Make histogram from iterable range and default storage.
@param iterable Iterable range of axis objects.
*/
template <class Iterable, class = detail::requires_sequence_of_any_axis<Iterable>>
auto make_histogram(Iterable&& iterable) {
return make_histogram_with(default_storage(), std::forward<Iterable>(iterable));
}
/**
Make histogram from iterable range and weight-counting storage.
@param iterable Iterable range of axis objects.
*/
template <class Iterable, class = detail::requires_sequence_of_any_axis<Iterable>>
auto make_weighted_histogram(Iterable&& iterable) {
return make_histogram_with(weight_storage(), std::forward<Iterable>(iterable));
}
/**
Make histogram from iterator interval and custom storage.
@param storage Storage or container with standard interface (any vector, array, or map).
@param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects.
*/
template <class Storage, class Iterator, class = detail::requires_iterator<Iterator>>
auto make_histogram_with(Storage&& storage, Iterator begin, Iterator end) {
using T = detail::remove_cvref_t<decltype(*begin)>;
return make_histogram_with(std::forward<Storage>(storage), std::vector<T>(begin, end));
}
/**
Make histogram from iterator interval and default storage.
@param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects.
*/
template <class Iterator, class = detail::requires_iterator<Iterator>>
auto make_histogram(Iterator begin, Iterator end) {
return make_histogram_with(default_storage(), begin, end);
}
/**
Make histogram from iterator interval and weight-counting storage.
@param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects.
*/
template <class Iterator, class = detail::requires_iterator<Iterator>>
auto make_weighted_histogram(Iterator begin, Iterator end) {
return make_histogram_with(weight_storage(), begin, end);
}
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,90 @@
// 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_MAKE_PROFILE_HPP
#define BOOST_HISTOGRAM_MAKE_PROFILE_HPP
#include <boost/histogram/accumulators/mean.hpp>
#include <boost/histogram/accumulators/weighted_mean.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/make_histogram.hpp>
/**
\file boost/histogram/make_profile.hpp
Collection of factory functions to conveniently create profiles.
Profiles are histograms which accept an additional sample and compute the mean of the
sample in each cell.
*/
namespace boost {
namespace histogram {
/**
Make profle from compile-time axis configuration.
@param axis First axis instance.
@param axes Other axis instances.
*/
template <typename Axis, typename... Axes, typename = detail::requires_axis<Axis>>
auto make_profile(Axis&& axis, Axes&&... axes) {
return make_histogram_with(profile_storage(), std::forward<Axis>(axis),
std::forward<Axes>(axes)...);
}
/**
Make profle from compile-time axis configuration which accepts weights.
@param axis First axis instance.
@param axes Other axis instances.
*/
template <typename Axis, typename... Axes, typename = detail::requires_axis<Axis>>
auto make_weighted_profile(Axis&& axis, Axes&&... axes) {
return make_histogram_with(weighted_profile_storage(), std::forward<Axis>(axis),
std::forward<Axes>(axes)...);
}
/**
Make profile from iterable range.
@param iterable Iterable range of axis objects.
*/
template <typename Iterable, typename = detail::requires_sequence_of_any_axis<Iterable>>
auto make_profile(Iterable&& iterable) {
return make_histogram_with(profile_storage(), std::forward<Iterable>(iterable));
}
/**
Make profile from iterable range which accepts weights.
@param iterable Iterable range of axis objects.
*/
template <typename Iterable, typename = detail::requires_sequence_of_any_axis<Iterable>>
auto make_weighted_profile(Iterable&& iterable) {
return make_histogram_with(weighted_profile_storage(),
std::forward<Iterable>(iterable));
}
/**
Make profile from iterator interval.
@param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects.
*/
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
auto make_profile(Iterator begin, Iterator end) {
return make_histogram_with(profile_storage(), begin, end);
}
/**
Make profile from iterator interval which accepts weights.
@param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects.
*/
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
auto make_weighted_profile(Iterator begin, Iterator end) {
return make_histogram_with(weighted_profile_storage(), begin, end);
}
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,39 @@
// 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_OSTREAM_HPP
#define BOOST_HISTOGRAM_OSTREAM_HPP
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/axis/ostream.hpp>
#include <boost/histogram/fwd.hpp>
#include <iosfwd>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace boost {
namespace histogram {
template <typename CharT, typename Traits, typename A, typename S>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const histogram<A, S>& h) {
os << "histogram(";
unsigned n = 0;
h.for_each_axis([&](const auto& a) {
if (h.rank() > 1) os << "\n ";
os << a;
if (++n < h.rank()) os << ",";
});
os << (h.rank() > 1 ? "\n)" : ")");
return os;
}
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
#endif

View File

@@ -0,0 +1,214 @@
// 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_SERIALIZATION_HPP
#define BOOST_HISTOGRAM_SERIALIZATION_HPP
#include <boost/assert.hpp>
#include <boost/histogram/accumulators/mean.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/accumulators/weighted_mean.hpp>
#include <boost/histogram/accumulators/weighted_sum.hpp>
#include <boost/histogram/axis/category.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/axis/variable.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/histogram.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/unlimited_storage.hpp>
#include <boost/histogram/unsafe_access.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/variant.hpp>
#include <boost/serialization/vector.hpp>
#include <tuple>
#include <type_traits>
/**
\file boost/histogram/serialization.hpp
Implemenations of the serialization functions using
[Boost.Serialization](https://www.boost.org/doc/libs/develop/libs/serialization/doc/index.html).
*/
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace std {
template <class Archive, class... Ts>
void serialize(Archive& ar, tuple<Ts...>& t, unsigned /* version */) {
::boost::mp11::tuple_for_each(
t, [&ar](auto& x) { ar& boost::serialization::make_nvp("item", x); });
}
} // namespace std
namespace boost {
namespace histogram {
namespace accumulators {
template <class RealType>
template <class Archive>
void sum<RealType>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("large", large_);
ar& serialization::make_nvp("small", small_);
}
template <class RealType>
template <class Archive>
void weighted_sum<RealType>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("sum_of_weights", sum_of_weights_);
ar& serialization::make_nvp("sum_of_weights_squared", sum_of_weights_squared_);
}
template <class RealType>
template <class Archive>
void mean<RealType>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("sum", sum_);
ar& serialization::make_nvp("mean", mean_);
ar& serialization::make_nvp("sum_of_deltas_squared", sum_of_deltas_squared_);
}
template <class RealType>
template <class Archive>
void weighted_mean<RealType>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("sum_of_weights", sum_of_weights_);
ar& serialization::make_nvp("sum_of_weights_squared", sum_of_weights_squared_);
ar& serialization::make_nvp("weighted_mean", weighted_mean_);
ar& serialization::make_nvp("sum_of_weighted_deltas_squared",
sum_of_weighted_deltas_squared_);
}
} // namespace accumulators
namespace axis {
namespace transform {
template <class Archive>
void serialize(Archive&, id&, unsigned /* version */) {}
template <class Archive>
void serialize(Archive&, log&, unsigned /* version */) {}
template <class Archive>
void serialize(Archive&, sqrt&, unsigned /* version */) {}
template <class Archive>
void serialize(Archive& ar, pow& t, unsigned /* version */) {
ar& serialization::make_nvp("power", t.power);
}
} // namespace transform
template <class Archive>
void serialize(Archive&, null_type&, unsigned /* version */) {}
template <class T, class Tr, class M, class O>
template <class Archive>
void regular<T, Tr, M, O>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("transform", static_cast<transform_type&>(*this));
ar& serialization::make_nvp("size", size_meta_.first());
ar& serialization::make_nvp("meta", size_meta_.second());
ar& serialization::make_nvp("min", min_);
ar& serialization::make_nvp("delta", delta_);
}
template <class T, class M, class O>
template <class Archive>
void integer<T, M, O>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("size", size_meta_.first());
ar& serialization::make_nvp("meta", size_meta_.second());
ar& serialization::make_nvp("min", min_);
}
template <class T, class M, class O, class A>
template <class Archive>
void variable<T, M, O, A>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("seq", vec_meta_.first());
ar& serialization::make_nvp("meta", vec_meta_.second());
}
template <class T, class M, class O, class A>
template <class Archive>
void category<T, M, O, A>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("seq", vec_meta_.first());
ar& serialization::make_nvp("meta", vec_meta_.second());
}
template <class... Ts>
template <class Archive>
void variant<Ts...>::serialize(Archive& ar, unsigned /* version */) {
ar& serialization::make_nvp("variant", impl);
}
} // namespace axis
namespace detail {
template <class Archive, class T>
void serialize(Archive& ar, vector_impl<T>& impl, unsigned /* version */) {
ar& serialization::make_nvp("vector", static_cast<T&>(impl));
}
template <class Archive, class T>
void serialize(Archive& ar, array_impl<T>& impl, unsigned /* version */) {
ar& serialization::make_nvp("size", impl.size_);
ar& serialization::make_nvp("array",
serialization::make_array(&impl.front(), impl.size_));
}
template <class Archive, class T>
void serialize(Archive& ar, map_impl<T>& impl, unsigned /* version */) {
ar& serialization::make_nvp("size", impl.size_);
ar& serialization::make_nvp("map", static_cast<T&>(impl));
}
template <class Archive, class Allocator>
void serialize(Archive& ar, mp_int<Allocator>& x, unsigned /* version */) {
ar& serialization::make_nvp("data", x.data);
}
} // namespace detail
template <class Archive, class T>
void serialize(Archive& ar, storage_adaptor<T>& s, unsigned /* version */) {
ar& serialization::make_nvp("impl", static_cast<detail::storage_adaptor_impl<T>&>(s));
}
template <class A>
template <class Archive>
void unlimited_storage<A>::serialize(Archive& ar, unsigned /* version */) {
if (Archive::is_loading::value) {
buffer_type dummy(buffer.alloc);
std::size_t size;
ar& serialization::make_nvp("type", dummy.type);
ar& serialization::make_nvp("size", size);
dummy.apply([this, size](auto* tp) {
BOOST_ASSERT(tp == nullptr);
using T = detail::remove_cvref_t<decltype(*tp)>;
buffer.template make<T>(size);
});
} else {
ar& serialization::make_nvp("type", buffer.type);
ar& serialization::make_nvp("size", buffer.size);
}
buffer.apply([this, &ar](auto* tp) {
using T = detail::remove_cvref_t<decltype(*tp)>;
ar& serialization::make_nvp(
"buffer",
serialization::make_array(reinterpret_cast<T*>(buffer.ptr), buffer.size));
});
}
template <class Archive, class A, class S>
void serialize(Archive& ar, histogram<A, S>& h, unsigned /* version */) {
ar& serialization::make_nvp("axes", unsafe_access::axes(h));
ar& serialization::make_nvp("storage", unsafe_access::storage(h));
}
} // namespace histogram
} // namespace boost
#endif
#endif

View File

@@ -0,0 +1,298 @@
// 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 <boost/assert.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
struct vector_impl : T {
vector_impl() = default;
explicit vector_impl(const T& t) : T(t) {}
explicit vector_impl(typename T::allocator_type a) : T(a) {}
template <class U, class = requires_iterable<U>>
explicit vector_impl(const U& u) {
T::reserve(u.size());
for (auto&& x : u) T::emplace_back(x);
}
template <class U, class = requires_iterable<U>>
vector_impl& operator=(const U& u) {
T::resize(u.size());
auto it = T::begin();
for (auto&& x : u) *it++ = x;
return *this;
}
void reset(std::size_t n) {
using value_type = typename T::value_type;
const auto old_size = T::size();
T::resize(n, value_type());
std::fill_n(T::begin(), std::min(n, old_size), value_type());
}
};
template <class T>
struct array_impl : T {
array_impl() = default;
explicit array_impl(const T& t) : T(t) {}
template <class U, class = requires_iterable<U>>
explicit array_impl(const U& u) : size_(u.size()) {
std::size_t i = 0;
for (auto&& x : u) T::operator[](i++) = x;
}
template <class U, class = requires_iterable<U>>
array_impl& operator=(const U& u) {
size_ = u.size();
if (size_ > T::max_size()) // for std::array
BOOST_THROW_EXCEPTION(std::length_error(
detail::cat("size ", size_, " exceeds maximum capacity ", T::max_size())));
auto it = T::begin();
for (auto&& x : u) *it++ = x;
return *this;
}
void reset(std::size_t n) {
using value_type = typename T::value_type;
if (n > T::max_size()) // for std::array
BOOST_THROW_EXCEPTION(std::length_error(
detail::cat("size ", n, " exceeds maximum capacity ", T::max_size())));
std::fill_n(T::begin(), n, value_type());
size_ = n;
}
typename T::iterator end() noexcept { return T::begin() + size_; }
typename T::const_iterator end() const noexcept { return T::begin() + size_; }
std::size_t size() const noexcept { return size_; }
std::size_t size_ = 0;
};
template <class T>
struct map_impl : T {
static_assert(std::is_same<typename T::key_type, std::size_t>::value,
"requires std::size_t as key_type");
using value_type = typename T::mapped_type;
using const_reference = const value_type&;
struct reference {
reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
reference(const reference&) noexcept = default;
reference operator=(reference o) {
if (this != &o) operator=(static_cast<const_reference>(o));
return *this;
}
operator const_reference() const noexcept {
return static_cast<const map_impl*>(map)->operator[](idx);
}
template <class U, class = requires_convertible<U, value_type>>
reference& operator=(const U& u) {
auto it = map->find(idx);
if (u == value_type()) {
if (it != static_cast<T*>(map)->end()) map->erase(it);
} else {
if (it != static_cast<T*>(map)->end())
it->second = u;
else {
map->emplace(idx, u);
}
}
return *this;
}
template <class U, class V = value_type,
class = std::enable_if_t<has_operator_radd<V, U>::value>>
reference operator+=(const U& u) {
auto it = map->find(idx);
if (it != static_cast<T*>(map)->end())
it->second += u;
else
map->emplace(idx, u);
return *this;
}
template <class U, class V = value_type,
class = std::enable_if_t<has_operator_rsub<V, U>::value>>
reference operator-=(const U& u) {
auto it = map->find(idx);
if (it != static_cast<T*>(map)->end())
it->second -= u;
else
map->emplace(idx, -u);
return *this;
}
template <class U, class V = value_type,
class = std::enable_if_t<has_operator_rmul<V, U>::value>>
reference operator*=(const U& u) {
auto it = map->find(idx);
if (it != static_cast<T*>(map)->end()) it->second *= u;
return *this;
}
template <class U, class V = value_type,
class = std::enable_if_t<has_operator_rdiv<V, U>::value>>
reference operator/=(const U& u) {
auto it = map->find(idx);
if (it != static_cast<T*>(map)->end())
it->second /= u;
else if (!(value_type{} / u == value_type{}))
map->emplace(idx, value_type{} / u);
return *this;
}
template <class V = value_type,
class = std::enable_if_t<has_operator_preincrement<V>::value>>
reference operator++() {
auto it = map->find(idx);
if (it != static_cast<T*>(map)->end())
++it->second;
else
map->emplace(idx, 1);
return *this;
}
template <class V = value_type,
class = std::enable_if_t<has_operator_preincrement<V>::value>>
value_type operator++(int) {
const value_type tmp = operator const_reference();
operator++();
return tmp;
}
template <class... Ts>
decltype(auto) operator()(Ts&&... args) {
return map->operator[](idx)(std::forward<Ts>(args)...);
}
map_impl* map;
std::size_t idx;
};
template <class Value, class Reference, class MapPtr>
struct iterator_t
: boost::iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Value,
std::random_access_iterator_tag, Reference,
std::ptrdiff_t> {
iterator_t() = default;
template <class V, class R, class M, class = requires_convertible<M, MapPtr>>
iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {}
iterator_t(MapPtr m, std::size_t i) noexcept
: iterator_t::iterator_adaptor_(i), map_(m) {}
template <class V, class R, class M>
bool equal(const iterator_t<V, R, M>& rhs) const noexcept {
return map_ == rhs.map_ && iterator_t::base() == rhs.base();
}
decltype(auto) dereference() const { return (*map_)[iterator_t::base()]; }
MapPtr map_ = nullptr;
};
using iterator = iterator_t<value_type, reference, map_impl*>;
using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>;
map_impl() = default;
explicit map_impl(const T& t) : T(t) {}
explicit map_impl(typename T::allocator_type a) : T(a) {}
template <class U, class = requires_iterable<U>>
explicit map_impl(const U& u) : size_(u.size()) {
using std::begin;
using std::end;
std::copy(begin(u), end(u), this->begin());
}
template <class U, class = requires_iterable<U>>
map_impl& operator=(const U& u) {
if (u.size() < size_)
reset(u.size());
else
size_ = u.size();
using std::begin;
using std::end;
std::copy(begin(u), end(u), this->begin());
return *this;
}
void reset(std::size_t n) {
T::clear();
size_ = n;
}
reference operator[](std::size_t i) noexcept { return {this, i}; }
const_reference operator[](std::size_t i) const noexcept {
auto it = T::find(i);
static const value_type null = value_type{};
if (it == T::end()) return null;
return it->second;
}
iterator begin() noexcept { return {this, 0}; }
iterator end() noexcept { return {this, size_}; }
const_iterator begin() const noexcept { return {this, 0}; }
const_iterator end() const noexcept { return {this, size_}; }
std::size_t size() const noexcept { return size_; }
std::size_t size_ = 0;
};
template <typename T>
struct ERROR_type_passed_to_storage_adaptor_not_recognized;
template <typename T>
using storage_adaptor_impl = mp11::mp_if<
is_vector_like<T>, vector_impl<T>,
mp11::mp_if<is_array_like<T>, array_impl<T>,
mp11::mp_if<is_map_like<T>, map_impl<T>,
ERROR_type_passed_to_storage_adaptor_not_recognized<T>>>>;
} // namespace detail
/// Turns any vector-like array-like, and map-like container into a storage type.
template <typename T>
class storage_adaptor : public detail::storage_adaptor_impl<T> {
using base_type = detail::storage_adaptor_impl<T>;
public:
using base_type::base_type;
using base_type::operator=;
template <class U, class = detail::requires_iterable<U>>
bool operator==(const U& u) const {
using std::begin;
using std::end;
return std::equal(this->begin(), this->end(), begin(u), end(u));
}
};
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,923 @@
// 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_UNLIMTED_STORAGE_HPP
#define BOOST_HISTOGRAM_UNLIMTED_STORAGE_HPP
#include <algorithm>
#include <boost/assert.hpp>
#include <boost/config/workaround.hpp>
#include <boost/cstdint.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <cmath>
#include <functional>
#include <limits>
#include <memory>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
// version of std::equal_to<> which handles comparison of signed and unsigned
struct equal {
template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept {
return impl(std::is_signed<T>{}, std::is_signed<U>{}, t, u);
}
template <class T, class U>
bool impl(std::false_type, std::false_type, const T& t, const U& u) const noexcept {
return t == u;
}
template <class T, class U>
bool impl(std::false_type, std::true_type, const T& t, const U& u) const noexcept {
return u >= 0 && t == make_unsigned(u);
}
template <class T, class U>
bool impl(std::true_type, std::false_type, const T& t, const U& u) const noexcept {
return t >= 0 && make_unsigned(t) == u;
}
template <class T, class U>
bool impl(std::true_type, std::true_type, const T& t, const U& u) const noexcept {
return t == u;
}
};
// version of std::less<> which handles comparison of signed and unsigned
struct less {
template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept {
return impl(std::is_signed<T>{}, std::is_signed<U>{}, t, u);
}
template <class T, class U>
bool impl(std::false_type, std::false_type, const T& t, const U& u) const noexcept {
return t < u;
}
template <class T, class U>
bool impl(std::false_type, std::true_type, const T& t, const U& u) const noexcept {
return u >= 0 && t < make_unsigned(u);
}
template <class T, class U>
bool impl(std::true_type, std::false_type, const T& t, const U& u) const noexcept {
return t < 0 || make_unsigned(t) < u;
}
template <class T, class U>
bool impl(std::true_type, std::true_type, const T& t, const U& u) const noexcept {
return t < u;
}
};
// version of std::greater<> which handles comparison of signed and unsigned
struct greater {
template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept {
return impl(std::is_signed<T>{}, std::is_signed<U>{}, t, u);
}
template <class T, class U>
bool impl(std::false_type, std::false_type, const T& t, const U& u) const noexcept {
return t > u;
}
template <class T, class U>
bool impl(std::false_type, std::true_type, const T& t, const U& u) const noexcept {
return u < 0 || t > make_unsigned(u);
}
template <class T, class U>
bool impl(std::true_type, std::false_type, const T& t, const U& u) const noexcept {
return t >= 0 && make_unsigned(t) > u;
}
template <class T, class U>
bool impl(std::true_type, std::true_type, const T& t, const U& u) const noexcept {
return t > u;
}
};
template <class Allocator>
struct mp_int;
template <class T>
struct is_unsigned_integral : mp11::mp_and<std::is_integral<T>, std::is_unsigned<T>> {};
template <class T>
bool safe_increment(T& t) {
if (t < std::numeric_limits<T>::max()) {
++t;
return true;
}
return false;
}
template <class T, class U>
bool safe_radd(T& t, const U& u) {
static_assert(is_unsigned_integral<T>::value, "T must be unsigned integral type");
static_assert(is_unsigned_integral<U>::value, "T must be unsigned integral type");
if (static_cast<T>(std::numeric_limits<T>::max() - t) >= u) {
t += static_cast<T>(u); // static_cast to suppress conversion warning
return true;
}
return false;
}
// use boost.multiprecision.cpp_int in your own code, it is much more sophisticated
// than this implementation; we use it here to reduce coupling between boost libs
template <class Allocator>
struct mp_int {
explicit mp_int(Allocator a = {}) : data(1, 0, std::move(a)) {}
explicit mp_int(uint64_t v, Allocator a = {}) : data(1, v, std::move(a)) {}
mp_int(const mp_int&) = default;
mp_int& operator=(const mp_int&) = default;
mp_int(mp_int&&) = default;
mp_int& operator=(mp_int&&) = default;
mp_int& operator=(uint64_t o) {
data = decltype(data)(1, o);
return *this;
}
mp_int& operator++() {
BOOST_ASSERT(data.size() > 0u);
std::size_t i = 0;
while (!safe_increment(data[i])) {
data[i] = 0;
++i;
if (i == data.size()) {
data.push_back(1);
break;
}
}
return *this;
}
mp_int& operator+=(const mp_int& o) {
if (this == &o) {
auto tmp{o};
return operator+=(tmp);
}
bool carry = false;
std::size_t i = 0;
for (uint64_t oi : o.data) {
auto& di = maybe_extend(i);
if (carry) {
if (safe_increment(oi))
carry = false;
else {
++i;
continue;
}
}
if (!safe_radd(di, oi)) {
add_remainder(di, oi);
carry = true;
}
++i;
}
while (carry) {
auto& di = maybe_extend(i);
if (safe_increment(di)) break;
di = 0;
++i;
}
return *this;
}
mp_int& operator+=(uint64_t o) {
BOOST_ASSERT(data.size() > 0u);
if (safe_radd(data[0], o)) return *this;
add_remainder(data[0], o);
// carry the one, data may grow several times
std::size_t i = 1;
while (true) {
auto& di = maybe_extend(i);
if (safe_increment(di)) break;
di = 0;
++i;
}
return *this;
}
operator double() const noexcept {
BOOST_ASSERT(data.size() > 0u);
double result = static_cast<double>(data[0]);
std::size_t i = 0;
while (++i < data.size())
result += static_cast<double>(data[i]) * std::pow(2.0, i * 64);
return result;
}
// total ordering for mp_int, mp_int
bool operator<(const mp_int& o) const noexcept {
BOOST_ASSERT(data.size() > 0u);
BOOST_ASSERT(o.data.size() > 0u);
// no leading zeros allowed
BOOST_ASSERT(data.size() == 1 || data.back() > 0u);
BOOST_ASSERT(o.data.size() == 1 || o.data.back() > 0u);
if (data.size() < o.data.size()) return true;
if (data.size() > o.data.size()) return false;
auto s = data.size();
while (s > 0u) {
--s;
if (data[s] < o.data[s]) return true;
if (data[s] > o.data[s]) return false;
}
return false; // args are equal
}
bool operator==(const mp_int& o) const noexcept {
BOOST_ASSERT(data.size() > 0u);
BOOST_ASSERT(o.data.size() > 0u);
// no leading zeros allowed
BOOST_ASSERT(data.size() == 1 || data.back() > 0u);
BOOST_ASSERT(o.data.size() == 1 || o.data.back() > 0u);
if (data.size() != o.data.size()) return false;
return std::equal(data.begin(), data.end(), o.data.begin());
}
// copied from boost/operators.hpp
friend bool operator>(const mp_int& x, const mp_int& y) { return y < x; }
friend bool operator<=(const mp_int& x, const mp_int& y) { return !(y < x); }
friend bool operator>=(const mp_int& x, const mp_int& y) { return !(x < y); }
friend bool operator!=(const mp_int& x, const mp_int& y) { return !(x == y); }
// total ordering for mp_int, uint64; partial ordering for mp_int, double
template <class U>
bool operator<(const U& o) const noexcept {
BOOST_ASSERT(data.size() > 0u);
return static_if<is_unsigned_integral<U>>(
[this](uint64_t o) { return data.size() == 1 && data[0] < o; },
[this](double o) { return operator double() < o; }, o);
}
template <class U>
bool operator>(const U& o) const noexcept {
BOOST_ASSERT(data.size() > 0u);
BOOST_ASSERT(data.back() > 0u); // no leading zeros allowed
return static_if<is_unsigned_integral<U>>(
[this](uint64_t o) { return data.size() > 1 || data[0] > o; },
[this](double o) { return operator double() > o; }, o);
}
template <class U>
bool operator==(const U& o) const noexcept {
BOOST_ASSERT(data.size() > 0u);
return static_if<is_unsigned_integral<U>>(
[this](uint64_t o) { return data.size() == 1 && data[0] == o; },
[this](double o) { return operator double() == o; }, o);
}
// adapted copy from boost/operators.hpp
template <class U>
friend bool operator<=(const mp_int& x, const U& y) {
if (is_unsigned_integral<U>::value) return !(x > y);
return (x < y) || (x == y);
}
template <class U>
friend bool operator>=(const mp_int& x, const U& y) {
if (is_unsigned_integral<U>::value) return !(x < y);
return (x > y) || (x == y);
}
template <class U>
friend bool operator>(const U& x, const mp_int& y) {
if (is_unsigned_integral<U>::value) return y < x;
return y < x;
}
template <class U>
friend bool operator<(const U& x, const mp_int& y) {
if (is_unsigned_integral<U>::value) return y > x;
return y > x;
}
template <class U>
friend bool operator<=(const U& x, const mp_int& y) {
if (is_unsigned_integral<U>::value) return !(y < x);
return (y > x) || (y == x);
}
template <class U>
friend bool operator>=(const U& x, const mp_int& y) {
if (is_unsigned_integral<U>::value) return !(y > x);
return (y < x) || (y == x);
}
template <class U>
friend bool operator==(const U& y, const mp_int& x) {
return x == y;
}
template <class U>
friend bool operator!=(const U& y, const mp_int& x) {
return !(x == y);
}
template <class U>
friend bool operator!=(const mp_int& y, const U& x) {
return !(y == x);
}
uint64_t& maybe_extend(std::size_t i) {
while (i >= data.size()) data.push_back(0);
return data[i];
}
static void add_remainder(uint64_t& d, const uint64_t o) noexcept {
BOOST_ASSERT(d > 0u);
// in decimal system it would look like this:
// 8 + 8 = 6 = 8 - (9 - 8) - 1
// 9 + 1 = 0 = 9 - (9 - 1) - 1
auto tmp = std::numeric_limits<uint64_t>::max();
tmp -= o;
--d -= tmp;
}
std::vector<uint64_t, Allocator> data;
};
template <class Allocator>
auto create_buffer(Allocator& a, std::size_t n) {
using AT = std::allocator_traits<Allocator>;
auto ptr = AT::allocate(a, n); // may throw
static_assert(std::is_trivially_copyable<decltype(ptr)>::value,
"ptr must be trivially copyable");
auto it = ptr;
const auto end = ptr + n;
try {
// this loop may throw
while (it != end) AT::construct(a, it++, typename AT::value_type{});
} catch (...) {
// release resources that were already acquired before rethrowing
while (it != ptr) AT::destroy(a, --it);
AT::deallocate(a, ptr, n);
throw;
}
return ptr;
}
template <class Allocator, class Iterator>
auto create_buffer(Allocator& a, std::size_t n, Iterator iter) {
BOOST_ASSERT(n > 0u);
using AT = std::allocator_traits<Allocator>;
auto ptr = AT::allocate(a, n); // may throw
static_assert(std::is_trivially_copyable<decltype(ptr)>::value,
"ptr must be trivially copyable");
auto it = ptr;
const auto end = ptr + n;
try {
// this loop may throw
while (it != end) AT::construct(a, it++, *iter++);
} catch (...) {
// release resources that were already acquired before rethrowing
while (it != ptr) AT::destroy(a, --it);
AT::deallocate(a, ptr, n);
throw;
}
return ptr;
}
template <class Allocator>
void destroy_buffer(Allocator& a, typename std::allocator_traits<Allocator>::pointer p,
std::size_t n) {
BOOST_ASSERT(p);
BOOST_ASSERT(n > 0u);
using AT = std::allocator_traits<Allocator>;
auto it = p + n;
while (it != p) AT::destroy(a, --it);
AT::deallocate(a, p, n);
}
} // namespace detail
/**
Memory-efficient storage for integral counters which cannot overflow.
This storage provides a no-overflow-guarantee if it is filled with integral weights
only. This storage implementation keeps a contiguous array of elemental counters, one
for each cell. If an operation is requested, which would overflow a counter, the whole
array is replaced with another of a wider integral type, then the operation is executed.
The storage uses integers of 8, 16, 32, 64 bits, and then switches to a multiprecision
integral type, similar to those in
[Boost.Multiprecision](https://www.boost.org/doc/libs/develop/libs/multiprecision/doc/html/index.html).
A scaling operation or adding a floating point number turns the elements into doubles,
which voids the no-overflow-guarantee.
*/
template <class Allocator>
class unlimited_storage {
static_assert(
std::is_same<typename std::allocator_traits<Allocator>::pointer,
typename std::allocator_traits<Allocator>::value_type*>::value,
"unlimited_storage requires allocator with trivial pointer type");
public:
using allocator_type = Allocator;
using value_type = double;
using mp_int = detail::mp_int<
typename std::allocator_traits<allocator_type>::template rebind_alloc<uint64_t>>;
private:
using types = mp11::mp_list<uint8_t, uint16_t, uint32_t, uint64_t, mp_int, double>;
template <class T>
static constexpr char type_index() noexcept {
return static_cast<char>(mp11::mp_find<types, T>::value);
}
struct buffer_type {
allocator_type alloc;
std::size_t size = 0;
char type = 0;
void* ptr = nullptr;
template <class F, class... Ts>
decltype(auto) apply(F&& f, Ts&&... ts) const {
// this is intentionally not a switch, the if-chain is faster in benchmarks
if (type == type_index<uint8_t>())
return f(static_cast<uint8_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<uint16_t>())
return f(static_cast<uint16_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<uint32_t>())
return f(static_cast<uint32_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<uint64_t>())
return f(static_cast<uint64_t*>(ptr), std::forward<Ts>(ts)...);
if (type == type_index<mp_int>())
return f(static_cast<mp_int*>(ptr), std::forward<Ts>(ts)...);
return f(static_cast<double*>(ptr), std::forward<Ts>(ts)...);
}
buffer_type(allocator_type a = {}) : alloc(std::move(a)) {}
buffer_type(buffer_type&& o) noexcept
: alloc(std::move(o.alloc)), size(o.size), type(o.type), ptr(o.ptr) {
o.size = 0;
o.type = 0;
o.ptr = nullptr;
}
buffer_type& operator=(buffer_type&& o) noexcept {
if (this != &o) {
using std::swap;
swap(alloc, o.alloc);
swap(size, o.size);
swap(type, o.type);
swap(ptr, o.ptr);
}
return *this;
}
buffer_type(const buffer_type& o) : alloc(o.alloc) {
o.apply([this, &o](auto* otp) {
using T = detail::remove_cvref_t<decltype(*otp)>;
this->template make<T>(o.size, otp);
});
}
buffer_type& operator=(const buffer_type& o) {
*this = buffer_type(o);
return *this;
}
~buffer_type() noexcept { destroy(); }
void destroy() noexcept {
BOOST_ASSERT((ptr == nullptr) == (size == 0));
if (ptr == nullptr) return;
apply([this](auto* tp) {
using T = detail::remove_cvref_t<decltype(*tp)>;
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(alloc); // rebind allocator
detail::destroy_buffer(a, tp, size);
});
size = 0;
type = 0;
ptr = nullptr;
}
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4244) // possible loss of data
#endif
template <class T>
void make(std::size_t n) {
// note: order of commands is to not leave buffer in invalid state upon throw
destroy();
if (n > 0) {
// rebind allocator
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(alloc);
ptr = detail::create_buffer(a, n); // may throw
}
size = n;
type = type_index<T>();
}
template <class T, class U>
void make(std::size_t n, U iter) {
// note: iter may be current ptr, so create new buffer before deleting old buffer
T* new_ptr = nullptr;
const auto new_type = type_index<T>();
if (n > 0) {
// rebind allocator
using alloc_type =
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
alloc_type a(alloc);
new_ptr = detail::create_buffer(a, n, iter); // may throw
}
destroy();
size = n;
type = new_type;
ptr = new_ptr;
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
};
template <class Buffer>
class reference_t {
public:
reference_t(Buffer* b, std::size_t i) : buffer_(b), idx_(i) {}
reference_t(const reference_t&) = default;
reference_t& operator=(const reference_t&) = delete; // references do not rebind
reference_t& operator=(reference_t&&) = delete; // references do not rebind
// minimal operators for partial ordering
bool operator<(reference_t rhs) const { return op<detail::less>(rhs); }
bool operator>(reference_t rhs) const { return op<detail::greater>(rhs); }
bool operator==(reference_t rhs) const { return op<detail::equal>(rhs); }
// adapted copy from boost/operators.hpp for partial ordering
friend bool operator<=(reference_t x, reference_t y) { return !(y < x); }
friend bool operator>=(reference_t x, reference_t y) { return !(y > x); }
friend bool operator!=(reference_t y, reference_t x) { return !(x == y); }
template <class U>
bool operator<(const U& rhs) const {
return op<detail::less>(rhs);
}
template <class U>
bool operator>(const U& rhs) const {
return op<detail::greater>(rhs);
}
template <class U>
bool operator==(const U& rhs) const {
return op<detail::equal>(rhs);
}
// adapted copy from boost/operators.hpp
template <class U>
friend bool operator<=(reference_t x, const U& y) {
if (detail::is_unsigned_integral<U>::value) return !(x > y);
return (x < y) || (x == y);
}
template <class U>
friend bool operator>=(reference_t x, const U& y) {
if (detail::is_unsigned_integral<U>::value) return !(x < y);
return (x > y) || (x == y);
}
template <class U>
friend bool operator>(const U& x, reference_t y) {
return y < x;
}
template <class U>
friend bool operator<(const U& x, reference_t y) {
return y > x;
}
template <class U>
friend bool operator<=(const U& x, reference_t y) {
if (detail::is_unsigned_integral<U>::value) return !(y < x);
return (y > x) || (y == x);
}
template <class U>
friend bool operator>=(const U& x, reference_t y) {
if (detail::is_unsigned_integral<U>::value) return !(y > x);
return (y < x) || (y == x);
}
template <class U>
friend bool operator==(const U& y, reference_t x) {
return x == y;
}
template <class U>
friend bool operator!=(const U& y, reference_t x) {
return !(x == y);
}
template <class U>
friend bool operator!=(reference_t y, const U& x) {
return !(y == x);
}
operator double() const {
return buffer_->apply(
[this](const auto* tp) { return static_cast<double>(tp[idx_]); });
}
protected:
template <class Binary, class U>
bool op(const reference_t<U>& rhs) const {
const auto i = idx_;
const auto j = rhs.idx_;
return buffer_->apply([i, j, &rhs](const auto* ptr) {
const auto& pi = ptr[i];
return rhs.buffer_->apply([&pi, j](const auto* q) { return Binary()(pi, q[j]); });
});
}
template <class Binary, class U>
bool op(const U& rhs) const {
const auto i = idx_;
return buffer_->apply([i, &rhs](const auto* tp) { return Binary()(tp[i], rhs); });
}
template <class U>
friend class reference_t;
Buffer* buffer_;
std::size_t idx_;
};
public:
using const_reference = reference_t<const buffer_type>;
class reference : public reference_t<buffer_type> {
using base_type = reference_t<buffer_type>;
public:
using base_type::base_type;
reference operator=(reference t) {
t.buffer_->apply([this, &t](const auto* otp) { *this = otp[t.idx_]; });
return *this;
}
reference operator=(const_reference t) {
t.buffer_->apply([this, &t](const auto* otp) { *this = otp[t.idx_]; });
return *this;
}
template <class U>
reference operator=(const U& t) {
base_type::buffer_->apply([this, &t](auto* tp) {
tp[this->idx_] = 0;
adder()(tp, *(this->buffer_), this->idx_, t);
});
return *this;
}
template <class U>
reference operator+=(const U& t) {
base_type::buffer_->apply(adder(), *base_type::buffer_, base_type::idx_, t);
return *this;
}
template <class U>
reference operator*=(const U& t) {
base_type::buffer_->apply(multiplier(), *base_type::buffer_, base_type::idx_, t);
return *this;
}
template <class U>
reference operator-=(const U& t) {
return operator+=(-t);
}
template <class U>
reference operator/=(const U& t) {
return operator*=(1.0 / static_cast<double>(t));
}
reference operator++() {
base_type::buffer_->apply(incrementor(), *base_type::buffer_, base_type::idx_);
return *this;
}
// minimal operators for partial ordering
bool operator<(reference rhs) const { return base_type::operator<(rhs); }
bool operator>(reference rhs) const { return base_type::operator>(rhs); }
bool operator==(reference rhs) const { return base_type::operator==(rhs); }
// adapted copy from boost/operators.hpp for partial ordering
friend bool operator<=(reference x, reference y) { return !(y < x); }
friend bool operator>=(reference x, reference y) { return !(y > x); }
friend bool operator!=(reference y, reference x) { return !(x == y); }
};
private:
template <class Value, class Reference, class Buffer>
class iterator_t
: public boost::iterator_adaptor<iterator_t<Value, Reference, Buffer>, std::size_t,
Value, std::random_access_iterator_tag, Reference,
std::ptrdiff_t> {
public:
iterator_t() = default;
template <class V, class R, class B>
iterator_t(const iterator_t<V, R, B>& it)
: iterator_t::iterator_adaptor_(it.base()), buffer_(it.buffer_) {}
iterator_t(Buffer* b, std::size_t i) noexcept
: iterator_t::iterator_adaptor_(i), buffer_(b) {}
protected:
template <class V, class R, class B>
bool equal(const iterator_t<V, R, B>& rhs) const noexcept {
return buffer_ == rhs.buffer_ && this->base() == rhs.base();
}
Reference dereference() const { return {buffer_, this->base()}; }
friend class ::boost::iterator_core_access;
template <class V, class R, class B>
friend class iterator_t;
private:
Buffer* buffer_ = nullptr;
};
public:
using const_iterator = iterator_t<const value_type, const_reference, const buffer_type>;
using iterator = iterator_t<value_type, reference, buffer_type>;
explicit unlimited_storage(allocator_type a = {}) : buffer(std::move(a)) {}
unlimited_storage(const unlimited_storage&) = default;
unlimited_storage& operator=(const unlimited_storage&) = default;
unlimited_storage(unlimited_storage&&) = default;
unlimited_storage& operator=(unlimited_storage&&) = default;
template <class T>
unlimited_storage(const storage_adaptor<T>& s) {
using V = detail::remove_cvref_t<decltype(s[0])>;
constexpr auto ti = type_index<V>();
detail::static_if_c<(ti < mp11::mp_size<types>::value)>(
[&](auto) { buffer.template make<V>(s.size(), s.begin()); },
[&](auto) { buffer.template make<double>(s.size(), s.begin()); }, 0);
}
template <class Iterable, class = detail::requires_iterable<Iterable>>
unlimited_storage& operator=(const Iterable& s) {
*this = unlimited_storage(s);
return *this;
}
allocator_type get_allocator() const { return buffer.alloc; }
void reset(std::size_t s) { buffer.template make<uint8_t>(s); }
std::size_t size() const noexcept { return buffer.size; }
reference operator[](std::size_t i) noexcept { return {&buffer, i}; }
const_reference operator[](std::size_t i) const noexcept { return {&buffer, i}; }
bool operator==(const unlimited_storage& o) const noexcept {
if (size() != o.size()) return false;
return buffer.apply([&o](const auto* ptr) {
return o.buffer.apply([ptr, &o](const auto* optr) {
return std::equal(ptr, ptr + o.size(), optr, detail::equal{});
});
});
}
template <class T>
bool operator==(const T& o) const {
if (size() != o.size()) return false;
return buffer.apply([&o](const auto* ptr) {
return std::equal(ptr, ptr + o.size(), std::begin(o), detail::equal{});
});
}
unlimited_storage& operator*=(const double x) {
buffer.apply(multiplier(), buffer, x);
return *this;
}
iterator begin() noexcept { return {&buffer, 0}; }
iterator end() noexcept { return {&buffer, size()}; }
const_iterator begin() const noexcept { return {&buffer, 0}; }
const_iterator end() const noexcept { return {&buffer, size()}; }
/// @private used by unit tests, not part of generic storage interface
template <class T>
unlimited_storage(std::size_t s, const T* p, allocator_type a = {})
: buffer(std::move(a)) {
buffer.template make<T>(s, p);
}
template <class Archive>
void serialize(Archive&, unsigned);
private:
struct incrementor {
template <class T, class Buffer>
void operator()(T* tp, Buffer& b, std::size_t i) {
if (!detail::safe_increment(tp[i])) {
using U = mp11::mp_at_c<types, (type_index<T>() + 1)>;
b.template make<U>(b.size, tp);
++static_cast<U*>(b.ptr)[i];
}
}
template <class Buffer>
void operator()(mp_int* tp, Buffer&, std::size_t i) {
++tp[i];
}
template <class Buffer>
void operator()(double* tp, Buffer&, std::size_t i) {
++tp[i];
}
};
struct adder {
template <class Buffer, class U>
void operator()(double* tp, Buffer&, std::size_t i, const U& x) {
tp[i] += static_cast<double>(x);
}
template <class T, class Buffer, class U>
void operator()(T* tp, Buffer& b, std::size_t i, const U& x) {
U_is_integral(std::is_integral<U>{}, tp, b, i, x);
}
template <class T, class Buffer, class U>
void U_is_integral(std::false_type, T* tp, Buffer& b, std::size_t i, const U& x) {
b.template make<double>(b.size, tp);
operator()(static_cast<double*>(b.ptr), b, i, x);
}
template <class T, class Buffer, class U>
void U_is_integral(std::true_type, T* tp, Buffer& b, std::size_t i, const U& x) {
U_is_unsigned_integral(std::is_unsigned<U>{}, tp, b, i, x);
}
template <class T, class Buffer, class U>
void U_is_unsigned_integral(std::false_type, T* tp, Buffer& b, std::size_t i,
const U& x) {
if (x >= 0)
U_is_unsigned_integral(std::true_type{}, tp, b, i, detail::make_unsigned(x));
else
U_is_integral(std::false_type{}, tp, b, i, static_cast<double>(x));
}
template <class Buffer, class U>
void U_is_unsigned_integral(std::true_type, mp_int* tp, Buffer&, std::size_t i,
const U& x) {
tp[i] += x;
}
template <class T, class Buffer, class U>
void U_is_unsigned_integral(std::true_type, T* tp, Buffer& b, std::size_t i,
const U& x) {
if (detail::safe_radd(tp[i], x)) return;
using V = mp11::mp_at_c<types, (type_index<T>() + 1)>;
b.template make<V>(b.size, tp);
U_is_unsigned_integral(std::true_type{}, static_cast<V*>(b.ptr), b, i, x);
}
};
struct multiplier {
template <class T, class Buffer>
void operator()(T* tp, Buffer& b, const double x) {
// potential lossy conversion that cannot be avoided
b.template make<double>(b.size, tp);
operator()(static_cast<double*>(b.ptr), b, x);
}
template <class Buffer>
void operator()(double* tp, Buffer& b, const double x) {
for (auto end = tp + b.size; tp != end; ++tp) *tp *= x;
}
template <class T, class Buffer, class U>
void operator()(T* tp, Buffer& b, std::size_t i, const U& x) {
b.template make<double>(b.size, tp);
operator()(static_cast<double*>(b.ptr), b, i, x);
}
template <class Buffer, class U>
void operator()(double* tp, Buffer&, std::size_t i, const U& x) {
tp[i] *= static_cast<double>(x);
}
};
buffer_type buffer;
};
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,74 @@
// 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_UNSAFE_ACCESS_HPP
#define BOOST_HISTOGRAM_UNSAFE_ACCESS_HPP
#include <boost/histogram/detail/axes.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
/// Unsafe read/write access to classes that potentially break consistency
struct unsafe_access {
/**
Get axes.
@param hist histogram.
*/
template <class Histogram>
static auto& axes(Histogram& hist) {
return hist.axes_;
}
/// @copydoc axes()
template <class Histogram>
static const auto& axes(const Histogram& hist) {
return hist.axes_;
}
/**
Get mutable axis reference with compile-time number.
@param hist histogram.
@tparam I axis index (optional, default: 0).
*/
template <class Histogram, unsigned I = 0>
static decltype(auto) axis(Histogram& hist, std::integral_constant<unsigned, I> = {}) {
detail::axis_index_is_valid(hist.axes_, I);
return detail::axis_get<I>(hist.axes_);
}
/**
Get mutable axis reference with run-time number.
@param hist histogram.
@param i axis index.
*/
template <class Histogram>
static decltype(auto) axis(Histogram& hist, unsigned i) {
detail::axis_index_is_valid(hist.axes_, i);
return detail::axis_get(hist.axes_, i);
}
/**
Get storage.
@param hist histogram.
*/
template <class Histogram>
static auto& storage(Histogram& hist) {
return hist.storage_;
}
/// @copydoc storage()
template <class Histogram>
static const auto& storage(const Histogram& hist) {
return hist.storage_;
}
};
} // namespace histogram
} // namespace boost
#endif