//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2019 James D. Mitchell
//
// 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 .
//
#include "libsemigroups/froidure-pin-base.hpp"
#include
#include "libsemigroups/libsemigroups-exception.hpp"
#include "libsemigroups/report.hpp"
namespace libsemigroups {
using element_index_type = FroidurePinBase::element_index_type;
////////////////////////////////////////////////////////////////////////
// FroidurePinBase - constructors and destructor - public
////////////////////////////////////////////////////////////////////////
FroidurePinBase::FroidurePinBase()
: Runner(),
_degree(UNDEFINED),
_duplicate_gens(),
_enumerate_order(),
_final(),
_first(),
_found_one(false),
_idempotents_found(false),
_is_idempotent(),
_left(),
_length(),
_lenindex({0, 0}),
_letter_to_pos(),
_nr(0),
_nr_rules(0),
_pos(0),
_pos_one(0),
_prefix(),
_reduced(),
_right(),
_suffix(),
_wordlen(0), // (length of the current word) - 1
// Deprecated, remove in v2
_relation_gen(0),
_relation_pos(UNDEFINED) {
#ifdef LIBSEMIGROUPS_VERBOSE
_nr_products = 0;
#endif
_right.set_default_value(UNDEFINED);
}
FroidurePinBase::FroidurePinBase(FroidurePinBase const& S)
: Runner(S),
_degree(UNDEFINED), // _degree must be UNDEFINED until !_gens.empty()
_duplicate_gens(S._duplicate_gens),
_enumerate_order(S._enumerate_order),
_final(S._final),
_first(S._first),
_found_one(S._found_one),
_idempotents_found(S._idempotents_found),
_is_idempotent(S._is_idempotent),
_left(S._left),
_length(S._length),
_lenindex(S._lenindex),
_letter_to_pos(S._letter_to_pos),
_nr(S._nr),
_nr_rules(S._nr_rules),
_pos(S._pos),
_pos_one(S._pos_one),
_prefix(S._prefix),
_reduced(S._reduced),
_right(S._right),
_suffix(S._suffix),
_wordlen(S._wordlen),
_relation_gen(S._relation_gen),
_relation_pos(S._relation_pos) {
#ifdef LIBSEMIGROUPS_VERBOSE
_nr_products = 0;
#endif
}
FroidurePinBase::~FroidurePinBase() = default;
////////////////////////////////////////////////////////////////////////
// FroidurePinBase - constructors - private
////////////////////////////////////////////////////////////////////////
// Partial copy.
// \p copy a semigroup
// \p coll a collection of additional generators
//
// This is a constructor for a semigroup generated by the generators of the
// FroidurePin copy and the (possibly) additional generators coll.
//
// The relevant parts of the data structure of copy are copied and
// \c this will be corrupt unless add_generators or closure is called
// subsequently. This is why this member function is private.
//
// The same effect can be obtained by copying copy using the copy
// constructor and then calling add_generators or closure. However,
// this constructor avoids copying those parts of the data structure of
// copy that add_generators invalidates anyway. If copy has not been
// enumerated at all, then these two routes for adding more generators are
// equivalent.
//
// or should usually be called after this.
void FroidurePinBase::partial_copy(FroidurePinBase const& S) {
_degree = S._degree; // copy for comparison in add_generators
_duplicate_gens = S._duplicate_gens;
_found_one = S._found_one; // copy in case degree doesn't change in
// add_generators
_idempotents_found = S._idempotents_found;
_is_idempotent = S._is_idempotent;
_left = S._left;
_lenindex = {0, S._lenindex[1]};
_letter_to_pos = S._letter_to_pos;
_nr = S._nr;
_nr_rules = 0;
_pos = S._pos;
_pos_one = S._pos_one; // copy in case degree doesn't change in
// add_generators
_reduced = S._reduced;
_right = S._right;
_wordlen = 0;
_relation_gen = 0;
_relation_pos = UNDEFINED;
LIBSEMIGROUPS_ASSERT(S._lenindex.size() > 1);
#ifdef LIBSEMIGROUPS_VERBOSE
_nr_products = 0;
#endif
// the following are required for assignment to specific positions in
// add_generators
_final.resize(S._nr, 0);
_first.resize(S._nr, 0);
_length.resize(S._nr, 0);
_prefix.resize(S._nr, 0);
_suffix.resize(S._nr, 0);
_enumerate_order.reserve(S._nr);
// add the distinct old generators to new _enumerate_order
LIBSEMIGROUPS_ASSERT(S._lenindex.size() > 1);
for (enumerate_index_type i = 0; i < S._lenindex[1]; i++) {
_enumerate_order.push_back(S._enumerate_order[i]);
_final[_enumerate_order[i]] = S._final[S._enumerate_order[i]];
_first[_enumerate_order[i]] = S._first[S._enumerate_order[i]];
_prefix[_enumerate_order[i]] = UNDEFINED;
_suffix[_enumerate_order[i]] = UNDEFINED;
_length[_enumerate_order[i]] = 1;
}
}
////////////////////////////////////////////////////////////////////////
// FroidurePinBase - member functions - public
////////////////////////////////////////////////////////////////////////
element_index_type FroidurePinBase::word_to_pos(word_type const& w) const {
// w is a word in the generators (i.e. a vector of letter_type's)
if (w.size() == 0) {
LIBSEMIGROUPS_EXCEPTION("the given word has length 0");
}
for (auto x : w) {
validate_letter_index(x);
}
element_index_type out = _letter_to_pos[w[0]];
for (auto it = w.cbegin() + 1; it < w.cend() && out != UNDEFINED; ++it) {
out = _right.get(out, *it);
}
return out;
}
element_index_type
FroidurePinBase::product_by_reduction(element_index_type i,
element_index_type j) const {
validate_element_index(i);
validate_element_index(j);
if (length_const(i) <= length_const(j)) {
while (i != UNDEFINED) {
j = _left.get(j, _final[i]);
i = _prefix[i];
}
return j;
} else {
while (j != UNDEFINED) {
i = _right.get(i, _first[j]);
j = _suffix[j];
}
return i;
}
}
// Deprecated, remove in v2
void FroidurePinBase::next_relation(word_type& relation) {
if (!finished()) {
run();
}
relation.clear();
if (_relation_pos == _nr) { // no more relations
return;
}
if (_relation_pos != UNDEFINED) {
while (_relation_pos < _nr) {
while (_relation_gen < nr_generators()) {
if (!_reduced.get(_enumerate_order[_relation_pos], _relation_gen)
&& (_relation_pos < _lenindex[1]
|| _reduced.get(_suffix[_enumerate_order[_relation_pos]],
_relation_gen))) {
relation.push_back(_enumerate_order[_relation_pos]);
relation.push_back(_relation_gen);
relation.push_back(
_right.get(_enumerate_order[_relation_pos], _relation_gen));
break;
}
_relation_gen++;
}
if (_relation_gen == nr_generators()) { // then relation is empty
_relation_gen = 0;
_relation_pos++;
} else {
break;
}
}
_relation_gen++;
} else {
// duplicate generators
if (_relation_gen < _duplicate_gens.size()) {
relation.push_back(_duplicate_gens[_relation_gen].first);
relation.push_back(_duplicate_gens[_relation_gen].second);
_relation_gen++;
} else {
_relation_gen = 0;
_relation_pos++;
next_relation(relation);
}
}
}
void FroidurePinBase::enumerate(size_t limit) {
if (finished() || limit <= current_size()) {
return;
} else if (LIMIT_MAX - batch_size() > current_size()) {
limit = std::max(limit, current_size() + batch_size());
} else { // batch_size() is very big for some reason
limit = batch_size();
}
REPORT_DEFAULT(
"limit = %llu (%s)\n", static_cast(limit), __func__);
run_until([this, &limit]() -> bool { return current_size() >= limit; });
}
////////////////////////////////////////////////////////////////////////
// FroidurePinBase - settings - public
////////////////////////////////////////////////////////////////////////
FroidurePinBase& FroidurePinBase::batch_size(size_t batch_size) noexcept {
_settings._batch_size = batch_size;
return *this;
}
size_t FroidurePinBase::batch_size() const noexcept {
return _settings._batch_size;
}
FroidurePinBase& FroidurePinBase::max_threads(size_t nr_threads) noexcept {
unsigned int n
= static_cast(nr_threads == 0 ? 1 : nr_threads);
_settings._max_threads = std::min(n, std::thread::hardware_concurrency());
return *this;
}
size_t FroidurePinBase::max_threads() const noexcept {
return _settings._max_threads;
}
FroidurePinBase&
FroidurePinBase::concurrency_threshold(size_t thrshld) noexcept {
_settings._concurrency_threshold = thrshld;
return *this;
}
size_t FroidurePinBase::concurrency_threshold() const noexcept {
return _settings._concurrency_threshold;
}
FroidurePinBase& FroidurePinBase::immutable(bool val) noexcept {
_settings._immutable = val;
return *this;
}
bool FroidurePinBase::immutable() const noexcept {
return _settings._immutable;
}
////////////////////////////////////////////////////////////////////////
// FroidurePinBase - helper non-member functions
////////////////////////////////////////////////////////////////////////
void relations(FroidurePinBase& S,
std::function&& hook) {
S.run();
std::vector relation; // a triple
S.reset_next_relation();
S.next_relation(relation);
while (relation.size() == 2 && !relation.empty()) {
hook(word_type({relation[0]}), word_type({relation[1]}));
S.next_relation(relation);
}
word_type lhs, rhs; // changed in-place by factorisation
while (!relation.empty()) {
S.factorisation(lhs, relation[0]);
S.factorisation(rhs, relation[2]);
lhs.push_back(relation[1]);
hook(lhs, rhs);
S.next_relation(relation);
}
}
void relations(FroidurePinBase& S, std::function&& hook) {
S.run();
std::vector relation; // a triple
S.reset_next_relation();
S.next_relation(relation);
while (relation.size() == 2 && !relation.empty()) {
hook(word_type({relation[0]}));
hook(word_type({relation[1]}));
S.next_relation(relation);
}
word_type word; // changed in-place by factorisation
while (!relation.empty()) {
S.factorisation(word, relation[0]);
word.push_back(relation[1]);
hook(word);
S.factorisation(word, relation[2]);
hook(word);
S.next_relation(relation);
}
}
} // namespace libsemigroups