Blame view

3rdparty/boost_1_81_0/boost/histogram/detail/linearize.hpp 4.78 KB
63e88f80   Hu Chunming   提交三方库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  // Copyright 2019 Hans Dembinski
  //
  // Distributed under the Boost Software License, Version 1.0.
  // (See accompanying file LICENSE_1_0.txt
  // or copy at http://www.boost.org/LICENSE_1_0.txt)
  
  #ifndef BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
  #define BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
  
  #include <boost/histogram/axis/option.hpp>
  #include <boost/histogram/axis/traits.hpp>
  #include <boost/histogram/axis/variant.hpp>
  #include <boost/histogram/detail/optional_index.hpp>
  #include <boost/histogram/fwd.hpp>
  #include <boost/histogram/multi_index.hpp>
  #include <cassert>
  
  namespace boost {
  namespace histogram {
  namespace detail {
  
  // initial offset to out must be set;
  // this faster code can be used if all axes are inclusive
  template <class Opts>
  std::size_t linearize(Opts, std::size_t& out, const std::size_t stride,
                        const axis::index_type size, const axis::index_type idx) {
    constexpr bool u = Opts::test(axis::option::underflow);
    constexpr bool o = Opts::test(axis::option::overflow);
    assert(idx >= (u ? -1 : 0));
    assert(idx < (o ? size + 1 : size));
    assert(idx >= 0 || static_cast<std::size_t>(-idx * stride) <= out);
    out += idx * stride;
    return size + u + o;
  }
  
  // initial offset to out must be set
  // this slower code must be used if not all axes are inclusive
  template <class Opts>
  std::size_t linearize(Opts, optional_index& out, const std::size_t stride,
                        const axis::index_type size, const axis::index_type idx) {
    constexpr bool u = Opts::test(axis::option::underflow);
    constexpr bool o = Opts::test(axis::option::overflow);
    assert(idx >= -1);
    assert(idx < size + 1);
    const bool is_valid = (u || idx >= 0) && (o || idx < size);
    if (is_valid)
      out += idx * stride;
    else
      out = invalid_index;
    return size + u + o;
  }
  
  template <class Index, class Axis, class Value>
  std::size_t linearize(Index& out, const std::size_t stride, const Axis& ax,
                        const Value& v) {
    // mask options to reduce no. of template instantiations
    constexpr auto opts = axis::traits::get_options<Axis>{} &
                          (axis::option::underflow | axis::option::overflow);
    return linearize(opts, out, stride, ax.size(), axis::traits::index(ax, v));
  }
  
  /**
    Must be used when axis is potentially growing. Also works for non-growing axis.
  
    Initial offset of `out` must be zero. We cannot assert on this, because we do not
    know if this is the first call of `linearize_growth`.
  */
  template <class Index, class Axis, class Value>
  std::size_t linearize_growth(Index& out, axis::index_type& shift,
                               const std::size_t stride, Axis& a, const Value& v) {
    axis::index_type idx;
    std::tie(idx, shift) = axis::traits::update(a, v);
    constexpr bool u = axis::traits::get_options<Axis>::test(axis::option::underflow);
    if (u) ++idx;
    if (std::is_same<Index, std::size_t>::value) {
      assert(idx < axis::traits::extent(a));
      out += idx * stride;
    } else {
      if (0 <= idx && idx < axis::traits::extent(a))
        out += idx * stride;
      else
        out = invalid_index;
    }
    return axis::traits::extent(a);
  }
  
  // initial offset of out must be zero
  template <class A>
  std::size_t linearize_index(optional_index& out, const std::size_t stride, const A& ax,
                              const axis::index_type idx) noexcept {
    const auto opt = axis::traits::get_options<A>();
    const axis::index_type begin = opt & axis::option::underflow ? -1 : 0;
    const axis::index_type end = opt & axis::option::overflow ? ax.size() + 1 : ax.size();
    const axis::index_type extent = end - begin;
    // i may be arbitrarily out of range
    if (begin <= idx && idx < end)
      out += (idx - begin) * stride;
    else
      out = invalid_index;
    return extent;
  }
  
  template <class A, std::size_t N>
  optional_index linearize_indices(const A& axes, const multi_index<N>& indices) noexcept {
    assert(axes_rank(axes) == detail::size(indices));
  
    optional_index idx{0}; // offset not used by linearize_index
    auto stride = static_cast<std::size_t>(1);
    using std::begin;
    auto i = begin(indices);
    for_each_axis(axes,
                  [&](const auto& a) { stride *= linearize_index(idx, stride, a, *i++); });
    return idx;
  }
  
  template <class Index, class... Ts, class Value>
  std::size_t linearize(Index& o, const std::size_t s, const axis::variant<Ts...>& a,
                        const Value& v) {
    return axis::visit([&o, &s, &v](const auto& a) { return linearize(o, s, a, v); }, a);
  }
  
  template <class Index, class... Ts, class Value>
  std::size_t linearize_growth(Index& o, axis::index_type& sh, const std::size_t st,
                               axis::variant<Ts...>& a, const Value& v) {
    return axis::visit([&](auto& a) { return linearize_growth(o, sh, st, a, v); }, a);
  }
  
  } // namespace detail
  } // namespace histogram
  } // namespace boost
  
  #endif // BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP