SpECTRE Documentation Coverage Report
Current view: top level - Utilities - StdArrayHelpers.hpp Hit Total Coverage
Commit: 17268ad40c735180c87c60a23676ef5c9a12b1bf Lines: 14 24 58.3 %
Date: 2024-07-24 22:11:27
Legend: Lines: hit not hit

          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             :   }

Generated by: LCOV version 1.14