PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
generic_matrix.hpp
Go to the documentation of this file.
1#pragma once
3#include <vector>
4#include <string>
5#include <type_traits>
6#include <algorithm>
7#include <stdexcept>
8#include <cstdint>
9#include <limits>
10#include <memory>
11#include <utility>
12
13namespace pineforge {
14
15namespace detail {
16
17// A matrix call the script got wrong: the exception type and English it always
18// had, coded pine_matrix_error with the Pine function and the reason. Out of
19// line and cold, so the accessors that check stay as small as they were.
20template <class Base>
21[[noreturn, gnu::noinline, gnu::cold]] void matrix_call_error(const char* function,
22 const char* reason,
23 const char* what) {
25 {{"function", function}, {"reason", reason}}, what);
26}
27
28// The matrix element cap (the int range): pine_runtime_limit.
29template <class Base>
30[[noreturn, gnu::noinline, gnu::cold]] void matrix_limit_error(const char* what) {
32 {{"limit", "matrix_elements"},
33 {"max", std::numeric_limits<int>::max()}}, what);
34}
35
36// A method called on an na matrix ID: pine_na_reference.
37[[noreturn, gnu::noinline, gnu::cold]] inline void matrix_na_error(const char* what) {
39 {{"object", "matrix"}}, what);
40}
41
42// Restoring a checkpoint the generated code never took: an engine invariant.
43[[noreturn, gnu::noinline, gnu::cold]] inline void matrix_snapshot_error(const char* what) {
45}
46
47// Storage-agnostic structural helpers parameterized on the underlying
48// row container type. Used by both GenericMatrix<T> (T != bool;
49// row = std::vector<T>) and GenericMatrix<bool> (row =
50// std::vector<char>) so both share a single implementation for every
51// method that doesn't care about the element type at the boundary.
52
53template <typename Row>
54inline void erase_row(std::vector<Row>& data, int idx) {
55 data.erase(data.begin() + idx);
56}
57
58template <typename Row>
59inline void erase_col(std::vector<Row>& data, int idx) {
60 // Strong guarantee: build a new buffer, swap on success.
61 std::vector<Row> next;
62 next.reserve(data.size());
63 for (const auto& r : data) {
64 Row row = r;
65 row.erase(row.begin() + idx);
66 next.push_back(std::move(row));
67 }
68 data.swap(next);
69}
70
71template <typename Row>
72inline void swap_rows_impl(std::vector<Row>& data, int i, int j) {
73 std::swap(data[i], data[j]);
74}
75
76template <typename Row>
77inline void swap_cols_impl(std::vector<Row>& data, int i, int j) {
78 // Strong guarantee: build a new buffer, swap on success.
79 std::vector<Row> next = data;
80 for (auto& r : next) std::swap(r[i], r[j]);
81 data.swap(next);
82}
83
84template <typename Row>
85inline std::vector<Row> copy_submatrix(const std::vector<Row>& data,
86 int from_row, int to_row,
87 int from_col, int to_col) {
88 std::vector<Row> out;
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));
93 }
94 return out;
95}
96
97template <typename Row>
98inline std::vector<Row> transpose_impl(const std::vector<Row>& data,
99 int r, int c,
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];
106 return out;
107}
108
109template <typename Row>
110inline void concat_impl(std::vector<Row>& m, const std::vector<Row>& other,
111 bool horizontal) {
112 if (horizontal) {
113 for (size_t r = 0; r < m.size(); ++r)
114 m[r].insert(m[r].end(), other[r].begin(), other[r].end());
115 } else {
116 for (const auto& row : other) m.push_back(row);
117 }
118}
119
120template <typename Row>
121inline void reshape_impl(std::vector<Row>& data, int new_rows, int new_cols,
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()))
129 matrix_limit_error<std::runtime_error>("matrix.reshape: dimension overflow");
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));
136 size_t k = 0;
137 for (int r = 0; r < new_rows; ++r)
138 for (int c = 0; c < new_cols; ++c)
139 next[r][c] = flat[k++];
140 data.swap(next);
141}
142
143template <typename Row>
144inline int elements_count_impl(const std::vector<Row>& data) {
145 int64_t total = 0;
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);
151}
152
153template <typename Row>
154inline void sort_impl(std::vector<Row>& data, int column, bool ascending) {
155 std::sort(data.begin(), data.end(),
156 [column, ascending](const Row& a, const Row& b) {
157 return ascending ? (a[column] < b[column]) : (b[column] < a[column]);
158 });
159}
160
161} // namespace detail
162
163template <typename T>
164class GenericMatrix {
165 using Data = std::vector<std::vector<T>>;
166
167 struct Storage {
168 Data data;
169
170 Storage() = default;
171 explicit Storage(Data value) : data(std::move(value)) {}
172 };
173
174 static constexpr const char* kNaIdError =
175 "matrix operation on na ID";
176 static constexpr const char* kInvalidSnapshotError =
177 "matrix restore from invalid snapshot";
178
179 std::shared_ptr<Storage> storage_;
180
181 explicit GenericMatrix(Data data)
182 : storage_(std::make_shared<Storage>(std::move(data))) {}
183
184 Storage& require_storage() {
185 if (!storage_) detail::matrix_na_error(kNaIdError);
186 return *storage_;
187 }
188
189 const Storage& require_storage() const {
190 if (!storage_) detail::matrix_na_error(kNaIdError);
191 return *storage_;
192 }
193
194 Data& data() { return require_storage().data; }
195 const Data& data() const { return require_storage().data; }
196
197public:
198 // A GenericMatrix is a reference handle (a source language's matrix ID).
199 // Copies and assignments alias their backing store; copy() is the
200 // explicit operation that creates an independent handle.
201 GenericMatrix() noexcept = default;
202 GenericMatrix(const GenericMatrix&) noexcept = default;
203 GenericMatrix& operator=(const GenericMatrix&) noexcept = default;
204
205 // Preserve the source handle across C++ moves, matching handle assignment.
206 GenericMatrix(GenericMatrix&& other) noexcept
207 : storage_(other.storage_) {}
208 GenericMatrix& operator=(GenericMatrix&& other) noexcept {
209 if (this != &other) storage_ = other.storage_;
210 return *this;
211 }
212
213 // Snapshot copies the outer matrix state with ordinary T copy semantics.
214 // Primitive elements are detached values; UDT/collection handles remain
215 // shallow aliases so generated checkpoints can recurse through them.
216 class Snapshot {
217 std::shared_ptr<Storage> identity_;
218 Data state_;
219
220 Snapshot(std::shared_ptr<Storage> identity, const Data& state)
221 : identity_(std::move(identity)), state_(state) {}
222
223 friend class GenericMatrix;
224
225 public:
226 Snapshot(const Snapshot&) = default;
227 Snapshot& operator=(const Snapshot&) = default;
228 Snapshot(Snapshot&&) = default;
229 Snapshot& operator=(Snapshot&&) = default;
230 };
231
232 ~GenericMatrix() = default;
233
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));
241 return GenericMatrix(std::move(data));
242 }
243
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{}));
253 return GenericMatrix(std::move(data));
254 }
255
256 T get(int row, int col) const {
257 const Data& values = data();
258 if (row < 0 || row >= rows())
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)];
267 }
268
269 void set(int row, int col, T val) {
270 Data& values = data();
271 if (row < 0 || row >= rows())
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;
280 }
281
282 void fill(T val) {
283 for (auto& r : data()) std::fill(r.begin(), r.end(), val);
284 }
285
286 int rows() const {
287 return static_cast<int>(data().size());
288 }
289 int columns() const {
290 const Data& values = data();
291 return values.empty() ? 0 : static_cast<int>(values[0].size());
292 }
293
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)];
301 }
302
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");
309 std::vector<T> out;
310 out.reserve(values.size());
311 for (const auto& r : values) out.push_back(r[static_cast<size_t>(idx)]);
312 return out;
313 }
314
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)];
324 }
325
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);
338 }
339
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()");
354 // Strong guarantee: build a new buffer, swap on success.
355 Data next;
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));
361 }
362 matrix_data.swap(next);
363 }
364
365 void remove_row(int idx) {
366 (void)data();
367 if (idx < 0 || idx >= rows())
369 "matrix.remove_row", "row_index_out_of_range",
370 "matrix.remove_row: row index out of range");
371 detail::erase_row(data(), idx);
372 }
373
374 void remove_col(int idx) {
375 (void)data();
376 if (idx < 0 || idx >= columns())
378 "matrix.remove_col", "column_index_out_of_range",
379 "matrix.remove_col: column index out of range");
380 detail::erase_col(data(), idx);
381 }
382
383 void swap_rows(int i, int j) {
384 (void)data();
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");
389 detail::swap_rows_impl(data(), i, j);
390 }
391
392 void swap_columns(int i, int j) {
393 (void)data();
394 if (i < 0 || i >= columns() || j < 0 || j >= columns())
396 "matrix.swap_columns", "column_index_out_of_range",
397 "matrix.swap_columns: column index out of range");
398 detail::swap_cols_impl(data(), i, j);
399 }
400
401 [[nodiscard]] GenericMatrix copy() const {
402 return GenericMatrix(data());
403 }
404
405 [[nodiscard]] GenericMatrix submatrix(int from_row, int to_row,
406 int from_col, int to_col) const {
407 (void)data();
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");
424 return GenericMatrix(
425 detail::copy_submatrix(data(), from_row, to_row,
426 from_col, to_col));
427 }
428
429 [[nodiscard]] GenericMatrix transpose() const {
430 static_assert(std::is_default_constructible_v<T>,
431 "matrix.transpose: requires default-constructible element type");
432 return GenericMatrix(
433 detail::transpose_impl(data(), rows(), columns(), T{}));
434 }
435
436 [[nodiscard]] GenericMatrix concat(const GenericMatrix& other, bool horizontal) const {
437 (void)data();
438 (void)other.data();
439 if (horizontal) {
440 if (rows() != other.rows())
442 "matrix.concat", "row_count_mismatch",
443 "matrix.concat: row count mismatch");
444 } else {
445 if (columns() != other.columns())
447 "matrix.concat", "column_count_mismatch",
448 "matrix.concat: column count mismatch");
449 }
450 GenericMatrix m = copy();
451 detail::concat_impl(m.data(), other.data(), horizontal);
452 return m;
453 }
454
455 void reshape(int new_rows, int new_cols) {
456 static_assert(std::is_default_constructible_v<T>,
457 "matrix.reshape: requires default-constructible element type");
458 detail::reshape_impl(data(), new_rows, new_cols, T{});
459 }
460
461 void reverse() {
462 Data& values = data();
463 std::reverse(values.begin(), values.end());
464 }
465
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");
471 detail::sort_impl(data(), column, ascending);
472 }
473
474 int elements_count() const {
475 return detail::elements_count_impl(data());
476 }
477
478 [[nodiscard]] bool is_na() const noexcept { return !storage_; }
479
480 [[nodiscard]] Snapshot snapshot() const {
481 const Storage& storage = require_storage();
482 return Snapshot(storage_, storage.data);
483 }
484
485 void restore(const Snapshot& snapshot) {
486 if (!snapshot.identity_) {
487 detail::matrix_snapshot_error(kInvalidSnapshotError);
488 }
489 Data replacement(snapshot.state_);
490 snapshot.identity_->data.swap(replacement);
491 storage_ = snapshot.identity_;
492 }
493};
494
495// GenericMatrix<bool> uses std::vector<char> as the row container because
496// std::vector<bool>'s proxy storage doesn't expose a stable element reference.
497// Only the boundary methods (get/set/fill/row/col/add_row/add_col) need
498// char<->bool conversion; every storage-agnostic structural method delegates
499// to the same detail:: helpers used by the primary template.
500template <>
501class GenericMatrix<bool> {
502 using Data = std::vector<std::vector<char>>;
503
504 struct Storage {
505 Data data;
506
507 Storage() = default;
508 explicit Storage(Data value) : data(std::move(value)) {}
509 };
510
511 static constexpr const char* kNaIdError =
512 "matrix operation on na ID";
513 static constexpr const char* kInvalidSnapshotError =
514 "matrix restore from invalid snapshot";
515
516 std::shared_ptr<Storage> storage_;
517
518 explicit GenericMatrix(Data data)
519 : storage_(std::make_shared<Storage>(std::move(data))) {}
520
521 Storage& require_storage() {
522 if (!storage_) detail::matrix_na_error(kNaIdError);
523 return *storage_;
524 }
525
526 const Storage& require_storage() const {
527 if (!storage_) detail::matrix_na_error(kNaIdError);
528 return *storage_;
529 }
530
531 Data& data() { return require_storage().data; }
532 const Data& data() const { return require_storage().data; }
533
534public:
535 GenericMatrix() noexcept = default;
536 GenericMatrix(const GenericMatrix&) noexcept = default;
537 GenericMatrix& operator=(const GenericMatrix&) noexcept = default;
538
539 GenericMatrix(GenericMatrix&& other) noexcept
540 : storage_(other.storage_) {}
541 GenericMatrix& operator=(GenericMatrix&& other) noexcept {
542 if (this != &other) storage_ = other.storage_;
543 return *this;
544 }
545
546 class Snapshot {
547 std::shared_ptr<Storage> identity_;
548 Data state_;
549
550 Snapshot(std::shared_ptr<Storage> identity, const Data& state)
551 : identity_(std::move(identity)), state_(state) {}
552
553 friend class GenericMatrix;
554
555 public:
556 Snapshot(const Snapshot&) = default;
557 Snapshot& operator=(const Snapshot&) = default;
558 Snapshot(Snapshot&&) = default;
559 Snapshot& operator=(Snapshot&&) = default;
560 };
561
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));
569 return GenericMatrix(std::move(data));
570 }
571
572 [[nodiscard]] static GenericMatrix new_(int rows, int cols) {
573 return new_(rows, cols, false);
574 }
575
576 bool get(int row, int col) const {
577 const Data& values = data();
578 if (row < 0 || row >= rows())
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;
587 }
588
589 void set(int row, int col, bool val) {
590 Data& values = data();
591 if (row < 0 || row >= rows())
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;
600 }
601
602 void fill(bool val) {
603 char c = val ? 1 : 0;
604 for (auto& r : data()) std::fill(r.begin(), r.end(), c);
605 }
606
607 int rows() const {
608 return static_cast<int>(data().size());
609 }
610 int columns() const {
611 const Data& values = data();
612 return values.empty() ? 0 : static_cast<int>(values[0].size());
613 }
614
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);
625 return out;
626 }
627
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);
637 return out;
638 }
639
640 // row_ref intentionally NOT exposed for T=bool — proxy storage prevents
641 // returning const std::vector<bool>&.
642
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));
658 }
659
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()");
674 Data next;
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));
680 }
681 matrix_data.swap(next);
682 }
683
684 void remove_row(int idx) {
685 (void)data();
686 if (idx < 0 || idx >= rows())
688 "matrix.remove_row", "row_index_out_of_range",
689 "matrix.remove_row: row index out of range");
690 detail::erase_row(data(), idx);
691 }
692
693 void remove_col(int idx) {
694 (void)data();
695 if (idx < 0 || idx >= columns())
697 "matrix.remove_col", "column_index_out_of_range",
698 "matrix.remove_col: column index out of range");
699 detail::erase_col(data(), idx);
700 }
701
702 void swap_rows(int i, int j) {
703 (void)data();
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");
708 detail::swap_rows_impl(data(), i, j);
709 }
710
711 void swap_columns(int i, int j) {
712 (void)data();
713 if (i < 0 || i >= columns() || j < 0 || j >= columns())
715 "matrix.swap_columns", "column_index_out_of_range",
716 "matrix.swap_columns: column index out of range");
717 detail::swap_cols_impl(data(), i, j);
718 }
719
720 [[nodiscard]] GenericMatrix copy() const {
721 return GenericMatrix(data());
722 }
723
724 [[nodiscard]] GenericMatrix submatrix(int from_row, int to_row,
725 int from_col, int to_col) const {
726 (void)data();
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");
743 return GenericMatrix(
744 detail::copy_submatrix(data(), from_row, to_row,
745 from_col, to_col));
746 }
747
748 void reshape(int new_rows, int new_cols) {
749 detail::reshape_impl(data(), new_rows, new_cols, static_cast<char>(0));
750 }
751
752 void reverse() {
753 Data& values = data();
754 std::reverse(values.begin(), values.end());
755 }
756
757 [[nodiscard]] GenericMatrix transpose() const {
758 return GenericMatrix(
760 static_cast<char>(0)));
761 }
762
763 [[nodiscard]] GenericMatrix concat(const GenericMatrix& other, bool horizontal) const {
764 (void)data();
765 (void)other.data();
766 if (horizontal) {
767 if (rows() != other.rows())
769 "matrix.concat", "row_count_mismatch",
770 "matrix.concat: row count mismatch");
771 } else {
772 if (columns() != other.columns())
774 "matrix.concat", "column_count_mismatch",
775 "matrix.concat: column count mismatch");
776 }
777 GenericMatrix m = copy();
778 detail::concat_impl(m.data(), other.data(), horizontal);
779 return m;
780 }
781
782 template <typename Dummy = void>
783 void sort(int /*column*/, bool /*ascending*/ = true) {
784 (void)data();
785 static_assert(!std::is_same_v<Dummy, void> && std::is_same_v<Dummy, void>,
786 "matrix.sort: not supported on bool element type");
787 }
788
789 int elements_count() const {
790 return detail::elements_count_impl(data());
791 }
792
793 [[nodiscard]] bool is_na() const noexcept { return !storage_; }
794
795 [[nodiscard]] Snapshot snapshot() const {
796 const Storage& storage = require_storage();
797 return Snapshot(storage_, storage.data);
798 }
799
800 void restore(const Snapshot& snapshot) {
801 if (!snapshot.identity_) {
802 detail::matrix_snapshot_error(kInvalidSnapshotError);
803 }
804 Data replacement(snapshot.state_);
805 snapshot.identity_->data.swap(replacement);
806 storage_ = snapshot.identity_;
807 }
808};
809
810template <typename T>
811inline bool is_na(const GenericMatrix<T>& matrix) noexcept {
812 return matrix.is_na();
813}
814
815// Deprecated spelling, kept as an exact alias template so generated code and
816// the source adapter compile unchanged (R5 lane N14, the same treatment
817// session_time.hpp / str_utils.hpp received in lane L11). New code names
818// GenericMatrix<T>.
819template <typename T>
821
822} // namespace pineforge
Snapshot & operator=(const Snapshot &)=default
Snapshot(const Snapshot &)=default
Snapshot & operator=(Snapshot &&)=default
Snapshot & operator=(const Snapshot &)=default
Snapshot & operator=(Snapshot &&)=default
void restore(const Snapshot &snapshot)
void set(int row, int col, bool val)
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
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 & operator=(GenericMatrix &&other) noexcept
static GenericMatrix new_(int rows, int cols, bool init)
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
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)
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)
Definition drawing.hpp:58