//
// 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