GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/md_layout.hpp
Date: 2024-11-20 14:41:59
Exec Total Coverage
Lines: 82 82 100.0%
Functions: 95 95 100.0%
Branches: 46 83 55.4%

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 570452 constexpr void set_sub_extents(Iterator it) {
33 570452 *it = static_size<R>;
34 570452 ++it;
35 if constexpr (has_sub_range<R>)
36 2 set_sub_extents<std::ranges::range_value_t<R>>(it);
37 570452 }
38
39 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
40 2116939 auto get_md_extents() {
41 if constexpr (Concepts::Scalar<T>)
42 901891 return std::array<std::size_t, 0>{};
43 else {
44 std::array<std::size_t, mdrange_dimension<T>> extents;
45 1215048 extents[0] = static_size<T>;
46 if constexpr (mdrange_dimension<T> > 1)
47 588846 Detail::set_sub_extents<std::ranges::range_value_t<T>>(extents.begin() + 1);
48 1215048 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 647991 explicit MDLayout(R&& extents) {
79
1/2
✓ Branch 2 taken 323996 times.
✗ Branch 3 not taken.
647991 _extents.reserve(Ranges::size(extents));
80 if constexpr (!std::is_lvalue_reference_v<R>)
81
2/4
✓ Branch 1 taken 321406 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 321406 times.
✗ Branch 6 not taken.
642811 std::ranges::move(std::move(extents), std::back_inserter(_extents));
82 else
83
2/4
✓ Branch 1 taken 2590 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2590 times.
✗ Branch 5 not taken.
5180 std::ranges::copy(extents, std::back_inserter(_extents));
84 647991 }
85
86 template<std::integral T>
87 1205803 explicit MDLayout(const std::initializer_list<T>& extents) {
88
1/2
✓ Branch 2 taken 1071759 times.
✗ Branch 3 not taken.
1205803 _extents.reserve(Ranges::size(extents));
89
2/4
✓ Branch 1 taken 1071759 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1071759 times.
✗ Branch 5 not taken.
1205803 std::ranges::copy(extents, std::back_inserter(_extents));
90 1205803 }
91
92 5431 auto begin() const { return _extents.begin(); }
93 447 auto begin() { return _extents.begin(); }
94
95 5787 auto end() const { return _extents.end(); }
96 447 auto end() { return _extents.end(); }
97
98 3458031 std::size_t dimension() const {
99 3458031 return _extents.size();
100 }
101
102 4535711 std::size_t extent(unsigned int codim) const {
103 4535711 return _extents.at(codim);
104 }
105
106 626746 std::size_t number_of_entries() const {
107
2/2
✓ Branch 1 taken 8056 times.
✓ Branch 2 taken 618690 times.
626746 if (_is_scalar())
108 8056 return 1;
109 618690 return std::accumulate(
110 _extents.begin(),
111 _extents.end(),
112 std::size_t{1},
113 std::multiplies{}
114 618690 );
115 }
116
117 170515 std::size_t number_of_entries(unsigned int codim) const {
118
2/4
✓ Branch 1 taken 170515 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 170515 times.
✗ Branch 5 not taken.
170515 return sub_layout(codim).number_of_entries();
119 }
120
121 224724 MDLayout sub_layout(unsigned int codim) const {
122
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 224723 times.
224724 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 224723 return MDLayout{std::vector<std::size_t>{
128 224723 _extents.begin() + codim,
129 224723 _extents.end()
130
2/4
✓ Branch 1 taken 224723 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 224723 times.
✗ Branch 5 not taken.
449446 }};
131 }
132
133 16816 bool operator==(const MDLayout& other) const {
134 16816 return std::ranges::equal(_extents, other._extents);
135 }
136
137 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
138 2099756 MDLayout& with_sub_layout_from() {
139
1/2
✓ Branch 1 taken 294423 times.
✗ Branch 2 not taken.
2099756 auto sub_extents = Detail::get_md_extents<T>();
140
1/2
✓ Branch 2 taken 1049879 times.
✗ Branch 3 not taken.
4199512 _extents.reserve(_extents.size() + sub_extents.size());
141
2/4
✓ Branch 1 taken 1049879 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1049879 times.
✗ Branch 6 not taken.
2099756 std::ranges::move(std::move(sub_extents), std::back_inserter(_extents));
142 2099756 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 template<std::ranges::range R>
154 6305 void export_to(R&& out) const {
155
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3367 times.
6305 if (Ranges::size(out) < dimension())
156
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw SizeError("Given output range is too small");
157 6304 std::ranges::copy(_extents, std::ranges::begin(out));
158 6304 }
159
160 3 friend std::ostream& operator<<(std::ostream& s, const MDLayout& layout) {
161 3 s << "(";
162
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (layout._extents.size() > 0) {
163 3 s << layout._extents[0];
164
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) {
165 3 s << "," << ext;
166 3 });
167 }
168 3 s << ")";
169 3 return s;
170 }
171
172 private:
173 626746 bool _is_scalar() const {
174 626746 return _extents.size() == 0;
175 }
176
177 ReservedVector<std::size_t, buffered_dimensions> _extents;
178 };
179
180
181 /*!
182 * \ingroup Common
183 * \brief Get the layout of a range (or a scalar) whose size is known at compile-time
184 */
185 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
186 16830 MDLayout get_md_layout() {
187
1/2
✓ Branch 2 taken 8264 times.
✗ Branch 3 not taken.
16830 return MDLayout{Detail::get_md_extents<T>()};
188 }
189
190 /*!
191 * \ingroup Common
192 * \brief Get the layout for a range consisting of n instances of the range (or scalar)
193 * given as template argument, whose size is known at compile-time.
194 */
195 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
196 [[deprecated("use MDLayout{{n}}.with_sub_layout_from<T>() instead.")]]
197 MDLayout get_md_layout(std::size_t n) {
198 if constexpr (Concepts::Scalar<T>)
199 return MDLayout{{n}};
200 else {
201 std::array<std::size_t, mdrange_dimension<T> + 1> extents;
202 extents[0] = n;
203 Detail::set_sub_extents<T>(extents.begin() + 1);
204 return MDLayout{extents};
205 }
206 }
207
208 /*!
209 * \ingroup Common
210 * \brief Get the multi-dimensional layout for the given range
211 */
212 template<std::ranges::range R>
213 requires(
214 Concepts::StaticallySizedRange<std::ranges::range_value_t<R>> or
215 Concepts::Scalar<std::ranges::range_value_t<R>>
216 )
217 246 MDLayout get_md_layout(R&& r) {
218
7/11
✓ Branch 1 taken 178 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 178 times.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 178 times.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 178 times.
✗ Branch 11 not taken.
246 return MDLayout{{Ranges::size(r)}}.template with_sub_layout_from<std::ranges::range_value_t<R>>();
219 }
220
221 /*!
222 * \ingroup Common
223 * \brief Overload for scalars
224 */
225 template<Concepts::Scalar T>
226 1 MDLayout get_md_layout(const T&) {
227 1 return MDLayout{};
228 }
229
230 } // namespace GridFormat
231
232 #endif // GRIDFORMAT_COMMON_MD_LAYOUT_HPP_
233