add boost on mac

This commit is contained in:
Bassem Girgis
2019-08-10 16:38:17 -05:00
parent 861b918727
commit be945cb63b
14105 changed files with 2714968 additions and 0 deletions

View File

@@ -0,0 +1,184 @@
// 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/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace boost {
namespace histogram {
namespace axis {
/**
Maps at a set of unique values to bin indices.
The axis maps a set of values to bins, following the order of arguments in the
constructor. The optional overflow bin for this axis counts input values that
are not part of the set. Binning has O(N) complexity, but with a very small
factor. For small N (the typical use case) it beats other kinds of lookup.
@tparam Value input value type, must be equal-comparable.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option.
@tparam Allocator allocator to use for dynamic memory management.
The options `underflow` and `circular` are not allowed. The options `growth`
and `overflow` are mutually exclusive.
*/
template <class Value, class MetaData, class Options, class Allocator>
class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>> {
using value_type = Value;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type = detail::replace_default<Options, option::overflow_t>;
static_assert(!options_type::test(option::underflow),
"category axis cannot have underflow");
static_assert(!options_type::test(option::circular),
"category axis cannot be circular");
static_assert(!options_type::test(option::growth) ||
!options_type::test(option::overflow),
"growing category axis cannot have overflow");
using allocator_type = Allocator;
using vector_type = std::vector<value_type, allocator_type>;
public:
explicit category(allocator_type alloc = {}) : vec_meta_(vector_type(alloc)) {}
/** Construct from iterator range of unique values.
*
* \param begin begin of category range of unique values.
* \param end end of category range of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
: vec_meta_(vector_type(begin, end, alloc), std::move(meta)) {
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
}
/** Construct axis from iterable sequence of unique values.
*
* \param iterable sequence of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class C, class = detail::requires_iterable<C>>
category(const C& iterable, metadata_type meta = {}, allocator_type alloc = {})
: category(std::begin(iterable), std::end(iterable), std::move(meta),
std::move(alloc)) {}
/** Construct axis from an initializer list of unique values.
*
* \param list `std::initializer_list` of unique values.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class U>
category(std::initializer_list<U> list, metadata_type meta = {},
allocator_type alloc = {})
: category(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
/// Constructor used by algorithm::reduce to shrink and rebin.
category(const category& src, index_type begin, index_type end, unsigned merge)
: category(src.vec_meta_.first().begin() + begin,
src.vec_meta_.first().begin() + end, src.metadata()) {
if (merge > 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for category axis"));
}
/// Return index for value argument.
index_type index(const value_type& x) const noexcept {
const auto beg = vec_meta_.first().begin();
const auto end = vec_meta_.first().end();
return static_cast<index_type>(std::distance(beg, std::find(beg, end, x)));
}
/// Returns index and shift (if axis has grown) for the passed argument.
auto update(const value_type& x) {
const auto i = index(x);
if (i < size()) return std::make_pair(i, 0);
vec_meta_.first().emplace_back(x);
return std::make_pair(i, -1);
}
/// Return value for index argument.
/// Throws `std::out_of_range` if the index is out of bounds.
decltype(auto) value(index_type idx) const {
if (idx < 0 || idx >= size())
BOOST_THROW_EXCEPTION(std::out_of_range("category index out of range"));
return vec_meta_.first()[idx];
}
/// Return value for index argument.
decltype(auto) bin(index_type idx) const noexcept { return value(idx); }
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept {
return static_cast<index_type>(vec_meta_.first().size());
}
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return vec_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return vec_meta_.second(); }
template <class V, class M, class O, class A>
bool operator==(const category<V, M, O, A>& o) const noexcept {
const auto& a = vec_meta_.first();
const auto& b = o.vec_meta_.first();
return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
detail::relaxed_equal(metadata(), o.metadata());
}
template <class V, class M, class O, class A>
bool operator!=(const category<V, M, O, A>& o) const noexcept {
return !operator==(o);
}
auto get_allocator() const { return vec_meta_.first().get_allocator(); }
template <class Archive>
void serialize(Archive&, unsigned);
private:
detail::compressed_pair<vector_type, metadata_type> vec_meta_;
template <class V, class M, class O, class A>
friend class category;
};
#if __cpp_deduction_guides >= 201606
template <class T>
category(std::initializer_list<T>)->category<T>;
category(std::initializer_list<const char*>)->category<std::string>;
template <class T>
category(std::initializer_list<T>, const char*)->category<T>;
template <class T, class M>
category(std::initializer_list<T>, const M&)->category<T, M>;
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,194 @@
// 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/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
/**
Axis for an interval of integer values with unit steps.
Binning is a O(1) operation. This axis bins faster than a regular axis.
@tparam Value input value type. Must be integer or floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option (all values allowed).
*/
template <class Value, class MetaData, class Options>
class integer : public iterator_mixin<integer<Value, MetaData, Options>> {
static_assert(std::is_integral<Value>::value || std::is_floating_point<Value>::value,
"integer axis requires type floating point or integral type");
using value_type = Value;
using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
index_type, real_index_type>;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type =
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
static_assert(!options_type::test(option::circular) ||
std::is_floating_point<value_type>::value ||
!options_type::test(option::overflow),
"integer axis with integral type cannot have overflow");
public:
constexpr integer() = default;
/** Construct over semi-open integer interval [start, stop).
*
* \param start first integer of covered range.
* \param stop one past last integer of covered range.
* \param meta description of the axis.
*/
integer(value_type start, value_type stop, metadata_type meta = {})
: size_meta_(static_cast<index_type>(stop - start), std::move(meta)), min_(start) {
if (stop <= start) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
}
/// Constructor used by algorithm::reduce to shrink and rebin.
integer(const integer& src, index_type begin, index_type end, unsigned merge)
: integer(src.value(begin), src.value(end), src.metadata()) {
if (merge > 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for integer axis"));
if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
}
/// Return index for value argument.
index_type index(value_type x) const noexcept {
return index_impl(std::is_floating_point<value_type>(), x);
}
/// Returns index and shift (if axis has grown) for the passed argument.
auto update(value_type x) noexcept {
auto impl = [this](long x) {
const auto i = x - min_;
if (i >= 0) {
const auto k = static_cast<axis::index_type>(i);
if (k < size()) return std::make_pair(k, 0);
const auto n = k - size() + 1;
size_meta_.first() += n;
return std::make_pair(k, -n);
}
const auto k = static_cast<axis::index_type>(
detail::static_if<std::is_floating_point<value_type>>(
[](auto x) { return std::floor(x); }, [](auto x) { return x; }, i));
min_ += k;
size_meta_.first() -= k;
return std::make_pair(0, -k);
};
return detail::static_if<std::is_floating_point<value_type>>(
[this, impl](auto x) {
if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x)));
// this->size() is workaround for gcc-5 bug
return std::make_pair(x < 0 ? -1 : this->size(), 0);
},
impl, x);
}
/// Return value for index argument.
value_type value(local_index_type i) const noexcept {
if (!options_type::test(option::circular)) {
if (i < 0) return detail::lowest<value_type>();
if (i > size()) { return detail::highest<value_type>(); }
}
return min_ + i;
}
/// Return bin for index argument.
decltype(auto) bin(index_type idx) const noexcept {
return detail::static_if<std::is_floating_point<local_index_type>>(
[this](auto idx) { return interval_view<integer>(*this, idx); },
[this](auto idx) { return this->value(idx); }, idx);
}
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept { return size_meta_.first(); }
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return size_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
template <class V, class M, class O>
bool operator==(const integer<V, M, O>& o) const noexcept {
return size() == o.size() && detail::relaxed_equal(metadata(), o.metadata()) &&
min_ == o.min_;
}
template <class V, class M, class O>
bool operator!=(const integer<V, M, O>& o) const noexcept {
return !operator==(o);
}
template <class Archive>
void serialize(Archive&, unsigned);
private:
index_type index_impl(std::false_type, int x) const noexcept {
const auto z = x - min_;
if (options_type::test(option::circular))
return static_cast<index_type>(z - std::floor(float(z) / size()) * size());
if (z < size()) return z >= 0 ? z : -1;
return size();
}
template <typename T>
index_type index_impl(std::true_type, T x) const noexcept {
// need to handle NaN, cannot simply cast to int and call int-implementation
const auto z = x - min_;
if (options_type::test(option::circular)) {
if (std::isfinite(z))
return static_cast<index_type>(std::floor(z) - std::floor(z / size()) * size());
} else if (z < size()) {
return z >= 0 ? static_cast<index_type>(z) : -1;
}
return size();
}
detail::compressed_pair<index_type, metadata_type> size_meta_{0};
value_type min_{0};
template <class V, class M, class O>
friend class integer;
};
#if __cpp_deduction_guides >= 201606
template <class T>
integer(T, T)->integer<detail::convert_integer<T, index_type>>;
template <class T>
integer(T, T, const char*)->integer<detail::convert_integer<T, index_type>>;
template <class T, class M>
integer(T, T, M)->integer<detail::convert_integer<T, index_type>, M>;
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,54 @@
// Copyright 2015-2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
#define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
namespace boost {
namespace histogram {
namespace axis {
/**
Lightweight bin view.
Represents the current bin interval.
*/
template <typename Axis>
class interval_view {
public:
interval_view(const Axis& axis, int idx) : axis_(axis), idx_(idx) {}
// avoid viewing a temporary that goes out of scope
interval_view(Axis&& axis, int idx) = delete;
/// Return lower edge of bin.
decltype(auto) lower() const noexcept { return axis_.value(idx_); }
/// Return upper edge of bin.
decltype(auto) upper() const noexcept { return axis_.value(idx_ + 1); }
/// Return center of bin.
decltype(auto) center() const noexcept { return axis_.value(idx_ + 0.5); }
/// Return width of bin.
decltype(auto) width() const noexcept { return upper() - lower(); }
template <typename BinType>
bool operator==(const BinType& rhs) const noexcept {
return lower() == rhs.lower() && upper() == rhs.upper();
}
template <typename BinType>
bool operator!=(const BinType& rhs) const noexcept {
return !operator==(rhs);
}
private:
const Axis& axis_;
const int idx_;
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,74 @@
// Copyright 2015-2017 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_ITERATOR_HPP
#define BOOST_HISTOGRAM_AXIS_ITERATOR_HPP
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/reverse_iterator.hpp>
namespace boost {
namespace histogram {
namespace axis {
template <typename Axis>
class iterator
: public boost::iterator_adaptor<iterator<Axis>, int,
decltype(std::declval<const Axis&>().bin(0)),
std::random_access_iterator_tag,
decltype(std::declval<const Axis&>().bin(0)), int> {
public:
explicit iterator(const Axis& axis, int idx)
: iterator::iterator_adaptor_(idx), axis_(axis) {}
protected:
bool equal(const iterator& other) const noexcept {
return &axis_ == &other.axis_ && this->base() == other.base();
}
decltype(auto) dereference() const { return axis_.bin(this->base()); }
private:
const Axis& axis_;
friend class boost::iterator_core_access;
};
/// Uses CRTP to inject iterator logic into Derived.
template <typename Derived>
class iterator_mixin {
public:
using const_iterator = iterator<Derived>;
using const_reverse_iterator = boost::reverse_iterator<const_iterator>;
/// Bin iterator to beginning of the axis (read-only).
const_iterator begin() const noexcept {
return const_iterator(*static_cast<const Derived*>(this), 0);
}
/// Bin iterator to the end of the axis (read-only).
const_iterator end() const noexcept {
return const_iterator(*static_cast<const Derived*>(this),
static_cast<const Derived*>(this)->size());
}
/// Reverse bin iterator to the last entry of the axis (read-only).
const_reverse_iterator rbegin() const noexcept {
return boost::make_reverse_iterator(end());
}
/// Reverse bin iterator to the end (read-only).
const_reverse_iterator rend() const noexcept {
return boost::make_reverse_iterator(begin());
}
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,82 @@
// Copyright 2015-2019 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_OPTION_HPP
#define BOOST_HISTOGRAM_AXIS_OPTION_HPP
#include <boost/mp11.hpp>
#include <type_traits>
/**
\file option.hpp Options for builtin axis types.
Options `circular` and `growth` are mutually exclusive.
Options `circular` and `underflow` are mutually exclusive.
*/
namespace boost {
namespace histogram {
namespace axis {
namespace option {
/// Holder of axis options.
template <unsigned Bits>
struct bitset : std::integral_constant<unsigned, Bits> {
/// Returns true if all option flags in the argument are set and false otherwise.
template <unsigned B>
static constexpr auto test(bitset<B>) {
return std::integral_constant<bool, static_cast<bool>(Bits & B)>{};
}
};
/// Set union of the axis option arguments.
template <unsigned B1, unsigned B2>
constexpr auto operator|(bitset<B1>, bitset<B2>) {
return bitset<(B1 | B2)>{};
}
/// Set intersection of the option arguments.
template <unsigned B1, unsigned B2>
constexpr auto operator&(bitset<B1>, bitset<B2>) {
return bitset<(B1 & B2)>{};
}
/// Set difference of the option arguments.
template <unsigned B1, unsigned B2>
constexpr auto operator-(bitset<B1>, bitset<B2>) {
return bitset<(B1 & ~B2)>{};
}
/**
Single option flag.
@tparam Pos position of the bit in the set.
*/
template <unsigned Pos>
struct bit : bitset<(1 << Pos)> {};
/// All options off.
using none_t = bitset<0>;
constexpr none_t none{}; ///< Instance of `none_t`.
/// Axis has an underflow bin. Mutually exclusive with `circular`.
using underflow_t = bit<0>;
constexpr underflow_t underflow{}; ///< Instance of `underflow_t`.
/// Axis has overflow bin.
using overflow_t = bit<1>;
constexpr overflow_t overflow{}; ///< Instance of `overflow_t`.
/// Axis is circular. Mutually exclusive with `growth` and `underflow`.
using circular_t = bit<2>;
constexpr circular_t circular{}; ///< Instance of `circular_t`.
/// Axis can grow. Mutually exclusive with `circular`.
using growth_t = bit<3>;
constexpr growth_t growth{}; ///< Instance of `growth_t`.
} // namespace option
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,193 @@
// Copyright 2015-2017 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// String representations here evaluate correctly in Python.
#ifndef BOOST_HISTOGRAM_AXIS_OSTREAM_HPP
#define BOOST_HISTOGRAM_AXIS_OSTREAM_HPP
#include <boost/assert.hpp>
#include <boost/core/typeinfo.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <iomanip>
#include <iosfwd>
#include <stdexcept>
#include <type_traits>
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
namespace boost {
namespace histogram {
namespace detail {
inline const char* axis_suffix(const axis::transform::id&) { return ""; }
inline const char* axis_suffix(const axis::transform::log&) { return "_log"; }
inline const char* axis_suffix(const axis::transform::sqrt&) { return "_sqrt"; }
inline const char* axis_suffix(const axis::transform::pow&) { return "_pow"; }
template <class OStream, class T>
void stream_metadata(OStream& os, const T& t) {
detail::static_if<detail::is_streamable<T>>(
[&os](const auto& t) {
std::ostringstream oss;
oss << t;
if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); }
},
[&os](const auto&) {
os << ", metadata=" << boost::core::demangled_name(BOOST_CORE_TYPEID(T));
},
t);
}
template <class OStream>
void stream_options(OStream& os, const unsigned bits) {
os << ", options=";
bool first = true;
#define BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(x) \
if (bits & axis::option::x) { \
if (first) \
first = false; \
else { \
os << " | "; \
} \
os << #x; \
}
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(underflow);
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(overflow);
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(circular);
BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(growth);
#undef BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM
if (first) os << "none";
}
template <class OStream, class T>
void stream_transform(OStream&, const T&) {}
template <class OStream>
void stream_transform(OStream& os, const axis::transform::pow& t) {
os << ", power=" << t.power;
}
template <class OStream, class T>
void stream_value(OStream& os, const T& t) {
os << t;
}
template <class OStream, class... Ts>
void stream_value(OStream& os, const std::basic_string<Ts...>& t) {
os << std::quoted(t);
}
} // namespace detail
namespace axis {
template <class T>
class polymorphic_bin;
template <class... Ts>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const null_type&) {
return os; // do nothing
}
template <class... Ts, class U>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const interval_view<U>& i) {
os << "[" << i.lower() << ", " << i.upper() << ")";
return os;
}
template <class... Ts, class U>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const polymorphic_bin<U>& i) {
if (i.is_discrete())
os << static_cast<double>(i);
else
os << "[" << i.lower() << ", " << i.upper() << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const regular<Us...>& a) {
os << "regular" << detail::axis_suffix(a.transform()) << "(" << 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());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const integer<Us...>& a) {
os << "integer(" << a.value(0) << ", " << a.value(a.size());
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const variable<Us...>& a) {
os << "variable(" << a.value(0);
for (index_type i = 1, n = a.size(); i <= n; ++i) { os << ", " << a.value(i); }
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const category<Us...>& a) {
os << "category(";
for (index_type i = 0, n = a.size(); i < n; ++i) {
detail::stream_value(os, a.value(i));
os << (i == (a.size() - 1) ? "" : ", ");
}
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const variant<Us...>& v) {
visit(
[&os](const auto& x) {
using A = detail::remove_cvref_t<decltype(x)>;
detail::static_if<detail::is_streamable<A>>(
[&os](const auto& x) { os << x; },
[](const auto&) {
BOOST_THROW_EXCEPTION(std::runtime_error(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(A)),
" is not streamable")));
},
x);
},
v);
return os;
}
} // namespace axis
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
#endif

View File

@@ -0,0 +1,88 @@
// Copyright 2018 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_HPP
#define BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_HPP
#include <boost/histogram/detail/meta.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace axis {
/**
Holds the bin data of a axis::variant.
The interface is a superset of the `value_bin_view` and `interval_bin_view`
classes. The methods value() and lower() return the same number. For a value bin,
upper() and lower() and center() return the same number, width() returns zero.
This is not a view like interval_bin_view or value_bin_view for two reasons.
- Sequential calls to lower() and upper() would have to each loop through
the variant types. This is likely to be slower than filling all the data in
one loop.
- polymorphic_bin may be created from a temporary instance of axis::variant,
like in the call histogram::axis(0). Storing a reference to the axis would
result in a dangling reference. Rather than specialing the code to handle
this, it seems easier to just use a value instead of a view here.
*/
template <typename RealType>
class polymorphic_bin {
using value_type = RealType;
public:
polymorphic_bin(value_type lower, value_type upper)
: lower_or_value_(lower), upper_(upper) {}
/// Implicitly convert to bin value (for axis with discrete values).
operator const value_type&() const noexcept { return lower_or_value_; }
/// Return lower edge of bin.
value_type lower() const noexcept { return lower_or_value_; }
/// Return upper edge of bin.
value_type upper() const noexcept { return upper_; }
/// Return center of bin.
value_type center() const noexcept { return 0.5 * (lower() + upper()); }
/// Return width of bin.
value_type width() const noexcept { return upper() - lower(); }
template <typename BinType>
bool operator==(const BinType& rhs) const noexcept {
return equal_impl(detail::has_method_lower<BinType>(), rhs);
}
template <typename BinType>
bool operator!=(const BinType& rhs) const noexcept {
return !operator==(rhs);
}
/// Return true if bin is discrete.
bool is_discrete() const noexcept { return lower_or_value_ == upper_; }
private:
bool equal_impl(std::true_type, const polymorphic_bin& rhs) const noexcept {
return lower_or_value_ == rhs.lower_or_value_ && upper_ == rhs.upper_;
}
template <typename BinType>
bool equal_impl(std::true_type, const BinType& rhs) const noexcept {
return lower() == rhs.lower() && upper() == rhs.upper();
}
template <typename BinType>
bool equal_impl(std::false_type, const BinType& rhs) const noexcept {
return is_discrete() && static_cast<value_type>(*this) == rhs;
}
const value_type lower_or_value_, upper_;
};
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,359 @@
// 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/assert.hpp>
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
namespace transform {
/// Identity transform for equidistant bins.
struct id {
/// Pass-through.
template <typename T>
static T forward(T&& x) noexcept {
return std::forward<T>(x);
}
/// Pass-through.
template <typename T>
static T inverse(T&& x) noexcept {
return std::forward<T>(x);
}
};
/// Log transform for equidistant bins in log-space.
struct log {
/// Returns log(x) of external value x.
template <typename T>
static T forward(T x) {
return std::log(x);
}
/// Returns exp(x) for internal value x.
template <typename T>
static T inverse(T x) {
return std::exp(x);
}
};
/// Sqrt transform for equidistant bins in sqrt-space.
struct sqrt {
/// Returns sqrt(x) of external value x.
template <typename T>
static T forward(T x) {
return std::sqrt(x);
}
/// Returns x^2 of internal value x.
template <typename T>
static T inverse(T x) {
return x * x;
}
};
/// Pow transform for equidistant bins in pow-space.
struct pow {
double power = 1; /**< power index */
/// Make transform with index p.
explicit pow(double p) : power(p) {}
pow() = default;
/// Returns pow(x, power) of external value x.
template <typename T>
auto forward(T x) const {
return std::pow(x, power);
}
/// Returns pow(x, 1/power) of external value x.
template <typename T>
auto inverse(T x) const {
return std::pow(x, 1.0 / power);
}
bool operator==(const pow& o) const noexcept { return power == o.power; }
};
} // namespace transform
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
// Type envelope to mark value as step size
template <typename T>
struct step_type {
T value;
};
#endif
/**
Helper function to mark argument as step size.
*/
template <typename T>
auto step(T&& t) {
return step_type<T&&>{std::forward<T>(t)};
}
/**
Axis for equidistant intervals on the real line.
The most common binning strategy. Very fast. Binning is a O(1) operation.
@tparam Value input value type, must be floating point.
@tparam Transform builtin or user-defined transform type.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option (all values allowed).
*/
template <class Value, class Transform, class MetaData, class Options>
class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
protected detail::replace_default<Transform, transform::id> {
using value_type = Value;
using transform_type = detail::replace_default<Transform, transform::id>;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type =
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
using unit_type = detail::get_unit_type<value_type>;
using internal_value_type = detail::get_scale_type<value_type>;
static_assert(std::is_floating_point<internal_value_type>::value,
"variable axis requires floating point type");
public:
constexpr regular() = default;
/** Construct n bins over real transformed range [start, stop).
*
* @param trans transform instance to use.
* @param n number of bins.
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
*/
regular(transform_type trans, unsigned n, value_type start, value_type stop,
metadata_type meta = {})
: transform_type(std::move(trans))
, size_meta_(static_cast<index_type>(n), std::move(meta))
, min_(this->forward(detail::get_scale(start)))
, delta_(this->forward(detail::get_scale(stop)) - min_) {
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
if (!std::isfinite(min_) || !std::isfinite(delta_))
BOOST_THROW_EXCEPTION(
std::invalid_argument("forward transform of start or stop invalid"));
if (delta_ == 0)
BOOST_THROW_EXCEPTION(std::invalid_argument("range of axis is zero"));
}
/** Construct n bins over real range [start, stop).
*
* @param n number of bins.
* @param start low edge of first bin.
* @param stop high edge of last bin.
* @param meta description of the axis (optional).
*/
regular(unsigned n, value_type start, value_type stop, metadata_type meta = {})
: regular({}, n, start, stop, std::move(meta)) {}
/** Construct bins with the given step size over real transformed range
* [start, stop).
*
* @param trans transform instance to use.
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
* (start + n * step).
*/
template <class T>
regular(transform_type trans, const step_type<T>& step, value_type start,
value_type stop, metadata_type meta = {})
: regular(trans, static_cast<index_type>(std::abs(stop - start) / step.value),
start,
start + static_cast<index_type>(std::abs(stop - start) / step.value) *
step.value,
std::move(meta)) {}
/** Construct bins with the given step size over real range [start, stop).
*
* @param step width of a single bin.
* @param start low edge of first bin.
* @param stop upper limit of high edge of last bin (see below).
* @param meta description of the axis (optional).
*
* The axis computes the number of bins as n = abs(stop - start) / step,
* rounded down. This means that stop is an upper limit to the actual value
* (start + n * step).
*/
template <class T>
regular(const step_type<T>& step, value_type start, value_type stop,
metadata_type meta = {})
: regular({}, step, start, stop, std::move(meta)) {}
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
regular(const regular& src, index_type begin, index_type end, unsigned merge)
: regular(src.transform(), (end - begin) / merge, src.value(begin), src.value(end),
src.metadata()) {
BOOST_ASSERT((end - begin) % merge == 0);
if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
}
/// Return instance of the transform type.
const transform_type& transform() const noexcept { return *this; }
/// Return index for value argument.
index_type index(value_type x) const noexcept {
// Runs in hot loop, please measure impact of changes
auto z = (this->forward(x / unit_type{}) - min_) / delta_;
if (options_type::test(option::circular)) {
if (std::isfinite(z)) {
z -= std::floor(z);
return static_cast<index_type>(z * size());
}
} else {
if (z < 1) {
if (z >= 0)
return static_cast<index_type>(z * size());
else
return -1;
}
}
return size(); // also returned if x is NaN
}
/// Returns index and shift (if axis has grown) for the passed argument.
auto update(value_type x) noexcept {
BOOST_ASSERT(options_type::test(option::growth));
const auto z = (this->forward(x / unit_type{}) - min_) / delta_;
if (z < 1) { // don't use i here!
if (z >= 0) {
const auto i = static_cast<axis::index_type>(z * size());
return std::make_pair(i, 0);
}
if (z != -std::numeric_limits<internal_value_type>::infinity()) {
const auto stop = min_ + delta_;
const auto i = static_cast<axis::index_type>(std::floor(z * size()));
min_ += i * (delta_ / size());
delta_ = stop - min_;
size_meta_.first() -= i;
return std::make_pair(0, -i);
}
// z is -infinity
return std::make_pair(-1, 0);
}
// z either beyond range, infinite, or NaN
if (z < std::numeric_limits<internal_value_type>::infinity()) {
const auto i = static_cast<axis::index_type>(z * size());
const auto n = i - size() + 1;
delta_ /= size();
delta_ *= size() + n;
size_meta_.first() += n;
return std::make_pair(i, -n);
}
// z either infinite or NaN
return std::make_pair(size(), 0);
}
/// Return value for fractional index argument.
value_type value(real_index_type i) const noexcept {
auto z = i / size();
if (!options_type::test(option::circular) && z < 0.0)
z = -std::numeric_limits<internal_value_type>::infinity() * delta_;
else if (options_type::test(option::circular) || z <= 1.0)
z = (1.0 - z) * min_ + z * (min_ + delta_);
else {
z = std::numeric_limits<internal_value_type>::infinity() * delta_;
}
return static_cast<value_type>(this->inverse(z) * unit_type());
}
/// Return bin for index argument.
decltype(auto) bin(index_type idx) const noexcept {
return interval_view<regular>(*this, idx);
}
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept { return size_meta_.first(); }
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return size_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
template <class V, class T, class M, class O>
bool operator==(const regular<V, T, M, O>& o) const noexcept {
return detail::relaxed_equal(transform(), o.transform()) && size() == o.size() &&
detail::relaxed_equal(metadata(), o.metadata()) && min_ == o.min_ &&
delta_ == o.delta_;
}
template <class V, class T, class M, class O>
bool operator!=(const regular<V, T, M, O>& o) const noexcept {
return !operator==(o);
}
template <class Archive>
void serialize(Archive&, unsigned);
private:
detail::compressed_pair<index_type, metadata_type> size_meta_{0};
internal_value_type min_{0}, delta_{1};
template <class V, class T, class M, class O>
friend class regular;
};
#if __cpp_deduction_guides >= 201606
template <class T>
regular(unsigned, T, T)->regular<detail::convert_integer<T, double>>;
template <class T>
regular(unsigned, T, T, const char*)->regular<detail::convert_integer<T, double>>;
template <class T, class M>
regular(unsigned, T, T, M)->regular<detail::convert_integer<T, double>, transform::id, M>;
template <class Tr, class T>
regular(Tr, unsigned, T, T)->regular<detail::convert_integer<T, double>, Tr>;
template <class Tr, class T>
regular(Tr, unsigned, T, T, const char*)->regular<detail::convert_integer<T, double>, Tr>;
template <class Tr, class T, class M>
regular(Tr, unsigned, T, T, M)->regular<detail::convert_integer<T, double>, Tr, M>;
#endif
template <class Value = double, class MetaData = use_default, class Options = use_default>
using circular = regular<Value, transform::id, MetaData,
decltype(detail::replace_default<Options, option::overflow_t>{} |
option::circular)>;
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,283 @@
// Copyright 2018 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_HISTOGRAM_AXIS_TRAITS_HPP
#define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
#include <boost/core/typeinfo.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
using static_options_impl = axis::option::bitset<T::options()>;
template <class FIntArg, class FDoubleArg, class T>
decltype(auto) value_method_switch(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
return static_if<has_method_value<T>>(
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) {
using A = remove_cvref_t<decltype(t)>;
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
},
[](FIntArg&&, FDoubleArg&&, const auto& t) -> double {
using A = remove_cvref_t<decltype(t)>;
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
boost::core::demangled_name(BOOST_CORE_TYPEID(A)), " has no value method")));
#ifndef _MSC_VER // msvc warns about unreachable return
return double{};
#endif
},
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
}
template <class R1, class R2, class FIntArg, class FDoubleArg, class T>
R2 value_method_switch_with_return_type(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
return static_if<has_method_value_with_convertible_return_type<T, R1>>(
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) -> R2 {
using A = remove_cvref_t<decltype(t)>;
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
},
[](FIntArg&&, FDoubleArg&&, const auto&) -> R2 {
BOOST_THROW_EXCEPTION(std::runtime_error(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
" has no value method or return type is not convertible to ",
boost::core::demangled_name(BOOST_CORE_TYPEID(R1)))));
#ifndef _MSC_VER // msvc warns about unreachable return
// conjure a value out of thin air to satisfy syntactic requirement
return *reinterpret_cast<R2*>(0);
#endif
},
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
}
} // namespace detail
namespace axis {
namespace traits {
/** Returns reference to metadata of an axis.
If the expression x.metadata() for an axis instance `x` (maybe const) is valid, return
the result. Otherwise, return a reference to a static instance of
boost::histogram::axis::null_type.
@param axis any axis instance
*/
template <class Axis>
decltype(auto) metadata(Axis&& axis) noexcept {
return detail::static_if<
detail::has_method_metadata<const detail::remove_cvref_t<Axis>>>(
[](auto&& a) -> decltype(auto) { return a.metadata(); },
[](auto &&) -> detail::copy_qualifiers<Axis, null_type> {
static null_type m;
return m;
},
std::forward<Axis>(axis));
}
/** Generates static axis option type for axis type.
WARNING: Doxygen does not render the synopsis correctly. This is a templated using
directive, which accepts axis type and returns boost::histogram::axis::option::bitset.
If Axis::options() is valid and constexpr, return the corresponding option type.
Otherwise, return boost::histogram::axis::option::growth_t, if the axis has a method
`update`, else return boost::histogram::axis::option::none_t.
@tparam Axis axis type
*/
template <class Axis>
using static_options = detail::mp_eval_or<
mp11::mp_if<detail::has_method_update<detail::remove_cvref_t<Axis>>, option::growth_t,
option::none_t>,
detail::static_options_impl, detail::remove_cvref_t<Axis>>;
/** Returns axis options as unsigned integer.
If axis.options() is valid, return the result. Otherwise, return
boost::histogram::axis::traits::static_options<decltype(axis)>::value.
@param axis any axis instance
*/
template <class Axis>
constexpr unsigned options(const Axis& axis) noexcept {
// cannot reuse static_options here, because this should also work for axis::variant
return detail::static_if<detail::has_method_options<Axis>>(
[](const auto& a) { return a.options(); },
[](const auto&) { return static_options<Axis>::value; }, axis);
}
/** Returns axis size plus any extra bins for under- and overflow.
@param axis any axis instance
*/
template <class Axis>
constexpr index_type extent(const Axis& axis) noexcept {
const auto opt = options(axis);
return axis.size() + (opt & option::underflow ? 1 : 0) +
(opt & option::overflow ? 1 : 0);
}
/** Returns axis value for index.
If the axis has no `value` method, throw std::runtime_error. If the method exists and
accepts a floating point index, pass the index and return the result. If the method
exists but accepts only integer indices, cast the floating point index to int, pass this
index and return the result.
@param axis any axis instance
@param index floating point axis index
*/
template <class Axis>
decltype(auto) value(const Axis& axis, real_index_type index) {
return detail::value_method_switch(
[index](const auto& a) { return a.value(static_cast<int>(index)); },
[index](const auto& a) { return a.value(index); }, axis);
}
/** Returns axis value for index if it is convertible to target type or throws.
Like boost::histogram::axis::traits::value, but converts the result into the requested
return type. If the conversion is not possible, throws std::runtime_error.
@tparam Result requested return type
@tparam Axis axis type
@param axis any axis instance
@param index floating point axis index
*/
template <class Result, class Axis>
Result value_as(const Axis& axis, real_index_type index) {
return detail::value_method_switch_with_return_type<Result, Result>(
[index](const auto& a) {
return static_cast<Result>(a.value(static_cast<int>(index)));
},
[index](const auto& a) { return static_cast<Result>(a.value(index)); }, axis);
}
/** Returns axis index for value.
Throws std::invalid_argument if the value argument is not implicitly convertible.
@param axis any axis instance
@param value argument to be passed to `index` method
*/
template <class Axis, class U>
auto index(const Axis& axis, const U& value) {
using V = detail::arg_type<decltype(&Axis::index)>;
return detail::static_if<std::is_convertible<U, V>>(
[&value](const auto& axis) {
using A = detail::remove_cvref_t<decltype(axis)>;
using V2 = detail::arg_type<decltype(&A::index)>;
return axis.index(static_cast<V2>(value));
},
[](const Axis&) -> index_type {
BOOST_THROW_EXCEPTION(std::invalid_argument(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(Axis)),
": cannot convert argument of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " to ",
boost::core::demangled_name(BOOST_CORE_TYPEID(V)))));
#ifndef _MSC_VER // msvc warns about unreachable return
return index_type{};
#endif
},
axis);
}
/// @copydoc index(const Axis&, const U& value)
template <class... Ts, class U>
auto index(const variant<Ts...>& axis, const U& value) {
return axis.index(value);
}
/** Returns pair of axis index and shift for the value argument.
Throws `std::invalid_argument` if the value argument is not implicitly convertible to
the argument expected by the `index` method. If the result of
boost::histogram::axis::traits::static_options<decltype(axis)> has the growth flag set,
call `update` method with the argument and return the result. Otherwise, call `index`
and return the pair of the result and a zero shift.
@param axis any axis instance
@param value argument to be passed to `update` or `index` method
*/
template <class Axis, class U>
std::pair<int, int> update(Axis& axis, const U& value) {
using V = detail::arg_type<decltype(&Axis::index)>;
return detail::static_if<std::is_convertible<U, V>>(
[&value](auto& a) {
using A = detail::remove_cvref_t<decltype(a)>;
return detail::static_if_c<static_options<A>::test(option::growth)>(
[&value](auto& a) { return a.update(value); },
[&value](auto& a) { return std::make_pair(a.index(value), 0); }, a);
},
[](Axis&) -> std::pair<index_type, index_type> {
BOOST_THROW_EXCEPTION(std::invalid_argument(
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(Axis)),
": cannot convert argument of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " to ",
boost::core::demangled_name(BOOST_CORE_TYPEID(V)))));
#ifndef _MSC_VER // msvc warns about unreachable return
return std::make_pair(index_type{}, index_type{});
#endif
},
axis);
}
/// @copydoc update(Axis& axis, const U& value)
template <class... Ts, class U>
auto update(variant<Ts...>& axis, const U& value) {
return axis.update(value);
}
/** Returns bin width at axis index.
If the axis has no `value` method, throw std::runtime_error. If the method exists and
accepts a floating point index, return the result of `axis.value(index + 1) -
axis.value(index)`. If the method exists but accepts only integer indices, return 0.
@param axis any axis instance
@param index bin index
*/
template <class Axis>
decltype(auto) width(const Axis& axis, index_type index) {
return detail::value_method_switch(
[](const auto&) { return 0; },
[index](const auto& a) { return a.value(index + 1) - a.value(index); }, axis);
}
/** Returns bin width at axis index.
Like boost::histogram::axis::traits::width, but converts the result into the requested
return type. If the conversion is not possible, throw std::runtime_error.
@param axis any axis instance
@param index bin index
*/
template <class Result, class Axis>
Result width_as(const Axis& axis, index_type index) {
return detail::value_method_switch_with_return_type<Result, Result>(
[](const auto&) { return Result{}; },
[index](const auto& a) {
return static_cast<Result>(a.value(index + 1) - a.value(index));
},
axis);
}
} // namespace traits
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,242 @@
// 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/assert.hpp>
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace boost {
namespace histogram {
namespace axis {
/**
Axis for non-equidistant bins on the real line.
Binning is a O(log(N)) operation. If speed matters and the problem domain
allows it, prefer a regular axis, possibly with a transform.
@tparam Value input value type, must be floating point.
@tparam MetaData type to store meta data.
@tparam Options see boost::histogram::axis::option (all values allowed).
@tparam Allocator allocator to use for dynamic memory management.
*/
template <class Value, class MetaData, class Options, class Allocator>
class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>> {
static_assert(std::is_floating_point<Value>::value,
"variable axis requires floating point type");
using value_type = Value;
using metadata_type = detail::replace_default<MetaData, std::string>;
using options_type =
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
using allocator_type = Allocator;
using vec_type = std::vector<Value, allocator_type>;
public:
explicit variable(allocator_type alloc = {}) : vec_meta_(std::move(alloc)) {}
/** Construct from iterator range of bin edges.
*
* \param begin begin of edge sequence.
* \param end end of edge sequence.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class It, class = detail::requires_iterator<It>>
variable(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
: vec_meta_(vec_type(std::move(alloc)), std::move(meta)) {
if (std::distance(begin, end) <= 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
auto& v = vec_meta_.first();
v.reserve(std::distance(begin, end));
v.emplace_back(*begin++);
while (begin != end) {
if (*begin <= v.back())
BOOST_THROW_EXCEPTION(
std::invalid_argument("input sequence must be strictly ascending"));
v.emplace_back(*begin++);
}
}
/** Construct variable axis from iterable range of bin edges.
*
* \param iterable iterable range of bin edges.
* \param meta description of the axis.
* \param alloc allocator instance to use.
*/
template <class U, class = detail::requires_iterable<U>>
variable(const U& iterable, metadata_type meta = {}, allocator_type alloc = {})
: variable(std::begin(iterable), std::end(iterable), std::move(meta),
std::move(alloc)) {}
/** Construct variable axis from initializer list of bin edges.
*
* @param list `std::initializer_list` of bin edges.
* @param meta description of the axis.
* @param alloc allocator instance to use.
*/
template <class U>
variable(std::initializer_list<U> list, metadata_type meta = {},
allocator_type alloc = {})
: variable(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
variable(const variable& src, index_type begin, index_type end, unsigned merge)
: vec_meta_(vec_type(src.get_allocator()), src.metadata()) {
BOOST_ASSERT((end - begin) % merge == 0);
if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
auto& vec = vec_meta_.first();
vec.reserve((end - begin) / merge);
const auto beg = src.vec_meta_.first().begin();
for (index_type i = begin; i <= end; i += merge) vec.emplace_back(*(beg + i));
}
/// Return index for value argument.
index_type index(value_type x) const noexcept {
const auto& v = vec_meta_.first();
if (options_type::test(option::circular)) {
const auto a = v[0];
const auto b = v[size()];
x -= std::floor((x - a) / (b - a)) * (b - a);
}
return static_cast<index_type>(std::upper_bound(v.begin(), v.end(), x) - v.begin() -
1);
}
auto update(value_type x) noexcept {
const auto i = index(x);
if (std::isfinite(x)) {
auto& vec = vec_meta_.first();
if (0 <= i) {
if (i < size()) return std::make_pair(i, 0);
const auto d = value(size()) - value(size() - 0.5);
x = std::nextafter(x, std::numeric_limits<value_type>::max());
x = std::max(x, vec.back() + d);
vec.push_back(x);
return std::make_pair(i, -1);
}
const auto d = value(0.5) - value(0);
x = std::min(x, value(0) - d);
vec.insert(vec.begin(), x);
return std::make_pair(0, -i);
}
return std::make_pair(x < 0 ? -1 : size(), 0);
}
/// Return value for fractional index argument.
value_type value(real_index_type i) const noexcept {
const auto& v = vec_meta_.first();
if (options_type::test(option::circular)) {
auto shift = std::floor(i / size());
i -= shift * size();
double z;
const auto k = static_cast<index_type>(std::modf(i, &z));
const auto a = v[0];
const auto b = v[size()];
return (1.0 - z) * v[k] + z * v[k + 1] + shift * (b - a);
}
if (i < 0) return detail::lowest<value_type>();
if (i == size()) return v.back();
if (i > size()) return detail::highest<value_type>();
const auto k = static_cast<index_type>(i); // precond: i >= 0
const real_index_type z = i - k;
return (1.0 - z) * v[k] + z * v[k + 1];
}
/// Return bin for index argument.
auto bin(index_type idx) const noexcept { return interval_view<variable>(*this, idx); }
/// Returns the number of bins, without over- or underflow.
index_type size() const noexcept {
return static_cast<index_type>(vec_meta_.first().size()) - 1;
}
/// Returns the options.
static constexpr unsigned options() noexcept { return options_type::value; }
/// Returns reference to metadata.
metadata_type& metadata() noexcept { return vec_meta_.second(); }
/// Returns reference to const metadata.
const metadata_type& metadata() const noexcept { return vec_meta_.second(); }
template <class V, class M, class O, class A>
bool operator==(const variable<V, M, O, A>& o) const noexcept {
const auto& a = vec_meta_.first();
const auto& b = o.vec_meta_.first();
return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
detail::relaxed_equal(metadata(), o.metadata());
}
template <class V, class M, class O, class A>
bool operator!=(const variable<V, M, O, A>& o) const noexcept {
return !operator==(o);
}
/// Return allocator instance.
auto get_allocator() const { return vec_meta_.first().get_allocator(); }
template <class Archive>
void serialize(Archive&, unsigned);
private:
detail::compressed_pair<vec_type, metadata_type> vec_meta_;
template <class V, class M, class O, class A>
friend class variable;
};
#if __cpp_deduction_guides >= 201606
template <class U, class T = detail::convert_integer<U, double>>
variable(std::initializer_list<U>)->variable<T>;
template <class U, class T = detail::convert_integer<U, double>>
variable(std::initializer_list<U>, const char*)->variable<T>;
template <class U, class M, class T = detail::convert_integer<U, double>>
variable(std::initializer_list<U>, M)->variable<T, M>;
template <
class Iterable,
class T = detail::convert_integer<
detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
variable(Iterable)->variable<T>;
template <
class Iterable,
class T = detail::convert_integer<
detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
variable(Iterable, const char*)->variable<T>;
template <
class Iterable, class M,
class T = detail::convert_integer<
detail::remove_cvref_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
variable(Iterable, M)->variable<T, M>;
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,299 @@
// 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_VARIANT_HPP
#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP
#include <boost/core/typeinfo.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/polymorphic_bin.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/detail/cat.hpp>
#include <boost/histogram/detail/meta.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/list.hpp>
#include <boost/throw_exception.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/get.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/variant/variant.hpp>
#include <ostream>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace axis {
template <class T, class... Us>
T* get_if(variant<Us...>* v);
template <class T, class... Us>
const T* get_if(const variant<Us...>* v);
/// Polymorphic axis type
template <class... Ts>
class variant : public iterator_mixin<variant<Ts...>> {
using impl_type = boost::variant<Ts...>;
using raw_types = mp11::mp_transform<detail::remove_cvref_t, impl_type>;
template <class T>
using is_bounded_type = mp11::mp_contains<raw_types, detail::remove_cvref_t<T>>;
template <typename T>
using requires_bounded_type = std::enable_if_t<is_bounded_type<T>::value>;
// maybe metadata_type or const metadata_type, if bounded type is const
using metadata_type = std::remove_reference_t<decltype(
traits::metadata(std::declval<mp11::mp_first<impl_type>>()))>;
public:
// cannot import ctors with using directive, it breaks gcc and msvc
variant() = default;
variant(const variant&) = default;
variant& operator=(const variant&) = default;
variant(variant&&) = default;
variant& operator=(variant&&) = default;
template <typename T, typename = requires_bounded_type<T>>
variant(T&& t) : impl(std::forward<T>(t)) {}
template <typename T, typename = requires_bounded_type<T>>
variant& operator=(T&& t) {
impl = std::forward<T>(t);
return *this;
}
template <class... Us>
variant(const variant<Us...>& u) {
this->operator=(u);
}
template <class... Us>
variant& operator=(const variant<Us...>& u) {
visit(
[this](const auto& u) {
using U = detail::remove_cvref_t<decltype(u)>;
detail::static_if<is_bounded_type<U>>(
[this](const auto& u) { this->operator=(u); },
[](const auto&) {
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
" is not convertible to a bounded type of ",
boost::core::demangled_name(BOOST_CORE_TYPEID(variant)))));
},
u);
},
u);
return *this;
}
/// Return size of axis.
index_type size() const {
return visit([](const auto& x) { return x.size(); }, *this);
}
/// Return options of axis or option::none_t if axis has no options.
unsigned options() const {
return visit([](const auto& x) { return axis::traits::options(x); }, *this);
}
/// Return reference to const metadata or instance of null_type if axis has no
/// metadata.
const metadata_type& metadata() const {
return visit(
[](const auto& a) -> const metadata_type& {
using M = decltype(traits::metadata(a));
return detail::static_if<std::is_same<M, const metadata_type&>>(
[](const auto& a) -> const metadata_type& { return traits::metadata(a); },
[](const auto&) -> const metadata_type& {
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
"cannot return metadata of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(M)),
" 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")));
},
a);
},
*this);
}
/// Return reference to metadata or instance of null_type if axis has no
/// metadata.
metadata_type& metadata() {
return visit(
[](auto&& a) -> metadata_type& {
using M = decltype(traits::metadata(a));
return detail::static_if<std::is_same<M, metadata_type&>>(
[](auto& a) -> metadata_type& { return traits::metadata(a); },
[](auto&) -> metadata_type& {
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
"cannot return metadata of type ",
boost::core::demangled_name(BOOST_CORE_TYPEID(M)),
" 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")));
},
a);
},
*this);
}
/// Return index for value argument.
/// Throws std::invalid_argument if axis has incompatible call signature.
template <class U>
index_type index(const U& u) const {
return visit([&u](const auto& a) { return traits::index(a, u); }, *this);
}
/// Return value for index argument.
/// Only works for axes with value method that returns something convertible
/// to double and will throw a runtime_error otherwise, see
/// axis::traits::value().
double value(real_index_type idx) const {
return visit([idx](const auto& a) { return traits::value_as<double>(a, idx); },
*this);
}
/// Return bin for index argument.
/// Only works for axes with value method that returns something convertible
/// to double and will throw a runtime_error otherwise, see
/// axis::traits::value().
auto bin(index_type idx) const {
return visit(
[idx](const auto& a) {
return detail::value_method_switch_with_return_type<double,
polymorphic_bin<double>>(
[idx](const auto& a) { // axis is discrete
const auto x = a.value(idx);
return polymorphic_bin<double>(x, x);
},
[idx](const auto& a) { // axis is continuous
return polymorphic_bin<double>(a.value(idx), a.value(idx + 1));
},
a);
},
*this);
}
template <class... Us>
bool operator==(const variant<Us...>& u) const {
return visit([&u](const auto& x) { return u == x; }, *this);
}
template <class T>
bool operator==(const T& t) const {
// boost::variant::operator==(T) implemented only to fail, cannot use it
auto tp = get_if<T>(this);
return tp && detail::relaxed_equal(*tp, t);
}
template <class T>
bool operator!=(const T& t) const {
return !operator==(t);
}
template <class Archive>
void serialize(Archive& ar, unsigned);
template <class Visitor, class Variant>
friend auto visit(Visitor&&, Variant &&)
-> detail::visitor_return_type<Visitor, Variant>;
template <class T, class... Us>
friend T& get(variant<Us...>& v);
template <class T, class... Us>
friend const T& get(const variant<Us...>& v);
template <class T, class... Us>
friend T&& get(variant<Us...>&& v);
template <class T, class... Us>
friend T* get_if(variant<Us...>* v);
template <class T, class... Us>
friend const T* get_if(const variant<Us...>* v);
private:
boost::variant<Ts...> impl;
};
/// Apply visitor to variant.
template <class Visitor, class Variant>
auto visit(Visitor&& vis, Variant&& var)
-> detail::visitor_return_type<Visitor, Variant> {
return boost::apply_visitor(std::forward<Visitor>(vis), var.impl);
}
/// Return lvalue reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
T& get(variant<Us...>& v) {
return boost::get<T>(v.impl);
}
/// Return rvalue reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
T&& get(variant<Us...>&& v) {
return boost::get<T>(std::move(v.impl));
}
/// Return const reference to T, throws unspecified exception if type does not
/// match.
template <class T, class... Us>
const T& get(const variant<Us...>& v) {
return boost::get<T>(v.impl);
}
/// Returns pointer to T in variant or null pointer if type does not match.
template <class T, class... Us>
T* get_if(variant<Us...>* v) {
return boost::relaxed_get<T>(&(v->impl));
}
/// Returns pointer to const T in variant or null pointer if type does not
/// match.
template <class T, class... Us>
const T* get_if(const variant<Us...>* v) {
return boost::relaxed_get<T>(&(v->impl));
}
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
// pass-through version of get for generic programming
template <class T, class U>
decltype(auto) get(U&& u) {
return static_cast<detail::copy_qualifiers<U, T>>(u);
}
// pass-through version of get_if for generic programming
template <class T, class U>
T* get_if(U* u) {
return std::is_same<T, detail::remove_cvref_t<U>>::value ? reinterpret_cast<T*>(u)
: nullptr;
}
// pass-through version of get_if for generic programming
template <class T, class U>
const T* get_if(const U* u) {
return std::is_same<T, detail::remove_cvref_t<U>>::value ? reinterpret_cast<const T*>(u)
: nullptr;
}
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif