/* 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 . Copyright 2017-2019 Telegram Systems LLP */ #pragma once #include "td/fec/algebra/MatrixGF2.h" namespace td { inline std::vector inverse_permutation(Span p) { std::vector res(p.size()); for (size_t i = 0; i < p.size(); i++) { res[p[i]] = narrow_cast(i); } return res; } class IdentityGenerator { public: IdentityGenerator(uint32 N) : N(N) { } template 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 PermutationGenerator { public: PermutationGenerator(const GeneratorT& m, Span 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 void generate(F&& f) const { m_.generate([&f, this](auto row, auto col) { f(row, p_[col]); }); } private: const GeneratorT& m_; std::vector p_; }; template 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 void generate(F&& f) const { m_.generate([&f](auto row, auto col) { f(col, row); }); } private: const GeneratorT& m_; std::vector p_; }; class SparseMatrixGF2 { public: uint32 non_zeroes() const { return narrow_cast(data_.size()); } Span col(uint32 i) const { return Span(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 void generate(F&& f) const { return block_for_each(0, 0, rows_, cols_, f); } template 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 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 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(*this)); } SparseMatrixGF2 apply_col_permutation(Span p) const { return SparseMatrixGF2(PermutationGenerator(*this, p)); } SparseMatrixGF2 apply_row_permutation(Span p) const { return transpose().apply_col_permutation(p).transpose(); } private: uint32 rows_{0}; uint32 cols_{0}; std::vector data_; std::vector col_offset_; }; template 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 BlockGenerator { public: BlockGenerator(uint32 rows, uint32 cols, ArgsT&&... args) : rows_(rows), cols_(cols), tuple_(std::forward_as_tuple(std::forward(args)...)) { } template 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 tuple_; }; template auto block_generator(uint32 rows, uint32 cols, ArgsT&&... args) { return BlockGenerator(rows, cols, std::forward(args)...); } } // namespace td