NicheLibrary

This documentation is automatically generated by NotLeonian/competitive-verifier (forked from competitive-verifier/competitive-verifier)

View the Project on GitHub NotLeonian/NicheLibrary

:heavy_check_mark: verify/standalone-dynamic-matrix-rank.test.cpp

Depends on

Code

// competitive-verifier: STANDALONE

#include <cassert>
#include <vector>

#include "../math/matrix/dynamic-matrix-rank.hpp"

namespace {
struct F2 {
    int value;

    F2(int value = 0) : value(value & 1) {}

    F2 &operator+=(const F2 &other) {
        value ^= other.value;
        return *this;
    }

    F2 &operator-=(const F2 &other) {
        value ^= other.value;
        return *this;
    }

    F2 &operator*=(const F2 &other) {
        value &= other.value;
        return *this;
    }

    F2 &operator/=(const F2 &other) {
        assert(other.value == 1);
        return *this;
    }

    [[maybe_unused]] friend F2 operator+(F2 lhs, const F2 &rhs) {
        return lhs += rhs;
    }

    [[maybe_unused]] friend F2 operator-(F2 lhs, const F2 &rhs) {
        return lhs -= rhs;
    }

    friend F2 operator*(F2 lhs, const F2 &rhs) { return lhs *= rhs; }

    friend F2 operator/(F2 lhs, const F2 &rhs) { return lhs /= rhs; }

    friend bool operator==(const F2 &lhs, const F2 &rhs) {
        return lhs.value == rhs.value;
    }

    friend bool operator!=(const F2 &lhs, const F2 &rhs) {
        return lhs.value != rhs.value;
    }
};

template <class T> int brute_rank(std::vector<std::vector<T>> matrix) {
    const int h = static_cast<int>(matrix.size());
    if (h == 0) {
        return 0;
    }
    const int w = static_cast<int>(matrix[0].size());
    int rank = 0;
    for (int column = 0; column < w; ++column) {
        int pivot = -1;
        for (int row = rank; row < h; ++row) {
            if (matrix[row][column] != T()) {
                pivot = row;
                break;
            }
        }
        if (pivot < 0) {
            continue;
        }
        if (pivot != rank) {
            matrix[pivot].swap(matrix[rank]);
        }
        const T inverse = T(1) / matrix[rank][column];
        for (int j = column; j < w; ++j) {
            matrix[rank][j] *= inverse;
        }
        for (int row = rank + 1; row < h; ++row) {
            const T factor = matrix[row][column];
            if (factor == T()) {
                continue;
            }
            for (int j = column; j < w; ++j) {
                matrix[row][j] -= matrix[rank][j] * factor;
            }
        }
        ++rank;
        if (rank == h) {
            break;
        }
    }
    return rank;
}

std::vector<std::vector<F2>> matrix_from_mask(int h, int w, int mask) {
    std::vector<std::vector<F2>> matrix(h, std::vector<F2>(w, F2()));
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            matrix[i][j] = F2((mask >> (i * w + j)) & 1);
        }
    }
    return matrix;
}

std::vector<F2> vector_from_mask(int n, int mask) {
    std::vector<F2> values(n, F2());
    for (int i = 0; i < n; ++i) {
        values[i] = F2((mask >> i) & 1);
    }
    return values;
}

std::vector<std::vector<F2>>
apply_rank_one(std::vector<std::vector<F2>> matrix,
               const std::vector<F2> &column_vector,
               const std::vector<F2> &row_vector) {
    const int h = static_cast<int>(matrix.size());
    const int w = h == 0 ? 0 : static_cast<int>(matrix[0].size());
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            matrix[i][j] += column_vector[i] * row_vector[j];
        }
    }
    return matrix;
}

void check_solver(const DynamicMatrixRank<F2> &solver,
                  const std::vector<std::vector<F2>> &matrix) {
    const int h = static_cast<int>(matrix.size());
    const int w = h == 0 ? 0 : static_cast<int>(matrix[0].size());
    assert(solver.rank() == brute_rank(matrix));
    assert(solver.materialize_matrix() == matrix);
    for (int row = 0; row < h; ++row) {
        assert(solver.get_row(row) == matrix[row]);
    }
    for (int column = 0; column < w; ++column) {
        std::vector<F2> expected(h, F2());
        for (int row = 0; row < h; ++row) {
            expected[row] = matrix[row][column];
        }
        assert(solver.get_column(column) == expected);
    }
}

void check_updates(const std::vector<std::vector<F2>> &matrix) {
    const int h = static_cast<int>(matrix.size());
    const int w = h == 0 ? 0 : static_cast<int>(matrix[0].size());
    DynamicMatrixRank<F2> solver(matrix);
    check_solver(solver, matrix);

    for (int column_mask = 0; column_mask < (1 << h); ++column_mask) {
        const std::vector<F2> column_vector = vector_from_mask(h, column_mask);
        for (int row_mask = 0; row_mask < (1 << w); ++row_mask) {
            const std::vector<F2> row_vector = vector_from_mask(w, row_mask);
            const auto updated =
                apply_rank_one(matrix, column_vector, row_vector);
            const int expected = brute_rank(updated);
            assert(solver.rank_after_rank_one_update(column_vector,
                                                     row_vector) == expected);

            auto applied = solver;
            assert(applied.apply_rank_one_update(column_vector, row_vector) ==
                   expected);
            check_solver(applied, updated);
            applied.build();
            check_solver(applied, updated);

            for (int row = 0; row < h; ++row) {
                for (int new_row_mask = 0; new_row_mask < (1 << w);
                     ++new_row_mask) {
                    const std::vector<F2> new_row =
                        vector_from_mask(w, new_row_mask);
                    auto row_updated = updated;
                    row_updated[row] = new_row;
                    const int row_expected = brute_rank(row_updated);
                    assert(applied.rank_after_row_replacement(row, new_row) ==
                           row_expected);
                    auto row_applied = applied;
                    assert(row_applied.apply_row_replacement(row, new_row) ==
                           row_expected);
                    check_solver(row_applied, row_updated);
                }
            }

            for (int column = 0; column < w; ++column) {
                for (int new_column_mask = 0; new_column_mask < (1 << h);
                     ++new_column_mask) {
                    const std::vector<F2> new_column =
                        vector_from_mask(h, new_column_mask);
                    auto column_updated = updated;
                    for (int row = 0; row < h; ++row) {
                        column_updated[row][column] = new_column[row];
                    }
                    const int column_expected = brute_rank(column_updated);
                    assert(applied.rank_after_column_replacement(
                               column, new_column) == column_expected);
                    auto column_applied = applied;
                    assert(column_applied.apply_column_replacement(
                               column, new_column) == column_expected);
                    check_solver(column_applied, column_updated);
                }
            }
        }
    }
}

void self_test() {
    {
        DynamicMatrixRank<F2> solver;
        solver.build(std::vector<std::vector<F2>>{});
        assert(solver.rank() == 0);
        assert(solver.materialize_matrix().empty());
    }

    for (int h = 1; h <= 3; ++h) {
        for (int w = 1; w <= 3; ++w) {
            const int states = 1 << (h * w);
            for (int mask = 0; mask < states; ++mask) {
                check_updates(matrix_from_mask(h, w, mask));
            }
        }
    }
}
} // namespace

int main() {
    self_test();
    return 0;
}
#line 1 "verify/standalone-dynamic-matrix-rank.test.cpp"
// competitive-verifier: STANDALONE

#include <cassert>
#include <vector>

#line 1 "math/matrix/dynamic-matrix-rank.hpp"



// 行列の階数を求め、外積 1 項更新後の階数を判定する。
// さらに、内部状態を O(k(r + c)) で更新しつつ変更後の階数を返せる。
// 現在の行列は左右の階数分解と片側逆元で保持する。
// 1 行差し替え・1 列差し替えはそれぞれ e_i (b-a_i)^T, (b-a_j) e_j^T に帰着する。
// 前処理は O(rc min(r, c) + k^2(r + c))、各更新・各判定は O(k(r + c)) である。
// T は体を成し、零判定と四則演算ができることを仮定する。

#line 13 "math/matrix/dynamic-matrix-rank.hpp"

template <class T> struct DynamicMatrixRank {
    int row_size = 0;
    int column_size = 0;
    int matrix_rank = 0;

    std::vector<std::vector<T>> column_space_basis;
    std::vector<std::vector<T>> row_space_basis;
    std::vector<std::vector<T>> column_space_left_inverse;
    std::vector<std::vector<T>> row_space_right_inverse;

    DynamicMatrixRank() = default;

    explicit DynamicMatrixRank(const std::vector<std::vector<T>> &matrix) {
        build(matrix);
    }

    void build(const std::vector<std::vector<T>> &matrix) {
        row_size = static_cast<int>(matrix.size());
        column_size = row_size == 0 ? 0 : static_cast<int>(matrix[0].size());
        for (int i = 1; i < row_size; ++i) {
            assert(static_cast<int>(matrix[i].size()) == column_size);
        }

        const std::vector<int> basis_columns = find_independent_columns(matrix);
        const std::vector<int> basis_rows =
            find_independent_columns(transpose_matrix(matrix));
        matrix_rank = static_cast<int>(basis_columns.size());
        assert(static_cast<int>(basis_rows.size()) == matrix_rank);

        std::vector<std::vector<T>> intersection_matrix(
            matrix_rank, std::vector<T>(matrix_rank, T()));
        for (int i = 0; i < matrix_rank; ++i) {
            for (int j = 0; j < matrix_rank; ++j) {
                intersection_matrix[i][j] =
                    matrix[basis_rows[i]][basis_columns[j]];
            }
        }
        const std::vector<std::vector<T>> intersection_inverse =
            inverse_matrix(intersection_matrix);

        column_space_basis.assign(row_size, std::vector<T>(matrix_rank, T()));
        for (int i = 0; i < row_size; ++i) {
            for (int j = 0; j < matrix_rank; ++j) {
                column_space_basis[i][j] = matrix[i][basis_columns[j]];
            }
        }

        row_space_basis.assign(matrix_rank, std::vector<T>(column_size, T()));
        for (int i = 0; i < matrix_rank; ++i) {
            for (int mid = 0; mid < matrix_rank; ++mid) {
                const T value = intersection_inverse[i][mid];
                if (value == T()) {
                    continue;
                }
                for (int j = 0; j < column_size; ++j) {
                    row_space_basis[i][j] += value * matrix[basis_rows[mid]][j];
                }
            }
        }

        column_space_left_inverse.assign(matrix_rank,
                                         std::vector<T>(row_size, T()));
        for (int i = 0; i < matrix_rank; ++i) {
            for (int j = 0; j < matrix_rank; ++j) {
                column_space_left_inverse[i][basis_rows[j]] =
                    intersection_inverse[i][j];
            }
        }

        row_space_right_inverse.assign(column_size,
                                       std::vector<T>(matrix_rank, T()));
        for (int i = 0; i < matrix_rank; ++i) {
            row_space_right_inverse[basis_columns[i]][i] = T(1);
        }
    }

    void build() { build(materialize_matrix()); }

    int rank() const { return matrix_rank; }

    std::vector<T> get_row(int row_index) const {
        assert(0 <= row_index && row_index < row_size);
        std::vector<T> row(column_size, T());
        for (int i = 0; i < matrix_rank; ++i) {
            const T value = column_space_basis[row_index][i];
            if (value == T()) {
                continue;
            }
            for (int j = 0; j < column_size; ++j) {
                row[j] += value * row_space_basis[i][j];
            }
        }
        return row;
    }

    std::vector<T> get_column(int column_index) const {
        assert(0 <= column_index && column_index < column_size);
        std::vector<T> column(row_size, T());
        for (int i = 0; i < row_size; ++i) {
            for (int j = 0; j < matrix_rank; ++j) {
                column[i] +=
                    column_space_basis[i][j] * row_space_basis[j][column_index];
            }
        }
        return column;
    }

    std::vector<std::vector<T>> materialize_matrix() const {
        std::vector<std::vector<T>> matrix(row_size,
                                           std::vector<T>(column_size, T()));
        for (int i = 0; i < row_size; ++i) {
            for (int mid = 0; mid < matrix_rank; ++mid) {
                const T value = column_space_basis[i][mid];
                if (value == T()) {
                    continue;
                }
                for (int j = 0; j < column_size; ++j) {
                    matrix[i][j] += value * row_space_basis[mid][j];
                }
            }
        }
        return matrix;
    }

    int rank_after_rank_one_update(const std::vector<T> &column_vector,
                                   const std::vector<T> &row_vector) const {
        return analyze_rank_one_update(column_vector, row_vector).next_rank;
    }

    int rank_after_row_replacement(int row_index,
                                   const std::vector<T> &new_row) const {
        assert(0 <= row_index && row_index < row_size);
        assert(static_cast<int>(new_row.size()) == column_size);
        std::vector<T> difference = new_row;
        const std::vector<T> current_row = get_row(row_index);
        for (int j = 0; j < column_size; ++j) {
            difference[j] -= current_row[j];
        }
        std::vector<T> column_vector(row_size, T());
        column_vector[row_index] = T(1);
        return rank_after_rank_one_update(column_vector, difference);
    }

    int rank_after_column_replacement(int column_index,
                                      const std::vector<T> &new_column) const {
        assert(0 <= column_index && column_index < column_size);
        assert(static_cast<int>(new_column.size()) == row_size);
        std::vector<T> difference = new_column;
        const std::vector<T> current_column = get_column(column_index);
        for (int i = 0; i < row_size; ++i) {
            difference[i] -= current_column[i];
        }
        std::vector<T> row_vector(column_size, T());
        row_vector[column_index] = T(1);
        return rank_after_rank_one_update(difference, row_vector);
    }

    int apply_rank_one_update(const std::vector<T> &column_vector,
                              const std::vector<T> &row_vector) {
        RankOneUpdateInfo info =
            analyze_rank_one_update(column_vector, row_vector);

        if (info.next_rank == matrix_rank + 1) {
            const int pivot_row = first_nonzero(info.column_residual);
            const int pivot_column = first_nonzero(info.row_residual);
            const std::vector<T> lambda = normalized_left_annihilator(
                pivot_row, info.column_residual[pivot_row]);
            const std::vector<T> rho = normalized_right_annihilator(
                pivot_column, info.row_residual[pivot_column]);

            const std::vector<std::vector<T>> old_row_space_right_inverse =
                row_space_right_inverse;

            append_column(column_space_basis, info.column_residual);
            column_space_left_inverse.push_back(lambda);

            row_space_basis.push_back(row_vector);
            for (int i = 0; i < matrix_rank; ++i) {
                for (int j = 0; j < column_size; ++j) {
                    row_space_basis[i][j] += info.alpha[i] * row_vector[j];
                }
            }

            std::vector<T> right_alpha(column_size, T());
            for (int i = 0; i < column_size; ++i) {
                for (int j = 0; j < matrix_rank; ++j) {
                    right_alpha[i] +=
                        old_row_space_right_inverse[i][j] * info.alpha[j];
                }
            }

            row_space_right_inverse.assign(
                column_size, std::vector<T>(matrix_rank + 1, T()));
            for (int i = 0; i < column_size; ++i) {
                for (int j = 0; j < matrix_rank; ++j) {
                    row_space_right_inverse[i][j] =
                        old_row_space_right_inverse[i][j] -
                        rho[i] * info.beta[j];
                }
                row_space_right_inverse[i][matrix_rank] =
                    T() - right_alpha[i] + rho[i] * info.schur;
            }
            ++matrix_rank;
            return matrix_rank;
        }

        if (!info.column_inside && info.row_inside) {
            const int pivot_row = first_nonzero(info.column_residual);
            const std::vector<T> lambda = normalized_left_annihilator(
                pivot_row, info.column_residual[pivot_row]);
            for (int i = 0; i < row_size; ++i) {
                for (int j = 0; j < matrix_rank; ++j) {
                    column_space_basis[i][j] += column_vector[i] * info.beta[j];
                }
            }
            std::vector<std::vector<T>> new_left_inverse =
                column_space_left_inverse;
            for (int i = 0; i < matrix_rank; ++i) {
                for (int j = 0; j < row_size; ++j) {
                    new_left_inverse[i][j] -= info.alpha[i] * lambda[j];
                }
            }
            column_space_left_inverse.swap(new_left_inverse);
            return matrix_rank;
        }

        if (info.column_inside && !info.row_inside) {
            const int pivot_column = first_nonzero(info.row_residual);
            const std::vector<T> rho = normalized_right_annihilator(
                pivot_column, info.row_residual[pivot_column]);
            for (int i = 0; i < matrix_rank; ++i) {
                for (int j = 0; j < column_size; ++j) {
                    row_space_basis[i][j] += info.alpha[i] * row_vector[j];
                }
            }
            std::vector<std::vector<T>> new_right_inverse =
                row_space_right_inverse;
            for (int i = 0; i < column_size; ++i) {
                for (int j = 0; j < matrix_rank; ++j) {
                    new_right_inverse[i][j] -= rho[i] * info.beta[j];
                }
            }
            row_space_right_inverse.swap(new_right_inverse);
            return matrix_rank;
        }

        if (info.schur != T()) {
            std::vector<T> right_alpha(column_size, T());
            for (int i = 0; i < column_size; ++i) {
                for (int j = 0; j < matrix_rank; ++j) {
                    right_alpha[i] +=
                        row_space_right_inverse[i][j] * info.alpha[j];
                }
            }
            for (int i = 0; i < matrix_rank; ++i) {
                for (int j = 0; j < column_size; ++j) {
                    row_space_basis[i][j] += info.alpha[i] * row_vector[j];
                }
            }
            for (int i = 0; i < column_size; ++i) {
                for (int j = 0; j < matrix_rank; ++j) {
                    row_space_right_inverse[i][j] -=
                        right_alpha[i] * (info.beta[j] / info.schur);
                }
            }
            return matrix_rank;
        }

        const int removed = first_nonzero(info.alpha);
        const T removed_inverse = T(1) / info.alpha[removed];

        std::vector<T> right_alpha(column_size, T());
        for (int i = 0; i < column_size; ++i) {
            for (int j = 0; j < matrix_rank; ++j) {
                right_alpha[i] += row_space_right_inverse[i][j] * info.alpha[j];
            }
        }

        std::vector<std::vector<T>> new_column_space_basis(
            row_size, std::vector<T>(matrix_rank - 1, T()));
        std::vector<std::vector<T>> new_row_space_basis(
            matrix_rank - 1, std::vector<T>(column_size, T()));
        std::vector<std::vector<T>> new_column_space_left_inverse(
            matrix_rank - 1, std::vector<T>(row_size, T()));
        std::vector<std::vector<T>> new_row_space_right_inverse(
            column_size, std::vector<T>(matrix_rank - 1, T()));

        int new_index = 0;
        for (int old = 0; old < matrix_rank; ++old) {
            if (old == removed) {
                continue;
            }
            for (int i = 0; i < row_size; ++i) {
                new_column_space_basis[i][new_index] =
                    column_space_basis[i][old] +
                    column_vector[i] * info.beta[old];
            }
            const T factor = info.alpha[old] * removed_inverse;
            for (int j = 0; j < column_size; ++j) {
                new_row_space_basis[new_index][j] =
                    row_space_basis[old][j] -
                    factor * row_space_basis[removed][j];
            }
            for (int j = 0; j < row_size; ++j) {
                new_column_space_left_inverse[new_index][j] =
                    column_space_left_inverse[old][j] -
                    factor * column_space_left_inverse[removed][j];
            }
            for (int i = 0; i < column_size; ++i) {
                new_row_space_right_inverse[i][new_index] =
                    row_space_right_inverse[i][old] +
                    right_alpha[i] * info.beta[old];
            }
            ++new_index;
        }

        column_space_basis.swap(new_column_space_basis);
        row_space_basis.swap(new_row_space_basis);
        column_space_left_inverse.swap(new_column_space_left_inverse);
        row_space_right_inverse.swap(new_row_space_right_inverse);
        --matrix_rank;
        return matrix_rank;
    }

    int apply_row_replacement(int row_index, const std::vector<T> &new_row) {
        assert(0 <= row_index && row_index < row_size);
        assert(static_cast<int>(new_row.size()) == column_size);
        std::vector<T> difference = new_row;
        const std::vector<T> current_row = get_row(row_index);
        for (int j = 0; j < column_size; ++j) {
            difference[j] -= current_row[j];
        }
        std::vector<T> column_vector(row_size, T());
        column_vector[row_index] = T(1);
        return apply_rank_one_update(column_vector, difference);
    }

    int apply_column_replacement(int column_index,
                                 const std::vector<T> &new_column) {
        assert(0 <= column_index && column_index < column_size);
        assert(static_cast<int>(new_column.size()) == row_size);
        std::vector<T> difference = new_column;
        const std::vector<T> current_column = get_column(column_index);
        for (int i = 0; i < row_size; ++i) {
            difference[i] -= current_column[i];
        }
        std::vector<T> row_vector(column_size, T());
        row_vector[column_index] = T(1);
        return apply_rank_one_update(difference, row_vector);
    }

  private:
    struct RankOneUpdateInfo {
        std::vector<T> alpha;
        std::vector<T> beta;
        std::vector<T> column_residual;
        std::vector<T> row_residual;
        bool column_inside = false;
        bool row_inside = false;
        T schur = T();
        int next_rank = 0;
    };

    RankOneUpdateInfo
    analyze_rank_one_update(const std::vector<T> &column_vector,
                            const std::vector<T> &row_vector) const {
        assert(static_cast<int>(column_vector.size()) == row_size);
        assert(static_cast<int>(row_vector.size()) == column_size);

        RankOneUpdateInfo info;
        info.alpha = multiply_left_inverse(column_vector);
        info.beta = multiply_right_inverse(row_vector);

        const std::vector<T> projected_column =
            reconstruct_column_vector(info.alpha);
        const std::vector<T> projected_row = reconstruct_row_vector(info.beta);

        info.column_residual.resize(row_size, T());
        info.row_residual.resize(column_size, T());
        info.column_inside = true;
        info.row_inside = true;
        for (int i = 0; i < row_size; ++i) {
            info.column_residual[i] = column_vector[i] - projected_column[i];
            if (info.column_residual[i] != T()) {
                info.column_inside = false;
            }
        }
        for (int j = 0; j < column_size; ++j) {
            info.row_residual[j] = row_vector[j] - projected_row[j];
            if (info.row_residual[j] != T()) {
                info.row_inside = false;
            }
        }

        info.schur = T(1);
        for (int i = 0; i < matrix_rank; ++i) {
            info.schur += info.beta[i] * info.alpha[i];
        }

        if (!info.column_inside && !info.row_inside) {
            info.next_rank = matrix_rank + 1;
        } else if (info.column_inside != info.row_inside) {
            info.next_rank = matrix_rank;
        } else {
            info.next_rank = info.schur == T() ? matrix_rank - 1 : matrix_rank;
        }
        return info;
    }

    std::vector<T>
    multiply_left_inverse(const std::vector<T> &column_vector) const {
        std::vector<T> result(matrix_rank, T());
        for (int i = 0; i < matrix_rank; ++i) {
            for (int j = 0; j < row_size; ++j) {
                result[i] += column_space_left_inverse[i][j] * column_vector[j];
            }
        }
        return result;
    }

    std::vector<T>
    multiply_right_inverse(const std::vector<T> &row_vector) const {
        std::vector<T> result(matrix_rank, T());
        for (int j = 0; j < column_size; ++j) {
            const T value = row_vector[j];
            if (value == T()) {
                continue;
            }
            for (int i = 0; i < matrix_rank; ++i) {
                result[i] += value * row_space_right_inverse[j][i];
            }
        }
        return result;
    }

    std::vector<T>
    reconstruct_column_vector(const std::vector<T> &coefficients) const {
        std::vector<T> result(row_size, T());
        for (int i = 0; i < row_size; ++i) {
            for (int j = 0; j < matrix_rank; ++j) {
                result[i] += column_space_basis[i][j] * coefficients[j];
            }
        }
        return result;
    }

    std::vector<T>
    reconstruct_row_vector(const std::vector<T> &coefficients) const {
        std::vector<T> result(column_size, T());
        for (int i = 0; i < matrix_rank; ++i) {
            const T value = coefficients[i];
            if (value == T()) {
                continue;
            }
            for (int j = 0; j < column_size; ++j) {
                result[j] += value * row_space_basis[i][j];
            }
        }
        return result;
    }

    std::vector<T> normalized_left_annihilator(int pivot_row,
                                               const T &pivot_value) const {
        std::vector<T> result(row_size, T());
        result[pivot_row] = T(1);
        for (int j = 0; j < matrix_rank; ++j) {
            const T value = column_space_basis[pivot_row][j];
            if (value == T()) {
                continue;
            }
            for (int i = 0; i < row_size; ++i) {
                result[i] -= value * column_space_left_inverse[j][i];
            }
        }
        for (int i = 0; i < row_size; ++i) {
            result[i] /= pivot_value;
        }
        return result;
    }

    std::vector<T> normalized_right_annihilator(int pivot_column,
                                                const T &pivot_value) const {
        std::vector<T> result(column_size, T());
        result[pivot_column] = T(1);
        for (int i = 0; i < column_size; ++i) {
            for (int j = 0; j < matrix_rank; ++j) {
                result[i] -= row_space_right_inverse[i][j] *
                             row_space_basis[j][pivot_column];
            }
        }
        for (int i = 0; i < column_size; ++i) {
            result[i] /= pivot_value;
        }
        return result;
    }

    static void append_column(std::vector<std::vector<T>> &matrix,
                              const std::vector<T> &column) {
        assert(static_cast<int>(matrix.size()) ==
               static_cast<int>(column.size()));
        for (int i = 0; i < static_cast<int>(matrix.size()); ++i) {
            matrix[i].push_back(column[i]);
        }
    }

    static int first_nonzero(const std::vector<T> &vector) {
        for (int i = 0; i < static_cast<int>(vector.size()); ++i) {
            if (vector[i] != T()) {
                return i;
            }
        }
        assert(false);
        return -1;
    }

    static std::vector<std::vector<T>>
    transpose_matrix(const std::vector<std::vector<T>> &matrix) {
        const int h = static_cast<int>(matrix.size());
        if (h == 0) {
            return {};
        }
        const int w = static_cast<int>(matrix[0].size());
        std::vector<std::vector<T>> transposed(w, std::vector<T>(h, T()));
        for (int i = 0; i < h; ++i) {
            assert(static_cast<int>(matrix[i].size()) == w);
            for (int j = 0; j < w; ++j) {
                transposed[j][i] = matrix[i][j];
            }
        }
        return transposed;
    }

    static std::vector<int>
    find_independent_columns(const std::vector<std::vector<T>> &matrix) {
        const int h = static_cast<int>(matrix.size());
        if (h == 0) {
            return {};
        }
        const int w = static_cast<int>(matrix[0].size());
        std::vector<std::vector<T>> b = matrix;
        int rank = 0;
        std::vector<int> pivot_columns;
        for (int column = 0; column < w; ++column) {
            int pivot = -1;
            for (int row = rank; row < h; ++row) {
                if (b[row][column] != T()) {
                    pivot = row;
                    break;
                }
            }
            if (pivot < 0) {
                continue;
            }
            if (pivot != rank) {
                b[pivot].swap(b[rank]);
            }

            const T inverse = T(1) / b[rank][column];
            for (int j = column; j < w; ++j) {
                b[rank][j] *= inverse;
            }
            for (int row = rank + 1; row < h; ++row) {
                const T factor = b[row][column];
                if (factor == T()) {
                    continue;
                }
                for (int j = column; j < w; ++j) {
                    b[row][j] -= b[rank][j] * factor;
                }
            }
            pivot_columns.push_back(column);
            ++rank;
            if (rank == h) {
                break;
            }
        }
        return pivot_columns;
    }

    static std::vector<std::vector<T>>
    inverse_matrix(std::vector<std::vector<T>> matrix) {
        const int n = static_cast<int>(matrix.size());
        for (int i = 0; i < n; ++i) {
            assert(static_cast<int>(matrix[i].size()) == n);
        }

        std::vector<std::vector<T>> inverse(n, std::vector<T>(n, T()));
        for (int i = 0; i < n; ++i) {
            inverse[i][i] = T(1);
        }

        for (int column = 0; column < n; ++column) {
            int pivot = -1;
            for (int row = column; row < n; ++row) {
                if (matrix[row][column] != T()) {
                    pivot = row;
                    break;
                }
            }
            assert(pivot >= 0);
            if (pivot != column) {
                matrix[pivot].swap(matrix[column]);
                inverse[pivot].swap(inverse[column]);
            }

            const T diagonal_inverse = T(1) / matrix[column][column];
            for (int j = 0; j < n; ++j) {
                matrix[column][j] *= diagonal_inverse;
                inverse[column][j] *= diagonal_inverse;
            }
            for (int row = 0; row < n; ++row) {
                if (row == column) {
                    continue;
                }
                const T factor = matrix[row][column];
                if (factor == T()) {
                    continue;
                }
                for (int j = 0; j < n; ++j) {
                    matrix[row][j] -= matrix[column][j] * factor;
                    inverse[row][j] -= inverse[column][j] * factor;
                }
            }
        }
        return inverse;
    }
};


#line 7 "verify/standalone-dynamic-matrix-rank.test.cpp"

namespace {
struct F2 {
    int value;

    F2(int value = 0) : value(value & 1) {}

    F2 &operator+=(const F2 &other) {
        value ^= other.value;
        return *this;
    }

    F2 &operator-=(const F2 &other) {
        value ^= other.value;
        return *this;
    }

    F2 &operator*=(const F2 &other) {
        value &= other.value;
        return *this;
    }

    F2 &operator/=(const F2 &other) {
        assert(other.value == 1);
        return *this;
    }

    [[maybe_unused]] friend F2 operator+(F2 lhs, const F2 &rhs) {
        return lhs += rhs;
    }

    [[maybe_unused]] friend F2 operator-(F2 lhs, const F2 &rhs) {
        return lhs -= rhs;
    }

    friend F2 operator*(F2 lhs, const F2 &rhs) { return lhs *= rhs; }

    friend F2 operator/(F2 lhs, const F2 &rhs) { return lhs /= rhs; }

    friend bool operator==(const F2 &lhs, const F2 &rhs) {
        return lhs.value == rhs.value;
    }

    friend bool operator!=(const F2 &lhs, const F2 &rhs) {
        return lhs.value != rhs.value;
    }
};

template <class T> int brute_rank(std::vector<std::vector<T>> matrix) {
    const int h = static_cast<int>(matrix.size());
    if (h == 0) {
        return 0;
    }
    const int w = static_cast<int>(matrix[0].size());
    int rank = 0;
    for (int column = 0; column < w; ++column) {
        int pivot = -1;
        for (int row = rank; row < h; ++row) {
            if (matrix[row][column] != T()) {
                pivot = row;
                break;
            }
        }
        if (pivot < 0) {
            continue;
        }
        if (pivot != rank) {
            matrix[pivot].swap(matrix[rank]);
        }
        const T inverse = T(1) / matrix[rank][column];
        for (int j = column; j < w; ++j) {
            matrix[rank][j] *= inverse;
        }
        for (int row = rank + 1; row < h; ++row) {
            const T factor = matrix[row][column];
            if (factor == T()) {
                continue;
            }
            for (int j = column; j < w; ++j) {
                matrix[row][j] -= matrix[rank][j] * factor;
            }
        }
        ++rank;
        if (rank == h) {
            break;
        }
    }
    return rank;
}

std::vector<std::vector<F2>> matrix_from_mask(int h, int w, int mask) {
    std::vector<std::vector<F2>> matrix(h, std::vector<F2>(w, F2()));
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            matrix[i][j] = F2((mask >> (i * w + j)) & 1);
        }
    }
    return matrix;
}

std::vector<F2> vector_from_mask(int n, int mask) {
    std::vector<F2> values(n, F2());
    for (int i = 0; i < n; ++i) {
        values[i] = F2((mask >> i) & 1);
    }
    return values;
}

std::vector<std::vector<F2>>
apply_rank_one(std::vector<std::vector<F2>> matrix,
               const std::vector<F2> &column_vector,
               const std::vector<F2> &row_vector) {
    const int h = static_cast<int>(matrix.size());
    const int w = h == 0 ? 0 : static_cast<int>(matrix[0].size());
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            matrix[i][j] += column_vector[i] * row_vector[j];
        }
    }
    return matrix;
}

void check_solver(const DynamicMatrixRank<F2> &solver,
                  const std::vector<std::vector<F2>> &matrix) {
    const int h = static_cast<int>(matrix.size());
    const int w = h == 0 ? 0 : static_cast<int>(matrix[0].size());
    assert(solver.rank() == brute_rank(matrix));
    assert(solver.materialize_matrix() == matrix);
    for (int row = 0; row < h; ++row) {
        assert(solver.get_row(row) == matrix[row]);
    }
    for (int column = 0; column < w; ++column) {
        std::vector<F2> expected(h, F2());
        for (int row = 0; row < h; ++row) {
            expected[row] = matrix[row][column];
        }
        assert(solver.get_column(column) == expected);
    }
}

void check_updates(const std::vector<std::vector<F2>> &matrix) {
    const int h = static_cast<int>(matrix.size());
    const int w = h == 0 ? 0 : static_cast<int>(matrix[0].size());
    DynamicMatrixRank<F2> solver(matrix);
    check_solver(solver, matrix);

    for (int column_mask = 0; column_mask < (1 << h); ++column_mask) {
        const std::vector<F2> column_vector = vector_from_mask(h, column_mask);
        for (int row_mask = 0; row_mask < (1 << w); ++row_mask) {
            const std::vector<F2> row_vector = vector_from_mask(w, row_mask);
            const auto updated =
                apply_rank_one(matrix, column_vector, row_vector);
            const int expected = brute_rank(updated);
            assert(solver.rank_after_rank_one_update(column_vector,
                                                     row_vector) == expected);

            auto applied = solver;
            assert(applied.apply_rank_one_update(column_vector, row_vector) ==
                   expected);
            check_solver(applied, updated);
            applied.build();
            check_solver(applied, updated);

            for (int row = 0; row < h; ++row) {
                for (int new_row_mask = 0; new_row_mask < (1 << w);
                     ++new_row_mask) {
                    const std::vector<F2> new_row =
                        vector_from_mask(w, new_row_mask);
                    auto row_updated = updated;
                    row_updated[row] = new_row;
                    const int row_expected = brute_rank(row_updated);
                    assert(applied.rank_after_row_replacement(row, new_row) ==
                           row_expected);
                    auto row_applied = applied;
                    assert(row_applied.apply_row_replacement(row, new_row) ==
                           row_expected);
                    check_solver(row_applied, row_updated);
                }
            }

            for (int column = 0; column < w; ++column) {
                for (int new_column_mask = 0; new_column_mask < (1 << h);
                     ++new_column_mask) {
                    const std::vector<F2> new_column =
                        vector_from_mask(h, new_column_mask);
                    auto column_updated = updated;
                    for (int row = 0; row < h; ++row) {
                        column_updated[row][column] = new_column[row];
                    }
                    const int column_expected = brute_rank(column_updated);
                    assert(applied.rank_after_column_replacement(
                               column, new_column) == column_expected);
                    auto column_applied = applied;
                    assert(column_applied.apply_column_replacement(
                               column, new_column) == column_expected);
                    check_solver(column_applied, column_updated);
                }
            }
        }
    }
}

void self_test() {
    {
        DynamicMatrixRank<F2> solver;
        solver.build(std::vector<std::vector<F2>>{});
        assert(solver.rank() == 0);
        assert(solver.materialize_matrix().empty());
    }

    for (int h = 1; h <= 3; ++h) {
        for (int w = 1; w <= 3; ++w) {
            const int states = 1 << (h * w);
            for (int mask = 0; mask < states; ++mask) {
                check_updates(matrix_from_mask(h, w, mask));
            }
        }
    }
}
} // namespace

int main() {
    self_test();
    return 0;
}
Back to top page