// // libsemigroups - C++ library for semigroups and monoids // Copyright (C) 2019 Finn Smith // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //
// This file contains an implemesntation of fast boolean matrices up to // dimension 8 x 8.
#include"libsemigroups/bmat8.hpp"
#include <algorithm> // for uniform_int_distribution #include <array> // for array #include <climits> // for CHAR_BIT #include <iostream> // for operator<<, ostringstream #include <random> // for mt19937, random_device #include <vector> // for vector
#include"libsemigroups/exception.hpp"// for LIBSEMIGROUPS_EXCEPTION
namespace libsemigroups {
//////////////////////////////////////////////////////////////////////// // Static helper data and functions ////////////////////////////////////////////////////////////////////////
// Cyclically shifts bits to left by 8m // https://stackoverflow.com/a/776523 static uint64_t cyclic_shift(uint64_t n) noexcept { constunsignedint mask
= (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2. // assert ( (c<=mask) &&"rotate by type width or more"); unsignedint c = 8;
c &= mask; return (n << c) | (n >> ((-c) & mask));
}
// Given 8 x 8 matrices A and B, this function returns a matrix where each // row is the corresponding row of B if that row of B is a subset of the // corresponding row of A, and 0 otherwise. static uint64_t zero_if_row_not_contained(uint64_t A, uint64_t B) noexcept {
uint64_t w = A & B; for (size_t i = 0; i < 8; ++i) { if ((w & ROW_MASK[i]) == (B & ROW_MASK[i])) {
w |= (B & ROW_MASK[i]);
} else {
w &= ~(ROW_MASK[i]);
}
} return w;
}
staticinlinevoid swap_for_sorting(size_t a, size_t b) noexcept { if (for_sorting[b] < for_sorting[a]) {
std::swap(for_sorting[a], for_sorting[b]);
}
}
//////////////////////////////////////////////////////////////////////// // BMat8 - static data members - private ////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////// // BMat8 - constructors - public ////////////////////////////////////////////////////////////////////////
BMat8::BMat8(std::vector<std::vector<bool>> const& mat) { if (0 == mat.size() || mat.size() > 8) {
LIBSEMIGROUPS_EXCEPTION( "expected a vector with size in [1, 8], got a vector of size %d",
mat.size());
}
_data = 0;
uint64_t pow = 1;
pow = pow << 63; for (auto row : mat) { if (row.size() != mat.size()) {
LIBSEMIGROUPS_EXCEPTION("the vectors must all have the same length!");
} for (auto entry : row) { if (entry) {
_data ^= pow;
}
pow = pow >> 1;
}
pow = pow >> (8 - mat.size());
}
}
// Not noexcept because it can throw.
BMat8 BMat8::random(size_t dim) { if (0 == dim || dim > 8) {
LIBSEMIGROUPS_EXCEPTION("the argument should be in [1, 8], got %d", dim);
}
BMat8 bm = BMat8::random(); for (size_t i = dim; i < 8; ++i) {
bm._data &= ~(ROW_MASK[i]);
bm._data &= ~(COL_MASK[i]);
} return bm;
}
//////////////////////////////////////////////////////////////////////// // BMat8 - member functions - public ////////////////////////////////////////////////////////////////////////
BMat8 bm(_data);
bm.sort_rows();
uint64_t no_dups = bm._data; for (size_t i = 0; i < 7; ++i) {
combined_masks |= ROW_MASK[i]; while ((no_dups & ROW_MASK[i + 1]) << 8 == (no_dups & ROW_MASK[i])
&& (no_dups & ROW_MASK[i]) != 0) {
no_dups = ((no_dups & combined_masks)
| (no_dups & ~combined_masks & ~ROW_MASK[i + 1]) << 8);
}
}
uint64_t cm = no_dups; for (size_t i = 0; i < 7; ++i) {
cm = cyclic_shift(cm);
out |= zero_if_row_not_contained(no_dups, cm);
} for (size_t i = 0; i < 8; ++i) { if ((out & ROW_MASK[i]) == (no_dups & ROW_MASK[i])) {
out &= ~ROW_MASK[i];
} else {
out |= (no_dups & ROW_MASK[i]);
}
}
combined_masks = 0; for (size_t i = 0; i < 8; ++i) {
combined_masks |= ROW_MASK[i]; while ((out & ROW_MASK[i]) == 0 && ((out & ~combined_masks) != 0)) {
out = (out & combined_masks) | ((out & ~combined_masks) << 8);
}
} return BMat8(out);
}
void BMat8::set(size_t i, size_t j, bool val) { if (7 < i || 7 < j) {
LIBSEMIGROUPS_EXCEPTION( "the arguments should each be at most 7, got %d as the %s argument",
(7 < i) ? i : j,
(7 < i) ? "first" : "second");
}
_data ^= (-val ^ _data) & BIT_MASK[8 * i + j];
}