• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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_LINEARIZE_HPP
8 #define BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
9 
10 #include <boost/histogram/axis/option.hpp>
11 #include <boost/histogram/axis/traits.hpp>
12 #include <boost/histogram/axis/variant.hpp>
13 #include <boost/histogram/detail/optional_index.hpp>
14 #include <boost/histogram/fwd.hpp>
15 #include <boost/histogram/multi_index.hpp>
16 #include <cassert>
17 
18 namespace boost {
19 namespace histogram {
20 namespace detail {
21 
22 // initial offset to out must be set
23 template <class Index, class Opts>
linearize(Opts,Index & out,const std::size_t stride,const axis::index_type size,const axis::index_type idx)24 std::size_t linearize(Opts, Index& out, const std::size_t stride,
25                       const axis::index_type size, const axis::index_type idx) {
26   constexpr bool u = Opts::test(axis::option::underflow);
27   constexpr bool o = Opts::test(axis::option::overflow);
28 
29   // must be non-const to avoid if constexpr warning from msvc
30   bool fast_track = std::is_same<Index, std::size_t>::value || (u && o);
31   if (fast_track) {
32     assert(idx >= (u ? -1 : 0));
33     assert(idx < (o ? size + 1 : size));
34     assert(idx >= 0 || static_cast<std::size_t>(-idx * stride) <= out);
35     out += idx * stride;
36   } else {
37     assert(idx >= -1);
38     assert(idx < size + 1);
39     // must be non-const to avoid if constexpr warning from msvc
40     bool is_valid = (u || idx >= 0) && (o || idx < size);
41     if (is_valid)
42       out += idx * stride;
43     else
44       out = invalid_index;
45   }
46   return size + u + o;
47 }
48 
49 template <class Index, class Axis, class Value>
linearize(Index & out,const std::size_t stride,const Axis & ax,const Value & v)50 std::size_t linearize(Index& out, const std::size_t stride, const Axis& ax,
51                       const Value& v) {
52   // mask options to reduce no. of template instantiations
53   constexpr auto opts = axis::traits::get_options<Axis>{} &
54                         (axis::option::underflow | axis::option::overflow);
55   return linearize(opts, out, stride, ax.size(), axis::traits::index(ax, v));
56 }
57 
58 // initial offset of out must be zero
59 template <class Index, class Axis, class Value>
linearize_growth(Index & out,axis::index_type & shift,const std::size_t stride,Axis & a,const Value & v)60 std::size_t linearize_growth(Index& out, axis::index_type& shift,
61                              const std::size_t stride, Axis& a, const Value& v) {
62   axis::index_type idx;
63   std::tie(idx, shift) = axis::traits::update(a, v);
64   constexpr bool u = axis::traits::get_options<Axis>::test(axis::option::underflow);
65   if (u) ++idx;
66   if (std::is_same<Index, std::size_t>::value) {
67     assert(idx < axis::traits::extent(a));
68     out += idx * stride;
69   } else {
70     if (0 <= idx && idx < axis::traits::extent(a))
71       out += idx * stride;
72     else
73       out = invalid_index;
74   }
75   return axis::traits::extent(a);
76 }
77 
78 // initial offset of out must be zero
79 template <class A>
linearize_index(optional_index & out,const std::size_t stride,const A & ax,const axis::index_type idx)80 std::size_t linearize_index(optional_index& out, const std::size_t stride, const A& ax,
81                             const axis::index_type idx) noexcept {
82   const auto opt = axis::traits::get_options<A>();
83   const axis::index_type begin = opt & axis::option::underflow ? -1 : 0;
84   const axis::index_type end = opt & axis::option::overflow ? ax.size() + 1 : ax.size();
85   const axis::index_type extent = end - begin;
86   // i may be arbitrarily out of range
87   if (begin <= idx && idx < end)
88     out += (idx - begin) * stride;
89   else
90     out = invalid_index;
91   return extent;
92 }
93 
94 template <class A, std::size_t N>
linearize_indices(const A & axes,const multi_index<N> & indices)95 optional_index linearize_indices(const A& axes, const multi_index<N>& indices) noexcept {
96   assert(axes_rank(axes) == detail::size(indices));
97 
98   optional_index idx{0}; // offset not used by linearize_index
99   auto stride = static_cast<std::size_t>(1);
100   using std::begin;
101   auto i = begin(indices);
102   for_each_axis(axes,
103                 [&](const auto& a) { stride *= linearize_index(idx, stride, a, *i++); });
104   return idx;
105 }
106 
107 template <class Index, class... Ts, class Value>
linearize(Index & o,const std::size_t s,const axis::variant<Ts...> & a,const Value & v)108 std::size_t linearize(Index& o, const std::size_t s, const axis::variant<Ts...>& a,
109                       const Value& v) {
110   return axis::visit([&o, &s, &v](const auto& a) { return linearize(o, s, a, v); }, a);
111 }
112 
113 template <class Index, class... Ts, class Value>
linearize_growth(Index & o,axis::index_type & sh,const std::size_t st,axis::variant<Ts...> & a,const Value & v)114 std::size_t linearize_growth(Index& o, axis::index_type& sh, const std::size_t st,
115                              axis::variant<Ts...>& a, const Value& v) {
116   return axis::visit([&](auto& a) { return linearize_growth(o, sh, st, a, v); }, a);
117 }
118 
119 } // namespace detail
120 } // namespace histogram
121 } // namespace boost
122 
123 #endif // BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
124