diff --git a/src/utils/SubArray.hpp b/src/utils/SubArray.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2ab94694febea144c527402848302d6bca7cd38b --- /dev/null +++ b/src/utils/SubArray.hpp @@ -0,0 +1,101 @@ +#ifndef SUB_ARRAY_HPP +#define SUB_ARRAY_HPP + +#include <utils/Array.hpp> +#include <utils/PugsAssert.hpp> +#include <utils/PugsMacros.hpp> +#include <utils/PugsUtils.hpp> + +#include <algorithm> + +template <typename DataType> +class [[nodiscard]] SubArray +{ + public: + using data_type = DataType; + using index_type = size_t; + + private: + // underlying array + Array<DataType> m_array; + + DataType* m_sub_values; + size_t m_size; + + // Allows const version to access our data + friend SubArray<std::add_const_t<DataType>>; + + public: + PUGS_INLINE size_t size() const noexcept + { + return m_size; + } + + PUGS_INLINE DataType& operator[](index_type i) const noexcept(NO_ASSERT) + { + Assert(i < m_size); + return m_sub_values[i]; + } + + PUGS_INLINE + void fill(const DataType& data) const + { + static_assert(not std::is_const<DataType>(), "Cannot modify SubArray of const"); + + // could consider to use std::fill + parallel_for( + this->size(), PUGS_LAMBDA(index_type i) { m_sub_values[i] = data; }); + } + + template <typename DataType2> + PUGS_INLINE SubArray& operator=(const SubArray<DataType2>& sub_array) noexcept + { + // ensures that DataType is the same as source DataType2 + static_assert(std::is_same<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>(), + "Cannot assign SubArray of different type"); + // ensures that const is not lost through copy + static_assert(((std::is_const<DataType2>() and std::is_const<DataType>()) or not std::is_const<DataType2>()), + "Cannot assign SubArray of const to SubArray of non-const"); + + m_array = sub_array.m_array; + m_size = sub_array.m_size; + m_sub_values = sub_array.m_sub_values; + + return *this; + } + + PUGS_INLINE + SubArray& operator=(const SubArray&) = default; + + PUGS_INLINE + SubArray& operator=(SubArray&&) = default; + + PUGS_INLINE + explicit SubArray(Array<DataType> array, size_t begin, size_t size) + : m_array{array}, m_sub_values{&array[0] + begin}, m_size{size} + { + Assert(begin + size <= array.size(), "SubView is not contained in the source Array"); + static_assert(not std::is_const<DataType>(), "Cannot allocate SubArray of const data: only view is " + "supported"); + } + + PUGS_INLINE + SubArray() = default; + + PUGS_INLINE + SubArray(const SubArray&) = default; + + template <typename DataType2> + PUGS_INLINE SubArray(const SubArray<DataType2>& sub_array) noexcept + { + this->operator=(sub_array); + } + + PUGS_INLINE + SubArray(SubArray &&) = default; + + PUGS_INLINE + ~SubArray() = default; +}; + +#endif // SUB_ARRAY_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c4413ddc7c6f06665b4efb7b091548fb26140fab..c8df103aab80a8d3688868823b88da4289317f48 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,6 +85,7 @@ add_executable (unit_tests test_PugsUtils.cpp test_RevisionInfo.cpp test_SparseMatrixDescriptor.cpp + test_SubArray.cpp test_SymbolTable.cpp test_Timer.cpp test_TinyMatrix.cpp diff --git a/tests/test_SubArray.cpp b/tests/test_SubArray.cpp new file mode 100644 index 0000000000000000000000000000000000000000..98e303dd1681378a50fdb281ab65bb83273a50e2 --- /dev/null +++ b/tests/test_SubArray.cpp @@ -0,0 +1,107 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/PugsAssert.hpp> +#include <utils/SubArray.hpp> +#include <utils/Types.hpp> + +// Instantiate to ensure full coverage is performed +template class SubArray<int>; + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("SubArray", "[utils]") +{ + Array<int> a(10); + REQUIRE(a.size() == 10); + + SECTION("shared values") + { + SubArray sub_a{a, 0, 10}; + for (size_t i = 0; i < sub_a.size(); ++i) { + sub_a[i] = 2 * i; + } + + REQUIRE(((a[0] == 0) and (a[1] == 2) and (a[2] == 4) and (a[3] == 6) and (a[4] == 8) and (a[5] == 10) and + (a[6] == 12) and (a[7] == 14) and (a[8] == 16) and (a[9] == 18))); + + for (size_t i = 0; i < a.size(); ++i) { + a[i] = (i + 1) * (2 * i + 1); + } + + REQUIRE(((sub_a[0] == 1) and (sub_a[1] == 6) and (sub_a[2] == 15) and (sub_a[3] == 28) and (sub_a[4] == 45) and + (sub_a[5] == 66) and (sub_a[6] == 91) and (sub_a[7] == 120) and (sub_a[8] == 153) and (sub_a[9] == 190))); + } + + SECTION("sub array") + { + a.fill(0); + SubArray sub_a{a, 5, 5}; + for (size_t i = 0; i < sub_a.size(); ++i) { + sub_a[i] = i + 1; + } + + REQUIRE(((a[0] == 0) and (a[1] == 0) and (a[2] == 0) and (a[3] == 0) and (a[4] == 0) and (a[5] == 1) and + (a[6] == 2) and (a[7] == 3) and (a[8] == 4) and (a[9] == 5))); + + for (size_t i = 0; i < a.size(); ++i) { + a[i] = (i + 1) * (2 * i + 1); + } + + REQUIRE(((sub_a[0] == 66) and (sub_a[1] == 91) and (sub_a[2] == 120) and (sub_a[3] == 153) and (sub_a[4] == 190))); + } + + SECTION("sub array copy") + { + a.fill(0); + SubArray<int> sub_a; + sub_a = SubArray{a, 5, 5}; + for (size_t i = 0; i < sub_a.size(); ++i) { + sub_a[i] = i + 1; + } + + REQUIRE(((a[0] == 0) and (a[1] == 0) and (a[2] == 0) and (a[3] == 0) and (a[4] == 0) and (a[5] == 1) and + (a[6] == 2) and (a[7] == 3) and (a[8] == 4) and (a[9] == 5))); + + for (size_t i = 0; i < a.size(); ++i) { + a[i] = (i + 1) * (2 * i + 1); + } + + REQUIRE(((sub_a[0] == 66) and (sub_a[1] == 91) and (sub_a[2] == 120) and (sub_a[3] == 153) and (sub_a[4] == 190))); + + SubArray sub_b = sub_a; + + REQUIRE(((sub_b[0] == 66) and (sub_b[1] == 91) and (sub_b[2] == 120) and (sub_b[3] == 153) and (sub_b[4] == 190))); + + sub_b = sub_a; + + REQUIRE(((sub_b[0] == 66) and (sub_b[1] == 91) and (sub_b[2] == 120) and (sub_b[3] == 153) and (sub_b[4] == 190))); + + SubArray<const int> const_sub_a{sub_a}; + + REQUIRE(((const_sub_a[0] == 66) and (const_sub_a[1] == 91) and (const_sub_a[2] == 120) and + (const_sub_a[3] == 153) and (const_sub_a[4] == 190))); + + sub_a.fill(2); + REQUIRE(((const_sub_a[0] == 2) and (const_sub_a[1] == 2) and (const_sub_a[2] == 2) and (const_sub_a[3] == 2) and + (const_sub_a[4] == 2))); + + SubArray sub_c{SubArray{a, 3, 6}}; + REQUIRE(((sub_c[0] == a[3]) and (sub_c[1] == a[4]) and (sub_c[2] == a[5]) and (sub_c[3] == a[6]) and + (sub_c[4] == a[7]) and (sub_c[5] == a[8]))); + } + +#ifndef NDEBUG + SECTION("errors") + { + a.fill(0); + SubArray<int> sub_a; + sub_a = SubArray{a, 5, 5}; + for (size_t i = 0; i < sub_a.size(); ++i) { + sub_a[i] = i + 1; + } + + REQUIRE_THROWS_AS(sub_a[5], AssertError); + } +#endif // NDEBUG +}