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