diff --git a/libs/main/containers/pivector2d.h b/libs/main/containers/pivector2d.h index 8f208ef3..7bd8a990 100644 --- a/libs/main/containers/pivector2d.h +++ b/libs/main/containers/pivector2d.h @@ -25,6 +25,7 @@ #ifndef PIVECTOR2D_H #define PIVECTOR2D_H +#include "pipair.h" #include "pivector.h" //! \addtogroup Containers @@ -77,12 +78,6 @@ public: //! \param rows Number of rows. //! \param cols Number of columns. //! \param v The source 1D vector. Its size must be at least `rows * cols`. - //! \~english \param rows Количество строк. - //! \~russian \param rows Количество строк. - //! \~english \param cols Количество столбцов. - //! \~russian \param cols Количество столбцов. - //! \~english \param v Исходный одномерный вектор. Его размер должен быть не меньше `rows * cols`. - //! \~russian \param v Исходный одномерный вектор. Его размер должен быть не меньше `rows * cols`. inline PIVector2D(size_t rows, size_t cols, const PIVector & v): rows_(rows), cols_(cols), mat(v) { mat.resize(rows * cols); } //! \~english Move constructs a 2D array from an existing 1D vector, reshaping it. @@ -90,19 +85,11 @@ public: //! \param rows Number of rows. //! \param cols Number of columns. //! \param v The source 1D vector (rvalue reference). Its size must be at least `rows * cols`. - //! \~english \param rows Количество строк. - //! \~russian \param rows Количество строк. - //! \~english \param cols Количество столбцов. - //! \~russian \param cols Количество столбцов. - //! \~english \param v Исходный одномерный вектор (rvalue-ссылка). Его размер должен быть не меньше `rows * cols`. - //! \~russian \param v Исходный одномерный вектор (rvalue-ссылка). Его размер должен быть не меньше `rows * cols`. inline PIVector2D(size_t rows, size_t cols, PIVector && v): rows_(rows), cols_(cols), mat(std::move(v)) { mat.resize(rows * cols); } //! \~english Constructs a 2D array from a vector of vectors (jagged array). Assumes all inner vectors have the same size. //! \~russian Создает двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют //! одинаковый размер. \param v The source vector of vectors. - //! \~english \param v Исходный вектор векторов. - //! \~russian \param v Исходный вектор векторов. inline PIVector2D(const PIVector> & v) { rows_ = v.size(); if (rows_) { @@ -205,6 +192,36 @@ public: //! \~english Converts the row to a \a PIVector. //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } + + // --- Поиск в строке --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[st_ + i] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[st_ + i] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[st_ + i])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[st_ + i])) return i; + } + return -1; + } }; //! \class Col @@ -272,6 +289,36 @@ public: ret << (*p_)[i * step_ + col_]; return ret; } + + // --- Поиск в столбце --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[i * step_ + col_] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[i * step_ + col_] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[i * step_ + col_])) return i; + } + return -1; + } }; //! \class RowConst @@ -305,6 +352,36 @@ public: //! \~english Converts the row to a \a PIVector. //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } + + // --- Поиск в строке (только чтение) --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[st_ + i] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[st_ + i] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[st_ + i])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[st_ + i])) return i; + } + return -1; + } }; //! \class ColConst @@ -345,6 +422,36 @@ public: ret << (*p_)[i * step_ + col_]; return ret; } + + // --- Поиск в столбце (только чтение) --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[i * step_ + col_] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[i * step_ + col_] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[i * step_ + col_])) return i; + } + return -1; + } }; //! \~english Returns a reference to the element at the given row and column. @@ -549,22 +656,39 @@ public: inline int entries(std::function test, ssize_t start = 0) const { return mat.entries(test, start); } - // TODO: next 4 functions implement in to RowConst and ColConst and rewtite thisto return index as PIPair - //! \~english Returns the first index of `e` in the flat vector. - //! \~russian Возвращает первый индекс `e` в плоском векторе. - inline ssize_t indexOf(const T & e, ssize_t start = 0) const { return mat.indexOf(e, start); } - //! \~english Returns the first index in the flat vector that passes the `test`. - //! \~russian Возвращает первый индекс в плоском векторе, проходящий `test`. - inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { return mat.indexWhere(test, start); } - //! \~english Returns the last index of `e` in the flat vector. - //! \~russian Возвращает последний индекс `e` в плоском векторе. - inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { return mat.lastIndexOf(e, start); } - //! \~english Returns the last index in the flat vector that passes the `test`. - //! \~russian Возвращает последний индекс в плоском векторе, проходящий `test`. - inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { - return mat.lastIndexWhere(test, start); + //! \~english Returns the first index (row, col) of `e` in the 2D array. + //! \~russian Возвращает первый индекс (строка, столбец) элемента `e` в двумерном массиве. + inline PIPair indexOf(const T & e, ssize_t start = 0) const { + ssize_t flat = mat.indexOf(e, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); } + //! \~english Returns the first index (row, col) in the 2D array that passes the `test`. + //! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`. + inline PIPair indexWhere(std::function test, ssize_t start = 0) const { + ssize_t flat = mat.indexWhere(test, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); + } + + //! \~english Returns the last index (row, col) of `e` in the 2D array. + //! \~russian Возвращает последний индекс (строка, столбец) элемента `e` в двумерном массиве. + inline PIPair lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t flat = mat.lastIndexOf(e, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); + } + + //! \~english Returns the last index (row, col) in the 2D array that passes the `test`. + //! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`. + inline PIPair lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t flat = mat.lastIndexWhere(test, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); + } + + //! \~english Tests if any element in the flat vector passes the `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`. inline bool any(std::function test) const { return mat.any(test); } @@ -591,22 +715,17 @@ public: //! \~russian То же, что и \a fill(). inline PIVector2D & assign(const T & e = T()) { return fill(e); } - - // TODO: rewrite with size_t rows, size_t cols arguments - //! \~english Assigns a new size to the underlying flat vector and resets the 2D structure to 1 row. - //! \~russian Присваивает новый размер внутреннему плоскому вектору и сбрасывает 2D-структуру до 1 строки. - inline PIVector2D & assign(size_t new_size, const T & f) { - mat.assign(new_size, f); - if (mat.isEmpty()) { - rows_ = cols_ = 0; - } else { - rows_ = 1; - cols_ = mat.size(); - } + //! \~english Assigns new size and fills with value. + //! \~russian Задаёт новый размер и заполняет значением. + inline PIVector2D & assign(size_t rows, size_t cols, const T & f = T()) { + mat.assign(rows * cols, f); + rows_ = rows; + cols_ = cols; return *this; } - // TODO: fix for different rows and cols count + + // TODO: исправить - при транспонировании количество строк становится количеством столбцов и наоборот //! \~english Returns a transposed 2D array (rows become columns and vice versa). //! \~russian Возвращает транспонированный двумерный массив (строки становятся столбцами и наоборот). inline PIVector2D transposed() const { @@ -620,17 +739,16 @@ public: return result; } - // TODO: переписать по возможности избегая копирования данных, в идеале использовать piSwap //! \~english Reverses the order of rows in place. //! \~russian Изменяет порядок строк на обратный на месте. inline PIVector2D & reverseRows() { const size_t half = rows_ / 2; for (size_t i = 0; i < half; ++i) { - Row r1 = row(i); - Row r2 = row(rows_ - 1 - i); - PIVector temp = r1.toVector(); - r1 = r2.toVector(); - r2 = temp; + T * row1 = data(i * cols_); + T * row2 = data((rows_ - 1 - i) * cols_); + for (size_t j = 0; j < cols_; ++j) { + piSwap(row1[j], row2[j]); + } } return *this; } @@ -685,42 +803,33 @@ public: return PIVector2D(rows_, cols_, std::move(mappedMat)); } - - /* - //! \~english Executes a read-only function for each element. - //! \~russian Выполняет функцию только для чтения для каждого элемента. - inline void forEach(std::function f) const { mat.forEach(f); } - - //! \~english Executes a function for each element, allowing modification. - //! \~russian Выполняет функцию для каждого элемента, позволяя их изменять. - inline PIVector2D & forEach(std::function f) { - mat.forEach(f); - return *this; + // --- Итерация по строкам и столбцам --- + //! \~english Applies a function to each row (modifiable). + //! \~russian Применяет функцию к каждой строке (с возможностью изменения). + inline PIVector2D & forEachRow(std::function f) { + for (size_t r = 0; r < rows_; ++r) + f(row(r)); + return *this; } - - //! \~english Executes a read-only function (with row and col indices) for each element. - //! \~russian Выполняет функцию только для чтения (с индексами строки и столбца) для каждого элемента. - inline void forEachIndexed(std::function f) const { - for (size_t r = 0; r < rows_; ++r) { - for (size_t c = 0; c < cols_; ++c) { - f(r, c, element(r, c)); - } - } + //! \~english Applies a function to each row (read-only). + //! \~russian Применяет функцию к каждой строке (только чтение). + inline void forEachRow(std::function f) const { + for (size_t r = 0; r < rows_; ++r) + f(row(r)); } - - //! \~english Executes a function (with row and col indices) for each element, allowing modification. - //! \~russian Выполняет функцию (с индексами строки и столбца) для каждого элемента, позволяя их изменять. - inline PIVector2D & forEachIndexed(std::function f) { - for (size_t r = 0; r < rows_; ++r) { - for (size_t c = 0; c < cols_; ++c) { - f(r, c, element(r, c)); - } - } - return *this; + //! \~english Applies a function to each column (modifiable). + //! \~russian Применяет функцию к каждому столбцу (с возможностью изменения). + inline PIVector2D & forEachColumn(std::function f) { + for (size_t c = 0; c < cols_; ++c) + f(col(c)); + return *this; + } + //! \~english Applies a function to each column (read-only). + //! \~russian Применяет функцию к каждому столбцу (только чтение). + inline void forEachColumn(std::function f) const { + for (size_t c = 0; c < cols_; ++c) + f(col(c)); } - */ - // TODO: Переделать закомментаренные выше функции на функции forEachRow, forEachColumn - //! \~english Accumulates a value across all elements. //! \~russian Аккумулирует значение по всем элементам. diff --git a/tests/math/testpivector2d.cpp b/tests/math/testpivector2d.cpp index 357c2383..f2a47361 100644 --- a/tests/math/testpivector2d.cpp +++ b/tests/math/testpivector2d.cpp @@ -411,40 +411,6 @@ TEST_F(Vector2DTest, entries_with_predicate_counts_matches) { 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) { @@ -495,11 +461,13 @@ TEST_F(Vector2DTest, assign_is_alias_for_fill) { } 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); + vec.assign(3, 4, 77); + EXPECT_EQ(vec.rows(), 3); + EXPECT_EQ(vec.cols(), 4); + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), 77); + } } }