/*! \file pivector2d.h * \brief 2D wrapper around PIVector * * This file declares PIVector2D */ /* PIP - Platform Independent Primitives 2D wrapper around PIVector Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef PIVECTOR2D_H #define PIVECTOR2D_H #include "pivector.h" //! \addtogroup Containers //! \{ //! \class PIVector2D //! \brief //! \~english 2D array container. //! \~russian Двумерный контейнер-массив. //! \details //! \~english //! This class is used to store a 2D array of elements of any type as a single continuous block of memory (a plain PIVector). //! Elements can be accessed using the `[][]` operators, where the first index is the row and the second is the column. //! Rows can be manipulated as \a PIVector objects, allowing modification of individual elements or assignment of entire rows. //! You cannot directly add or remove elements to change the dimensions of the array after construction //! (use \a resize(), \a addRow(), \a removeRow(), \a removeColumn() instead), but you can modify the values of existing elements. //! \~russian //! Этот класс используется для хранения двумерного массива элементов любого типа в виде единого непрерывного блока памяти (обычного //! \a PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со //! строками можно работать как с объектами \a PIVector, что позволяет изменять отдельные элементы или присваивать целые строки. Нельзя //! напрямую добавлять или удалять элементы, чтобы изменить размеры массива после создания (используйте \a resize(), \a addRow(), \a //! removeRow(), \a removeColumn() для этого), но можно изменять значения существующих элементов. template class PIVector2D { public: //! \brief //! \~english Index structure for 2D array elements (row, column). //! \~russian Структура индекса для элементов двумерного массива (строка, столбец). struct Index { ssize_t row = -1; ssize_t col = -1; inline Index() = default; inline Index(ssize_t r, ssize_t c): row(r), col(c) {} inline bool isValid() const { return row >= 0 && col >= 0; } inline bool isNotValid() const { return !isValid(); } }; //! \~english Constructs an empty 2D array. No memory is allocated. //! \~russian Создаёт пустой двумерный массив. Память не выделяется. //! \details //! \~english After this constructor, \a rows() and \a cols() return 0, and \a isEmpty() returns true. //! \~russian После этого конструктора \a rows() и \a cols() возвращают 0, а \a isEmpty() возвращает true. //! \sa PIVector::PIVector() inline PIVector2D() { rows_ = cols_ = 0; } //! \~english Constructs a 2D array with the given dimensions, filled with copies of `f`. //! \~russian Создаёт двумерный массив заданного размера, заполненный копиями `f`. //! \details //! \~english The underlying storage is a single contiguous block of memory of size `rows * cols`. //! All elements are initialized with the value `f`. //! \~russian Внутреннее хранилище представляет собой единый непрерывный блок памяти размером `rows * cols`. //! Все элементы инициализируются значением `f`. //! \sa PIVector::PIVector(size_t, const T&) inline PIVector2D(size_t rows, size_t cols, const T & f = T()) { rows_ = rows; cols_ = cols; mat.resize(rows * cols, f); } //! \~english Constructs a 2D array from an existing 1D vector, reshaping it. //! \~russian Создаёт двумерный массив из существующего одномерного вектора, изменяя его форму. //! \details //! \~english The constructor copies the data from `v` into the internal flat vector. //! If `v` is larger than `rows * cols`, the excess elements are ignored (the vector is truncated). //! If `v` is smaller, other values filled whith default cunstructor T() //! \~russian Конструктор копирует данные из `v` во внутренний плоский вектор. //! Если `v` больше, чем `rows * cols`, лишние элементы игнорируются (вектор обрезается). //! Если `v` меньше, остальные значения будут заполнены из конструктора по умолчанию T() //! \sa PIVector::PIVector(const PIVector&), reshape() 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. //! \~russian Конструктор перемещения из существующего одномерного вектора, изменяя его форму. //! \details //! \~english The data is moved from `v` into the internal flat vector, avoiding a copy. //! After construction, `v` is left in a valid but unspecified state. //! \~russian Данные перемещаются из `v` во внутренний плоский вектор, что позволяет избежать копирования. //! После завершения конструктора `v` остаётся в корректном, но неопределённом состоянии. //! \sa PIVector::PIVector(PIVector&&) 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 Создаёт двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют //! одинаковый размер. //! \details //! \~english If the input is empty, the constructed array is also empty. Otherwise, the number of columns is taken from the size of the //! first inner vector. All inner vectors are concatenated in the internal flat storage. //! \~russian Если входной массив пуст, создаётся пустой двумерный массив. В противном случае количество столбцов берётся из размера //! первого внутреннего вектора. Все внутренние векторы конкатенируются во внутреннем плоском хранилище. \sa PIVector::append() inline PIVector2D(const PIVector> & v) { rows_ = v.size(); if (rows_) { cols_ = v[0].size(); mat.reserve(rows_ * cols_); for (size_t i = 0; i < rows_; i++) { mat.append(v[i]); } mat.resize(rows_ * cols_); } if (mat.isEmpty()) rows_ = cols_ = 0; } //! \~english Returns the number of rows in the 2D array. //! \~russian Возвращает количество строк в двумерном массиве. //! \return Number of rows. //! \details //! \~english The result is always non-negative. If the array is empty, returns 0. //! \~russian Результат всегда неотрицательный. Если массив пуст, возвращает 0. //! \sa cols(), size(), PIVector::size() inline size_t rows() const { return rows_; } //! \~english Returns the number of columns in the 2D array. //! \~russian Возвращает количество столбцов в двумерном массиве. //! \return Number of columns. //! \details //! \~english The result is always non-negative. If the array is empty, returns 0. //! \~russian Результат всегда неотрицательный. Если массив пуст, возвращает 0. //! \sa rows(), size(), PIVector::size() inline size_t cols() const { return cols_; } //! \~english Returns the total number of elements (`rows * cols`). //! \~russian Возвращает общее количество элементов (`строки * столбцы`). //! \return Total number of elements. //! \details //! \~english This is equivalent to the size of the underlying flat vector. //! \~russian Это эквивалентно размеру внутреннего плоского вектора. //! \sa rows(), cols(), PIVector::size() inline size_t size() const { return mat.size(); } //! \~english Returns the total number of elements as a signed value. //! \~russian Возвращает общее количество элементов в виде знакового числа. //! \return Signed size. //! \sa size(), PIVector::size_s() inline ssize_t size_s() const { return mat.size_s(); } //! \~english Returns the total number of elements (same as \a size()). //! \~russian Возвращает общее количество элементов (то же, что и \a size()). //! \return Total number of elements. //! \sa size(), PIVector::length() inline size_t length() const { return mat.length(); } //! \~english Returns the number of elements that the underlying container has currently allocated space for. //! \~russian Возвращает количество элементов, для которого сейчас выделена память во внутреннем контейнере. //! \return Capacity of the flat vector. //! \details //! \~english This value may be larger than \a size(). It indicates how many elements can be added before a reallocation is needed. //! \~russian Это значение может быть больше, чем \a size(). Оно показывает, сколько элементов можно добавить до того, как потребуется //! перераспределение памяти. \sa reserve(), PIVector::capacity() inline size_t capacity() const { return mat.capacity(); } //! \~english Checks if the array has no elements. //! \~russian Проверяет, пуст ли массив. //! \return \c true if the array is empty, \c false otherwise. //! \details //! \~english An empty array has both rows and columns equal to 0. //! \~russian Пустой массив имеет и строки, и столбцы равные 0. //! \sa isNotEmpty(), PIVector::isEmpty() inline bool isEmpty() const { return mat.isEmpty(); } //! \~english Checks if the array has at least one element. //! \~russian Проверяет, не пуст ли массив. //! \return \c true if the array is not empty, \c false otherwise. //! \sa isEmpty(), PIVector::isNotEmpty() inline bool isNotEmpty() const { return mat.isNotEmpty(); } class RowConst; class ColConst; class Row; class Col; //! \class RowConst //! \brief //! \~english Proxy class representing a single read-only row in a \a PIVector2D. //! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D только для чтения. //! \details //! \~english Returned by const \a operator[] or \a row(). Provides const access to row elements. //! \~russian Возвращается константными версиями \a operator[] или \a row(). Предоставляет константный доступ к элементам строки. //! \sa Row, ColConst class RowConst { friend class PIVector2D; protected: inline RowConst(const PIVector2D * p, size_t row): p_(&(p->mat)), st_(p->cols_ * row), sz_(p->cols_) {} const PIVector * p_; const size_t st_, sz_; public: inline RowConst(const PIVector2D::Row & r): p_(r.p_), st_(r.st_), sz_(r.sz_) {} //! \~english Size of the row (number of columns). //! \~russian Размер строки (количество столбцов). inline size_t size() const { return sz_; } //! \~english Const access to the element at the given column index within the row. //! \~russian Константный доступ к элементу по заданному индексу столбца в строке. inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; } //! \~english Returns a const pointer to the row data starting at an optional offset. //! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения. inline const T * data(size_t index = 0) const { return p_->data(st_ + index); } //! \~english Converts the row to a \a PIVector. //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } //! \~english Returns the first index of element `e` in the row, starting from `start`. //! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`. //! \details //! \~english See \a PIVector::indexOf() for details on negative start handling. //! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf(). //! \sa PIVector::indexOf() 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; } //! \~english Returns the last index of element `e` in the row, searching backwards from `start`. //! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`. //! \return Index if found, -1 otherwise. //! \sa PIVector::lastIndexOf() 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; } //! \~english Returns the first index where the predicate `test` returns true, starting from `start`. //! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`. //! \sa PIVector::indexWhere() 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; } //! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`. //! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true, //! выполняя поиск в обратном направлении от `start`. //! \sa PIVector::lastIndexWhere() 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; } //! \~english Applies a function to each element of the row (read-only). //! \~russian Применяет функцию к каждому элементу строки (только чтение). //! \details //! \~english The function can't modify the elements. //! \~russian Функция не может изменять элементы. //! \sa forEach (modifiable) inline void forEach(std::function func) const { for (size_t i = 0; i < sz_; ++i) { func((*p_)[st_ + i]); } } //! \~english Checks if the row contains the element `e`. //! \~russian Проверяет, содержит ли строка элемент `e`. //! \sa PIVector::contains() inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } //! \~english Counts occurrences of `e` in the row. //! \~russian Подсчитывает количество вхождений `e` в строке. //! \sa PIVector::entries() inline int entries(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; int count = 0; for (size_t i = (size_t)start; i < sz_; ++i) { if ((*p_)[st_ + i] == e) ++count; } return count; } //! \~english Counts elements in the row that pass the `test`. //! \~russian Подсчитывает элементы в строке, проходящие `test`. //! \sa PIVector::entries(std::function) inline int entries(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; int count = 0; for (size_t i = (size_t)start; i < sz_; ++i) { if (test((*p_)[st_ + i])) ++count; } return count; } //! \~english Tests if any element in the row passes the `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в строке `test`. //! \sa PIVector::any() inline bool any(std::function test) const { for (size_t i = 0; i < sz_; ++i) { if (test((*p_)[st_ + i])) return true; } return false; } //! \~english Tests if all elements in the row pass the `test`. //! \~russian Проверяет, проходят ли все элементы в строке `test`. //! \sa PIVector::every() inline bool every(std::function test) const { for (size_t i = 0; i < sz_; ++i) { if (!test((*p_)[st_ + i])) return false; } return true; } }; //! \class ColConst //! \brief //! \~english Proxy class representing a single read-only column in a \a PIVector2D. //! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D только для чтения. //! \details //! \~english Returned by const \a col(). Provides const access to column elements. //! \~russian Возвращается константной версией \a col(). Предоставляет константный доступ к элементам столбца. //! \sa Col, RowConst class ColConst { friend class PIVector2D; protected: inline ColConst(const PIVector2D * p, size_t col): p_(&(p->mat)), step_(p->cols_), col_(col), sz_(p->rows_) {} const PIVector * p_; const size_t step_, col_, sz_; public: inline ColConst(const PIVector2D::Col & c): p_(c.p_), step_(c.step_), col_(c.col_), sz_(c.sz_) {} //! \~english Size of the column (number of rows). //! \~russian Размер столбца (количество строк). inline size_t size() const { return sz_; } //! \~english Const access to the element at the given row index within the column. //! \~russian Константный доступ к элементу по заданному индексу строки в столбце. inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; } //! \~english Returns a const pointer to the column data starting at an optional row offset. //! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам. inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); } //! \~english Converts the column to a \a PIVector. //! \~russian Преобразует столбец в \a PIVector. inline PIVector toVector() const { PIVector ret; ret.reserve(sz_); for (size_t i = 0; i < size(); i++) ret << (*p_)[i * step_ + col_]; return ret; } //! \~english Returns the first index of element `e` in the row, starting from `start`. //! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`. //! \details //! \~english See \a PIVector::indexOf() for details on negative start handling. //! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf(). //! \sa PIVector::indexOf() 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; } //! \~english Returns the last index of element `e` in the row, searching backwards from `start`. //! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`. //! \sa PIVector::lastIndexOf() 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; } //! \~english Returns the first index where the predicate `test` returns true, starting from `start`. //! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`. //! \sa PIVector::indexWhere() 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; } //! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`. //! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true, //! выполняя поиск в обратном направлении от `start`. //! \sa PIVector::lastIndexWhere() 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 Applies a function to each element of the column (read-only). //! \~russian Применяет функцию к каждому элементу столбца (только чтение). //! \details //! \~english The function can't modify the elements. //! \~russian Функция не может изменять элементы. //! \sa forEach (modifiable) inline void forEach(std::function func) const { for (size_t i = 0; i < sz_; ++i) { func((*p_)[i * step_ + col_]); } } //! \~english Checks if the column contains the element `e`. //! \~russian Проверяет, содержит ли столбец элемент `e`. //! \sa PIVector::contains() inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } //! \~english Counts occurrences of `e` in the column. //! \~russian Подсчитывает количество вхождений `e` в столбце. //! \sa PIVector::entries() inline int entries(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; int count = 0; for (size_t i = (size_t)start; i < sz_; ++i) { if ((*p_)[i * step_ + col_] == e) ++count; } return count; } //! \~english Counts elements in the column that pass the `test`. //! \~russian Подсчитывает элементы в столбце, проходящие `test`. //! \sa PIVector::entries(std::function) inline int entries(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; int count = 0; for (size_t i = (size_t)start; i < sz_; ++i) { if (test((*p_)[i * step_ + col_])) ++count; } return count; } //! \~english Tests if any element in the column passes the `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в столбце `test`. //! \sa PIVector::any() inline bool any(std::function test) const { for (size_t i = 0; i < sz_; ++i) { if (test((*p_)[i * step_ + col_])) return true; } return false; } //! \~english Tests if all elements in the column pass the `test`. //! \~russian Проверяет, проходят ли все элементы в столбце `test`. //! \sa PIVector::every() inline bool every(std::function test) const { for (size_t i = 0; i < sz_; ++i) { if (!test((*p_)[i * step_ + col_])) return false; } return true; } }; //! \class Row //! \brief //! \~english Proxy class representing a single row in a \a PIVector2D for modification. //! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D для модификации. //! \details //! \~english Objects of this class are returned by non-const \a operator[] or \a row(). //! They provide array-like access to the elements of a specific row and allow operations such as assignment from another row or a \a //! PIVector, searching, filling, and iteration. //! \~russian Объекты этого класса возвращаются неконстантными операторами \a operator[] или методом \a row(). //! Они предоставляют доступ к элементам конкретной строки, подобный массиву, и позволяют выполнять такие операции, как присваивание из //! другой строки или \a PIVector, поиск, заполнение и итерацию. \sa Col, RowConst class Row: public RowConst { friend class PIVector2D; private: inline Row(PIVector2D * p, size_t row): RowConst(p, row), p_(&(p->mat)) {} PIVector * p_; public: using RowConst::operator[]; using RowConst::data; using RowConst::size; //! \~english Accesses the element at the given column index within the row. //! \~russian Доступ к элементу по заданному индексу столбца в строке. //! \details //! \~english No bounds checking is performed; use with caution. //! \~russian Проверка границ не выполняется; используйте с осторожностью. //! \sa PIVector::operator[] inline T & operator[](size_t index) { return (*p_)[this->st_ + index]; } //! \~english Returns a pointer to the row data starting at an optional offset. //! \~russian Возвращает указатель на данные строки, начиная с опционального смещения. //! \details //! \~english The pointer can be used for direct memory operations. It remains valid as long as the underlying 2D array is not //! reallocated. //! \~russian Указатель можно использовать для прямых операций с памятью. Он остаётся действительным, пока не произойдёт //! перераспределение памяти внутреннего двумерного массива. \sa PIVector::data() inline T * data(size_t index = 0) { return p_->data(this->st_ + index); } //! \~english Assigns the contents of another Row to this row. //! \~russian Присваивает этой строке содержимое другой строки. //! \details //! \~english Only the minimum of the two row sizes is copied; if this row is shorter, excess elements in `other` are ignored. //! \~russian Копируется только минимум из размеров двух строк; если эта строка короче, лишние элементы из `other` игнорируются. //! \sa PIVector::operator= inline Row & operator=(const Row & other) { if (p_ == other.p_ && this->st_ == other.st_) return *this; const size_t sz = piMin(this->sz_, other.sz_); p_->_copyRaw(p_->data(this->st_), other.data(), sz); return *this; } //! \~english Assigns the contents of a \a PIVector to this row. //! \~russian Присваивает этой строке содержимое \a PIVector. //! \details //! \~english Only the minimum of the row size and vector size is copied. //! \~russian Копируется только минимум из размера строки и размера вектора. //! \sa PIVector::operator= inline Row & operator=(const PIVector & other) { const size_t sz = piMin(this->sz_, other.size()); p_->_copyRaw(p_->data(this->st_), other.data(), sz); return *this; } //! \~english Applies a function to each element of the row (modifiable). //! \~russian Применяет функцию к каждому элементу строки (с возможностью изменения). //! \param func Function that takes a reference to T. //! \details //! \~english The function can modify the elements. //! \~russian Функция может изменять элементы. //! \sa PIVector::forEach() inline void forEach(std::function func) { for (size_t i = 0; i < this->sz_; ++i) { func((*p_)[this->st_ + i]); } } //! \~english Fills the row with copies of `value`. //! \~russian Заполняет строку копиями `value`. //! \sa PIVector::fill() inline void fill(const T & value) { for (size_t i = 0; i < this->sz_; ++i) { (*p_)[this->st_ + i] = value; } } }; //! \class Col //! \brief //! \~english Proxy class representing a single column in a \a PIVector2D for modification. //! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D для модификации. //! \details //! \~english Objects of this class are returned by non-const \a col(). They provide column-wise access and operations similar to \a //! Row. //! \~russian Объекты этого класса возвращаются неконстантным методом \a col(). Они предоставляют доступ к столбцам и операции, //! аналогичные \a Row. \sa Row, ColConst class Col: public ColConst { friend class PIVector2D; private: inline Col(PIVector2D * p, size_t col): ColConst(p, col), p_(&(p->mat)) {} PIVector * p_; public: using ColConst::operator[]; using ColConst::data; using ColConst::size; //! \~english Accesses the element at the given row index within the column. //! \~russian Доступ к элементу по заданному индексу строки в столбце. //! \return Reference to the element. inline T & operator[](size_t index) { return (*p_)[index * this->step_ + this->col_]; } //! \~english Returns a pointer to the column data starting at an optional row offset. //! \~russian Возвращает указатель на данные столбца, начиная с опционального смещения по строкам. //! \details //! \~english Note that column elements are not stored contiguously in memory, so this pointer cannot be used to iterate over the //! whole column. //! \~russian Обратите внимание, что элементы столбца не хранятся в памяти непрерывно, поэтому этот указатель нельзя использовать //! для итерации по всему столбцу. inline T * data(size_t index = 0) { return p_->data(index * this->step_ + this->col_); } //! \~english Assigns the contents of another Col to this column. //! \~russian Присваивает этому столбцу содержимое другого столбца. inline Col & operator=(const Col & other) { if (p_ == other.p_ && this->col_ == other.col_) return *this; const size_t sz = piMin(this->sz_, other.sz_); for (size_t i = 0; i < sz; ++i) (*p_)[i * this->step_ + this->col_] = other[i]; return *this; } //! \~english Assigns the contents of a \a PIVector to this column. //! \~russian Присваивает этому столбцу содержимое \a PIVector. inline Col & operator=(const PIVector & other) { const size_t sz = piMin(this->sz_, other.size()); for (size_t i = 0; i < sz; ++i) (*p_)[i * this->step_ + this->col_] = other[i]; return *this; } //! \~english Applies a function to each element of the column (modifiable). //! \~russian Применяет функцию к каждому элементу столбца (с возможностью изменения). //! \details //! \~english The function can modify the elements. //! \~russian Функция может изменять элементы. //! \sa PIVector::forEach() inline void forEach(std::function func) { for (size_t i = 0; i < this->sz_; ++i) { func((*p_)[i * this->step_ + this->col_]); } } //! \~english Fills the column with copies of `value`. //! \~russian Заполняет столбец копиями `value`. //! \sa PIVector::fill() inline void fill(const T & value) { for (size_t i = 0; i < this->sz_; ++i) { (*p_)[i * this->step_ + this->col_] = value; } } }; //! \~english Returns a reference to the element at the given row and column. //! \~russian Возвращает ссылку на элемент по заданной строке и столбцу. //! \details //! \~english No bounds checking is performed. //! \~russian Проверка границ не выполняется. //! \sa at() (const version), PIVector::operator[] inline T & element(size_t row, size_t col) { return mat[row * cols_ + col]; } //! \~english Returns a const reference to the element at the given row and column. //! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу. inline const T & element(size_t row, size_t col) const { return mat[row * cols_ + col]; } //! \~english Returns a const reference to the element at the given row and column //! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу //! \details //! \~english No bounds checking is performed. //! \~russian Проверка границ не выполняется. inline const T & at(size_t row, size_t col) const { return mat[row * cols_ + col]; } //! \~english Returns a reference to the element at the given Index. //! \~russian Возвращает ссылку на элемент по заданному Index. inline T & operator[](const Index & idx) { return element(idx.row, idx.col); } //! \~english Returns a const reference to the element at the given Index. //! \~russian Возвращает константную ссылку на элемент по заданному Index. inline const T & operator[](const Index & idx) const { return element(idx.row, idx.col); } //! \~english Returns a reference to the element at the given Index. //! \~russian Возвращает ссылку на элемент по заданному Index. inline T & element(const Index & idx) { return element(idx.row, idx.col); } //! \~english Returns a const reference to the element at the given Index. //! \~russian Возвращает константную ссылку на элемент по заданному Index. inline const T & element(const Index & idx) const { return element(idx.row, idx.col); } //! \~english Returns a const reference to the element at the given Index (bounds-checked only in debug). //! \~russian Возвращает константную ссылку на элемент по заданному Index (проверка границ только в отладочном режиме). inline const T & at(const Index & idx) const { return at(idx.row, idx.col); } //! \~english Returns a proxy object for the row at the given index for modification. //! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации. //! \sa row(), Col inline Row operator[](size_t index) { return Row(this, index); } //! \~english Returns a proxy object for the row at the given index for read-only access. //! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения. inline RowConst operator[](size_t index) const { return RowConst(this, index); } //! \~english Returns a pointer to the underlying flat data starting at an optional offset. //! \~russian Возвращает указатель на внутренние плоские данные, начиная с опционального смещения. //! \sa PIVector::data() inline T * data(size_t index = 0) { return mat.data(index); } //! \~english Returns a const pointer to the underlying flat data starting at an optional offset. //! \~russian Возвращает константный указатель на внутренние плоские данные, начиная с опционального смещения. inline const T * data(size_t index = 0) const { return mat.data(index); } //! \~english Returns a proxy object for the row at the given index for modification. //! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации. //! \sa operator[] inline Row row(size_t index) { return Row(this, index); } //! \~english Returns a proxy object for the row at the given index for read-only access. //! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения. inline RowConst row(size_t index) const { return RowConst(this, index); } //! \~english Returns a proxy object for the column at the given index for modification. //! \~russian Возвращает прокси-объект для столбца по заданному индексу для модификации. //! \sa col() const inline Col col(size_t index) { return Col(this, index); } //! \~english Returns a proxy object for the column at the given index for read-only access. //! \~russian Возвращает прокси-объект для столбца по заданному индексу только для чтения. inline ColConst col(size_t index) const { return ColConst(this, index); } //! \~english Replaces a row with the contents of a read-only RowConst object. //! \~russian Заменяет строку содержимым объекта RowConst только для чтения. inline PIVector2D & setRow(size_t row, const RowConst & other) { const size_t sz = piMin(cols_, other.sz_); mat._copyRaw(mat.data(cols_ * row), other.data(), sz); return *this; } //! \~english Replaces a row with the contents of a \a PIVector. //! \~russian Заменяет строку содержимым \a PIVector. inline PIVector2D & setRow(size_t row, const PIVector & other) { const size_t sz = piMin(cols_, other.size()); mat._copyRaw(mat.data(cols_ * row), other.data(), sz); return *this; } //! \~english Appends a new row to the bottom of the array from another Row object. //! \~russian Добавляет новую строку в конец массива из другого объекта Row. //! \details //! \~english If the array was empty, its column count is set to the size of the source row. //! Otherwise, only `min(cols(), other.size())` elements are copied; the rest of the new row is default-initialized. //! \~russian Если массив был пуст, количество столбцов устанавливается равным размеру исходной строки. //! В противном случае копируется только `min(cols(), other.size())` элементов; остальные элементы новой строки инициализируются по //! умолчанию. \sa PIVector::push_back() inline PIVector2D & addRow(const RowConst & other) { if (cols_ == 0) cols_ = other.sz_; const size_t sz = piMin(cols_, other.sz_); const size_t ps = mat.size(); mat.resize(mat.size() + cols_); mat._copyRaw(mat.data(ps), other.data(), sz); rows_++; return *this; } //! \~english Appends a new row to the bottom of the array from a \a PIVector. //! \~russian Добавляет новую строку в конец массива из \a PIVector. inline PIVector2D & addRow(const PIVector & other) { if (cols_ == 0) cols_ = other.size(); mat.append(other); rows_++; mat.resize(rows_ * cols_); return *this; } inline PIVector2D & appendRows(size_t count, const T & f = T()) { if (count == 0) return *this; if (cols_ == 0) ++cols_; mat.resize(mat.size() + count * cols_, f); rows_ += count; return *this; } inline PIVector2D & appendColumns(size_t count, const T & f = T()) { if (count == 0) return *this; if (rows_ == 0) { mat.resize(count, f); rows_ = 1; cols_ = count; return *this; } 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; return *this; } inline PIVector2D & deleteRows(size_t row_start, size_t count) { if (row_start >= rows_ || count == 0) return *this; mat.remove(row_start * cols_, cols_ * count); if (isEmpty()) { cols_ = 0; rows_ = 0; } else { rows_ -= count; } return *this; } inline PIVector2D & deleteColumns(size_t col_start, size_t count) { if (col_start >= cols_ || rows_ == 0) return *this; count = piMin(count, cols_ - col_start); if (count == 0) return *this; for (size_t r = 0; r < rows_; ++r) { mat.remove(r * (cols_ - count) + col_start, count); } cols_ -= count; mat.resize(rows_ * cols_); return *this; } //! \~english Appends a new column to the right of the array from a \a ColConst. //! \~russian Добавляет новую строку в конец массива из \a ColConst. inline PIVector2D & addColumn(const ColConst & other) { if (other.size() == 0) return *this; if (isEmpty()) { mat.reserve(other.size()); for (size_t r = 0; r < other.size(); ++r) { mat.append(other[r]); } rows_ = mat.size(); cols_ = 1; return *this; } const size_t newCols = cols_ + 1; mat.reserve(rows_ * newCols); for (size_t r = rows_; r > 0; --r) { if (r - 1 < other.size()) { mat.insert(r * cols_, other[r - 1]); } else { mat.insert(r * cols_); } } cols_ = newCols; return *this; } //! \~english Appends a new column to the right of the array from a \a PIVector. //! \~russian Добавляет новую строку в конец массива из \a PIVector. inline PIVector2D & addColumn(const PIVector & other) { if (other.size() == 0) return *this; if (isEmpty()) { mat.append(other); rows_ = mat.size(); cols_ = 1; return *this; } const size_t newCols = cols_ + 1; mat.reserve(rows_ * newCols); for (size_t r = rows_; r > 0; --r) { if (r - 1 < other.size()) { mat.insert(r * cols_, other[r - 1]); } else { mat.insert(r * cols_); } } cols_ = newCols; return *this; } //! \~english Resizes the 2D array to new dimensions. //! \~russian Изменяет размер двумерного массива. //! \details //! \~english If the new dimensions are larger, new elements are appended and filled with copies of `f`. //! If they are smaller, the array is truncated (excess elements are destroyed). The underlying memory may be reallocated. //! \~russian Если новые размеры больше текущих, новые элементы добавляются в конец и заполняются копиями `f`. //! Если новые размеры меньше, массив усекается (лишние элементы уничтожаются). Внутренняя память может быть перераспределена. //! \code //! PIVector2D mat(2, 3, 0); // 2x3 matrix filled with 0 //! mat.resize(3, 4, 1); // becomes 3x4, new elements filled with 1 //! \endcode //! \sa PIVector::resize() inline PIVector2D & resize(size_t rows, size_t cols, const T & f = T()) { if (rows == rows_ && cols == cols_) return *this; if (rows_ == 0 || cols_ == 0) { mat.resize(rows * cols, f); rows_ = rows; cols_ = cols; return *this; } if (rows != rows_ && cols == cols_) { mat.resize(rows * cols_, f); rows_ = rows; return *this; } 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. //! \~russian Оператор равенства. //! \sa PIVector::operator== inline bool operator==(const PIVector2D & t) const { if (cols_ != t.cols_ || rows_ != t.rows_) return false; return mat == t.mat; } //! \~english Inequality operator. //! \~russian Оператор неравенства. inline bool operator!=(const PIVector2D & t) const { return !(*this == t); } //! \~english Converts the 2D array to a vector of vectors (PIVector>). //! \~russian Преобразует двумерный массив в вектор векторов (PIVector>). //! \details //! \~english Each row vector is a copy of the corresponding row. //! \~russian Каждый вектор-строка является копией соответствующей строки. //! \sa fromVectors(), PIVector::PIVector(const T*, size_t) inline PIVector> toVectors() const { PIVector> ret; ret.reserve(rows_); for (size_t i = 0; i < rows_; ++i) ret << PIVector(mat.data(i * cols_), cols_); return ret; } //! \~english Returns a const reference to the underlying flat \a PIVector. //! \~russian Возвращает константную ссылку на внутренний плоский \a PIVector. inline const PIVector & asPlainVector() const { return mat; } //! \~english Returns a reference to the underlying flat \a PIVector. //! \~russian Возвращает ссылку на внутренний плоский \a PIVector. inline PIVector & asPlainVector() { return mat; } //! \~english Returns a copy of the underlying flat \a PIVector. //! \~russian Возвращает копию внутреннего плоского \a PIVector. inline PIVector toPlainVector() const { return mat; } //! \~english Swaps this 2D array with another. //! \~russian Меняет местами этот двумерный массив с другим. //! \details //! \~english Swaps the flat vectors and the dimension members. Very fast, no memory allocation. //! \~russian Обменивает внутренние плоские векторы и члены, хранящие размеры. Очень быстро, без выделения памяти. //! \sa PIVector::swap() inline void swap(PIVector2D & other) { mat.swap(other.mat); piSwap(rows_, other.rows_); piSwap(cols_, other.cols_); } //! \internal template::value, int>::type = 0> inline PIVector2D & _resizeRaw(size_t r, size_t c) { rows_ = r; cols_ = c; mat._resizeRaw(r * c); return *this; } //! \~english Clears the array, removing all elements and setting dimensions to 0. //! \~russian Очищает массив, удаляя все элементы и устанавливая размеры в 0. //! \details //! \~english The capacity of the underlying flat vector may remain unchanged. //! \~russian Ёмкость внутреннего плоского вектора может остаться неизменной. //! \sa PIVector::clear() inline void clear() { rows_ = cols_ = 0; mat.clear(); } //! \~english Checks if the underlying flat vector contains the element `e`. //! \~russian Проверяет, содержит ли внутренний плоский вектор элемент `e`. //! \sa PIVector::contains() inline bool contains(const T & e) const { return mat.contains(e); } //! \~english Counts occurrences of `e` in the underlying flat vector. //! \~russian Подсчитывает количество вхождений `e` во внутреннем плоском векторе. //! \sa PIVector::entries() inline int entries(const T & e) const { return mat.entries(e); } //! \~english Counts elements in the flat vector that pass the `test`. //! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`. //! \sa PIVector::entries(std::function) inline int entries(std::function test) const { return mat.entries(test); } //! \~english Returns the first index (row, col) of `e` in the 2D array. //! \~russian Возвращает первый индекс (строка, столбец) элемента `e` в двумерном массиве. //! \sa PIVector::indexOf() inline Index indexOf(const T & e) const { ssize_t flat = mat.indexOf(e); if (flat < 0 || cols_ == 0) return Index{-1, -1}; return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Returns the first index (row, col) in the 2D array that passes the `test`. //! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`. //! \sa PIVector::indexWhere() inline Index indexWhere(std::function test, ssize_t start = 0) const { ssize_t flat = mat.indexWhere(test, start); if (flat < 0 || cols_ == 0) return Index{-1, -1}; return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Returns the last index (row, col) of `e` in the 2D array. //! \~russian Возвращает последний индекс (строка, столбец) элемента `e` в двумерном массиве. //! \sa PIVector::lastIndexOf() inline Index lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t flat = mat.lastIndexOf(e, start); if (flat < 0 || cols_ == 0) return Index{-1, -1}; return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Returns the last index (row, col) in the 2D array that passes the `test`. //! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`. //! \sa PIVector::lastIndexWhere() inline Index lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t flat = mat.lastIndexWhere(test, start); if (flat < 0 || cols_ == 0) return Index{-1, -1}; return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Tests if any element in the flat vector passes the `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`. //! \sa PIVector::any() inline bool any(std::function test) const { return mat.any(test); } //! \~english Tests if all elements in the flat vector pass the `test`. //! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`. //! \sa PIVector::every() inline bool every(std::function test) const { return mat.every(test); } //! \~english Fills the entire 2D array with copies of `e`. //! \~russian Заполняет весь двумерный массив копиями `e`. //! \sa PIVector::fill() inline PIVector2D & fill(const T & e = T()) { mat.fill(e); return *this; } //! \~english Fills the entire 2D array using a generator function `f` based on flat index. //! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса. //! \sa PIVector::fill(std::function) inline PIVector2D & fill(std::function f) { mat.fill(f); return *this; } //! \~english Same as \a fill(). //! \~russian То же, что и \a fill(). inline PIVector2D & assign(const T & e = T()) { return fill(e); } //! \~english Assigns new size and fills with value. //! \~russian Задаёт новый размер и заполняет значением. //! \sa PIVector::assign(size_t, const T&) inline PIVector2D & assign(size_t rows, size_t cols, const T & f = T()) { mat.assign(rows * cols, f); rows_ = rows; cols_ = cols; return *this; } //! \~english Returns a transposed 2D array (rows become columns and vice versa). //! \~russian Возвращает транспонированный двумерный массив (строки становятся столбцами и наоборот). //! \details //! \~english The element at (r, c) in the original becomes at (c, r) in the result. //! \~russian Элемент (r, c) исходного массива становится элементом (c, r) в результате. //! \code //! PIVector2D mat(2, 3, ...); //! auto t = mat.transposed(); // now 3x2 //! \endcode inline PIVector2D transposed() const { if (isEmpty()) return PIVector2D(); PIVector2D result(cols_, rows_); for (size_t r = 0; r < rows_; ++r) { for (size_t c = 0; c < cols_; ++c) { result.element(c, r) = element(r, c); } } return result; } //! \~english Reverses the order of rows in place. //! \~russian Изменяет порядок строк на обратный на месте. //! \sa reverseColumns(), PIVector::reverse() inline PIVector2D & reverseRows() { const size_t half = rows_ / 2; for (size_t i = 0; i < half; ++i) { 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; } //! \~english Reverses the order of columns in each row in place. //! \~russian Изменяет порядок столбцов в каждой строке на обратный на месте. //! \sa reverseRows(), PIVector::reverse() inline PIVector2D & reverseColumns() { for (size_t r = 0; r < rows_; ++r) { Row currentRow = row(r); const size_t half = cols_ / 2; for (size_t c = 0; c < half; ++c) { piSwap(currentRow[c], currentRow[cols_ - 1 - c]); } } return *this; } //! \~english Returns a sub-2D array (a range of rows and columns). //! \~russian Возвращает подмассив (диапазон строк и столбцов). //! \details //! \~english If the range exceeds the array boundaries, it is clipped. If rowCount or colCount is 0, an empty array is returned. //! \~russian Если диапазон выходит за границы массива, он обрезается. Если rowCount или colCount равны 0, возвращается пустой массив. //! \sa PIVector::getRange() inline PIVector2D getRange(size_t rowStart, size_t rowCount, size_t colStart, size_t colCount) const { if (rowStart >= rows_ || colStart >= cols_ || rowCount == 0 || colCount == 0) return PIVector2D(); const size_t actualRowCount = piMin(rowCount, rows_ - rowStart); const size_t actualColCount = piMin(colCount, cols_ - colStart); PIVector2D result(actualRowCount, actualColCount); for (size_t r = 0; r < actualRowCount; ++r) { for (size_t c = 0; c < actualColCount; ++c) { result.element(r, c) = element(rowStart + r, colStart + c); } } return result; } //! \~english Applies a function to each element and returns a new 2D array of a different type. //! \~russian Применяет функцию к каждому элементу и возвращает новый двумерный массив другого типа. //! \details //! \~english The original array is not modified. //! \~russian Исходный массив не изменяется. //! \sa PIVector::map() template inline PIVector2D map(std::function f) const { return PIVector2D(rows_, cols_, mat.template map(f)); } //! \~english Applies a function (with row and col indices) to each element and returns a new 2D array. //! \~russian Применяет функцию (с индексами строки и столбца) к каждому элементу и возвращает новый двумерный массив. //! \sa PIVector::mapIndexed() template inline PIVector2D mapIndexed(std::function f) const { PIVector mappedMat; mappedMat.reserve(size()); for (size_t r = 0; r < rows_; ++r) { for (size_t c = 0; c < cols_; ++c) { mappedMat << f(r, c, element(r, c)); } } return PIVector2D(rows_, cols_, std::move(mappedMat)); } //! \~english Applies a function to each row (modifiable). //! \~russian Применяет функцию к каждой строке (с возможностью изменения). //! \sa forEachRow() const, PIVector::forEach() inline PIVector2D & forEachRow(std::function f) { for (size_t r = 0; r < rows_; ++r) f(row(r)); return *this; } //! \~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 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 Применяет функцию к каждому столбцу (только чтение). //! \param f Function taking a \a ColConst. inline void forEachColumn(std::function f) const { for (size_t c = 0; c < cols_; ++c) f(col(c)); } //! \~english Accumulates a value across all elements. //! \~russian Аккумулирует значение по всем элементам. //! \sa PIVector::reduce() template inline ST reduce(std::function f, const ST & initial = ST()) const { return mat.template reduce(f, initial); } //! \~english Accumulates a value across all elements with indices. //! \~russian Аккумулирует значение по всем элементам с индексами. //! \sa PIVector::reduceIndexed() template inline ST reduceIndexed(std::function f, const ST & initial = ST()) const { ST ret(initial); for (size_t r = 0; r < rows_; ++r) { for (size_t c = 0; c < cols_; ++c) { ret = f(r, c, element(r, c), ret); } } return ret; } //! \~english Removes a row from the 2D array. //! \~russian Удаляет строку из двумерного массива. //! \details //! \~english If the last row is removed and the array becomes empty, \a cols() is set to 0. //! \~russian Если удаляется последняя строка и массив становится пустым, \a cols() устанавливается в 0. //! \sa removeColumn(), PIVector::remove() inline PIVector2D & removeRow(size_t row) { return deleteRows(row, 1); } //! \~english Removes a column from the 2D array. //! \~russian Удаляет столбец из двумерного массива. //! \details //! \~english This operation is more expensive than removing a row because elements must be moved. //! \~russian Эта операция дороже, чем удаление строки, поскольку требуется перемещение элементов. //! \sa removeRow(), PIVector::remove() inline PIVector2D & removeColumn(size_t col) { return deleteColumns(col, 1); } //! \~english Removes all rows that satisfy a condition. //! \~russian Удаляет все строки, удовлетворяющие условию. //! \details //! \~english Rows are removed from the bottom to avoid index shifting issues. //! \~russian Строки удаляются снизу вверх, чтобы избежать проблем со смещением индексов. //! \sa removeColumnsWhere(), PIVector::removeWhere() inline PIVector2D & removeRowsWhere(std::function test) { ssize_t r = rows_; while (--r >= 0) { if (test(RowConst(this, r))) { removeRow(r); } } return *this; } //! \~english Removes all columns that satisfy a condition. //! \~russian Удаляет все столбцы, удовлетворяющие условию. //! \sa removeRowsWhere() inline PIVector2D & removeColumnsWhere(std::function test) { ssize_t c = cols_; while (--c >= 0) { if (test(ColConst(this, c))) { removeColumn(c); } } return *this; } //! \~english Returns a new 2D array containing only the rows that pass the test. //! \~russian Возвращает новый двумерный массив, содержащий только строки, прошедшие проверку. //! \sa filterColumns(), PIVector::filter() inline PIVector2D filterRows(std::function test) const { PIVector2D result; for (size_t r = 0; r < rows_; ++r) { RowConst currentRow = row(r); if (test(currentRow)) { result.addRow(currentRow); } } return result; } //! \~english Returns a new 2D array containing only the columns that pass the test. //! \~russian Возвращает новый двумерный массив, содержащий только столбцы, прошедшие проверку. //! \sa filterRows() inline PIVector2D filterColumns(std::function test) const { if (isEmpty()) return PIVector2D(); PIVector goodCols; for (size_t c = 0; c < cols_; ++c) { if (test(col(c))) { goodCols << c; } } PIVector2D result; result._resizeRaw(rows_, goodCols.size()); for (size_t r = 0; r < rows_; ++r) { const size_t start_dst = r * result.cols_; const size_t start_src = r * cols_; for (size_t gc = 0; gc < goodCols.size(); ++gc) { result.mat._copyRaw(result.mat.data(start_dst + gc), mat.data(start_src + goodCols[gc]), 1); } } return result; } protected: size_t rows_, cols_; PIVector mat; }; //! \relatesalso PICout //! \~english Output operator for \a PIVector2D to \a PICout. //! \~russian Оператор вывода \a PIVector2D в \a PICout. template inline PICout operator<<(PICout s, const PIVector2D & v) { s.saveAndSetControls(0); s << "{"; for (size_t i = 0; i < v.rows(); ++i) { s << "{ "; for (size_t j = 0; j < v.cols(); ++j) { s << v[i][j]; if (j < v.cols() - 1) s << ", "; } s << " }"; if (i < v.rows() - 1) s << PICoutManipulators::NewLine; } if (v.isEmpty()) s << "{ }"; s << "}"; s.restoreControls(); return s; } //! \} #endif // PIVECTOR2D_H