1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/fec/algebra/BeliefPropagationDecoding.h"
namespace td {
BeliefPropagationDecoding::BeliefPropagationDecoding(size_t symbols_count, size_t symbol_size)
: max_equation_count_{static_cast<size_t>(static_cast<double>(symbols_count) * 1.1 + 5)}
, C_{symbols_count, symbol_size}
, D_{max_equation_count_, symbol_size} {
equations_.reserve(max_equation_count_);
symbols_.resize(symbols_count);
edges_.resize(1);
}
Slice BeliefPropagationDecoding::get_symbol(uint32 symbol_id) const {
CHECK(symbols_[symbol_id].is_ready);
return C_.row(symbol_id);
}
void BeliefPropagationDecoding::add_equation(Span<uint32> symbol_ids, Slice data) {
if (equations_.size() >= D_.rows()) {
MatrixGF256 new_D(D_.rows() * 2, D_.cols());
new_D.set_from(D_, 0, 0);
D_ = std::move(new_D);
}
CHECK(symbol_ids.size() != 0);
uint32 equation_id = static_cast<uint32>(equations_.size());
D_.row_set(equation_id, data);
EquationInfo equation;
for (auto symbol_id : symbol_ids) {
CHECK(symbol_id < symbols_.size());
auto &symbol = symbols_[symbol_id];
if (symbol.is_ready) {
D_.row_add(equation_id, C_.row(symbol_id));
} else {
equation.symbols_xor ^= symbol_id;
equation.symbols_count++;
edges_.push_back({equation_id, symbol.head_});
symbol.head_ = uint32(edges_.size() - 1);
}
}
if (equation.symbols_count == 0) {
return;
}
equations_.push_back(equation);
if (equation.symbols_count == 1) {
ready_equations_.push_back(equation_id);
loop();
}
}
bool BeliefPropagationDecoding::is_ready() const {
return ready_symbols().size() == C_.rows();
}
Span<uint32> BeliefPropagationDecoding::ready_symbols() const {
return ready_symbols_;
}
void BeliefPropagationDecoding::loop() {
while (!is_ready() && !ready_equations_.empty()) {
auto equation_id = ready_equations_.back();
ready_equations_.pop_back();
auto &equation = equations_[equation_id];
LOG_CHECK(equation.symbols_count <= 1) << equation.symbols_count;
if (equation.symbols_count == 0) {
continue;
}
auto symbol_id = equation.symbols_xor;
auto &symbol = symbols_[symbol_id];
LOG_CHECK(symbol_id < symbols_.size())
<< equation.symbols_xor << " " << equation.symbols_count << " " << equation_id;
if (symbol.is_ready) {
continue;
}
C_.row_set(symbol_id, D_.row(equation_id));
symbol.is_ready = true;
ready_symbols_.push_back(symbol_id);
for (auto i = symbol.head_; i != 0;) {
auto &edge = edges_[i];
auto next_equation_id = edge.value;
i = edge.next;
D_.row_add(next_equation_id, C_.row(symbol_id));
auto &next_equation = equations_[next_equation_id];
next_equation.symbols_xor ^= symbol_id;
next_equation.symbols_count--;
if (next_equation.symbols_count == 1) {
ready_equations_.push_back(next_equation_id);
}
}
}
}
} // namespace td

View file

@ -0,0 +1,62 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/buffer.h"
#include "td/utils/Span.h"
#include "td/fec/algebra/MatrixGF256.h"
namespace td {
class BeliefPropagationDecoding {
public:
explicit BeliefPropagationDecoding(size_t symbols_count, size_t symbol_size);
void add_equation(Span<uint32> symbol_ids, Slice data);
bool is_ready() const;
Span<uint32> ready_symbols() const;
Slice get_symbol(uint32 symbol_id) const;
private:
struct SymbolInfo {
bool is_ready{false};
uint32 head_ = 0;
};
struct EquationInfo {
uint32 symbols_xor{0};
uint32 symbols_count{0};
};
size_t max_equation_count_;
MatrixGF256 C_;
MatrixGF256 D_;
std::vector<SymbolInfo> symbols_;
std::vector<EquationInfo> equations_;
std::vector<uint32> ready_equations_;
std::vector<uint32> ready_symbols_;
struct Edge {
uint32 value;
uint32 next;
};
std::vector<Edge> edges_;
void loop();
};
} // namespace td

View file

@ -0,0 +1,59 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/fec/algebra/GaussianElimination.h"
namespace td {
Result<MatrixGF256> GaussianElimination::run(MatrixGF256 A, MatrixGF256 D) {
const size_t cols = A.cols();
const size_t rows = A.rows();
CHECK(cols <= rows);
std::vector<uint32> row_perm(rows);
for (uint32 i = 0; i < rows; i++) {
row_perm[i] = i;
}
for (size_t row = 0; row < cols; row++) {
size_t non_zero_row = row;
for (; non_zero_row < rows && A.get(row_perm[non_zero_row], row).is_zero(); non_zero_row++) {
}
if (non_zero_row == rows) {
return Status::Error("Non solvable");
}
if (non_zero_row != row) {
std::swap(row_perm[non_zero_row], row_perm[row]);
}
auto mul = A.get(row_perm[row], row).inverse();
A.row_multiply(row_perm[row], mul);
D.row_multiply(row_perm[row], mul);
CHECK(A.get(row_perm[row], row).value() == 1);
for (size_t zero_row = 0; zero_row < rows; zero_row++) {
if (zero_row == row) {
continue;
}
auto x = A.get(row_perm[zero_row], row);
if (!x.is_zero()) {
A.row_add_mul(row_perm[zero_row], row_perm[row], x);
D.row_add_mul(row_perm[zero_row], row_perm[row], x);
}
}
}
return D.apply_row_permutation(row_perm);
}
} // namespace td

View file

@ -0,0 +1,28 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/fec/algebra/MatrixGF256.h"
namespace td {
class GaussianElimination {
public:
static Result<MatrixGF256> run(MatrixGF256 A, MatrixGF256 D);
};
} // namespace td

View file

@ -0,0 +1,186 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/fec/algebra/InactivationDecoding.h"
#include "td/utils/logging.h"
#include <algorithm>
namespace td {
InactivationDecodingResult InactivationDecoding::run() {
init();
loop();
LOG(DEBUG) << tag("A_small.cols", L_.cols() - p_rows_.size()) << tag("Total columns", L_.cols()) << tag("PI", PI_)
<< tag("A_small.cols - PI", L_.cols() - PI_ - p_rows_.size());
for (uint32 row = 0; row < rows_; row++) {
if (!was_row_[row]) {
p_rows_.push_back(row);
}
}
uint32 side = narrow_cast<uint32>(p_cols_.size());
std::reverse(inactive_cols_.begin(), inactive_cols_.end());
for (auto col : inactive_cols_) {
p_cols_.push_back(col);
}
for (uint32 i = 0; i < PI_; i++) {
p_cols_.push_back(cols_ + i);
}
check(side);
return {side, std::move(p_rows_), std::move(p_cols_)};
}
void InactivationDecoding::init() {
was_row_ = vector<bool>(rows_, false);
was_col_ = vector<bool>(cols_, false);
col_cnt_ = vector<uint32>(cols_, 0);
row_cnt_ = vector<uint32>(rows_, 0);
row_xor_ = vector<uint32>(rows_, 0);
L_.generate([&](auto row, auto col) {
if (col >= cols_) {
return;
}
col_cnt_[col]++;
row_cnt_[row]++;
row_xor_[row] ^= col;
});
sort_rows();
}
void InactivationDecoding::loop() {
while (row_cnt_offset_[1] != rows_) {
auto row = sorted_rows_[row_cnt_offset_[1]];
uint32 col = choose_col(row);
LOG_CHECK(col_cnt_[col] >= 1) << col;
auto cnt = row_cnt_[row];
CHECK(row_cnt_offset_[cnt] == row_cnt_offset_[1]);
CHECK(row_pos_[row] == row_cnt_offset_[1]);
p_cols_.push_back(col);
p_rows_.push_back(row);
if (cnt == 1) {
inactivate_col(col);
} else {
for (auto x : L_rows_.col(row)) {
if (x >= cols_ || was_col_[x]) {
continue;
}
if (x != col) {
inactive_cols_.push_back(x);
}
inactivate_col(x);
}
}
was_row_[row] = true;
}
}
void InactivationDecoding::check_sorted() {
for (size_t i = 0; i < rows_; i++) {
CHECK(sorted_rows_[row_pos_[i]] == i);
}
for (size_t i = 1; i < rows_; i++) {
CHECK(row_cnt_[sorted_rows_[i - 1]] <= row_cnt_[sorted_rows_[i]]);
}
for (size_t i = 1; i <= cols_ + 1; i++) {
CHECK(row_cnt_offset_[i - 1] <= row_cnt_offset_[i]);
}
for (size_t i = 0; i < rows_; i++) {
auto pos = row_pos_[i];
auto cnt = row_cnt_[i];
CHECK(pos >= row_cnt_offset_[cnt]);
CHECK(pos < row_cnt_offset_[cnt + 1]);
}
}
uint32 InactivationDecoding::choose_col(uint32 row) {
auto cnt = row_cnt_[row];
if (cnt == 1) {
return row_xor_[row];
}
uint32 best_col = uint32(-1);
for (auto col : L_rows_.col(row)) {
if (col >= cols_ || was_col_[col]) {
continue;
}
DCHECK(col_cnt_[col] >= 1);
if (best_col == uint32(-1) || col_cnt_[col] < col_cnt_[best_col]) {
best_col = col;
}
}
DCHECK(best_col != uint32(-1));
return best_col;
}
void InactivationDecoding::inactivate_col(uint32 col) {
was_col_[col] = true;
for (auto row : L_.col(col)) {
if (was_row_[row]) {
continue;
}
auto pos = row_pos_[row];
DCHECK(sorted_rows_[pos] == row);
auto cnt = row_cnt_[row];
LOG_DCHECK(cnt >= 1) << row << " " << col;
auto offset = row_cnt_offset_[cnt];
std::swap(sorted_rows_[pos], sorted_rows_[offset]);
row_pos_[sorted_rows_[pos]] = pos;
row_pos_[sorted_rows_[offset]] = offset;
row_cnt_offset_[cnt]++;
row_cnt_[row]--;
row_xor_[row] ^= col;
}
}
void InactivationDecoding::sort_rows() {
vector<uint32> offset(cols_ + 2, 0);
for (size_t i = 0; i < rows_; i++) {
offset[row_cnt_[i] + 1]++;
}
for (size_t i = 1; i <= cols_ + 1; i++) {
offset[i] += offset[i - 1];
}
row_cnt_offset_ = offset;
sorted_rows_.resize(rows_);
row_pos_.resize(rows_);
for (uint32 i = 0; i < rows_; i++) {
auto pos = offset[row_cnt_[i]]++;
sorted_rows_[pos] = i;
row_pos_[i] = pos;
}
}
void InactivationDecoding::check(uint32 side) {
auto inv_p_cols = inverse_permutation(p_cols_);
auto inv_p_rows = inverse_permutation(p_rows_);
for (uint32 i = 0; i < side; i++) {
CHECK(inv_p_cols[p_cols_[i]] == i);
auto col = L_.col(p_cols_[i]);
CHECK(col.size() >= 1);
for (auto x : col) {
CHECK(inv_p_rows[x] >= i);
}
}
}
} // namespace td

View file

@ -0,0 +1,73 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/fec/algebra/SparseMatrixGF2.h"
namespace td {
struct InactivationDecodingResult {
uint32 size;
vector<uint32> p_rows;
vector<uint32> p_cols;
};
class InactivationDecoding {
public:
InactivationDecoding(const SparseMatrixGF2 &L, uint32 PI) : L_(L), PI_(PI) {
}
InactivationDecodingResult run();
private:
const SparseMatrixGF2 &L_;
uint32 PI_;
const SparseMatrixGF2 L_rows_{L_.transpose()};
const uint32 cols_ = L_.cols() - PI_;
const uint32 rows_ = L_.rows();
vector<bool> was_row_;
vector<bool> was_col_;
vector<uint32> col_cnt_;
vector<uint32> row_cnt_;
vector<uint32> row_xor_;
vector<uint32> sorted_rows_;
vector<uint32> row_cnt_offset_;
vector<uint32> row_pos_;
vector<uint32> p_rows_;
vector<uint32> p_cols_;
vector<uint32> inactive_cols_;
void init();
void loop();
void check_sorted();
uint32 choose_col(uint32 row);
void inactivate_col(uint32 col);
void sort_rows();
void check(uint32 side);
};
} // namespace td

View file

@ -0,0 +1,20 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/fec/algebra/MatrixGF2.h"
char disable_linker_warning_about_empty_file_matrixgf2_cpp TD_UNUSED;

View file

@ -0,0 +1,103 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/fec/algebra/MatrixGF256.h"
namespace td {
class MatrixGF2 {
public:
MatrixGF2(size_t rows, size_t cols) : rows_(rows), cols_(cols) {
CHECK(Simd::alignment() % 8 == 0);
stride_ = ((cols_ + 7) / 8 + Simd::alignment() - 1) / Simd::alignment() * Simd::alignment();
CHECK(stride_ * 8 >= cols_);
storage_ = std::make_unique<uint8[]>(stride_ * rows + Simd::alignment() - 1);
matrix_ = storage_.get();
while (!Simd::is_aligned_pointer(matrix_)) {
matrix_++;
}
CHECK(Simd::is_aligned_pointer(matrix_));
CHECK(Simd::is_aligned_pointer(matrix_ + stride_));
CHECK(static_cast<size_t>(matrix_ - storage_.get()) < Simd::alignment());
}
void set_zero() {
std::fill(matrix_, matrix_ + stride_ * rows_, 0);
}
size_t rows() const {
return rows_;
}
size_t cols() const {
return cols_;
}
void set_one(size_t row, size_t col) {
DCHECK(row < rows_ && col < cols_);
matrix_[row * stride_ + col / 8] |= uint8(1 << (col % 8));
}
bool get(size_t row, size_t col) const {
DCHECK(row < rows_ && col < cols_);
return (matrix_[row * stride_ + col / 8] & (uint8(1) << (col % 8))) != 0;
}
// row(a) += row(b)
void row_add(size_t a, size_t b) {
row_add(row_ptr(a), row_ptr(b));
}
void row_add(size_t a, Slice b) {
DCHECK(b.size() == stride_);
row_add(row_ptr(a), b.ubegin());
}
Slice row(size_t a) const {
return Slice(row_ptr(a), stride_);
}
MutableSlice row(size_t a) {
return MutableSlice(row_ptr(a), stride_);
}
void row_set(size_t a, Slice b) {
row(a).copy_from(b);
}
MatrixGF256 to_gf256() const {
MatrixGF256 res(rows(), cols());
for (size_t i = 0; i < rows(); i++) {
Simd::gf256_from_gf2(res.row(i).data(), row(i).data(), ((cols_ + 7) / 8 + 3) / 4 * 4);
}
return res;
}
private:
uint8* matrix_;
size_t rows_;
size_t cols_;
size_t stride_;
std::unique_ptr<uint8[]> storage_;
uint8* row_ptr(size_t row) {
return matrix_ + stride_ * row;
}
const uint8* row_ptr(size_t row) const {
return matrix_ + stride_ * row;
}
void row_add(uint8* pa, const uint8* pb) {
Simd::gf256_add(pa, pb, stride_);
}
};
} // namespace td

View file

@ -0,0 +1,20 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/fec/algebra/MatrixGF256.h"
char disable_linker_warning_about_empty_file_matrixgf256_cpp TD_UNUSED;

View file

@ -0,0 +1,199 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/fec/algebra/Octet.h"
#include "td/fec/algebra/Simd.h"
#include "td/utils/Span.h"
#include "td/utils/format.h"
namespace td {
class MatrixGF256 {
public:
MatrixGF256(size_t rows, size_t cols) : rows_(rows), cols_(cols) {
stride_ = (cols_ + Simd::alignment() - 1) / Simd::alignment() * Simd::alignment();
storage_ = std::make_unique<uint8[]>(stride_ * rows + Simd::alignment() - 1);
matrix_ = storage_.get();
while (!Simd::is_aligned_pointer(matrix_)) {
matrix_++;
}
CHECK(Simd::is_aligned_pointer(matrix_));
CHECK(Simd::is_aligned_pointer(matrix_ + stride_));
CHECK(static_cast<size_t>(matrix_ - storage_.get()) < Simd::alignment());
}
void set_zero() {
std::fill(matrix_, matrix_ + stride_ * rows_, 0);
}
size_t rows() const {
return rows_;
}
size_t cols() const {
return cols_;
}
MatrixGF256 apply_row_permutation(Span<uint32> permutation) {
MatrixGF256 res(rows_, cols_);
for (size_t row = 0; row < rows_; row++) {
res.row(row).copy_from(this->row(permutation[row]));
}
return res;
}
Octet get(size_t row, size_t col) const {
DCHECK(row < rows_ && col < cols_);
return Octet(matrix_[row * stride_ + col]);
}
void set(size_t row, size_t col, Octet o) {
DCHECK(row < rows_ && col < cols_);
matrix_[row * stride_ + col] = o.value();
}
void row_multiply(size_t row, Octet o) {
uint8* p = row_ptr(row);
Simd::gf256_mul(p, o.value(), stride_);
}
Slice row(size_t row) const {
return Slice(row_ptr(row), cols());
}
MutableSlice row(size_t row) {
return MutableSlice(row_ptr(row), cols());
}
template <class M>
void set_from(const M& m, size_t row_offset, size_t col_offset) {
auto to = block_view(row_offset, col_offset, rows() - row_offset, cols() - col_offset);
for (size_t i = 0; i < m.rows(); i++) {
to.row(i).copy_from(m.row(i));
}
}
MatrixGF256 copy() {
MatrixGF256 res(rows(), cols());
res.set_from(*this, 0, 0);
return res;
}
void add(const MatrixGF256& m) {
CHECK(m.rows() == rows());
CHECK(m.cols() == cols());
for (size_t i = 0; i < m.rows(); i++) {
auto* to = row_ptr(i);
auto* from = m.row_ptr(i);
row_add(to, from);
}
}
// row(a) += row(b) * m
void row_add_mul(size_t a, size_t b, Octet m) {
row_add_mul(row_ptr(a), row_ptr(b), m);
}
void row_add_mul(size_t a, Slice b, Octet m) {
row_add_mul(row_ptr(a), b.ubegin(), m);
}
// row(a) += row(b)
void row_add(size_t a, size_t b) {
row_add(row_ptr(a), row_ptr(b));
}
void row_add(size_t a, Slice b) {
row_add(row_ptr(a), b.ubegin());
}
void row_set(size_t a, Slice b) {
row(a).copy_from(b);
}
class BlockView {
public:
BlockView(size_t row_offset, size_t col_offset, size_t row_size, size_t col_size, MatrixGF256& m)
: row_offset_(row_offset), col_offset_(col_offset), row_size_(row_size), col_size_(col_size), m_(m) {
}
size_t cols() const {
return col_size_;
}
size_t rows() const {
return row_size_;
}
Slice row(size_t row) const {
return m_.row(row_offset_ + row).remove_prefix(col_offset_);
}
MutableSlice row(size_t row) {
return m_.row(row_offset_ + row).remove_prefix(col_offset_);
}
private:
size_t row_offset_;
size_t col_offset_;
size_t row_size_;
size_t col_size_;
MatrixGF256& m_;
};
BlockView block_view(size_t row_offset, size_t col_offset, size_t row_size, size_t col_size) {
return BlockView(row_offset, col_offset, row_size, col_size, *this);
}
private:
uint8* matrix_;
size_t rows_;
size_t cols_;
size_t stride_;
std::unique_ptr<uint8[]> storage_;
uint8* row_ptr(size_t row) {
return matrix_ + stride_ * row;
}
const uint8* row_ptr(size_t row) const {
return matrix_ + stride_ * row;
}
void row_add_mul(uint8* ap, const uint8* bp, Octet m) {
uint8 u = m.value();
if (u == 0) {
return;
}
if (u == 1) {
return row_add(ap, bp);
}
Simd::gf256_add_mul(ap, bp, u, stride_);
}
void row_add(uint8* ap, const uint8* bp) {
Simd::gf256_add(ap, bp, stride_);
}
};
inline StringBuilder& operator<<(StringBuilder& sb, const MatrixGF256& m) {
sb << "\n";
for (uint32 i = 0; i < m.rows(); i++) {
auto row = m.row(i);
for (uint32 j = 0; j < m.cols(); j++) {
uint8 x = row[j];
sb << " " << format::hex_digit(x / 16) << format::hex_digit(x % 16);
}
sb << "\n";
}
return sb;
}
} // namespace td

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,152 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/logging.h"
#include <array>
#include <iomanip>
namespace td {
class Octet {
public:
Octet() = default;
explicit Octet(const uint8 val) : data_(val) {
}
uint8 value() const {
return data_;
}
Octet& operator+=(const Octet a) {
data_ ^= a.data_;
return *this;
}
friend Octet operator+(Octet lhs, const Octet rhs) {
return lhs += rhs;
}
Octet& operator-=(const Octet a) {
return *this += a;
}
friend Octet operator-(Octet lhs, const Octet rhs) {
return lhs + rhs;
}
Octet& operator*=(const Octet a) {
if (data_ != 0 && a.data_ != 0) {
data_ = oct_exp(oct_log(uint8(data_ - 1)) + oct_log(uint8(a.data_ - 1)));
} else {
data_ = 0;
}
return *this;
}
friend Octet operator*(Octet lhs, const Octet rhs) {
return lhs *= rhs;
}
Octet& operator/=(const Octet a) {
if (a.data_ != 0 && data_ != 0) {
data_ = oct_exp(oct_log(uint8(data_ - 1)) - oct_log(uint8(a.data_ - 1)) + 255);
}
return *this;
}
friend Octet operator/(Octet lhs, const Octet rhs) {
return lhs /= rhs;
}
Octet inverse() const {
return Octet(oct_exp(255 - oct_log(uint8(data_ - 1))));
}
bool operator==(const Octet a) const {
return data_ == a.data_;
}
bool operator!=(const Octet a) const {
return data_ != a.data_;
}
bool operator<(const Octet a) const {
return data_ < a.data_;
}
bool operator>(const Octet a) const {
return data_ > a.data_;
}
bool is_zero() const {
return data_ == 0;
}
friend std::ostream& operator<<(std::ostream& os, const Octet m) {
os << std::hex << static_cast<uint32>(m.data_);
return os;
}
static constexpr uint8 oct_exp(uint32 data) {
constexpr std::array<uint8, 510> precalc = {
{1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117,
234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119,
238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190,
97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127,
254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189,
103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92,
184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146,
57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255,
227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141,
7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86,
172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88,
176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1, 2, 4, 8, 16, 32, 64, 128, 29,
58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96,
192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20,
40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137,
15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217,
175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199,
147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132,
21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191,
99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149,
55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83,
166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61,
122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108,
216, 173, 71, 142}};
return precalc[data];
}
static constexpr uint8 oct_log(uint8 data) {
constexpr std::array<uint8, 255> precalc = {
{0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239,
129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240,
18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139,
98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241,
210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186,
61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140,
128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146,
217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242,
86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164,
118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187,
204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213,
233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175}};
return precalc[data];
}
static const uint8 OctMulLo[256][16];
static const uint8 OctMulHi[256][16];
private:
uint8 data_;
};
} // namespace td

266
tdfec/td/fec/algebra/Simd.h Normal file
View file

@ -0,0 +1,266 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/misc.h"
#include "td/fec/algebra/Octet.h"
#if __SSSE3__
#define TD_SSE3 1
#endif
#if __AVX2__
#define TD_AVX2 1
#define TD_SSE3 1
#endif
#if TD_AVX2
#include <immintrin.h> /* avx2 */
#elif TD_SSE3
#include <tmmintrin.h> /* ssse3 */
#endif
namespace td {
class Simd_null {
public:
static constexpr size_t alignment() {
return 32; // gf256_from_gf2 relies on 32 alignment
}
static std::string get_name() {
return "Without simd";
}
static bool is_aligned_pointer(const void *ptr) {
return ::td::is_aligned_pointer<alignment()>(ptr);
}
static void gf256_add(void *a, const void *b, size_t size) {
DCHECK(is_aligned_pointer(a));
DCHECK(is_aligned_pointer(b));
uint8 *ap = reinterpret_cast<uint8 *>(a);
const uint8 *bp = reinterpret_cast<const uint8 *>(b);
for (size_t i = 0; i < size; i++) {
ap[i] ^= bp[i];
}
}
static void gf256_mul(void *a, uint8 u, size_t size) {
DCHECK(is_aligned_pointer(a));
uint8 *ap = reinterpret_cast<uint8 *>(a);
for (size_t i = 0; i < size; i++) {
ap[i] = (Octet(ap[i]) * Octet(u)).value();
}
}
static void gf256_add_mul(void *a, const void *b, uint8 u, size_t size) {
DCHECK(is_aligned_pointer(a));
DCHECK(is_aligned_pointer(b));
uint8 *ap = reinterpret_cast<uint8 *>(a);
const uint8 *bp = reinterpret_cast<const uint8 *>(b);
for (size_t i = 0; i < size; i++) {
ap[i] = (Octet(ap[i]) + Octet(bp[i]) * Octet(u)).value();
}
}
static void gf256_from_gf2(void *a, const void *b, size_t size) {
uint8 *ap = reinterpret_cast<uint8 *>(a);
const uint8 *bp = reinterpret_cast<const uint8 *>(b);
size *= 8;
for (size_t i = 0; i < size; i++, ap++) {
*ap = (bp[i / 8] >> (i % 8)) & 1;
}
}
};
#if TD_SSE3
class Simd_sse : public Simd_null {
public:
static constexpr size_t alignment() {
return 32;
}
static std::string get_name() {
return "With SSE";
}
static bool is_aligned_pointer(const void *ptr) {
return ::td::is_aligned_pointer<alignment()>(ptr);
}
static void gf256_add(void *a, const void *b, size_t size) {
DCHECK(is_aligned_pointer(a));
DCHECK(is_aligned_pointer(b));
uint8 *ap = reinterpret_cast<uint8 *>(a);
const uint8 *bp = reinterpret_cast<const uint8 *>(b);
__m128i *ap128 = reinterpret_cast<__m128i *>(ap);
const __m128i *bp128 = reinterpret_cast<const __m128i *>(bp);
for (size_t idx = 0; idx < size; idx += 16) {
_mm_storeu_si128(ap128, _mm_xor_si128(_mm_loadu_si128(ap128), _mm_loadu_si128(bp128)));
ap128++;
bp128++;
}
}
static void gf256_mul(void *a, uint8 u, size_t size) {
DCHECK(is_aligned_pointer(a));
uint8 *ap = reinterpret_cast<uint8 *>(a);
const __m128i mask = _mm_set1_epi8(0x0f);
const __m128i urow_hi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(Octet::OctMulHi[u]));
const __m128i urow_lo = _mm_loadu_si128(reinterpret_cast<const __m128i *>(Octet::OctMulLo[u]));
__m128i *ap128 = reinterpret_cast<__m128i *>(ap);
for (size_t idx = 0; idx < size; idx += 16) {
__m128i ax = _mm_loadu_si128(ap128);
__m128i lo = _mm_and_si128(ax, mask);
ax = _mm_srli_epi64(ax, 4);
__m128i hi = _mm_and_si128(ax, mask);
lo = _mm_shuffle_epi8(urow_lo, lo);
hi = _mm_shuffle_epi8(urow_hi, hi);
_mm_storeu_si128(ap128, _mm_xor_si128(lo, hi));
ap128++;
}
}
static void gf256_add_mul(void *a, const void *b, uint8 u, size_t size) {
DCHECK(is_aligned_pointer(a));
DCHECK(is_aligned_pointer(b));
uint8 *ap = reinterpret_cast<uint8 *>(a);
const uint8 *bp = reinterpret_cast<const uint8 *>(b);
const __m128i mask = _mm_set1_epi8(0x0f);
const __m128i urow_hi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(Octet::OctMulHi[u]));
const __m128i urow_lo = _mm_loadu_si128(reinterpret_cast<const __m128i *>(Octet::OctMulLo[u]));
__m128i *ap128 = reinterpret_cast<__m128i *>(ap);
const __m128i *bp128 = reinterpret_cast<const __m128i *>(bp);
for (size_t idx = 0; idx < size; idx += 16) {
__m128i bx = _mm_loadu_si128(bp128++);
__m128i lo = _mm_and_si128(bx, mask);
bx = _mm_srli_epi64(bx, 4);
__m128i hi = _mm_and_si128(bx, mask);
lo = _mm_shuffle_epi8(urow_lo, lo);
hi = _mm_shuffle_epi8(urow_hi, hi);
_mm_storeu_si128(ap128, _mm_xor_si128(_mm_loadu_si128(ap128), _mm_xor_si128(lo, hi)));
ap128++;
}
}
};
#endif // SSSE3
#ifdef TD_AVX2
class Simd_avx : public Simd_sse {
public:
static std::string get_name() {
return "With AVX";
}
static void gf256_add(void *a, const void *b, size_t size) {
DCHECK(is_aligned_pointer(a));
DCHECK(is_aligned_pointer(b));
uint8 *ap = reinterpret_cast<uint8 *>(a);
const uint8 *bp = reinterpret_cast<const uint8 *>(b);
__m256i *ap256 = reinterpret_cast<__m256i *>(ap);
const __m256i *bp256 = reinterpret_cast<const __m256i *>(bp);
for (size_t idx = 0; idx < size; idx += 32) {
_mm256_storeu_si256(ap256, _mm256_xor_si256(_mm256_loadu_si256(ap256), _mm256_loadu_si256(bp256)));
ap256++;
bp256++;
}
}
static __m256i get_mask(const uint32 mask) {
// abcd -> abcd * 8
__m256i vmask(_mm256_set1_epi32(mask));
// abcd * 8 -> aaaaaaaabbbbbbbbccccccccdddddddd
const __m256i shuffle(
_mm256_setr_epi64x(0x0000000000000000, 0x0101010101010101, 0x0202020202020202, 0x0303030303030303));
vmask = _mm256_shuffle_epi8(vmask, shuffle);
const __m256i bit_mask(_mm256_set1_epi64x(0x7fbfdfeff7fbfdfe));
vmask = _mm256_or_si256(vmask, bit_mask);
return _mm256_and_si256(_mm256_cmpeq_epi8(vmask, _mm256_set1_epi64x(-1)), _mm256_set1_epi8(1));
}
static void gf256_from_gf2(void *a, const void *b, size_t size) {
DCHECK(is_aligned_pointer(a));
DCHECK(size % 4 == 0);
__m256i *ap256 = reinterpret_cast<__m256i *>(a);
const uint32 *bp = reinterpret_cast<const uint32 *>(b);
size /= 4;
for (size_t i = 0; i < size; i++, bp++, ap256++) {
*ap256 = get_mask(*bp);
}
}
static __attribute__((noinline)) void gf256_mul(void *a, uint8 u, size_t size) {
const __m128i urow_hi_small = _mm_load_si128(reinterpret_cast<const __m128i *>(Octet::OctMulHi[u]));
const __m256i urow_hi = _mm256_broadcastsi128_si256(urow_hi_small);
const __m128i urow_lo_small = _mm_load_si128(reinterpret_cast<const __m128i *>(Octet::OctMulLo[u]));
const __m256i urow_lo = _mm256_broadcastsi128_si256(urow_lo_small);
const __m256i mask = _mm256_set1_epi8(0x0f);
__m256i *ap256 = (__m256i *)a;
for (size_t idx = 0; idx < size; idx += 32) {
__m256i ax = _mm256_load_si256(ap256);
__m256i lo = _mm256_and_si256(ax, mask);
ax = _mm256_srli_epi64(ax, 4);
__m256i hi = _mm256_and_si256(ax, mask);
lo = _mm256_shuffle_epi8(urow_lo, lo);
hi = _mm256_shuffle_epi8(urow_hi, hi);
_mm256_store_si256(ap256, _mm256_xor_si256(lo, hi));
ap256++;
}
}
static __attribute__((noinline)) void gf256_add_mul(void *a, const void *b, uint8 u, size_t size) {
const __m128i urow_hi_small = _mm_load_si128(reinterpret_cast<const __m128i *>(Octet::OctMulHi[u]));
const __m256i urow_hi = _mm256_broadcastsi128_si256(urow_hi_small);
const __m128i urow_lo_small = _mm_load_si128(reinterpret_cast<const __m128i *>(Octet::OctMulLo[u]));
const __m256i urow_lo = _mm256_broadcastsi128_si256(urow_lo_small);
const __m256i mask = _mm256_set1_epi8(0x0f);
__m256i *ap256 = (__m256i *)a;
const __m256i *bp256 = (const __m256i *)b;
for (size_t idx = 0; idx < size; idx += 32) {
__m256i bx = _mm256_load_si256(bp256++);
__m256i lo = _mm256_and_si256(bx, mask);
bx = _mm256_srli_epi64(bx, 4);
__m256i hi = _mm256_and_si256(bx, mask);
lo = _mm256_shuffle_epi8(urow_lo, lo);
hi = _mm256_shuffle_epi8(urow_hi, hi);
_mm256_store_si256(ap256, _mm256_xor_si256(_mm256_load_si256(ap256), _mm256_xor_si256(lo, hi)));
ap256++;
}
}
};
#endif // AVX2
#if TD_AVX2
using Simd = Simd_avx;
#elif TD_SSE3
using Simd = Simd_sse;
#else
using Simd = Simd_null;
#endif
} // namespace td

View file

@ -0,0 +1,20 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/fec/algebra/SparseMatrixGF2.h"
char disable_linker_warning_about_empty_file_sparsematrixgf2_cpp TD_UNUSED;

View file

@ -0,0 +1,277 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/fec/algebra/MatrixGF2.h"
namespace td {
inline std::vector<uint32> inverse_permutation(Span<uint32> p) {
std::vector<uint32> res(p.size());
for (size_t i = 0; i < p.size(); i++) {
res[p[i]] = narrow_cast<uint32>(i);
}
return res;
}
class IdentityGenerator {
public:
IdentityGenerator(uint32 N) : N(N) {
}
template <class F>
void generate(F&& f) const {
for (uint32 col = 0; col < N; col++) {
f(col, col);
}
}
uint32 non_zeroes() const {
return N;
}
uint32 cols() const {
return N;
}
uint32 rows() const {
return N;
}
private:
uint32 N;
};
template <class GeneratorT>
class PermutationGenerator {
public:
PermutationGenerator(const GeneratorT& m, Span<uint32> p) : m_(m), p_(inverse_permutation(p)) {
}
uint32 non_zeroes() const {
return m_.non_zeroes();
}
uint32 rows() const {
return m_.rows();
}
uint32 cols() const {
return m_.cols();
}
template <class F>
void generate(F&& f) const {
m_.generate([&f, this](auto row, auto col) { f(row, p_[col]); });
}
private:
const GeneratorT& m_;
std::vector<uint32> p_;
};
template <class GeneratorT>
class TransposeGenerator {
public:
explicit TransposeGenerator(const GeneratorT& m) : m_(m) {
}
uint32 non_zeroes() const {
return m_.non_zeroes();
}
uint32 rows() const {
return m_.cols();
}
uint32 cols() const {
return m_.rows();
}
template <class F>
void generate(F&& f) const {
m_.generate([&f](auto row, auto col) { f(col, row); });
}
private:
const GeneratorT& m_;
std::vector<uint32> p_;
};
class SparseMatrixGF2 {
public:
uint32 non_zeroes() const {
return narrow_cast<uint32>(data_.size());
}
Span<uint32> col(uint32 i) const {
return Span<uint32>(data_.data() + col_offset_[i], col_size(i));
}
uint32 col_size(uint32 i) const {
return col_offset_[i + 1] - col_offset_[i];
}
uint32 cols() const {
return cols_;
}
uint32 rows() const {
return rows_;
}
template <class F>
void generate(F&& f) const {
return block_for_each(0, 0, rows_, cols_, f);
}
template <class F>
void block_for_each(uint32 row_from, uint32 col_from, uint32 row_size, uint32 col_size, F&& f) const {
auto col_till = col_from + col_size;
auto row_till = row_from + row_size;
for (uint32 col_i = col_from; col_i < col_till; col_i++) {
auto col_span = col(col_i);
auto* it = row_from == 0 ? col_span.begin() : std::lower_bound(col_span.begin(), col_span.end(), row_from);
while (it != col_span.end() && *it < row_till) {
f(*it - row_from, col_i - col_from);
it++;
}
}
}
MatrixGF2 block_dense(uint32 row_from, uint32 col_from, uint32 row_size, uint32 col_size) const {
MatrixGF2 res(row_size, col_size);
res.set_zero();
block_for_each(row_from, col_from, row_size, col_size, [&](auto row, auto col) { res.set_one(row, col); });
return res;
}
template <class GeneratorT>
explicit SparseMatrixGF2(GeneratorT&& generator) : rows_(generator.rows()), cols_(generator.cols()) {
data_.resize(generator.non_zeroes());
col_offset_.resize(cols_ + 1, 0);
generator.generate([&](uint32 row, uint32 col) {
LOG_DCHECK(row < rows_ && col < cols_) << "(" << row << "," << col << ") (" << rows_ << "," << cols_ << ")";
col_offset_[col + 1]++;
});
for (uint32 i = 1; i < col_offset_.size(); i++) {
col_offset_[i] += col_offset_[i - 1];
}
auto col_pos = col_offset_;
generator.generate([&](uint32 row, uint32 col) { data_[col_pos[col]++] = row; });
for (uint32 col_i = 0; col_i < cols_; col_i++) {
auto c = col(col_i);
for (size_t j = 1; j < c.size(); j++) {
LOG_DCHECK(c[j] > c[j - 1]) << c[j] << " > " << c[j - 1] << tag("row", col_i);
}
}
}
class BlockView {
public:
BlockView(uint32 row_offset, uint32 col_offset, uint32 row_size, uint32 col_size, const SparseMatrixGF2& m)
: row_offset_(row_offset), col_offset_(col_offset), row_size_(row_size), col_size_(col_size), m_(m) {
}
uint32 cols() const {
return col_size_;
}
uint32 rows() const {
return row_size_;
}
uint32 non_zeroes() const {
uint32 res = 0;
m_.block_for_each(row_offset_, col_offset_, row_size_, col_size_, [&res](auto row, auto col) { res++; });
return res;
}
template <class F>
void generate(F&& f) const {
m_.block_for_each(row_offset_, col_offset_, row_size_, col_size_, [&f](auto row, auto col) { f(row, col); });
}
private:
uint32 row_offset_;
uint32 col_offset_;
uint32 row_size_;
uint32 col_size_;
const SparseMatrixGF2& m_;
};
SparseMatrixGF2 block_sparse(uint32 row_from, uint32 col_from, uint32 row_size, uint32 col_size) const {
return SparseMatrixGF2(BlockView(row_from, col_from, row_size, col_size, *this));
}
SparseMatrixGF2 transpose() const {
return SparseMatrixGF2(TransposeGenerator<SparseMatrixGF2>(*this));
}
SparseMatrixGF2 apply_col_permutation(Span<uint32> p) const {
return SparseMatrixGF2(PermutationGenerator<SparseMatrixGF2>(*this, p));
}
SparseMatrixGF2 apply_row_permutation(Span<uint32> p) const {
return transpose().apply_col_permutation(p).transpose();
}
private:
uint32 rows_{0};
uint32 cols_{0};
std::vector<uint32> data_;
std::vector<uint32> col_offset_;
};
template <class M>
M operator*(const SparseMatrixGF2& a, const M& b) {
M res(a.rows(), b.cols());
res.set_zero();
a.generate([&](auto row, auto col) { res.row_add(row, b.row(col)); });
return res;
}
template <class... ArgsT>
class BlockGenerator {
public:
BlockGenerator(uint32 rows, uint32 cols, ArgsT&&... args)
: rows_(rows), cols_(cols), tuple_(std::forward_as_tuple(std::forward<ArgsT>(args)...)) {
}
template <class F>
void generate(F&& f) const {
uint32 row_offset = 0;
uint32 next_row_offset = 0;
uint32 col_offset = 0;
tuple_for_each(tuple_, [&](auto& g) {
if (col_offset == 0) {
next_row_offset = row_offset + g.rows();
} else {
CHECK(next_row_offset == row_offset + g.rows());
}
g.generate([&](auto row, auto col) { f(row_offset + row, col_offset + col); });
col_offset += g.cols();
if (col_offset >= cols_) {
CHECK(col_offset == cols_);
col_offset = 0;
row_offset = next_row_offset;
}
});
}
uint32 non_zeroes() const {
uint32 res = 0;
tuple_for_each(tuple_, [&](auto& g) { res += g.non_zeroes(); });
return res;
}
uint32 cols() const {
return cols_;
}
uint32 rows() const {
return rows_;
}
private:
uint32 rows_;
uint32 cols_;
std::tuple<ArgsT...> tuple_;
};
template <class... ArgsT>
auto block_generator(uint32 rows, uint32 cols, ArgsT&&... args) {
return BlockGenerator<ArgsT...>(rows, cols, std::forward<ArgsT>(args)...);
}
} // namespace td