feat():initial version
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
// Copyright 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_ACCUMULATORS_HPP
|
||||
#define BOOST_HISTOGRAM_ACCUMULATORS_HPP
|
||||
|
||||
/**
|
||||
\file boost/histogram/accumulators.hpp
|
||||
Includes all accumulator headers of the Boost.Histogram library.
|
||||
|
||||
Extra header not automatically included:
|
||||
- [boost/histogram/accumulators/ostream.hpp][1]
|
||||
|
||||
[1]: histogram/reference.html#header.boost.histogram.accumulators.ostream_hpp
|
||||
*/
|
||||
|
||||
#include <boost/histogram/accumulators/count.hpp>
|
||||
#include <boost/histogram/accumulators/mean.hpp>
|
||||
#include <boost/histogram/accumulators/sum.hpp>
|
||||
#include <boost/histogram/accumulators/thread_safe.hpp>
|
||||
#include <boost/histogram/accumulators/weighted_mean.hpp>
|
||||
#include <boost/histogram/accumulators/weighted_sum.hpp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,137 @@
|
||||
// Copyright 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_ACCUMULATORS_NUMBER_HPP
|
||||
#define BOOST_HISTOGRAM_ACCUMULATORS_NUMBER_HPP
|
||||
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/fwd.hpp> // for count<>
|
||||
#include <type_traits> // for std::common_type
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace accumulators {
|
||||
|
||||
/**
|
||||
Uses a C++ builtin arithmetic type to accumulate a count.
|
||||
|
||||
This wrapper class may be used as a base class by users who want to add custom metadata
|
||||
to each bin of a histogram. Otherwise, arithmetic types should be used directly as
|
||||
accumulators in storages for simplicity. In other words, prefer `dense_storage<double>`
|
||||
over `dense_storage<count<double>>`, both are functionally equivalent.
|
||||
|
||||
When weighted data is accumulated and high precision is required, use
|
||||
`accumulators::sum` instead. If a local variance estimate for the weight distribution
|
||||
should be computed as well (generally needed for a detailed statistical analysis), use
|
||||
`accumulators::weighted_sum`.
|
||||
*/
|
||||
template <class ValueType>
|
||||
class count {
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
count() = default;
|
||||
|
||||
/// Initialize count to value and allow implicit conversion
|
||||
count(const_reference value) noexcept : value_(value) {}
|
||||
|
||||
/// Allow implicit conversion from other count
|
||||
template <class T>
|
||||
count(const count<T>& c) noexcept : count(c.value()) {}
|
||||
|
||||
/// Increment count by one
|
||||
count& operator++() noexcept {
|
||||
++value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Increment count by value
|
||||
count& operator+=(const_reference value) noexcept {
|
||||
value_ += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add another count
|
||||
count& operator+=(const count& s) noexcept {
|
||||
value_ += s.value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scale by value
|
||||
count& operator*=(const_reference value) noexcept {
|
||||
value_ *= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const count& rhs) const noexcept { return value_ == rhs.value_; }
|
||||
|
||||
bool operator!=(const count& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
/// Return count
|
||||
const_reference value() const noexcept { return value_; }
|
||||
|
||||
// conversion to value_type must be explicit
|
||||
explicit operator value_type() const noexcept { return value_; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("value", value_);
|
||||
}
|
||||
|
||||
// begin: extra operators to make count behave like a regular number
|
||||
|
||||
count& operator*=(const count& rhs) noexcept {
|
||||
value_ *= rhs.value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
count operator*(const count& rhs) const noexcept {
|
||||
count x = *this;
|
||||
x *= rhs;
|
||||
return x;
|
||||
}
|
||||
|
||||
count& operator/=(const count& rhs) noexcept {
|
||||
value_ /= rhs.value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
count operator/(const count& rhs) const noexcept {
|
||||
count x = *this;
|
||||
x /= rhs;
|
||||
return x;
|
||||
}
|
||||
|
||||
bool operator<(const count& rhs) const noexcept { return value_ < rhs.value_; }
|
||||
|
||||
bool operator>(const count& rhs) const noexcept { return value_ > rhs.value_; }
|
||||
|
||||
bool operator<=(const count& rhs) const noexcept { return value_ <= rhs.value_; }
|
||||
|
||||
bool operator>=(const count& rhs) const noexcept { return value_ >= rhs.value_; }
|
||||
|
||||
// end: extra operators
|
||||
|
||||
private:
|
||||
value_type value_{};
|
||||
};
|
||||
|
||||
} // namespace accumulators
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
namespace std {
|
||||
template <class T, class U>
|
||||
struct common_type<boost::histogram::accumulators::count<T>,
|
||||
boost::histogram::accumulators::count<U>> {
|
||||
using type = boost::histogram::accumulators::count<common_type_t<T, U>>;
|
||||
};
|
||||
} // namespace std
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/fwd.hpp> // for mean<>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#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 ValueType>
|
||||
class mean {
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
mean() = default;
|
||||
|
||||
/// Allow implicit conversion from mean<T>
|
||||
template <class T>
|
||||
mean(const mean<T>& o) noexcept
|
||||
: sum_{o.sum_}, mean_{o.mean_}, sum_of_deltas_squared_{o.sum_of_deltas_squared_} {}
|
||||
|
||||
/// Initialize to external count, mean, and variance
|
||||
mean(const_reference n, const_reference mean, const_reference variance) noexcept
|
||||
: sum_(n), mean_(mean), sum_of_deltas_squared_(variance * (n - 1)) {}
|
||||
|
||||
/// Insert sample x
|
||||
void operator()(const_reference x) noexcept {
|
||||
sum_ += static_cast<value_type>(1);
|
||||
const auto delta = x - mean_;
|
||||
mean_ += delta / sum_;
|
||||
sum_of_deltas_squared_ += delta * (x - mean_);
|
||||
}
|
||||
|
||||
/// Insert sample x with weight w
|
||||
void operator()(const weight_type<value_type>& w, const_reference x) noexcept {
|
||||
sum_ += w.value;
|
||||
const auto delta = x - mean_;
|
||||
mean_ += w.value * delta / sum_;
|
||||
sum_of_deltas_squared_ += w.value * delta * (x - mean_);
|
||||
}
|
||||
|
||||
/// Add another mean accumulator
|
||||
mean& operator+=(const mean& rhs) noexcept {
|
||||
if (sum_ != 0 || rhs.sum_ != 0) {
|
||||
const auto tmp = mean_ * sum_ + rhs.mean_ * rhs.sum_;
|
||||
sum_ += rhs.sum_;
|
||||
mean_ = tmp / sum_;
|
||||
}
|
||||
sum_of_deltas_squared_ += rhs.sum_of_deltas_squared_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Scale by value
|
||||
|
||||
This acts as if all samples were scaled by the value.
|
||||
*/
|
||||
mean& operator*=(const_reference s) noexcept {
|
||||
mean_ *= s;
|
||||
sum_of_deltas_squared_ *= s * s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const mean& rhs) const noexcept {
|
||||
return sum_ == rhs.sum_ && mean_ == rhs.mean_ &&
|
||||
sum_of_deltas_squared_ == rhs.sum_of_deltas_squared_;
|
||||
}
|
||||
|
||||
bool operator!=(const mean& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
/// Return how many samples were accumulated
|
||||
const_reference count() const noexcept { return sum_; }
|
||||
|
||||
/** Return mean value of accumulated samples.
|
||||
|
||||
The result is undefined, if `count() < 1`.
|
||||
*/
|
||||
const_reference value() const noexcept { return mean_; }
|
||||
|
||||
/** Return variance of accumulated samples
|
||||
|
||||
The result is undefined, if `count() < 2`.
|
||||
*/
|
||||
value_type variance() const noexcept { return sum_of_deltas_squared_ / (sum_ - 1); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned version) {
|
||||
if (version == 0) {
|
||||
// read only
|
||||
std::size_t sum;
|
||||
ar& make_nvp("sum", sum);
|
||||
sum_ = static_cast<value_type>(sum);
|
||||
} else {
|
||||
ar& make_nvp("sum", sum_);
|
||||
}
|
||||
ar& make_nvp("mean", mean_);
|
||||
ar& make_nvp("sum_of_deltas_squared", sum_of_deltas_squared_);
|
||||
}
|
||||
|
||||
private:
|
||||
value_type sum_{};
|
||||
value_type mean_{};
|
||||
value_type sum_of_deltas_squared_{};
|
||||
};
|
||||
|
||||
} // namespace accumulators
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
|
||||
template <class T>
|
||||
struct version;
|
||||
|
||||
// version 1 for boost::histogram::accumulators::mean<T>
|
||||
template <class T>
|
||||
struct version<boost::histogram::accumulators::mean<T>> : std::integral_constant<int, 1> {
|
||||
};
|
||||
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
|
||||
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
|
||||
@@ -0,0 +1,107 @@
|
||||
// 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/detail/counting_streambuf.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <ios>
|
||||
|
||||
/**
|
||||
\file boost/histogram/accumulators/ostream.hpp
|
||||
Simple streaming operators for the builtin accumulator types.
|
||||
|
||||
The text representation is not guaranteed to be stable between versions of
|
||||
Boost.Histogram. This header is only included by
|
||||
[boost/histogram/ostream.hpp](histogram/reference.html#header.boost.histogram.ostream_hpp).
|
||||
To use your own, include your own implementation instead of this header and do not
|
||||
include
|
||||
[boost/histogram/ostream.hpp](histogram/reference.html#header.boost.histogram.ostream_hpp).
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class CharT, class Traits, class T>
|
||||
std::basic_ostream<CharT, Traits>& handle_nonzero_width(
|
||||
std::basic_ostream<CharT, Traits>& os, const T& x) {
|
||||
const auto w = os.width();
|
||||
os.width(0);
|
||||
std::streamsize count = 0;
|
||||
{
|
||||
auto g = make_count_guard(os, count);
|
||||
os << x;
|
||||
}
|
||||
if (os.flags() & std::ios::left) {
|
||||
os << x;
|
||||
for (auto i = count; i < w; ++i) os << os.fill();
|
||||
} else {
|
||||
for (auto i = count; i < w; ++i) os << os.fill();
|
||||
os << x;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace accumulators {
|
||||
|
||||
template <class CharT, class Traits, class U>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const count<U>& x) {
|
||||
return os << x.value();
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class U>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const sum<U>& x) {
|
||||
if (os.width() == 0) return os << "sum(" << x.large() << " + " << x.small() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class U>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const weighted_sum<U>& x) {
|
||||
if (os.width() == 0)
|
||||
return os << "weighted_sum(" << x.value() << ", " << x.variance() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class U>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const mean<U>& x) {
|
||||
if (os.width() == 0)
|
||||
return os << "mean(" << x.count() << ", " << x.value() << ", " << x.variance() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class U>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const weighted_mean<U>& x) {
|
||||
if (os.width() == 0)
|
||||
return os << "weighted_mean(" << x.sum_of_weights() << ", " << x.value() << ", "
|
||||
<< x.variance() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class T>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const thread_safe<T>& x) {
|
||||
os << x.load();
|
||||
return os;
|
||||
}
|
||||
} // namespace accumulators
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/fwd.hpp> // for sum<>
|
||||
#include <cmath> // std::abs
|
||||
#include <type_traits> // std::is_floating_point, std::common_type
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace accumulators {
|
||||
|
||||
/**
|
||||
Uses Neumaier algorithm to compute accurate sums of floats.
|
||||
|
||||
The algorithm is an improved Kahan algorithm
|
||||
(https://en.wikipedia.org/wiki/Kahan_summation_algorithm). The algorithm uses memory for
|
||||
two numbers and is three to five times slower compared to using a single number 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 <class ValueType>
|
||||
class sum {
|
||||
static_assert(std::is_floating_point<ValueType>::value,
|
||||
"ValueType must be a floating point type");
|
||||
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
sum() = default;
|
||||
|
||||
/// Initialize sum to value and allow implicit conversion
|
||||
sum(const_reference value) noexcept : sum(value, 0) {}
|
||||
|
||||
/// Allow implicit conversion from sum<T>
|
||||
template <class T>
|
||||
sum(const sum<T>& s) noexcept : sum(s.large(), s.small()) {}
|
||||
|
||||
/// Initialize sum explicitly with large and small parts
|
||||
sum(const_reference large, const_reference small) noexcept
|
||||
: large_(large), small_(small) {}
|
||||
|
||||
/// Increment sum by one
|
||||
sum& operator++() noexcept { return operator+=(1); }
|
||||
|
||||
/// Increment sum by value
|
||||
sum& operator+=(const_reference value) noexcept {
|
||||
// prevent compiler optimization from destroying the algorithm
|
||||
// when -ffast-math is enabled
|
||||
volatile value_type l;
|
||||
value_type s;
|
||||
if (std::abs(large_) >= std::abs(value)) {
|
||||
l = large_;
|
||||
s = value;
|
||||
} else {
|
||||
l = value;
|
||||
s = large_;
|
||||
}
|
||||
large_ += value;
|
||||
l = l - large_;
|
||||
l = l + s;
|
||||
small_ += l;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add another sum
|
||||
sum& operator+=(const sum& s) noexcept {
|
||||
operator+=(s.large_);
|
||||
small_ += s.small_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scale by value
|
||||
sum& operator*=(const_reference value) noexcept {
|
||||
large_ *= value;
|
||||
small_ *= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const sum& rhs) const noexcept {
|
||||
return large_ + small_ == rhs.large_ + rhs.small_;
|
||||
}
|
||||
|
||||
bool operator!=(const sum& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
/// Return value of the sum.
|
||||
value_type value() const noexcept { return large_ + small_; }
|
||||
|
||||
/// Return large part of the sum.
|
||||
const_reference large() const noexcept { return large_; }
|
||||
|
||||
/// Return small part of the sum.
|
||||
const_reference small() const noexcept { return small_; }
|
||||
|
||||
// lossy conversion to value type must be explicit
|
||||
explicit operator value_type() const noexcept { return value(); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("large", large_);
|
||||
ar& make_nvp("small", small_);
|
||||
}
|
||||
|
||||
// begin: extra operators to make sum behave like a regular number
|
||||
|
||||
sum& operator*=(const sum& rhs) noexcept {
|
||||
const auto scale = static_cast<value_type>(rhs);
|
||||
large_ *= scale;
|
||||
small_ *= scale;
|
||||
return *this;
|
||||
}
|
||||
|
||||
sum operator*(const sum& rhs) const noexcept {
|
||||
sum s = *this;
|
||||
s *= rhs;
|
||||
return s;
|
||||
}
|
||||
|
||||
sum& operator/=(const sum& rhs) noexcept {
|
||||
const auto scale = 1.0 / static_cast<value_type>(rhs);
|
||||
large_ *= scale;
|
||||
small_ *= scale;
|
||||
return *this;
|
||||
}
|
||||
|
||||
sum operator/(const sum& rhs) const noexcept {
|
||||
sum s = *this;
|
||||
s /= rhs;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool operator<(const sum& rhs) const noexcept {
|
||||
return operator value_type() < rhs.operator value_type();
|
||||
}
|
||||
|
||||
bool operator>(const sum& rhs) const noexcept {
|
||||
return operator value_type() > rhs.operator value_type();
|
||||
}
|
||||
|
||||
bool operator<=(const sum& rhs) const noexcept {
|
||||
return operator value_type() <= rhs.operator value_type();
|
||||
}
|
||||
|
||||
bool operator>=(const sum& rhs) const noexcept {
|
||||
return operator value_type() >= rhs.operator value_type();
|
||||
}
|
||||
|
||||
// end: extra operators
|
||||
|
||||
private:
|
||||
value_type large_{};
|
||||
value_type small_{};
|
||||
};
|
||||
|
||||
} // 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
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 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_ACCUMULATORS_THREAD_SAFE_HPP
|
||||
#define BOOST_HISTOGRAM_ACCUMULATORS_THREAD_SAFE_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace accumulators {
|
||||
|
||||
/** Thread-safe adaptor for builtin integral and floating point numbers.
|
||||
|
||||
This adaptor uses std::atomic to make concurrent increments and additions safe for the
|
||||
stored value.
|
||||
|
||||
On common computing platforms, the adapted integer has the same size and
|
||||
alignment as underlying type. The atomicity is implemented with a special CPU
|
||||
instruction. On exotic platforms the size of the adapted number may be larger and/or the
|
||||
type may have different alignment, which means it cannot be tightly packed into arrays.
|
||||
|
||||
@tparam T type to adapt, must be supported by std::atomic.
|
||||
*/
|
||||
template <class T>
|
||||
class thread_safe : public std::atomic<T> {
|
||||
public:
|
||||
using value_type = T;
|
||||
using super_t = std::atomic<T>;
|
||||
|
||||
thread_safe() noexcept : super_t(static_cast<T>(0)) {}
|
||||
// non-atomic copy and assign is allowed, because storage is locked in this case
|
||||
thread_safe(const thread_safe& o) noexcept : super_t(o.load()) {}
|
||||
thread_safe& operator=(const thread_safe& o) noexcept {
|
||||
super_t::store(o.load());
|
||||
return *this;
|
||||
}
|
||||
|
||||
thread_safe(value_type arg) : super_t(arg) {}
|
||||
thread_safe& operator=(value_type arg) {
|
||||
super_t::store(arg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
thread_safe& operator+=(const thread_safe& arg) {
|
||||
operator+=(arg.load());
|
||||
return *this;
|
||||
}
|
||||
thread_safe& operator+=(value_type arg) {
|
||||
super_t::fetch_add(arg, std::memory_order_relaxed);
|
||||
return *this;
|
||||
}
|
||||
thread_safe& operator++() {
|
||||
operator+=(static_cast<value_type>(1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
auto value = super_t::load();
|
||||
ar& make_nvp("value", value);
|
||||
super_t::store(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace accumulators
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/fwd.hpp> // for weighted_mean<>
|
||||
#include <boost/histogram/weight.hpp>
|
||||
#include <cassert>
|
||||
#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 <class ValueType>
|
||||
class weighted_mean {
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
weighted_mean() = default;
|
||||
|
||||
/// Allow implicit conversion from other weighted_means
|
||||
template <class T>
|
||||
weighted_mean(const weighted_mean<T>& o)
|
||||
: sum_of_weights_{o.sum_of_weights_}
|
||||
, sum_of_weights_squared_{o.sum_of_weights_squared_}
|
||||
, weighted_mean_{o.weighted_mean_}
|
||||
, sum_of_weighted_deltas_squared_{o.sum_of_weighted_deltas_squared_} {}
|
||||
|
||||
/// Initialize to external sum of weights, sum of weights squared, mean, and variance
|
||||
weighted_mean(const_reference wsum, const_reference wsum2, const_reference mean,
|
||||
const_reference 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_)) {}
|
||||
|
||||
/// Insert sample x
|
||||
void operator()(const_reference x) { operator()(weight(1), x); }
|
||||
|
||||
/// Insert sample x with weight w
|
||||
void operator()(const weight_type<value_type>& w, const_reference x) {
|
||||
sum_of_weights_ += w.value;
|
||||
sum_of_weights_squared_ += w.value * w.value;
|
||||
const auto delta = x - weighted_mean_;
|
||||
weighted_mean_ += w.value * delta / sum_of_weights_;
|
||||
sum_of_weighted_deltas_squared_ += w.value * delta * (x - weighted_mean_);
|
||||
}
|
||||
|
||||
/// Add another weighted_mean
|
||||
weighted_mean& operator+=(const weighted_mean& rhs) {
|
||||
if (sum_of_weights_ != 0 || rhs.sum_of_weights_ != 0) {
|
||||
const auto tmp =
|
||||
weighted_mean_ * sum_of_weights_ + rhs.weighted_mean_ * rhs.sum_of_weights_;
|
||||
sum_of_weights_ += rhs.sum_of_weights_;
|
||||
sum_of_weights_squared_ += rhs.sum_of_weights_squared_;
|
||||
weighted_mean_ = tmp / sum_of_weights_;
|
||||
}
|
||||
sum_of_weighted_deltas_squared_ += rhs.sum_of_weighted_deltas_squared_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Scale by value
|
||||
|
||||
This acts as if all samples were scaled by the value.
|
||||
*/
|
||||
weighted_mean& operator*=(const_reference s) {
|
||||
weighted_mean_ *= s;
|
||||
sum_of_weighted_deltas_squared_ *= s * s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const weighted_mean& 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_;
|
||||
}
|
||||
|
||||
bool operator!=(const weighted_mean& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
/// Return sum of weights
|
||||
const_reference sum_of_weights() const noexcept { return sum_of_weights_; }
|
||||
|
||||
/// Return sum of weights squared (variance of weight distribution)
|
||||
const_reference sum_of_weights_squared() const noexcept {
|
||||
return sum_of_weights_squared_;
|
||||
}
|
||||
|
||||
/** Return mean value of accumulated weighted samples.
|
||||
|
||||
The result is undefined, if `sum_of_weights() == 0`.
|
||||
*/
|
||||
const_reference value() const noexcept { return weighted_mean_; }
|
||||
|
||||
/** Return variance of accumulated weighted samples
|
||||
|
||||
The result is undefined, if `sum_of_weights() == 0` or
|
||||
`sum_of_weights() == sum_of_weights_squared()`.
|
||||
*/
|
||||
value_type variance() const {
|
||||
return sum_of_weighted_deltas_squared_ /
|
||||
(sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("sum_of_weights", sum_of_weights_);
|
||||
ar& make_nvp("sum_of_weights_squared", sum_of_weights_squared_);
|
||||
ar& make_nvp("weighted_mean", weighted_mean_);
|
||||
ar& make_nvp("sum_of_weighted_deltas_squared", sum_of_weighted_deltas_squared_);
|
||||
}
|
||||
|
||||
private:
|
||||
value_type sum_of_weights_{};
|
||||
value_type sum_of_weights_squared_{};
|
||||
value_type weighted_mean_{};
|
||||
value_type sum_of_weighted_deltas_squared_{};
|
||||
};
|
||||
|
||||
} // 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
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/fwd.hpp> // for weighted_sum<>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace accumulators {
|
||||
|
||||
/// Holds sum of weights and its variance estimate
|
||||
template <class ValueType>
|
||||
class weighted_sum {
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
weighted_sum() = default;
|
||||
|
||||
/// Initialize sum to value and allow implicit conversion
|
||||
weighted_sum(const_reference value) noexcept : weighted_sum(value, value) {}
|
||||
|
||||
/// Allow implicit conversion from sum<T>
|
||||
template <class T>
|
||||
weighted_sum(const weighted_sum<T>& s) noexcept
|
||||
: weighted_sum(s.value(), s.variance()) {}
|
||||
|
||||
/// Initialize sum to value and variance
|
||||
weighted_sum(const_reference value, const_reference variance) noexcept
|
||||
: sum_of_weights_(value), sum_of_weights_squared_(variance) {}
|
||||
|
||||
/// Increment by one.
|
||||
weighted_sum& operator++() {
|
||||
++sum_of_weights_;
|
||||
++sum_of_weights_squared_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Increment by weight.
|
||||
template <class T>
|
||||
weighted_sum& operator+=(const weight_type<T>& w) {
|
||||
sum_of_weights_ += w.value;
|
||||
sum_of_weights_squared_ += w.value * w.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Added another weighted sum.
|
||||
weighted_sum& operator+=(const weighted_sum& rhs) {
|
||||
sum_of_weights_ += rhs.sum_of_weights_;
|
||||
sum_of_weights_squared_ += rhs.sum_of_weights_squared_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scale by value.
|
||||
weighted_sum& operator*=(const_reference x) {
|
||||
sum_of_weights_ *= x;
|
||||
sum_of_weights_squared_ *= x * x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const weighted_sum& rhs) const noexcept {
|
||||
return sum_of_weights_ == rhs.sum_of_weights_ &&
|
||||
sum_of_weights_squared_ == rhs.sum_of_weights_squared_;
|
||||
}
|
||||
|
||||
bool operator!=(const weighted_sum& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
/// Return value of the sum.
|
||||
const_reference value() const noexcept { return sum_of_weights_; }
|
||||
|
||||
/// Return estimated variance of the sum.
|
||||
const_reference variance() const noexcept { return sum_of_weights_squared_; }
|
||||
|
||||
// lossy conversion must be explicit
|
||||
explicit operator const_reference() const { return sum_of_weights_; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("sum_of_weights", sum_of_weights_);
|
||||
ar& make_nvp("sum_of_weights_squared", sum_of_weights_squared_);
|
||||
}
|
||||
|
||||
private:
|
||||
value_type sum_of_weights_{};
|
||||
value_type sum_of_weights_squared_{};
|
||||
};
|
||||
|
||||
} // 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
|
||||
20
install/boost_1_75_0/include/boost/histogram/algorithm.hpp
Normal file
20
install/boost_1_75_0/include/boost/histogram/algorithm.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 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_ALGORITHM_HPP
|
||||
#define BOOST_HISTOGRAM_ALGORITHM_HPP
|
||||
|
||||
/**
|
||||
\file boost/histogram/algorithm.hpp
|
||||
Includes all algorithm headers of the Boost.Histogram library.
|
||||
*/
|
||||
|
||||
#include <boost/histogram/algorithm/empty.hpp>
|
||||
#include <boost/histogram/algorithm/project.hpp>
|
||||
#include <boost/histogram/algorithm/reduce.hpp>
|
||||
#include <boost/histogram/algorithm/sum.hpp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2019 Henry Schreiner
|
||||
//
|
||||
// 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_EMPTY_HPP
|
||||
#define BOOST_HISTOGRAM_ALGORITHM_EMPTY_HPP
|
||||
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/indexed.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace algorithm {
|
||||
/** Check to see if all histogram cells are empty. Use coverage to include or
|
||||
exclude the underflow/overflow bins.
|
||||
|
||||
This algorithm has O(N) complexity, where N is the number of cells.
|
||||
|
||||
Returns true if all cells are empty, and false otherwise.
|
||||
*/
|
||||
template <class A, class S>
|
||||
auto empty(const histogram<A, S>& h, coverage cov) {
|
||||
using value_type = typename histogram<A, S>::value_type;
|
||||
const value_type default_value = value_type();
|
||||
for (auto&& ind : indexed(h, cov)) {
|
||||
if (*ind != default_value) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace algorithm
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/make_default.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/histogram/indexed.hpp>
|
||||
#include <boost/histogram/unsafe_access.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/set.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
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 std::decay_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) {
|
||||
using namespace boost::mp11;
|
||||
const auto& old_axes = unsafe_access::axes(h);
|
||||
|
||||
// axes is always std::vector<...>, even if A is tuple
|
||||
auto axes = detail::make_empty_dynamic_axes(old_axes);
|
||||
axes.reserve(c.size());
|
||||
auto seen = detail::make_stack_buffer(old_axes, false);
|
||||
for (auto d : c) {
|
||||
if (static_cast<unsigned>(d) >= h.rank())
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("invalid axis index"));
|
||||
if (seen[d]) BOOST_THROW_EXCEPTION(std::invalid_argument("indices are not unique"));
|
||||
seen[d] = true;
|
||||
axes.emplace_back(detail::axis_get(old_axes, d));
|
||||
}
|
||||
|
||||
const auto& old_storage = unsafe_access::storage(h);
|
||||
auto result =
|
||||
histogram<decltype(axes), 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
|
||||
@@ -0,0 +1,453 @@
|
||||
// Copyright 2018-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_ALGORITHM_REDUCE_HPP
|
||||
#define BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP
|
||||
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/make_default.hpp>
|
||||
#include <boost/histogram/detail/reduce_command.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/indexed.hpp>
|
||||
#include <boost/histogram/unsafe_access.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace algorithm {
|
||||
|
||||
/** Holder for a reduce command.
|
||||
|
||||
Use this type to store reduce commands in a container. The internals of this type are an
|
||||
implementation detail.
|
||||
*/
|
||||
using reduce_command = detail::reduce_command;
|
||||
|
||||
using reduce_option [[deprecated("use reduce_command instead")]] =
|
||||
reduce_command; ///< deprecated
|
||||
|
||||
/** Shrink command to be used in `reduce`.
|
||||
|
||||
Command is applied to axis with given index.
|
||||
|
||||
Shrinking is based on an inclusive value interval. The bin which contains the first
|
||||
value starts the range of bins to keep. The bin which contains the second value is the
|
||||
last included in that range. When the second value is exactly equal to a lower bin edge,
|
||||
then the previous bin is the last in the range.
|
||||
|
||||
The counts in removed bins are added to the corresponding underflow and overflow bins,
|
||||
if they are present. If they are not present, the counts are discarded. Also see
|
||||
`crop`, which always discards the counts.
|
||||
|
||||
@param iaxis which axis to operate on.
|
||||
@param lower bin which contains lower is first to be kept.
|
||||
@param upper bin which contains upper is last to be kept, except if upper is equal to
|
||||
the lower edge.
|
||||
*/
|
||||
inline reduce_command shrink(unsigned iaxis, double lower, double upper) {
|
||||
if (lower == upper)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("lower != upper required"));
|
||||
reduce_command r;
|
||||
r.iaxis = iaxis;
|
||||
r.range = reduce_command::range_t::values;
|
||||
r.begin.value = lower;
|
||||
r.end.value = upper;
|
||||
r.merge = 1;
|
||||
r.crop = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Shrink command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
Shrinking is based on an inclusive value interval. The bin which contains the first
|
||||
value starts the range of bins to keep. The bin which contains the second value is the
|
||||
last included in that range. When the second value is exactly equal to a lower bin edge,
|
||||
then the previous bin is the last in the range.
|
||||
|
||||
The counts in removed bins are added to the corresponding underflow and overflow bins,
|
||||
if they are present. If they are not present, the counts are discarded. Also see
|
||||
`crop`, which always discards the counts.
|
||||
|
||||
@param lower bin which contains lower is first to be kept.
|
||||
@param upper bin which contains upper is last to be kept, except if upper is equal to
|
||||
the lower edge.
|
||||
*/
|
||||
inline reduce_command shrink(double lower, double upper) {
|
||||
return shrink(reduce_command::unset, lower, upper);
|
||||
}
|
||||
|
||||
/** Crop command to be used in `reduce`.
|
||||
|
||||
Command is applied to axis with given index.
|
||||
|
||||
Works like `shrink` (see shrink documentation for details), but counts in removed
|
||||
bins are always discarded, whether underflow and overflow bins are present or not.
|
||||
|
||||
@param iaxis which axis to operate on.
|
||||
@param lower bin which contains lower is first to be kept.
|
||||
@param upper bin which contains upper is last to be kept, except if upper is equal to
|
||||
the lower edge.
|
||||
*/
|
||||
inline reduce_command crop(unsigned iaxis, double lower, double upper) {
|
||||
reduce_command r = shrink(iaxis, lower, upper);
|
||||
r.crop = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Crop command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
Works like `shrink` (see shrink documentation for details), but counts in removed bins
|
||||
are discarded, whether underflow and overflow bins are present or not.
|
||||
|
||||
@param lower bin which contains lower is first to be kept.
|
||||
@param upper bin which contains upper is last to be kept, except if upper is equal to
|
||||
the lower edge.
|
||||
*/
|
||||
inline reduce_command crop(double lower, double upper) {
|
||||
return crop(reduce_command::unset, lower, upper);
|
||||
}
|
||||
|
||||
/// Whether to behave like `shrink` or `crop` regarding removed bins.
|
||||
enum class slice_mode { shrink, crop };
|
||||
|
||||
/** Slice command to be used in `reduce`.
|
||||
|
||||
Command is applied to axis with given index.
|
||||
|
||||
Slicing works like `shrink` or `crop`, but uses bin indices instead of values.
|
||||
|
||||
@param iaxis which axis to operate on.
|
||||
@param begin first index that should be kept.
|
||||
@param end one past the last index that should be kept.
|
||||
@param mode whether to behave like `shrink` or `crop` regarding removed bins.
|
||||
*/
|
||||
inline reduce_command slice(unsigned iaxis, axis::index_type begin, axis::index_type end,
|
||||
slice_mode mode = slice_mode::shrink) {
|
||||
if (!(begin < end))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("begin < end required"));
|
||||
|
||||
reduce_command r;
|
||||
r.iaxis = iaxis;
|
||||
r.range = reduce_command::range_t::indices;
|
||||
r.begin.index = begin;
|
||||
r.end.index = end;
|
||||
r.merge = 1;
|
||||
r.crop = mode == slice_mode::crop;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Slice command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
Slicing works like `shrink` or `crop`, but uses bin indices instead of values.
|
||||
|
||||
@param begin first index that should be kept.
|
||||
@param end one past the last index that should be kept.
|
||||
@param mode whether to behave like `shrink` or `crop` regarding removed bins.
|
||||
*/
|
||||
inline reduce_command slice(axis::index_type begin, axis::index_type end,
|
||||
slice_mode mode = slice_mode::shrink) {
|
||||
return slice(reduce_command::unset, begin, end, mode);
|
||||
}
|
||||
|
||||
/** Rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to axis with given index.
|
||||
|
||||
The command merges N adjacent bins into one. This makes the axis coarser and the bins
|
||||
wider. The original number of bins is divided by N. If there is a rest to this devision,
|
||||
the axis is implicitly shrunk at the upper end by that rest.
|
||||
|
||||
@param iaxis which axis to operate on.
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
*/
|
||||
inline reduce_command rebin(unsigned iaxis, unsigned merge) {
|
||||
if (merge == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("merge > 0 required"));
|
||||
reduce_command r;
|
||||
r.iaxis = iaxis;
|
||||
r.merge = merge;
|
||||
r.range = reduce_command::range_t::none;
|
||||
r.crop = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
The command merges N adjacent bins into one. This makes the axis coarser and the bins
|
||||
wider. The original number of bins is divided by N. If there is a rest to this devision,
|
||||
the axis is implicitly shrunk at the upper end by that rest.
|
||||
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
*/
|
||||
inline reduce_command rebin(unsigned merge) {
|
||||
return rebin(reduce_command::unset, merge);
|
||||
}
|
||||
|
||||
/** Shrink and rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
To shrink(unsigned, double, double) and rebin(unsigned, unsigned) in one command (see
|
||||
the respective commands for more details). Equivalent to passing both commands for the
|
||||
same axis to `reduce`.
|
||||
|
||||
@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 reduce_command shrink_and_rebin(unsigned iaxis, double lower, double upper,
|
||||
unsigned merge) {
|
||||
reduce_command r = shrink(iaxis, lower, upper);
|
||||
r.merge = rebin(merge).merge;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Shrink and rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
To `shrink` and `rebin` in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to `reduce`.
|
||||
|
||||
@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 reduce_command shrink_and_rebin(double lower, double upper, unsigned merge) {
|
||||
return shrink_and_rebin(reduce_command::unset, lower, upper, merge);
|
||||
}
|
||||
|
||||
/** Crop and rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to axis with given index.
|
||||
|
||||
To `crop` and `rebin` in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to `reduce`.
|
||||
|
||||
@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 reduce_command crop_and_rebin(unsigned iaxis, double lower, double upper,
|
||||
unsigned merge) {
|
||||
reduce_command r = crop(iaxis, lower, upper);
|
||||
r.merge = rebin(merge).merge;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Crop and rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
To `crop` and `rebin` in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to `reduce`.
|
||||
|
||||
@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 reduce_command crop_and_rebin(double lower, double upper, unsigned merge) {
|
||||
return crop_and_rebin(reduce_command::unset, lower, upper, merge);
|
||||
}
|
||||
|
||||
/** Slice and rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to axis with given index.
|
||||
|
||||
To `slice` and `rebin` in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to `reduce`.
|
||||
|
||||
@param iaxis which axis to operate on.
|
||||
@param begin first index that should be kept.
|
||||
@param end one past the last index that should be kept.
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
@param mode slice mode, see slice_mode.
|
||||
*/
|
||||
inline reduce_command slice_and_rebin(unsigned iaxis, axis::index_type begin,
|
||||
axis::index_type end, unsigned merge,
|
||||
slice_mode mode = slice_mode::shrink) {
|
||||
reduce_command r = slice(iaxis, begin, end, mode);
|
||||
r.merge = rebin(merge).merge;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Slice and rebin command to be used in `reduce`.
|
||||
|
||||
Command is applied to corresponding axis in order of reduce arguments.
|
||||
|
||||
To `slice` and `rebin` in one command (see the respective commands for more
|
||||
details). Equivalent to passing both commands for the same axis to `reduce`.
|
||||
|
||||
@param begin first index that should be kept.
|
||||
@param end one past the last index that should be kept.
|
||||
@param merge how many adjacent bins to merge into one.
|
||||
@param mode slice mode, see slice_mode.
|
||||
*/
|
||||
inline reduce_command slice_and_rebin(axis::index_type begin, axis::index_type end,
|
||||
unsigned merge,
|
||||
slice_mode mode = slice_mode::shrink) {
|
||||
return slice_and_rebin(reduce_command::unset, begin, end, merge, mode);
|
||||
}
|
||||
|
||||
/** Shrink, crop, slice, and/or rebin axes of a histogram.
|
||||
|
||||
Returns a new reduced histogram and leaves the original histogram untouched.
|
||||
|
||||
The commands `rebin` and `shrink` or `slice` for the same axis are
|
||||
automatically combined, this is not an error. Passing a `shrink` and a `slice`
|
||||
command for the same axis or two `rebin` commands triggers an `invalid_argument`
|
||||
exception. Trying to reducing a non-reducible axis triggers an `invalid_argument`
|
||||
exception. Histograms with non-reducible axes can still be reduced along the
|
||||
other axes that are reducible.
|
||||
|
||||
@param hist original histogram.
|
||||
@param options iterable sequence of reduce commands: `shrink`, `slice`, `rebin`,
|
||||
`shrink_and_rebin`, or `slice_and_rebin`. The element type of the iterable should be
|
||||
`reduce_command`.
|
||||
*/
|
||||
template <class Histogram, class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
Histogram reduce(const Histogram& hist, const Iterable& options) {
|
||||
using axis::index_type;
|
||||
|
||||
const auto& old_axes = unsafe_access::axes(hist);
|
||||
auto opts = detail::make_stack_buffer(old_axes, reduce_command{});
|
||||
detail::normalize_reduce_commands(opts, options);
|
||||
|
||||
auto axes =
|
||||
detail::axes_transform(old_axes, [&opts](std::size_t iaxis, const auto& a_in) {
|
||||
using A = std::decay_t<decltype(a_in)>;
|
||||
using AO = axis::traits::get_options<A>;
|
||||
auto& o = opts[iaxis];
|
||||
o.is_ordered = axis::traits::ordered(a_in);
|
||||
if (o.merge > 0) { // option is set?
|
||||
o.use_underflow_bin = !o.crop && AO::test(axis::option::underflow);
|
||||
o.use_overflow_bin = !o.crop && AO::test(axis::option::overflow);
|
||||
return detail::static_if_c<axis::traits::is_reducible<A>::value>(
|
||||
[&o](const auto& a_in) {
|
||||
if (o.range == reduce_command::range_t::none) {
|
||||
o.begin.index = 0;
|
||||
o.end.index = a_in.size();
|
||||
} else {
|
||||
if (o.range == reduce_command::range_t::values) {
|
||||
const auto end_value = o.end.value;
|
||||
o.begin.index = axis::traits::index(a_in, o.begin.value);
|
||||
o.end.index = axis::traits::index(a_in, o.end.value);
|
||||
// end = index + 1, unless end_value equal to upper bin edge
|
||||
if (axis::traits::value_as<double>(a_in, o.end.index) != end_value)
|
||||
++o.end.index;
|
||||
}
|
||||
// limit [begin, end] to [0, size()]
|
||||
if (o.begin.index < 0) o.begin.index = 0;
|
||||
if (o.end.index > a_in.size()) o.end.index = a_in.size();
|
||||
}
|
||||
// shorten the index range to a multiple of o.merge;
|
||||
// example [1, 4] with merge = 2 is reduced to [1, 3]
|
||||
o.end.index -=
|
||||
(o.end.index - o.begin.index) % static_cast<index_type>(o.merge);
|
||||
using A = std::decay_t<decltype(a_in)>;
|
||||
return A(a_in, o.begin.index, o.end.index, o.merge);
|
||||
},
|
||||
[iaxis](const auto& a_in) {
|
||||
return BOOST_THROW_EXCEPTION(std::invalid_argument(
|
||||
"axis " + std::to_string(iaxis) + " is not reducible")),
|
||||
a_in;
|
||||
},
|
||||
a_in);
|
||||
} else {
|
||||
// command was not set for this axis; fill noop values and copy original axis
|
||||
o.use_underflow_bin = AO::test(axis::option::underflow);
|
||||
o.use_overflow_bin = AO::test(axis::option::overflow);
|
||||
o.merge = 1;
|
||||
o.begin.index = 0;
|
||||
o.end.index = a_in.size();
|
||||
return a_in;
|
||||
}
|
||||
});
|
||||
|
||||
auto result =
|
||||
Histogram(std::move(axes), detail::make_default(unsafe_access::storage(hist)));
|
||||
|
||||
auto idx = detail::make_stack_buffer<index_type>(unsafe_access::axes(result));
|
||||
for (auto&& x : indexed(hist, coverage::all)) {
|
||||
auto i = idx.begin();
|
||||
auto o = opts.begin();
|
||||
bool skip = false;
|
||||
|
||||
for (auto j : x.indices()) {
|
||||
*i = (j - o->begin.index);
|
||||
if (o->is_ordered && *i <= -1) {
|
||||
*i = -1;
|
||||
if (!o->use_underflow_bin) skip = true;
|
||||
} else {
|
||||
if (*i >= 0)
|
||||
*i /= static_cast<index_type>(o->merge);
|
||||
else
|
||||
*i = o->end.index;
|
||||
const auto reduced_axis_end =
|
||||
(o->end.index - o->begin.index) / static_cast<index_type>(o->merge);
|
||||
if (*i >= reduced_axis_end) {
|
||||
*i = reduced_axis_end;
|
||||
if (!o->use_overflow_bin) skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
++o;
|
||||
}
|
||||
|
||||
if (!skip) result.at(idx) += *x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Shrink, slice, and/or rebin axes of a histogram.
|
||||
|
||||
Returns a new reduced histogram and leaves the original histogram untouched.
|
||||
|
||||
The commands `rebin` and `shrink` or `slice` for the same axis are
|
||||
automatically combined, this is not an error. Passing a `shrink` and a `slice`
|
||||
command for the same axis or two `rebin` commands triggers an invalid_argument
|
||||
exception. It is safe to reduce histograms with some axis that are not reducible along
|
||||
the other axes. Trying to reducing a non-reducible axis triggers an invalid_argument
|
||||
exception.
|
||||
|
||||
@param hist original histogram.
|
||||
@param opt first reduce command; one of `shrink`, `slice`, `rebin`,
|
||||
`shrink_and_rebin`, or `slice_or_rebin`.
|
||||
@param opts more reduce commands.
|
||||
*/
|
||||
template <class Histogram, class... Ts>
|
||||
Histogram reduce(const Histogram& hist, const reduce_command& opt, const Ts&... opts) {
|
||||
// this must be in one line, because any of the ts could be a temporary
|
||||
return reduce(hist, std::initializer_list<reduce_command>{opt, opts...});
|
||||
}
|
||||
|
||||
} // namespace algorithm
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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/histogram/indexed.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace algorithm {
|
||||
|
||||
/** Compute the sum over all histogram cells (underflow/overflow included by default).
|
||||
|
||||
The implementation favors accuracy and protection against overflow over speed. If the
|
||||
value type of the histogram is an integral or floating point type,
|
||||
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+=. The return type
|
||||
is double if the value type of the histogram is integral or floating point, and the
|
||||
original value type otherwise.
|
||||
|
||||
If you need a different trade-off, you can write your own loop or use `std::accumulate`:
|
||||
```
|
||||
// iterate over all bins
|
||||
auto sum_all = std::accumulate(hist.begin(), hist.end(), 0.0);
|
||||
|
||||
// skip underflow/overflow bins
|
||||
double sum = 0;
|
||||
for (auto&& x : indexed(hist))
|
||||
sum += *x; // dereference accessor
|
||||
|
||||
// or:
|
||||
// auto ind = boost::histogram::indexed(hist);
|
||||
// auto sum = std::accumulate(ind.begin(), ind.end(), 0.0);
|
||||
```
|
||||
|
||||
@returns accumulator type or double
|
||||
|
||||
@param hist Const reference to the histogram.
|
||||
@param cov Iterate over all or only inner bins (optional, default: all).
|
||||
*/
|
||||
template <class A, class S>
|
||||
auto sum(const histogram<A, S>& hist, const coverage cov = coverage::all) {
|
||||
using T = typename histogram<A, S>::value_type;
|
||||
using sum_type = mp11::mp_if<std::is_arithmetic<T>, accumulators::sum<double>, T>;
|
||||
sum_type sum;
|
||||
if (cov == coverage::all)
|
||||
for (auto&& x : hist) sum += x;
|
||||
else
|
||||
// sum += x also works if sum_type::operator+=(const sum_type&) exists
|
||||
for (auto&& x : indexed(hist)) 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
|
||||
27
install/boost_1_75_0/include/boost/histogram/axis.hpp
Normal file
27
install/boost_1_75_0/include/boost/histogram/axis.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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
|
||||
|
||||
/**
|
||||
\file boost/histogram/axis.hpp
|
||||
Includes all axis headers of the Boost.Histogram library.
|
||||
|
||||
Extra header not automatically included:
|
||||
- [boost/histogram/axis/ostream.hpp][1]
|
||||
|
||||
[1]: histogram/reference.html#header.boost.histogram.axis.ostream_hpp
|
||||
*/
|
||||
|
||||
#include <boost/histogram/axis/boolean.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
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright Hans Dembinski 2020
|
||||
//
|
||||
// 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_BOOLEAN_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_BOOLEAN_HPP
|
||||
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_type.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
/**
|
||||
Discrete axis for boolean data.
|
||||
|
||||
Binning is a pass-though operation with zero cost, making this the
|
||||
fastest possible axis. The axis has no internal state apart from the
|
||||
optional metadata state. The axis has no under- and overflow bins.
|
||||
It cannot grow and cannot be reduced.
|
||||
|
||||
@tparam MetaData type to store meta data.
|
||||
*/
|
||||
template <class MetaData>
|
||||
class boolean : public iterator_mixin<boolean<MetaData>>,
|
||||
public metadata_base_t<MetaData> {
|
||||
using value_type = bool;
|
||||
using metadata_base = metadata_base_t<MetaData>;
|
||||
using metadata_type = typename metadata_base::metadata_type;
|
||||
|
||||
public:
|
||||
/** Construct a boolean axis.
|
||||
*
|
||||
* \param meta description of the axis.
|
||||
*/
|
||||
explicit boolean(metadata_type meta = {}) : metadata_base(std::move(meta)) {}
|
||||
|
||||
/// Return index for value argument.
|
||||
index_type index(value_type x) const noexcept { return static_cast<index_type>(x); }
|
||||
|
||||
/// Return value for index argument.
|
||||
value_type value(index_type i) const noexcept { return static_cast<value_type>(i); }
|
||||
|
||||
/// Return bin for index argument.
|
||||
value_type bin(index_type i) const noexcept { return value(i); }
|
||||
|
||||
/// Returns the number of bins, without over- or underflow.
|
||||
index_type size() const noexcept { return 2; }
|
||||
|
||||
/// Whether the axis is inclusive (see axis::traits::is_inclusive).
|
||||
static constexpr bool inclusive() noexcept { return true; }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return option::none_t::value; }
|
||||
|
||||
template <class M>
|
||||
bool operator==(const boolean<M>& o) const noexcept {
|
||||
return detail::relaxed_equal{}(this->metadata(), o.metadata());
|
||||
}
|
||||
|
||||
template <class M>
|
||||
bool operator!=(const boolean<M>& o) const noexcept {
|
||||
return !operator==(o);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
}
|
||||
|
||||
private:
|
||||
template <class M>
|
||||
friend class boolean;
|
||||
};
|
||||
|
||||
#if __cpp_deduction_guides >= 201606
|
||||
|
||||
boolean()->boolean<null_type>;
|
||||
|
||||
template <class M>
|
||||
boolean(M) -> boolean<detail::replace_type<std::decay_t<M>, const char*, std::string>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_HISTOGRAM_AXIS_BOOLEAN_HPP
|
||||
205
install/boost_1_75_0/include/boost/histogram/axis/category.hpp
Normal file
205
install/boost_1_75_0/include/boost/histogram/axis/category.hpp
Normal file
@@ -0,0 +1,205 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#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>>,
|
||||
public metadata_base_t<MetaData> {
|
||||
// these must be private, so that they are not automatically inherited
|
||||
using value_type = Value;
|
||||
using metadata_base = metadata_base_t<MetaData>;
|
||||
using metadata_type = typename metadata_base::metadata_type;
|
||||
using options_type = detail::replace_default<Options, option::overflow_t>;
|
||||
using allocator_type = Allocator;
|
||||
using vector_type = std::vector<value_type, allocator_type>;
|
||||
|
||||
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 entries in overflow bin");
|
||||
|
||||
public:
|
||||
constexpr category() = default;
|
||||
explicit category(allocator_type alloc) : vec_(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 = {})
|
||||
: metadata_base(std::move(meta)), vec_(alloc) {
|
||||
if (std::distance(begin, end) < 0)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("end must be reachable by incrementing begin"));
|
||||
vec_.reserve(std::distance(begin, end));
|
||||
while (begin != end) vec_.emplace_back(*begin++);
|
||||
}
|
||||
|
||||
/** 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 (not for users).
|
||||
category(const category& src, index_type begin, index_type end, unsigned merge)
|
||||
// LCOV_EXCL_START: gcc-8 is missing the delegated ctor for no reason
|
||||
: category(src.vec_.begin() + begin, src.vec_.begin() + end, src.metadata(),
|
||||
src.get_allocator())
|
||||
// LCOV_EXCL_STOP
|
||||
{
|
||||
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_.begin();
|
||||
const auto end = vec_.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.
|
||||
std::pair<index_type, index_type> update(const value_type& x) {
|
||||
const auto i = index(x);
|
||||
if (i < size()) return {i, 0};
|
||||
vec_.emplace_back(x);
|
||||
return {i, -1};
|
||||
}
|
||||
|
||||
/// Return value for index argument.
|
||||
/// Throws `std::out_of_range` if the index is out of bounds.
|
||||
auto value(index_type idx) const
|
||||
-> std::conditional_t<std::is_scalar<value_type>::value, value_type,
|
||||
const value_type&> {
|
||||
if (idx < 0 || idx >= size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("category index out of range"));
|
||||
return vec_[idx];
|
||||
}
|
||||
|
||||
/// Return value for index argument; alias for value(...).
|
||||
decltype(auto) bin(index_type idx) const { return value(idx); }
|
||||
|
||||
/// Returns the number of bins, without over- or underflow.
|
||||
index_type size() const noexcept { return static_cast<index_type>(vec_.size()); }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
|
||||
/// Whether the axis is inclusive (see axis::traits::is_inclusive).
|
||||
static constexpr bool inclusive() noexcept {
|
||||
return options() & (option::overflow | option::growth);
|
||||
}
|
||||
|
||||
/// Indicate that the axis is not ordered.
|
||||
static constexpr bool ordered() noexcept { return false; }
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
bool operator==(const category<V, M, O, A>& o) const noexcept {
|
||||
const auto& a = vec_;
|
||||
const auto& b = o.vec_;
|
||||
return std::equal(a.begin(), a.end(), b.begin(), b.end(), detail::relaxed_equal{}) &&
|
||||
detail::relaxed_equal{}(this->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);
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return vec_.get_allocator(); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("seq", vec_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
}
|
||||
|
||||
private:
|
||||
vector_type vec_;
|
||||
|
||||
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<detail::replace_cstring<std::decay_t<T>>, null_type>;
|
||||
|
||||
template <class T, class M>
|
||||
category(std::initializer_list<T>, M)
|
||||
->category<detail::replace_cstring<std::decay_t<T>>,
|
||||
detail::replace_cstring<std::decay_t<M>>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
221
install/boost_1_75_0/include/boost/histogram/axis/integer.hpp
Normal file
221
install/boost_1_75_0/include/boost/histogram/axis/integer.hpp
Normal file
@@ -0,0 +1,221 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/convert_integer.hpp>
|
||||
#include <boost/histogram/detail/limits.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_type.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#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>>,
|
||||
public metadata_base_t<MetaData> {
|
||||
// these must be private, so that they are not automatically inherited
|
||||
using value_type = Value;
|
||||
using metadata_base = metadata_base_t<MetaData>;
|
||||
using metadata_type = typename metadata_base::metadata_type;
|
||||
using options_type =
|
||||
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
|
||||
|
||||
static_assert(std::is_integral<value_type>::value ||
|
||||
std::is_floating_point<value_type>::value,
|
||||
"integer axis requires floating point or integral type");
|
||||
|
||||
static_assert(!options_type::test(option::circular | option::growth) ||
|
||||
(options_type::test(option::circular) ^
|
||||
options_type::test(option::growth)),
|
||||
"circular and growth options are mutually exclusive");
|
||||
|
||||
static_assert(std::is_floating_point<value_type>::value ||
|
||||
(!options_type::test(option::circular) &&
|
||||
!options_type::test(option::growth)) ||
|
||||
(!options_type::test(option::overflow) &&
|
||||
!options_type::test(option::underflow)),
|
||||
"circular or growing integer axis with integral type "
|
||||
"cannot have entries in underflow or overflow bins");
|
||||
|
||||
using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
|
||||
index_type, real_index_type>;
|
||||
|
||||
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 = {})
|
||||
: metadata_base(std::move(meta))
|
||||
, size_(static_cast<index_type>(stop - start))
|
||||
, min_(start) {
|
||||
if (!(stop >= start))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start 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(options_type::test(axis::option::circular),
|
||||
std::is_floating_point<value_type>{},
|
||||
static_cast<double>(x - min_));
|
||||
}
|
||||
|
||||
/// Returns index and shift (if axis has grown) for the passed argument.
|
||||
auto update(value_type x) noexcept {
|
||||
auto impl = [this](long x) -> std::pair<index_type, index_type> {
|
||||
const auto i = x - min_;
|
||||
if (i >= 0) {
|
||||
const auto k = static_cast<axis::index_type>(i);
|
||||
if (k < size()) return {k, 0};
|
||||
const auto n = k - size() + 1;
|
||||
size_ += n;
|
||||
return {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_ -= k;
|
||||
return {0, -k};
|
||||
};
|
||||
|
||||
return detail::static_if<std::is_floating_point<value_type>>(
|
||||
[this, impl](auto x) -> std::pair<index_type, index_type> {
|
||||
if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x)));
|
||||
return {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) &&
|
||||
std::is_floating_point<value_type>::value) {
|
||||
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<value_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_; }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
|
||||
/// Whether the axis is inclusive (see axis::traits::is_inclusive).
|
||||
static constexpr bool inclusive() noexcept {
|
||||
return (options() & option::underflow || options() & option::overflow) ||
|
||||
(std::is_integral<value_type>::value &&
|
||||
(options() & (option::growth | option::circular)));
|
||||
}
|
||||
|
||||
template <class V, class M, class O>
|
||||
bool operator==(const integer<V, M, O>& o) const noexcept {
|
||||
return size() == o.size() && min_ == o.min_ &&
|
||||
detail::relaxed_equal{}(this->metadata(), o.metadata());
|
||||
}
|
||||
|
||||
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& ar, unsigned /* version */) {
|
||||
ar& make_nvp("size", size_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
ar& make_nvp("min", min_);
|
||||
}
|
||||
|
||||
private:
|
||||
// axis not circular
|
||||
template <class B>
|
||||
index_type index_impl(std::false_type, B, double z) const noexcept {
|
||||
if (z < size()) return z >= 0 ? static_cast<index_type>(z) : -1;
|
||||
return size();
|
||||
}
|
||||
|
||||
// value_type is integer, axis circular
|
||||
index_type index_impl(std::true_type, std::false_type, double z) const noexcept {
|
||||
return static_cast<index_type>(z - std::floor(z / size()) * size());
|
||||
}
|
||||
|
||||
// value_type is floating point, must handle +/-infinite or nan, axis circular
|
||||
index_type index_impl(std::true_type, std::true_type, double z) const noexcept {
|
||||
if (std::isfinite(z)) return index_impl(std::true_type{}, std::false_type{}, z);
|
||||
return z < size() ? -1 : size();
|
||||
}
|
||||
|
||||
index_type size_{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>, null_type>;
|
||||
|
||||
template <class T, class M>
|
||||
integer(T, T, M)
|
||||
->integer<detail::convert_integer<T, index_type>,
|
||||
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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
|
||||
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
/**
|
||||
Lightweight bin view.
|
||||
|
||||
Represents the current bin interval.
|
||||
*/
|
||||
template <class Axis>
|
||||
class interval_view {
|
||||
public:
|
||||
interval_view(const Axis& axis, index_type idx) : axis_(axis), idx_(idx) {}
|
||||
// avoid viewing a temporary that goes out of scope
|
||||
interval_view(Axis&& axis, index_type 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 <class BinType>
|
||||
bool operator==(const BinType& rhs) const noexcept {
|
||||
return lower() == rhs.lower() && upper() == rhs.upper();
|
||||
}
|
||||
|
||||
template <class BinType>
|
||||
bool operator!=(const BinType& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
const Axis& axis_;
|
||||
const index_type idx_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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/iterator_adaptor.hpp>
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
template <class Axis>
|
||||
class iterator : public detail::iterator_adaptor<iterator<Axis>, index_type,
|
||||
decltype(std::declval<Axis>().bin(0))> {
|
||||
public:
|
||||
/// Make iterator from axis and index.
|
||||
iterator(const Axis& axis, index_type idx)
|
||||
: iterator::iterator_adaptor_(idx), axis_(axis) {}
|
||||
|
||||
/// Return current bin object.
|
||||
decltype(auto) operator*() const { return axis_.bin(this->base()); }
|
||||
|
||||
private:
|
||||
const Axis& axis_;
|
||||
};
|
||||
|
||||
/// Uses CRTP to inject iterator logic into Derived.
|
||||
template <class Derived>
|
||||
class iterator_mixin {
|
||||
public:
|
||||
using const_iterator = iterator<Derived>;
|
||||
using const_reverse_iterator = std::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 std::make_reverse_iterator(end());
|
||||
}
|
||||
|
||||
/// Reverse bin iterator to the end (read-only).
|
||||
const_reverse_iterator rend() const noexcept {
|
||||
return std::make_reverse_iterator(begin());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 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_METADATA_BASE_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_METADATA_BASE_HPP
|
||||
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/replace_type.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
/** Meta data holder with space optimization for empty meta data types.
|
||||
|
||||
Allows write-access to metadata even if const.
|
||||
|
||||
@tparam Metadata Wrapped meta data type.
|
||||
*/
|
||||
template <class Metadata, bool Detail>
|
||||
class metadata_base {
|
||||
protected:
|
||||
using metadata_type = Metadata;
|
||||
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_constructible<metadata_type>::value,
|
||||
"metadata must be nothrow move constructible");
|
||||
|
||||
metadata_base() = default;
|
||||
metadata_base(const metadata_base&) = default;
|
||||
metadata_base& operator=(const metadata_base&) = default;
|
||||
|
||||
// make noexcept because std::string is nothrow move constructible only in C++17
|
||||
metadata_base(metadata_base&& o) noexcept : data_(std::move(o.data_)) {}
|
||||
metadata_base(metadata_type&& o) noexcept : data_(std::move(o)) {}
|
||||
// make noexcept because std::string is nothrow move constructible only in C++17
|
||||
metadata_base& operator=(metadata_base&& o) noexcept {
|
||||
data_ = std::move(o.data_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable metadata_type data_;
|
||||
|
||||
public:
|
||||
/// Returns reference to metadata.
|
||||
metadata_type& metadata() noexcept { return data_; }
|
||||
|
||||
/// Returns reference to mutable metadata from const axis.
|
||||
metadata_type& metadata() const noexcept { return data_; }
|
||||
};
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
// specialization for empty metadata
|
||||
template <class Metadata>
|
||||
class metadata_base<Metadata, true> {
|
||||
protected:
|
||||
using metadata_type = Metadata;
|
||||
|
||||
metadata_base() = default;
|
||||
|
||||
metadata_base(metadata_type&&) {}
|
||||
metadata_base& operator=(metadata_type&&) { return *this; }
|
||||
|
||||
public:
|
||||
metadata_type& metadata() noexcept {
|
||||
return static_cast<const metadata_base&>(*this).metadata();
|
||||
}
|
||||
|
||||
metadata_type& metadata() const noexcept {
|
||||
static metadata_type data;
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Metadata, class Detail = detail::replace_default<Metadata, std::string>>
|
||||
using metadata_base_t =
|
||||
metadata_base<Detail, (std::is_empty<Detail>::value && std::is_final<Detail>::value)>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
84
install/boost_1_75_0/include/boost/histogram/axis/option.hpp
Normal file
84
install/boost_1_75_0/include/boost/histogram/axis/option.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// 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 <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>) {
|
||||
// B + 0 needed to avoid false positive -Wtautological-compare in gcc-6
|
||||
return std::integral_constant<bool, static_cast<bool>((Bits & B) == (B + 0))>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// 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>;
|
||||
/// Axis has an underflow bin. Mutually exclusive with `circular`.
|
||||
using underflow_t = bit<0>;
|
||||
/// Axis has overflow bin.
|
||||
using overflow_t = bit<1>;
|
||||
/// Axis is circular. Mutually exclusive with `growth` and `underflow`.
|
||||
using circular_t = bit<2>;
|
||||
/// Axis can grow. Mutually exclusive with `circular`.
|
||||
using growth_t = bit<3>;
|
||||
|
||||
constexpr none_t none{}; ///< Instance of `none_t`.
|
||||
constexpr underflow_t underflow{}; ///< Instance of `underflow_t`.
|
||||
constexpr overflow_t overflow{}; ///< Instance of `overflow_t`.
|
||||
constexpr circular_t circular{}; ///< Instance of `circular_t`.
|
||||
constexpr growth_t growth{}; ///< Instance of `growth_t`.
|
||||
|
||||
} // namespace option
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
220
install/boost_1_75_0/include/boost/histogram/axis/ostream.hpp
Normal file
220
install/boost_1_75_0/include/boost/histogram/axis/ostream.hpp
Normal file
@@ -0,0 +1,220 @@
|
||||
// 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/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/detail/counting_streambuf.hpp>
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/histogram/detail/type_name.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <iosfwd>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
\file boost/histogram/axis/ostream.hpp
|
||||
Simple streaming operators for the builtin axis types.
|
||||
|
||||
The text representation is not guaranteed to be stable between versions of
|
||||
Boost.Histogram. This header is only included by
|
||||
[boost/histogram/ostream.hpp](histogram/reference.html#header.boost.histogram.ostream_hpp).
|
||||
To use your own, include your own implementation instead of this header and do not
|
||||
include
|
||||
[boost/histogram/ostream.hpp](histogram/reference.html#header.boost.histogram.ostream_hpp).
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class OStream, class T>
|
||||
auto ostream_any_impl(OStream& os, const T& t, priority<1>) -> decltype(os << t) {
|
||||
return os << t;
|
||||
}
|
||||
|
||||
template <class OStream, class T>
|
||||
OStream& ostream_any_impl(OStream& os, const T&, priority<0>) {
|
||||
return os << type_name<T>();
|
||||
}
|
||||
|
||||
template <class OStream, class T>
|
||||
OStream& ostream_any(OStream& os, const T& t) {
|
||||
return ostream_any_impl(os, t, priority<1>{});
|
||||
}
|
||||
|
||||
template <class OStream, class... Ts>
|
||||
OStream& ostream_any_quoted(OStream& os, const std::basic_string<Ts...>& s) {
|
||||
return os << std::quoted(s);
|
||||
}
|
||||
|
||||
template <class OStream, class T>
|
||||
OStream& ostream_any_quoted(OStream& os, const T& t) {
|
||||
return ostream_any(os, t);
|
||||
}
|
||||
|
||||
template <class... Ts, class T>
|
||||
std::basic_ostream<Ts...>& ostream_metadata(std::basic_ostream<Ts...>& os, const T& t,
|
||||
const char* prefix = ", ") {
|
||||
std::streamsize count = 0;
|
||||
{
|
||||
auto g = make_count_guard(os, count);
|
||||
ostream_any(os, t);
|
||||
}
|
||||
if (!count) return os;
|
||||
os << prefix << "metadata=";
|
||||
return ostream_any_quoted(os, t);
|
||||
}
|
||||
|
||||
template <class OStream>
|
||||
void ostream_options(OStream& os, const unsigned bits) {
|
||||
bool first = true;
|
||||
os << ", options=";
|
||||
|
||||
#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";
|
||||
}
|
||||
|
||||
} // 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) {
|
||||
return os << "[" << i.lower() << ", " << i.upper() << ")";
|
||||
}
|
||||
|
||||
template <class... Ts, class U>
|
||||
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
|
||||
const polymorphic_bin<U>& i) {
|
||||
if (i.is_discrete()) return os << static_cast<double>(i);
|
||||
return os << "[" << i.lower() << ", " << i.upper() << ")";
|
||||
}
|
||||
|
||||
namespace transform {
|
||||
template <class... Ts>
|
||||
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const id&) {
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const log&) {
|
||||
return os << "transform::log{}";
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const sqrt&) {
|
||||
return os << "transform::sqrt{}";
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const pow& p) {
|
||||
return os << "transform::pow{" << p.power << "}";
|
||||
}
|
||||
} // namespace transform
|
||||
|
||||
template <class... Ts, class... Us>
|
||||
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
|
||||
const regular<Us...>& a) {
|
||||
os << "regular(";
|
||||
const auto pos = os.tellp();
|
||||
os << a.transform();
|
||||
if (os.tellp() > pos) os << ", ";
|
||||
os << a.size() << ", " << a.value(0) << ", " << a.value(a.size());
|
||||
detail::ostream_metadata(os, a.metadata());
|
||||
detail::ostream_options(os, a.options());
|
||||
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::ostream_metadata(os, a.metadata());
|
||||
detail::ostream_options(os, a.options());
|
||||
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::ostream_metadata(os, a.metadata());
|
||||
detail::ostream_options(os, a.options());
|
||||
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::ostream_any_quoted(os, a.value(i));
|
||||
os << (i == (a.size() - 1) ? "" : ", ");
|
||||
}
|
||||
detail::ostream_metadata(os, a.metadata());
|
||||
detail::ostream_options(os, a.options());
|
||||
return os << ")";
|
||||
}
|
||||
|
||||
template <class... Ts, class M>
|
||||
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
|
||||
const boolean<M>& a) {
|
||||
os << "boolean(";
|
||||
detail::ostream_metadata(os, a.metadata(), "");
|
||||
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) { detail::ostream_any(os, x); }, v);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
/**
|
||||
Holds the bin data of an axis::variant.
|
||||
|
||||
The interface is a superset of the axis::interval_view
|
||||
class. In addition, the object is implicitly convertible to the value type,
|
||||
returning the equivalent of a call to lower(). For discrete axes, lower() ==
|
||||
upper(), and width() returns zero.
|
||||
|
||||
This is not a view like axis::interval_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.
|
||||
*/
|
||||
template <class 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 <class BinType>
|
||||
bool operator==(const BinType& rhs) const noexcept {
|
||||
return equal_impl(rhs, 0);
|
||||
}
|
||||
|
||||
template <class 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(const polymorphic_bin& rhs, int) const noexcept {
|
||||
return lower_or_value_ == rhs.lower_or_value_ && upper_ == rhs.upper_;
|
||||
}
|
||||
|
||||
template <class BinType>
|
||||
auto equal_impl(const BinType& rhs, decltype(rhs.lower(), 0)) const noexcept {
|
||||
return lower() == rhs.lower() && upper() == rhs.upper();
|
||||
}
|
||||
|
||||
template <class BinType>
|
||||
bool equal_impl(const BinType& rhs, float) 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
|
||||
431
install/boost_1_75_0/include/boost/histogram/axis/regular.hpp
Normal file
431
install/boost_1_75_0/include/boost/histogram/axis/regular.hpp
Normal file
@@ -0,0 +1,431 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/convert_integer.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_type.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
using get_scale_type_helper = typename T::value_type;
|
||||
|
||||
template <class T>
|
||||
using get_scale_type = mp11::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 = mp11::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>();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace axis {
|
||||
|
||||
namespace transform {
|
||||
|
||||
/// Identity transform for equidistant bins.
|
||||
struct id {
|
||||
/// Pass-through.
|
||||
template <class T>
|
||||
static T forward(T&& x) noexcept {
|
||||
return std::forward<T>(x);
|
||||
}
|
||||
|
||||
/// Pass-through.
|
||||
template <class T>
|
||||
static T inverse(T&& x) noexcept {
|
||||
return std::forward<T>(x);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned /* version */) {}
|
||||
};
|
||||
|
||||
/// Log transform for equidistant bins in log-space.
|
||||
struct log {
|
||||
/// Returns log(x) of external value x.
|
||||
template <class T>
|
||||
static T forward(T x) {
|
||||
return std::log(x);
|
||||
}
|
||||
|
||||
/// Returns exp(x) for internal value x.
|
||||
template <class T>
|
||||
static T inverse(T x) {
|
||||
return std::exp(x);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned /* version */) {}
|
||||
};
|
||||
|
||||
/// Sqrt transform for equidistant bins in sqrt-space.
|
||||
struct sqrt {
|
||||
/// Returns sqrt(x) of external value x.
|
||||
template <class T>
|
||||
static T forward(T x) {
|
||||
return std::sqrt(x);
|
||||
}
|
||||
|
||||
/// Returns x^2 of internal value x.
|
||||
template <class T>
|
||||
static T inverse(T x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned /* version */) {}
|
||||
};
|
||||
|
||||
/// 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 <class T>
|
||||
auto forward(T x) const {
|
||||
return std::pow(x, power);
|
||||
}
|
||||
|
||||
/// Returns pow(x, 1/power) of external value x.
|
||||
template <class T>
|
||||
auto inverse(T x) const {
|
||||
return std::pow(x, 1.0 / power);
|
||||
}
|
||||
|
||||
bool operator==(const pow& o) const noexcept { return power == o.power; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("power", power);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace transform
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
// Type envelope to mark value as step size
|
||||
template <class T>
|
||||
struct step_type {
|
||||
T value;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
Helper function to mark argument as step size.
|
||||
*/
|
||||
template <class T>
|
||||
step_type<T> step(T t) {
|
||||
return step_type<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>,
|
||||
public metadata_base_t<MetaData> {
|
||||
// these must be private, so that they are not automatically inherited
|
||||
using value_type = Value;
|
||||
using transform_type = detail::replace_default<Transform, transform::id>;
|
||||
using metadata_base = metadata_base_t<MetaData>;
|
||||
using metadata_type = typename metadata_base::metadata_type;
|
||||
using options_type =
|
||||
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
|
||||
|
||||
static_assert(std::is_nothrow_move_constructible<transform_type>::value,
|
||||
"transform must be no-throw move constructible");
|
||||
static_assert(std::is_nothrow_move_assignable<transform_type>::value,
|
||||
"transform must be no-throw move assignable");
|
||||
|
||||
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,
|
||||
"regular axis requires floating point type");
|
||||
|
||||
static_assert(
|
||||
(!options_type::test(option::circular) && !options_type::test(option::growth)) ||
|
||||
(options_type::test(option::circular) ^ options_type::test(option::growth)),
|
||||
"circular and growth options are mutually exclusive");
|
||||
|
||||
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))
|
||||
, metadata_base(std::move(meta))
|
||||
, size_(static_cast<index_type>(n))
|
||||
, 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, 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(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()) {
|
||||
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.
|
||||
std::pair<index_type, index_type> update(value_type x) noexcept {
|
||||
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 {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_ -= i;
|
||||
return {0, -i};
|
||||
}
|
||||
// z is -infinity
|
||||
return {-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_ += n;
|
||||
return {i, -n};
|
||||
}
|
||||
// z either infinite or NaN
|
||||
return {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_; }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
|
||||
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() &&
|
||||
min_ == o.min_ && delta_ == o.delta_ &&
|
||||
detail::relaxed_equal{}(this->metadata(), o.metadata());
|
||||
}
|
||||
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& ar, unsigned /* version */) {
|
||||
ar& make_nvp("transform", static_cast<transform_type&>(*this));
|
||||
ar& make_nvp("size", size_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
ar& make_nvp("min", min_);
|
||||
ar& make_nvp("delta", delta_);
|
||||
}
|
||||
|
||||
private:
|
||||
index_type size_{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>, transform::id, null_type>;
|
||||
|
||||
template <class T, class M>
|
||||
regular(unsigned, T, T, M)
|
||||
->regular<detail::convert_integer<T, double>, transform::id,
|
||||
detail::replace_cstring<std::decay_t<M>>>;
|
||||
|
||||
template <class Tr, class T, class = detail::requires_transform<Tr, T>>
|
||||
regular(Tr, unsigned, T, T)->regular<detail::convert_integer<T, double>, Tr, null_type>;
|
||||
|
||||
template <class Tr, class T, class M>
|
||||
regular(Tr, unsigned, T, T, M)
|
||||
->regular<detail::convert_integer<T, double>, Tr,
|
||||
detail::replace_cstring<std::decay_t<M>>>;
|
||||
|
||||
#endif
|
||||
|
||||
/// Regular axis with circular option already set.
|
||||
template <class Value = double, class MetaData = use_default, class Options = use_default>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using circular = regular<Value, transform::id, MetaData,
|
||||
decltype(detail::replace_default<Options, option::overflow_t>{} |
|
||||
option::circular)>;
|
||||
#else
|
||||
class circular;
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
498
install/boost_1_75_0/include/boost/histogram/axis/traits.hpp
Normal file
498
install/boost_1_75_0/include/boost/histogram/axis/traits.hpp
Normal file
@@ -0,0 +1,498 @@
|
||||
// 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/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/args_type.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/detail/try_cast.hpp>
|
||||
#include <boost/histogram/detail/type_name.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class Axis>
|
||||
struct value_type_deducer {
|
||||
using type =
|
||||
std::remove_cv_t<std::remove_reference_t<detail::arg_type<decltype(&Axis::index)>>>;
|
||||
};
|
||||
|
||||
template <class Axis>
|
||||
auto traits_options(priority<2>) -> axis::option::bitset<Axis::options()>;
|
||||
|
||||
template <class Axis>
|
||||
auto traits_options(priority<1>) -> decltype(&Axis::update, axis::option::growth_t{});
|
||||
|
||||
template <class Axis>
|
||||
auto traits_options(priority<0>) -> axis::option::none_t;
|
||||
|
||||
template <class Axis>
|
||||
auto traits_is_inclusive(priority<1>) -> std::integral_constant<bool, Axis::inclusive()>;
|
||||
|
||||
template <class Axis>
|
||||
auto traits_is_inclusive(priority<0>)
|
||||
-> decltype(traits_options<Axis>(priority<2>{})
|
||||
.test(axis::option::underflow | axis::option::overflow));
|
||||
|
||||
template <class Axis>
|
||||
auto traits_is_ordered(priority<1>) -> std::integral_constant<bool, Axis::ordered()>;
|
||||
|
||||
template <class Axis, class ValueType = typename value_type_deducer<Axis>::type>
|
||||
auto traits_is_ordered(priority<0>) -> typename std::is_arithmetic<ValueType>::type;
|
||||
|
||||
template <class I, class D, class A,
|
||||
class J = std::decay_t<arg_type<decltype(&A::value)>>>
|
||||
decltype(auto) value_method_switch(I&& i, D&& d, const A& a, priority<1>) {
|
||||
return static_if<std::is_same<J, axis::index_type>>(std::forward<I>(i),
|
||||
std::forward<D>(d), a);
|
||||
}
|
||||
|
||||
template <class I, class D, class A>
|
||||
double value_method_switch(I&&, D&&, const A&, priority<0>) {
|
||||
// comma trick to make all compilers happy; some would complain about
|
||||
// unreachable code after the throw, others about a missing return
|
||||
return BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error(type_name<A>() + " has no value method")),
|
||||
double{};
|
||||
}
|
||||
|
||||
struct variant_access {
|
||||
template <class T, class Variant>
|
||||
static auto get_if(Variant* v) noexcept {
|
||||
using T0 = mp11::mp_first<std::decay_t<Variant>>;
|
||||
return static_if<std::is_pointer<T0>>(
|
||||
[](auto* vptr) {
|
||||
using TP = mp11::mp_if<std::is_const<std::remove_pointer_t<T0>>, const T*, T*>;
|
||||
auto ptp = variant2::get_if<TP>(vptr);
|
||||
return ptp ? *ptp : nullptr;
|
||||
},
|
||||
[](auto* vptr) { return variant2::get_if<T>(vptr); }, &(v->impl));
|
||||
}
|
||||
|
||||
template <class T0, class Visitor, class Variant>
|
||||
static decltype(auto) visit_impl(mp11::mp_identity<T0>, Visitor&& vis, Variant&& v) {
|
||||
return variant2::visit(std::forward<Visitor>(vis), v.impl);
|
||||
}
|
||||
|
||||
template <class T0, class Visitor, class Variant>
|
||||
static decltype(auto) visit_impl(mp11::mp_identity<T0*>, Visitor&& vis, Variant&& v) {
|
||||
return variant2::visit(
|
||||
[&vis](auto&& x) -> decltype(auto) { return std::forward<Visitor>(vis)(*x); },
|
||||
v.impl);
|
||||
}
|
||||
|
||||
template <class Visitor, class Variant>
|
||||
static decltype(auto) visit(Visitor&& vis, Variant&& v) {
|
||||
using T0 = mp11::mp_first<std::decay_t<Variant>>;
|
||||
return visit_impl(mp11::mp_identity<T0>{}, std::forward<Visitor>(vis),
|
||||
std::forward<Variant>(v));
|
||||
}
|
||||
};
|
||||
|
||||
template <class A>
|
||||
decltype(auto) metadata_impl(A&& a, decltype(a.metadata(), 0)) {
|
||||
return std::forward<A>(a).metadata();
|
||||
}
|
||||
|
||||
template <class A>
|
||||
axis::null_type& metadata_impl(A&&, float) {
|
||||
static axis::null_type null_value;
|
||||
return null_value;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace axis {
|
||||
namespace traits {
|
||||
|
||||
/** Value type for axis type.
|
||||
|
||||
Doxygen does not render this well. This is a meta-function (template alias), it accepts
|
||||
an axis type and returns the value type.
|
||||
|
||||
The value type is deduced from the argument of the `Axis::index` method. Const
|
||||
references are decayed to the their value types, for example, the type deduced for
|
||||
`Axis::index(const int&)` is `int`.
|
||||
|
||||
The deduction always succeeds if the axis type models the Axis concept correctly. Errors
|
||||
come from violations of the concept, in particular, an index method that is templated or
|
||||
overloaded is not allowed.
|
||||
|
||||
@tparam Axis axis type.
|
||||
*/
|
||||
template <class Axis>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using value_type = typename detail::value_type_deducer<Axis>::type;
|
||||
#else
|
||||
struct value_type;
|
||||
#endif
|
||||
|
||||
/** Whether axis is continuous or discrete.
|
||||
|
||||
Doxygen does not render this well. This is a meta-function (template alias), it accepts
|
||||
an axis type and returns a compile-time boolean.
|
||||
|
||||
If the boolean is true, the axis is continuous (covers a continuous range of values).
|
||||
Otherwise it is discrete (covers discrete values).
|
||||
*/
|
||||
template <class Axis>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using is_continuous = typename std::is_floating_point<traits::value_type<Axis>>::type;
|
||||
#else
|
||||
struct is_continuous;
|
||||
#endif
|
||||
|
||||
/** Meta-function to detect whether an axis is reducible.
|
||||
|
||||
Doxygen does not render this well. This is a meta-function (template alias), it accepts
|
||||
an axis type and represents compile-time boolean which is true or false, depending on
|
||||
whether the axis can be reduced with boost::histogram::algorithm::reduce().
|
||||
|
||||
An axis can be made reducible by adding a special constructor, see Axis concept for
|
||||
details.
|
||||
|
||||
@tparam Axis axis type.
|
||||
*/
|
||||
template <class Axis>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using is_reducible = std::is_constructible<Axis, const Axis&, axis::index_type,
|
||||
axis::index_type, unsigned>;
|
||||
#else
|
||||
struct is_reducible;
|
||||
#endif
|
||||
|
||||
/** Get axis options for axis type.
|
||||
|
||||
Doxygen does not render this well. This is a meta-function (template alias), it accepts
|
||||
an axis type and returns the boost::histogram::axis::option::bitset.
|
||||
|
||||
If Axis::options() is valid and constexpr, get_options is the corresponding
|
||||
option type. Otherwise, it is boost::histogram::axis::option::growth_t, if the
|
||||
axis has a method `update`, else boost::histogram::axis::option::none_t.
|
||||
|
||||
@tparam Axis axis type
|
||||
*/
|
||||
template <class Axis>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using get_options = decltype(detail::traits_options<Axis>(detail::priority<2>{}));
|
||||
|
||||
template <class Axis>
|
||||
using static_options [[deprecated("use get_options instead")]] = get_options<Axis>;
|
||||
|
||||
#else
|
||||
struct get_options;
|
||||
#endif
|
||||
|
||||
/** Meta-function to detect whether an axis is inclusive.
|
||||
|
||||
Doxygen does not render this well. This is a meta-function (template alias), it accepts
|
||||
an axis type and represents compile-time boolean which is true or false, depending on
|
||||
whether the axis is inclusive or not.
|
||||
|
||||
An axis with underflow and overflow bins is always inclusive, but an axis may be
|
||||
inclusive under other conditions. The meta-function checks for the method `constexpr
|
||||
static bool inclusive()`, and uses the result. If this method is not present, it uses
|
||||
get_options<Axis> and checks whether the underflow and overflow bits are present.
|
||||
|
||||
An inclusive axis has a bin for every possible input value. A histogram which consists
|
||||
only of inclusive axes can be filled more efficiently, since input values always
|
||||
end up in a valid cell and there is no need to keep track of input tuples that need to
|
||||
be discarded.
|
||||
|
||||
@tparam Axis axis type
|
||||
*/
|
||||
template <class Axis>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using is_inclusive = decltype(detail::traits_is_inclusive<Axis>(detail::priority<1>{}));
|
||||
|
||||
template <class Axis>
|
||||
using static_is_inclusive [[deprecated("use is_inclusive instead")]] = is_inclusive<Axis>;
|
||||
|
||||
#else
|
||||
struct is_inclusive;
|
||||
#endif
|
||||
|
||||
/** Meta-function to detect whether an axis is ordered.
|
||||
|
||||
Doxygen does not render this well. This is a meta-function (template alias), it accepts
|
||||
an axis type and returns a compile-time boolean. If the boolean is true, the axis is
|
||||
ordered.
|
||||
|
||||
The meta-function checks for the method `constexpr static bool ordered()`, and uses the
|
||||
result. If this method is not present, it returns true if the value type of the Axis is
|
||||
arithmetic and false otherwise.
|
||||
|
||||
An ordered axis has a value type that is ordered, which means that indices i <
|
||||
j < k implies either value(i) < value(j) < value(k) or value(i) > value(j) > value(k)
|
||||
for all i,j,k. For example, the integer axis is ordered, but the category axis is not.
|
||||
Axis which are not ordered must not have underflow bins, because they only have an
|
||||
"other" category, which is identified with the overflow bin if it is available.
|
||||
|
||||
@tparam Axis axis type
|
||||
*/
|
||||
template <class Axis>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using is_ordered = decltype(detail::traits_is_ordered<Axis>(detail::priority<1>{}));
|
||||
#else
|
||||
struct is_ordered;
|
||||
#endif
|
||||
|
||||
/** Returns axis options as unsigned integer.
|
||||
|
||||
See get_options for details.
|
||||
|
||||
@param axis any axis instance
|
||||
*/
|
||||
template <class Axis>
|
||||
constexpr unsigned options(const Axis& axis) noexcept {
|
||||
(void)axis;
|
||||
return get_options<Axis>::value;
|
||||
}
|
||||
|
||||
// specialization for variant
|
||||
template <class... Ts>
|
||||
unsigned options(const variant<Ts...>& axis) noexcept {
|
||||
return axis.options();
|
||||
}
|
||||
|
||||
/** Returns true if axis is inclusive or false.
|
||||
|
||||
See is_inclusive for details.
|
||||
|
||||
@param axis any axis instance
|
||||
*/
|
||||
template <class Axis>
|
||||
constexpr bool inclusive(const Axis& axis) noexcept {
|
||||
(void)axis;
|
||||
return is_inclusive<Axis>::value;
|
||||
}
|
||||
|
||||
// specialization for variant
|
||||
template <class... Ts>
|
||||
bool inclusive(const variant<Ts...>& axis) noexcept {
|
||||
return axis.inclusive();
|
||||
}
|
||||
|
||||
/** Returns true if axis is ordered or false.
|
||||
|
||||
See is_ordered for details.
|
||||
|
||||
@param axis any axis instance
|
||||
*/
|
||||
template <class Axis>
|
||||
constexpr bool ordered(const Axis& axis) noexcept {
|
||||
(void)axis;
|
||||
return is_ordered<Axis>::value;
|
||||
}
|
||||
|
||||
// specialization for variant
|
||||
template <class... Ts>
|
||||
bool ordered(const variant<Ts...>& axis) noexcept {
|
||||
return axis.ordered();
|
||||
}
|
||||
|
||||
/** Returns true if axis is continuous or false.
|
||||
|
||||
See is_continuous for details.
|
||||
|
||||
@param axis any axis instance
|
||||
*/
|
||||
template <class Axis>
|
||||
constexpr bool continuous(const Axis& axis) noexcept {
|
||||
(void)axis;
|
||||
return is_continuous<Axis>::value;
|
||||
}
|
||||
|
||||
// specialization for variant
|
||||
template <class... Ts>
|
||||
bool continuous(const variant<Ts...>& axis) noexcept {
|
||||
return axis.continuous();
|
||||
}
|
||||
|
||||
/** Returns axis size plus any extra bins for under- and overflow.
|
||||
|
||||
@param axis any axis instance
|
||||
*/
|
||||
template <class Axis>
|
||||
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 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::metadata_impl(std::forward<Axis>(axis), 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<index_type>(index)); },
|
||||
[index](const auto& a) { return a.value(index); }, axis, detail::priority<1>{});
|
||||
}
|
||||
|
||||
/** 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::try_cast<Result, std::runtime_error>(
|
||||
axis::traits::value(axis, index)); // avoid conversion warning
|
||||
}
|
||||
|
||||
/** 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>
|
||||
axis::index_type index(const Axis& axis, const U& value) noexcept(
|
||||
std::is_convertible<U, value_type<Axis>>::value) {
|
||||
return axis.index(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
|
||||
}
|
||||
|
||||
// specialization for variant
|
||||
template <class... Ts, class U>
|
||||
axis::index_type index(const variant<Ts...>& axis, const U& value) {
|
||||
return axis.index(value);
|
||||
}
|
||||
|
||||
/** Return axis rank (how many arguments it processes).
|
||||
|
||||
@param axis any axis instance
|
||||
*/
|
||||
// gcc workaround: must use unsigned int not unsigned as return type
|
||||
template <class Axis>
|
||||
constexpr unsigned int rank(const Axis& axis) {
|
||||
(void)axis;
|
||||
using T = value_type<Axis>;
|
||||
// cannot use mp_eval_or since T could be a fixed-sized sequence
|
||||
return mp11::mp_eval_if_not<detail::is_tuple<T>, mp11::mp_size_t<1>, mp11::mp_size,
|
||||
T>::value;
|
||||
}
|
||||
|
||||
// specialization for variant
|
||||
// gcc workaround: must use unsigned int not unsigned as return type
|
||||
template <class... Ts>
|
||||
unsigned int rank(const axis::variant<Ts...>& axis) {
|
||||
return detail::variant_access::visit(
|
||||
[](const auto& a) { return axis::traits::rank(a); }, axis);
|
||||
}
|
||||
|
||||
/** 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::get_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<index_type, index_type> update(Axis& axis, const U& value) noexcept(
|
||||
std::is_convertible<U, value_type<Axis>>::value) {
|
||||
return detail::static_if_c<get_options<Axis>::test(option::growth)>(
|
||||
[&value](auto& a) {
|
||||
return a.update(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
|
||||
},
|
||||
[&value](auto& a) -> std::pair<index_type, index_type> {
|
||||
return {axis::traits::index(a, value), 0};
|
||||
},
|
||||
axis);
|
||||
}
|
||||
|
||||
// specialization for variant
|
||||
template <class... Ts, class U>
|
||||
std::pair<index_type, index_type> update(variant<Ts...>& axis, const U& value) {
|
||||
return visit([&value](auto& a) { return a.update(value); }, axis);
|
||||
}
|
||||
|
||||
/** 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,
|
||||
detail::priority<1>{});
|
||||
}
|
||||
|
||||
/** 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(
|
||||
[](const auto&) { return Result{}; },
|
||||
[index](const auto& a) {
|
||||
return detail::try_cast<Result, std::runtime_error>(a.value(index + 1) -
|
||||
a.value(index));
|
||||
},
|
||||
axis, detail::priority<1>{});
|
||||
}
|
||||
|
||||
} // namespace traits
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
251
install/boost_1_75_0/include/boost/histogram/axis/variable.hpp
Normal file
251
install/boost_1_75_0/include/boost/histogram/axis/variable.hpp
Normal file
@@ -0,0 +1,251 @@
|
||||
// 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/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/convert_integer.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/limits.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_type.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#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>>,
|
||||
public metadata_base_t<MetaData> {
|
||||
// these must be private, so that they are not automatically inherited
|
||||
using value_type = Value;
|
||||
using metadata_base = metadata_base_t<MetaData>;
|
||||
using metadata_type = typename metadata_base::metadata_type;
|
||||
using options_type =
|
||||
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
|
||||
using allocator_type = Allocator;
|
||||
using vector_type = std::vector<Value, allocator_type>;
|
||||
|
||||
static_assert(
|
||||
std::is_floating_point<value_type>::value,
|
||||
"current version of variable axis requires floating point type; "
|
||||
"if you need a variable axis with an integral type, please submit an issue");
|
||||
|
||||
static_assert(
|
||||
(!options_type::test(option::circular) && !options_type::test(option::growth)) ||
|
||||
(options_type::test(option::circular) ^ options_type::test(option::growth)),
|
||||
"circular and growth options are mutually exclusive");
|
||||
|
||||
public:
|
||||
constexpr variable() = default;
|
||||
explicit variable(allocator_type alloc) : vec_(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 = {})
|
||||
: metadata_base(std::move(meta)), vec_(std::move(alloc)) {
|
||||
if (std::distance(begin, end) < 2)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
|
||||
|
||||
vec_.reserve(std::distance(begin, end));
|
||||
vec_.emplace_back(*begin++);
|
||||
bool strictly_ascending = true;
|
||||
for (; begin != end; ++begin) {
|
||||
strictly_ascending &= vec_.back() < *begin;
|
||||
vec_.emplace_back(*begin);
|
||||
}
|
||||
if (!strictly_ascending)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("input sequence must be strictly ascending"));
|
||||
}
|
||||
|
||||
/** 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)
|
||||
: metadata_base(src), vec_(src.get_allocator()) {
|
||||
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"));
|
||||
vec_.reserve((end - begin) / merge);
|
||||
const auto beg = src.vec_.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 {
|
||||
if (options_type::test(option::circular)) {
|
||||
const auto a = vec_[0];
|
||||
const auto b = vec_[size()];
|
||||
x -= std::floor((x - a) / (b - a)) * (b - a);
|
||||
}
|
||||
return static_cast<index_type>(std::upper_bound(vec_.begin(), vec_.end(), x) -
|
||||
vec_.begin() - 1);
|
||||
}
|
||||
|
||||
std::pair<index_type, index_type> update(value_type x) noexcept {
|
||||
const auto i = index(x);
|
||||
if (std::isfinite(x)) {
|
||||
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 {i, -1};
|
||||
}
|
||||
const auto d = value(0.5) - value(0);
|
||||
x = (std::min)(x, value(0) - d);
|
||||
vec_.insert(vec_.begin(), x);
|
||||
return {0, -i};
|
||||
}
|
||||
return {x < 0 ? -1 : size(), 0};
|
||||
}
|
||||
|
||||
/// Return value for fractional index argument.
|
||||
value_type value(real_index_type i) const noexcept {
|
||||
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 = vec_[0];
|
||||
const auto b = vec_[size()];
|
||||
return (1.0 - z) * vec_[k] + z * vec_[k + 1] + shift * (b - a);
|
||||
}
|
||||
if (i < 0) return detail::lowest<value_type>();
|
||||
if (i == size()) return vec_.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;
|
||||
// check z == 0 needed to avoid returning nan when vec_[k + 1] is infinity
|
||||
return (1.0 - z) * vec_[k] + (z == 0 ? 0 : z * vec_[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_.size()) - 1; }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
bool operator==(const variable<V, M, O, A>& o) const noexcept {
|
||||
const auto& a = vec_;
|
||||
const auto& b = o.vec_;
|
||||
return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
|
||||
detail::relaxed_equal{}(this->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_.get_allocator(); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("seq", vec_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
}
|
||||
|
||||
private:
|
||||
vector_type vec_;
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
friend class variable;
|
||||
};
|
||||
|
||||
#if __cpp_deduction_guides >= 201606
|
||||
|
||||
template <class T>
|
||||
variable(std::initializer_list<T>)
|
||||
->variable<detail::convert_integer<T, double>, null_type>;
|
||||
|
||||
template <class T, class M>
|
||||
variable(std::initializer_list<T>, M)
|
||||
->variable<detail::convert_integer<T, double>,
|
||||
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
|
||||
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
variable(Iterable)
|
||||
->variable<
|
||||
detail::convert_integer<
|
||||
std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
|
||||
null_type>;
|
||||
|
||||
template <class Iterable, class M>
|
||||
variable(Iterable, M)
|
||||
->variable<
|
||||
detail::convert_integer<
|
||||
std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
|
||||
detail::replace_type<std::decay_t<M>, const char*, std::string>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
347
install/boost_1_75_0/include/boost/histogram/axis/variant.hpp
Normal file
347
install/boost_1_75_0/include/boost/histogram/axis/variant.hpp
Normal file
@@ -0,0 +1,347 @@
|
||||
// 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_VARIANT_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP
|
||||
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/polymorphic_bin.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/detail/type_name.hpp>
|
||||
#include <boost/histogram/detail/variant_proxy.hpp>
|
||||
#include <boost/mp11/algorithm.hpp> // mp_contains
|
||||
#include <boost/mp11/list.hpp> // mp_first
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
/// Polymorphic axis type
|
||||
template <class... Ts>
|
||||
class variant : public iterator_mixin<variant<Ts...>> {
|
||||
using impl_type = boost::variant2::variant<Ts...>;
|
||||
|
||||
template <class T>
|
||||
using is_bounded_type = mp11::mp_contains<variant, std::decay_t<T>>;
|
||||
|
||||
template <class T>
|
||||
using requires_bounded_type = std::enable_if_t<is_bounded_type<T>::value>;
|
||||
|
||||
using metadata_type = std::remove_const_t<std::remove_reference_t<decltype(
|
||||
traits::metadata(std::declval<std::remove_pointer_t<mp11::mp_first<variant>>>()))>>;
|
||||
|
||||
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 <class T, class = requires_bounded_type<T>>
|
||||
variant(T&& t) : impl(std::forward<T>(t)) {}
|
||||
|
||||
template <class T, class = 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 = std::decay_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::type_name<U>() + " is not convertible to a bounded type of " +
|
||||
detail::type_name<variant>()));
|
||||
},
|
||||
u);
|
||||
},
|
||||
u);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return size of axis.
|
||||
index_type size() const {
|
||||
return visit([](const auto& a) -> index_type { return a.size(); }, *this);
|
||||
}
|
||||
|
||||
/// Return options of axis or option::none_t if axis has no options.
|
||||
unsigned options() const {
|
||||
return visit([](const auto& a) { return traits::options(a); }, *this);
|
||||
}
|
||||
|
||||
/// Returns true if the axis is inclusive or false.
|
||||
bool inclusive() const {
|
||||
return visit([](const auto& a) { return traits::inclusive(a); }, *this);
|
||||
}
|
||||
|
||||
/// Returns true if the axis is ordered or false.
|
||||
bool ordered() const {
|
||||
return visit([](const auto& a) { return traits::ordered(a); }, *this);
|
||||
}
|
||||
|
||||
/// Returns true if the axis is continuous or false.
|
||||
bool continuous() const {
|
||||
return visit([](const auto& a) { return traits::continuous(a); }, *this);
|
||||
}
|
||||
|
||||
/// Return reference to const metadata or instance of null_type if axis has no
|
||||
/// metadata.
|
||||
metadata_type& metadata() const {
|
||||
return visit(
|
||||
[](const auto& a) -> metadata_type& {
|
||||
using M = decltype(traits::metadata(a));
|
||||
return detail::static_if<std::is_same<M, metadata_type&>>(
|
||||
[](const auto& a) -> metadata_type& { return traits::metadata(a); },
|
||||
[](const auto&) -> metadata_type& {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(
|
||||
"cannot return metadata of type " + detail::type_name<M>() +
|
||||
" through axis::variant interface which uses type " +
|
||||
detail::type_name<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(
|
||||
"cannot return metadata of type " + detail::type_name<M>() +
|
||||
" through axis::variant interface which uses type " +
|
||||
detail::type_name<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(
|
||||
[idx](const auto& a) { // axis is discrete
|
||||
const double x = traits::value_as<double>(a, idx);
|
||||
return polymorphic_bin<double>(x, x);
|
||||
},
|
||||
[idx](const auto& a) { // axis is continuous
|
||||
const double x1 = traits::value_as<double>(a, idx);
|
||||
const double x2 = traits::value_as<double>(a, idx + 1);
|
||||
return polymorphic_bin<double>(x1, x2);
|
||||
},
|
||||
a, detail::priority<1>{});
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
detail::variant_proxy<variant> p{*this};
|
||||
ar& make_nvp("variant", p);
|
||||
}
|
||||
|
||||
private:
|
||||
impl_type impl;
|
||||
|
||||
friend struct detail::variant_access;
|
||||
friend struct boost::histogram::unsafe_access;
|
||||
};
|
||||
|
||||
// specialization for empty argument list, useful for meta-programming
|
||||
template <>
|
||||
class variant<> {};
|
||||
|
||||
/// Apply visitor to variant (reference).
|
||||
template <class Visitor, class... Us>
|
||||
decltype(auto) visit(Visitor&& vis, variant<Us...>& var) {
|
||||
return detail::variant_access::visit(vis, var);
|
||||
}
|
||||
|
||||
/// Apply visitor to variant (movable reference).
|
||||
template <class Visitor, class... Us>
|
||||
decltype(auto) visit(Visitor&& vis, variant<Us...>&& var) {
|
||||
return detail::variant_access::visit(vis, std::move(var));
|
||||
}
|
||||
|
||||
/// Apply visitor to variant (const reference).
|
||||
template <class Visitor, class... Us>
|
||||
decltype(auto) visit(Visitor&& vis, const variant<Us...>& var) {
|
||||
return detail::variant_access::visit(vis, var);
|
||||
}
|
||||
|
||||
/// Returns pointer to T in variant or null pointer if type does not match.
|
||||
template <class T, class... Us>
|
||||
auto get_if(variant<Us...>* v) {
|
||||
return detail::variant_access::template get_if<T>(v);
|
||||
}
|
||||
|
||||
/// Returns pointer to const T in variant or null pointer if type does not match.
|
||||
template <class T, class... Us>
|
||||
auto get_if(const variant<Us...>* v) {
|
||||
return detail::variant_access::template get_if<T>(v);
|
||||
}
|
||||
|
||||
/// Return reference to T, throws std::runtime_error if type does not match.
|
||||
template <class T, class... Us>
|
||||
decltype(auto) get(variant<Us...>& v) {
|
||||
auto tp = get_if<T>(&v);
|
||||
if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
|
||||
return *tp;
|
||||
}
|
||||
|
||||
/// Return movable reference to T, throws unspecified exception if type does not match.
|
||||
template <class T, class... Us>
|
||||
decltype(auto) get(variant<Us...>&& v) {
|
||||
auto tp = get_if<T>(&v);
|
||||
if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
|
||||
return std::move(*tp);
|
||||
}
|
||||
|
||||
/// Return const reference to T, throws unspecified exception if type does not match.
|
||||
template <class T, class... Us>
|
||||
decltype(auto) get(const variant<Us...>& v) {
|
||||
auto tp = get_if<T>(&v);
|
||||
if (!tp) BOOST_THROW_EXCEPTION(std::runtime_error("T is not the held type"));
|
||||
return *tp;
|
||||
}
|
||||
|
||||
// pass-through version of visit for generic programming
|
||||
template <class Visitor, class T>
|
||||
decltype(auto) visit(Visitor&& vis, T&& var) {
|
||||
return std::forward<Visitor>(vis)(std::forward<T>(var));
|
||||
}
|
||||
|
||||
// pass-through version of get for generic programming
|
||||
template <class T, class U>
|
||||
decltype(auto) get(U&& u) {
|
||||
return std::forward<U>(u);
|
||||
}
|
||||
|
||||
// pass-through version of get_if for generic programming
|
||||
template <class T, class U>
|
||||
auto get_if(U* u) {
|
||||
return reinterpret_cast<T*>(std::is_same<T, std::decay_t<U>>::value ? u : nullptr);
|
||||
}
|
||||
|
||||
// pass-through version of get_if for generic programming
|
||||
template <class T, class U>
|
||||
auto get_if(const U* u) {
|
||||
return reinterpret_cast<const T*>(std::is_same<T, std::decay_t<U>>::value ? u
|
||||
: nullptr);
|
||||
}
|
||||
|
||||
/** Compare two variants.
|
||||
|
||||
Return true if the variants point to the same concrete axis type and the types compare
|
||||
equal. Otherwise return false.
|
||||
*/
|
||||
template <class... Us, class... Vs>
|
||||
bool operator==(const variant<Us...>& u, const variant<Vs...>& v) noexcept {
|
||||
return visit([&](const auto& vi) { return u == vi; }, v);
|
||||
}
|
||||
|
||||
/** Compare variant with a concrete axis type.
|
||||
|
||||
Return true if the variant point to the same concrete axis type and the types compare
|
||||
equal. Otherwise return false.
|
||||
*/
|
||||
template <class... Us, class T>
|
||||
bool operator==(const variant<Us...>& u, const T& t) noexcept {
|
||||
using V = variant<Us...>;
|
||||
return detail::static_if_c<(mp11::mp_contains<V, T>::value ||
|
||||
mp11::mp_contains<V, T*>::value ||
|
||||
mp11::mp_contains<V, const T*>::value)>(
|
||||
[&](const auto& t) {
|
||||
using U = std::decay_t<decltype(t)>;
|
||||
const U* tp = detail::variant_access::template get_if<U>(&u);
|
||||
return tp && detail::relaxed_equal{}(*tp, t);
|
||||
},
|
||||
[&](const auto&) { return false; }, t);
|
||||
}
|
||||
|
||||
template <class T, class... Us>
|
||||
bool operator==(const T& t, const variant<Us...>& u) noexcept {
|
||||
return u == t;
|
||||
}
|
||||
|
||||
/// The negation of operator==.
|
||||
template <class... Us, class... Ts>
|
||||
bool operator!=(const variant<Us...>& u, const variant<Ts...>& t) noexcept {
|
||||
return !(u == t);
|
||||
}
|
||||
|
||||
/// The negation of operator==.
|
||||
template <class... Us, class T>
|
||||
bool operator!=(const variant<Us...>& u, const T& t) noexcept {
|
||||
return !(u == t);
|
||||
}
|
||||
|
||||
/// The negation of operator==.
|
||||
template <class T, class... Us>
|
||||
bool operator!=(const T& t, const variant<Us...>& u) noexcept {
|
||||
return u != t;
|
||||
}
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 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_DETAIL_ACCUMULATOR_TRAITS_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_ACCUMULATOR_TRAITS_HPP
|
||||
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
|
||||
// forward declare accumulator_set so that it can be matched below
|
||||
namespace accumulators {
|
||||
template <class, class, class>
|
||||
struct accumulator_set;
|
||||
}
|
||||
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <bool WeightSupport, class... Ts>
|
||||
struct accumulator_traits_holder {
|
||||
static constexpr bool weight_support = WeightSupport;
|
||||
using args = std::tuple<Ts...>;
|
||||
};
|
||||
|
||||
// member function pointer with weight_type as first argument is better match
|
||||
template <class R, class T, class U, class... Ts>
|
||||
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
|
||||
R (T::*)(boost::histogram::weight_type<U>, Ts...));
|
||||
|
||||
template <class R, class T, class U, class... Ts>
|
||||
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
|
||||
R (T::*)(boost::histogram::weight_type<U>&, Ts...));
|
||||
|
||||
template <class R, class T, class U, class... Ts>
|
||||
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
|
||||
R (T::*)(boost::histogram::weight_type<U>&&, Ts...));
|
||||
|
||||
template <class R, class T, class U, class... Ts>
|
||||
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
|
||||
R (T::*)(const boost::histogram::weight_type<U>&, Ts...));
|
||||
|
||||
// member function pointer only considered if all specializations above fail
|
||||
template <class R, class T, class... Ts>
|
||||
accumulator_traits_holder<false, Ts...> accumulator_traits_impl_call_op(R (T::*)(Ts...));
|
||||
|
||||
template <class T>
|
||||
auto accumulator_traits_impl(T&, priority<1>)
|
||||
-> decltype(accumulator_traits_impl_call_op(&T::operator()));
|
||||
|
||||
template <class T>
|
||||
auto accumulator_traits_impl(T&, priority<1>)
|
||||
-> decltype(std::declval<T&>() += 0, accumulator_traits_holder<true>{});
|
||||
|
||||
template <class T>
|
||||
auto accumulator_traits_impl(T&, priority<0>) -> accumulator_traits_holder<false>;
|
||||
|
||||
// for boost.accumulators compatibility
|
||||
template <class S, class F, class W>
|
||||
accumulator_traits_holder<false, S> accumulator_traits_impl(
|
||||
boost::accumulators::accumulator_set<S, F, W>&, priority<1>) {
|
||||
static_assert(std::is_same<W, void>::value,
|
||||
"accumulator_set with weights is not directly supported, please use "
|
||||
"a wrapper class that implements the Accumulator concept");
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using accumulator_traits =
|
||||
decltype(accumulator_traits_impl(std::declval<T&>(), priority<1>{}));
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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_ARGS_TYPE_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_ARGS_TYPE_HPP
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
struct args_type_impl {
|
||||
using T::ERROR_this_should_never_be_instantiated_please_write_an_issue;
|
||||
};
|
||||
|
||||
template <class R, class T, class... Ts>
|
||||
struct args_type_impl<R (T::*)(Ts...)> {
|
||||
using type = std::tuple<Ts...>;
|
||||
};
|
||||
|
||||
template <class R, class T, class... Ts>
|
||||
struct args_type_impl<R (T ::*)(Ts...) const> {
|
||||
using type = std::tuple<Ts...>;
|
||||
};
|
||||
|
||||
template <class R, class... Ts>
|
||||
struct args_type_impl<R (*)(Ts...)> {
|
||||
using type = std::tuple<Ts...>;
|
||||
};
|
||||
|
||||
#if __cpp_noexcept_function_type >= 201510
|
||||
template <class R, class T, class... Ts>
|
||||
struct args_type_impl<R (T::*)(Ts...) noexcept> {
|
||||
using type = std::tuple<Ts...>;
|
||||
};
|
||||
|
||||
template <class R, class T, class... Ts>
|
||||
struct args_type_impl<R (T ::*)(Ts...) const noexcept> {
|
||||
using type = std::tuple<Ts...>;
|
||||
};
|
||||
|
||||
template <class R, class... Ts>
|
||||
struct args_type_impl<R (*)(Ts...) noexcept> {
|
||||
using type = std::tuple<Ts...>;
|
||||
};
|
||||
#endif
|
||||
|
||||
template <class FunctionPointer>
|
||||
using args_type = typename args_type_impl<FunctionPointer>::type;
|
||||
|
||||
template <class T, std::size_t N = 0>
|
||||
using arg_type = std::tuple_element_t<N, args_type<T>>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 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_DETAIL_ARGUMENT_TRAITS_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_ARGUMENT_TRAITS_HPP
|
||||
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/integral.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <tuple>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
struct is_weight_impl : mp11::mp_false {};
|
||||
|
||||
template <class T>
|
||||
struct is_weight_impl<weight_type<T>> : mp11::mp_true {};
|
||||
|
||||
template <class T>
|
||||
using is_weight = is_weight_impl<T>;
|
||||
|
||||
template <class T>
|
||||
struct is_sample_impl : mp11::mp_false {};
|
||||
|
||||
template <class T>
|
||||
struct is_sample_impl<sample_type<T>> : mp11::mp_true {};
|
||||
|
||||
template <class T>
|
||||
using is_sample = is_sample_impl<T>;
|
||||
|
||||
template <int Idx, class L>
|
||||
struct sample_args_impl {
|
||||
using type = mp11::mp_first<std::decay_t<mp11::mp_at_c<L, (Idx >= 0 ? Idx : 0)>>>;
|
||||
};
|
||||
|
||||
template <class L>
|
||||
struct sample_args_impl<-1, L> {
|
||||
using type = std::tuple<>;
|
||||
};
|
||||
|
||||
template <std::size_t NArgs, std::size_t Start, int WeightPos, int SamplePos,
|
||||
class SampleArgs>
|
||||
struct argument_traits_holder {
|
||||
using nargs = mp11::mp_size_t<NArgs>;
|
||||
using start = mp11::mp_size_t<Start>;
|
||||
using wpos = mp11::mp_int<WeightPos>;
|
||||
using spos = mp11::mp_int<SamplePos>;
|
||||
using sargs = SampleArgs;
|
||||
};
|
||||
|
||||
template <class... Ts>
|
||||
struct argument_traits_impl {
|
||||
using list_ = mp11::mp_list<Ts...>;
|
||||
static constexpr std::size_t size_ = sizeof...(Ts);
|
||||
static constexpr std::size_t weight_ = mp11::mp_find_if<list_, is_weight>::value;
|
||||
static constexpr std::size_t sample_ = mp11::mp_find_if<list_, is_sample>::value;
|
||||
static constexpr int spos_ = (sample_ < size_ ? static_cast<int>(sample_) : -1);
|
||||
static constexpr int wpos_ = (weight_ < size_ ? static_cast<int>(weight_) : -1);
|
||||
|
||||
using type =
|
||||
argument_traits_holder<(size_ - (weight_ < size_) - (sample_ < size_)),
|
||||
(weight_ < size_ && sample_ < size_ &&
|
||||
(weight_ + sample_ < 2)
|
||||
? 2
|
||||
: ((weight_ == 0 || sample_ == 0) ? 1 : 0)),
|
||||
wpos_, spos_, typename sample_args_impl<spos_, list_>::type>;
|
||||
};
|
||||
|
||||
template <class... Ts>
|
||||
using argument_traits = typename argument_traits_impl<Ts...>::type;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 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_DETAIL_ARRAY_WRAPPER_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_ARRAY_WRAPPER_HPP
|
||||
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/detail/span.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/mp11/function.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class = decltype(&T::template save_array<int>)>
|
||||
struct has_save_array_impl;
|
||||
|
||||
template <class T, class = decltype(&T::template load_array<int>)>
|
||||
struct has_load_array_impl;
|
||||
|
||||
template <class T>
|
||||
using has_array_optimization = mp11::mp_or<mp11::mp_valid<has_save_array_impl, T>,
|
||||
mp11::mp_valid<has_load_array_impl, T>>;
|
||||
|
||||
template <class T>
|
||||
struct array_wrapper {
|
||||
using pointer = T*;
|
||||
|
||||
pointer ptr;
|
||||
std::size_t size;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
static_if_c<(has_array_optimization<Archive>::value &&
|
||||
std::is_trivially_copyable<T>::value)>(
|
||||
[this](auto& ar) {
|
||||
// cannot use and therefore bypass save_array / load_array interface, because
|
||||
// it requires exact type boost::serialization::array_wrapper<T>
|
||||
static_if_c<Archive::is_loading::value>(
|
||||
[this](auto& ar) { ar.load_binary(this->ptr, sizeof(T) * this->size); },
|
||||
[this](auto& ar) { ar.save_binary(this->ptr, sizeof(T) * this->size); },
|
||||
ar);
|
||||
},
|
||||
[this](auto& ar) {
|
||||
for (auto&& x : boost::histogram::detail::make_span(this->ptr, this->size))
|
||||
ar& make_nvp("item", x);
|
||||
},
|
||||
ar);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
auto make_array_wrapper(T* t, std::size_t s) {
|
||||
return array_wrapper<T>{t, s};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
439
install/boost_1_75_0/include/boost/histogram/detail/axes.hpp
Normal file
439
install/boost_1_75_0/include/boost/histogram/detail/axes.hpp
Normal file
@@ -0,0 +1,439 @@
|
||||
// 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 <array>
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/make_default.hpp>
|
||||
#include <boost/histogram/detail/nonmember_container_access.hpp>
|
||||
#include <boost/histogram/detail/optional_index.hpp>
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/histogram/detail/relaxed_tuple_size.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/detail/sub_array.hpp>
|
||||
#include <boost/histogram/detail/try_cast.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/integer_sequence.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class Unary>
|
||||
void for_each_axis_impl(dynamic_size, T& t, Unary& p) {
|
||||
for (auto& a : t) axis::visit(p, a);
|
||||
}
|
||||
|
||||
template <class N, class T, class Unary>
|
||||
void for_each_axis_impl(N, T& t, Unary& p) {
|
||||
mp11::tuple_for_each(t, p);
|
||||
}
|
||||
|
||||
// also matches const T and const Unary
|
||||
template <class T, class Unary>
|
||||
void for_each_axis(T&& t, Unary&& p) {
|
||||
for_each_axis_impl(relaxed_tuple_size(t), t, p);
|
||||
}
|
||||
|
||||
// merge if a and b are discrete and growing
|
||||
struct axis_merger {
|
||||
template <class T, class U>
|
||||
T operator()(const T& a, const U& u) {
|
||||
const T* bp = ptr_cast<T>(&u);
|
||||
if (!bp) BOOST_THROW_EXCEPTION(std::invalid_argument("axes not mergable"));
|
||||
using O = axis::traits::get_options<T>;
|
||||
constexpr bool discrete_and_growing =
|
||||
axis::traits::is_continuous<T>::value == false && O::test(axis::option::growth);
|
||||
return impl(mp11::mp_bool<discrete_and_growing>{}, a, *bp);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T impl(std::false_type, const T& a, const T& b) {
|
||||
if (!relaxed_equal{}(a, b))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes not mergable"));
|
||||
return a;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T impl(std::true_type, const T& a, const T& b) {
|
||||
if (relaxed_equal{}(axis::traits::metadata(a), axis::traits::metadata(b))) {
|
||||
auto r = a;
|
||||
if (axis::traits::is_ordered<T>::value) {
|
||||
r.update(b.value(0));
|
||||
r.update(b.value(b.size() - 1));
|
||||
} else
|
||||
for (auto&& v : b) r.update(v);
|
||||
return r;
|
||||
}
|
||||
return impl(std::false_type{}, a, b);
|
||||
}
|
||||
};
|
||||
|
||||
// create empty dynamic axis which can store any axes types from the argument
|
||||
template <class T>
|
||||
auto make_empty_dynamic_axes(const T& axes) {
|
||||
return make_default(axes);
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
auto make_empty_dynamic_axes(const std::tuple<Ts...>&) {
|
||||
using namespace ::boost::mp11;
|
||||
using L = mp_unique<axis::variant<Ts...>>;
|
||||
// return std::vector<axis::variant<Axis0, Axis1, ...>> or std::vector<Axis0>
|
||||
return std::vector<mp_if_c<(mp_size<L>::value == 1), mp_first<L>, L>>{};
|
||||
}
|
||||
|
||||
template <class T, class Functor, std::size_t... Is>
|
||||
auto axes_transform_impl(const T& t, Functor&& f, mp11::index_sequence<Is...>) {
|
||||
return std::make_tuple(f(Is, std::get<Is>(t))...);
|
||||
}
|
||||
|
||||
// warning: sequential order of functor execution is platform-dependent!
|
||||
template <class... Ts, class Functor>
|
||||
auto axes_transform(const std::tuple<Ts...>& old_axes, Functor&& f) {
|
||||
return axes_transform_impl(old_axes, std::forward<Functor>(f),
|
||||
mp11::make_index_sequence<sizeof...(Ts)>{});
|
||||
}
|
||||
|
||||
// changing axes type is not supported
|
||||
template <class T, class Functor>
|
||||
T axes_transform(const T& old_axes, Functor&& f) {
|
||||
T axes = make_default(old_axes);
|
||||
axes.reserve(old_axes.size());
|
||||
for_each_axis(old_axes, [&](const auto& a) { axes.emplace_back(f(axes.size(), a)); });
|
||||
return axes;
|
||||
}
|
||||
|
||||
template <class... Ts, class Binary, std::size_t... Is>
|
||||
std::tuple<Ts...> axes_transform_impl(const std::tuple<Ts...>& lhs,
|
||||
const std::tuple<Ts...>& rhs, Binary&& bin,
|
||||
mp11::index_sequence<Is...>) {
|
||||
return std::make_tuple(bin(std::get<Is>(lhs), std::get<Is>(rhs))...);
|
||||
}
|
||||
|
||||
template <class... Ts, class Binary>
|
||||
std::tuple<Ts...> axes_transform(const std::tuple<Ts...>& lhs,
|
||||
const std::tuple<Ts...>& rhs, Binary&& bin) {
|
||||
return axes_transform_impl(lhs, rhs, bin, mp11::make_index_sequence<sizeof...(Ts)>{});
|
||||
}
|
||||
|
||||
template <class T, class Binary>
|
||||
T axes_transform(const T& lhs, const T& rhs, Binary&& bin) {
|
||||
T ax = make_default(lhs);
|
||||
ax.reserve(lhs.size());
|
||||
using std::begin;
|
||||
auto ir = begin(rhs);
|
||||
for (auto&& li : lhs) {
|
||||
axis::visit(
|
||||
[&](const auto& li) {
|
||||
axis::visit([&](const auto& ri) { ax.emplace_back(bin(li, ri)); }, *ir);
|
||||
},
|
||||
li);
|
||||
++ir;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
unsigned axes_rank(const T& axes) {
|
||||
using std::begin;
|
||||
using std::end;
|
||||
return static_cast<unsigned>(std::distance(begin(axes), end(axes)));
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
constexpr unsigned axes_rank(const std::tuple<Ts...>&) {
|
||||
return static_cast<unsigned>(sizeof...(Ts));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void throw_if_axes_is_too_large(const T& axes) {
|
||||
if (axes_rank(axes) > BOOST_HISTOGRAM_DETAIL_AXES_LIMIT)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("length of axis vector exceeds internal buffers, "
|
||||
"recompile with "
|
||||
"-DBOOST_HISTOGRAM_DETAIL_AXES_LIMIT=<new max size> "
|
||||
"to increase internal buffers"));
|
||||
}
|
||||
|
||||
// tuple is never too large because internal buffers adapt to size of tuple
|
||||
template <class... Ts>
|
||||
void throw_if_axes_is_too_large(const std::tuple<Ts...>&) {}
|
||||
|
||||
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>
|
||||
auto axis_get(std::tuple<Ts...>& axes, const unsigned i) {
|
||||
constexpr auto S = sizeof...(Ts);
|
||||
using V = mp11::mp_unique<axis::variant<Ts*...>>;
|
||||
return mp11::mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
auto axis_get(const std::tuple<Ts...>& axes, const unsigned i) {
|
||||
constexpr auto S = sizeof...(Ts);
|
||||
using V = mp11::mp_unique<axis::variant<const Ts*...>>;
|
||||
return mp11::mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
|
||||
}
|
||||
|
||||
template <class T>
|
||||
decltype(auto) axis_get(T& axes, const unsigned i) {
|
||||
return axes[i];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
decltype(auto) axis_get(const T& axes, const unsigned i) {
|
||||
return axes[i];
|
||||
}
|
||||
|
||||
template <class T, class U, std::size_t... Is>
|
||||
bool axes_equal_impl(const T& t, const U& u, mp11::index_sequence<Is...>) noexcept {
|
||||
bool result = true;
|
||||
// operator folding emulation
|
||||
(void)std::initializer_list<bool>{
|
||||
(result &= relaxed_equal{}(std::get<Is>(t), std::get<Is>(u)))...};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class... Ts, class... Us>
|
||||
bool axes_equal_impl(const std::tuple<Ts...>& t, const std::tuple<Us...>& u) noexcept {
|
||||
return axes_equal_impl(
|
||||
t, u, mp11::make_index_sequence<std::min(sizeof...(Ts), sizeof...(Us))>{});
|
||||
}
|
||||
|
||||
template <class... Ts, class U>
|
||||
bool axes_equal_impl(const std::tuple<Ts...>& t, const U& u) noexcept {
|
||||
using std::begin;
|
||||
auto iu = begin(u);
|
||||
bool result = true;
|
||||
mp11::tuple_for_each(t, [&](const auto& ti) {
|
||||
axis::visit([&](const auto& ui) { result &= relaxed_equal{}(ti, ui); }, *iu);
|
||||
++iu;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class... Us>
|
||||
bool axes_equal_impl(const T& t, const std::tuple<Us...>& u) noexcept {
|
||||
return axes_equal_impl(u, t);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool axes_equal_impl(const T& t, const U& u) noexcept {
|
||||
using std::begin;
|
||||
auto iu = begin(u);
|
||||
bool result = true;
|
||||
for (auto&& ti : t) {
|
||||
axis::visit(
|
||||
[&](const auto& ti) {
|
||||
axis::visit([&](const auto& ui) { result &= relaxed_equal{}(ti, ui); }, *iu);
|
||||
},
|
||||
ti);
|
||||
++iu;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool axes_equal(const T& t, const U& u) noexcept {
|
||||
return axes_rank(t) == axes_rank(u) && axes_equal_impl(t, u);
|
||||
}
|
||||
|
||||
// enable_if_t needed by msvc :(
|
||||
template <class... Ts, class... Us>
|
||||
std::enable_if_t<!(std::is_same<std::tuple<Ts...>, std::tuple<Us...>>::value)>
|
||||
axes_assign(std::tuple<Ts...>&, const std::tuple<Us...>&) {
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot assign axes, types do not match"));
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
void axes_assign(std::tuple<Ts...>& t, const std::tuple<Ts...>& u) {
|
||||
t = u;
|
||||
}
|
||||
|
||||
template <class... Ts, class U>
|
||||
void axes_assign(std::tuple<Ts...>& t, const U& u) {
|
||||
if (sizeof...(Ts) == detail::size(u)) {
|
||||
using std::begin;
|
||||
auto iu = begin(u);
|
||||
mp11::tuple_for_each(t, [&](auto& ti) {
|
||||
using T = std::decay_t<decltype(ti)>;
|
||||
ti = axis::get<T>(*iu);
|
||||
++iu;
|
||||
});
|
||||
return;
|
||||
}
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot assign axes, sizes do not match"));
|
||||
}
|
||||
|
||||
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));
|
||||
using std::begin;
|
||||
auto it = begin(t);
|
||||
mp11::tuple_for_each(u, [&](const auto& ui) { *it++ = ui; });
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void axes_assign(T& t, const U& u) {
|
||||
t.assign(u.begin(), u.end());
|
||||
}
|
||||
|
||||
template <class Archive, class T>
|
||||
void axes_serialize(Archive& ar, T& axes) {
|
||||
ar& make_nvp("axes", axes);
|
||||
}
|
||||
|
||||
template <class Archive, class... Ts>
|
||||
void axes_serialize(Archive& ar, std::tuple<Ts...>& axes) {
|
||||
// needed to keep serialization format backward compatible
|
||||
struct proxy {
|
||||
std::tuple<Ts...>& t;
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
mp11::tuple_for_each(t, [&ar](auto& x) { ar& make_nvp("item", x); });
|
||||
}
|
||||
};
|
||||
proxy p{axes};
|
||||
ar& make_nvp("axes", p);
|
||||
}
|
||||
|
||||
// total number of bins including *flow bins
|
||||
template <class 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;
|
||||
}
|
||||
|
||||
// initial offset for the linear index
|
||||
template <class T>
|
||||
std::size_t offset(const T& axes) {
|
||||
std::size_t n = 0;
|
||||
auto stride = static_cast<std::size_t>(1);
|
||||
for_each_axis(axes, [&](const auto& a) {
|
||||
if (axis::traits::options(a) & axis::option::growth)
|
||||
n = invalid_index;
|
||||
else if (n != invalid_index && axis::traits::options(a) & axis::option::underflow)
|
||||
n += stride;
|
||||
stride *= axis::traits::extent(a);
|
||||
});
|
||||
return n;
|
||||
}
|
||||
|
||||
// make default-constructed buffer (no initialization for POD types)
|
||||
template <class T, class A>
|
||||
auto make_stack_buffer(const A& a) {
|
||||
return sub_array<T, buffer_size<A>::value>(axes_rank(a));
|
||||
}
|
||||
|
||||
// make buffer with elements initialized to v
|
||||
template <class T, class A>
|
||||
auto make_stack_buffer(const A& a, const T& t) {
|
||||
return sub_array<T, buffer_size<A>::value>(axes_rank(a), t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using has_underflow =
|
||||
decltype(axis::traits::get_options<T>::test(axis::option::underflow));
|
||||
|
||||
template <class T>
|
||||
using is_growing = decltype(axis::traits::get_options<T>::test(axis::option::growth));
|
||||
|
||||
template <class T>
|
||||
using is_not_inclusive = mp11::mp_not<axis::traits::is_inclusive<T>>;
|
||||
|
||||
// for vector<T>
|
||||
template <class T>
|
||||
struct axis_types_impl {
|
||||
using type = mp11::mp_list<std::decay_t<T>>;
|
||||
};
|
||||
|
||||
// for vector<variant<Ts...>>
|
||||
template <class... Ts>
|
||||
struct axis_types_impl<axis::variant<Ts...>> {
|
||||
using type = mp11::mp_list<std::decay_t<Ts>...>;
|
||||
};
|
||||
|
||||
// for tuple<Ts...>
|
||||
template <class... Ts>
|
||||
struct axis_types_impl<std::tuple<Ts...>> {
|
||||
using type = mp11::mp_list<std::decay_t<Ts>...>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using axis_types =
|
||||
typename axis_types_impl<mp11::mp_if<is_vector_like<T>, mp11::mp_first<T>, T>>::type;
|
||||
|
||||
template <template <class> class Trait, class Axes>
|
||||
using has_special_axis = mp11::mp_any_of<axis_types<Axes>, Trait>;
|
||||
|
||||
template <class Axes>
|
||||
using has_growing_axis = mp11::mp_any_of<axis_types<Axes>, is_growing>;
|
||||
|
||||
template <class Axes>
|
||||
using has_non_inclusive_axis = mp11::mp_any_of<axis_types<Axes>, is_not_inclusive>;
|
||||
|
||||
template <class T>
|
||||
constexpr std::size_t type_score() {
|
||||
return sizeof(T) *
|
||||
(std::is_integral<T>::value ? 1 : std::is_floating_point<T>::value ? 10 : 100);
|
||||
}
|
||||
|
||||
// arbitrary ordering of types
|
||||
template <class T, class U>
|
||||
using type_less = mp11::mp_bool<(type_score<T>() < type_score<U>())>;
|
||||
|
||||
template <class Axes>
|
||||
using value_types = mp11::mp_sort<
|
||||
mp11::mp_unique<mp11::mp_transform<axis::traits::value_type, axis_types<Axes>>>,
|
||||
type_less>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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/detect.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
|
||||
>;
|
||||
// clang-format on
|
||||
|
||||
// Non-PODs rank highest, then floats, than integers; types with more capacity are higher
|
||||
template <class Storage>
|
||||
constexpr std::size_t type_rank() {
|
||||
using T = typename Storage::value_type;
|
||||
return !std::is_arithmetic<T>::value * 10000 + std::is_floating_point<T>::value * 100 +
|
||||
10 * sizeof(T) + 2 * is_array_like<Storage>::value +
|
||||
is_vector_like<Storage>::value;
|
||||
;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
using common_storage = mp11::mp_if_c<(type_rank<T>() >= type_rank<U>()), T, U>;
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2018-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_DETAIL_CONVERT_INTEGER_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_CONVERT_INTEGER_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class U>
|
||||
using convert_integer =
|
||||
std::conditional_t<std::is_integral<std::decay_t<T>>::value, U, T>;
|
||||
|
||||
}
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 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_DETAIL_COUNTING_STREAMBUF_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_COUNTING_STREAMBUF_HPP
|
||||
|
||||
#include <boost/core/exchange.hpp>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
// detect how many characters will be printed by formatted output
|
||||
template <class CharT, class Traits = std::char_traits<CharT>>
|
||||
struct counting_streambuf : std::basic_streambuf<CharT, Traits> {
|
||||
using base_t = std::basic_streambuf<CharT, Traits>;
|
||||
using typename base_t::char_type;
|
||||
using typename base_t::int_type;
|
||||
|
||||
std::streamsize* p_count;
|
||||
|
||||
counting_streambuf(std::streamsize& c) : p_count(&c) {}
|
||||
|
||||
std::streamsize xsputn(const char_type* /* s */, std::streamsize n) override {
|
||||
*p_count += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
int_type overflow(int_type ch) override {
|
||||
++*p_count;
|
||||
return ch;
|
||||
}
|
||||
};
|
||||
|
||||
template <class C, class T>
|
||||
struct count_guard {
|
||||
using bos = std::basic_ostream<C, T>;
|
||||
using bsb = std::basic_streambuf<C, T>;
|
||||
|
||||
counting_streambuf<C, T> csb;
|
||||
bos* p_os;
|
||||
bsb* p_rdbuf;
|
||||
|
||||
count_guard(bos& os, std::streamsize& s) : csb(s), p_os(&os), p_rdbuf(os.rdbuf(&csb)) {}
|
||||
|
||||
count_guard(count_guard&& o)
|
||||
: csb(o.csb), p_os(boost::exchange(o.p_os, nullptr)), p_rdbuf(o.p_rdbuf) {}
|
||||
|
||||
count_guard& operator=(count_guard&& o) {
|
||||
if (this != &o) {
|
||||
csb = std::move(o.csb);
|
||||
p_os = boost::exchange(o.p_os, nullptr);
|
||||
p_rdbuf = o.p_rdbuf;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~count_guard() {
|
||||
if (p_os) p_os->rdbuf(p_rdbuf);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C, class T>
|
||||
count_guard<C, T> make_count_guard(std::basic_ostream<C, T>& os, std::streamsize& s) {
|
||||
return {os, s};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
207
install/boost_1_75_0/include/boost/histogram/detail/detect.hpp
Normal file
207
install/boost_1_75_0/include/boost/histogram/detail/detect.hpp
Normal file
@@ -0,0 +1,207 @@
|
||||
// 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_DETAIL_DETECT_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_DETECT_HPP
|
||||
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/function.hpp> // mp_and, mp_or
|
||||
#include <boost/mp11/integral.hpp> // mp_not
|
||||
#include <boost/mp11/list.hpp> // mp_first
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
// forward declaration
|
||||
namespace boost {
|
||||
namespace variant2 {
|
||||
template <class...>
|
||||
class variant;
|
||||
} // namespace variant2
|
||||
} // namespace boost
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
#define BOOST_HISTOGRAM_DETAIL_DETECT(name, cond) \
|
||||
template <class U> \
|
||||
struct name##_impl { \
|
||||
typedef char yes[1]; \
|
||||
typedef char no[2]; \
|
||||
template <class T> \
|
||||
static yes& test(T& t, decltype(cond, 0)); \
|
||||
template <class T> \
|
||||
static no& test(T&, float); \
|
||||
using type = \
|
||||
std::integral_constant<bool, (sizeof(test(std::declval<U&>(), 0)) == 1)>; \
|
||||
}; \
|
||||
template <class T> \
|
||||
using name = typename name##_impl<T>::type
|
||||
|
||||
#define BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(name, cond) \
|
||||
template <class V, class W> \
|
||||
struct name##_impl { \
|
||||
typedef char yes[1]; \
|
||||
typedef char no[2]; \
|
||||
template <class T, class U> \
|
||||
static yes& test(decltype(cond, 0)); \
|
||||
template <class, class> \
|
||||
static no& test(float); \
|
||||
using type = std::integral_constant<bool, (sizeof(test<V, W>(0)) == 1)>; \
|
||||
}; \
|
||||
template <class T, class U = T> \
|
||||
using name = typename name##_impl<T, U>::type
|
||||
|
||||
// reset has overloads, trying to get pmf in this case always fails
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(has_method_reset, t.reset(0));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_indexable, t[0]);
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(
|
||||
is_transform,
|
||||
(std::declval<T&>().inverse(std::declval<T&>().forward(std::declval<U>()))));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_indexable_container,
|
||||
(t[0], t.size(), std::begin(t), std::end(t)));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_vector_like,
|
||||
(t[0], t.size(), t.resize(0), std::begin(t), std::end(t)));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_array_like, (t[0], t.size(), std::tuple_size<T>::value,
|
||||
std::begin(t), std::end(t)));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_map_like, ((typename T::key_type*)nullptr,
|
||||
(typename T::mapped_type*)nullptr,
|
||||
std::begin(t), std::end(t)));
|
||||
|
||||
// ok: is_axis is false for axis::variant, because T::index is templated
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_axis, (t.size(), &T::index));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_iterable, (std::begin(t), std::end(t)));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_iterator,
|
||||
(typename std::iterator_traits<T>::iterator_category{}));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(is_streamable, (std::declval<std::ostream&>() << t));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(has_operator_preincrement, ++t);
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_equal, (std::declval<const T&>() ==
|
||||
std::declval<const U&>()));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_radd,
|
||||
(std::declval<T&>() += std::declval<U>()));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_rsub,
|
||||
(std::declval<T&>() -= std::declval<U>()));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_rmul,
|
||||
(std::declval<T&>() *= std::declval<U>()));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_rdiv,
|
||||
(std::declval<T&>() /= std::declval<U>()));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(
|
||||
has_method_eq, (std::declval<const T&>().operator==(std::declval<const U&>())));
|
||||
|
||||
BOOST_HISTOGRAM_DETAIL_DETECT(has_threading_support, (T::has_threading_support));
|
||||
|
||||
template <class T>
|
||||
using is_storage = mp11::mp_and<is_indexable_container<T>, has_method_reset<T>,
|
||||
has_threading_support<T>>;
|
||||
|
||||
template <class T>
|
||||
using is_adaptible =
|
||||
mp11::mp_and<mp11::mp_not<is_storage<T>>,
|
||||
mp11::mp_or<is_vector_like<T>, is_array_like<T>, is_map_like<T>>>;
|
||||
|
||||
template <class T>
|
||||
struct is_tuple_impl : std::false_type {};
|
||||
|
||||
template <class... Ts>
|
||||
struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
using is_tuple = typename is_tuple_impl<T>::type;
|
||||
|
||||
template <class T>
|
||||
struct is_variant_impl : std::false_type {};
|
||||
|
||||
template <class... Ts>
|
||||
struct is_variant_impl<boost::variant2::variant<Ts...>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
using is_variant = typename is_variant_impl<T>::type;
|
||||
|
||||
template <class T>
|
||||
struct is_axis_variant_impl : std::false_type {};
|
||||
|
||||
template <class... Ts>
|
||||
struct is_axis_variant_impl<axis::variant<Ts...>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
using is_axis_variant = typename is_axis_variant_impl<T>::type;
|
||||
|
||||
template <class T>
|
||||
using is_any_axis = mp11::mp_or<is_axis<T>, is_axis_variant<T>>;
|
||||
|
||||
template <class T>
|
||||
using is_sequence_of_axis = mp11::mp_and<is_iterable<T>, is_axis<mp11::mp_first<T>>>;
|
||||
|
||||
template <class T>
|
||||
using is_sequence_of_axis_variant =
|
||||
mp11::mp_and<is_iterable<T>, is_axis_variant<mp11::mp_first<T>>>;
|
||||
|
||||
template <class T>
|
||||
using is_sequence_of_any_axis =
|
||||
mp11::mp_and<is_iterable<T>, is_any_axis<mp11::mp_first<T>>>;
|
||||
|
||||
// poor-mans concept checks
|
||||
template <class T, class = std::enable_if_t<is_storage<std::decay_t<T>>::value>>
|
||||
struct requires_storage {};
|
||||
|
||||
template <class T, class _ = std::decay_t<T>,
|
||||
class = std::enable_if_t<(is_storage<_>::value || is_adaptible<_>::value)>>
|
||||
struct requires_storage_or_adaptible {};
|
||||
|
||||
template <class T, class = std::enable_if_t<is_iterator<std::decay_t<T>>::value>>
|
||||
struct requires_iterator {};
|
||||
|
||||
template <class T, class = std::enable_if_t<
|
||||
is_iterable<std::remove_cv_t<std::remove_reference_t<T>>>::value>>
|
||||
struct requires_iterable {};
|
||||
|
||||
template <class T, class = std::enable_if_t<is_axis<std::decay_t<T>>::value>>
|
||||
struct requires_axis {};
|
||||
|
||||
template <class T, class = std::enable_if_t<is_any_axis<std::decay_t<T>>::value>>
|
||||
struct requires_any_axis {};
|
||||
|
||||
template <class T, class = std::enable_if_t<is_sequence_of_axis<std::decay_t<T>>::value>>
|
||||
struct requires_sequence_of_axis {};
|
||||
|
||||
template <class T,
|
||||
class = std::enable_if_t<is_sequence_of_axis_variant<std::decay_t<T>>::value>>
|
||||
struct requires_sequence_of_axis_variant {};
|
||||
|
||||
template <class T,
|
||||
class = std::enable_if_t<is_sequence_of_any_axis<std::decay_t<T>>::value>>
|
||||
struct requires_sequence_of_any_axis {};
|
||||
|
||||
template <class T,
|
||||
class = std::enable_if_t<is_any_axis<mp11::mp_first<std::decay_t<T>>>::value>>
|
||||
struct requires_axes {};
|
||||
|
||||
template <class T, class U,
|
||||
class = std::enable_if_t<is_transform<std::decay_t<T>, U>::value>>
|
||||
struct requires_transform {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
348
install/boost_1_75_0/include/boost/histogram/detail/fill.hpp
Normal file
348
install/boost_1_75_0/include/boost/histogram/detail/fill.hpp
Normal file
@@ -0,0 +1,348 @@
|
||||
// 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_FILL_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_FILL_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/argument_traits.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/linearize.hpp>
|
||||
#include <boost/histogram/detail/make_default.hpp>
|
||||
#include <boost/histogram/detail/optional_index.hpp>
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/histogram/detail/tuple_slice.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/integral.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class U>
|
||||
struct sample_args_passed_vs_expected;
|
||||
|
||||
template <class... Passed, class... Expected>
|
||||
struct sample_args_passed_vs_expected<std::tuple<Passed...>, std::tuple<Expected...>> {
|
||||
static_assert(!(sizeof...(Expected) > 0 && sizeof...(Passed) == 0),
|
||||
"error: accumulator requires samples, but sample argument is missing");
|
||||
static_assert(
|
||||
!(sizeof...(Passed) > 0 && sizeof...(Expected) == 0),
|
||||
"error: accumulator does not accept samples, but sample argument is passed");
|
||||
static_assert(sizeof...(Passed) == sizeof...(Expected),
|
||||
"error: numbers of passed and expected sample arguments differ");
|
||||
static_assert(
|
||||
std::is_convertible<std::tuple<Passed...>, std::tuple<Expected...>>::value,
|
||||
"error: sample argument(s) not convertible to accumulator argument(s)");
|
||||
};
|
||||
|
||||
template <class A>
|
||||
struct storage_grower {
|
||||
const A& axes_;
|
||||
struct {
|
||||
axis::index_type idx, old_extent;
|
||||
std::size_t new_stride;
|
||||
} data_[buffer_size<A>::value];
|
||||
std::size_t new_size_;
|
||||
|
||||
storage_grower(const A& axes) noexcept : axes_(axes) {}
|
||||
|
||||
void from_shifts(const axis::index_type* shifts) noexcept {
|
||||
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(*shifts++), s};
|
||||
s *= n;
|
||||
});
|
||||
new_size_ = s;
|
||||
}
|
||||
|
||||
// must be extents before any shifts were applied
|
||||
void from_extents(const axis::index_type* old_extents) noexcept {
|
||||
auto dit = data_;
|
||||
std::size_t s = 1;
|
||||
for_each_axis(axes_, [&](const auto& a) {
|
||||
const auto n = axis::traits::extent(a);
|
||||
*dit++ = {0, *old_extents++, s};
|
||||
s *= n;
|
||||
});
|
||||
new_size_ = s;
|
||||
}
|
||||
|
||||
template <class S>
|
||||
void apply(S& storage, const axis::index_type* shifts) {
|
||||
auto new_storage = make_default(storage);
|
||||
new_storage.reset(new_size_);
|
||||
const auto dlast = data_ + axes_rank(axes_) - 1;
|
||||
for (auto&& x : storage) {
|
||||
auto ns = new_storage.begin();
|
||||
auto sit = shifts;
|
||||
auto dit = data_;
|
||||
for_each_axis(axes_, [&](const auto& a) {
|
||||
using opt = axis::traits::get_options<std::decay_t<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 if any
|
||||
ns += (dit->idx + (*sit >= 0 ? *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);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class... Us>
|
||||
auto fill_storage_element_impl(priority<2>, T&& t, const Us&... args) noexcept
|
||||
-> decltype(t(args...), void()) {
|
||||
t(args...);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
auto fill_storage_element_impl(priority<1>, T&& t, const weight_type<U>& w) noexcept
|
||||
-> decltype(t += w, void()) {
|
||||
t += w;
|
||||
}
|
||||
|
||||
// fallback for arithmetic types and accumulators that do not handle the weight
|
||||
template <class T, class U>
|
||||
auto fill_storage_element_impl(priority<0>, T&& t, const weight_type<U>& w) noexcept
|
||||
-> decltype(t += w.value, void()) {
|
||||
t += w.value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto fill_storage_element_impl(priority<1>, T&& t) noexcept -> decltype(++t, void()) {
|
||||
++t;
|
||||
}
|
||||
|
||||
template <class T, class... Us>
|
||||
void fill_storage_element(T&& t, const Us&... args) noexcept {
|
||||
fill_storage_element_impl(priority<2>{}, std::forward<T>(t), args...);
|
||||
}
|
||||
|
||||
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||
template <class IW, class IS, class T, class U>
|
||||
void fill_storage_2(IW, IS, T&& t, U&& u) noexcept {
|
||||
mp11::tuple_apply(
|
||||
[&](const auto&... args) {
|
||||
fill_storage_element(std::forward<T>(t), std::get<IW::value>(u), args...);
|
||||
},
|
||||
std::get<IS::value>(u).value);
|
||||
}
|
||||
|
||||
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||
template <class IS, class T, class U>
|
||||
void fill_storage_2(mp11::mp_int<-1>, IS, T&& t, const U& u) noexcept {
|
||||
mp11::tuple_apply(
|
||||
[&](const auto&... args) { fill_storage_element(std::forward<T>(t), args...); },
|
||||
std::get<IS::value>(u).value);
|
||||
}
|
||||
|
||||
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||
template <class IW, class T, class U>
|
||||
void fill_storage_2(IW, mp11::mp_int<-1>, T&& t, const U& u) noexcept {
|
||||
fill_storage_element(std::forward<T>(t), std::get<IW::value>(u));
|
||||
}
|
||||
|
||||
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
|
||||
template <class T, class U>
|
||||
void fill_storage_2(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, const U&) noexcept {
|
||||
fill_storage_element(std::forward<T>(t));
|
||||
}
|
||||
|
||||
template <class IW, class IS, class Storage, class Index, class Args>
|
||||
auto fill_storage(IW, IS, Storage& s, const Index idx, const Args& a) noexcept {
|
||||
if (is_valid(idx)) {
|
||||
assert(idx < s.size());
|
||||
fill_storage_2(IW{}, IS{}, s[idx], a);
|
||||
return s.begin() + idx;
|
||||
}
|
||||
return s.end();
|
||||
}
|
||||
|
||||
template <int S, int N>
|
||||
struct linearize_args {
|
||||
template <class Index, class A, class Args>
|
||||
static void impl(mp11::mp_int<N>, Index&, const std::size_t, A&, const Args&) {}
|
||||
|
||||
template <int I, class Index, class A, class Args>
|
||||
static void impl(mp11::mp_int<I>, Index& o, const std::size_t s, A& ax,
|
||||
const Args& args) {
|
||||
const auto e = linearize(o, s, axis_get<I>(ax), std::get<(S + I)>(args));
|
||||
impl(mp11::mp_int<(I + 1)>{}, o, s * e, ax, args);
|
||||
}
|
||||
|
||||
template <class Index, class A, class Args>
|
||||
static void apply(Index& o, A& ax, const Args& args) {
|
||||
impl(mp11::mp_int<0>{}, o, 1, ax, args);
|
||||
}
|
||||
};
|
||||
|
||||
template <int S>
|
||||
struct linearize_args<S, 1> {
|
||||
template <class Index, class A, class Args>
|
||||
static void apply(Index& o, A& ax, const Args& args) {
|
||||
linearize(o, 1, axis_get<0>(ax), std::get<S>(args));
|
||||
}
|
||||
};
|
||||
|
||||
template <class A>
|
||||
constexpr unsigned min(const unsigned n) noexcept {
|
||||
constexpr unsigned a = buffer_size<A>::value;
|
||||
return a < n ? a : n;
|
||||
}
|
||||
|
||||
// not growing
|
||||
template <class ArgTraits, class Storage, class Axes, class Args>
|
||||
auto fill_2(ArgTraits, mp11::mp_false, const std::size_t offset, Storage& st,
|
||||
const Axes& axes, const Args& args) {
|
||||
mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{offset};
|
||||
linearize_args<ArgTraits::start::value, min<Axes>(ArgTraits::nargs::value)>::apply(
|
||||
idx, axes, args);
|
||||
return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
|
||||
args);
|
||||
}
|
||||
|
||||
// at least one axis is growing
|
||||
template <class ArgTraits, class Storage, class Axes, class Args>
|
||||
auto fill_2(ArgTraits, mp11::mp_true, const std::size_t, Storage& st, Axes& axes,
|
||||
const Args& args) {
|
||||
std::array<axis::index_type, ArgTraits::nargs::value> shifts;
|
||||
// offset must be zero for linearize_growth
|
||||
mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{0};
|
||||
std::size_t stride = 1;
|
||||
bool update_needed = false;
|
||||
mp11::mp_for_each<mp11::mp_iota_c<min<Axes>(ArgTraits::nargs::value)>>([&](auto i) {
|
||||
auto& ax = axis_get<i>(axes);
|
||||
const auto extent = linearize_growth(idx, shifts[i], stride, ax,
|
||||
std::get<(ArgTraits::start::value + i)>(args));
|
||||
update_needed |= shifts[i] != 0;
|
||||
stride *= extent;
|
||||
});
|
||||
if (update_needed) {
|
||||
storage_grower<Axes> g(axes);
|
||||
g.from_shifts(shifts.data());
|
||||
g.apply(st, shifts.data());
|
||||
}
|
||||
return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
|
||||
args);
|
||||
}
|
||||
|
||||
// pack original args tuple into another tuple (which is unpacked later)
|
||||
template <int Start, int Size, class IW, class IS, class Args>
|
||||
decltype(auto) pack_args(IW, IS, const Args& args) noexcept {
|
||||
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args),
|
||||
std::get<IS::value>(args));
|
||||
}
|
||||
|
||||
template <int Start, int Size, class IW, class Args>
|
||||
decltype(auto) pack_args(IW, mp11::mp_int<-1>, const Args& args) noexcept {
|
||||
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args));
|
||||
}
|
||||
|
||||
template <int Start, int Size, class IS, class Args>
|
||||
decltype(auto) pack_args(mp11::mp_int<-1>, IS, const Args& args) noexcept {
|
||||
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IS::value>(args));
|
||||
}
|
||||
|
||||
template <int Start, int Size, class Args>
|
||||
decltype(auto) pack_args(mp11::mp_int<-1>, mp11::mp_int<-1>, const Args& args) noexcept {
|
||||
return std::make_tuple(args);
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
|
||||
#pragma warning(disable : 4702) // fixing warning would reduce code readability a lot
|
||||
#endif
|
||||
|
||||
template <class ArgTraits, class S, class A, class Args>
|
||||
auto fill(std::true_type, ArgTraits, const std::size_t offset, S& storage, A& axes,
|
||||
const Args& args) -> typename S::iterator {
|
||||
using growing = has_growing_axis<A>;
|
||||
|
||||
// Sometimes we need to pack the tuple into another tuple:
|
||||
// - histogram contains one axis which accepts tuple
|
||||
// - user passes tuple to fill(...)
|
||||
// Tuple is normally unpacked and arguments are processed, this causes pos::nargs > 1.
|
||||
// Now we pack tuple into another tuple so that original tuple is send to axis.
|
||||
// Notes:
|
||||
// - 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)
|
||||
|
||||
if (axes_rank(axes) == ArgTraits::nargs::value)
|
||||
return fill_2(ArgTraits{}, growing{}, offset, storage, axes, args);
|
||||
else if (axes_rank(axes) == 1 &&
|
||||
axis::traits::rank(axis_get<0>(axes)) == ArgTraits::nargs::value)
|
||||
return fill_2(
|
||||
argument_traits_holder<
|
||||
1, 0, (ArgTraits::wpos::value >= 0 ? 1 : -1),
|
||||
(ArgTraits::spos::value >= 0 ? (ArgTraits::wpos::value >= 0 ? 2 : 1) : -1),
|
||||
typename ArgTraits::sargs>{},
|
||||
growing{}, offset, storage, axes,
|
||||
pack_args<ArgTraits::start::value, ArgTraits::nargs::value>(
|
||||
typename ArgTraits::wpos{}, typename ArgTraits::spos{}, args));
|
||||
return BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("number of arguments != histogram rank")),
|
||||
storage.end();
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
|
||||
#pragma warning(default : 4702)
|
||||
#endif
|
||||
|
||||
// empty implementation for bad arguments to stop compiler from showing internals
|
||||
template <class ArgTraits, class S, class A, class Args>
|
||||
auto fill(std::false_type, ArgTraits, const std::size_t, S& storage, A&, const Args&) ->
|
||||
typename S::iterator {
|
||||
return storage.end();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
339
install/boost_1_75_0/include/boost/histogram/detail/fill_n.hpp
Normal file
339
install/boost_1_75_0/include/boost/histogram/detail/fill_n.hpp
Normal file
@@ -0,0 +1,339 @@
|
||||
// Copyright 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_DETAIL_FILL_N_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_FILL_N_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/fill.hpp>
|
||||
#include <boost/histogram/detail/linearize.hpp>
|
||||
#include <boost/histogram/detail/nonmember_container_access.hpp>
|
||||
#include <boost/histogram/detail/optional_index.hpp>
|
||||
#include <boost/histogram/detail/span.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/bind.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
namespace dtl = boost::histogram::detail;
|
||||
|
||||
template <class Axes, class T>
|
||||
using is_convertible_to_any_value_type =
|
||||
mp11::mp_any_of_q<value_types<Axes>, mp11::mp_bind_front<std::is_convertible, T>>;
|
||||
|
||||
template <class T>
|
||||
auto to_ptr_size(const T& x) {
|
||||
return static_if<std::is_scalar<T>>(
|
||||
[](const auto& x) { return std::make_pair(&x, static_cast<std::size_t>(0)); },
|
||||
[](const auto& x) { return std::make_pair(dtl::data(x), dtl::size(x)); }, x);
|
||||
}
|
||||
|
||||
template <class F, class V>
|
||||
decltype(auto) maybe_visit(F&& f, V&& v) {
|
||||
return static_if<is_variant<std::decay_t<V>>>(
|
||||
[](auto&& f, auto&& v) {
|
||||
return variant2::visit(std::forward<F>(f), std::forward<V>(v));
|
||||
},
|
||||
[](auto&& f, auto&& v) { return std::forward<F>(f)(std::forward<V>(v)); },
|
||||
std::forward<F>(f), std::forward<V>(v));
|
||||
}
|
||||
|
||||
template <class Index, class Axis, class IsGrowing>
|
||||
struct index_visitor {
|
||||
using index_type = Index;
|
||||
using pointer = index_type*;
|
||||
using value_type = axis::traits::value_type<Axis>;
|
||||
using Opt = axis::traits::get_options<Axis>;
|
||||
|
||||
Axis& axis_;
|
||||
const std::size_t stride_, start_, size_; // start and size of value collection
|
||||
const pointer begin_;
|
||||
axis::index_type* shift_;
|
||||
|
||||
index_visitor(Axis& a, std::size_t& str, const std::size_t& sta, const std::size_t& si,
|
||||
const pointer it, axis::index_type* shift)
|
||||
: axis_(a), stride_(str), start_(sta), size_(si), begin_(it), shift_(shift) {}
|
||||
|
||||
template <class T>
|
||||
void call_2(std::true_type, pointer it, const T& x) const {
|
||||
// must use this code for all axes if one of them is growing
|
||||
axis::index_type shift;
|
||||
linearize_growth(*it, shift, stride_, axis_,
|
||||
try_cast<value_type, std::invalid_argument>(x));
|
||||
if (shift > 0) { // shift previous indices, because axis zero-point has changed
|
||||
while (it != begin_) *--it += static_cast<std::size_t>(shift) * stride_;
|
||||
*shift_ += shift;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void call_2(std::false_type, pointer it, const T& x) const {
|
||||
// no axis is growing
|
||||
linearize(*it, stride_, axis_, try_cast<value_type, std::invalid_argument>(x));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void call_1(std::false_type, const T& iterable) const {
|
||||
// T is iterable; fill N values
|
||||
const auto* tp = dtl::data(iterable) + start_;
|
||||
for (auto it = begin_; it != begin_ + size_; ++it) call_2(IsGrowing{}, it, *tp++);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void call_1(std::true_type, const T& value) const {
|
||||
// T is compatible value; fill single value N times
|
||||
index_type idx{*begin_};
|
||||
call_2(IsGrowing{}, &idx, value);
|
||||
if (is_valid(idx)) {
|
||||
const auto delta =
|
||||
static_cast<std::intptr_t>(idx) - static_cast<std::intptr_t>(*begin_);
|
||||
for (auto&& i : make_span(begin_, size_)) i += delta;
|
||||
} else
|
||||
std::fill(begin_, begin_ + size_, invalid_index);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void operator()(const T& iterable_or_value) const {
|
||||
call_1(mp11::mp_bool<(std::is_convertible<T, value_type>::value ||
|
||||
!is_iterable<T>::value)>{},
|
||||
iterable_or_value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Index, class S, class Axes, class T>
|
||||
void fill_n_indices(Index* indices, const std::size_t start, const std::size_t size,
|
||||
const std::size_t offset, S& storage, Axes& axes, const T* viter) {
|
||||
axis::index_type extents[buffer_size<Axes>::value];
|
||||
axis::index_type shifts[buffer_size<Axes>::value];
|
||||
for_each_axis(axes, [eit = extents, sit = shifts](const auto& a) mutable {
|
||||
*sit++ = 0;
|
||||
*eit++ = axis::traits::extent(a);
|
||||
}); // LCOV_EXCL_LINE: gcc-8 is missing this line for no reason
|
||||
|
||||
// offset must be zero for growing axes
|
||||
using IsGrowing = has_growing_axis<Axes>;
|
||||
std::fill(indices, indices + size, IsGrowing::value ? 0 : offset);
|
||||
for_each_axis(axes, [&, stride = static_cast<std::size_t>(1),
|
||||
pshift = shifts](auto& axis) mutable {
|
||||
using Axis = std::decay_t<decltype(axis)>;
|
||||
maybe_visit(
|
||||
index_visitor<Index, Axis, IsGrowing>{axis, stride, start, size, indices, pshift},
|
||||
*viter++);
|
||||
stride *= static_cast<std::size_t>(axis::traits::extent(axis));
|
||||
++pshift;
|
||||
});
|
||||
|
||||
bool update_needed = false;
|
||||
for_each_axis(axes, [&update_needed, eit = extents](const auto& a) mutable {
|
||||
update_needed |= *eit++ != axis::traits::extent(a);
|
||||
});
|
||||
if (update_needed) {
|
||||
storage_grower<Axes> g(axes);
|
||||
g.from_extents(extents);
|
||||
g.apply(storage, shifts);
|
||||
}
|
||||
}
|
||||
|
||||
template <class S, class Index, class... Ts>
|
||||
void fill_n_storage(S& s, const Index idx, Ts&&... p) noexcept {
|
||||
if (is_valid(idx)) {
|
||||
assert(idx < s.size());
|
||||
fill_storage_element(s[idx], *p.first...);
|
||||
}
|
||||
// operator folding emulation
|
||||
(void)std::initializer_list<int>{(p.second ? (++p.first, 0) : 0)...};
|
||||
}
|
||||
|
||||
template <class S, class Index, class T, class... Ts>
|
||||
void fill_n_storage(S& s, const Index idx, weight_type<T>&& w, Ts&&... ps) noexcept {
|
||||
if (is_valid(idx)) {
|
||||
assert(idx < s.size());
|
||||
fill_storage_element(s[idx], weight(*w.value.first), *ps.first...);
|
||||
}
|
||||
if (w.value.second) ++w.value.first;
|
||||
// operator folding emulation
|
||||
(void)std::initializer_list<int>{(ps.second ? (++ps.first, 0) : 0)...};
|
||||
}
|
||||
|
||||
// general Nd treatment
|
||||
template <class Index, class S, class A, class T, class... Ts>
|
||||
void fill_n_nd(const std::size_t offset, S& storage, A& axes, const std::size_t vsize,
|
||||
const T* values, Ts&&... ts) {
|
||||
constexpr std::size_t buffer_size = 1ul << 14;
|
||||
Index indices[buffer_size];
|
||||
|
||||
/*
|
||||
Parallelization options.
|
||||
|
||||
A) Run the whole fill2 method in parallel, each thread fills its own buffer of
|
||||
indices, synchronization (atomics) are needed to synchronize the incrementing of
|
||||
the storage cells. This leads to a lot of congestion for small histograms.
|
||||
|
||||
B) Run only fill_n_indices in parallel, subsections of the indices buffer
|
||||
can be filled by different threads. The final loop that fills the storage runs
|
||||
in the main thread, this requires no synchronization for the storage, cells do
|
||||
not need to support atomic operations.
|
||||
|
||||
C) Like B), then sort the indices in the main thread and fill the
|
||||
storage in parallel, where each thread uses a disjunct set of indices. This
|
||||
should create less congestion and requires no synchronization for the storage.
|
||||
|
||||
Note on C): Let's say we have an axis with 5 bins (with *flow to simplify).
|
||||
Then after filling 10 values, converting to indices and sorting, the index
|
||||
buffer may look like this: 0 0 0 1 2 2 2 4 4 5. Let's use two threads to fill
|
||||
the storage. Still in the main thread, we compute an iterator to the middle of
|
||||
the index buffer and move it to the right until the pointee changes. Now we have
|
||||
two ranges which contain disjunct sets of indices. We pass these ranges to the
|
||||
threads which then fill the storage. Since the threads by construction do not
|
||||
compete to increment the same cell, no further synchronization is required.
|
||||
|
||||
In all cases, growing axes cannot be parallelized.
|
||||
*/
|
||||
|
||||
for (std::size_t start = 0; start < vsize; start += buffer_size) {
|
||||
const std::size_t n = std::min(buffer_size, vsize - start);
|
||||
// fill buffer of indices...
|
||||
fill_n_indices(indices, start, n, offset, storage, axes, values);
|
||||
// ...and fill corresponding storage cells
|
||||
for (auto&& idx : make_span(indices, n))
|
||||
fill_n_storage(storage, idx, std::forward<Ts>(ts)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <class S, class... As, class T, class... Us>
|
||||
void fill_n_1(const std::size_t offset, S& storage, std::tuple<As...>& axes,
|
||||
const std::size_t vsize, const T* values, Us&&... us) {
|
||||
using index_type =
|
||||
mp11::mp_if<has_non_inclusive_axis<std::tuple<As...>>, optional_index, std::size_t>;
|
||||
fill_n_nd<index_type>(offset, storage, axes, vsize, values, std::forward<Us>(us)...);
|
||||
}
|
||||
|
||||
template <class S, class A, class T, class... Us>
|
||||
void fill_n_1(const std::size_t offset, S& storage, A& axes, const std::size_t vsize,
|
||||
const T* values, Us&&... us) {
|
||||
bool all_inclusive = true;
|
||||
for_each_axis(axes,
|
||||
[&](const auto& ax) { all_inclusive &= axis::traits::inclusive(ax); });
|
||||
if (axes_rank(axes) == 1) {
|
||||
axis::visit(
|
||||
[&](auto& ax) {
|
||||
std::tuple<decltype(ax)> axes{ax};
|
||||
fill_n_1(offset, storage, axes, vsize, values, std::forward<Us>(us)...);
|
||||
},
|
||||
axes[0]);
|
||||
} else {
|
||||
if (all_inclusive)
|
||||
fill_n_nd<std::size_t>(offset, storage, axes, vsize, values,
|
||||
std::forward<Us>(us)...);
|
||||
else
|
||||
fill_n_nd<optional_index>(offset, storage, axes, vsize, values,
|
||||
std::forward<Us>(us)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <class A, class T, std::size_t N>
|
||||
std::size_t get_total_size(const A& axes, const dtl::span<const T, N>& values) {
|
||||
// supported cases (T = value type; CT = containter of T; V<T, CT, ...> = variant):
|
||||
// - span<CT, N>: for any histogram, N == rank
|
||||
// - span<V<T, CT>, N>: for any histogram, N == rank
|
||||
assert(axes_rank(axes) == values.size());
|
||||
constexpr auto unset = static_cast<std::size_t>(-1);
|
||||
std::size_t size = unset;
|
||||
for_each_axis(axes, [&size, vit = values.begin()](const auto& ax) mutable {
|
||||
using AV = axis::traits::value_type<std::decay_t<decltype(ax)>>;
|
||||
maybe_visit(
|
||||
[&size](const auto& v) {
|
||||
// v is either convertible to value or a sequence of values
|
||||
using V = std::remove_const_t<std::remove_reference_t<decltype(v)>>;
|
||||
static_if_c<(std::is_convertible<decltype(v), AV>::value ||
|
||||
!is_iterable<V>::value)>(
|
||||
[](const auto&) {},
|
||||
[&size](const auto& v) {
|
||||
const auto n = dtl::size(v);
|
||||
// must repeat this here for msvc :(
|
||||
constexpr auto unset = static_cast<std::size_t>(-1);
|
||||
if (size == unset)
|
||||
size = dtl::size(v);
|
||||
else if (size != n)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("spans must have compatible lengths"));
|
||||
},
|
||||
v);
|
||||
},
|
||||
*vit++);
|
||||
});
|
||||
// if all arguments are not iterables, return size of 1
|
||||
return size == unset ? 1 : size;
|
||||
}
|
||||
|
||||
inline void fill_n_check_extra_args(std::size_t) noexcept {}
|
||||
|
||||
template <class T, class... Ts>
|
||||
void fill_n_check_extra_args(std::size_t size, T&& x, Ts&&... ts) {
|
||||
// sequences must have same lengths, but sequences of length 0 are broadcast
|
||||
if (x.second != 0 && x.second != size)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("spans must have compatible lengths"));
|
||||
fill_n_check_extra_args(size, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template <class T, class... Ts>
|
||||
void fill_n_check_extra_args(std::size_t size, weight_type<T>&& w, Ts&&... ts) {
|
||||
fill_n_check_extra_args(size, w.value, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template <class S, class A, class T, std::size_t N, class... Us>
|
||||
void fill_n(std::true_type, const std::size_t offset, S& storage, A& axes,
|
||||
const dtl::span<const T, N> values, Us&&... us) {
|
||||
// supported cases (T = value type; CT = containter of T; V<T, CT, ...> = variant):
|
||||
// - span<T, N>: only valid for 1D histogram, N > 1 allowed
|
||||
// - span<CT, N>: for any histogram, N == rank
|
||||
// - span<V<T, CT>, N>: for any histogram, N == rank
|
||||
static_if<is_convertible_to_any_value_type<A, T>>(
|
||||
[&](const auto& values, auto&&... us) {
|
||||
// T matches one of the axis value types, must be 1D special case
|
||||
if (axes_rank(axes) != 1)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("number of arguments must match histogram rank"));
|
||||
fill_n_check_extra_args(values.size(), std::forward<Us>(us)...);
|
||||
fill_n_1(offset, storage, axes, values.size(), &values, std::forward<Us>(us)...);
|
||||
},
|
||||
[&](const auto& values, auto&&... us) {
|
||||
// generic ND case
|
||||
if (axes_rank(axes) != values.size())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("number of arguments must match histogram rank"));
|
||||
const auto vsize = get_total_size(axes, values);
|
||||
fill_n_check_extra_args(vsize, std::forward<Us>(us)...);
|
||||
fill_n_1(offset, storage, axes, vsize, values.data(), std::forward<Us>(us)...);
|
||||
},
|
||||
values, std::forward<Us>(us)...);
|
||||
}
|
||||
|
||||
// empty implementation for bad arguments to stop compiler from showing internals
|
||||
template <class... Ts>
|
||||
void fill_n(std::false_type, Ts...) {}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DETAIL_FILL_N_HPP
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright 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_DETAIL_INDEX_TRANSLATOR_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_INDEX_TRANSLATOR_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/relaxed_tuple_size.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/multi_index.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/integer_sequence.hpp>
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class A>
|
||||
struct index_translator {
|
||||
using index_type = axis::index_type;
|
||||
using multi_index_type = multi_index<relaxed_tuple_size_t<A>::value>;
|
||||
using cref = const A&;
|
||||
|
||||
cref dst, src;
|
||||
bool pass_through[buffer_size<A>::value];
|
||||
|
||||
index_translator(cref d, cref s) : dst{d}, src{s} { init(dst, src); }
|
||||
|
||||
template <class T>
|
||||
void init(const T& a, const T& b) {
|
||||
std::transform(a.begin(), a.end(), b.begin(), pass_through,
|
||||
[](const auto& a, const auto& b) {
|
||||
return axis::visit(
|
||||
[&](const auto& a) {
|
||||
using U = std::decay_t<decltype(a)>;
|
||||
return relaxed_equal{}(a, axis::get<U>(b));
|
||||
},
|
||||
a);
|
||||
});
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
void init(const std::tuple<Ts...>& a, const std::tuple<Ts...>& b) {
|
||||
using Seq = mp11::mp_iota_c<sizeof...(Ts)>;
|
||||
mp11::mp_for_each<Seq>([&](auto I) {
|
||||
pass_through[I] = relaxed_equal{}(std::get<I>(a), std::get<I>(b));
|
||||
});
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static index_type translate(const T& dst, const T& src, index_type i) noexcept {
|
||||
assert(axis::traits::is_continuous<T>::value == false); // LCOV_EXCL_LINE: unreachable
|
||||
return dst.index(src.value(i));
|
||||
}
|
||||
|
||||
template <class... Ts, class It>
|
||||
void impl(const std::tuple<Ts...>& a, const std::tuple<Ts...>& b, It i,
|
||||
index_type* j) const noexcept {
|
||||
using Seq = mp11::mp_iota_c<sizeof...(Ts)>;
|
||||
mp11::mp_for_each<Seq>([&](auto I) {
|
||||
if (pass_through[I])
|
||||
*(j + I) = *(i + I);
|
||||
else
|
||||
*(j + I) = this->translate(std::get<I>(a), std::get<I>(b), *(i + I));
|
||||
});
|
||||
}
|
||||
|
||||
template <class T, class It>
|
||||
void impl(const T& a, const T& b, It i, index_type* j) const noexcept {
|
||||
const bool* p = pass_through;
|
||||
for (unsigned k = 0; k < a.size(); ++k, ++i, ++j, ++p) {
|
||||
if (*p)
|
||||
*j = *i;
|
||||
else {
|
||||
const auto& bk = b[k];
|
||||
axis::visit(
|
||||
[&](const auto& ak) {
|
||||
using U = std::decay_t<decltype(ak)>;
|
||||
*j = this->translate(ak, axis::get<U>(bk), *i);
|
||||
},
|
||||
a[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Indices>
|
||||
auto operator()(const Indices& seq) const noexcept {
|
||||
auto mi = multi_index_type::create(seq.size());
|
||||
impl(dst, src, seq.begin(), mi.begin());
|
||||
return mi;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Axes>
|
||||
auto make_index_translator(const Axes& dst, const Axes& src) noexcept {
|
||||
return index_translator<Axes>{dst, src};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright 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)
|
||||
//
|
||||
// Uses code segments from boost/iterator/iterator_adaptor.hpp
|
||||
// and boost/iterator/iterator_fascade.hpp
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
// operator->() needs special support for input iterators to strictly meet the
|
||||
// standard's requirements. If *i is not a reference type, we must still
|
||||
// produce an lvalue to which a pointer can be formed. We do that by
|
||||
// returning a proxy object containing an instance of the reference object.
|
||||
template <class Reference>
|
||||
struct operator_arrow_dispatch_t // proxy references
|
||||
{
|
||||
struct proxy {
|
||||
explicit proxy(Reference const& x) noexcept : m_ref(x) {}
|
||||
Reference* operator->() noexcept { return std::addressof(m_ref); }
|
||||
Reference m_ref;
|
||||
};
|
||||
|
||||
using result_type = proxy;
|
||||
static result_type apply(Reference const& x) noexcept { return proxy(x); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct operator_arrow_dispatch_t<T&> // "real" references
|
||||
{
|
||||
using result_type = T*;
|
||||
static result_type apply(T& x) noexcept { return std::addressof(x); }
|
||||
};
|
||||
|
||||
// only for random access Base
|
||||
template <class Derived, class Base, class Reference = std::remove_pointer_t<Base>&,
|
||||
class Value = std::decay_t<Reference>>
|
||||
class iterator_adaptor {
|
||||
using operator_arrow_dispatch = operator_arrow_dispatch_t<Reference>;
|
||||
|
||||
public:
|
||||
using base_type = Base;
|
||||
|
||||
using reference = Reference;
|
||||
using value_type = std::remove_const_t<Value>;
|
||||
using pointer = typename operator_arrow_dispatch::result_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
iterator_adaptor() = default;
|
||||
|
||||
explicit iterator_adaptor(base_type const& iter) : iter_(iter) {}
|
||||
|
||||
pointer operator->() const noexcept {
|
||||
return operator_arrow_dispatch::apply(this->derived().operator*());
|
||||
}
|
||||
reference operator[](difference_type n) const { return *(this->derived() + n); }
|
||||
|
||||
Derived& operator++() {
|
||||
++iter_;
|
||||
return this->derived();
|
||||
}
|
||||
|
||||
Derived& operator--() {
|
||||
--iter_;
|
||||
return this->derived();
|
||||
}
|
||||
|
||||
Derived operator++(int) {
|
||||
Derived tmp(this->derived());
|
||||
++iter_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Derived operator--(int) {
|
||||
Derived tmp(this->derived());
|
||||
--iter_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Derived& operator+=(difference_type n) {
|
||||
iter_ += n;
|
||||
return this->derived();
|
||||
}
|
||||
|
||||
Derived& operator-=(difference_type n) {
|
||||
iter_ -= n;
|
||||
return this->derived();
|
||||
}
|
||||
|
||||
Derived operator+(difference_type n) const {
|
||||
Derived tmp(this->derived());
|
||||
tmp += n;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
Derived operator-(difference_type n) const { return operator+(-n); }
|
||||
|
||||
template <class... Ts>
|
||||
difference_type operator-(const iterator_adaptor<Ts...>& x) const noexcept {
|
||||
return iter_ - x.iter_;
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
bool operator==(const iterator_adaptor<Ts...>& x) const noexcept {
|
||||
return iter_ == x.iter_;
|
||||
}
|
||||
template <class... Ts>
|
||||
bool operator!=(const iterator_adaptor<Ts...>& x) const noexcept {
|
||||
return !this->derived().operator==(x); // equal operator may be overridden in derived
|
||||
}
|
||||
template <class... Ts>
|
||||
bool operator<(const iterator_adaptor<Ts...>& x) const noexcept {
|
||||
return iter_ < x.iter_;
|
||||
}
|
||||
template <class... Ts>
|
||||
bool operator>(const iterator_adaptor<Ts...>& x) const noexcept {
|
||||
return iter_ > x.iter_;
|
||||
}
|
||||
template <class... Ts>
|
||||
bool operator<=(const iterator_adaptor<Ts...>& x) const noexcept {
|
||||
return iter_ <= x.iter_;
|
||||
}
|
||||
template <class... Ts>
|
||||
bool operator>=(const iterator_adaptor<Ts...>& x) const noexcept {
|
||||
return iter_ >= x.iter_;
|
||||
}
|
||||
|
||||
friend Derived operator+(difference_type n, const Derived& x) { return x + n; }
|
||||
|
||||
Base const& base() const noexcept { return iter_; }
|
||||
|
||||
protected:
|
||||
// for convenience in derived classes
|
||||
using iterator_adaptor_ = iterator_adaptor;
|
||||
|
||||
private:
|
||||
Derived& derived() noexcept { return *static_cast<Derived*>(this); }
|
||||
const Derived& derived() const noexcept { return *static_cast<Derived const*>(this); }
|
||||
|
||||
Base iter_;
|
||||
|
||||
template <class, class, class, class>
|
||||
friend class iterator_adaptor;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,255 @@
|
||||
// Copyright 2018-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_DETAIL_LARGE_INT_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP
|
||||
|
||||
#include <boost/histogram/detail/operators.hpp>
|
||||
#include <boost/histogram/detail/safe_comparison.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/function.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
using 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;
|
||||
}
|
||||
|
||||
// An integer type which can grow arbitrarily large (until memory is exhausted).
|
||||
// Use boost.multiprecision.cpp_int in your own code, it is much more sophisticated.
|
||||
// We use it only to reduce coupling between boost libs.
|
||||
template <class Allocator>
|
||||
struct large_int : totally_ordered<large_int<Allocator>, large_int<Allocator>>,
|
||||
partially_ordered<large_int<Allocator>, void> {
|
||||
explicit large_int(const Allocator& a = {}) : data(1, 0, a) {}
|
||||
explicit large_int(std::uint64_t v, const Allocator& a = {}) : data(1, v, a) {}
|
||||
|
||||
large_int(const large_int&) = default;
|
||||
large_int& operator=(const large_int&) = default;
|
||||
large_int(large_int&&) = default;
|
||||
large_int& operator=(large_int&&) = default;
|
||||
|
||||
large_int& operator=(std::uint64_t o) {
|
||||
data = decltype(data)(1, o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
large_int& operator++() {
|
||||
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;
|
||||
}
|
||||
|
||||
large_int& operator+=(const large_int& o) {
|
||||
if (this == &o) {
|
||||
auto tmp{o};
|
||||
return operator+=(tmp);
|
||||
}
|
||||
bool carry = false;
|
||||
std::size_t i = 0;
|
||||
for (std::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;
|
||||
}
|
||||
|
||||
large_int& operator+=(std::uint64_t o) {
|
||||
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;
|
||||
}
|
||||
|
||||
explicit operator double() const noexcept {
|
||||
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;
|
||||
}
|
||||
|
||||
bool operator<(const large_int& o) const noexcept {
|
||||
assert(data.size() > 0u);
|
||||
assert(o.data.size() > 0u);
|
||||
// no leading zeros allowed
|
||||
assert(data.size() == 1 || data.back() > 0u);
|
||||
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 large_int& o) const noexcept {
|
||||
assert(data.size() > 0u);
|
||||
assert(o.data.size() > 0u);
|
||||
// no leading zeros allowed
|
||||
assert(data.size() == 1 || data.back() > 0u);
|
||||
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());
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<std::is_integral<U>::value, bool> operator<(
|
||||
const U& o) const noexcept {
|
||||
assert(data.size() > 0u);
|
||||
return data.size() == 1 && safe_less()(data[0], o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<std::is_integral<U>::value, bool> operator>(
|
||||
const U& o) const noexcept {
|
||||
assert(data.size() > 0u);
|
||||
assert(data.size() == 1 || data.back() > 0u); // no leading zeros allowed
|
||||
return data.size() > 1 || safe_less()(o, data[0]);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<std::is_integral<U>::value, bool> operator==(
|
||||
const U& o) const noexcept {
|
||||
assert(data.size() > 0u);
|
||||
return data.size() == 1 && safe_equal()(data[0], o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<std::is_floating_point<U>::value, bool> operator<(
|
||||
const U& o) const noexcept {
|
||||
return operator double() < o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<std::is_floating_point<U>::value, bool> operator>(
|
||||
const U& o) const noexcept {
|
||||
return operator double() > o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<std::is_floating_point<U>::value, bool> operator==(
|
||||
const U& o) const noexcept {
|
||||
return operator double() == o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<
|
||||
(!std::is_arithmetic<U>::value && std::is_convertible<U, double>::value), bool>
|
||||
operator<(const U& o) const noexcept {
|
||||
return operator double() < o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<
|
||||
(!std::is_arithmetic<U>::value && std::is_convertible<U, double>::value), bool>
|
||||
operator>(const U& o) const noexcept {
|
||||
return operator double() > o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
std::enable_if_t<
|
||||
(!std::is_arithmetic<U>::value && std::is_convertible<U, double>::value), bool>
|
||||
operator==(const U& o) const noexcept {
|
||||
return operator double() == o;
|
||||
}
|
||||
|
||||
std::uint64_t& maybe_extend(std::size_t i) {
|
||||
while (i >= data.size()) data.push_back(0);
|
||||
return data[i];
|
||||
}
|
||||
|
||||
static void add_remainder(std::uint64_t& d, const std::uint64_t o) noexcept {
|
||||
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<std::uint64_t>::max)();
|
||||
tmp -= o;
|
||||
--d -= tmp;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("data", data);
|
||||
}
|
||||
|
||||
std::vector<std::uint64_t, Allocator> data;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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_DETAIL_LIMITS_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_LIMITS_HPP
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class 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 <class 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();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 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_DETAIL_LINEARIZE_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
|
||||
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/optional_index.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/multi_index.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
// initial offset to out must be set
|
||||
template <class Index, class Opts>
|
||||
std::size_t linearize(Opts, Index& out, const std::size_t stride,
|
||||
const axis::index_type size, const axis::index_type idx) {
|
||||
constexpr bool u = Opts::test(axis::option::underflow);
|
||||
constexpr bool o = Opts::test(axis::option::overflow);
|
||||
|
||||
// must be non-const to avoid if constexpr warning from msvc
|
||||
bool fast_track = std::is_same<Index, std::size_t>::value || (u && o);
|
||||
if (fast_track) {
|
||||
assert(idx >= (u ? -1 : 0));
|
||||
assert(idx < (o ? size + 1 : size));
|
||||
assert(idx >= 0 || static_cast<std::size_t>(-idx * stride) <= out);
|
||||
out += idx * stride;
|
||||
} else {
|
||||
assert(idx >= -1);
|
||||
assert(idx < size + 1);
|
||||
// must be non-const to avoid if constexpr warning from msvc
|
||||
bool is_valid = (u || idx >= 0) && (o || idx < size);
|
||||
if (is_valid)
|
||||
out += idx * stride;
|
||||
else
|
||||
out = invalid_index;
|
||||
}
|
||||
return size + u + o;
|
||||
}
|
||||
|
||||
template <class Index, class Axis, class Value>
|
||||
std::size_t linearize(Index& out, const std::size_t stride, const Axis& ax,
|
||||
const Value& v) {
|
||||
// mask options to reduce no. of template instantiations
|
||||
constexpr auto opts = axis::traits::get_options<Axis>{} &
|
||||
(axis::option::underflow | axis::option::overflow);
|
||||
return linearize(opts, out, stride, ax.size(), axis::traits::index(ax, v));
|
||||
}
|
||||
|
||||
// initial offset of out must be zero
|
||||
template <class Index, class Axis, class Value>
|
||||
std::size_t linearize_growth(Index& out, axis::index_type& shift,
|
||||
const std::size_t stride, Axis& a, const Value& v) {
|
||||
axis::index_type idx;
|
||||
std::tie(idx, shift) = axis::traits::update(a, v);
|
||||
constexpr bool u = axis::traits::get_options<Axis>::test(axis::option::underflow);
|
||||
if (u) ++idx;
|
||||
if (std::is_same<Index, std::size_t>::value) {
|
||||
assert(idx < axis::traits::extent(a));
|
||||
out += idx * stride;
|
||||
} else {
|
||||
if (0 <= idx && idx < axis::traits::extent(a))
|
||||
out += idx * stride;
|
||||
else
|
||||
out = invalid_index;
|
||||
}
|
||||
return axis::traits::extent(a);
|
||||
}
|
||||
|
||||
// initial offset of out must be zero
|
||||
template <class A>
|
||||
std::size_t linearize_index(optional_index& out, const std::size_t stride, const A& ax,
|
||||
const axis::index_type idx) noexcept {
|
||||
const auto opt = axis::traits::get_options<A>();
|
||||
const axis::index_type begin = opt & axis::option::underflow ? -1 : 0;
|
||||
const axis::index_type end = opt & axis::option::overflow ? ax.size() + 1 : ax.size();
|
||||
const axis::index_type extent = end - begin;
|
||||
// i may be arbitrarily out of range
|
||||
if (begin <= idx && idx < end)
|
||||
out += (idx - begin) * stride;
|
||||
else
|
||||
out = invalid_index;
|
||||
return extent;
|
||||
}
|
||||
|
||||
template <class A, std::size_t N>
|
||||
optional_index linearize_indices(const A& axes, const multi_index<N>& indices) noexcept {
|
||||
assert(axes_rank(axes) == detail::size(indices));
|
||||
|
||||
optional_index idx{0}; // offset not used by linearize_index
|
||||
auto stride = static_cast<std::size_t>(1);
|
||||
using std::begin;
|
||||
auto i = begin(indices);
|
||||
for_each_axis(axes,
|
||||
[&](const auto& a) { stride *= linearize_index(idx, stride, a, *i++); });
|
||||
return idx;
|
||||
}
|
||||
|
||||
template <class Index, class... Ts, class Value>
|
||||
std::size_t linearize(Index& o, const std::size_t s, const axis::variant<Ts...>& a,
|
||||
const Value& v) {
|
||||
return axis::visit([&o, &s, &v](const auto& a) { return linearize(o, s, a, v); }, a);
|
||||
}
|
||||
|
||||
template <class Index, class... Ts, class Value>
|
||||
std::size_t linearize_growth(Index& o, axis::index_type& sh, const std::size_t st,
|
||||
axis::variant<Ts...>& a, const Value& v) {
|
||||
return axis::visit([&](auto& a) { return linearize_growth(o, sh, st, a, v); }, a);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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_DETAIL_MAKE_DEFAULT_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_MAKE_DEFAULT_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
T make_default_impl(const T& t, decltype(t.get_allocator(), 0)) {
|
||||
return T(t.get_allocator());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T make_default_impl(const T&, float) {
|
||||
return T{};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T make_default(const T& t) {
|
||||
return make_default_impl(t, 0);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 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_DETAIL_NOOP_MUTEX_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_NOOP_MUTEX_HPP
|
||||
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/mp11/utility.hpp> // mp_if
|
||||
#include <mutex>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
struct null_mutex {
|
||||
bool try_lock() noexcept { return true; }
|
||||
void lock() noexcept {}
|
||||
void unlock() noexcept {}
|
||||
};
|
||||
|
||||
template <class Axes, class Storage,
|
||||
class DetailMutex = mp11::mp_if_c<(Storage::has_threading_support &&
|
||||
detail::has_growing_axis<Axes>::value),
|
||||
std::mutex, detail::null_mutex>>
|
||||
struct mutex_base : empty_value<DetailMutex> {
|
||||
mutex_base() = default;
|
||||
// do not copy or move mutex
|
||||
mutex_base(const mutex_base&) : empty_value<DetailMutex>() {}
|
||||
// do not copy or move mutex
|
||||
mutex_base& operator=(const mutex_base&) { return *this; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 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_DETAIL_NONMEMBER_CONTAINER_ACCESS_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_NONMEMBER_CONTAINER_ACCESS_HPP
|
||||
|
||||
#if __cpp_lib_nonmember_container_access >= 201411
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
using std::data;
|
||||
using std::size;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#else // std implementations are not available
|
||||
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class C>
|
||||
constexpr auto data(C& c) -> decltype(c.data()) {
|
||||
return c.data();
|
||||
}
|
||||
|
||||
template <class C>
|
||||
constexpr auto data(const C& c) -> decltype(c.data()) {
|
||||
return c.data();
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
constexpr T* data(T (&array)[N]) noexcept {
|
||||
return array;
|
||||
}
|
||||
|
||||
template <class E>
|
||||
constexpr const E* data(std::initializer_list<E> il) noexcept {
|
||||
return il.begin();
|
||||
}
|
||||
|
||||
template <class C>
|
||||
constexpr auto size(const C& c) -> decltype(c.size()) {
|
||||
return c.size();
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
constexpr std::size_t size(const T (&)[N]) noexcept {
|
||||
return N;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DETAIL_NONMEMBER_CONTAINER_ACCESS_HPP
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright 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_DETAIL_OPERATORS_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_OPERATORS_HPP
|
||||
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class U>
|
||||
using if_not_same = std::enable_if_t<(!std::is_same<T, U>::value), bool>;
|
||||
|
||||
// template <class T, class U>
|
||||
// using if_not_same_and_has_eq =
|
||||
// std::enable_if_t<(!std::is_same<T, U>::value && !has_method_eq<T, U>::value),
|
||||
// bool>;
|
||||
|
||||
// totally_ordered is for types with a <= b == !(a > b) [floats with NaN violate this]
|
||||
// Derived must implement <,== for symmetric form and <,>,== for non-symmetric.
|
||||
|
||||
// partially_ordered is for types with a <= b == a < b || a == b [for floats with NaN]
|
||||
// Derived must implement <,== for symmetric form and <,>,== for non-symmetric.
|
||||
|
||||
template <class T, class U>
|
||||
struct mirrored {
|
||||
friend bool operator<(const U& a, const T& b) noexcept { return b > a; }
|
||||
friend bool operator>(const U& a, const T& b) noexcept { return b < a; }
|
||||
friend bool operator==(const U& a, const T& b) noexcept { return b == a; }
|
||||
friend bool operator<=(const U& a, const T& b) noexcept { return b >= a; }
|
||||
friend bool operator>=(const U& a, const T& b) noexcept { return b <= a; }
|
||||
friend bool operator!=(const U& a, const T& b) noexcept { return b != a; }
|
||||
}; // namespace histogram
|
||||
|
||||
template <class T>
|
||||
struct mirrored<T, void> {
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator<(const U& a, const T& b) noexcept {
|
||||
return b > a;
|
||||
}
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator>(const U& a, const T& b) noexcept {
|
||||
return b < a;
|
||||
}
|
||||
template <class U>
|
||||
friend std::enable_if_t<(!has_method_eq<U, T>::value), bool> operator==(
|
||||
const U& a, const T& b) noexcept {
|
||||
return b.operator==(a);
|
||||
}
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator<=(const U& a, const T& b) noexcept {
|
||||
return b >= a;
|
||||
}
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator>=(const U& a, const T& b) noexcept {
|
||||
return b <= a;
|
||||
}
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator!=(const U& a, const T& b) noexcept {
|
||||
return b != a;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct mirrored<T, T> {
|
||||
friend bool operator>(const T& a, const T& b) noexcept { return b.operator<(a); }
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
struct equality {
|
||||
friend bool operator!=(const T& a, const U& b) noexcept { return !a.operator==(b); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct equality<T, void> {
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator!=(const T& a, const U& b) noexcept {
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
struct totally_ordered_impl : equality<T, U>, mirrored<T, U> {
|
||||
friend bool operator<=(const T& a, const U& b) noexcept { return !(a > b); }
|
||||
friend bool operator>=(const T& a, const U& b) noexcept { return !(a < b); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct totally_ordered_impl<T, void> : equality<T, void>, mirrored<T, void> {
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator<=(const T& a, const U& b) noexcept {
|
||||
return !(a > b);
|
||||
}
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator>=(const T& a, const U& b) noexcept {
|
||||
return !(a < b);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class... Ts>
|
||||
using totally_ordered = mp11::mp_rename<
|
||||
mp11::mp_product<totally_ordered_impl, mp11::mp_list<T>, mp11::mp_list<Ts...> >,
|
||||
mp11::mp_inherit>;
|
||||
|
||||
template <class T, class U>
|
||||
struct partially_ordered_impl : equality<T, U>, mirrored<T, U> {
|
||||
friend bool operator<=(const T& a, const U& b) noexcept { return a < b || a == b; }
|
||||
friend bool operator>=(const T& a, const U& b) noexcept { return a > b || a == b; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct partially_ordered_impl<T, void> : equality<T, void>, mirrored<T, void> {
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator<=(const T& a, const U& b) noexcept {
|
||||
return a < b || a == b;
|
||||
}
|
||||
template <class U>
|
||||
friend if_not_same<T, U> operator>=(const T& a, const U& b) noexcept {
|
||||
return a > b || a == b;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class... Ts>
|
||||
using partially_ordered = mp11::mp_rename<
|
||||
mp11::mp_product<partially_ordered_impl, mp11::mp_list<T>, mp11::mp_list<Ts...> >,
|
||||
mp11::mp_inherit>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DETAIL_OPERATORS_HPP
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 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_DETAIL_OPTIONAL_INDEX_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_OPTIONAL_INDEX_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
constexpr auto invalid_index = ~static_cast<std::size_t>(0);
|
||||
|
||||
// integer with a persistent invalid state, similar to NaN
|
||||
struct optional_index {
|
||||
std::size_t value;
|
||||
|
||||
optional_index& operator=(std::size_t x) noexcept {
|
||||
value = x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional_index& operator+=(std::intptr_t x) noexcept {
|
||||
assert(x >= 0 || static_cast<std::size_t>(-x) <= value);
|
||||
if (value != invalid_index) { value += x; }
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional_index& operator+=(const optional_index& x) noexcept {
|
||||
if (value != invalid_index) return operator+=(x.value);
|
||||
value = invalid_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator std::size_t() const noexcept { return value; }
|
||||
|
||||
friend bool operator<=(std::size_t x, optional_index idx) noexcept {
|
||||
return x <= idx.value;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline bool is_valid(const std::size_t) noexcept { return true; }
|
||||
|
||||
inline bool is_valid(const optional_index x) noexcept { return x.value != invalid_index; }
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 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_DETAIL_PRIORITY_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_PRIORITY_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
// priority is used to priorise ambiguous overloads
|
||||
|
||||
template <std::size_t N>
|
||||
struct priority : priority<(N - 1)> {};
|
||||
|
||||
template <>
|
||||
struct priority<0> {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2020 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_REDUCE_COMMAND_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_REDUCE_COMMAND_HPP
|
||||
|
||||
#include <boost/histogram/detail/span.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
struct reduce_command {
|
||||
static constexpr unsigned unset = static_cast<unsigned>(-1);
|
||||
unsigned iaxis = unset;
|
||||
enum class range_t : char {
|
||||
none,
|
||||
indices,
|
||||
values,
|
||||
} range = range_t::none;
|
||||
union {
|
||||
axis::index_type index;
|
||||
double value;
|
||||
} begin{0}, end{0};
|
||||
unsigned merge = 0; // default value indicates unset option
|
||||
bool crop = false;
|
||||
// for internal use by the reduce algorithm
|
||||
bool is_ordered = true;
|
||||
bool use_underflow_bin = true;
|
||||
bool use_overflow_bin = true;
|
||||
};
|
||||
|
||||
// - puts commands in correct axis order
|
||||
// - sets iaxis for positional commands
|
||||
// - detects and fails on invalid settings
|
||||
// - fuses merge commands with non-merge commands
|
||||
inline void normalize_reduce_commands(span<reduce_command> out,
|
||||
span<const reduce_command> in) {
|
||||
unsigned iaxis = 0;
|
||||
for (const auto& o_in : in) {
|
||||
assert(o_in.merge > 0);
|
||||
if (o_in.iaxis != reduce_command::unset && o_in.iaxis >= out.size())
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("invalid axis index"));
|
||||
auto& o_out = out.begin()[o_in.iaxis == reduce_command::unset ? iaxis : o_in.iaxis];
|
||||
if (o_out.merge == 0) {
|
||||
o_out = o_in;
|
||||
} else {
|
||||
// Some command was already set for this axis, try to fuse commands.
|
||||
if (!((o_in.range == reduce_command::range_t::none) ^
|
||||
(o_out.range == reduce_command::range_t::none)) ||
|
||||
(o_out.merge > 1 && o_in.merge > 1))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument(
|
||||
"multiple conflicting reduce commands for axis " +
|
||||
std::to_string(o_in.iaxis == reduce_command::unset ? iaxis : o_in.iaxis)));
|
||||
if (o_in.range != reduce_command::range_t::none) {
|
||||
o_out.range = o_in.range;
|
||||
o_out.begin = o_in.begin;
|
||||
o_out.end = o_in.end;
|
||||
} else {
|
||||
o_out.merge = o_in.merge;
|
||||
}
|
||||
}
|
||||
++iaxis;
|
||||
}
|
||||
|
||||
iaxis = 0;
|
||||
for (auto& o : out) o.iaxis = iaxis++;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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_DETAIL_RELAXED_EQUAL_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_RELAXED_EQUAL_HPP
|
||||
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
struct relaxed_equal {
|
||||
template <class T, class U>
|
||||
constexpr auto impl(const T& t, const U& u, priority<1>) const noexcept
|
||||
-> decltype(t == u) const {
|
||||
return t == u;
|
||||
}
|
||||
|
||||
// consider T and U not equal, if there is no operator== defined for them
|
||||
template <class T, class U>
|
||||
constexpr bool impl(const T&, const U&, priority<0>) const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
// consider two T equal if they are stateless
|
||||
template <class T>
|
||||
constexpr bool impl(const T&, const T&, priority<0>) const noexcept {
|
||||
return std::is_empty<T>::value;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr bool operator()(const T& t, const U& u) const noexcept {
|
||||
return impl(t, u, priority<1>{});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 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_DETAIL_RELAXED_TUPLE_SIZE_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_RELAXED_TUPLE_SIZE_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
using dynamic_size = std::integral_constant<std::size_t, static_cast<std::size_t>(-1)>;
|
||||
|
||||
// Returns static size of tuple or dynamic_size
|
||||
template <class T>
|
||||
constexpr dynamic_size relaxed_tuple_size(const T&) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
constexpr std::integral_constant<std::size_t, sizeof...(Ts)> relaxed_tuple_size(
|
||||
const std::tuple<Ts...>&) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using relaxed_tuple_size_t = decltype(relaxed_tuple_size(std::declval<T>()));
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 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_DETAIL_REPLACE_TYPE_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_REPLACE_TYPE_HPP
|
||||
|
||||
#include <boost/core/use_default.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class From, class To>
|
||||
using replace_type = std::conditional_t<std::is_same<T, From>::value, To, T>;
|
||||
|
||||
template <class T, class Default>
|
||||
using replace_default = replace_type<T, boost::use_default, Default>;
|
||||
|
||||
template <class T>
|
||||
using replace_cstring = replace_type<T, const char*, std::string>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 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_DETAIL_SAFE_COMPARISON_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_SAFE_COMPARISON_HPP
|
||||
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/type.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
auto make_unsigned(const T& t) noexcept {
|
||||
static_assert(std::is_integral<T>::value, "");
|
||||
return static_cast<std::make_unsigned_t<T>>(t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using number_category =
|
||||
mp11::mp_if<std::is_integral<T>,
|
||||
mp11::mp_if<std::is_signed<T>, type<int>, type<unsigned>>, type<void>>;
|
||||
|
||||
// version of std::equal_to<> which handles signed and unsigned integers correctly
|
||||
struct safe_equal {
|
||||
template <class T, class U>
|
||||
bool operator()(const T& t, const U& u) const noexcept {
|
||||
return impl(number_category<T>{}, number_category<U>{}, t, u);
|
||||
}
|
||||
|
||||
template <class C1, class C2, class T, class U>
|
||||
bool impl(C1, C2, const T& t, const U& u) const noexcept {
|
||||
return t == u;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool impl(type<int>, type<unsigned>, const T& t, const U& u) const noexcept {
|
||||
return t >= 0 && make_unsigned(t) == u;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool impl(type<unsigned>, type<int>, const T& t, const U& u) const noexcept {
|
||||
return impl(type<int>{}, type<unsigned>{}, u, t);
|
||||
}
|
||||
};
|
||||
|
||||
// version of std::less<> which handles signed and unsigned integers correctly
|
||||
struct safe_less {
|
||||
template <class T, class U>
|
||||
bool operator()(const T& t, const U& u) const noexcept {
|
||||
return impl(number_category<T>{}, number_category<U>{}, t, u);
|
||||
}
|
||||
|
||||
template <class C1, class C2, class T, class U>
|
||||
bool impl(C1, C2, const T& t, const U& u) const noexcept {
|
||||
return t < u;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool impl(type<int>, type<unsigned>, const T& t, const U& u) const noexcept {
|
||||
return t < 0 || make_unsigned(t) < u;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool impl(type<unsigned>, type<int>, const T& t, const U& u) const noexcept {
|
||||
return 0 < u && t < make_unsigned(u);
|
||||
}
|
||||
};
|
||||
|
||||
// version of std::greater<> which handles signed and unsigned integers correctly
|
||||
struct safe_greater {
|
||||
template <class T, class U>
|
||||
bool operator()(const T& t, const U& u) const noexcept {
|
||||
return safe_less()(u, t);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
277
install/boost_1_75_0/include/boost/histogram/detail/span.hpp
Normal file
277
install/boost_1_75_0/include/boost/histogram/detail/span.hpp
Normal file
@@ -0,0 +1,277 @@
|
||||
// Copyright 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_DETAIL_SPAN_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_SPAN_HPP
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<version>)
|
||||
#include <version>
|
||||
#ifdef __cpp_lib_span
|
||||
#if __cpp_lib_span >= 201902
|
||||
#define BOOST_HISTOGRAM_DETAIL_HAS_STD_SPAN
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_HISTOGRAM_DETAIL_HAS_STD_SPAN
|
||||
|
||||
#include <span>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
using std::span;
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#else // C++17 span not available, so we use our implementation
|
||||
|
||||
// to be replaced by boost::span
|
||||
|
||||
#include <array>
|
||||
#include <boost/histogram/detail/nonmember_container_access.hpp>
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
namespace dtl = ::boost::histogram::detail;
|
||||
|
||||
static constexpr std::size_t dynamic_extent = ~static_cast<std::size_t>(0);
|
||||
|
||||
template <class T, std::size_t N>
|
||||
class span_base {
|
||||
public:
|
||||
constexpr T* data() noexcept { return begin_; }
|
||||
constexpr const T* data() const noexcept { return begin_; }
|
||||
constexpr std::size_t size() const noexcept { return N; }
|
||||
|
||||
protected:
|
||||
constexpr span_base(T* b, std::size_t s) noexcept : begin_(b) {
|
||||
(void)s;
|
||||
assert(N == s);
|
||||
}
|
||||
constexpr void set(T* b, std::size_t s) noexcept {
|
||||
(void)s;
|
||||
begin_ = b;
|
||||
assert(N == s);
|
||||
}
|
||||
|
||||
private:
|
||||
T* begin_;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class span_base<T, dynamic_extent> {
|
||||
public:
|
||||
constexpr span_base() noexcept : begin_(nullptr), size_(0) {}
|
||||
|
||||
constexpr T* data() noexcept { return begin_; }
|
||||
constexpr const T* data() const noexcept { return begin_; }
|
||||
constexpr std::size_t size() const noexcept { return size_; }
|
||||
|
||||
protected:
|
||||
constexpr span_base(T* b, std::size_t s) noexcept : begin_(b), size_(s) {}
|
||||
constexpr void set(T* b, std::size_t s) noexcept {
|
||||
begin_ = b;
|
||||
size_ = s;
|
||||
}
|
||||
|
||||
private:
|
||||
T* begin_;
|
||||
std::size_t size_;
|
||||
};
|
||||
|
||||
template <class T, std::size_t Extent = dynamic_extent>
|
||||
class span : public span_base<T, Extent> {
|
||||
using base = span_base<T, Extent>;
|
||||
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using index_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
static constexpr std::size_t extent = Extent;
|
||||
|
||||
using base::base;
|
||||
|
||||
constexpr span(pointer first, pointer last)
|
||||
: span(first, static_cast<std::size_t>(last - first)) {
|
||||
assert(extent == dynamic_extent ||
|
||||
static_cast<difference_type>(extent) == (last - first));
|
||||
}
|
||||
|
||||
constexpr span(pointer ptr, index_type count) : base(ptr, count) {}
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr span(element_type (&arr)[N]) noexcept : span(dtl::data(arr), N) {
|
||||
static_assert(extent == dynamic_extent || extent == N, "static sizes do not match");
|
||||
}
|
||||
|
||||
template <std::size_t N,
|
||||
class = std::enable_if_t<(extent == dynamic_extent || extent == N)> >
|
||||
constexpr span(std::array<value_type, N>& arr) noexcept : span(dtl::data(arr), N) {}
|
||||
|
||||
template <std::size_t N,
|
||||
class = std::enable_if_t<(extent == dynamic_extent || extent == N)> >
|
||||
constexpr span(const std::array<value_type, N>& arr) noexcept
|
||||
: span(dtl::data(arr), N) {}
|
||||
|
||||
template <class Container, class = std::enable_if_t<std::is_convertible<
|
||||
decltype(dtl::size(std::declval<const Container&>()),
|
||||
dtl::data(std::declval<const Container&>())),
|
||||
pointer>::value> >
|
||||
constexpr span(const Container& cont) : span(dtl::data(cont), dtl::size(cont)) {}
|
||||
|
||||
template <class Container, class = std::enable_if_t<std::is_convertible<
|
||||
decltype(dtl::size(std::declval<Container&>()),
|
||||
dtl::data(std::declval<Container&>())),
|
||||
pointer>::value> >
|
||||
constexpr span(Container& cont) : span(dtl::data(cont), dtl::size(cont)) {}
|
||||
|
||||
template <class U, std::size_t N,
|
||||
class = std::enable_if_t<((extent == dynamic_extent || extent == N) &&
|
||||
std::is_convertible<U, element_type>::value)> >
|
||||
constexpr span(const span<U, N>& s) noexcept : span(s.data(), s.size()) {}
|
||||
|
||||
template <class U, std::size_t N,
|
||||
class = std::enable_if_t<((extent == dynamic_extent || extent == N) &&
|
||||
std::is_convertible<U, element_type>::value)> >
|
||||
constexpr span(span<U, N>& s) noexcept : span(s.data(), s.size()) {}
|
||||
|
||||
constexpr span(const span& other) noexcept = default;
|
||||
|
||||
constexpr iterator begin() { return base::data(); }
|
||||
constexpr const_iterator begin() const { return base::data(); }
|
||||
constexpr const_iterator cbegin() const { return base::data(); }
|
||||
|
||||
constexpr iterator end() { return base::data() + base::size(); }
|
||||
constexpr const_iterator end() const { return base::data() + base::size(); }
|
||||
constexpr const_iterator cend() const { return base::data() + base::size(); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
const_reverse_iterator rbegin() const { return reverse_iterator(end()); }
|
||||
const_reverse_iterator crbegin() { return reverse_iterator(end()); }
|
||||
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rend() const { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator crend() { return reverse_iterator(begin()); }
|
||||
|
||||
constexpr reference front() { return *base::data(); }
|
||||
constexpr reference back() { return *(base::data() + base::size() - 1); }
|
||||
|
||||
constexpr reference operator[](index_type idx) const { return base::data()[idx]; }
|
||||
|
||||
constexpr std::size_t size_bytes() const noexcept {
|
||||
return base::size() * sizeof(element_type);
|
||||
}
|
||||
|
||||
constexpr bool empty() const noexcept { return base::size() == 0; }
|
||||
|
||||
template <std::size_t Count>
|
||||
constexpr span<element_type, Count> first() const {
|
||||
assert(Count <= base::size());
|
||||
return span<element_type, Count>(base::data(), Count);
|
||||
}
|
||||
|
||||
constexpr span<element_type, dynamic_extent> first(std::size_t count) const {
|
||||
assert(count <= base::size());
|
||||
return span<element_type, dynamic_extent>(base::data(), count);
|
||||
}
|
||||
|
||||
template <std::size_t Count>
|
||||
constexpr span<element_type, Count> last() const {
|
||||
assert(Count <= base::size());
|
||||
return span<element_type, Count>(base::data() + base::size() - Count, Count);
|
||||
}
|
||||
|
||||
constexpr span<element_type, dynamic_extent> last(std::size_t count) const {
|
||||
assert(count <= base::size());
|
||||
return span<element_type, dynamic_extent>(base::data() + base::size() - count, count);
|
||||
}
|
||||
|
||||
template <std::size_t Offset, std::size_t Count = dynamic_extent>
|
||||
constexpr span<element_type,
|
||||
(Count != dynamic_extent
|
||||
? Count
|
||||
: (extent != dynamic_extent ? extent - Offset : dynamic_extent))>
|
||||
subspan() const {
|
||||
assert(Offset <= base::size());
|
||||
constexpr std::size_t E =
|
||||
(Count != dynamic_extent
|
||||
? Count
|
||||
: (extent != dynamic_extent ? extent - Offset : dynamic_extent));
|
||||
assert(E == dynamic_extent || E <= base::size());
|
||||
return span<element_type, E>(base::data() + Offset,
|
||||
Count == dynamic_extent ? base::size() - Offset : Count);
|
||||
}
|
||||
|
||||
constexpr span<element_type, dynamic_extent> subspan(
|
||||
std::size_t offset, std::size_t count = dynamic_extent) const {
|
||||
assert(offset <= base::size());
|
||||
const std::size_t s = count == dynamic_extent ? base::size() - offset : count;
|
||||
assert(s <= base::size());
|
||||
return span<element_type, dynamic_extent>(base::data() + offset, s);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
|
||||
#include <boost/histogram/detail/nonmember_container_access.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
namespace dtl = ::boost::histogram::detail;
|
||||
|
||||
template <class T>
|
||||
auto make_span(T* begin, T* end) {
|
||||
return dtl::span<T>{begin, end};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto make_span(T* begin, std::size_t size) {
|
||||
return dtl::span<T>{begin, size};
|
||||
}
|
||||
|
||||
template <class Container, class = decltype(dtl::size(std::declval<Container>()),
|
||||
dtl::data(std::declval<Container>()))>
|
||||
auto make_span(const Container& cont) {
|
||||
return make_span(dtl::data(cont), dtl::size(cont));
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
auto make_span(T (&arr)[N]) {
|
||||
return dtl::span<T, N>(arr, N);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2018-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_DETAIL_STATIC_IF_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_STATIC_IF_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class F, class... Args>
|
||||
constexpr decltype(auto) static_if_impl(
|
||||
std::true_type, T&& t, F&&,
|
||||
Args&&... args) noexcept(noexcept(std::declval<T>()(std::declval<Args>()...))) {
|
||||
return std::forward<T>(t)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class T, class F, class... Args>
|
||||
constexpr decltype(auto) static_if_impl(
|
||||
std::false_type, T&&, F&& f,
|
||||
Args&&... args) noexcept(noexcept(std::declval<F>()(std::declval<Args>()...))) {
|
||||
return std::forward<F>(f)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <bool B, class... Ts>
|
||||
constexpr decltype(auto) static_if_c(Ts&&... ts) noexcept(
|
||||
noexcept(static_if_impl(std::integral_constant<bool, B>{}, std::declval<Ts>()...))) {
|
||||
return static_if_impl(std::integral_constant<bool, B>{}, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template <class Bool, class... Ts>
|
||||
constexpr decltype(auto) static_if(Ts&&... ts) noexcept(
|
||||
noexcept(static_if_impl(Bool{}, std::declval<Ts>()...))) {
|
||||
return static_if_impl(Bool{}, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 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_DETAIL_SUB_ARRAY_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_SUB_ARRAY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, std::size_t N>
|
||||
class sub_array {
|
||||
constexpr bool swap_element_is_noexcept() noexcept {
|
||||
using std::swap;
|
||||
return noexcept(swap(std::declval<T&>(), std::declval<T&>()));
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
sub_array() = default;
|
||||
|
||||
explicit sub_array(std::size_t s) noexcept : size_(s) { assert(size_ <= N); }
|
||||
|
||||
sub_array(std::size_t s, const T& value) noexcept(
|
||||
std::is_nothrow_assignable<T, const_reference>::value)
|
||||
: sub_array(s) {
|
||||
fill(value);
|
||||
}
|
||||
|
||||
reference at(size_type pos) noexcept {
|
||||
if (pos >= size()) BOOST_THROW_EXCEPTION(std::out_of_range{"pos is out of range"});
|
||||
return data_[pos];
|
||||
}
|
||||
|
||||
const_reference at(size_type pos) const noexcept {
|
||||
if (pos >= size()) BOOST_THROW_EXCEPTION(std::out_of_range{"pos is out of range"});
|
||||
return data_[pos];
|
||||
}
|
||||
|
||||
reference operator[](size_type pos) noexcept { return data_[pos]; }
|
||||
const_reference operator[](size_type pos) const noexcept { return data_[pos]; }
|
||||
|
||||
reference front() noexcept { return data_[0]; }
|
||||
const_reference front() const noexcept { return data_[0]; }
|
||||
|
||||
reference back() noexcept { return data_[size_ - 1]; }
|
||||
const_reference back() const noexcept { return data_[size_ - 1]; }
|
||||
|
||||
pointer data() noexcept { return static_cast<pointer>(data_); }
|
||||
const_pointer data() const noexcept { return static_cast<const_pointer>(data_); }
|
||||
|
||||
iterator begin() noexcept { return data_; }
|
||||
const_iterator begin() const noexcept { return data_; }
|
||||
|
||||
iterator end() noexcept { return begin() + size_; }
|
||||
const_iterator end() const noexcept { return begin() + size_; }
|
||||
|
||||
const_iterator cbegin() noexcept { return data_; }
|
||||
const_iterator cbegin() const noexcept { return data_; }
|
||||
|
||||
const_iterator cend() noexcept { return cbegin() + size_; }
|
||||
const_iterator cend() const noexcept { return cbegin() + size_; }
|
||||
|
||||
constexpr size_type max_size() const noexcept { return N; }
|
||||
size_type size() const noexcept { return size_; }
|
||||
bool empty() const noexcept { return size_ == 0; }
|
||||
|
||||
void fill(const_reference value) noexcept(
|
||||
std::is_nothrow_assignable<T, const_reference>::value) {
|
||||
std::fill(begin(), end(), value);
|
||||
}
|
||||
|
||||
void swap(sub_array& other) noexcept(swap_element_is_noexcept()) {
|
||||
using std::swap;
|
||||
for (auto i = begin(), j = other.begin(); i != end(); ++i, ++j) swap(*i, *j);
|
||||
}
|
||||
|
||||
private:
|
||||
size_type size_ = 0;
|
||||
value_type data_[N];
|
||||
};
|
||||
|
||||
template <class T, std::size_t N>
|
||||
bool operator==(const sub_array<T, N>& a, const sub_array<T, N>& b) noexcept {
|
||||
return std::equal(a.begin(), a.end(), b.begin());
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
bool operator!=(const sub_array<T, N>& a, const sub_array<T, N>& b) noexcept {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
namespace std {
|
||||
template <class T, std::size_t N>
|
||||
void swap(::boost::histogram::detail::sub_array<T, N>& a,
|
||||
::boost::histogram::detail::sub_array<T, N>& b) noexcept(noexcept(a.swap(b))) {
|
||||
a.swap(b);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 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_DETAIL_TRY_CAST_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_TRY_CAST_HPP
|
||||
|
||||
#include <boost/config.hpp> // BOOST_NORETURN
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class U>
|
||||
constexpr T* ptr_cast(U*) noexcept {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr T* ptr_cast(T* p) noexcept {
|
||||
return p;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr const T* ptr_cast(const T* p) noexcept {
|
||||
return p;
|
||||
}
|
||||
|
||||
template <class T, class E, class U>
|
||||
BOOST_NORETURN T try_cast_impl(std::false_type, std::false_type, U&&) {
|
||||
BOOST_THROW_EXCEPTION(E("type cast error"));
|
||||
}
|
||||
|
||||
// converting cast
|
||||
template <class T, class E, class U>
|
||||
T try_cast_impl(std::false_type, std::true_type, U&& u) noexcept {
|
||||
return static_cast<T>(u); // cast to avoid warnings
|
||||
}
|
||||
|
||||
// pass-through cast
|
||||
template <class T, class E>
|
||||
T&& try_cast_impl(std::true_type, std::true_type, T&& t) noexcept {
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
|
||||
// cast fails at runtime with exception E instead of compile-time, T must be a value
|
||||
template <class T, class E, class U>
|
||||
T try_cast(U&& u) noexcept(std::is_convertible<U, T>::value) {
|
||||
return try_cast_impl<T, E>(std::is_same<U, T>{}, std::is_convertible<U, T>{},
|
||||
std::forward<U>(u));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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_DETAIL_TUPLE_SLICE_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_TUPLE_SLICE_HPP
|
||||
|
||||
#include <boost/mp11/integer_sequence.hpp>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <std::size_t I, class T, std::size_t... K>
|
||||
decltype(auto) tuple_slice_impl(T&& t, mp11::index_sequence<K...>) {
|
||||
return std::forward_as_tuple(std::get<(I + K)>(std::forward<T>(t))...);
|
||||
}
|
||||
|
||||
template <std::size_t I, std::size_t N, class Tuple>
|
||||
decltype(auto) tuple_slice(Tuple&& t) {
|
||||
constexpr auto S = std::tuple_size<std::decay_t<Tuple>>::value;
|
||||
static_assert(I + N <= S, "I, N must be a valid subset");
|
||||
return tuple_slice_impl<I>(std::forward<Tuple>(t), mp11::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 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_DETAIL_TYPE_NAME_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_TYPE_NAME_HPP
|
||||
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <boost/type.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
std::string type_name_impl(boost::type<T>) {
|
||||
return boost::core::demangled_name(BOOST_CORE_TYPEID(T));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::string type_name_impl(boost::type<T const>) {
|
||||
return type_name_impl(boost::type<T>{}) + " const";
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::string type_name_impl(boost::type<T&>) {
|
||||
return type_name_impl(boost::type<T>{}) + " &";
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::string type_name_impl(boost::type<T&&>) {
|
||||
return type_name_impl(boost::type<T>{}) + " &&";
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::string type_name() {
|
||||
return type_name_impl(boost::type<T>{});
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 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_DETAIL_VARIANT_PROXY_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_VARIANT_PROXY_HPP
|
||||
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp> // variant_access
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/mp11/algorithm.hpp> // mp_with_index, mp_find, mp_at
|
||||
#include <boost/mp11/list.hpp> // mp_size
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
// This is a workaround to remain backward compatible in the serialization format. The
|
||||
// proxy uses only the public interface of axis::variant for serialization and works
|
||||
// independently of the underlying variant implementation.
|
||||
template <class Variant>
|
||||
struct variant_proxy {
|
||||
Variant& variant;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
detail::static_if_c<Archive::is_loading::value>(
|
||||
[this](auto& ar) { // loading
|
||||
int which = 0;
|
||||
ar >> make_nvp("which", which);
|
||||
constexpr unsigned N = mp11::mp_size<Variant>::value;
|
||||
if (which < 0 || static_cast<unsigned>(which) >= N)
|
||||
// throw if which >= N, can happen if type was removed from variant
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error("variant has fewer types than stored version"));
|
||||
mp11::mp_with_index<N>(static_cast<unsigned>(which), [&ar, this](auto i) {
|
||||
using T = mp11::mp_at_c<Variant, i>;
|
||||
T value;
|
||||
ar >> make_nvp("value", value);
|
||||
this->variant = std::move(value);
|
||||
T* new_address = variant_access::template get_if<T>(&this->variant);
|
||||
ar.reset_object_address(new_address, &value);
|
||||
});
|
||||
},
|
||||
[this](auto& ar) { // saving
|
||||
visit(
|
||||
[&ar](const auto& value) {
|
||||
using T = std::decay_t<decltype(value)>;
|
||||
const int which = static_cast<int>(mp11::mp_find<Variant, T>::value);
|
||||
ar << make_nvp("which", which);
|
||||
ar << make_nvp("value", value);
|
||||
},
|
||||
this->variant);
|
||||
},
|
||||
ar);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
183
install/boost_1_75_0/include/boost/histogram/fwd.hpp
Normal file
183
install/boost_1_75_0/include/boost/histogram/fwd.hpp
Normal file
@@ -0,0 +1,183 @@
|
||||
// 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_FWD_HPP
|
||||
#define BOOST_HISTOGRAM_FWD_HPP
|
||||
|
||||
/**
|
||||
\file boost/histogram/fwd.hpp
|
||||
Forward declarations, tag types and type aliases.
|
||||
*/
|
||||
|
||||
#include <boost/config.hpp> // BOOST_ATTRIBUTE_NODISCARD
|
||||
#include <boost/core/use_default.hpp>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#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 {
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned /* version */) {}
|
||||
};
|
||||
|
||||
/// Another alias for an empty metadata type
|
||||
using empty_type = null_type;
|
||||
|
||||
// some forward declarations must be hidden from doxygen to fix the reference docu :(
|
||||
#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 MetaData = use_default>
|
||||
class boolean;
|
||||
|
||||
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 ValueType = double>
|
||||
class count;
|
||||
|
||||
template <class ValueType = double>
|
||||
class sum;
|
||||
|
||||
template <class ValueType = double>
|
||||
class weighted_sum;
|
||||
|
||||
template <class ValueType = double>
|
||||
class mean;
|
||||
|
||||
template <class ValueType = double>
|
||||
class weighted_mean;
|
||||
|
||||
template <class T>
|
||||
class thread_safe;
|
||||
|
||||
template <class T>
|
||||
struct is_thread_safe : std::false_type {};
|
||||
template <class T>
|
||||
struct is_thread_safe<thread_safe<T>> : std::true_type {};
|
||||
|
||||
} // 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<>>;
|
||||
|
||||
// some forward declarations must be hidden from doxygen to fix the reference docu :(
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
template <class Axes, class Storage = default_storage>
|
||||
class BOOST_ATTRIBUTE_NODISCARD histogram;
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* 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, 32 axes
|
||||
* would generate of 4 GB.
|
||||
*/
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT
|
||||
#define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct buffer_size_impl
|
||||
: std::integral_constant<std::size_t, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT> {};
|
||||
|
||||
template <class... Ts>
|
||||
struct buffer_size_impl<std::tuple<Ts...>>
|
||||
: std::integral_constant<std::size_t, sizeof...(Ts)> {};
|
||||
|
||||
template <class T>
|
||||
using buffer_size = typename buffer_size_impl<T>::type;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
714
install/boost_1_75_0/include/boost/histogram/histogram.hpp
Normal file
714
install/boost_1_75_0/include/boost/histogram/histogram.hpp
Normal file
@@ -0,0 +1,714 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_HISTOGRAM_HPP
|
||||
#define BOOST_HISTOGRAM_HISTOGRAM_HPP
|
||||
|
||||
#include <boost/histogram/detail/accumulator_traits.hpp>
|
||||
#include <boost/histogram/detail/argument_traits.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/common_type.hpp>
|
||||
#include <boost/histogram/detail/fill.hpp>
|
||||
#include <boost/histogram/detail/fill_n.hpp>
|
||||
#include <boost/histogram/detail/index_translator.hpp>
|
||||
#include <boost/histogram/detail/mutex_base.hpp>
|
||||
#include <boost/histogram/detail/nonmember_container_access.hpp>
|
||||
#include <boost/histogram/detail/span.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/indexed.hpp>
|
||||
#include <boost/histogram/multi_index.hpp>
|
||||
#include <boost/histogram/sample.hpp>
|
||||
#include <boost/histogram/storage_adaptor.hpp>
|
||||
#include <boost/histogram/unsafe_access.hpp>
|
||||
#include <boost/histogram/weight.hpp>
|
||||
#include <boost/mp11/integral.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
/** Central class of the histogram library.
|
||||
|
||||
Histogram uses the call operator to insert data, like the
|
||||
[Boost.Accumulators](https://www.boost.org/doc/libs/develop/doc/html/accumulators.html).
|
||||
|
||||
Use factory functions (see
|
||||
[make_histogram.hpp](histogram/reference.html#header.boost.histogram.make_histogram_hpp)
|
||||
and
|
||||
[make_profile.hpp](histogram/reference.html#header.boost.histogram.make_profile_hpp)) to
|
||||
conveniently create histograms rather than calling the ctors directly.
|
||||
|
||||
Use the [indexed](boost/histogram/indexed.html) range generator to iterate over filled
|
||||
histograms, which is convenient and faster than hand-written loops for multi-dimensional
|
||||
histograms.
|
||||
|
||||
@tparam Axes std::tuple of axis types OR std::vector of an axis type or axis::variant
|
||||
@tparam Storage class that implements the storage interface
|
||||
*/
|
||||
template <class Axes, class Storage>
|
||||
class histogram : detail::mutex_base<Axes, Storage> {
|
||||
static_assert(std::is_same<std::decay_t<Storage>, Storage>::value,
|
||||
"Storage may not be a reference or const or volatile");
|
||||
static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
|
||||
|
||||
public:
|
||||
using axes_type = Axes;
|
||||
using storage_type = Storage;
|
||||
using value_type = typename storage_type::value_type;
|
||||
// typedefs for boost::range_iterator
|
||||
using iterator = typename storage_type::iterator;
|
||||
using const_iterator = typename storage_type::const_iterator;
|
||||
using multi_index_type = multi_index<detail::relaxed_tuple_size_t<axes_type>::value>;
|
||||
|
||||
private:
|
||||
using mutex_base = typename detail::mutex_base<axes_type, storage_type>;
|
||||
|
||||
public:
|
||||
histogram() = default;
|
||||
|
||||
template <class A, class S>
|
||||
explicit histogram(histogram<A, S>&& rhs)
|
||||
: storage_(std::move(unsafe_access::storage(rhs)))
|
||||
, offset_(unsafe_access::offset(rhs)) {
|
||||
detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
}
|
||||
|
||||
template <class A, class S>
|
||||
explicit histogram(const histogram<A, S>& rhs)
|
||||
: storage_(unsafe_access::storage(rhs)), offset_(unsafe_access::offset(rhs)) {
|
||||
detail::axes_assign(axes_, unsafe_access::axes(rhs));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
}
|
||||
|
||||
template <class A, class S>
|
||||
histogram& operator=(histogram<A, S>&& rhs) {
|
||||
detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
storage_ = std::move(unsafe_access::storage(rhs));
|
||||
offset_ = unsafe_access::offset(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class A, class S>
|
||||
histogram& operator=(const histogram<A, S>& rhs) {
|
||||
detail::axes_assign(axes_, unsafe_access::axes(rhs));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
storage_ = unsafe_access::storage(rhs);
|
||||
offset_ = unsafe_access::offset(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class A, class = detail::requires_axes<A>>
|
||||
histogram(A&& a, Storage s)
|
||||
: axes_(std::forward<A>(a))
|
||||
, storage_(std::move(s))
|
||||
, offset_(detail::offset(axes_)) {
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
storage_.reset(detail::bincount(axes_));
|
||||
}
|
||||
|
||||
explicit histogram(Axes axes) : histogram(axes, storage_type()) {}
|
||||
|
||||
template <class... As, class = detail::requires_axes<std::tuple<std::decay_t<As>...>>>
|
||||
explicit histogram(As&&... as)
|
||||
: histogram(std::tuple<std::decay_t<As>...>(std::forward<As>(as)...),
|
||||
storage_type()) {}
|
||||
|
||||
/// Number of axes (dimensions).
|
||||
constexpr unsigned rank() const noexcept { return detail::axes_rank(axes_); }
|
||||
|
||||
/// Total number of bins (including underflow/overflow).
|
||||
std::size_t size() const noexcept { return storage_.size(); }
|
||||
|
||||
/// Reset all bins to default initialized values.
|
||||
void reset() { storage_.reset(size()); }
|
||||
|
||||
/// Get N-th axis using a compile-time number.
|
||||
/// This version is more efficient than the one accepting a run-time number.
|
||||
template <unsigned N = 0>
|
||||
decltype(auto) axis(std::integral_constant<unsigned, N> = {}) const {
|
||||
assert(N < rank());
|
||||
return detail::axis_get<N>(axes_);
|
||||
}
|
||||
|
||||
/// Get N-th axis with run-time number.
|
||||
/// Prefer the version that accepts a compile-time number, if you can use it.
|
||||
decltype(auto) axis(unsigned i) const {
|
||||
assert(i < rank());
|
||||
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.
|
||||
|
||||
Returns iterator to located cell.
|
||||
|
||||
Arguments are passed in order to the axis objects. Passing an argument type that is
|
||||
not convertible to the value type accepted by the axis or passing the wrong number
|
||||
of arguments causes a throw of `std::invalid_argument`.
|
||||
|
||||
__Optional weight__
|
||||
|
||||
An optional weight can be passed as the first or last argument
|
||||
with the [weight](boost/histogram/weight.html) helper function. Compilation fails if
|
||||
the storage elements do not support weights.
|
||||
|
||||
__Samples__
|
||||
|
||||
If the storage elements accept samples, pass them with the sample helper function
|
||||
in addition to the axis arguments, which can be the first or last argument. The
|
||||
[sample](boost/histogram/sample.html) helper function can pass one or more arguments
|
||||
to the storage element. If samples and weights are used together, they can be passed
|
||||
in any order at the beginning or end of the argument list.
|
||||
|
||||
__Axis with multiple arguments__
|
||||
|
||||
If the histogram contains an axis which accepts a `std::tuple` of arguments, the
|
||||
arguments for that axis need to be passed as a `std::tuple`, for example,
|
||||
`std::make_tuple(1.2, 2.3)`. If the histogram contains only this axis and no other,
|
||||
the arguments can be passed directly.
|
||||
*/
|
||||
template <class T0, class... Ts,
|
||||
class = std::enable_if_t<(detail::is_tuple<T0>::value == false ||
|
||||
sizeof...(Ts) > 0)>>
|
||||
iterator operator()(const T0& arg0, const Ts&... args) {
|
||||
return operator()(std::forward_as_tuple(arg0, args...));
|
||||
}
|
||||
|
||||
/// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`.
|
||||
template <class... Ts>
|
||||
iterator operator()(const std::tuple<Ts...>& args) {
|
||||
using arg_traits = detail::argument_traits<std::decay_t<Ts>...>;
|
||||
using acc_traits = detail::accumulator_traits<value_type>;
|
||||
constexpr bool weight_valid =
|
||||
arg_traits::wpos::value == -1 || acc_traits::weight_support;
|
||||
static_assert(weight_valid, "error: accumulator does not support weights");
|
||||
detail::sample_args_passed_vs_expected<typename arg_traits::sargs,
|
||||
typename acc_traits::args>();
|
||||
constexpr bool sample_valid =
|
||||
std::is_convertible<typename arg_traits::sargs, typename acc_traits::args>::value;
|
||||
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
|
||||
return detail::fill(mp11::mp_bool<(weight_valid && sample_valid)>{}, arg_traits{},
|
||||
offset_, storage_, axes_, args);
|
||||
}
|
||||
|
||||
/** Fill histogram with several values at once.
|
||||
|
||||
The argument must be an iterable with a size that matches the
|
||||
rank of the histogram. The element of an iterable may be 1) a value or 2) an iterable
|
||||
with contiguous storage over values or 3) a variant of 1) and 2). Sub-iterables must
|
||||
have the same length.
|
||||
|
||||
Values are passed to the corresponding histogram axis in order. If a single value is
|
||||
passed together with an iterable of values, the single value is treated like an
|
||||
iterable with matching length of copies of this value.
|
||||
|
||||
If the histogram has only one axis, an iterable of values may be passed directly.
|
||||
|
||||
@param args iterable as explained in the long description.
|
||||
*/
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args) {
|
||||
using acc_traits = detail::accumulator_traits<value_type>;
|
||||
constexpr unsigned n_sample_args_expected =
|
||||
std::tuple_size<typename acc_traits::args>::value;
|
||||
static_assert(n_sample_args_expected == 0,
|
||||
"sample argument is missing but required by accumulator");
|
||||
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
|
||||
detail::fill_n(mp11::mp_bool<(n_sample_args_expected == 0)>{}, offset_, storage_,
|
||||
axes_, detail::make_span(args));
|
||||
}
|
||||
|
||||
/** Fill histogram with several values and weights at once.
|
||||
|
||||
@param args iterable of values.
|
||||
@param weights single weight or an iterable of weights.
|
||||
*/
|
||||
template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args, const weight_type<T>& weights) {
|
||||
using acc_traits = detail::accumulator_traits<value_type>;
|
||||
constexpr bool weight_valid = acc_traits::weight_support;
|
||||
static_assert(weight_valid, "error: accumulator does not support weights");
|
||||
detail::sample_args_passed_vs_expected<std::tuple<>, typename acc_traits::args>();
|
||||
constexpr bool sample_valid =
|
||||
std::is_convertible<std::tuple<>, typename acc_traits::args>::value;
|
||||
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
|
||||
detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_, storage_,
|
||||
axes_, detail::make_span(args),
|
||||
weight(detail::to_ptr_size(weights.value)));
|
||||
}
|
||||
|
||||
/** Fill histogram with several values and weights at once.
|
||||
|
||||
@param weights single weight or an iterable of weights.
|
||||
@param args iterable of values.
|
||||
*/
|
||||
template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const weight_type<T>& weights, const Iterable& args) {
|
||||
fill(args, weights);
|
||||
}
|
||||
|
||||
/** Fill histogram with several values and samples at once.
|
||||
|
||||
@param args iterable of values.
|
||||
@param samples single sample or an iterable of samples.
|
||||
*/
|
||||
template <class Iterable, class... Ts, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args, const sample_type<std::tuple<Ts...>>& samples) {
|
||||
using acc_traits = detail::accumulator_traits<value_type>;
|
||||
using sample_args_passed =
|
||||
std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
|
||||
detail::sample_args_passed_vs_expected<sample_args_passed,
|
||||
typename acc_traits::args>();
|
||||
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
|
||||
mp11::tuple_apply(
|
||||
[&](const auto&... sargs) {
|
||||
constexpr bool sample_valid =
|
||||
std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
|
||||
detail::fill_n(mp11::mp_bool<(sample_valid)>{}, offset_, storage_, axes_,
|
||||
detail::make_span(args), detail::to_ptr_size(sargs)...);
|
||||
},
|
||||
samples.value);
|
||||
}
|
||||
|
||||
/** Fill histogram with several values and samples at once.
|
||||
|
||||
@param samples single sample or an iterable of samples.
|
||||
@param args iterable of values.
|
||||
*/
|
||||
template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const sample_type<T>& samples, const Iterable& args) {
|
||||
fill(args, samples);
|
||||
}
|
||||
|
||||
template <class Iterable, class T, class... Ts,
|
||||
class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args, const weight_type<T>& weights,
|
||||
const sample_type<std::tuple<Ts...>>& samples) {
|
||||
using acc_traits = detail::accumulator_traits<value_type>;
|
||||
using sample_args_passed =
|
||||
std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
|
||||
detail::sample_args_passed_vs_expected<sample_args_passed,
|
||||
typename acc_traits::args>();
|
||||
std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
|
||||
mp11::tuple_apply(
|
||||
[&](const auto&... sargs) {
|
||||
constexpr bool weight_valid = acc_traits::weight_support;
|
||||
static_assert(weight_valid, "error: accumulator does not support weights");
|
||||
constexpr bool sample_valid =
|
||||
std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
|
||||
detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_,
|
||||
storage_, axes_, detail::make_span(args),
|
||||
weight(detail::to_ptr_size(weights.value)),
|
||||
detail::to_ptr_size(sargs)...);
|
||||
},
|
||||
samples.value);
|
||||
}
|
||||
|
||||
template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const sample_type<T>& samples, const weight_type<U>& weights,
|
||||
const Iterable& args) {
|
||||
fill(args, weights, samples);
|
||||
}
|
||||
|
||||
template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const weight_type<T>& weights, const sample_type<U>& samples,
|
||||
const Iterable& args) {
|
||||
fill(args, weights, samples);
|
||||
}
|
||||
|
||||
template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args, const sample_type<T>& samples,
|
||||
const weight_type<U>& weights) {
|
||||
fill(args, weights, samples);
|
||||
}
|
||||
|
||||
/** Access cell value at integral indices.
|
||||
|
||||
You can pass indices as individual arguments, as a std::tuple of integers, or as an
|
||||
interable range of integers. Passing the wrong number of arguments causes a throw of
|
||||
std::invalid_argument. Passing an index which is out of bounds causes a throw of
|
||||
std::out_of_range.
|
||||
|
||||
@param i index of first axis.
|
||||
@param is indices of second, third, ... axes.
|
||||
@returns reference to cell value.
|
||||
*/
|
||||
template <class... Is>
|
||||
decltype(auto) at(axis::index_type i, Is... is) {
|
||||
return at(multi_index_type{i, static_cast<axis::index_type>(is)...});
|
||||
}
|
||||
|
||||
/// Access cell value at integral indices (read-only).
|
||||
template <class... Is>
|
||||
decltype(auto) at(axis::index_type i, Is... is) const {
|
||||
return at(multi_index_type{i, static_cast<axis::index_type>(is)...});
|
||||
}
|
||||
|
||||
/// Access cell value at integral indices stored in iterable.
|
||||
decltype(auto) at(const multi_index_type& is) {
|
||||
if (rank() != is.size())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("number of arguments != histogram rank"));
|
||||
const auto idx = detail::linearize_indices(axes_, is);
|
||||
if (!is_valid(idx))
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
|
||||
assert(idx < storage_.size());
|
||||
return storage_[idx];
|
||||
}
|
||||
|
||||
/// Access cell value at integral indices stored in iterable (read-only).
|
||||
decltype(auto) at(const multi_index_type& is) const {
|
||||
if (rank() != is.size())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("number of arguments != histogram rank"));
|
||||
const auto idx = detail::linearize_indices(axes_, is);
|
||||
if (!is_valid(idx))
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
|
||||
assert(idx < storage_.size());
|
||||
return storage_[idx];
|
||||
}
|
||||
|
||||
/// Access value at index (for rank = 1).
|
||||
decltype(auto) operator[](axis::index_type i) {
|
||||
const axis::index_type shift =
|
||||
axis::traits::options(axis()) & axis::option::underflow ? 1 : 0;
|
||||
return storage_[static_cast<std::size_t>(i + shift)];
|
||||
}
|
||||
|
||||
/// Access value at index (for rank = 1, read-only).
|
||||
decltype(auto) operator[](axis::index_type i) const {
|
||||
const axis::index_type shift =
|
||||
axis::traits::options(axis()) & axis::option::underflow ? 1 : 0;
|
||||
return storage_[static_cast<std::size_t>(i + shift)];
|
||||
}
|
||||
|
||||
/// Access value at index tuple.
|
||||
decltype(auto) operator[](const multi_index_type& is) {
|
||||
return storage_[detail::linearize_indices(axes_, is)];
|
||||
}
|
||||
|
||||
/// Access value at index tuple (read-only).
|
||||
decltype(auto) operator[](const multi_index_type& is) const {
|
||||
return storage_[detail::linearize_indices(axes_, is)];
|
||||
}
|
||||
|
||||
/// Equality operator, tests equality for all axes and the storage.
|
||||
template <class A, class S>
|
||||
bool operator==(const histogram<A, S>& rhs) const noexcept {
|
||||
// testing offset is redundant, but offers fast return if it fails
|
||||
return offset_ == unsafe_access::offset(rhs) &&
|
||||
detail::axes_equal(axes_, unsafe_access::axes(rhs)) &&
|
||||
storage_ == unsafe_access::storage(rhs);
|
||||
}
|
||||
|
||||
/// Negation of the equality operator.
|
||||
template <class A, class S>
|
||||
bool operator!=(const histogram<A, S>& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
/** Add values of another histogram.
|
||||
|
||||
This operator is only available if the value_type supports operator+=.
|
||||
|
||||
Both histograms must be compatible to be addable. The histograms are compatible, if
|
||||
the axes are either all identical. If the axes only differ in the states of their
|
||||
discrete growing axis types, then they are also compatible. The discrete growing
|
||||
axes are merged in this case.
|
||||
*/
|
||||
template <class A, class S>
|
||||
#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
histogram&
|
||||
#else
|
||||
std::enable_if_t<
|
||||
detail::has_operator_radd<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
#endif
|
||||
operator+=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
for (auto&& x : storage_) x += *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// specialization that allows axes merging
|
||||
template <class S>
|
||||
#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
histogram&
|
||||
#else
|
||||
std::enable_if_t<detail::has_operator_radd<
|
||||
value_type, typename histogram<axes_type, S>::value_type>::value,
|
||||
histogram&>
|
||||
#endif
|
||||
operator+=(const histogram<axes_type, S>& rhs) {
|
||||
const auto& raxes = unsafe_access::axes(rhs);
|
||||
if (detail::axes_equal(axes_, unsafe_access::axes(rhs))) {
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
for (auto&& x : storage_) x += *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (rank() != detail::axes_rank(raxes))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes have different length"));
|
||||
auto h = histogram<axes_type, storage_type>(
|
||||
detail::axes_transform(axes_, raxes, detail::axis_merger{}),
|
||||
detail::make_default(storage_));
|
||||
const auto& axes = unsafe_access::axes(h);
|
||||
const auto tr1 = detail::make_index_translator(axes, axes_);
|
||||
for (auto&& x : indexed(*this, coverage::all)) h[tr1(x.indices())] += *x;
|
||||
const auto tr2 = detail::make_index_translator(axes, raxes);
|
||||
for (auto&& x : indexed(rhs, coverage::all)) h[tr2(x.indices())] += *x;
|
||||
*this = std::move(h);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Subtract values of another histogram.
|
||||
|
||||
This operator is only available if the value_type supports operator-=.
|
||||
*/
|
||||
template <class A, class S>
|
||||
#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
histogram&
|
||||
#else
|
||||
std::enable_if_t<
|
||||
detail::has_operator_rsub<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
#endif
|
||||
operator-=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
for (auto&& x : storage_) x -= *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Multiply by values of another histogram.
|
||||
|
||||
This operator is only available if the value_type supports operator*=.
|
||||
*/
|
||||
template <class A, class S>
|
||||
#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
histogram&
|
||||
#else
|
||||
std::enable_if_t<
|
||||
detail::has_operator_rmul<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
#endif
|
||||
operator*=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
for (auto&& x : storage_) x *= *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Divide by values of another histogram.
|
||||
|
||||
This operator is only available if the value_type supports operator/=.
|
||||
*/
|
||||
template <class A, class S>
|
||||
#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
histogram&
|
||||
#else
|
||||
std::enable_if_t<
|
||||
detail::has_operator_rdiv<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
#endif
|
||||
operator/=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
for (auto&& x : storage_) x /= *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Multiply all values with a scalar.
|
||||
|
||||
This operator is only available if the value_type supports operator*=.
|
||||
*/
|
||||
#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
histogram&
|
||||
#else
|
||||
template <class V = value_type>
|
||||
std::enable_if_t<(detail::has_operator_rmul<V, double>::value), histogram&>
|
||||
#endif
|
||||
operator*=(const double x) {
|
||||
// use special storage implementation of scaling if available,
|
||||
// else fallback to scaling item by item
|
||||
detail::static_if<detail::has_operator_rmul<storage_type, double>>(
|
||||
[x](auto& s) { s *= x; },
|
||||
[x](auto& s) {
|
||||
for (auto&& si : s) si *= x;
|
||||
},
|
||||
storage_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Divide all values by a scalar.
|
||||
|
||||
This operator is only available if operator*= is available.
|
||||
*/
|
||||
#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
histogram&
|
||||
#else
|
||||
template <class H = histogram>
|
||||
std::enable_if_t<(detail::has_operator_rmul<H, double>::value), histogram&>
|
||||
#endif
|
||||
operator/=(const double x) {
|
||||
return operator*=(1.0 / x);
|
||||
}
|
||||
|
||||
/// Return value iterator to the beginning of the histogram.
|
||||
iterator begin() noexcept { return storage_.begin(); }
|
||||
|
||||
/// Return value iterator to the end in the histogram.
|
||||
iterator end() noexcept { return storage_.end(); }
|
||||
|
||||
/// Return value iterator to the beginning of the histogram (read-only).
|
||||
const_iterator begin() const noexcept { return storage_.begin(); }
|
||||
|
||||
/// Return value iterator to the end in the histogram (read-only).
|
||||
const_iterator end() const noexcept { return storage_.end(); }
|
||||
|
||||
/// Return value iterator to the beginning of the histogram (read-only).
|
||||
const_iterator cbegin() const noexcept { return begin(); }
|
||||
|
||||
/// Return value iterator to the end in the histogram (read-only).
|
||||
const_iterator cend() const noexcept { return end(); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
detail::axes_serialize(ar, axes_);
|
||||
ar& make_nvp("storage", storage_);
|
||||
if (Archive::is_loading::value) {
|
||||
offset_ = detail::offset(axes_);
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
axes_type axes_;
|
||||
storage_type storage_;
|
||||
std::size_t offset_ = 0;
|
||||
|
||||
friend struct unsafe_access;
|
||||
};
|
||||
|
||||
/**
|
||||
Pairwise add cells of two histograms and return histogram with the sum.
|
||||
|
||||
The returned histogram type is the most efficient and safest one constructible from the
|
||||
inputs, if they are not the same type. If one histogram has a tuple axis, the result has
|
||||
a tuple axis. The chosen storage is the one with the larger dynamic range.
|
||||
*/
|
||||
template <class A1, class S1, class A2, class S2>
|
||||
auto operator+(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
|
||||
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
|
||||
return r += b;
|
||||
}
|
||||
|
||||
/** Pairwise multiply cells of two histograms and return histogram with the product.
|
||||
|
||||
For notes on the returned histogram type, see operator+.
|
||||
*/
|
||||
template <class A1, class S1, class A2, class S2>
|
||||
auto operator*(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
|
||||
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
|
||||
return r *= b;
|
||||
}
|
||||
|
||||
/** Pairwise subtract cells of two histograms and return histogram with the difference.
|
||||
|
||||
For notes on the returned histogram type, see operator+.
|
||||
*/
|
||||
template <class A1, class S1, class A2, class S2>
|
||||
auto operator-(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
|
||||
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
|
||||
return r -= b;
|
||||
}
|
||||
|
||||
/** Pairwise divide cells of two histograms and return histogram with the quotient.
|
||||
|
||||
For notes on the returned histogram type, see operator+.
|
||||
*/
|
||||
template <class A1, class S1, class A2, class S2>
|
||||
auto operator/(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
|
||||
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
|
||||
return r /= b;
|
||||
}
|
||||
|
||||
/** Multiply all cells of the histogram by a number and return a new histogram.
|
||||
|
||||
If the original histogram has integer cells, the result has double cells.
|
||||
*/
|
||||
template <class A, class S>
|
||||
auto operator*(const histogram<A, S>& h, double x) {
|
||||
auto r = histogram<A, detail::common_storage<S, dense_storage<double>>>(h);
|
||||
return r *= x;
|
||||
}
|
||||
|
||||
/** Multiply all cells of the histogram by a number and return a new histogram.
|
||||
|
||||
If the original histogram has integer cells, the result has double cells.
|
||||
*/
|
||||
template <class A, class S>
|
||||
auto operator*(double x, const histogram<A, S>& h) {
|
||||
return h * x;
|
||||
}
|
||||
|
||||
/** Divide all cells of the histogram by a number and return a new histogram.
|
||||
|
||||
If the original histogram has integer cells, the result has double cells.
|
||||
*/
|
||||
template <class A, class S>
|
||||
auto operator/(const histogram<A, S>& h, double x) {
|
||||
return h * (1.0 / x);
|
||||
}
|
||||
|
||||
#if __cpp_deduction_guides >= 201606
|
||||
|
||||
template <class... Axes, class = detail::requires_axes<std::tuple<std::decay_t<Axes>...>>>
|
||||
histogram(Axes...) -> histogram<std::tuple<std::decay_t<Axes>...>>;
|
||||
|
||||
template <class... Axes, class S, class = detail::requires_storage_or_adaptible<S>>
|
||||
histogram(std::tuple<Axes...>, S)
|
||||
-> histogram<std::tuple<Axes...>, std::conditional_t<detail::is_adaptible<S>::value,
|
||||
storage_adaptor<S>, S>>;
|
||||
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>,
|
||||
class = detail::requires_any_axis<typename Iterable::value_type>>
|
||||
histogram(Iterable) -> histogram<std::vector<typename Iterable::value_type>>;
|
||||
|
||||
template <class Iterable, class S, class = detail::requires_iterable<Iterable>,
|
||||
class = detail::requires_any_axis<typename Iterable::value_type>,
|
||||
class = detail::requires_storage_or_adaptible<S>>
|
||||
histogram(Iterable, S) -> histogram<
|
||||
std::vector<typename Iterable::value_type>,
|
||||
std::conditional_t<detail::is_adaptible<S>::value, storage_adaptor<S>, S>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
372
install/boost_1_75_0/include/boost/histogram/indexed.hpp
Normal file
372
install/boost_1_75_0/include/boost/histogram/indexed.hpp
Normal file
@@ -0,0 +1,372 @@
|
||||
// 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 <array>
|
||||
#include <boost/config.hpp> // BOOST_ATTRIBUTE_NODISCARD
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/iterator_adaptor.hpp>
|
||||
#include <boost/histogram/detail/operators.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <iterator>
|
||||
#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 */
|
||||
};
|
||||
|
||||
/** Input iterator range over histogram bins with multi-dimensional index.
|
||||
|
||||
The iterator returned by begin() can only be incremented. begin() may only be called
|
||||
once, calling it a second time returns the end() iterator. If several copies of the
|
||||
input iterators exist, the other copies become invalid if one of them is incremented.
|
||||
*/
|
||||
template <class Histogram>
|
||||
class BOOST_ATTRIBUTE_NODISCARD indexed_range {
|
||||
private:
|
||||
using histogram_type = Histogram;
|
||||
static constexpr unsigned buffer_size =
|
||||
detail::buffer_size<typename std::decay_t<histogram_type>::axes_type>::value;
|
||||
|
||||
public:
|
||||
using value_iterator = std::conditional_t<std::is_const<histogram_type>::value,
|
||||
typename histogram_type::const_iterator,
|
||||
typename histogram_type::iterator>;
|
||||
using value_reference = typename std::iterator_traits<value_iterator>::reference;
|
||||
using value_type = typename std::iterator_traits<value_iterator>::value_type;
|
||||
|
||||
class iterator;
|
||||
using range_iterator [[deprecated("use iterator instead")]] = iterator; ///< deprecated
|
||||
|
||||
/** Lightweight view to access value and index of current cell.
|
||||
|
||||
The methods provide access to the current cell indices and bins. It acts like a
|
||||
pointer to the cell value, and in a limited way also like a reference. To interoperate
|
||||
with the algorithms of the standard library, the accessor is implicitly convertible to
|
||||
a cell value. Assignments and comparisons are passed through to the cell. An accessor
|
||||
is coupled to its parent indexed_range::iterator. Moving the parent iterator
|
||||
forward also updates the linked accessor. Accessors are not copyable. They cannot be
|
||||
stored in containers, but indexed_range::iterator can be stored.
|
||||
*/
|
||||
class BOOST_ATTRIBUTE_NODISCARD accessor : detail::mirrored<accessor, void> {
|
||||
public:
|
||||
/// Array-like view into the current multi-dimensional index.
|
||||
class index_view {
|
||||
using index_pointer = const typename iterator::index_data*;
|
||||
|
||||
public:
|
||||
using const_reference = const axis::index_type&;
|
||||
using reference [[deprecated("use const_reference instead")]] =
|
||||
const_reference; ///< deprecated
|
||||
|
||||
/// implementation detail
|
||||
class const_iterator
|
||||
: public detail::iterator_adaptor<const_iterator, index_pointer,
|
||||
const_reference> {
|
||||
public:
|
||||
const_reference operator*() const noexcept { return const_iterator::base()->idx; }
|
||||
|
||||
private:
|
||||
explicit const_iterator(index_pointer i) noexcept
|
||||
: const_iterator::iterator_adaptor_(i) {}
|
||||
|
||||
friend class index_view;
|
||||
};
|
||||
|
||||
const_iterator begin() const noexcept { return const_iterator{begin_}; }
|
||||
const_iterator end() const noexcept { return const_iterator{end_}; }
|
||||
std::size_t size() const noexcept {
|
||||
return static_cast<std::size_t>(end_ - begin_);
|
||||
}
|
||||
const_reference operator[](unsigned d) const noexcept { return begin_[d].idx; }
|
||||
const_reference at(unsigned d) const { return begin_[d].idx; }
|
||||
|
||||
private:
|
||||
/// implementation detail
|
||||
index_view(index_pointer b, index_pointer e) : begin_(b), end_(e) {}
|
||||
|
||||
index_pointer begin_, end_;
|
||||
friend class accessor;
|
||||
};
|
||||
|
||||
// assignment is pass-through
|
||||
accessor& operator=(const accessor& o) {
|
||||
get() = o.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// assignment is pass-through
|
||||
template <class T>
|
||||
accessor& operator=(const T& x) {
|
||||
get() = x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns the cell reference.
|
||||
value_reference get() const noexcept { return *(iter_.iter_); }
|
||||
/// @copydoc get()
|
||||
value_reference operator*() const noexcept { return get(); }
|
||||
/// Access fields and methods of the cell object.
|
||||
value_iterator operator->() const noexcept { return iter_.iter_; }
|
||||
|
||||
/// Access current index.
|
||||
/// @param d axis dimension.
|
||||
axis::index_type index(unsigned d = 0) const noexcept {
|
||||
return iter_.indices_[d].idx;
|
||||
}
|
||||
|
||||
/// Access indices as an iterable range.
|
||||
index_view indices() const noexcept {
|
||||
assert(iter_.indices_.hist_);
|
||||
return {iter_.indices_.begin(), iter_.indices_.end()};
|
||||
}
|
||||
|
||||
/// Access current bin.
|
||||
/// @tparam N axis dimension.
|
||||
template <unsigned N = 0>
|
||||
decltype(auto) bin(std::integral_constant<unsigned, N> = {}) const {
|
||||
assert(iter_.indices_.hist_);
|
||||
return iter_.indices_.hist_->axis(std::integral_constant<unsigned, N>())
|
||||
.bin(index(N));
|
||||
}
|
||||
|
||||
/// Access current bin.
|
||||
/// @param d axis dimension.
|
||||
decltype(auto) bin(unsigned d) const {
|
||||
assert(iter_.indices_.hist_);
|
||||
return iter_.indices_.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 {
|
||||
assert(iter_.indices_.hist_);
|
||||
double x = 1;
|
||||
unsigned d = 0;
|
||||
iter_.indices_.hist_->for_each_axis([&](const auto& a) {
|
||||
const auto w = axis::traits::width_as<double>(a, this->index(d++));
|
||||
x *= w != 0 ? w : 1;
|
||||
});
|
||||
return get() / x;
|
||||
}
|
||||
|
||||
// forward all comparison operators to the value
|
||||
bool operator<(const accessor& o) noexcept { return get() < o.get(); }
|
||||
bool operator>(const accessor& o) noexcept { return get() > o.get(); }
|
||||
bool operator==(const accessor& o) noexcept { return get() == o.get(); }
|
||||
bool operator!=(const accessor& o) noexcept { return get() != o.get(); }
|
||||
bool operator<=(const accessor& o) noexcept { return get() <= o.get(); }
|
||||
bool operator>=(const accessor& o) noexcept { return get() >= o.get(); }
|
||||
|
||||
template <class U>
|
||||
bool operator<(const U& o) const noexcept {
|
||||
return get() < o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator>(const U& o) const noexcept {
|
||||
return get() > o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator==(const U& o) const noexcept {
|
||||
return get() == o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator!=(const U& o) const noexcept {
|
||||
return get() != o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator<=(const U& o) const noexcept {
|
||||
return get() <= o;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
bool operator>=(const U& o) const noexcept {
|
||||
return get() >= o;
|
||||
}
|
||||
|
||||
operator value_type() const noexcept { return get(); }
|
||||
|
||||
private:
|
||||
accessor(iterator& i) noexcept : iter_(i) {}
|
||||
|
||||
accessor(const accessor&) = default; // only callable by indexed_range::iterator
|
||||
|
||||
iterator& iter_;
|
||||
|
||||
friend class iterator;
|
||||
};
|
||||
|
||||
/// implementation detail
|
||||
class iterator {
|
||||
public:
|
||||
using value_type = typename indexed_range::value_type;
|
||||
using reference = accessor;
|
||||
|
||||
private:
|
||||
struct pointer_proxy {
|
||||
reference* operator->() noexcept { return std::addressof(ref_); }
|
||||
reference ref_;
|
||||
};
|
||||
|
||||
public:
|
||||
using pointer = pointer_proxy;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
reference operator*() noexcept { return *this; }
|
||||
pointer operator->() noexcept { return pointer_proxy{operator*()}; }
|
||||
|
||||
iterator& operator++() {
|
||||
assert(iter_ < indices_.hist_->end());
|
||||
const auto cbeg = indices_.begin();
|
||||
auto c = cbeg;
|
||||
++iter_;
|
||||
++c->idx;
|
||||
if (c->idx < c->end) return *this;
|
||||
while (c->idx == c->end) {
|
||||
iter_ += c->end_skip;
|
||||
if (++c == indices_.end()) return *this;
|
||||
++c->idx;
|
||||
}
|
||||
while (c-- != cbeg) {
|
||||
c->idx = c->begin;
|
||||
iter_ += c->begin_skip;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
auto prev = *this;
|
||||
operator++();
|
||||
return prev;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& x) const noexcept { return iter_ == x.iter_; }
|
||||
bool operator!=(const iterator& x) const noexcept { return !operator==(x); }
|
||||
|
||||
// make iterator ready for C++17 sentinels
|
||||
bool operator==(const value_iterator& x) const noexcept { return iter_ == x; }
|
||||
bool operator!=(const value_iterator& x) const noexcept { return !operator==(x); }
|
||||
|
||||
// useful for iterator debugging
|
||||
std::size_t offset() const noexcept { return iter_ - indices_.hist_->begin(); }
|
||||
|
||||
private:
|
||||
iterator(value_iterator i, histogram_type& h) : iter_(i), indices_(&h) {}
|
||||
|
||||
value_iterator iter_;
|
||||
|
||||
struct index_data {
|
||||
axis::index_type idx, begin, end;
|
||||
std::size_t begin_skip, end_skip;
|
||||
};
|
||||
|
||||
struct indices_t : private std::array<index_data, buffer_size> {
|
||||
using base_type = std::array<index_data, buffer_size>;
|
||||
using pointer = index_data*;
|
||||
using const_pointer = const index_data*;
|
||||
|
||||
indices_t(histogram_type* h) noexcept : hist_{h} {}
|
||||
|
||||
using base_type::operator[];
|
||||
unsigned size() const noexcept { return hist_->rank(); }
|
||||
pointer begin() noexcept { return base_type::data(); }
|
||||
const_pointer begin() const noexcept { return base_type::data(); }
|
||||
pointer end() noexcept { return begin() + size(); }
|
||||
const_pointer end() const noexcept { return begin() + size(); }
|
||||
|
||||
histogram_type* hist_;
|
||||
} indices_;
|
||||
|
||||
friend class indexed_range;
|
||||
};
|
||||
|
||||
indexed_range(histogram_type& hist, coverage cov)
|
||||
: begin_(hist.begin(), hist), end_(hist.end(), hist) {
|
||||
begin_.indices_.hist_->for_each_axis([ca = begin_.indices_.begin(), cov,
|
||||
stride = std::size_t{1},
|
||||
this](const auto& a) mutable {
|
||||
using opt = axis::traits::get_options<std::decay_t<decltype(a)>>;
|
||||
constexpr axis::index_type under = opt::test(axis::option::underflow);
|
||||
constexpr axis::index_type over = opt::test(axis::option::overflow);
|
||||
const auto size = a.size();
|
||||
|
||||
// -1 if underflow and cover all, else 0
|
||||
ca->begin = cov == coverage::all ? -under : 0;
|
||||
// size + 1 if overflow and cover all, else size
|
||||
ca->end = cov == coverage::all ? size + over : size;
|
||||
ca->idx = ca->begin;
|
||||
|
||||
// if axis has *flow and coverage::all OR axis has no *flow:
|
||||
// begin + under == 0, size + over - end == 0
|
||||
// if axis has *flow and coverage::inner:
|
||||
// begin + under == 1, size + over - end == 1
|
||||
ca->begin_skip = static_cast<std::size_t>(ca->begin + under) * stride;
|
||||
ca->end_skip = static_cast<std::size_t>(size + over - ca->end) * stride;
|
||||
begin_.iter_ += ca->begin_skip;
|
||||
|
||||
stride *= size + under + over;
|
||||
++ca;
|
||||
});
|
||||
}
|
||||
|
||||
iterator begin() noexcept { return begin_; }
|
||||
iterator end() noexcept { return end_; }
|
||||
|
||||
private:
|
||||
iterator begin_, end_;
|
||||
};
|
||||
|
||||
/** Generates an indexed range of <a
|
||||
href="https://en.cppreference.com/w/cpp/named_req/ForwardIterator">forward iterators</a>
|
||||
over the histogram cells.
|
||||
|
||||
Use this in a range-based for loop:
|
||||
|
||||
```
|
||||
for (auto&& x : indexed(hist)) { ... }
|
||||
```
|
||||
|
||||
This generates an optimized loop which is nearly always faster than a hand-written loop
|
||||
over the histogram cells. 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. The returned iterators are forward iterators. They can be stored in a container,
|
||||
but may not be used after the life-time of the histogram ends.
|
||||
|
||||
@returns indexed_range
|
||||
|
||||
@param hist Reference to the histogram.
|
||||
@param cov Iterate over all or only inner bins (optional, default: inner).
|
||||
*/
|
||||
template <class 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
|
||||
33
install/boost_1_75_0/include/boost/histogram/literals.hpp
Normal file
33
install/boost_1_75_0/include/boost/histogram/literals.hpp
Normal 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
|
||||
138
install/boost_1_75_0/include/boost/histogram/make_histogram.hpp
Normal file
138
install/boost_1_75_0/include/boost/histogram/make_histogram.hpp
Normal file
@@ -0,0 +1,138 @@
|
||||
// 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/detect.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_storage_or_adaptible<Storage>,
|
||||
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 = std::decay_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_storage_or_adaptible<Storage>,
|
||||
class = detail::requires_sequence_of_any_axis<Iterable>>
|
||||
auto make_histogram_with(Storage&& storage, Iterable&& iterable) {
|
||||
using U = std::decay_t<Storage>;
|
||||
using S = mp11::mp_if<detail::is_storage<U>, U, storage_adaptor<U>>;
|
||||
using It = std::decay_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_storage_or_adaptible<Storage>,
|
||||
class = detail::requires_iterator<Iterator>>
|
||||
auto make_histogram_with(Storage&& storage, Iterator begin, Iterator end) {
|
||||
using T = std::decay_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
|
||||
@@ -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 <class Axis, class... Axes, class = 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 <class Axis, class... Axes, class = 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 <class Iterable, class = 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 <class Iterable, class = 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 <class Iterator, class = 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 <class Iterator, class = 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
|
||||
123
install/boost_1_75_0/include/boost/histogram/multi_index.hpp
Normal file
123
install/boost_1_75_0/include/boost/histogram/multi_index.hpp
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2020 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_MULTI_INDEX_HPP
|
||||
#define BOOST_HISTOGRAM_MULTI_INDEX_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/nonmember_container_access.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/integer_sequence.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
/** Holder for multiple axis indices.
|
||||
|
||||
Adapts external iterable, tuple, or explicit list of indices to the same representation.
|
||||
*/
|
||||
template <std::size_t Size>
|
||||
struct multi_index {
|
||||
using value_type = axis::index_type;
|
||||
using iterator = value_type*;
|
||||
using const_iterator = const value_type*;
|
||||
|
||||
static multi_index create(std::size_t s) {
|
||||
if (s != size())
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("size does not match static size"));
|
||||
return multi_index(priv_tag{});
|
||||
}
|
||||
|
||||
template <class... Is>
|
||||
multi_index(axis::index_type i, Is... is)
|
||||
: multi_index(std::initializer_list<axis::index_type>{
|
||||
i, static_cast<axis::index_type>(is)...}) {}
|
||||
|
||||
template <class... Is>
|
||||
multi_index(const std::tuple<axis::index_type, Is...>& is)
|
||||
: multi_index(is, mp11::make_index_sequence<(1 + sizeof...(Is))>{}) {}
|
||||
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
multi_index(const Iterable& is) {
|
||||
if (detail::size(is) != size())
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("no. of axes != no. of indices"));
|
||||
using std::begin;
|
||||
using std::end;
|
||||
std::copy(begin(is), end(is), data_);
|
||||
}
|
||||
|
||||
iterator begin() noexcept { return data_; }
|
||||
iterator end() noexcept { return data_ + size(); }
|
||||
const_iterator begin() const noexcept { return data_; }
|
||||
const_iterator end() const noexcept { return data_ + size(); }
|
||||
static constexpr std::size_t size() noexcept { return Size; }
|
||||
|
||||
private:
|
||||
struct priv_tag {};
|
||||
|
||||
multi_index(priv_tag) {}
|
||||
|
||||
template <class T, std::size_t... Is>
|
||||
multi_index(const T& is, mp11::index_sequence<Is...>)
|
||||
: multi_index(static_cast<axis::index_type>(std::get<Is>(is))...) {}
|
||||
|
||||
axis::index_type data_[size()];
|
||||
};
|
||||
|
||||
template <>
|
||||
struct multi_index<static_cast<std::size_t>(-1)> {
|
||||
using value_type = axis::index_type;
|
||||
using iterator = value_type*;
|
||||
using const_iterator = const value_type*;
|
||||
|
||||
static multi_index create(std::size_t s) { return multi_index(priv_tag{}, s); }
|
||||
|
||||
template <class... Is>
|
||||
multi_index(axis::index_type i, Is... is)
|
||||
: multi_index(std::initializer_list<axis::index_type>{
|
||||
i, static_cast<axis::index_type>(is)...}) {}
|
||||
|
||||
template <class... Is>
|
||||
multi_index(const std::tuple<axis::index_type, Is...>& is)
|
||||
: multi_index(is, mp11::make_index_sequence<(1 + sizeof...(Is))>{}) {}
|
||||
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
multi_index(const Iterable& is) : size_(detail::size(is)) {
|
||||
using std::begin;
|
||||
using std::end;
|
||||
std::copy(begin(is), end(is), data_);
|
||||
}
|
||||
|
||||
iterator begin() noexcept { return data_; }
|
||||
iterator end() noexcept { return data_ + size_; }
|
||||
const_iterator begin() const noexcept { return data_; }
|
||||
const_iterator end() const noexcept { return data_ + size_; }
|
||||
std::size_t size() const noexcept { return size_; }
|
||||
|
||||
private:
|
||||
struct priv_tag {};
|
||||
|
||||
multi_index(priv_tag, std::size_t s) : size_(s) {}
|
||||
|
||||
template <class T, std::size_t... Ns>
|
||||
multi_index(const T& is, mp11::index_sequence<Ns...>)
|
||||
: multi_index(static_cast<axis::index_type>(std::get<Ns>(is))...) {}
|
||||
|
||||
std::size_t size_ = 0;
|
||||
static constexpr std::size_t max_size_ = BOOST_HISTOGRAM_DETAIL_AXES_LIMIT;
|
||||
axis::index_type data_[max_size_];
|
||||
};
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
322
install/boost_1_75_0/include/boost/histogram/ostream.hpp
Normal file
322
install/boost_1_75_0/include/boost/histogram/ostream.hpp
Normal file
@@ -0,0 +1,322 @@
|
||||
// Copyright 2015-2019 Hans Dembinski
|
||||
// Copyright 2019 Przemyslaw Bartosik
|
||||
//
|
||||
// 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/detail/counting_streambuf.hpp>
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/histogram/indexed.hpp>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
\file boost/histogram/ostream.hpp
|
||||
|
||||
A simple streaming operator for the histogram type. The text representation is
|
||||
rudimentary and not guaranteed to be stable between versions of Boost.Histogram. This
|
||||
header is not included by any other header and must be explicitly included to use the
|
||||
streaming operator.
|
||||
|
||||
To use your own, simply include your own implementation instead of this header.
|
||||
*/
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class OStream, unsigned N>
|
||||
class tabular_ostream_wrapper : public std::array<int, N> {
|
||||
using base_t = std::array<int, N>;
|
||||
using char_type = typename OStream::char_type;
|
||||
using traits_type = typename OStream::traits_type;
|
||||
|
||||
public:
|
||||
template <class T>
|
||||
tabular_ostream_wrapper& operator<<(const T& t) {
|
||||
if (collect_) {
|
||||
if (static_cast<unsigned>(iter_ - base_t::begin()) == size_) {
|
||||
++size_;
|
||||
assert(size_ <= N);
|
||||
assert(iter_ != end());
|
||||
*iter_ = 0;
|
||||
}
|
||||
count_ = 0;
|
||||
os_ << t;
|
||||
*iter_ = std::max(*iter_, static_cast<int>(count_));
|
||||
} else {
|
||||
assert(iter_ != end());
|
||||
os_ << std::setw(*iter_) << t;
|
||||
}
|
||||
++iter_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tabular_ostream_wrapper& operator<<(decltype(std::setprecision(0)) t) {
|
||||
os_ << t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tabular_ostream_wrapper& operator<<(decltype(std::fixed) t) {
|
||||
os_ << t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tabular_ostream_wrapper& row() {
|
||||
iter_ = base_t::begin();
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit tabular_ostream_wrapper(OStream& os)
|
||||
: os_(os), cbuf_(count_), orig_(os_.rdbuf(&cbuf_)) {}
|
||||
|
||||
auto end() { return base_t::begin() + size_; }
|
||||
auto end() const { return base_t::begin() + size_; }
|
||||
auto cend() const { return base_t::cbegin() + size_; }
|
||||
|
||||
void complete() {
|
||||
assert(collect_); // only call this once
|
||||
collect_ = false;
|
||||
os_.rdbuf(orig_);
|
||||
}
|
||||
|
||||
private:
|
||||
typename base_t::iterator iter_ = base_t::begin();
|
||||
unsigned size_ = 0;
|
||||
std::streamsize count_ = 0;
|
||||
bool collect_ = true;
|
||||
OStream& os_;
|
||||
counting_streambuf<char_type, traits_type> cbuf_;
|
||||
std::basic_streambuf<char_type, traits_type>* orig_;
|
||||
};
|
||||
|
||||
template <class OStream, class T>
|
||||
void ostream_value_impl(OStream& os, const T& t,
|
||||
decltype(static_cast<double>(t), priority<1>{})) {
|
||||
// a value from histogram cell
|
||||
const auto d = static_cast<double>(t);
|
||||
if (std::numeric_limits<int>::min() <= d && d <= std::numeric_limits<int>::max()) {
|
||||
const auto i = static_cast<int>(d);
|
||||
if (i == d) {
|
||||
os << i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
os << std::defaultfloat << std::setprecision(4) << d;
|
||||
}
|
||||
|
||||
template <class OStream, class T>
|
||||
void ostream_value_impl(OStream& os, const T& t, priority<0>) {
|
||||
os << t;
|
||||
}
|
||||
|
||||
template <class OStream, class T>
|
||||
void ostream_value(OStream& os, const T& t) {
|
||||
ostream_value_impl(os << std::left, t, priority<1>{});
|
||||
}
|
||||
|
||||
template <class OStream, class Axis>
|
||||
auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::true_type,
|
||||
priority<1>) -> decltype((void)ax.value(i)) {
|
||||
auto a = ax.value(i), b = ax.value(i + 1);
|
||||
os << std::right << std::defaultfloat << std::setprecision(4);
|
||||
// round edges to zero if deviation from zero is small
|
||||
const auto eps = 1e-8 * std::abs(b - a);
|
||||
if (std::abs(a) < 1e-14 && std::abs(a) < eps) a = 0;
|
||||
if (std::abs(b) < 1e-14 && std::abs(b) < eps) b = 0;
|
||||
os << "[" << a << ", " << b << ")";
|
||||
}
|
||||
|
||||
template <class OStream, class Axis>
|
||||
auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::false_type,
|
||||
priority<1>) -> decltype((void)ax.value(i)) {
|
||||
os << std::right;
|
||||
os << ax.value(i);
|
||||
}
|
||||
|
||||
template <class OStream, class... Ts>
|
||||
void ostream_bin(OStream& os, const axis::category<Ts...>& ax, axis::index_type i,
|
||||
std::false_type, priority<1>) {
|
||||
os << std::right;
|
||||
if (i < ax.size())
|
||||
os << ax.value(i);
|
||||
else
|
||||
os << "other";
|
||||
}
|
||||
|
||||
template <class OStream, class Axis, class B>
|
||||
void ostream_bin(OStream& os, const Axis&, axis::index_type i, B, priority<0>) {
|
||||
os << std::right;
|
||||
os << i;
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
struct line_t {
|
||||
CharT ch;
|
||||
int size;
|
||||
};
|
||||
|
||||
template <class CharT>
|
||||
auto line(CharT c, int n) {
|
||||
return line_t<CharT>{c, n};
|
||||
}
|
||||
|
||||
template <class C, class T>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os, line_t<C>&& l) {
|
||||
for (int i = 0; i < l.size; ++i) os << l.ch;
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class OStream, class Axis>
|
||||
void ostream_head(OStream& os, const Axis& ax, int index, double val) {
|
||||
axis::visit(
|
||||
[&](const auto& ax) {
|
||||
using A = std::decay_t<decltype(ax)>;
|
||||
ostream_bin(os, ax, index, axis::traits::is_continuous<A>{}, priority<1>{});
|
||||
os << ' ';
|
||||
ostream_value(os, val);
|
||||
},
|
||||
ax);
|
||||
}
|
||||
|
||||
// cannot display generalized histograms yet; line not reachable by coverage tests
|
||||
template <class OStream, class Histogram>
|
||||
void ascii_plot(OStream&, const Histogram&, int, std::false_type) {} // LCOV_EXCL_LINE
|
||||
|
||||
template <class OStream, class Histogram>
|
||||
void ascii_plot(OStream& os, const Histogram& h, int w_total, std::true_type) {
|
||||
if (w_total == 0) w_total = 78; // TODO detect actual width of terminal
|
||||
|
||||
const auto& ax = h.axis();
|
||||
|
||||
// value range; can be integer or float, positive or negative
|
||||
double vmin = 0;
|
||||
double vmax = 0;
|
||||
tabular_ostream_wrapper<OStream, 7> tos(os);
|
||||
// first pass to get widths
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
ostream_head(tos.row(), ax, v.index(), *v);
|
||||
vmin = std::min(vmin, static_cast<double>(*v));
|
||||
vmax = std::max(vmax, static_cast<double>(*v));
|
||||
}
|
||||
tos.complete();
|
||||
if (vmax == 0) vmax = 1;
|
||||
|
||||
// calculate width useable by bar (notice extra space at top)
|
||||
// <-- head --> |<--- bar ---> |
|
||||
// w_head + 2 + 2
|
||||
const auto w_head = std::accumulate(tos.begin(), tos.end(), 0);
|
||||
const auto w_bar = w_total - 4 - w_head;
|
||||
if (w_bar < 0) return;
|
||||
|
||||
// draw upper line
|
||||
os << '\n' << line(' ', w_head + 1) << '+' << line('-', w_bar + 1) << "+\n";
|
||||
|
||||
const int zero_offset = static_cast<int>(std::lround((-vmin) / (vmax - vmin) * w_bar));
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
ostream_head(tos.row(), ax, v.index(), *v);
|
||||
// rest uses os, not tos
|
||||
os << " |";
|
||||
const int k = static_cast<int>(std::lround(*v / (vmax - vmin) * w_bar));
|
||||
if (k < 0) {
|
||||
os << line(' ', zero_offset + k) << line('=', -k) << line(' ', w_bar - zero_offset);
|
||||
} else {
|
||||
os << line(' ', zero_offset) << line('=', k) << line(' ', w_bar - zero_offset - k);
|
||||
}
|
||||
os << " |\n";
|
||||
}
|
||||
|
||||
// draw lower line
|
||||
os << line(' ', w_head + 1) << '+' << line('-', w_bar + 1) << "+\n";
|
||||
}
|
||||
|
||||
template <class OStream, class Histogram>
|
||||
void ostream(OStream& os, const Histogram& h, const bool show_values = true) {
|
||||
os << "histogram(";
|
||||
|
||||
unsigned iaxis = 0;
|
||||
const auto rank = h.rank();
|
||||
h.for_each_axis([&](const auto& ax) {
|
||||
if ((show_values && rank > 0) || rank > 1) os << "\n ";
|
||||
ostream_any(os, ax);
|
||||
});
|
||||
|
||||
if (show_values && rank > 0) {
|
||||
tabular_ostream_wrapper<OStream, (BOOST_HISTOGRAM_DETAIL_AXES_LIMIT + 1)> tos(os);
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
tos.row();
|
||||
for (auto i : v.indices()) tos << std::right << i;
|
||||
ostream_value(tos, *v);
|
||||
}
|
||||
tos.complete();
|
||||
|
||||
const int w_item = std::accumulate(tos.begin(), tos.end(), 0) + 4 + h.rank();
|
||||
const int nrow = std::max(1, 65 / w_item);
|
||||
int irow = 0;
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
os << (irow == 0 ? "\n (" : " (");
|
||||
tos.row();
|
||||
iaxis = 0;
|
||||
for (auto i : v.indices()) {
|
||||
tos << std::right << i;
|
||||
os << (++iaxis == h.rank() ? "):" : " ");
|
||||
}
|
||||
os << ' ';
|
||||
ostream_value(tos, *v);
|
||||
++irow;
|
||||
if (nrow > 0 && irow == nrow) irow = 0;
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
os << ')';
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
template <class CharT, class Traits, class A, class S>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const histogram<A, S>& h) {
|
||||
// save fmt
|
||||
const auto flags = os.flags();
|
||||
|
||||
os.flags(std::ios::dec | std::ios::left);
|
||||
|
||||
const auto w = static_cast<int>(os.width());
|
||||
os.width(0);
|
||||
|
||||
using value_type = typename histogram<A, S>::value_type;
|
||||
|
||||
// must be non-const to avoid a msvc warning about possible use of if constexpr
|
||||
bool show_ascii = std::is_convertible<value_type, double>::value && h.rank() == 1;
|
||||
if (show_ascii) {
|
||||
detail::ostream(os, h, false);
|
||||
detail::ascii_plot(os, h, w, std::is_convertible<value_type, double>{});
|
||||
} else {
|
||||
detail::ostream(os, h);
|
||||
}
|
||||
|
||||
// restore fmt
|
||||
os.flags(flags);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
#endif
|
||||
39
install/boost_1_75_0/include/boost/histogram/sample.hpp
Normal file
39
install/boost_1_75_0/include/boost/histogram/sample.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 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_SAMPLE_HPP
|
||||
#define BOOST_HISTOGRAM_SAMPLE_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
/** Sample holder and type envelope.
|
||||
|
||||
You should not construct these directly, use the sample() helper function.
|
||||
|
||||
@tparam Underlying type.
|
||||
*/
|
||||
template <class T>
|
||||
struct sample_type {
|
||||
T value;
|
||||
};
|
||||
|
||||
/** Helper function to mark arguments as sample.
|
||||
|
||||
@param ts arguments to be forwarded to the accumulator.
|
||||
*/
|
||||
template <class... Ts>
|
||||
auto sample(Ts&&... ts) noexcept {
|
||||
return sample_type<std::tuple<Ts...>>{std::forward_as_tuple(std::forward<Ts>(ts)...)};
|
||||
}
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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_SERIALIZATION_HPP
|
||||
#define BOOST_HISTOGRAM_SERIALIZATION_HPP
|
||||
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/map.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
|
||||
/**
|
||||
\file boost/histogram/serialization.hpp
|
||||
|
||||
Headers from
|
||||
[Boost.Serialization](https://www.boost.org/doc/libs/develop/libs/serialization/doc/index.html)
|
||||
needed to serialize STL types that are used internally by the Boost.Histogram classes.
|
||||
*/
|
||||
|
||||
#endif
|
||||
396
install/boost_1_75_0/include/boost/histogram/storage_adaptor.hpp
Normal file
396
install/boost_1_75_0/include/boost/histogram/storage_adaptor.hpp
Normal file
@@ -0,0 +1,396 @@
|
||||
// Copyright 2018-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_STORAGE_ADAPTOR_HPP
|
||||
#define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/detail/array_wrapper.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/iterator_adaptor.hpp>
|
||||
#include <boost/histogram/detail/safe_comparison.hpp>
|
||||
#include <boost/histogram/fwd.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 {
|
||||
using allocator_type = typename T::allocator_type;
|
||||
|
||||
static constexpr bool has_threading_support =
|
||||
accumulators::is_thread_safe<typename T::value_type>::value;
|
||||
|
||||
vector_impl(const allocator_type& a = {}) : T(a) {}
|
||||
vector_impl(const vector_impl&) = default;
|
||||
vector_impl& operator=(const vector_impl&) = default;
|
||||
vector_impl(vector_impl&&) = default;
|
||||
vector_impl& operator=(vector_impl&&) = default;
|
||||
|
||||
explicit vector_impl(T&& t) : T(std::move(t)) {}
|
||||
explicit vector_impl(const T& t) : T(t) {}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
explicit vector_impl(const U& u, const allocator_type& a = {})
|
||||
: T(std::begin(u), std::end(u), a) {}
|
||||
|
||||
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 Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("vector", static_cast<T&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct array_impl : T {
|
||||
static constexpr bool has_threading_support =
|
||||
accumulators::is_thread_safe<typename T::value_type>::value;
|
||||
|
||||
array_impl() = default;
|
||||
array_impl(const array_impl& t) : T(t), size_(t.size_) {}
|
||||
array_impl& operator=(const array_impl& t) {
|
||||
T::operator=(t);
|
||||
size_ = t.size_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit array_impl(T&& t) : T(std::move(t)) {}
|
||||
explicit array_impl(const T& t) : T(t) {}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
explicit array_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>>
|
||||
array_impl& operator=(const U& u) {
|
||||
if (u.size() > T::max_size()) // for std::arra
|
||||
BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
|
||||
size_ = u.size();
|
||||
using std::begin;
|
||||
using std::end;
|
||||
std::copy(begin(u), end(u), T::begin());
|
||||
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("argument size exceeds maximum capacity"));
|
||||
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_; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("size", size_);
|
||||
auto w = detail::make_array_wrapper(T::data(), size_);
|
||||
ar& make_nvp("array", w);
|
||||
}
|
||||
|
||||
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&;
|
||||
|
||||
static constexpr bool has_threading_support = false;
|
||||
static_assert(
|
||||
!accumulators::is_thread_safe<value_type>::value,
|
||||
"std::map and std::unordered_map do not support thread-safe element access. "
|
||||
"If you have a map with thread-safe element access, please file an issue and"
|
||||
"support will be added.");
|
||||
|
||||
struct reference {
|
||||
reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
|
||||
|
||||
reference(const reference&) noexcept = default;
|
||||
reference& operator=(const 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);
|
||||
}
|
||||
|
||||
reference& operator=(const_reference 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 {
|
||||
value_type tmp{};
|
||||
++tmp;
|
||||
map->emplace(idx, tmp);
|
||||
}
|
||||
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 = *this;
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
|
||||
bool operator==(const U& rhs) const {
|
||||
return operator const_reference() == rhs;
|
||||
}
|
||||
|
||||
template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
|
||||
bool operator!=(const U& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits>
|
||||
friend std::basic_ostream<CharT, Traits>& operator<<(
|
||||
std::basic_ostream<CharT, Traits>& os, reference x) {
|
||||
os << static_cast<const_reference>(x);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) {
|
||||
return (*map)[idx](args...);
|
||||
}
|
||||
|
||||
map_impl* map;
|
||||
std::size_t idx;
|
||||
};
|
||||
|
||||
template <class Value, class Reference, class MapPtr>
|
||||
struct iterator_t
|
||||
: iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Reference> {
|
||||
iterator_t() = default;
|
||||
template <class V, class R, class M,
|
||||
class = std::enable_if_t<std::is_convertible<M, MapPtr>::value>>
|
||||
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();
|
||||
}
|
||||
Reference operator*() 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*>;
|
||||
|
||||
using allocator_type = typename T::allocator_type;
|
||||
|
||||
map_impl(const allocator_type& a = {}) : T(a) {}
|
||||
|
||||
map_impl(const map_impl&) = default;
|
||||
map_impl& operator=(const map_impl&) = default;
|
||||
map_impl(map_impl&&) = default;
|
||||
map_impl& operator=(map_impl&&) = default;
|
||||
|
||||
map_impl(const T& t) : T(t), size_(t.size()) {}
|
||||
map_impl(T&& t) : T(std::move(t)), size_(t.size()) {}
|
||||
|
||||
template <class U, class = requires_iterable<U>>
|
||||
explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), 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_; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("size", size_);
|
||||
ar& make_nvp("map", static_cast<T&>(*this));
|
||||
}
|
||||
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct ERROR_type_passed_to_storage_adaptor_not_recognized;
|
||||
|
||||
// clang-format off
|
||||
template <class T>
|
||||
using storage_adaptor_impl =
|
||||
mp11::mp_cond<
|
||||
is_vector_like<T>, vector_impl<T>,
|
||||
is_array_like<T>, array_impl<T>,
|
||||
is_map_like<T>, map_impl<T>,
|
||||
std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized<T>
|
||||
>;
|
||||
// clang-format on
|
||||
} // namespace detail
|
||||
|
||||
/// Turns any vector-like, array-like, and map-like container into a storage type.
|
||||
template <class T>
|
||||
class storage_adaptor : public detail::storage_adaptor_impl<T> {
|
||||
using impl_type = detail::storage_adaptor_impl<T>;
|
||||
|
||||
public:
|
||||
// standard copy, move, assign
|
||||
storage_adaptor(storage_adaptor&&) = default;
|
||||
storage_adaptor(const storage_adaptor&) = default;
|
||||
storage_adaptor& operator=(storage_adaptor&&) = default;
|
||||
storage_adaptor& operator=(const storage_adaptor&) = default;
|
||||
|
||||
// forwarding constructor
|
||||
template <class... Ts>
|
||||
storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {}
|
||||
|
||||
// forwarding assign
|
||||
template <class U>
|
||||
storage_adaptor& operator=(U&& u) {
|
||||
impl_type::operator=(std::forward<U>(u));
|
||||
return *this;
|
||||
}
|
||||
|
||||
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), detail::safe_equal{});
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("impl", static_cast<impl_type&>(*this));
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct unsafe_access;
|
||||
};
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,636 @@
|
||||
// Copyright 2015-2019 Hans Dembinski
|
||||
// Copyright 2019 Glen Joseph Fernandes (glenjofe@gmail.com)
|
||||
//
|
||||
// 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/core/alloc_construct.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/detail/array_wrapper.hpp>
|
||||
#include <boost/histogram/detail/iterator_adaptor.hpp>
|
||||
#include <boost/histogram/detail/large_int.hpp>
|
||||
#include <boost/histogram/detail/operators.hpp>
|
||||
#include <boost/histogram/detail/safe_comparison.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
struct is_large_int : std::false_type {};
|
||||
|
||||
template <class A>
|
||||
struct is_large_int<large_int<A>> : std::true_type {};
|
||||
|
||||
template <class T, class ReturnType>
|
||||
using if_arithmetic_or_large_int =
|
||||
std::enable_if_t<(std::is_arithmetic<T>::value || is_large_int<T>::value),
|
||||
ReturnType>;
|
||||
|
||||
template <class L, class T>
|
||||
using next_type = mp11::mp_at_c<L, (mp11::mp_find<L, T>::value + 1)>;
|
||||
|
||||
template <class Allocator>
|
||||
class construct_guard {
|
||||
public:
|
||||
using pointer = typename std::allocator_traits<Allocator>::pointer;
|
||||
|
||||
construct_guard(Allocator& a, pointer p, std::size_t n) noexcept
|
||||
: a_(a), p_(p), n_(n) {}
|
||||
|
||||
~construct_guard() {
|
||||
if (p_) { a_.deallocate(p_, n_); }
|
||||
}
|
||||
|
||||
void release() { p_ = pointer(); }
|
||||
|
||||
construct_guard(const construct_guard&) = delete;
|
||||
construct_guard& operator=(const construct_guard&) = delete;
|
||||
|
||||
private:
|
||||
Allocator& a_;
|
||||
pointer p_;
|
||||
std::size_t n_;
|
||||
};
|
||||
|
||||
template <class Allocator>
|
||||
void* buffer_create(Allocator& a, std::size_t n) {
|
||||
auto ptr = a.allocate(n); // may throw
|
||||
static_assert(std::is_trivially_copyable<decltype(ptr)>::value,
|
||||
"ptr must be trivially copyable");
|
||||
construct_guard<Allocator> guard(a, ptr, n);
|
||||
boost::alloc_construct_n(a, ptr, n);
|
||||
guard.release();
|
||||
return static_cast<void*>(ptr);
|
||||
}
|
||||
|
||||
template <class Allocator, class Iterator>
|
||||
auto buffer_create(Allocator& a, std::size_t n, Iterator iter) {
|
||||
assert(n > 0u);
|
||||
auto ptr = a.allocate(n); // may throw
|
||||
static_assert(std::is_trivially_copyable<decltype(ptr)>::value,
|
||||
"ptr must be trivially copyable");
|
||||
construct_guard<Allocator> guard(a, ptr, n);
|
||||
using T = typename std::allocator_traits<Allocator>::value_type;
|
||||
struct casting_iterator {
|
||||
void operator++() noexcept { ++iter_; }
|
||||
T operator*() noexcept {
|
||||
return static_cast<T>(*iter_);
|
||||
} // silence conversion warnings
|
||||
Iterator iter_;
|
||||
};
|
||||
boost::alloc_construct_n(a, ptr, n, casting_iterator{iter});
|
||||
guard.release();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <class Allocator>
|
||||
void buffer_destroy(Allocator& a, typename std::allocator_traits<Allocator>::pointer p,
|
||||
std::size_t n) {
|
||||
assert(p);
|
||||
assert(n > 0u);
|
||||
boost::alloc_destroy_n(a, p, n);
|
||||
a.deallocate(p, n);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
Memory-efficient storage for integral counters which cannot overflow.
|
||||
|
||||
This storage provides a no-overflow-guarantee if the counters are incremented with
|
||||
integer weights. It maintains a contiguous array of elemental counters, one for each
|
||||
cell. If an operation is requested which would overflow a counter, the 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 triggers a conversion of the
|
||||
elemental counters 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");
|
||||
using U8 = std::uint8_t;
|
||||
using U16 = std::uint16_t;
|
||||
using U32 = std::uint32_t;
|
||||
using U64 = std::uint64_t;
|
||||
|
||||
public:
|
||||
static constexpr bool has_threading_support = false;
|
||||
|
||||
using allocator_type = Allocator;
|
||||
using value_type = double;
|
||||
using large_int = detail::large_int<
|
||||
typename std::allocator_traits<allocator_type>::template rebind_alloc<U64>>;
|
||||
|
||||
struct buffer_type {
|
||||
// cannot be moved outside of scope of unlimited_storage, large_int is dependent type
|
||||
using types = mp11::mp_list<U8, U16, U32, U64, large_int, double>;
|
||||
|
||||
template <class T>
|
||||
static constexpr unsigned type_index() noexcept {
|
||||
return static_cast<unsigned>(mp11::mp_find<types, T>::value);
|
||||
}
|
||||
|
||||
template <class F, class... Ts>
|
||||
decltype(auto) visit(F&& f, Ts&&... ts) const {
|
||||
// this is intentionally not a switch, the if-chain is faster in benchmarks
|
||||
if (type == type_index<U8>())
|
||||
return f(static_cast<U8*>(ptr), std::forward<Ts>(ts)...);
|
||||
if (type == type_index<U16>())
|
||||
return f(static_cast<U16*>(ptr), std::forward<Ts>(ts)...);
|
||||
if (type == type_index<U32>())
|
||||
return f(static_cast<U32*>(ptr), std::forward<Ts>(ts)...);
|
||||
if (type == type_index<U64>())
|
||||
return f(static_cast<U64*>(ptr), std::forward<Ts>(ts)...);
|
||||
if (type == type_index<large_int>())
|
||||
return f(static_cast<large_int*>(ptr), std::forward<Ts>(ts)...);
|
||||
return f(static_cast<double*>(ptr), std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
buffer_type(const allocator_type& a = {}) : alloc(a) {}
|
||||
|
||||
buffer_type(buffer_type&& o) noexcept
|
||||
: alloc(std::move(o.alloc))
|
||||
, size(boost::exchange(o.size, 0))
|
||||
, type(boost::exchange(o.type, 0))
|
||||
, ptr(boost::exchange(o.ptr, nullptr)) {}
|
||||
|
||||
buffer_type& operator=(buffer_type&& o) noexcept {
|
||||
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& x) : alloc(x.alloc) {
|
||||
x.visit([this, n = x.size](const auto* xp) {
|
||||
using T = std::decay_t<decltype(*xp)>;
|
||||
this->template make<T>(n, xp);
|
||||
});
|
||||
}
|
||||
|
||||
buffer_type& operator=(const buffer_type& o) {
|
||||
*this = buffer_type(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~buffer_type() noexcept { destroy(); }
|
||||
|
||||
void destroy() noexcept {
|
||||
assert((ptr == nullptr) == (size == 0));
|
||||
if (ptr == nullptr) return;
|
||||
visit([this](auto* p) {
|
||||
using T = std::decay_t<decltype(*p)>;
|
||||
using alloc_type =
|
||||
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
|
||||
alloc_type a(alloc); // rebind allocator
|
||||
detail::buffer_destroy(a, p, this->size);
|
||||
});
|
||||
size = 0;
|
||||
type = 0;
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
||||
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::buffer_create(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
|
||||
void* 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::buffer_create(a, n, iter); // may throw
|
||||
}
|
||||
destroy();
|
||||
size = n;
|
||||
type = new_type;
|
||||
ptr = new_ptr;
|
||||
}
|
||||
|
||||
allocator_type alloc;
|
||||
std::size_t size = 0;
|
||||
unsigned type = 0;
|
||||
mutable void* ptr = nullptr;
|
||||
};
|
||||
|
||||
class reference; // forward declare to make friend of const_reference
|
||||
|
||||
/// implementation detail
|
||||
class const_reference
|
||||
: detail::partially_ordered<const_reference, const_reference, void> {
|
||||
public:
|
||||
const_reference(buffer_type& b, std::size_t i) noexcept : bref_(b), idx_(i) {
|
||||
assert(idx_ < bref_.size);
|
||||
}
|
||||
|
||||
const_reference(const const_reference&) noexcept = default;
|
||||
|
||||
// no assignment for const_references
|
||||
const_reference& operator=(const const_reference&) = delete;
|
||||
const_reference& operator=(const_reference&&) = delete;
|
||||
|
||||
operator double() const noexcept {
|
||||
return bref_.visit(
|
||||
[this](const auto* p) { return static_cast<double>(p[this->idx_]); });
|
||||
}
|
||||
|
||||
bool operator<(const const_reference& o) const noexcept {
|
||||
return apply_binary<detail::safe_less>(o);
|
||||
}
|
||||
|
||||
bool operator==(const const_reference& o) const noexcept {
|
||||
return apply_binary<detail::safe_equal>(o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, bool> operator<(const U& o) const noexcept {
|
||||
return apply_binary<detail::safe_less>(o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, bool> operator>(const U& o) const noexcept {
|
||||
return apply_binary<detail::safe_greater>(o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, bool> operator==(const U& o) const noexcept {
|
||||
return apply_binary<detail::safe_equal>(o);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Binary>
|
||||
bool apply_binary(const const_reference& x) const noexcept {
|
||||
return x.bref_.visit([this, ix = x.idx_](const auto* xp) {
|
||||
return this->apply_binary<Binary>(xp[ix]);
|
||||
});
|
||||
}
|
||||
|
||||
template <class Binary, class U>
|
||||
bool apply_binary(const U& x) const noexcept {
|
||||
return bref_.visit([i = idx_, &x](const auto* p) { return Binary()(p[i], x); });
|
||||
}
|
||||
|
||||
protected:
|
||||
buffer_type& bref_;
|
||||
std::size_t idx_;
|
||||
friend class reference;
|
||||
};
|
||||
|
||||
/// implementation detail
|
||||
class reference : public const_reference,
|
||||
public detail::partially_ordered<reference, reference, void> {
|
||||
public:
|
||||
reference(buffer_type& b, std::size_t i) noexcept : const_reference(b, i) {}
|
||||
|
||||
// references do copy-construct
|
||||
reference(const reference& x) noexcept = default;
|
||||
|
||||
// references do not rebind, assign through
|
||||
reference& operator=(const reference& x) {
|
||||
return operator=(static_cast<const_reference>(x));
|
||||
}
|
||||
|
||||
// references do not rebind, assign through
|
||||
reference& operator=(const const_reference& x) {
|
||||
// safe for self-assignment, assigning matching type doesn't invalide buffer
|
||||
x.bref_.visit([this, ix = x.idx_](const auto* xp) { this->operator=(xp[ix]); });
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, reference&> operator=(const U& x) {
|
||||
this->bref_.visit([this, &x](auto* p) {
|
||||
// gcc-8 optimizes the expression `p[this->idx_] = 0` away even at -O0,
|
||||
// so we merge it into the next line which is properly counted
|
||||
adder()((p[this->idx_] = 0, p), this->bref_, this->idx_, x);
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator<(const reference& o) const noexcept {
|
||||
return const_reference::operator<(o);
|
||||
}
|
||||
|
||||
bool operator==(const reference& o) const noexcept {
|
||||
return const_reference::operator==(o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, bool> operator<(const U& o) const noexcept {
|
||||
return const_reference::operator<(o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, bool> operator>(const U& o) const noexcept {
|
||||
return const_reference::operator>(o);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, bool> operator==(const U& o) const noexcept {
|
||||
return const_reference::operator==(o);
|
||||
}
|
||||
|
||||
reference& operator+=(const const_reference& x) {
|
||||
x.bref_.visit([this, ix = x.idx_](const auto* xp) { this->operator+=(xp[ix]); });
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
detail::if_arithmetic_or_large_int<U, reference&> operator+=(const U& x) {
|
||||
this->bref_.visit(adder(), this->bref_, this->idx_, x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference& operator-=(const double x) { return operator+=(-x); }
|
||||
|
||||
reference& operator*=(const double x) {
|
||||
this->bref_.visit(multiplier(), this->bref_, this->idx_, x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference& operator/=(const double x) { return operator*=(1.0 / x); }
|
||||
|
||||
reference& operator++() {
|
||||
this->bref_.visit(incrementor(), this->bref_, this->idx_);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
template <class Value, class Reference>
|
||||
class iterator_impl : public detail::iterator_adaptor<iterator_impl<Value, Reference>,
|
||||
std::size_t, Reference, Value> {
|
||||
public:
|
||||
iterator_impl() = default;
|
||||
template <class V, class R>
|
||||
iterator_impl(const iterator_impl<V, R>& it)
|
||||
: iterator_impl::iterator_adaptor_(it.base()), buffer_(it.buffer_) {}
|
||||
iterator_impl(buffer_type* b, std::size_t i) noexcept
|
||||
: iterator_impl::iterator_adaptor_(i), buffer_(b) {}
|
||||
|
||||
Reference operator*() const noexcept { return {*buffer_, this->base()}; }
|
||||
|
||||
template <class V, class R>
|
||||
friend class iterator_impl;
|
||||
|
||||
private:
|
||||
mutable buffer_type* buffer_ = nullptr;
|
||||
};
|
||||
|
||||
public:
|
||||
using const_iterator = iterator_impl<const value_type, const_reference>;
|
||||
using iterator = iterator_impl<value_type, reference>;
|
||||
|
||||
explicit unlimited_storage(const allocator_type& a = {}) : buffer_(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;
|
||||
|
||||
// TODO
|
||||
// template <class Allocator>
|
||||
// unlimited_storage(const unlimited_storage<Allocator>& s)
|
||||
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
explicit unlimited_storage(const Iterable& s) {
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto s_begin = begin(s);
|
||||
auto s_end = end(s);
|
||||
using V = typename std::iterator_traits<decltype(begin(s))>::value_type;
|
||||
// must be non-const to avoid msvc warning about if constexpr
|
||||
auto ti = buffer_type::template type_index<V>();
|
||||
auto nt = mp11::mp_size<typename buffer_type::types>::value;
|
||||
const std::size_t size = static_cast<std::size_t>(std::distance(s_begin, s_end));
|
||||
if (ti < nt)
|
||||
buffer_.template make<V>(size, s_begin);
|
||||
else
|
||||
buffer_.template make<double>(size, s_begin);
|
||||
}
|
||||
|
||||
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 n) { buffer_.template make<U8>(n); }
|
||||
|
||||
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& x) const noexcept {
|
||||
if (size() != x.size()) return false;
|
||||
return buffer_.visit([&x](const auto* p) {
|
||||
return x.buffer_.visit([p, n = x.size()](const auto* xp) {
|
||||
return std::equal(p, p + n, xp, detail::safe_equal{});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
template <class Iterable>
|
||||
bool operator==(const Iterable& iterable) const {
|
||||
if (size() != iterable.size()) return false;
|
||||
return buffer_.visit([&iterable](const auto* p) {
|
||||
return std::equal(p, p + iterable.size(), std::begin(iterable),
|
||||
detail::safe_equal{});
|
||||
});
|
||||
}
|
||||
|
||||
unlimited_storage& operator*=(const double x) {
|
||||
buffer_.visit(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()}; }
|
||||
|
||||
/// implementation detail; used by unit tests, not part of generic storage interface
|
||||
template <class T>
|
||||
unlimited_storage(std::size_t s, const T* p, const allocator_type& a = {})
|
||||
: buffer_(std::move(a)) {
|
||||
buffer_.template make<T>(s, p);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
if (Archive::is_loading::value) {
|
||||
buffer_type tmp(buffer_.alloc);
|
||||
std::size_t size;
|
||||
ar& make_nvp("type", tmp.type);
|
||||
ar& make_nvp("size", size);
|
||||
tmp.visit([this, size](auto* tp) {
|
||||
assert(tp == nullptr);
|
||||
using T = std::decay_t<decltype(*tp)>;
|
||||
buffer_.template make<T>(size);
|
||||
});
|
||||
} else {
|
||||
ar& make_nvp("type", buffer_.type);
|
||||
ar& make_nvp("size", buffer_.size);
|
||||
}
|
||||
buffer_.visit([this, &ar](auto* tp) {
|
||||
auto w = detail::make_array_wrapper(tp, this->buffer_.size);
|
||||
ar& make_nvp("buffer", w);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
struct incrementor {
|
||||
template <class T>
|
||||
void operator()(T* tp, buffer_type& b, std::size_t i) {
|
||||
assert(tp && i < b.size);
|
||||
if (!detail::safe_increment(tp[i])) {
|
||||
using U = detail::next_type<typename buffer_type::types, T>;
|
||||
b.template make<U>(b.size, tp);
|
||||
++static_cast<U*>(b.ptr)[i];
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(large_int* tp, buffer_type&, std::size_t i) { ++tp[i]; }
|
||||
|
||||
void operator()(double* tp, buffer_type&, std::size_t i) { ++tp[i]; }
|
||||
};
|
||||
|
||||
struct adder {
|
||||
template <class U>
|
||||
void operator()(double* tp, buffer_type&, std::size_t i, const U& x) {
|
||||
tp[i] += static_cast<double>(x);
|
||||
}
|
||||
|
||||
void operator()(large_int* tp, buffer_type&, std::size_t i, const large_int& x) {
|
||||
tp[i] += x; // potentially adding large_int to itself is safe
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void operator()(T* tp, buffer_type& b, std::size_t i, const U& x) {
|
||||
is_x_integral(std::is_integral<U>{}, tp, b, i, x);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void is_x_integral(std::false_type, T* tp, buffer_type& b, std::size_t i,
|
||||
const U& x) {
|
||||
// x could be reference to buffer we manipulate, make copy before changing buffer
|
||||
const auto v = static_cast<double>(x);
|
||||
b.template make<double>(b.size, tp);
|
||||
operator()(static_cast<double*>(b.ptr), b, i, v);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void is_x_integral(std::false_type, T* tp, buffer_type& b, std::size_t i,
|
||||
const large_int& x) {
|
||||
// x could be reference to buffer we manipulate, make copy before changing buffer
|
||||
const auto v = static_cast<large_int>(x);
|
||||
b.template make<large_int>(b.size, tp);
|
||||
operator()(static_cast<large_int*>(b.ptr), b, i, v);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void is_x_integral(std::true_type, T* tp, buffer_type& b, std::size_t i, const U& x) {
|
||||
is_x_unsigned(std::is_unsigned<U>{}, tp, b, i, x);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void is_x_unsigned(std::false_type, T* tp, buffer_type& b, std::size_t i,
|
||||
const U& x) {
|
||||
if (x >= 0)
|
||||
is_x_unsigned(std::true_type{}, tp, b, i, detail::make_unsigned(x));
|
||||
else
|
||||
is_x_integral(std::false_type{}, tp, b, i, static_cast<double>(x));
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void is_x_unsigned(std::true_type, T* tp, buffer_type& b, std::size_t i, const U& x) {
|
||||
if (detail::safe_radd(tp[i], x)) return;
|
||||
// x could be reference to buffer we manipulate, need to convert to value
|
||||
const auto y = x;
|
||||
using TN = detail::next_type<typename buffer_type::types, T>;
|
||||
b.template make<TN>(b.size, tp);
|
||||
is_x_unsigned(std::true_type{}, static_cast<TN*>(b.ptr), b, i, y);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
void is_x_unsigned(std::true_type, large_int* tp, buffer_type&, std::size_t i,
|
||||
const U& x) {
|
||||
tp[i] += x;
|
||||
}
|
||||
};
|
||||
|
||||
struct multiplier {
|
||||
template <class T>
|
||||
void operator()(T* tp, buffer_type& 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);
|
||||
}
|
||||
|
||||
void operator()(double* tp, buffer_type& b, const double x) {
|
||||
for (auto end = tp + b.size; tp != end; ++tp) *tp *= x;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void operator()(T* tp, buffer_type& b, std::size_t i, const double x) {
|
||||
b.template make<double>(b.size, tp);
|
||||
operator()(static_cast<double*>(b.ptr), b, i, x);
|
||||
}
|
||||
|
||||
void operator()(double* tp, buffer_type&, std::size_t i, const double x) {
|
||||
tp[i] *= static_cast<double>(x);
|
||||
}
|
||||
};
|
||||
|
||||
mutable buffer_type buffer_;
|
||||
friend struct unsafe_access;
|
||||
};
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
118
install/boost_1_75_0/include/boost/histogram/unsafe_access.hpp
Normal file
118
install/boost_1_75_0/include/boost/histogram/unsafe_access.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
// 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 private data that potentially breaks consistency.
|
||||
|
||||
This struct enables access to private data of some classes. It is intended for library
|
||||
developers who need this to implement algorithms efficiently, for example,
|
||||
serialization. Users should not use this. If you are a user who absolutely needs this to
|
||||
get a specific effect, please submit an issue on Github. Perhaps the public
|
||||
interface is insufficient and should be extended for your use case.
|
||||
|
||||
Unlike the normal interface, the unsafe_access interface may change between versions.
|
||||
If your code relies on unsafe_access, it may or may not break when you update Boost.
|
||||
This is another reason to not use it unless you are ok with these conditions.
|
||||
*/
|
||||
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> = {}) {
|
||||
assert(I < hist.rank());
|
||||
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) {
|
||||
assert(i < hist.rank());
|
||||
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_;
|
||||
}
|
||||
|
||||
/**
|
||||
Get index offset.
|
||||
@param hist histogram
|
||||
*/
|
||||
template <class Histogram>
|
||||
static auto& offset(Histogram& hist) {
|
||||
return hist.offset_;
|
||||
}
|
||||
|
||||
/// @copydoc offset()
|
||||
template <class Histogram>
|
||||
static const auto& offset(const Histogram& hist) {
|
||||
return hist.offset_;
|
||||
}
|
||||
|
||||
/**
|
||||
Get buffer of unlimited_storage.
|
||||
@param storage instance of unlimited_storage.
|
||||
*/
|
||||
template <class Allocator>
|
||||
static constexpr auto& unlimited_storage_buffer(unlimited_storage<Allocator>& storage) {
|
||||
return storage.buffer_;
|
||||
}
|
||||
|
||||
/**
|
||||
Get implementation of storage_adaptor.
|
||||
@param storage instance of storage_adaptor.
|
||||
*/
|
||||
template <class T>
|
||||
static constexpr auto& storage_adaptor_impl(storage_adaptor<T>& storage) {
|
||||
return static_cast<typename storage_adaptor<T>::impl_type&>(storage);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
45
install/boost_1_75_0/include/boost/histogram/weight.hpp
Normal file
45
install/boost_1_75_0/include/boost/histogram/weight.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 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_WEIGHT_HPP
|
||||
#define BOOST_HISTOGRAM_WEIGHT_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
/** Weight holder and type envelope.
|
||||
|
||||
You should not construct these directly, use the weight() helper function.
|
||||
|
||||
@tparam Underlying arithmetic type.
|
||||
*/
|
||||
template <class T>
|
||||
struct weight_type {
|
||||
/// Access underlying value.
|
||||
T value;
|
||||
|
||||
/// Allow implicit conversions of types when the underlying value type allows them.
|
||||
template <class U>
|
||||
operator weight_type<U>() const {
|
||||
return weight_type<U>{static_cast<U>(value)};
|
||||
}
|
||||
};
|
||||
|
||||
/** Helper function to mark argument as weight.
|
||||
|
||||
@param t argument to be forward to the histogram.
|
||||
*/
|
||||
template <class T>
|
||||
auto weight(T&& t) noexcept {
|
||||
return weight_type<T>{std::forward<T>(t)};
|
||||
}
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user