SpECTRE Documentation Coverage Report
Current view: top level - DataStructures - LeviCivitaIterator.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 28.6 % 14 4
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <algorithm>
       7              : #include <array>
       8              : #include <cstddef>
       9              : 
      10              : #include "Utilities/Algorithm.hpp"
      11              : #include "Utilities/Array.hpp"
      12              : #include "Utilities/ConstantExpressions.hpp"
      13              : #include "Utilities/Numeric.hpp"
      14              : 
      15              : /*!
      16              :  * \ingroup DataStructuresGroup
      17              :  *
      18              :  * \brief Iterate over all nonzero index permutations for a Levi-Civita
      19              :  * symbol.
      20              :  *
      21              :  * \details This class provides an iterator that allows you to loop over
      22              :  * only the nonzero index permutations of a Levi-Civita symbol of dimension
      23              :  * `dimension`. Inside the loop, the operator `()`
      24              :  * returns an `std::array` containing an ordered list of the indices of this
      25              :  * permutation, the operator `[]` returns a specific index from the same
      26              :  * `std::array`, and the function `sign()` returns the sign of the
      27              :  * Levi-Civita symbol for this permutation.
      28              :  *
      29              :  * Note that the Levi-Civita symbol in odd dimensions (like 3) is invariant
      30              :  * under cyclic permutations of its indices, but in even dimensions (like 4)
      31              :  * it changes sign under cyclic permutations.
      32              :  *
      33              :  * \example
      34              :  * \snippet Test_LeviCivitaIterator.cpp levi_civita_iterator_example
      35              :  */
      36              : template <size_t Dim>
      37            1 : class LeviCivitaIterator {
      38              :  public:
      39            0 :   explicit LeviCivitaIterator() = default;
      40              : 
      41              :   /// \cond HIDDEN_SYMBOLS
      42              :   ~LeviCivitaIterator() = default;
      43              :   /// @{
      44              :   /// No copy or move semantics
      45              :   LeviCivitaIterator(const LeviCivitaIterator<Dim>&) = delete;
      46              :   LeviCivitaIterator(LeviCivitaIterator<Dim>&&) = delete;
      47              :   LeviCivitaIterator<Dim>& operator=(const LeviCivitaIterator<Dim>&) = delete;
      48              :   LeviCivitaIterator<Dim>& operator=(LeviCivitaIterator<Dim>&&) = delete;
      49              :   /// @}
      50              :   /// \endcond
      51              : 
      52              :   // Return false if the end of the loop is reached
      53            0 :   explicit operator bool() const { return valid_; }
      54              : 
      55              :   // Increment the current permutation
      56            0 :   LeviCivitaIterator& operator++() {
      57              :     ++permutation_;
      58              :     if (permutation_ >= signs_.size()) {
      59              :       valid_ = false;
      60              :     }
      61              :     return *this;
      62              :   }
      63              : 
      64              :   /// Return a `std::array` containing the current multi-index, an ordered list
      65              :   /// of indices for the current permutation.
      66            1 :   const std::array<size_t, Dim> operator()() const {
      67              :     return static_cast<std::array<size_t, Dim>>(indexes_[permutation_]);
      68              :   }
      69              : 
      70              :   /// Return a specific index from the multi-index of the current permutation.
      71            1 :   const size_t& operator[](const size_t i) const {
      72              :     return indexes_[permutation_][i];
      73              :   }
      74              : 
      75              :   /// Return the sign of the Levi-Civita symbol for the current permutation.
      76            1 :   int sign() const { return signs_[permutation_]; };
      77              : 
      78              :  private:
      79              :   static constexpr cpp20::array<cpp20::array<size_t, Dim>, factorial(Dim)>
      80            0 :   indexes() {
      81              :     cpp20::array<cpp20::array<size_t, Dim>, factorial(Dim)> indexes{};
      82              :     cpp20::array<size_t, Dim> index{};
      83              :     cpp2b::iota(index.begin(), index.end(), size_t(0));
      84              : 
      85              :     indexes[0] = index;
      86              : 
      87              :     // cpp20::next_permutation generates the different permutations of index
      88              :     // loop over them to fill in the rest of the permutations in indexes
      89              :     size_t permutation = 1;
      90              :     while (cpp20::next_permutation(index.begin(), index.end())) {
      91              :       indexes[permutation] = index;
      92              :       ++permutation;
      93              :     }
      94              :     return indexes;
      95              :   };
      96              : 
      97            0 :   static constexpr cpp20::array<int, factorial(Dim)> signs() {
      98              :     cpp20::array<int, factorial(Dim)> signs{};
      99              :     // By construction, the sign of the first permutation is +1
     100              :     signs[0] = 1;
     101              : 
     102              :     // How do you know whether the corresponding Levi Civita symbol is
     103              :     // +1 or -1? To find out, compute the number, in the factoradic number
     104              :     // system, corresponding to each permutation, and sum the digits. If the sum
     105              :     // is even (odd), the corresponding Levi-Civita symbol will be +1 (-1). This
     106              :     // works because there are Dim! unique permutations of the Levi Civita
     107              :     // symbol indices, so each one can be represented by a
     108              :     // (`Dim`)-digit factorial-number-system number. For more on the
     109              :     // factoradic number system, see
     110              :     // https://en.wikipedia.org/wiki/Factorial_number_system
     111              :     auto factoradic_counter = cpp20::array<size_t, Dim>();
     112              :     for (size_t permutation = 1; permutation < factorial(Dim); ++permutation) {
     113              :       for (size_t i = 0; i < Dim; ++i) {
     114              :         factoradic_counter[i]++;
     115              :         if (factoradic_counter[i] < i + 1) {
     116              :           break;
     117              :         } else {
     118              :           factoradic_counter[i] = 0;
     119              :         }
     120              :       }
     121              :       signs[permutation] =
     122              :           (cpp2b::accumulate(factoradic_counter.begin(),
     123              :                              factoradic_counter.end(), size_t(0)) %
     124              :            2) == 0
     125              :               ? 1
     126              :               : -1;
     127              :     }
     128              :     return signs;
     129              :   };
     130              : 
     131              :   // Note: here and throughout, use cpp20::array, which is constexpr
     132              :   // (unlike std::array), to enable constexpr signs_ and indexes_.
     133              :   static constexpr cpp20::array<cpp20::array<size_t, Dim>, factorial(Dim)>
     134            0 :       indexes_ = indexes();
     135            0 :   static constexpr cpp20::array<int, factorial(Dim)> signs_{signs()};
     136            0 :   size_t permutation_{0};
     137            0 :   bool valid_{true};
     138              : };
        

Generated by: LCOV version 2.0-1