mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-11 13:14:06 +00:00
refactoring axis types into separate functions, make them use compressed_pair instead of crappy custom empty base optimization
This commit is contained in:
parent
22745ca571
commit
b8d158e94e
@ -13,22 +13,18 @@ int main() {
|
||||
- in addition, the factory also accepts iterators over a sequence of
|
||||
axis::variant, the polymorphic type that can hold concrete axis types
|
||||
*/
|
||||
std::vector<
|
||||
bh::axis::variant<
|
||||
bh::axis::regular<>,
|
||||
bh::axis::category<std::string>
|
||||
>
|
||||
> axes;
|
||||
std::vector<bh::axis::variant<bh::axis::regular<>, bh::axis::category<std::string> > >
|
||||
axes;
|
||||
axes.emplace_back(bh::axis::category<std::string>({"red", "blue"}));
|
||||
axes.emplace_back(bh::axis::regular<>(5, 0, 1, "x"));
|
||||
axes.emplace_back(bh::axis::regular<>(5, 0, 1, "y"));
|
||||
axes.emplace_back(bh::axis::regular<>(3, 0, 1, "x"));
|
||||
axes.emplace_back(bh::axis::regular<>(3, 0, 1, "y"));
|
||||
auto h = bh::make_histogram(axes.begin(), axes.end());
|
||||
|
||||
// fill histogram with data, usually this happens in a loop
|
||||
h("red", 0.1, 0.2);
|
||||
h("blue", 0.3, 0.4);
|
||||
h("red", 0.5, 0.6);
|
||||
h("red", 0.7, 0.8);
|
||||
h("blue", 0.7, 0.3);
|
||||
h("red", 0.3, 0.7);
|
||||
h("red", 0.7, 0.7);
|
||||
|
||||
/*
|
||||
Print dynamic histogram by iterating over bins.
|
||||
@ -42,14 +38,17 @@ int main() {
|
||||
for (auto xbin : h.axis(1)) { // columns
|
||||
const auto v = h.at(cbin, xbin, ybin);
|
||||
if (v)
|
||||
std::printf("%4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n",
|
||||
cbin.value().c_str(),
|
||||
xbin.lower(), xbin.upper(),
|
||||
ybin.lower(), ybin.upper(),
|
||||
v);
|
||||
std::printf("(%i, %i, %i) %4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n",
|
||||
cbin.idx(), xbin.idx(), ybin.idx(), cbin.value().c_str(),
|
||||
xbin.lower(), xbin.upper(), ybin.lower(), ybin.upper(), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(h.at(0, 0, 0) == 1);
|
||||
assert(h.at(0, 0, 2) == 1);
|
||||
assert(h.at(0, 2, 2) == 1);
|
||||
assert(h.at(1, 2, 0) == 1);
|
||||
}
|
||||
|
||||
//]
|
||||
|
@ -7,8 +7,12 @@
|
||||
#ifndef BOOST_HISTOGRAM_HPP
|
||||
#define BOOST_HISTOGRAM_HPP
|
||||
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/histogram/literals.hpp>
|
||||
#include <boost/histogram/storage/adaptive_storage.hpp>
|
||||
|
@ -7,13 +7,11 @@
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_BASE_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_BASE_HPP
|
||||
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/iterator/reverse_iterator.hpp>
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
@ -22,107 +20,62 @@ namespace axis {
|
||||
|
||||
/// Base class for all axes
|
||||
template <typename MetaData>
|
||||
class base
|
||||
{
|
||||
class base {
|
||||
using metadata_type = MetaData;
|
||||
struct data : metadata_type // empty base class optimization
|
||||
{
|
||||
int size = 0;
|
||||
option_type opt = option_type::none;
|
||||
|
||||
data() = default;
|
||||
data(const data&) = default;
|
||||
data& operator=(const data&) = default;
|
||||
data(data&& rhs)
|
||||
: metadata_type(std::move(rhs))
|
||||
, size(rhs.size), opt(rhs.opt)
|
||||
{ rhs.size = 0; rhs.opt = option_type::none; }
|
||||
data& operator=(data&& rhs) {
|
||||
if (this != &rhs) {
|
||||
metadata_type::operator=(std::move(rhs));
|
||||
size = rhs.size;
|
||||
opt = rhs.opt;
|
||||
rhs.size = 0;
|
||||
rhs.opt = option_type::none;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
data(metadata_type&& m, int s, option_type o)
|
||||
: metadata_type(std::move(m)), size(s), opt(o) {}
|
||||
|
||||
bool operator==(const data& rhs) const noexcept {
|
||||
return size == rhs.size && opt == rhs.opt &&
|
||||
detail::static_if<detail::is_equal_comparable<metadata_type>>(
|
||||
[&rhs](const auto& m) { return m == rhs; },
|
||||
[](const auto&) { return true; },
|
||||
static_cast<const metadata_type&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/// Returns the number of bins, without extra bins.
|
||||
unsigned size() const noexcept { return data_.size; }
|
||||
unsigned size() const noexcept { return size_meta_.first(); }
|
||||
/// Returns the options.
|
||||
option_type options() const noexcept { return data_.opt; }
|
||||
option_type options() const noexcept { return opt_; }
|
||||
/// Returns the metadata.
|
||||
metadata_type& metadata() noexcept { return static_cast<metadata_type&>(data_); }
|
||||
metadata_type& metadata() noexcept { return size_meta_.second(); }
|
||||
/// Returns the metadata (const version).
|
||||
const metadata_type& metadata() const noexcept { return static_cast<const metadata_type&>(data_); }
|
||||
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
|
||||
|
||||
friend void swap(base& a, base& b) noexcept // ADL works with friend functions
|
||||
{
|
||||
std::swap(static_cast<metadata_type&>(a), static_cast<metadata_type&>(b));
|
||||
std::swap(a.data_.size, b.data_.size);
|
||||
std::swap(a.data_.opt, b.data_.opt);
|
||||
using std::swap;
|
||||
swap(a.size_meta_, b.size_meta_);
|
||||
swap(a.opt_, b.opt_);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
protected:
|
||||
base(unsigned size, metadata_type&& m, option_type opt)
|
||||
: data_(std::move(m), size, opt)
|
||||
{
|
||||
if (size == 0) { throw std::invalid_argument("bins > 0 required"); }
|
||||
const auto max_index = static_cast<unsigned>(std::numeric_limits<int>::max()
|
||||
- static_cast<int>(data_.opt));
|
||||
if (size > max_index)
|
||||
throw std::invalid_argument(
|
||||
detail::cat("bins <= ", max_index, " required")
|
||||
);
|
||||
base(unsigned n, metadata_type&& m, option_type opt)
|
||||
: size_meta_(n, std::move(m)), opt_(opt) {
|
||||
if (size() == 0) { throw std::invalid_argument("bins > 0 required"); }
|
||||
const auto max_index =
|
||||
static_cast<unsigned>(std::numeric_limits<int>::max() - static_cast<int>(opt_));
|
||||
if (size() > max_index)
|
||||
throw std::invalid_argument(detail::cat("bins <= ", max_index, " required"));
|
||||
}
|
||||
|
||||
base() = default;
|
||||
base() : size_meta_(0), opt_(option_type::none) {}
|
||||
base(const base&) = default;
|
||||
base& operator=(const base&) = default;
|
||||
base(base&& rhs) : size_meta_(std::move(rhs.size_meta_)), opt_(rhs.opt_) {}
|
||||
base& operator=(base&& rhs) {
|
||||
if (this != &rhs) {
|
||||
size_meta_ = std::move(rhs.size_meta_);
|
||||
opt_ = rhs.opt_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const base& rhs) const noexcept {
|
||||
return data_ == rhs.data_;
|
||||
return size() == rhs.size() && opt_ == rhs.opt_ &&
|
||||
detail::static_if<detail::is_equal_comparable<metadata_type>>(
|
||||
[&rhs](const auto& m) { return m == rhs.metadata(); },
|
||||
[](const auto&) { return true; }, metadata());
|
||||
}
|
||||
|
||||
private:
|
||||
data data_;
|
||||
};
|
||||
|
||||
/// Uses CRTP to inject iterator logic into Derived.
|
||||
template <typename Derived>
|
||||
class iterator_mixin {
|
||||
public:
|
||||
using const_iterator = iterator_over<Derived>;
|
||||
using const_reverse_iterator = boost::reverse_iterator<const_iterator>;
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this), 0);
|
||||
}
|
||||
const_iterator end() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this),
|
||||
static_cast<const Derived*>(this)->size());
|
||||
}
|
||||
const_reverse_iterator rbegin() const noexcept {
|
||||
return boost::make_reverse_iterator(end());
|
||||
}
|
||||
const_reverse_iterator rend() const noexcept {
|
||||
return boost::make_reverse_iterator(begin());
|
||||
}
|
||||
};
|
||||
detail::compressed_pair<int, metadata_type> size_meta_;
|
||||
option_type opt_;
|
||||
}; // namespace axis
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
|
155
include/boost/histogram/axis/category.hpp
Normal file
155
include/boost/histogram/axis/category.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/value_view.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
/** Axis which maps unique 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(n) operation for n values in the worst case and O(1) in
|
||||
* the best case. The value types must be equal-comparable.
|
||||
*/
|
||||
template <typename T, typename Allocator, typename MetaData>
|
||||
class category : public base<MetaData>,
|
||||
public iterator_mixin<category<T, Allocator, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using metadata_type = MetaData;
|
||||
using value_type = T;
|
||||
using allocator_type = Allocator;
|
||||
|
||||
public:
|
||||
/** Construct from iterator range of unique values.
|
||||
*
|
||||
* \param begin begin of category range of unique values.
|
||||
* \param end end of category range of unique values.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename It, typename = detail::requires_iterator<It>>
|
||||
category(It begin, It end, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: base_type(std::distance(begin, end), std::move(m), o), x_(nullptr, std::move(a)) {
|
||||
x_.first() = detail::create_buffer_from_iter(x_.second(), base_type::size(), begin);
|
||||
}
|
||||
|
||||
/** Construct axis from iterable sequence of unique values.
|
||||
*
|
||||
* \param seq sequence of unique values.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename C, typename = detail::requires_iterable<C>>
|
||||
category(const C& iterable, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: category(std::begin(iterable), std::end(iterable), std::move(m), o,
|
||||
std::move(a)) {}
|
||||
|
||||
/** Construct axis from an initializer list of unique values.
|
||||
*
|
||||
* \param seq sequence of unique values.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename U>
|
||||
category(std::initializer_list<U> l, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: category(l.begin(), l.end(), std::move(m), o, std::move(a)) {}
|
||||
|
||||
category() : x_(nullptr) {}
|
||||
|
||||
category(const category& o) : base_type(o), x_(o.x_) {
|
||||
x_.first() =
|
||||
detail::create_buffer_from_iter(x_.second(), base_type::size(), o.x_.first());
|
||||
}
|
||||
|
||||
category& operator=(const category& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() != o.size()) {
|
||||
detail::destroy_buffer(x_.second(), x_.first(), base_type::size());
|
||||
base_type::operator=(o);
|
||||
x_ = o.x_;
|
||||
x_.first() =
|
||||
detail::create_buffer_from_iter(x_.second(), base_type::size(), o.x_.first());
|
||||
} else {
|
||||
base_type::operator=(o);
|
||||
std::copy(o.x_.first(), o.x_.first() + base_type::size(), x_.first());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
category(category&& o) : category() {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
|
||||
category& operator=(category&& o) {
|
||||
if (this != &o) {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~category() { detail::destroy_buffer(x_.second(), x_.first(), base_type::size()); }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(const value_type& x) const noexcept {
|
||||
const auto begin = x_.first();
|
||||
const auto end = begin + base_type::size();
|
||||
return std::distance(begin, std::find(begin, end, x));
|
||||
}
|
||||
|
||||
/// Returns the value for the bin index (performs a range check).
|
||||
const value_type& value(unsigned idx) const {
|
||||
if (idx >= base_type::size()) throw std::out_of_range("category index out of range");
|
||||
return x_.first()[idx];
|
||||
}
|
||||
|
||||
auto operator[](int idx) const noexcept { return value_view<category>(idx, *this); }
|
||||
|
||||
bool operator==(const category& o) const noexcept {
|
||||
return base_type::operator==(o) &&
|
||||
std::equal(x_.first(), x_.first() + base_type::size(), o.x_.first());
|
||||
}
|
||||
|
||||
bool operator!=(const category& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
using pointer = typename std::allocator_traits<allocator_type>::pointer;
|
||||
detail::compressed_pair<pointer, allocator_type> x_;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
92
include/boost/histogram/axis/circular.hpp
Normal file
92
include/boost/histogram/axis/circular.hpp
Normal file
@ -0,0 +1,92 @@
|
||||
// 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_CIRCULAR_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_CIRCULAR_HPP
|
||||
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
// two_pi can be found in boost/math, but it is defined here to reduce deps
|
||||
constexpr double two_pi = 6.283185307179586;
|
||||
|
||||
/** Axis for real values on a circle.
|
||||
*
|
||||
* The axis is circular and wraps around reaching the perimeter value.
|
||||
* It has no underflow bin and the overflow bin merely counts special
|
||||
* values like NaN and infinity. Binning is a O(1) operation.
|
||||
*/
|
||||
template <typename RealType, typename MetaData>
|
||||
class circular : public base<MetaData>,
|
||||
public iterator_mixin<circular<RealType, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using value_type = RealType;
|
||||
using metadata_type = MetaData;
|
||||
|
||||
public:
|
||||
/** Construct n bins with an optional offset.
|
||||
*
|
||||
* \param n number of bins.
|
||||
* \param phase starting phase.
|
||||
* \param perimeter range after which value wraps around.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
circular(unsigned n, RealType phase = 0, RealType perimeter = two_pi,
|
||||
MetaData m = MetaData(), option_type o = option_type::overflow)
|
||||
: base_type(n, std::move(m),
|
||||
o == option_type::underflow_and_overflow ? option_type::overflow : o)
|
||||
, phase_(phase)
|
||||
, delta_(perimeter / n) {
|
||||
if (!std::isfinite(phase) || !(perimeter > 0))
|
||||
throw std::invalid_argument("invalid phase or perimeter");
|
||||
}
|
||||
|
||||
circular() = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
const auto z = std::floor((x - phase_) / delta_);
|
||||
if (std::isfinite(z)) {
|
||||
const auto i = static_cast<int>(z) % base_type::size();
|
||||
return i + (i < 0) * base_type::size();
|
||||
}
|
||||
return base_type::size();
|
||||
}
|
||||
|
||||
/// Returns axis value for fractional index.
|
||||
value_type value(value_type i) const noexcept { return phase_ + i * delta_; }
|
||||
|
||||
auto operator[](int idx) const noexcept { return interval_view<circular>(idx, *this); }
|
||||
|
||||
bool operator==(const circular& o) const noexcept {
|
||||
return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_;
|
||||
}
|
||||
|
||||
bool operator!=(const circular<>& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
value_type phase_ = 0.0, delta_ = 1.0;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
86
include/boost/histogram/axis/integer.hpp
Normal file
86
include/boost/histogram/axis/integer.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_INTEGER_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_INTEGER_HPP
|
||||
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
/** Axis for an interval of integer values with unit steps.
|
||||
*
|
||||
* Binning is a O(1) operation. This axis operates
|
||||
* faster than a regular axis.
|
||||
*/
|
||||
template <typename IntType, typename MetaData>
|
||||
class integer : public base<MetaData>, public iterator_mixin<integer<IntType, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using value_type = IntType;
|
||||
using metadata_type = MetaData;
|
||||
using bin_type = interval_view<integer>;
|
||||
|
||||
public:
|
||||
/** 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 metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
integer(value_type start, value_type stop, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow)
|
||||
: base_type(stop - start, std::move(m), o), min_(start) {
|
||||
if (start >= stop) { throw std::invalid_argument("start < stop required"); }
|
||||
}
|
||||
|
||||
integer() = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
x = std::floor(x - min_);
|
||||
return x >= 0 ? (x > static_cast<value_type>(base_type::size()) ? base_type::size()
|
||||
: static_cast<int>(x))
|
||||
: -1;
|
||||
}
|
||||
|
||||
/// Returns axis value for index.
|
||||
value_type value(value_type i) const noexcept {
|
||||
if (i < 0) { return std::numeric_limits<value_type>::min(); }
|
||||
if (i > static_cast<int>(base_type::size())) {
|
||||
return std::numeric_limits<value_type>::max();
|
||||
}
|
||||
return min_ + i;
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const integer& o) const noexcept {
|
||||
return base_type::operator==(o) && min_ == o.min_;
|
||||
}
|
||||
|
||||
bool operator!=(const integer& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
value_type min_ = 0;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -7,8 +7,6 @@
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
@ -18,29 +16,17 @@ class interval_view {
|
||||
public:
|
||||
interval_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {}
|
||||
|
||||
interval_view(const interval_view&) = default;
|
||||
interval_view& operator=(const interval_view&) = default;
|
||||
interval_view(interval_view&&) = default;
|
||||
interval_view& operator=(interval_view&&) = default;
|
||||
|
||||
int idx() const noexcept { return idx_; }
|
||||
|
||||
auto lower() const noexcept -> decltype(std::declval<Axis&>().lower(0)) {
|
||||
return axis_.lower(idx_);
|
||||
}
|
||||
auto upper() const noexcept -> decltype(std::declval<Axis&>().lower(0)) {
|
||||
return axis_.lower(idx_ + 1);
|
||||
}
|
||||
auto width() const noexcept -> decltype(upper() - lower()) {
|
||||
return upper() - lower();
|
||||
}
|
||||
decltype(auto) lower() const noexcept { return axis_.value(idx_); }
|
||||
decltype(auto) upper() const noexcept { return axis_.value(idx_ + 1); }
|
||||
decltype(auto) center() const noexcept { return axis_.value(idx_ + 0.5); }
|
||||
decltype(auto) width() const noexcept { return upper() - lower(); }
|
||||
|
||||
bool operator==(const interval_view& rhs) const noexcept {
|
||||
return idx_ == rhs.idx_ && axis_ == rhs.axis_;
|
||||
}
|
||||
bool operator!=(const interval_view& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
bool operator!=(const interval_view& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
explicit operator int() const noexcept { return idx_; }
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define BOOST_HISTOGRAM_AXIS_ITERATOR_HPP
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/iterator/reverse_iterator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
@ -15,16 +16,11 @@ namespace axis {
|
||||
|
||||
template <typename Axis>
|
||||
class iterator_over
|
||||
: public iterator_facade<
|
||||
iterator_over<Axis>,
|
||||
decltype(std::declval<Axis&>()[0]),
|
||||
random_access_traversal_tag,
|
||||
decltype(std::declval<Axis&>()[0]),
|
||||
int
|
||||
> {
|
||||
: public iterator_facade<iterator_over<Axis>, decltype(std::declval<Axis&>()[0]),
|
||||
random_access_traversal_tag,
|
||||
decltype(std::declval<Axis&>()[0]), int> {
|
||||
public:
|
||||
explicit iterator_over(const Axis& axis, int idx)
|
||||
: axis_(axis), idx_(idx) {}
|
||||
explicit iterator_over(const Axis& axis, int idx) : axis_(axis), idx_(idx) {}
|
||||
|
||||
iterator_over(const iterator_over&) = default;
|
||||
iterator_over& operator=(const iterator_over&) = default;
|
||||
@ -33,14 +29,11 @@ protected:
|
||||
void increment() noexcept { ++idx_; }
|
||||
void decrement() noexcept { --idx_; }
|
||||
void advance(int n) noexcept { idx_ += n; }
|
||||
int distance_to(const iterator_over& other) const noexcept {
|
||||
return other.idx_ - idx_;
|
||||
}
|
||||
int distance_to(const iterator_over& other) const noexcept { return other.idx_ - idx_; }
|
||||
bool equal(const iterator_over& other) const noexcept {
|
||||
return &axis_ == &other.axis_ && idx_ == other.idx_;
|
||||
}
|
||||
decltype(std::declval<Axis&>()[0])
|
||||
dereference() const { return axis_[idx_]; }
|
||||
decltype(std::declval<Axis&>()[0]) dereference() const { return axis_[idx_]; }
|
||||
|
||||
friend class ::boost::iterator_core_access;
|
||||
|
||||
@ -48,6 +41,28 @@ protected:
|
||||
int idx_;
|
||||
};
|
||||
|
||||
/// Uses CRTP to inject iterator logic into Derived.
|
||||
template <typename Derived>
|
||||
class iterator_mixin {
|
||||
public:
|
||||
using const_iterator = iterator_over<Derived>;
|
||||
using const_reverse_iterator = boost::reverse_iterator<const_iterator>;
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this), 0);
|
||||
}
|
||||
const_iterator end() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this),
|
||||
static_cast<const Derived*>(this)->size());
|
||||
}
|
||||
const_reverse_iterator rbegin() const noexcept {
|
||||
return boost::make_reverse_iterator(end());
|
||||
}
|
||||
const_reverse_iterator rend() const noexcept {
|
||||
return boost::make_reverse_iterator(begin());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
@ -10,9 +10,13 @@
|
||||
#define BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP
|
||||
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/value_view.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <iomanip>
|
||||
@ -39,10 +43,6 @@ template <typename T>
|
||||
const char* to_string(const axis::transform::pow<T>&) {
|
||||
return "_pow";
|
||||
}
|
||||
template <typename Q, typename U>
|
||||
const char* to_string(const axis::transform::quantity<Q, U>&) {
|
||||
return "_quantity";
|
||||
}
|
||||
|
||||
template <typename OStream, typename T>
|
||||
void stream_metadata(OStream& os, const T& t) {
|
||||
@ -83,40 +83,45 @@ void stream_transform(OStream& os, const axis::transform::pow<T>& t) {
|
||||
os << ", power=" << t.power;
|
||||
}
|
||||
|
||||
template <typename OStream, typename Q, typename U>
|
||||
void stream_transform(OStream& os, const axis::transform::quantity<Q, U>& t) {
|
||||
os << ", unit=" << t.unit;
|
||||
template <typename OStream, typename T>
|
||||
void stream_value(OStream& os, const T& t) {
|
||||
os << t;
|
||||
}
|
||||
|
||||
template <typename OStream, typename... Ts>
|
||||
void stream_value(OStream& os, const std::basic_string<Ts...>& t) {
|
||||
os << std::quoted(t);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace axis {
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const empty_metadata_type&) {
|
||||
template <typename C, typename T>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const empty_metadata_type&) {
|
||||
return os; // do nothing
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const interval_view<T>& i) {
|
||||
template <typename C, typename T, typename U>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const interval_view<U>& i) {
|
||||
os << "[" << i.lower() << ", " << i.upper() << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const value_view<T>& i) {
|
||||
template <typename C, typename T, typename U>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const value_view<U>& i) {
|
||||
os << i.value();
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename M>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const regular<T, M>& a) {
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const regular<Ts...>& a) {
|
||||
os << "regular" << detail::to_string(a.transform()) << "(" << a.size() << ", "
|
||||
<< a.lower(0) << ", " << a.lower(a.size());
|
||||
<< a.value(0) << ", " << a.value(a.size());
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
detail::stream_transform(os, a.transform());
|
||||
@ -124,56 +129,43 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const circular<T, A>& a) {
|
||||
os << "circular(" << a.size() << ", " << a.lower(0) << ", " << a.lower(a.size());
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const circular<Ts...>& a) {
|
||||
os << "circular(" << a.size() << ", " << a.value(0) << ", " << a.value(a.size());
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const variable<T, A>& a) {
|
||||
os << "variable(" << a.lower(0);
|
||||
for (unsigned i = 1; i <= a.size(); ++i) { os << ", " << a.lower(i); }
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const variable<Ts...>& a) {
|
||||
os << "variable(" << a.value(0);
|
||||
for (unsigned i = 1; i <= a.size(); ++i) { os << ", " << a.value(i); }
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const integer<T, A>& a) {
|
||||
os << "integer(" << a.lower(0) << ", " << a.lower(a.size());
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const integer<Ts...>& a) {
|
||||
os << "integer(" << a.value(0) << ", " << a.value(a.size());
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const category<T, A>& a) {
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const category<Ts...>& a) {
|
||||
os << "category(";
|
||||
for (unsigned i = 0; i < a.size(); ++i) {
|
||||
os << a[i] << (i == (a.size() - 1) ? "" : ", ");
|
||||
}
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const category<std::string, A>& a) {
|
||||
os << "category(";
|
||||
for (unsigned i = 0; i < a.size(); ++i) {
|
||||
os << std::quoted(a.value(i));
|
||||
detail::stream_value(os, a.value(i));
|
||||
os << (i == (a.size() - 1) ? "" : ", ");
|
||||
}
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
|
169
include/boost/histogram/axis/regular.hpp
Normal file
169
include/boost/histogram/axis/regular.hpp
Normal file
@ -0,0 +1,169 @@
|
||||
// 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/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
namespace transform {
|
||||
template <typename T>
|
||||
struct identity {
|
||||
static T forward(T x) { return x; }
|
||||
static T inverse(T x) { return x; }
|
||||
|
||||
bool operator==(const identity&) const noexcept { return true; }
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned) {} // noop
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct log : identity<T> {
|
||||
static T forward(T x) { return std::log(x); }
|
||||
static T inverse(T x) { return std::exp(x); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct sqrt : identity<T> {
|
||||
static T forward(T x) { return std::sqrt(x); }
|
||||
static T inverse(T x) { return x * x; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct pow {
|
||||
using U = mp11::mp_if<std::is_integral<T>, double, T>;
|
||||
U power = 1;
|
||||
|
||||
pow() = default;
|
||||
pow(U p) : power(p) {}
|
||||
|
||||
U forward(U v) const { return std::pow(v, power); }
|
||||
U inverse(U v) const { return std::pow(v, 1.0 / power); }
|
||||
|
||||
bool operator==(const pow& o) const noexcept { return power == o.power; }
|
||||
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 Transform, typename MetaData>
|
||||
class regular : public base<MetaData>,
|
||||
public iterator_mixin<regular<Transform, MetaData>>,
|
||||
protected Transform {
|
||||
using base_type = base<MetaData>;
|
||||
using transform_type = Transform;
|
||||
using external_type = detail::return_type<decltype(&Transform::inverse)>;
|
||||
using internal_type = detail::return_type<decltype(&Transform::forward)>;
|
||||
static_assert(!std::is_integral<internal_type>::value,
|
||||
"type returned by forward transform cannot be integral");
|
||||
using metadata_type = MetaData;
|
||||
|
||||
public:
|
||||
/** Construct n bins over real transformed range [begin, end).
|
||||
*
|
||||
* \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 metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
regular(transform_type trans, unsigned n, external_type start, external_type stop,
|
||||
metadata_type m = {}, option_type o = option_type::underflow_and_overflow)
|
||||
: base_type(n, std::move(m), o)
|
||||
, transform_type(std::move(trans))
|
||||
, min_(this->forward(start))
|
||||
, delta_((this->forward(stop) - this->forward(start)) / n) {
|
||||
if (!std::isfinite(min_) || !std::isfinite(delta_))
|
||||
throw std::invalid_argument("forward transform of lower or upper invalid");
|
||||
}
|
||||
|
||||
/** Construct n bins over real range [begin, end).
|
||||
*
|
||||
* \param n number of bins.
|
||||
* \param start low edge of first bin.
|
||||
* \param stop high edge of last bin.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
regular(unsigned n, external_type start, external_type stop, metadata_type m = {},
|
||||
option_type o = option_type::underflow_and_overflow)
|
||||
: regular({}, n, start, stop, std::move(m), o) {}
|
||||
|
||||
regular() = default;
|
||||
|
||||
/// Returns instance of the transform type
|
||||
const transform_type& transform() const { return *this; }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(external_type x) const noexcept {
|
||||
// Runs in hot loop, please measure impact of changes
|
||||
const auto z = (this->forward(x) - min_) / delta_;
|
||||
if (z < base_type::size()) {
|
||||
if (z >= 0)
|
||||
return static_cast<int>(z);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
return base_type::size(); // also returned if z is NaN
|
||||
|
||||
// const auto lt_max = z < base_type::size();
|
||||
// const auto ge_zero = z >= 0;
|
||||
// return lt_max * (ge_zero * static_cast<int>(z) - !ge_zero) + !lt_max *
|
||||
// base_type::size();
|
||||
}
|
||||
|
||||
/// Returns axis value for fractional index.
|
||||
external_type value(internal_type i) const noexcept {
|
||||
i /= base_type::size();
|
||||
if (i < 0)
|
||||
i = std::copysign(std::numeric_limits<internal_type>::infinity(), -delta_);
|
||||
else if (i > 1)
|
||||
i = std::copysign(std::numeric_limits<internal_type>::infinity(), delta_);
|
||||
else {
|
||||
i = (1 - i) * min_ + i * (min_ + delta_ * base_type::size());
|
||||
}
|
||||
return this->inverse(i);
|
||||
}
|
||||
|
||||
/// Access bin at index
|
||||
auto operator[](int idx) const noexcept { return interval_view<regular>(idx, *this); }
|
||||
|
||||
bool operator==(const regular& o) const noexcept {
|
||||
return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ &&
|
||||
delta_ == o.delta_;
|
||||
}
|
||||
|
||||
bool operator!=(const regular& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
internal_type min_, delta_;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,600 +0,0 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_TYPES_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_TYPES_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/value_view.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace axis {
|
||||
|
||||
namespace transform {
|
||||
|
||||
template <typename T>
|
||||
struct identity {
|
||||
static T forward(T x) { return x; }
|
||||
static T inverse(T x) { return x; }
|
||||
|
||||
constexpr bool operator==(const identity&) const noexcept { return true; }
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned) {} // noop
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct log : public identity<T> {
|
||||
static T forward(T x) { return std::log(x); }
|
||||
static T inverse(T x) { return std::exp(x); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct sqrt : public identity<T> {
|
||||
static T forward(T x) { return std::sqrt(x); }
|
||||
static T inverse(T x) { return x * x; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct pow {
|
||||
T power = 1.0;
|
||||
|
||||
pow() = default;
|
||||
pow(T p) : power(p) {}
|
||||
|
||||
T forward(T v) const { return std::pow(v, power); }
|
||||
T inverse(T v) const { return std::pow(v, 1.0 / power); }
|
||||
|
||||
bool operator==(const pow& o) const noexcept { return power == o.power; }
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
template <typename Quantity, typename Unit>
|
||||
struct quantity {
|
||||
Unit unit;
|
||||
|
||||
quantity(const Unit& u) : unit(u) {}
|
||||
|
||||
using Dimensionless = decltype(std::declval<Quantity&>() / std::declval<Unit&>());
|
||||
|
||||
Dimensionless forward(Quantity x) const { return x / unit; }
|
||||
Quantity inverse(Dimensionless x) const { return x * unit; }
|
||||
|
||||
bool operator==(const quantity& o) const noexcept { return unit == o.unit; }
|
||||
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 Transform, typename MetaData>
|
||||
class regular : public base<MetaData>,
|
||||
public iterator_mixin<regular<Transform, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using transform_type = Transform;
|
||||
using value_type = detail::arg_type<-1, decltype(&transform_type::forward)>;
|
||||
using internal_type = detail::return_type<decltype(&transform_type::forward)>;
|
||||
static_assert(std::is_floating_point<internal_type>::value,
|
||||
"type returned by forward transform must be floating point");
|
||||
using metadata_type = MetaData;
|
||||
using bin_type = interval_view<regular>;
|
||||
|
||||
struct data : Transform // empty base class optimization,
|
||||
// MSVC fails if transform_type is used here
|
||||
|
||||
{
|
||||
internal_type min = 0, delta = 1;
|
||||
|
||||
data(const Transform& t, unsigned n, value_type b, value_type e)
|
||||
: Transform(t)
|
||||
, min(this->forward(b))
|
||||
, delta((this->forward(e) - this->forward(b)) / n) {}
|
||||
|
||||
data() = default;
|
||||
|
||||
bool operator==(const data& rhs) const noexcept {
|
||||
return Transform::operator==(rhs) && min == rhs.min && delta == rhs.delta;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/** Construct axis with n bins over real range [begin, end).
|
||||
*
|
||||
* \param n number of bins.
|
||||
* \param start low edge of first bin.
|
||||
* \param stop high edge of last bin.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param trans transform instance to use.
|
||||
*/
|
||||
regular(unsigned n, value_type start, value_type stop,
|
||||
metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
transform_type trans = transform_type())
|
||||
: base_type(n, std::move(m), o), data_(std::move(trans), n, start, stop) {
|
||||
if (!std::isfinite(data_.min) || !std::isfinite(data_.delta))
|
||||
throw std::invalid_argument("forward transform of lower or upper invalid");
|
||||
}
|
||||
|
||||
regular() = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
// Runs in hot loop, please measure impact of changes
|
||||
const auto z = (data_.forward(x) - data_.min) / data_.delta;
|
||||
if (z < base_type::size()) {
|
||||
if (z >= 0)
|
||||
return static_cast<int>(z);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
return base_type::size(); // also returned if z is NaN
|
||||
|
||||
// const auto lt_max = z < base_type::size();
|
||||
// const auto ge_zero = z >= 0;
|
||||
// return lt_max * (ge_zero * static_cast<int>(z) - !ge_zero) + !lt_max *
|
||||
// base_type::size();
|
||||
}
|
||||
|
||||
/// Returns lower edge of bin.
|
||||
value_type lower(int idx) const noexcept {
|
||||
const auto z = internal_type(idx) / base_type::size();
|
||||
internal_type x;
|
||||
if (z < 0)
|
||||
x = -std::numeric_limits<internal_type>::infinity();
|
||||
else if (z > 1)
|
||||
x = std::numeric_limits<internal_type>::infinity();
|
||||
else {
|
||||
x = (1 - z) * data_.min + z * (data_.min + data_.delta * base_type::size());
|
||||
}
|
||||
return data_.inverse(x);
|
||||
}
|
||||
|
||||
const transform_type& transform() const { return data_; }
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const regular& o) const noexcept {
|
||||
return base_type::operator==(o) && data_ == o.data_;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
data data_;
|
||||
};
|
||||
|
||||
/** 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, typename MetaData>
|
||||
class circular : public base<MetaData>,
|
||||
public iterator_mixin<circular<RealType, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using value_type = RealType;
|
||||
using metadata_type = MetaData;
|
||||
using bin_type = interval_view<circular>;
|
||||
|
||||
public:
|
||||
// two_pi can be found in boost/math, but it is defined here to reduce deps
|
||||
static constexpr value_type two_pi() { return 6.283185307179586; }
|
||||
|
||||
/** 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 metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
explicit circular(unsigned n, value_type phase = 0.0, value_type perimeter = two_pi(),
|
||||
metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow)
|
||||
: base_type(n, std::move(m),
|
||||
o == option_type::underflow_and_overflow ? option_type::overflow : o)
|
||||
, phase_(phase)
|
||||
, delta_(perimeter / n) {
|
||||
if (!std::isfinite(phase) || !(perimeter > 0))
|
||||
throw std::invalid_argument("invalid phase or perimeter");
|
||||
}
|
||||
|
||||
circular() = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
const auto z = std::floor((x - phase_) / delta_);
|
||||
if (std::isfinite(z)) {
|
||||
const auto i = static_cast<int>(z) % base_type::size();
|
||||
return i + (i < 0) * base_type::size();
|
||||
}
|
||||
return base_type::size();
|
||||
}
|
||||
|
||||
/// Returns lower edge of bin.
|
||||
value_type lower(int i) const noexcept { return phase_ + i * delta_; }
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const circular& o) const noexcept {
|
||||
return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
value_type phase_ = 0.0, delta_ = 1.0;
|
||||
};
|
||||
|
||||
/** 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, typename Allocator, typename MetaData>
|
||||
class variable : public base<MetaData>,
|
||||
public iterator_mixin<variable<RealType, Allocator, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using value_type = RealType;
|
||||
using allocator_type = Allocator;
|
||||
using metadata_type = MetaData;
|
||||
using bin_type = interval_view<variable>;
|
||||
|
||||
struct data : Allocator // empty base class optimization,
|
||||
// MSVC fails if allocator_type is used here
|
||||
{
|
||||
typename std::allocator_traits<Allocator>::pointer x = nullptr;
|
||||
|
||||
using Allocator::Allocator;
|
||||
|
||||
data(const Allocator& a) : Allocator(a) {}
|
||||
data() = default;
|
||||
|
||||
friend void swap(data& a, data& b) noexcept {
|
||||
std::swap(a.x, b.x);
|
||||
auto tmp = static_cast<Allocator&&>(a);
|
||||
a = static_cast<Allocator&&>(b);
|
||||
b = static_cast<Allocator&&>(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/** Construct an axis from iterator range of bin edges.
|
||||
*
|
||||
* \param begin begin of edge sequence.
|
||||
* \param end end of edge sequence.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
|
||||
variable(Iterator begin, Iterator end, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o)
|
||||
, data_(std::move(a)) {
|
||||
using AT = std::allocator_traits<allocator_type>;
|
||||
data_.x = AT::allocate(data_, nx());
|
||||
try {
|
||||
auto xit = data_.x;
|
||||
try {
|
||||
AT::construct(data_, xit, *begin++);
|
||||
while (begin != end) {
|
||||
if (*begin <= *xit) {
|
||||
++xit; // to make sure catch code works
|
||||
throw std::invalid_argument("input sequence must be strictly ascending");
|
||||
}
|
||||
++xit;
|
||||
AT::construct(data_, xit, *begin++);
|
||||
}
|
||||
} catch (...) {
|
||||
// release resources that were already acquired before rethrowing
|
||||
while (xit != data_.x) AT::destroy(data_, --xit);
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
// release resources that were already acquired before rethrowing
|
||||
AT::deallocate(data_, data_.x, nx());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/** Construct an axis from iterable range of bin edges.
|
||||
*
|
||||
* \param iterable iterable range of bin edges.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename T, typename = detail::requires_iterable<T>>
|
||||
variable(const T& t, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: variable(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {}
|
||||
|
||||
template <typename T>
|
||||
variable(std::initializer_list<T> t, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: variable(t.begin(), t.end(), std::move(m), o, std::move(a)) {}
|
||||
|
||||
variable() = default;
|
||||
|
||||
variable(const variable& o) : base_type(o), data_(o.data_) {
|
||||
data_.x = detail::create_buffer_from_iter(data_, nx(), o.data_.x);
|
||||
}
|
||||
|
||||
variable& operator=(const variable& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() != o.size()) {
|
||||
detail::destroy_buffer(data_, data_.x, nx());
|
||||
data_ = o.data_;
|
||||
base_type::operator=(o);
|
||||
data_.x = detail::create_buffer_from_iter(data_, nx(), o.data_.x);
|
||||
} else {
|
||||
base_type::operator=(o);
|
||||
std::copy(o.data_.x, o.data_.x + nx(), data_.x);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
variable(variable&& o) : base_type(std::move(o)), data_(std::move(o.data_)) {
|
||||
o.data_.x = nullptr;
|
||||
}
|
||||
|
||||
variable& operator=(variable&& o) {
|
||||
if (this != &o) {
|
||||
std::swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
std::swap(data_, o.data_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~variable() { detail::destroy_buffer(data_, data_.x, nx()); }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
return std::upper_bound(data_.x, data_.x + nx(), x) - data_.x - 1;
|
||||
}
|
||||
|
||||
/// Returns the starting edge of the bin.
|
||||
value_type lower(int i) const noexcept {
|
||||
if (i < 0) { return -std::numeric_limits<value_type>::infinity(); }
|
||||
if (i > static_cast<int>(base_type::size())) {
|
||||
return std::numeric_limits<value_type>::infinity();
|
||||
}
|
||||
return data_.x[i];
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const variable& o) const noexcept {
|
||||
return base_type::operator==(o) && std::equal(data_.x, data_.x + nx(), o.data_.x);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
int nx() const { return base_type::size() + 1; }
|
||||
data data_;
|
||||
};
|
||||
|
||||
/** 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, typename MetaData>
|
||||
class integer : public base<MetaData>, public iterator_mixin<integer<IntType, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using value_type = IntType;
|
||||
using metadata_type = MetaData;
|
||||
using bin_type = interval_view<integer>;
|
||||
|
||||
public:
|
||||
/** Construct axis over a semi-open integer interval [begin, end).
|
||||
*
|
||||
* \param begin first integer of covered range.
|
||||
* \param end one past last integer of covered range.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
integer(value_type begin, value_type end, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow)
|
||||
: base_type(end - begin, std::move(m), o), min_(begin) {
|
||||
if (begin >= end) { throw std::invalid_argument("begin < end 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.
|
||||
int operator()(value_type x) const noexcept {
|
||||
const int z = x - min_;
|
||||
return z >= 0 ? (z > static_cast<int>(base_type::size()) ? base_type::size() : z)
|
||||
: -1;
|
||||
}
|
||||
|
||||
/// Returns lower edge of the integral bin.
|
||||
value_type lower(int i) const noexcept {
|
||||
if (i < 0) { return std::numeric_limits<value_type>::min(); }
|
||||
if (i > static_cast<int>(base_type::size())) {
|
||||
return std::numeric_limits<value_type>::max();
|
||||
}
|
||||
return min_ + i;
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const integer& o) const noexcept {
|
||||
return base_type::operator==(o) && min_ == o.min_;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
value_type min_ = 0;
|
||||
};
|
||||
|
||||
/** Axis which maps unique 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(n) operation for n values in the worst case and O(1) in
|
||||
* the best case. The value types must be equal-comparable.
|
||||
*/
|
||||
template <typename ValueType, typename Allocator, typename MetaData>
|
||||
class category : public base<MetaData>,
|
||||
public iterator_mixin<category<ValueType, Allocator, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using metadata_type = MetaData;
|
||||
using value_type = ValueType;
|
||||
using allocator_type = Allocator;
|
||||
using bin_type = value_view<category>;
|
||||
|
||||
struct data : Allocator // empty base class optimization,
|
||||
// MSVC fails if allocator_type is used here
|
||||
{
|
||||
typename std::allocator_traits<Allocator>::pointer x = nullptr;
|
||||
using Allocator::Allocator;
|
||||
data(const Allocator& a) : Allocator(a) {}
|
||||
data() = default;
|
||||
|
||||
friend void swap(data& a, data& b) noexcept {
|
||||
std::swap(a.x, b.x);
|
||||
auto tmp = static_cast<Allocator&&>(a);
|
||||
a = static_cast<Allocator&&>(b);
|
||||
b = static_cast<Allocator&&>(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/** Construct an axis from iterator range of categories.
|
||||
*
|
||||
* \param begin begin of category range of unique values.
|
||||
* \param end end of category range of unique values.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
|
||||
category(Iterator begin, Iterator end, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: base_type(std::distance(begin, end), std::move(m), o), data_(std::move(a)) {
|
||||
data_.x = detail::create_buffer_from_iter(data_, base_type::size(), begin);
|
||||
}
|
||||
|
||||
/** Construct from an initializer list of strings.
|
||||
*
|
||||
* \param seq sequence of unique values.
|
||||
* \param metadata description of the axis.
|
||||
*/
|
||||
template <typename T, typename = detail::requires_iterable<T>>
|
||||
category(const T& t, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: category(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {}
|
||||
|
||||
template <typename T>
|
||||
category(std::initializer_list<T> t, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: category(t.begin(), t.end(), std::move(m), o, std::move(a)) {}
|
||||
|
||||
category() = default;
|
||||
|
||||
category(const category& o) : base_type(o), data_(o.data_) {
|
||||
data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x);
|
||||
}
|
||||
|
||||
category& operator=(const category& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() != o.size()) {
|
||||
detail::destroy_buffer(data_, data_.x, base_type::size());
|
||||
base_type::operator=(o);
|
||||
data_ = o.data_;
|
||||
data_.x = detail::create_buffer_from_iter(data_, base_type::size(), o.data_.x);
|
||||
} else {
|
||||
base_type::operator=(o);
|
||||
std::copy(o.data_.x, o.data_.x + base_type::size(), data_.x);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
category(category&& o) : base_type(std::move(o)), data_(std::move(o.data_)) {
|
||||
o.data_.x = nullptr;
|
||||
}
|
||||
|
||||
category& operator=(category&& o) {
|
||||
if (this != &o) {
|
||||
std::swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
std::swap(data_, o.data_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~category() { detail::destroy_buffer(data_, data_.x, base_type::size()); }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(const value_type& x) const noexcept {
|
||||
const auto begin = data_.x;
|
||||
const auto end = begin + base_type::size();
|
||||
return std::distance(begin, std::find(begin, end, x));
|
||||
}
|
||||
|
||||
/// Returns the value for the bin index (performs a range check).
|
||||
const value_type& value(int idx) const {
|
||||
if (idx < 0 || idx >= static_cast<int>(base_type::size()))
|
||||
throw std::out_of_range("category index out of range");
|
||||
return data_.x[idx];
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const category& o) const noexcept {
|
||||
return base_type::operator==(o) &&
|
||||
std::equal(data_.x, data_.x + base_type::size(), o.data_.x);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
data data_;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -7,9 +7,6 @@
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
@ -19,23 +16,14 @@ class value_view {
|
||||
public:
|
||||
value_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {}
|
||||
|
||||
value_view(const value_view&) = default;
|
||||
value_view& operator=(const value_view&) = default;
|
||||
value_view(value_view&&) = default;
|
||||
value_view& operator=(value_view&&) = default;
|
||||
|
||||
int idx() const noexcept { return idx_; }
|
||||
|
||||
auto value() const -> decltype(std::declval<Axis&>().value(0)) {
|
||||
return axis_.value(idx_);
|
||||
}
|
||||
decltype(auto) value() const { return axis_.value(idx_); }
|
||||
|
||||
bool operator==(const value_view& rhs) const noexcept {
|
||||
return idx_ == rhs.idx_ && axis_ == rhs.axis_;
|
||||
}
|
||||
bool operator!=(const value_view& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
bool operator!=(const value_view& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
explicit operator int() const noexcept { return idx_; }
|
||||
|
||||
|
182
include/boost/histogram/axis/variable.hpp
Normal file
182
include/boost/histogram/axis/variable.hpp
Normal file
@ -0,0 +1,182 @@
|
||||
// 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/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
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.
|
||||
*/
|
||||
template <typename RealType, typename Allocator, typename MetaData>
|
||||
class variable : public base<MetaData>,
|
||||
public iterator_mixin<variable<RealType, Allocator, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using allocator_type = Allocator;
|
||||
using metadata_type = MetaData;
|
||||
using value_type = RealType;
|
||||
|
||||
public:
|
||||
/** Construct from iterator range of bin edges.
|
||||
*
|
||||
* \param begin begin of edge sequence.
|
||||
* \param end end of edge sequence.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename It, typename = detail::requires_iterator<It>>
|
||||
variable(It begin, It end, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o)
|
||||
, x_(nullptr, std::move(a)) {
|
||||
using AT = std::allocator_traits<allocator_type>;
|
||||
x_.first() = AT::allocate(x_.second(), nx());
|
||||
try {
|
||||
auto xit = x_.first();
|
||||
try {
|
||||
AT::construct(x_.second(), xit, *begin++);
|
||||
while (begin != end) {
|
||||
if (*begin <= *xit) {
|
||||
++xit; // to make sure catch code works
|
||||
throw std::invalid_argument("input sequence must be strictly ascending");
|
||||
}
|
||||
++xit;
|
||||
AT::construct(x_.second(), xit, *begin++);
|
||||
}
|
||||
} catch (...) {
|
||||
// release resources that were already acquired before rethrowing
|
||||
while (xit != x_.first()) AT::destroy(x_.second(), --xit);
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
// release resources that were already acquired before rethrowing
|
||||
AT::deallocate(x_.second(), x_.first(), nx());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/** Construct variable axis from iterable range of bin edges.
|
||||
*
|
||||
* \param iterable iterable range of bin edges.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename U, typename = detail::requires_iterable<U>>
|
||||
variable(const U& iterable, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: variable(std::begin(iterable), std::end(iterable), std::move(m), o,
|
||||
std::move(a)) {}
|
||||
|
||||
/** Construct variable axis from initializer list of bin edges.
|
||||
*
|
||||
* \param edgelist list of of bin edges.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename U>
|
||||
variable(const std::initializer_list<U>& l, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: variable(l.begin(), l.end(), std::move(m), o, std::move(a)) {}
|
||||
|
||||
variable() : x_(nullptr) {}
|
||||
|
||||
variable(const variable& o) : base_type(o), x_(o.x_) {
|
||||
x_.first() = detail::create_buffer_from_iter(x_.second(), nx(), o.x_.first());
|
||||
}
|
||||
|
||||
variable& operator=(const variable& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() == o.size()) {
|
||||
base_type::operator=(o);
|
||||
std::copy(o.x_.first(), o.x_.first() + nx(), x_.first());
|
||||
} else {
|
||||
detail::destroy_buffer(x_.second(), x_.first(), nx());
|
||||
base_type::operator=(o);
|
||||
x_.second() = o.x_.second();
|
||||
x_.first() = detail::create_buffer_from_iter(x_.second(), nx(), o.x_.first());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
variable(variable&& o) : variable() {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
|
||||
variable& operator=(variable&& o) {
|
||||
if (this != &o) {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~variable() { detail::destroy_buffer(x_.second(), x_.first(), nx()); }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
const auto p = x_.first();
|
||||
return std::upper_bound(p, p + nx(), x) - p - 1;
|
||||
}
|
||||
|
||||
/// Returns axis value for fractional index.
|
||||
value_type value(value_type i) const noexcept {
|
||||
if (i < 0) { return -std::numeric_limits<value_type>::infinity(); }
|
||||
if (i > base_type::size()) { return std::numeric_limits<value_type>::infinity(); }
|
||||
value_type z;
|
||||
const int k = std::modf(i, &z);
|
||||
return (1.0 - z) * x_.first()[k] + z * x_.first()[k + 1];
|
||||
}
|
||||
|
||||
auto operator[](int idx) const noexcept { return interval_view<variable>(idx, *this); }
|
||||
|
||||
bool operator==(const variable& o) const noexcept {
|
||||
return base_type::operator==(o) &&
|
||||
std::equal(x_.first(), x_.first() + nx(), o.x_.first());
|
||||
}
|
||||
|
||||
bool operator!=(const variable<>& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
int nx() const { return base_type::size() + 1; }
|
||||
using pointer = typename std::allocator_traits<allocator_type>::pointer;
|
||||
detail::compressed_pair<pointer, allocator_type> x_;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_VARIANT_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP
|
||||
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
@ -16,44 +17,15 @@
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <ostream>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct call_visitor {
|
||||
const double x;
|
||||
call_visitor(const double arg) : x(arg) {}
|
||||
template <typename T>
|
||||
int operator()(const T& t) const {
|
||||
using args = axis::traits::args<T>;
|
||||
return impl(mp11::mp_bool<
|
||||
(mp11::mp_size<args>::value == 1 &&
|
||||
std::is_convertible<double, mp11::mp_first<args>>::value)
|
||||
>(), t);
|
||||
}
|
||||
template <typename T>
|
||||
int impl(std::true_type, const T& a) const {
|
||||
return a(x);
|
||||
}
|
||||
template <typename T>
|
||||
int impl(std::false_type, const T&) const {
|
||||
using args = axis::traits::args<T>;
|
||||
throw std::invalid_argument(detail::cat(
|
||||
"cannot convert double to ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(args) ),
|
||||
" for ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(T) )
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename R>
|
||||
struct functor_wrapper : public boost::static_visitor<R> {
|
||||
F& fcn;
|
||||
@ -64,25 +36,22 @@ struct functor_wrapper : public boost::static_visitor<R> {
|
||||
return fcn(std::forward<T>(t));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace axis {
|
||||
|
||||
/// Polymorphic axis type
|
||||
template <typename... Ts>
|
||||
class variant
|
||||
: private boost::variant<Ts...>
|
||||
, public iterator_mixin<variant<Ts...>>
|
||||
{
|
||||
class variant : private boost::variant<Ts...>, public iterator_mixin<variant<Ts...>> {
|
||||
using base_type = boost::variant<Ts...>;
|
||||
using bin_type = interval_view<variant>;
|
||||
using first_bounded_type = mp11::mp_first<base_type>;
|
||||
using metadata_type = detail::rm_cvref<decltype(traits::metadata(std::declval<first_bounded_type&>()))>;
|
||||
using metadata_type =
|
||||
detail::rm_cvref<decltype(traits::metadata(std::declval<first_bounded_type&>()))>;
|
||||
|
||||
template <typename T>
|
||||
using requires_bounded_type =
|
||||
mp11::mp_if<mp11::mp_contains<base_type, detail::rm_cvref<T>>, void>;
|
||||
mp11::mp_if<mp11::mp_contains<base_type, detail::rm_cvref<T>>, void>;
|
||||
|
||||
public:
|
||||
variant() = default;
|
||||
@ -107,18 +76,20 @@ public:
|
||||
|
||||
template <typename... Us>
|
||||
variant& operator=(const variant<Us...>& u) {
|
||||
visit([this](const auto& u) {
|
||||
using U = detail::rm_cvref<decltype(u)>;
|
||||
detail::static_if<mp11::mp_contains<base_type, U>>(
|
||||
[this](const auto& u) { this->operator=(u); },
|
||||
[](const auto&) {
|
||||
throw std::runtime_error(detail::cat(
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(U) ),
|
||||
" is not a bounded type of ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(variant) )
|
||||
));
|
||||
}, u);
|
||||
}, u);
|
||||
visit(
|
||||
[this](const auto& u) {
|
||||
using U = detail::rm_cvref<decltype(u)>;
|
||||
detail::static_if<mp11::mp_contains<base_type, U>>(
|
||||
[this](const auto& u) { this->operator=(u); },
|
||||
[](const auto&) {
|
||||
throw std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
" is not a bounded type of ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(variant))));
|
||||
},
|
||||
u);
|
||||
},
|
||||
u);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -131,56 +102,102 @@ public:
|
||||
}
|
||||
|
||||
const metadata_type& metadata() const {
|
||||
return visit([](const auto& x) -> const metadata_type& {
|
||||
using U = decltype(traits::metadata(x));
|
||||
return detail::static_if<std::is_same<U, const metadata_type&>>(
|
||||
[](const auto& x) -> const metadata_type& { return traits::metadata(x); },
|
||||
[](const auto&) -> const metadata_type& {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"cannot return metadata of type ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(U) ),
|
||||
" through axis::variant interface which uses type ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(const metadata_type&) )
|
||||
));
|
||||
}, x);
|
||||
}, *this);
|
||||
return visit(
|
||||
[](const auto& x) -> const metadata_type& {
|
||||
using U = decltype(traits::metadata(x));
|
||||
return detail::static_if<std::is_same<U, const metadata_type&>>(
|
||||
[](const auto& x) -> const metadata_type& { return traits::metadata(x); },
|
||||
[](const auto&) -> const metadata_type& {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"cannot return metadata of type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
" through axis::variant interface which uses type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(const metadata_type&)),
|
||||
"; use boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
x);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
metadata_type& metadata() {
|
||||
return visit([](auto& x) -> metadata_type& {
|
||||
using U = decltype(traits::metadata(x));
|
||||
return detail::static_if<std::is_same<U, metadata_type&>>(
|
||||
[](auto& x) -> metadata_type& { return traits::metadata(x); },
|
||||
[](auto&) -> metadata_type& {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"cannot return metadata of type ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(U) ),
|
||||
" through axis::variant interface which uses type ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type&) )
|
||||
));
|
||||
}, x);
|
||||
}, *this);
|
||||
return visit(
|
||||
[](auto& x) -> metadata_type& {
|
||||
using U = decltype(traits::metadata(x));
|
||||
return detail::static_if<std::is_same<U, metadata_type&>>(
|
||||
[](auto& x) -> metadata_type& { return traits::metadata(x); },
|
||||
[](auto&) -> metadata_type& {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"cannot return metadata of type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
" through axis::variant interface which uses type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type&)),
|
||||
"; use boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
x);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
// Only works for axes with compatible call signature
|
||||
// and will throw a invalid_argument exception otherwise
|
||||
int operator()(double x) const {
|
||||
return visit(detail::call_visitor(x), *this);
|
||||
return visit(
|
||||
[x](const auto& a) {
|
||||
using T = detail::rm_cvref<decltype(a)>;
|
||||
using args = axis::traits::args<T>;
|
||||
return detail::static_if_c<(
|
||||
mp11::mp_size<args>::value == 1 &&
|
||||
std::is_convertible<double, mp11::mp_first<args>>::value)>(
|
||||
[x](const auto& a) -> int { return a(x); },
|
||||
[](const auto& a) -> int {
|
||||
throw std::invalid_argument(detail::cat(
|
||||
"cannot convert double to ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(args)), " for ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(decltype(a))),
|
||||
"; use boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
a);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
// Only works for axes with a lower method
|
||||
// and will throw a runtime_error otherwise
|
||||
double lower(int idx) const {
|
||||
return visit([idx](const auto& x) {
|
||||
using T = detail::rm_cvref<decltype(x)>;
|
||||
return detail::static_if<detail::has_method_lower<T>>(
|
||||
[idx](const auto& x) -> double { return x.lower(idx); },
|
||||
[](const auto&) -> double {
|
||||
throw std::runtime_error(detail::cat(
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(T) ),
|
||||
" has no lower method"));
|
||||
}, x);
|
||||
}, *this);
|
||||
// Only works for axes with value method that returns something convertible to
|
||||
// double and will throw a runtime_error otherwise
|
||||
double value(double idx) const {
|
||||
return visit(
|
||||
[idx](const auto& a) {
|
||||
using T = detail::rm_cvref<decltype(a)>;
|
||||
return detail::static_if<detail::has_method_value<T>>(
|
||||
[idx](const auto& a) -> double {
|
||||
using T = detail::rm_cvref<decltype(a)>;
|
||||
using U = detail::return_type<decltype(&T::value)>;
|
||||
return detail::static_if<std::is_convertible<U, double>>(
|
||||
[idx](const auto& a) -> double { return a.value(idx); },
|
||||
[](const auto&) -> double {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"return value ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " of ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
|
||||
"::value(double) is not convertible to double; use "
|
||||
"boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
a);
|
||||
},
|
||||
[](const auto&) -> double {
|
||||
throw std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
|
||||
" has no value method; use "
|
||||
"boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
a);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
// this only works for axes with compatible bin type
|
||||
@ -213,7 +230,7 @@ public:
|
||||
|
||||
template <typename Functor, typename Variant>
|
||||
friend auto visit(Functor&& f, Variant&& v)
|
||||
-> detail::visitor_return_type<Functor, Variant>;
|
||||
-> detail::visitor_return_type<Functor, Variant>;
|
||||
|
||||
template <typename T, typename... Us>
|
||||
friend T& get(variant<Us...>& v);
|
||||
@ -229,33 +246,34 @@ public:
|
||||
|
||||
template <typename T, typename... Us>
|
||||
friend const T* get(const variant<Us...>* v);
|
||||
};
|
||||
}; // namespace histogram
|
||||
|
||||
template <typename Functor, typename Variant>
|
||||
auto visit(Functor&& f, Variant&& v)
|
||||
-> detail::visitor_return_type<Functor, Variant>
|
||||
{
|
||||
auto visit(Functor&& f, Variant&& v) -> detail::visitor_return_type<Functor, Variant> {
|
||||
using R = detail::visitor_return_type<Functor, Variant>;
|
||||
return boost::apply_visitor(detail::functor_wrapper<Functor, R>(f),
|
||||
static_cast<
|
||||
detail::copy_qualifiers<Variant,
|
||||
typename detail::rm_cvref<Variant>::base_type>
|
||||
>(v));
|
||||
return boost::apply_visitor(
|
||||
detail::functor_wrapper<Functor, R>(f),
|
||||
static_cast<detail::copy_qualifiers<Variant,
|
||||
typename detail::rm_cvref<Variant>::base_type>>(
|
||||
v));
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename... Ts>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const variant<Ts...>& v)
|
||||
{
|
||||
visit([&os](const auto& x) {
|
||||
using T = detail::rm_cvref<decltype(x)>;
|
||||
detail::static_if<detail::is_streamable<T>>(
|
||||
[&os](const auto& x) { os << x; },
|
||||
[](const auto&) {
|
||||
throw std::runtime_error(detail::cat(
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(T) ),
|
||||
" is not streamable"));
|
||||
}, x);
|
||||
}, v);
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const variant<Ts...>& v) {
|
||||
visit(
|
||||
[&os](const auto& x) {
|
||||
using T = detail::rm_cvref<decltype(x)>;
|
||||
detail::static_if<detail::is_streamable<T>>(
|
||||
[&os](const auto& x) { os << x; },
|
||||
[](const auto&) {
|
||||
throw std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
|
||||
" is not streamable"));
|
||||
},
|
||||
x);
|
||||
},
|
||||
v);
|
||||
return os;
|
||||
}
|
||||
|
||||
@ -285,8 +303,7 @@ const T* get(const variant<Us...>* v) {
|
||||
}
|
||||
|
||||
// pass-through if T is an axis instead of a variant
|
||||
template <typename T, typename U,
|
||||
typename = detail::requires_axis<detail::rm_cvref<U>>,
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::rm_cvref<U>>,
|
||||
typename = detail::requires_same<T, detail::rm_cvref<U>>>
|
||||
U get(U&& u) {
|
||||
return std::forward<U>(u);
|
||||
|
74
include/boost/histogram/detail/compressed_pair.hpp
Normal file
74
include/boost/histogram/detail/compressed_pair.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2018 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <typename T1, typename T2, bool B>
|
||||
class compressed_pair_impl;
|
||||
|
||||
template <typename T1, typename T2>
|
||||
class compressed_pair_impl<T1, T2, true> : protected T2 {
|
||||
public:
|
||||
template <typename U1, typename U2>
|
||||
compressed_pair_impl(U1&& u1, U2&& u2)
|
||||
: T2(std::forward<U2>(u2)), first_(std::forward<U1>(u1)) {}
|
||||
template <typename U1>
|
||||
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
|
||||
compressed_pair_impl() = default;
|
||||
|
||||
T1& first() { return first_; }
|
||||
T2& second() { return static_cast<T2&>(*this); }
|
||||
const T1& first() const { return first_; }
|
||||
const T2& second() const { return static_cast<const T2&>(*this); }
|
||||
|
||||
private:
|
||||
T1 first_;
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
class compressed_pair_impl<T1, T2, false> {
|
||||
public:
|
||||
template <typename U1, typename U2>
|
||||
compressed_pair_impl(U1&& u1, U2&& u2)
|
||||
: first_(std::forward<U1>(u1)), second_(std::forward<U2>(u2)) {}
|
||||
template <typename U1>
|
||||
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
|
||||
compressed_pair_impl() = default;
|
||||
|
||||
T1& first() { return first_; }
|
||||
T2& second() { return second_; }
|
||||
const T1& first() const { return first_; }
|
||||
const T2& second() const { return second_; }
|
||||
|
||||
private:
|
||||
T1 first_;
|
||||
T2 second_;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
void swap(compressed_pair_impl<Ts...>& a, compressed_pair_impl<Ts...>& b) {
|
||||
using std::swap;
|
||||
swap(a.first(), b.first());
|
||||
swap(a.second(), b.second());
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
using compressed_pair =
|
||||
compressed_pair_impl<T1, T2, (!std::is_final<T2>::value && std::is_empty<T2>::value)>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -7,10 +7,10 @@
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_META_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_META_HPP
|
||||
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/callable_traits/args.hpp>
|
||||
#include <boost/callable_traits/return_type.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
@ -49,37 +49,32 @@ template <typename T>
|
||||
using container_element_type = mp11::mp_first<rm_cvref<T>>;
|
||||
|
||||
template <typename T>
|
||||
using unqualified_iterator_value_type = rm_cvref<typename std::iterator_traits<T>::value_type>;
|
||||
using unqualified_iterator_value_type =
|
||||
rm_cvref<typename std::iterator_traits<T>::value_type>;
|
||||
|
||||
template <typename T>
|
||||
using return_type = typename boost::callable_traits::return_type<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using args_type = mp11::mp_if<
|
||||
std::is_member_function_pointer<T>,
|
||||
mp11::mp_pop_front<boost::callable_traits::args_t<T>>,
|
||||
boost::callable_traits::args_t<T>
|
||||
>;
|
||||
using args_type = mp11::mp_if<std::is_member_function_pointer<T>,
|
||||
mp11::mp_pop_front<boost::callable_traits::args_t<T>>,
|
||||
boost::callable_traits::args_t<T>>;
|
||||
|
||||
template <int N, typename T>
|
||||
using arg_type = typename mp11::mp_at_c<args_type<T>, (N < 0 ? mp11::mp_size<args_type<T>>::value + N : N)>;
|
||||
template <typename T, std::size_t N = 0>
|
||||
using arg_type = typename mp11::mp_at_c<args_type<T>, N>;
|
||||
|
||||
template <typename F, typename V>
|
||||
using visitor_return_type = decltype(std::declval<F>()(std::declval<copy_qualifiers<V, mp_at_c<V, 0>>>()));
|
||||
using visitor_return_type =
|
||||
decltype(std::declval<F>()(std::declval<copy_qualifiers<V, mp_at_c<V, 0>>>()));
|
||||
|
||||
template <bool B, typename T, typename F, typename... Ts>
|
||||
constexpr decltype(auto) static_if_c(T&& t, F&& f, Ts&&... ts)
|
||||
{
|
||||
return std::get<(B ? 0 : 1)>(
|
||||
std::forward_as_tuple(
|
||||
std::forward<T>(t),
|
||||
std::forward<F>(f)
|
||||
))(std::forward<Ts>(ts)...);
|
||||
constexpr decltype(auto) static_if_c(T&& t, F&& f, Ts&&... ts) {
|
||||
return std::get<(B ? 0 : 1)>(std::forward_as_tuple(
|
||||
std::forward<T>(t), std::forward<F>(f)))(std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template <typename B, typename... Ts>
|
||||
constexpr decltype(auto) static_if(Ts&&... ts)
|
||||
{
|
||||
constexpr decltype(auto) static_if(Ts&&... ts) {
|
||||
return static_if_c<B::value>(std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
@ -100,33 +95,29 @@ constexpr decltype(auto) static_if(Ts&&... ts)
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support,
|
||||
(std::declval<T&>().value(), std::declval<T&>().variance()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower,
|
||||
(std::declval<T&>().lower(0)));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, (std::declval<T&>().value(0)));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options,
|
||||
(static_cast<axis::option_type>(std::declval<T&>().options())));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(
|
||||
has_method_options, (static_cast<axis::option_type>(std::declval<T&>().options())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata,
|
||||
(std::declval<T&>().metadata()));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval<T&>().metadata()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_transform, (&T::forward, &T::inverse));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container,
|
||||
(std::declval<T&>()[0], std::declval<T&>().size()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container,
|
||||
(std::get<0>(std::declval<T&>())));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, (std::get<0>(std::declval<T&>())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int,
|
||||
(static_cast<int>(std::declval<T&>())));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, (static_cast<int>(std::declval<T&>())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_equal_comparable,
|
||||
(std::declval<T&>() == std::declval<T&>()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_axis,
|
||||
(std::declval<T&>().size(), &T::operator()));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_axis, (std::declval<T&>().size(), &T::operator()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable,
|
||||
(std::begin(std::declval<T&>()),
|
||||
std::end(std::declval<T&>())));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable, (std::begin(std::declval<T&>()),
|
||||
std::end(std::declval<T&>())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_streamable,
|
||||
(std::declval<std::ostream&>() << std::declval<T&>()));
|
||||
@ -137,16 +128,14 @@ struct is_axis_variant_impl : std::false_type {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct is_axis_variant_impl<axis::variant<Ts...>> : std::true_type {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename T>
|
||||
using is_axis_variant = typename is_axis_variant_impl<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using is_axis_vector = mp11::mp_all<
|
||||
is_random_access_container<T>,
|
||||
is_axis<container_element_type<rm_cvref<T>>>
|
||||
>;
|
||||
using is_axis_vector = mp11::mp_all<is_random_access_container<T>,
|
||||
is_axis<container_element_type<rm_cvref<T>>>>;
|
||||
|
||||
struct static_container_tag {};
|
||||
struct iterable_container_tag {};
|
||||
@ -155,8 +144,8 @@ struct no_container_tag {};
|
||||
template <typename T>
|
||||
using classify_container = typename std::conditional<
|
||||
is_iterable<T>::value, iterable_container_tag,
|
||||
typename std::conditional<is_static_container<T>::value,
|
||||
static_container_tag, no_container_tag>::type>::type;
|
||||
typename std::conditional<is_static_container<T>::value, static_container_tag,
|
||||
no_container_tag>::type>::type;
|
||||
|
||||
namespace {
|
||||
struct bool_mask_impl {
|
||||
@ -167,7 +156,7 @@ struct bool_mask_impl {
|
||||
b[Int::value] = v;
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename... Ns>
|
||||
std::vector<bool> bool_mask(unsigned n, bool v) {
|
||||
@ -191,26 +180,26 @@ struct requires_iterable {};
|
||||
template <typename T, typename = mp11::mp_if<is_static_container<T>, void>>
|
||||
struct requires_static_container {};
|
||||
|
||||
template <typename T,
|
||||
typename = mp11::mp_if<is_axis<T>, void>>
|
||||
template <typename T, typename = mp11::mp_if<is_axis<T>, void>>
|
||||
struct requires_axis {};
|
||||
|
||||
template <typename T,
|
||||
typename = mp11::mp_if_c<(is_axis<T>::value ||
|
||||
is_axis_variant<T>::value), void>>
|
||||
typename =
|
||||
mp11::mp_if_c<(is_axis<T>::value || is_axis_variant<T>::value), void>>
|
||||
struct requires_axis_or_axis_variant {};
|
||||
|
||||
template <typename T,
|
||||
typename U = container_element_type<T>,
|
||||
template <typename T, typename U = container_element_type<T>,
|
||||
typename = mp11::mp_if_c<(is_random_access_container<T>::value &&
|
||||
(is_axis<U>::value ||
|
||||
is_axis_variant<U>::value)), void>>
|
||||
(is_axis<U>::value || is_axis_variant<U>::value)),
|
||||
void>>
|
||||
struct requires_axis_vector {};
|
||||
|
||||
template <typename T, typename U,
|
||||
typename = mp11::mp_if<std::is_same<T, U>, void>>
|
||||
template <typename T, typename U, typename = mp11::mp_if<std::is_same<T, U>, void>>
|
||||
struct requires_same {};
|
||||
|
||||
template <typename T, typename = mp11::mp_if<is_transform<T>, void>>
|
||||
struct requires_transform {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
@ -10,11 +10,6 @@
|
||||
#include <memory> // for std::allocator
|
||||
#include <string>
|
||||
|
||||
namespace std {
|
||||
template <typename T>
|
||||
class allocator;
|
||||
}
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
@ -35,21 +30,20 @@ template <typename T = double>
|
||||
struct sqrt;
|
||||
template <typename T = double>
|
||||
struct pow;
|
||||
template <typename Q, typename U>
|
||||
struct quantity;
|
||||
} // namespace transform
|
||||
|
||||
template <typename Transform = transform::identity<>, typename MetaData = std::string>
|
||||
template <typename Transform = transform::identity<double>,
|
||||
typename MetaData = std::string>
|
||||
class regular;
|
||||
|
||||
template <typename T = double, typename MetaData = std::string>
|
||||
template <typename RealType = double, typename MetaData = std::string>
|
||||
class circular;
|
||||
|
||||
template <typename T = double, typename Allocator = std::allocator<T>,
|
||||
template <typename RealType = double, typename Allocator = std::allocator<RealType>,
|
||||
typename MetaData = std::string>
|
||||
class variable;
|
||||
|
||||
template <typename T = int, typename MetaData = std::string>
|
||||
template <typename IntType = int, typename MetaData = std::string>
|
||||
class integer;
|
||||
|
||||
template <typename T = int, typename Allocator = std::allocator<T>,
|
||||
|
@ -8,7 +8,11 @@
|
||||
#define BOOST_HISTOGRAM_SERIALIZATION_HPP
|
||||
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
@ -33,7 +37,7 @@ template <class Archive, typename... Ts>
|
||||
void serialize(Archive& ar, tuple<Ts...>& t, unsigned /* version */) {
|
||||
::boost::mp11::tuple_for_each(t, [&ar](auto& x) { ar& x; });
|
||||
}
|
||||
} // namespace std
|
||||
} // namespace std
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
@ -62,19 +66,15 @@ struct serializer {
|
||||
|
||||
template <typename Buffer, typename Archive>
|
||||
void operator()(void*, Buffer& b, Archive&) {
|
||||
if (Archive::is_loading::value) {
|
||||
b.ptr = nullptr;
|
||||
}
|
||||
if (Archive::is_loading::value) { b.ptr = nullptr; }
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
template <class Archive, typename A>
|
||||
void serialize(Archive& ar, adaptive_storage<A>& s, unsigned /* version */) {
|
||||
using S = adaptive_storage<A>;
|
||||
if (Archive::is_loading::value) {
|
||||
S::apply(typename S::destroyer(), s.buffer);
|
||||
}
|
||||
if (Archive::is_loading::value) { S::apply(typename S::destroyer(), s.buffer); }
|
||||
ar& s.buffer.type;
|
||||
ar& s.buffer.size;
|
||||
S::apply(detail::serializer(), s.buffer, ar);
|
||||
@ -89,15 +89,14 @@ void histogram<A, S>::serialize(Archive& ar, unsigned /* version */) {
|
||||
|
||||
namespace axis {
|
||||
template <class Archive>
|
||||
void serialize(Archive&, empty_metadata_type&, unsigned /* version */) {
|
||||
} // noop
|
||||
void serialize(Archive&, empty_metadata_type&, unsigned /* version */) {} // noop
|
||||
|
||||
template <typename M>
|
||||
template <class Archive>
|
||||
void base<M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& metadata();
|
||||
ar& data_.size;
|
||||
ar& data_.opt;
|
||||
ar& size_meta_.first();
|
||||
ar& size_meta_.second();
|
||||
ar& opt_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -106,25 +105,19 @@ void transform::pow<T>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& power;
|
||||
}
|
||||
|
||||
template <typename Q, typename U>
|
||||
template <class Archive>
|
||||
void transform::quantity<Q, U>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& unit;
|
||||
}
|
||||
|
||||
template <typename T, typename M>
|
||||
template <class Archive>
|
||||
void regular<T, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<base_type>(*this);
|
||||
ar& boost::serialization::base_object<T>(data_);
|
||||
ar& data_.min;
|
||||
ar& data_.delta;
|
||||
ar& static_cast<base_type&>(*this);
|
||||
ar& static_cast<transform_type&>(*this);
|
||||
ar& min_;
|
||||
ar& delta_;
|
||||
}
|
||||
|
||||
template <typename R, typename M>
|
||||
template <class Archive>
|
||||
void circular<R, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<base_type>(*this);
|
||||
ar& static_cast<base_type&>(*this);
|
||||
ar& phase_;
|
||||
ar& delta_;
|
||||
}
|
||||
@ -133,17 +126,17 @@ template <typename R, typename A, typename M>
|
||||
template <class Archive>
|
||||
void variable<R, A, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
// destroy must happen before base serialization with old size
|
||||
if (Archive::is_loading::value) detail::destroy_buffer(data_, data_.x, nx());
|
||||
ar& boost::serialization::base_object<base_type>(*this);
|
||||
if (Archive::is_loading::value) detail::destroy_buffer(x_.second(), x_.first(), nx());
|
||||
ar& static_cast<base_type&>(*this);
|
||||
if (Archive::is_loading::value)
|
||||
data_.x = boost::histogram::detail::create_buffer(data_, nx());
|
||||
ar& boost::serialization::make_array(data_.x, nx());
|
||||
x_.first() = boost::histogram::detail::create_buffer(x_.second(), nx());
|
||||
ar& boost::serialization::make_array(x_.first(), nx());
|
||||
}
|
||||
|
||||
template <typename I, typename M>
|
||||
template <class Archive>
|
||||
void integer<I, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<base_type>(*this);
|
||||
ar& static_cast<base_type&>(*this);
|
||||
ar& min_;
|
||||
}
|
||||
|
||||
@ -152,11 +145,11 @@ template <class Archive>
|
||||
void category<V, A, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
// destroy must happen before base serialization with old size
|
||||
if (Archive::is_loading::value)
|
||||
detail::destroy_buffer(data_, data_.x, base_type::size());
|
||||
ar& boost::serialization::base_object<base_type>(*this);
|
||||
detail::destroy_buffer(x_.second(), x_.first(), base_type::size());
|
||||
ar& static_cast<base_type&>(*this);
|
||||
if (Archive::is_loading::value)
|
||||
data_.x = boost::histogram::detail::create_buffer(data_, base_type::size());
|
||||
ar& boost::serialization::make_array(data_.x, base_type::size());
|
||||
x_.first() = boost::histogram::detail::create_buffer(x_.second(), base_type::size());
|
||||
ar& boost::serialization::make_array(x_.first(), base_type::size());
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
@ -164,9 +157,9 @@ template <class Archive>
|
||||
void variant<Ts...>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& static_cast<base_type&>(*this);
|
||||
}
|
||||
} // namespace axis
|
||||
} // namespace axis
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
|
@ -7,12 +7,18 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/units/quantity.hpp>
|
||||
#include <boost/units/systems/si/length.hpp>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@ -38,38 +44,10 @@ void test_axis_iterator(const Axis& a, int begin, int end) {
|
||||
}
|
||||
}
|
||||
|
||||
// quantity with unit for testing
|
||||
template <typename Unit>
|
||||
struct quantity {
|
||||
double value;
|
||||
};
|
||||
|
||||
struct length {
|
||||
double value;
|
||||
};
|
||||
|
||||
auto meter = length{1.0};
|
||||
|
||||
template <typename Unit>
|
||||
double operator/(const quantity<Unit>& a, const Unit& u) {
|
||||
return a.value / u.value;
|
||||
}
|
||||
|
||||
template <typename Unit>
|
||||
quantity<Unit> operator*(double x, const Unit& u) {
|
||||
return quantity<Unit>{x * u.value};
|
||||
}
|
||||
|
||||
template <typename Unit>
|
||||
quantity<Unit> operator-(const quantity<Unit>& a, const quantity<Unit>& b) {
|
||||
return quantity<Unit>{a.value - b.value};
|
||||
}
|
||||
|
||||
int main() {
|
||||
// bad_ctors
|
||||
{
|
||||
BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument);
|
||||
// BOOST_TEST_THROWS(axis::regular<>(1, 1, -1), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(axis::variable<>(std::vector<double>()), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument);
|
||||
@ -83,16 +61,15 @@ int main() {
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
axis::regular<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::regular<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
axis::regular<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a(-10.), -1);
|
||||
@ -110,15 +87,21 @@ int main() {
|
||||
|
||||
// regular axis with inverted range
|
||||
{
|
||||
axis::regular<> a(2, 1, -1);
|
||||
axis::regular<> a{2, 1, -2};
|
||||
BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[0].lower(), 1);
|
||||
BOOST_TEST_EQ(a[1].lower(), 0);
|
||||
BOOST_TEST_EQ(a[2].lower(), -1);
|
||||
BOOST_TEST_EQ(a[1].lower(), -0.5);
|
||||
BOOST_TEST_EQ(a[2].lower(), -2);
|
||||
BOOST_TEST_EQ(a[2].upper(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a(2), -1);
|
||||
BOOST_TEST_EQ(a(1.001), -1);
|
||||
BOOST_TEST_EQ(a(1), 0);
|
||||
BOOST_TEST_EQ(a(0), 1);
|
||||
BOOST_TEST_EQ(a(-1), 2);
|
||||
BOOST_TEST_EQ(a(0), 0);
|
||||
BOOST_TEST_EQ(a(-0.499), 0);
|
||||
BOOST_TEST_EQ(a(-0.5), 1);
|
||||
BOOST_TEST_EQ(a(-1), 1);
|
||||
BOOST_TEST_EQ(a(-2), 2);
|
||||
BOOST_TEST_EQ(a(-20), 2);
|
||||
}
|
||||
|
||||
// axis::regular with log transform
|
||||
@ -160,25 +143,27 @@ int main() {
|
||||
BOOST_TEST_EQ(b(std::numeric_limits<double>::infinity()), 2);
|
||||
}
|
||||
|
||||
// axis::regular with quantity transform
|
||||
// axis::regular with quantity
|
||||
{
|
||||
axis::regular<axis::transform::quantity<quantity<length>, length>> b{
|
||||
2, 0*meter, 2*meter, {}, axis::option_type::underflow_and_overflow, meter
|
||||
};
|
||||
BOOST_TEST_EQ(b[-1].lower()/meter, -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_IS_CLOSE(b[0].lower()/meter, 0.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[1].lower()/meter, 1.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[2].lower()/meter, 2.0, 1e-9);
|
||||
BOOST_TEST_EQ(b[2].upper()/meter, std::numeric_limits<double>::infinity());
|
||||
using namespace boost::units;
|
||||
using namespace boost::units::si;
|
||||
using Q = quantity<length>;
|
||||
|
||||
BOOST_TEST_EQ(b(-1*meter), -1); // produces NaN in conversion
|
||||
BOOST_TEST_EQ(b(0*meter), 0);
|
||||
BOOST_TEST_EQ(b(0.99*meter), 0);
|
||||
BOOST_TEST_EQ(b(1*meter), 1);
|
||||
BOOST_TEST_EQ(b(1.99*meter), 1);
|
||||
BOOST_TEST_EQ(b(2*meter), 2);
|
||||
BOOST_TEST_EQ(b(100*meter), 2);
|
||||
BOOST_TEST_EQ(b(std::numeric_limits<double>::infinity()*meter), 2);
|
||||
// axis::regular<axis::transform::identity<Q>> b(2, 0 * meter, 2 * meter);
|
||||
// BOOST_TEST_EQ(b[-1].lower() / meter, -std::numeric_limits<double>::infinity());
|
||||
// BOOST_TEST_IS_CLOSE(b[0].lower() / meter, 0.0, 1e-9);
|
||||
// BOOST_TEST_IS_CLOSE(b[1].lower() / meter, 1.0, 1e-9);
|
||||
// BOOST_TEST_IS_CLOSE(b[2].lower() / meter, 2.0, 1e-9);
|
||||
// BOOST_TEST_EQ(b[2].upper() / meter, std::numeric_limits<double>::infinity());
|
||||
|
||||
// BOOST_TEST_EQ(b(-1 * meter), -1); // produces NaN in conversion
|
||||
// BOOST_TEST_EQ(b(0 * meter), 0);
|
||||
// BOOST_TEST_EQ(b(0.99 * meter), 0);
|
||||
// BOOST_TEST_EQ(b(1 * meter), 1);
|
||||
// BOOST_TEST_EQ(b(1.99 * meter), 1);
|
||||
// BOOST_TEST_EQ(b(2 * meter), 2);
|
||||
// BOOST_TEST_EQ(b(100 * meter), 2);
|
||||
// BOOST_TEST_EQ(b(std::numeric_limits<double>::infinity() * meter), 2);
|
||||
}
|
||||
|
||||
// axis::circular
|
||||
@ -186,16 +171,15 @@ int main() {
|
||||
axis::circular<> a{4, 0, 1};
|
||||
BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1);
|
||||
axis::circular<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::circular<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
axis::circular<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a(-1.0 * 3), 0);
|
||||
@ -215,25 +199,25 @@ int main() {
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
axis::variable<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::variable<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
BOOST_TEST_NE(b, a);
|
||||
axis::variable<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
axis::variable<> e{-2, 0, 2};
|
||||
BOOST_TEST_NOT(a == e);
|
||||
BOOST_TEST_EQ(a(-10.), -1);
|
||||
BOOST_TEST_EQ(a(-1.), 0);
|
||||
BOOST_TEST_EQ(a(0.), 1);
|
||||
BOOST_TEST_EQ(a(1.), 2);
|
||||
BOOST_TEST_EQ(a(10.), 2);
|
||||
BOOST_TEST_NE(a, e);
|
||||
BOOST_TEST_EQ(a(-10), -1);
|
||||
BOOST_TEST_EQ(a(-1), 0);
|
||||
BOOST_TEST_EQ(a(0), 1);
|
||||
BOOST_TEST_EQ(a(1), 2);
|
||||
BOOST_TEST_EQ(a(10), 2);
|
||||
BOOST_TEST_EQ(a(-std::numeric_limits<double>::infinity()), -1);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::infinity()), 2);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::quiet_NaN()), 2);
|
||||
@ -245,16 +229,15 @@ int main() {
|
||||
BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits<int>::min());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<int>::max());
|
||||
axis::integer<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::integer<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
axis::integer<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a(-10), -1);
|
||||
@ -271,19 +254,19 @@ int main() {
|
||||
std::string A("A"), B("B"), C("C"), other;
|
||||
axis::category<std::string> a({A, B, C});
|
||||
axis::category<std::string> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = axis::category<std::string>{{B, A, C}};
|
||||
BOOST_TEST_NOT(a == b);
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::category<std::string> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
BOOST_TEST_NE(b, a);
|
||||
axis::category<std::string> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a.size(), 3);
|
||||
@ -301,13 +284,15 @@ int main() {
|
||||
{
|
||||
enum { A, B, C };
|
||||
test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5);
|
||||
test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5);
|
||||
test_axis_iterator(
|
||||
axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5);
|
||||
test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5);
|
||||
test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2);
|
||||
test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4);
|
||||
test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3);
|
||||
test_axis_iterator(axis::variant<axis::regular<>>(axis::regular<>(5, 0, 1)), 0, 5);
|
||||
// BOOST_TEST_THROWS(axis::variant<axis::category<>>(axis::category<>({A, B, C}))[0].lower(),
|
||||
// BOOST_TEST_THROWS(axis::variant<axis::category<>>(axis::category<>({A, B,
|
||||
// C}))[0].lower(),
|
||||
// std::runtime_error);
|
||||
}
|
||||
|
||||
@ -355,14 +340,15 @@ int main() {
|
||||
|
||||
struct user_defined {};
|
||||
|
||||
test(axis::regular<>{2, -1, 1, "regular1"},
|
||||
namespace tr = axis::transform;
|
||||
test(axis::regular<>(2, -1, 1, "regular1"),
|
||||
"regular(2, -1, 1, metadata=\"regular1\", options=underflow_and_overflow)");
|
||||
test(axis::regular<axis::transform::log<>>(2, 1, 10, "regular2", axis::option_type::none),
|
||||
test(axis::regular<tr::log<>>(2, 1, 10, "regular2", axis::option_type::none),
|
||||
"regular_log(2, 1, 10, metadata=\"regular2\", options=none)");
|
||||
test(axis::regular<axis::transform::pow<>>(2, 1, 10, "regular3", axis::option_type::overflow, 0.5),
|
||||
"regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=0.5)");
|
||||
test(axis::regular<axis::transform::pow<>>(2, 1, 10, "regular4", axis::option_type::none, -0.5),
|
||||
"regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-0.5)");
|
||||
test(axis::regular<tr::pow<>>(1.5, 2, 1, 10, "regular3", axis::option_type::overflow),
|
||||
"regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=1.5)");
|
||||
test(axis::regular<tr::pow<>>(-1.5, 2, 1, 10, "regular4", axis::option_type::none),
|
||||
"regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-1.5)");
|
||||
test(axis::circular<double, axis::empty_metadata_type>(4, 0.1, 1.0),
|
||||
"circular(4, 0.1, 1.1, options=overflow)");
|
||||
test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none),
|
||||
@ -387,8 +373,9 @@ int main() {
|
||||
BOOST_TEST_EQ(axis(10), 0);
|
||||
BOOST_TEST_EQ(axis.size(), 1);
|
||||
BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error);
|
||||
BOOST_TEST_THROWS(axis.lower(0), std::runtime_error);
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<decltype(axis.metadata()), axis::empty_metadata_type&>));
|
||||
BOOST_TEST_THROWS(axis.value(0), std::runtime_error);
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<decltype(axis.metadata()), axis::empty_metadata_type&>));
|
||||
}
|
||||
|
||||
// bin_type streamable
|
||||
@ -409,18 +396,13 @@ int main() {
|
||||
// axis::variant equal_comparable
|
||||
{
|
||||
enum { A, B, C };
|
||||
using variant = axis::variant<
|
||||
axis::regular<>,
|
||||
axis::regular<axis::transform::pow<>>,
|
||||
axis::circular<>,
|
||||
axis::variable<>,
|
||||
axis::category<>,
|
||||
axis::integer<>
|
||||
>;
|
||||
using variant = axis::variant<axis::regular<>, axis::regular<axis::transform::pow<>>,
|
||||
axis::circular<>, axis::variable<>, axis::category<>,
|
||||
axis::integer<>>;
|
||||
std::vector<variant> axes;
|
||||
axes.push_back(axis::regular<>{2, -1, 1});
|
||||
axes.push_back(
|
||||
axis::regular<axis::transform::pow<>>(2, 1, 4, "", axis::option_type::underflow_and_overflow, 0.5));
|
||||
axes.push_back(axis::regular<axis::transform::pow<>>(
|
||||
0.5, 2, 1, 4, "", axis::option_type::underflow_and_overflow));
|
||||
axes.push_back(axis::circular<>{4});
|
||||
axes.push_back(axis::variable<>{-1, 0, 1});
|
||||
axes.push_back(axis::category<>({A, B, C}));
|
||||
@ -435,16 +417,17 @@ int main() {
|
||||
|
||||
// axis::variant value_to_index_failure
|
||||
{
|
||||
axis::variant<axis::category<std::string>> x = axis::category<std::string>({"A", "B"}, "category");
|
||||
axis::variant<axis::category<std::string>> x =
|
||||
axis::category<std::string>({"A", "B"}, "category");
|
||||
auto cx = axis::get<axis::category<std::string>>(x);
|
||||
// BOOST_TEST_EQ(cx(b), 1);
|
||||
BOOST_TEST_EQ(cx("B"), 1);
|
||||
}
|
||||
|
||||
// sequence equality
|
||||
{
|
||||
enum { A, B, C };
|
||||
std::vector<
|
||||
axis::variant<axis::regular<>, axis::variable<>, axis::category<>, axis::integer<>>>
|
||||
std::vector<axis::variant<axis::regular<>, axis::variable<>, axis::category<>,
|
||||
axis::integer<>>>
|
||||
std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B, C}};
|
||||
|
||||
@ -480,8 +463,8 @@ int main() {
|
||||
// sequence assign
|
||||
{
|
||||
enum { A, B, C, D };
|
||||
std::vector<
|
||||
axis::variant<axis::regular<>, axis::variable<>, axis::category<>, axis::integer<>>>
|
||||
std::vector<axis::variant<axis::regular<>, axis::variable<>, axis::category<>,
|
||||
axis::integer<>>>
|
||||
std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B, C}};
|
||||
|
||||
@ -561,7 +544,8 @@ int main() {
|
||||
axes.reserve(5);
|
||||
axes.emplace_back(T1(1, 0, 1, M(3, 'c', a)));
|
||||
axes.emplace_back(T2(2));
|
||||
axes.emplace_back(T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a));
|
||||
axes.emplace_back(
|
||||
T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a));
|
||||
axes.emplace_back(T4(0, 4));
|
||||
axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a));
|
||||
}
|
||||
|
@ -52,9 +52,8 @@ int main() {
|
||||
|
||||
// axis methods
|
||||
{
|
||||
enum { A, B };
|
||||
auto c = make(dynamic_tag(), axis::category<>({A, B}));
|
||||
BOOST_TEST_THROWS(c.axis().lower(0), std::runtime_error);
|
||||
auto c = make(dynamic_tag(), axis::category<std::string>({"A", "B"}));
|
||||
BOOST_TEST_THROWS(c.axis().value(0), std::runtime_error);
|
||||
}
|
||||
|
||||
// reduce
|
||||
@ -88,7 +87,8 @@ int main() {
|
||||
|
||||
// histogram iterator
|
||||
{
|
||||
auto h = make_s(dynamic_tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 3));
|
||||
auto h =
|
||||
make_s(dynamic_tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 3));
|
||||
const auto& a = h.axis();
|
||||
h(weight(2), 0);
|
||||
h(1);
|
||||
|
@ -4,15 +4,15 @@
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/archive/text_iarchive.hpp>
|
||||
#include <boost/archive/text_oarchive.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/histogram/literals.hpp>
|
||||
#include <boost/archive/text_iarchive.hpp>
|
||||
#include <boost/archive/text_oarchive.hpp>
|
||||
#include <boost/histogram/serialization.hpp>
|
||||
#include <boost/histogram/ostream_operators.hpp>
|
||||
#include <string>
|
||||
#include <boost/histogram/serialization.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
@ -21,13 +21,14 @@ template <typename Tag>
|
||||
void run_tests() {
|
||||
// histogram_serialization
|
||||
{
|
||||
auto a = make(
|
||||
Tag(), axis::regular<>(3, -1, 1, "axis 0"), axis::circular<>(4, 0.0, 1.0, "axis 1"),
|
||||
axis::regular<axis::transform::log<>>(3, 1, 100, "axis 2"),
|
||||
axis::regular<axis::transform::pow<>>(3, 1, 100, "axis 3", axis::option_type::overflow, 0.5),
|
||||
axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"),
|
||||
axis::category<>{3, 1, 2},
|
||||
axis::integer<int, axis::empty_metadata_type>(0, 2));
|
||||
auto a = make(Tag(), axis::regular<>(3, -1, 1, "axis 0"),
|
||||
axis::circular<>(4, 0.0, 1.0, "axis 1"),
|
||||
axis::regular<axis::transform::log<>>(3, 1, 100, "axis 2"),
|
||||
axis::regular<axis::transform::pow<>>(0.5, 3, 1, 100, "axis 3",
|
||||
axis::option_type::overflow),
|
||||
axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"),
|
||||
axis::category<>{3, 1, 2},
|
||||
axis::integer<int, axis::empty_metadata_type>(0, 2));
|
||||
a(0.5, 0.2, 20, 20, 0.25, 1, 1);
|
||||
std::string buf;
|
||||
{
|
||||
|
@ -6,16 +6,16 @@
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/literals.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
@ -64,21 +64,27 @@ int main() {
|
||||
BOOST_TEST_TRAIT_TRUE((has_variance_support<D>));
|
||||
}
|
||||
|
||||
// has_method_lower
|
||||
// has_method_value
|
||||
{
|
||||
struct A {};
|
||||
struct B { void lower(int) {} };
|
||||
struct B {
|
||||
void value(int) {}
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_lower<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_lower<B>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value<B>));
|
||||
}
|
||||
|
||||
// has_method_options
|
||||
{
|
||||
struct NotOptions {};
|
||||
struct A {};
|
||||
struct B { NotOptions options(); };
|
||||
struct C { bh::axis::option_type options(); };
|
||||
struct B {
|
||||
NotOptions options();
|
||||
};
|
||||
struct C {
|
||||
bh::axis::option_type options();
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_options<A>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_options<B>));
|
||||
@ -88,16 +94,32 @@ int main() {
|
||||
// has_method_metadata
|
||||
{
|
||||
struct A {};
|
||||
struct B { void metadata(); };
|
||||
struct B {
|
||||
void metadata();
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_metadata<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_metadata<B>));
|
||||
}
|
||||
|
||||
// is_transform
|
||||
{
|
||||
struct A {};
|
||||
struct B {
|
||||
double forward(double);
|
||||
double inverse(double);
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_transform<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_transform<B>));
|
||||
}
|
||||
|
||||
// is_equal_comparable
|
||||
{
|
||||
struct A {};
|
||||
struct B { bool operator==(const B&); };
|
||||
struct B {
|
||||
bool operator==(const B&);
|
||||
};
|
||||
BOOST_TEST_TRAIT_TRUE((is_equal_comparable<int>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_equal_comparable<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_equal_comparable<B>));
|
||||
@ -106,9 +128,16 @@ int main() {
|
||||
// is_axis
|
||||
{
|
||||
struct A {};
|
||||
struct B { int operator()(double); unsigned size() const; };
|
||||
struct C { int operator()(double); };
|
||||
struct D { unsigned size(); };
|
||||
struct B {
|
||||
int operator()(double);
|
||||
unsigned size() const;
|
||||
};
|
||||
struct C {
|
||||
int operator()(double);
|
||||
};
|
||||
struct D {
|
||||
unsigned size();
|
||||
};
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_axis<B>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis<C>));
|
||||
@ -266,8 +295,10 @@ int main() {
|
||||
int f2(long) const;
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<args_type<decltype(&Foo::f1)>, std::tuple<char>>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<args_type<decltype(&Foo::f2)>, std::tuple<long>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<args_type<decltype(&Foo::f1)>, std::tuple<char>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<args_type<decltype(&Foo::f2)>, std::tuple<long>>));
|
||||
}
|
||||
|
||||
// visitor_return_type
|
||||
@ -275,19 +306,23 @@ int main() {
|
||||
using V1 = bh::axis::variant<char>;
|
||||
using V2 = bh::axis::variant<int>&;
|
||||
using V3 = const bh::axis::variant<long>&;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<visitor_return_type<VisitorTestFunctor, V1>, char>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<visitor_return_type<VisitorTestFunctor, V2>, int&>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<visitor_return_type<VisitorTestFunctor, V3>, const long&>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<visitor_return_type<VisitorTestFunctor, V1>, char>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<visitor_return_type<VisitorTestFunctor, V2>, int&>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<visitor_return_type<VisitorTestFunctor, V3>, const long&>));
|
||||
}
|
||||
|
||||
// static_if
|
||||
{
|
||||
struct callable { int operator()() { return 1; }; };
|
||||
struct callable {
|
||||
int operator()() { return 1; };
|
||||
};
|
||||
struct not_callable {};
|
||||
auto fcn = [](auto b, auto x) {
|
||||
return static_if<decltype(b)>(
|
||||
[](auto x) { return x(); },
|
||||
[](auto) { return 2; }, x);
|
||||
return static_if<decltype(b)>([](auto x) { return x(); }, [](auto) { return 2; },
|
||||
x);
|
||||
};
|
||||
BOOST_TEST_EQ(fcn(std::true_type(), callable()), 1);
|
||||
BOOST_TEST_EQ(fcn(std::false_type(), not_callable()), 2);
|
||||
|
Loading…
x
Reference in New Issue
Block a user