refactoring axis types into separate functions, make them use compressed_pair instead of crappy custom empty base optimization

This commit is contained in:
Hans Dembinski 2018-10-26 00:23:41 +02:00
parent 22745ca571
commit b8d158e94e
22 changed files with 1264 additions and 1156 deletions

View File

@ -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);
}
//]

View File

@ -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>

View File

@ -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

View 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

View 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

View 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

View File

@ -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_; }

View File

@ -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

View File

@ -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());

View 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

View File

@ -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

View File

@ -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_; }

View 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

View File

@ -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);

View File

@ -0,0 +1,74 @@
// Copyright 2018 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
#define BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
template <typename T1, typename T2, bool B>
class compressed_pair_impl;
template <typename T1, typename T2>
class compressed_pair_impl<T1, T2, true> : protected T2 {
public:
template <typename U1, typename U2>
compressed_pair_impl(U1&& u1, U2&& u2)
: T2(std::forward<U2>(u2)), first_(std::forward<U1>(u1)) {}
template <typename U1>
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
compressed_pair_impl() = default;
T1& first() { return first_; }
T2& second() { return static_cast<T2&>(*this); }
const T1& first() const { return first_; }
const T2& second() const { return static_cast<const T2&>(*this); }
private:
T1 first_;
};
template <typename T1, typename T2>
class compressed_pair_impl<T1, T2, false> {
public:
template <typename U1, typename U2>
compressed_pair_impl(U1&& u1, U2&& u2)
: first_(std::forward<U1>(u1)), second_(std::forward<U2>(u2)) {}
template <typename U1>
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
compressed_pair_impl() = default;
T1& first() { return first_; }
T2& second() { return second_; }
const T1& first() const { return first_; }
const T2& second() const { return second_; }
private:
T1 first_;
T2 second_;
};
template <typename... Ts>
void swap(compressed_pair_impl<Ts...>& a, compressed_pair_impl<Ts...>& b) {
using std::swap;
swap(a.first(), b.first());
swap(a.second(), b.second());
}
template <typename T1, typename T2>
using compressed_pair =
compressed_pair_impl<T1, T2, (!std::is_final<T2>::value && std::is_empty<T2>::value)>;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@ -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

View File

@ -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>,

View File

@ -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

View File

@ -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));
}

View File

@ -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);

View File

@ -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;
{

View File

@ -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);