LCOV - code coverage report
Current view: top level - src/AH/Containers - Array.hpp (source / functions) Hit Total Coverage
Test: 91b605873905a6fcb78324052c97dbac10849539 Lines: 119 119 100.0 %
Date: 2022-11-08 01:34:37 Functions: 166 184 90.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.15