mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-12 05:31:51 +00:00
759 lines
24 KiB
C++
759 lines
24 KiB
C++
// Copyright 2015-2016 Hans Dembinski
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt
|
|
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#ifndef _BOOST_HISTOGRAM_AXIS_HPP_
|
|
#define _BOOST_HISTOGRAM_AXIS_HPP_
|
|
|
|
#include <algorithm>
|
|
#include <boost/bimap.hpp>
|
|
#include <boost/histogram/histogram_fwd.hpp>
|
|
#include <boost/histogram/interval.hpp>
|
|
#include <boost/iterator/iterator_facade.hpp>
|
|
#include <boost/math/constants/constants.hpp>
|
|
#include <boost/mpl/vector.hpp>
|
|
#include <boost/utility/string_view.hpp>
|
|
#include <boost/histogram/detail/axis_visitor.hpp>
|
|
#include <boost/mpl/contains.hpp>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
// forward declaration for serialization
|
|
namespace boost {
|
|
namespace serialization {
|
|
class access;
|
|
} // namespace serialization
|
|
} // namespace boost
|
|
|
|
namespace boost {
|
|
namespace histogram {
|
|
|
|
namespace axis {
|
|
|
|
enum class uoflow { off = false, on = true };
|
|
|
|
template <typename Axis>
|
|
class axis_iterator
|
|
: public iterator_facade<axis_iterator<Axis>,
|
|
std::pair<int, typename Axis::bin_type>,
|
|
random_access_traversal_tag,
|
|
std::pair<int, typename Axis::bin_type>> {
|
|
public:
|
|
explicit axis_iterator(const Axis &axis, int idx) : axis_(axis), idx_(idx) {}
|
|
|
|
axis_iterator(const axis_iterator &o) = default;
|
|
axis_iterator &operator=(const axis_iterator &o) = default;
|
|
|
|
private:
|
|
void increment() noexcept { ++idx_; }
|
|
void decrement() noexcept { --idx_; }
|
|
void advance(int n) noexcept { idx_ += n; }
|
|
int distance_to(const axis_iterator &other) const noexcept {
|
|
return other.idx_ - idx_;
|
|
}
|
|
bool equal(const axis_iterator &other) const noexcept {
|
|
return idx_ == other.idx_;
|
|
}
|
|
std::pair<int, typename Axis::bin_type> dereference() const {
|
|
return std::make_pair(idx_, axis_[idx_]);
|
|
}
|
|
const Axis& axis_;
|
|
int idx_;
|
|
friend class boost::iterator_core_access;
|
|
};
|
|
|
|
/// Base class for all axes.
|
|
class axis_base {
|
|
public:
|
|
/// Returns the number of bins, excluding overflow/underflow.
|
|
inline int size() const noexcept { return size_; }
|
|
/// Returns the number of bins, including overflow/underflow.
|
|
inline int shape() const noexcept { return size_; }
|
|
/// Returns true if axis has extra overflow and underflow bins.
|
|
inline bool uoflow() const noexcept { return false; }
|
|
/// Returns the axis label, which is a name or description.
|
|
string_view label() const noexcept { return label_; }
|
|
/// Change the label of an axis.
|
|
void label(string_view label) { label_.assign(label.begin(), label.end()); }
|
|
|
|
protected:
|
|
axis_base(unsigned n, string_view label)
|
|
: size_(n), label_(label.begin(), label.end()) {
|
|
if (n == 0) {
|
|
throw std::logic_error("bins > 0 required");
|
|
}
|
|
std::copy(label.begin(), label.end(), label_.begin());
|
|
}
|
|
|
|
axis_base() = default;
|
|
axis_base(const axis_base &) = default;
|
|
axis_base &operator=(const axis_base &) = default;
|
|
axis_base(axis_base &&rhs) : size_(rhs.size_), label_(std::move(rhs.label_)) {
|
|
rhs.size_ = 0;
|
|
}
|
|
axis_base &operator=(axis_base &&rhs) {
|
|
if (this != &rhs) {
|
|
size_ = rhs.size_;
|
|
label_ = std::move(rhs.label_);
|
|
rhs.size_ = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const axis_base &rhs) const noexcept {
|
|
return size_ == rhs.size_ && label_ == rhs.label_;
|
|
}
|
|
|
|
private:
|
|
int size_ = 0;
|
|
std::string label_;
|
|
|
|
friend class ::boost::serialization::access;
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
|
|
/// Base class for axes with overflow/underflow bins.
|
|
class axis_base_uoflow : public axis_base {
|
|
public:
|
|
/// Returns the number of bins, including overflow/underflow.
|
|
inline int shape() const noexcept { return shape_; }
|
|
/// Returns whether axis has extra overflow and underflow bins.
|
|
inline bool uoflow() const noexcept { return shape_ > size(); }
|
|
|
|
protected:
|
|
axis_base_uoflow(unsigned n, string_view label, enum uoflow uo)
|
|
: axis_base(n, label), shape_(n + 2u * static_cast<unsigned>(uo)) {}
|
|
|
|
axis_base_uoflow() = default;
|
|
axis_base_uoflow(const axis_base_uoflow &) = default;
|
|
axis_base_uoflow &operator=(const axis_base_uoflow &) = default;
|
|
axis_base_uoflow(axis_base_uoflow &&rhs)
|
|
: axis_base(std::move(rhs)), shape_(rhs.shape_) {
|
|
rhs.shape_ = 0;
|
|
}
|
|
axis_base_uoflow &operator=(axis_base_uoflow &&rhs) {
|
|
if (this != &rhs) {
|
|
axis_base::operator=(std::move(rhs));
|
|
shape_ = rhs.shape_;
|
|
rhs.shape_ = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const axis_base_uoflow &rhs) const noexcept {
|
|
return axis_base::operator==(rhs) && shape_ == rhs.shape_;
|
|
}
|
|
|
|
private:
|
|
int shape_ = 0;
|
|
|
|
friend class ::boost::serialization::access;
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
|
|
namespace transform {
|
|
namespace detail {
|
|
struct stateless {
|
|
bool operator==(const stateless &) const noexcept { return true; }
|
|
template <class Archive> void serialize(Archive &, unsigned) {}
|
|
};
|
|
} // namespace detail
|
|
|
|
struct identity : public detail::stateless {
|
|
template <typename T> static T forward(T v) { return v; }
|
|
template <typename T> static T inverse(T v) { return v; }
|
|
};
|
|
|
|
struct log : public detail::stateless {
|
|
template <typename T> static T forward(T v) { return std::log(v); }
|
|
template <typename T> static T inverse(T v) { return std::exp(v); }
|
|
};
|
|
|
|
struct sqrt : public detail::stateless {
|
|
template <typename T> static T forward(T v) { return std::sqrt(v); }
|
|
template <typename T> static T inverse(T v) { return v * v; }
|
|
};
|
|
|
|
struct cos : public detail::stateless {
|
|
template <typename T> static T forward(T v) { return std::cos(v); }
|
|
template <typename T> static T inverse(T v) { return std::acos(v); }
|
|
};
|
|
|
|
struct pow {
|
|
pow() = default;
|
|
pow(double exponent) : value(exponent) {}
|
|
template <typename T> T forward(T v) const { return std::pow(v, value); }
|
|
template <typename T> T inverse(T v) const {
|
|
return std::pow(v, 1.0 / value);
|
|
}
|
|
double value = 1.0;
|
|
bool operator==(const pow &other) const noexcept {
|
|
return value == other.value;
|
|
}
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
} // namespace transform
|
|
|
|
/** Axis for equidistant intervals on the real line.
|
|
*
|
|
* The most common binning strategy.
|
|
* Very fast. Binning is a O(1) operation.
|
|
*/
|
|
template <typename RealType, typename Transform>
|
|
class regular : public axis_base_uoflow, Transform {
|
|
public:
|
|
using value_type = RealType;
|
|
using bin_type = interval<value_type>;
|
|
using const_iterator = axis_iterator<regular>;
|
|
|
|
/** Construct axis with n bins over real range [lower, upper).
|
|
*
|
|
* \param n number of bins.
|
|
* \param lower low edge of first bin.
|
|
* \param upper high edge of last bin.
|
|
* \param label description of the axis.
|
|
* \param uoflow whether to add under-/overflow bins.
|
|
* \param trans arguments passed to the transform.
|
|
*/
|
|
regular(unsigned n, value_type lower, value_type upper,
|
|
string_view label = {},
|
|
enum uoflow uo = ::boost::histogram::axis::uoflow::on,
|
|
Transform trans = Transform())
|
|
: axis_base_uoflow(n, label, uo), Transform(trans),
|
|
min_(trans.forward(lower)),
|
|
delta_((trans.forward(upper) - trans.forward(lower)) / n) {
|
|
if (!(lower < upper)) {
|
|
throw std::logic_error("lower < upper required");
|
|
}
|
|
BOOST_ASSERT(!std::isnan(min_));
|
|
BOOST_ASSERT(!std::isnan(delta_));
|
|
}
|
|
|
|
regular() = default;
|
|
regular(const regular &) = default;
|
|
regular &operator=(const regular &) = default;
|
|
regular(regular &&) = default;
|
|
regular &operator=(regular &&) = default;
|
|
|
|
/// Returns the bin index for the passed argument.
|
|
inline int index(value_type x) const noexcept {
|
|
// Optimized code
|
|
const value_type z = (this->forward(x) - min_) / delta_;
|
|
return z >= 0.0 ? (z > size() ? size() : static_cast<int>(z)) : -1;
|
|
}
|
|
|
|
/// Returns the starting edge of the bin.
|
|
bin_type operator[](int idx) const noexcept {
|
|
auto eval = [this](int i) {
|
|
const auto n = size();
|
|
if (i < 0)
|
|
return this->inverse(-std::numeric_limits<value_type>::infinity());
|
|
if (i > n)
|
|
return this->inverse(std::numeric_limits<value_type>::infinity());
|
|
const auto z = value_type(i) / n;
|
|
return this->inverse((1.0 - z) * min_ + z * (min_ + delta_ * n));
|
|
};
|
|
return {eval(idx), eval(idx + 1)};
|
|
}
|
|
|
|
bool operator==(const regular &o) const noexcept {
|
|
return axis_base_uoflow::operator==(o) && Transform::operator==(o) &&
|
|
min_ == o.min_ && delta_ == o.delta_;
|
|
}
|
|
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
|
|
const Transform &transform() const noexcept {
|
|
return static_cast<const Transform &>(*this);
|
|
}
|
|
|
|
private:
|
|
value_type min_ = 0.0, delta_ = 1.0;
|
|
|
|
friend class ::boost::serialization::access;
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
|
|
/** Axis for real values on a circle.
|
|
*
|
|
* The axis is circular and wraps around reaching the
|
|
* perimeter value. Therefore, there are no overflow/underflow
|
|
* bins for this axis. Binning is a O(1) operation.
|
|
*/
|
|
template <typename RealType> class circular : public axis_base {
|
|
public:
|
|
using value_type = RealType;
|
|
using bin_type = interval<value_type>;
|
|
using const_iterator = axis_iterator<circular>;
|
|
|
|
/** Constructor for n bins with an optional offset.
|
|
*
|
|
* \param n number of bins.
|
|
* \param phase starting phase.
|
|
* \param perimeter range after which value wraps around.
|
|
* \param label description of the axis.
|
|
*/
|
|
explicit circular(unsigned n, value_type phase = 0.0,
|
|
value_type perimeter = math::double_constants::two_pi,
|
|
string_view label = {})
|
|
: axis_base(n, label), phase_(phase), perimeter_(perimeter) {}
|
|
|
|
circular() = default;
|
|
circular(const circular &) = default;
|
|
circular &operator=(const circular &) = default;
|
|
circular(circular &&) = default;
|
|
circular &operator=(circular &&) = default;
|
|
|
|
/// Returns the bin index for the passed argument.
|
|
inline int index(value_type x) const noexcept {
|
|
const value_type z = (x - phase_) / perimeter_;
|
|
const int i = static_cast<int>(std::floor(z * size())) % size();
|
|
return i + (i < 0) * size();
|
|
}
|
|
|
|
/// Returns the starting edge of the bin.
|
|
bin_type operator[](int idx) const {
|
|
auto eval = [this](int i) {
|
|
const value_type z = value_type(i) / size();
|
|
return z * perimeter_ + phase_;
|
|
};
|
|
return {eval(idx), eval(idx + 1)};
|
|
}
|
|
|
|
bool operator==(const circular &o) const noexcept {
|
|
return axis_base::operator==(o) && phase_ == o.phase_ &&
|
|
perimeter_ == o.perimeter_;
|
|
}
|
|
|
|
value_type perimeter() const { return perimeter_; }
|
|
value_type phase() const { return phase_; }
|
|
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
|
|
private:
|
|
value_type phase_ = 0.0, perimeter_ = 1.0;
|
|
|
|
friend class ::boost::serialization::access;
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
|
|
/** 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.
|
|
*/
|
|
template <typename RealType> class variable : public axis_base_uoflow {
|
|
public:
|
|
using value_type = RealType;
|
|
using bin_type = interval<value_type>;
|
|
using const_iterator = axis_iterator<variable>;
|
|
|
|
/** Construct an axis from bin edges.
|
|
*
|
|
* \param x sequence of bin edges.
|
|
* \param label description of the axis.
|
|
* \param uoflow whether to add under-/overflow bins.
|
|
*/
|
|
variable(std::initializer_list<value_type> x, string_view label = {},
|
|
enum uoflow uo = ::boost::histogram::axis::uoflow::on)
|
|
: axis_base_uoflow(x.size() - 1, label, uo),
|
|
x_(new value_type[x.size()]) {
|
|
if (x.size() < 2) {
|
|
throw std::logic_error("at least two values required");
|
|
}
|
|
std::copy(x.begin(), x.end(), x_.get());
|
|
std::sort(x_.get(), x_.get() + size() + 1);
|
|
}
|
|
|
|
template <typename Iterator>
|
|
variable(Iterator begin, Iterator end, string_view label = {},
|
|
enum uoflow uo = ::boost::histogram::axis::uoflow::on)
|
|
: axis_base_uoflow(std::distance(begin, end) - 1, label, uo),
|
|
x_(new value_type[std::distance(begin, end)]) {
|
|
std::copy(begin, end, x_.get());
|
|
std::sort(x_.get(), x_.get() + size() + 1);
|
|
}
|
|
|
|
variable() = default;
|
|
variable(const variable &o)
|
|
: axis_base_uoflow(o), x_(new value_type[size() + 1]) {
|
|
std::copy(o.x_.get(), o.x_.get() + size() + 1, x_.get());
|
|
}
|
|
variable &operator=(const variable &o) {
|
|
if (this != &o) {
|
|
axis_base_uoflow::operator=(o);
|
|
x_.reset(new value_type[size() + 1]);
|
|
std::copy(o.x_.get(), o.x_.get() + size() + 1, x_.get());
|
|
}
|
|
return *this;
|
|
}
|
|
variable(variable &&) = default;
|
|
variable &operator=(variable &&) = default;
|
|
|
|
/// Returns the bin index for the passed argument.
|
|
inline int index(value_type x) const noexcept {
|
|
return std::upper_bound(x_.get(), x_.get() + size() + 1, x) - x_.get() - 1;
|
|
}
|
|
|
|
/// Returns the starting edge of the bin.
|
|
bin_type operator[](int idx) const {
|
|
auto eval = [this](int i) {
|
|
if (i < 0) {
|
|
return -std::numeric_limits<value_type>::infinity();
|
|
}
|
|
if (i > size()) {
|
|
return std::numeric_limits<value_type>::infinity();
|
|
}
|
|
return x_[i];
|
|
};
|
|
return {eval(idx), eval(idx + 1)};
|
|
}
|
|
|
|
bool operator==(const variable &o) const noexcept {
|
|
if (!axis_base_uoflow::operator==(o)) {
|
|
return false;
|
|
}
|
|
return std::equal(x_.get(), x_.get() + size() + 1, o.x_.get());
|
|
}
|
|
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
|
|
private:
|
|
std::unique_ptr<value_type[]> x_; // smaller size compared to std::vector
|
|
|
|
friend class ::boost::serialization::access;
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
|
|
/** Axis for an interval of integral values with unit steps.
|
|
*
|
|
* Binning is a O(1) operation. This axis operates
|
|
* faster than a regular.
|
|
*/
|
|
template <typename IntType> class integer : public axis_base_uoflow {
|
|
public:
|
|
using value_type = IntType;
|
|
using bin_type = interval<value_type>;
|
|
using const_iterator = axis_iterator<integer>;
|
|
|
|
/** Construct axis over a semi-open integer interval [lower, upper).
|
|
*
|
|
* \param lower smallest integer of the covered range.
|
|
* \param upper largest integer of the covered range.
|
|
* \param label description of the axis.
|
|
* \param uoflow whether to add under-/overflow bins.
|
|
*/
|
|
integer(value_type lower, value_type upper, string_view label = {},
|
|
enum uoflow uo = ::boost::histogram::axis::uoflow::on)
|
|
: axis_base_uoflow(upper - lower, label, uo), min_(lower) {
|
|
if (lower > upper) {
|
|
throw std::logic_error("lower <= upper required");
|
|
}
|
|
}
|
|
|
|
integer() = default;
|
|
integer(const integer &) = default;
|
|
integer &operator=(const integer &) = default;
|
|
integer(integer &&) = default;
|
|
integer &operator=(integer &&) = default;
|
|
|
|
/// Returns the bin index for the passed argument.
|
|
inline int index(value_type x) const noexcept {
|
|
const int z = x - min_;
|
|
return z >= 0 ? (z > size() ? size() : z) : -1;
|
|
}
|
|
|
|
/// Returns the integer that is mapped to the bin index.
|
|
bin_type operator[](int idx) const {
|
|
auto eval = [this](int i) {
|
|
if (i < 0) {
|
|
return -std::numeric_limits<value_type>::max();
|
|
}
|
|
if (i > size()) {
|
|
return std::numeric_limits<value_type>::max();
|
|
}
|
|
return min_ + i;
|
|
};
|
|
return {eval(idx), eval(idx + 1)};
|
|
}
|
|
|
|
bool operator==(const integer &o) const noexcept {
|
|
return axis_base_uoflow::operator==(o) && min_ == o.min_;
|
|
}
|
|
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
|
|
private:
|
|
value_type min_ = 0;
|
|
|
|
friend class ::boost::serialization::access;
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
|
|
/** Axis which maps unique single values to bins (one on one).
|
|
*
|
|
* The axis maps a set of values to bins, following the order of
|
|
* arguments in the constructor. There is an optional overflow bin
|
|
* for this axis, which counts values that are not part of the set.
|
|
* Binning is a O(1) operation. The value type must be hashable.
|
|
*/
|
|
template <typename T> class category : public axis_base {
|
|
using map_type = bimap<T, int>;
|
|
|
|
public:
|
|
using value_type = T;
|
|
using bin_type = T;
|
|
using const_iterator = axis_iterator<category<T>>;
|
|
|
|
category() = default;
|
|
category(const category &rhs)
|
|
: axis_base(rhs), map_(new map_type(*rhs.map_)) {}
|
|
category &operator=(const category &rhs) {
|
|
if (this != &rhs) {
|
|
axis_base::operator=(rhs);
|
|
map_.reset(new map_type(*rhs.map_));
|
|
}
|
|
return *this;
|
|
}
|
|
category(category &&rhs) = default;
|
|
category &operator=(category &&rhs) = default;
|
|
|
|
/** Construct from an initializer list of strings.
|
|
*
|
|
* \param seq sequence of unique values.
|
|
*/
|
|
category(std::initializer_list<T> seq, string_view label = {})
|
|
: axis_base(seq.size(), label), map_(new map_type()) {
|
|
int index = 0;
|
|
for (const auto &x : seq)
|
|
map_->insert({x, index++});
|
|
if (index == 0)
|
|
throw std::logic_error("sequence is empty");
|
|
}
|
|
|
|
template <typename Iterator>
|
|
category(Iterator begin, Iterator end, string_view label = {})
|
|
: axis_base(std::distance(begin, end), label), map_(new map_type()) {
|
|
int index = 0;
|
|
while (begin != end)
|
|
map_->insert({*begin++, index++});
|
|
if (index == 0)
|
|
throw std::logic_error("iterator range is empty");
|
|
}
|
|
|
|
/// Returns the bin index for the passed argument.
|
|
/// Performs a range check.
|
|
inline int index(const value_type &x) const noexcept {
|
|
auto it = map_->left.find(x);
|
|
if (it == map_->left.end())
|
|
return size();
|
|
return it->second;
|
|
}
|
|
|
|
/// Returns the value for the bin index.
|
|
bin_type operator[](int idx) const {
|
|
auto it = map_->right.find(idx);
|
|
BOOST_ASSERT_MSG(it != map_->right.end(), "category index out of range");
|
|
return it->second;
|
|
}
|
|
|
|
bool operator==(const category &o) const noexcept {
|
|
return axis_base::operator==(o) &&
|
|
std::equal(map_->begin(), map_->end(), o.map_->begin());
|
|
}
|
|
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
|
|
private:
|
|
std::unique_ptr<map_type> map_;
|
|
|
|
friend class ::boost::serialization::access;
|
|
template <class Archive> void serialize(Archive &, unsigned);
|
|
};
|
|
|
|
namespace detail {
|
|
struct size : public static_visitor<int> {
|
|
template <typename A> int operator()(const A &a) const { return a.size(); }
|
|
};
|
|
|
|
struct shape : public static_visitor<int> {
|
|
template <typename A> int operator()(const A &a) const { return a.shape(); }
|
|
};
|
|
|
|
struct uoflow : public static_visitor<bool> {
|
|
template <typename A> bool operator()(const A &a) const { return a.uoflow(); }
|
|
};
|
|
|
|
struct get_label : public static_visitor<string_view> {
|
|
template <typename A> ::boost::string_view operator()(const A& a) const { return a.label(); }
|
|
};
|
|
|
|
struct set_label : public static_visitor<void> {
|
|
const ::boost::string_view label;
|
|
set_label(const ::boost::string_view x) : label(x) {}
|
|
template <typename A> void operator()(A& a) const { a.label(label); }
|
|
};
|
|
|
|
template <typename T> struct index : public static_visitor<int> {
|
|
const T &t;
|
|
explicit index(const T &arg) : t(arg) {}
|
|
template <typename Axis> int operator()(const Axis &a) const {
|
|
return impl(std::is_convertible<T, typename Axis::value_type>(), a);
|
|
}
|
|
template <typename Axis> int impl(std::true_type, const Axis& a) const {
|
|
return a.index(t);
|
|
}
|
|
template <typename Axis> int impl(std::false_type, const Axis&) const {
|
|
throw std::runtime_error("index argument not convertible to axis value type");
|
|
}
|
|
};
|
|
|
|
struct bin : public static_visitor<axis::interval<double>> {
|
|
using double_interval = axis::interval<double>;
|
|
const int i;
|
|
bin(const int v) : i(v) {}
|
|
template <typename A> double_interval operator()(const A &a) const {
|
|
return impl(is_convertible<typename A::bin_type, double_interval>(),
|
|
std::forward<typename A::bin_type>(a[i]));
|
|
}
|
|
template <typename B> double_interval impl(true_type, B &&b) const {
|
|
return b;
|
|
}
|
|
template <typename B> double_interval impl(false_type, B &&) const {
|
|
throw std::runtime_error("cannot convert bin_type to interval<double>");
|
|
}
|
|
};
|
|
} // namespace detail
|
|
|
|
/// Polymorphic axis type
|
|
template <typename Axes = builtins>
|
|
class any : public make_variant_over<Axes>::type {
|
|
using base_type = typename make_variant_over<Axes>::type;
|
|
public:
|
|
using types = typename base_type::types;
|
|
using value_type = double;
|
|
using bin_type = interval<double>;
|
|
using const_iterator = axis_iterator<any>;
|
|
|
|
any() = default;
|
|
any(const any& t) = default;
|
|
any(any&& t) = default;
|
|
any& operator=(const any& t) = default;
|
|
any& operator=(any&& t) = default;
|
|
|
|
template <typename T, typename = typename std::enable_if<
|
|
mpl::contains<Axes, T>::value
|
|
>::type>
|
|
any(const T& t) : base_type(t) {}
|
|
|
|
template <typename T, typename = typename std::enable_if<
|
|
mpl::contains<Axes, T>::value
|
|
>::type>
|
|
any& operator=(const T& t) {
|
|
// ugly workaround for compiler bug
|
|
return reinterpret_cast<any&>(base_type::operator=(t));
|
|
}
|
|
|
|
template <typename T, typename = typename std::enable_if<
|
|
mpl::contains<Axes, T>::value
|
|
>::type>
|
|
any& operator=(T&& t) {
|
|
// ugly workaround for compiler bug
|
|
return reinterpret_cast<any&>(base_type::operator=(std::move(t)));
|
|
}
|
|
|
|
int size() const {
|
|
return apply_visitor(detail::size(), *this);
|
|
}
|
|
|
|
int shape() const {
|
|
return apply_visitor(detail::shape(), *this);
|
|
}
|
|
|
|
bool uoflow() const {
|
|
return apply_visitor(detail::uoflow(), *this);
|
|
}
|
|
|
|
// note: this only works for axes with compatible value type
|
|
int index(const value_type x) const {
|
|
return apply_visitor(detail::index<value_type>(x), *this);
|
|
}
|
|
|
|
string_view label() const {
|
|
return apply_visitor(detail::get_label(), *this);
|
|
}
|
|
|
|
void label(const string_view x) {
|
|
return apply_visitor(detail::set_label(x), *this);
|
|
}
|
|
|
|
// this only works for axes with compatible bin type
|
|
// and will raise an error otherwise
|
|
bin_type operator[](const int i) const {
|
|
return apply_visitor(detail::bin(i), *this);
|
|
}
|
|
|
|
bool operator==(const any& rhs) const {
|
|
return base_type::operator==(static_cast<const base_type&>(rhs));
|
|
}
|
|
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
|
|
private:
|
|
friend class ::boost::serialization::access;
|
|
template <typename Archive> void serialize(Archive&, unsigned);
|
|
};
|
|
|
|
// dynamic casts
|
|
template <typename T, typename Axes>
|
|
typename std::add_lvalue_reference<T>::type cast(any<Axes>& any) { return get<T>(any); }
|
|
|
|
template <typename T, typename Axes>
|
|
const typename std::add_lvalue_reference<T>::type cast(const any<Axes>& any) { return get<T>(any); }
|
|
|
|
template <typename T, typename Axes>
|
|
typename std::add_pointer<T>::type cast(any<Axes>* any) { return get<T>(&any); }
|
|
|
|
template <typename T, typename Axes>
|
|
const typename std::add_pointer<T>::type cast(const any<Axes>* any) { return get<T>(&any); }
|
|
|
|
// pass-through versions for generic programming, i.e. when you switch to static histogram
|
|
template <typename T>
|
|
typename std::add_lvalue_reference<T>::type cast(T& t) { return t; }
|
|
|
|
template <typename T>
|
|
const typename std::add_lvalue_reference<T>::type cast(const T& t) { return t; }
|
|
|
|
template <typename T>
|
|
typename std::add_pointer<T>::type cast(T* t) { return t; }
|
|
|
|
template <typename T>
|
|
const typename std::add_pointer<T>::type cast(const T* t) { return t; }
|
|
|
|
} // namespace axis
|
|
} // namespace histogram
|
|
} // namespace boost
|
|
|
|
#endif
|