GF2++
Loading...
Searching...
No Matches
BitMatrix.h
Go to the documentation of this file.
1#pragma once
2// SPDX-FileCopyrightText: 2025 Nessan Fitzmaurice <nzznfitz+gh@icloud.com>
3// SPDX-License-Identifier: MIT
4
8
9#include <gf2/BitPolynomial.h>
10#include <gf2/BitVector.h>
11#include <gf2/RNG.h>
12
13#include <string>
14#include <optional>
15#include <regex>
16
17namespace gf2 {
18
19// Forward declarations to avoid recursive inclusion issues
20// clang-format off
21template<Unsigned Word> class BitGauss;
22template<Unsigned Word> class BitLU;
23// clang-format on
24
32template<Unsigned Word = usize>
33class BitMatrix {
34private:
36 std::vector<BitVector<Word>> m_rows;
37
38public:
39 // The row type is a `BitVector` ...
40 using row_type = BitVector<Word>;
41
43 using word_type = Word;
44
47
55 constexpr BitMatrix() : m_rows{} {}
56
66 constexpr BitMatrix(usize n) : m_rows{} {
67 if (n > 0) resize(n, n);
68 }
69
79 constexpr BitMatrix(usize m, usize n) : m_rows{} {
80 if (m > 0 && n > 0) resize(m, n);
81 }
82
94 constexpr BitMatrix(std::vector<row_type> const& rows) : m_rows{rows} {
95 gf2_assert(check_rows(m_rows), "Not all rows have the same size!");
96 }
97
112 constexpr BitMatrix(std::vector<BitVector<Word>>&& rows) : m_rows{std::move(rows)} {
113 gf2_assert(check_rows(m_rows), "Not all rows have the same size!");
114 }
115
119
127 static constexpr BitMatrix zeros(usize m, usize n) { return BitMatrix{m, n}; }
128
136 static constexpr BitMatrix zeros(usize m) { return BitMatrix{m, m}; }
137
145 static constexpr BitMatrix ones(usize m, usize n) {
146 auto rows = std::vector{m, BitVector<Word>::ones(n)};
147 return BitMatrix{std::move(rows)};
148 }
149
157 static constexpr BitMatrix ones(usize m) { return BitMatrix::ones(m, m); }
158
166 static constexpr BitMatrix alternating(usize m, usize n) {
167 auto rows = std::vector{m, BitVector<Word>::alternating(n)};
168 // Flip every other row.
169 for (auto i = 1uz; i < m; i += 2) rows[i].flip_all();
170 return BitMatrix{std::move(rows)};
171 }
172
180 static constexpr BitMatrix alternating(usize m) { return BitMatrix::alternating(m, m); }
181
191 template<BitStore Lhs, BitStore Rhs>
192 requires std::same_as<typename Lhs::word_type, Word> && std::same_as<typename Rhs::word_type, Word>
193 static constexpr BitMatrix from_outer_product(Lhs const& u, Rhs const& v) {
194 BitMatrix result;
195 for (auto i = 0uz; i < u.size(); ++i) {
196 row_type row = u.get(i) ? row_type(v) : row_type(v.size());
197 result.m_rows.push_back(std::move(row));
198 }
199 return result;
200 }
201
211 template<BitStore Lhs, BitStore Rhs>
212 requires std::same_as<typename Lhs::word_type, Word> && std::same_as<typename Rhs::word_type, Word>
213 static constexpr BitMatrix from_outer_sum(Lhs const& u, Rhs const& v) {
214 BitMatrix result;
215 for (auto i = 0uz; i < u.size(); ++i) {
216 result.m_rows.push_back(v);
217 if (u.get(i)) result.m_rows.back().flip_all();
218 }
219 return result;
220 }
221
229 static constexpr BitMatrix from(usize m, usize n, std::invocable<usize, usize> auto f) {
230 BitMatrix result{m, n};
231 for (auto i = 0uz; i < m; ++i)
232 for (auto j = 0uz; j < n; ++j)
233 if (f(i, j)) result.set(i, j, true);
234 return result;
235 }
236
240
264 static BitMatrix random(usize m, usize n, double p = 0.5, std::uint64_t seed = 0) {
265 // Keep a single static RNG per thread for all calls to this method, seeded with entropy on the first call.
266 thread_local RNG rng;
267
268 // Edge case handling ...
269 if (p < 0) return BitMatrix::zeros(m, n);
270
271 // Scale p by 2^64 to remove floating point arithmetic from the main loop below.
272 // If we determine p rounds to 1 then we can just set all elements to 1 and return early.
273 p = p * 0x1p64 + 0.5;
274 if (p >= 0x1p64) return BitMatrix::ones(m, n);
275
276 // p does not round to 1 so we use a 64-bit URNG and check each draw against the 64-bit scaled p.
277 auto scaled_p = static_cast<std::uint64_t>(p);
278
279 // If a seed was provided, set the RNG's seed to it. Otherwise, we carry on from where we left off.
280 std::uint64_t old_seed = rng.seed();
281 if (seed != 0) rng.set_seed(seed);
282
283 auto result = BitMatrix::zeros(m, n);
284 for (auto i = 0uz; i < m; ++i) {
285 for (auto j = 0uz; j < n; ++j) {
286 if (rng.u64() < scaled_p) result.set(i, j, true);
287 }
288 }
289
290 // Restore the old seed if necessary.
291 if (seed != 0) rng.set_seed(old_seed);
292
293 return result;
294 }
295
314 static BitMatrix seeded_random(usize m, usize n, std::uint64_t seed) { return random(m, n, 0.5, seed); }
315
333 static BitMatrix seeded_random(usize m, std::uint64_t seed) { return random(m, m, 0.5, seed); }
334
348 static BitMatrix biased_random(usize m, usize n, double p) { return random(m, n, p, 0); }
349
361 static BitMatrix biased_random(usize m, double p) { return random(m, m, p, 0); }
362
366
374 static constexpr BitMatrix zero(usize m) { return BitMatrix{m, m}; }
375
383 static constexpr BitMatrix identity(usize m) {
384 BitMatrix result{m, m};
385 for (auto i = 0uz; i < m; ++i) result.set(i, i, true);
386 return result;
387 }
388
400 template<BitStore Store>
401 requires std::same_as<typename Store::word_type, Word>
402 static constexpr BitMatrix companion(Store const& top_row) {
403 // Edge case:
404 if (top_row.size() == 0) return BitMatrix{};
405 auto result = BitMatrix::zero(top_row.size());
406 result.m_rows[0].copy(top_row);
407 result.set_sub_diagonal(1);
408 return result;
409 }
410
421 static constexpr BitMatrix left_shift(usize n, usize p) {
422 auto result = BitMatrix::zeros(n, n);
423 result.set_super_diagonal(p);
424 return result;
425 }
426
438 auto result = BitMatrix::zeros(n, n);
439 result.set_sub_diagonal(p);
440 return result;
441 }
442
454 auto result = BitMatrix::zeros(n, n);
455 for (auto i = 0uz; i < n; ++i) {
456 auto j = (i + n - p) % n;
457 result.set(i, j, true);
458 }
459 return result;
460 }
461
473 auto result = BitMatrix::zeros(n, n);
474 for (auto i = 0uz; i < n; ++i) {
475 auto j = (i + p) % n;
476 result.set(i, j, true);
477 }
478 return result;
479 }
480
484
486 //
497 template<BitStore Store>
498 requires std::same_as<typename Store::word_type, Word>
499 static std::optional<BitMatrix> from_row_store(Store const& v, usize r) {
500 // Edge case?
501 if (v.size() == 0) return BitMatrix{};
502
503 // Error handling ...
504 if (r == 0 || v.size() % r != 0) return std::nullopt;
505
506 // Number of columns (for sure this is an even division)
507 usize c = v.size() / r;
508
509 // Create the bit-matrix and copy the rows.
510 BitMatrix result{r, c};
511 for (auto i = 0uz; i < r; ++i) {
512 auto begin = i * c;
513 auto end = begin + c;
514 result.m_rows[i].copy(v.span(begin, end));
515 }
516 return result;
517 }
518
534 template<BitStore Store>
535 requires std::same_as<typename Store::word_type, Word>
536 static std::optional<BitMatrix> from_col_store(Store const& v, usize c) {
537 // Edge case?
538 if (v.size() == 0) return BitMatrix{};
539
540 // Error handling ...
541 if (c == 0 || v.size() % c != 0) return std::nullopt;
542
543 // Number of columns (for sure this is an even division)
544 usize r = v.size() / c;
545
546 // Create the bit-matrix and copy the rows.
547 BitMatrix result{r, c};
548 usize iv = 0;
549 for (auto j = 0uz; j < c; ++j) {
550 for (auto i = 0uz; i < r; ++i) {
551 if (v.get(iv)) result.m_rows[i].set(j);
552 iv++;
553 }
554 }
555 return result;
556 }
557
561
582 static std::optional<BitMatrix> from_string(std::string_view s) {
583 // Edge case
584 if (s.empty()) return BitMatrix{};
585
586 // We split the string into tokens using the standard regex library.
587 std::string src(s);
588 auto delims = std::regex(R"([\s|,|;]+)");
589 std::sregex_token_iterator iter{src.cbegin(), src.cend(), delims, -1};
590 std::vector<std::string> tokens{iter, {}};
591
592 // Zap any empty tokens & check there is something to do
593 tokens.erase(std::remove_if(tokens.begin(), tokens.end(), [](std::string_view x) { return x.empty(); }),
594 tokens.end());
595 if (tokens.empty()) return BitMatrix{};
596
597 // We hope to fill a BitMatrix.
598 BitMatrix result;
599
600 // Iterate through the possible rows
601 usize n_rows = tokens.size();
602 usize n_cols = 0;
603 for (auto i = 0uz; i < n_rows; ++i) {
604 // Attempt to parse the current token as a row for the bit-matrix.
605 auto r = row_type::from_string(tokens[i]);
606
607 // Parse failure?
608 if (!r) return std::nullopt;
609
610 // We've read a potentially valid BitMatrix row.
611 if (i == 0) {
612 // First row sets the number of columns
613 n_cols = r->size();
614 result.resize(n_rows, n_cols);
615 } else {
616 // Subsequent rows better have the same number of elements!
617 if (r->size() != n_cols) return std::nullopt;
618 }
619 result.m_rows[i] = *r;
620 }
621 return result;
622 }
623
627
629 constexpr usize rows() const { return m_rows.size(); }
630
632 constexpr usize cols() const { return m_rows.size() > 0 ? m_rows[0].size() : 0; }
633
635 constexpr usize size() const { return rows() * cols(); }
636
638 constexpr bool is_empty() const { return rows() == 0; }
639
643
655 constexpr bool is_square() const { return rows() != 0 && rows() == cols(); }
656
668 constexpr bool is_zero() const { return is_square() && none(); }
669
678 constexpr bool is_identity() const {
679 if (!is_square()) return false;
680 for (auto i = 0uz; i < rows(); ++i) {
681 row_type r = m_rows[i];
682 r.flip(i);
683 if (r.any()) return false;
684 }
685 return true;
686 }
687
697 constexpr bool is_symmetric() const {
698 if (!is_square()) return false;
699 for (auto i = 0uz; i < rows(); ++i) {
700 for (auto j = 0uz; j < cols(); ++j) {
701 if (get(i, j) != get(j, i)) return false;
702 }
703 }
704 return true;
705 }
706
710
720 constexpr usize count_ones() const {
721 usize count = 0;
722 for (const auto& row : m_rows) count += row.count_ones();
723 return count;
724 }
725
733 constexpr usize count_zeros() const { return size() - count_ones(); }
734
745 constexpr usize count_ones_on_diagonal() const {
746 gf2_debug_assert(is_square(), "BitMatrix is {} x {} but it should be square!", rows(), cols());
747 usize result = 0;
748 for (auto i = 0uz; i < rows(); ++i)
749 if (get(i, i)) ++result;
750 return result;
751 }
752
767 constexpr bool trace() const { return count_ones_on_diagonal() % 2 == 1; }
768
772
786 constexpr bool any() const {
787 for (const auto& row : m_rows)
788 if (row.any()) return true;
789 return false;
790 }
791
805 constexpr bool all() const {
806 for (const auto& row : m_rows)
807 if (!row.all()) return false;
808 return true;
809 }
810
824 constexpr bool none() const {
825 for (const auto& row : m_rows)
826 if (!row.none()) return false;
827 return true;
828 }
829
833
846 constexpr bool get(usize r, usize c) const {
847 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
848 gf2_debug_assert(c < cols(), "Column index {} out of bounds [0,{})", c, cols());
849 return m_rows[r].get(c);
850 }
851
864 constexpr bool operator()(usize r, usize c) const {
865 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
866 gf2_debug_assert(c < cols(), "Column index {} out of bounds [0,{})", c, cols());
867 return m_rows[r].get(c);
868 }
869
882 constexpr void set(usize r, usize c, bool val = true) {
883 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
884 gf2_debug_assert(c < cols(), "Column index {} out of bounds [0,{})", c, cols());
885 m_rows[r].set(c, val);
886 }
887
901 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
902 gf2_debug_assert(c < cols(), "Column index {} out of bounds [0,{})", c, cols());
903 return m_rows[r][c];
904 }
905
917 constexpr void flip(usize r, usize c) {
918 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
919 gf2_debug_assert(c < cols(), "Column index {} out of bounds [0,{})", c, cols());
920 m_rows[r].flip(c);
921 }
922
926
939 constexpr const row_type& row(usize r) const {
940 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
941 return m_rows[r];
942 }
943
957 constexpr row_type& row(usize r) {
958 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
959 return m_rows[r];
960 }
961
974 constexpr const row_type& operator[](usize r) const {
975 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
976 return m_rows[r];
977 }
978
992 constexpr row_type& operator[](usize r) {
993 gf2_debug_assert(r < rows(), "Row index {} out of bounds [0,{})", r, rows());
994 return m_rows[r];
995 }
996
1000
1018 constexpr BitVector<Word> col(usize c) const {
1019 gf2_debug_assert(c < cols(), "Column {} out of bounds [0, {})", c, cols());
1020 auto result = BitVector<Word>::zeros(rows());
1021 for (auto r = 0uz; r < rows(); ++r) {
1022 if (get(r, c)) { result.set(r); }
1023 }
1024 return result;
1025 }
1026
1030
1041 constexpr void set_all(bool value = true) {
1042 for (auto& row : m_rows) row.set_all(value);
1043 }
1044
1053 constexpr void flip_all() {
1054 for (auto& row : m_rows) row.flip_all();
1055 }
1056
1060
1072 constexpr void set_diagonal(bool val = true) {
1073 gf2_debug_assert(is_square(), "BitMatrix is {} x {} but it should be square!", rows(), cols());
1074 for (auto i = 0uz; i < rows(); ++i) { set(i, i, val); }
1075 }
1076
1088 constexpr void flip_diagonal() {
1089 gf2_debug_assert(is_square(), "BitMatrix is {} x {} but it should be square!", rows(), cols());
1090 for (auto i = 0uz; i < rows(); ++i) { flip(i, i); }
1091 }
1092
1106 constexpr void set_super_diagonal(usize d, bool val = true) {
1107 gf2_debug_assert(is_square(), "BitMatrix is {} x {} but it should be square!", rows(), cols());
1108 for (auto i = 0uz; i < rows() - d; ++i) { set(i, i + d, val); }
1109 }
1110
1124 constexpr void flip_super_diagonal(usize d) {
1125 gf2_debug_assert(is_square(), "BitMatrix is {} x {} but it should be square!", rows(), cols());
1126 for (auto i = 0uz; i < rows() - d; ++i) { flip(i, i + d); }
1127 }
1128
1142 constexpr void set_sub_diagonal(usize d, bool val = true) {
1143 gf2_debug_assert(is_square(), "BitMatrix is {} x {} but it should be square!", rows(), cols());
1144 for (auto i = 0uz; i < rows() - d; ++i) { set(i + d, i, val); }
1145 }
1146
1160 constexpr void flip_sub_diagonal(usize d) {
1161 gf2_debug_assert(is_square(), "BitMatrix is {} x {} but it should be square!", rows(), cols());
1162 for (auto i = 0uz; i < rows() - d; ++i) { flip(i + d, i); }
1163 }
1164
1168
1189 constexpr void resize(usize r, usize c) {
1190 // Edge case ...
1191 if (rows() == r && cols() == c) return;
1192
1193 // Resizes to zero in either dimension is taken as a zap the lot instruction
1194 if (r == 0 || c == 0) r = c = 0;
1195
1196 // Rows, then columns
1197 m_rows.resize(r);
1198 for (auto& row : m_rows) row.resize(c);
1199 }
1200
1210 constexpr void clear() { resize(0, 0); }
1211
1222 constexpr void make_square(usize n) { resize(n, n); }
1223
1227
1241 template<BitStore Store>
1242 requires std::same_as<typename Store::word_type, Word>
1243 constexpr BitMatrix& append_row(Store const& row) {
1244 gf2_assert_eq(row.size(), cols(), "Row has {} elements but bit-matrix has {} columns!", row.size(), cols());
1245 m_rows.push_back(row);
1246 return *this;
1247 }
1248
1264 template<BitStore Store>
1265 requires std::same_as<typename Store::word_type, Word>
1266 constexpr BitMatrix& append_row(Store&& row) {
1267 gf2_assert_eq(row.size(), cols(), "Row has {} elements but bit-matrix has {} columns!", row.size(), cols());
1268 m_rows.push_back(std::move(row));
1269 return *this;
1270 }
1271
1285 constexpr BitMatrix& append_rows(BitMatrix<Word> const& src) {
1286 gf2_assert_eq(src.cols(), cols(), "Source has {} columns but bit-matrix has {} columns!", src.cols(), cols());
1287 m_rows.insert(m_rows.end(), src.m_rows.begin(), src.m_rows.end());
1288 return *this;
1289 }
1290
1307 gf2_assert_eq(src.cols(), cols(), "Source has {} columns but bit-matrix has {} columns!", src.cols(), cols());
1308 m_rows.insert(m_rows.end(), std::make_move_iterator(src.m_rows.begin()),
1309 std::make_move_iterator(src.m_rows.end()));
1310 return *this;
1311 }
1312
1326 template<BitStore Store>
1327 requires std::same_as<typename Store::word_type, Word>
1328 constexpr BitMatrix& append_col(Store const& col) {
1329 gf2_assert_eq(col.size(), rows(), "Column has {} elements but bit-matrix has {} rows!", col.size(), rows());
1330 for (auto i = 0uz; i < rows(); ++i) m_rows[i].push(col.get(i));
1331 return *this;
1332 }
1333
1347 constexpr BitMatrix& append_cols(BitMatrix<Word> const& src) {
1348 gf2_assert_eq(src.rows(), rows(), "Source has {} rows but bit-matrix has {} rows!", src.rows(), rows());
1349 for (auto i = 0uz; i < rows(); ++i) m_rows[i].append(src.m_rows[i]);
1350 return *this;
1351 }
1352
1356
1366 constexpr std::optional<BitVector<Word>> remove_row() {
1367 if (rows() == 0) return std::nullopt;
1368 auto row = std::move(m_rows.back());
1369 m_rows.pop_back();
1370 return row;
1371 }
1372
1383 constexpr std::optional<BitMatrix<Word>> remove_rows(usize k) {
1384 if (rows() < k) return std::nullopt;
1385 auto begin = m_rows.end() - static_cast<std::ptrdiff_t>(k);
1386 auto end = m_rows.end();
1387 auto result = BitMatrix<Word>(std::vector<row_type>(begin, end));
1388 m_rows.erase(begin, end);
1389 return result;
1390 }
1391
1402 constexpr std::optional<BitVector<Word>> remove_col() {
1403 if (cols() == 0) return std::nullopt;
1404 auto result = col(cols() - 1);
1405 for (auto i = 0uz; i < rows(); ++i) m_rows[i].pop();
1406 return result;
1407 }
1408
1412
1428 constexpr BitMatrix sub_matrix(usize r_start, usize r_end, usize c_start, usize c_end) const {
1429
1430 // Check that the row range is valid.
1431 gf2_assert(r_start <= r_end, "Invalid row range");
1432 gf2_assert(r_end <= rows(), "Row range extends beyond the end of the bit-matrix");
1433
1434 // Check that the column range is valid.
1435 gf2_assert(c_start <= c_end, "Invalid column range");
1436 gf2_assert(c_end <= cols(), "Column range extends beyond the right edge of the bit-matrix");
1437
1438 // Get the number of rows and columns in the sub-matrix.
1439 auto r = r_end - r_start;
1440 auto c = c_end - c_start;
1441
1442 // Create the sub-matrix.
1443 BitMatrix result{r, c};
1444 for (auto i = 0uz; i < r; ++i) result.m_rows[i].copy(m_rows[i + r_start].span(c_start, c_end));
1445
1446 return result;
1447 }
1448
1460 constexpr void replace_sub_matrix(usize top, usize left, BitMatrix<Word> const& src) {
1461 auto r = src.rows();
1462 auto c = src.cols();
1463 gf2_assert(top + r <= rows(), "Too many rows for the replacement sub-matrix to fit");
1464 gf2_assert(left + c <= cols(), "Too many columns for the replacement sub-matrix to fit");
1465 for (auto i = 0uz; i < r; ++i) m_rows[top + i].span(left, left + c).copy(src.m_rows[i]);
1466 }
1467
1471
1480 constexpr BitMatrix lower() const {
1481 // Edge case:
1482 if (is_empty()) return BitMatrix{};
1483
1484 // Start with a copy of the bit-matrix.
1485 BitMatrix result = *this;
1486
1487 // Set the upper triangular part to zero.
1488 auto nc = cols();
1489 for (auto i = 0uz; i < rows(); ++i) {
1490 auto first = i + 1;
1491 if (first < nc) result.m_rows[i].span(first, nc).set_all(false);
1492 }
1493 return result;
1494 }
1495
1504 constexpr BitMatrix upper() const {
1505 // Edge case:
1506 if (is_empty()) return BitMatrix{};
1507
1508 // Start with a copy of the bit-matrix.
1509 auto result = *this;
1510
1511 // Set the upper triangular part to zero.
1512 auto c = cols();
1513 for (auto i = 0uz; i < rows(); ++i) {
1514 auto len = std::min(i, c);
1515 if (len > 0) result.m_rows[i].span(0, len).set_all(false);
1516 }
1517 return result;
1518 }
1519
1530 constexpr BitMatrix strictly_lower() const {
1531 auto result = lower();
1532 result.set_diagonal(false);
1533 return result;
1534 }
1535
1546 constexpr BitMatrix strictly_upper() const {
1547 auto result = upper();
1548 result.set_diagonal(false);
1549 return result;
1550 }
1551
1562 constexpr BitMatrix unit_lower() const {
1563 auto result = lower();
1564 result.set_diagonal(true);
1565 return result;
1566 }
1567
1578 constexpr BitMatrix unit_upper() const {
1579 auto result = upper();
1580 result.set_diagonal(true);
1581 return result;
1582 }
1583
1587
1599 constexpr void swap_rows(usize i, usize j) {
1600 gf2_debug_assert(i < rows(), "Row index {} out of bounds [0,{})", i, rows());
1601 gf2_debug_assert(j < rows(), "Row index {} out of bounds [0,{})", j, rows());
1602 std::swap(m_rows[i], m_rows[j]);
1603 }
1604
1616 constexpr void swap_cols(usize i, usize j) {
1617 gf2_debug_assert(i < cols(), "Column index {} out of bounds [0,{})", i, cols());
1618 gf2_debug_assert(j < cols(), "Column index {} out of bounds [0,{})", j, cols());
1619 for (auto k = 0uz; k < rows(); ++k) m_rows[k].swap(i, j);
1620 }
1621
1633 constexpr void add_identity() {
1634 gf2_debug_assert(is_square(), "This bit-matrix is {} x {} but it should be square!", rows(), cols());
1635 for (auto i = 0uz; i < rows(); ++i) flip(i, i);
1636 }
1637
1641
1654 constexpr BitMatrix transposed() const {
1655 auto r = rows();
1656 auto c = cols();
1657 BitMatrix result{c, r};
1658 for (auto i = 0uz; i < r; ++i) {
1659 for (auto j = 0uz; j < c; ++j) {
1660 if (get(i, j)) { result.set(j, i); }
1661 }
1662 }
1663 return result;
1664 }
1665
1679 constexpr void transpose() {
1680 gf2_assert(is_square(), "`transpose()` requires a square matrix");
1681 for (auto i = 0uz; i < rows(); ++i) {
1682 for (auto j = 0uz; j < i; ++j) {
1683 if (get(i, j) != get(j, i)) {
1684 flip(i, j);
1685 flip(j, i);
1686 }
1687 }
1688 }
1689 }
1690
1694
1713 constexpr BitMatrix to_the(usize n, bool n_is_log2 = false) const {
1714 gf2_assert(is_square(), "Bit-matrix is {} x {} but it should be square!", rows(), cols());
1715
1716 // Perhaps we just need lots of square steps? Note that 2^0 = 1 so M^(2^0) = M.
1717 if (n_is_log2) {
1718 auto result = *this;
1719 for (auto i = 0uz; i < n; ++i) result = dot(result, result);
1720 return result;
1721 }
1722
1723 // Otherwise we need square & multiply steps but we first handle the edge case:
1724 if (n == 0) return BitMatrix::identity(rows());
1725
1726 // OK n != 0: Note that if e.g. n = 0b00010111 then std::bit_floor(n) = 0b00010000.
1727 usize n_bit = std::bit_floor(n);
1728
1729 // Start with a copy of this bit-matrix which takes care of the most significant binary digit in n.
1730 auto result = *this;
1731 n_bit >>= 1;
1732
1733 // More to go?
1734 while (n_bit) {
1735 // Always square and then multiply if necessary (i.e. if current bit in n is set).
1736 result = dot(result, result);
1737 if (n & n_bit) result = dot(result, *this);
1738
1739 // On to the next bit position in n.
1740 n_bit >>= 1;
1741 }
1742 return result;
1743 }
1744
1748
1773 gf2_assert(!is_empty(), "Bit-matrix must not be empty");
1774
1775 // We return a bit-vector that shows which columns have a pivot -- start by assuming none.
1776 auto has_pivot = BitVector<Word>::zeros(cols());
1777
1778 // The current row of the echelon form we are working on.
1779 auto r = 0uz;
1780 auto nr = rows();
1781
1782 // Iterate over each column.
1783 for (auto j = 0uz; j < cols(); ++j) {
1784 // Find a non-zero entry in this column below the diagonal (a "pivot").
1785 auto p = r;
1786 while (p < nr && !get(p, j)) p++;
1787
1788 // Did we find a pivot in this column?
1789 if (p < nr) {
1790 // Mark this column as having a pivot.
1791 has_pivot.set(j);
1792
1793 // If necessary, swap the current row with the row that has the pivot.
1794 if (p != r) swap_rows(p, r);
1795
1796 // Below the working row make sure column j is zero by elimination if necessary.
1797 auto row_r = m_rows[r];
1798 for (auto i = r + 1; i < nr; ++i)
1799 if (get(i, j)) m_rows[i] ^= row_r;
1800
1801 // Move to the next row. If we've reached the end of the matrix, we're done.
1802 r += 1;
1803 if (r == nr) break;
1804 }
1805 }
1806
1807 // Return the bit-vector that shows which columns have a pivot.
1808 return has_pivot;
1809 }
1810
1831 // Start with the echelon form.
1832 auto has_pivot = to_echelon_form();
1833
1834 // Iterate over each row from the bottom up - Gauss Jordan elimination.
1835 for (auto r = rows(); r--;) {
1836 // Find the first set bit in the current row if there is one.
1837 if (auto p = m_rows[r].first_set()) {
1838 // Clear out everything in column p *above* row r (already cleared out below the pivot).
1839 auto row_r = m_rows[r];
1840 for (auto i = 0uz; i < r; ++i)
1841 if (get(i, *p)) m_rows[i] ^= row_r;
1842 }
1843 }
1844
1845 // Return the bit-vector that shows which columns have a pivot.
1846 return has_pivot;
1847 }
1848
1852
1860 std::optional<BitMatrix> inverse() const {
1861 // The bit-matrix must be square & non-empty.
1862 if (is_empty() || !is_square()) return std::nullopt;
1863
1864 // Create a copy of the bit-matrix & augment it with the identity matrix on the right.
1865 auto matrix = *this;
1866 auto nr = rows();
1867 auto nc = cols();
1868 matrix.append_cols(BitMatrix::identity(nr));
1869
1870 // Transform the augmented matrix to reduced row-echelon form.
1871 matrix.to_reduced_echelon_form();
1872
1873 // If all went well the left half is the identity matrix and the right half is the inverse.
1874 if (matrix.sub_matrix(0, nr, 0, nc).is_identity()) return matrix.sub_matrix(0, nr, nc, nc * 2);
1875
1876 return std::nullopt;
1877 }
1878
1893 static constexpr double probability_invertible(usize n) {
1894 // A 0x0 matrix is not well defined throw an error!
1895 if (n == 0) throw std::invalid_argument("Matrix should not be 0x0--likely an error somewhere upstream!");
1896
1897 // Formula is p(n) = \prod_{k = 1}^{n} (1 - 2^{-k}) which runs out of juice once n hits any size at all!
1898 usize n_prod = std::min(n, static_cast<size_t>(std::numeric_limits<double>::digits));
1899
1900 // Compute the product over the range that matters
1901 double product = 1;
1902 double pow2 = 1;
1903 while (n_prod--) {
1904 pow2 *= 0.5;
1905 product *= 1 - pow2;
1906 }
1907 return product;
1908 }
1909
1924 static constexpr double probability_singular(usize n) { return 1 - probability_invertible(n); }
1925
1929
1938 BitLU<Word> LU() const { return BitLU<Word>{*this}; }
1939
1943
1957 template<BitStore Rhs>
1958 requires std::same_as<typename Rhs::word_type, Word>
1959 auto solver_for(Rhs const& b) const {
1960 return BitGauss<Word>{*this, b};
1961 }
1962
1972 template<BitStore Rhs>
1973 requires std::same_as<typename Rhs::word_type, Word>
1974 auto x_for(Rhs const& b) const {
1975 return solver_for(b)();
1976 }
1977
1981
2002 gf2_assert(is_square(), "Bit-matrix must be square not {} x {}", rows(), cols());
2004 }
2005
2015 static BitPolynomial<Word>
2017 auto n_companions = top_rows.size();
2018 if (n_companions == 0) return BitPolynomial<Word>::zero();
2019
2020 // Compute the product of the characteristic polynomials of the companion matrices.
2021 auto result = companion_matrix_characteristic_polynomial(top_rows[0]);
2022 for (auto i = 1uz; i < n_companions; ++i) { result *= companion_matrix_characteristic_polynomial(top_rows[i]); }
2023 return result;
2024 }
2025
2041 auto n = top_row.size();
2042
2043 // The characteristic polynomial is degree n with n + 1 coefficients (leading coefficient is 1).
2044 auto coeffs = BitVector<Word>::ones(n + 1);
2045
2046 // The lower order coefficients are the top row of the companion matrix in reverse order.
2047 for (auto j = 0uz; j < n; ++j) { coeffs.set(n - j - 1, top_row.get(j)); }
2048 return BitPolynomial<Word>{std::move(coeffs)};
2049 }
2050
2065 std::vector<BitVector<Word>> frobenius_form() const {
2066 // The bit-matrix must be square.
2067 gf2_assert(is_square(), "Bit-matrix must be square not {} x {}", rows(), cols());
2068
2069 // Space for the top rows of the companion matrices which we will return.
2070 auto nr = rows();
2071 auto top_rows = std::vector<BitVector<Word>>{};
2072 top_rows.reserve(nr);
2073
2074 // Make a working copy of the bit-matrix to work through using Danilevsky's algorithm.
2075 auto copy = *this;
2076 while (nr > 0) {
2077 auto companion = copy.danilevsky_step(nr);
2078 nr -= companion.size();
2079 top_rows.push_back(companion);
2080 }
2081 return top_rows;
2082 }
2083
2087
2101 constexpr void operator^=(BitMatrix<Word> const& rhs) {
2102 gf2_assert(rows() == rhs.rows(), "Row dimensions do not match: {} != {}.", rows(), rhs.rows());
2103 gf2_assert(cols() == rhs.cols(), "Column dimensions do not match: {} != {}.", cols(), rhs.cols());
2104 for (auto i = 0uz; i < rows(); ++i) row(i) ^= rhs.row(i);
2105 }
2106
2120 constexpr void operator&=(BitMatrix<Word> const& rhs) {
2121 gf2_assert(rows() == rhs.rows(), "Row dimensions do not match: {} != {}.", rows(), rhs.rows());
2122 gf2_assert(cols() == rhs.cols(), "Column dimensions do not match: {} != {}.", cols(), rhs.cols());
2123 for (auto i = 0uz; i < rows(); ++i) row(i) &= rhs.row(i);
2124 }
2125
2139 constexpr void operator|=(BitMatrix<Word> const& rhs) {
2140 gf2_assert(rows() == rhs.rows(), "Row dimensions do not match: {} != {}.", rows(), rhs.rows());
2141 gf2_assert(cols() == rhs.cols(), "Column dimensions do not match: {} != {}.", cols(), rhs.cols());
2142 for (auto i = 0uz; i < rows(); ++i) row(i) |= rhs.row(i);
2143 }
2144
2158 constexpr auto operator^(BitMatrix<Word> const& rhs) const {
2159 gf2_assert(rows() == rhs.rows(), "Row dimensions do not match: {} != {}.", rows(), rhs.rows());
2160 gf2_assert(cols() == rhs.cols(), "Column dimensions do not match: {} != {}.", cols(), rhs.cols());
2161 auto result = *this;
2162 result ^= rhs;
2163 return result;
2164 }
2165
2179 constexpr auto operator&(BitMatrix<Word> const& rhs) const {
2180 gf2_assert(rows() == rhs.rows(), "Row dimensions do not match: {} != {}.", rows(), rhs.rows());
2181 gf2_assert(cols() == rhs.cols(), "Column dimensions do not match: {} != {}.", cols(), rhs.cols());
2182 auto result = *this;
2183 result &= rhs;
2184 return result;
2185 }
2186
2200 constexpr auto operator|(BitMatrix<Word> const& rhs) const {
2201 gf2_assert(rows() == rhs.rows(), "Row dimensions do not match: {} != {}.", rows(), rhs.rows());
2202 gf2_assert(cols() == rhs.cols(), "Column dimensions do not match: {} != {}.", cols(), rhs.cols());
2203 auto result = *this;
2204 result |= rhs;
2205 return result;
2206 }
2207
2216 constexpr auto operator~() {
2217 auto result = *this;
2218 result.flip_all();
2219 return result;
2220 }
2221
2225
2239 constexpr void operator+=(BitMatrix<Word> const& rhs) { operator^=(rhs); }
2240
2254 constexpr void operator-=(BitMatrix<Word> const& rhs) { operator^=(rhs); }
2255
2269 constexpr auto operator+(BitMatrix<Word> const& rhs) const {
2270 auto result = *this;
2271 result += rhs;
2272 return result;
2273 }
2274
2288 constexpr auto operator-(BitMatrix<Word> const& rhs) const {
2289 auto result = *this;
2290 result -= rhs;
2291 return result;
2292 }
2293
2297
2312 std::string to_binary_string(std::string_view row_sep = "\n", std::string_view bit_sep = "",
2313 std::string_view row_prefix = "", std::string_view row_suffix = "") const {
2314 usize nr = rows();
2315
2316 // Edge case?
2317 if (nr == 0) return std::string{};
2318
2319 usize nc = cols();
2320 usize per_row = nc + (nc - 1) * bit_sep.size() + row_prefix.size() + row_suffix.size();
2321 std::string result;
2322 result.reserve(nr * per_row);
2323
2324 for (auto i = 0uz; i < nr; ++i) {
2325 result += m_rows[i].to_binary_string(bit_sep, row_prefix, row_suffix);
2326 if (i + 1 < nr) result += row_sep;
2327 }
2328 return result;
2329 }
2330
2340 std::string to_compact_binary_string() const { return to_binary_string(" "); }
2341
2351 std::string to_string() const { return to_binary_string("\n"); }
2352
2366 std::string to_pretty_string() const { return to_binary_string("\n", " ", "\u2502", "\u2502"); }
2367
2395 std::string to_hex_string(std::string_view row_sep = "\n") const {
2396 // Edge case.
2397 usize nr = rows();
2398 if (nr == 0) return std::string{};
2399
2400 // Set up the return string with enough space for the entire matrix.
2401 usize nc = cols();
2402 std::string result;
2403 result.reserve(nr * (nc / 4 + row_sep.size()));
2404
2405 // Append each row to the result string.
2406 for (auto i = 0uz; i < nr; ++i) {
2407 result += m_rows[i].to_hex_string();
2408 if (i + 1 < nr) result += row_sep;
2409 }
2410 return result;
2411 }
2412
2440 std::string to_compact_hex_string() const { return to_hex_string(" "); }
2441
2442 // The usual output stream operator for a bit-store
2443 constexpr std::ostream& operator<<(std::ostream& s) const { return s << to_string(); }
2444
2448
2457 friend constexpr bool operator==(BitMatrix const& lhs, BitMatrix const& rhs) {
2458 // Edge case.
2459 if (&lhs == &rhs) return true;
2460
2461 // Check the active words for equality.
2462 auto row_count = lhs.rows();
2463 if (rhs.rows() != row_count) return false;
2464 for (auto i = 0uz; i < row_count; ++i)
2465 if (lhs.m_rows[i] != rhs.m_rows[i]) return false;
2466
2467 // Made it
2468 return true;
2469 }
2470
2471private:
2472 // Runs a check to see that all the rows in a vector of rows have the same number of columns.
2473 constexpr bool check_rows(std::vector<row_type> const& rows) const {
2474 if (rows.empty()) return true;
2475 auto nc = rows[0].size();
2476 for (auto i = 1uz; i < rows.size(); ++i) {
2477 if (rows[i].size() != nc) return false;
2478 }
2479 return true;
2480 }
2481
2482 // Performs a single step of Danilevsky's algorithm to reduce a bit-matrix to Frobenius form.
2483 //
2484 // Frobenius form is block diagonal with one or more companion matrices along the diagonal. All matrices can be
2485 // reduced to this form via a sequence of similarity transformations. This methods performs a single one of those
2486 // transformations.
2487 //
2488 // This `frobenius_form` function calls here with an N x N bit-matrix. In each call the method concentrates on just
2489 // the top-left n x n sub-matrix. On the first call, n should be set to N. The method returns the top row of the
2490 // companion matrix that is the transformation of the bottom-right (n-k) x (n-k) sub-matrix. The caller can store
2491 // that result, decrement n, and call again on the smaller top-left sub-matrix. It may be that the whole matrix
2492 // gets reduced to a single companion matrix in one step and then there will be no need to call again.
2493 //
2494 // The method tries to transform the n x n top-left sub-matrix to a companion matrix working from its bottom-right
2495 // corner up. It stops when it gets to a point where the bottom-right (n-k) x (n-k) sub-matrix is in companion form
2496 // and returns the top row of that sub-matrix. The caller can store that result, decrement n, and call again on
2497 // the smaller top-left sub-matrix.
2498 //
2499 // NOTE: This method panics if the bit-matrix is not square.
2500 BitVector<Word> danilevsky_step(usize n) {
2501 gf2_assert(n <= rows(), "No top-left {} x {} sub-matrix in a matrix with {} rows", n, n, rows());
2502
2503 // Edge case: A 1 x 1 matrix is already in companion form.
2504 if (n == 1) return BitVector<Word>::constant(1, get(0, 0));
2505
2506 // Step k of algorithm attempts to reduce row k to companion form.
2507 // By construction, rows k+1 or later are already in companion form.
2508 auto k = n - 1;
2509 while (k > 0) {
2510 // If row k's sub-diagonal is all zeros we look for an earlier column with a 1.
2511 // If found, we swap that column here & then swap the equivalent rows to preserve similarity.
2512 if (!get(k, k - 1)) {
2513 for (auto j = 0uz; j < k - 1; ++j) {
2514 if (get(k, j)) {
2515 swap_rows(j, k - 1);
2516 swap_cols(j, k - 1);
2517 break;
2518 }
2519 }
2520 }
2521
2522 // No joy? Perhaps we have a companion matrix in the lower left corner and can return its top row?
2523 if (!get(k, k - 1)) break;
2524
2525 // Still no joy? The sub-diagonal is not all zeros so apply transform to make it so: self <- M^-1 * self *
2526 // M, where M is the identity matrix with the (k-1)'st row replaced by the k'th row of `self`. We can
2527 // sparsely represent M as just a clone of that k'th row of `self`.
2528 auto m = m_rows[k];
2529
2530 // Note the M^-1 is the same as M and self <- M^-1 * self just alters a few of our elements.
2531 for (auto j = 0uz; j < n; ++j) set(k - 1, j, dot(m, col(j)));
2532
2533 // We also use the sparsity of M when computing self <- self * M.
2534 for (auto i = 0uz; i < k; ++i) {
2535 for (auto j = 0uz; j < n; ++j) {
2536 auto tmp = get(i, k - 1) && m.get(j);
2537 if (j == k - 1) {
2538 set(i, j, tmp);
2539 } else {
2540 set(i, j, get(i, j) ^ tmp);
2541 }
2542 }
2543 }
2544
2545 // Now put row k into companion form of all zeros with one on the sub-diagonal.
2546 // All the rows below k are already in companion form.
2547 m_rows[k].set_all(false);
2548 set(k, k - 1);
2549
2550 // Done with row k
2551 k -= 1;
2552 }
2553
2554 // At this point, k == 0 OR the bit-matrix has non-removable zero on the sub-diagonal of row k.
2555 // Either way, the bottom-right (n-k) x (n-k) sub-matrix, starting at self[k][k], is in companion form.
2556 // We return the top row of that companion sub-matrix.
2557 auto top_row = BitVector<Word>::zeros(n - k);
2558 for (auto j = 0uz; j < n - k; ++j) top_row.set(j, get(k, k + j));
2559
2560 // Done
2561 return top_row;
2562 }
2563};
2564
2565// --------------------------------------------------------------------------------------------------------------------
2566// Matrix multiplication ...
2567// -------------------------------------------------------------------------------------------------------------------
2568
2570template<Unsigned Word, BitStore Rhs>
2571 requires std::same_as<typename Rhs::word_type, Word>
2572constexpr auto
2573dot(BitMatrix<Word> const& lhs, Rhs const& rhs) {
2574 gf2_assert_eq(lhs.cols(), rhs.size(), "Incompatible dimensions: {} != {}", lhs.cols(), rhs.size());
2575 auto n_rows = lhs.rows();
2576 auto result = BitVector<Word>::zeros(n_rows);
2577 for (auto i = 0uz; i < n_rows; ++i) {
2578 if (dot(lhs.row(i), rhs)) result.set(i, true);
2579 }
2580 return result;
2581}
2582
2584template<Unsigned Word, BitStore Rhs>
2585 requires std::same_as<typename Rhs::word_type, Word>
2586constexpr auto
2587operator*(BitMatrix<Word> const& lhs, Rhs const& rhs) {
2588 return dot(lhs, rhs);
2589}
2590
2594template<Unsigned Word, BitStore Lhs>
2595 requires std::same_as<typename Lhs::word_type, Word>
2596constexpr auto
2597dot(Lhs const& lhs, BitMatrix<Word> const& rhs) {
2598 gf2_assert_eq(lhs.size(), rhs.rows(), "Incompatible dimensions: {} != {}", lhs.size(), rhs.rows());
2599 auto n_cols = rhs.cols();
2600 auto result = BitVector<Word>::zeros(n_cols);
2601 for (auto j = 0uz; j < n_cols; ++j) {
2602 if (dot(lhs, rhs.col(j))) result.set(j, true);
2603 }
2604 return result;
2605}
2606
2610template<Unsigned Word, BitStore Lhs>
2611 requires std::same_as<typename Lhs::word_type, Word>
2612constexpr auto
2613operator*(Lhs const& lhs, BitMatrix<Word> const& rhs) {
2614 return dot(lhs, rhs);
2615}
2616
2618template<Unsigned Word>
2619constexpr auto
2620dot(BitMatrix<Word> const& lhs, BitMatrix<Word> const& rhs) {
2621 gf2_assert_eq(lhs.cols(), rhs.rows(), "Incompatible dimensions: {} != {}", lhs.cols(), rhs.rows());
2622
2623 auto n_rows = lhs.rows();
2624 auto n_cols = rhs.cols();
2625 auto result = BitMatrix<Word>::zeros(n_rows, n_cols);
2626
2627 // Row access is cheap, columns expensive, so arrange things to pull out columns as few times as possible.
2628 for (auto j = 0uz; j < n_cols; ++j) {
2629 auto rhs_col = rhs.col(j);
2630 for (auto i = 0uz; i < n_rows; ++i) {
2631 if (dot(lhs.row(i), rhs_col)) result.set(i, j, true);
2632 }
2633 }
2634 return result;
2635}
2636
2638template<Unsigned Word>
2639constexpr auto
2641 return dot(lhs, rhs);
2642}
2643
2644// --------------------------------------------------------------------------------------------------------------------
2645// Some utility methods to print multiple matrices & vectors side-by-side ...
2646// -------------------------------------------------------------------------------------------------------------------
2647
2649template<Unsigned Word, BitStore Rhs>
2650 requires std::same_as<typename Rhs::word_type, Word>
2651std::string
2652string_for(BitMatrix<Word> const& A, Rhs const& b) {
2653
2654 // If either is empty there was likely a bug somewhere!
2655 gf2_assert(!A.is_empty(), "Matrix A is empty which is likely an error!");
2656 gf2_assert(!b.is_empty(), "Vector b is empty which is likely an error!");
2657
2658 // Most rows we ever print
2659 auto nr = std::max(A.rows(), b.size());
2660
2661 // Space filler might be needed if the matrix has fewer rows than the vector
2662 std::string A_fill(A.cols(), ' ');
2663
2664 // Little lambda that turns a bool into a string
2665 auto b2s = [](bool x) { return x ? "1" : "0"; };
2666
2667 // Build the result string
2668 std::string result;
2669 for (auto r = 0uz; r < nr; ++r) {
2670 auto A_str = r < A.rows() ? A[r].to_string() : A_fill;
2671 auto b_str = r < b.size() ? b2s(b[r]) : " ";
2672 result += A_str + "\t" + b_str;
2673 if (r + 1 < nr) result += "\n";
2674 }
2675
2676 return result;
2677}
2678
2680template<Unsigned Word, BitStore Rhs>
2681 requires std::same_as<typename Rhs::word_type, Word>
2682std::string
2683string_for(BitMatrix<Word> const& A, Rhs const& b, Rhs const& c) {
2684
2685 // If either is empty there was likely a bug somewhere!
2686 gf2_assert(!A.is_empty(), "Matrix A is empty which is likely an error!");
2687 gf2_assert(!b.is_empty(), "Vector b is empty which is likely an error!");
2688 gf2_assert(!c.is_empty(), "Vector c is empty which is likely an error!");
2689
2690 // Most rows we ever print
2691 auto nr = std::max({A.rows(), b.size(), c.size()});
2692
2693 // Space filler might be needed if the matrix has fewer rows than the vector
2694 std::string A_fill(A.cols(), ' ');
2695
2696 // Little lambda that turns a bool into a string
2697 auto b2s = [](bool x) { return x ? "1" : "0"; };
2698
2699 // Build the result string
2700 std::string result;
2701 for (auto r = 0uz; r < nr; ++r) {
2702 auto A_str = r < A.rows() ? A[r].to_string() : A_fill;
2703 auto b_str = r < b.size() ? b2s(b[r]) : " ";
2704 auto c_str = r < c.size() ? b2s(c[r]) : " ";
2705 result += A_str + "\t" + b_str + "\t" + c_str;
2706 if (r + 1 < nr) result += "\n";
2707 }
2708
2709 return result;
2710}
2711
2713template<Unsigned Word, BitStore Rhs>
2714 requires std::same_as<typename Rhs::word_type, Word>
2715std::string
2716string_for(BitMatrix<Word> const& A, Rhs const& b, Rhs const& c, Rhs const& d) {
2717
2718 // If either is empty there was likely a bug somewhere!
2719 gf2_assert(!A.is_empty(), "Matrix A is empty which is likely an error!");
2720 gf2_assert(!b.is_empty(), "Vector b is empty which is likely an error!");
2721 gf2_assert(!c.is_empty(), "Vector c is empty which is likely an error!");
2722 gf2_assert(!d.is_empty(), "Vector d is empty which is likely an error!");
2723
2724 // Most rows we ever print
2725 auto nr = std::max({A.rows(), b.size(), c.size(), d.size()});
2726
2727 // Space filler might be needed if the matrix has fewer rows than the vector
2728 std::string A_fill(A.cols(), ' ');
2729
2730 // Little lambda that turns a bool into a string
2731 auto b2s = [](bool x) { return x ? "1" : "0"; };
2732
2733 // Build the result string
2734 std::string result;
2735 for (auto r = 0uz; r < nr; ++r) {
2736 auto A_str = r < A.rows() ? A[r].to_string() : A_fill;
2737 auto b_str = r < b.size() ? b2s(b[r]) : " ";
2738 auto c_str = r < c.size() ? b2s(c[r]) : " ";
2739 auto d_str = r < d.size() ? b2s(d[r]) : " ";
2740 result += A_str + "\t" + b_str + "\t" + c_str + "\t" + d_str;
2741 if (r + 1 < nr) result += "\n";
2742 }
2743
2744 return result;
2745}
2746
2748template<Unsigned Word>
2749std::string
2751
2752 // If either is empty there was likely a bug somewhere!
2753 gf2_assert(!A.is_empty(), "Matrix A is empty which is likely an error!");
2754 gf2_assert(!B.is_empty(), "Matrix B is empty which is likely an error!");
2755
2756 // Most rows we ever print
2757 auto nr = std::max(A.rows(), B.rows());
2758
2759 // Space filler might be needed if the matrix has fewer rows than the vector
2760 std::string A_fill(A.cols(), ' ');
2761 std::string B_fill(B.cols(), ' ');
2762
2763 // Build the result string
2764 std::string result;
2765 for (auto r = 0uz; r < nr; ++r) {
2766 auto A_str = r < A.rows() ? A[r].to_string() : A_fill;
2767 auto B_str = r < B.rows() ? B[r].to_string() : B_fill;
2768 result += A_str + "\t" + B_str;
2769 if (r + 1 < nr) result += "\n";
2770 }
2771
2772 return result;
2773}
2774
2776template<Unsigned Word>
2777std::string
2779
2780 // If either is empty there was likely a bug somewhere!
2781 gf2_assert(!A.is_empty(), "Matrix A is empty which is likely an error!");
2782 gf2_assert(!B.is_empty(), "Matrix B is empty which is likely an error!");
2783 gf2_assert(!C.is_empty(), "Matrix C is empty which is likely an error!");
2784
2785 // Most rows we ever print
2786 auto nr = std::max({A.rows(), B.rows(), C.rows()});
2787
2788 // Space filler might be needed if the matrix has fewer rows than the vector
2789 std::string A_fill(A.cols(), ' ');
2790 std::string B_fill(B.cols(), ' ');
2791 std::string C_fill(C.cols(), ' ');
2792
2793 // Build the result string
2794 std::string result;
2795 for (auto r = 0uz; r < nr; ++r) {
2796 auto A_str = r < A.rows() ? A[r].to_string() : A_fill;
2797 auto B_str = r < B.rows() ? B[r].to_string() : B_fill;
2798 auto C_str = r < C.rows() ? C[r].to_string() : C_fill;
2799 result += A_str + "\t" + B_str + "\t" + C_str;
2800 if (r + 1 < nr) result += "\n";
2801 }
2802
2803 return result;
2804}
2805
2806} // namespace gf2
2807
2808// --------------------------------------------------------------------------------------------------------------------
2809// Specialises `std::formatter` to handle bit-matrices ...
2810// -------------------------------------------------------------------------------------------------------------------
2811
2830template<gf2::Unsigned Word>
2831struct std::formatter<gf2::BitMatrix<Word>> {
2832
2834 constexpr auto parse(std::format_parse_context const& ctx) {
2835 auto it = ctx.begin();
2836 while (it != ctx.end() && *it != '}') {
2837 switch (*it) {
2838 case 'p': m_pretty = true; break;
2839 case 'x': m_hex = true; break;
2840 default: m_error = true;
2841 }
2842 ++it;
2843 }
2844 return it;
2845 }
2846
2848 template<class FormatContext>
2849 auto format(gf2::BitMatrix<Word> const& rhs, FormatContext& ctx) const {
2850 // Was there a format specification error?
2851 if (m_error) return std::format_to(ctx.out(), "'UNRECOGNIZED FORMAT SPECIFIER FOR BIT-MATRIX'");
2852
2853 // Special handling requested?
2854 if (m_hex) return std::format_to(ctx.out(), "{}", rhs.to_hex_string());
2855 if (m_pretty) return std::format_to(ctx.out(), "{}", rhs.to_pretty_string());
2856
2857 // Default
2858 return std::format_to(ctx.out(), "{}", rhs.to_string());
2859 }
2860
2861 bool m_hex = false;
2862 bool m_pretty = false;
2863 bool m_error = false;
2864};
Polynomials over GF(2). See the BitPolynomial page for more details.
Vectors over GF(2) with compactly stored bit-elements in a standard vector of primitive unsigned word...
#define gf2_assert_eq(lhs, rhs,...)
A confirmation macro that checks the equality of two values lhs and rhs. On failure,...
Definition assert.h:113
#define gf2_debug_assert(cond,...)
A confirmation macro that checks a boolean condition cond. On failure, the confirmation prints an e...
Definition assert.h:68
#define gf2_assert(cond,...)
A confirmation macro that checks a boolean condition cond. On failure, the confirmation prints an e...
Definition assert.h:98
The BitGauss class is a Gaussian elimination solver for systems of linear equations over GF(2)....
Definition BitGauss.h:45
The BitLU class provides the LU decomposition for bit-matrices.
Definition BitLU.h:30
A dynamically-sized matrix over GF(2) stored as a vector of bit-vectors representing the rows of the ...
Definition BitMatrix.h:33
constexpr void swap_cols(usize i, usize j)
Swaps columns i and j of the bit-matrix.
Definition BitMatrix.h:1616
constexpr void set(usize r, usize c, bool val=true)
Sets the bit at row r and column c to the bool value val. The default is to set the bit to true.
Definition BitMatrix.h:882
constexpr void set_sub_diagonal(usize d, bool val=true)
Sets the elements on sub-diagonal d of a square bit-matrix to the boolean value val.
Definition BitMatrix.h:1142
static std::optional< BitMatrix > from_string(std::string_view s)
Factory method to construct a bit-matrix from a string that is assumed to be a sequence of rows.
Definition BitMatrix.h:582
constexpr bool is_symmetric() const
Returns true if this is a symmetric square bit-matrix.
Definition BitMatrix.h:697
constexpr auto operator|(BitMatrix< Word > const &rhs) const
TheOR of this with a bit-matrix rhs returning a new bit-matrix.
Definition BitMatrix.h:2200
constexpr BitMatrix & append_rows(BitMatrix< Word > const &src)
Appends all the rows from the src bit-matrix onto the end of this bit-matrix by copying them and retu...
Definition BitMatrix.h:1285
constexpr BitMatrix transposed() const
Returns a new bit-matrix that is the transpose of this one.
Definition BitMatrix.h:1654
static BitPolynomial< Word > companion_matrix_characteristic_polynomial(BitVector< Word > const &top_row)
Class method to return the characteristic polynomial of a companion matrix as a gf2::BitPolynomial.
Definition BitMatrix.h:2040
constexpr void clear()
Removes all the elements from the bit-matrix.
Definition BitMatrix.h:1210
constexpr void swap_rows(usize i, usize j)
Swaps rows i and j of the bit-matrix.
Definition BitMatrix.h:1599
constexpr usize count_zeros() const
Returns the number of zero elements in the bit-matrix.
Definition BitMatrix.h:733
static BitMatrix biased_random(usize m, usize n, double p)
Factory method to generate a bit-matrix of size m x n where the elements are from independent fair co...
Definition BitMatrix.h:348
constexpr void operator&=(BitMatrix< Word > const &rhs)
In-place AND with a bit-matrix rhs.
Definition BitMatrix.h:2120
constexpr bool operator()(usize r, usize c) const
Returns the value of the bit at row r and column c as a bool.
Definition BitMatrix.h:864
constexpr void set_all(bool value=true)
Sets all the elements of the bit-matrix to the specified boolean value.
Definition BitMatrix.h:1041
constexpr BitMatrix(std::vector< BitVector< Word > > &&rows)
Construct a bit-matrix by moving the given rows.
Definition BitMatrix.h:112
constexpr bool is_empty() const
Is this an empty bit-matrix?
Definition BitMatrix.h:638
std::string to_binary_string(std::string_view row_sep="\n", std::string_view bit_sep="", std::string_view row_prefix="", std::string_view row_suffix="") const
Returns a configurable binary string representation for this bit-matrix.
Definition BitMatrix.h:2312
constexpr BitMatrix(usize n)
Constructs the n x n square bit-matrix with all the elements set to 0.
Definition BitMatrix.h:66
constexpr const row_type & row(usize r) const
Returns a read-only reference to the row at index r.
Definition BitMatrix.h:939
constexpr BitMatrix(usize m, usize n)
Constructs the m x n bit-matrix with all the elements set to 0.
Definition BitMatrix.h:79
constexpr bool get(usize r, usize c) const
Returns true if the element at row r and column c is set.
Definition BitMatrix.h:846
constexpr usize rows() const
Returns the number of rows in the bit-matrix.
Definition BitMatrix.h:629
constexpr BitMatrix upper() const
Returns an independent clone of the upper triangular part of the bit-matrix.
Definition BitMatrix.h:1504
constexpr std::optional< BitVector< Word > > remove_row()
Removes a row off the end of the bit-matrix and returns it or std::nullopt if the bit-matrix is empty...
Definition BitMatrix.h:1366
constexpr auto operator+(BitMatrix< Word > const &rhs) const
Returns a new bit-matrix that is *this + rhs which is *this ^ rhs in GF(2).
Definition BitMatrix.h:2269
BitLU< Word > LU() const
Returns the LU decomposition of the bit-matrix.
Definition BitMatrix.h:1938
constexpr BitMatrix sub_matrix(usize r_start, usize r_end, usize c_start, usize c_end) const
Returns an independent clone of the sub-matrix delimited by the given row and column ranges.
Definition BitMatrix.h:1428
std::string to_compact_hex_string() const
Returns a compact "hex" string representation of the bit-matrix.
Definition BitMatrix.h:2440
constexpr std::optional< BitVector< Word > > remove_col()
Removes a column off the right of the bit-matrix and returns it or std::nullopt if the bit-matrix is ...
Definition BitMatrix.h:1402
std::string to_string() const
Returns the default string representation for this bit-matrix.
Definition BitMatrix.h:2351
constexpr row_type & row(usize r)
Returns a read-write reference to the row at index r.
Definition BitMatrix.h:957
constexpr usize count_ones() const
Returns the number of one elements in the bit-matrix.
Definition BitMatrix.h:720
constexpr bool any() const
Returns true if any element of the bit-matrix is set.
Definition BitMatrix.h:786
constexpr usize cols() const
Returns the number of columns in the bit-matrix.
Definition BitMatrix.h:632
constexpr void flip_super_diagonal(usize d)
Flips all the elements on the super-diagonal d of a square bit-matrix.
Definition BitMatrix.h:1124
static BitMatrix random(usize m, usize n, double p=0.5, std::uint64_t seed=0)
Factory method to generate a bit-matrix of size m x n where the elements are picked at random.
Definition BitMatrix.h:264
constexpr BitMatrix & append_cols(BitMatrix< Word > const &src)
Appends all the columns from the src bit-matrix onto the right of this bit-matrix so M -> M|src and r...
Definition BitMatrix.h:1347
static constexpr BitMatrix alternating(usize m)
Factory method to create the m x m square bit-matrix with alternating elements.
Definition BitMatrix.h:180
static constexpr BitMatrix zero(usize m)
Factory method to create the m x m square "zero" bit-matrix.
Definition BitMatrix.h:374
auto solver_for(Rhs const &b) const
Returns the Gaussian elimination solver for this bit-matrix and the passed r.h.s. vector b.
Definition BitMatrix.h:1959
static BitMatrix right_shift(usize n, usize p)
Constructs the n x n shift-right by p places BitMatrix.
Definition BitMatrix.h:437
constexpr BitRef< row_type > operator()(usize r, usize c)
Returns the bit at row r and column c as a BitRef reference which can be used to set the bit.
Definition BitMatrix.h:900
constexpr void flip(usize r, usize c)
Flips the bit at row r and column c.
Definition BitMatrix.h:917
constexpr bool is_square() const
Returns true if this a square bit-matrix? Note that empty bit-matrices are NOT considered square.
Definition BitMatrix.h:655
constexpr void flip_diagonal()
Flips all the elements on the main diagonal of a square bit-matrix.
Definition BitMatrix.h:1088
std::string to_hex_string(std::string_view row_sep="\n") const
"Returns the "hex" string representation of the bit-matrix
Definition BitMatrix.h:2395
constexpr BitMatrix strictly_upper() const
Returns an independent clone of the strictly upper triangular part of the bit-matrix.
Definition BitMatrix.h:1546
constexpr void operator-=(BitMatrix< Word > const &rhs)
In-place difference with a bit-matrix rhs – in GF(2) subtraction is the same as XOR.
Definition BitMatrix.h:2254
constexpr BitMatrix & append_col(Store const &col)
Appends a single column col onto the right of the bit-matrix so M -> M|col and returns a reference to...
Definition BitMatrix.h:1328
constexpr auto operator-(BitMatrix< Word > const &rhs) const
Returns a new bit-matrix that is *this - rhs which is *this ^ rhs in GF(2).
Definition BitMatrix.h:2288
constexpr usize size() const
Returns the totalnumber of elements in the bit-matrix.
Definition BitMatrix.h:635
BitPolynomial< Word > characteristic_polynomial() const
Returns the characteristic polynomial of any square bit-matrix as a gf2::BitPolynomial.
Definition BitMatrix.h:2001
BitVector< Word > to_reduced_echelon_form()
Transforms the bit-matrix to reduced row-echelon form (in-place).
Definition BitMatrix.h:1830
static BitMatrix right_rotation(usize n, usize p)
Constructs the n x n rotate-right by p places matrix.
Definition BitMatrix.h:472
constexpr bool none() const
Returns true if no elements of the bit-matrix are set.
Definition BitMatrix.h:824
constexpr bool all() const
Returns true if all elements of the bit-matrix are set.
Definition BitMatrix.h:805
constexpr void replace_sub_matrix(usize top, usize left, BitMatrix< Word > const &src)
Replaces the sub-matrix starting at row top and column left with a copy of the sub-matrix src.
Definition BitMatrix.h:1460
constexpr usize count_ones_on_diagonal() const
Returns the number of ones on the main diagonal of the bit-matrix.
Definition BitMatrix.h:745
static constexpr BitMatrix from_outer_product(Lhs const &u, Rhs const &v)
Factory method to create the m x n bit-matrix from the outer product of the given bit-stores.
Definition BitMatrix.h:193
constexpr void make_square(usize n)
Makes an arbitrary rectangular bit-matrix into a square BitMatrix.
Definition BitMatrix.h:1222
Word word_type
The underlying unsigned word type used to store the bits.
Definition BitMatrix.h:43
static BitPolynomial< Word > frobenius_matrix_characteristic_polynomial(std::vector< BitVector< Word > > const &top_rows)
Class method that returns the characteristic polynomial of a Frobenius matrix as a gf2::BitPolynomial...
Definition BitMatrix.h:2016
static BitMatrix left_rotation(usize n, usize p)
Constructs the n x n rotate-left by p places matrix.
Definition BitMatrix.h:453
static constexpr BitMatrix from(usize m, usize n, std::invocable< usize, usize > auto f)
Factory method to construct an bit-matrix by repeatedly calling f(i, j) for each index pair.
Definition BitMatrix.h:229
constexpr void resize(usize r, usize c)
Resize the bit-matrix to r rows and c columns.
Definition BitMatrix.h:1189
std::vector< BitVector< Word > > frobenius_form() const
Returns the Frobenius form of this bit-matrix in compact top-row only form.
Definition BitMatrix.h:2065
static constexpr BitMatrix ones(usize m, usize n)
Factory method to create the m x n bit-matrix with all the elements set to 1.
Definition BitMatrix.h:145
constexpr BitMatrix unit_lower() const
Returns an independent clone of the unit lower triangular part of the bit-matrix.
Definition BitMatrix.h:1562
friend constexpr bool operator==(BitMatrix const &lhs, BitMatrix const &rhs)
Equality operator checks that any pair of bit-matrices are equal in content.
Definition BitMatrix.h:2457
static std::optional< BitMatrix > from_col_store(Store const &v, usize c)
Factory method to reshape a bit-vector that is assumed to a sequence of c columns into a bit-matrix.
Definition BitMatrix.h:536
static constexpr BitMatrix ones(usize m)
Factory method to create the m x m square bit-matrix with all the elements set to 1.
Definition BitMatrix.h:157
constexpr BitMatrix & append_row(Store const &row)
Appends a single row onto the end of the bit-matrix by copying it and returns a reference to the this...
Definition BitMatrix.h:1243
constexpr void flip_all()
Flips all the elements of the bit-matrix.
Definition BitMatrix.h:1053
static BitMatrix seeded_random(usize m, std::uint64_t seed)
Factory method to generate a square bit-matrix of size m x m where the elements are from independent ...
Definition BitMatrix.h:333
static constexpr BitMatrix companion(Store const &top_row)
Constructs a square companion BitMatrix with a copy of the given top row and a sub-diagonal of 1s.
Definition BitMatrix.h:402
auto x_for(Rhs const &b) const
Returns a solution to the system of linear equations A.x_ = b or std::nullopt if there are none.
Definition BitMatrix.h:1974
constexpr bool is_identity() const
Returns true if this is the identity bit-matrix.
Definition BitMatrix.h:678
constexpr auto operator^(BitMatrix< Word > const &rhs) const
TheXOR of this with a bit-matrix rhs returning a new bit-matrix.
Definition BitMatrix.h:2158
static constexpr BitMatrix identity(usize m)
Factory method to create the m x m identity bit-matrix.
Definition BitMatrix.h:383
constexpr void flip_sub_diagonal(usize d)
Flips all the elements on the sub-diagonal d of a square bit-matrix.
Definition BitMatrix.h:1160
constexpr BitMatrix to_the(usize n, bool n_is_log2=false) const
Returns a new bit-matrix that is this one raised to some power n or 2^n.
Definition BitMatrix.h:1713
constexpr bool trace() const
Returns the "sum" of the main diagonal elements of the bit-matrix.
Definition BitMatrix.h:767
constexpr BitMatrix strictly_lower() const
Returns an independent clone of the strictly lower triangular part of the bit-matrix.
Definition BitMatrix.h:1530
constexpr void set_diagonal(bool val=true)
Sets the main diagonal of a square bit-matrix to the boolean value val.
Definition BitMatrix.h:1072
static std::optional< BitMatrix > from_row_store(Store const &v, usize r)
Factory method to reshape a bit-vector v that is assumed to a sequence of r rows into a bit-matrix.
Definition BitMatrix.h:499
constexpr void add_identity()
Adds the identity bit-matrix to the bit-matrix.
Definition BitMatrix.h:1633
static constexpr BitMatrix from_outer_sum(Lhs const &u, Rhs const &v)
Factory method to create the m x n bit-matrix from the outer sum of the given bit-stores.
Definition BitMatrix.h:213
static constexpr BitMatrix zeros(usize m, usize n)
Factory method to create the m x n zero bit-matrix with all the elements set to 0.
Definition BitMatrix.h:127
static constexpr double probability_singular(usize n)
Class method that returns the probability that a square n x n bit-matrix is singular if each element ...
Definition BitMatrix.h:1924
constexpr void transpose()
Transposes a square bit-matrix in place.
Definition BitMatrix.h:1679
constexpr BitMatrix & append_rows(BitMatrix< Word > &&src)
Appends all the rows from the src bit-matrix onto the end of this bit-matrix by moving them and retur...
Definition BitMatrix.h:1306
constexpr void set_super_diagonal(usize d, bool val=true)
Sets the elements on super-diagonal d of a square bit-matrix to the boolean value val.
Definition BitMatrix.h:1106
static constexpr BitMatrix zeros(usize m)
Factory method to create the m x m square bit-matrix with all the elements set to 0.
Definition BitMatrix.h:136
static BitMatrix biased_random(usize m, double p)
Factory method to generate a square bit-matrix of size m x m where the elements are from independent ...
Definition BitMatrix.h:361
static constexpr double probability_invertible(usize n)
Class method that returns the probability that a square n x n bit-matrix is invertible if each elemen...
Definition BitMatrix.h:1893
constexpr BitMatrix unit_upper() const
Returns an independent clone of the unit upper triangular part of the bit-matrix.
Definition BitMatrix.h:1578
constexpr auto operator&(BitMatrix< Word > const &rhs) const
TheAND of this with a bit-matrix rhs returning a new bit-matrix.
Definition BitMatrix.h:2179
constexpr void operator+=(BitMatrix< Word > const &rhs)
In-place addition with a bit-matrix rhs – in GF(2) addition is the same as XOR.
Definition BitMatrix.h:2239
std::string to_compact_binary_string() const
Returns a one-line minimal binary string representation for this bit-matrix.
Definition BitMatrix.h:2340
std::string to_pretty_string() const
Returns the default pretty string representation for this bit-matrix.
Definition BitMatrix.h:2366
constexpr std::optional< BitMatrix< Word > > remove_rows(usize k)
Removes k rows off the end of the bit-matrix and returns them as a new bit-matrix or std::nullopt if ...
Definition BitMatrix.h:1383
constexpr void operator^=(BitMatrix< Word > const &rhs)
In-place XOR with a bit-matrix rhs.
Definition BitMatrix.h:2101
constexpr auto operator~()
Returns a copy of this bit-matrix with all the elements flipped.
Definition BitMatrix.h:2216
static constexpr BitMatrix alternating(usize m, usize n)
Factory method to create the m x n bit-matrix with alternating elements.
Definition BitMatrix.h:166
constexpr const row_type & operator[](usize r) const
Returns a read-only reference to the row at index r.
Definition BitMatrix.h:974
constexpr bool is_zero() const
Returns true if this a square zero bit-matrix?
Definition BitMatrix.h:668
constexpr BitMatrix()
The default constructor creates an empty bit-matrix with no rows or columns.
Definition BitMatrix.h:55
static BitMatrix seeded_random(usize m, usize n, std::uint64_t seed)
Factory method to generate a bit-matrix of size m x n where the elements are from independent fair co...
Definition BitMatrix.h:314
constexpr BitVector< Word > col(usize c) const
Returns a clone of the elements in column c from the bit-matrix as an independent bit-vector.
Definition BitMatrix.h:1018
constexpr BitMatrix lower() const
Returns an independent clone of the lower triangular part of the bit-matrix.
Definition BitMatrix.h:1480
static constexpr BitMatrix left_shift(usize n, usize p)
Constructs the n x n shift-left by p places BitMatrix.
Definition BitMatrix.h:421
BitVector< Word > to_echelon_form()
Transforms an arbitrary shaped, non-empty, bit-matrix to row-echelon form (in-place).
Definition BitMatrix.h:1772
constexpr BitMatrix(std::vector< row_type > const &rows)
Construct a bit-matrix by copying a given set of rows which can be any BitStore subclass.
Definition BitMatrix.h:94
std::optional< BitMatrix > inverse() const
Returns the inverse of a square bit-matrix or std::nullopt if the matrix is singular.
Definition BitMatrix.h:1860
constexpr BitMatrix & append_row(Store &&row)
Appends a single row onto the end of the bit-matrix by moving it and returns a reference to the this ...
Definition BitMatrix.h:1266
constexpr row_type & operator[](usize r)
Returns a read-write reference to the row at index r.
Definition BitMatrix.h:992
constexpr void operator|=(BitMatrix< Word > const &rhs)
In-place OR with a bit-matrix rhs.
Definition BitMatrix.h:2139
A BitPolynomial represents a polynomial over GF(2) where we store the polynomial coefficients in a bi...
Definition BitPolynomial.h:30
static constexpr BitPolynomial zero()
Factory method to return the "zero" bit-polynomial p(x) := 0.
Definition BitPolynomial.h:95
A BitRef is a proxy class to reference a single bit in a bit-store.
Definition BitRef.h:33
A dynamically-sized vector over GF(2) with bit elements compactly stored in a standard vector of prim...
Definition BitVector.h:23
static std::optional< BitVector > from_string(std::string_view sv)
Definition BitVector.h:436
constexpr bool get(usize i) const
Returns true if the bit at the given index i is set, false otherwise.
Definition BitVector.h:978
static constexpr BitVector zeros(usize n)
Factory method to generate a bit-vector of length n where the elements are all 0.
Definition BitVector.h:196
static constexpr BitVector constant(usize n, bool value)
Factory method to generate a bit-vector of length n where the elements are set to value.
Definition BitVector.h:215
constexpr usize size() const
Returns the number of bit-elements in the bit-vector.
Definition BitVector.h:50
static constexpr BitVector ones(usize n)
Factory method to generate a bit-vector of length n where the elements are all 1.
Definition BitVector.h:204
constexpr bool any() const
Returns true if at least one bit in the bit-vector is set, false otherwise.
Definition BitVector.h:1138
constexpr auto flip(usize i)
Flips the value of the bit-element i and returns this for chaining.
Definition BitVector.h:1083
static constexpr BitVector alternating(usize n)
Factory method to generate a bit-vector of length n looking like 101010....
Definition BitVector.h:239
The namespace for the gf2 library.
Definition assert.h:119
constexpr std::optional< usize > first_set(Store const &store)
Returns the index of the first set bit in the bit-store or {} if no bits are set.
Definition BitStore.h:841
constexpr void swap(Store &store, usize i0, usize i1)
Swaps the bits in the bit-store at indices i0 and i1.
Definition BitStore.h:307
constexpr auto operator*(BitMatrix< Word > const &lhs, Rhs const &rhs)
Operator form for bit-matrix, bit-store multiplication, M * v, returning a new bit-vector.
Definition BitMatrix.h:2587
std::ostream & operator<<(Store const &store, std::ostream &s)
The usual output stream operator for a bit-store.
Definition BitStore.h:1711
std::string string_for(BitMatrix< Word > const &A, Rhs const &b)
Returns a string that shows a bit-matrix and a bit-vector side-by-side.
Definition BitMatrix.h:2652
constexpr auto dot(BitMatrix< Word > const &lhs, Rhs const &rhs)
Bit-matrix, bit-store multiplication, M * v, returning a new bit-vector.
Definition BitMatrix.h:2573
static constexpr auto span(Store const &store, usize begin, usize end)
Constructs a read-only bit-span over the const bit-store store for its bits in the range [begin,...
Definition BitStore.h:1277
constexpr void copy(Src src, Store &store)
Copies all the bits from any unsigned integral src value to an equal-sized bit-store.
Definition BitStore.h:485
std::size_t usize
Word type alias for the platform's "native"-sized unsigned integer.
Definition Unsigned.h:42
STL namespace.
constexpr auto parse(std::format_parse_context const &ctx)
Parse a bit-store format specifier where where we recognize {:p} and {:x}.
Definition BitMatrix.h:2834
auto format(gf2::BitMatrix< Word > const &rhs, FormatContext &ctx) const
Push out a formatted bit-matrix using the various to_string(...) methods in the class.
Definition BitMatrix.h:2849