mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-05 15:07:49 +00:00
Added thirdparty: boost library
This commit is contained in:
+102
@@ -0,0 +1,102 @@
|
||||
// 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.
|
||||
|
||||
The constructor is nothrow if meta is nothrow move constructible.
|
||||
*/
|
||||
explicit boolean(metadata_type meta = {}) noexcept(
|
||||
std::is_nothrow_move_constructible<metadata_type>::value)
|
||||
: 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
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
// 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>;
|
||||
|
||||
public:
|
||||
constexpr category() = default;
|
||||
explicit category(allocator_type alloc) : vec_(alloc) {}
|
||||
|
||||
/** Construct from forward 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 (optional).
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
@param alloc allocator instance to use (optional).
|
||||
|
||||
The constructor throws `std::invalid_argument` if iterator range is invalid. If the
|
||||
range contains duplicated values, the behavior of the axis is undefined.
|
||||
|
||||
The arguments meta and alloc are passed by value. If you move either of them into the
|
||||
axis and the constructor throws, their values are lost. Do not move if you cannot
|
||||
guarantee that the bin description is not valid.
|
||||
*/
|
||||
template <class It, class = detail::requires_iterator<It>>
|
||||
category(It begin, It end, metadata_type meta = {}, options_type options = {},
|
||||
allocator_type alloc = {})
|
||||
: metadata_base(std::move(meta)), vec_(alloc) {
|
||||
// static_asserts were moved here from class scope to satisfy deduction in gcc>=11
|
||||
static_assert(!options.test(option::underflow),
|
||||
"category axis cannot have underflow");
|
||||
static_assert(!options.test(option::circular), "category axis cannot be circular");
|
||||
static_assert(!(options.test(option::growth) && options.test(option::overflow)),
|
||||
"growing category axis cannot have entries in overflow bin");
|
||||
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++);
|
||||
}
|
||||
|
||||
// kept for backward compatibility; requires_allocator is a workaround for deduction
|
||||
// guides in gcc>=11
|
||||
template <class It, class A, class = detail::requires_iterator<It>,
|
||||
class = detail::requires_allocator<A>>
|
||||
category(It begin, It end, metadata_type meta, A alloc)
|
||||
: category(begin, end, std::move(meta), {}, std::move(alloc)) {}
|
||||
|
||||
/** Construct axis from iterable sequence of unique values.
|
||||
|
||||
@param iterable sequence of unique values.
|
||||
@param meta description of the axis.
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
@param alloc allocator instance to use.
|
||||
*/
|
||||
template <class C, class = detail::requires_iterable<C>>
|
||||
category(const C& iterable, metadata_type meta = {}, options_type options = {},
|
||||
allocator_type alloc = {})
|
||||
: category(std::begin(iterable), std::end(iterable), std::move(meta), options,
|
||||
std::move(alloc)) {}
|
||||
|
||||
// kept for backward compatibility; requires_allocator is a workaround for deduction
|
||||
// guides in gcc>=11
|
||||
template <class C, class A, class = detail::requires_iterable<C>,
|
||||
class = detail::requires_allocator<A>>
|
||||
category(const C& iterable, metadata_type meta, A 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 options see boost::histogram::axis::option (optional).
|
||||
@param alloc allocator instance to use.
|
||||
*/
|
||||
template <class U>
|
||||
category(std::initializer_list<U> list, metadata_type meta = {},
|
||||
options_type options = {}, allocator_type alloc = {})
|
||||
: category(list.begin(), list.end(), std::move(meta), options, std::move(alloc)) {}
|
||||
|
||||
// kept for backward compatibility; requires_allocator is a workaround for deduction
|
||||
// guides in gcc>=11
|
||||
template <class U, class A, class = detail::requires_allocator<A>>
|
||||
category(std::initializer_list<U> list, metadata_type meta, A 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>>>;
|
||||
|
||||
template <class T, class M, unsigned B>
|
||||
category(std::initializer_list<T>, M, const option::bitset<B>&)
|
||||
-> category<detail::replace_cstring<std::decay_t<T>>,
|
||||
detail::replace_cstring<std::decay_t<M>>, option::bitset<B>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
// 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 even faster than a regular axis.
|
||||
|
||||
The options `growth` and `circular` are mutually exclusive. If the axis uses
|
||||
integers and either `growth` or `circular` are set, the axis cannot have
|
||||
the options `underflow` or `overflow` set.
|
||||
|
||||
@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.
|
||||
*/
|
||||
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)>;
|
||||
|
||||
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 (optional).
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
|
||||
The constructor throws `std::invalid_argument` if start is not less than stop.
|
||||
|
||||
The arguments meta and alloc are passed by value. If you move either of them into the
|
||||
axis and the constructor throws, their values are lost. Do not move if you cannot
|
||||
guarantee that the bin description is not valid.
|
||||
*/
|
||||
integer(value_type start, value_type stop, metadata_type meta = {},
|
||||
options_type options = {})
|
||||
: metadata_base(std::move(meta))
|
||||
, size_(static_cast<index_type>(stop - start))
|
||||
, min_(start) {
|
||||
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.test(option::circular) && options.test(option::growth)),
|
||||
"circular and growth options are mutually exclusive");
|
||||
|
||||
static_assert(
|
||||
std::is_floating_point<value_type>::value ||
|
||||
!((options.test(option::growth) || options.test(option::circular)) &&
|
||||
(options.test(option::overflow) || options.test(option::underflow))),
|
||||
"circular or growing integer axis with integral type "
|
||||
"cannot have entries in underflow or overflow bins");
|
||||
|
||||
if (!(stop >= start)) // double negation so it works with NaN
|
||||
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 {
|
||||
// If axis has underflow and overflow, it is inclusive.
|
||||
// If axis is growing or circular:
|
||||
// - it is inclusive if value_type is an integer.
|
||||
// - it is not inclusive if value_type is floating point, because of nan and inf.
|
||||
constexpr bool full_flow = options_type().test(option::underflow | option::overflow);
|
||||
return full_flow || (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>>;
|
||||
|
||||
template <class T, class M, unsigned B>
|
||||
integer(T, T, M, const option::bitset<B>&)
|
||||
-> integer<detail::convert_integer<T, index_type>,
|
||||
detail::replace_type<std::decay_t<M>, const char*, std::string>,
|
||||
option::bitset<B>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+56
@@ -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
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// 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:
|
||||
using reference = typename iterator::iterator_adaptor_::reference;
|
||||
|
||||
/// Make iterator from axis and index.
|
||||
iterator(const Axis& axis, index_type idx)
|
||||
: iterator::iterator_adaptor_(idx), axis_(axis) {}
|
||||
|
||||
/// Return current bin object.
|
||||
reference 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
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
// 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;
|
||||
|
||||
static_assert(std::is_default_constructible<metadata_type>::value,
|
||||
"metadata must be default constructible");
|
||||
|
||||
static_assert(std::is_copy_constructible<metadata_type>::value,
|
||||
"metadata must be copy constructible");
|
||||
|
||||
static_assert(std::is_copy_assignable<metadata_type>::value,
|
||||
"metadata must be copy assignable");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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_; }
|
||||
|
||||
private:
|
||||
mutable metadata_type 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
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
// 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>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using bit = bitset<(1 << Pos)>;
|
||||
#else
|
||||
struct bit;
|
||||
#endif
|
||||
|
||||
/// 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
@@ -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
|
||||
+86
@@ -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
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
// 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.
|
||||
|
||||
If the axis has an overflow bin (the default), a value on the upper edge of the last
|
||||
bin is put in the overflow bin. The axis range represents a semi-open interval.
|
||||
|
||||
If the overflow bin is deactivated, then a value on the upper edge of the last bin is
|
||||
still counted towards the last bin. The axis range represents a closed interval.
|
||||
|
||||
The options `growth` and `circular` are mutually exclusive.
|
||||
|
||||
@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.
|
||||
*/
|
||||
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)>;
|
||||
|
||||
using unit_type = detail::get_unit_type<value_type>;
|
||||
using internal_value_type = detail::get_scale_type<value_type>;
|
||||
|
||||
public:
|
||||
constexpr regular() = default;
|
||||
|
||||
/** Construct n bins over real transformed range [start, stop).
|
||||
|
||||
@param trans transform instance to use.
|
||||
@param n number of bins.
|
||||
@param start low edge of first bin.
|
||||
@param stop high edge of last bin.
|
||||
@param meta description of the axis (optional).
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
|
||||
The constructor throws `std::invalid_argument` if n is zero, or if start and stop
|
||||
produce an invalid range after transformation.
|
||||
|
||||
The arguments meta and alloc are passed by value. If you move either of them into the
|
||||
axis and the constructor throws, their values are lost. Do not move if you cannot
|
||||
guarantee that the bin description is not valid.
|
||||
*/
|
||||
regular(transform_type trans, unsigned n, value_type start, value_type stop,
|
||||
metadata_type meta = {}, options_type options = {})
|
||||
: 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_) {
|
||||
// static_asserts were moved here from class scope to satisfy deduction in gcc>=11
|
||||
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");
|
||||
static_assert(std::is_floating_point<internal_value_type>::value,
|
||||
"regular axis requires floating point type");
|
||||
static_assert(!(options.test(option::circular) && options.test(option::growth)),
|
||||
"circular and growth options are mutually exclusive");
|
||||
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).
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
*/
|
||||
explicit regular(unsigned n, value_type start, value_type stop, metadata_type meta = {},
|
||||
options_type options = {})
|
||||
: regular({}, n, start, stop, std::move(meta), options) {}
|
||||
|
||||
/** 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).
|
||||
@param options see boost::histogram::axis::option (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>
|
||||
explicit regular(transform_type trans, step_type<T> step, value_type start,
|
||||
value_type stop, metadata_type meta = {}, options_type options = {})
|
||||
: 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), options) {}
|
||||
|
||||
/** 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).
|
||||
@param options see boost::histogram::axis::option (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>
|
||||
explicit regular(step_type<T> step, value_type start, value_type stop,
|
||||
metadata_type meta = {}, options_type options = {})
|
||||
: regular({}, step, start, stop, std::move(meta), options) {}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
// upper edge of last bin is inclusive if overflow bin is not present
|
||||
if (!options_type::test(option::overflow) && z == 1) return size() - 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 T, class M, unsigned B>
|
||||
regular(unsigned, T, T, M, const option::bitset<B>&)
|
||||
-> regular<detail::convert_integer<T, double>, transform::id,
|
||||
detail::replace_cstring<std::decay_t<M>>, option::bitset<B>>;
|
||||
|
||||
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>>>;
|
||||
|
||||
template <class Tr, class T, class M, unsigned B>
|
||||
regular(Tr, unsigned, T, T, M, const option::bitset<B>&)
|
||||
-> regular<detail::convert_integer<T, double>, Tr,
|
||||
detail::replace_cstring<std::decay_t<M>>, option::bitset<B>>;
|
||||
|
||||
#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
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
// 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>{}));
|
||||
#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 inclusive axis has a bin for every possible input value. In other words, all
|
||||
possible 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. A histogram which consists entirely of
|
||||
inclusive axes can be filled more efficiently, which can be a factor 2 faster.
|
||||
|
||||
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.
|
||||
|
||||
@tparam Axis axis type
|
||||
*/
|
||||
template <class Axis>
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
using is_inclusive = decltype(detail::traits_is_inclusive<Axis>(detail::priority<1>{}));
|
||||
#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
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
// 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.
|
||||
|
||||
If the axis has an overflow bin (the default), a value on the upper edge of the last
|
||||
bin is put in the overflow bin. The axis range represents a semi-open interval.
|
||||
|
||||
If the overflow bin is deactivated, then a value on the upper edge of the last bin is
|
||||
still counted towards the last bin. The axis range represents a closed interval. This
|
||||
is the desired behavior for random numbers drawn from a bounded interval, which is
|
||||
usually closed.
|
||||
|
||||
@tparam Value input value type, must be floating point.
|
||||
@tparam MetaData type to store meta data.
|
||||
@tparam Options see boost::histogram::axis::option.
|
||||
@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>;
|
||||
|
||||
public:
|
||||
constexpr variable() = default;
|
||||
explicit variable(allocator_type alloc) : vec_(alloc) {}
|
||||
|
||||
/** Construct from forward iterator range of bin edges.
|
||||
|
||||
@param begin begin of edge sequence.
|
||||
@param end end of edge sequence.
|
||||
@param meta description of the axis (optional).
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
@param alloc allocator instance to use (optional).
|
||||
|
||||
The constructor throws `std::invalid_argument` if iterator range is invalid, if less
|
||||
than two edges are provided or if bin edges are not in ascending order.
|
||||
|
||||
The arguments meta and alloc are passed by value. If you move either of them into the
|
||||
axis and the constructor throws, their values are lost. Do not move if you cannot
|
||||
guarantee that the bin description is not valid.
|
||||
*/
|
||||
template <class It, class = detail::requires_iterator<It>>
|
||||
variable(It begin, It end, metadata_type meta = {}, options_type options = {},
|
||||
allocator_type alloc = {})
|
||||
: metadata_base(std::move(meta)), vec_(std::move(alloc)) {
|
||||
// static_asserts were moved here from class scope to satisfy deduction in gcc>=11
|
||||
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.test(option::circular) && !options.test(option::growth)) ||
|
||||
(options.test(option::circular) ^ options.test(option::growth)),
|
||||
"circular and growth options are mutually exclusive");
|
||||
|
||||
const auto n = std::distance(begin, end);
|
||||
if (n < 0)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("end must be reachable by incrementing begin"));
|
||||
|
||||
if (n < 2) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 1 required"));
|
||||
|
||||
vec_.reserve(n);
|
||||
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"));
|
||||
}
|
||||
|
||||
// kept for backward compatibility; requires_allocator is a workaround for deduction
|
||||
// guides in gcc>=11
|
||||
template <class It, class A, class = detail::requires_iterator<It>,
|
||||
class = detail::requires_allocator<A>>
|
||||
variable(It begin, It end, metadata_type meta, A alloc)
|
||||
: variable(begin, end, std::move(meta), {}, std::move(alloc)) {}
|
||||
|
||||
/** Construct variable axis from iterable range of bin edges.
|
||||
|
||||
@param iterable iterable range of bin edges.
|
||||
@param meta description of the axis (optional).
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
@param alloc allocator instance to use (optional).
|
||||
*/
|
||||
template <class U, class = detail::requires_iterable<U>>
|
||||
variable(const U& iterable, metadata_type meta = {}, options_type options = {},
|
||||
allocator_type alloc = {})
|
||||
: variable(std::begin(iterable), std::end(iterable), std::move(meta), options,
|
||||
std::move(alloc)) {}
|
||||
|
||||
// kept for backward compatibility; requires_allocator is a workaround for deduction
|
||||
// guides in gcc>=11
|
||||
template <class U, class A, class = detail::requires_iterable<U>,
|
||||
class = detail::requires_allocator<A>>
|
||||
variable(const U& iterable, metadata_type meta, A 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 (optional).
|
||||
@param options see boost::histogram::axis::option (optional).
|
||||
@param alloc allocator instance to use (optional).
|
||||
*/
|
||||
template <class U>
|
||||
variable(std::initializer_list<U> list, metadata_type meta = {},
|
||||
options_type options = {}, allocator_type alloc = {})
|
||||
: variable(list.begin(), list.end(), std::move(meta), options, std::move(alloc)) {}
|
||||
|
||||
// kept for backward compatibility; requires_allocator is a workaround for deduction
|
||||
// guides in gcc>=11
|
||||
template <class U, class A, class = detail::requires_allocator<A>>
|
||||
variable(std::initializer_list<U> list, metadata_type meta, A 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);
|
||||
}
|
||||
// upper edge of last bin is inclusive if overflow bin is not present
|
||||
if (!options_type::test(option::overflow) && x == vec_.back()) return size() - 1;
|
||||
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 T, class M, unsigned B>
|
||||
variable(std::initializer_list<T>, M, const option::bitset<B>&)
|
||||
-> variable<detail::convert_integer<T, double>,
|
||||
detail::replace_type<std::decay_t<M>, const char*, std::string>,
|
||||
option::bitset<B>>;
|
||||
|
||||
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>>;
|
||||
|
||||
template <class Iterable, class M, unsigned B>
|
||||
variable(Iterable, M, const option::bitset<B>&) -> 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>, option::bitset<B>>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user