SpECTRE Documentation Coverage Report
Current view: top level - Utilities - ConstantExpressions.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 21 23 91.3 %
Date: 2025-12-05 05:03:31
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             : /// Define simple functions for constant expressions.
       6             : 
       7             : #pragma once
       8             : 
       9             : #include <algorithm>
      10             : #include <array>
      11             : #include <cassert>
      12             : #include <cstddef>
      13             : #include <cstdint>
      14             : #include <functional>
      15             : #include <initializer_list>
      16             : #include <utility>
      17             : 
      18             : #include "Utilities/ForceInline.hpp"
      19             : #include "Utilities/Kokkos/KokkosCore.hpp"
      20             : #include "Utilities/MakeWithValue.hpp"
      21             : #include "Utilities/Requires.hpp"
      22             : #include "Utilities/TMPL.hpp"
      23             : #include "Utilities/TypeTraits.hpp"
      24             : #include "Utilities/TypeTraits/GetFundamentalType.hpp"
      25             : #include "Utilities/TypeTraits/IsA.hpp"
      26             : #include "Utilities/TypeTraits/IsInteger.hpp"
      27             : 
      28             : /// \ingroup ConstantExpressionsGroup
      29             : /// Compute 2 to the n for integral types.
      30             : ///
      31             : /// \param n the power of two to compute.
      32             : /// \return 2^n
      33             : template <typename T,
      34             :           Requires<tt::is_integer_v<T> and std::is_unsigned_v<T>> = nullptr>
      35           1 : SPECTRE_ALWAYS_INLINE constexpr T two_to_the(T n) {
      36             :   return T(1) << n;
      37             : }
      38             : 
      39             : /// \ingroup ConstantExpressionsGroup
      40             : /// Get the nth bit from the right, counting from zero.
      41             : ///
      42             : /// \param i the value to be acted on.
      43             : /// \param N which place to extract the bit
      44             : /// \return the value of the bit at that place.
      45           1 : SPECTRE_ALWAYS_INLINE constexpr size_t get_nth_bit(const size_t i,
      46             :                                                    const size_t N) {
      47             :   return (i >> N) % 2;
      48             : }
      49             : 
      50             : /// \ingroup ConstantExpressionsGroup
      51             : /// \brief Compute the square of `x`
      52             : template <typename T>
      53             : // NOLINTNEXTLINE(readability-const-return-type)
      54           1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) square(const T& x) {
      55             :   return x * x;
      56             : }
      57             : 
      58             : /// \ingroup ConstantExpressionsGroup
      59             : /// \brief Compute the cube of `x`
      60             : template <typename T>
      61           1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) cube(const T& x) {
      62             :   return x * x * x;
      63             : }
      64             : 
      65             : /*!
      66             :  * \ingroup ConstantExpressionsGroup
      67             :  * \brief Compute the falling factorial of \f$(x)_{n}\f$
      68             :  *
      69             :  * \details The falling factorial \f$(x_)_n = x (x - 1) \ldots (x - n - 1) =
      70             :  * \frac{x!}{(x-n)!}\f$
      71             :  *
      72             :  * \note The largest representable factorial is 20!. It is
      73             :  * up to the user to ensure this is satisfied
      74             :  */
      75           1 : KOKKOS_FUNCTION constexpr uint64_t falling_factorial(const uint64_t x,
      76             :                                                      const uint64_t n) {
      77             :   // clang-tidy: don't warn about STL internals, I can't fix them
      78             :   assert(n <= x);  // NOLINT
      79             :   uint64_t r = 1;
      80             :   for (uint64_t k = 0; k < n; ++k) {
      81             :     r *= (x - k);
      82             :   }
      83             :   return r;
      84             : }
      85             : 
      86             : /*!
      87             :  * \ingroup ConstantExpressionsGroup
      88             :  * \brief Compute the factorial of \f$n!\f$
      89             :  */
      90           1 : KOKKOS_FUNCTION constexpr uint64_t factorial(const uint64_t n) {
      91             :   assert(n <= 20);  // NOLINT
      92             :   return falling_factorial(n, n);
      93             : }
      94             : 
      95             : /*!
      96             :  * \ingroup ConstantExpressionsGroup
      97             :  * \brief Compute the binomial coefficient $\binom{n}{k}$
      98             :  *
      99             :  * \details The binomial coefficient is defined as
     100             :  * $\binom{n}{k} = \frac{n!}{k!(n-k)!}$
     101             :  */
     102           1 : KOKKOS_FUNCTION constexpr uint64_t binomial(const uint64_t n,
     103             :                                             const uint64_t k) {
     104             :   assert(n < 63);  // NOLINT
     105             :   assert(k <= n);  // NOLINT
     106             :   uint64_t result = 1;
     107             :   for (uint64_t j = 1; j <= std::min(k, n - k); ++j) {
     108             :     result *= n + 1 - j;
     109             :     result /= j;
     110             :   }
     111             :   return result;
     112             : }
     113             : 
     114             : /// \ingroup ConstantExpressionsGroup
     115             : namespace ConstantExpressions_detail {
     116             : 
     117             : // Implementation functions for the pow template function below which computes
     118             : // optimized powers where the exponent is a compile-time integer
     119             : 
     120             : // base case power 0: Returns 1.0. This will need to be overloaded for types for
     121             : // which the simple return is inappropriate.
     122             : template <typename T>
     123             : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(
     124             :     const T& /*t*/, std::integral_constant<int, 0> /*meta*/,
     125             :     std::bool_constant<true> /*exponent_was_positive*/) {
     126             :   return static_cast<tt::get_fundamental_type_t<T>>(1.0);
     127             : }
     128             : 
     129             : // special case power 1: acts as a direct identity function for efficiency
     130             : template <typename T>
     131             : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(
     132             :     const T& t, std::integral_constant<int, 1> /*meta*/,
     133             :     std::bool_constant<true> /*exponent_was_positive*/) {
     134             :   return t;
     135             : }
     136             : 
     137             : // general case for positive powers: return the power via recursive inline call,
     138             : // which expands to a series of multiplication operations.
     139             : template <int N, typename T>
     140             : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(
     141             :     const T& t, std::integral_constant<int, N> /*meta*/,
     142             :     std::bool_constant<true> /*exponent_was_positive*/) {
     143             :   return t * pow_impl(t, std::integral_constant<int, N - 1>{},
     144             :                       std::bool_constant<true>{});
     145             : }
     146             : 
     147             : // general case for negative powers: return the multiplicative inverse of the
     148             : // result from the recursive inline call, which expands to a series of
     149             : // multiplication operations. This assumes that division is supported with
     150             : // tt::get_fundamental_type_t<T> and T. If not, this utility will need further
     151             : // specialization.
     152             : template <int N, typename T>
     153             : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(
     154             :     const T& t, std::integral_constant<int, N> /*meta*/,
     155             :     std::bool_constant<false> /*exponent_was_positive*/) {
     156             :   return static_cast<tt::get_fundamental_type_t<T>>(1) /
     157             :          (pow_impl(t, std::integral_constant<int, -N>{},
     158             :                    std::bool_constant<true>{}));
     159             : }
     160             : }  // namespace ConstantExpressions_detail
     161             : 
     162             : /// \ingroup ConstantExpressionsGroup
     163             : /// \brief Compute t^N where N is an integer (positive or negative)
     164             : ///
     165             : /// \warning If passing an integer this will do integer arithmetic, e.g.
     166             : /// `pow<-10>(2) == 0` evaluates to `true`
     167             : ///
     168             : /// \warning For optimization, it is assumed that the `pow<0>` of a vector type
     169             : /// (e.g. `DataVector`) will not be used for initialization or directly indexed,
     170             : /// so `pow<0>` returns simply `1.0`. In the case of use for initialization, a
     171             : /// constructor should be used instead, and in the case of a direct index, the
     172             : /// expression may be simplifyable to avoid the use of `pow<0>` altogether. If a
     173             : /// more complete treatment of `pow<0>` is required, further overloads may be
     174             : /// added to the `ConstantExpressions_detail` namespace.
     175             : ///
     176             : /// \tparam N the integer power being raised to in \f$t^N\f$
     177             : /// \param t the value being exponentiated
     178             : /// \return value \f$t^N\f$ determined via repeated multiplication
     179             : template <int N, typename T>
     180           1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow(const T& t) {
     181             :   return ConstantExpressions_detail::pow_impl(
     182             :       t, std::integral_constant<int, N>{}, std::bool_constant<(N >= 0)>{});
     183             : }
     184             : 
     185             : /// \ingroup ConstantExpressionsGroup
     186             : /// \brief Compute the absolute value of of its argument
     187             : ///
     188             : /// The argument must be comparable to an int and must be negatable.
     189             : template <typename T, Requires<tt::is_integer_v<T> or
     190             :                                std::is_floating_point_v<T>> = nullptr>
     191           1 : KOKKOS_FUNCTION SPECTRE_ALWAYS_INLINE constexpr T ce_abs(const T& x) {
     192             :   return x < 0 ? -x : x;
     193             : }
     194             : 
     195             : /// \cond
     196             : template <>
     197             : KOKKOS_FUNCTION SPECTRE_ALWAYS_INLINE constexpr double ce_abs(const double& x) {
     198             :   return __builtin_fabs(x);
     199             : }
     200             : 
     201             : template <>
     202             : KOKKOS_FUNCTION SPECTRE_ALWAYS_INLINE constexpr float ce_abs(const float& x) {
     203             :   return __builtin_fabsf(x);
     204             : }
     205             : /// \endcond
     206             : 
     207             : /// \ingroup ConstantExpressionsGroup
     208             : /// \brief Compute the absolute value of its argument
     209           1 : KOKKOS_FUNCTION constexpr SPECTRE_ALWAYS_INLINE double ce_fabs(const double x) {
     210             :   return ce_abs(x);
     211             : }
     212             : 
     213           0 : KOKKOS_FUNCTION constexpr SPECTRE_ALWAYS_INLINE float ce_fabs(const float x) {
     214             :   return ce_abs(x);
     215             : }
     216             : 
     217             : namespace ConstantExpressions_detail {
     218             : struct CompareByMagnitude {
     219             :   template <typename T>
     220             :   constexpr bool operator()(const T& a, const T& b) {
     221             :     return ce_abs(a) < ce_abs(b);
     222             :   }
     223             : };
     224             : }  // namespace ConstantExpressions_detail
     225             : 
     226             : /// \ingroup ConstantExpressionsGroup
     227             : /// \brief Return the argument with the largest magnitude
     228             : ///
     229             : /// Magnitude is determined by constexpr_abs.  In case of a tie,
     230             : /// returns the leftmost of the tied values.
     231             : /// @{
     232             : template <typename T>
     233           1 : constexpr const T& max_by_magnitude(const T& a, const T& b) {
     234             :   return std::max(a, b, ConstantExpressions_detail::CompareByMagnitude{});
     235             : }
     236             : 
     237             : template <typename T>
     238           1 : constexpr T max_by_magnitude(std::initializer_list<T> ilist) {
     239             :   return std::max(std::move(ilist),
     240             :                   ConstantExpressions_detail::CompareByMagnitude{});
     241             : }
     242             : /// @}
     243             : 
     244             : /// \ingroup ConstantExpressionsGroup
     245             : /// \brief Return the argument with the smallest magnitude
     246             : ///
     247             : /// Magnitude is determined by constexpr_abs.  In case of a tie,
     248             : /// returns the leftmost of the tied values.
     249             : /// @{
     250             : template <typename T>
     251           1 : constexpr const T& min_by_magnitude(const T& a, const T& b) {
     252             :   return std::min(a, b, ConstantExpressions_detail::CompareByMagnitude{});
     253             : }
     254             : 
     255             : template <typename T>
     256           1 : constexpr T min_by_magnitude(std::initializer_list<T> ilist) {
     257             :   return std::min(std::move(ilist),
     258             :                   ConstantExpressions_detail::CompareByMagnitude{});
     259             : }
     260             : /// @}
     261             : 
     262             : /// \ingroup ConstantExpressionsGroup
     263             : /// \brief Returns `f(ic<0>{}) + f(ic<1>{}) + ... + f(ic<NumTerms-1>{})`
     264             : /// where `ic<N>` stands for `std::integral_constant<size_t, N>`.
     265             : /// This function allows the result types of each operation to be
     266             : /// different, and so works efficiently with expression templates.
     267             : /// \note When summing expression templates one must be careful of
     268             : /// referring to temporaries in `f`.
     269             : template <size_t NumTerms, typename Function, Requires<NumTerms == 1> = nullptr>
     270           1 : constexpr decltype(auto) constexpr_sum(Function&& f) {
     271             :   return f(std::integral_constant<size_t, 0>{});
     272             : }
     273             : 
     274             : /// \cond HIDDEN_SYMBOLS
     275             : template <size_t NumTerms, typename Function,
     276             :           Requires<(NumTerms > 1)> = nullptr>
     277             : constexpr decltype(auto) constexpr_sum(Function&& f) {
     278             :   return constexpr_sum<NumTerms - 1>(f) +
     279             :          f(std::integral_constant<size_t, NumTerms - 1>{});
     280             : }
     281             : /// \endcond
     282             : 
     283             : namespace detail {
     284             : template <typename List, size_t... indices,
     285             :           Requires<not tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>
     286             : inline constexpr std::array<std::decay_t<decltype(tmpl::front<List>::value)>,
     287             :                             tmpl::size<List>::value>
     288             :     make_array_from_list_helper(
     289             :         std::integer_sequence<size_t, indices...> /*meta*/) {
     290             :   return std::array<std::decay_t<decltype(tmpl::front<List>::value)>,
     291             :                     tmpl::size<List>::value>{
     292             :       {tmpl::at<List, tmpl::size_t<indices>>::value...}};
     293             : }
     294             : }  // namespace detail
     295             : 
     296             : /// \ingroup ConstantExpressionsGroup
     297             : /// Make an array from a typelist that holds std::integral_constant's all of
     298             : /// which have the same `value_type`
     299             : ///
     300             : /// \tparam List the typelist of std::integral_constant's
     301             : /// \return array of integral values from the typelist
     302             : template <typename List,
     303             :           Requires<not tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>
     304           1 : inline constexpr auto make_array_from_list()
     305             :     -> std::array<std::decay_t<decltype(tmpl::front<List>::value)>,
     306             :                   tmpl::size<List>::value> {
     307             :   return detail::make_array_from_list_helper<List>(
     308             :       std::make_integer_sequence<size_t, tmpl::size<List>::value>{});
     309             : }
     310             : 
     311             : template <typename TypeForZero,
     312             :           Requires<not tt::is_a_v<tmpl::list, TypeForZero>> = nullptr>
     313             : inline constexpr std::array<std::decay_t<TypeForZero>, 0>
     314           0 : make_array_from_list() {
     315             :   return std::array<std::decay_t<TypeForZero>, 0>{{}};
     316             : }
     317             : 
     318             : namespace detail {
     319             : /// \cond
     320             : template <typename List, size_t... indices,
     321             :           Requires<tt::is_a<tmpl::list, tmpl::front<List>>::value> = nullptr>
     322             : inline constexpr std::array<
     323             :     std::decay_t<
     324             :         decltype(make_array_from_list<tmpl::at<List, tmpl::size_t<0>>>())>,
     325             :     tmpl::size<List>::value>
     326             :     make_array_from_list_helper(
     327             :         std::integer_sequence<size_t, indices...> /*unused*/) {
     328             :   return std::array<std::decay_t<decltype(make_array_from_list<
     329             :                                           tmpl::at<List, tmpl::size_t<0>>>())>,
     330             :                     tmpl::size<List>::value>{
     331             :       {make_array_from_list_helper<tmpl::at<List, tmpl::size_t<indices>>>(
     332             :           std::make_integer_sequence<
     333             :               size_t,
     334             :               tmpl::size<tmpl::at<List, tmpl::size_t<indices>>>::value>{})...}};
     335             : }
     336             : /// \endcond
     337             : }  // namespace detail
     338             : 
     339             : /// \ingroup ConstantExpressionsGroup
     340             : ///
     341             : /// Make an array of arrays from a typelist that holds typelists of
     342             : /// std::integral_constant's all of which have the same `value_type`
     343             : ///
     344             : /// \tparam List the typelist of typelists of std::integral_constant's
     345             : /// \return array of arrays of integral values from the typelists
     346             : template <typename List,
     347             :           Requires<tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>
     348             : inline constexpr auto make_array_from_list() {
     349             :   return detail::make_array_from_list_helper<List>(
     350             :       std::make_integer_sequence<size_t, tmpl::size<List>::value>{});
     351             : }
     352             : 
     353             : /// \ingroup ConstantExpressionsGroup
     354             : /// \brief Compute the length of a const char* at compile time
     355           1 : SPECTRE_ALWAYS_INLINE constexpr size_t cstring_length(const char* str) {
     356             :   // clang-tidy: do not use pointer arithmetic
     357             :   return *str != 0 ? 1 + cstring_length(str + 1) : 0;  // NOLINT
     358             : }
     359             : 
     360             : /// \ingroup ConstantExpressionsGroup
     361             : /// \brief Compute a hash of a const char* at compile time
     362           1 : SPECTRE_ALWAYS_INLINE constexpr size_t cstring_hash(const char* str) {
     363             :   // clang-tidy: do not use pointer arithmetic
     364             :   return *str != 0
     365             :              ? (cstring_hash(str + 1) * 33) ^  // NOLINT
     366             :                    static_cast<size_t>(*str)
     367             :              : 5381;
     368             : }
     369             : 
     370             : namespace ConstantExpression_detail {
     371             : template <typename T, size_t Size, size_t... I, size_t... J>
     372             : inline constexpr std::array<std::decay_t<T>, Size> replace_at_helper(
     373             :     const std::array<T, Size>& arr, const T& value, const size_t i,
     374             :     std::integer_sequence<size_t, I...> /*unused*/,
     375             :     std::integer_sequence<size_t, J...> /*unused*/) {
     376             :   // clang-tidy: Cannot use gsl::at because we want constexpr evaluation and
     377             :   // Parallel::abort violates this
     378             :   return std::array<std::decay_t<T>, Size>{
     379             :       {arr[I]..., value, arr[i + J]...}};  // NOLINT
     380             : }
     381             : }  // namespace ConstantExpression_detail
     382             : 
     383             : /// \ingroup ConstantExpressionsGroup
     384             : /// Replace at compile time the `I`th entry in the array with `value`
     385             : template <size_t I, typename T, size_t Size>
     386           1 : inline constexpr std::array<std::decay_t<T>, Size> replace_at(
     387             :     const std::array<T, Size>& arr, T value) {
     388             :   return ConstantExpression_detail::replace_at_helper(
     389             :       arr, std::forward<T>(value), I + 1,
     390             :       std::make_integer_sequence<size_t, I>{},
     391             :       std::make_integer_sequence<size_t, Size - I - 1>{});
     392             : }
     393             : 
     394             : /// \ingroup ConstantExpressionsGroup
     395             : /// Check at compile time if two `std::array`s are equal
     396             : template <typename T, typename S, size_t size>
     397           1 : inline constexpr bool array_equal(const std::array<T, size>& lhs,
     398             :                                   const std::array<S, size>& rhs,
     399             :                                   const size_t i = 0) {
     400             :   // clang-tidy: Cannot use gsl::at because we want constexpr evaluation and
     401             :   // Parallel::abort violates this
     402             :   return i < size ? (lhs[i] == rhs[i]  // NOLINT
     403             :                      and array_equal(lhs, rhs, i + 1))
     404             :                   : true;
     405             : }

Generated by: LCOV version 1.14