Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:38:10

0001 // Copyright 2018 Hans Dembinski
0002 //
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt
0005 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_HISTOGRAM_AXIS_TRAITS_HPP
0008 #define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
0009 
0010 #include <boost/histogram/axis/option.hpp>
0011 #include <boost/histogram/detail/args_type.hpp>
0012 #include <boost/histogram/detail/detect.hpp>
0013 #include <boost/histogram/detail/priority.hpp>
0014 #include <boost/histogram/detail/static_if.hpp>
0015 #include <boost/histogram/detail/try_cast.hpp>
0016 #include <boost/histogram/detail/type_name.hpp>
0017 #include <boost/histogram/fwd.hpp>
0018 #include <boost/mp11/algorithm.hpp>
0019 #include <boost/mp11/list.hpp>
0020 #include <boost/mp11/utility.hpp>
0021 #include <boost/throw_exception.hpp>
0022 #include <boost/variant2/variant.hpp>
0023 #include <stdexcept>
0024 #include <string>
0025 #include <utility>
0026 
0027 namespace boost {
0028 namespace histogram {
0029 namespace detail {
0030 
0031 template <class Axis>
0032 struct value_type_deducer {
0033   using type =
0034       std::remove_cv_t<std::remove_reference_t<detail::arg_type<decltype(&Axis::index)>>>;
0035 };
0036 
0037 template <class Axis>
0038 auto traits_options(priority<2>) -> axis::option::bitset<Axis::options()>;
0039 
0040 template <class Axis>
0041 auto traits_options(priority<1>) -> decltype(&Axis::update, axis::option::growth_t{});
0042 
0043 template <class Axis>
0044 auto traits_options(priority<0>) -> axis::option::none_t;
0045 
0046 template <class Axis>
0047 auto traits_is_inclusive(priority<1>) -> std::integral_constant<bool, Axis::inclusive()>;
0048 
0049 template <class Axis>
0050 auto traits_is_inclusive(priority<0>)
0051     -> decltype(traits_options<Axis>(priority<2>{})
0052                     .test(axis::option::underflow | axis::option::overflow));
0053 
0054 template <class Axis>
0055 auto traits_is_ordered(priority<1>) -> std::integral_constant<bool, Axis::ordered()>;
0056 
0057 template <class Axis, class ValueType = typename value_type_deducer<Axis>::type>
0058 auto traits_is_ordered(priority<0>) -> typename std::is_arithmetic<ValueType>::type;
0059 
0060 template <class I, class D, class A,
0061           class J = std::decay_t<arg_type<decltype(&A::value)>>>
0062 decltype(auto) value_method_switch(I&& i, D&& d, const A& a, priority<1>) {
0063   return static_if<std::is_same<J, axis::index_type>>(std::forward<I>(i),
0064                                                       std::forward<D>(d), a);
0065 }
0066 
0067 template <class I, class D, class A>
0068 double value_method_switch(I&&, D&&, const A&, priority<0>) {
0069   // comma trick to make all compilers happy; some would complain about
0070   // unreachable code after the throw, others about a missing return
0071   return BOOST_THROW_EXCEPTION(
0072              std::runtime_error(type_name<A>() + " has no value method")),
0073          double{};
0074 }
0075 
0076 struct variant_access {
0077   template <class T, class Variant>
0078   static auto get_if(Variant* v) noexcept {
0079     using T0 = mp11::mp_first<std::decay_t<Variant>>;
0080     return static_if<std::is_pointer<T0>>(
0081         [](auto* vptr) {
0082           using TP = mp11::mp_if<std::is_const<std::remove_pointer_t<T0>>, const T*, T*>;
0083           auto ptp = variant2::get_if<TP>(vptr);
0084           return ptp ? *ptp : nullptr;
0085         },
0086         [](auto* vptr) { return variant2::get_if<T>(vptr); }, &(v->impl));
0087   }
0088 
0089   template <class T0, class Visitor, class Variant>
0090   static decltype(auto) visit_impl(mp11::mp_identity<T0>, Visitor&& vis, Variant&& v) {
0091     return variant2::visit(std::forward<Visitor>(vis), v.impl);
0092   }
0093 
0094   template <class T0, class Visitor, class Variant>
0095   static decltype(auto) visit_impl(mp11::mp_identity<T0*>, Visitor&& vis, Variant&& v) {
0096     return variant2::visit(
0097         [&vis](auto&& x) -> decltype(auto) { return std::forward<Visitor>(vis)(*x); },
0098         v.impl);
0099   }
0100 
0101   template <class Visitor, class Variant>
0102   static decltype(auto) visit(Visitor&& vis, Variant&& v) {
0103     using T0 = mp11::mp_first<std::decay_t<Variant>>;
0104     return visit_impl(mp11::mp_identity<T0>{}, std::forward<Visitor>(vis),
0105                       std::forward<Variant>(v));
0106   }
0107 };
0108 
0109 template <class A>
0110 decltype(auto) metadata_impl(A&& a, decltype(a.metadata(), 0)) {
0111   return std::forward<A>(a).metadata();
0112 }
0113 
0114 template <class A>
0115 axis::null_type& metadata_impl(A&&, float) {
0116   static axis::null_type null_value;
0117   return null_value;
0118 }
0119 
0120 } // namespace detail
0121 
0122 namespace axis {
0123 namespace traits {
0124 
0125 /** Value type for axis type.
0126 
0127   Doxygen does not render this well. This is a meta-function (template alias), it accepts
0128   an axis type and returns the value type.
0129 
0130   The value type is deduced from the argument of the `Axis::index` method. Const
0131   references are decayed to the their value types, for example, the type deduced for
0132   `Axis::index(const int&)` is `int`.
0133 
0134   The deduction always succeeds if the axis type models the Axis concept correctly. Errors
0135   come from violations of the concept, in particular, an index method that is templated or
0136   overloaded is not allowed.
0137 
0138   @tparam Axis axis type.
0139 */
0140 template <class Axis>
0141 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0142 using value_type = typename detail::value_type_deducer<Axis>::type;
0143 #else
0144 struct value_type;
0145 #endif
0146 
0147 /** Whether axis is continuous or discrete.
0148 
0149   Doxygen does not render this well. This is a meta-function (template alias), it accepts
0150   an axis type and returns a compile-time boolean.
0151 
0152   If the boolean is true, the axis is continuous (covers a continuous range of values).
0153   Otherwise it is discrete (covers discrete values).
0154 */
0155 template <class Axis>
0156 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0157 using is_continuous = typename std::is_floating_point<traits::value_type<Axis>>::type;
0158 #else
0159 struct is_continuous;
0160 #endif
0161 
0162 /** Meta-function to detect whether an axis is reducible.
0163 
0164   Doxygen does not render this well. This is a meta-function (template alias), it accepts
0165   an axis type and represents compile-time boolean which is true or false, depending on
0166   whether the axis can be reduced with boost::histogram::algorithm::reduce().
0167 
0168   An axis can be made reducible by adding a special constructor, see Axis concept for
0169   details.
0170 
0171   @tparam Axis axis type.
0172  */
0173 template <class Axis>
0174 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0175 using is_reducible = std::is_constructible<Axis, const Axis&, axis::index_type,
0176                                            axis::index_type, unsigned>;
0177 #else
0178 struct is_reducible;
0179 #endif
0180 
0181 /** Get axis options for axis type.
0182 
0183   Doxygen does not render this well. This is a meta-function (template alias), it accepts
0184   an axis type and returns the boost::histogram::axis::option::bitset.
0185 
0186   If Axis::options() is valid and constexpr, get_options is the corresponding
0187   option type. Otherwise, it is boost::histogram::axis::option::growth_t, if the
0188   axis has a method `update`, else boost::histogram::axis::option::none_t.
0189 
0190   @tparam Axis axis type
0191 */
0192 template <class Axis>
0193 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0194 using get_options = decltype(detail::traits_options<Axis>(detail::priority<2>{}));
0195 #else
0196 struct get_options;
0197 #endif
0198 
0199 /** Meta-function to detect whether an axis is inclusive.
0200 
0201   Doxygen does not render this well. This is a meta-function (template alias), it accepts
0202   an axis type and represents compile-time boolean which is true or false, depending on
0203   whether the axis is inclusive or not.
0204 
0205   An inclusive axis has a bin for every possible input value. In other words, all
0206   possible input values always end up in a valid cell and there is no need to keep track
0207   of input tuples that need to be discarded. A histogram which consists entirely of
0208   inclusive axes can be filled more efficiently, which can be a factor 2 faster.
0209 
0210   An axis with underflow and overflow bins is always inclusive, but an axis may be
0211   inclusive under other conditions. The meta-function checks for the method `constexpr
0212   static bool inclusive()`, and uses the result. If this method is not present, it uses
0213   get_options<Axis> and checks whether the underflow and overflow bits are present.
0214 
0215   @tparam Axis axis type
0216 */
0217 template <class Axis>
0218 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0219 using is_inclusive = decltype(detail::traits_is_inclusive<Axis>(detail::priority<1>{}));
0220 #else
0221 struct is_inclusive;
0222 #endif
0223 
0224 /** Meta-function to detect whether an axis is ordered.
0225 
0226   Doxygen does not render this well. This is a meta-function (template alias), it accepts
0227   an axis type and returns a compile-time boolean. If the boolean is true, the axis is
0228   ordered.
0229 
0230   The meta-function checks for the method `constexpr static bool ordered()`, and uses the
0231   result. If this method is not present, it returns true if the value type of the Axis is
0232   arithmetic and false otherwise.
0233 
0234   An ordered axis has a value type that is ordered, which means that indices i <
0235   j < k implies either value(i) < value(j) < value(k) or value(i) > value(j) > value(k)
0236   for all i,j,k. For example, the integer axis is ordered, but the category axis is not.
0237   Axis which are not ordered must not have underflow bins, because they only have an
0238   "other" category, which is identified with the overflow bin if it is available.
0239 
0240   @tparam Axis axis type
0241 */
0242 template <class Axis>
0243 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0244 using is_ordered = decltype(detail::traits_is_ordered<Axis>(detail::priority<1>{}));
0245 #else
0246 struct is_ordered;
0247 #endif
0248 
0249 /** Returns axis options as unsigned integer.
0250 
0251   See get_options for details.
0252 
0253   @param axis any axis instance
0254 */
0255 template <class Axis>
0256 constexpr unsigned options(const Axis& axis) noexcept {
0257   (void)axis;
0258   return get_options<Axis>::value;
0259 }
0260 
0261 // specialization for variant
0262 template <class... Ts>
0263 unsigned options(const variant<Ts...>& axis) noexcept {
0264   return axis.options();
0265 }
0266 
0267 /** Returns true if axis is inclusive or false.
0268 
0269   See is_inclusive for details.
0270 
0271   @param axis any axis instance
0272 */
0273 template <class Axis>
0274 constexpr bool inclusive(const Axis& axis) noexcept {
0275   (void)axis;
0276   return is_inclusive<Axis>::value;
0277 }
0278 
0279 // specialization for variant
0280 template <class... Ts>
0281 bool inclusive(const variant<Ts...>& axis) noexcept {
0282   return axis.inclusive();
0283 }
0284 
0285 /** Returns true if axis is ordered or false.
0286 
0287   See is_ordered for details.
0288 
0289   @param axis any axis instance
0290 */
0291 template <class Axis>
0292 constexpr bool ordered(const Axis& axis) noexcept {
0293   (void)axis;
0294   return is_ordered<Axis>::value;
0295 }
0296 
0297 // specialization for variant
0298 template <class... Ts>
0299 bool ordered(const variant<Ts...>& axis) noexcept {
0300   return axis.ordered();
0301 }
0302 
0303 /** Returns true if axis is continuous or false.
0304 
0305   See is_continuous for details.
0306 
0307   @param axis any axis instance
0308 */
0309 template <class Axis>
0310 constexpr bool continuous(const Axis& axis) noexcept {
0311   (void)axis;
0312   return is_continuous<Axis>::value;
0313 }
0314 
0315 // specialization for variant
0316 template <class... Ts>
0317 bool continuous(const variant<Ts...>& axis) noexcept {
0318   return axis.continuous();
0319 }
0320 
0321 /** Returns axis size plus any extra bins for under- and overflow.
0322 
0323   @param axis any axis instance
0324 */
0325 template <class Axis>
0326 index_type extent(const Axis& axis) noexcept {
0327   const auto opt = options(axis);
0328   return axis.size() + (opt & option::underflow ? 1 : 0) +
0329          (opt & option::overflow ? 1 : 0);
0330 }
0331 
0332 /** Returns reference to metadata of an axis.
0333 
0334   If the expression x.metadata() for an axis instance `x` (maybe const) is valid, return
0335   the result. Otherwise, return a reference to a static instance of
0336   boost::histogram::axis::null_type.
0337 
0338   @param axis any axis instance
0339 */
0340 template <class Axis>
0341 decltype(auto) metadata(Axis&& axis) noexcept {
0342   return detail::metadata_impl(std::forward<Axis>(axis), 0);
0343 }
0344 
0345 /** Returns axis value for index.
0346 
0347   If the axis has no `value` method, throw std::runtime_error. If the method exists and
0348   accepts a floating point index, pass the index and return the result. If the method
0349   exists but accepts only integer indices, cast the floating point index to int, pass this
0350   index and return the result.
0351 
0352   @param axis any axis instance
0353   @param index floating point axis index
0354 */
0355 template <class Axis>
0356 decltype(auto) value(const Axis& axis, real_index_type index) {
0357   return detail::value_method_switch(
0358       [index](const auto& a) { return a.value(static_cast<index_type>(index)); },
0359       [index](const auto& a) { return a.value(index); }, axis, detail::priority<1>{});
0360 }
0361 
0362 /** Returns axis value for index if it is convertible to target type or throws.
0363 
0364   Like boost::histogram::axis::traits::value, but converts the result into the requested
0365   return type. If the conversion is not possible, throws std::runtime_error.
0366 
0367   @tparam Result requested return type
0368   @tparam Axis axis type
0369   @param axis any axis instance
0370   @param index floating point axis index
0371 */
0372 template <class Result, class Axis>
0373 Result value_as(const Axis& axis, real_index_type index) {
0374   return detail::try_cast<Result, std::runtime_error>(
0375       axis::traits::value(axis, index)); // avoid conversion warning
0376 }
0377 
0378 /** Returns axis index for value.
0379 
0380   Throws std::invalid_argument if the value argument is not implicitly convertible.
0381 
0382   @param axis any axis instance
0383   @param value argument to be passed to `index` method
0384 */
0385 template <class Axis, class U>
0386 axis::index_type index(const Axis& axis, const U& value) noexcept(
0387     std::is_convertible<U, value_type<Axis>>::value) {
0388   return axis.index(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
0389 }
0390 
0391 // specialization for variant
0392 template <class... Ts, class U>
0393 axis::index_type index(const variant<Ts...>& axis, const U& value) {
0394   return axis.index(value);
0395 }
0396 
0397 /** Return axis rank (how many arguments it processes).
0398 
0399   @param axis any axis instance
0400 */
0401 // gcc workaround: must use unsigned int not unsigned as return type
0402 template <class Axis>
0403 constexpr unsigned int rank(const Axis& axis) {
0404   (void)axis;
0405   using T = value_type<Axis>;
0406   // cannot use mp_eval_or since T could be a fixed-sized sequence
0407   return mp11::mp_eval_if_not<detail::is_tuple<T>, mp11::mp_size_t<1>, mp11::mp_size,
0408                               T>::value;
0409 }
0410 
0411 // specialization for variant
0412 // gcc workaround: must use unsigned int not unsigned as return type
0413 template <class... Ts>
0414 unsigned int rank(const axis::variant<Ts...>& axis) {
0415   return detail::variant_access::visit(
0416       [](const auto& a) { return axis::traits::rank(a); }, axis);
0417 }
0418 
0419 /** Returns pair of axis index and shift for the value argument.
0420 
0421   Throws `std::invalid_argument` if the value argument is not implicitly convertible to
0422   the argument expected by the `index` method. If the result of
0423   boost::histogram::axis::traits::get_options<decltype(axis)> has the growth flag set,
0424   call `update` method with the argument and return the result. Otherwise, call `index`
0425   and return the pair of the result and a zero shift.
0426 
0427   @param axis any axis instance
0428   @param value argument to be passed to `update` or `index` method
0429 */
0430 template <class Axis, class U>
0431 std::pair<index_type, index_type> update(Axis& axis, const U& value) noexcept(
0432     std::is_convertible<U, value_type<Axis>>::value) {
0433   return detail::static_if_c<get_options<Axis>::test(option::growth)>(
0434       [&value](auto& a) {
0435         return a.update(detail::try_cast<value_type<Axis>, std::invalid_argument>(value));
0436       },
0437       [&value](auto& a) -> std::pair<index_type, index_type> {
0438         return {axis::traits::index(a, value), 0};
0439       },
0440       axis);
0441 }
0442 
0443 // specialization for variant
0444 template <class... Ts, class U>
0445 std::pair<index_type, index_type> update(variant<Ts...>& axis, const U& value) {
0446   return visit([&value](auto& a) { return a.update(value); }, axis);
0447 }
0448 
0449 /** Returns bin width at axis index.
0450 
0451   If the axis has no `value` method, throw std::runtime_error. If the method exists and
0452   accepts a floating point index, return the result of `axis.value(index + 1) -
0453   axis.value(index)`. If the method exists but accepts only integer indices, return 0.
0454 
0455   @param axis any axis instance
0456   @param index bin index
0457  */
0458 template <class Axis>
0459 decltype(auto) width(const Axis& axis, index_type index) {
0460   return detail::value_method_switch(
0461       [](const auto&) { return 0; },
0462       [index](const auto& a) { return a.value(index + 1) - a.value(index); }, axis,
0463       detail::priority<1>{});
0464 }
0465 
0466 /** Returns bin width at axis index.
0467 
0468   Like boost::histogram::axis::traits::width, but converts the result into the requested
0469   return type. If the conversion is not possible, throw std::runtime_error.
0470 
0471   @param axis any axis instance
0472   @param index bin index
0473  */
0474 template <class Result, class Axis>
0475 Result width_as(const Axis& axis, index_type index) {
0476   return detail::value_method_switch(
0477       [](const auto&) { return Result{}; },
0478       [index](const auto& a) {
0479         return detail::try_cast<Result, std::runtime_error>(a.value(index + 1) -
0480                                                             a.value(index));
0481       },
0482       axis, detail::priority<1>{});
0483 }
0484 
0485 } // namespace traits
0486 } // namespace axis
0487 } // namespace histogram
0488 } // namespace boost
0489 
0490 #endif