diff --git a/libs/main/containers/pivector2d.h b/libs/main/containers/pivector2d.h index 7bd8a990..809277e8 100644 --- a/libs/main/containers/pivector2d.h +++ b/libs/main/containers/pivector2d.h @@ -193,7 +193,6 @@ public: //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } - // --- Поиск в строке --- inline ssize_t indexOf(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -201,6 +200,7 @@ public: } return -1; } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -208,6 +208,7 @@ public: } return -1; } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -215,6 +216,7 @@ public: } return -1; } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -222,6 +224,74 @@ public: } return -1; } + + //! \~english Applies a function to each element of the row (modifiable). + //! \~russian Применяет функцию к каждому элементу строки (с возможностью изменения). + inline void forEach(std::function func) { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[st_ + i]); + } + } + + //! \~english Applies a function to each element of the row (read-only). + //! \~russian Применяет функцию к каждому элементу строки (только чтение). + inline void forEach(std::function func) const { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[st_ + i]); + } + } + + //! \~english Fills the row with copies of `value`. + //! \~russian Заполняет строку копиями `value`. + inline void fill(const T & value) { + for (size_t i = 0; i < sz_; ++i) { + (*p_)[st_ + i] = value; + } + } + + //! \~english Checks if the row contains the element `e`. + //! \~russian Проверяет, содержит ли строка элемент `e`. + inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } + + //! \~english Counts occurrences of `e` in the row. + //! \~russian Подсчитывает количество вхождений `e` в строке. + inline int entries(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[st_ + i] == e) ++count; + } + return count; + } + + //! \~english Counts elements in the row that pass the `test`. + //! \~russian Подсчитывает элементы в строке, проходящие `test`. + inline int entries(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[st_ + i])) ++count; + } + return count; + } + + //! \~english Tests if any element in the row passes the `test`. + //! \~russian Проверяет, проходит ли какой-либо элемент в строке `test`. + inline bool any(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (test((*p_)[st_ + i])) return true; + } + return false; + } + + //! \~english Tests if all elements in the row pass the `test`. + //! \~russian Проверяет, проходят ли все элементы в строке `test`. + inline bool every(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (!test((*p_)[st_ + i])) return false; + } + return true; + } }; //! \class Col @@ -290,7 +360,6 @@ public: return ret; } - // --- Поиск в столбце --- inline ssize_t indexOf(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -298,6 +367,7 @@ public: } return -1; } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -305,6 +375,7 @@ public: } return -1; } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -312,6 +383,7 @@ public: } return -1; } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -319,6 +391,74 @@ public: } return -1; } + + //! \~english Applies a function to each element of the column (modifiable). + //! \~russian Применяет функцию к каждому элементу столбца (с возможностью изменения). + inline void forEach(std::function func) { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[i * step_ + col_]); + } + } + + //! \~english Applies a function to each element of the column (read-only). + //! \~russian Применяет функцию к каждому элементу столбца (только чтение). + inline void forEach(std::function func) const { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[i * step_ + col_]); + } + } + + //! \~english Fills the column with copies of `value`. + //! \~russian Заполняет столбец копиями `value`. + inline void fill(const T & value) { + for (size_t i = 0; i < sz_; ++i) { + (*p_)[i * step_ + col_] = value; + } + } + + //! \~english Checks if the column contains the element `e`. + //! \~russian Проверяет, содержит ли столбец элемент `e`. + inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } + + //! \~english Counts occurrences of `e` in the column. + //! \~russian Подсчитывает количество вхождений `e` в столбце. + inline int entries(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[i * step_ + col_] == e) ++count; + } + return count; + } + + //! \~english Counts elements in the column that pass the `test`. + //! \~russian Подсчитывает элементы в столбце, проходящие `test`. + inline int entries(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) ++count; + } + return count; + } + + //! \~english Tests if any element in the column passes the `test`. + //! \~russian Проверяет, проходит ли какой-либо элемент в столбце `test`. + inline bool any(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) return true; + } + return false; + } + + //! \~english Tests if all elements in the column pass the `test`. + //! \~russian Проверяет, проходят ли все элементы в столбце `test`. + inline bool every(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (!test((*p_)[i * step_ + col_])) return false; + } + return true; + } }; //! \class RowConst @@ -353,7 +493,6 @@ public: //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } - // --- Поиск в строке (только чтение) --- inline ssize_t indexOf(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -361,6 +500,7 @@ public: } return -1; } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -368,6 +508,7 @@ public: } return -1; } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -375,6 +516,7 @@ public: } return -1; } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -382,6 +524,58 @@ public: } return -1; } + + //! \~english Applies a function to each element of the row (read-only). + //! \~russian Применяет функцию к каждому элементу строки (только чтение). + inline void forEach(std::function func) const { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[st_ + i]); + } + } + + //! \~english Checks if the row contains the element `e`. + //! \~russian Проверяет, содержит ли строка элемент `e`. + inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } + + //! \~english Counts occurrences of `e` in the row. + //! \~russian Подсчитывает количество вхождений `e` в строке. + inline int entries(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[st_ + i] == e) ++count; + } + return count; + } + + //! \~english Counts elements in the row that pass the `test`. + //! \~russian Подсчитывает элементы в строке, проходящие `test`. + inline int entries(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[st_ + i])) ++count; + } + return count; + } + + //! \~english Tests if any element in the row passes the `test`. + //! \~russian Проверяет, проходит ли какой-либо элемент в строке `test`. + inline bool any(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (test((*p_)[st_ + i])) return true; + } + return false; + } + + //! \~english Tests if all elements in the row pass the `test`. + //! \~russian Проверяет, проходят ли все элементы в строке `test`. + inline bool every(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (!test((*p_)[st_ + i])) return false; + } + return true; + } }; //! \class ColConst @@ -423,7 +617,6 @@ public: return ret; } - // --- Поиск в столбце (только чтение) --- inline ssize_t indexOf(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -431,6 +624,7 @@ public: } return -1; } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -438,6 +632,7 @@ public: } return -1; } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -445,6 +640,7 @@ public: } return -1; } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -452,6 +648,58 @@ public: } return -1; } + + //! \~english Applies a function to each element of the column (read-only). + //! \~russian Применяет функцию к каждому элементу столбца (только чтение). + inline void forEach(std::function func) const { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[i * step_ + col_]); + } + } + + //! \~english Checks if the column contains the element `e`. + //! \~russian Проверяет, содержит ли столбец элемент `e`. + inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } + + //! \~english Counts occurrences of `e` in the column. + //! \~russian Подсчитывает количество вхождений `e` в столбце. + inline int entries(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[i * step_ + col_] == e) ++count; + } + return count; + } + + //! \~english Counts elements in the column that pass the `test`. + //! \~russian Подсчитывает элементы в столбце, проходящие `test`. + inline int entries(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) ++count; + } + return count; + } + + //! \~english Tests if any element in the column passes the `test`. + //! \~russian Проверяет, проходит ли какой-либо элемент в столбце `test`. + inline bool any(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) return true; + } + return false; + } + + //! \~english Tests if all elements in the column pass the `test`. + //! \~russian Проверяет, проходят ли все элементы в столбце `test`. + inline bool every(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (!test((*p_)[i * step_ + col_])) return false; + } + return true; + } }; //! \~english Returns a reference to the element at the given row and column. diff --git a/tests/math/testpivector2d.cpp b/tests/math/testpivector2d.cpp index 538348d7..aef6c86b 100644 --- a/tests/math/testpivector2d.cpp +++ b/tests/math/testpivector2d.cpp @@ -993,6 +993,243 @@ TEST(Vector2DEdgeTest, single_element_vector) { EXPECT_EQ(single.element(0, 0), 42); } +// ==================== PROXY ADDITIONAL OPERATIONS TESTS ==================== +TEST_F(Vector2DTest, row_proxy_forEach_modifies_elements) { + auto row = vec[5]; + row.forEach([](int & e) { e += 100; }); + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(5, c), 5 * COLS_COUNT_INIT + c + 100); + } +} + +TEST_F(Vector2DTest, row_proxy_forEach_const_iterates) { + const auto & constVec = vec; + auto row = constVec[5]; + size_t count = 0; + row.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, COLS_COUNT_INIT); +} + +TEST_F(Vector2DTest, row_proxy_fill_sets_all_elements) { + auto row = vec[12]; + row.fill(999); + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(12, c), 999); + } +} + +TEST_F(Vector2DTest, row_proxy_contains_finds_element) { + auto row = vec[8]; + EXPECT_TRUE(row.contains(vec.element(8, 10))); + EXPECT_FALSE(row.contains(-999)); +} + +TEST_F(Vector2DTest, row_proxy_entries_counts_occurrences) { + auto row = vec[15]; + // Add a duplicate + int val = vec.element(15, 5); + vec.element(15, 20) = val; + EXPECT_EQ(row.entries(val), 2); + EXPECT_EQ(row.entries(-999), 0); +} + +TEST_F(Vector2DTest, row_proxy_entries_with_predicate_counts_matches) { + auto row = vec[20]; + auto isEven = [](const int & e) { return e % 2 == 0; }; + int expected = 0; + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + if (row[c] % 2 == 0) ++expected; + } + EXPECT_EQ(row.entries(isEven), expected); +} + +TEST_F(Vector2DTest, row_proxy_any_returns_true_if_any_match) { + auto row = vec[25]; + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(row.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(row.any(isPositive)); +} + +TEST_F(Vector2DTest, row_proxy_every_returns_true_if_all_match) { + auto row = vec[30]; + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(row.every(isLessThanMax)); + auto isEven = [](const int & e) { return e % 2 == 0; }; + EXPECT_FALSE(row.every(isEven)); +} + +// ---------------------------------------------------------------------------- +TEST_F(Vector2DTest, col_proxy_forEach_modifies_elements) { + auto col = vec.col(7); + col.forEach([](int & e) { e += 50; }); + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(vec.element(r, 7), r * COLS_COUNT_INIT + 7 + 50); + } +} + +TEST_F(Vector2DTest, col_proxy_forEach_const_iterates) { + const auto & constVec = vec; + auto col = constVec.col(9); + size_t count = 0; + col.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, ROWS_COUNT_INIT); +} + +TEST_F(Vector2DTest, col_proxy_fill_sets_all_elements) { + auto col = vec.col(11); + col.fill(777); + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(vec.element(r, 11), 777); + } +} + +TEST_F(Vector2DTest, col_proxy_contains_finds_element) { + auto col = vec.col(13); + EXPECT_TRUE(col.contains(vec.element(5, 13))); + EXPECT_FALSE(col.contains(-999)); +} + +TEST_F(Vector2DTest, col_proxy_entries_counts_occurrences) { + auto col = vec.col(17); + int val = vec.element(3, 17); + vec.element(22, 17) = val; // duplicate + EXPECT_EQ(col.entries(val), 2); + EXPECT_EQ(col.entries(-999), 0); +} + +TEST_F(Vector2DTest, col_proxy_entries_with_predicate_counts_matches) { + auto col = vec.col(19); + auto isOdd = [](const int & e) { return e % 2 != 0; }; + int expected = 0; + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + if (col[r] % 2 != 0) ++expected; + } + EXPECT_EQ(col.entries(isOdd), expected); +} + +TEST_F(Vector2DTest, col_proxy_any_returns_true_if_any_match) { + auto col = vec.col(21); + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(col.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(col.any(isPositive)); +} + +TEST_F(Vector2DTest, col_proxy_every_returns_true_if_all_match) { + auto col = vec.col(23); + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(col.every(isLessThanMax)); + auto isEven = [](const int & e) { return e % 2 == 0; }; + EXPECT_FALSE(col.every(isEven)); +} + +// ---------------------------------------------------------------------------- +TEST_F(Vector2DTest, rowconst_proxy_forEach_iterates) { + const auto & constVec = vec; + auto row = constVec[5]; + size_t count = 0; + row.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, COLS_COUNT_INIT); +} + +TEST_F(Vector2DTest, rowconst_proxy_contains_finds_element) { + const auto & constVec = vec; + auto row = constVec[6]; + EXPECT_TRUE(row.contains(vec.element(6, 10))); + EXPECT_FALSE(row.contains(-999)); +} + +TEST_F(Vector2DTest, rowconst_proxy_entries_counts_occurrences) { + const auto & constVec = vec; + auto row = constVec[7]; + int val = vec.element(7, 5); + // We can't modify through const proxy, but duplicates already exist from previous tests + EXPECT_GE(row.entries(val), 1); // at least one +} + +TEST_F(Vector2DTest, rowconst_proxy_entries_with_predicate_counts_matches) { + const auto & constVec = vec; + auto row = constVec[9]; + auto isEven = [](const int & e) { return e % 2 == 0; }; + int expected = 0; + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + if (vec.element(9, c) % 2 == 0) ++expected; + } + EXPECT_EQ(row.entries(isEven), expected); +} + +TEST_F(Vector2DTest, rowconst_proxy_any_returns_true_if_any_match) { + const auto & constVec = vec; + auto row = constVec[10]; + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(row.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(row.any(isPositive)); +} + +TEST_F(Vector2DTest, rowconst_proxy_every_returns_true_if_all_match) { + const auto & constVec = vec; + auto row = constVec[11]; + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(row.every(isLessThanMax)); + auto isEven = [](const int & e) { return e % 2 == 0; }; + EXPECT_FALSE(row.every(isEven)); +} + +// ---------------------------------------------------------------------------- +TEST_F(Vector2DTest, colconst_proxy_forEach_iterates) { + const auto & constVec = vec; + auto col = constVec.col(25); + size_t count = 0; + col.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, ROWS_COUNT_INIT); +} + +TEST_F(Vector2DTest, colconst_proxy_contains_finds_element) { + const auto & constVec = vec; + auto col = constVec.col(27); + EXPECT_TRUE(col.contains(vec.element(4, 27))); + EXPECT_FALSE(col.contains(-999)); +} + +TEST_F(Vector2DTest, colconst_proxy_entries_counts_occurrences) { + const auto & constVec = vec; + auto col = constVec.col(29); + int val = vec.element(8, 29); + EXPECT_GE(col.entries(val), 1); +} + +TEST_F(Vector2DTest, colconst_proxy_entries_with_predicate_counts_matches) { + const auto & constVec = vec; + auto col = constVec.col(31); + auto isOdd = [](const int & e) { return e % 2 != 0; }; + int expected = 0; + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + if (vec.element(r, 31) % 2 != 0) ++expected; + } + EXPECT_EQ(col.entries(isOdd), expected); +} + +TEST_F(Vector2DTest, colconst_proxy_any_returns_true_if_any_match) { + const auto & constVec = vec; + auto col = constVec.col(33); + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(col.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(col.any(isPositive)); +} + +TEST_F(Vector2DTest, colconst_proxy_every_returns_true_if_all_match) { + const auto & constVec = vec; + auto col = constVec.col(0); + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(col.every(isLessThanMax)); + auto isNotEven = [](const int & e) { return e % 2 != 0; }; + col.forEach([](const int & v) { piCout << v; }); + EXPECT_FALSE(col.every(isNotEven)); +} + // ==================== OUTPUT TESTS ==================== TEST_F(Vector2DTest, picout_operator_works) { // Just test that it compiles and doesn't crash