StdArrayHelpers.hpp
Go to the documentation of this file.
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 "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 inline std::array<T, Dim>& operator+=(std::array<T, Dim>& lhs,
22  const std::array<U, Dim>& rhs) noexcept {
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 inline auto operator+(const std::array<T, Dim>& lhs,
31  const std::array<U, Dim>& rhs) noexcept
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 inline std::array<T, Dim>& operator-=(std::array<T, Dim>& lhs,
42  const std::array<U, Dim>& rhs) noexcept {
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 inline auto operator-(const std::array<T, Dim>& lhs,
51  const std::array<U, Dim>& rhs) noexcept
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>
62  const U& scale) noexcept {
63  std::array<T, Dim> result{};
64  for (size_t i = 0; i < Dim; ++i) {
65  gsl::at(result, i) = gsl::at(lhs, i) * scale;
66  }
67  return result;
68 }
69 
70 template <size_t Dim, typename T, typename U>
71 inline std::array<T, Dim> operator*(const U& scale,
72  const std::array<T, Dim>& rhs) noexcept {
73  return rhs * scale;
74 }
75 
76 template <size_t Dim, typename T, typename U>
77 inline std::array<T, Dim> operator/(const std::array<T, Dim>& lhs,
78  const U& scale) noexcept {
79  std::array<T, Dim> result{};
80  for (size_t i = 0; i < Dim; ++i) {
81  gsl::at(result, i) = gsl::at(lhs, i) / scale;
82  }
83  return result;
84 }
85 
86 template <size_t Dim, typename T>
87 inline std::array<T, Dim> operator-(const std::array<T, Dim>& rhs) noexcept {
88  std::array<T, Dim> result{};
89  for (size_t i = 0; i < Dim; ++i) {
90  gsl::at(result, i) = -gsl::at(rhs, i);
91  }
92  return result;
93 }
94 
95 /// \ingroup UtilitiesGroup
96 /// \brief Construct an array from an existing array omitting one element
97 template <typename T, size_t Dim>
99  const std::array<T, Dim>& a, const size_t element_to_remove) noexcept {
100  ASSERT(element_to_remove < Dim, "Specified element does not exist");
101  std::array<T, Dim - 1> result{};
102  for (size_t i = 0; i < element_to_remove; ++i) {
103  gsl::at(result, i) = gsl::at(a, i);
104  }
105  for (size_t i = element_to_remove + 1; i < Dim; ++i) {
106  gsl::at(result, i - 1) = gsl::at(a, i);
107  }
108  return result;
109 }
110 
111 /// \ingroup UtilitiesGroup
112 /// \brief Construct an array from an existing array adding one element
113 template <typename T, size_t Dim>
115  const size_t element_to_add,
116  T value) noexcept {
117  ASSERT(element_to_add <= Dim, "Specified element is out of range");
118  std::array<T, Dim + 1> result{};
119  for (size_t i = 0; i < element_to_add; ++i) {
120  gsl::at(result, i) = std::move(gsl::at(a, i));
121  }
122  gsl::at(result, element_to_add) = std::move(value);
123  for (size_t i = element_to_add; i < Dim; ++i) {
124  gsl::at(result, i + 1) = std::move(gsl::at(a, i));
125  }
126  return result;
127 }
128 
129 /// \ingroup UtilitiesGroup
130 /// \brief Construct an array from an existing array prepending a value
131 template <typename T, size_t Dim>
133  T value) noexcept {
134  std::array<T, Dim + 1> result{};
135  gsl::at(result, 0) = std::move(value);
136  for (size_t i = 0; i < Dim; ++i) {
137  gsl::at(result, i + 1) = gsl::at(a, i);
138  }
139  return result;
140 }
141 
142 //@{
143 /// \ingroup UtilitiesGroup
144 /// \brief Euclidean magnitude of the elements of the array.
145 ///
146 /// \details If T is a container the magnitude is computed separately for each
147 /// element of the container.
148 ///
149 /// \requires If T is a container, T must have following mathematical operators:
150 /// abs(), sqrt(), and element-wise addition and multiplication. In addition,
151 /// each T in the array must have the same size.
152 template <typename T>
153 inline T magnitude(const std::array<T, 1>& a) noexcept {
154  return abs(a[0]);
155 }
156 
157 template <>
158 inline double magnitude(const std::array<double, 1>& a) noexcept {
159  return std::abs(a[0]);
160 }
161 
162 template <typename T>
163 inline T magnitude(const std::array<T, 2>& a) noexcept {
164  return sqrt(a[0] * a[0] + a[1] * a[1]);
165 }
166 
167 template <typename T>
168 inline T magnitude(const std::array<T, 3>& a) noexcept {
169  return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
170 }
171 //@}
172 
173 namespace std_array_helpers_detail {
174 template <typename T, size_t Dim, typename F, size_t... Indices>
175 auto map_array_impl(const std::array<T, Dim>& array, const F& f,
176  const std::index_sequence<Indices...> /*meta*/) noexcept {
178  {f(array[Indices])...}};
179 }
180 } // namespace std_array_helpers_detail
181 
182 /// \ingroup UtilitiesGroup
183 /// Applies a function to each element of an array, producing a new
184 /// array of the results. The elements of the new array are
185 /// constructed in place, so they need not be default constructible.
186 template <typename T, size_t Dim, typename F>
187 auto map_array(const std::array<T, Dim>& array, const F& f) noexcept {
188  return std_array_helpers_detail::map_array_impl(
189  array, f, std::make_index_sequence<Dim>{});
190 }
191 
192 /*!
193  * \ingroup UtilitiesGroup
194  * \brief Declares a binary function on an array, intended for binary
195  * operators such as `+`
196  *
197  * \param RESULT_TYPE the `value_type` that is the result of the operation
198  * (e.g. `A` for resulting `std::array<A,2>`)
199  *
200  * \param LTYPE the `value_type` of the first argument of the function (so the
201  * left value if the function is an operator overload)
202  *
203  * \param RTYPE the `value_type` of the second argument of the function
204  *
205  * \param OP_FUNCTION_NAME the function which should be declared
206  * (e.g. `operator+`)
207  *
208  * \param BINARY_OP the binary function which should be applied elementwise to
209  * the pair of arrays. (e.g. `std::plus<>()`)
210  */
211 // clang-tidy: complaints about uninitialized member, but the transform will set
212 // data
213 #define DEFINE_STD_ARRAY_BINOP(RESULT_TYPE, LTYPE, RTYPE, OP_FUNCTION_NAME, \
214  BINARY_OP) \
215  template <size_t Dim> \
216  std::array<RESULT_TYPE, Dim> OP_FUNCTION_NAME( \
217  const std::array<LTYPE, Dim>& lhs, \
218  const std::array<RTYPE, Dim>& rhs) noexcept { \
219  std::array<RESULT_TYPE, Dim> result; /*NOLINT*/ \
220  std::transform(lhs.begin(), lhs.end(), rhs.begin(), result.begin(), \
221  BINARY_OP); \
222  return result; \
223  }
224 
225 /*!
226  * \ingroup UtilitiesGroup
227  * \brief Declares an in-place binary function on an array, intended for
228  * operations such as `+=`
229  *
230  * \param LTYPE the `value_type` of the first argument of the function which is
231  * also the result `value_tye` of the operation (so the left value if the
232  * function is an operator overload)
233  *
234  * \param RTYPE the `value_type` of the second argument of the function
235  *
236  * \param OP_FUNCTION_NAME the function which should be declared
237  * (e.g. `operator+=`)
238  *
239  * \param BINARY_OP the binary function which should be applied elementwise to
240  * the pair of arrays. (e.g. `std::plus<>()`)
241  */
242 #define DEFINE_STD_ARRAY_INPLACE_BINOP(LTYPE, RTYPE, OP_FUNCTION_NAME, \
243  BINARY_OP) \
244  template <size_t Dim> \
245  std::array<LTYPE, Dim>& OP_FUNCTION_NAME( \
246  std::array<LTYPE, Dim>& lhs, \
247  const std::array<RTYPE, Dim>& rhs) noexcept { \
248  std::transform(lhs.begin(), lhs.end(), rhs.begin(), lhs.begin(), \
249  BINARY_OP); \
250  return lhs; \
251  }
std::array< T, Dim - 1 > all_but_specified_element_of(const std::array< T, Dim > &a, const size_t element_to_remove) noexcept
Construct an array from an existing array omitting one element.
Definition: StdArrayHelpers.hpp:98
auto operator*(const TensorExpression< T1, X, Symm1, IndexList1, Args1 > &t1, const TensorExpression< T2, X, Symm2, IndexList2, Args2 > &t2)
Definition: Product.hpp:89
std::array< T, Dim+1 > insert_element(std::array< T, Dim > a, const size_t element_to_add, T value) noexcept
Construct an array from an existing array adding one element.
Definition: StdArrayHelpers.hpp:114
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
constexpr std::array< T, Dim+1 > prepend(const std::array< T, Dim > &a, T value) noexcept
Construct an array from an existing array prepending a value.
Definition: StdArrayHelpers.hpp:132
Definition: StdArrayHelpers.hpp:173
auto map_array(const std::array< T, Dim > &array, const F &f) noexcept
Applies a function to each element of an array, producing a new array of the results. The elements of the new array are constructed in place, so they need not be default constructible.
Definition: StdArrayHelpers.hpp:187
Defines macro ASSERT.
Defines functions and classes from the GSL.
T magnitude(const std::array< T, 1 > &a) noexcept
Euclidean magnitude of the elements of the array.
Definition: StdArrayHelpers.hpp:153
constexpr T & at(std::array< T, N > &arr, Size index)
Retrieve a entry from a container, with checks in Debug mode that the index being retrieved is valid...
Definition: Gsl.hpp:124