LCOV - code coverage report
Current view: top level - src/AH/Containers - Array.hpp (source / functions) Hit Total Coverage
Test: 3a807a259ebe0769dd942f7f612dca5273937539 Lines: 119 119 100.0 %
Date: 2024-03-24 17:16:54 Functions: 167 223 74.9 %
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        1120 :     T &operator[](size_t index) {
      47        1120 :         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        1119 :         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 1.15