/* ✔ */ #pragma once #include #include #include // conditional #include // size_t BEGIN_AH_NAMESPACE template constexpr T abs_diff(const T &a, const T &b) { return a < b ? b - a : a - b; } /// @addtogroup AH_Containers /// @{ template class ArraySlice; /** * @brief An array wrapper for easy copying, comparing, and iterating. * * @tparam T * The type of the elements in the array. * @tparam N * The number of elements in the array. */ template struct Array { T data[N]; using type = T; constexpr static size_t length = N; /** * @brief Get the element at the given index. * * @note Bounds checking is performed. If fatal errors are disabled, the * last element is returned if the index is out of bounds. * * @param index * The (zero-based) index of the element to return. */ T &operator[](size_t index) { if (index >= N) { // TODO ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDED); index = N - 1; // LCOV_EXCL_LINE } // LCOV_EXCL_LINE return data[index]; } /** * @brief Get the element at the given index. * * @note Bounds checking is performed. If fatal errors are disabled, the * last element is returned if the index is out of bounds. * * @param index * The (zero-based) index of the element to return. */ const T &operator[](size_t index) const { if (index >= N) { // TODO ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDED); index = N - 1; // LCOV_EXCL_LINE } // LCOV_EXCL_LINE return data[index]; } /** * @brief Get a pointer to the first element. */ T *begin() { return &data[0]; } /** * @brief Get a pointer to the first element. */ const T *begin() const { return &data[0]; } /** * @brief Get a pointer to the memory beyond the array. */ T *end() { return &data[N]; } /** * @brief Get a pointer to the memory beyond the array. */ const T *end() const { return &data[N]; } /** * @brief Check the equality of all elements in two arrays. * * @param rhs * The array to compare this array to. */ bool operator==(const Array &rhs) const { if (this == &rhs) return true; for (size_t i = 0; i < N; i++) if ((*this)[i] != rhs[i]) return false; return true; } /** * @brief Check the inequality of all elements in two arrays. * * @param rhs * The array to compare this array to. */ bool operator!=(const Array &rhs) const { return !(*this == rhs); } public: /** * @brief Get a view on a slice of the Array. * * Doesn't copy the contents of the array, it's just a reference to the * original array. * * @tparam Start * The start index of the slice. * @tparam End * The end index of the slice. */ template ArraySlice slice(); /** * @brief Get a read-only view on a slice of the Array. * @copydetails slice() */ template ArraySlice slice() const; /** * @brief Get a read-only view on a slice of the Array. * @copydetails slice() */ template ArraySlice cslice() const { const Array *This = this; return This->template slice(); } }; /** * @brief Class for a view on a slice of an array. * * Doesn't copy the contents of the array, it's just a reference to the original * array. * * @tparam T * The type of elements of the Array. * @tparam N * The size of the slice. * @tparam Reverse * Whether the slice is reversed or not. * @tparam Const * Whether to save a read-only or mutable reference to the Array. */ template class ArraySlice { using ElementRefType = typename std::conditional::type; using ElementPtrType = typename std::conditional::type; public: /// Constructor ArraySlice(ElementPtrType array) : array {array} {} /// Implicit conversion from slice to new array (creates a copy). operator Array() const { return asArray(); } Array asArray() const { Array slice = {{}}; for (size_t i = 0; i < N; ++i) slice[i] = (*this)[i]; return slice; } using iterator = typename std::conditional< Reverse, std::reverse_iterator, ElementPtrType>::type; /** * @brief Get the element at the given index. * * @note Bounds checking is performed. If fatal errors are disabled, the * last element is returned if the index is out of bounds. * * @param index * The (zero-based) index of the element to return. */ ElementRefType operator[](size_t index) const { if (index >= N) { // TODO ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDEF); index = N - 1; // LCOV_EXCL_LINE } // LCOV_EXCL_LINE if (Reverse) return *(array - index); else return *(array + index); } iterator begin() const { if (Reverse) return iterator {array + 1}; else return iterator {array}; } iterator end() const { if (Reverse) return iterator {array + 1 - N}; else return iterator {array + N}; } template ArraySlice slice() const; private: ElementPtrType array; }; template template auto Array::slice() -> ArraySlice { static_assert(Start < N, ""); static_assert(End < N, ""); return &(*this)[Start]; } template template auto Array::slice() const -> ArraySlice { static_assert(Start < N, ""); static_assert(End < N, ""); return &(*this)[Start]; } template template auto ArraySlice::slice() const -> ArraySlice { static_assert(Start < N, ""); static_assert(End < N, ""); return &(*this)[Start]; } /// @related ArraySlice::iterator template typename ArraySlice::iterator operator+( typename ArraySlice::iterator::difference_type n, typename ArraySlice::iterator a) { return a + n; } // Equality :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// Slice == Slice /// @related ArraySlice template bool operator==(ArraySlice a, ArraySlice b) { static_assert(N1 == N2, "Error: sizes do not match"); for (size_t i = 0; i < N1; ++i) if (a[i] != b[i]) return false; return true; } /// Array == Slice /// @related ArraySlice template bool operator==(const Array &a, ArraySlice b) { return a.slice() == b; } /// Slice == Array /// @related ArraySlice template bool operator==(ArraySlice a, const Array &b) { return a == b.slice(); } // Inequality :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// Slice != Slice /// @related ArraySlice template bool operator!=(ArraySlice a, ArraySlice b) { return !(a == b); } /// Array != Slice /// @related ArraySlice template bool operator!=(const Array &a, ArraySlice b) { return a.slice() != b; } /// Slice != Array /// @related ArraySlice template bool operator!=(ArraySlice a, const Array &b) { return a != b.slice(); } // Addition :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// Slice + Slice /// @related ArraySlice template Array operator+(ArraySlice a, ArraySlice b) { static_assert(N1 == N2, "Error: sizes do not match"); Array result = {{}}; for (size_t i = 0; i < N1; ++i) result[i] = a[i] + b[i]; return result; } /// Array + Array /// @related Array template Array operator+(const Array &a, const Array &b) { return a.slice() + b.slice(); } /// Slice += Slice /// @related ArraySlice template const ArraySlice & operator+=(const ArraySlice &a, const ArraySlice &b) { static_assert(N1 == N2, "Error: sizes do not match"); for (size_t i = 0; i < N1; ++i) a[i] += b[i]; return a; } /// Array += Array /// @related Array template Array &operator+=(Array &a, const Array &b) { a.slice() += b.slice(); return a; } // Subtraction ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// Slice - Slice /// @related ArraySlice template Array operator-(ArraySlice a, ArraySlice b) { static_assert(N1 == N2, "Error: sizes do not match"); Array result = {{}}; for (size_t i = 0; i < N1; ++i) result[i] = a[i] - b[i]; return result; } /// Array - Array /// @related Array template Array operator-(const Array &a, const Array &b) { return a.slice() - b.slice(); } /// Slice -= Slice /// @related ArraySlice template const ArraySlice & operator-=(const ArraySlice &a, const ArraySlice &b) { static_assert(N1 == N2, "Error: sizes do not match"); for (size_t i = 0; i < N1; ++i) a[i] -= b[i]; return a; } /// Array -= Array /// @related Array template Array &operator-=(Array &a, const Array &b) { a.slice() -= b.slice(); return a; } // Scalar Multiplication ::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// Slice * Scalar /// @related ArraySlice template Array operator*(ArraySlice a, T2 b) { Array result = {{}}; for (size_t i = 0; i < N1; ++i) result[i] = a[i] * b; return result; } /// Array * Scalar /// @related Array template Array operator*(const Array &a, T2 b) { return a.slice() * b; } /// Scalar * Slice /// @related ArraySlice template Array operator*(T1 a, ArraySlice b) { Array result = {{}}; for (size_t i = 0; i < N2; ++i) result[i] = a * b[i]; return result; } /// Scalar * Array /// @related Array template Array operator*(T1 a, const Array &b) { return a * b.slice(); } /// Slice *= Scalar /// @related ArraySlice template const ArraySlice & operator*=(const ArraySlice &a, T2 b) { for (size_t i = 0; i < N1; ++i) a[i] *= b; return a; } /// Array *= Scalar /// @related Array template Array &operator*=(Array &a, T2 b) { a.slice() *= b; return a; } // Scalar Division ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// Slice / Scalar /// @related ArraySlice template Array operator/(ArraySlice a, T2 b) { Array result = {{}}; for (size_t i = 0; i < N1; ++i) result[i] = a[i] / b; return result; } /// Array / Scalar /// @related Array template Array operator/(const Array &a, T2 b) { return a.slice() / b; } /// Slice /= Scalar /// @related ArraySlice template const ArraySlice & operator/=(const ArraySlice &a, T2 b) { for (size_t i = 0; i < N1; ++i) a[i] /= b; return a; } /// Array /= Scalar /// @related Array template Array &operator/=(Array &a, T2 b) { a.slice() /= b; return a; } // Negation :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// -Slice /// @related ArraySlice template Array operator-(ArraySlice a) { Array result = {{}}; for (size_t i = 0; i < N; ++i) result[i] = -a[i]; return result; } /// -Array /// @related Array template Array operator-(const Array &a) { return -a.slice(); } // Type aliases :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// An easy alias for two-dimensional Arrays. template using Array2D = Array, NumRows>; /// @} END_AH_NAMESPACE