LCOV - code coverage report
Current view: top level - src/AH/Containers - Array.hpp (source / functions) Hit Total Coverage
Test: e224b347cd670555e44f06608ac41bd1ace9d9d8 Lines: 140 140 100.0 %
Date: 2020-09-08 17:44:46 Functions: 178 195 91.3 %
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         640 : 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         484 :     T &operator[](size_t index) {
      51         484 :         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         483 :         return data[index];
      56           1 :     }
      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        1026 :     const T &operator[](size_t index) const {
      68        1026 :         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        1025 :         return data[index];
      73           1 :     }
      74             : 
      75             :     /**
      76             :      * @brief   Get a pointer to the first element.
      77             :      */
      78          36 :     T *begin() { return &data[0]; }
      79             : 
      80             :     /**
      81             :      * @brief   Get a pointer to the first element.
      82             :      */
      83          33 :     const T *begin() const { return &data[0]; }
      84             : 
      85             :     /**
      86             :      * @brief   Get a pointer to the memory beyond the array.
      87             :      */
      88          34 :     T *end() { return &data[N]; }
      89             : 
      90             :     /**
      91             :      * @brief   Get a pointer to the memory beyond the array.
      92             :      */
      93          33 :     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          43 :     bool operator==(const Array<T, N> &rhs) const {
     102          43 :         if (this == &rhs)
     103           3 :             return true;
     104         222 :         for (size_t i = 0; i < N; i++)
     105         182 :             if ((*this)[i] != rhs[i])
     106           3 :                 return false;
     107          37 :         return true;
     108          43 :     }
     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             :     cslice() const {
     147             :         const Array<T, N> *This = this;
     148             :         return This->slice();
     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          56 :     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           2 :     Array<T, N> asArray() const {
     182           2 :         Array<T, N> slice = {{}};
     183          10 :         for (size_t i = 0; i < N; ++i)
     184           8 :             slice[i] = (*this)[i];
     185           2 :         return slice;
     186             :     }
     187             : 
     188             :     class Iterator {
     189             :       public:
     190          18 :         Iterator(ElementPtrType ptr) : ptr(ptr) {}
     191             : 
     192             :         using difference_type = std::ptrdiff_t;
     193             :         using value_type = T;
     194             :         using pointer = ElementPtrType;
     195             :         using reference = ElementRefType;
     196             :         using iterator_category = std::random_access_iterator_tag;
     197             : 
     198          12 :         bool operator!=(Iterator rhs) const { return ptr != rhs.ptr; }
     199           3 :         bool operator==(Iterator rhs) const { return ptr == rhs.ptr; }
     200             : 
     201          62 :         reference operator*() const { return *ptr; }
     202             : 
     203          10 :         Iterator &operator++() {
     204          10 :             Reverse ? --ptr : ++ptr;
     205          10 :             return *this;
     206             :         }
     207             : 
     208          25 :         Iterator &operator--() {
     209          25 :             Reverse ? ++ptr : --ptr;
     210          25 :             return *this;
     211             :         }
     212             : 
     213           8 :         difference_type operator-(Iterator rhs) const {
     214           8 :             return Reverse ? rhs.ptr - ptr : ptr - rhs.ptr;
     215             :         }
     216             : 
     217           4 :         Iterator operator+(difference_type rhs) const {
     218           4 :             return Reverse ? ptr - rhs : ptr + rhs;
     219             :         }
     220             : 
     221           4 :         Iterator operator-(difference_type rhs) const {
     222           4 :             return Reverse ? ptr + rhs : ptr - rhs;
     223             :         }
     224             : 
     225           3 :         bool operator<(Iterator rhs) const {
     226           3 :             return Reverse ? rhs.ptr < ptr : ptr < rhs.ptr;
     227             :         }
     228             : 
     229             :         bool operator<=(Iterator rhs) const {
     230             :             return Reverse ? rhs.ptr <= ptr : ptr <= rhs.ptr;
     231             :         }
     232             : 
     233             :       private:
     234             :         ElementPtrType ptr;
     235             :     };
     236             : 
     237             :     /**
     238             :      * @brief   Get the element at the given index.
     239             :      * 
     240             :      * @note    Bounds checking is performed. If fatal errors are disabled, the
     241             :      *          last element is returned if the index is out of bounds. 
     242             :      * 
     243             :      * @param   index
     244             :      *          The (zero-based) index of the element to return.
     245             :      */
     246         361 :     ElementRefType operator[](size_t index) const {
     247         361 :         if (index >= N) { // TODO
     248           2 :             ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDEF);
     249             :             index = N - 1; // LCOV_EXCL_LINE
     250             :         }                  // LCOV_EXCL_LINE
     251             :         if (Reverse)
     252          12 :             return array[-index];
     253             :         else
     254         347 :             return array[index];
     255           2 :     }
     256             : 
     257           3 :     Iterator begin() const {
     258             :         if (Reverse)
     259           2 :             return array;
     260             :         else
     261           1 :             return array;
     262             :     }
     263             : 
     264           7 :     Iterator end() const {
     265             :         if (Reverse)
     266           4 :             return array - N;
     267             :         else
     268           3 :             return array + N;
     269             :     }
     270             : 
     271             :     template <size_t Start, size_t End>
     272             :     ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const>
     273             :     slice() const;
     274             : 
     275             :   private:
     276             :     ElementPtrType array;
     277             : };
     278             : 
     279             : template <class T, size_t N>
     280             : template <size_t Start, size_t End>
     281             : inline ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), false>
     282          35 : Array<T, N>::slice() {
     283             :     static_assert(Start < N, "");
     284             :     static_assert(End < N, "");
     285          35 :     return &(*this)[Start];
     286             : }
     287             : 
     288             : template <class T, size_t N>
     289             : template <size_t Start, size_t End>
     290             : inline ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true>
     291          19 : Array<T, N>::slice() const {
     292             :     static_assert(Start < N, "");
     293             :     static_assert(End < N, "");
     294          19 :     return &(*this)[Start];
     295             : }
     296             : 
     297             : template <class T, size_t N, bool Reverse, bool Const>
     298             : template <size_t Start, size_t End>
     299             : ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const>
     300           2 : ArraySlice<T, N, Reverse, Const>::slice() const {
     301             :     static_assert(Start < N, "");
     302             :     static_assert(End < N, "");
     303           2 :     return &(*this)[Start];
     304             : }
     305             : 
     306             : // Equality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     307             : 
     308             : /// Slice == Slice
     309             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     310             :           bool Reverse2, bool Const1, bool Const2>
     311          16 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
     312             :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     313             :     static_assert(N1 == N2, "Error: sizes do not match");
     314          88 :     for (size_t i = 0; i < N1; ++i)
     315          72 :         if (a[i] != b[i])
     316           3 :             return false;
     317          13 :     return true;
     318          16 : }
     319             : 
     320             : /// Array == Slice
     321             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
     322             : bool operator==(const Array<T1, N1> &a,
     323             :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     324             :     return a.slice() == b;
     325             : }
     326             : 
     327             : /// Slice == Array
     328             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
     329           3 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
     330             :                 const Array<T2, N2> &b) {
     331           3 :     return a == b.slice();
     332             : }
     333             : 
     334             : // Inequality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     335             : 
     336             : /// Slice != Slice
     337             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     338             :           bool Reverse2, bool Const1, bool Const2>
     339           4 : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
     340             :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     341           4 :     return !(a == b);
     342             : }
     343             : 
     344             : /// Array != Slice
     345             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
     346             : bool operator!=(const Array<T1, N1> &a,
     347             :                 ArraySlice<T2, N2, Reverse2, Const2> b) {
     348             :     return a.slice() != b;
     349             : }
     350             : 
     351             : /// Slice != Array
     352             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
     353             : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
     354             :                 const Array<T2, N2> &b) {
     355             :     return a != b.slice();
     356             : }
     357             : 
     358             : // Addition ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     359             : 
     360             : /// Slice + Slice
     361             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     362             :           bool Reverse2, bool Const1, bool Const2>
     363             : Array<decltype(T1{} + T2{}), N1>
     364           2 : operator+(ArraySlice<T1, N1, Reverse1, Const1> a,
     365             :           ArraySlice<T2, N2, Reverse2, Const2> b) {
     366             :     static_assert(N1 == N2, "Error: sizes do not match");
     367           2 :     Array<decltype(T1{} + T2{}), N1> result = {{}};
     368           8 :     for (size_t i = 0; i < N1; ++i)
     369           6 :         result[i] = a[i] + b[i];
     370           2 :     return result;
     371             : }
     372             : 
     373             : /// Array + Array
     374             : template <class T1, class T2, size_t N1, size_t N2>
     375           1 : Array<decltype(T1{} + T2{}), N1> operator+(const Array<T1, N1> &a,
     376             :                                            const Array<T2, N2> &b) {
     377           1 :     return a.slice() + b.slice();
     378             : }
     379             : 
     380             : /// Slice += Slice
     381             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     382             :           bool Reverse2, bool Const1, bool Const2>
     383             : const ArraySlice<T1, N1, Reverse1, Const1> &
     384           2 : operator+=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
     385             :            const ArraySlice<T2, N2, Reverse2, Const2> &b) {
     386             :     static_assert(N1 == N2, "Error: sizes do not match");
     387           8 :     for (size_t i = 0; i < N1; ++i)
     388           6 :         a[i] += b[i];
     389           2 :     return a;
     390             : }
     391             : 
     392             : /// Array += Array
     393             : template <class T1, class T2, size_t N1, size_t N2>
     394           1 : Array<T1, N1> &operator+=(Array<T1, N1> &a, const Array<T2, N2> &b) {
     395           1 :     a.slice() += b.slice();
     396           1 :     return a;
     397             : }
     398             : 
     399             : // Subtraction :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     400             : 
     401             : /// Slice - Slice
     402             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     403             :           bool Reverse2, bool Const1, bool Const2>
     404             : Array<decltype(T1{} - T2{}), N1>
     405           2 : operator-(ArraySlice<T1, N1, Reverse1, Const1> a,
     406             :           ArraySlice<T2, N2, Reverse2, Const2> b) {
     407             :     static_assert(N1 == N2, "Error: sizes do not match");
     408           2 :     Array<decltype(T1{} - T2{}), N1> result = {{}};
     409           8 :     for (size_t i = 0; i < N1; ++i)
     410           6 :         result[i] = a[i] - b[i];
     411           2 :     return result;
     412             : }
     413             : 
     414             : /// Array - Array
     415             : template <class T1, class T2, size_t N1, size_t N2>
     416           1 : Array<decltype(T1{} - T2{}), N1> operator-(const Array<T1, N1> &a,
     417             :                                            const Array<T2, N2> &b) {
     418           1 :     return a.slice() - b.slice();
     419             : }
     420             : 
     421             : /// Slice -= Slice
     422             : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
     423             :           bool Reverse2, bool Const1, bool Const2>
     424             : const ArraySlice<T1, N1, Reverse1, Const1> &
     425             : operator-=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
     426             :            const ArraySlice<T2, N2, Reverse2, Const2> &b) {
     427             :     static_assert(N1 == N2, "Error: sizes do not match");
     428             :     for (size_t i = 0; i < N1; ++i)
     429             :         a[i] -= b[i];
     430             :     return a;
     431             : }
     432             : 
     433             : /// Array -= Array
     434             : template <class T1, class T2, size_t N1, size_t N2>
     435             : Array<T1, N1> &operator-=(Array<T1, N1> &a, const Array<T2, N2> &b) {
     436             :     a.slice() -= b.slice();
     437             :     return a;
     438             : }
     439             : 
     440             : // Scalar Multiplication :::::::::::::::::::::::::::::::::::::::::::::::::::::::
     441             : 
     442             : /// Slice * Scalar
     443             : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     444             : Array<decltype(T1{} * T2{}), N1>
     445           2 : operator*(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
     446           2 :     Array<decltype(T1{} * T2{}), N1> result = {{}};
     447          10 :     for (size_t i = 0; i < N1; ++i)
     448           8 :         result[i] = a[i] * b;
     449           2 :     return result;
     450             : }
     451             : 
     452             : /// Array * Scalar
     453             : template <class T1, class T2, size_t N1>
     454           1 : Array<decltype(T1{} * T2{}), N1> operator*(const Array<T1, N1> &a, T2 b) {
     455           1 :     return a.slice() * b;
     456             : }
     457             : 
     458             : /// Scalar * Slice
     459             : template <class T1, class T2, size_t N2, bool Reverse2, bool Const2>
     460             : Array<decltype(T1{} * T2{}), N2>
     461           2 : operator*(T1 a, ArraySlice<T2, N2, Reverse2, Const2> b) {
     462           2 :     Array<decltype(T1{} * T2{}), N2> result = {{}};
     463          10 :     for (size_t i = 0; i < N2; ++i)
     464           8 :         result[i] = a * b[i];
     465           2 :     return result;
     466             : }
     467             : 
     468             : /// Scalar * Array
     469             : template <class T1, class T2, size_t N2>
     470           1 : Array<decltype(T1{} * T2{}), N2> operator*(T1 a, const Array<T2, N2> &b) {
     471           1 :     return a * b.slice();
     472             : }
     473             : 
     474             : /// Slice *= Scalar
     475             : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     476             : const ArraySlice<T1, N1, Reverse1, Const1> &
     477           2 : operator*=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
     478          10 :     for (size_t i = 0; i < N1; ++i)
     479           8 :         a[i] *= b;
     480           2 :     return a;
     481             : }
     482             : 
     483             : /// Array *= Scalar
     484             : template <class T1, class T2, size_t N1>
     485           1 : Array<T1, N1> &operator*=(Array<T1, N1> &a, T2 b) {
     486           1 :     a.slice() *= b;
     487           1 :     return a;
     488             : }
     489             : 
     490             : // Scalar Division :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     491             : 
     492             : /// Slice / Scalar
     493             : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     494             : Array<decltype(T1{} / T2{}), N1>
     495           2 : operator/(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
     496           2 :     Array<decltype(T1{} / T2{}), N1> result = {{}};
     497          10 :     for (size_t i = 0; i < N1; ++i)
     498           8 :         result[i] = a[i] / b;
     499           2 :     return result;
     500             : }
     501             : 
     502             : /// Array / Scalar
     503             : template <class T1, class T2, size_t N1>
     504           1 : Array<decltype(T1{} / T2{}), N1> operator/(const Array<T1, N1> &a, T2 b) {
     505           1 :     return a.slice() / b;
     506             : }
     507             : 
     508             : /// Slice /= Scalar
     509             : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
     510             : const ArraySlice<T1, N1, Reverse1, Const1> &
     511           2 : operator/=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
     512          10 :     for (size_t i = 0; i < N1; ++i)
     513           8 :         a[i] /= b;
     514           2 :     return a;
     515             : }
     516             : 
     517             : /// Array /= Scalar
     518             : template <class T1, class T2, size_t N1>
     519           1 : Array<T1, N1> &operator/=(Array<T1, N1> &a, T2 b) {
     520           1 :     a.slice() /= b;
     521           1 :     return a;
     522             : }
     523             : 
     524             : // Negation ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     525             : 
     526             : /// -Slice
     527             : template <class T, size_t N, bool Reverse, bool Const>
     528           2 : Array<decltype(-T{}), N> operator-(ArraySlice<T, N, Reverse, Const> a) {
     529           2 :     Array<decltype(-T{}), N> result = {{}};
     530          11 :     for (size_t i = 0; i < N; ++i)
     531           9 :         result[i] = -a[i];
     532           2 :     return result;
     533             : }
     534             : 
     535             : /// -Array
     536             : template <class T, size_t N>
     537           1 : Array<decltype(-T{}), N> operator-(const Array<T, N> &a) {
     538           1 :     return -a.slice();
     539             : }
     540             : 
     541             : // Type aliases ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
     542             : 
     543             : /// An easy alias for two-dimensional Arrays.
     544             : template <class T, size_t nb_rows, size_t nb_cols>
     545             : using Array2D = Array<Array<T, nb_cols>, nb_rows>;
     546             : 
     547             : /// @}
     548             : 
     549             : END_AH_NAMESPACE
     550             : 
     551             : AH_DIAGNOSTIC_POP()

Generated by: LCOV version 1.14-6-g40580cd