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

Generated by: LCOV version 1.14-5-g4ff2ed6