// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2021 James D. Mitchell + Maria Tsalakou
//
// 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/>.
//
#include <stddef.h>
// for size_t
#include <algorithm>
// for count_if, all_of
#include <iostream>
// for string, char_traits
#include <iterator>
// for distance
#include <memory>
// for allocator, shared_ptr
#include <string>
// for basic_string, operator==, operator!=, operator+
#include <unordered_set>
// for unordered_set
#include <vector>
// for vector
#include "catch.hpp" // for REQUIRE, REQUIRE_THROWS_AS
#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE
#include "libsemigroups/constants.hpp" // for UNDEFINED
#include "libsemigroups/froidure-pin-base.hpp" // for FroidurePinBase
#include "libsemigroups/iterator.hpp" // for ConstIteratorStateful
#include "libsemigroups/kambites.hpp" // for Kambites
#include "libsemigroups/knuth-bendix.hpp" // for KnuthBendix
#include "libsemigroups/report.hpp" // for ReportGuard
#include "libsemigroups/siso.hpp" // for const_sislo_iterator
#include "libsemigroups/string.hpp" // for random_string etc
#include "libsemigroups/transf.hpp" // for LeastTransf
#include "libsemigroups/types.hpp" // for tril etc
#include "libsemigroups/word.hpp" // for number_of_words
namespace libsemigroups {
struct LibsemigroupsException;
// Forward decl
constexpr
bool REPORT =
false;
using detail::MultiStringView;
using detail::random_string;
namespace {
// TODO(later) remove this, or put it in test-suffix-tree.cpp
template <
typename T>
size_t number_of_subwords(T first, T last) {
std::unordered_set<std::string> mp;
for (
auto it = first; it < last; ++it) {
{
auto& w = it->first;
for (
auto suffix = w.cbegin(); suffix < w.cend(); ++suffix) {
for (
auto prefix = suffix + 1; prefix < w.cend(); ++prefix) {
mp.emplace(suffix, prefix);
}
}
}
{
auto& w = it->second;
for (
auto suffix = w.cbegin(); suffix < w.cend(); ++suffix) {
for (
auto prefix = suffix + 1; prefix < w.cend(); ++prefix) {
mp.emplace(suffix, prefix);
}
}
}
}
return mp.size();
}
// TODO(later) remove this
template <
typename T>
size_t sum_lengths(T first, T last) {
size_t result = 0;
for (
auto it = first; it < last; ++it) {
result += it->first.size();
result += it->second.size();
}
return result;
}
std::string random_power_string(std::string
const& s,
std::string
const& t,
std::string
const& u,
size_t exp) {
static std::random_device rd;
static std::mt19937 generator(rd());
static std::uniform_int_distribution<> distribution(0, 2);
std::string result =
"";
while (exp > 0) {
switch (distribution(generator)) {
case 0: {
result += s;
break;
}
case 1: {
result += t;
break;
}
case 2: {
result += u;
}
default:
break;
}
exp--;
}
return result;
}
// std::string swap_a_and_b(std::string const& w) {
// std::string result;
// for (auto l : w) {
// if (l == 'a') {
// result += "b";
// } else if (l == '#') {
// result += '#';
// } else {
// result += "a";
// }
// }
// return result;
// }
auto sample(std::string A,
size_t R,
size_t min,
size_t max,
size_t sample_size) {
if (min < 7) {
LIBSEMIGROUPS_EXCEPTION(
"the minimum value of is at least 7");
// Otherwise we get lhs = rhs in many of the things below and this
// skews the results.
}
else if (min < max && max - min <= 1) {
LIBSEMIGROUPS_EXCEPTION(
"the minimum and maximum values must be at least 2 apart");
}
auto rg = ReportGuard(
false);
uint64_t total_c4 = 0;
uint64_t total_confluent = 0;
for (size_t j = 0; j < sample_size; ++j) {
fpsemigroup::Kambites<std::string> k;
k.set_alphabet(A);
fpsemigroup::KnuthBendix kb1;
kb1.set_alphabet(A);
fpsemigroup::KnuthBendix kb2;
std::reverse(A.begin(), A.end());
kb2.set_alphabet(A);
std::reverse(A.begin(), A.end());
for (size_t r = 0; r < R; ++r) {
auto lhs = random_string(A, min, max);
std::string rhs;
if (lhs.size() == min) {
rhs = random_string(A, min + 1, max);
}
else {
rhs = random_string(A, min, lhs.size());
}
k.add_rule(lhs, rhs);
kb1.add_rule(lhs, rhs);
kb2.add_rule(lhs, rhs);
}
kb1.run_for(std::chrono::milliseconds(1));
kb2.run_for(std::chrono::milliseconds(1));
if (k.small_overlap_class() >= 4) {
total_c4++;
}
if (kb1.confluent() || kb2.confluent()) {
total_confluent++;
}
}
return std::make_tuple(total_c4, total_confluent);
}
std::array<std::string, 5> swap_a_b_c(std::string
const& w) {
static std::array<std::string, 5> perms
= {
"bac",
"acb",
"cba",
"bca",
"cab"};
std::array<std::string, 5> result;
size_t count = 0;
for (
auto const& p : perms) {
std::string ww;
for (
auto l : w) {
if (l ==
'a') {
ww += p[0];
}
else if (l ==
'b') {
ww += p[1];
}
else {
ww += p[2];
}
}
result[count] = ww;
count++;
}
return result;
}
}
// namespace
#ifdef false
namespace {
Kambites<> random_example(std::string
const& alphabet) {
static std::random_device rd;
static std::mt19937 generator(rd());
std::uniform_int_distribution<> distribution(1, 25);
Kambites<> k;
k.set_alphabet(alphabet);
std::vector<std::string> pieces;
for (size_t i = 0; i < 13; ++i) {
pieces.push_back(random_string(alphabet, distribution(generator)));
}
k.add_rule(pieces[0] + pieces[1] + pieces[2] + pieces[3],
pieces[2] + pieces[0] + pieces[7] + pieces[4]);
k.add_rule(pieces[4] + pieces[0] + pieces[5], pieces[6]);
k.add_rule(pieces[7] + pieces[1] + pieces[8], pieces[9]);
k.add_rule(pieces[10] + pieces[3] + pieces[11], pieces[12]);
std::cout <<
"k.add_rule(\""
<< pieces[0] + pieces[1] + pieces[2] + pieces[3] <<
"\", \
""
<< pieces[2] + pieces[0] + pieces[7] + pieces[4] <<
"\");\n
";
std::cout <<
"k.add_rule(\"" << pieces[4] + pieces[0] + pieces[5]
<<
"\", \
"" << pieces[6] <<
"\");\n
";
std::cout <<
"k.add_rule(\"" << pieces[7] + pieces[1] + pieces[8]
<<
"\", \
"" << pieces[9] <<
"\");\n
";
std::cout <<
"k.add_rule(\"" << pieces[10] + pieces[3] + pieces[11]
<<
"\", \
"" << pieces[12] <<
"\");\n
";
return k;
}
}
// namespace
#endif
namespace fpsemigroup {
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_4() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcdefg");
k.add_rule(
"abcd",
"aaaeaa");
k.add_rule(
"ef",
"dg");
REQUIRE(k.equal_to(
"abcd",
"aaaeaa"));
REQUIRE(k.equal_to(
"ef",
"dg"));
REQUIRE(k.equal_to(
"aaaaaef",
"aaaaadg"));
REQUIRE(k.equal_to(
"efababa",
"dgababa"));
k.froidure_pin()->enumerate(100);
REQUIRE(k.froidure_pin()->current_size() == 8205);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"000",
"(fpsemi) MT test 4 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_4<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"001",
"(fpsemi) MT test 4 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_4<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_no_name_1() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"aAbBcCe");
k.add_rule(
"aaa",
"e");
k.add_rule(
"bbb",
"e");
k.add_rule(
"ccc",
"e");
k.add_rule(
"ABa",
"BaB");
k.add_rule(
"bcB",
"cBc");
k.add_rule(
"caC",
"aCa");
k.add_rule(
"abcABCabcABCabcABC",
"e");
k.add_rule(
"BcabCABcabCABcabCA",
"e");
k.add_rule(
"cbACBacbACBacbACBa",
"e");
REQUIRE(k.number_of_pieces(0) == 2);
REQUIRE(k.number_of_pieces(1) == POSITIVE_INFINITY);
REQUIRE(k.number_of_pieces(2) == 2);
REQUIRE(k.number_of_pieces(3) == POSITIVE_INFINITY);
REQUIRE(k.number_of_pieces(4) == 2);
REQUIRE(k.number_of_pieces(5) == POSITIVE_INFINITY);
REQUIRE(k.number_of_pieces(6) == 2);
REQUIRE(k.number_of_pieces(7) == 2);
REQUIRE(k.number_of_pieces(8) == 2);
REQUIRE(k.number_of_pieces(9) == 2);
REQUIRE(k.number_of_pieces(10) == 2);
REQUIRE(k.number_of_pieces(11) == 2);
REQUIRE(k.number_of_pieces(12) == 2);
REQUIRE(k.number_of_pieces(13) == POSITIVE_INFINITY);
REQUIRE(k.number_of_pieces(14) == 2);
REQUIRE(k.number_of_pieces(15) == POSITIVE_INFINITY);
REQUIRE(k.number_of_pieces(16) == 2);
REQUIRE(k.number_of_pieces(17) == POSITIVE_INFINITY);
REQUIRE(k.small_overlap_class() == 2);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"002",
"(fpsemi) number_of_pieces (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_no_name_1<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"003",
"(fpsemi) number_of_pieces (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_no_name_1<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_no_name_2() {
auto rg = ReportGuard(REPORT);
for (size_t i = 4; i < 20; ++i) {
std::string lhs;
for (size_t b = 1; b <= i; ++b) {
lhs +=
"a" + std::string(b,
'b');
}
std::string rhs;
for (size_t b = i + 1; b <= 2 * i; ++b) {
rhs +=
"a" + std::string(b,
'b');
}
Kambites<T> k;
k.set_alphabet(
"ab");
k.add_rule(lhs, rhs);
REQUIRE(k.number_of_pieces(0) == i);
REQUIRE(k.number_of_pieces(1) == i + 1);
REQUIRE(k.small_overlap_class() == i);
}
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"004",
"(fpsemi) small_overlap_class (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_no_name_2<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"005",
"(fpsemi) small_overlap_class (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_no_name_2<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_random() {
auto rg = ReportGuard(REPORT);
{
Kambites<T> k;
k.set_alphabet(
"abcdefghi");
k.add_rule(
"eiehiegiggfaigcdfdfdgiidcebacgfaf",
"cgfaeiehiegiggfaigcdfdfdgigcccbddchbbhgaaedfiiahhehihcba");
k.add_rule(
"hihcbaeiehiegiggfaigcdfdfdgiefhbidhbdgb",
"chhfgafiiddg");
k.add_rule(
"gcccbddchbbhgaaedfiiahheidcebacbdefegcehgffedacddiaiih",
"eddfcfhbedecacheahcdeeeda");
k.add_rule(
"dfbiccfeagaiffcfifg",
"dceibahghaedhefh");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(number_of_subwords(k.cbegin_rules(), k.cend_rules()) == 3762);
size_t n = sum_lengths(k.cbegin_rules(), k.cend_rules());
REQUIRE(n == 254);
REQUIRE(n * n == 64516);
}
{
Kambites<T> k;
k.set_alphabet(
"abcdefghi");
k.add_rule(
"feffgccdgcfbeagiifheabecdfbgebfcibeifibccahaafabeihfgfieade"
"bciheddeigbaf",
"ifibccahaafabeihfgfiefeffgccdgcfbeagiifheabecfeibghddfgbaia"
"acghhdhggagaide");
k.add_rule(
"ghhdhggagaidefeffgccdgcfbeagiifheabeccbeiddgdcbcf",
"ahccccffdeb");
k.add_rule(
"feibghddfgbaiaacdfbgebfcibeieaacdbdb",
"gahdfgbghhhbcci");
k.add_rule(
"dgibafaahiabfgeiiibadebciheddeigbaficfbfdbfbbiddgdcifbe",
"iahcfgdbggaciih");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(number_of_subwords(k.cbegin_rules(), k.cend_rules()) == 7197);
size_t n = sum_lengths(k.cbegin_rules(), k.cend_rules());
REQUIRE(n == 327);
REQUIRE(n * n == 106929);
}
{
Kambites<T> k;
k.set_alphabet(
"abcdefghi");
k.add_rule(
"adichhbhibfchbfbbibaidfibifgagcgdedfeeibhggdbchfdaefbefcbaa"
"hcbhbidgaahbahhahhb",
"edfeeibhggdbchfdaefbeadichhbhibfchbfbbibaiihebabeabahcgdbic"
"bgiciffhfggbfadf");
k.add_rule(
"bgiciffhfggbfadfadichhbhibfchbfbbibaaggfdcfcebehhbdegiaeaf",
"hebceeicbhidcgahhcfbb");
k.add_rule(
"iihebabeabahcgdbicidfibifgagcgdedehed",
"ecbcgaieieicdcdfdbgagdbf");
k.add_rule(
"iagaadbfcbaahcbhbidgaahbahhahhbd",
"ddddh");
REQUIRE(k.small_overlap_class() == 3);
REQUIRE(number_of_subwords(k.cbegin_rules(), k.cend_rules()) == 7408);
size_t n = sum_lengths(k.cbegin_rules(), k.cend_rules());
REQUIRE(n == 330);
REQUIRE(n * n == 108900);
}
{
Kambites<T> k;
k.set_alphabet(
"abcdefghi");
k.add_rule(
"ibddgdgddiabcahbidbedffeddciiabahbbiacbfehdfccacbhgafbgcdg",
"iabahibddgdgddbdfacbafhcgfhdheieihd");
k.add_rule(
"hdheieihdibddgdgddebhaeaicciidebegg",
"giaeehdeeec");
k.add_rule(
"bdfacbafhcgfiabcahbidbedffeddcifdfcdcdadhhcbcbebhei",
"icaebehdff");
k.add_rule(
"aggiiacdbbiacbfehdfccacbhgafbgcdghiahfccdchaiagaha",
"hhafbagbhghhihg");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(number_of_subwords(k.cbegin_rules(), k.cend_rules()) == 4560);
size_t n = sum_lengths(k.cbegin_rules(), k.cend_rules());
REQUIRE(n == 265);
REQUIRE(n * n == 70225);
}
{
Kambites<T> k;
k.set_alphabet(
"abcdefghi");
k.add_rule(
"fibehffegdeggaddgfdaeaiacbhbgbbccceaibfcabbiedhecggbbdgihddd",
"ceafibehffegdeggafidbaefcebegahcbhciheceaehaaehih");
k.add_rule(
"haaehihfibehffegdeggaecbedccaeabifeafi",
"bfcccibgefiidgaih");
k.add_rule(
"fidbaefcebegahcbhciheceaeddgfdaeaiacbhbgbbcccgiahbibehgbgab"
"efdieiggc",
"abigdadaecdfdeeciggbdfdf");
k.add_rule(
"eeaaiicigieiabibfcabbiedhecggbbdgihdddifadgbgidbfeg",
"daheebdgdiaeceeiicddg");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(number_of_subwords(k.cbegin_rules(), k.cend_rules()) == 6398);
size_t n = sum_lengths(k.cbegin_rules(), k.cend_rules());
REQUIRE(n == 328);
REQUIRE(n * n == 107584);
}
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"006",
"(fpsemi) random (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_random<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"007",
"(fpsemi) random (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_random<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_knuth_bendix_055() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcdefg");
k.add_rule(
"abcd",
"ce");
k.add_rule(
"df",
"dg");
REQUIRE(k.small_overlap_class() == POSITIVE_INFINITY);
REQUIRE(k.is_obviously_infinite());
REQUIRE(k.equal_to(
"dfabcdf",
"dfabcdg"));
REQUIRE(k.normal_form(
"dfabcdg") ==
"dfabcdf");
REQUIRE(k.equal_to(
"abcdf",
"ceg"));
REQUIRE(k.equal_to(
"abcdf",
"cef"));
REQUIRE(k.equal_to(
"dfabcdf",
"dfabcdg"));
REQUIRE(k.equal_to(
"abcdf",
"ceg"));
REQUIRE(k.equal_to(
"abcdf",
"cef"));
REQUIRE(k.normal_form(
"abcdfceg") ==
"abcdfabcdf");
REQUIRE(k.equal_to(
"abcdfceg",
"abcdfabcdf"));
REQUIRE(k.size() == POSITIVE_INFINITY);
REQUIRE(number_of_words(k.alphabet().size(), 0, 6) == 19608);
REQUIRE(k.number_of_normal_forms(0, 6) == 17921);
REQUIRE(number_of_subwords(k.cbegin_rules(), k.cend_rules()) == 7);
// REQUIRE(std::vector<std::string>(k.cbegin_normal_forms(0, 2),
// k.cend_normal_forms())
// == std::vector<std::string>({"a", "b", "c", "d", "e", "f",
// "g"}));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"008",
"(fpsemi) KnuthBendix 055 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_knuth_bendix_055<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"009",
"(fpsemi) KnuthBendix 055 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_knuth_bendix_055<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_gap_smalloverlap_85() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"cab");
k.add_rule(
"aabc",
"acba");
REQUIRE(!k.equal_to(
"a",
"b"));
REQUIRE(k.equal_to(
"aabcabc",
"aabccba"));
REQUIRE(k.size() == POSITIVE_INFINITY);
REQUIRE(number_of_words(3, 4, 16) == 21523320);
REQUIRE(std::distance(cbegin_sislo(
"cab",
"aabc",
"aaabc"),
cend_sislo(
"cab",
"aabc",
"aaabc"))
== 162);
REQUIRE(std::count_if(
cbegin_sislo(
"cab",
"cccc",
"ccccc"),
cend_sislo(
"cab",
"cccc",
"ccccc"),
[&k](std::string
const& w) {
return k.equal_to(w,
"acba"); })
== 2);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"010",
"(fpsemi) smalloverlap/gap/test.gi:85 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_gap_smalloverlap_85<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"011",
"(fpsemi) smalloverlap/gap/test.gi:85 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_gap_smalloverlap_85<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"012",
"(fpsemi) free semigroup",
"[quick][kambites][fpsemigroup][fpsemi]") {
Kambites<std::string> k;
k.set_alphabet(
"cab");
REQUIRE(k.small_overlap_class() == POSITIVE_INFINITY);
Kambites<detail::MultiStringView> kk;
kk.set_alphabet(
"cab");
REQUIRE(kk.small_overlap_class() == POSITIVE_INFINITY);
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_gap_smalloverlap_49() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcdefgh");
k.add_rule(
"abcd",
"ce");
k.add_rule(
"df",
"hd");
REQUIRE(k.is_obviously_infinite());
REQUIRE(k.equal_to(
"abchd",
"abcdf"));
REQUIRE(!k.equal_to(
"abchf",
"abcdf"));
REQUIRE(k.equal_to(
"abchd",
"abchd"));
REQUIRE(k.equal_to(
"abchdf",
"abchhd"));
// Test cases (4) and (5)
REQUIRE(k.equal_to(
"abchd",
"cef"));
REQUIRE(k.equal_to(
"cef",
"abchd"));
REQUIRE(k.size() == POSITIVE_INFINITY);
REQUIRE(k.number_of_normal_forms(0, 6) == 35199);
REQUIRE(k.normal_form(
"hdfabce") ==
"dffababcd");
REQUIRE(k.equal_to(
"hdfabce",
"dffababcd"));
// TODO(later) include when we have cbegin_normal_forms,
// cend_normal_forms
// REQUIRE(std::vector<std::string>(k.cbegin_normal_forms(0, 2),
// k.cend_normal_forms())
// == std::vector<std::string>(
// {"a", "b", "c", "d", "e", "f", "g", "h"}));*/
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"013",
"(fpsemi) smalloverlap/gap/test.gi:49 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_gap_smalloverlap_49<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"014",
"(fpsemi) smalloverlap/gap/test.gi:49 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_gap_smalloverlap_49<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_gap_smalloverlap_63() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcdefgh");
k.add_rule(
"afh",
"bgh");
k.add_rule(
"hc",
"d");
REQUIRE(k.is_obviously_infinite());
// Test case (6)
REQUIRE(k.equal_to(
"afd",
"bgd"));
REQUIRE(k.equal_to(
"bghcafhbgd",
"afdafhafd"));
REQUIRE(k.normal_form(
"bghcafhbgd") ==
"afdafhafd");
REQUIRE(k.number_of_normal_forms(0, 6) == 34819);
REQUIRE(k.size() == POSITIVE_INFINITY);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"015",
"(fpsemi) smalloverlap/gap/test.gi:63 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_gap_smalloverlap_63<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"016",
"(fpsemi) smalloverlap/gap/test.gi:63 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_gap_smalloverlap_63<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_gap_smalloverlap_70() {
auto rg = ReportGuard(REPORT);
// The following permits a more complex test of case (6), which also
// involves using the case (2) code to change the prefix being looked
// for:
Kambites<T> k;
k.set_alphabet(
"abcdefghij");
k.add_rule(
"afh",
"bgh");
k.add_rule(
"hc",
"de");
k.add_rule(
"ei",
"j");
REQUIRE(k.number_of_normal_forms(0, 6) == 102255);
REQUIRE(k.is_obviously_infinite());
REQUIRE(k.equal_to(
"afdj",
"bgdj"));
REQUIRE(!k.equal_to(
"xxxxxxxxxxxxxxxxxxxxxxx",
"b"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"017",
"(fpsemi) smalloverlap/gap/test.gi:70 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_gap_smalloverlap_70<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"018",
"(fpsemi) smalloverlap/gap/test.gi:70 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_gap_smalloverlap_70<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_1_million_equals() {
auto rg = ReportGuard(REPORT);
// A slightly more complicated presentation for testing case (6), in
// which the max piece suffixes of the first two relation words no
// longer agree (since fh and gh are now pieces).
Kambites<T> k;
k.set_alphabet(
"abcdefghijkl");
k.add_rule(
"afh",
"bgh");
k.add_rule(
"hc",
"de");
k.add_rule(
"ei",
"j");
k.add_rule(
"fhk",
"ghl");
REQUIRE(k.is_obviously_infinite());
REQUIRE(k.equal_to(
"afdj",
"bgdj"));
REQUIRE(k.equal_to(
"afdj",
"afdj"));
REQUIRE(k.normal_form(
"bfhk") ==
"afhl");
REQUIRE(k.equal_to(
"bfhk",
"afhl"));
REQUIRE(k.size() == POSITIVE_INFINITY);
size_t N = std::distance(cbegin_sislo(
"abcdefghijkl",
"a",
"bgdk"),
cend_sislo(
"abcdefghijkl",
"a",
"bgdk"));
REQUIRE(N == 4522);
size_t M = 0;
for (
auto it1 = cbegin_sislo(
"abcdefghijkl",
"a",
"bgdk");
it1 != cend_sislo(
"abcdefghijkl",
"a",
"bgdk");
++it1) {
for (
auto it2 = cbegin_sislo(
"abcdefghijkl",
"a",
"bgdk"); it2 != it1;
++it2) {
M++;
if (k.equal_to(*it1, *it2)) {
N--;
break;
}
}
}
REQUIRE(M == 10052729);
REQUIRE(N == 4392);
REQUIRE(k.number_of_normal_forms(0, 6) == 255932);
// TODO(later) include when we have cbegin_normal_forms,
// cend_normal_forms
// REQUIRE(
// std::vector<std::string>(k.cbegin_normal_forms(0, 2),
// k.cend_normal_forms())
// == std::vector<std::string>(
// {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
// "l"}));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"019",
"(fpsemi) std::string smalloverlap/gap/test.gi:77 "
"(infinite) (KnuthBendix 059)",
"[standard][kambites][fpsemigroup][fpsemi]") {
test_case_1_million_equals<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"020",
"(fpsemi) MultiStringView smalloverlap/gap/test.gi:77 "
"(infinite) (KnuthBendix 059)",
"[standard][kambites][fpsemigroup][fpsemi]") {
test_case_1_million_equals<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_code_cov() {
auto rg = ReportGuard(REPORT);
// A slightly more complicated presentation for testing case (6), in
// which the max piece suffixes of the first two relation words no
// longer agree (since fh and gh are now pieces).
Kambites<T> k;
k.set_alphabet(
"abcde");
k.add_rule(
"cadeca",
"baedba");
REQUIRE(!k.equal_to(
"cadece",
"baedce"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"021",
"(fpsemi) code coverage (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_code_cov<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"022",
"(fpsemi) code coverage (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_code_cov<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_ex_3_13_14() {
auto rg = ReportGuard(REPORT);
// Example 3.13 + 3.14
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"abbba",
"cdc");
std::string p =
"c";
REQUIRE(k.normal_form(
"cdcdcabbbabbbabbcd") ==
"abbbadcabbbabbbabbcd");
REQUIRE(k.equal_to(k.normal_form(
"cdcdcabbbabbbabbcd"),
"cdcdcabbbabbbabbcd"));
REQUIRE(k.equal_to(
"abbbadcbbba",
"cdabbbcdc"));
REQUIRE(k.equal_to(k.normal_form(
"cdabbbcdc"),
"cdabbbcdc"));
REQUIRE(k.normal_form(
"cdabbbcdc") ==
"abbbadcbbba");
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"023",
"(fpsemi) prefix (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_ex_3_13_14<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"024",
"(fpsemi) prefix (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_ex_3_13_14<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_ex_3_15() {
auto rg = ReportGuard(REPORT);
// Example 3.15
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"aabc",
"acba");
std::string original =
"cbacbaabcaabcacbacba";
std::string expected =
"cbaabcabcaabcaabcabc";
REQUIRE(k.equal_to(
"cbaabcabcaabcaabccba", original));
REQUIRE(k.equal_to(original, expected));
REQUIRE(k.equal_to(expected, original));
REQUIRE(k.equal_to(
"cbaabcabcaabcaabccba", expected));
REQUIRE(k.equal_to(original,
"cbaabcabcaabcaabccba"));
REQUIRE(k.equal_to(expected,
"cbaabcabcaabcaabccba"));
REQUIRE(k.equal_to(k.normal_form(original), original));
REQUIRE(k.normal_form(original) == expected);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"025",
"(fpsemi) normal_form (Example 3.15) (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_ex_3_15<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"026",
"(fpsemi) normal_form (Example 3.15) (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_ex_3_15<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_ex_3_16() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"abcd",
"acca");
std::string original =
"bbcabcdaccaccabcddd";
std::string expected =
"bbcabcdabcdbcdbcddd";
REQUIRE(k.equal_to(original, expected));
REQUIRE(k.equal_to(expected, original));
REQUIRE(k.normal_form(original) == expected);
REQUIRE(k.equal_to(k.normal_form(original), original));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"027",
"(fpsemi) normal_form (Example 3.16) (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_ex_3_16<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"028",
"(fpsemi) normal_form (Example 3.16) (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_ex_3_16<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_ex_3_16_again() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"abcd",
"acca");
REQUIRE(std::all_of(
cbegin_sislo(
"abcd",
"a",
"aaaa"),
cend_sislo(
"abcd",
"a",
"aaaa"),
[&k](std::string
const& w) {
return k.normal_form(w) == w; }));
REQUIRE(std::count_if(
cbegin_sislo(
"abcd",
"aaaa",
"aaaaa"),
cend_sislo(
"abcd",
"aaaa",
"aaaaa"),
[&k](std::string
const& w) {
return k.normal_form(w) != w; })
== 1);
REQUIRE(std::count_if(
cbegin_sislo(
"abcd",
"aaaaa",
"aaaaaa"),
cend_sislo(
"abcd",
"aaaaa",
"aaaaaa"),
[&k](std::string
const& w) {
return k.normal_form(w) != w; })
== 8);
REQUIRE(std::count_if(
cbegin_sislo(
"abcd",
"aaaaaa",
"aaaaaaa"),
cend_sislo(
"abcd",
"aaaaaa",
"aaaaaaa"),
[&k](std::string
const& w) {
return k.normal_form(w) != w; })
== 48);
{
std::string w = k.normal_form(
"accaccabd");
REQUIRE(w ==
"abcdbcdbd");
REQUIRE(std::count_if(cbegin_sislo(
"abcd",
"aaaaaaaaa", w),
cend_sislo(
"abcd",
"aaaaaaaaa", w),
[&k, &w](std::string
const& u) {
return !k.equal_to(u, w);
})
== std::distance(cbegin_sislo(
"abcd",
"aaaaaaaaa", w),
cend_sislo(
"abcd",
"aaaaaaaaa", w)));
}
{
std::string w = k.normal_form(
"accbaccad");
REQUIRE(w ==
"accbabcdd");
REQUIRE(std::count_if(cbegin_sislo(
"abcd",
"aaaaaaaaa", w),
cend_sislo(
"abcd",
"aaaaaaaaa", w),
[&k, &w](std::string
const& u) {
return !k.equal_to(u, w);
})
== std::distance(cbegin_sislo(
"abcd",
"aaaaaaaaa", w),
cend_sislo(
"abcd",
"aaaaaaaaa", w)));
}
{
std::string w = k.normal_form(
"abcdbcacca");
REQUIRE(w ==
"abcdbcabcd");
REQUIRE(k.equal_to(w,
"abcdbcacca"));
REQUIRE(
std::count_if(
cbegin_sislo(
"abcd", std::string(w.size(),
'a'), w),
cend_sislo(
"abcd", std::string(w.size(),
'a'), w),
[&k, &w](std::string
const& u) {
return !k.equal_to(u, w); })
== std::distance(
cbegin_sislo(
"abcd", std::string(w.size(),
'a'), w),
cend_sislo(
"abcd", std::string(w.size(),
'a'), w)));
}
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"029",
"(fpsemi) normal_form (Example 3.16) more exhaustive (std::string)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_ex_3_16_again<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"030",
"(fpsemi) normal_form (Example 3.16) more exhaustive (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_ex_3_16_again<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_small() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"ab");
REQUIRE(k.is_obviously_infinite());
REQUIRE(k.size() == POSITIVE_INFINITY);
k.add_rule(
"aaa",
"a");
k.add_rule(
"a",
"bb");
REQUIRE(k.small_overlap_class() == 1);
REQUIRE_THROWS_AS(k.size(), LibsemigroupsException);
REQUIRE_THROWS_AS(k.equal_to(
"a",
"aaa"), LibsemigroupsException);
REQUIRE(!k.finished());
k.run();
REQUIRE(!k.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"031",
"(fpsemi) small presentation (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_small<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"032",
"(fpsemi) small presentation (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_small<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_non_smalloverlap() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcdefg");
k.add_rule(
"abcd",
"aaaeaa");
k.add_rule(
"ef",
"dg");
k.add_rule(
"a",
"b");
REQUIRE(k.small_overlap_class() == 1);
REQUIRE_THROWS_AS(k.size(), LibsemigroupsException);
REQUIRE_THROWS_AS(k.equal_to(
"a",
"aaa"), LibsemigroupsException);
REQUIRE(!k.finished());
k.run();
REQUIRE(!k.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"033",
"(fpsemi) non-smalloverlap (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_non_smalloverlap<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"034",
"(fpsemi) non-smalloverlap (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_non_smalloverlap<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_3() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"abcd",
"accca");
REQUIRE(k.number_of_pieces(0) == POSITIVE_INFINITY);
REQUIRE(k.number_of_pieces(1) == 4);
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"bbcabcdaccaccabcddd") ==
"bbcabcdaccaccabcddd");
REQUIRE(k.equal_to(
"bbcabcdaccaccabcddd",
"bbcabcdaccaccabcddd"));
k.run();
REQUIRE(k.started());
REQUIRE(k.finished());
Kambites<T> l(k);
REQUIRE(l.started());
REQUIRE(l.finished());
REQUIRE(l.number_of_pieces(0) == POSITIVE_INFINITY);
REQUIRE(l.number_of_pieces(1) == 4);
REQUIRE(l.small_overlap_class() == 4);
REQUIRE(l.normal_form(
"bbcabcdaccaccabcddd") ==
"bbcabcdaccaccabcddd");
REQUIRE(l.equal_to(
"bbcabcdaccaccabcddd",
"bbcabcdaccaccabcddd"));
REQUIRE(l.number_of_normal_forms(0, 0) == 0);
REQUIRE(l.number_of_normal_forms(6, 6) == 0);
REQUIRE(l.number_of_normal_forms(10, 1) == 0);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"035",
"(fpsemi) MT test 3 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_3<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"036",
"(fpsemi) MT test 3 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_3<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_5() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abc");
k.add_rule(
"ac",
"cbbbbc");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"acbbbbc") ==
"aac");
REQUIRE(k.equal_to(
"acbbbbc",
"aac"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"037",
"(fpsemi) MT test 5 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_5<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"038",
"(fpsemi) MT test 5 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_5<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_6() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abc");
k.add_rule(
"ccab",
"cbac");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"bacbaccabccabcbacbac") ==
"bacbacbaccbaccbacbac");
REQUIRE(k.equal_to(
"bacbaccabccabcbacbac",
"bacbacbaccbaccbacbac"));
REQUIRE(k.normal_form(
"ccabcbaccab") ==
"cbaccbacbac");
REQUIRE(k.equal_to(
"ccabcbaccab",
"cbaccbacbac"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"039",
"(fpsemi) MT test 6 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_6<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"040",
"(fpsemi) MT test 6 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_6<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_10() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcdefghij");
k.add_rule(
"afh",
"bgh");
k.add_rule(
"hc",
"de");
k.add_rule(
"ei",
"j");
REQUIRE(k.small_overlap_class() == POSITIVE_INFINITY);
REQUIRE(k.normal_form(
"bgdj") ==
"afdei");
REQUIRE(k.equal_to(
"bgdj",
"afdei"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"041",
"(fpsemi) MT test 10 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_6<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"042",
"(fpsemi) MT test 10 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_6<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_13() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"abcd",
"dcba");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"dcbdcba") ==
"abcdbcd");
REQUIRE(k.equal_to(
"dcbdcba",
"abcdbcd"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"043",
"(fpsemi) MT test 13 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_13<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"044",
"(fpsemi) MT test 13 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_13<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_14() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"abca",
"dcbd");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"dcbabca") ==
"abcacbd");
REQUIRE(k.equal_to(
"dcbabca",
"abcacbd"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"045",
"(fpsemi) MT test 14 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_14<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"046",
"(fpsemi) MT test 14 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_14<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_15() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"abcd",
"dcba");
k.add_rule(
"adda",
"dbbd");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"dbbabcd") ==
"addacba");
REQUIRE(k.equal_to(
"dbbabcd",
"addacba"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"047",
"(fpsemi) MT test 15 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_15<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"048",
"(fpsemi) MT test 15 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_15<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_16() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcdefg");
k.add_rule(
"abcd",
"acca");
k.add_rule(
"gf",
"ge");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"accabcdgf") ==
"abcdbcdge");
REQUIRE(k.equal_to(
"accabcdgf",
"abcdbcdge"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"049",
"(fpsemi) MT test 16 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_16<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"050",
"(fpsemi) MT test 16 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_16<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_mt_17() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"ababbabbbabbbb",
"abbbbbabbbbbbabbbbbbbabbbbbbbb");
k.add_rule(
"cdcddcdddcdddd",
"cdddddcddddddcdddddddcdddddddd");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.normal_form(
"abbbacdddddcddddddcdddddddcdddddddd")
==
"abbbacdcddcdddcdddd");
REQUIRE(k.equal_to(
"abbbacdddddcddddddcdddddddcdddddddd",
"abbbacdcddcdddcdddd"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"051",
"(fpsemi) MT test 17 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_17<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"052",
"(fpsemi) MT test 17 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_mt_17<MultiStringView>();
}
///////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_weak_1() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"acba",
"aabc");
k.add_rule(
"acba",
"dbbbd");
REQUIRE(k.small_overlap_class() == 4);
REQUIRE(k.equal_to(
"aaabc",
"adbbbd"));
REQUIRE(k.equal_to(
"adbbbd",
"aaabc"));
REQUIRE(number_of_words(4, 4, 6) == 1280);
REQUIRE(std::count_if(
cbegin_sislo(
"abcd",
"aaaa",
"aaaaaa"),
cend_sislo(
"abcd",
"aaaa",
"aaaaaa"),
[&k](std::string
const& w) {
return k.equal_to(
"acba", w); })
== 3);
REQUIRE(k.equal_to(
"aaabcadbbbd",
"adbbbdadbbbd"));
REQUIRE(k.equal_to(
"aaabcaaabc",
"adbbbdadbbbd"));
REQUIRE(k.equal_to(
"acba",
"dbbbd"));
REQUIRE(k.equal_to(
"acbabbbd",
"aabcbbbd"));
REQUIRE(k.equal_to(
"aabcbbbd",
"acbabbbd"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"053",
"(fpsemi) weak C(4) not strong x 1 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_1<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"054",
"(fpsemi) weak C(4) not strong x 1 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_1<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_weak_2() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"acba",
"aabc");
k.add_rule(
"acba",
"adbd");
REQUIRE(k.equal_to(
"acbacba",
"aabcabc"));
REQUIRE(k.normal_form(
"acbacba") ==
"aabcabc");
REQUIRE(k.equal_to(k.normal_form(
"acbacba"),
"aabcabc"));
REQUIRE(k.equal_to(
"aabcabc", k.normal_form(
"acbacba")));
REQUIRE(std::count_if(
cbegin_sislo(
"abcd",
"aaaa",
"aaaaaa"),
cend_sislo(
"abcd",
"aaaa",
"aaaaaa"),
[&k](std::string
const& w) {
return k.equal_to(
"acba", w); })
== 3);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"055",
"(fpsemi) weak C(4) not strong x 2 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_2<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"056",
"(fpsemi) weak C(4) not strong x 2 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_2<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_weak_3() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcde");
k.add_rule(
"bceac",
"aeebbc");
k.add_rule(
"aeebbc",
"dabcd");
REQUIRE(k.normal_form(
"bceacdabcd") ==
"aeebbcaeebbc");
REQUIRE(k.equal_to(k.normal_form(
"bceacdabcd"),
"aeebbcaeebbc"));
REQUIRE(k.equal_to(
"aeebbcaeebbc", k.normal_form(
"bceacdabcd")));
REQUIRE(std::count_if(
cbegin_sislo(
"abcd",
"aaaa",
"aaaaaa"),
cend_sislo(
"abcd",
"aaaa",
"aaaaaa"),
[&k](std::string
const& w) {
return k.equal_to(
"acba", w); })
== 1);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"057",
"(fpsemi) weak C(4) not strong x 3 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_3<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"058",
"(fpsemi) weak C(4) not strong x 3 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_3<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_weak_4() {
auto rg = ReportGuard(REPORT);
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"acba",
"aabc");
k.add_rule(
"acba",
"dbbd");
REQUIRE(k.normal_form(
"bbacbcaaabcbbd") ==
"bbacbcaaabcbbd");
REQUIRE(k.equal_to(k.normal_form(
"bbacbcaaabcbbd"),
"bbacbcaaabcbbd"));
REQUIRE(k.equal_to(
"bbacbcaaabcbbd", k.normal_form(
"bbacbcaaabcbbd")));
REQUIRE(k.normal_form(
"acbacba") ==
"aabcabc");
REQUIRE(k.equal_to(k.normal_form(
"acbacba"),
"aabcabc"));
REQUIRE(k.equal_to(
"aabcabc", k.normal_form(
"acbacba")));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"059",
"(fpsemi) weak C(4) not strong x 4 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_4<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"060",
"(fpsemi) weak C(4) not strong x 4 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_4<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_weak_5() {
Kambites<T> k;
k.set_alphabet(
"abcde");
k.add_rule(
"abcd",
"aaeaaa");
REQUIRE(number_of_subwords(k.cbegin_rules(), k.cend_rules()) == 16);
size_t n = sum_lengths(k.cbegin_rules(), k.cend_rules());
REQUIRE(n == 10);
REQUIRE(n * n == 100);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"061",
"(fpsemi) weak C(4) not strong x 5 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_5<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"062",
"(fpsemi) weak C(4) not strong x 5 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_5<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_weak_6() {
Kambites<T> k;
k.set_alphabet(
"abcd");
k.add_rule(
"acba",
"aabc");
k.add_rule(
"acba",
"adbd");
REQUIRE(k.normal_form(
"acbacba") ==
"aabcabc");
REQUIRE(k.equal_to(k.normal_form(
"acbacba"),
"aabcabc"));
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"063",
"(fpsemi) weak C(4) not strong x 6 (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_6<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"064",
"(fpsemi) weak C(4) not strong x 6 (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_weak_6<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_konovalov() {
Kambites<T> k;
k.set_alphabet(
"abAB");
k.add_rule(
"Abba",
"BB");
k.add_rule(
"Baab",
"AA");
REQUIRE(k.small_overlap_class() == 2);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"065",
"(fpsemi) Konovalov example (std::string)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_konovalov<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"066",
"(fpsemi) Konovalov example (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi]") {
test_case_konovalov<MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
template <
typename T>
void test_case_long_words() {
Kambites<T> k;
k.set_alphabet(
"abcde");
k.add_rule(
"bceac",
"aeebbc");
k.add_rule(
"aeebbc",
"dabcd");
REQUIRE(k.small_overlap_class() == 4);
std::string w1 =
"bceac";
std::string w2 =
"dabcd";
std::string w3 =
"aeebbc";
auto lhs = random_power_string(w1, w2, w3, 4000);
auto rhs = random_power_string(w1, w2, w3, 4000);
for (size_t i = 0; i < 10; ++i) {
REQUIRE(k.equal_to(lhs, rhs));
}
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"067",
"(fpsemi) long words (std::string)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_long_words<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"068",
"(fpsemi) long words (MultiStringView)",
"[quick][kambites][fpsemigroup][fpsemi][no-valgrind]") {
test_case_long_words<detail::MultiStringView>();
}
////////////////////////////////////////////////////////////////////////
// Some tests for exploration of the space of all 2-generator 1-relation
// semigroups
////////////////////////////////////////////////////////////////////////
// TODO(v3) reuse the same Kambites in-place rather than this
template <
typename T>
auto count_2_gen_1_rel(size_t min, size_t max) {
Sislo x;
x.alphabet(
"ab");
x.first(min);
x.last(max);
auto last = x.cbegin();
std::advance(last, std::distance(x.cbegin(), x.cend()) - 1);
uint64_t total_c4 = 0;
uint64_t total = 0;
for (
auto it1 = x.cbegin(); it1 != last; ++it1) {
for (
auto it2 = it1 + 1; it2 != x.cend(); ++it2) {
total++;
Kambites<std::string> k;
k.set_alphabet(
"ab");
k.add_rule(*it1, *it2);
if (k.small_overlap_class() >= 4) {
total_c4++;
}
}
}
return std::make_pair(total_c4, total);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"069",
"(fpsemi) almost all 2-generated 1-relation monoids are C(4)",
"[extreme][kambites][fpsemigroup][fpsemi]") {
auto x = count_2_gen_1_rel<std::string>(1, 7);
REQUIRE(x.first == 1);
REQUIRE(x.second == 7875);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"070",
"(fpsemi) almost all 2-generated 1-relation monoids are C(4)",
"[extreme][kambites][fpsemigroup][fpsemi]") {
auto x = count_2_gen_1_rel<std::string>(1, 11);
REQUIRE(x.first == 18171);
REQUIRE(x.second == 2092035);
}
// Takes approx. 31s
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"071",
"(fpsemi) almost all 2-generated 1-relation monoids are C(4)",
"[extreme][kambites][fpsemigroup][fpsemi]") {
auto x = count_2_gen_1_rel<std::string>(1, 12);
REQUIRE(x.first == 235
'629);
REQUIRE(x.second == 8
'378'371);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"072",
"(fpsemi) almost all 2-generated 1-relation monoids are C(4)",
"[fail][kambites][fpsemigroup][fpsemi]") {
auto x = count_2_gen_1_rel<std::string>(1, 13);
REQUIRE(x.first == 0);
REQUIRE(x.second == 0);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"073",
"(fpsemi) almost all 2-generated 1-relation monoids are C(4)",
"[extreme][kambites][fpsemigroup][fpsemi]") {
std::cout.precision(10);
size_t
const sample_size = 1000;
std::cout <<
"Sample size = " << sample_size << std::endl;
for (size_t i = 8; i < 100; ++i) {
size_t
const min = 7;
size_t
const max = i + 1;
auto x = sample(
"ab", 1, min, max, sample_size);
std::cout <<
"Estimate of C(4) / non-C(4) (length " << min <<
" to "
<< max <<
") = " << std::fixed
<<
double(std::get<0>(x)) / sample_size << std::endl;
std::cout <<
"Estimate of confluent / non-confluent (length " << min
<<
" to " << max <<
") = " << std::fixed
<<
double(std::get<1>(x)) / sample_size << std::endl
<< std::endl;
}
}
////////////////////////////////////////////////////////////////////////
// Some tests for exploration of the space of all 3-generator 1-relation
// semigroups
////////////////////////////////////////////////////////////////////////
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"074",
"(fpsemi) 3-generated 1-relation C(4)-semigroups",
"[extreme][kambites][fpsemigroup][fpsemi]") {
auto first = cbegin_sislo(
"abc",
"a", std::string(8,
'a'));
auto last = cbegin_sislo(
"abc",
"a", std::string(8,
'a'));
size_t N
= std::distance(first, cend_sislo(
"abc",
"a", std::string(8,
'a')));
REQUIRE(N == 3279);
std::advance(last, N - 1);
size_t total_c4 = 0;
size_t total = 0;
auto llast = last;
++llast;
std::unordered_set<std::string> set;
for (
auto it1 = first; it1 != last; ++it1) {
auto it2 = it1;
++it2;
for (; it2 != llast; ++it2) {
total++;
Kambites<std::string> k;
k.set_alphabet(
"abc");
k.add_rule(*it1, *it2);
if (k.small_overlap_class() >= 4) {
auto tmp = *it1 +
"#" + *it2;
if (set.insert(tmp).second) {
auto u = swap_a_b_c(*it1);
auto v = swap_a_b_c(*it2);
for (size_t i = 0; i < 5; ++i) {
if (shortlex_compare(u[i], v[i])) {
set.insert(u[i] +
"#" + v[i]);
}
else {
set.insert(v[i] +
"#" + u[i]);
}
}
std::cout << *it1 <<
" = " << *it2 << std::endl;
total_c4++;
}
}
}
}
REQUIRE(total_c4 == 307511);
REQUIRE(total == 5374281);
}
LIBSEMIGROUPS_TEST_CASE(
"Kambites",
"079",
"(fpsemi) normal form possible bug",
"[standard][kambites][fpsemigroup][fpsemi]") {
// There was a bug in MultiStringView::append, that caused this test to
// fail, so we keep this test to check that the bug in
// MultiStringView::append is resolved.
Kambites<MultiStringView> k;
k.set_alphabet(
"ab");
k.add_rule(
"aaabbab",
"bbbaaba");
std::vector<std::string> words = {
"bbbaabaabbbbbaabaabaaabbaabbbbbaaabaaabababbbbaaabbabababbaabbabaabb"
"aaabbabbaaaabbabbbbbbaabbbbaabbabaaabaaaaabbaabababababaaaabaabbabba"
"bbaaabbabababbabaabbbbbbabaabbabaaaababbababbabbabbabbbabbbabbbabbbb"
"aaaaaabbabbaababbbaaababbbbababababbabaabbbbbabaaaababaaabbaaabbaaab"
"babaabbbaababbbaaabbaabbbbaabbbbaaaaababbabbbaaaaaababbbbaaabbbabbba"
"babbbbbbaabaabababbabbbbbaaaabbbbabbababbbaaaabbbbaabbbbbabbbbbabaab"
"bbaaabaaabbababbbabbaaaaaabbbbabababbaabbabbbbabbabbaabbbaaabaaabbab"
"abbbabbbbaabaaababbabbaababbbabbaababbabbbbabbbbabaabaaaabaaaabababa"
"abababbaaabbabbbbbbaaaaaabbbbabbabbabaaaaabaabbaababbbbaabaaabbabaaa"
"abaaabbbaaaabbabbababaaaabbbbaaabbababababaabbaaaabaabbababbabbaaaba"
"bbaaabbbbbabbbaababaaabbababbbbbaabbbabaaaaabbbbabbabaaaababbabbabab"
"aabbaababbaaabbabbbbabbbaaabbabbbaabbababbaabbaaaaaabaaabbbaababbaaa"
"ababaabbaaabbbaabababbbbbababbbbbbbaabbbbaabababbbaabbbbbbbaabbbbaaa"
"babaaaabaababbbaabaaabaaabaaaaabaabbbbabbabaaabbabbaabbaabbabaaabbbb"
"baaabababbaabbbaababababaababbaabbabaaaaabaaabaaababaababaaaaaababaa"
"aaaaaabaababbbbaabaabbabbabaaaaaabaabbabbbabbaabbbbbbbaaaababababbbb"
"ababbbabbbaaabbabbabbaabbbbbbbababaabaabababbaaabbaabbbaabbbabbbbbab"
"aaabbbababbbaabaaaabaabbaaaabbabbbabababbaaabbbbaabaabbababaaaabbbaa"
"aabbbaabaa",
"aaabbababbbbbaabaabaaabbaabbbbbaaabaaabababbbbaaabbabababbaabbabaabb"
"bbbaababaaaabbabbbbbbaabbbbaabbabaaabaaaaabbaabababababaaaabaabbabba"
"bbaaabbabababbabaabbbbbbabaabbabaaaababbababbabbabbabbbabbbabbbabbbb"
"aaabbbaababaababbbaaababbbbababababbabaabbbbbabaaaababaaabbaaabbaaab"
"babaabbbaababbbaaabbaabbbbaabbbbaaaaababbabbbaaaaaababbbbaaabbbabbba"
"babbbbbbaabaabababbabbbbbaaaabbbbabbababbbaaaabbbbaabbbbbabbbbbabaab"
"bbaaabbbbaabaabbbabbaaaaaabbbbabababbaabbabbbbabbabbaabbbaaabaaabbab"
"abbbabbbbaabaaababbabbaababbbabbaababbabbbbabbbbabaabaaaabaaaabababa"
"abababbaaabbabbbbbbaaaaaabbbbabbabbabaaaaabaabbaababbbbaabaaabbabaaa"
"abaaabbbabbbaababababaaaabbbbaaabbababababaabbaaaabaabbababbabbaaaba"
"bbaaabbbbbabbbaababaaabbababbbbbaabbbabaaaaabbbbabbabaaaababbabbabab"
"aabbaababbbbbaababbbabbbaaabbabbbaabbababbaabbaaaaaabaaabbbaababbaaa"
"ababaabbaaabbbaabababbbbbababbbbbbbaabbbbaabababbbaabbbbbbbaabbbbaaa"
"babaaaabaabaaaabbabaabaaabaaaaabaabbbbabbabaaabbabbaabbaabbabaaabbbb"
"baaabababbaaaaabbabbababaababbaabbabaaaaabaaabaaababaababaaaaaababaa"
"aaaaaabaababbbbaabaabbabbabaaaaaabaabbabbbabbaabbbbbbbaaaababababbbb"
--> --------------------
--> maximum size reached
--> --------------------