25 {{
"function", function}, {
"reason", reason}}, what);
32 {{
"limit",
"matrix_elements"},
33 {
"max", std::numeric_limits<int>::max()}}, what);
37[[noreturn, gnu::noinline, gnu::cold]]
inline void matrix_na_error(
const char* what) {
39 {{
"object",
"matrix"}}, what);
53template <
typename Row>
58template <
typename Row>
61 std::vector<Row> next;
62 next.reserve(
data.size());
63 for (
const auto& r :
data) {
65 row.erase(row.begin() + idx);
66 next.push_back(std::move(row));
71template <
typename Row>
76template <
typename Row>
79 std::vector<Row> next =
data;
80 for (
auto& r : next) std::swap(r[i], r[j]);
84template <
typename Row>
86 int from_row,
int to_row,
87 int from_col,
int to_col) {
89 out.reserve(
static_cast<size_t>(to_row - from_row));
90 for (
int r = from_row; r < to_row; ++r) {
91 Row row(
data[r].begin() + from_col,
data[r].begin() + to_col);
92 out.push_back(std::move(row));
97template <
typename Row>
100 const typename Row::value_type& zero) {
101 std::vector<Row> out(
static_cast<size_t>(c),
102 Row(
static_cast<size_t>(r), zero));
103 for (
int i = 0; i < r; ++i)
104 for (
int j = 0; j < c; ++j)
105 out[j][i] =
data[i][j];
109template <
typename Row>
110inline void concat_impl(std::vector<Row>& m,
const std::vector<Row>& other,
113 for (
size_t r = 0; r < m.size(); ++r)
114 m[r].insert(m[r].
end(), other[r].begin(), other[r].
end());
116 for (
const auto& row : other) m.push_back(row);
120template <
typename Row>
122 const typename Row::value_type& zero) {
123 if (new_rows < 0 || new_cols < 0)
125 "matrix.reshape",
"negative_dimensions",
126 "GenericMatrix::reshape: negative dimension");
127 int64_t total =
static_cast<int64_t
>(new_rows) *
static_cast<int64_t
>(new_cols);
128 if (total >
static_cast<int64_t
>(std::numeric_limits<int>::max()))
130 std::vector<typename Row::value_type> flat;
131 flat.reserve(
static_cast<size_t>(total));
132 for (
const auto& r :
data)
for (
const auto& v : r) flat.push_back(v);
133 flat.resize(
static_cast<size_t>(total), zero);
134 std::vector<Row> next(
static_cast<size_t>(new_rows),
135 Row(
static_cast<size_t>(new_cols), zero));
137 for (
int r = 0; r < new_rows; ++r)
138 for (
int c = 0; c < new_cols; ++c)
139 next[r][c] = flat[k++];
143template <
typename Row>
146 for (
const auto& r :
data) total +=
static_cast<int64_t
>(r.size());
147 if (total >
static_cast<int64_t
>(std::numeric_limits<int>::max()))
149 "matrix.elements_count: total exceeds int range");
150 return static_cast<int>(total);
153template <
typename Row>
156 [column, ascending](
const Row& a,
const Row& b) {
157 return ascending ? (a[column] < b[column]) : (b[column] < a[column]);
165 using Data = std::vector<std::vector<T>>;
171 explicit Storage(Data value) : data(std::move(value)) {}
174 static constexpr const char* kNaIdError =
175 "matrix operation on na ID";
176 static constexpr const char* kInvalidSnapshotError =
177 "matrix restore from invalid snapshot";
179 std::shared_ptr<Storage> storage_;
182 : storage_(std::make_shared<Storage>(std::move(data))) {}
184 Storage& require_storage() {
189 const Storage& require_storage()
const {
194 Data& data() {
return require_storage().data; }
195 const Data& data()
const {
return require_storage().data; }
202 GenericMatrix(const GenericMatrix&) noexcept = default;
203 GenericMatrix& operator=(const GenericMatrix&) noexcept = default;
206 GenericMatrix(GenericMatrix&& other) noexcept
207 : storage_(other.storage_) {}
208 GenericMatrix&
operator=(GenericMatrix&& other)
noexcept {
209 if (
this != &other) storage_ = other.storage_;
217 std::shared_ptr<Storage> identity_;
220 Snapshot(std::shared_ptr<Storage> identity,
const Data& state)
221 : identity_(std::move(identity)), state_(state) {}
234 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols, T init) {
235 if (
rows < 0 || cols < 0)
237 "matrix.new",
"negative_dimensions",
238 "matrix.new: negative dimensions");
239 Data data(
static_cast<size_t>(
rows),
240 std::vector<T>(
static_cast<size_t>(cols), init));
244 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols) {
245 static_assert(std::is_default_constructible_v<T>,
246 "matrix.new: no-init overload requires default-constructible T");
247 if (
rows < 0 || cols < 0)
249 "matrix.new",
"negative_dimensions",
250 "matrix.new: negative dimensions");
251 Data data(
static_cast<size_t>(
rows),
252 std::vector<T>(
static_cast<size_t>(cols), T{}));
257 const Data& values = data();
260 "matrix.get",
"row_index_out_of_range",
261 "matrix.get: row index out of range");
264 "matrix.get",
"column_index_out_of_range",
265 "matrix.get: column index out of range");
266 return values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)];
270 Data& values = data();
273 "matrix.set",
"row_index_out_of_range",
274 "matrix.set: row index out of range");
277 "matrix.set",
"column_index_out_of_range",
278 "matrix.set: column index out of range");
279 values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)] = val;
283 for (
auto& r : data()) std::fill(r.begin(), r.end(), val);
287 return static_cast<int>(data().size());
290 const Data& values = data();
291 return values.empty() ? 0 :
static_cast<int>(values[0].size());
294 std::vector<T>
row(
int idx)
const {
295 const Data& values = data();
296 if (idx < 0 || idx >=
rows())
298 "matrix.row",
"row_index_out_of_range",
299 "matrix.row: row index out of range");
300 return values[
static_cast<size_t>(idx)];
303 std::vector<T>
col(
int idx)
const {
304 const Data& values = data();
305 if (idx < 0 || idx >=
columns())
307 "matrix.col",
"column_index_out_of_range",
308 "matrix.col: column index out of range");
310 out.reserve(values.size());
311 for (
const auto& r : values) out.push_back(r[
static_cast<size_t>(idx)]);
315 template <
typename U = T,
316 typename = std::enable_if_t<!std::is_same_v<U, bool>>>
317 const std::vector<T>&
row_ref(
int idx)
const {
318 const Data& values = data();
319 if (idx < 0 || idx >=
rows())
321 "matrix.row",
"row_index_out_of_range",
322 "matrix.row_ref: row index out of range");
323 return values[
static_cast<size_t>(idx)];
326 void add_row(
int idx,
const std::vector<T>& values) {
327 Data& matrix_data = data();
328 if (idx < 0 || idx >
rows())
330 "matrix.add_row",
"row_index_out_of_range",
331 "matrix.add_row: row index out of range");
332 if (!matrix_data.empty() && values.size() !=
static_cast<size_t>(
columns()))
334 "matrix.add_row",
"values_size_mismatch",
335 "matrix.add_row: values size must equal columns()");
336 matrix_data.reserve(matrix_data.size() + 1);
337 matrix_data.insert(matrix_data.begin() + idx, values);
340 void add_col(
int idx,
const std::vector<T>& values) {
341 Data& matrix_data = data();
342 if (matrix_data.empty())
344 "matrix.add_col",
"empty_matrix",
345 "matrix.add_col on empty matrix: use add_row first");
346 if (idx < 0 || idx >
columns())
348 "matrix.add_col",
"column_index_out_of_range",
349 "matrix.add_col: column index out of range");
350 if (values.size() != matrix_data.size())
352 "matrix.add_col",
"values_size_mismatch",
353 "matrix.add_col: values size must equal rows()");
356 next.reserve(matrix_data.size());
357 for (
size_t r = 0; r < matrix_data.size(); ++r) {
358 std::vector<T>
row = matrix_data[r];
359 row.insert(
row.begin() + idx, values[r]);
360 next.push_back(std::move(
row));
362 matrix_data.swap(next);
367 if (idx < 0 || idx >=
rows())
369 "matrix.remove_row",
"row_index_out_of_range",
370 "matrix.remove_row: row index out of range");
376 if (idx < 0 || idx >=
columns())
378 "matrix.remove_col",
"column_index_out_of_range",
379 "matrix.remove_col: column index out of range");
385 if (i < 0 || i >=
rows() || j < 0 || j >=
rows())
387 "matrix.swap_rows",
"row_index_out_of_range",
388 "matrix.swap_rows: row index out of range");
396 "matrix.swap_columns",
"column_index_out_of_range",
397 "matrix.swap_columns: column index out of range");
401 [[nodiscard]] GenericMatrix
copy()
const {
405 [[nodiscard]] GenericMatrix
submatrix(
int from_row,
int to_row,
406 int from_col,
int to_col)
const {
408 if (from_row < 0 || to_row >
rows())
410 "matrix.submatrix",
"row_index_out_of_range",
411 "matrix.submatrix: row index out of range");
412 if (from_col < 0 || to_col >
columns())
414 "matrix.submatrix",
"column_index_out_of_range",
415 "matrix.submatrix: column index out of range");
416 if (from_row > to_row)
418 "matrix.submatrix",
"from_row_after_to_row",
419 "matrix.submatrix: from_row must be <= to_row");
420 if (from_col > to_col)
422 "matrix.submatrix",
"from_col_after_to_col",
423 "matrix.submatrix: from_col must be <= to_col");
430 static_assert(std::is_default_constructible_v<T>,
431 "matrix.transpose: requires default-constructible element type");
436 [[nodiscard]] GenericMatrix
concat(
const GenericMatrix& other,
bool horizontal)
const {
442 "matrix.concat",
"row_count_mismatch",
443 "matrix.concat: row count mismatch");
447 "matrix.concat",
"column_count_mismatch",
448 "matrix.concat: column count mismatch");
450 GenericMatrix m =
copy();
456 static_assert(std::is_default_constructible_v<T>,
457 "matrix.reshape: requires default-constructible element type");
462 Data& values = data();
463 std::reverse(values.begin(), values.end());
466 void sort(
int column,
bool ascending =
true) {
467 static_assert(std::is_same_v<T, int> ||
468 std::is_same_v<T, bool> ||
469 std::is_same_v<T, std::string>,
470 "matrix.sort: requires int, bool, or std::string element type");
478 [[nodiscard]]
bool is_na() const noexcept {
return !storage_; }
481 const Storage& storage = require_storage();
482 return Snapshot(storage_, storage.data);
490 snapshot.identity_->data.swap(replacement);
501class GenericMatrix<bool> {
502 using Data = std::vector<std::vector<char>>;
508 explicit Storage(Data value) : data(std::move(value)) {}
511 static constexpr const char* kNaIdError =
512 "matrix operation on na ID";
513 static constexpr const char* kInvalidSnapshotError =
514 "matrix restore from invalid snapshot";
516 std::shared_ptr<Storage> storage_;
519 : storage_(std::make_shared<Storage>(std::move(data))) {}
521 Storage& require_storage() {
526 const Storage& require_storage()
const {
531 Data& data() {
return require_storage().data; }
532 const Data& data()
const {
return require_storage().data; }
536 GenericMatrix(const GenericMatrix&) noexcept = default;
537 GenericMatrix& operator=(const GenericMatrix&) noexcept = default;
539 GenericMatrix(GenericMatrix&& other) noexcept
540 : storage_(other.storage_) {}
541 GenericMatrix&
operator=(GenericMatrix&& other)
noexcept {
542 if (
this != &other) storage_ = other.storage_;
547 std::shared_ptr<Storage> identity_;
550 Snapshot(std::shared_ptr<Storage> identity,
const Data& state)
551 : identity_(std::move(identity)), state_(state) {}
562 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols,
bool init) {
563 if (
rows < 0 || cols < 0)
565 "matrix.new",
"negative_dimensions",
566 "matrix.new: negative dimensions");
567 Data data(
static_cast<size_t>(
rows),
568 std::vector<char>(
static_cast<size_t>(cols), init ? 1 : 0));
572 [[nodiscard]]
static GenericMatrix
new_(
int rows,
int cols) {
577 const Data& values = data();
580 "matrix.get",
"row_index_out_of_range",
581 "matrix.get: row index out of range");
584 "matrix.get",
"column_index_out_of_range",
585 "matrix.get: column index out of range");
586 return values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)] != 0;
590 Data& values = data();
593 "matrix.set",
"row_index_out_of_range",
594 "matrix.set: row index out of range");
597 "matrix.set",
"column_index_out_of_range",
598 "matrix.set: column index out of range");
599 values[
static_cast<size_t>(
row)][
static_cast<size_t>(
col)] = val ? 1 : 0;
603 char c = val ? 1 : 0;
604 for (
auto& r : data()) std::fill(r.begin(), r.end(), c);
608 return static_cast<int>(data().size());
611 const Data& values = data();
612 return values.empty() ? 0 :
static_cast<int>(values[0].size());
615 std::vector<bool>
row(
int idx)
const {
616 const Data& values = data();
617 if (idx < 0 || idx >=
rows())
619 "matrix.row",
"row_index_out_of_range",
620 "matrix.row: row index out of range");
621 std::vector<bool> out;
622 const auto& src = values[
static_cast<size_t>(idx)];
623 out.reserve(src.size());
624 for (
char c : src) out.push_back(c != 0);
628 std::vector<bool>
col(
int idx)
const {
629 const Data& values = data();
630 if (idx < 0 || idx >=
columns())
632 "matrix.col",
"column_index_out_of_range",
633 "matrix.col: column index out of range");
634 std::vector<bool> out;
635 out.reserve(values.size());
636 for (
const auto& r : values) out.push_back(r[
static_cast<size_t>(idx)] != 0);
643 void add_row(
int idx,
const std::vector<bool>& values) {
644 Data& matrix_data = data();
645 if (idx < 0 || idx >
rows())
647 "matrix.add_row",
"row_index_out_of_range",
648 "matrix.add_row: row index out of range");
649 if (!matrix_data.empty() && values.size() !=
static_cast<size_t>(
columns()))
651 "matrix.add_row",
"values_size_mismatch",
652 "matrix.add_row: values size must equal columns()");
653 std::vector<char>
row;
654 row.reserve(values.size());
655 for (
bool v : values)
row.push_back(v ? 1 : 0);
656 matrix_data.reserve(matrix_data.size() + 1);
657 matrix_data.insert(matrix_data.begin() + idx, std::move(
row));
660 void add_col(
int idx,
const std::vector<bool>& values) {
661 Data& matrix_data = data();
662 if (matrix_data.empty())
664 "matrix.add_col",
"empty_matrix",
665 "matrix.add_col on empty matrix: use add_row first");
666 if (idx < 0 || idx >
columns())
668 "matrix.add_col",
"column_index_out_of_range",
669 "matrix.add_col: column index out of range");
670 if (values.size() != matrix_data.size())
672 "matrix.add_col",
"values_size_mismatch",
673 "matrix.add_col: values size must equal rows()");
675 next.reserve(matrix_data.size());
676 for (
size_t r = 0; r < matrix_data.size(); ++r) {
677 std::vector<char>
row = matrix_data[r];
678 row.insert(
row.begin() + idx, values[r] ? 1 : 0);
679 next.push_back(std::move(
row));
681 matrix_data.swap(next);
686 if (idx < 0 || idx >=
rows())
688 "matrix.remove_row",
"row_index_out_of_range",
689 "matrix.remove_row: row index out of range");
695 if (idx < 0 || idx >=
columns())
697 "matrix.remove_col",
"column_index_out_of_range",
698 "matrix.remove_col: column index out of range");
704 if (i < 0 || i >=
rows() || j < 0 || j >=
rows())
706 "matrix.swap_rows",
"row_index_out_of_range",
707 "matrix.swap_rows: row index out of range");
715 "matrix.swap_columns",
"column_index_out_of_range",
716 "matrix.swap_columns: column index out of range");
720 [[nodiscard]] GenericMatrix
copy()
const {
724 [[nodiscard]] GenericMatrix
submatrix(
int from_row,
int to_row,
725 int from_col,
int to_col)
const {
727 if (from_row < 0 || to_row >
rows())
729 "matrix.submatrix",
"row_index_out_of_range",
730 "matrix.submatrix: row index out of range");
731 if (from_col < 0 || to_col >
columns())
733 "matrix.submatrix",
"column_index_out_of_range",
734 "matrix.submatrix: column index out of range");
735 if (from_row > to_row)
737 "matrix.submatrix",
"from_row_after_to_row",
738 "matrix.submatrix: from_row must be <= to_row");
739 if (from_col > to_col)
741 "matrix.submatrix",
"from_col_after_to_col",
742 "matrix.submatrix: from_col must be <= to_col");
753 Data& values = data();
754 std::reverse(values.begin(), values.end());
760 static_cast<char>(0)));
763 [[nodiscard]] GenericMatrix
concat(
const GenericMatrix& other,
bool horizontal)
const {
769 "matrix.concat",
"row_count_mismatch",
770 "matrix.concat: row count mismatch");
774 "matrix.concat",
"column_count_mismatch",
775 "matrix.concat: column count mismatch");
777 GenericMatrix m =
copy();
782 template <
typename Dummy =
void>
785 static_assert(!std::is_same_v<Dummy, void> && std::is_same_v<Dummy, void>,
786 "matrix.sort: not supported on bool element type");
793 [[nodiscard]]
bool is_na() const noexcept {
return !storage_; }
796 const Storage& storage = require_storage();
797 return Snapshot(storage_, storage.data);
805 snapshot.identity_->data.swap(replacement);
812 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
Base with a code: throw coded<std::invalid_argument>(code, {args}, text).
void erase_col(std::vector< Row > &data, int idx)
void matrix_snapshot_error(const char *what)
void matrix_limit_error(const char *what)
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 matrix_call_error(const char *function, const char *reason, const char *what)
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)
void matrix_na_error(const char *what)
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)