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

Generated by: LCOV version 1.14