#include "pistring.h"
#include "pivector2d.h"

#include "gtest/gtest.h"
#include <numeric>

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<int> & 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<int> & 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<int> vec = PIVector2D<int>(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<int> 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<int> 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<int> plain(20);
	std::iota(plain.data(), plain.data() + 20, 0);

	PIVector2D<int> 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<int>(r * 5 + c));
		}
	}
}

TEST_F(Vector2DTest, fromPlainVector_move_constructor_reshapesCorrectly) {
	PIVector<int> plain(20);
	std::iota(plain.data(), plain.data() + 20, 0);

	PIVector2D<int> 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<int>(r * 5 + c));
		}
	}
}

TEST_F(Vector2DTest, fromVectorOfVectors_constructor_reshapesCorrectly) {
	PIVector<PIVector<int>> vectors;
	vectors << PIVector<int>({1, 2, 3}) << PIVector<int>({4, 5, 6}) << PIVector<int>({7, 8, 9});

	PIVector2D<int> 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<ssize_t>(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<int> 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<int> 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<int> 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<int> 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]);
}

// ==================== PROXY SEARCH TESTS ====================
TEST_F(Vector2DTest, row_proxy_search_works) {
	auto row    = vec[10];
	// indexOf
	ssize_t idx = row.indexOf(vec.element(10, 5));
	EXPECT_EQ(idx, 5);
	EXPECT_EQ(row.indexOf(-999), -1);

	// lastIndexOf (add a duplicate)
	vec.element(10, 7) = vec.element(10, 5); // duplicate
	idx                = row.lastIndexOf(vec.element(10, 5));
	EXPECT_EQ(idx, 7);

	// indexWhere
	auto isEven       = [](const int & e) { return e % 2 == 0; };
	ssize_t firstEven = row.indexWhere(isEven);
	EXPECT_GE(firstEven, 0);

	// lastIndexWhere
	ssize_t lastEven = row.lastIndexWhere(isEven);
	EXPECT_GE(lastEven, firstEven);
}

TEST_F(Vector2DTest, col_proxy_search_works) {
	auto col    = vec.col(9); // используем столбец 9, где все элементы нечётные
	ssize_t idx = col.indexOf(vec.element(12, 9));
	EXPECT_EQ(idx, 12);
	EXPECT_EQ(col.indexOf(-999), -1);

	vec.element(20, 9) = vec.element(12, 9); // duplicate
	idx                = col.lastIndexOf(vec.element(12, 9));
	EXPECT_EQ(idx, 20);

	auto isOdd       = [](const int & e) { return e % 2 != 0; };
	ssize_t firstOdd = col.indexWhere(isOdd);
	EXPECT_GE(firstOdd, 0);
	ssize_t lastOdd = col.lastIndexWhere(isOdd);
	EXPECT_GE(lastOdd, firstOdd);
}

TEST_F(Vector2DTest, rowconst_proxy_search_works) {
	const auto & constVec = vec;
	const auto row        = constVec[10];
	ssize_t idx           = row.indexOf(vec.element(10, 5));
	EXPECT_EQ(idx, 5);
	idx = row.lastIndexOf(vec.element(10, 5));
	EXPECT_EQ(idx, 5);
}

TEST_F(Vector2DTest, colconst_proxy_search_works) {
	const auto & constVec = vec;
	auto col              = constVec.col(8);
	ssize_t idx           = col.indexOf(vec.element(12, 8));
	EXPECT_EQ(idx, 12);
	idx = col.lastIndexOf(vec.element(12, 8));
	EXPECT_EQ(idx, 12);
}

// ==================== ROW/COLUMN ITERATION TESTS ====================
TEST_F(Vector2DTest, forEachRow_modifies_rows) {
	vec.forEachRow([](PIVector2D<int>::Row row) {
		for (size_t c = 0; c < row.size(); ++c)
			row[c] = 999;
	});
	for (size_t r = 0; r < vec.rows(); ++r)
		for (size_t c = 0; c < vec.cols(); ++c)
			EXPECT_EQ(vec.element(r, c), 999);
}

TEST_F(Vector2DTest, forEachRow_readonly_counts_rows) {
	size_t count = 0;
	vec.forEachRow([&count](PIVector2D<int>::RowConst) { ++count; });
	EXPECT_EQ(count, vec.rows());
}

TEST_F(Vector2DTest, forEachColumn_modifies_columns) {
	vec.forEachColumn([](PIVector2D<int>::Col col) {
		for (size_t r = 0; r < col.size(); ++r)
			col[r] = 777;
	});
	for (size_t r = 0; r < vec.rows(); ++r)
		for (size_t c = 0; c < vec.cols(); ++c)
			EXPECT_EQ(vec.element(r, c), 777);
}

TEST_F(Vector2DTest, forEachColumn_readonly_counts_columns) {
	size_t count = 0;
	vec.forEachColumn([&count](PIVector2D<int>::ColConst) { ++count; });
	EXPECT_EQ(count, vec.cols());
}

// ==================== MODIFIER TESTS ====================
TEST_F(Vector2DTest, setRow_replaces_row_correctly) {
	PIVector<int> 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<int>(100 + c));
	}
}

TEST_F(Vector2DTest, setRow_with_shorter_vector_truncates) {
	PIVector<int> 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<int> empty;
	PIVector<int> 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<int> 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<int> 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
	}
}

// ==================== APPEND ROWS TESTS ====================

TEST_F(Vector2DTest, appendRows_adds_rows_at_bottom) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	vec.appendRows(5, 42);

	EXPECT_EQ(vec.rows(), oldRows + 5);
	EXPECT_EQ(vec.cols(), oldCols);

	// Original data preserved
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = 0; c < oldCols; ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}

	// New rows filled with 42
	for (size_t r = oldRows; r < vec.rows(); ++r) {
		for (size_t c = 0; c < oldCols; ++c) {
			EXPECT_EQ(vec.element(r, c), 42);
		}
	}
}

TEST_F(Vector2DTest, appendRows_with_zero_count_does_nothing) {
	auto oldVec = vec;
	vec.appendRows(0);
	EXPECT_EQ(vec, oldVec);
}

TEST_F(Vector2DTest, appendRows_on_empty_matrix) {
	PIVector2D<int> empty;
	empty.appendRows(5, 99);
	EXPECT_TRUE(empty.isNotEmpty());
	EXPECT_EQ(empty.rows(), 5);
	EXPECT_EQ(empty.cols(), 1);
	EXPECT_EQ(empty.size(), empty.entries(99));
}

TEST_F(Vector2DTest, appendRows_with_default_value) {
	size_t oldRows = vec.rows();

	vec.appendRows(3);

	for (size_t r = oldRows; r < vec.rows(); ++r) {
		for (size_t c = 0; c < vec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), 0);
		}
	}
}

// ==================== APPEND COLUMNS TESTS ====================

TEST_F(Vector2DTest, appendColumns_adds_columns_at_right) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	vec.appendColumns(3, 99);

	EXPECT_EQ(vec.rows(), oldRows);
	EXPECT_EQ(vec.cols(), oldCols + 3);

	// Original data preserved in original columns
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = 0; c < oldCols; ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}

	// New columns filled with 99
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = oldCols; c < vec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), 99);
		}
	}
}

TEST_F(Vector2DTest, appendColumns_with_zero_count_does_nothing) {
	auto oldVec = vec;
	vec.appendColumns(0);
	EXPECT_EQ(vec, oldVec);
}

TEST_F(Vector2DTest, appendColumns_on_empty_matrix) {
	PIVector2D<int> empty;
	empty.appendColumns(5, 99);
	EXPECT_TRUE(empty.isNotEmpty());
	EXPECT_EQ(empty.cols(), 5);
	EXPECT_EQ(empty.rows(), 1);
	EXPECT_EQ(empty.size(), empty.entries(99));
}

TEST_F(Vector2DTest, appendColumns_single_column) {
	auto oldVec = vec;

	vec.appendColumns(1, 77);

	EXPECT_EQ(vec.rows(), oldVec.rows());
	EXPECT_EQ(vec.cols(), oldVec.cols() + 1);

	// Check original data
	for (size_t r = 0; r < vec.rows(); ++r) {
		for (size_t c = 0; c < oldVec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), oldVec.element(r, c));
		}
	}

	// Check new column
	for (size_t r = 0; r < vec.rows(); ++r) {
            EXPECT_EQ(vec.element(r, oldVec.cols()), 77);
	}
}

// ==================== DELETE ROWS TESTS ====================

TEST_F(Vector2DTest, deleteRows_removes_rows_from_middle) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	vec.deleteRows(5, 3);

	EXPECT_EQ(vec.rows(), oldRows - 3);
	EXPECT_EQ(vec.cols(), oldCols);

	// Rows before deleted remain
	for (size_t r = 0; r < 5; ++r) {
		for (size_t c = 0; c < oldCols; ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}

	// Rows after deleted shifted up
	for (size_t r = 5; r < vec.rows(); ++r) {
		for (size_t c = 0; c < oldCols; ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>((r + 3) * COLS_COUNT_INIT + c));
		}
	}
}

TEST_F(Vector2DTest, deleteRows_at_end_works) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	vec.deleteRows(oldRows - 2, 2);

	EXPECT_EQ(vec.rows(), oldRows - 2);
	EXPECT_EQ(vec.cols(), oldCols);

	// All remaining rows should have original content
	for (size_t r = 0; r < vec.rows(); ++r) {
		for (size_t c = 0; c < oldCols; ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}
}

TEST_F(Vector2DTest, deleteRows_beyond_bounds_is_limited) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();
	size_t count = 10;

	vec.deleteRows(oldRows - 2, count);

	EXPECT_EQ(vec.rows(), oldRows - count);
	EXPECT_EQ(vec.cols(), oldCols);

	// All remaining rows should have original content
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = 0; c < vec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}

	// All new rows should have original content
	for (size_t r = oldRows; r < vec.rows(); ++r) {
		for (size_t c = 0; c < vec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>((r+count) * COLS_COUNT_INIT + c));
		}
	}
}

TEST_F(Vector2DTest, deleteRows_invalid_start_does_nothing) {
	auto oldVec = vec;
	vec.deleteRows(oldVec.rows() + 10, 2);
	EXPECT_EQ(vec, oldVec);
}

TEST_F(Vector2DTest, deleteRows_zero_count_does_nothing) {
	auto oldVec = vec;
	vec.deleteRows(5, 0);
	EXPECT_EQ(vec, oldVec);
}

TEST_F(Vector2DTest, deleteRows_all_rows_creates_empty) {
	vec.deleteRows(0, vec.rows());
	EXPECT_TRUE(vec.isEmpty());
	EXPECT_EQ(vec.rows(), 0);
	EXPECT_EQ(vec.cols(), 0);
}

// ==================== DELETE COLUMNS TESTS ====================

TEST_F(Vector2DTest, deleteColumns_removes_columns_from_middle) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	vec.deleteColumns(4, 3);

	EXPECT_EQ(vec.rows(), oldRows);
	EXPECT_EQ(vec.cols(), oldCols - 3);

	// Columns before deleted remain
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = 0; c < 4; ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}

	// Columns after deleted shifted left
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = 4; c < vec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c + 3));
		}
	}
}

TEST_F(Vector2DTest, deleteColumns_at_end_works) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	vec.deleteColumns(oldCols - 2, 2);

	EXPECT_EQ(vec.rows(), oldRows);
	EXPECT_EQ(vec.cols(), oldCols - 2);

	// All remaining columns should have original content
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = 0; c < vec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}
}

TEST_F(Vector2DTest, deleteColumns_beyond_bounds_is_limited) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	vec.deleteColumns(oldCols - 2, 10);

	EXPECT_EQ(vec.rows(), oldRows);
	EXPECT_EQ(vec.cols(), oldCols - 2);
}

TEST_F(Vector2DTest, deleteColumns_invalid_start_does_nothing) {
	auto oldVec = vec;
	vec.deleteColumns(oldVec.cols() + 10, 2);
	EXPECT_EQ(vec, oldVec);
}

TEST_F(Vector2DTest, deleteColumns_zero_count_does_nothing) {
	auto oldVec = vec;
	vec.deleteColumns(5, 0);
	EXPECT_EQ(vec, oldVec);
}

TEST_F(Vector2DTest, deleteColumns_all_columns_preserves_rows) {
	vec.deleteColumns(0, vec.cols());
	EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT);
	EXPECT_EQ(vec.cols(), 0);
	EXPECT_EQ(vec.size(), 0);
}

// ==================== ADD COLUMN TESTS ====================

TEST_F(Vector2DTest, addColumn_appends_column_to_empty) {
	PIVector2D<int> empty;
	PIVector<int> newCol(5);
	for (size_t i = 0; i < 5; ++i)
		newCol[i] = static_cast<int>(100 + i);

	empty.addColumn(newCol);

	EXPECT_EQ(empty.rows(), 5);
	EXPECT_EQ(empty.cols(), 1);
	for (size_t r = 0; r < 5; ++r) {
		EXPECT_EQ(empty.element(r, 0), static_cast<int>(100 + r));
	}
}

TEST_F(Vector2DTest, addColumn_appends_column_to_existing) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();

	PIVector<int> newCol(oldRows, [](size_t i){return -900 - (int)i;});
	vec.addColumn(newCol);

	EXPECT_EQ(vec.rows(), oldRows);
	EXPECT_EQ(vec.cols(), oldCols + 1);

	// Check that old data is preserved
	for (size_t r = 0; r < oldRows; ++r) {
		for (size_t c = 0; c < oldCols; ++c) {
			EXPECT_EQ(vec.element(r, c), static_cast<int>(r * COLS_COUNT_INIT + c));
		}
	}

	// Check new column
	for (size_t r = 0; r < oldRows; ++r) {
		EXPECT_EQ(vec.element(r, oldCols), -900 - (int)r);
	}
}

TEST_F(Vector2DTest, addColumn_with_shorter_vector_uses_min) {
	size_t oldRows  = vec.rows();
	size_t oldCols  = vec.cols();
	size_t shortLen = oldRows - 10;
	PIVector<int> shortCol(shortLen, 777);

	vec.addColumn(shortCol);

	EXPECT_EQ(vec.cols(), oldCols + 1);

	// First shortLen rows should be 777
	for (size_t r = 0; r < shortLen; ++r) {
		EXPECT_EQ(vec.element(r, oldCols), 777);
	}
	// Remaining rows should be default-initialized (0)
	for (size_t r = shortLen; r < oldRows; ++r) {
		EXPECT_EQ(vec.element(r, oldCols), 0);
	}
}

TEST_F(Vector2DTest, addColumn_with_longer_vector_truncates) {
	size_t oldRows = vec.rows();
	size_t oldCols = vec.cols();
	size_t longLen = oldRows + 10;
	PIVector<int> longCol(longLen, 555);

	vec.addColumn(longCol);

	EXPECT_EQ(vec.cols(), oldCols + 1);

	// All rows should be 555 (only first oldRows elements are used)
	for (size_t r = 0; r < oldRows; ++r) {
		EXPECT_EQ(vec.element(r, oldCols), 555);
	}
}

TEST_F(Vector2DTest, addColumn_with_empty_source_does_nothing_on_empty) {
	PIVector2D<int> empty;
	PIVector<int> emptyCol;

	empty.addColumn(emptyCol);
	EXPECT_TRUE(empty.isEmpty());
	EXPECT_EQ(empty.rows(), 0);
	EXPECT_EQ(empty.cols(), 0);
}

TEST_F(Vector2DTest, addColumn_with_empty_source_adds_default_column) {
	auto oldVec = vec;

	vec.addColumn({});

	EXPECT_EQ(vec.cols(), oldVec.cols());
	EXPECT_EQ(vec.rows(), oldVec.rows());

	for (size_t r = 0; r < oldVec.rows(); ++r) {
		for (size_t c = 0; c < oldVec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), oldVec.element(r, c));
		}
	}
}

TEST_F(Vector2DTest, addColumn_with_Col_proxy_works) {
	auto oldVec           = vec;
	const size_t colIndex = 5;

	auto srcCol           = oldVec.col(colIndex);
	vec.addColumn(srcCol);

	EXPECT_EQ(vec.cols(), oldVec.cols() + 1);
	EXPECT_EQ(vec.rows(), oldVec.rows());

	for (size_t r = 0; r < oldVec.rows(); ++r) {
		for (size_t c = 0; c < oldVec.cols(); ++c) {
			EXPECT_EQ(vec.element(r, c), oldVec.element(r, c));
		}
	}
	for (size_t r = 0; r < vec.rows(); ++r) {
		// EXPECT_EQ(vec.element(r, oldVec.cols()), int());
		EXPECT_EQ(vec.element(r, oldVec.cols()), oldVec.element(r, colIndex));
	}
}

TEST_F(Vector2DTest, addColumn_with_ColConst_proxy_works) {
	size_t oldRows             = vec.rows();
	size_t oldCols             = vec.cols();

	const auto & constVec      = vec;
	auto srcCol                = constVec.col(7);
	// Need a non-const array to add to
	PIVector2D<int> mutableVec = vec; // copy
	mutableVec.addColumn(srcCol);

	EXPECT_EQ(mutableVec.cols(), oldCols + 1);

	for (size_t r = 0; r < oldRows; ++r) {
		EXPECT_EQ(mutableVec.element(r, oldCols), vec.element(r, 7));
	}
}

// ==================== 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<int> 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));
}

TEST_F(Vector2DTest, contains_vector_of_elements_works) {
	PIVector<int> searchFor;
	searchFor << 100 << 200 << 300;
	EXPECT_TRUE(vec.asPlainVector().containsAll(searchFor));

	searchFor << -999;
	EXPECT_FALSE(vec.asPlainVector().containsAll(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);
}


// ==================== 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<int>(i * 2); });
	for (size_t i = 0; i < vec.size(); ++i) {
		EXPECT_EQ(vec.asPlainVector()[i], static_cast<int>(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_dimensions_resets_and_fills) {
	vec.assign(5, 7, 123);
	EXPECT_EQ(vec.rows(), 5);
	EXPECT_EQ(vec.cols(), 7);
	for (size_t r = 0; r < 5; ++r) {
		for (size_t c = 0; c < 7; ++c) {
			EXPECT_EQ(vec.element(r, c), 123);
		}
	}
}

// ==================== COMPARISON TESTS ====================
TEST_F(Vector2DTest, equality_operator_works) {
	PIVector2D<int> same = vec;
	EXPECT_EQ(vec, same);

	PIVector2D<int> differentRows(ROWS_COUNT_INIT + 1, COLS_COUNT_INIT);
	EXPECT_NE(vec, differentRows);

	PIVector2D<int> differentCols(ROWS_COUNT_INIT, COLS_COUNT_INIT + 1);
	EXPECT_NE(vec, differentCols);

	PIVector2D<int> 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<int> other(5, 5, 42);

	size_t oldRows        = vec.rows();
	size_t oldCols        = vec.cols();
	PIVector<int> 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(Vector2DTransposeTest, emptyMatrix_returnsEmpty) {
	PIVector2D<int> empty;
	auto transposed = empty.transposed();
	EXPECT_TRUE(transposed.isEmpty());
	EXPECT_EQ(transposed.rows(), 0);
	EXPECT_EQ(transposed.cols(), 0);
}

TEST(Vector2DTransposeTest, singleElement_returnsSame) {
	PIVector2D<int> single(1, 1, 42);
	auto transposed = single.transposed();
	EXPECT_EQ(transposed.rows(), 1);
	EXPECT_EQ(transposed.cols(), 1);
	EXPECT_EQ(transposed.element(0, 0), 42);
}

TEST(Vector2DTransposeTest, oneRow_becomesOneColumn) {
	PIVector2D<int> rowVec(1, 5);
	for (size_t c = 0; c < 5; ++c)
		rowVec.element(0, c) = static_cast<int>(c);
	auto transposed = rowVec.transposed();
	EXPECT_EQ(transposed.rows(), 5);
	EXPECT_EQ(transposed.cols(), 1);
	for (size_t r = 0; r < 5; ++r) {
		EXPECT_EQ(transposed.element(r, 0), static_cast<int>(r));
	}
}

TEST(Vector2DTransposeTest, oneColumn_becomesOneRow) {
	PIVector2D<int> colVec(5, 1);
	for (size_t r = 0; r < 5; ++r)
		colVec.element(r, 0) = static_cast<int>(r);
	auto transposed = colVec.transposed();
	EXPECT_EQ(transposed.rows(), 1);
	EXPECT_EQ(transposed.cols(), 5);
	for (size_t c = 0; c < 5; ++c) {
		EXPECT_EQ(transposed.element(0, c), static_cast<int>(c));
	}
}

TEST_F(Vector2DTest, transposed_doesNotModifyOriginal) {
	auto original   = vec; // копия для сравнения
	auto transposed = vec.transposed();
	// Проверяем, что исходный массив не изменился
	EXPECT_EQ(vec, original);
}

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<int>([](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<PIString>([](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<int>([](size_t r, size_t c, const int & e) { return static_cast<int>(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<int>(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, indexOf_returns_correct_pair) {
	auto p = vec.indexOf(vec.element(10, 15));
	EXPECT_EQ(p.row, 10);
	EXPECT_EQ(p.col, 15);
	p = vec.indexOf(-999);
	EXPECT_EQ(p.row, -1);
	EXPECT_EQ(p.col, -1);
}

TEST_F(Vector2DTest, indexWhere_returns_correct_pair) {
	vec.element(5, 5) = -42;
	auto isTarget     = [](const int & e) { return e == -42; };
	auto p            = vec.indexWhere(isTarget);
	EXPECT_EQ(p.row, 5);
	EXPECT_EQ(p.col, 5);
}

TEST_F(Vector2DTest, lastIndexOf_works) {
	int val             = vec.element(10, 10);
	vec.element(20, 20) = val; // duplicate
	auto p              = vec.lastIndexOf(val);
	EXPECT_EQ(p.row, 20);
	EXPECT_EQ(p.col, 20);
}

TEST_F(Vector2DTest, lastIndexWhere_works) {
	auto isLarge = [](const int & e) { return e > 500; };
	auto p       = vec.lastIndexWhere(isLarge);
	EXPECT_GE(p.row, 0);
	EXPECT_GE(p.col, 0);
	// The last element with value >500 should be the largest index
	size_t lastFlat         = p.row * vec.cols() + p.col;
	size_t expectedLastFlat = vec.size() - 1;
	EXPECT_EQ(lastFlat, expectedLastFlat);
}

TEST_F(Vector2DTest, reduce_accumulates_correctly) {
	int sum      = vec.reduce<int>([](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<int>([](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<int>([](size_t r, size_t c, const int & e, const int & acc) { return acc + static_cast<int>(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<int>::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<int>(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<int>::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<int>::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<int>::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<int>::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<int>::ColConst &) { return false; });
	EXPECT_TRUE(noCols.isEmpty());
}

// ==================== EDGE CASE TESTS ====================
TEST(Vector2DEdgeTest, empty_vector_operations) {
	PIVector2D<int> 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<int>::RowConst &) { return true; });
	EXPECT_TRUE(filtered.isEmpty());
}

TEST(Vector2DEdgeTest, single_element_vector) {
	PIVector2D<int> 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);
}

// ==================== 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<int>(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<int>(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<int>(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<int>(vec.size()); };
	EXPECT_TRUE(col.every(isLessThanMax));
	auto isNotEven = [](const int & e) { return e % 2 != 0; };
	EXPECT_FALSE(col.every(isNotEven));
}

// ==================== 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

// ==================== INDEX STRUCTURE ACCESS TESTS ====================
TEST_F(Vector2DTest, operator_bracket_with_index_allows_read_access) {
	PIVector2D<int>::Index idx = {5, 7};
	int value                  = vec[idx];
	int expected               = vec.element(5, 7);
	EXPECT_EQ(value, expected);
}

TEST_F(Vector2DTest, operator_bracket_with_index_allows_write_access) {
	PIVector2D<int>::Index idx = {10, 15};
	vec[idx]                   = 999;
	EXPECT_EQ(vec.element(10, 15), 999);
}

TEST_F(Vector2DTest, operator_bracket_with_const_index_works) {
	const auto & constVec      = vec;
	PIVector2D<int>::Index idx = {3, 12};
	int value                  = constVec[idx];
	int expected               = vec.element(3, 12);
	EXPECT_EQ(value, expected);
}

TEST_F(Vector2DTest, element_function_with_index_works) {
	PIVector2D<int>::Index idx = {8, 20};
	int value                  = vec.element(idx);
	int expected               = vec.element(8, 20);
	EXPECT_EQ(value, expected);
}

TEST_F(Vector2DTest, element_function_with_index_modifies_value) {
	PIVector2D<int>::Index idx = {12, 8};
	vec.element(idx)           = 555;
	EXPECT_EQ(vec.element(12, 8), 555);
}

TEST_F(Vector2DTest, element_function_with_const_index_works) {
	const auto & constVec      = vec;
	PIVector2D<int>::Index idx = {7, 5};
	int value                  = constVec.element(idx);
	int expected               = vec.element(7, 5);
	EXPECT_EQ(value, expected);
}

TEST_F(Vector2DTest, at_function_with_index_works) {
	PIVector2D<int>::Index idx = {4, 11};
	int value                  = vec.at(idx);
	int expected               = vec.at(4, 11);
	EXPECT_EQ(value, expected);
}

TEST_F(Vector2DTest, index_structure_with_brace_initialization_works) {
	vec[{0, 0}] = 100;
	EXPECT_EQ(vec.element(0, 0), 100);

	vec[{static_cast<ssize_t>(ROWS_COUNT_INIT - 1), static_cast<ssize_t>(COLS_COUNT_INIT - 1)}] = 200;
	EXPECT_EQ(vec.element(ROWS_COUNT_INIT - 1, COLS_COUNT_INIT - 1), 200);
}

TEST(Vector2DIndexTest, default_constructor_initializes_to_invalid) {
	PIVector2D<int>::Index idx;
	EXPECT_TRUE(idx.isNotValid());
	EXPECT_FALSE(idx.isValid());
	EXPECT_EQ(idx.row, -1);
	EXPECT_EQ(idx.col, -1);
}

TEST(Vector2DIndexTest, parameterized_constructor_initializes_correctly) {
	PIVector2D<int>::Index idx(5, 10);
	EXPECT_TRUE(idx.isValid());
	EXPECT_FALSE(idx.isNotValid());
	EXPECT_EQ(idx.row, 5);
	EXPECT_EQ(idx.col, 10);
}

TEST(Vector2DIndexTest, isValid_returns_true_for_non_negative_values) {
	PIVector2D<int>::Index idx(0, 0);
	EXPECT_TRUE(idx.isValid());
	EXPECT_FALSE(idx.isNotValid());
}

TEST(Vector2DIndexTest, isNotValid_returns_true_for_negative_values) {
	PIVector2D<int>::Index idx1;
	EXPECT_TRUE(idx1.isNotValid());

	PIVector2D<int>::Index idx2(-1, 5);
	EXPECT_TRUE(idx2.isNotValid());

	PIVector2D<int>::Index idx3(5, -1);
	EXPECT_TRUE(idx3.isNotValid());
}
