pivector2d fix add\remove rows\cols and resize

This commit is contained in:
2026-02-27 18:40:09 +03:00
parent 97691e888c
commit 0b7d565b93
2 changed files with 342 additions and 103 deletions

View File

@@ -777,66 +777,57 @@ public:
//! \~russian Добавляет новую строку в конец массива из \a PIVector. //! \~russian Добавляет новую строку в конец массива из \a PIVector.
inline PIVector2D<T> & addRow(const PIVector<T> & other) { inline PIVector2D<T> & addRow(const PIVector<T> & other) {
if (cols_ == 0) cols_ = other.size(); if (cols_ == 0) cols_ = other.size();
const size_t sz = piMin<size_t>(cols_, other.size()); mat.append(other);
const size_t ps = mat.size();
mat.resize(mat.size() + cols_);
mat._copyRaw(mat.data(ps), other.data(), sz);
rows_++; rows_++;
mat.resize(rows_ * cols_);
return *this; return *this;
} }
// TODO: fix - memove existing elements inline PIVector2D<T> & appendRows(size_t count, const T & f = T()) {
inline PIVector2D<T> & addEmptyRows(size_t count, const T & f = T()) { if (count == 0) return *this;
if (count == 0 || cols_ == 0) return *this; if (cols_ == 0) ++cols_;
mat.resize(mat.size() + count * cols_, f); mat.resize(mat.size() + count * cols_, f);
rows_ += count; rows_ += count;
return *this; return *this;
} }
// TODO: fix - memove existing elements inline PIVector2D<T> & appendColumns(size_t count, const T & f = T()) {
inline PIVector2D<T> & addEmptyColumns(size_t count, const T & f = T()) { if (count == 0) return *this;
if (count == 0 || rows_ == 0) return *this; if (rows_ == 0) {
const size_t newCols = cols_ + count; mat.resize(count, f);
const size_t newSize = rows_ * newCols; rows_ = 1;
mat.resize(newSize); cols_ = count;
for (size_t r = rows_ - 1; r < rows_; --r) { return *this;
for (size_t c = newCols - 1; c >= cols_; --c) {
element(r, c) = f;
}
for (size_t c = cols_; c > 0; --c) {
element(r, c - 1 + count) = element(r, c - 1);
}
} }
const size_t newCols = cols_ + count;
mat.reserve(rows_ * newCols);
for (size_t r = rows_; r > 0; --r) {
mat.insert(r * cols_, f, count);
}
cols_ = newCols; cols_ = newCols;
return *this; return *this;
} }
inline PIVector2D<T> & deleteRows(size_t row, size_t count) { inline PIVector2D<T> & deleteRows(size_t row_start, size_t count) {
if (row >= rows_) return *this; if (row_start >= rows_ || count == 0) return *this;
mat.remove(row * cols_, cols_ * count); mat.remove(row_start * cols_, cols_ * count);
rows_ -= count; if (isEmpty()) {
if (mat.isEmpty()) {
cols_ = 0; cols_ = 0;
rows_ = 0; rows_ = 0;
} else {
rows_ -= count;
} }
return *this; return *this;
} }
inline PIVector2D<T> & deleteColumns(size_t col, size_t count) { inline PIVector2D<T> & deleteColumns(size_t col_start, size_t count) {
if (col >= cols_ || rows_ == 0 || count == 0) return *this; if (col_start >= cols_ || rows_ == 0) return *this;
count = piMin(count, cols_ - col); count = piMin(count, cols_ - col_start);
if (count == 0) return *this;
for (size_t r = 0; r < rows_; ++r) { for (size_t r = 0; r < rows_; ++r) {
T * dst = mat.data(r * (cols_ - count)); mat.remove(r * (cols_ - count) + col_start, count);
T * src = mat.data(r * cols_);
if (col > 0) {
memmove(dst, src, col * sizeof(T));
dst += col;
src += col;
}
size_t remaining = (cols_ - count) - col;
if (remaining > 0) {
memmove(dst, src + 1, remaining * sizeof(T));
}
} }
cols_ -= count; cols_ -= count;
mat.resize(rows_ * cols_); mat.resize(rows_ * cols_);
@@ -847,32 +838,25 @@ public:
//! \~russian Добавляет новую строку в конец массива из \a ColConst. //! \~russian Добавляет новую строку в конец массива из \a ColConst.
inline PIVector2D<T> & addColumn(const ColConst & other) { inline PIVector2D<T> & addColumn(const ColConst & other) {
if (other.size() == 0) return *this; if (other.size() == 0) return *this;
if (size() == 0) { if (isEmpty()) {
_resizeRaw(other.size(), 1); mat.reserve(other.size());
for (size_t r = 0; r < other.size(); ++r) { for (size_t r = 0; r < other.size(); ++r) {
element(r, 0) = other[r]; mat.append(other[r]);
} }
rows_ = mat.size();
cols_ = 1;
return *this; return *this;
} }
const size_t oldCols = cols_; const size_t newCols = cols_ + 1;
const size_t newCols = oldCols + 1; mat.reserve(rows_ * newCols);
const size_t newSize = rows_ * newCols; for (size_t r = rows_; r > 0; --r) {
if (r - 1 < other.size()) {
mat._resizeRaw(newSize); mat.insert(r * cols_, other[r - 1]);
for (size_t r = rows_ - 1; r > 0; --r) {
T * src = mat.data(r * oldCols);
T * dst = mat.data(r * newCols);
memmove(dst, src, oldCols * sizeof(T));
if (r < other.size()) {
mat._copyRaw(&(dst[oldCols]), &(other[r]), 1);
} else { } else {
const T tmp = T(); mat.insert(r * cols_);
mat._copyRaw(&(dst[oldCols]), &tmp, 1);
} }
} }
mat._copyRaw(mat.data(oldCols), &(other[0]), 1);
cols_ = newCols; cols_ = newCols;
return *this; return *this;
@@ -882,32 +866,22 @@ public:
//! \~russian Добавляет новую строку в конец массива из \a PIVector. //! \~russian Добавляет новую строку в конец массива из \a PIVector.
inline PIVector2D<T> & addColumn(const PIVector<T> & other) { inline PIVector2D<T> & addColumn(const PIVector<T> & other) {
if (other.size() == 0) return *this; if (other.size() == 0) return *this;
if (size() == 0) { if (isEmpty()) {
_resizeRaw(other.size(), 1); mat.append(other);
for (size_t r = 0; r < other.size(); ++r) { rows_ = mat.size();
element(r, 0) = other[r]; cols_ = 1;
}
return *this; return *this;
} }
const size_t oldCols = cols_; const size_t newCols = cols_ + 1;
const size_t newCols = oldCols + 1; mat.reserve(rows_ * newCols);
const size_t newSize = rows_ * newCols; for (size_t r = rows_; r > 0; --r) {
if (r - 1 < other.size()) {
mat._resizeRaw(newSize); mat.insert(r * cols_, other[r - 1]);
for (size_t r = rows_ - 1; r > 0; --r) {
T * src = mat.data(r * oldCols);
T * dst = mat.data(r * newCols);
memmove(dst, src, oldCols * sizeof(T));
if (r < other.size()) {
mat._copyRaw(&(dst[oldCols]), &(other[r]), 1);
} else { } else {
const T tmp = T(); mat.insert(r * cols_);
mat._copyRaw(&(dst[oldCols]), &tmp, 1);
} }
} }
mat._copyRaw(mat.data(oldCols), &(other[0]), 1);
cols_ = newCols; cols_ = newCols;
return *this; return *this;
@@ -938,7 +912,19 @@ public:
rows_ = rows; rows_ = rows;
return *this; return *this;
} }
return __resize(rows, cols, f); if (cols > cols_) {
appendColumns(cols - cols_, f);
}
if (rows > rows_) {
appendRows(rows - rows_, f);
}
if (cols < cols_) {
deleteColumns(cols, cols_ - cols);
}
if (rows < rows_) {
deleteRows(rows, rows_ - rows);
}
return *this;
} }
//! \~english Equality operator. //! \~english Equality operator.
@@ -1261,9 +1247,7 @@ public:
//! \~english If the last row is removed and the array becomes empty, \a cols() is set to 0. //! \~english If the last row is removed and the array becomes empty, \a cols() is set to 0.
//! \~russian Если удаляется последняя строка и массив становится пустым, \a cols() устанавливается в 0. //! \~russian Если удаляется последняя строка и массив становится пустым, \a cols() устанавливается в 0.
//! \sa removeColumn(), PIVector::remove() //! \sa removeColumn(), PIVector::remove()
inline PIVector2D<T> & removeRow(size_t row) { inline PIVector2D<T> & removeRow(size_t row) { return deleteRows(row, 1); }
return deleteRows(row, 1);
}
//! \~english Removes a column from the 2D array. //! \~english Removes a column from the 2D array.
//! \~russian Удаляет столбец из двумерного массива. //! \~russian Удаляет столбец из двумерного массива.
@@ -1271,9 +1255,7 @@ public:
//! \~english This operation is more expensive than removing a row because elements must be moved. //! \~english This operation is more expensive than removing a row because elements must be moved.
//! \~russian Эта операция дороже, чем удаление строки, поскольку требуется перемещение элементов. //! \~russian Эта операция дороже, чем удаление строки, поскольку требуется перемещение элементов.
//! \sa removeRow(), PIVector::remove() //! \sa removeRow(), PIVector::remove()
inline PIVector2D<T> & removeColumn(size_t col) { inline PIVector2D<T> & removeColumn(size_t col) { return deleteColumns(col, 1); }
return deleteColumns(col, 1);
}
//! \~english Removes all rows that satisfy a condition. //! \~english Removes all rows that satisfy a condition.
//! \~russian Удаляет все строки, удовлетворяющие условию. //! \~russian Удаляет все строки, удовлетворяющие условию.
@@ -1343,22 +1325,6 @@ public:
} }
protected: protected:
inline PIVector2D<T> & __resize(size_t rows, size_t cols, const T & f) {
if (cols > cols_) {
addEmptyColumns(cols - cols_, f);
}
if (rows > rows_) {
addEmptyRows(rows - rows_, f);
}
if (cols < cols_) {
deleteColumns(cols, cols_ - cols);
}
if (rows < rows_) {
deleteRows(rows, rows_ - rows);
}
return *this;
}
size_t rows_, cols_; size_t rows_, cols_;
PIVector<T> mat; PIVector<T> mat;
}; };

View File

@@ -379,6 +379,280 @@ TEST_F(Vector2DTest, addRow_with_shorter_vector_uses_min) {
} }
} }
// ==================== 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 ==================== // ==================== ADD COLUMN TESTS ====================
TEST_F(Vector2DTest, addColumn_appends_column_to_empty) { TEST_F(Vector2DTest, addColumn_appends_column_to_empty) {
@@ -400,7 +674,7 @@ TEST_F(Vector2DTest, addColumn_appends_column_to_existing) {
size_t oldRows = vec.rows(); size_t oldRows = vec.rows();
size_t oldCols = vec.cols(); size_t oldCols = vec.cols();
PIVector<int> newCol(oldRows, 999); PIVector<int> newCol(oldRows, [](size_t i){return -900 - (int)i;});
vec.addColumn(newCol); vec.addColumn(newCol);
EXPECT_EQ(vec.rows(), oldRows); EXPECT_EQ(vec.rows(), oldRows);
@@ -415,7 +689,7 @@ TEST_F(Vector2DTest, addColumn_appends_column_to_existing) {
// Check new column // Check new column
for (size_t r = 0; r < oldRows; ++r) { for (size_t r = 0; r < oldRows; ++r) {
EXPECT_EQ(vec.element(r, oldCols), 999); EXPECT_EQ(vec.element(r, oldCols), -900 - (int)r);
} }
} }
@@ -497,7 +771,6 @@ TEST_F(Vector2DTest, addColumn_with_Col_proxy_works) {
} }
for (size_t r = 0; r < vec.rows(); ++r) { for (size_t r = 0; r < vec.rows(); ++r) {
// EXPECT_EQ(vec.element(r, oldVec.cols()), int()); // EXPECT_EQ(vec.element(r, oldVec.cols()), int());
piCout << r << vec.cols() << oldVec.cols() << colIndex;
EXPECT_EQ(vec.element(r, oldVec.cols()), oldVec.element(r, colIndex)); EXPECT_EQ(vec.element(r, oldVec.cols()), oldVec.element(r, colIndex));
} }
} }