22template <
typename Row>
23inline void erase_row(std::vector<Row>& data,
int idx) {
24 data.erase(data.begin() + idx);
27template <
typename Row>
28inline void erase_col(std::vector<Row>& data,
int idx) {
30 std::vector<Row> next;
31 next.reserve(data.size());
32 for (
const auto& r : data) {
34 row.erase(row.begin() + idx);
35 next.push_back(std::move(row));
40template <
typename Row>
42 std::swap(data[i], data[j]);
45template <
typename Row>
48 std::vector<Row> next = data;
49 for (
auto& r : next) std::swap(r[i], r[j]);
53template <
typename Row>
55 int from_row,
int to_row,
56 int from_col,
int to_col) {
58 out.reserve(
static_cast<size_t>(to_row - from_row));
59 for (
int r = from_row; r < to_row; ++r) {
60 Row row(data[r].begin() + from_col, data[r].begin() + to_col);
61 out.push_back(std::move(row));
66template <
typename Row>
69 const typename Row::value_type& zero) {
70 std::vector<Row> out(
static_cast<size_t>(c),
71 Row(
static_cast<size_t>(r), zero));
72 for (
int i = 0; i < r; ++i)
73 for (
int j = 0; j < c; ++j)
74 out[j][i] = data[i][j];
78template <
typename Row>
79inline void concat_impl(std::vector<Row>& m,
const std::vector<Row>& other,
82 for (
size_t r = 0; r < m.size(); ++r)
83 m[r].insert(m[r].end(), other[r].begin(), other[r].end());
85 for (
const auto& row : other) m.push_back(row);
89template <
typename Row>
90inline void reshape_impl(std::vector<Row>& data,
int new_rows,
int new_cols,
91 const typename Row::value_type& zero) {
92 if (new_rows < 0 || new_cols < 0)
93 throw std::runtime_error(
"GenericMatrix::reshape: negative dimension");
94 int64_t total =
static_cast<int64_t
>(new_rows) *
static_cast<int64_t
>(new_cols);
95 if (total >
static_cast<int64_t
>(std::numeric_limits<int>::max()))
96 throw std::runtime_error(
"matrix.reshape: dimension overflow");
97 std::vector<typename Row::value_type> flat;
98 flat.reserve(
static_cast<size_t>(total));
99 for (
const auto& r : data)
for (
const auto& v : r) flat.push_back(v);
100 flat.resize(
static_cast<size_t>(total), zero);
101 std::vector<Row> next(
static_cast<size_t>(new_rows),
102 Row(
static_cast<size_t>(new_cols), zero));
104 for (
int r = 0; r < new_rows; ++r)
105 for (
int c = 0; c < new_cols; ++c)
106 next[r][c] = flat[k++];
110template <
typename Row>
113 for (
const auto& r : data) total +=
static_cast<int64_t
>(r.size());
114 if (total >
static_cast<int64_t
>(std::numeric_limits<int>::max()))
115 throw std::overflow_error(
"matrix.elements_count: total exceeds int range");
116 return static_cast<int>(total);
119template <
typename Row>
120inline void sort_impl(std::vector<Row>& data,
int column,
bool ascending) {
121 std::sort(data.begin(), data.end(),
122 [column, ascending](
const Row& a,
const Row& b) {
123 return ascending ? (a[column] < b[column]) : (b[column] < a[column]);
131 using Data = std::vector<std::vector<T>>;
137 explicit Storage(Data value) : data(std::move(value)) {}
140 static constexpr const char* kNaIdError =
141 "matrix operation on na ID";
142 static constexpr const char* kInvalidSnapshotError =
143 "matrix restore from invalid snapshot";
145 std::shared_ptr<Storage> storage_;
148 : storage_(std::make_shared<Storage>(std::move(data))) {}
150 Storage& require_storage() {
151 if (!storage_)
throw std::runtime_error(kNaIdError);
155 const Storage& require_storage()
const {
156 if (!storage_)
throw std::runtime_error(kNaIdError);
160 Data& data() {
return require_storage().data; }
161 const Data& data()
const {
return require_storage().data; }
168 GenericMatrix(const GenericMatrix&) noexcept = default;
169 GenericMatrix& operator=(const GenericMatrix&) noexcept = default;
172 GenericMatrix(GenericMatrix&& other) noexcept
173 : storage_(other.storage_) {}
174 GenericMatrix&
operator=(GenericMatrix&& other)
noexcept {
175 if (
this != &other) storage_ = other.storage_;
183 std::shared_ptr<Storage> identity_;
186 Snapshot(std::shared_ptr<Storage> identity,
const Data& state)
187 : identity_(std::move(identity)), state_(state) {}
200 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols, T init) {
201 if (
rows < 0 || cols < 0)
202 throw std::invalid_argument(
"matrix.new: negative dimensions");
203 Data data(
static_cast<size_t>(
rows),
204 std::vector<T>(
static_cast<size_t>(cols), init));
208 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols) {
209 static_assert(std::is_default_constructible_v<T>,
210 "matrix.new: no-init overload requires default-constructible T");
211 if (
rows < 0 || cols < 0)
212 throw std::invalid_argument(
"matrix.new: negative dimensions");
213 Data data(
static_cast<size_t>(
rows),
214 std::vector<T>(
static_cast<size_t>(cols), T{}));
219 const Data& values = data();
221 throw std::out_of_range(
"matrix.get: row index out of range");
223 throw std::out_of_range(
"matrix.get: column index out of range");
224 return values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)];
228 Data& values = data();
230 throw std::out_of_range(
"matrix.set: row index out of range");
232 throw std::out_of_range(
"matrix.set: column index out of range");
233 values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)] = val;
237 for (
auto& r : data()) std::fill(r.begin(), r.end(), val);
241 return static_cast<int>(data().size());
244 const Data& values = data();
245 return values.empty() ? 0 :
static_cast<int>(values[0].size());
248 std::vector<T>
row(
int idx)
const {
249 const Data& values = data();
250 if (idx < 0 || idx >=
rows())
251 throw std::out_of_range(
"matrix.row: row index out of range");
252 return values[
static_cast<size_t>(idx)];
255 std::vector<T>
col(
int idx)
const {
256 const Data& values = data();
257 if (idx < 0 || idx >=
columns())
258 throw std::out_of_range(
"matrix.col: column index out of range");
260 out.reserve(values.size());
261 for (
const auto& r : values) out.push_back(r[
static_cast<size_t>(idx)]);
265 template <
typename U = T,
266 typename = std::enable_if_t<!std::is_same_v<U, bool>>>
267 const std::vector<T>&
row_ref(
int idx)
const {
268 const Data& values = data();
269 if (idx < 0 || idx >=
rows())
270 throw std::out_of_range(
"matrix.row_ref: row index out of range");
271 return values[
static_cast<size_t>(idx)];
274 void add_row(
int idx,
const std::vector<T>& values) {
275 Data& matrix_data = data();
276 if (idx < 0 || idx >
rows())
277 throw std::out_of_range(
"matrix.add_row: row index out of range");
278 if (!matrix_data.empty() && values.size() !=
static_cast<size_t>(
columns()))
279 throw std::runtime_error(
"matrix.add_row: values size must equal columns()");
280 matrix_data.reserve(matrix_data.size() + 1);
281 matrix_data.insert(matrix_data.begin() + idx, values);
284 void add_col(
int idx,
const std::vector<T>& values) {
285 Data& matrix_data = data();
286 if (matrix_data.empty())
287 throw std::logic_error(
"matrix.add_col on empty matrix: use add_row first");
288 if (idx < 0 || idx >
columns())
289 throw std::out_of_range(
"matrix.add_col: column index out of range");
290 if (values.size() != matrix_data.size())
291 throw std::runtime_error(
"matrix.add_col: values size must equal rows()");
294 next.reserve(matrix_data.size());
295 for (
size_t r = 0; r < matrix_data.size(); ++r) {
296 std::vector<T>
row = matrix_data[r];
297 row.insert(
row.begin() + idx, values[r]);
298 next.push_back(std::move(
row));
300 matrix_data.swap(next);
305 if (idx < 0 || idx >=
rows())
306 throw std::out_of_range(
"matrix.remove_row: row index out of range");
312 if (idx < 0 || idx >=
columns())
313 throw std::out_of_range(
"matrix.remove_col: column index out of range");
319 if (i < 0 || i >=
rows() || j < 0 || j >=
rows())
320 throw std::out_of_range(
"matrix.swap_rows: row index out of range");
327 throw std::out_of_range(
"matrix.swap_columns: column index out of range");
331 [[nodiscard]] GenericMatrix
copy()
const {
335 [[nodiscard]] GenericMatrix
submatrix(
int from_row,
int to_row,
336 int from_col,
int to_col)
const {
338 if (from_row < 0 || to_row >
rows())
339 throw std::out_of_range(
"matrix.submatrix: row index out of range");
340 if (from_col < 0 || to_col >
columns())
341 throw std::out_of_range(
"matrix.submatrix: column index out of range");
342 if (from_row > to_row)
343 throw std::invalid_argument(
"matrix.submatrix: from_row must be <= to_row");
344 if (from_col > to_col)
345 throw std::invalid_argument(
"matrix.submatrix: from_col must be <= to_col");
352 static_assert(std::is_default_constructible_v<T>,
353 "matrix.transpose: requires default-constructible element type");
358 [[nodiscard]] GenericMatrix
concat(
const GenericMatrix& other,
bool horizontal)
const {
363 throw std::invalid_argument(
"matrix.concat: row count mismatch");
366 throw std::invalid_argument(
"matrix.concat: column count mismatch");
368 GenericMatrix m =
copy();
374 static_assert(std::is_default_constructible_v<T>,
375 "matrix.reshape: requires default-constructible element type");
380 Data& values = data();
381 std::reverse(values.begin(), values.end());
384 void sort(
int column,
bool ascending =
true) {
385 static_assert(std::is_same_v<T, int> ||
386 std::is_same_v<T, bool> ||
387 std::is_same_v<T, std::string>,
388 "matrix.sort: requires int, bool, or std::string element type");
396 [[nodiscard]]
bool is_na() const noexcept {
return !storage_; }
399 const Storage& storage = require_storage();
400 return Snapshot(storage_, storage.data);
405 throw std::runtime_error(kInvalidSnapshotError);
408 snapshot.identity_->data.swap(replacement);
419class GenericMatrix<bool> {
420 using Data = std::vector<std::vector<char>>;
426 explicit Storage(Data value) : data(std::move(value)) {}
429 static constexpr const char* kNaIdError =
430 "matrix operation on na ID";
431 static constexpr const char* kInvalidSnapshotError =
432 "matrix restore from invalid snapshot";
434 std::shared_ptr<Storage> storage_;
437 : storage_(std::make_shared<Storage>(std::move(data))) {}
439 Storage& require_storage() {
440 if (!storage_)
throw std::runtime_error(kNaIdError);
444 const Storage& require_storage()
const {
445 if (!storage_)
throw std::runtime_error(kNaIdError);
449 Data& data() {
return require_storage().data; }
450 const Data& data()
const {
return require_storage().data; }
454 GenericMatrix(const GenericMatrix&) noexcept = default;
455 GenericMatrix& operator=(const GenericMatrix&) noexcept = default;
457 GenericMatrix(GenericMatrix&& other) noexcept
458 : storage_(other.storage_) {}
459 GenericMatrix&
operator=(GenericMatrix&& other)
noexcept {
460 if (
this != &other) storage_ = other.storage_;
465 std::shared_ptr<Storage> identity_;
468 Snapshot(std::shared_ptr<Storage> identity,
const Data& state)
469 : identity_(std::move(identity)), state_(state) {}
480 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols,
bool init) {
481 if (
rows < 0 || cols < 0)
482 throw std::invalid_argument(
"matrix.new: negative dimensions");
483 Data data(
static_cast<size_t>(
rows),
484 std::vector<char>(
static_cast<size_t>(cols), init ? 1 : 0));
488 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols) {
493 const Data& values = data();
495 throw std::out_of_range(
"matrix.get: row index out of range");
497 throw std::out_of_range(
"matrix.get: column index out of range");
498 return values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)] != 0;
502 Data& values = data();
504 throw std::out_of_range(
"matrix.set: row index out of range");
506 throw std::out_of_range(
"matrix.set: column index out of range");
507 values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)] = val ? 1 : 0;
511 char c = val ? 1 : 0;
512 for (
auto& r : data()) std::fill(r.begin(), r.end(), c);
516 return static_cast<int>(data().size());
519 const Data& values = data();
520 return values.empty() ? 0 :
static_cast<int>(values[0].size());
523 std::vector<bool>
row(
int idx)
const {
524 const Data& values = data();
525 if (idx < 0 || idx >=
rows())
526 throw std::out_of_range(
"matrix.row: row index out of range");
527 std::vector<bool> out;
528 const auto& src = values[
static_cast<size_t>(idx)];
529 out.reserve(src.size());
530 for (
char c : src) out.push_back(c != 0);
534 std::vector<bool>
col(
int idx)
const {
535 const Data& values = data();
536 if (idx < 0 || idx >=
columns())
537 throw std::out_of_range(
"matrix.col: column index out of range");
538 std::vector<bool> out;
539 out.reserve(values.size());
540 for (
const auto& r : values) out.push_back(r[
static_cast<size_t>(idx)] != 0);
547 void add_row(
int idx,
const std::vector<bool>& values) {
548 Data& matrix_data = data();
549 if (idx < 0 || idx >
rows())
550 throw std::out_of_range(
"matrix.add_row: row index out of range");
551 if (!matrix_data.empty() && values.size() !=
static_cast<size_t>(
columns()))
552 throw std::runtime_error(
"matrix.add_row: values size must equal columns()");
553 std::vector<char>
row;
554 row.reserve(values.size());
555 for (
bool v : values)
row.push_back(v ? 1 : 0);
556 matrix_data.reserve(matrix_data.size() + 1);
557 matrix_data.insert(matrix_data.begin() + idx, std::move(
row));
560 void add_col(
int idx,
const std::vector<bool>& values) {
561 Data& matrix_data = data();
562 if (matrix_data.empty())
563 throw std::logic_error(
"matrix.add_col on empty matrix: use add_row first");
564 if (idx < 0 || idx >
columns())
565 throw std::out_of_range(
"matrix.add_col: column index out of range");
566 if (values.size() != matrix_data.size())
567 throw std::runtime_error(
"matrix.add_col: values size must equal rows()");
569 next.reserve(matrix_data.size());
570 for (
size_t r = 0; r < matrix_data.size(); ++r) {
571 std::vector<char>
row = matrix_data[r];
572 row.insert(
row.begin() + idx, values[r] ? 1 : 0);
573 next.push_back(std::move(
row));
575 matrix_data.swap(next);
580 if (idx < 0 || idx >=
rows())
581 throw std::out_of_range(
"matrix.remove_row: row index out of range");
587 if (idx < 0 || idx >=
columns())
588 throw std::out_of_range(
"matrix.remove_col: column index out of range");
594 if (i < 0 || i >=
rows() || j < 0 || j >=
rows())
595 throw std::out_of_range(
"matrix.swap_rows: row index out of range");
602 throw std::out_of_range(
"matrix.swap_columns: column index out of range");
606 [[nodiscard]] GenericMatrix
copy()
const {
610 [[nodiscard]] GenericMatrix
submatrix(
int from_row,
int to_row,
611 int from_col,
int to_col)
const {
613 if (from_row < 0 || to_row >
rows())
614 throw std::out_of_range(
"matrix.submatrix: row index out of range");
615 if (from_col < 0 || to_col >
columns())
616 throw std::out_of_range(
"matrix.submatrix: column index out of range");
617 if (from_row > to_row)
618 throw std::invalid_argument(
"matrix.submatrix: from_row must be <= to_row");
619 if (from_col > to_col)
620 throw std::invalid_argument(
"matrix.submatrix: from_col must be <= to_col");
631 Data& values = data();
632 std::reverse(values.begin(), values.end());
638 static_cast<char>(0)));
641 [[nodiscard]] GenericMatrix
concat(
const GenericMatrix& other,
bool horizontal)
const {
646 throw std::invalid_argument(
"matrix.concat: row count mismatch");
649 throw std::invalid_argument(
"matrix.concat: column count mismatch");
651 GenericMatrix m =
copy();
656 template <
typename Dummy =
void>
659 static_assert(!std::is_same_v<Dummy, void> && std::is_same_v<Dummy, void>,
660 "matrix.sort: not supported on bool element type");
667 [[nodiscard]]
bool is_na() const noexcept {
return !storage_; }
670 const Storage& storage = require_storage();
671 return Snapshot(storage_, storage.data);
676 throw std::runtime_error(kInvalidSnapshotError);
679 snapshot.identity_->data.swap(replacement);
686 return matrix.is_na();
friend class GenericMatrix
Snapshot(Snapshot &&)=default
Snapshot & operator=(const Snapshot &)=default
Snapshot(const Snapshot &)=default
Snapshot & operator=(Snapshot &&)=default
Snapshot & operator=(const Snapshot &)=default
Snapshot(Snapshot &&)=default
Snapshot & operator=(Snapshot &&)=default
friend class GenericMatrix
Snapshot(const Snapshot &)=default
int elements_count() const
void restore(const Snapshot &snapshot)
void set(int row, int col, bool val)
GenericMatrix copy() const
bool is_na() const noexcept
static GenericMatrix new_(int rows, int cols)
std::vector< bool > row(int idx) const
void add_col(int idx, const std::vector< bool > &values)
std::vector< bool > col(int idx) const
Snapshot snapshot() const
void add_row(int idx, const std::vector< bool > &values)
bool get(int row, int col) const
GenericMatrix() noexcept=default
void reshape(int new_rows, int new_cols)
GenericMatrix submatrix(int from_row, int to_row, int from_col, int to_col) const
GenericMatrix transpose() const
void swap_rows(int i, int j)
GenericMatrix & operator=(GenericMatrix &&other) noexcept
static GenericMatrix new_(int rows, int cols, bool init)
void sort(int, bool=true)
void swap_columns(int i, int j)
GenericMatrix concat(const GenericMatrix &other, bool horizontal) const
bool is_na() const noexcept
GenericMatrix submatrix(int from_row, int to_row, int from_col, int to_col) const
int elements_count() const
GenericMatrix & operator=(GenericMatrix &&other) noexcept
void swap_rows(int i, int j)
void sort(int column, bool ascending=true)
void add_row(int idx, const std::vector< T > &values)
GenericMatrix() noexcept=default
GenericMatrix copy() const
std::vector< T > row(int idx) const
static GenericMatrix new_(int rows, int cols)
void restore(const Snapshot &snapshot)
GenericMatrix concat(const GenericMatrix &other, bool horizontal) const
void reshape(int new_rows, int new_cols)
Snapshot snapshot() const
void set(int row, int col, T val)
void add_col(int idx, const std::vector< T > &values)
static GenericMatrix new_(int rows, int cols, T init)
const std::vector< T > & row_ref(int idx) const
GenericMatrix transpose() const
T get(int row, int col) const
void swap_columns(int i, int j)
std::vector< T > col(int idx) const
void erase_col(std::vector< Row > &data, int idx)
void swap_cols_impl(std::vector< Row > &data, int i, int j)
int elements_count_impl(const std::vector< Row > &data)
void sort_impl(std::vector< Row > &data, int column, bool ascending)
void swap_rows_impl(std::vector< Row > &data, int i, int j)
void erase_row(std::vector< Row > &data, int idx)
void reshape_impl(std::vector< Row > &data, int new_rows, int new_cols, const typename Row::value_type &zero)
std::vector< Row > transpose_impl(const std::vector< Row > &data, int r, int c, const typename Row::value_type &zero)
void concat_impl(std::vector< Row > &m, const std::vector< Row > &other, bool horizontal)
std::vector< Row > copy_submatrix(const std::vector< Row > &data, int from_row, int to_row, int from_col, int to_col)
GenericMatrix< T > PineGenericMatrix
bool is_na(const Line &h)