//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2019-2021 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 <http://www.gnu.org/licenses/>.
// The purpose of this file is to test the ToddCoxeter classes.
#include <algorithm>
// for is_sorted, copy, all_of
#include <array>
// for array
#include <chrono>
// for duration, milliseconds
#include <cstddef>
// for size_t
#include <fstream>
// for ofstream
#include <functional>
// for mem_fn
#include <iostream>
// for string, operator<<, ostream
#include <memory>
// for unique_ptr, shared_ptr
#include <string>
// for basic_string, operator==
#include <unordered_map>
// for operator!=, operator==
#include <unordered_set>
// for unordered_set
#include <utility>
// for pair
#include <vector>
// for vector, operator==, swap
#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
#include "catch.hpp" // for TEST_CASE
#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE
#include "libsemigroups/bmat8.hpp" // for BMat8
#include "libsemigroups/cong-intf.hpp" // for CongruenceInterface::class...
#include "libsemigroups/cong-wrap.hpp" // for CongruenceWrapper...
#include "libsemigroups/constants.hpp" // for operator==, operator!=
#include "libsemigroups/containers.hpp" // for DynamicArray2, DynamicArra...
#include "libsemigroups/fpsemi-examples.hpp" // for setup, RennerTypeDMonoid
#include "libsemigroups/fpsemi-intf.hpp" // for FpSemigroupInterface::rule...
#include "libsemigroups/fpsemi.hpp" // for FpSemigroup
#include "libsemigroups/froidure-pin-base.hpp" // for FroidurePinBase
#include "libsemigroups/froidure-pin.hpp" // for FroidurePin, FroidurePin<>...
#include "libsemigroups/iterator.hpp" // for ConstIteratorStateful, ope...
#include "libsemigroups/knuth-bendix.hpp" // for KnuthBendix
#include "libsemigroups/order.hpp" // for RecursivePathCompare, Lexi...
#include "libsemigroups/report.hpp" // for ReportGuard
#include "libsemigroups/string.hpp" // for operator<<, to_string
#include "libsemigroups/tce.hpp" // for TCE, operator<<, IncreaseD...
#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, operator|
#include "libsemigroups/transf.hpp" // for Transf, LeastTransf
#include "libsemigroups/types.hpp" // for word_type, relation_type
#include "libsemigroups/wislo.hpp" // for const_wislo_iterator, cbeg...
namespace libsemigroups {
struct LibsemigroupsException;
// Forward declaration
constexpr
bool REPORT =
false;
congruence_kind constexpr twosided = congruence_kind::twosided;
congruence_kind constexpr left = congruence_kind::left;
congruence_kind constexpr right = congruence_kind::right;
using KnuthBendix = fpsemigroup::KnuthBendix;
using tc_order = congruence::ToddCoxeter::order;
using options = congruence::ToddCoxeter::options;
using fpsemigroup::author;
using fpsemigroup::make;
using fpsemigroup::setup;
using fpsemigroup::brauer_monoid;
using fpsemigroup::dual_symmetric_inverse_monoid;
using fpsemigroup::fibonacci_semigroup;
using fpsemigroup::full_transformation_monoid;
using fpsemigroup::orientation_preserving_monoid;
using fpsemigroup::orientation_reversing_monoid;
using fpsemigroup::partial_transformation_monoid;
using fpsemigroup::partition_monoid;
using fpsemigroup::rook_monoid;
using fpsemigroup::singular_brauer_monoid;
using fpsemigroup::stellar_monoid;
using fpsemigroup::stylic_monoid;
using fpsemigroup::symmetric_group;
using fpsemigroup::symmetric_inverse_monoid;
using fpsemigroup::temperley_lieb_monoid;
using fpsemigroup::uniform_block_bijection_monoid;
namespace {
// Test functions
void check_felsch(congruence::ToddCoxeter& var) {
SECTION(
"Felsch + no standardisation") {
var.strategy(options::strategy::felsch).standardize(
false);
}
SECTION(
"Felsch + standardisation") {
var.strategy(options::strategy::felsch).standardize(
true);
}
}
void check_felsch(fpsemigroup::ToddCoxeter& var) {
check_felsch(var.congruence());
}
void check_felsch_throws(congruence::ToddCoxeter& var) {
SECTION(
"Felsch (throws)") {
REQUIRE_THROWS_AS(var.strategy(options::strategy::felsch),
LibsemigroupsException);
}
}
void check_felsch_throws(fpsemigroup::ToddCoxeter& var) {
check_felsch_throws(var.congruence());
}
void check_hlt_no_save(congruence::ToddCoxeter& var) {
SECTION(
"HLT + no standardise + full lookahead + no save") {
var.strategy(options::strategy::hlt);
var.standardize(
false).lookahead(options::lookahead::full).save(
false);
}
SECTION(
"HLT + standardise + full lookahead + no save") {
var.strategy(options::strategy::hlt);
var.standardize(
true).lookahead(options::lookahead::full).save(
false);
}
SECTION(
"HLT + no standardise + partial lookahead + no save") {
var.strategy(options::strategy::hlt);
var.standardize(
false)
.lookahead(options::lookahead::partial)
.save(
false);
}
SECTION(
"HLT + standardise + partial lookahead + no save") {
var.strategy(options::strategy::hlt);
var.standardize(
true)
.lookahead(options::lookahead::partial)
.save(
false);
}
}
void check_hlt_no_save(fpsemigroup::ToddCoxeter& var) {
check_hlt_no_save(var.congruence());
}
void check_hlt_save(congruence::ToddCoxeter& var) {
SECTION(
"HLT + no standardise + full lookahead + save") {
var.strategy(options::strategy::hlt);
var.standardize(
false).lookahead(options::lookahead::full).save(
true);
}
SECTION(
"HLT + standardise + full lookahead + save") {
var.strategy(options::strategy::hlt);
var.standardize(
true).lookahead(options::lookahead::full).save(
true);
}
SECTION(
"HLT + no standardise + partial lookahead + save") {
var.strategy(options::strategy::hlt);
var.standardize(
false)
.lookahead(options::lookahead::partial)
.save(
true);
}
SECTION(
"HLT + standardise + partial lookahead + save") {
var.strategy(options::strategy::hlt);
var.standardize(
true).lookahead(options::lookahead::partial).save(
true);
}
}
void check_hlt_save_throws(congruence::ToddCoxeter& var) {
SECTION(
"HLT + save (throws)") {
REQUIRE_THROWS_AS(var.strategy(options::strategy::hlt).save(
true),
LibsemigroupsException);
}
}
void check_hlt_save_throws(fpsemigroup::ToddCoxeter& var) {
check_hlt_save_throws(var.congruence());
}
void check_hlt(congruence::ToddCoxeter& var) {
check_hlt_no_save(var);
check_hlt_save(var);
}
void check_hlt(fpsemigroup::ToddCoxeter& var) {
check_hlt(var.congruence());
}
void check_random(congruence::ToddCoxeter& var) {
SECTION(
"random strategy") {
var.strategy(options::strategy::random);
}
}
void check_random(fpsemigroup::ToddCoxeter& var) {
check_random(var.congruence());
}
void check_Rc_style(congruence::ToddCoxeter& tc) {
SECTION(
"Rc style + full lookahead") {
tc.strategy(options::strategy::Rc).lookahead(options::lookahead::full);
tc.run();
}
SECTION(
"Rc style + partial lookahead") {
tc.strategy(options::strategy::Rc)
.lookahead(options::lookahead::partial);
tc.run();
}
}
void check_Rc_style(fpsemigroup::ToddCoxeter& tc) {
check_Rc_style(tc.congruence());
}
void check_Cr_style(congruence::ToddCoxeter& tc) {
SECTION(
"Cr style") {
tc.strategy(options::strategy::Cr);
tc.run();
}
}
void check_Cr_style(fpsemigroup::ToddCoxeter& tc) {
check_Cr_style(tc.congruence());
}
void check_R_over_C_style(congruence::ToddCoxeter& tc) {
SECTION(
"R/C style") {
tc.strategy(options::strategy::R_over_C);
tc.run();
}
}
void check_R_over_C_style(fpsemigroup::ToddCoxeter& tc) {
check_R_over_C_style(tc.congruence());
}
void check_CR_style(congruence::ToddCoxeter& tc) {
SECTION(
"CR style") {
tc.strategy(options::strategy::CR);
tc.run();
}
}
void check_CR_style(fpsemigroup::ToddCoxeter& tc) {
check_CR_style(tc.congruence());
}
// This is how the recursive words up to a given length M, and on an
// arbitrary finite alphabet are generated. On a single letter alphabet,
// this order is just increasing powers of the only generator:
//
// a < aa < aaa < aaaa < ... < aa...a (M times)
//
// With an n-letter alphabet A = {a_1, a_2, ..., a_n}, suppose we have
// already obtained all of the words W_{n - 1} containing {a_1, ..., a_{n
// - 1}}. Every word in W_{n - 1} is less than any word containing a_n,
// and the least word greater than every word in W_{n - 1} is a_n. Words
// greater than a_n are obtain in the follow way, where:
//
// x: is the maximum word in W_{n - 1}, this is constant, in the
// description
// that follows.
// u: the first word obtained in point (1), the first time it is applied
// after (2) has been applied, starting with u = a_{n - 1}.
// v: a word with one fewer letters than u, starting with the empty word.
// w: a word such that w < u, also starting with the empty word.
//
// 1. If v < x, then v is replaced by the next word in the order. If |uv|
// <=
// M, then the next word is uv. Otherwise, goto 1.
//
// 2. If v = x, then and there exists a word w' in the set of words
// obtained
// so far such that w' > w and |w'| <= M - 1, then replace w with w',
// replace u by wa_n, replace v by the empty word, and the next word is
// wa_n.
//
// If no such word w' exists, then we have enumerated all the required
// words, and we can stop.
//
// For example, if A = {a, b} and M = 4, then the initial elements in the
// order are:
//
// e < a < aa < aaa < aaaa (e is the empty word)
//
// Set b > aaaa. At this point, x = aaaa, u = b, v = e, w = e, and so
// (1) applies, v <- a, and since |uv| = ba <= 4 = M, the next word is
// ba. Repeatedly applying (1), until it fails to hold, we obtain the
// following:
//
// aaaa < b < ba < baa < baaa
//
// After defining baa < baaa, x = aaaa, u = b, v = aaaa, and w = e. Hence
// v = x, and so (2) applies. The next w' in the set of words so far
// enumerated is a, and |a| = 1 <= 3 = M - 1, and so w <- a, u <- ab, v <-
// e, and the next word is ab. We repeatedly apply (1), until it fails, to
// obtain
//
// baaa < ab < aba < abaa
//
// At which point u = b, v = aaaa = x, and w = a. Hence (2) applies, w <-
// aa, v <- e, u <- aab, and the next word is: aab. And so on ...
//
// The next function implements this order, returning the words on an
// n-letter alphabet of length up to M.
std::vector<word_type> recursive_path_words(size_t n, size_t M) {
std::vector<word_type> out;
size_t a = 0;
for (size_t i = 0; i < M; ++i) {
out.push_back(word_type(i + 1, a));
}
a++;
int x = out.size();
int u = out.size();
int v = -1;
// -1 is the empty word
int w = -1;
// -1 is the empty word
out.push_back({a});
while (a < n) {
if (v < x - 1) {
do {
v++;
}
while (v < x && out[u].size() + out[v].size() > M);
if (v < x && out[u].size() + out[v].size() <= M) {
word_type nxt = out[u];
nxt.insert(nxt.end(), out[v].begin(), out[v].end());
out.push_back(nxt);
}
}
else {
do {
w++;
}
while (
static_cast<size_t>(w) < out.size()
&& out[w].size() + 1 > M);
if (
static_cast<size_t>(w) < out.size()) {
word_type nxt = out[w];
u = out.size();
v = -1;
nxt.push_back(a);
out.push_back(nxt);
}
else {
a++;
if (a < n) {
x = out.size();
u = out.size();
v = -1;
w = -1;
out.push_back({a});
}
}
}
}
return out;
}
void output_gap_benchmark_file(std::string
const& fname,
congruence::ToddCoxeter& tc) {
std::ofstream file;
file.open(fname);
file <<
"local free, rules, R, S, T;\n";
file << tc.to_gap_string();
file <<
"R := RightMagmaCongruenceByGeneratingPairs(S, []);\n";
file <<
"T := CosetTableOfFpSemigroup(R);;\n";
file <<
"Assert(0, Length(T) = Size(GeneratorsOfSemigroup(S)));\n";
file <<
"Assert(0, Length(T[1]) - 1 = "
<< std::to_string(tc.number_of_classes()) <<
");\n";
file.close();
}
}
// namespace
namespace congruence {
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"000",
"small 2-sided congruence",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({1, 1, 1, 1}, {1});
tc.add_pair({0, 1, 0, 1}, {0, 0});
REQUIRE(!tc.finished());
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 27);
tc.shrink_to_fit();
auto words
= std::vector<word_type>(tc.cbegin_class(1, 0, 10), tc.cend_class());
REQUIRE(words
== std::vector<word_type>({word_type({1}),
word_type({1, 1, 1, 1}),
word_type({1, 1, 1, 1, 1, 1, 1})}));
words = std::vector<word_type>(
tc.cbegin_class(word_type({1, 1, 1, 1}), 0, 10), tc.cend_class());
REQUIRE(words
== std::vector<word_type>({word_type({1}),
word_type({1, 1, 1, 1}),
word_type({1, 1, 1, 1, 1, 1, 1})}));
REQUIRE(tc.number_of_words(1) == POSITIVE_INFINITY);
std::vector<size_t> class_sizes;
for (size_t i = 0; i < tc.number_of_classes(); ++i) {
class_sizes.push_back(tc.number_of_words(i));
}
REQUIRE(class_sizes
== std::vector<size_t>(tc.number_of_classes(),
size_t(POSITIVE_INFINITY)));
REQUIRE(tc.word_to_class_index(words[0]) == 1);
REQUIRE(
std::all_of(words.cbegin(), words.cend(), [&tc](word_type
const& w) {
return tc.word_to_class_index(w) == 1;
}));
// Too small for lookahead to kick in...
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"001",
"small 2-sided congruence",
"[no-valgrind][todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
// (a^3, a)
tc.add_pair({0}, {1, 1});
// (a, b^2)
REQUIRE(!tc.finished());
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 5);
REQUIRE(tc.finished());
REQUIRE(!tc.is_standardized());
REQUIRE(tc.word_to_class_index({0, 0, 1})
== tc.word_to_class_index({0, 0, 0, 0, 1}));
REQUIRE(tc.word_to_class_index({0, 1, 1, 0, 0, 1})
== tc.word_to_class_index({0, 0, 0, 0, 1}));
REQUIRE(tc.word_to_class_index({0, 0, 0}) != tc.word_to_class_index({1}));
tc.standardize(tc_order::lex);
REQUIRE(tc.class_index_to_word(0) == word_type({0}));
REQUIRE(tc.class_index_to_word(1) == word_type({0, 0}));
REQUIRE(tc.class_index_to_word(2) == word_type({0, 0, 1}));
REQUIRE(tc.class_index_to_word(3) == word_type({0, 0, 1, 0}));
REQUIRE(tc.word_to_class_index(word_type({0, 0, 0, 1})) == 3);
REQUIRE(tc.class_index_to_word(4) == word_type({1}));
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(0)) == 0);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(1)) == 1);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(2)) == 2);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(3)) == 3);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(4)) == 4);
REQUIRE(tc.word_to_class_index({0, 1}) == 3);
REQUIRE(LexicographicalCompare<word_type>{}({0, 0, 1}, {0, 1}));
REQUIRE(std::is_sorted(tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
LexicographicalCompare<word_type>{}));
tc.standardize(tc_order::shortlex);
REQUIRE(std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cend_normal_forms())
== std::vector<word_type>({{0}, {1}, {0, 0}, {0, 1}, {0, 0, 1}}));
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(0)) == 0);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(1)) == 1);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(2)) == 2);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(3)) == 3);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(4)) == 4);
REQUIRE(std::is_sorted(tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
ShortLexCompare<word_type>{}));
auto nf = std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cend_normal_forms());
REQUIRE(nf
== std::vector<word_type>({{0}, {1}, {0, 0}, {0, 1}, {0, 0, 1}}));
REQUIRE(std::all_of(nf.begin(), nf.end(), [&tc](word_type& w) {
return w == *tc.cbegin_class(w, 0, w.size() + 1);
}));
for (size_t i = 2; i < 6; ++i) {
for (size_t j = 2; j < 10 - i; ++j) {
auto v = std::vector<word_type>(
cbegin_wislo(i, {0}, word_type(j + 1, 0)),
cend_wislo(i, {0}, word_type(j + 1, 0)));
std::sort(v.begin(), v.end(), RecursivePathCompare<word_type>{});
REQUIRE(v == recursive_path_words(i, j));
}
}
tc.standardize(tc_order::recursive);
REQUIRE(tc.class_index_to_word(0) == word_type({0}));
REQUIRE(tc.class_index_to_word(1) == word_type({0, 0}));
REQUIRE(tc.class_index_to_word(2) == word_type({1}));
REQUIRE(tc.class_index_to_word(3) == word_type({1, 0}));
REQUIRE(tc.class_index_to_word(4) == word_type({1, 0, 0}));
REQUIRE(std::is_sorted(tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
RecursivePathCompare<word_type>{}));
}
// Felsch is actually faster here!
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"002",
"Example 6.6 in Sims (see also KnuthBendix 013)",
"[todd-coxeter][standard]") {
using TCE = detail::TCE;
using FroidurePinTCE
= FroidurePin<TCE, FroidurePinTraits<TCE, TCE::Table>>;
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(4);
tc.add_pair({0, 0}, {0});
tc.add_pair({1, 0}, {1});
tc.add_pair({0, 1}, {1});
tc.add_pair({2, 0}, {2});
tc.add_pair({0, 2}, {2});
tc.add_pair({3, 0}, {3});
tc.add_pair({0, 3}, {3});
tc.add_pair({1, 1}, {0});
tc.add_pair({2, 3}, {0});
tc.add_pair({2, 2, 2}, {0});
tc.add_pair({1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2}, {0});
tc.add_pair({1, 2, 1, 3, 1, 2, 1, 3, 1, 2, 1, 3, 1, 2, 1, 3,
1, 2, 1, 3, 1, 2, 1, 3, 1, 2, 1, 3, 1, 2, 1, 3},
{0});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 10
'752);
REQUIRE(tc.complete());
REQUIRE(tc.compatible());
// Take a copy to test copy constructor
auto& S =
static_cast<FroidurePinTCE&>(*tc.quotient_froidure_pin());
auto T = S.copy_closure({S.generator(0)});
REQUIRE(T.size() == S.size());
REQUIRE(T.number_of_generators() == S.number_of_generators());
REQUIRE(S.size() == 10
'752);
REQUIRE(S.number_of_idempotents() == 1);
for (size_t c = 0; c < tc.number_of_classes(); ++c) {
REQUIRE(tc.class_index_to_word(c) == S.factorisation(c));
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(c)) == c);
}
REQUIRE(tc.finished());
tc.standardize(tc_order::recursive);
REQUIRE(std::is_sorted(tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
RecursivePathCompare<word_type>{}));
REQUIRE(std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cbegin_normal_forms() + 10)
== std::vector<word_type>({{{0},
{1},
{2},
{2, 1},
{1, 2},
{1, 2, 1},
{2, 2},
{2, 2, 1},
{2, 1, 2},
{2, 1, 2, 1}}}));
tc.standardize(tc_order::lex);
for (size_t c = 0; c < tc.number_of_classes(); ++c) {
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(c)) == c);
}
REQUIRE(std::is_sorted(tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
LexicographicalCompare<word_type>{}));
REQUIRE(std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cbegin_normal_forms() + 10)
== std::vector<word_type>({{0},
{0, 1},
{0, 1, 2},
{0, 1, 2, 1},
{0, 1, 2, 1, 2},
{0, 1, 2, 1, 2, 1},
{0, 1, 2, 1, 2, 1, 2},
{0, 1, 2, 1, 2, 1, 2, 1},
{0, 1, 2, 1, 2, 1, 2, 1, 2},
{0, 1, 2, 1, 2, 1, 2, 1, 2, 1}}));
tc.standardize(tc_order::shortlex);
for (size_t c = 0; c < tc.number_of_classes(); ++c) {
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(c)) == c);
}
REQUIRE(std::is_sorted(tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
ShortLexCompare<word_type>{}));
REQUIRE(std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cbegin_normal_forms() + 10)
== std::vector<word_type>({{0},
{1},
{2},
{3},
{1, 2},
{1, 3},
{2, 1},
{3, 1},
{1, 2, 1},
{1, 3, 1}}));
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"003",
"constructed from FroidurePin",
"[no-valgrind][todd-coxeter][quick][no-coverage]") {
auto rg = ReportGuard(REPORT);
FroidurePin<BMat8> S(
{BMat8({{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}),
BMat8({{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {1, 0, 0, 0}}),
BMat8({{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {1, 0, 0, 1}}),
BMat8({{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}})});
ToddCoxeter tc(twosided, S);
tc.froidure_pin_policy(options::froidure_pin::use_relations);
tc.add_pair({0}, {1});
check_felsch(tc);
check_hlt(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
tc.random_interval(std::chrono::milliseconds(100));
tc.lower_bound(3);
tc.run();
REQUIRE(tc.complete());
REQUIRE(tc.compatible());
REQUIRE(tc.number_of_classes() == 3);
// REQUIRE(tc.number_of_generators() == 4);
REQUIRE(tc.contains({0}, {1}));
tc.standardize(tc_order::shortlex);
REQUIRE(tc.contains({0}, {1}));
tc.shrink_to_fit();
REQUIRE(tc.contains({0}, {1}));
auto& T = *tc.quotient_froidure_pin();
REQUIRE(T.size() == 3);
REQUIRE(tc.class_index_to_word(0) == T.factorisation(0));
REQUIRE(tc.class_index_to_word(1) == T.factorisation(1));
REQUIRE(tc.class_index_to_word(2) == T.factorisation(2));
REQUIRE(tc.class_index_to_word(0) == word_type({0}));
REQUIRE(tc.class_index_to_word(1) == word_type({2}));
REQUIRE(tc.class_index_to_word(2) == word_type({0, 0}));
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(0)) == 0);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(1)) == 1);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(2)) == 2);
tc.standardize(tc_order::lex);
REQUIRE(tc.class_index_to_word(0) == word_type({0}));
REQUIRE(tc.class_index_to_word(1) == word_type({0, 0}));
REQUIRE(tc.class_index_to_word(2) == word_type({0, 0, 2}));
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(0)) == 0);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(1)) == 1);
REQUIRE(tc.word_to_class_index(tc.class_index_to_word(2)) == 2);
tc.standardize(tc_order::shortlex);
REQUIRE(tc.class_index_to_word(0) == word_type({0}));
REQUIRE(tc.class_index_to_word(1) == word_type({2}));
REQUIRE(tc.class_index_to_word(2) == word_type({0, 0}));
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"004",
"2-sided congruence from FroidurePin",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
using Transf = LeastTransf<5>;
FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3, 3})});
REQUIRE(S.size() == 88);
ToddCoxeter tc(twosided, S);
tc.froidure_pin_policy(options::froidure_pin::use_cayley_graph);
tc.add_pair(S.factorisation(Transf({3, 4, 4, 4, 4})),
S.factorisation(Transf({3, 1, 3, 3, 3})));
REQUIRE(!tc.finished());
tc.shrink_to_fit();
// does nothing
REQUIRE(!tc.finished());
tc.standardize(tc_order::none);
// does nothing
REQUIRE(!tc.finished());
check_hlt_no_save(tc);
check_hlt_save_throws(tc);
check_felsch_throws(tc);
check_random(tc);
REQUIRE(tc.number_of_classes() == 21);
tc.shrink_to_fit();
REQUIRE(tc.number_of_classes() == 21);
tc.standardize(tc_order::recursive);
auto w = std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cend_normal_forms());
REQUIRE(w.size() == 21);
REQUIRE(w
== std::vector<word_type>({{0},
{0, 0},
{0, 0, 0},
{0, 0, 0, 0},
{1},
{1, 0},
{1, 0, 0},
{1, 0, 0, 0},
{0, 1},
{0, 1, 0},
{0, 1, 0, 0},
{0, 1, 0, 0, 0},
{0, 0, 1},
{1, 1},
{1, 1, 0},
{1, 1, 0, 0},
{1, 1, 0, 0, 0},
{0, 1, 1},
{0, 1, 1, 0},
{0, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 0}}));
REQUIRE(std::unique(w.begin(), w.end()) == w.end());
REQUIRE(std::is_sorted(tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
RecursivePathCompare<word_type>{}));
REQUIRE(std::all_of(
tc.cbegin_normal_forms(),
tc.cend_normal_forms(),
[&tc](word_type
const& ww) ->
bool {
return tc.class_index_to_word(tc.word_to_class_index(ww)) == ww;
}));
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"005",
"non-trivial two-sided from relations",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(3);
tc.add_pair({0, 1}, {1, 0});
tc.add_pair({0, 2}, {2, 2});
tc.add_pair({0, 2}, {0});
tc.add_pair({2, 2}, {0});
tc.add_pair({1, 2}, {1, 2});
tc.add_pair({1, 2}, {2, 2});
tc.add_pair({1, 2, 2}, {1});
tc.add_pair({1, 2}, {1});
tc.add_pair({2, 2}, {1});
tc.add_pair({0}, {1});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 2);
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"006",
"small right cong. on free semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(right);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({0}, {1, 1});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 5);
REQUIRE(tc.finished());
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"007",
"left cong. on free semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
ToddCoxeter tc(left);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({0}, {1, 1});
tc.growth_factor(1.5);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(!tc.is_standardized());
REQUIRE(tc.word_to_class_index({0, 0, 1})
== tc.word_to_class_index({0, 0, 0, 0, 1}));
REQUIRE(tc.word_to_class_index({0, 1, 1, 0, 0, 1})
== tc.word_to_class_index({0, 0, 0, 0, 1}));
REQUIRE(tc.word_to_class_index({1})
!= tc.word_to_class_index({0, 0, 0, 0}));
REQUIRE(tc.word_to_class_index({0, 0, 0})
!= tc.word_to_class_index({0, 0, 0, 0}));
tc.standardize(tc_order::shortlex);
REQUIRE(tc.is_standardized());
}
{
ToddCoxeter tc(left);
REQUIRE_NOTHROW(ToddCoxeter(left, tc));
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"008",
"for small fp semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
// (a^3, a)
tc.add_pair({0}, {1, 1});
// (a, b^2)
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.word_to_class_index({0, 0, 1})
== tc.word_to_class_index({0, 0, 0, 0, 1}));
REQUIRE(tc.word_to_class_index({0, 1, 1, 0, 0, 1})
== tc.word_to_class_index({0, 0, 0, 0, 1}));
REQUIRE(tc.word_to_class_index({0, 0, 0}) != tc.word_to_class_index({1}));
REQUIRE(tc.word_to_class_index({0, 0, 0, 0}) < tc.number_of_classes());
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"009",
"2-sided cong. trans. semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
auto S = FroidurePin<Transf<>>(
{Transf<>({1, 3, 4, 2, 3}), Transf<>({3, 2, 1, 3, 3})});
REQUIRE(S.size() == 88);
REQUIRE(S.number_of_rules() == 18);
ToddCoxeter tc(twosided, S);
tc.froidure_pin_policy(options::froidure_pin::use_relations);
tc.add_pair(S.factorisation(Transf<>({3, 4, 4, 4, 4})),
S.factorisation(Transf<>({3, 1, 3, 3, 3})));
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 21);
REQUIRE(tc.number_of_classes() == 21);
REQUIRE(tc.word_to_class_index(S.factorisation(Transf<>({1, 3, 1, 3, 3})))
== tc.word_to_class_index(
S.factorisation(Transf<>({4, 2, 4, 4, 2}))));
tc.standardize(tc_order::shortlex);
REQUIRE(tc.number_of_non_trivial_classes() == 1);
REQUIRE(tc.cbegin_ntc()->size() == 68);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"010",
"left congruence on transformation semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
auto S = FroidurePin<Transf<>>(
{Transf<>({1, 3, 4, 2, 3}), Transf<>({3, 2, 1, 3, 3})});
REQUIRE(S.size() == 88);
REQUIRE(S.number_of_rules() == 18);
ToddCoxeter tc(left, S);
tc.froidure_pin_policy(options::froidure_pin::use_relations);
tc.add_pair(S.factorisation(Transf<>({3, 4, 4, 4, 4})),
S.factorisation(Transf<>({3, 1, 3, 3, 3})));
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 69);
REQUIRE(tc.number_of_classes() == 69);
REQUIRE(tc.word_to_class_index(S.factorisation(Transf<>({1, 3, 1, 3, 3})))
!= tc.word_to_class_index(
S.factorisation(Transf<>({4, 2, 4, 4, 2}))));
tc.standardize(tc_order::shortlex);
REQUIRE(tc.number_of_non_trivial_classes() == 1);
REQUIRE(tc.cbegin_ntc()->size() == 20);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"011",
"right cong. trans. semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
auto S = FroidurePin<Transf<>>(
{Transf<>({1, 3, 4, 2, 3}), Transf<>({3, 2, 1, 3, 3})});
REQUIRE(S.size() == 88);
REQUIRE(S.number_of_rules() == 18);
ToddCoxeter tc(right, S);
tc.froidure_pin_policy(options::froidure_pin::use_relations);
tc.add_pair(S.factorisation(Transf<>({3, 4, 4, 4, 4})),
S.factorisation(Transf<>({3, 1, 3, 3, 3})));
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 72);
REQUIRE(tc.number_of_classes() == 72);
REQUIRE(tc.word_to_class_index(S.factorisation(Transf<>({1, 3, 1, 3, 3})))
!= tc.word_to_class_index(
S.factorisation(Transf<>({4, 2, 4, 4, 2}))));
REQUIRE(tc.word_to_class_index(S.factorisation(Transf<>({1, 3, 3, 3, 3})))
!= tc.word_to_class_index(
S.factorisation(Transf<>({4, 2, 4, 4, 2}))));
REQUIRE(tc.word_to_class_index(S.factorisation(Transf<>({2, 4, 2, 2, 2})))
== tc.word_to_class_index(
S.factorisation(Transf<>({2, 3, 3, 3, 3}))));
REQUIRE(tc.word_to_class_index(S.factorisation(Transf<>({1, 3, 3, 3, 3})))
!= tc.word_to_class_index(
S.factorisation(Transf<>({2, 3, 3, 3, 3}))));
tc.standardize(tc_order::shortlex);
REQUIRE(tc.number_of_non_trivial_classes() == 4);
std::vector<size_t> v(tc.number_of_non_trivial_classes(), 0);
std::transform(tc.cbegin_ntc(),
tc.cend_ntc(),
v.begin(),
std::mem_fn(&std::vector<word_type>::size));
REQUIRE(std::count(v.cbegin(), v.cend(), 3) == 1);
REQUIRE(std::count(v.cbegin(), v.cend(), 5) == 2);
REQUIRE(std::count(v.cbegin(), v.cend(), 7) == 1);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"012",
"trans. semigroup (size 88)",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
FroidurePin<Transf<>> S;
S.add_generator(Transf<>({1, 3, 4, 2, 3}));
S.add_generator(Transf<>({3, 2, 1, 3, 3}));
REQUIRE(S.size() == 88);
REQUIRE(S.number_of_rules() == 18);
REQUIRE(S.degree() == 5);
ToddCoxeter tc(twosided, S);
tc.froidure_pin_policy(options::froidure_pin::use_cayley_graph);
word_type w1, w2;
S.factorisation(w1, S.position(Transf<>({3, 4, 4, 4, 4})));
S.factorisation(w2, S.position(Transf<>({3, 1, 3, 3, 3})));
tc.add_pair(w1, w2);
check_hlt_no_save(tc);
check_hlt_save_throws(tc);
check_felsch_throws(tc);
check_random(tc);
REQUIRE(tc.number_of_classes() == 21);
REQUIRE(tc.number_of_classes() == 21);
word_type w3, w4;
S.factorisation(w3, S.position(Transf<>({1, 3, 1, 3, 3})));
S.factorisation(w4, S.position(Transf<>({4, 2, 4, 4, 2})));
REQUIRE(tc.word_to_class_index(w3) == tc.word_to_class_index(w4));
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"013",
"left cong. on trans. semigroup (size 88)",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
FroidurePin<Transf<>> S;
S.add_generator(Transf<>({1, 3, 4, 2, 3}));
S.add_generator(Transf<>({3, 2, 1, 3, 3}));
REQUIRE(S.size() == 88);
REQUIRE(S.degree() == 5);
word_type w1, w2;
S.factorisation(w1, S.position(Transf<>({3, 4, 4, 4, 4})));
S.factorisation(w2, S.position(Transf<>({3, 1, 3, 3, 3})));
ToddCoxeter tc(left, S);
tc.froidure_pin_policy(options::froidure_pin::use_relations);
tc.add_pair(w1, w2);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 69);
REQUIRE(tc.number_of_classes() == 69);
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"014",
"right cong. on trans. semigroup (size 88)",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
FroidurePin<Transf<>> S;
S.add_generator(Transf<>({1, 3, 4, 2, 3}));
S.add_generator(Transf<>({3, 2, 1, 3, 3}));
REQUIRE(S.size() == 88);
REQUIRE(S.number_of_rules() == 18);
REQUIRE(S.degree() == 5);
word_type w1, w2;
S.factorisation(w1, S.position(Transf<>({3, 4, 4, 4, 4})));
S.factorisation(w2, S.position(Transf<>({3, 1, 3, 3, 3})));
ToddCoxeter tc(right, S);
tc.froidure_pin_policy(options::froidure_pin::use_relations);
tc.add_pair(w1, w2);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 72);
REQUIRE(tc.number_of_classes() == 72);
word_type w3, w4, w5, w6;
S.factorisation(w3, S.position(Transf<>({1, 3, 3, 3, 3})));
S.factorisation(w4, S.position(Transf<>({4, 2, 4, 4, 2})));
S.factorisation(w5, S.position(Transf<>({2, 4, 2, 2, 2})));
S.factorisation(w6, S.position(Transf<>({2, 3, 3, 3, 3})));
REQUIRE(tc.word_to_class_index(w3) != tc.word_to_class_index(w4));
REQUIRE(tc.word_to_class_index(w5) == tc.word_to_class_index(w6));
REQUIRE(tc.word_to_class_index(w3) != tc.word_to_class_index(w6));
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"015",
"finite fp-semigroup, dihedral group of order 6",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(5);
tc.add_pair({0, 0}, {0});
tc.add_pair({0, 1}, {1});
tc.add_pair({1, 0}, {1});
tc.add_pair({0, 2}, {2});
tc.add_pair({2, 0}, {2});
tc.add_pair({0, 3}, {3});
tc.add_pair({3, 0}, {3});
tc.add_pair({0, 4}, {4});
tc.add_pair({4, 0}, {4});
tc.add_pair({1, 2}, {0});
tc.add_pair({2, 1}, {0});
tc.add_pair({3, 4}, {0});
tc.add_pair({4, 3}, {0});
tc.add_pair({2, 2}, {0});
tc.add_pair({1, 4, 2, 3, 3}, {0});
tc.add_pair({4, 4, 4}, {0});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 6);
REQUIRE(tc.word_to_class_index({1}) == tc.word_to_class_index({2}));
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"016",
"finite fp-semigroup, size 16",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(4);
tc.add_pair({3}, {2});
tc.add_pair({0, 3}, {0, 2});
tc.add_pair({1, 1}, {1});
tc.add_pair({1, 3}, {1, 2});
tc.add_pair({2, 1}, {2});
tc.add_pair({2, 2}, {2});
tc.add_pair({2, 3}, {2});
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({0, 0, 1}, {1});
tc.add_pair({0, 0, 2}, {2});
tc.add_pair({0, 1, 2}, {1, 2});
tc.add_pair({1, 0, 0}, {1});
tc.add_pair({1, 0, 2}, {0, 2});
tc.add_pair({2, 0, 0}, {2});
tc.add_pair({0, 1, 0, 1}, {1, 0, 1});
tc.add_pair({0, 2, 0, 2}, {2, 0, 2});
tc.add_pair({1, 0, 1, 0}, {1, 0, 1});
tc.add_pair({1, 2, 0, 1}, {1, 0, 1});
tc.add_pair({1, 2, 0, 2}, {2, 0, 2});
tc.add_pair({2, 0, 1, 0}, {2, 0, 1});
tc.add_pair({2, 0, 2, 0}, {2, 0, 2});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 16);
REQUIRE(tc.word_to_class_index({2}) == tc.word_to_class_index({3}));
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"017",
"finite fp-semigroup, size 16",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(11);
tc.add_pair({2}, {1});
tc.add_pair({4}, {3});
tc.add_pair({5}, {0});
tc.add_pair({6}, {3});
tc.add_pair({7}, {1});
tc.add_pair({8}, {3});
tc.add_pair({9}, {3});
tc.add_pair({10}, {0});
tc.add_pair({0, 2}, {0, 1});
tc.add_pair({0, 4}, {0, 3});
tc.add_pair({0, 5}, {0, 0});
tc.add_pair({0, 6}, {0, 3});
tc.add_pair({0, 7}, {0, 1});
tc.add_pair({0, 8}, {0, 3});
tc.add_pair({0, 9}, {0, 3});
tc.add_pair({0, 10}, {0, 0});
tc.add_pair({1, 1}, {1});
tc.add_pair({1, 2}, {1});
tc.add_pair({1, 4}, {1, 3});
tc.add_pair({1, 5}, {1, 0});
tc.add_pair({1, 6}, {1, 3});
tc.add_pair({1, 7}, {1});
tc.add_pair({1, 8}, {1, 3});
tc.add_pair({1, 9}, {1, 3});
tc.add_pair({1, 10}, {1, 0});
tc.add_pair({3, 1}, {3});
tc.add_pair({3, 2}, {3});
tc.add_pair({3, 3}, {3});
tc.add_pair({3, 4}, {3});
tc.add_pair({3, 5}, {3, 0});
tc.add_pair({3, 6}, {3});
tc.add_pair({3, 7}, {3});
tc.add_pair({3, 8}, {3});
tc.add_pair({3, 9}, {3});
tc.add_pair({3, 10}, {3, 0});
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({0, 0, 1}, {1});
tc.add_pair({0, 0, 3}, {3});
tc.add_pair({0, 1, 3}, {1, 3});
tc.add_pair({1, 0, 0}, {1});
tc.add_pair({1, 0, 3}, {0, 3});
tc.add_pair({3, 0, 0}, {3});
tc.add_pair({0, 1, 0, 1}, {1, 0, 1});
tc.add_pair({0, 3, 0, 3}, {3, 0, 3});
tc.add_pair({1, 0, 1, 0}, {1, 0, 1});
tc.add_pair({1, 3, 0, 1}, {1, 0, 1});
tc.add_pair({1, 3, 0, 3}, {3, 0, 3});
tc.add_pair({3, 0, 1, 0}, {3, 0, 1});
tc.add_pair({3, 0, 3, 0}, {3, 0, 3});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 16);
REQUIRE(tc.word_to_class_index({0}) == tc.word_to_class_index({5}));
REQUIRE(tc.word_to_class_index({0}) == tc.word_to_class_index({10}));
REQUIRE(tc.word_to_class_index({1}) == tc.word_to_class_index({2}));
REQUIRE(tc.word_to_class_index({1}) == tc.word_to_class_index({7}));
REQUIRE(tc.word_to_class_index({3}) == tc.word_to_class_index({4}));
REQUIRE(tc.word_to_class_index({3}) == tc.word_to_class_index({6}));
REQUIRE(tc.word_to_class_index({3}) == tc.word_to_class_index({8}));
REQUIRE(tc.word_to_class_index({3}) == tc.word_to_class_index({9}));
tc.standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"018",
"test lookahead",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.next_lookahead(10);
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({1, 0, 0}, {1, 0});
tc.add_pair({1, 0, 1, 1, 1}, {1, 0});
tc.add_pair({1, 1, 1, 1, 1}, {1, 1});
tc.add_pair({1, 1, 0, 1, 1, 0}, {1, 0, 1, 0, 1, 1});
tc.add_pair({0, 0, 1, 0, 1, 1, 0}, {0, 1, 0, 1, 1, 0});
tc.add_pair({0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 0, 1, 0});
tc.add_pair({0, 1, 0, 1, 0, 1, 0}, {1, 0, 1, 0, 1, 0});
tc.add_pair({1, 0, 1, 0, 1, 0, 1}, {1, 0, 1, 0, 1, 0});
tc.add_pair({1, 0, 1, 0, 1, 1, 0}, {1, 0, 1, 0, 1, 1});
tc.add_pair({1, 0, 1, 1, 0, 1, 0}, {1, 0, 1, 1, 0, 1});
tc.add_pair({1, 1, 0, 1, 0, 1, 0}, {1, 0, 1, 0, 1, 0});
tc.add_pair({1, 1, 1, 1, 0, 1, 0}, {1, 0, 1, 0});
tc.add_pair({0, 0, 1, 1, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0});
check_hlt(tc);
REQUIRE(tc.number_of_classes() == 78);
tc.standardize(tc_order::shortlex);
}
{
ToddCoxeter tc(left);
tc.set_number_of_generators(2);
tc.next_lookahead(10);
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({1, 0, 0}, {1, 0});
tc.add_pair({1, 0, 1, 1, 1}, {1, 0});
tc.add_pair({1, 1, 1, 1, 1}, {1, 1});
tc.add_pair({1, 1, 0, 1, 1, 0}, {1, 0, 1, 0, 1, 1});
tc.add_pair({0, 0, 1, 0, 1, 1, 0}, {0, 1, 0, 1, 1, 0});
tc.add_pair({0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 0, 1, 0});
tc.add_pair({0, 1, 0, 1, 0, 1, 0}, {1, 0, 1, 0, 1, 0});
tc.add_pair({1, 0, 1, 0, 1, 0, 1}, {1, 0, 1, 0, 1, 0});
tc.add_pair({1, 0, 1, 0, 1, 1, 0}, {1, 0, 1, 0, 1, 1});
tc.add_pair({1, 0, 1, 1, 0, 1, 0}, {1, 0, 1, 1, 0, 1});
tc.add_pair({1, 1, 0, 1, 0, 1, 0}, {1, 0, 1, 0, 1, 0});
tc.add_pair({1, 1, 1, 1, 0, 1, 0}, {1, 0, 1, 0});
tc.add_pair({0, 0, 1, 1, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0});
check_hlt(tc);
REQUIRE(tc.number_of_classes() == 78);
tc.standardize(tc_order::shortlex);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"019",
"non-trivial left cong. from semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
FroidurePin<Transf<>> S;
S.add_generator(Transf<>({1, 3, 4, 2, 3}));
S.add_generator(Transf<>({3, 2, 1, 3, 3}));
REQUIRE(S.size() == 88);
REQUIRE(S.degree() == 5);
word_type w1, w2;
S.factorisation(w1, S.position(Transf<>({3, 4, 4, 4, 4})));
S.factorisation(w2, S.position(Transf<>({3, 1, 3, 3, 3})));
ToddCoxeter tc(left, S);
tc.froidure_pin_policy(options::froidure_pin::use_cayley_graph);
tc.add_pair(w1, w2);
check_hlt_no_save(tc);
check_hlt_save_throws(tc);
check_felsch_throws(tc);
check_random(tc);
REQUIRE(tc.number_of_classes() == 69);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"020",
"2-sided cong. on free semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(1);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
REQUIRE(tc.contains({0, 0}, {0, 0}));
REQUIRE(!tc.contains({0, 0}, {0}));
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"021",
"calling run when obviously infinite",
"[todd-coxeter][quick]") {
ToddCoxeter tc(twosided);
tc.set_number_of_generators(5);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
REQUIRE_THROWS_AS(tc.run(), LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"022",
"stellar_monoid S3",
"[todd-coxeter][quick][hivert]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(4);
tc.add_pair({3, 3}, {3});
tc.add_pair({0, 3}, {0});
tc.add_pair({3, 0}, {0});
tc.add_pair({1, 3}, {1});
tc.add_pair({3, 1}, {1});
tc.add_pair({2, 3}, {2});
tc.add_pair({3, 2}, {2});
tc.add_pair({0, 0}, {0});
tc.add_pair({1, 1}, {1});
tc.add_pair({2, 2}, {2});
tc.add_pair({0, 2}, {2, 0});
tc.add_pair({2, 0}, {0, 2});
tc.add_pair({1, 2, 1}, {2, 1, 2});
tc.add_pair({1, 0, 1, 0}, {0, 1, 0, 1});
tc.add_pair({1, 0, 1, 0}, {0, 1, 0});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 34);
REQUIRE(tc.quotient_froidure_pin()->size() == 34);
using froidure_pin_type =
typename ToddCoxeter::froidure_pin_type;
using detail::TCE;
auto& S =
static_cast<froidure_pin_type&>(*tc.quotient_froidure_pin());
S.run();
std::vector<TCE> v(S.cbegin(), S.cend());
std::sort(v.begin(), v.end());
REQUIRE(v
== std::vector<TCE>({TCE(1), TCE(2), TCE(3), TCE(4), TCE(5),
TCE(6), TCE(7), TCE(8), TCE(9), TCE(10),
TCE(11), TCE(12), TCE(13), TCE(14), TCE(15),
TCE(16), TCE(17), TCE(18), TCE(19), TCE(20),
TCE(21), TCE(22), TCE(23), TCE(24), TCE(25),
TCE(26), TCE(27), TCE(28), TCE(29), TCE(30),
TCE(31), TCE(32), TCE(33), TCE(34)}));
REQUIRE(std::vector<TCE>(S.cbegin_sorted(), S.cend_sorted())
== std::vector<TCE>({TCE(1), TCE(2), TCE(3), TCE(4), TCE(5),
TCE(6), TCE(7), TCE(8), TCE(9), TCE(10),
TCE(11), TCE(12), TCE(13), TCE(14), TCE(15),
TCE(16), TCE(17), TCE(18), TCE(19), TCE(20),
TCE(21), TCE(22), TCE(23), TCE(24), TCE(25),
TCE(26), TCE(27), TCE(28), TCE(29), TCE(30),
TCE(31), TCE(32), TCE(33), TCE(34)}));
REQUIRE(detail::to_string(TCE(1)) ==
"1");
REQUIRE_NOTHROW(IncreaseDegree<TCE>()(TCE(1), 10));
std::ostringstream oss;
oss << TCE(10);
// Does not do anything visible
std::stringbuf buf;
std::ostream os(&buf);
os << TCE(32);
// Does not do anything visible
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"023",
"finite semigroup (size 5)",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
congruence::ToddCoxeter tc(left);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
// (a^3, a)
tc.add_pair({0}, {1, 1});
// (a, b^2)
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 5);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"024",
"exceptions",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
congruence::ToddCoxeter tc1(left);
tc1.set_number_of_generators(2);
tc1.add_pair({0, 0, 0}, {0});
tc1.add_pair({0}, {1, 1});
REQUIRE(tc1.number_of_classes() == 5);
REQUIRE_THROWS_AS(ToddCoxeter(right, tc1), LibsemigroupsException);
REQUIRE_THROWS_AS(ToddCoxeter(twosided, tc1), LibsemigroupsException);
ToddCoxeter tc2(left, tc1);
REQUIRE(!tc1.contains({0}, {1}));
tc2.add_pair({0}, {1});
check_hlt(tc2);
check_felsch(tc2);
check_random(tc2);
check_Rc_style(tc2);
check_R_over_C_style(tc2);
check_CR_style(tc2);
check_Cr_style(tc2);
REQUIRE(tc2.number_of_classes() == 1);
ToddCoxeter tc3(left);
tc3.set_number_of_generators(2);
tc3.add_pair({0, 0, 0}, {0});
tc3.add_pair({0}, {1, 1});
tc3.add_pair({0}, {1});
REQUIRE(tc3.number_of_classes() == 1);
}
{
congruence::ToddCoxeter tc1(right);
tc1.set_number_of_generators(2);
tc1.add_pair({0, 0, 0}, {0});
tc1.add_pair({0}, {1, 1});
REQUIRE(tc1.number_of_classes() == 5);
REQUIRE_THROWS_AS(ToddCoxeter(left, tc1), LibsemigroupsException);
REQUIRE_THROWS_AS(ToddCoxeter(twosided, tc1), LibsemigroupsException);
ToddCoxeter tc2(right, tc1);
REQUIRE(!tc1.contains({0}, {1}));
tc2.add_pair({0}, {1});
check_hlt(tc2);
check_felsch(tc2);
check_random(tc2);
check_Rc_style(tc2);
check_R_over_C_style(tc2);
check_CR_style(tc2);
check_Cr_style(tc2);
REQUIRE(tc2.number_of_classes() == 1);
ToddCoxeter tc3(right);
tc3.set_number_of_generators(2);
tc3.add_pair({0, 0, 0}, {0});
tc3.add_pair({0}, {1, 1});
tc3.add_pair({0}, {1});
REQUIRE(tc3.number_of_classes() == 1);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"025",
"obviously infinite",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
congruence::ToddCoxeter tc(left);
tc.set_number_of_generators(3);
tc.add_pair({0, 0, 0}, {0});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
REQUIRE(tc.number_of_classes() == POSITIVE_INFINITY);
REQUIRE(!tc.is_quotient_obviously_finite());
}
{
congruence::ToddCoxeter tc(right);
tc.set_number_of_generators(3);
tc.add_pair({0, 0, 0}, {0});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
REQUIRE(tc.number_of_classes() == POSITIVE_INFINITY);
REQUIRE(!tc.is_quotient_obviously_finite());
}
{
congruence::ToddCoxeter tc(twosided);
tc.set_number_of_generators(3);
tc.add_pair({0, 0, 0}, {0});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
REQUIRE(tc.number_of_classes() == POSITIVE_INFINITY);
REQUIRE(!tc.is_quotient_obviously_finite());
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"026",
"exceptions",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
congruence::ToddCoxeter tc(right);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({0}, {1, 1});
check_hlt(tc);
check_felsch(tc);
REQUIRE(tc.number_of_classes() == 5);
REQUIRE(tc.class_index_to_word(0) == word_type({0}));
// This next one should throw
REQUIRE_THROWS_AS(tc.quotient_froidure_pin(), LibsemigroupsException);
}
{
congruence::ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.add_pair({0, 0, 0}, {0});
tc.add_pair({0}, {1, 1});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 5);
REQUIRE(tc.class_index_to_word(0) == word_type({0}));
REQUIRE(tc.class_index_to_word(1) == word_type({1}));
REQUIRE(tc.class_index_to_word(2) == word_type({0, 0}));
REQUIRE(tc.class_index_to_word(3) == word_type({0, 1}));
REQUIRE(tc.class_index_to_word(4) == word_type({0, 0, 1}));
REQUIRE_THROWS_AS(tc.class_index_to_word(5), LibsemigroupsException);
REQUIRE_THROWS_AS(tc.class_index_to_word(100), LibsemigroupsException);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"027",
"empty",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
congruence::ToddCoxeter tc(left);
REQUIRE(tc.empty());
tc.set_number_of_generators(3);
REQUIRE(tc.empty());
tc.add_pair({0}, {2});
REQUIRE(tc.empty());
tc.reserve(100);
tc.reserve(200);
REQUIRE(tc.empty());
}
{
FroidurePin<BMat8> S(
{BMat8({{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}})});
ToddCoxeter tc(twosided, S);
REQUIRE(tc.empty());
tc.add_pair({0}, {0, 0});
REQUIRE(tc.empty());
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"028",
"congruence of fpsemigroup::ToddCoxeter",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
fpsemigroup::ToddCoxeter tc1;
tc1.set_alphabet(
"ab");
tc1.add_rule(
"aaa",
"a");
tc1.add_rule(
"a",
"bb");
REQUIRE(tc1.size() == 5);
congruence::ToddCoxeter tc2(left, tc1);
REQUIRE(tc2.empty());
tc2.add_pair({0}, {1});
REQUIRE_THROWS_AS(tc2.add_pair({0}, {2}), LibsemigroupsException);
check_hlt_no_save(tc2);
check_hlt_save_throws(tc2);
check_felsch_throws(tc2);
check_random(tc2);
REQUIRE(tc2.number_of_classes() == 1);
}
{
fpsemigroup::ToddCoxeter tc1;
tc1.set_alphabet(
"ab");
tc1.add_rule(
"aaa",
"a");
tc1.add_rule(
"a",
"bb");
congruence::ToddCoxeter tc2(left, tc1);
tc2.add_pair({0}, {1});
check_hlt(tc2);
check_felsch(tc2);
check_random(tc2);
check_Rc_style(tc2);
check_R_over_C_style(tc2);
check_CR_style(tc2);
check_Cr_style(tc2);
REQUIRE(!tc2.empty());
REQUIRE_THROWS_AS(tc2.add_pair({0}, {2}), LibsemigroupsException);
REQUIRE(tc2.number_of_classes() == 1);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"029",
"!KnuthBendix.started()",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
"abB");
kb.add_rule(
"bb",
"B");
kb.add_rule(
"BaB",
"aba");
REQUIRE(!kb.confluent());
REQUIRE(!kb.started());
std::unique_ptr<ToddCoxeter> tc = nullptr;
SECTION(
"2-sided") {
tc = std::make_unique<ToddCoxeter>(twosided, kb);
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
// Don't use the other check_* functions because they run to avoid an
// issue with fpsemigroup::ToddCoxeter.
}
SECTION(
"left") {
tc = std::make_unique<ToddCoxeter>(left, kb);
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
// Don't use the other check_* functions because they run to avoid an
// issue with fpsemigroup::ToddCoxeter.
}
SECTION(
"right") {
tc = std::make_unique<ToddCoxeter>(left, kb);
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
// Don't use the other check_* functions because they run to avoid an
// issue with fpsemigroup::ToddCoxeter.
}
REQUIRE(!tc->has_parent_froidure_pin());
tc->add_pair({1}, {2});
REQUIRE(tc->is_quotient_obviously_infinite());
REQUIRE(tc->number_of_classes() == POSITIVE_INFINITY);
REQUIRE(std::vector<relation_type>(tc->cbegin_generating_pairs(),
tc->cend_generating_pairs())
== std::vector<relation_type>(
{{{1, 1}, {2}}, {{2, 0, 2}, {0, 1, 0}}, {{1}, {2}}}));
REQUIRE(!tc->finished());
REQUIRE(!tc->started());
tc->add_pair({1}, {0});
REQUIRE(!tc->is_quotient_obviously_infinite());
REQUIRE(tc->number_of_classes() == 1);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"030",
"KnuthBendix.finished()",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
"abB");
kb.add_rule(
"bb",
"B");
kb.add_rule(
"BaB",
"aba");
REQUIRE(!kb.confluent());
kb.run();
REQUIRE(kb.confluent());
REQUIRE(kb.number_of_active_rules() == 6);
REQUIRE(kb.finished());
std::unique_ptr<ToddCoxeter> tc = nullptr;
SECTION(
"2-sided") {
tc = std::make_unique<ToddCoxeter>(twosided, kb);
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
// Don't use the other check_* functions because they run to avoid an
// issue with fpsemigroup::ToddCoxeter.
}
SECTION(
"left") {
tc = std::make_unique<ToddCoxeter>(left, kb);
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
// Don't use the other check_* functions because they run to avoid an
// issue with fpsemigroup::ToddCoxeter.
}
SECTION(
"right") {
tc = std::make_unique<ToddCoxeter>(right, kb);
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
// Don't use the other check_* functions because they run to avoid an
// issue with fpsemigroup::ToddCoxeter.
}
REQUIRE(tc->has_parent_froidure_pin());
tc->add_pair({1}, {2});
REQUIRE(tc->is_quotient_obviously_infinite());
REQUIRE(tc->number_of_classes() == POSITIVE_INFINITY);
REQUIRE(std::vector<relation_type>(tc->cbegin_generating_pairs(),
tc->cend_generating_pairs())
== std::vector<relation_type>(
{{{1, 1}, {2}}, {{2, 0, 2}, {0, 1, 0}}, {{1}, {2}}}));
tc->add_pair({1}, {0});
REQUIRE(!tc->is_quotient_obviously_infinite());
REQUIRE(tc->number_of_classes() == 1);
if (tc->kind() == twosided) {
REQUIRE(tc->quotient_froidure_pin()->size() == 1);
}
else {
REQUIRE_THROWS_AS(tc->quotient_froidure_pin(), LibsemigroupsException);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"031",
"KnuthBendix.finished()",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
"abB");
kb.add_rule(
"bb",
"B");
kb.add_rule(
"BaB",
"aba");
kb.add_rule(
"a",
"b");
kb.add_rule(
"b",
"B");
REQUIRE(kb.confluent());
kb.run();
REQUIRE(kb.confluent());
REQUIRE(kb.number_of_active_rules() == 3);
REQUIRE(kb.size() == 1);
REQUIRE(kb.is_obviously_finite());
REQUIRE(kb.finished());
std::unique_ptr<ToddCoxeter> tc = nullptr;
SECTION(
"2-sided") {
tc = std::make_unique<ToddCoxeter>(twosided, kb);
tc->add_pair({1}, {2});
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
check_Rc_style(*tc);
check_R_over_C_style(*tc);
check_CR_style(*tc);
check_Cr_style(*tc);
}
SECTION(
"left") {
tc = std::make_unique<ToddCoxeter>(left, kb);
tc->add_pair({1}, {2});
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
check_Rc_style(*tc);
check_R_over_C_style(*tc);
check_CR_style(*tc);
check_Cr_style(*tc);
}
SECTION(
"right") {
tc = std::make_unique<ToddCoxeter>(left, kb);
tc->add_pair({1}, {2});
check_hlt(*tc);
check_felsch(*tc);
check_random(*tc);
check_Rc_style(*tc);
check_R_over_C_style(*tc);
check_CR_style(*tc);
check_Cr_style(*tc);
}
REQUIRE(tc->has_parent_froidure_pin());
REQUIRE(tc->number_of_classes() == 1);
if (tc->kind() == twosided) {
REQUIRE(tc->quotient_froidure_pin()->size() == 1);
}
else {
REQUIRE_THROWS_AS(tc->quotient_froidure_pin(), LibsemigroupsException);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"032",
"prefill",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
detail::DynamicArray2<ToddCoxeter::class_index_type> rv(2, 1);
REQUIRE(rv.number_of_cols() == 2);
REQUIRE(rv.number_of_rows() == 1);
{
ToddCoxeter tc(twosided);
// prefill before number_of_generators are set
REQUIRE_THROWS_AS(tc.prefill(rv), LibsemigroupsException);
tc.set_number_of_generators(3);
// prefill where number_of_generators != number_of_cols of rv
REQUIRE_THROWS_AS(tc.prefill(rv), LibsemigroupsException);
}
{
ToddCoxeter tc(left);
tc.set_number_of_generators(2);
rv.set(0, 0, 0);
rv.set(0, 1, 1);
// prefill with too few rows
REQUIRE_THROWS_AS(tc.prefill(rv), LibsemigroupsException);
rv.add_rows(1);
REQUIRE(rv.number_of_rows() == 2);
rv.set(1, 0, UNDEFINED);
rv.set(1, 1, UNDEFINED);
// prefill with bad value (0, 0)
REQUIRE_THROWS_AS(tc.prefill(rv), LibsemigroupsException);
rv.set(0, 0, 2);
// prefill with bad value (0, 0)
REQUIRE_THROWS_AS(tc.prefill(rv), LibsemigroupsException);
rv.set(0, 0, 1);
// UNDEFINED is not allowed
REQUIRE_THROWS_AS(tc.prefill(rv), LibsemigroupsException);
rv.set(1, 0, 1);
rv.set(1, 1, 1);
tc.prefill(rv);
}
{
detail::DynamicArray2<ToddCoxeter::class_index_type> rv2(2, 0);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
REQUIRE_THROWS_AS(tc.prefill(rv2), LibsemigroupsException);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"033",
"congruence of ToddCoxeter",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc1(twosided);
tc1.set_number_of_generators(2);
tc1.add_pair({0, 0, 0}, {0});
tc1.add_pair({0}, {1, 1});
REQUIRE(tc1.number_of_classes() == 5);
ToddCoxeter tc2(left, tc1);
tc2.next_lookahead(1);
tc2.report_every(1);
REQUIRE(!tc2.empty());
check_hlt(tc2);
check_random(tc2);
tc2.add_pair({0}, {0, 0});
REQUIRE(tc2.number_of_classes() == 3);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"034",
"congruence of ToddCoxeter",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
using Transf = LeastTransf<5>;
FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3, 3})});
REQUIRE(S.size() == 88);
REQUIRE(S.number_of_rules() == 18);
ToddCoxeter tc(twosided, S);
tc.froidure_pin_policy(options::froidure_pin::none);
tc.set_number_of_generators(2);
check_hlt_no_save(tc);
check_hlt_save_throws(tc);
check_felsch_throws(tc);
check_random(tc);
tc.add_pair({0}, {1, 1});
REQUIRE(tc.number_of_classes() == 1);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"035",
"congruence on FpSemigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
FpSemigroup S;
S.set_alphabet(
"abe");
S.set_identity(
"e");
S.add_rule(
"abb",
"bb");
S.add_rule(
"bbb",
"bb");
S.add_rule(
"aaaa",
"a");
S.add_rule(
"baab",
"bb");
S.add_rule(
"baaab",
"b");
S.add_rule(
"babab",
"b");
S.add_rule(
"bbaaa",
"bb");
S.add_rule(
"bbaba",
"bbaa");
REQUIRE(S.knuth_bendix()->confluent());
REQUIRE(S.knuth_bendix()->number_of_rules() == 13);
ToddCoxeter tc(left, *S.knuth_bendix());
tc.add_pair({0}, {1, 1, 1});
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 2);
REQUIRE(std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cend_normal_forms())
== std::vector<word_type>({{0}, {2}}));
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"036",
"exceptions",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
using Transf = LeastTransf<5>;
FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3, 3})});
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.add_pair({0}, {1});
tc.add_pair({0, 0}, {0});
REQUIRE(tc.number_of_classes() == 1);
REQUIRE_THROWS_AS(tc.prefill(S.right_cayley_graph()),
LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"037",
"copy constructor",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.add_pair({0}, {1});
tc.add_pair({0, 0}, {0});
tc.strategy(options::strategy::felsch);
REQUIRE(tc.strategy() == options::strategy::felsch);
REQUIRE(!tc.complete());
REQUIRE(tc.compatible());
REQUIRE(tc.number_of_classes() == 1);
REQUIRE(std::vector<word_type>(tc.cbegin_normal_forms(),
tc.cend_normal_forms())
== std::vector<word_type>(1, {0}));
REQUIRE(tc.complete());
REQUIRE(tc.compatible());
ToddCoxeter copy(tc);
REQUIRE(copy.number_of_generators() == 2);
REQUIRE(copy.number_of_generating_pairs() == 2);
REQUIRE(copy.finished());
REQUIRE(copy.number_of_classes() == 1);
REQUIRE(copy.froidure_pin_policy() == options::froidure_pin::none);
REQUIRE(copy.complete());
REQUIRE(copy.compatible());
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"038",
"simplify",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(2);
tc.add_pair({0, 0}, {1});
tc.add_pair({0, 0}, {0});
tc.add_pair({0, 1, 0}, {0, 0});
tc.add_pair({0, 1, 0, 1}, {0, 1, 0});
tc.simplify();
REQUIRE(tc.number_of_generating_pairs() == 4);
REQUIRE(tc.number_of_classes() == 1);
std::vector<word_type> result(tc.cbegin_relations(), tc.cend_relations());
std::sort(result.begin(), result.end());
REQUIRE(result
== std::vector<word_type>(
{{0}, {0}, {0}, {0}, {0, 0}, {0, 1, 0}, {0, 1, 0, 1}, {1}}));
ToddCoxeter tc2(right, tc);
tc2.add_pair({0, 0}, {1});
tc2.add_pair({0, 0}, {0});
tc2.add_pair({0, 1, 0}, {0, 0});
tc2.add_pair({0, 1, 0, 1}, {0, 1, 0});
REQUIRE(tc2.felsch_tree_height() == 4);
REQUIRE(std::equal(tc.cbegin_relations(),
tc.cend_relations(),
tc2.cbegin_relations(),
tc2.cend_relations()));
REQUIRE(std::vector<word_type>(tc2.cbegin_extra(), tc2.cend_extra())
== std::vector<word_type>({{0, 0},
{1},
{0, 0},
{0},
{0, 1, 0},
{0, 0},
{0, 1, 0, 1},
{0, 1, 0}}));
tc2.simplify();
REQUIRE(
std::vector<word_type>(tc2.cbegin_extra(), tc2.cend_extra()).empty());
REQUIRE(tc2.felsch_tree_height() == 4);
REQUIRE(tc2.number_of_classes() == 1);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"039",
"stylic_monoid",
"[todd-coxeter][quick][no-coverage][no-valgrind]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(9);
for (
auto const& w : stylic_monoid(9)) {
tc.add_pair(w.first, w.second);
}
tc.strategy(options::strategy::random);
REQUIRE_THROWS_AS(tc.run_for(std::chrono::milliseconds(100)),
LibsemigroupsException);
tc.remove_duplicate_generating_pairs()
.sort_generating_pairs()
.strategy(options::strategy::hlt)
.lookahead(options::lookahead::partial | options::lookahead::hlt);
REQUIRE(tc.number_of_classes() == 115
'974);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"040",
"fibonacci_semigroup(4, 6)",
"[todd-coxeter][fail]") {
auto rg = ReportGuard();
ToddCoxeter tc(twosided);
tc.set_number_of_generators(6);
for (
auto const& w : fibonacci_semigroup(4, 6)) {
tc.add_pair(w.first, w.second);
}
tc.strategy(options::strategy::felsch);
REQUIRE(tc.number_of_classes() == 0);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"041",
"some finite classes",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc(twosided);
tc.set_number_of_generators(1);
REQUIRE(tc.number_of_classes() == POSITIVE_INFINITY);
tc.add_pair({0, 0, 0, 0, 0, 0}, {0, 0, 0, 0});
tc.add_pair({0, 0, 0, 0, 0, 0}, {0, 0, 0, 0});
tc.add_pair({0, 0, 0, 0, 0, 0}, {0, 0, 0, 0});
tc.strategy(options::strategy::random)
.remove_duplicate_generating_pairs()
.standardize(
true);
REQUIRE(!tc.compatible());
REQUIRE_THROWS_AS(tc.run_for(std::chrono::microseconds(1)),
LibsemigroupsException);
tc.strategy(options::strategy::CR);
size_t x = 0;
REQUIRE_THROWS_AS(tc.run_until([&x] {
return x > 4; }),
LibsemigroupsException);
tc.lower_bound(100)
.use_relations_in_extra(
true)
.deduction_policy(options::deductions::v1
| options::deductions::unlimited)
.restandardize(
true)
.max_preferred_defs(0);
REQUIRE_THROWS_AS(tc.hlt_defs(0), LibsemigroupsException);
REQUIRE_THROWS_AS(tc.f_defs(0), LibsemigroupsException);
tc.hlt_defs(10)
.f_defs(10)
.lookahead_growth_factor(3.0)
.lookahead_growth_threshold(100
'000)
.large_collapse(1);
REQUIRE_THROWS_AS(tc.lookahead_growth_factor(0.1),
LibsemigroupsException);
REQUIRE(tc.random_interval() == std::chrono::milliseconds(200));
REQUIRE(tc.felsch_tree_height() == 6);
REQUIRE(tc.number_of_classes() == 5);
REQUIRE(tc.number_of_words(0) == 1);
REQUIRE(tc.number_of_words(1) == 1);
REQUIRE(tc.number_of_words(2) == 1);
REQUIRE(tc.number_of_words(3) == POSITIVE_INFINITY);
REQUIRE(tc.number_of_words(4) == POSITIVE_INFINITY);
REQUIRE(tc.standardization_order() == ToddCoxeter::order::none);
REQUIRE(tc.felsch_tree_number_of_nodes() == 7);
REQUIRE_THROWS_AS(tc.remove_duplicate_generating_pairs(),
LibsemigroupsException);
ToddCoxeter tc2(left, tc);
tc2.add_pair({0, 0}, {0});
tc2.add_pair({0, 0}, {0});
tc2.remove_duplicate_generating_pairs();
// Uses CongruenceInterface's generating pairs
REQUIRE(tc2.number_of_generating_pairs() == 2);
ToddCoxeter tc3(twosided);
tc3.set_number_of_generators(1);
REQUIRE(tc3.is_non_trivial() == tril::
TRUE);
tc3.add_pair({0, 0}, {0});
REQUIRE(tc3.is_non_trivial() == tril::unknown);
REQUIRE(tc3.number_of_classes() == 1);
REQUIRE(tc3.is_non_trivial() == tril::
FALSE);
REQUIRE(!tc.settings_string().empty());
REQUIRE(!tc3.settings_string().empty());
REQUIRE(!tc.stats_string().empty());
}
// Takes about 6m
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"042",
"symmetric_group(10, Moore)",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard(
true);
auto s = symmetric_group(10, author::Moore);
for (
auto& rel : s) {
if (rel.first.empty()) {
rel.first = {2};
}
if (rel.second.empty()) {
rel.second = {2};
}
}
auto p = make<Presentation<word_type>>(s);
p.alphabet(3);
presentation::add_identity_rules(p, 2);
p.validate();
ToddCoxeter tc(twosided);
tc.set_number_of_generators(3);
for (size_t i = 0; i < p.rules.size() - 1; i += 2) {
tc.add_pair(p.rules[i], p.rules[i + 1]);
}
REQUIRE(tc.number_of_classes() == 3
'628'800);
std::cout << tc.stats_string();
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"043",
"symmetric_group(7, Coxeter + Moser)",
"[todd-coxeter][quick][no-valgrind]") {
auto rg = ReportGuard(REPORT);
size_t n = 7;
auto s = symmetric_group(n, author::Coxeter + author::Moser);
for (
auto& rel : s) {
if (rel.first.empty()) {
rel.first = {n - 1};
}
if (rel.second.empty()) {
rel.second = {n - 1};
}
}
auto p = make<Presentation<word_type>>(s);
p.alphabet(n);
presentation::add_identity_rules(p, n - 1);
p.validate();
ToddCoxeter tc(twosided);
tc.set_number_of_generators(n);
for (size_t i = 0; i < p.rules.size() - 1; i += 2) {
tc.add_pair(p.rules[i], p.rules[i + 1]);
}
tc.run_for(std::chrono::microseconds(1));
REQUIRE(tc.is_non_trivial() == tril::
TRUE);
REQUIRE(!tc.finished());
tc.standardize(ToddCoxeter::order::shortlex);
tc.standardize(ToddCoxeter::order::none);
tc.strategy(options::strategy::CR).f_defs(100);
REQUIRE(tc.number_of_classes() == 5
'040);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"116",
"symmetric_group(7, Burnside + Miller)",
"[todd-coxeter][quick][no-valgrind]") {
auto rg = ReportGuard(REPORT);
size_t n = 7;
auto s = symmetric_group(n, author::Burnside + author::Miller);
for (
auto& rel : s) {
if (rel.first.empty()) {
rel.first = {n - 1};
}
if (rel.second.empty()) {
rel.second = {n - 1};
}
}
auto p = make<Presentation<word_type>>(s);
p.alphabet(n);
presentation::add_identity_rules(p, n - 1);
p.validate();
ToddCoxeter tc(twosided);
tc.set_number_of_generators(n);
for (size_t i = 0; i < p.rules.size() - 1; i += 2) {
tc.add_pair(p.rules[i], p.rules[i + 1]);
}
REQUIRE(tc.number_of_classes() == 5
'040);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"044",
"Option exceptions",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
REQUIRE_THROWS_AS(options::deductions::unlimited
| options::deductions::unlimited,
LibsemigroupsException);
REQUIRE_THROWS_AS(options::deductions::v1 | options::deductions::v2,
LibsemigroupsException);
REQUIRE_THROWS_AS(options::lookahead::hlt | options::lookahead::hlt,
LibsemigroupsException);
REQUIRE_THROWS_AS(options::lookahead::hlt | options::lookahead::felsch,
LibsemigroupsException);
REQUIRE_THROWS_AS(options::lookahead::full | options::lookahead::partial,
LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"045",
"Options operator<<",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
ToddCoxeter tc(twosided);
tc.strategy(options::strategy::hlt);
tc.settings_string();
tc.strategy(options::strategy::felsch);
tc.settings_string();
tc.strategy(options::strategy::random);
tc.settings_string();
tc.strategy(options::strategy::CR);
tc.settings_string();
tc.strategy(options::strategy::R_over_C);
tc.settings_string();
tc.strategy(options::strategy::Cr);
tc.settings_string();
tc.strategy(options::strategy::Rc);
tc.settings_string();
}
{
ToddCoxeter tc(twosided);
tc.lookahead(options::lookahead::full | options::lookahead::felsch);
tc.settings_string();
tc.lookahead(options::lookahead::full | options::lookahead::hlt);
tc.settings_string();
tc.lookahead(options::lookahead::partial | options::lookahead::felsch);
tc.settings_string();
tc.lookahead(options::lookahead::partial | options::lookahead::hlt);
tc.settings_string();
}
{
ToddCoxeter tc(twosided);
tc.deduction_policy(options::deductions::v1
| options::deductions::no_stack_if_no_space);
tc.settings_string();
tc.deduction_policy(options::deductions::v1
| options::deductions::purge_all);
tc.settings_string();
tc.deduction_policy(options::deductions::v1
| options::deductions::purge_from_top);
tc.settings_string();
tc.deduction_policy(options::deductions::v1
| options::deductions::discard_all_if_no_space);
tc.settings_string();
tc.deduction_policy(options::deductions::v1
| options::deductions::unlimited);
tc.settings_string();
tc.deduction_policy(options::deductions::v2
| options::deductions::no_stack_if_no_space);
tc.settings_string();
tc.deduction_policy(options::deductions::v2
| options::deductions::purge_all);
tc.settings_string();
tc.deduction_policy(options::deductions::v2
| options::deductions::purge_from_top);
tc.deduction_policy(options::deductions::v2
| options::deductions::discard_all_if_no_space);
tc.settings_string();
tc.deduction_policy(options::deductions::v2
| options::deductions::unlimited);
tc.settings_string();
}
{
ToddCoxeter tc(twosided);
tc.froidure_pin_policy(options::froidure_pin::none);
tc.settings_string();
tc.froidure_pin_policy(options::froidure_pin::use_cayley_graph);
tc.settings_string();
tc.froidure_pin_policy(options::froidure_pin::use_relations);
tc.settings_string();
}
{
ToddCoxeter tc(twosided);
tc.preferred_defs(options::preferred_defs::none);
tc.settings_string();
tc.preferred_defs(options::preferred_defs::immediate_no_stack);
tc.settings_string();
tc.preferred_defs(options::preferred_defs::immediate_yes_stack);
tc.settings_string();
tc.preferred_defs(options::preferred_defs::deferred);
tc.settings_string();
}
}
// Takes about 9m3s (2021 - MacBook Air M1 - 8GB RAM)
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"046",
"Easdown-East-FitzGerald DualSymInv(5)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(REPORT);
auto const n = 5;
ToddCoxeter tc(twosided);
setup(tc,
n + 1,
dual_symmetric_inverse_monoid,
n,
author::Easdown + author::East + author::FitzGerald);
// tc.strategy(options::strategy::Rc)
// .max_deductions(10'000)
// .max_preferred_defs(512)
// .random_interval(std::chrono::seconds(10));
check_hlt(tc);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_R_over_C_style(tc);
check_Rc_style(tc);
REQUIRE(tc.number_of_classes() == 6
'721);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"047",
"uniform_block_bijection_monoid(3) (FitzGerald)",
"[todd-coxeter][quick]") {
// 16, 131, 1496, 22482, 426833, 9934563, 9934563
auto rg = ReportGuard(REPORT);
auto const n = 3;
ToddCoxeter tc(twosided);
setup(tc, n + 1, uniform_block_bijection_monoid, n, author::FitzGerald);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_R_over_C_style(tc);
check_Rc_style(tc);
REQUIRE(tc.number_of_classes() == 16);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"048",
"stellar_monoid(7) (Gay-Hivert)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(
false);
size_t
const n = 7;
ToddCoxeter tc1(congruence_kind::twosided);
setup(tc1, n + 1, rook_monoid, n, 0);
ToddCoxeter tc2(congruence_kind::twosided, tc1);
setup(tc2, n + 1, stellar_monoid, n);
tc2.strategy(options::strategy::felsch);
REQUIRE(tc2.number_of_classes() == 13
'700);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"049",
"partition_monoid(4) (East)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(REPORT);
size_t
const n = 4;
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, 5, partition_monoid, n, author::East);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_R_over_C_style(tc);
check_Rc_style(tc);
REQUIRE(tc.number_of_classes() == 4
'140);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"050",
"singular_brauer_monoid(6) (Maltcev + Mazorchuk)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(REPORT);
size_t
const n = 6;
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, n * n - n, singular_brauer_monoid, n);
tc.sort_generating_pairs().remove_duplicate_generating_pairs();
REQUIRE(tc.number_of_classes() == 9
'675);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"051",
"orientation_preserving_monoid(6) (Ruskuc + Arthur)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(REPORT);
size_t
const n = 6;
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, 3, orientation_preserving_monoid, n);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_R_over_C_style(tc);
check_Rc_style(tc);
REQUIRE(tc.number_of_classes() == 2
'742);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"052",
"orientation_reversing_monoid(5) (Ruskuc + Arthur)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(REPORT);
size_t
const n = 5;
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, 4, orientation_reversing_monoid, n);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.number_of_classes() == 1
'015);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"053",
"temperley_lieb_monoid(10) (East)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(REPORT);
size_t
const n = 10;
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, n - 1, temperley_lieb_monoid, n);
REQUIRE(tc.number_of_classes() == 16
'795);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"054",
"Generate GAP benchmarks for stellar_monoid(n) (Gay-Hivert)",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 9; ++n) {
ToddCoxeter tc1(congruence_kind::twosided);
setup(tc1, n + 1, rook_monoid, n, 0);
ToddCoxeter tc2(congruence_kind::twosided, tc1);
setup(tc2, n + 1, stellar_monoid, n);
output_gap_benchmark_file(
"stellar-" + std::to_string(n) +
".g", tc2);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"055",
"Generate GAP benchmarks for partition_monoid(n) (East)",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 4; n <= 6; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, 5, partition_monoid, n, author::East);
tc.save(
true);
output_gap_benchmark_file(
"partition-" + std::to_string(n) +
".g", tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"056",
"Generate GAP benchmarks for dual symmetric inverse monoid (East)",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 6; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc,
n + 1,
dual_symmetric_inverse_monoid,
n,
author::Easdown + author::East + author::FitzGerald);
output_gap_benchmark_file(
"dual-sym-inv-" + std::to_string(n) +
".g",
tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"057",
"Generate GAP benchmarks for "
"uniform_block_bijection_monoid (FitzGerald)",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 7; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, n + 1, uniform_block_bijection_monoid, n, author::FitzGerald);
output_gap_benchmark_file(
"uniform-block-bijection-" + std::to_string(n) +
".g", tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"058",
"Generate GAP benchmarks for stylic monoids",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 9; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, n, stylic_monoid, n);
output_gap_benchmark_file(
"stylic-" + std::to_string(n) +
".g", tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"059",
"Generate GAP benchmarks for OP_n",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 9; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, 3, orientation_preserving_monoid, n);
output_gap_benchmark_file(
"orient-" + std::to_string(n) +
".g", tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"060",
"Generate GAP benchmarks for OR_n",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 8; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, 4, orientation_reversing_monoid, n);
output_gap_benchmark_file(
"orient-reverse-" + std::to_string(n) +
".g",
tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"061",
"Generate GAP benchmarks for temperley_lieb_monoid(n)",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 13; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, n - 1, temperley_lieb_monoid, n);
output_gap_benchmark_file(
"temperley-lieb-" + std::to_string(n) +
".g",
tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"062",
"Generate GAP benchmarks for singular_brauer_monoid(n)",
"[todd-coxeter][fail]") {
auto rg = ReportGuard(
false);
for (size_t n = 3; n <= 7; ++n) {
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, n * n - n, singular_brauer_monoid, n);
output_gap_benchmark_file(
"singular-brauer-" + std::to_string(n) +
".g",
tc);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"111",
"partition_monoid(2)",
"[todd-coxeter][quick]") {
ToddCoxeter p(congruence_kind::twosided);
p.set_number_of_generators(4);
p.add_pair({0, 1}, {1});
p.add_pair({1, 0}, {1});
p.add_pair({0, 2}, {2});
p.add_pair({2, 0}, {2});
p.add_pair({0, 3}, {3});
p.add_pair({3, 0}, {3});
p.add_pair({1, 1}, {0});
p.add_pair({1, 3}, {3});
p.add_pair({2, 2}, {2});
p.add_pair({3, 1}, {3});
p.add_pair({3, 3}, {3});
p.add_pair({2, 3, 2}, {2});
p.add_pair({3, 2, 3}, {3});
p.add_pair({1, 2, 1, 2}, {2, 1, 2});
p.add_pair({2, 1, 2, 1}, {2, 1, 2});
auto rg = ReportGuard(
false);
REQUIRE(p.number_of_classes() == 15);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"112",
"brauer_monoid(4) (Kudryavtseva + Mazorchuk)",
"[todd-coxeter][quick][no-valgrind][no-coverage]") {
auto rg = ReportGuard(REPORT);
size_t
const n = 4;
ToddCoxeter tc(congruence_kind::twosided);
setup(tc, 2 * n - 1, brauer_monoid, n);
tc.sort_generating_pairs().remove_duplicate_generating_pairs();
REQUIRE(tc.number_of_classes() == 105);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"113",
"symmetric_inverse_monoid",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
size_t n = 5;
auto s = symmetric_inverse_monoid(n, author::Sutov);
auto p = make<Presentation<word_type>>(s);
p.alphabet(n + 1);
presentation::replace_word(p, word_type({}), {n});
presentation::add_identity_rules(p, n);
p.validate();
ToddCoxeter tc(congruence_kind::twosided);
tc.set_number_of_generators(n + 1);
for (size_t i = 0; i < p.rules.size() - 1; i += 2) {
tc.add_pair(p.rules[i], p.rules[i + 1]);
}
REQUIRE(tc.number_of_classes() == 1546);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"114",
"partial_transformation_monoid(5)",
"[todd-coxeter][standard]") {
auto rg = ReportGuard(REPORT);
size_t n = 5;
auto s = partial_transformation_monoid(n, author::Sutov);
auto p = make<Presentation<word_type>>(s);
p.alphabet(n + 2);
presentation::replace_word(p, word_type({}), {n + 1});
presentation::add_identity_rules(p, n + 1);
p.validate();
ToddCoxeter tc(congruence_kind::twosided);
tc.set_number_of_generators(n + 2);
for (size_t i = 0; i < p.rules.size() - 1; i += 2) {
tc.add_pair(p.rules[i], p.rules[i + 1]);
}
REQUIRE(tc.number_of_classes() == 7776);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"115",
"full_transformation_monoid(5)",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard(REPORT);
size_t n = 7;
auto s = full_transformation_monoid(n, author::Iwahori);
auto p = make<Presentation<word_type>>(s);
p.alphabet(n + 1);
presentation::replace_word(p, word_type({}), {n});
presentation::add_identity_rules(p, n);
p.validate();
ToddCoxeter tc(congruence_kind::twosided);
tc.set_number_of_generators(n + 1);
for (size_t i = 0; i < p.rules.size() - 1; i += 2) {
tc.add_pair(p.rules[i], p.rules[i + 1]);
}
REQUIRE(tc.number_of_classes() == 823543);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"110",
"to_gap_string",
"[todd-coxeter][quick]") {
ToddCoxeter tc1(congruence_kind::right);
tc1.set_number_of_generators(2);
tc1.add_pair({0, 1}, {1, 0});
REQUIRE_THROWS_AS(tc1.to_gap_string(), LibsemigroupsException);
ToddCoxeter tc2(congruence_kind::left);
tc2.set_number_of_generators(2);
tc2.add_pair({0, 1}, {1, 0});
REQUIRE_THROWS_AS(tc2.to_gap_string(), LibsemigroupsException);
ToddCoxeter tc3(congruence_kind::twosided);
tc3.set_number_of_generators(2);
tc3.add_pair({0, 1}, {1, 0});
REQUIRE(tc3.to_gap_string().size() > 0);
}
}
// namespace congruence
namespace fpsemigroup {
constexpr
bool REPORT =
false;
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"063",
"add_rule",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
{
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"a",
"bb");
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
SECTION(
"R/C + Felsch lookahead") {
tc.congruence()
.strategy(options::strategy::R_over_C)
.lookahead(options::lookahead::felsch | options::lookahead::full);
tc.congruence().run();
}
SECTION(
"HLT + Felsch lookahead + save") {
tc.congruence()
.strategy(options::strategy::hlt)
.save(
true)
.lookahead(options::lookahead::felsch | options::lookahead::full)
.next_lookahead(2);
tc.congruence().run();
}
SECTION(
"Cr + small number of f_defs") {
tc.congruence().strategy(options::strategy::Cr).f_defs(3);
tc.congruence().run();
}
SECTION(
"Rc + small number of deductions") {
tc.congruence().strategy(options::strategy::Rc).max_deductions(0);
tc.congruence().run();
}
SECTION(
"Felsch + v2 + no preferred defs") {
tc.congruence()
.strategy(options::strategy::felsch)
.deduction_policy(options::deductions::v2
| options::deductions::purge_all)
.preferred_defs(options::preferred_defs::none);
}
SECTION(
"Felsch + v2 + immediate no stack") {
tc.congruence()
.strategy(options::strategy::felsch)
.deduction_policy(options::deductions::v2
| options::deductions::purge_from_top)
.preferred_defs(options::preferred_defs::immediate_no_stack);
}
SECTION(
"Felsch + v1 + immediate no stack") {
tc.congruence()
.strategy(options::strategy::felsch)
.deduction_policy(options::deductions::v1
| options::deductions::discard_all_if_no_space)
.preferred_defs(options::preferred_defs::immediate_no_stack);
}
SECTION(
"Felsch + v1 + immediate yes stack") {
tc.congruence()
.strategy(options::strategy::felsch)
.deduction_policy(options::deductions::v1
| options::deductions::no_stack_if_no_space)
.preferred_defs(options::preferred_defs::immediate_yes_stack);
}
SECTION(
"large collapse") {
tc.congruence().large_collapse(0);
}
REQUIRE(tc.size() == 5);
}
{
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"a",
"bb");
tc.congruence().next_lookahead(1);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 5);
}
}
// KnuthBendix methods fail for this one
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"064",
"(from kbmag/standalone/kb_data/s4) (KnuthBendix 49)",
"[todd-coxeter][quick][kbmag]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"abcd");
tc.add_rule(
"bb",
"c");
tc.add_rule(
"caca",
"abab");
tc.add_rule(
"bc",
"d");
tc.add_rule(
"cb",
"d");
tc.add_rule(
"aa",
"d");
tc.add_rule(
"ad",
"a");
tc.add_rule(
"da",
"a");
tc.add_rule(
"bd",
"b");
tc.add_rule(
"db",
"b");
tc.add_rule(
"cd",
"c");
tc.add_rule(
"dc",
"c");
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 24);
REQUIRE(tc.froidure_pin()->size() == 24);
REQUIRE(tc.normal_form(
"aaaaaaaaaaaaaaaaaaa") ==
"a");
REQUIRE(KnuthBendix(tc.froidure_pin()).confluent());
}
// Second of BHN's series of increasingly complicated presentations
// of 1. Doesn't terminate
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"065",
"(from kbmag/standalone/kb_data/degen4b) "
"(KnuthBendix 065)",
"[fail][todd-coxeter][kbmag][shortlex]") {
auto rg = ReportGuard();
ToddCoxeter tc;
tc.set_alphabet(
"abcdefg");
tc.set_identity(
"g");
tc.set_inverses(
"defabcg");
tc.add_rule(
"bbdeaecbffdbaeeccefbccefb",
"g");
tc.add_rule(
"ccefbfacddecbffaafdcaafdc",
"g");
tc.add_rule(
"aafdcdbaeefacddbbdeabbdea",
"g");
tc.congruence().lookahead(options::lookahead::full
| options::lookahead::felsch);
REQUIRE(!tc.is_obviously_infinite());
REQUIRE(tc.size() == 1);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"066",
"test validate",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"a",
"b");
tc.add_rule(
"bb",
"b");
REQUIRE_THROWS_AS(tc.add_rule(
"b",
"c"), LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"067",
"add_rules after construct. from semigroup",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
using Transf = LeastTransf<5>;
FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3, 3})});
REQUIRE(S.size() == 88);
REQUIRE(S.number_of_rules() == 18);
word_type w1, w2, w3, w4;
S.factorisation(w1, S.position(Transf({3, 4, 4, 4, 4})));
S.factorisation(w2, S.position(Transf({3, 1, 3, 3, 3})));
S.factorisation(w3, S.position(Transf({1, 3, 1, 3, 3})));
S.factorisation(w4, S.position(Transf({4, 2, 4, 4, 2})));
ToddCoxeter tc1(S);
tc1.add_rule(w1, w2);
check_hlt_no_save(tc1);
check_hlt_save_throws(tc1);
check_felsch_throws(tc1);
check_random(tc1);
REQUIRE(tc1.size() == 21);
REQUIRE(tc1.size() == tc1.froidure_pin()->size());
REQUIRE(tc1.equal_to(w3, w4));
REQUIRE(tc1.normal_form(w3) == tc1.normal_form(w4));
ToddCoxeter tc2(S);
tc2.add_rule(w1, w2);
check_hlt_no_save(tc2);
check_hlt_save_throws(tc2);
check_felsch_throws(tc2);
REQUIRE(tc2.size() == 21);
REQUIRE(tc2.size() == tc2.froidure_pin()->size());
REQUIRE(tc2.equal_to(w3, w4));
REQUIRE(tc2.normal_form(w3) == tc2.normal_form(w4));
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"068",
"Sym(5) from Chapter 3, Proposition 1.1 in NR",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ABabe");
tc.set_identity(
"e");
tc.add_rule(
"aa",
"e");
tc.add_rule(
"bbbbb",
"e");
tc.add_rule(
"babababa",
"e");
tc.add_rule(
"bB",
"e");
tc.add_rule(
"Bb",
"e");
tc.add_rule(
"BabBab",
"e");
tc.add_rule(
"aBBabbaBBabb",
"e");
tc.add_rule(
"aBBBabbbaBBBabbb",
"e");
tc.add_rule(
"aA",
"e");
tc.add_rule(
"Aa",
"e");
SECTION(
"Deduction policy == purge_from_top") {
tc.congruence()
.max_deductions(2)
.strategy(options::strategy::felsch)
.max_preferred_defs(3);
REQUIRE_THROWS_AS(tc.congruence().deduction_policy(
options::deductions::purge_from_top),
LibsemigroupsException);
tc.congruence().deduction_policy(options::deductions::v1
| options::deductions::purge_from_top);
}
SECTION(
"Deduction policy == purge_all") {
tc.congruence().max_deductions(2).strategy(options::strategy::felsch);
tc.congruence().deduction_policy(options::deductions::v1
| options::deductions::purge_all);
}
SECTION(
"Deduction policy == discard_all_if_no_space") {
tc.congruence().max_deductions(2).strategy(options::strategy::felsch);
tc.congruence().deduction_policy(
options::deductions::v2
| options::deductions::discard_all_if_no_space);
}
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 120);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"069",
"Chapter 7, Theorem 3.6 in NR (size 243)",
"[no-valgrind][todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbbb",
"b");
tc.add_rule(
"ababababab",
"aa");
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 243);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"070",
"finite semigroup (size 99)",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbbb",
"b");
tc.add_rule(
"abababab",
"aa");
REQUIRE(!tc.is_obviously_finite());
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 99);
REQUIRE(tc.finished());
REQUIRE(tc.is_obviously_finite());
}
// The following 8 examples are from Trevor Walker's Thesis: Semigroup
// enumeration - computer implementation and applications, p41.
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"071",
"Walker 1",
"[todd-coxeter][standard]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"abcABCDEFGHIXYZ");
tc.add_rule(
"A",
"aaaaaaaaaaaaaa");
tc.add_rule(
"B",
"bbbbbbbbbbbbbb");
tc.add_rule(
"C",
"cccccccccccccc");
tc.add_rule(
"D",
"aaaaba");
tc.add_rule(
"E",
"bbbbab");
tc.add_rule(
"F",
"aaaaca");
tc.add_rule(
"G",
"ccccac");
tc.add_rule(
"H",
"bbbbcb");
tc.add_rule(
"I",
"ccccbc");
tc.add_rule(
"X",
"aaa");
tc.add_rule(
"Y",
"bbb");
tc.add_rule(
"Z",
"ccc");
tc.add_rule(
"A",
"a");
tc.add_rule(
"B",
"b");
tc.add_rule(
"C",
"c");
tc.add_rule(
"D",
"Y");
tc.add_rule(
"E",
"X");
tc.add_rule(
"F",
"Z");
tc.add_rule(
"G",
"X");
tc.add_rule(
"H",
"Z");
tc.add_rule(
"I",
"Y");
tc.congruence()
.sort_generating_pairs(shortlex_compare)
.next_lookahead(500
'000)
.run_until([&tc]() ->
bool {
return tc.congruence().coset_capacity() >= 10
'000;
});
REQUIRE(!tc.finished());
REQUIRE(!tc.is_obviously_finite());
tc.congruence().standardize(tc_order::shortlex);
REQUIRE(!tc.finished());
tc.congruence().standardize(tc_order::lex);
REQUIRE(!tc.finished());
tc.congruence().standardize(tc_order::recursive);
REQUIRE(!tc.finished());
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
// This takes approx 1 seconds with Felsch . . .
REQUIRE(tc.size() == 1);
tc.congruence().standardize(tc_order::shortlex);
REQUIRE(std::is_sorted(tc.congruence().cbegin_normal_forms(),
tc.congruence().cend_normal_forms(),
ShortLexCompare<word_type>{}));
tc.congruence().standardize(tc_order::lex);
REQUIRE(std::is_sorted(tc.congruence().cbegin_normal_forms(),
tc.congruence().cend_normal_forms(),
LexicographicalCompare<word_type>{}));
tc.congruence().standardize(tc_order::recursive);
REQUIRE(std::is_sorted(tc.congruence().cbegin_normal_forms(),
tc.congruence().cend_normal_forms(),
RecursivePathCompare<word_type>{}));
}
// The following example is a good one for using the lookahead.
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"072",
"Walker 2",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"a");
tc.add_rule(
"bbb",
"b");
tc.add_rule(
"ababa",
"b");
tc.add_rule(
"aaaaaaaaaaaaaaaabaaaabaaaaaaaaaaaaaaaabaaaa",
"b");
REQUIRE(!tc.is_obviously_finite());
SECTION(
"custom HLT") {
tc.congruence()
.sort_generating_pairs()
.next_lookahead(1
'000'000)
.max_deductions(2
'000)
.use_relations_in_extra(
true)
.strategy(options::strategy::hlt)
.lookahead(options::lookahead::partial | options::lookahead::felsch)
.deduction_policy(options::deductions::v2
| options::deductions::no_stack_if_no_space);
}
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 14
'911);
tc.congruence().standardize(tc_order::shortlex);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"073",
"Walker 3",
"[todd-coxeter][standard]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaaaaaaaaaaaaaaa",
"a");
tc.add_rule(
"bbbbbbbbbbbbbbbb",
"b");
tc.add_rule(
"abb",
"baa");
tc.congruence().next_lookahead(2
'000'000);
tc.congruence().simplify();
REQUIRE(!tc.is_obviously_finite());
check_hlt(tc);
check_felsch(tc);
check_random(tc);
// check_Rc_style(tc); // Rc_style + partial lookahead works very badly
// 2m30s
check_R_over_C_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 20
'490);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"074",
"Walker 4",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard();
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbbbbb",
"b");
tc.add_rule(
"ababbbbababbbbababbbbababbbbababbbbababbbbababbbbabba",
"bb");
tc.congruence().next_lookahead(3
'000'000);
REQUIRE(!tc.is_obviously_finite());
check_hlt(tc);
// Felsch very slow
check_random(tc);
SECTION(
"custom R/C") {
tc.congruence()
.next_lookahead(3
'000'000)
.strategy(options::strategy::R_over_C)
.max_deductions(100
'000);
}
tc.congruence().run();
REQUIRE(tc.size() == 36
'412);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"075",
"Walker 5",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbbbbb",
"b");
tc.add_rule(
"ababbbbababbbbababbbbababbbbababbbbababbbbababbbbabbabbbbbaa",
"bb");
tc.congruence().next_lookahead(5
'000'000);
REQUIRE(!tc.is_obviously_finite());
// This example is extremely slow with Felsch
check_hlt(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
tc.congruence().run();
REQUIRE(tc.congruence().complete());
REQUIRE(tc.congruence().compatible());
REQUIRE(tc.size() == 72
'822);
std::cout << tc.congruence().stats_string();
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"076",
"not Walker 6",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard();
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbbbbbbbb",
"b");
tc.add_rule(
"ababbbbababbbbababbbbababbbbababbbbababbbbababbbbabbabbbbbbbb",
"bb");
tc.congruence().next_lookahead(5
'000'000);
REQUIRE(!tc.is_obviously_finite());
// This example is extremely slow with Felsch, the random strategy
// strategy is typically fastest
check_hlt(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 8);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"077",
"Walker 6",
"[todd-coxeter][standard]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbbbbbbbb",
"b");
std::string lng(
"ababbbbbbb");
lng += lng;
lng +=
"abbabbbbbbbb";
tc.add_rule(lng,
"bb");
REQUIRE(!tc.is_obviously_finite());
// This example is extremely slow with Felsch
check_hlt(tc);
check_random(tc);
// check_Rc_style(tc); // partial lookahead is too slow
// check_Cr_style(tc); // very slow
check_R_over_C_style(tc);
REQUIRE(tc.size() == 78
'722);
}
// Felsch is faster here too!
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"078",
"Walker 7",
"[todd-coxeter][standard]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"abcde");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbb",
"b");
tc.add_rule(
"ccc",
"c");
tc.add_rule(
"ddd",
"d");
tc.add_rule(
"eee",
"e");
tc.add_rule(
"ababab",
"aa");
tc.add_rule(
"bcbcbc",
"bb");
tc.add_rule(
"cdcdcd",
"cc");
tc.add_rule(
"dedede",
"dd");
tc.add_rule(
"ac",
"ca");
tc.add_rule(
"ad",
"da");
tc.add_rule(
"ae",
"ea");
tc.add_rule(
"bd",
"db");
tc.add_rule(
"be",
"eb");
tc.add_rule(
"ce",
"ec");
REQUIRE(!tc.is_obviously_finite());
check_hlt(tc);
check_felsch(tc);
check_random(tc);
// check_Rc_style(tc); // partial lookahead very slow ~8s
check_R_over_C_style(tc);
check_Cr_style(tc);
tc.congruence()
.deduction_policy(options::deductions::v1
| options::deductions::no_stack_if_no_space)
.preferred_defs(options::preferred_defs::none);
REQUIRE(tc.size() == 153
'500);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"079",
"Walker 8",
"[todd-coxeter][standard]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aaa",
"a");
tc.add_rule(
"bbbbbbbbbbbbbbbbbbbbbbb",
"b");
tc.add_rule(
"abbbbbbbbbbbabb",
"bba");
REQUIRE(tc.congruence().length_of_generating_pairs() == 46);
REQUIRE(!tc.is_obviously_finite());
tc.congruence().next_lookahead(500
'000);
// This example is extremely slow with Felsch
check_hlt(tc);
check_random(tc);
// check_Rc_style(tc); + partial lookahead too slow
// check_Cr_style(tc); // too slow
check_R_over_C_style(tc);
REQUIRE(tc.congruence().number_of_classes() == 270
'272);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"080",
"KnuthBendix 098",
"[todd-coxeter][quick][no-valgrind]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"aAbBcCdDyYfFgGe");
tc.set_identity(
"e");
tc.set_inverses(
"AaBbCcDdYyFfGge");
tc.add_rule(
"ab",
"c");
tc.add_rule(
"bc",
"d");
tc.add_rule(
"cd",
"y");
tc.add_rule(
"dy",
"f");
tc.add_rule(
"yf",
"g");
tc.add_rule(
"fg",
"a");
tc.add_rule(
"ga",
"b");
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 29);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"081",
"Holt 2 - SL(2, p)",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard();
ToddCoxeter tc;
tc.set_alphabet(
"xXyYe");
tc.set_identity(
"e");
tc.set_inverses(
"XxYye");
tc.add_rule(
"xxYXYXYX",
"e");
auto second = [](size_t p) -> std::string {
std::string s =
"xyyyyx";
s += std::string((p + 1) / 2,
'y');
s += s;
s += std::string(p,
'y');
s += std::string(2 * (p / 3),
'x');
return s;
};
REQUIRE(second(3) ==
"xyyyyxyyxyyyyxyyyyyxx");
SECTION(
"p = 3") {
tc.add_rule(second(3),
"e");
check_hlt(tc);
check_felsch(tc);
REQUIRE(tc.size() == 24);
}
SECTION(
"p = 5") {
tc.add_rule(second(5),
"e");
check_hlt(tc);
check_felsch(tc);
REQUIRE(tc.size() == 120);
}
SECTION(
"p = 7") {
tc.add_rule(second(7),
"e");
check_hlt(tc);
check_felsch(tc);
REQUIRE(tc.size() == 336);
}
SECTION(
"p = 11") {
tc.add_rule(second(11),
"e");
check_hlt(tc);
check_random(tc);
REQUIRE(tc.size() == 1
'320);
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"082",
"Holt 3",
"[todd-coxeter][standard]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"aAbBcCe");
tc.set_identity(
"e");
tc.set_inverses(
"AaBbCce");
tc.add_rule(
"bbCbc",
"e");
tc.add_rule(
"aaBab",
"e");
tc.add_rule(
"cABcabc",
"e");
REQUIRE(tc.congruence().is_non_trivial() == tril::
TRUE);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 6
'561);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"083",
"Holt 3",
"[todd-coxeter][fail]") {
auto rg = ReportGuard();
ToddCoxeter tc;
tc.set_alphabet(
"aAbBcCe");
tc.set_identity(
"e");
tc.set_inverses(
"AaBbCce");
tc.add_rule(
"aaCac",
"e");
tc.add_rule(
"acbbACb",
"e");
tc.add_rule(
"ABabccc",
"e");
REQUIRE(!tc.is_obviously_infinite());
REQUIRE(tc.congruence().number_of_generating_pairs() == 22);
tc.congruence().strategy(options::strategy::R_over_C);
tc.congruence()
.sort_generating_pairs()
.remove_duplicate_generating_pairs();
REQUIRE(tc.congruence().number_of_generating_pairs() == 22);
tc.congruence()
.lookahead(options::lookahead::partial | options::lookahead::hlt)
.lookahead_growth_factor(1.01)
.lookahead_growth_threshold(10)
.f_defs(250
'000)
.hlt_defs(20
'000'000);
// REQUIRE(tc.congruence().is_non_trivial() == tril::TRUE);
tc.congruence().run();
REQUIRE(tc.size() == 6
'561);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"084",
"Campbell-Reza 1",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"ab");
tc.add_rule(
"aa",
"bb");
tc.add_rule(
"ba",
"aaaaaab");
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
REQUIRE(tc.size() == 14);
tc.congruence().standardize(tc_order::shortlex);
REQUIRE(std::vector<word_type>(tc.congruence().cbegin_normal_forms(),
tc.congruence().cend_normal_forms())
== std::vector<word_type>({{0},
{1},
{0, 0},
{0, 1},
{1, 0},
{0, 0, 0},
{0, 0, 1},
{0, 0, 0, 0},
{0, 0, 0, 1},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 1},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0}}));
REQUIRE(tc.froidure_pin()->number_of_rules() == 6);
REQUIRE(tc.normal_form(
"aaaaaaab") ==
"aab");
REQUIRE(tc.normal_form(
"bab") ==
"aaa");
}
// The next example demonstrates why we require deferred standardization
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"085",
"Renner monoid type D4 (Gay-Hivert), q = 1",
"[no-valgrind][quick][todd-coxeter][no-coverage]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(11);
for (relation_type
const& rl : RennerTypeDMonoid(4, 1)) {
tc.add_rule(rl);
}
REQUIRE(tc.number_of_rules() == 121);
REQUIRE(!tc.is_obviously_infinite());
REQUIRE(tc.size() == 10
'625);
check_hlt(tc);
check_felsch(tc);
check_random(tc);
check_Rc_style(tc);
check_R_over_C_style(tc);
check_CR_style(tc);
check_Cr_style(tc);
tc.congruence().standardize(tc_order::shortlex);
REQUIRE(std::is_sorted(tc.congruence().cbegin_normal_forms(),
tc.congruence().cend_normal_forms(),
ShortLexCompare<word_type>{}));
tc.congruence().standardize(tc_order::lex);
REQUIRE(std::is_sorted(tc.congruence().cbegin_normal_forms(),
tc.congruence().cend_normal_forms(),
LexicographicalCompare<word_type>{}));
// The next section is very slow
// SECTION("standardizing with recursive order") {
// tc.congruence().standardize(tc_order::recursive);
// REQUIRE(std::is_sorted(tc.congruence().cbegin_normal_forms(),
// tc.congruence().cend_normal_forms(),
// RecursivePathCompare<word_type>{}));
// }
}
// Felsch very slow here
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"086",
"trivial semigroup",
"[no-valgrind][todd-coxeter][quick][no-coverage]") {
auto rg = ReportGuard(REPORT);
for (size_t N = 2; N < 1000; N += 199) {
ToddCoxeter tc;
tc.set_alphabet(
"eab");
tc.set_identity(
"e");
std::string lhs =
"a" + std::string(N,
'b');
std::string rhs =
"e";
tc.add_rule(lhs, rhs);
lhs = std::string(N,
'a');
rhs = std::string(N + 1,
'b');
tc.add_rule(lhs, rhs);
lhs =
"ba";
rhs = std::string(N,
'b') +
"a";
tc.add_rule(lhs, rhs);
tc.run();
if (N % 3 == 1) {
REQUIRE(tc.size() == 3);
}
else {
REQUIRE(tc.size() == 1);
}
}
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"087",
"ACE --- 2p17-2p14 - HLT",
"[todd-coxeter][standard][ace]") {
auto rg = ReportGuard(
false);
ToddCoxeter G;
G.set_alphabet(
"abcABCe");
G.set_identity(
"e");
G.set_inverses(
"ABCabce");
G.add_rule(
"aBCbac",
"e");
G.add_rule(
"bACbaacA",
"e");
G.add_rule(
"accAABab",
"e");
congruence::ToddCoxeter H(right, G.congruence());
H.add_pair({1, 2}, {6});
H.next_lookahead(1
'000'000);
REQUIRE(H.number_of_classes() == 16
'384);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"088",
"ACE --- 2p17-2p3 - HLT",
"[todd-coxeter][standard][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abcABCe");
G.set_identity(
"e");
G.set_inverses(
"ABCabce");
G.add_rule(
"aBCbac",
"e");
G.add_rule(
"bACbaacA",
"e");
G.add_rule(
"accAABab",
"e");
letter_type a = 0;
letter_type b = 1;
letter_type c = 2;
letter_type A = 3;
letter_type B = 4;
letter_type C = 5;
letter_type e = 6;
congruence::ToddCoxeter H(right, G.congruence());
H.add_pair({b, c}, {e});
H.add_pair({b, c}, {A, B, A, A, b, c, a, b, C});
H.strategy(options::strategy::hlt)
.save(
true)
.lookahead(options::lookahead::partial);
REQUIRE(H.number_of_classes() == 8);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"089",
"ACE --- 2p17-1a - HLT",
"[todd-coxeter][standard][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abcABCe");
G.set_identity(
"e");
G.set_inverses(
"ABCabce");
G.add_rule(
"aBCbac",
"e");
G.add_rule(
"bACbaacA",
"e");
G.add_rule(
"accAABab",
"e");
letter_type a = 0;
letter_type b = 1;
letter_type c = 2;
letter_type A = 3;
letter_type B = 4;
letter_type C = 5;
letter_type e = 6;
congruence::ToddCoxeter H(right, G.congruence());
H.add_pair({b, c}, {e});
H.add_pair({A, B, A, A, b, c, a, b, C}, {e});
H.add_pair({A, c, c, c, a, c, B, c, A}, {e});
H.large_collapse(10
'000);
H.strategy(options::strategy::hlt)
.save(
true)
.lookahead(options::lookahead::partial);
REQUIRE(H.number_of_classes() == 1);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"090",
"ACE --- F27",
"[todd-coxeter][standard][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abcdxyzABCDXYZe");
G.set_identity(
"e");
G.set_inverses(
"ABCDXYZabcdxyze");
G.add_rule(
"abC",
"e");
G.add_rule(
"bcD",
"e");
G.add_rule(
"cdX",
"e");
G.add_rule(
"dxY",
"e");
G.add_rule(
"xyZ",
"e");
G.add_rule(
"yzA",
"e");
G.add_rule(
"zaB",
"e");
congruence::ToddCoxeter H(twosided, G);
check_felsch(H);
check_hlt(H);
check_random(H);
REQUIRE(H.number_of_classes() == 29);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"091",
"ACE --- SL219 - HLT",
"[todd-coxeter][standard][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abABe");
G.set_identity(
"e");
G.set_inverses(
"ABabe");
G.add_rule(
"aBABAB",
"e");
G.add_rule(
"BAAbaa",
"e");
G.add_rule(
"abbbbabbbbbbbbbbabbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaa",
"e");
letter_type b = 1;
letter_type e = 4;
congruence::ToddCoxeter H(right, G);
H.add_pair({b}, {e});
H.strategy(options::strategy::hlt)
.save(
false)
.lookahead(options::lookahead::partial);
REQUIRE(H.number_of_classes() == 180);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"092",
"ACE --- perf602p5 - HLT",
"[no-valgrind][todd-coxeter][quick][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abstuvdABSTUVDe");
G.set_identity(
"e");
G.set_inverses(
"ABSTUVDabstuvde");
G.add_rule(
"aaD",
"e");
G.add_rule(
"bbb",
"e");
G.add_rule(
"ababababab",
"e");
G.add_rule(
"ss",
"e");
G.add_rule(
"tt",
"e");
G.add_rule(
"uu",
"e");
G.add_rule(
"vv",
"e");
G.add_rule(
"dd",
"e");
G.add_rule(
"STst",
"e");
G.add_rule(
"UVuv",
"e");
G.add_rule(
"SUsu",
"e");
G.add_rule(
"SVsv",
"e");
G.add_rule(
"TUtu",
"e");
G.add_rule(
"TVtv",
"e");
G.add_rule(
"AsaU",
"e");
G.add_rule(
"AtaV",
"e");
G.add_rule(
"AuaS",
"e");
G.add_rule(
"AvaT",
"e");
G.add_rule(
"BsbDVT",
"e");
G.add_rule(
"BtbVUTS",
"e");
G.add_rule(
"BubVU",
"e");
G.add_rule(
"BvbU",
"e");
G.add_rule(
"DAda",
"e");
G.add_rule(
"DBdb",
"e");
G.add_rule(
"DSds",
"e");
G.add_rule(
"DTdt",
"e");
G.add_rule(
"DUdu",
"e");
G.add_rule(
"DVdv",
"e");
congruence::ToddCoxeter H(right, G);
letter_type a = 0;
letter_type e = 14;
H.add_pair({a}, {e});
check_hlt(H);
check_random(H);
check_felsch(H);
REQUIRE(H.number_of_classes() == 480);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"093",
"ACE --- M12",
"[todd-coxeter][standard][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abcABCe");
G.set_identity(
"e");
G.set_inverses(
"ABCabce");
G.add_rule(
"aaaaaaaaaaa",
"e");
G.add_rule(
"bb",
"e");
G.add_rule(
"cc",
"e");
G.add_rule(
"ababab",
"e");
G.add_rule(
"acacac",
"e");
G.add_rule(
"bcbcbcbcbcbcbcbcbcbc",
"e");
G.add_rule(
"cbcbabcbcAAAAA",
"e");
congruence::ToddCoxeter H(twosided, G);
SECTION(
"HLT + save + partial lookahead") {
H.strategy(options::strategy::hlt)
.save(
true)
.lookahead(options::lookahead::partial);
}
SECTION(
"random") {
H.strategy(options::strategy::random)
.random_interval(std::chrono::milliseconds(100));
}
check_felsch(H);
REQUIRE(H.number_of_classes() == 95
'040);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"094",
"ACE --- C5 - HLT",
"[todd-coxeter][quick][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abABe");
G.set_identity(
"e");
G.set_inverses(
"ABabe");
G.add_rule(
"aaaaa",
"e");
G.add_rule(
"b",
"e");
congruence::ToddCoxeter H(twosided, G);
check_hlt(H);
check_random(H);
check_felsch(H);
REQUIRE(H.number_of_classes() == 5);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"095",
"ACE --- A5-C5",
"[todd-coxeter][quick][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abABe");
G.set_identity(
"e");
G.set_inverses(
"ABabe");
G.add_rule(
"aa",
"e");
G.add_rule(
"bbb",
"e");
G.add_rule(
"ababababab",
"e");
congruence::ToddCoxeter H(right, G);
letter_type
const a = 0, b = 1, e = 4;
H.add_pair({a, b}, {e});
check_hlt(H);
check_random(H);
check_felsch(H);
REQUIRE(H.number_of_classes() == 12);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"096",
"ACE --- A5",
"[todd-coxeter][quick][ace]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter G;
G.set_alphabet(
"abABe");
G.set_identity(
"e");
G.set_inverses(
"ABabe");
G.add_rule(
"aa",
"e");
G.add_rule(
"bbb",
"e");
G.add_rule(
"ababababab",
"e");
congruence::ToddCoxeter H(twosided, G);
check_hlt(H);
check_random(H);
check_felsch(H);
H.random_shuffle_generating_pairs();
REQUIRE(H.number_of_classes() == 60);
REQUIRE_THROWS_AS(H.random_shuffle_generating_pairs(),
LibsemigroupsException);
}
// Felsch is much much better here
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"097",
"relation ordering",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard();
ToddCoxeter tc;
tc.set_alphabet(13);
for (relation_type
const& rl : RennerTypeDMonoid(5, 1)) {
tc.add_rule(rl);
}
REQUIRE(tc.number_of_rules() == 173);
REQUIRE(!tc.is_obviously_infinite());
tc.congruence()
.sort_generating_pairs(&shortlex_compare)
.sort_generating_pairs(recursive_path_compare)
.remove_duplicate_generating_pairs();
REQUIRE(tc.number_of_rules() == 173);
tc.congruence().strategy(options::strategy::felsch).f_defs(100
'000).run();
REQUIRE(tc.size() == 258
'661);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"098",
"relation ordering",
"[todd-coxeter][quick]") {
ToddCoxeter tc;
tc.set_alphabet(10);
tc.add_rule({0, 1}, {0});
tc.add_rule({0, 2}, {0});
tc.add_rule({0, 3}, {0});
tc.add_rule({0, 4}, {0});
tc.add_rule({0, 5}, {0});
tc.add_rule({0, 6}, {0});
tc.add_rule({0, 7}, {0});
tc.add_rule({0, 8}, {0});
tc.add_rule({0, 9}, {0});
tc.add_rule({1, 0}, {1});
tc.add_rule({1, 1}, {1});
tc.add_rule({1, 2}, {1});
tc.add_rule({1, 3}, {1});
tc.add_rule({1, 4}, {1});
tc.add_rule({1, 5}, {1});
tc.add_rule({1, 6}, {1});
tc.add_rule({1, 7}, {1});
tc.add_rule({1, 8}, {1});
tc.add_rule({1, 9}, {1});
tc.add_rule({2, 0}, {2});
tc.add_rule({2, 1}, {2});
tc.add_rule({2, 2}, {2});
tc.add_rule({2, 3}, {2});
tc.add_rule({2, 4}, {2});
tc.add_rule({2, 5}, {2});
tc.add_rule({2, 6}, {2});
tc.add_rule({2, 7}, {2});
tc.add_rule({2, 8}, {2});
tc.add_rule({2, 9}, {2});
tc.add_rule({3, 0}, {3});
tc.add_rule({3, 1}, {3});
tc.add_rule({3, 2}, {3});
tc.add_rule({3, 3}, {3});
tc.add_rule({3, 4}, {3});
tc.add_rule({3, 5}, {3});
tc.add_rule({3, 6}, {3});
tc.add_rule({3, 7}, {3});
tc.add_rule({3, 8}, {3});
tc.add_rule({3, 9}, {3});
tc.add_rule({4, 0}, {4});
tc.add_rule({4, 1}, {4});
tc.add_rule({4, 2}, {4});
tc.add_rule({4, 3}, {4});
tc.add_rule({4, 4}, {4});
tc.add_rule({4, 5}, {4});
tc.add_rule({4, 6}, {4});
tc.add_rule({4, 7}, {4});
tc.add_rule({4, 8}, {4});
tc.add_rule({4, 9}, {4});
tc.add_rule({5, 0}, {5});
tc.add_rule({5, 1}, {5});
tc.add_rule({5, 2}, {5});
tc.add_rule({5, 3}, {5});
tc.add_rule({5, 4}, {5});
tc.add_rule({5, 5}, {5});
tc.add_rule({5, 6}, {5});
tc.add_rule({5, 7}, {5});
tc.add_rule({5, 8}, {5});
tc.add_rule({5, 9}, {5});
tc.add_rule({6, 0}, {6});
tc.add_rule({6, 1}, {6});
tc.add_rule({6, 2}, {6});
tc.add_rule({6, 3}, {6});
tc.add_rule({6, 4}, {6});
tc.add_rule({6, 5}, {6});
tc.add_rule({6, 6}, {6});
tc.add_rule({6, 7}, {6});
tc.add_rule({6, 8}, {6});
tc.add_rule({6, 9}, {6});
tc.add_rule({7, 0}, {7});
tc.add_rule({7, 1}, {7});
tc.add_rule({7}, {7, 2});
tc.add_rule({7, 3}, {7});
tc.add_rule({7, 4}, {7});
tc.add_rule({7, 5}, {7});
tc.add_rule({7, 6}, {7});
tc.add_rule({7, 7}, {7});
tc.add_rule({7, 8}, {7});
tc.add_rule({7, 9}, {7});
tc.add_rule({8, 0}, {8});
tc.add_rule({8, 1}, {8});
tc.add_rule({8, 2}, {8});
tc.add_rule({8, 3}, {8});
tc.add_rule({8, 4}, {8});
tc.add_rule({8, 5}, {8});
tc.add_rule({8, 6}, {8});
tc.add_rule({8, 7}, {8});
tc.add_rule({8, 8}, {8});
tc.add_rule({8, 9}, {8});
tc.add_rule({9, 0}, {9});
tc.add_rule({9, 0, 1, 2, 3, 4, 5, 5, 1, 5, 6, 9, 8, 8, 8, 8, 8, 0}, {9});
tc.congruence().sort_generating_pairs(recursive_path_compare);
check_felsch(tc);
check_hlt(tc);
check_random(tc);
REQUIRE(tc.size() == 10);
REQUIRE_THROWS_AS(tc.congruence().sort_generating_pairs(shortlex_compare),
LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"099",
"short circuit size in obviously infinite",
"[todd-coxeter][quick]") {
auto rg = ReportGuard(REPORT);
ToddCoxeter tc;
tc.set_alphabet(
"abc");
tc.add_rule(
"aaaa",
"a");
REQUIRE(tc.size() == POSITIVE_INFINITY);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"100",
"http://brauer.maths.qmul.ac.uk/Atlas/misc/24A8/mag/24A8G1-P1.M",
"[todd-coxeter][standard]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"X");
tc.add_rule(
"yyyyyy",
"Y");
tc.add_rule(
"YXyx",
"XYxy");
tc.add_rule(
"xYYYxYYYxYY",
"yyXyyyXyyyX");
tc.add_rule(
"xyxyyXyxYYxyyyx",
"yyyXyyy");
tc.congruence()
.next_lookahead(2
'000'000)
.strategy(options::strategy::hlt)
.sort_generating_pairs()
.lookahead(options::lookahead::partial)
.standardize(
true);
tc.congruence().run();
REQUIRE(tc.size() == 322
'560);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"101",
"http://brauer.maths.qmul.ac.uk/Atlas/spor/M11/mag/M11G1-P1.M",
"[todd-coxeter][quick][no-coverage][no-valgrind]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxyxyxyxyxy",
"e");
tc.add_rule(
"xyyxyyxyyxyyxyyxyy",
"e");
tc.add_rule(
"xyxyxYxyxyyxYxyxYxY",
"e");
REQUIRE(tc.size() == 7
'920);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"102",
"http://brauer.maths.qmul.ac.uk/Atlas/spor/M12/mag/M12G1-P1.M",
"[todd-coxeter][standard]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxyxyxyxyxy",
"e");
tc.add_rule(
"XYxyXYxyXYxyXYxyXYxyXYxy",
"e");
tc.add_rule(
"xyxyxYxyxyxYxyxyxYxyxyxYxyxyxYxyxyxY",
"e");
tc.add_rule(
"XYXYxyxyXYXYxyxyXYXYxyxyXYXYxyxyXYXYxyxy",
"e");
REQUIRE(tc.size() == 95
'040);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"103",
"http://brauer.maths.qmul.ac.uk/Atlas/spor/M22/mag/M22G1-P1.M",
"[todd-coxeter][extreme]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxyxyxyxyxy",
"e");
tc.add_rule(
"xyyxyyxyyxyyxyy",
"e");
tc.add_rule(
"XYxyXYxyXYxyXYxyXYxyXYxy",
"e");
tc.add_rule(
"XYXYxyxyXYXYxyxyXYXYxyxy",
"e");
tc.add_rule(
"xyxyxYxyxyxYxyxyxYxyxyxYxyxyxY",
"e");
REQUIRE(tc.size() == 443
'520);
}
// Takes about 4 minutes (2021 - MacBook Air M1 - 8GB RAM)
// with Felsch (3.5mins or 2.5mins with lowerbound) or HLT (4.5mins)
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"104",
"http://brauer.maths.qmul.ac.uk/Atlas/spor/M23/mag/M23G1-P1.M",
"[todd-coxeter][extreme]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxy",
"e");
tc.add_rule(
"xyyxyyxyyxyyxyyxyy",
"e");
tc.add_rule(
"XYxyXYxyXYxyXYxyXYxyXYxy",
"e");
tc.add_rule(
"xyxYxyyxyxYxyyxyxYxyyxyxYxyy",
"e");
tc.add_rule(
"xyxyxyxYxyyxyxYxyxYxyxyxyxYxYxY",
"e");
tc.add_rule(
"xyxyyxyyxyxyyxyyxyxyyxyyxyxyyxyyxyxyyxyyxyxyyxyy",
"e");
tc.add_rule(
"xyxyyxyxyyxyxyyxyyxYxyyxYxyxyyxyxYxyy",
"e");
tc.congruence()
.sort_generating_pairs()
.strategy(options::strategy::felsch)
.use_relations_in_extra(
true)
.lower_bound(10
'200'960)
.deduction_policy(options::deductions::v2
| options::deductions::no_stack_if_no_space)
.reserve(50
'000'000);
std::cout << tc.congruence().settings_string();
tc.congruence().run();
REQUIRE(tc.size() == 10
'200'960);
}
// Takes about 3 minutes
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"105",
"http://brauer.maths.qmul.ac.uk/Atlas/clas/S62/mag/S62G1-P1.M",
"[todd-coxeter][extreme]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxy",
"e");
tc.add_rule(
"XYxyXYxyXYxyXYxyXYxyXYxyXYxyXYxyXYxyXYxyXYxyXYxy",
"e");
tc.add_rule(
"XYXYxyxyXYXYxyxyXYXYxyxyXYXYxyxyXYXYxyxy",
"e");
tc.add_rule(
"xyxyxYxyxyxYxyxyxYxyxYxYxyxYxYxyxYxY",
"e");
tc.add_rule(
"xyxyxYxyxYxyxYxyxyxYxyxYxyxYxyxyxYxyxYxyxYxyxyxYxyxYxyxY",
"e");
congruence::ToddCoxeter tc2(congruence_kind::right, tc);
tc2.add_pair(tc.string_to_word(
"xy"), tc.string_to_word(
"e"));
REQUIRE(tc2.number_of_classes() == 10
'644'480);
}
// Approx. 32 minutes (2021 - MacBook Air M1 - 8GB RAM)
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"106",
"http://brauer.maths.qmul.ac.uk/Atlas/spor/HS/mag/HSG1-P1.M",
"[todd-coxeter][extreme]") {
auto rg = ReportGuard();
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyyyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxyxyxyxyxy",
"e");
tc.add_rule(
"xyyxyyxyyxyyxyyxyyxyyxyyxyyxyy",
"e");
tc.add_rule(
"XYxyXYxyXYxyXYxyXYxy",
"e");
tc.add_rule(
"XYXYxyxyXYXYxyxyXYXYxyxy",
"e");
tc.add_rule(
"XYYxyyXYYxyyXYYxyyXYYxyyXYYxyyXYYxyy",
"e");
tc.add_rule(
"xyxyxyyxYxYYxYxyyxyxyxYYxYYxYYxYY",
"e");
tc.add_rule(
"xyxyyxYYxYYxyyxYYxYYxyyxyxyyxYxyyxYxyy",
"e");
tc.add_rule(
"xyxyxyyxyyxyxYxYxyxyyxyyxyxyxYYxYxYY",
"e");
tc.add_rule(
"xyxyxyyxYxYYxyxyxYxyxyxyyxYxYYxyxyxY",
"e");
tc.add_rule(
"xyxyxyyxyxyxyyxyxyxYxyxyxyyxyyxyyxyxyxY",
"e");
tc.add_rule(
"xyxyxyyxyxyyxyxyyxyxyxyyxYxyxYYxyxYxyy",
"e");
congruence::ToddCoxeter tc2(congruence_kind::right, tc);
tc2.add_pair(tc.string_to_word(
"xy"), tc.string_to_word(
"e"));
tc2.sort_generating_pairs()
.use_relations_in_extra(
true)
.strategy(options::strategy::hlt)
.lookahead(options::lookahead::felsch | options::lookahead::partial);
REQUIRE(tc2.number_of_classes() == 4
'032'000);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"107",
"http://brauer.maths.qmul.ac.uk/Atlas/spor/J1/mag/J1G1-P1.M",
"[todd-coxeter][standard]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxy",
"e");
tc.add_rule(
"xyxyxYxyxYxyxYxyxyxYxyxYxyxYxyxyxYxyxYxyxYxyxyxYxyxYxyxYxyxy"
"xYxyxYxyxY",
"e");
tc.add_rule(
"xyxyxYxyxYxyxYxyxYxyxYxyxYxyxyxYxYxyxyxYxyxYxyxYxyxYxyxYxyxY"
"xyxyxYxY",
"e");
REQUIRE(tc.size() == 175
'560);
}
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"108",
"http://brauer.maths.qmul.ac.uk/Atlas/lin/L34/mag/L34G1-P1.M",
"[todd-coxeter][quick][no-coverage][no-valgrind]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxy",
"e");
tc.add_rule(
"xyyxyyxyyxyyxyy",
"e");
tc.add_rule(
"XYxyXYxyXYxyXYxyXYxy",
"e");
tc.add_rule(
"xyxyxYxyxyxYxyxyxYxyxyxYxyxyxY",
"e");
tc.add_rule(
"xyxyxyyxYxyxyxyyxYxyxyxyyxYxyxyxyyxYxyxyxyyxY",
"e");
REQUIRE(tc.size() == 20
'160);
}
// Takes about 10 seconds (2021 - MacBook Air M1 - 8GB RAM)
LIBSEMIGROUPS_TEST_CASE(
"ToddCoxeter",
"109",
"http://brauer.maths.qmul.ac.uk/Atlas/clas/S62/mag/S62G1-P1.M",
"[todd-coxeter][extreme]") {
ToddCoxeter tc;
tc.set_alphabet(
"xyXYe");
tc.set_identity(
"e");
tc.set_inverses(
"XYxye");
tc.add_rule(
"xx",
"e");
tc.add_rule(
"yyyyyyy",
"e");
tc.add_rule(
"xyxyxyxyxyxyxyxyxy",
"e");
tc.add_rule(
"xyyxyyxyyxyyxyyxyyxyyxyyxyyxyyxyyxyy",
"e");
tc.add_rule(
"XYXYXYxyxyxyXYXYXYxyxyxy",
"e");
tc.add_rule(
"XYxyXYxyXYxy",
"e");
tc.add_rule(
"XYYxyyXYYxyy",
"e");
REQUIRE(tc.size() == 1
'451'520);
std::cout << tc.congruence().stats_string();
}
}
// namespace fpsemigroup
}
// namespace libsemigroups