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