Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines arithmetic operators for std::array and other helpful functions.
6 :
7 : #pragma once
8 :
9 : #include <array>
10 : #include <cmath>
11 : #include <cstddef>
12 : #include <type_traits>
13 : #include <utility>
14 :
15 : #include "Utilities/ErrorHandling/Assert.hpp"
16 : #include "Utilities/Gsl.hpp"
17 :
18 : // Arithmetic operators for std::array<T, Dim>
19 :
20 : template <size_t Dim, typename T, typename U>
21 0 : inline std::array<T, Dim>& operator+=(std::array<T, Dim>& lhs,
22 : const std::array<U, Dim>& rhs) {
23 : for (size_t i = 0; i < Dim; ++i) {
24 : gsl::at(lhs, i) += gsl::at(rhs, i);
25 : }
26 : return lhs;
27 : }
28 :
29 : template <size_t Dim, typename T, typename U>
30 0 : inline auto operator+(const std::array<T, Dim>& lhs,
31 : const std::array<U, Dim>& rhs)
32 : -> std::array<decltype(lhs[0] + rhs[0]), Dim> {
33 : std::array<decltype(lhs[0] + rhs[0]), Dim> result{};
34 : for (size_t i = 0; i < Dim; ++i) {
35 : gsl::at(result, i) = gsl::at(lhs, i) + gsl::at(rhs, i);
36 : }
37 : return result;
38 : }
39 :
40 : template <size_t Dim, typename T, typename U>
41 0 : inline std::array<T, Dim>& operator-=(std::array<T, Dim>& lhs,
42 : const std::array<U, Dim>& rhs) {
43 : for (size_t i = 0; i < Dim; ++i) {
44 : gsl::at(lhs, i) -= gsl::at(rhs, i);
45 : }
46 : return lhs;
47 : }
48 :
49 : template <size_t Dim, typename T, typename U>
50 0 : inline auto operator-(const std::array<T, Dim>& lhs,
51 : const std::array<U, Dim>& rhs)
52 : -> std::array<decltype(lhs[0] - rhs[0]), Dim> {
53 : std::array<decltype(lhs[0] - rhs[0]), Dim> result{};
54 : for (size_t i = 0; i < Dim; ++i) {
55 : gsl::at(result, i) = gsl::at(lhs, i) - gsl::at(rhs, i);
56 : }
57 : return result;
58 : }
59 :
60 : template <size_t Dim, typename T, typename U>
61 0 : inline std::array<T, Dim>& operator*=(std::array<T, Dim>& lhs, const U& scale) {
62 : for (size_t i = 0; i < Dim; ++i) {
63 : gsl::at(lhs, i) *= scale;
64 : }
65 : return lhs;
66 : }
67 :
68 : template <size_t Dim, typename T, typename U>
69 0 : inline std::array<T, Dim> operator*(const std::array<T, Dim>& lhs,
70 : const U& scale) {
71 : std::array<T, Dim> result = lhs;
72 : result *= scale;
73 : return result;
74 : }
75 :
76 : template <size_t Dim, typename T, typename U>
77 0 : inline std::array<T, Dim> operator*(const U& scale,
78 : const std::array<T, Dim>& rhs) {
79 : return rhs * scale;
80 : }
81 :
82 : template <size_t Dim, typename T, typename U>
83 0 : inline std::array<T, Dim>& operator/=(std::array<T, Dim>& lhs,
84 : const U& scale) {
85 : for (size_t i = 0; i < Dim; ++i) {
86 : gsl::at(lhs, i) /= scale;
87 : }
88 : return lhs;
89 : }
90 :
91 : template <size_t Dim, typename T, typename U>
92 0 : inline std::array<T, Dim> operator/(const std::array<T, Dim>& lhs,
93 : const U& scale) {
94 : std::array<T, Dim> result = lhs;
95 : result /= scale;
96 : return result;
97 : }
98 :
99 : template <size_t Dim, typename T>
100 0 : inline std::array<T, Dim> operator-(const std::array<T, Dim>& rhs) {
101 : std::array<T, Dim> result{};
102 : for (size_t i = 0; i < Dim; ++i) {
103 : gsl::at(result, i) = -gsl::at(rhs, i);
104 : }
105 : return result;
106 : }
107 :
108 : /// \ingroup UtilitiesGroup
109 : /// \brief Construct an array from an existing array omitting one element
110 : template <typename T, size_t Dim>
111 1 : inline std::array<T, Dim - 1> all_but_specified_element_of(
112 : const std::array<T, Dim>& a, const size_t element_to_remove) {
113 : ASSERT(element_to_remove < Dim, "Specified element does not exist");
114 : std::array<T, Dim - 1> result{};
115 : for (size_t i = 0; i < element_to_remove; ++i) {
116 : gsl::at(result, i) = gsl::at(a, i);
117 : }
118 : for (size_t i = element_to_remove + 1; i < Dim; ++i) {
119 : gsl::at(result, i - 1) = gsl::at(a, i);
120 : }
121 : return result;
122 : }
123 :
124 : /// \ingroup UtilitiesGroup
125 : /// \brief Construct an array from an existing array adding one element
126 : template <typename T, size_t Dim>
127 1 : inline std::array<T, Dim + 1> insert_element(std::array<T, Dim> a,
128 : const size_t element_to_add,
129 : T value) {
130 : ASSERT(element_to_add <= Dim, "Specified element is out of range");
131 : std::array<T, Dim + 1> result{};
132 : for (size_t i = 0; i < element_to_add; ++i) {
133 : gsl::at(result, i) = std::move(gsl::at(a, i));
134 : }
135 : gsl::at(result, element_to_add) = std::move(value);
136 : for (size_t i = element_to_add; i < Dim; ++i) {
137 : gsl::at(result, i + 1) = std::move(gsl::at(a, i));
138 : }
139 : return result;
140 : }
141 :
142 : /// \ingroup UtilitiesGroup
143 : /// \brief Construct an array from an existing array prepending a value
144 : template <typename T, size_t Dim>
145 1 : inline constexpr std::array<T, Dim + 1> prepend(const std::array<T, Dim>& a,
146 : T value) {
147 : std::array<T, Dim + 1> result{};
148 : gsl::at(result, 0) = std::move(value);
149 : for (size_t i = 0; i < Dim; ++i) {
150 : gsl::at(result, i + 1) = gsl::at(a, i);
151 : }
152 : return result;
153 : }
154 :
155 : /// \ingroup UtilitiesGroup
156 : /// \brief Construct an array from the contents of two other arrays.
157 : template <typename T, size_t Dim1, size_t Dim2>
158 1 : constexpr std::array<T, Dim1 + Dim2> concatenate(std::array<T, Dim1> a,
159 : std::array<T, Dim2> b) {
160 : std::array<T, Dim1 + Dim2> result{};
161 : for (size_t i = 0; i < Dim1; ++i) {
162 : gsl::at(result, i) = std::move(gsl::at(a, i));
163 : }
164 : for (size_t i = 0; i < Dim2; ++i) {
165 : gsl::at(result, i + Dim1) = std::move(gsl::at(b, i));
166 : }
167 : return result;
168 : }
169 :
170 : /// @{
171 : /// \ingroup UtilitiesGroup
172 : /// \brief Euclidean magnitude of the elements of the array.
173 : ///
174 : /// \details If T is a container the magnitude is computed separately for each
175 : /// element of the container.
176 : ///
177 : /// \requires If T is a container, T must have following mathematical operators:
178 : /// abs(), sqrt(), and element-wise addition and multiplication. In addition,
179 : /// each T in the array must have the same size.
180 : template <typename T>
181 1 : decltype(auto) magnitude(const std::array<T, 1>& a) {
182 : using std::abs;
183 : return abs(a[0]);
184 : }
185 :
186 : template <typename T>
187 1 : decltype(auto) magnitude(const std::array<T, 2>& a) {
188 : return sqrt(a[0] * a[0] + a[1] * a[1]);
189 : }
190 :
191 : template <typename T>
192 1 : decltype(auto) magnitude(const std::array<T, 3>& a) {
193 : return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
194 : }
195 : /// @}
196 : /// @{
197 : /// \ingroup UtilitiesGroup
198 : /// \brief Dot product between two arrays
199 : ///
200 : /// \details This also works elementwise if T is a container and R is a float or
201 : /// the other way round. The return type will always be the same as the return
202 : /// type of the multiplication which may be a blaze expression template.
203 :
204 : template <typename T, typename R>
205 1 : decltype(auto) dot(const std::array<T, 1>& first,
206 : const std::array<R, 1>& second) {
207 : return first[0] * second[0];
208 : }
209 :
210 : template <typename T, typename R>
211 1 : decltype(auto) dot(const std::array<T, 2>& first,
212 : const std::array<R, 2>& second) {
213 : return first[0] * second[0] + first[1] * second[1];
214 : }
215 :
216 : template <typename T, typename R>
217 1 : decltype(auto) dot(const std::array<T, 3>& first,
218 : const std::array<R, 3>& second) {
219 : return first[0] * second[0] + first[1] * second[1] + first[2] * second[2];
220 : }
221 : /// @}
222 :
223 : namespace std_array_helpers_detail {
224 : template <typename T, size_t Dim, typename F, size_t... Indices>
225 : auto map_array_impl(const std::array<T, Dim>& array, const F& f,
226 : const std::index_sequence<Indices...> /*meta*/) {
227 : return std::array<std::decay_t<decltype(f(std::declval<T>()))>, Dim>{
228 : {f(array[Indices])...}};
229 : }
230 : } // namespace std_array_helpers_detail
231 :
232 : /// \ingroup UtilitiesGroup
233 : /// Applies a function to each element of an array, producing a new
234 : /// array of the results. The elements of the new array are
235 : /// constructed in place, so they need not be default constructible.
236 : template <typename T, size_t Dim, typename F>
237 1 : auto map_array(const std::array<T, Dim>& array, const F& f) {
238 : return std_array_helpers_detail::map_array_impl(
239 : array, f, std::make_index_sequence<Dim>{});
240 : }
241 :
242 : /*!
243 : * \ingroup UtilitiesGroup
244 : * \brief Declares a binary function on an array, intended for binary
245 : * operators such as `+`
246 : *
247 : * \param RESULT_TYPE the `value_type` that is the result of the operation
248 : * (e.g. `A` for resulting `std::array<A,2>`)
249 : *
250 : * \param LTYPE the `value_type` of the first argument of the function (so the
251 : * left value if the function is an operator overload)
252 : *
253 : * \param RTYPE the `value_type` of the second argument of the function
254 : *
255 : * \param OP_FUNCTION_NAME the function which should be declared
256 : * (e.g. `operator+`)
257 : *
258 : * \param BINARY_OP the binary function which should be applied elementwise to
259 : * the pair of arrays. (e.g. `std::plus<>()`)
260 : */
261 : // clang-tidy: complaints about uninitialized member, but the transform will set
262 : // data
263 : #define DEFINE_STD_ARRAY_BINOP(RESULT_TYPE, LTYPE, RTYPE, OP_FUNCTION_NAME, \
264 1 : BINARY_OP) \
265 : template <size_t Dim> \
266 : std::array<RESULT_TYPE, Dim> OP_FUNCTION_NAME( \
267 : const std::array<LTYPE, Dim>& lhs, const std::array<RTYPE, Dim>& rhs) { \
268 : std::array<RESULT_TYPE, Dim> result; /*NOLINT*/ \
269 : std::transform(lhs.begin(), lhs.end(), rhs.begin(), result.begin(), \
270 : BINARY_OP); \
271 : return result; \
272 : }
273 :
274 : /*!
275 : * \ingroup UtilitiesGroup
276 : * \brief Declares an in-place binary function on an array, intended for
277 : * operations such as `+=`
278 : *
279 : * \param LTYPE the `value_type` of the first argument of the function which is
280 : * also the result `value_tye` of the operation (so the left value if the
281 : * function is an operator overload)
282 : *
283 : * \param RTYPE the `value_type` of the second argument of the function
284 : *
285 : * \param OP_FUNCTION_NAME the function which should be declared
286 : * (e.g. `operator+=`)
287 : *
288 : * \param BINARY_OP the binary function which should be applied elementwise to
289 : * the pair of arrays. (e.g. `std::plus<>()`)
290 : */
291 : #define DEFINE_STD_ARRAY_INPLACE_BINOP(LTYPE, RTYPE, OP_FUNCTION_NAME, \
292 1 : BINARY_OP) \
293 : template <size_t Dim> \
294 : std::array<LTYPE, Dim>& OP_FUNCTION_NAME( \
295 : std::array<LTYPE, Dim>& lhs, const std::array<RTYPE, Dim>& rhs) { \
296 : std::transform(lhs.begin(), lhs.end(), rhs.begin(), lhs.begin(), \
297 : BINARY_OP); \
298 : return lhs; \
299 : }
|