GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/md_layout.hpp
Date: 2025-03-26 17:08:15
Exec Total Coverage
Lines: 87 87 100.0%
Functions: 100 100 100.0%
Branches: 50 89 56.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::MDLayout
7 */
8 #ifndef GRIDFORMAT_COMMON_MD_LAYOUT_HPP_
9 #define GRIDFORMAT_COMMON_MD_LAYOUT_HPP_
10
11 #include <ostream>
12 #include <utility>
13 #include <vector>
14 #include <numeric>
15 #include <iterator>
16 #include <algorithm>
17 #include <initializer_list>
18 #include <type_traits>
19 #include <string>
20
21 #include <gridformat/common/reserved_vector.hpp>
22 #include <gridformat/common/type_traits.hpp>
23 #include <gridformat/common/concepts.hpp>
24 #include <gridformat/common/ranges.hpp>
25
26 namespace GridFormat {
27
28 #ifndef DOXYGEN
29 namespace Detail {
30
31 template<Concepts::StaticallySizedRange R, typename Iterator>
32 570540 constexpr void set_sub_extents(Iterator it) {
33 570540 *it = static_size<R>;
34 570540 ++it;
35 if constexpr (has_sub_range<R>)
36 2 set_sub_extents<std::ranges::range_value_t<R>>(it);
37 570540 }
38
39 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
40 2117752 auto get_md_extents() {
41 if constexpr (Concepts::Scalar<T>)
42 902296 return std::array<std::size_t, 0>{};
43 else {
44 std::array<std::size_t, mdrange_dimension<T>> extents;
45 1215456 extents[0] = static_size<T>;
46 if constexpr (mdrange_dimension<T> > 1)
47 589022 Detail::set_sub_extents<std::ranges::range_value_t<T>>(extents.begin() + 1);
48 1215456 return extents;
49 }
50 }
51
52 template<typename T> requires(std::ranges::range<T> and !Concepts::StaticallySizedRange<T>)
53 1 auto get_md_extents(const T& range) {
54 static_assert(
55 Concepts::StaticallySizedRange<std::ranges::range_value_t<T>>,
56 "Sub-ranges must be statically sized in order to determine md layouts from md ranges"
57 );
58 std::array<std::size_t, mdrange_dimension<T>> extents;
59 1 extents[0] = Ranges::size(range);
60 1 Detail::set_sub_extents<std::ranges::range_value_t<T>>(extents.begin() + 1);
61 1 return extents;
62 }
63
64 } // namespace Detail
65 #endif // DOXYGEN
66
67 /*!
68 * \ingroup Common
69 * \brief Represents the layout (dimension, extents) of a multi-dimensional range
70 */
71 class MDLayout {
72 static constexpr std::size_t buffered_dimensions = 5;
73
74 public:
75 169 MDLayout() = default;
76
77 template<Concepts::MDRange<1> R>
78 648649 explicit MDLayout(R&& extents) {
79
1/2
✓ Branch 2 taken 324326 times.
✗ Branch 3 not taken.
648649 _extents.reserve(Ranges::size(extents));
80 if constexpr (!std::is_lvalue_reference_v<R>)
81
2/4
✓ Branch 1 taken 321726 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 321726 times.
✗ Branch 6 not taken.
643449 std::ranges::move(std::move(extents), std::back_inserter(_extents));
82 else
83
2/4
✓ Branch 1 taken 2600 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2600 times.
✗ Branch 5 not taken.
5200 std::ranges::copy(extents, std::back_inserter(_extents));
84 648649 }
85
86 template<std::integral T>
87 1206852 explicit MDLayout(const std::initializer_list<T>& extents) {
88
1/2
✓ Branch 2 taken 1072286 times.
✗ Branch 3 not taken.
1206852 _extents.reserve(Ranges::size(extents));
89
2/4
✓ Branch 1 taken 1072286 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1072286 times.
✗ Branch 5 not taken.
1206852 std::ranges::copy(extents, std::back_inserter(_extents));
90 1206852 }
91
92 5431 auto begin() const { return _extents.begin(); }
93 463 auto begin() { return _extents.begin(); }
94
95 5787 auto end() const { return _extents.end(); }
96 463 auto end() { return _extents.end(); }
97
98 3460670 std::size_t dimension() const {
99 3460670 return _extents.size();
100 }
101
102 4538773 std::size_t extent(unsigned int codim) const {
103 4538773 return _extents.at(codim);
104 }
105
106 627464 std::size_t number_of_entries() const {
107
2/2
✓ Branch 1 taken 8120 times.
✓ Branch 2 taken 619344 times.
627464 if (_is_scalar())
108 8120 return 1;
109 619344 return std::accumulate(
110 _extents.begin(),
111 _extents.end(),
112 std::size_t{1},
113 std::multiplies{}
114 619344 );
115 }
116
117 170662 std::size_t number_of_entries(unsigned int codim) const {
118
2/4
✓ Branch 1 taken 170662 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 170662 times.
✗ Branch 5 not taken.
170662 return sub_layout(codim).number_of_entries();
119 }
120
121 224895 MDLayout sub_layout(unsigned int codim) const {
122
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 224894 times.
224895 if (codim >= dimension())
123 throw ValueError(
124
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 "Given codim " + std::to_string(codim)
125
4/8
✓ 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.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
4 + " exceeds dimensions (" + std::to_string(dimension()) + ")"
126
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
3 );
127 224894 return MDLayout{std::vector<std::size_t>{
128 224894 _extents.begin() + codim,
129 224894 _extents.end()
130
2/4
✓ Branch 1 taken 224894 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 224894 times.
✗ Branch 5 not taken.
449788 }};
131 }
132
133 16845 bool operator==(const MDLayout& other) const {
134 16845 return std::ranges::equal(_extents, other._extents);
135 }
136
137 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
138 2100440 MDLayout& with_sub_layout_from() {
139
1/2
✓ Branch 1 taken 294511 times.
✗ Branch 2 not taken.
2100440 auto sub_extents = Detail::get_md_extents<T>();
140
1/2
✓ Branch 2 taken 1050221 times.
✗ Branch 3 not taken.
4200880 _extents.reserve(_extents.size() + sub_extents.size());
141
2/4
✓ Branch 1 taken 1050221 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1050221 times.
✗ Branch 6 not taken.
2100440 std::ranges::move(std::move(sub_extents), std::back_inserter(_extents));
142 2100440 return *this;
143 }
144
145 template<std::ranges::range T>
146 1 MDLayout& with_sub_layout_from(T&& range) {
147
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 auto sub_extents = Detail::get_md_extents<T>(std::forward<T>(range));
148
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 _extents.reserve(_extents.size() + sub_extents.size());
149
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 std::ranges::move(std::move(sub_extents), std::back_inserter(_extents));
150 1 return *this;
151 }
152
153 9 MDLayout& with_sub_layout(const MDLayout& layout) {
154 9 _extents.reserve(_extents.size() + layout.dimension());
155
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 9 times.
19 for (std::size_t i = 0; i < layout.dimension(); ++i)
156
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 _extents.push_back(layout.extent(i));
157 9 return *this;
158 }
159
160 template<std::ranges::range R>
161 6311 void export_to(R&& out) const {
162
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3370 times.
6311 if (Ranges::size(out) < dimension())
163
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw SizeError("Given output range is too small");
164 6310 std::ranges::copy(_extents, std::ranges::begin(out));
165 6310 }
166
167 3 friend std::ostream& operator<<(std::ostream& s, const MDLayout& layout) {
168 3 s << "(";
169
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (layout._extents.size() > 0) {
170 3 s << layout._extents[0];
171
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 std::ranges::for_each(layout._extents | std::views::drop(1), [&] (const auto ext) {
172 3 s << "," << ext;
173 3 });
174 }
175 3 s << ")";
176 3 return s;
177 }
178
179 private:
180 627464 bool _is_scalar() const {
181 627464 return _extents.size() == 0;
182 }
183
184 ReservedVector<std::size_t, buffered_dimensions> _extents;
185 };
186
187
188 /*!
189 * \ingroup Common
190 * \brief Get the layout of a range (or a scalar) whose size is known at compile-time
191 */
192 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
193 16902 MDLayout get_md_layout() {
194
1/2
✓ Branch 2 taken 8323 times.
✗ Branch 3 not taken.
16902 return MDLayout{Detail::get_md_extents<T>()};
195 }
196
197 /*!
198 * \ingroup Common
199 * \brief Get the layout for a range consisting of n instances of the range (or scalar)
200 * given as template argument, whose size is known at compile-time.
201 */
202 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
203 [[deprecated("use MDLayout{{n}}.with_sub_layout_from<T>() instead.")]]
204 MDLayout get_md_layout(std::size_t n) {
205 if constexpr (Concepts::Scalar<T>)
206 return MDLayout{{n}};
207 else {
208 std::array<std::size_t, mdrange_dimension<T> + 1> extents;
209 extents[0] = n;
210 Detail::set_sub_extents<T>(extents.begin() + 1);
211 return MDLayout{extents};
212 }
213 }
214
215 /*!
216 * \ingroup Common
217 * \brief Get the multi-dimensional layout for the given range
218 */
219 template<std::ranges::range R>
220 requires(
221 Concepts::StaticallySizedRange<std::ranges::range_value_t<R>> or
222 Concepts::Scalar<std::ranges::range_value_t<R>>
223 )
224 247 MDLayout get_md_layout(R&& r) {
225
7/11
✓ Branch 1 taken 179 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 179 times.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 179 times.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 179 times.
✗ Branch 11 not taken.
247 return MDLayout{{Ranges::size(r)}}.template with_sub_layout_from<std::ranges::range_value_t<R>>();
226 }
227
228 /*!
229 * \ingroup Common
230 * \brief Overload for scalars
231 */
232 template<Concepts::Scalar T>
233 1 MDLayout get_md_layout(const T&) {
234 1 return MDLayout{};
235 }
236
237 } // namespace GridFormat
238
239 #endif // GRIDFORMAT_COMMON_MD_LAYOUT_HPP_
240