#include "pistring.h" #include "pivector2d.h" #include "gtest/gtest.h" #include size_t ROWS_COUNT_INIT = 31; size_t COLS_COUNT_INIT = 34; int ROWS_COUNT_INCREASE = 41; int COLS_COUNT_INCREASE = 44; int ROWS_COUNT_REDUCE = 22; int COLS_COUNT_REDUCE = 13; void fill_with_sequential(PIVector2D & vec, int rows, int cols) { for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { vec.element(r, c) = r * cols + c; } } } void assert_fill_with_sequential(const PIVector2D & vec, int rows, int cols) { ASSERT_EQ(vec.rows(), rows); ASSERT_EQ(vec.cols(), cols); for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { ASSERT_EQ(vec.element(r, c), r * cols + c); } } } class Vector2DTest: public ::testing::Test { protected: PIVector2D vec = PIVector2D(ROWS_COUNT_INIT, COLS_COUNT_INIT); void SetUp() override { fill_with_sequential(vec, ROWS_COUNT_INIT, COLS_COUNT_INIT); } }; // ==================== CONSTRUCTOR TESTS ==================== TEST_F(Vector2DTest, defaultConstructor_createsEmptyVector) { PIVector2D emptyVec; EXPECT_TRUE(emptyVec.isEmpty()); EXPECT_EQ(emptyVec.rows(), 0); EXPECT_EQ(emptyVec.cols(), 0); EXPECT_EQ(emptyVec.size(), 0); } TEST_F(Vector2DTest, sizedConstructor_createsCorrectDimensions) { PIVector2D testVec(5, 3, 42); EXPECT_EQ(testVec.rows(), 5); EXPECT_EQ(testVec.cols(), 3); EXPECT_EQ(testVec.size(), 15); for (size_t r = 0; r < 5; ++r) { for (size_t c = 0; c < 3; ++c) { EXPECT_EQ(testVec.element(r, c), 42); } } } TEST_F(Vector2DTest, fromPlainVector_constructor_reshapesCorrectly) { PIVector plain(20); std::iota(plain.data(), plain.data() + 20, 0); PIVector2D vec2d(4, 5, plain); EXPECT_EQ(vec2d.rows(), 4); EXPECT_EQ(vec2d.cols(), 5); for (size_t r = 0; r < 4; ++r) { for (size_t c = 0; c < 5; ++c) { EXPECT_EQ(vec2d.element(r, c), static_cast(r * 5 + c)); } } } TEST_F(Vector2DTest, fromPlainVector_move_constructor_reshapesCorrectly) { PIVector plain(20); std::iota(plain.data(), plain.data() + 20, 0); PIVector2D vec2d(4, 5, std::move(plain)); EXPECT_EQ(vec2d.rows(), 4); EXPECT_EQ(vec2d.cols(), 5); EXPECT_TRUE(plain.isEmpty()); // Moved-from state for (size_t r = 0; r < 4; ++r) { for (size_t c = 0; c < 5; ++c) { EXPECT_EQ(vec2d.element(r, c), static_cast(r * 5 + c)); } } } TEST_F(Vector2DTest, fromVectorOfVectors_constructor_reshapesCorrectly) { PIVector> vectors; vectors << PIVector({1, 2, 3}) << PIVector({4, 5, 6}) << PIVector({7, 8, 9}); PIVector2D vec2d(vectors); EXPECT_EQ(vec2d.rows(), 3); EXPECT_EQ(vec2d.cols(), 3); EXPECT_EQ(vec2d.element(0, 0), 1); EXPECT_EQ(vec2d.element(1, 1), 5); EXPECT_EQ(vec2d.element(2, 2), 9); } // ==================== CAPACITY TESTS ==================== TEST_F(Vector2DTest, sizeMethods_returnCorrectValues) { EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT); EXPECT_EQ(vec.cols(), COLS_COUNT_INIT); EXPECT_EQ(vec.size(), ROWS_COUNT_INIT * COLS_COUNT_INIT); EXPECT_EQ(vec.size_s(), static_cast(ROWS_COUNT_INIT * COLS_COUNT_INIT)); EXPECT_EQ(vec.length(), ROWS_COUNT_INIT * COLS_COUNT_INIT); EXPECT_FALSE(vec.isEmpty()); EXPECT_TRUE(vec.isNotEmpty()); EXPECT_GE(vec.capacity(), vec.size()); } // ==================== ELEMENT ACCESS TESTS ==================== TEST_F(Vector2DTest, element_access_returnsCorrectValues) { EXPECT_EQ(vec.element(5, 7), 5 * COLS_COUNT_INIT + 7); EXPECT_EQ(vec.at(10, 20), 10 * COLS_COUNT_INIT + 20); vec.element(15, 15) = 999; EXPECT_EQ(vec.element(15, 15), 999); } TEST_F(Vector2DTest, row_proxy_allows_elementAccess) { auto row = vec[5]; EXPECT_EQ(row.size(), COLS_COUNT_INIT); EXPECT_EQ(row[7], 5 * COLS_COUNT_INIT + 7); row[10] = 123; EXPECT_EQ(vec.element(5, 10), 123); } TEST_F(Vector2DTest, row_proxy_data_pointer_works) { auto row = vec[10]; int * ptr = row.data(); EXPECT_EQ(ptr, vec.data(10 * COLS_COUNT_INIT)); ptr[5] = 777; EXPECT_EQ(vec.element(10, 5), 777); } TEST_F(Vector2DTest, row_proxy_const_access_works) { const auto & constVec = vec; auto row = constVec[5]; EXPECT_EQ(row[7], 5 * COLS_COUNT_INIT + 7); // Compilation test - uncommenting should fail // row[10] = 123; } TEST_F(Vector2DTest, row_proxy_assignment_works) { PIVector2D other(ROWS_COUNT_INIT, COLS_COUNT_INIT, 42); vec[10] = other[10]; for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(10, c), 42); } PIVector newRow(COLS_COUNT_INIT, 99); vec[15] = newRow; for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(15, c), 99); } } TEST_F(Vector2DTest, row_proxy_toVector_conversion_works) { auto rowVec = vec[7].toVector(); EXPECT_EQ(rowVec.size(), COLS_COUNT_INIT); for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(rowVec[c], vec.element(7, c)); } } TEST_F(Vector2DTest, col_proxy_allows_elementAccess) { auto col = vec.col(5); EXPECT_EQ(col.size(), ROWS_COUNT_INIT); EXPECT_EQ(col[10], 10 * COLS_COUNT_INIT + 5); col[15] = 456; EXPECT_EQ(vec.element(15, 5), 456); } TEST_F(Vector2DTest, col_proxy_data_pointer_works) { auto col = vec.col(8); int * ptr = col.data(5); // Start from row 5 EXPECT_EQ(ptr, &vec.element(5, 8)); col[2] = 888; // This should affect row 7 EXPECT_EQ(vec.element(2, 8), 888); } TEST_F(Vector2DTest, col_proxy_assignment_works) { PIVector2D other(ROWS_COUNT_INIT, COLS_COUNT_INIT, 42); vec.col(12) = other.col(12); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { EXPECT_EQ(vec.element(r, 12), 42); } PIVector newCol(ROWS_COUNT_INIT, 77); vec.col(20) = newCol; for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { EXPECT_EQ(vec.element(r, 20), 77); } } TEST_F(Vector2DTest, col_proxy_toVector_conversion_works) { auto colVec = vec.col(9).toVector(); EXPECT_EQ(colVec.size(), ROWS_COUNT_INIT); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { EXPECT_EQ(colVec[r], vec.element(r, 9)); } } TEST_F(Vector2DTest, row_and_col_methods_return_same_as_operator) { auto row1 = vec.row(10); auto row2 = vec[10]; EXPECT_EQ(row1[0], row2[0]); auto col1 = vec.col(15); auto col2 = vec.col(15); // No operator[] for col EXPECT_EQ(col1[5], col2[5]); } // ==================== MODIFIER TESTS ==================== TEST_F(Vector2DTest, setRow_replaces_row_correctly) { PIVector newRow(COLS_COUNT_INIT); std::iota(newRow.data(), newRow.data() + COLS_COUNT_INIT, 100); vec.setRow(12, newRow); for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(12, c), static_cast(100 + c)); } } TEST_F(Vector2DTest, setRow_with_shorter_vector_truncates) { PIVector shortRow(COLS_COUNT_INIT - 5, 999); vec.setRow(8, shortRow); for (size_t c = 0; c < COLS_COUNT_INIT - 5; ++c) { EXPECT_EQ(vec.element(8, c), 999); } // Rest unchanged EXPECT_EQ(vec.element(8, COLS_COUNT_INIT - 5), 8 * COLS_COUNT_INIT + COLS_COUNT_INIT - 5); } TEST_F(Vector2DTest, addRow_appends_row_to_empty) { PIVector2D empty; PIVector newRow(5, 42); empty.addRow(newRow); EXPECT_EQ(empty.rows(), 1); EXPECT_EQ(empty.cols(), 5); for (size_t c = 0; c < 5; ++c) { EXPECT_EQ(empty.element(0, c), 42); } } TEST_F(Vector2DTest, addRow_appends_row_to_existing) { size_t oldRows = vec.rows(); PIVector newRow(COLS_COUNT_INIT, 999); vec.addRow(newRow); EXPECT_EQ(vec.rows(), oldRows + 1); EXPECT_EQ(vec.cols(), COLS_COUNT_INIT); for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(oldRows, c), 999); } } TEST_F(Vector2DTest, addRow_with_shorter_vector_uses_min) { size_t oldRows = vec.rows(); size_t shortCols = COLS_COUNT_INIT - 10; PIVector shortRow(shortCols, 777); vec.addRow(shortRow); EXPECT_EQ(vec.rows(), oldRows + 1); EXPECT_EQ(vec.cols(), COLS_COUNT_INIT); // cols unchanged for (size_t c = 0; c < shortCols; ++c) { EXPECT_EQ(vec.element(oldRows, c), 777); } for (size_t c = shortCols; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(oldRows, c), 0); // default initialized } } // ==================== RESIZE TESTS ==================== class Vector2DResizeTest: public Vector2DTest { protected: void assert_resize_reduce_preserves_data(int newRows, int newCols) { vec.resize(newRows, newCols, 0); ASSERT_EQ(vec.rows(), newRows); ASSERT_EQ(vec.cols(), newCols); for (int r = 0; r < newRows; ++r) { for (int c = 0; c < newCols; ++c) { EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c); } } } void assert_resize_increase_initializes_new(size_t newRows, size_t newCols) { vec.resize(newRows, newCols, 0); ASSERT_EQ(vec.rows(), newRows); ASSERT_EQ(vec.cols(), newCols); // Check old data preserved for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c); } } // Check new elements initialized to 0 for (size_t r = 0; r < newRows; ++r) { for (size_t c = 0; c < newCols; ++c) { if (r >= ROWS_COUNT_INIT || c >= COLS_COUNT_INIT) { EXPECT_EQ(vec.element(r, c), 0); } } } } }; TEST_F(Vector2DResizeTest, resize_increase_both_preserves_data) { assert_resize_increase_initializes_new(ROWS_COUNT_INCREASE, COLS_COUNT_INCREASE); } TEST_F(Vector2DResizeTest, resize_increase_rows_only_preserves_data) { assert_resize_increase_initializes_new(ROWS_COUNT_INCREASE, COLS_COUNT_INIT); } TEST_F(Vector2DResizeTest, resize_increase_cols_only_preserves_data) { assert_resize_increase_initializes_new(ROWS_COUNT_INIT, COLS_COUNT_INCREASE); } TEST_F(Vector2DResizeTest, resize_reduce_both_preserves_data) { assert_resize_reduce_preserves_data(ROWS_COUNT_REDUCE, COLS_COUNT_REDUCE); } TEST_F(Vector2DResizeTest, resize_reduce_rows_only_preserves_data) { assert_resize_reduce_preserves_data(ROWS_COUNT_REDUCE, COLS_COUNT_INIT); } TEST_F(Vector2DResizeTest, resize_reduce_cols_only_preserves_data) { assert_resize_reduce_preserves_data(ROWS_COUNT_INIT, COLS_COUNT_REDUCE); } TEST_F(Vector2DResizeTest, resize_to_zero_creates_empty) { vec.resize(0, 0, 42); EXPECT_TRUE(vec.isEmpty()); EXPECT_EQ(vec.rows(), 0); EXPECT_EQ(vec.cols(), 0); } TEST_F(Vector2DResizeTest, resize_same_dimensions_does_nothing) { size_t oldRows = vec.rows(); size_t oldCols = vec.cols(); PIVector oldData = vec.asPlainVector(); vec.resize(oldRows, oldCols, 999); EXPECT_EQ(vec.rows(), oldRows); EXPECT_EQ(vec.cols(), oldCols); EXPECT_EQ(vec.asPlainVector(), oldData); // Data unchanged } // ==================== SEARCH AND LOOKUP TESTS ==================== TEST_F(Vector2DTest, contains_finds_element_in_flat_vector) { EXPECT_TRUE(vec.contains(5 * COLS_COUNT_INIT + 7)); EXPECT_FALSE(vec.contains(-999)); EXPECT_TRUE(vec.contains(0)); // first element EXPECT_TRUE(vec.contains(ROWS_COUNT_INIT * COLS_COUNT_INIT - 1)); // last element } TEST_F(Vector2DTest, contains_with_start_parameter_works) { int target = 10 * COLS_COUNT_INIT + 15; EXPECT_TRUE(vec.contains(target)); EXPECT_TRUE(vec.contains(target, target)); // start exactly at target (inclusive) EXPECT_FALSE(vec.contains(target, target + 1)); // start after target } TEST_F(Vector2DTest, contains_vector_of_elements_works) { PIVector searchFor; searchFor << 100 << 200 << 300; EXPECT_TRUE(vec.contains(searchFor)); searchFor << -999; EXPECT_FALSE(vec.contains(searchFor)); } TEST_F(Vector2DTest, entries_counts_occurrences) { // Add some duplicates vec.fill(0); vec.element(5, 5) = 42; vec.element(10, 10) = 42; EXPECT_EQ(vec.entries(42), 2); EXPECT_EQ(vec.entries(-1), 0); } TEST_F(Vector2DTest, entries_with_predicate_counts_matches) { auto isEven = [](const int & e) { return e % 2 == 0; }; int evenCount = 0; for (size_t i = 0; i < vec.size(); ++i) { if (vec.asPlainVector()[i] % 2 == 0) evenCount++; } EXPECT_EQ(vec.entries(isEven), evenCount); } TEST_F(Vector2DTest, indexOf_finds_first_occurrence) { int firstTarget = 5 * COLS_COUNT_INIT + 10; EXPECT_EQ(vec.indexOf(firstTarget), static_cast(firstTarget)); // Add duplicate later vec.asPlainVector()[20 * COLS_COUNT_INIT + 15] = firstTarget; EXPECT_EQ(vec.indexOf(firstTarget), static_cast(firstTarget)); // Still first } TEST_F(Vector2DTest, indexOf_with_start_works) { int target = 15 * COLS_COUNT_INIT + 20; EXPECT_EQ(vec.indexOf(target, target + 1), -1); EXPECT_EQ(vec.indexOf(target, target), static_cast(target)); } TEST_F(Vector2DTest, indexWhere_finds_first_match) { auto isLarge = [](const int & e) { return e > 500; }; ssize_t expected = 501; // First element > 500 EXPECT_EQ(vec.indexWhere(isLarge), expected); } TEST_F(Vector2DTest, lastIndexOf_finds_last_occurrence) { // Add duplicate later int target = 8 * COLS_COUNT_INIT + 8; vec.asPlainVector()[25 * COLS_COUNT_INIT + 25] = target; EXPECT_EQ(vec.lastIndexOf(target), static_cast(25 * COLS_COUNT_INIT + 25)); } TEST_F(Vector2DTest, lastIndexWhere_finds_last_match) { auto isLarge = [](const int & e) { return e > 900; }; ssize_t expected = vec.size() - 1; // Last element EXPECT_EQ(vec.lastIndexWhere(isLarge), expected); } // ==================== STATISTICS AND CONDITIONS TESTS ==================== TEST_F(Vector2DTest, any_returns_true_if_any_match) { auto isNegative = [](const int & e) { return e < 0; }; auto isLarge = [](const int & e) { return e > 1000000; }; EXPECT_FALSE(vec.any(isNegative)); EXPECT_FALSE(vec.any(isLarge)); auto isPositive = [](const int & e) { return e >= 0; }; EXPECT_TRUE(vec.any(isPositive)); } TEST_F(Vector2DTest, every_returns_true_if_all_match) { auto isNonNegative = [](const int & e) { return e >= 0; }; const int max = ROWS_COUNT_INIT * COLS_COUNT_INIT; auto isLessThan = [max](const int & e) { return e < max; }; EXPECT_TRUE(vec.every(isNonNegative)); EXPECT_TRUE(vec.every(isLessThan)); auto isEven = [](const int & e) { return e % 2 == 0; }; EXPECT_FALSE(vec.every(isEven)); } // ==================== FILL AND ASSIGN TESTS ==================== TEST_F(Vector2DTest, fill_sets_all_elements_to_value) { vec.fill(42); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(r, c), 42); } } } TEST_F(Vector2DTest, fill_with_function_generates_values) { vec.fill([](size_t i) { return static_cast(i * 2); }); for (size_t i = 0; i < vec.size(); ++i) { EXPECT_EQ(vec.asPlainVector()[i], static_cast(i * 2)); } } TEST_F(Vector2DTest, assign_is_alias_for_fill) { vec.assign(99); for (size_t i = 0; i < vec.size(); ++i) { EXPECT_EQ(vec.asPlainVector()[i], 99); } } TEST_F(Vector2DTest, assign_with_size_resets_to_single_row) { vec.assign(10, 77); EXPECT_EQ(vec.rows(), 1); EXPECT_EQ(vec.cols(), 10); for (size_t c = 0; c < 10; ++c) { EXPECT_EQ(vec.element(0, c), 77); } } // ==================== COMPARISON TESTS ==================== TEST_F(Vector2DTest, equality_operator_works) { PIVector2D same = vec; EXPECT_EQ(vec, same); PIVector2D differentRows(ROWS_COUNT_INIT + 1, COLS_COUNT_INIT); EXPECT_NE(vec, differentRows); PIVector2D differentCols(ROWS_COUNT_INIT, COLS_COUNT_INIT + 1); EXPECT_NE(vec, differentCols); PIVector2D differentData(ROWS_COUNT_INIT, COLS_COUNT_INIT, 99); EXPECT_NE(vec, differentData); } // ==================== CONVERSION TESTS ==================== TEST_F(Vector2DTest, toVectors_converts_correctly) { auto vectors = vec.toVectors(); EXPECT_EQ(vectors.size(), ROWS_COUNT_INIT); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { EXPECT_EQ(vectors[r].size(), COLS_COUNT_INIT); for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vectors[r][c], vec.element(r, c)); } } } TEST_F(Vector2DTest, plainVector_returns_underlying_storage) { const auto & plain = vec.asPlainVector(); EXPECT_EQ(plain.size(), vec.size()); for (size_t i = 0; i < plain.size(); ++i) { EXPECT_EQ(plain[i], vec.asPlainVector()[i]); } } TEST_F(Vector2DTest, toPlainVector_returns_copy) { auto copy = vec.toPlainVector(); EXPECT_EQ(copy.size(), vec.size()); EXPECT_NE(copy.data(), vec.data()); // Different memory for (size_t i = 0; i < copy.size(); ++i) { EXPECT_EQ(copy[i], vec.asPlainVector()[i]); } } // ==================== SWAP TESTS ==================== TEST_F(Vector2DTest, swap_exchanges_contents) { PIVector2D other(5, 5, 42); size_t oldRows = vec.rows(); size_t oldCols = vec.cols(); PIVector oldData = vec.asPlainVector(); vec.swap(other); EXPECT_EQ(vec.rows(), 5); EXPECT_EQ(vec.cols(), 5); for (size_t i = 0; i < vec.size(); ++i) { EXPECT_EQ(vec.asPlainVector()[i], 42); } EXPECT_EQ(other.rows(), oldRows); EXPECT_EQ(other.cols(), oldCols); EXPECT_EQ(other.asPlainVector(), oldData); } // ==================== CLEAR TESTS ==================== TEST_F(Vector2DTest, clear_removes_all_elements) { vec.clear(); EXPECT_TRUE(vec.isEmpty()); EXPECT_EQ(vec.rows(), 0); EXPECT_EQ(vec.cols(), 0); EXPECT_EQ(vec.size(), 0); } // ==================== TRANSPOSE AND REVERSE TESTS ==================== TEST_F(Vector2DTest, transposed_returns_correct_dimensions) { auto transposed = vec.transposed(); EXPECT_EQ(transposed.rows(), COLS_COUNT_INIT); EXPECT_EQ(transposed.cols(), ROWS_COUNT_INIT); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(transposed.element(c, r), vec.element(r, c)); } } } TEST_F(Vector2DTest, reverseRows_reverses_row_order) { auto original = vec; vec.reverseRows(); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(r, c), original.element(ROWS_COUNT_INIT - 1 - r, c)); } } } TEST_F(Vector2DTest, reverseColumns_reverses_column_order_in_each_row) { auto original = vec; vec.reverseColumns(); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(r, c), original.element(r, COLS_COUNT_INIT - 1 - c)); } } } TEST_F(Vector2DTest, reverseRows_and_reverseColumns_compose_correctly) { auto original = vec; vec.reverseRows(); vec.reverseColumns(); // This should be equivalent to 180-degree rotation for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(r, c), original.element(ROWS_COUNT_INIT - 1 - r, COLS_COUNT_INIT - 1 - c)); } } } // ==================== RANGE TESTS ==================== TEST_F(Vector2DTest, getRange_returns_submatrix) { auto sub = vec.getRange(5, 10, 8, 15); EXPECT_EQ(sub.rows(), 10); EXPECT_EQ(sub.cols(), 15); for (size_t r = 0; r < 10; ++r) { for (size_t c = 0; c < 15; ++c) { EXPECT_EQ(sub.element(r, c), vec.element(5 + r, 8 + c)); } } } TEST_F(Vector2DTest, getRange_with_invalid_params_returns_empty) { auto sub1 = vec.getRange(ROWS_COUNT_INIT, 5, 0, 5); EXPECT_TRUE(sub1.isEmpty()); auto sub2 = vec.getRange(0, 5, COLS_COUNT_INIT, 5); EXPECT_TRUE(sub2.isEmpty()); } TEST_F(Vector2DTest, getRange_truncates_out_of_bounds) { auto sub = vec.getRange(ROWS_COUNT_INIT - 5, 10, COLS_COUNT_INIT - 5, 10); EXPECT_EQ(sub.rows(), 5); EXPECT_EQ(sub.cols(), 5); } // ==================== FUNCTIONAL PROGRAMMING TESTS ==================== TEST_F(Vector2DTest, map_transforms_elements) { auto doubled = vec.map([](const int & e) { return e * 2; }); EXPECT_EQ(doubled.rows(), vec.rows()); EXPECT_EQ(doubled.cols(), vec.cols()); for (size_t r = 0; r < vec.rows(); ++r) { for (size_t c = 0; c < vec.cols(); ++c) { EXPECT_EQ(doubled.element(r, c), vec.element(r, c) * 2); } } } TEST_F(Vector2DTest, map_changes_type) { auto asString = vec.map([](const int & e) { return PIString::fromNumber(e); }); EXPECT_EQ(asString.rows(), vec.rows()); EXPECT_EQ(asString.cols(), vec.cols()); for (size_t r = 0; r < vec.rows(); ++r) { for (size_t c = 0; c < vec.cols(); ++c) { EXPECT_EQ(asString.element(r, c), PIString::fromNumber(vec.element(r, c))); } } } TEST_F(Vector2DTest, mapIndexed_uses_indices) { auto indexed = vec.mapIndexed([](size_t r, size_t c, const int & e) { return static_cast(r * 1000 + c); }); for (size_t r = 0; r < vec.rows(); ++r) { for (size_t c = 0; c < vec.cols(); ++c) { EXPECT_EQ(indexed.element(r, c), static_cast(r * 1000 + c)); } } } TEST_F(Vector2DTest, forEach_readonly_visits_all_elements) { size_t count = 0; vec.asPlainVector().forEach([&count](const int &) { count++; }); EXPECT_EQ(count, vec.size()); } TEST_F(Vector2DTest, forEach_modifying_changes_elements) { vec.asPlainVector().forEach([](int & e) { e++; }); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c + 1); } } } /* TEST_F(Vector2DTest, forEachIndexed_readonly_uses_indices) { bool allCorrect = true; vec.forEachIndexed([&allCorrect](size_t r, size_t c, const int & e) { if (e != static_cast(r * COLS_COUNT_INIT + c)) allCorrect = false; }); EXPECT_TRUE(allCorrect); } TEST_F(Vector2DTest, forEachIndexed_modifying_uses_indices) { vec.forEachIndexed([](size_t r, size_t c, int & e) { e = static_cast(r * 1000 + c); }); for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { EXPECT_EQ(vec.element(r, c), static_cast(r * 1000 + c)); } } } */ TEST_F(Vector2DTest, reduce_accumulates_correctly) { int sum = vec.reduce([](const int & e, const int & acc) { return e + acc; }); int expected = (vec.size() - 1) * vec.size() / 2; EXPECT_EQ(sum, expected); } TEST_F(Vector2DTest, reduce_with_initial_value) { int sum = vec.reduce([](const int & e, const int & acc) { return e + acc; }, 100); int expected = (vec.size() - 1) * vec.size() / 2 + 100; EXPECT_EQ(sum, expected); } TEST_F(Vector2DTest, reduceIndexed_uses_indices) { int sum = vec.reduceIndexed([](size_t r, size_t c, const int & e, const int & acc) { return acc + static_cast(r * 1000 + c); }); int expected = 0; for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { expected += r * 1000 + c; } } EXPECT_EQ(sum, expected); } // ==================== REMOVAL TESTS ==================== TEST_F(Vector2DTest, removeRow_removes_specified_row) { size_t oldRows = vec.rows(); auto rowContent = vec[10].toVector(); vec.removeRow(10); EXPECT_EQ(vec.rows(), oldRows - 1); // Check rows after 10 shifted up for (size_t r = 10; r < vec.rows(); ++r) { for (size_t c = 0; c < vec.cols(); ++c) { EXPECT_EQ(vec.element(r, c), (r + 1) * COLS_COUNT_INIT + c); } } } TEST_F(Vector2DTest, removeRow_invalid_index_does_nothing) { size_t oldRows = vec.rows(); vec.removeRow(ROWS_COUNT_INIT + 10); EXPECT_EQ(vec.rows(), oldRows); } TEST_F(Vector2DTest, removeRow_last_row_works) { size_t oldRows = vec.rows(); vec.removeRow(oldRows - 1); EXPECT_EQ(vec.rows(), oldRows - 1); } TEST_F(Vector2DTest, removeColumn_removes_specified_column) { size_t oldCols = vec.cols(); vec.removeColumn(15); EXPECT_EQ(vec.cols(), oldCols - 1); for (size_t r = 0; r < vec.rows(); ++r) { for (size_t c = 0; c < 15; ++c) { EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c); } for (size_t c = 15; c < vec.cols(); ++c) { EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c + 1); } } } TEST_F(Vector2DTest, removeColumn_invalid_index_does_nothing) { size_t oldCols = vec.cols(); vec.removeColumn(COLS_COUNT_INIT + 10); EXPECT_EQ(vec.cols(), oldCols); } TEST_F(Vector2DTest, removeColumn_last_column_works) { size_t oldCols = vec.cols(); vec.removeColumn(oldCols - 1); EXPECT_EQ(vec.cols(), oldCols - 1); } TEST_F(Vector2DTest, removeRowsWhere_removes_matching_rows) { auto isSpecial = [](const PIVector2D::RowConst & row) { return row[0] == 999; // First element is 999 }; const size_t rowsCont = 5; // Add some identifiable rows for (size_t r = 0; r < rowsCont; ++r) { vec.addRow(PIVector(COLS_COUNT_INIT, 999)); } EXPECT_EQ(vec.filterRows(isSpecial).rows(), rowsCont); vec.removeRowsWhere(isSpecial); EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT); // Verify no rows with 999 remain auto res = vec.filterRows(isSpecial); EXPECT_TRUE(res.isEmpty()); } TEST_F(Vector2DTest, removeColumnsWhere_removes_matching_columns) { // Make some columns have a distinctive first element for (size_t c = 0; c < 5; ++c) { vec.element(0, c) = 777; } auto isSpecial = [](const PIVector2D::ColConst & col) { return col[0] == 777; // First element is 777 }; size_t oldCols = vec.cols(); vec.removeColumnsWhere(isSpecial); EXPECT_EQ(vec.cols(), oldCols - 5); // Verify no columns with 777 in first row remain for (size_t c = 0; c < vec.cols(); ++c) { EXPECT_NE(vec.element(0, c), 777); } } // ==================== FILTER TESTS ==================== TEST_F(Vector2DTest, filterRows_returns_only_matching_rows) { auto rowsWithEvenFirst = vec.filterRows([](const PIVector2D::RowConst & row) { return row[0] % 2 == 0; }); // First element of row r is r * COLS_COUNT_INIT // This is even for all rows since COLS_COUNT_INIT is even (34) EXPECT_EQ(rowsWithEvenFirst.rows(), ROWS_COUNT_INIT); auto rowsWithLargeFirst = vec.filterRows([](const PIVector2D::RowConst & row) { return row[0] > 500; }); // First element > 500 means r * 34 > 500 -> r > 14.7 EXPECT_EQ(rowsWithLargeFirst.rows(), ROWS_COUNT_INIT - 15); } TEST_F(Vector2DTest, filterColumns_returns_only_matching_columns) { auto colsWithEvenFirst = vec.filterColumns([](const PIVector2D::ColConst & col) { return col[0] % 2 == 0; }); // First element of column c is c EXPECT_EQ(colsWithEvenFirst.cols(), COLS_COUNT_INIT / 2); } TEST_F(Vector2DTest, filterColumns_empty_result_returns_empty) { auto noCols = vec.filterColumns([](const PIVector2D::ColConst &) { return false; }); EXPECT_TRUE(noCols.isEmpty()); } // ==================== EDGE CASE TESTS ==================== TEST(Vector2DEdgeTest, empty_vector_operations) { PIVector2D empty; EXPECT_TRUE(empty.isEmpty()); EXPECT_EQ(empty.rows(), 0); EXPECT_EQ(empty.cols(), 0); // These should not crash empty.clear(); empty.fill(42); empty.transposed(); empty.reverseRows(); empty.reverseColumns(); auto range = empty.getRange(0, 5, 0, 5); EXPECT_TRUE(range.isEmpty()); auto filtered = empty.filterRows([](const PIVector2D::RowConst &) { return true; }); EXPECT_TRUE(filtered.isEmpty()); } TEST(Vector2DEdgeTest, single_element_vector) { PIVector2D single(1, 1, 42); EXPECT_EQ(single.rows(), 1); EXPECT_EQ(single.cols(), 1); EXPECT_EQ(single.element(0, 0), 42); auto row = single[0]; EXPECT_EQ(row.size(), 1); EXPECT_EQ(row[0], 42); auto col = single.col(0); EXPECT_EQ(col.size(), 1); EXPECT_EQ(col[0], 42); single.reverseRows(); // Should do nothing EXPECT_EQ(single.element(0, 0), 42); single.reverseColumns(); // Should do nothing EXPECT_EQ(single.element(0, 0), 42); } // ==================== OUTPUT TESTS ==================== TEST_F(Vector2DTest, picout_operator_works) { // Just test that it compiles and doesn't crash PICout s; s << vec; // No assertion, just ensure it runs } #ifdef PIP_STD_IOSTREAM TEST_F(Vector2DTest, iostream_operator_works) { // PIVector2D doesn't have direct iostream operator, // but PIVector does, and we can test conversion std::stringstream ss; ss << vec.plainVector(); // No assertion, just ensure it runs } #endif