PIVector2D - add funcs, optimize, tests, fixes, doxygen (#194)
Reviewed-on: #194 Co-authored-by: andrey.bychkov <andrey@signalmodelling.ru> Co-committed-by: andrey.bychkov <andrey@signalmodelling.ru>
This commit was merged in pull request #194.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@ CMakeLists.txt.user*
|
||||
/include
|
||||
/release
|
||||
/build*
|
||||
/AGENTS.md
|
||||
/plans
|
||||
|
||||
@@ -5,8 +5,8 @@ if (POLICY CMP0177)
|
||||
endif()
|
||||
project(PIP)
|
||||
set(PIP_MAJOR 5)
|
||||
set(PIP_MINOR 5)
|
||||
set(PIP_REVISION 5)
|
||||
set(PIP_MINOR 6)
|
||||
set(PIP_REVISION 0)
|
||||
set(PIP_SUFFIX )
|
||||
set(PIP_COMPANY SHS)
|
||||
set(PIP_DOMAIN org.SHS)
|
||||
|
||||
115
README.md
115
README.md
@@ -40,3 +40,118 @@ You should add ${<out_var>} to your target.
|
||||
[🇷🇺 Онлайн документация](https://shstk.ru/pip/html/ru/index.html)
|
||||
|
||||
[🇷🇺 Qt-help](https://shstk.ru/pip/pip_ru.qch)
|
||||
|
||||
## Основные опции сборки
|
||||
|
||||
### Стандартные опции (option())
|
||||
| Опция | Описание | По умолчанию |
|
||||
|-------|----------|--------------|
|
||||
| `ICU` | ICU support для конвертации кодовых страниц | ON (кроме Win/Android/Apple) |
|
||||
| `STD_IOSTREAM` | Поддержка std::iostream операторов | OFF |
|
||||
| `INTROSPECTION` | Сборка с интроспекцией | OFF |
|
||||
| `TESTS` | Сборка тестов | OFF |
|
||||
| `COVERAGE` | Сборка с информацией о покрытии | OFF |
|
||||
| `PIP_FFTW_F` | Поддержка FFTW для float | ON |
|
||||
| `PIP_FFTW_L` | Поддержка FFTW для long double | ON |
|
||||
| `PIP_FFTW_Q` | Поддержка FFTW для quad double | OFF |
|
||||
|
||||
### Опции модулей (PIP_BUILD_*)
|
||||
| Опция | Модуль |
|
||||
|-------|--------|
|
||||
| `PIP_BUILD_CONSOLE` | console |
|
||||
| `PIP_BUILD_CRYPT` | crypt (требует libsodium) |
|
||||
| `PIP_BUILD_COMPRESS` | compress (требует zlib) |
|
||||
| `PIP_BUILD_USB` | usb |
|
||||
| `PIP_BUILD_FFTW` | fftw |
|
||||
| `PIP_BUILD_OPENCL` | opencl |
|
||||
| `PIP_BUILD_IO_UTILS` | io_utils |
|
||||
| `PIP_BUILD_CLIENT_SERVER` | client_server |
|
||||
| `PIP_BUILD_CLOUD` | cloud |
|
||||
| `PIP_BUILD_LUA` | lua |
|
||||
| `PIP_BUILD_HTTP_CLIENT` | http_client (требует libcurl) |
|
||||
| `PIP_BUILD_HTTP_SERVER` | http_server (требует libmicrohttpd) |
|
||||
|
||||
### Дополнительные переменные
|
||||
| Переменная | Описание |
|
||||
|------------|----------|
|
||||
| `PIP_BUILD_DEBUG` | Сборка debug версии |
|
||||
| `PIP_FREERTOS` | Режим сборки для FreeRTOS |
|
||||
| `CROSSTOOLS` | Собрать инструменты кросс-сборки под хостовую систему (pip_cmg, pip_rc, ...) |
|
||||
| `LOCAL` | Локальная установка (bin/lib/include) |
|
||||
| `PIP_CONTAINERS_MIN_ALLOC` | Переопределить минимальный размер аллокации контейнеров |
|
||||
| `PIP_CONTAINERS_MAX_POT_ALLOC` | Переопределить максимальный размер дополнительной аллокации (поддерживает X_KiB, X_MiB) |
|
||||
|
||||
### Примеры использования
|
||||
```bash
|
||||
# Базовая сборка с тестами
|
||||
cmake -B build -DTESTS=ON
|
||||
|
||||
# Сборка с покрытием и ICU
|
||||
cmake -B build -DTESTS=ON -DCOVERAGE=ON -DICU=ON
|
||||
|
||||
# Отключение отдельных модулей
|
||||
cmake -B build -DPIP_BUILD_CRYPT=OFF -DPIP_BUILD_OPENCL=OFF
|
||||
|
||||
# Переопределение параметров контейнеров
|
||||
cmake -B build -DPIP_CONTAINERS_MIN_ALLOC=64
|
||||
|
||||
# Локальная установка
|
||||
cmake -B build -DLOCAL=ON
|
||||
```
|
||||
|
||||
## PIP Dependencies
|
||||
|
||||
### Встроенные (included in 3rd/)
|
||||
|
||||
| Библиотека | Назначение | Модуль PIP |
|
||||
|------------|------------|------------|
|
||||
| **PCRE2** | Регулярные выражения | main (internal) |
|
||||
| **BLAKE2** | Хеширование | main (internal) |
|
||||
| **SipHash** | Хеширование | main (internal) |
|
||||
| **Lua** | Lua scripting | lua |
|
||||
| **LuaBridge** | Lua bindings | lua |
|
||||
|
||||
### Внешние (системные)
|
||||
|
||||
| Библиотека | Опция | Модуль PIP |
|
||||
|------------|-------|------------|
|
||||
| **ICU** | `-DICU=ON` | main (string conversion) |
|
||||
| **zlib** | `PIP_BUILD_COMPRESS` | compress |
|
||||
| **libsodium** | `PIP_BUILD_CRYPT` | crypt, io_utils, cloud |
|
||||
| **libusb** | `PIP_BUILD_USB` | usb |
|
||||
| **FFTW3** (+ threads) | `PIP_BUILD_FFTW` | fftw |
|
||||
| **OpenCL** | `PIP_BUILD_OPENCL` | opencl |
|
||||
| **libmicrohttpd** | `PIP_BUILD_HTTP_SERVER` | http_server |
|
||||
| **libcurl** | `PIP_BUILD_HTTP_CLIENT` | http_client |
|
||||
|
||||
### Опциональные (тесты/документация)
|
||||
|
||||
| Инструмент | Назначение |
|
||||
|------------|------------|
|
||||
| **Google Test** | Тестирование (fetched automatically) |
|
||||
| **Doxygen** | Генерация документации |
|
||||
|
||||
|
||||
### Схема зависимостей модулей
|
||||
|
||||
```
|
||||
main (core)
|
||||
├── PCRE2 (встроен)
|
||||
├── BLAKE2 (встроен)
|
||||
├── SipHash (встроен)
|
||||
└── ICU (опционально)
|
||||
|
||||
console → main
|
||||
compress → zlib
|
||||
crypt → libsodium
|
||||
usb → libusb
|
||||
fftw → FFTW3
|
||||
opencl → OpenCL
|
||||
io_utils → [crypt, если доступен]
|
||||
client_server → io_utils
|
||||
cloud → io_utils, crypt
|
||||
lua → Lua (встроен), LuaBridge (встроен)
|
||||
http_server → libmicrohttpd
|
||||
http_client → libcurl
|
||||
```
|
||||
|
||||
|
||||
@@ -835,8 +835,8 @@ public:
|
||||
//! piCout << v.contains({1,4}); // true
|
||||
//! piCout << v.contains({1,5}); // false
|
||||
//! \endcode
|
||||
//! \~\sa \a every(), \a any(), \a entries(), \a forEach()
|
||||
inline bool contains(const PIVector<T> & v, ssize_t start = 0) const {
|
||||
//! \~\sa \a every(), \a any(), \a entries(), \a forEach(), \a contains()
|
||||
inline bool containsAll(const PIVector<T> & v, ssize_t start = 0) const {
|
||||
if (start < 0) {
|
||||
start = piv_size + start;
|
||||
if (start < 0) start = 0;
|
||||
@@ -854,6 +854,24 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
//! \~english Tests if any element of `v` exists in the array.
|
||||
//! \~russian Проверяет наличие хотя бы одного из элементов `v` в массиве.
|
||||
//! \~\sa \a containsAll(), \a contains()
|
||||
inline bool containsAny(const PIVector<T> & v, ssize_t start = 0) const {
|
||||
if (start < 0) {
|
||||
start = piv_size + start;
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
for (const T & e: v) {
|
||||
for (size_t i = start; i < piv_size; ++i) {
|
||||
if (e == piv_data[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! \~english Count elements equal `e` in the array.
|
||||
//! \~russian Подсчитывает количество элементов, совпадающих с элементом `e` в массиве.
|
||||
//! \~\details
|
||||
@@ -1303,14 +1321,16 @@ public:
|
||||
//! piCout << v; // {1, 3, 7, 5}
|
||||
//! \endcode
|
||||
//! \~\sa \a append(), \a prepend(), \a remove()
|
||||
inline PIVector<T> & insert(size_t index, const T & e = T()) {
|
||||
alloc(piv_size + 1);
|
||||
if (index < piv_size - 1) {
|
||||
const size_t os = piv_size - index - 1;
|
||||
memmove(reinterpret_cast<void *>(piv_data + index + 1), reinterpret_cast<const void *>(piv_data + index), os * sizeof(T));
|
||||
inline PIVector<T> & insert(size_t index, const T & e = T(), size_t count = 1) {
|
||||
alloc(piv_size + count);
|
||||
if (index < piv_size - count) {
|
||||
const size_t os = piv_size - index - count;
|
||||
memmove(reinterpret_cast<void *>(piv_data + index + count), reinterpret_cast<const void *>(piv_data + index), os * sizeof(T));
|
||||
}
|
||||
PIINTROSPECTION_CONTAINER_USED(T, count)
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
elementNew(piv_data + index + i, e);
|
||||
}
|
||||
PIINTROSPECTION_CONTAINER_USED(T, 1)
|
||||
elementNew(piv_data + index, e);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1349,8 +1369,8 @@ public:
|
||||
alloc(piv_size + v.piv_size);
|
||||
if (os > 0) {
|
||||
memmove(reinterpret_cast<void *>(piv_data + index + v.piv_size),
|
||||
reinterpret_cast<const void *>(piv_data + index),
|
||||
os * sizeof(T));
|
||||
reinterpret_cast<const void *>(piv_data + index),
|
||||
os * sizeof(T));
|
||||
}
|
||||
newT(piv_data + index, v.piv_data, v.piv_size);
|
||||
return *this;
|
||||
@@ -1372,8 +1392,8 @@ public:
|
||||
alloc(piv_size + init_list.size());
|
||||
if (os > 0) {
|
||||
memmove(reinterpret_cast<void *>(piv_data + index + init_list.size()),
|
||||
reinterpret_cast<const void *>(piv_data + index),
|
||||
os * sizeof(T));
|
||||
reinterpret_cast<const void *>(piv_data + index),
|
||||
os * sizeof(T));
|
||||
}
|
||||
newT(piv_data + index, init_list.begin(), init_list.size());
|
||||
return *this;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#ifndef PIVECTOR2D_H
|
||||
#define PIVECTOR2D_H
|
||||
|
||||
#include "pipair.h"
|
||||
#include "pivector.h"
|
||||
|
||||
//! \addtogroup Containers
|
||||
@@ -43,7 +42,7 @@
|
||||
//! (use \a resize(), \a addRow(), \a removeRow(), \a removeColumn() instead), but you can modify the values of existing elements.
|
||||
//! \~russian
|
||||
//! Этот класс используется для хранения двумерного массива элементов любого типа в виде единого непрерывного блока памяти (обычного
|
||||
//! PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со
|
||||
//! \a PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со
|
||||
//! строками можно работать как с объектами \a PIVector, что позволяет изменять отдельные элементы или присваивать целые строки. Нельзя
|
||||
//! напрямую добавлять или удалять элементы, чтобы изменить размеры массива после создания (используйте \a resize(), \a addRow(), \a
|
||||
//! removeRow(), \a removeColumn() для этого), но можно изменять значения существующих элементов.
|
||||
@@ -52,21 +51,51 @@
|
||||
template<typename T>
|
||||
class PIVector2D {
|
||||
public:
|
||||
//! \~english Constructs an empty 2D array.
|
||||
//! \~russian Создает пустой двумерный массив.
|
||||
//! \brief
|
||||
//! \~english Index structure for 2D array elements (row, column).
|
||||
//! \~russian Структура индекса для элементов двумерного массива (строка, столбец).
|
||||
struct Index {
|
||||
//! \~english Row index in the 2D array.
|
||||
//! \~russian Индекс строки в двумерном массиве.
|
||||
ssize_t row = -1;
|
||||
//! \~english Column index in the 2D array.
|
||||
//! \~russian Индекс столбца в двумерном массиве.
|
||||
ssize_t col = -1;
|
||||
|
||||
//! \~english Default constructor. Initializes row and col to -1 (invalid index).
|
||||
//! \~russian Конструктор по умолчанию. Инициализирует row и col значениями -1 (некорректный индекс).
|
||||
inline Index() = default;
|
||||
//! \~english Constructs an Index with the given row and column values.
|
||||
//! \~russian Создаёт Index с заданными значениями строки и столбца.
|
||||
inline Index(ssize_t r, ssize_t c): row(r), col(c) {}
|
||||
|
||||
//! \~english Checks if the index is valid (both row and column are non-negative).
|
||||
//! \~russian Проверяет, является ли индекс корректным (строка и столбец неотрицательны).
|
||||
//! \~\sa isNotValid()
|
||||
inline bool isValid() const { return row >= 0 && col >= 0; }
|
||||
|
||||
//! \~english Checks if the index is invalid (either row or column is negative).
|
||||
//! \~russian Проверяет, является ли индекс некорректным (строка или столбец отрицательны).
|
||||
//! \~\sa isValid()
|
||||
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`.
|
||||
//! \param rows Number of rows.
|
||||
//! \param cols Number of columns.
|
||||
//! \param f Value to fill the array with.
|
||||
//! \~english \param rows Количество строк.
|
||||
//! \~russian \param rows Количество строк.
|
||||
//! \~english \param cols Количество столбцов.
|
||||
//! \~russian \param cols Количество столбцов.
|
||||
//! \~english \param f Значение для заполнения массива.
|
||||
//! \~russian \param 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;
|
||||
@@ -74,22 +103,35 @@ public:
|
||||
}
|
||||
|
||||
//! \~english Constructs a 2D array from an existing 1D vector, reshaping it.
|
||||
//! \~russian Создает двумерный массив из существующего одномерного вектора, изменяя его форму.
|
||||
//! \param rows Number of rows.
|
||||
//! \param cols Number of columns.
|
||||
//! \param v The source 1D vector. Its size must be at least `rows * cols`.
|
||||
//! \~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<T> & 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 Конструктор перемещения из существующего одномерного вектора, изменяя его форму.
|
||||
//! \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`.
|
||||
//! \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<T> && 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.
|
||||
//! \~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<PIVector<T>> & v) {
|
||||
rows_ = v.size();
|
||||
if (rows_) {
|
||||
@@ -103,240 +145,97 @@ public:
|
||||
if (mat.isEmpty()) rows_ = cols_ = 0;
|
||||
}
|
||||
|
||||
//! \~english Number of rows.
|
||||
//! \~russian Количество строк.
|
||||
//! \~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 Number of columns.
|
||||
//! \~russian Количество столбцов.
|
||||
//! \~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 Total number of elements (`rows * cols`).
|
||||
//! \~russian Общее количество элементов (`строки * столбцы`).
|
||||
//! \~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 Total number of elements as signed value.
|
||||
//! \~russian Общее количество элементов в виде знакового числа.
|
||||
//! \~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 Total number of elements.
|
||||
//! \~russian Общее количество элементов.
|
||||
//! \~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 Number of elements that the underlying container has currently allocated space for.
|
||||
//! \~russian Количество элементов, для которого сейчас выделена память во внутреннем контейнере.
|
||||
//! \~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 elements.
|
||||
//! \~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 Row
|
||||
//! \brief
|
||||
//! \~english Proxy class representing a single row in a \a PIVector2D for modification.
|
||||
//! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D для модификации.
|
||||
class Row {
|
||||
friend class PIVector2D<T>;
|
||||
|
||||
private:
|
||||
inline Row(PIVector2D<T> * p, size_t row): p_(&(p->mat)) {
|
||||
st_ = p->cols_ * row;
|
||||
sz_ = p->cols_;
|
||||
}
|
||||
PIVector<T> * p_;
|
||||
size_t st_, sz_;
|
||||
|
||||
public:
|
||||
//! \~english Size of the row (number of columns).
|
||||
//! \~russian Размер строки (количество столбцов).
|
||||
inline size_t size() const { return sz_; }
|
||||
|
||||
//! \~english Accesses the element at the given column index within the row.
|
||||
//! \~russian Доступ к элементу по заданному индексу столбца в строке.
|
||||
inline T & operator[](size_t index) { return (*p_)[st_ + index]; }
|
||||
|
||||
//! \~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 pointer to the row data starting at an optional offset.
|
||||
//! \~russian Возвращает указатель на данные строки, начиная с опционального смещения.
|
||||
inline T * data(size_t index = 0) { return p_->data(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 Assigns the contents of another Row to this row.
|
||||
//! \~russian Присваивает этой строке содержимое другой строки.
|
||||
inline Row & operator=(const Row & other) {
|
||||
if (p_ == other.p_ && st_ == other.st_) return *this;
|
||||
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
||||
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Assigns the contents of a \a PIVector to this row.
|
||||
//! \~russian Присваивает этой строке содержимое \a PIVector.
|
||||
inline Row & operator=(const PIVector<T> & other) {
|
||||
const size_t sz = piMin<size_t>(sz_, other.size());
|
||||
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Converts the row to a \a PIVector.
|
||||
//! \~russian Преобразует строку в \a PIVector.
|
||||
inline PIVector<T> toVector() const { return PIVector<T>(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<bool(const T & e)> 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<bool(const T & e)> 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
|
||||
//! \brief
|
||||
//! \~english Proxy class representing a single column in a \a PIVector2D for modification.
|
||||
//! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D для модификации.
|
||||
class Col {
|
||||
friend class PIVector2D<T>;
|
||||
|
||||
private:
|
||||
inline Col(PIVector2D<T> * p, size_t col): p_(&(p->mat)) {
|
||||
step_ = p->cols_;
|
||||
col_ = col;
|
||||
sz_ = p->rows_;
|
||||
}
|
||||
PIVector<T> * p_;
|
||||
size_t step_, col_, sz_;
|
||||
|
||||
public:
|
||||
//! \~english Size of the column (number of rows).
|
||||
//! \~russian Размер столбца (количество строк).
|
||||
inline size_t size() const { return sz_; }
|
||||
|
||||
//! \~english Accesses the element at the given row index within the column.
|
||||
//! \~russian Доступ к элементу по заданному индексу строки в столбце.
|
||||
inline T & operator[](size_t index) { return (*p_)[index * step_ + col_]; }
|
||||
|
||||
//! \~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 pointer to the column data starting at an optional row offset.
|
||||
//! \~russian Возвращает указатель на данные столбца, начиная с опционального смещения по строкам.
|
||||
inline T * data(size_t index = 0) { return p_->data(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 Assigns the contents of another Col to this column.
|
||||
//! \~russian Присваивает этому столбцу содержимое другого столбца.
|
||||
inline Col & operator=(const Col & other) {
|
||||
if (p_ == other.p_ && col_ == other.col_) return *this;
|
||||
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
(*p_)[i * step_ + col_] = other[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Assigns the contents of a \a PIVector to this column.
|
||||
//! \~russian Присваивает этому столбцу содержимое \a PIVector.
|
||||
inline Col & operator=(const PIVector<T> & other) {
|
||||
const size_t sz = piMin<size_t>(sz_, other.size());
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
(*p_)[i * step_ + col_] = other[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Converts the column to a \a PIVector.
|
||||
//! \~russian Преобразует столбец в \a PIVector.
|
||||
inline PIVector<T> toVector() const {
|
||||
PIVector<T> ret;
|
||||
ret.reserve(sz_);
|
||||
for (size_t i = 0; i < sz_; i++)
|
||||
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<bool(const T & e)> 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<bool(const T & e)> 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
|
||||
//! \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<T>;
|
||||
|
||||
private:
|
||||
inline RowConst(const PIVector2D<T> * p, size_t row): p_(&(p->mat)) {
|
||||
st_ = p->cols_ * row;
|
||||
sz_ = p->cols_;
|
||||
}
|
||||
protected:
|
||||
inline RowConst(const PIVector2D<T> * p, size_t row): p_(&(p->mat)), st_(p->cols_ * row), sz_(p->cols_) {}
|
||||
const PIVector<T> * p_;
|
||||
size_t st_, sz_;
|
||||
const size_t st_, sz_;
|
||||
|
||||
public:
|
||||
//! \~english Copy constructor from modifiable Row to read-only RowConst.
|
||||
//! \~russian Конструктор копирования из модифицируемого класса Row в константный RowConst.
|
||||
//! \~\sa Row
|
||||
inline RowConst(const PIVector2D<T>::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_; }
|
||||
@@ -353,7 +252,12 @@ public:
|
||||
//! \~russian Преобразует строку в \a PIVector.
|
||||
inline PIVector<T> toVector() const { return PIVector<T>(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) {
|
||||
@@ -361,6 +265,11 @@ public:
|
||||
}
|
||||
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) {
|
||||
@@ -368,6 +277,10 @@ public:
|
||||
}
|
||||
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<bool(const T & e)> test, ssize_t start = 0) const {
|
||||
if (start < 0) start = 0;
|
||||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||
@@ -375,6 +288,11 @@ public:
|
||||
}
|
||||
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<bool(const T & e)> 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) {
|
||||
@@ -382,25 +300,92 @@ public:
|
||||
}
|
||||
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<void(const T &)> 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<bool(const T & e)> 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<bool(const T & e)> 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<bool(const T & e)> 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<T>;
|
||||
|
||||
private:
|
||||
inline ColConst(const PIVector2D<T> * p, size_t col): p_(&(p->mat)) {
|
||||
step_ = p->cols_;
|
||||
col_ = col;
|
||||
sz_ = p->rows_;
|
||||
}
|
||||
protected:
|
||||
inline ColConst(const PIVector2D<T> * p, size_t col): p_(&(p->mat)), step_(p->cols_), col_(col), sz_(p->rows_) {}
|
||||
const PIVector<T> * p_;
|
||||
size_t step_, col_, sz_;
|
||||
const size_t step_, col_, sz_;
|
||||
|
||||
public:
|
||||
//! \~english Copy constructor from modifiable Col to read-only ColConst.
|
||||
//! \~russian Конструктор копирования из модифицируемого класса Col в константный ColConst.
|
||||
//! \~\sa Col
|
||||
inline ColConst(const PIVector2D<T>::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_; }
|
||||
@@ -423,7 +408,12 @@ public:
|
||||
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) {
|
||||
@@ -431,6 +421,10 @@ public:
|
||||
}
|
||||
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) {
|
||||
@@ -438,6 +432,10 @@ public:
|
||||
}
|
||||
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<bool(const T & e)> test, ssize_t start = 0) const {
|
||||
if (start < 0) start = 0;
|
||||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||||
@@ -445,6 +443,11 @@ public:
|
||||
}
|
||||
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<bool(const T & e)> 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) {
|
||||
@@ -452,22 +455,277 @@ public:
|
||||
}
|
||||
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<void(const T &)> 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<bool(const T & e)> 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<bool(const T & e)> 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<bool(const T & e)> 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<T>;
|
||||
|
||||
private:
|
||||
inline Row(PIVector2D<T> * p, size_t row): RowConst(p, row), p_(&(p->mat)) {}
|
||||
PIVector<T> * 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<size_t>(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<T> & other) {
|
||||
const size_t sz = piMin<size_t>(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<void(T &)> 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<T>;
|
||||
|
||||
private:
|
||||
inline Col(PIVector2D<T> * p, size_t col): ColConst(p, col), p_(&(p->mat)) {}
|
||||
PIVector<T> * 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<size_t>(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<T> & other) {
|
||||
const size_t sz = piMin<size_t>(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<void(T &)> 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 (bounds-checked only in debug).
|
||||
//! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу (проверка границ только в отладочном режиме).
|
||||
//! \~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.
|
||||
@@ -476,6 +734,7 @@ public:
|
||||
|
||||
//! \~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.
|
||||
@@ -485,6 +744,7 @@ public:
|
||||
|
||||
//! \~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.
|
||||
@@ -493,21 +753,13 @@ public:
|
||||
|
||||
//! \~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 another Row object.
|
||||
//! \~russian Заменяет строку содержимым другого объекта Row.
|
||||
inline PIVector2D<T> & setRow(size_t row, const Row & other) {
|
||||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||||
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Replaces a row with the contents of a read-only RowConst object.
|
||||
//! \~russian Заменяет строку содержимым объекта RowConst только для чтения.
|
||||
inline PIVector2D<T> & setRow(size_t row, const RowConst & other) {
|
||||
@@ -526,25 +778,18 @@ public:
|
||||
|
||||
//! \~english Appends a new row to the bottom of the array from another Row object.
|
||||
//! \~russian Добавляет новую строку в конец массива из другого объекта Row.
|
||||
inline PIVector2D<T> & addRow(const Row & other) {
|
||||
if (cols_ == 0) cols_ = other.sz_;
|
||||
const size_t sz = piMin<size_t>(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 read-only RowConst object.
|
||||
//! \~russian Добавляет новую строку в конец массива из объекта RowConst только для чтения.
|
||||
//! \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<T> & addRow(const RowConst & other) {
|
||||
if (cols_ == 0) cols_ = other.sz_;
|
||||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||||
const size_t ps = mat.size();
|
||||
mat.resize(mat.size() + cols_);
|
||||
mat._copyRaw(mat.data(ps), other.data(), sz);
|
||||
mat.append(other.toVector());
|
||||
rows_++;
|
||||
mat.resize(rows_ * cols_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -552,37 +797,191 @@ public:
|
||||
//! \~russian Добавляет новую строку в конец массива из \a PIVector.
|
||||
inline PIVector2D<T> & addRow(const PIVector<T> & other) {
|
||||
if (cols_ == 0) cols_ = other.size();
|
||||
const size_t sz = piMin<size_t>(cols_, other.size());
|
||||
const size_t ps = mat.size();
|
||||
mat.resize(mat.size() + cols_);
|
||||
mat._copyRaw(mat.data(ps), other.data(), sz);
|
||||
mat.append(other);
|
||||
rows_++;
|
||||
mat.resize(rows_ * cols_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Appends \a count new empty rows to the bottom of the array, filled with value \a f.
|
||||
//! \~russian Добавляет \a count новых пустых строк в конец массива, заполненных значением \a f.
|
||||
//! \details
|
||||
//! \~english If the array was empty (no columns defined), the column count is set to 1.
|
||||
//! The new rows are filled with the default value \a f.
|
||||
//! \~russian Если массив был пуст (количество столбцов не определено), количество столбцов устанавливается равным 1.
|
||||
//! Новые строки заполняются значением по умолчанию \a f.
|
||||
//! \~\sa addRow(), appendColumns()
|
||||
inline PIVector2D<T> & 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;
|
||||
}
|
||||
|
||||
//! \~english Appends \a count new empty columns to the end of each row of the array.
|
||||
//! \~russian Добавляет \a count новых пустых столбцов в конец каждой строки массива.
|
||||
//! \details
|
||||
//! \~english If the array was empty (rows not defined), the array becomes a single row with \a count columns.
|
||||
//! If the array already has rows, new elements are inserted at the end of each existing row.
|
||||
//! \~russian Если массив был пуст (строки не определены), массив становится одной строкой с \a count столбцов.
|
||||
//! Если массив уже содержит строки, новые элементы добавляются в конец каждой существующей строки.
|
||||
//! \~\sa appendRows(), addColumn()
|
||||
inline PIVector2D<T> & 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;
|
||||
}
|
||||
|
||||
//! \~english Deletes `count` rows starting from the specified row index.
|
||||
//! \~russian Удаляет `count` строк, начиная с указанного индекса строки.
|
||||
//! \details
|
||||
//! \~english Removes the specified rows from the array and updates the row count. If all elements are deleted (array becomes empty),
|
||||
//! both rows and columns are set to 0.
|
||||
//! \~russian Удаляет указанные строки из массива и обновляет количество строк. Если все элементы удалены (массив становится пустым),
|
||||
//! количество строк и столбцов устанавливается в 0.
|
||||
//! \~\sa deleteColumns()
|
||||
inline PIVector2D<T> & 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;
|
||||
}
|
||||
|
||||
//! \~english Removes the specified columns from the array and updates the column count.
|
||||
//! \~russian Удаляет указанные столбцы из массива и обновляет количество столбцов.
|
||||
//! \details
|
||||
//! \~english Removes \a count columns starting from \a col_start. If \a col_start is out of range or \a count is 0,
|
||||
//! the function does nothing. If \a count extends beyond the last column, only available columns are deleted.
|
||||
//! \~russian Удаляет \a count столбцов начиная с \a col_start. Если \a col_start выходит за границы или \a count равен 0,
|
||||
//! функция ничего не делает. Если \a count выходит за последний столбец, удаляются только доступные столбцы.
|
||||
//! \~\sa removeColumn(), deleteRows()
|
||||
inline PIVector2D<T> & 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<T> & 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<T> & addColumn(const PIVector<T> & 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 filled with `f`.
|
||||
//! If they are smaller, the array is truncated.
|
||||
//! \~russian Если новые размеры больше, новые элементы заполняются `f`.
|
||||
//! Если они меньше, массив обрезается.
|
||||
//! \~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<int> 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<T> & resize(size_t rows, size_t cols, const T & f = T()) {
|
||||
if (rows == rows_ && cols == cols_) return *this;
|
||||
PIVector2D<T> tmp(rows, cols, f);
|
||||
size_t copyRows = piMin(rows_, rows);
|
||||
size_t copyCols = piMin(cols_, cols);
|
||||
for (size_t r = 0; r < copyRows; ++r) {
|
||||
for (size_t c = 0; c < copyCols; ++c) {
|
||||
tmp.element(r, c) = element(r, c);
|
||||
}
|
||||
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);
|
||||
}
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Equality operator.
|
||||
//! \~russian Оператор равенства.
|
||||
//! \~\sa PIVector::operator==
|
||||
inline bool operator==(const PIVector2D<T> & t) const {
|
||||
if (cols_ != t.cols_ || rows_ != t.rows_) return false;
|
||||
return mat == t.mat;
|
||||
@@ -594,6 +993,10 @@ public:
|
||||
|
||||
//! \~english Converts the 2D array to a vector of vectors (PIVector<PIVector<T>>).
|
||||
//! \~russian Преобразует двумерный массив в вектор векторов (PIVector<PIVector<T>>).
|
||||
//! \details
|
||||
//! \~english Each row vector is a copy of the corresponding row.
|
||||
//! \~russian Каждый вектор-строка является копией соответствующей строки.
|
||||
//! \~\sa fromVectors(), PIVector::PIVector(const T*, size_t)
|
||||
inline PIVector<PIVector<T>> toVectors() const {
|
||||
PIVector<PIVector<T>> ret;
|
||||
ret.reserve(rows_);
|
||||
@@ -616,6 +1019,10 @@ public:
|
||||
|
||||
//! \~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<T> & other) {
|
||||
mat.swap(other.mat);
|
||||
piSwap<size_t>(rows_, other.rows_);
|
||||
@@ -633,6 +1040,10 @@ public:
|
||||
|
||||
//! \~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();
|
||||
@@ -641,64 +1052,70 @@ public:
|
||||
|
||||
//! \~english Checks if the underlying flat vector contains the element `e`.
|
||||
//! \~russian Проверяет, содержит ли внутренний плоский вектор элемент `e`.
|
||||
inline bool contains(const T & e, ssize_t start = 0) const { return mat.contains(e, start); }
|
||||
|
||||
//! \~english Checks if the underlying flat vector contains all elements of `v`.
|
||||
//! \~russian Проверяет, содержит ли внутренний плоский вектор все элементы `v`.
|
||||
inline bool contains(const PIVector<T> & v, ssize_t start = 0) const { return mat.contains(v, start); }
|
||||
//! \~\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` во внутреннем плоском векторе.
|
||||
inline int entries(const T & e, ssize_t start = 0) const { return mat.entries(e, start); }
|
||||
//! \~\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`.
|
||||
inline int entries(std::function<bool(const T & e)> test, ssize_t start = 0) const { return mat.entries(test, start); }
|
||||
//! \~\sa PIVector::entries(std::function)
|
||||
inline int entries(std::function<bool(const T & e)> test) const { return mat.entries(test); }
|
||||
|
||||
|
||||
//! \~english Returns the first index (row, col) of `e` in the 2D array.
|
||||
//! \~russian Возвращает первый индекс (строка, столбец) элемента `e` в двумерном массиве.
|
||||
inline PIPair<ssize_t, ssize_t> indexOf(const T & e, ssize_t start = 0) const {
|
||||
ssize_t flat = mat.indexOf(e, start);
|
||||
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||
//! \~\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<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
||||
}
|
||||
|
||||
//! \~english Returns the first index (row, col) in the 2D array that passes the `test`.
|
||||
//! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||||
inline PIPair<ssize_t, ssize_t> indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||
//! \~\sa PIVector::indexWhere()
|
||||
inline Index indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||
ssize_t flat = mat.indexWhere(test, start);
|
||||
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||
if (flat < 0 || cols_ == 0) return Index{-1, -1};
|
||||
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
||||
}
|
||||
|
||||
//! \~english Returns the last index (row, col) of `e` in the 2D array.
|
||||
//! \~russian Возвращает последний индекс (строка, столбец) элемента `e` в двумерном массиве.
|
||||
inline PIPair<ssize_t, ssize_t> lastIndexOf(const T & e, ssize_t start = -1) const {
|
||||
//! \~\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 PIPair<ssize_t, ssize_t>(-1, -1);
|
||||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||
if (flat < 0 || cols_ == 0) return Index{-1, -1};
|
||||
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
||||
}
|
||||
|
||||
//! \~english Returns the last index (row, col) in the 2D array that passes the `test`.
|
||||
//! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||||
inline PIPair<ssize_t, ssize_t> lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||
//! \~\sa PIVector::lastIndexWhere()
|
||||
inline Index lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||
ssize_t flat = mat.lastIndexWhere(test, start);
|
||||
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||||
if (flat < 0 || cols_ == 0) return Index{-1, -1};
|
||||
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
||||
}
|
||||
|
||||
|
||||
//! \~english Tests if any element in the flat vector passes the `test`.
|
||||
//! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`.
|
||||
//! \~\sa PIVector::any()
|
||||
inline bool any(std::function<bool(const T & e)> 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<bool(const T & e)> test) const { return mat.every(test); }
|
||||
|
||||
//! \~english Fills the entire 2D array with copies of `e`.
|
||||
//! \~russian Заполняет весь двумерный массив копиями `e`.
|
||||
//! \~\sa PIVector::fill()
|
||||
inline PIVector2D<T> & fill(const T & e = T()) {
|
||||
mat.fill(e);
|
||||
return *this;
|
||||
@@ -706,6 +1123,7 @@ public:
|
||||
|
||||
//! \~english Fills the entire 2D array using a generator function `f` based on flat index.
|
||||
//! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса.
|
||||
//! \~\sa PIVector::fill(std::function)
|
||||
inline PIVector2D<T> & fill(std::function<T(size_t i)> f) {
|
||||
mat.fill(f);
|
||||
return *this;
|
||||
@@ -717,6 +1135,7 @@ public:
|
||||
|
||||
//! \~english Assigns new size and fills with value.
|
||||
//! \~russian Задаёт новый размер и заполняет значением.
|
||||
//! \~\sa PIVector::assign(size_t, const T&)
|
||||
inline PIVector2D<T> & assign(size_t rows, size_t cols, const T & f = T()) {
|
||||
mat.assign(rows * cols, f);
|
||||
rows_ = rows;
|
||||
@@ -725,9 +1144,15 @@ public:
|
||||
}
|
||||
|
||||
|
||||
// TODO: исправить - при транспонировании количество строк становится количеством столбцов и наоборот
|
||||
//! \~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<int> mat(2, 3, ...);
|
||||
//! auto t = mat.transposed(); // now 3x2
|
||||
//! \endcode
|
||||
inline PIVector2D<T> transposed() const {
|
||||
if (isEmpty()) return PIVector2D<T>();
|
||||
PIVector2D<T> result(cols_, rows_);
|
||||
@@ -741,6 +1166,7 @@ public:
|
||||
|
||||
//! \~english Reverses the order of rows in place.
|
||||
//! \~russian Изменяет порядок строк на обратный на месте.
|
||||
//! \~\sa reverseColumns(), PIVector::reverse()
|
||||
inline PIVector2D<T> & reverseRows() {
|
||||
const size_t half = rows_ / 2;
|
||||
for (size_t i = 0; i < half; ++i) {
|
||||
@@ -755,6 +1181,7 @@ public:
|
||||
|
||||
//! \~english Reverses the order of columns in each row in place.
|
||||
//! \~russian Изменяет порядок столбцов в каждой строке на обратный на месте.
|
||||
//! \~\sa reverseRows(), PIVector::reverse()
|
||||
inline PIVector2D<T> & reverseColumns() {
|
||||
for (size_t r = 0; r < rows_; ++r) {
|
||||
Row currentRow = row(r);
|
||||
@@ -768,10 +1195,14 @@ public:
|
||||
|
||||
//! \~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<T> 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<T>();
|
||||
size_t actualRowCount = piMin(rowCount, rows_ - rowStart);
|
||||
size_t actualColCount = piMin(colCount, cols_ - colStart);
|
||||
const size_t actualRowCount = piMin<size_t>(rowCount, rows_ - rowStart);
|
||||
const size_t actualColCount = piMin<size_t>(colCount, cols_ - colStart);
|
||||
|
||||
PIVector2D<T> result(actualRowCount, actualColCount);
|
||||
for (size_t r = 0; r < actualRowCount; ++r) {
|
||||
@@ -784,6 +1215,10 @@ public:
|
||||
|
||||
//! \~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<typename ST>
|
||||
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const {
|
||||
return PIVector2D<ST>(rows_, cols_, mat.template map<ST>(f));
|
||||
@@ -791,6 +1226,7 @@ public:
|
||||
|
||||
//! \~english Applies a function (with row and col indices) to each element and returns a new 2D array.
|
||||
//! \~russian Применяет функцию (с индексами строки и столбца) к каждому элементу и возвращает новый двумерный массив.
|
||||
//! \~\sa PIVector::mapIndexed()
|
||||
template<typename ST>
|
||||
inline PIVector2D<ST> mapIndexed(std::function<ST(size_t row, size_t col, const T & e)> f) const {
|
||||
PIVector<ST> mappedMat;
|
||||
@@ -803,20 +1239,22 @@ public:
|
||||
return PIVector2D<ST>(rows_, cols_, std::move(mappedMat));
|
||||
}
|
||||
|
||||
// --- Итерация по строкам и столбцам ---
|
||||
//! \~english Applies a function to each row (modifiable).
|
||||
//! \~russian Применяет функцию к каждой строке (с возможностью изменения).
|
||||
//! \~\sa forEachRow() const, PIVector::forEach()
|
||||
inline PIVector2D<T> & forEachRow(std::function<void(Row)> 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<void(RowConst)> f) const {
|
||||
for (size_t r = 0; r < rows_; ++r)
|
||||
f(row(r));
|
||||
}
|
||||
|
||||
//! \~english Applies a function to each column (modifiable).
|
||||
//! \~russian Применяет функцию к каждому столбцу (с возможностью изменения).
|
||||
inline PIVector2D<T> & forEachColumn(std::function<void(Col)> f) {
|
||||
@@ -824,8 +1262,10 @@ public:
|
||||
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<void(ColConst)> f) const {
|
||||
for (size_t c = 0; c < cols_; ++c)
|
||||
f(col(c));
|
||||
@@ -833,6 +1273,7 @@ public:
|
||||
|
||||
//! \~english Accumulates a value across all elements.
|
||||
//! \~russian Аккумулирует значение по всем элементам.
|
||||
//! \~\sa PIVector::reduce()
|
||||
template<typename ST>
|
||||
inline ST reduce(std::function<ST(const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||||
return mat.template reduce<ST>(f, initial);
|
||||
@@ -840,6 +1281,7 @@ public:
|
||||
|
||||
//! \~english Accumulates a value across all elements with indices.
|
||||
//! \~russian Аккумулирует значение по всем элементам с индексами.
|
||||
//! \~\sa PIVector::reduceIndexed()
|
||||
template<typename ST>
|
||||
inline ST reduceIndexed(std::function<ST(size_t row, size_t col, const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||||
ST ret(initial);
|
||||
@@ -853,52 +1295,45 @@ public:
|
||||
|
||||
//! \~english Removes a row from the 2D array.
|
||||
//! \~russian Удаляет строку из двумерного массива.
|
||||
inline PIVector2D<T> & removeRow(size_t row) {
|
||||
if (row >= rows_) return *this;
|
||||
size_t startIdx = row * cols_;
|
||||
mat.remove(startIdx, cols_);
|
||||
rows_--;
|
||||
if (rows_ == 0) cols_ = 0;
|
||||
return *this;
|
||||
}
|
||||
//! \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<T> & removeRow(size_t row) { return deleteRows(row, 1); }
|
||||
|
||||
//! \~english Removes a column from the 2D array.
|
||||
//! \~russian Удаляет столбец из двумерного массива.
|
||||
inline PIVector2D<T> & removeColumn(size_t col) {
|
||||
if (col >= cols_ || rows_ == 0) return *this;
|
||||
PIVector2D<T> result(rows_, cols_ - 1);
|
||||
for (size_t r = 0; r < rows_; ++r) {
|
||||
for (size_t c = 0, nc = 0; c < cols_; ++c) {
|
||||
if (c == col) continue;
|
||||
result.element(r, nc++) = element(r, c);
|
||||
}
|
||||
}
|
||||
swap(result);
|
||||
return *this;
|
||||
}
|
||||
//! \details
|
||||
//! \~english This operation is more expensive than removing a row because elements must be moved.
|
||||
//! \~russian Эта операция дороже, чем удаление строки, поскольку требуется перемещение элементов.
|
||||
//! \~\sa removeRow(), PIVector::remove()
|
||||
inline PIVector2D<T> & 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<T> & removeRowsWhere(std::function<bool(const RowConst &)> test) {
|
||||
ssize_t r = rows_ - 1;
|
||||
while (r >= 0) {
|
||||
ssize_t r = rows_;
|
||||
while (--r >= 0) {
|
||||
if (test(RowConst(this, r))) {
|
||||
removeRow(r);
|
||||
}
|
||||
--r;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Removes all columns that satisfy a condition.
|
||||
//! \~russian Удаляет все столбцы, удовлетворяющие условию.
|
||||
//! \~\sa removeRowsWhere()
|
||||
inline PIVector2D<T> & removeColumnsWhere(std::function<bool(const ColConst &)> test) {
|
||||
ssize_t c = cols_ - 1;
|
||||
while (c >= 0) {
|
||||
ssize_t c = cols_;
|
||||
while (--c >= 0) {
|
||||
if (test(ColConst(this, c))) {
|
||||
removeColumn(c);
|
||||
}
|
||||
--c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -906,6 +1341,7 @@ public:
|
||||
|
||||
//! \~english Returns a new 2D array containing only the rows that pass the test.
|
||||
//! \~russian Возвращает новый двумерный массив, содержащий только строки, прошедшие проверку.
|
||||
//! \~\sa filterColumns(), PIVector::filter()
|
||||
inline PIVector2D<T> filterRows(std::function<bool(const RowConst &)> test) const {
|
||||
PIVector2D<T> result;
|
||||
for (size_t r = 0; r < rows_; ++r) {
|
||||
@@ -919,6 +1355,7 @@ public:
|
||||
|
||||
//! \~english Returns a new 2D array containing only the columns that pass the test.
|
||||
//! \~russian Возвращает новый двумерный массив, содержащий только столбцы, прошедшие проверку.
|
||||
//! \~\sa filterRows()
|
||||
inline PIVector2D<T> filterColumns(std::function<bool(const ColConst &)> test) const {
|
||||
if (isEmpty()) return PIVector2D<T>();
|
||||
PIVector<size_t> goodCols;
|
||||
@@ -927,23 +1364,13 @@ public:
|
||||
goodCols << c;
|
||||
}
|
||||
}
|
||||
PIVector2D<T> result(rows_, goodCols.size());
|
||||
for (size_t r = 0; r < rows_; ++r) {
|
||||
for (size_t gc = 0; gc < goodCols.size(); ++gc) {
|
||||
result.element(r, gc) = element(r, goodCols[gc]);
|
||||
}
|
||||
PIVector2D<T> result;
|
||||
for (size_t gc = 0; gc < goodCols.size(); ++gc) {
|
||||
result.addColumn(col(goodCols[gc]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! \~english Returns a new 2D array (as a single row) containing only the elements that pass the test.
|
||||
//! \~russian Возвращает новый двумерный массив (в виде одной строки), содержащий только элементы, прошедшие проверку.
|
||||
inline PIVector2D<T> filterElements(std::function<bool(const T &)> test) const {
|
||||
PIVector<T> filtered = mat.filter(test);
|
||||
if (filtered.isEmpty()) return PIVector2D<T>();
|
||||
return PIVector2D<T>(1, filtered.size(), filtered);
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t rows_, cols_;
|
||||
PIVector<T> mat;
|
||||
|
||||
@@ -342,12 +342,12 @@ PIVector<PIHIDeviceInfo> PIHIDevice::allDevices(bool try_open) {
|
||||
|
||||
PIDir hid_dir("/sys/bus/hid/devices"_a);
|
||||
auto hid_devs = hid_dir.entries();
|
||||
for (auto hd: hid_devs) {
|
||||
for (const auto & hd: hid_devs) {
|
||||
// piCout << d.path;
|
||||
if (!isDir(hd)) continue;
|
||||
PIDir dir_input(hd.path + "/input"_a);
|
||||
auto hid_inputs = dir_input.entries();
|
||||
for (auto hd_i: hid_inputs) {
|
||||
for (const auto & hd_i: hid_inputs) {
|
||||
if (!isDir(hd_i)) continue;
|
||||
// now in /sys/bus/hid/devices/<dev>/input/input<N>
|
||||
// piCout << hd_i.path;
|
||||
@@ -364,7 +364,7 @@ PIVector<PIHIDeviceInfo> PIHIDevice::allDevices(bool try_open) {
|
||||
PIDir dir_e(hd_i.path);
|
||||
PIStringList devs;
|
||||
auto dl_e = dir_e.entries();
|
||||
for (auto d_e: dl_e) {
|
||||
for (const auto & d_e: dl_e) {
|
||||
if (!d_e.isDir() || d_e.flags[PIFile::FileInfo::Dot] || d_e.flags[PIFile::FileInfo::DotDot]) continue;
|
||||
devs << d_e.name();
|
||||
}
|
||||
@@ -392,7 +392,7 @@ PIVector<PIHIDeviceInfo> PIHIDevice::allDevices(bool try_open) {
|
||||
if (test_f.isClosed()) continue;
|
||||
}
|
||||
|
||||
ullong ev = readFile(hd_i.path + "/capabilities/ev"_a).toULLong(16);
|
||||
// ullong ev = readFile(hd_i.path + "/capabilities/ev"_a).toULLong(16);
|
||||
|
||||
auto readAxes = [readFile, checkBit, &hd_i, &dev](const PIString & file, bool is_relative) {
|
||||
PIVector<PIHIDeviceInfo::AxisInfo> ret;
|
||||
|
||||
@@ -379,6 +379,419 @@ 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 ====================
|
||||
|
||||
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:
|
||||
@@ -470,17 +883,15 @@ TEST_F(Vector2DTest, contains_finds_element_in_flat_vector) {
|
||||
TEST_F(Vector2DTest, contains_with_start_parameter_works) {
|
||||
int target = 10 * COLS_COUNT_INIT + 15;
|
||||
EXPECT_TRUE(vec.contains(target));
|
||||
EXPECT_TRUE(vec.contains(target, target)); // start exactly at target (inclusive)
|
||||
EXPECT_FALSE(vec.contains(target, target + 1)); // start after target
|
||||
}
|
||||
|
||||
TEST_F(Vector2DTest, contains_vector_of_elements_works) {
|
||||
PIVector<int> searchFor;
|
||||
searchFor << 100 << 200 << 300;
|
||||
EXPECT_TRUE(vec.contains(searchFor));
|
||||
EXPECT_TRUE(vec.asPlainVector().containsAll(searchFor));
|
||||
|
||||
searchFor << -999;
|
||||
EXPECT_FALSE(vec.contains(searchFor));
|
||||
EXPECT_FALSE(vec.asPlainVector().containsAll(searchFor));
|
||||
}
|
||||
|
||||
TEST_F(Vector2DTest, entries_counts_occurrences) {
|
||||
@@ -652,6 +1063,53 @@ TEST_F(Vector2DTest, transposed_returns_correct_dimensions) {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -767,36 +1225,36 @@ TEST_F(Vector2DTest, forEach_modifying_changes_elements) {
|
||||
|
||||
TEST_F(Vector2DTest, indexOf_returns_correct_pair) {
|
||||
auto p = vec.indexOf(vec.element(10, 15));
|
||||
EXPECT_EQ(p.first, 10);
|
||||
EXPECT_EQ(p.second, 15);
|
||||
EXPECT_EQ(p.row, 10);
|
||||
EXPECT_EQ(p.col, 15);
|
||||
p = vec.indexOf(-999);
|
||||
EXPECT_EQ(p.first, -1);
|
||||
EXPECT_EQ(p.second, -1);
|
||||
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.first, 5);
|
||||
EXPECT_EQ(p.second, 5);
|
||||
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.first, 20);
|
||||
EXPECT_EQ(p.second, 20);
|
||||
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.first, 0);
|
||||
EXPECT_GE(p.second, 0);
|
||||
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.first * vec.cols() + p.second;
|
||||
size_t lastFlat = p.row * vec.cols() + p.col;
|
||||
size_t expectedLastFlat = vec.size() - 1;
|
||||
EXPECT_EQ(lastFlat, expectedLastFlat);
|
||||
}
|
||||
@@ -993,6 +1451,242 @@ TEST(Vector2DEdgeTest, single_element_vector) {
|
||||
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
|
||||
@@ -1010,3 +1704,94 @@ TEST_F(Vector2DTest, iostream_operator_works) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user