reduce_command.hpp 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Copyright 2020 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_DETAIL_REDUCE_COMMAND_HPP
  7. #define BOOST_HISTOGRAM_DETAIL_REDUCE_COMMAND_HPP
  8. #include <boost/histogram/detail/span.hpp>
  9. #include <boost/histogram/fwd.hpp>
  10. #include <boost/throw_exception.hpp>
  11. #include <cassert>
  12. #include <stdexcept>
  13. #include <string>
  14. namespace boost {
  15. namespace histogram {
  16. namespace detail {
  17. struct reduce_command {
  18. static constexpr unsigned unset = static_cast<unsigned>(-1);
  19. unsigned iaxis = unset;
  20. enum class range_t : char {
  21. none,
  22. indices,
  23. values,
  24. } range = range_t::none;
  25. union {
  26. axis::index_type index;
  27. double value;
  28. } begin{0}, end{0};
  29. unsigned merge = 0; // default value indicates unset option
  30. bool crop = false;
  31. // for internal use by the reduce algorithm
  32. bool is_ordered = true;
  33. bool use_underflow_bin = true;
  34. bool use_overflow_bin = true;
  35. };
  36. // - puts commands in correct axis order
  37. // - sets iaxis for positional commands
  38. // - detects and fails on invalid settings
  39. // - fuses merge commands with non-merge commands
  40. inline void normalize_reduce_commands(span<reduce_command> out,
  41. span<const reduce_command> in) {
  42. unsigned iaxis = 0;
  43. for (const auto& o_in : in) {
  44. assert(o_in.merge > 0);
  45. if (o_in.iaxis != reduce_command::unset && o_in.iaxis >= out.size())
  46. BOOST_THROW_EXCEPTION(std::invalid_argument("invalid axis index"));
  47. auto& o_out = out.begin()[o_in.iaxis == reduce_command::unset ? iaxis : o_in.iaxis];
  48. if (o_out.merge == 0) {
  49. o_out = o_in;
  50. } else {
  51. // Some command was already set for this axis, try to fuse commands.
  52. if (!((o_in.range == reduce_command::range_t::none) ^
  53. (o_out.range == reduce_command::range_t::none)) ||
  54. (o_out.merge > 1 && o_in.merge > 1))
  55. BOOST_THROW_EXCEPTION(std::invalid_argument(
  56. "multiple conflicting reduce commands for axis " +
  57. std::to_string(o_in.iaxis == reduce_command::unset ? iaxis : o_in.iaxis)));
  58. if (o_in.range != reduce_command::range_t::none) {
  59. o_out.range = o_in.range;
  60. o_out.begin = o_in.begin;
  61. o_out.end = o_in.end;
  62. } else {
  63. o_out.merge = o_in.merge;
  64. }
  65. }
  66. ++iaxis;
  67. }
  68. iaxis = 0;
  69. for (auto& o : out) o.iaxis = iaxis++;
  70. }
  71. } // namespace detail
  72. } // namespace histogram
  73. } // namespace boost
  74. #endif