Line | Branch | Exec | Source |
---|---|---|---|
1 | // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de> | ||
2 | // SPDX-License-Identifier: MIT | ||
3 | /*! | ||
4 | * \file | ||
5 | * \ingroup Common | ||
6 | * \copydoc GridFormat::MDIndex | ||
7 | */ | ||
8 | #ifndef GRIDFORMAT_COMMON_MD_INDEX_HPP_ | ||
9 | #define GRIDFORMAT_COMMON_MD_INDEX_HPP_ | ||
10 | |||
11 | #include <cassert> | ||
12 | #include <ostream> | ||
13 | #include <utility> | ||
14 | #include <concepts> | ||
15 | #include <iterator> | ||
16 | #include <algorithm> | ||
17 | #include <initializer_list> | ||
18 | #include <functional> | ||
19 | #include <numeric> | ||
20 | |||
21 | #include <gridformat/common/concepts.hpp> | ||
22 | #include <gridformat/common/ranges.hpp> | ||
23 | #include <gridformat/common/reserved_vector.hpp> | ||
24 | #include <gridformat/common/iterator_facades.hpp> | ||
25 | #include <gridformat/common/md_layout.hpp> | ||
26 | |||
27 | namespace GridFormat { | ||
28 | |||
29 | //! \addtogroup Common | ||
30 | //! \{ | ||
31 | |||
32 | //! Represents a multi-dimensional index. | ||
33 | class MDIndex { | ||
34 | static constexpr std::size_t buffered_dimensions = 5; | ||
35 | using Indices = ReservedVector<std::size_t, buffered_dimensions>; | ||
36 | |||
37 | public: | ||
38 | 17621 | MDIndex() = default; | |
39 | |||
40 | //! Construct from a range of indices | ||
41 | template<Concepts::MDRange<1> R> | ||
42 | 20569 | explicit MDIndex(R&& indices) { | |
43 |
3/5✓ Branch 1 taken 1 times.
✓ Branch 2 taken 18070 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
20569 | _indices.reserve(Ranges::size(indices)); |
44 |
2/4✓ Branch 1 taken 18071 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18071 times.
✗ Branch 5 not taken.
|
20569 | std::ranges::copy(indices, std::back_inserter(_indices)); |
45 | 20569 | } | |
46 | |||
47 | //! Construct from a vector of indices | ||
48 | template<std::integral T> | ||
49 | 30 | explicit MDIndex(const std::initializer_list<T>& indices) { | |
50 |
1/2✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
30 | _indices.reserve(Ranges::size(indices)); |
51 |
2/4✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
|
30 | std::ranges::copy(indices, std::back_inserter(_indices)); |
52 | 30 | } | |
53 | |||
54 | //! Zero-initialize a md-index with a given size | ||
55 | 92476 | explicit MDIndex(std::integral auto size) { | |
56 |
1/2✓ Branch 1 taken 92476 times.
✗ Branch 2 not taken.
|
92476 | _indices.resize(size, std::size_t{0}); |
57 | 92476 | } | |
58 | |||
59 | //! Zero-initialize a md-index with a given layout | ||
60 | 92476 | explicit MDIndex(const MDLayout& layout) | |
61 | 92476 | : MDIndex(layout.dimension()) | |
62 | 92476 | {} | |
63 | |||
64 | 408571 | auto begin() const { return _indices.begin(); } | |
65 | 38885 | auto begin() { return _indices.begin(); } | |
66 | |||
67 | 58675 | auto end() const { return _indices.end(); } | |
68 | 25923 | auto end() { return _indices.end(); } | |
69 | |||
70 | 416611 | std::size_t size() const { | |
71 | 416611 | return _indices.size(); | |
72 | } | ||
73 | |||
74 | 6206968 | std::size_t get(unsigned int codim) const { | |
75 | 6206968 | return _indices[codim]; | |
76 | } | ||
77 | |||
78 | 2068106 | void set(unsigned int codim, std::size_t index) { | |
79 | 2068106 | _indices[codim] = index; | |
80 | 2068106 | } | |
81 | |||
82 | 676018 | bool operator==(const MDIndex& other) const { | |
83 | 676018 | return std::ranges::equal(_indices, other._indices); | |
84 | } | ||
85 | |||
86 | 12962 | MDIndex& operator+=(const MDIndex& other) { | |
87 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 12962 times.
|
12962 | if (size() != other.size()) |
88 | ✗ | throw ValueError("MDIndex size mismatch"); | |
89 | 12962 | std::transform( | |
90 | begin(), | ||
91 | end(), | ||
92 | other.begin(), | ||
93 | begin(), | ||
94 | std::plus<std::size_t>{} | ||
95 | ); | ||
96 | 12962 | return *this; | |
97 | } | ||
98 | |||
99 | 1 | MDIndex operator+(const MDIndex& other) const { | |
100 | 1 | MDIndex result(*this); | |
101 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | result += other; |
102 | 1 | return result; | |
103 | ✗ | } | |
104 | |||
105 | 1 | friend std::ostream& operator<<(std::ostream& s, const MDIndex& md_index) { | |
106 | 1 | s << "("; | |
107 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (md_index._indices.size() > 0) { |
108 | 1 | s << md_index._indices[0]; | |
109 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | std::ranges::for_each(md_index._indices | std::views::drop(1), [&] (const auto idx) { |
110 | 1 | s << "," << idx; | |
111 | 1 | }); | |
112 | } | ||
113 | 1 | s << ")"; | |
114 | 1 | return s; | |
115 | } | ||
116 | |||
117 | private: | ||
118 | Indices _indices; | ||
119 | }; | ||
120 | |||
121 | |||
122 | //! A range over multi-dimensional indices within a given layout | ||
123 | class MDIndexRange { | ||
124 | class Iterator | ||
125 | : public ForwardIteratorFacade<Iterator, MDIndex, const MDIndex&> { | ||
126 | public: | ||
127 | Iterator() = default; | ||
128 | 92476 | Iterator(const MDLayout& layout, bool is_end = false) | |
129 | 92476 | : _layout{&layout} | |
130 | 92476 | , _current{layout} | |
131 |
1/2✓ Branch 1 taken 92476 times.
✗ Branch 2 not taken.
|
92476 | , _last_dim{layout.dimension() - 1} { |
132 |
2/2✓ Branch 0 taken 46238 times.
✓ Branch 1 taken 46238 times.
|
92476 | if (is_end) |
133 |
1/2✓ Branch 1 taken 46238 times.
✗ Branch 2 not taken.
|
46238 | _current.set(_last_dim, layout.extent(_last_dim)); |
134 | 92476 | } | |
135 | |||
136 | private: | ||
137 | friend IteratorAccess; | ||
138 | |||
139 | 408583 | const MDIndex& _dereference() const { | |
140 | 408583 | return _current; | |
141 | } | ||
142 | |||
143 | 676002 | bool _is_equal(const Iterator& other) const { | |
144 |
3/4✓ Branch 0 taken 676002 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 46458 times.
✓ Branch 4 taken 629544 times.
|
676002 | return _layout == other._layout && _current == other._current; |
145 | } | ||
146 | |||
147 | 471683 | void _increment() { | |
148 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 471683 times.
|
471683 | assert(!_is_end()); |
149 | 471683 | unsigned int codim = 0; | |
150 | while (true) { | ||
151 | 645832 | _increment_at(codim); | |
152 |
2/2✓ Branch 2 taken 220337 times.
✓ Branch 3 taken 425495 times.
|
645832 | if (_current.get(codim) >= _layout->extent(codim)) { |
153 |
2/2✓ Branch 0 taken 46188 times.
✓ Branch 1 taken 174149 times.
|
220337 | if (codim == _last_dim) |
154 | 46188 | break; | |
155 | 174149 | _current.set(codim, 0); | |
156 | 174149 | codim++; | |
157 | } else { | ||
158 | 425495 | break; | |
159 | } | ||
160 | } | ||
161 | 471683 | } | |
162 | |||
163 | 645832 | void _increment_at(unsigned int codim) { | |
164 | 645832 | _current.set(codim, _current.get(codim) + 1); | |
165 | 645832 | } | |
166 | |||
167 | 471683 | bool _is_end() const { | |
168 | 471683 | return _current.get(_last_dim) >= _layout->extent(_last_dim); | |
169 | } | ||
170 | |||
171 | const MDLayout* _layout; | ||
172 | MDIndex _current; | ||
173 | std::size_t _last_dim; | ||
174 | }; | ||
175 | |||
176 | public: | ||
177 | 1876 | explicit MDIndexRange(MDLayout layout) | |
178 | 1876 | : _layout{std::move(layout)} | |
179 | 1876 | {} | |
180 | |||
181 | explicit MDIndexRange(const std::vector<std::size_t>& dimensions) | ||
182 | : MDIndexRange(MDLayout{dimensions}) | ||
183 | {} | ||
184 | |||
185 | 3 | explicit MDIndexRange(const std::initializer_list<std::size_t>& dimensions) | |
186 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | : MDIndexRange(MDLayout{dimensions}) |
187 | 3 | {} | |
188 | |||
189 | 46238 | auto begin() const { return Iterator{_layout}; } | |
190 | 46238 | auto end() const { return Iterator{_layout, true}; } | |
191 | |||
192 | const MDLayout& layout() const { | ||
193 | return _layout; | ||
194 | } | ||
195 | |||
196 | 28539 | std::size_t size() const { | |
197 | 28539 | return _layout.number_of_entries(); | |
198 | } | ||
199 | |||
200 | 834 | std::size_t size(unsigned int codim) const { | |
201 | 834 | return _layout.extent(codim); | |
202 | } | ||
203 | |||
204 | private: | ||
205 | MDLayout _layout; | ||
206 | }; | ||
207 | |||
208 | |||
209 | #ifndef DOXYGEN | ||
210 | namespace Detail { | ||
211 | |||
212 | template<std::ranges::range R> | ||
213 | 35248 | std::size_t flat_index_from_sub_sizes(const MDIndex& index, R&& sub_sizes) { | |
214 |
2/4✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35248 times.
|
35248 | assert(index.size() == Ranges::size(sub_sizes)); |
215 |
2/4✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35248 times.
✗ Branch 5 not taken.
|
35248 | auto offsets = std::views::iota(std::size_t{0}, index.size()) |
216 |
2/4✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35248 times.
✗ Branch 5 not taken.
|
70496 | | std::views::transform([&] (const std::integral auto dim) { |
217 | 88349 | return index.get(dim)*sub_sizes[dim]; | |
218 | }); | ||
219 |
3/6✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35248 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 35248 times.
✗ Branch 8 not taken.
|
35248 | return std::accumulate( |
220 | std::ranges::begin(offsets), | ||
221 | std::ranges::end(offsets), | ||
222 | 0 | ||
223 | 35248 | ); | |
224 | } | ||
225 | |||
226 | } // namespace Detail | ||
227 | #endif // DOXYGEN | ||
228 | |||
229 | //! Compute the flat index from a multidimensional index and layout | ||
230 | 35248 | inline std::size_t flat_index(const MDIndex& index, const MDLayout& layout) { | |
231 |
3/6✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35248 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 35248 times.
|
35248 | assert(index.size() == layout.dimension()); |
232 |
2/4✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 35248 times.
✗ Branch 5 not taken.
|
35248 | auto sub_sizes = std::views::iota(std::size_t{0}, index.size()) |
233 |
1/2✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
|
35248 | | std::views::transform([&] (const std::integral auto dim) { |
234 |
2/2✓ Branch 1 taken 53101 times.
✓ Branch 2 taken 35248 times.
|
88349 | if (dim < layout.dimension() - 1) |
235 | 53101 | return layout.number_of_entries(dim + 1); | |
236 | 35248 | return std::size_t{1}; | |
237 |
1/2✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
|
35248 | }); |
238 |
1/2✓ Branch 1 taken 35248 times.
✗ Branch 2 not taken.
|
70496 | return Detail::flat_index_from_sub_sizes(index, sub_sizes); |
239 | } | ||
240 | |||
241 | //! \} group Common | ||
242 | |||
243 | } // namespace GridFormat | ||
244 | |||
245 | #endif // GRIDFORMAT_COMMON_MD_INDEX_HPP_ | ||
246 |