GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/md_index.hpp
Date: 2024-11-20 14:41:59
Exec Total Coverage
Lines: 100 102 98.0%
Functions: 38 38 100.0%
Branches: 48 87 55.2%

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