LCOV - code coverage report
Current view: top level - src/AH/Containers - Array.hpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 100.0 % 119 119
Test Date: 2026-06-06 17:44:35 Functions: 75.0 % 224 168
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* ✔ */
       2              : 
       3              : #pragma once
       4              : 
       5              : #include <AH/Error/Error.hpp>
       6              : #include <AH/STL/iterator>
       7              : #include <AH/STL/type_traits> // conditional
       8              : #include <stddef.h>           // size_t
       9              : 
      10              : BEGIN_AH_NAMESPACE
      11              : 
      12              : template <class T>
      13              : constexpr T abs_diff(const T &a, const T &b) {
      14              :     return a < b ? b - a : a - b;
      15              : }
      16              : 
      17              : /// @addtogroup AH_Containers
      18              : /// @{
      19              : 
      20              : template <class T, size_t N, bool Reverse, bool Const>
      21              : class ArraySlice;
      22              : 
      23              : /**
      24              :  * @brief   An array wrapper for easy copying, comparing, and iterating.
      25              :  * 
      26              :  * @tparam  T
      27              :  *          The type of the elements in the array.
      28              :  * @tparam  N 
      29              :  *          The number of elements in the array.
      30              :  */
      31              : template <class T, size_t N>
      32              : struct Array {
      33              :     T data[N];
      34              :     using type = T;
      35              :     constexpr static size_t length = N;
      36              : 
      37              :     /**
      38              :      * @brief   Get the element at the given index.
      39              :      * 
      40              :      * @note    Bounds checking is performed. If fatal errors are disabled, the
      41              :      *          last element is returned if the index is out of bounds. 
      42              :      * 
      43              :      * @param   index
      44              :      *          The (zero-based) index of the element to return.
      45              :      */
      46         1247 :     T &operator[](size_t index) {
      47         1247 :         if (index >= N) { // TODO
      48            1 :             ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDED);
      49              :             index = N - 1; // LCOV_EXCL_LINE
      50              :         }                  // LCOV_EXCL_LINE
      51         1246 :         return data[index];
      52              :     }
      53              : 
      54              :     /**
      55              :      * @brief   Get the element at the given index.
      56              :      * 
      57              :      * @note    Bounds checking is performed. If fatal errors are disabled, the
      58              :      *          last element is returned if the index is out of bounds. 
      59              :      * 
      60              :      * @param   index
      61              :      *          The (zero-based) index of the element to return.
      62              :      */
      63         1607 :     const T &operator[](size_t index) const {
      64         1607 :         if (index >= N) { // TODO
      65            1 :             ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDED);
      66              :             index = N - 1; // LCOV_EXCL_LINE
      67              :         }                  // LCOV_EXCL_LINE
      68         1606 :         return data[index];
      69              :     }
      70              : 
      71              :     /**
      72              :      * @brief   Get a pointer to the first element.
      73              :      */
      74           54 :     T *begin() { return &data[0]; }
      75              : 
      76              :     /**
      77              :      * @brief   Get a pointer to the first element.
      78              :      */
      79           42 :     const T *begin() const { return &data[0]; }
      80              : 
      81              :     /**
      82              :      * @brief   Get a pointer to the memory beyond the array.
      83              :      */
      84           48 :     T *end() { return &data[N]; }
      85              : 
      86              :     /**
      87              :      * @brief   Get a pointer to the memory beyond the array.
      88              :      */
      89           42 :     const T *end() const { return &data[N]; }
      90              : 
      91              :     /**
      92              :      * @brief   Check the equality of all elements in two arrays.
      93              :      * 
      94              :      * @param   rhs 
      95              :      *          The array to compare this array to.
      96              :      */
      97           44 :     bool operator==(const Array<T, N> &rhs) const {
      98           44 :         if (this == &rhs)
      99            3 :             return true;
     100          223 :         for (size_t i = 0; i < N; i++)
     101          185 :             if ((*this)[i] != rhs[i])
     102            3 :                 return false;
     103           38 :         return true;
     104              :     }
     105              : 
     106              :     /**
     107              :      * @brief   Check the inequality of all elements in two arrays.
     108              :      * 
     109              :      * @param   rhs 
     110              :      *          The array to compare this array to.
     111              :      */
     112            4 :     bool operator!=(const Array<T, N> &rhs) const { return !(*this == rhs); }
     113              : 
     114              :   public:
     115              :     /**
     116              :      * @brief   Get a view on a slice of the Array.
     117              :      * 
     118              :      * Doesn't copy the contents of the array, it's just a reference to the 
     119              :      * original array.
     120              :      * 
     121              :      * @tparam  Start
     122              :      *          The start index of the slice.
     123              :      * @tparam  End
     124              :      *          The end index of the slice.
     125              :      */
     126              :     template <size_t Start = 0, size_t End = N - 1>
     127              :     ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), false> slice();
     128              : 
     129              :     /**
     130              :      * @brief   Get a read-only view on a slice of the Array.
     131              :      * @copydetails     slice()
     132              :      */
     133              :     template <size_t Start = 0, size_t End = N - 1>
     134              :     ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true> slice() const;
     135              : 
     136              :     /**
     137              :      * @brief   Get a read-only view on a slice of the Array.
     138              :      * @copydetails     slice()
     139              :      */
     140              :     template <size_t Start = 0, size_t End = N - 1>
     141              :     ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true>
     142            4 :     cslice() const {
     143            4 :         const Array<T, N> *This = this;
     144            4 :         return This->template slice<Start, End>();
     145              :     }
     146              : };
     147              : 
     148              : /**
     149              :  * @brief   Class for a view on a slice of an array.
     150              :  * 
     151              :  * Doesn't copy the contents of the array, it's just a reference to the original
     152              :  * array.
     153              :  * 
     154              :  * @tparam  T
     155              :  *          The type of elements of the Array.
     156              :  * @tparam  N 
     157              :  *          The size of the slice.
     158              :  * @tparam  Reverse
     159              :  *          Whether the slice is reversed or not.
     160              :  * @tparam  Const
     161              :  *          Whether to save a read-only or mutable reference to the Array.
     162              :  */
     163              : template <class T, size_t N, bool Reverse = false, bool Const = true>
     164              : class ArraySlice {
     165              :     using ElementRefType =
     166              :         typename std::conditional<Const, const T &, T &>::type;
     167              :     using ElementPtrType =
     168              :         typename std::conditional<Const, const T *, T *>::type;
     169              : 
     170              :   public:
     171              :     /// Constructor
     172           60 :     ArraySlice(ElementPtrType array) : array {array} {}
     173              : 
     174              :     /// Implicit conversion from slice to new array (creates a copy).
     175            1 :     operator Array<T, N>() const { return asArray(); }
     176              : 
     177            3 :     Array<T, N> asArray() const {
     178            3 :         Array<T, N> slice = {{}};
     179           14 :         for (size_t i = 0; i < N; ++i)
     180           11 :             slice[i] = (*this)[i];
     181            3 :         return slice;
     182              :     }
     183              : 
     184              :     using iterator = typename std::conditional<
     185              :         Reverse, std::reverse_iterator<ElementPtrType>, ElementPtrType>::type;
     186              : 
     187              :     /**
     188              :      * @brief   Get the element at the given index.
     189              :      * 
     190              :      * @note    Bounds checking is performed. If fatal errors are disabled, the
     191              :      *          last element is returned if the index is out of bounds. 
     192              :      * 
     193              :      * @param   index
     194              :      *          The (zero-based) index of the element to return.
     195              :      */
     196          459 :     ElementRefType operator[](size_t index) const {
     197          459 :         if (index >= N) { // TODO
     198            2 :             ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDEF);
     199              :             index = N - 1; // LCOV_EXCL_LINE
     200              :         }                  // LCOV_EXCL_LINE
     201              :         if (Reverse)
     202           17 :             return *(array - index);
     203              :         else
     204          440 :             return *(array + index);
     205              :     }
     206              : 
     207            3 :     iterator begin() const {
     208              :         if (Reverse)
     209            2 :             return iterator {array + 1};
     210              :         else
     211            1 :             return iterator {array};
     212              :     }
     213              : 
     214            7 :     iterator end() const {
     215              :         if (Reverse)
     216            4 :             return iterator {array + 1 - N};
     217              :         else
     218            3 :             return iterator {array + N};
     219              :     }
     220              : 
     221              :     template <size_t Start, size_t End>
     222              :     ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const>
     223              :     slice() const;
     224              : 
     225              :   private:
     226              :     ElementPtrType array;
     227              : };
     228              : 
     229              : template <class T, size_t N>
     230              : template <size_t Start, size_t End>
     231           35 : auto Array<T, N>::slice()
     232              :     -> ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), false> {
     233              :     static_assert(Start < N, "");
     234              :     static_assert(End < N, "");
     235           35 :     return &(*this)[Start];
     236              : }
     237              : 
     238              : template <class T, size_t N>
     239              : template <size_t Start, size_t End>
     240           23 : auto Array<T, N>::slice() const
     241              :     -> ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true> {
     242              :     static_assert(Start < N, "");
     243              :     static_assert(End < N, "");
     244           23 :     return &(*this)[Start];
     245              : }
     246              : 
     247              : template <class T, size_t N, bool Reverse, bool Const>
     248              : template <size_t Start, size_t End>
     249            2 : auto ArraySlice<T, N, Reverse, Const>::slice() const
     250              :     -> ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const> {
     251              :     static_assert(Start < N, "");
     252              :     static_assert(End < N, "");
     253            2 :     return &(*this)[Start];
     254              : }
     255              : 
     256              : /// @related ArraySlice<T, N, Reverse, Const>::iterator
     257              : template <class T, size_t N, bool Reverse, bool Const>
     258              : typename ArraySlice<T, N, Reverse, Const>::iterator operator+(
     259              :     typename ArraySlice<T, N, Reverse, Const>::iterator::difference_type n,
     260              :     typename ArraySlice<T, N, Reverse, Const>::iterator a) {
     261              :     return a + n;
     262              : }
     263              : 
     264              : // Equality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     265              : 
     266              : /// Slice == Slice
     267              : /// @related ArraySlice
     268              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     269              :           bool Reverse2, bool Const1, bool Const2>
     270           25 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
     271              :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     272              :     static_assert(N1 == N2, "Error: sizes do not match");
     273          136 :     for (size_t i = 0; i < N1; ++i)
     274          117 :         if (a[i] != b[i])
     275            6 :             return false;
     276           19 :     return true;
     277              : }
     278              : 
     279              : /// Array == Slice
     280              : /// @related ArraySlice
     281              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
     282              : bool operator==(const Array<T1, N1> &a,
     283              :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     284              :     return a.slice() == b;
     285              : }
     286              : 
     287              : /// Slice == Array
     288              : /// @related ArraySlice
     289              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
     290            3 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
     291              :                 const Array<T2, N2> &b) {
     292            3 :     return a == b.slice();
     293              : }
     294              : 
     295              : // Inequality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     296              : 
     297              : /// Slice != Slice
     298              : /// @related ArraySlice
     299              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     300              :           bool Reverse2, bool Const1, bool Const2>
     301            8 : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
     302              :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     303            8 :     return !(a == b);
     304              : }
     305              : 
     306              : /// Array != Slice
     307              : /// @related ArraySlice
     308              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
     309              : bool operator!=(const Array<T1, N1> &a,
     310              :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     311              :     return a.slice() != b;
     312              : }
     313              : 
     314              : /// Slice != Array
     315              : /// @related ArraySlice
     316              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
     317              : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
     318              :                 const Array<T2, N2> &b) {
     319              :     return a != b.slice();
     320              : }
     321              : 
     322              : // Addition ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     323              : 
     324              : /// Slice + Slice
     325              : /// @related ArraySlice
     326              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     327              :           bool Reverse2, bool Const1, bool Const2>
     328              : Array<decltype(T1 {} + T2 {}), N1>
     329            2 : operator+(ArraySlice<T1, N1, Reverse1, Const1> a,
     330              :           ArraySlice<T2, N2, Reverse2, Const2> b) {
     331              :     static_assert(N1 == N2, "Error: sizes do not match");
     332            2 :     Array<decltype(T1 {} + T2 {}), N1> result = {{}};
     333            8 :     for (size_t i = 0; i < N1; ++i)
     334            6 :         result[i] = a[i] + b[i];
     335            2 :     return result;
     336              : }
     337              : 
     338              : /// Array + Array
     339              : /// @related Array
     340              : template <class T1, class T2, size_t N1, size_t N2>
     341            1 : Array<decltype(T1 {} + T2 {}), N1> operator+(const Array<T1, N1> &a,
     342              :                                              const Array<T2, N2> &b) {
     343            1 :     return a.slice() + b.slice();
     344              : }
     345              : 
     346              : /// Slice += Slice
     347              : /// @related ArraySlice
     348              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     349              :           bool Reverse2, bool Const1, bool Const2>
     350              : const ArraySlice<T1, N1, Reverse1, Const1> &
     351            2 : operator+=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
     352              :            const ArraySlice<T2, N2, Reverse2, Const2> &b) {
     353              :     static_assert(N1 == N2, "Error: sizes do not match");
     354            8 :     for (size_t i = 0; i < N1; ++i)
     355            6 :         a[i] += b[i];
     356            2 :     return a;
     357              : }
     358              : 
     359              : /// Array += Array
     360              : /// @related Array
     361              : template <class T1, class T2, size_t N1, size_t N2>
     362            1 : Array<T1, N1> &operator+=(Array<T1, N1> &a, const Array<T2, N2> &b) {
     363            1 :     a.slice() += b.slice();
     364            1 :     return a;
     365              : }
     366              : 
     367              : // Subtraction :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     368              : 
     369              : /// Slice - Slice
     370              : /// @related ArraySlice
     371              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     372              :           bool Reverse2, bool Const1, bool Const2>
     373              : Array<decltype(T1 {} - T2 {}), N1>
     374            2 : operator-(ArraySlice<T1, N1, Reverse1, Const1> a,
     375              :           ArraySlice<T2, N2, Reverse2, Const2> b) {
     376              :     static_assert(N1 == N2, "Error: sizes do not match");
     377            2 :     Array<decltype(T1 {} - T2 {}), N1> result = {{}};
     378            8 :     for (size_t i = 0; i < N1; ++i)
     379            6 :         result[i] = a[i] - b[i];
     380            2 :     return result;
     381              : }
     382              : 
     383              : /// Array - Array
     384              : /// @related Array
     385              : template <class T1, class T2, size_t N1, size_t N2>
     386            1 : Array<decltype(T1 {} - T2 {}), N1> operator-(const Array<T1, N1> &a,
     387              :                                              const Array<T2, N2> &b) {
     388            1 :     return a.slice() - b.slice();
     389              : }
     390              : 
     391              : /// Slice -= Slice
     392              : /// @related ArraySlice
     393              : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     394              :           bool Reverse2, bool Const1, bool Const2>
     395              : const ArraySlice<T1, N1, Reverse1, Const1> &
     396              : operator-=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
     397              :            const ArraySlice<T2, N2, Reverse2, Const2> &b) {
     398              :     static_assert(N1 == N2, "Error: sizes do not match");
     399              :     for (size_t i = 0; i < N1; ++i)
     400              :         a[i] -= b[i];
     401              :     return a;
     402              : }
     403              : 
     404              : /// Array -= Array
     405              : /// @related Array
     406              : template <class T1, class T2, size_t N1, size_t N2>
     407              : Array<T1, N1> &operator-=(Array<T1, N1> &a, const Array<T2, N2> &b) {
     408              :     a.slice() -= b.slice();
     409              :     return a;
     410              : }
     411              : 
     412              : // Scalar Multiplication :::::::::::::::::::::::::::::::::::::::::::::::::::::::
     413              : 
     414              : /// Slice * Scalar
     415              : /// @related ArraySlice
     416              : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     417              : Array<decltype(T1 {} * T2 {}), N1>
     418            2 : operator*(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
     419            2 :     Array<decltype(T1 {} * T2 {}), N1> result = {{}};
     420           10 :     for (size_t i = 0; i < N1; ++i)
     421            8 :         result[i] = a[i] * b;
     422            2 :     return result;
     423              : }
     424              : 
     425              : /// Array * Scalar
     426              : /// @related Array
     427              : template <class T1, class T2, size_t N1>
     428            1 : Array<decltype(T1 {} * T2 {}), N1> operator*(const Array<T1, N1> &a, T2 b) {
     429            1 :     return a.slice() * b;
     430              : }
     431              : 
     432              : /// Scalar * Slice
     433              : /// @related ArraySlice
     434              : template <class T1, class T2, size_t N2, bool Reverse2, bool Const2>
     435              : Array<decltype(T1 {} * T2 {}), N2>
     436            2 : operator*(T1 a, ArraySlice<T2, N2, Reverse2, Const2> b) {
     437            2 :     Array<decltype(T1 {} * T2 {}), N2> result = {{}};
     438           10 :     for (size_t i = 0; i < N2; ++i)
     439            8 :         result[i] = a * b[i];
     440            2 :     return result;
     441              : }
     442              : 
     443              : /// Scalar * Array
     444              : /// @related Array
     445              : template <class T1, class T2, size_t N2>
     446            1 : Array<decltype(T1 {} * T2 {}), N2> operator*(T1 a, const Array<T2, N2> &b) {
     447            1 :     return a * b.slice();
     448              : }
     449              : 
     450              : /// Slice *= Scalar
     451              : /// @related ArraySlice
     452              : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     453              : const ArraySlice<T1, N1, Reverse1, Const1> &
     454            2 : operator*=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
     455           10 :     for (size_t i = 0; i < N1; ++i)
     456            8 :         a[i] *= b;
     457            2 :     return a;
     458              : }
     459              : 
     460              : /// Array *= Scalar
     461              : /// @related Array
     462              : template <class T1, class T2, size_t N1>
     463            1 : Array<T1, N1> &operator*=(Array<T1, N1> &a, T2 b) {
     464            1 :     a.slice() *= b;
     465            1 :     return a;
     466              : }
     467              : 
     468              : // Scalar Division :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     469              : 
     470              : /// Slice / Scalar
     471              : /// @related ArraySlice
     472              : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     473              : Array<decltype(T1 {} / T2 {}), N1>
     474            2 : operator/(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
     475            2 :     Array<decltype(T1 {} / T2 {}), N1> result = {{}};
     476           10 :     for (size_t i = 0; i < N1; ++i)
     477            8 :         result[i] = a[i] / b;
     478            2 :     return result;
     479              : }
     480              : 
     481              : /// Array / Scalar
     482              : /// @related Array
     483              : template <class T1, class T2, size_t N1>
     484            1 : Array<decltype(T1 {} / T2 {}), N1> operator/(const Array<T1, N1> &a, T2 b) {
     485            1 :     return a.slice() / b;
     486              : }
     487              : 
     488              : /// Slice /= Scalar
     489              : /// @related ArraySlice
     490              : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     491              : const ArraySlice<T1, N1, Reverse1, Const1> &
     492            2 : operator/=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
     493           10 :     for (size_t i = 0; i < N1; ++i)
     494            8 :         a[i] /= b;
     495            2 :     return a;
     496              : }
     497              : 
     498              : /// Array /= Scalar
     499              : /// @related Array
     500              : template <class T1, class T2, size_t N1>
     501            1 : Array<T1, N1> &operator/=(Array<T1, N1> &a, T2 b) {
     502            1 :     a.slice() /= b;
     503            1 :     return a;
     504              : }
     505              : 
     506              : // Negation ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     507              : 
     508              : /// -Slice
     509              : /// @related ArraySlice
     510              : template <class T, size_t N, bool Reverse, bool Const>
     511            2 : Array<decltype(-T {}), N> operator-(ArraySlice<T, N, Reverse, Const> a) {
     512            2 :     Array<decltype(-T {}), N> result = {{}};
     513           11 :     for (size_t i = 0; i < N; ++i)
     514            9 :         result[i] = -a[i];
     515            2 :     return result;
     516              : }
     517              : 
     518              : /// -Array
     519              : /// @related Array
     520              : template <class T, size_t N>
     521            1 : Array<decltype(-T {}), N> operator-(const Array<T, N> &a) {
     522            1 :     return -a.slice();
     523              : }
     524              : 
     525              : // Type aliases ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     526              : 
     527              : /// An easy alias for two-dimensional Arrays.
     528              : template <class T, size_t NumRows, size_t NumCols>
     529              : using Array2D = Array<Array<T, NumCols>, NumRows>;
     530              : 
     531              : /// @}
     532              : 
     533              : END_AH_NAMESPACE
        

Generated by: LCOV version 2.4-beta