| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 | // 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/core/nvp.hpp>#include <boost/histogram/axis/interval_view.hpp>#include <boost/histogram/axis/iterator.hpp>#include <boost/histogram/axis/metadata_base.hpp>#include <boost/histogram/axis/option.hpp>#include <boost/histogram/detail/convert_integer.hpp>#include <boost/histogram/detail/detect.hpp>#include <boost/histogram/detail/limits.hpp>#include <boost/histogram/detail/replace_type.hpp>#include <boost/histogram/fwd.hpp>#include <boost/throw_exception.hpp>#include <cmath>#include <limits>#include <memory>#include <stdexcept>#include <string>#include <type_traits>#include <utility>#include <vector>namespace boost {namespace histogram {namespace axis {/**  Axis for non-equidistant bins on the real line.  Binning is a O(log(N)) operation. If speed matters and the problem domain  allows it, prefer a regular axis, possibly with a transform.  @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>>,                 public metadata_base<MetaData> {  // these must be private, so that they are not automatically inherited  using value_type = Value;  using metadata_type = typename metadata_base<MetaData>::metadata_type;  using options_type =      detail::replace_default<Options, decltype(option::underflow | option::overflow)>;  using allocator_type = Allocator;  using vector_type = std::vector<Value, allocator_type>;  static_assert(      std::is_floating_point<value_type>::value,      "current version of variable axis requires floating point type; "      "if you need a variable axis with an integral type, please submit an issue");  static_assert(      (!options_type::test(option::circular) && !options_type::test(option::growth)) ||          (options_type::test(option::circular) ^ options_type::test(option::growth)),      "circular and growth options are mutually exclusive");public:  constexpr variable() = default;  explicit variable(allocator_type alloc) : vec_(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 = {})      : metadata_base<MetaData>(std::move(meta)), vec_(std::move(alloc)) {    if (std::distance(begin, end) < 2)      BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));    vec_.reserve(std::distance(begin, end));    vec_.emplace_back(*begin++);    bool strictly_ascending = true;    while (begin != end) {      if (*begin <= vec_.back()) strictly_ascending = false;      vec_.emplace_back(*begin++);    }    if (!strictly_ascending)      BOOST_THROW_EXCEPTION(          std::invalid_argument("input sequence must be strictly ascending"));  }  /** 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)      : metadata_base<MetaData>(src), vec_(src.get_allocator()) {    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"));    vec_.reserve((end - begin) / merge);    const auto beg = src.vec_.begin();    for (index_type i = begin; i <= end; i += merge) vec_.emplace_back(*(beg + i));  }  /// Return index for value argument.  index_type index(value_type x) const noexcept {    if (options_type::test(option::circular)) {      const auto a = vec_[0];      const auto b = vec_[size()];      x -= std::floor((x - a) / (b - a)) * (b - a);    }    return static_cast<index_type>(std::upper_bound(vec_.begin(), vec_.end(), x) -                                   vec_.begin() - 1);  }  std::pair<index_type, index_type> update(value_type x) noexcept {    const auto i = index(x);    if (std::isfinite(x)) {      if (0 <= i) {        if (i < size()) return std::make_pair(i, 0);        const auto d = value(size()) - value(size() - 0.5);        x = std::nextafter(x, (std::numeric_limits<value_type>::max)());        x = (std::max)(x, vec_.back() + d);        vec_.push_back(x);        return {i, -1};      }      const auto d = value(0.5) - value(0);      x = (std::min)(x, value(0) - d);      vec_.insert(vec_.begin(), x);      return {0, -i};    }    return {x < 0 ? -1 : size(), 0};  }  /// Return value for fractional index argument.  value_type value(real_index_type i) const noexcept {    if (options_type::test(option::circular)) {      auto shift = std::floor(i / size());      i -= shift * size();      double z;      const auto k = static_cast<index_type>(std::modf(i, &z));      const auto a = vec_[0];      const auto b = vec_[size()];      return (1.0 - z) * vec_[k] + z * vec_[k + 1] + shift * (b - a);    }    if (i < 0) return detail::lowest<value_type>();    if (i == size()) return vec_.back();    if (i > size()) return detail::highest<value_type>();    const auto k = static_cast<index_type>(i); // precond: i >= 0    const real_index_type z = i - k;    return (1.0 - z) * vec_[k] + z * vec_[k + 1];  }  /// Return bin for index argument.  auto bin(index_type idx) const noexcept { return interval_view<variable>(*this, idx); }  /// Returns the number of bins, without over- or underflow.  index_type size() const noexcept { return static_cast<index_type>(vec_.size()) - 1; }  /// Returns the options.  static constexpr unsigned options() noexcept { return options_type::value; }  template <class V, class M, class O, class A>  bool operator==(const variable<V, M, O, A>& o) const noexcept {    const auto& a = vec_;    const auto& b = o.vec_;    return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&           metadata_base<MetaData>::operator==(o);  }  template <class V, class M, class O, class A>  bool operator!=(const variable<V, M, O, A>& o) const noexcept {    return !operator==(o);  }  /// Return allocator instance.  auto get_allocator() const { return vec_.get_allocator(); }  template <class Archive>  void serialize(Archive& ar, unsigned /* version */) {    ar& make_nvp("seq", vec_);    ar& make_nvp("meta", this->metadata());  }private:  vector_type vec_;  template <class V, class M, class O, class A>  friend class variable;};#if __cpp_deduction_guides >= 201606template <class T>variable(std::initializer_list<T>)    ->variable<detail::convert_integer<T, double>, null_type>;template <class T, class M>variable(std::initializer_list<T>, M)    ->variable<detail::convert_integer<T, double>,               detail::replace_type<std::decay_t<M>, const char*, std::string>>;template <class Iterable, class = detail::requires_iterable<Iterable>>variable(Iterable)    ->variable<        detail::convert_integer<            std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,        null_type>;template <class Iterable, class M>variable(Iterable, M)    ->variable<        detail::convert_integer<            std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,        detail::replace_type<std::decay_t<M>, const char*, std::string>>;#endif} // namespace axis} // namespace histogram} // namespace boost#endif
 |