integer.hpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2015-2018 Hans Dembinski
  2. //
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_HISTOGRAM_AXIS_INTEGER_HPP
  7. #define BOOST_HISTOGRAM_AXIS_INTEGER_HPP
  8. #include <boost/core/nvp.hpp>
  9. #include <boost/histogram/axis/iterator.hpp>
  10. #include <boost/histogram/axis/metadata_base.hpp>
  11. #include <boost/histogram/axis/option.hpp>
  12. #include <boost/histogram/detail/convert_integer.hpp>
  13. #include <boost/histogram/detail/limits.hpp>
  14. #include <boost/histogram/detail/replace_type.hpp>
  15. #include <boost/histogram/detail/static_if.hpp>
  16. #include <boost/histogram/fwd.hpp>
  17. #include <boost/throw_exception.hpp>
  18. #include <cmath>
  19. #include <limits>
  20. #include <stdexcept>
  21. #include <string>
  22. #include <type_traits>
  23. #include <utility>
  24. namespace boost {
  25. namespace histogram {
  26. namespace axis {
  27. /**
  28. Axis for an interval of integer values with unit steps.
  29. Binning is a O(1) operation. This axis bins faster than a regular axis.
  30. @tparam Value input value type. Must be integer or floating point.
  31. @tparam MetaData type to store meta data.
  32. @tparam Options see boost::histogram::axis::option (all values allowed).
  33. */
  34. template <class Value, class MetaData, class Options>
  35. class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
  36. public metadata_base<MetaData> {
  37. static_assert(std::is_integral<Value>::value || std::is_floating_point<Value>::value,
  38. "integer axis requires floating point or integral type");
  39. using value_type = Value;
  40. using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
  41. index_type, real_index_type>;
  42. using metadata_type = typename metadata_base<MetaData>::metadata_type;
  43. using options_type =
  44. detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
  45. static_assert(!options_type::test(option::circular | option::growth) ||
  46. (options_type::test(option::circular) ^
  47. options_type::test(option::growth)),
  48. "circular and growth options are mutually exclusive");
  49. static_assert(std::is_floating_point<value_type>::value ||
  50. (!options_type::test(option::circular) &&
  51. !options_type::test(option::growth)) ||
  52. (!options_type::test(option::overflow) &&
  53. !options_type::test(option::underflow)),
  54. "circular or growing integer axis with integral type "
  55. "cannot have entries in underflow or overflow bins");
  56. public:
  57. constexpr integer() = default;
  58. /** Construct over semi-open integer interval [start, stop).
  59. *
  60. * \param start first integer of covered range.
  61. * \param stop one past last integer of covered range.
  62. * \param meta description of the axis.
  63. */
  64. integer(value_type start, value_type stop, metadata_type meta = {})
  65. : metadata_base<MetaData>(std::move(meta))
  66. , size_(static_cast<index_type>(stop - start))
  67. , min_(start) {
  68. if (!(stop >= start))
  69. BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required"));
  70. }
  71. /// Constructor used by algorithm::reduce to shrink and rebin.
  72. integer(const integer& src, index_type begin, index_type end, unsigned merge)
  73. : integer(src.value(begin), src.value(end), src.metadata()) {
  74. if (merge > 1)
  75. BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for integer axis"));
  76. if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
  77. BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
  78. }
  79. /// Return index for value argument.
  80. index_type index(value_type x) const noexcept {
  81. return index_impl(std::is_floating_point<value_type>(), x);
  82. }
  83. /// Returns index and shift (if axis has grown) for the passed argument.
  84. auto update(value_type x) noexcept {
  85. auto impl = [this](long x) {
  86. const auto i = x - min_;
  87. if (i >= 0) {
  88. const auto k = static_cast<axis::index_type>(i);
  89. if (k < size()) return std::make_pair(k, 0);
  90. const auto n = k - size() + 1;
  91. size_ += n;
  92. return std::make_pair(k, -n);
  93. }
  94. const auto k = static_cast<axis::index_type>(
  95. detail::static_if<std::is_floating_point<value_type>>(
  96. [](auto x) { return std::floor(x); }, [](auto x) { return x; }, i));
  97. min_ += k;
  98. size_ -= k;
  99. return std::make_pair(0, -k);
  100. };
  101. return detail::static_if<std::is_floating_point<value_type>>(
  102. [this, impl](auto x) {
  103. if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x)));
  104. return std::make_pair(x < 0 ? -1 : this->size(), 0);
  105. },
  106. impl, x);
  107. }
  108. /// Return value for index argument.
  109. value_type value(local_index_type i) const noexcept {
  110. if (!options_type::test(option::circular) &&
  111. std::is_floating_point<value_type>::value) {
  112. if (i < 0) return detail::lowest<value_type>();
  113. if (i > size()) return detail::highest<value_type>();
  114. }
  115. return min_ + i;
  116. }
  117. /// Return bin for index argument.
  118. decltype(auto) bin(index_type idx) const noexcept {
  119. return detail::static_if<std::is_floating_point<value_type>>(
  120. [this](auto idx) { return interval_view<integer>(*this, idx); },
  121. [this](auto idx) { return this->value(idx); }, idx);
  122. }
  123. /// Returns the number of bins, without over- or underflow.
  124. index_type size() const noexcept { return size_; }
  125. /// Returns the options.
  126. static constexpr unsigned options() noexcept { return options_type::value; }
  127. /// Whether the axis is inclusive (see axis::traits::is_inclusive).
  128. static constexpr bool inclusive() noexcept {
  129. return (options() & option::underflow || options() & option::overflow) ||
  130. (std::is_integral<value_type>::value &&
  131. (options() & (option::growth | option::circular)));
  132. }
  133. template <class V, class M, class O>
  134. bool operator==(const integer<V, M, O>& o) const noexcept {
  135. return size() == o.size() && min_ == o.min_ && metadata_base<MetaData>::operator==(o);
  136. }
  137. template <class V, class M, class O>
  138. bool operator!=(const integer<V, M, O>& o) const noexcept {
  139. return !operator==(o);
  140. }
  141. template <class Archive>
  142. void serialize(Archive& ar, unsigned /* version */) {
  143. ar& make_nvp("size", size_);
  144. ar& make_nvp("meta", this->metadata());
  145. ar& make_nvp("min", min_);
  146. }
  147. private:
  148. index_type index_impl(std::false_type, int x) const noexcept {
  149. const auto z = x - min_;
  150. if (options_type::test(option::circular))
  151. return static_cast<index_type>(z - std::floor(float(z) / size()) * size());
  152. if (z < size()) return z >= 0 ? z : -1;
  153. return size();
  154. }
  155. template <typename T>
  156. index_type index_impl(std::true_type, T x) const noexcept {
  157. // need to handle NaN, cannot simply cast to int and call int-implementation
  158. const auto z = x - min_;
  159. if (options_type::test(option::circular)) {
  160. if (std::isfinite(z))
  161. return static_cast<index_type>(std::floor(z) - std::floor(z / size()) * size());
  162. } else if (z < size()) {
  163. return z >= 0 ? static_cast<index_type>(z) : -1;
  164. }
  165. return size();
  166. }
  167. index_type size_{0};
  168. value_type min_{0};
  169. template <class V, class M, class O>
  170. friend class integer;
  171. };
  172. #if __cpp_deduction_guides >= 201606
  173. template <class T>
  174. integer(T, T)->integer<detail::convert_integer<T, index_type>, null_type>;
  175. template <class T, class M>
  176. integer(T, T, M)
  177. ->integer<detail::convert_integer<T, index_type>,
  178. detail::replace_type<std::decay_t<M>, const char*, std::string>>;
  179. #endif
  180. } // namespace axis
  181. } // namespace histogram
  182. } // namespace boost
  183. #endif