//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2019 Michael Young
//
// 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 classes CongruenceByPairs
// and FpSemigroupByPairs.
#include <algorithm>
// for sort, transform
#include <functional>
// for mem_fn
#include <vector>
// for vector
#include "catch.hpp" // for REQUIRE, SECTION, REQUIRE_THROWS_AS, REQ...
#include "libsemigroups/cong-intf.hpp" // for congruence_kind, CongruenceInterface::non_tr...
#include "libsemigroups/cong-pair.hpp" // for KnuthBendixCongruenceByPairs, CongruenceByPairs
#include "libsemigroups/knuth-bendix.hpp" // for KnuthBendix
#include "libsemigroups/report.hpp" // for ReportGuard
#include "libsemigroups/tce.hpp" // for TCE
#include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter
#include "libsemigroups/transf.hpp" // for Transf<>
#include "libsemigroups/types.hpp" // for relation_type, word_type
#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE
namespace libsemigroups {
struct LibsemigroupsException;
}
// namespace libsemigroups
namespace libsemigroups {
constexpr
bool REPORT =
false ;
congruence_kind
const twosided = congruence_kind::twosided;
congruence_kind
const left = congruence_kind::left;
congruence_kind
const right = congruence_kind::right;
using congruence::ToddCoxeter;
using detail::TCE;
using FroidurePinTCE = FroidurePin<TCE, FroidurePinTraits<TCE, TCE::Table>>;
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"001" ,
"(cong) 2-sided cong. on finite semigroup" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens
= {Transf<>({
1 ,
3 ,
4 ,
2 ,
3 }), Transf<>({
3 ,
2 ,
1 ,
3 ,
3 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size() == 88);
// REQUIRE(S.number_of_rules() == 18);
CongruenceByPairs<decltype(S)> p(twosided, S);
// p copies S!
REQUIRE(p.has_parent_froidure_pin());
p.add_pair({
0 ,
1 ,
0 ,
0 ,
0 ,
1 ,
1 ,
0 ,
0 }, {
1 ,
0 ,
0 ,
0 ,
1 });
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 })
== p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }));
REQUIRE(p.finished());
REQUIRE(!p.parent_froidure_pin()->started());
REQUIRE(!p.parent_froidure_pin()->finished());
REQUIRE(p.number_of_classes() ==
21 );
REQUIRE(p.number_of_classes() ==
21 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"002" ,
"(cong) left congruence on finite semigroup" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens
= {Transf<>({
1 ,
3 ,
4 ,
2 ,
3 }), Transf<>({
3 ,
2 ,
1 ,
3 ,
3 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 88);
// REQUIRE(S.number_of_rules(false) == 18);
CongruenceByPairs<decltype(S)> p(left, S);
p.add_pair({
0 ,
1 ,
0 ,
0 ,
0 ,
1 ,
1 ,
0 ,
0 }, {
1 ,
0 ,
0 ,
0 ,
1 });
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }) ==
1 );
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
69 );
REQUIRE(p.number_of_classes() ==
69 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"003" ,
"(cong) right congruence on finite semigroup" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens
= {Transf<>({
1 ,
3 ,
4 ,
2 ,
3 }), Transf<>({
3 ,
2 ,
1 ,
3 ,
3 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 88);
// REQUIRE(S.number_of_rules(false) == 18);
CongruenceByPairs<decltype(S)> p(right, S);
p.add_pair({
0 ,
1 ,
0 ,
0 ,
0 ,
1 ,
1 ,
0 ,
0 }, {
1 ,
0 ,
0 ,
0 ,
1 });
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
4 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }) ==
5 );
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
72 );
REQUIRE(p.number_of_classes() ==
72 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"004" ,
"(cong) trivial congruence on finite semigroup" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<PPerm<>> gens = {PPerm<>({
0 ,
1 ,
3 ,
4 }, {
1 ,
4 ,
0 ,
3 },
5 ),
PPerm<>({
0 ,
1 ,
2 }, {
0 ,
4 ,
3 },
5 )};
FroidurePin<PPerm<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 53);
// REQUIRE(S.number_of_rules(false) == 20);
CongruenceByPairs<decltype(S)> p(twosided, S);
// Class indices are assigned starting at 0
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }) ==
1 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
1 }) ==
2 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
1 }) ==
3 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
0 }) ==
3 );
REQUIRE(p.word_to_class_index({
1 ,
0 ,
0 ,
1 ,
0 ,
0 ,
0 }) ==
4 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 }) != p.word_to_class_index({
0 ,
0 ,
0 }));
REQUIRE(p.word_to_class_index({
1 ,
1 }) == p.word_to_class_index({
1 ,
1 ,
1 }));
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
53 );
REQUIRE(p.number_of_classes() ==
53 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"005" ,
"(cong) trivial left congruence on finite semigroup" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<PPerm<>> gens = {PPerm<>({
0 ,
1 ,
3 ,
4 }, {
1 ,
4 ,
0 ,
3 },
5 ),
PPerm<>({
0 ,
1 ,
2 }, {
0 ,
4 ,
3 },
5 )};
FroidurePin<PPerm<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 53);
// REQUIRE(S.number_of_rules(false) == 20);
CongruenceByPairs<decltype(S)> p(left, S);
// Class indices are assigned starting at 0
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }) ==
1 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
1 }) ==
2 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
1 }) ==
3 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
0 }) ==
3 );
REQUIRE(p.word_to_class_index({
1 ,
0 ,
0 ,
1 ,
0 ,
0 ,
0 }) ==
4 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 }) != p.word_to_class_index({
0 ,
0 ,
0 }));
REQUIRE(p.word_to_class_index({
1 ,
1 }) == p.word_to_class_index({
1 ,
1 ,
1 }));
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
53 );
REQUIRE(p.number_of_classes() ==
53 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"006" ,
"(cong) trivial right congruence on finite semigroup" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<PPerm<>> gens = {PPerm<>({
0 ,
1 ,
3 ,
4 }, {
1 ,
4 ,
0 ,
3 },
5 ),
PPerm<>({
0 ,
1 ,
2 }, {
0 ,
4 ,
3 },
5 )};
FroidurePin<PPerm<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 53);
// REQUIRE(S.number_of_rules(false) == 20);
CongruenceByPairs<decltype(S)> p(right, S);
// Class indices are assigned starting at 0
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }) ==
1 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
1 }) ==
2 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
1 }) ==
3 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
0 }) ==
3 );
REQUIRE(p.word_to_class_index({
1 ,
0 ,
0 ,
1 ,
0 ,
0 ,
0 }) ==
4 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 }) != p.word_to_class_index({
0 ,
0 ,
0 }));
REQUIRE(p.word_to_class_index({
1 ,
1 }) == p.word_to_class_index({
1 ,
1 ,
1 }));
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
53 );
REQUIRE(p.number_of_classes() ==
53 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"007" ,
"(cong) universal congruence on finite semigroup" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<PPerm<>> gens = {PPerm<>({
0 ,
1 ,
3 }, {
4 ,
1 ,
0 },
5 ),
PPerm<>({
0 ,
1 ,
2 ,
3 ,
4 }, {
0 ,
2 ,
4 ,
1 ,
3 },
5 )};
FroidurePin<PPerm<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 142);
// REQUIRE(S.number_of_rules(false) == 32);
CongruenceByPairs<decltype(S)> p(twosided, S);
p.add_pair({
1 }, {
0 ,
0 ,
0 ,
1 ,
0 });
// Class indices are assigned starting at 0
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
0 }) ==
0 );
REQUIRE(p.word_to_class_index({
1 ,
0 ,
0 ,
1 ,
0 ,
0 ,
0 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 }) == p.word_to_class_index({
0 ,
0 ,
0 }));
REQUIRE(p.word_to_class_index({
1 ,
1 }) == p.word_to_class_index({
1 ,
1 ,
1 }));
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
1 );
REQUIRE(p.number_of_classes() ==
1 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"008" ,
"(cong) 2-sided congruence on finite semigroup" ,
"[standard][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens = {Transf<>({
7 ,
3 ,
5 ,
3 ,
4 ,
2 ,
7 ,
7 }),
Transf<>({
1 ,
2 ,
4 ,
4 ,
7 ,
3 ,
0 ,
7 }),
Transf<>({
0 ,
6 ,
4 ,
2 ,
2 ,
6 ,
6 ,
4 }),
Transf<>({
3 ,
6 ,
3 ,
4 ,
0 ,
6 ,
0 ,
7 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 11804);
// REQUIRE(S.number_of_rules(false) == 2460);
CongruenceByPairs<decltype(S)> p(twosided, S);
p.add_pair({
0 ,
3 ,
2 ,
1 ,
3 ,
2 ,
2 }, {
3 ,
2 ,
2 ,
1 ,
3 ,
3 });
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
0 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
1 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
0 }) ==
1 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
3 }) ==
2 );
REQUIRE(p.word_to_class_index({
1 ,
2 ,
1 ,
3 ,
3 ,
2 ,
1 ,
2 })
== p.word_to_class_index({
2 ,
1 ,
3 ,
3 ,
2 ,
1 ,
0 }));
REQUIRE(p.word_to_class_index({
0 ,
3 ,
1 ,
1 ,
1 ,
3 ,
2 ,
2 ,
1 ,
0 })
== p.word_to_class_index({
0 ,
3 ,
2 ,
2 ,
1 }));
REQUIRE(p.word_to_class_index({
0 ,
3 ,
2 ,
1 ,
3 ,
3 ,
3 })
!= p.word_to_class_index({
0 ,
0 ,
3 }));
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 })
!= p.word_to_class_index({
1 ,
3 ,
3 ,
2 ,
2 ,
1 ,
0 }));
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
525 );
REQUIRE(p.number_of_classes() ==
525 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"009" ,
"(cong) 2-sided congruence on finite semigroup" ,
"[quick][cong][cong-pair][no-valgrind]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens = {Transf<>({
7 ,
3 ,
5 ,
3 ,
4 ,
2 ,
7 ,
7 }),
Transf<>({
1 ,
2 ,
4 ,
4 ,
7 ,
3 ,
0 ,
7 }),
Transf<>({
0 ,
6 ,
4 ,
2 ,
2 ,
6 ,
6 ,
4 }),
Transf<>({
3 ,
6 ,
3 ,
4 ,
0 ,
6 ,
0 ,
7 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 11804);
// REQUIRE(S.number_of_rules(false) == 2460);
std::vector<relation_type> extra(
{relation_type({
1 ,
3 ,
0 ,
1 ,
2 ,
2 ,
0 ,
2 }, {
1 ,
0 ,
0 ,
1 ,
3 ,
1 })});
CongruenceByPairs<decltype(S)> p(twosided, S);
p.add_pair({
1 ,
3 ,
0 ,
1 ,
2 ,
2 ,
0 ,
2 }, {
1 ,
0 ,
0 ,
1 ,
3 ,
1 });
REQUIRE(p.word_to_class_index({
0 ,
0 ,
0 ,
1 }) ==
1 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
3 }) ==
2 );
REQUIRE(p.word_to_class_index({
0 ,
1 ,
1 ,
2 ,
3 }) ==
0 );
REQUIRE(p.word_to_class_index({
0 ,
1 ,
1 ,
2 ,
3 })
== p.word_to_class_index({
1 ,
0 ,
3 ,
3 ,
3 ,
2 ,
0 }));
REQUIRE(p.word_to_class_index({
3 ,
0 ,
2 ,
0 ,
2 ,
0 ,
2 })
== p.word_to_class_index({
1 ,
2 ,
3 ,
1 ,
2 }));
REQUIRE(p.word_to_class_index({
0 ,
3 ,
2 ,
1 ,
3 ,
3 ,
3 })
!= p.word_to_class_index({
0 ,
0 ,
3 }));
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 })
!= p.word_to_class_index({
1 ,
3 ,
3 ,
2 ,
2 ,
1 ,
0 }));
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
9597 );
REQUIRE(p.number_of_classes() ==
9597 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"010" ,
"(cong) left congruence on big finite semigroup" ,
"[quick][cong][cong-pair][no-valgrind]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens = {Transf<>({
7 ,
3 ,
5 ,
3 ,
4 ,
2 ,
7 ,
7 }),
Transf<>({
1 ,
2 ,
4 ,
4 ,
7 ,
3 ,
0 ,
7 }),
Transf<>({
0 ,
6 ,
4 ,
2 ,
2 ,
6 ,
6 ,
4 }),
Transf<>({
3 ,
6 ,
3 ,
4 ,
0 ,
6 ,
0 ,
7 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that CongruenceByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size(false) == 11804);
// REQUIRE(S.number_of_rules(false) == 2460);
std::vector<relation_type> extra({relation_type()});
CongruenceByPairs<decltype(S)> p(left, S);
p.add_pair({
0 ,
3 ,
2 ,
1 ,
3 ,
2 ,
2 }, {
3 ,
2 ,
2 ,
1 ,
3 ,
3 });
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
3 }) ==
1 );
REQUIRE(p.word_to_class_index({
0 ,
0 ,
3 }) ==
2 );
REQUIRE(p.word_to_class_index({
2 ,
2 ,
0 ,
1 }) ==
0 );
REQUIRE(p.word_to_class_index({
1 ,
1 ,
3 ,
2 ,
2 ,
1 ,
3 ,
1 ,
3 ,
3 })
== p.word_to_class_index({
2 ,
2 ,
0 ,
1 }));
REQUIRE(p.word_to_class_index({
2 ,
1 ,
3 ,
1 ,
2 ,
2 ,
1 ,
3 ,
3 })
== p.word_to_class_index({
1 ,
2 ,
1 ,
2 ,
1 ,
3 ,
2 ,
2 ,
1 ,
3 ,
3 }));
REQUIRE(p.word_to_class_index({
1 ,
1 ,
0 ,
3 })
!= p.word_to_class_index({
1 ,
0 ,
3 ,
2 ,
0 ,
2 ,
0 ,
3 ,
2 ,
2 ,
1 }));
REQUIRE(p.word_to_class_index({
1 ,
3 ,
2 ,
1 ,
3 ,
1 ,
3 ,
2 ,
2 ,
1 ,
3 ,
3 ,
3 })
!= p.word_to_class_index({
3 ,
1 ,
0 ,
2 ,
0 ,
3 ,
1 }));
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.number_of_classes() ==
7449 );
REQUIRE(p.number_of_classes() ==
7449 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(p.parent_froidure_pin()->finished());
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"011" ,
"(cong) left congruence on TCE" ,
"[quick][cong][cong-pair]" ) {
using detail::TCE;
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 });
// TODO(later)
// This doesn't throw because the type of tc.quotient_froidure_pin() is
// std::shared_ptr<FroidurePinBase> and so it isn't easy to check if
// the element type is TCE.
REQUIRE_NOTHROW(
CongruenceByPairs<FroidurePinTCE>(left, tc.quotient_froidure_pin()));
REQUIRE_NOTHROW(CongruenceByPairs<FroidurePinTCE>(
twosided, tc.quotient_froidure_pin()));
REQUIRE_NOTHROW(
CongruenceByPairs<FroidurePinTCE>(right, tc.quotient_froidure_pin()));
REQUIRE_THROWS_AS(
CongruenceByPairs<FroidurePinTCE>(
left,
static_cast <FroidurePinTCE&>(*tc.quotient_froidure_pin())),
LibsemigroupsException);
REQUIRE_THROWS_AS(
CongruenceByPairs<FroidurePinTCE>(
twosided,
static_cast <FroidurePinTCE&>(*tc.quotient_froidure_pin())),
LibsemigroupsException);
REQUIRE_NOTHROW(CongruenceByPairs<FroidurePinTCE>(
right,
static_cast <FroidurePinTCE&>(*tc.quotient_froidure_pin())));
CongruenceByPairs<FroidurePinTCE> cong(
right,
static_cast <FroidurePinTCE&>(*tc.quotient_froidure_pin()));
REQUIRE_THROWS_AS(cong.quotient_froidure_pin(), LibsemigroupsException);
REQUIRE(cong.number_of_classes() ==
27 );
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"012" ,
"(cong) is_quotient_obviously_finite" ,
"[quick][cong][cong-pair]" ) {
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.quotient_froidure_pin()->size() ==
27 );
using detail::TCE;
CongruenceByPairs<FroidurePinTCE> cong(right, tc.quotient_froidure_pin());
REQUIRE(!cong.finished());
REQUIRE(cong.has_parent_froidure_pin());
REQUIRE(cong.parent_froidure_pin()->finished());
REQUIRE(cong.is_quotient_obviously_finite());
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"013" ,
"(cong) class_index_to_word/quotient" ,
"[quick][cong][cong-pair][no-valgrind]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens = {Transf<>({
5 ,
3 ,
5 ,
3 ,
4 ,
2 }),
Transf<>({
1 ,
2 ,
4 ,
4 ,
5 ,
3 }),
Transf<>({
0 ,
5 ,
4 ,
2 ,
2 ,
4 })};
FroidurePin<Transf<>> S(gens);
REQUIRE(S.size() ==
321 );
using P = CongruenceByPairs<decltype(S)>;
std::unique_ptr<P> cong;
SECTION(
"right congruence" ) {
cong = std::make_unique<P>(right, S);
cong->add_pair({
0 }, {
1 });
REQUIRE(cong->number_of_classes() ==
168 );
REQUIRE(cong->word_to_class_index(word_type({
0 })) ==
0 );
REQUIRE(cong->word_to_class_index(word_type({
1 })) ==
0 );
REQUIRE(cong->class_index_to_word(
0 ) == word_type({
0 }));
REQUIRE(cong->word_to_class_index(word_type({
0 ,
0 })) ==
1 );
REQUIRE(cong->class_index_to_word(
1 ) == word_type({
0 ,
0 }));
REQUIRE(cong->word_to_class_index(word_type({
0 ,
1 })) ==
2 );
REQUIRE(cong->class_index_to_word(
2 ) == word_type({
0 ,
1 }));
REQUIRE(cong->class_index_to_word(
3 ) == word_type({
0 ,
2 }));
REQUIRE(cong->word_to_class_index(word_type({
0 ,
2 })) ==
3 );
}
SECTION(
"left congruence" ) {
cong = std::make_unique<P>(left, S);
cong->add_pair({
0 }, {
1 });
REQUIRE(cong->number_of_classes() ==
24 );
}
SECTION(
"2-sided congruence" ) {
cong = std::make_unique<P>(twosided, S);
cong->add_pair({
0 }, {
1 });
REQUIRE(cong->number_of_classes() ==
4 );
}
for (size_t i =
0 ; i < cong->number_of_classes(); ++i) {
auto w = cong->class_index_to_word(i);
REQUIRE(cong->word_to_class_index(w) == i);
}
REQUIRE_THROWS_AS(cong->class_index_to_word(cong->number_of_classes() +
1 ),
LibsemigroupsException);
if (cong->kind() != twosided) {
REQUIRE_THROWS_AS(cong->quotient_froidure_pin(), LibsemigroupsException);
}
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"014" ,
"(cong) const_word_to_class_index" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
FroidurePin<Transf<>> S({Transf<>({
7 ,
3 ,
5 ,
3 ,
4 ,
2 ,
7 ,
7 }),
Transf<>({
1 ,
2 ,
4 ,
4 ,
7 ,
3 ,
0 ,
7 }),
Transf<>({
0 ,
6 ,
4 ,
2 ,
2 ,
6 ,
6 ,
4 }),
Transf<>({
3 ,
6 ,
3 ,
4 ,
0 ,
6 ,
0 ,
7 })});
using P = CongruenceByPairs<decltype(S)>;
std::unique_ptr<P> cong;
SECTION(
"right congruence" ) {
cong = std::make_unique<P>(right, S);
}
SECTION(
"left congruence" ) {
cong = std::make_unique<P>(left, S);
}
SECTION(
"2-sided congruence" ) {
cong = std::make_unique<P>(twosided, S);
}
cong->add_pair({
0 }, {
1 });
REQUIRE(!cong->finished());
REQUIRE(cong->const_contains({
0 }, {
1 }) == tril::unknown);
}
LIBSEMIGROUPS_TEST_CASE(
"CongruenceByPairs" ,
"015" ,
"(cong) size non-Element*" ,
"[quick][cong][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
FroidurePin<Transf<>> S({Transf<>({
7 ,
3 ,
5 ,
3 ,
4 ,
2 ,
7 ,
7 }),
Transf<>({
1 ,
2 ,
4 ,
4 ,
7 ,
3 ,
0 ,
7 }),
Transf<>({
0 ,
6 ,
4 ,
2 ,
2 ,
6 ,
6 ,
4 }),
Transf<>({
3 ,
6 ,
3 ,
4 ,
0 ,
6 ,
0 ,
7 })});
using P = CongruenceByPairs<decltype(S)>;
P cong1(right, S);
REQUIRE(cong1.number_of_classes() ==
11804 );
P cong2(left, S);
REQUIRE(cong2.number_of_classes() ==
11804 );
P cong3(twosided, S);
REQUIRE(cong3.number_of_classes() ==
11804 );
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"016" ,
"non-trivial congruence on an infinite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
3 );
kb.add_rule({
0 ,
1 }, {
1 ,
0 });
kb.add_rule({
0 ,
2 }, {
2 ,
0 });
kb.add_rule({
0 ,
0 }, {
0 });
kb.add_rule({
0 ,
2 }, {
0 });
kb.add_rule({
2 ,
0 }, {
0 });
kb.add_rule({
1 ,
2 }, {
2 ,
1 });
kb.add_rule({
1 ,
1 ,
1 }, {
1 });
kb.add_rule({
1 ,
2 }, {
1 });
kb.add_rule({
2 ,
1 }, {
1 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
kbp.add_pair({
0 }, {
1 });
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 }));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 ,
0 }));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 ,
1 }));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 ,
0 ,
1 }));
REQUIRE(kbp.number_of_non_trivial_classes() ==
1 );
REQUIRE(kbp.cbegin_ntc()->size() ==
5 );
REQUIRE(std::vector<word_type>(kbp.cbegin_ntc()->cbegin(),
kbp.cbegin_ntc()->cend())
== std::vector<word_type>({{
0 }, {
1 }, {
0 ,
1 }, {
1 ,
1 }, {
0 ,
1 ,
1 }}));
REQUIRE_NOTHROW(kbp.quotient_froidure_pin());
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"017" ,
"non-trivial congruence on an infinite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
4 );
kb.add_rule({
0 ,
1 }, {
1 ,
0 });
kb.add_rule({
0 ,
2 }, {
2 ,
0 });
kb.add_rule({
0 ,
0 }, {
0 });
kb.add_rule({
0 ,
2 }, {
0 });
kb.add_rule({
2 ,
0 }, {
0 });
kb.add_rule({
1 ,
2 }, {
2 ,
1 });
kb.add_rule({
1 ,
1 ,
1 }, {
1 });
kb.add_rule({
1 ,
2 }, {
1 });
kb.add_rule({
2 ,
1 }, {
1 });
kb.add_rule({
0 ,
3 }, {
0 });
kb.add_rule({
3 ,
0 }, {
0 });
kb.add_rule({
1 ,
3 }, {
1 });
kb.add_rule({
3 ,
1 }, {
1 });
kb.add_rule({
2 ,
3 }, {
2 });
kb.add_rule({
3 ,
2 }, {
2 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
kbp.add_pair({
0 }, {
1 });
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 }));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 ,
0 }));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 ,
1 }));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
1 ,
0 ,
1 }));
REQUIRE(kbp.number_of_non_trivial_classes() ==
1 );
REQUIRE(kbp.cbegin_ntc()->size() ==
5 );
REQUIRE(std::vector<word_type>(kbp.cbegin_ntc()->cbegin(),
kbp.cbegin_ntc()->cend())
== std::vector<word_type>({{
0 }, {
1 }, {
0 ,
1 }, {
1 ,
1 }, {
0 ,
1 ,
1 }}));
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"018" ,
"non-trivial congruence on an infinite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
5 );
kb.add_rule({
0 ,
1 }, {
0 });
kb.add_rule({
1 ,
0 }, {
0 });
kb.add_rule({
0 ,
2 }, {
0 });
kb.add_rule({
2 ,
0 }, {
0 });
kb.add_rule({
0 ,
3 }, {
0 });
kb.add_rule({
3 ,
0 }, {
0 });
kb.add_rule({
0 ,
0 }, {
0 });
kb.add_rule({
1 ,
1 }, {
0 });
kb.add_rule({
2 ,
2 }, {
0 });
kb.add_rule({
3 ,
3 }, {
0 });
kb.add_rule({
1 ,
2 }, {
0 });
kb.add_rule({
2 ,
1 }, {
0 });
kb.add_rule({
1 ,
3 }, {
0 });
kb.add_rule({
3 ,
1 }, {
0 });
kb.add_rule({
2 ,
3 }, {
0 });
kb.add_rule({
3 ,
2 }, {
0 });
kb.add_rule({
4 ,
0 }, {
0 });
kb.add_rule({
4 ,
1 }, {
1 });
kb.add_rule({
4 ,
2 }, {
2 });
kb.add_rule({
4 ,
3 }, {
3 });
kb.add_rule({
0 ,
4 }, {
0 });
kb.add_rule({
1 ,
4 }, {
1 });
kb.add_rule({
2 ,
4 }, {
2 });
kb.add_rule({
3 ,
4 }, {
3 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
kbp.add_pair({
1 }, {
2 });
REQUIRE(kbp.word_to_class_index({
1 }) == kbp.word_to_class_index({
2 }));
REQUIRE(kbp.number_of_non_trivial_classes() ==
1 );
REQUIRE(kbp.cbegin_ntc()->size() ==
2 );
REQUIRE(std::vector<word_type>(kbp.cbegin_ntc()->cbegin(),
kbp.cbegin_ntc()->cend())
== std::vector<word_type>({{
1 }, {
2 }}));
REQUIRE(kbp.word_to_class_index({
1 }) == kbp.word_to_class_index({
2 }));
REQUIRE(kbp.is_quotient_obviously_finite());
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"019" ,
"non-trivial congruence on an infinite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
5 );
kb.add_rule({
0 ,
1 }, {
0 });
kb.add_rule({
1 ,
0 }, {
0 });
kb.add_rule({
0 ,
2 }, {
0 });
kb.add_rule({
2 ,
0 }, {
0 });
kb.add_rule({
0 ,
3 }, {
0 });
kb.add_rule({
3 ,
0 }, {
0 });
kb.add_rule({
0 ,
0 }, {
0 });
kb.add_rule({
1 ,
1 }, {
0 });
kb.add_rule({
2 ,
2 }, {
0 });
kb.add_rule({
3 ,
3 }, {
0 });
kb.add_rule({
1 ,
2 }, {
0 });
kb.add_rule({
2 ,
1 }, {
0 });
kb.add_rule({
1 ,
3 }, {
0 });
kb.add_rule({
3 ,
1 }, {
0 });
kb.add_rule({
2 ,
3 }, {
0 });
kb.add_rule({
3 ,
2 }, {
0 });
kb.add_rule({
4 ,
0 }, {
0 });
kb.add_rule({
4 ,
1 }, {
2 });
kb.add_rule({
4 ,
2 }, {
3 });
kb.add_rule({
4 ,
3 }, {
1 });
kb.add_rule({
0 ,
4 }, {
0 });
kb.add_rule({
1 ,
4 }, {
2 });
kb.add_rule({
2 ,
4 }, {
3 });
kb.add_rule({
3 ,
4 }, {
1 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
kbp.add_pair({
2 }, {
3 });
REQUIRE(kbp.word_to_class_index({
3 }) == kbp.word_to_class_index({
2 }));
REQUIRE(kbp.number_of_non_trivial_classes() ==
1 );
REQUIRE(kbp.cbegin_ntc()->size() ==
3 );
REQUIRE(std::vector<word_type>(kbp.cbegin_ntc()->cbegin(),
kbp.cbegin_ntc()->cend())
== std::vector<word_type>({{
2 }, {
3 }, {
1 }}));
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"020" ,
"trivial congruence on a finite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
2 );
kb.add_rule({
0 ,
0 ,
1 }, {
0 ,
0 });
kb.add_rule({
0 ,
0 ,
0 ,
0 }, {
0 ,
0 });
kb.add_rule({
0 ,
1 ,
1 ,
0 }, {
0 ,
0 });
kb.add_rule({
0 ,
1 ,
1 ,
1 }, {
0 ,
0 ,
0 });
kb.add_rule({
1 ,
1 ,
1 ,
0 }, {
1 ,
1 ,
0 });
kb.add_rule({
1 ,
1 ,
1 ,
1 }, {
1 ,
1 ,
1 });
kb.add_rule({
0 ,
1 ,
0 ,
0 ,
0 }, {
0 ,
1 ,
0 ,
1 });
kb.add_rule({
0 ,
1 ,
0 ,
1 ,
0 }, {
0 ,
1 ,
0 ,
0 });
kb.add_rule({
0 ,
1 ,
0 ,
1 ,
1 }, {
0 ,
1 ,
0 ,
1 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
REQUIRE(kbp.number_of_classes() ==
27 );
REQUIRE(kbp.word_to_class_index({
0 }) ==
0 );
REQUIRE(kbp.word_to_class_index({
0 ,
0 ,
0 ,
0 }) ==
1 );
REQUIRE(kbp.word_to_class_index({
0 }) ==
0 );
REQUIRE(kbp.word_to_class_index({
1 ,
0 ,
1 }) ==
2 );
REQUIRE(kbp.word_to_class_index({
0 ,
1 ,
1 ,
0 }) ==
1 );
REQUIRE(kbp.number_of_non_trivial_classes() ==
0 );
REQUIRE(kbp.cbegin_ntc() == kbp.cend_ntc());
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"021" ,
"universal congruence on a finite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
2 );
kb.add_rule({
0 ,
0 ,
1 }, {
0 ,
0 });
kb.add_rule({
0 ,
0 ,
0 ,
0 }, {
0 ,
0 });
kb.add_rule({
0 ,
1 ,
1 ,
0 }, {
0 ,
0 });
kb.add_rule({
0 ,
1 ,
1 ,
1 }, {
0 ,
0 ,
0 });
kb.add_rule({
1 ,
1 ,
1 ,
0 }, {
1 ,
1 ,
0 });
kb.add_rule({
1 ,
1 ,
1 ,
1 }, {
1 ,
1 ,
1 });
kb.add_rule({
0 ,
1 ,
0 ,
0 ,
0 }, {
0 ,
1 ,
0 ,
1 });
kb.add_rule({
0 ,
1 ,
0 ,
1 ,
0 }, {
0 ,
1 ,
0 ,
0 });
kb.add_rule({
0 ,
1 ,
0 ,
1 ,
1 }, {
0 ,
1 ,
0 ,
1 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
kbp.add_pair({
0 }, {
1 });
kbp.add_pair({
0 ,
0 }, {
0 });
REQUIRE(kbp.number_of_classes() ==
1 );
REQUIRE(kbp.cbegin_ntc()->size() ==
27 );
REQUIRE(kb.size() ==
27 );
REQUIRE(std::vector<word_type>(kbp.cbegin_ntc()->cbegin(),
kbp.cbegin_ntc()->cend())
== std::vector<word_type>({{
0 },
{
1 },
{
0 ,
0 },
{
0 ,
1 },
{
1 ,
0 },
{
1 ,
1 },
{
0 ,
0 ,
0 },
{
1 ,
0 ,
0 },
{
0 ,
1 ,
0 },
{
1 ,
0 ,
1 },
{
0 ,
1 ,
1 },
{
1 ,
1 ,
0 },
{
1 ,
1 ,
1 },
{
1 ,
0 ,
0 ,
0 },
{
0 ,
1 ,
0 ,
0 },
{
1 ,
1 ,
0 ,
0 },
{
1 ,
0 ,
1 ,
0 },
{
0 ,
1 ,
0 ,
1 },
{
1 ,
1 ,
0 ,
1 },
{
1 ,
0 ,
1 ,
1 },
{
1 ,
1 ,
0 ,
0 ,
0 },
{
1 ,
0 ,
1 ,
0 ,
0 },
{
1 ,
1 ,
0 ,
1 ,
0 },
{
1 ,
0 ,
1 ,
0 ,
1 },
{
1 ,
1 ,
0 ,
1 ,
1 },
{
1 ,
1 ,
0 ,
1 ,
0 ,
0 },
{
1 ,
1 ,
0 ,
1 ,
0 ,
1 }}));
REQUIRE(kbp.number_of_non_trivial_classes() ==
1 );
REQUIRE(kbp.word_to_class_index({
0 ,
0 ,
0 ,
0 }) ==
0 );
REQUIRE(kbp.word_to_class_index({
0 }) ==
0 );
REQUIRE(kbp.word_to_class_index({
1 ,
0 ,
1 }) ==
0 );
REQUIRE(kbp.word_to_class_index({
0 ,
1 ,
1 ,
0 }) ==
0 );
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"022" ,
"left congruence on a finite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
2 );
kb.add_rule({
0 ,
0 ,
1 }, {
0 ,
0 });
kb.add_rule({
0 ,
0 ,
0 ,
0 }, {
0 ,
0 });
kb.add_rule({
0 ,
1 ,
1 ,
0 }, {
0 ,
0 });
kb.add_rule({
0 ,
1 ,
1 ,
1 }, {
0 ,
0 ,
0 });
kb.add_rule({
1 ,
1 ,
1 ,
0 }, {
1 ,
1 ,
0 });
kb.add_rule({
1 ,
1 ,
1 ,
1 }, {
1 ,
1 ,
1 });
kb.add_rule({
0 ,
1 ,
0 ,
0 ,
0 }, {
0 ,
1 ,
0 ,
1 });
kb.add_rule({
0 ,
1 ,
0 ,
1 ,
0 }, {
0 ,
1 ,
0 ,
0 });
kb.add_rule({
0 ,
1 ,
0 ,
1 ,
1 }, {
0 ,
1 ,
0 ,
1 });
KnuthBendixCongruenceByPairs kbp(left, kb);
kbp.add_pair({
0 }, {
1 });
kbp.add_pair({
0 ,
0 }, {
0 });
REQUIRE(kbp.number_of_non_trivial_classes() ==
6 );
std::vector<size_t> v(kbp.number_of_non_trivial_classes(),
0 );
std::transform(kbp.cbegin_ntc(),
kbp.cend_ntc(),
v.begin(),
std::mem_fn(&std::vector<word_type>::size));
std::sort(v.begin(), v.end());
REQUIRE(v == std::vector<size_t>({
4 ,
4 ,
4 ,
5 ,
5 ,
5 }));
REQUIRE(
std::vector<std::vector<word_type>>(kbp.cbegin_ntc(), kbp.cend_ntc())
== std::vector<std::vector<word_type>>(
{{{
0 }, {
1 }, {
0 ,
0 }, {
0 ,
1 }, {
0 ,
0 ,
0 }},
{{
1 ,
0 }, {
1 ,
1 }, {
1 ,
0 ,
0 }, {
1 ,
0 ,
1 }, {
1 ,
0 ,
0 ,
0 }},
{{
0 ,
1 ,
0 }, {
0 ,
1 ,
1 }, {
0 ,
1 ,
0 ,
0 }, {
0 ,
1 ,
0 ,
1 }},
{{
1 ,
1 ,
0 },
{
1 ,
1 ,
1 },
{
1 ,
1 ,
0 ,
0 },
{
1 ,
1 ,
0 ,
1 },
{
1 ,
1 ,
0 ,
0 ,
0 }},
{{
1 ,
0 ,
1 ,
0 }, {
1 ,
0 ,
1 ,
1 }, {
1 ,
0 ,
1 ,
0 ,
0 }, {
1 ,
0 ,
1 ,
0 ,
1 }},
{{
1 ,
1 ,
0 ,
1 ,
0 },
{
1 ,
1 ,
0 ,
1 ,
1 },
{
1 ,
1 ,
0 ,
1 ,
0 ,
0 },
{
1 ,
1 ,
0 ,
1 ,
0 ,
1 }}}));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
0 ,
0 ,
0 }));
REQUIRE(kbp.word_to_class_index({
1 ,
0 ,
1 ,
1 })
== kbp.word_to_class_index({
1 ,
0 ,
1 ,
0 ,
1 }));
REQUIRE(kbp.word_to_class_index({
1 ,
1 ,
0 ,
0 })
!= kbp.word_to_class_index({
0 ,
1 }));
REQUIRE(kbp.word_to_class_index({
1 ,
0 ,
1 ,
0 })
!= kbp.word_to_class_index({
1 ,
1 ,
0 ,
1 ,
0 ,
1 }));
REQUIRE(kbp.word_to_class_index({
1 ,
0 ,
1 }) ==
1 );
REQUIRE(kbp.word_to_class_index({
0 }) ==
0 );
REQUIRE(kbp.word_to_class_index({
0 ,
1 ,
1 ,
0 }) ==
0 );
REQUIRE(kbp.number_of_classes() ==
6 );
}
// KnuthBendixCongruenceByPairs 07 only really tests
// fpsemigroup::KnuthBendix
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"023" ,
"finite group, Chapter 11, Theorem 1.9, H, q = 4 in NR " ,
"[quick][kbp][cong-pair][no-valgrind]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
4 );
kb.add_rule({
0 ,
0 }, {
0 });
kb.add_rule({
0 ,
1 }, {
1 });
kb.add_rule({
1 ,
0 }, {
1 });
kb.add_rule({
0 ,
2 }, {
2 });
kb.add_rule({
2 ,
0 }, {
2 });
kb.add_rule({
0 ,
3 }, {
3 });
kb.add_rule({
3 ,
0 }, {
3 });
kb.add_rule({
2 ,
3 }, {
0 });
kb.add_rule({
3 ,
2 }, {
0 });
kb.add_rule({
1 ,
1 }, {
0 });
kb.add_rule({
2 ,
2 ,
2 ,
2 }, {
0 });
kb.add_rule({
1 ,
2 ,
1 ,
3 ,
1 ,
3 ,
1 ,
2 ,
1 ,
3 ,
1 ,
2 }, {
0 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
REQUIRE(kbp.number_of_classes() ==
120 );
REQUIRE(kbp.number_of_non_trivial_classes() ==
0 );
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"024" ,
"right congruence on infinite fp semigroup" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
3 );
kb.add_rule({
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 }, {
1 });
kb.add_rule({
2 ,
2 ,
2 ,
2 ,
2 }, {
2 });
kb.add_rule({
1 ,
2 ,
2 ,
1 ,
0 }, {
1 ,
2 ,
2 ,
1 });
kb.add_rule({
1 ,
2 ,
2 ,
1 ,
2 }, {
1 ,
2 ,
2 ,
1 });
kb.add_rule({
1 ,
1 ,
2 ,
1 ,
2 ,
0 }, {
1 ,
1 ,
2 ,
1 ,
2 });
kb.add_rule({
1 ,
1 ,
2 ,
1 ,
2 ,
1 }, {
1 ,
1 ,
2 ,
1 ,
2 });
KnuthBendixCongruenceByPairs kbp(right, kb);
kbp.add_pair({
1 ,
2 ,
2 ,
1 }, {
1 ,
1 ,
2 ,
1 ,
2 });
// Generating pair
REQUIRE(kbp.word_to_class_index({
1 ,
2 ,
2 ,
1 })
== kbp.word_to_class_index({
1 ,
1 ,
2 ,
1 ,
2 }));
REQUIRE(kbp.number_of_non_trivial_classes() ==
1 );
REQUIRE(std::vector<word_type>(kbp.cbegin_ntc()->begin(),
kbp.cbegin_ntc()->end())
== std::vector<word_type>({{
1 ,
2 ,
2 ,
1 }, {
1 ,
1 ,
2 ,
1 ,
2 }}));
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"025" ,
"finite fp semigroup, dihedral group of order 6" ,
"[quick][kbp][cong-pair]" ) {
using detail::KBE;
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
5 );
kb.add_rule({
0 ,
0 }, {
0 });
kb.add_rule({
0 ,
1 }, {
1 });
kb.add_rule({
1 ,
0 }, {
1 });
kb.add_rule({
0 ,
2 }, {
2 });
kb.add_rule({
2 ,
0 }, {
2 });
kb.add_rule({
0 ,
3 }, {
3 });
kb.add_rule({
3 ,
0 }, {
3 });
kb.add_rule({
0 ,
4 }, {
4 });
kb.add_rule({
4 ,
0 }, {
4 });
kb.add_rule({
1 ,
2 }, {
0 });
kb.add_rule({
2 ,
1 }, {
0 });
kb.add_rule({
3 ,
4 }, {
0 });
kb.add_rule({
4 ,
3 }, {
0 });
kb.add_rule({
2 ,
2 }, {
0 });
kb.add_rule({
1 ,
4 ,
2 ,
3 ,
3 }, {
0 });
kb.add_rule({
4 ,
4 ,
4 }, {
0 });
auto fp =
static_cast <
FroidurePin<KBE, FroidurePinTraits<KBE, fpsemigroup::KnuthBendix>>*>(
kb.froidure_pin().get());
REQUIRE(fp->size() ==
6 );
std::vector<detail::KBE> expected
= {KBE(kb, kb.alphabet(
0 )),
KBE(kb, kb.alphabet(
1 )),
KBE(kb, kb.alphabet(
3 )),
KBE(kb, kb.alphabet(
4 )),
KBE(kb, kb.alphabet(
1 ) + kb.alphabet(
3 )),
KBE(kb, kb.alphabet(
1 ) + kb.alphabet(
4 ))};
auto result = std::vector<detail::KBE>(fp->cbegin(), fp->cend());
REQUIRE(result == expected);
KnuthBendixCongruenceByPairs kbp(twosided, kb);
REQUIRE(kbp.number_of_classes() ==
6 );
REQUIRE(kbp.number_of_non_trivial_classes() ==
0 );
REQUIRE(kbp.word_to_class_index({
1 }) == kbp.word_to_class_index({
2 }));
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"026" ,
"finite fp semigroup, size 16" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
4 );
kb.add_rule({
3 }, {
2 });
kb.add_rule({
0 ,
3 }, {
0 ,
2 });
kb.add_rule({
1 ,
1 }, {
1 });
kb.add_rule({
1 ,
3 }, {
1 ,
2 });
kb.add_rule({
2 ,
1 }, {
2 });
kb.add_rule({
2 ,
2 }, {
2 });
kb.add_rule({
2 ,
3 }, {
2 });
kb.add_rule({
0 ,
0 ,
0 }, {
0 });
kb.add_rule({
0 ,
0 ,
1 }, {
1 });
kb.add_rule({
0 ,
0 ,
2 }, {
2 });
kb.add_rule({
0 ,
1 ,
2 }, {
1 ,
2 });
kb.add_rule({
1 ,
0 ,
0 }, {
1 });
kb.add_rule({
1 ,
0 ,
2 }, {
0 ,
2 });
kb.add_rule({
2 ,
0 ,
0 }, {
2 });
kb.add_rule({
0 ,
1 ,
0 ,
1 }, {
1 ,
0 ,
1 });
kb.add_rule({
0 ,
2 ,
0 ,
2 }, {
2 ,
0 ,
2 });
kb.add_rule({
1 ,
0 ,
1 ,
0 }, {
1 ,
0 ,
1 });
kb.add_rule({
1 ,
2 ,
0 ,
1 }, {
1 ,
0 ,
1 });
kb.add_rule({
1 ,
2 ,
0 ,
2 }, {
2 ,
0 ,
2 });
kb.add_rule({
2 ,
0 ,
1 ,
0 }, {
2 ,
0 ,
1 });
kb.add_rule({
2 ,
0 ,
2 ,
0 }, {
2 ,
0 ,
2 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
kbp.add_pair({
2 }, {
3 });
REQUIRE(kbp.number_of_classes() ==
16 );
REQUIRE(kbp.number_of_non_trivial_classes() ==
0 );
REQUIRE(kbp.word_to_class_index({
2 }) == kbp.word_to_class_index({
3 }));
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"027" ,
"finite fp semigroup, size 16" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
11 );
kb.add_rule({
2 }, {
1 });
kb.add_rule({
4 }, {
3 });
kb.add_rule({
5 }, {
0 });
kb.add_rule({
6 }, {
3 });
kb.add_rule({
7 }, {
1 });
kb.add_rule({
8 }, {
3 });
kb.add_rule({
9 }, {
3 });
kb.add_rule({
10 }, {
0 });
kb.add_rule({
0 ,
2 }, {
0 ,
1 });
kb.add_rule({
0 ,
4 }, {
0 ,
3 });
kb.add_rule({
0 ,
5 }, {
0 ,
0 });
kb.add_rule({
0 ,
6 }, {
0 ,
3 });
kb.add_rule({
0 ,
7 }, {
0 ,
1 });
kb.add_rule({
0 ,
8 }, {
0 ,
3 });
kb.add_rule({
0 ,
9 }, {
0 ,
3 });
kb.add_rule({
0 ,
10 }, {
0 ,
0 });
kb.add_rule({
1 ,
1 }, {
1 });
kb.add_rule({
1 ,
2 }, {
1 });
kb.add_rule({
1 ,
4 }, {
1 ,
3 });
kb.add_rule({
1 ,
5 }, {
1 ,
0 });
kb.add_rule({
1 ,
6 }, {
1 ,
3 });
kb.add_rule({
1 ,
7 }, {
1 });
kb.add_rule({
1 ,
8 }, {
1 ,
3 });
kb.add_rule({
1 ,
9 }, {
1 ,
3 });
kb.add_rule({
1 ,
10 }, {
1 ,
0 });
kb.add_rule({
3 ,
1 }, {
3 });
kb.add_rule({
3 ,
2 }, {
3 });
kb.add_rule({
3 ,
3 }, {
3 });
kb.add_rule({
3 ,
4 }, {
3 });
kb.add_rule({
3 ,
5 }, {
3 ,
0 });
kb.add_rule({
3 ,
6 }, {
3 });
kb.add_rule({
3 ,
7 }, {
3 });
kb.add_rule({
3 ,
8 }, {
3 });
kb.add_rule({
3 ,
9 }, {
3 });
kb.add_rule({
3 ,
10 }, {
3 ,
0 });
kb.add_rule({
0 ,
0 ,
0 }, {
0 });
kb.add_rule({
0 ,
0 ,
1 }, {
1 });
kb.add_rule({
0 ,
0 ,
3 }, {
3 });
kb.add_rule({
0 ,
1 ,
3 }, {
1 ,
3 });
kb.add_rule({
1 ,
0 ,
0 }, {
1 });
kb.add_rule({
1 ,
0 ,
3 }, {
0 ,
3 });
kb.add_rule({
3 ,
0 ,
0 }, {
3 });
kb.add_rule({
0 ,
1 ,
0 ,
1 }, {
1 ,
0 ,
1 });
kb.add_rule({
0 ,
3 ,
0 ,
3 }, {
3 ,
0 ,
3 });
kb.add_rule({
1 ,
0 ,
1 ,
0 }, {
1 ,
0 ,
1 });
kb.add_rule({
1 ,
3 ,
0 ,
1 }, {
1 ,
0 ,
1 });
kb.add_rule({
1 ,
3 ,
0 ,
3 }, {
3 ,
0 ,
3 });
kb.add_rule({
3 ,
0 ,
1 ,
0 }, {
3 ,
0 ,
1 });
kb.add_rule({
3 ,
0 ,
3 ,
0 }, {
3 ,
0 ,
3 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
kbp.add_pair({
1 }, {
3 });
REQUIRE(kbp.number_of_classes() ==
3 );
REQUIRE(kbp.number_of_non_trivial_classes() ==
1 );
REQUIRE(std::vector<word_type>(kbp.cbegin_ntc()->begin(),
kbp.cbegin_ntc()->end())
== std::vector<word_type>({{
1 },
{
3 },
{
0 ,
1 },
{
0 ,
3 },
{
1 ,
0 },
{
3 ,
0 },
{
1 ,
3 },
{
0 ,
1 ,
0 },
{
0 ,
3 ,
0 },
{
1 ,
0 ,
1 },
{
3 ,
0 ,
1 },
{
3 ,
0 ,
3 },
{
1 ,
3 ,
0 },
{
0 ,
3 ,
0 ,
1 }}));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
5 }));
REQUIRE(kbp.word_to_class_index({
0 }) == kbp.word_to_class_index({
10 }));
REQUIRE(kbp.word_to_class_index({
1 }) == kbp.word_to_class_index({
2 }));
REQUIRE(kbp.word_to_class_index({
1 }) == kbp.word_to_class_index({
7 }));
REQUIRE(kbp.word_to_class_index({
3 }) == kbp.word_to_class_index({
4 }));
REQUIRE(kbp.word_to_class_index({
3 }) == kbp.word_to_class_index({
6 }));
REQUIRE(kbp.word_to_class_index({
3 }) == kbp.word_to_class_index({
8 }));
REQUIRE(kbp.word_to_class_index({
3 }) == kbp.word_to_class_index({
9 }));
}
LIBSEMIGROUPS_TEST_CASE(
"KnuthBendixCongruenceByPairs" ,
"028" ,
"infinite fp semigroup with infinite classes" ,
"[quick][kbp][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
fpsemigroup::KnuthBendix kb;
kb.set_alphabet(
2 );
kb.add_rule({
0 ,
0 ,
0 }, {
0 });
kb.add_rule({
0 ,
1 }, {
1 ,
0 });
kb.add_rule({
0 }, {
0 ,
0 });
KnuthBendixCongruenceByPairs kbp(twosided, kb);
word_type x
= {
0 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 };
word_type y
= {
0 ,
0 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 ,
1 };
REQUIRE(kbp.contains(x, y));
REQUIRE(kbp.contains({
0 ,
0 }, {
0 }));
REQUIRE(!kbp.contains({
1 }, {
0 }));
REQUIRE(kbp.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"FpSemigroupByPairs" ,
"029" ,
"(fpsemi) 2-sided congruence on finite semigroup" ,
"[quick][fpsemi][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens
= {Transf<>({
1 ,
3 ,
4 ,
2 ,
3 }), Transf<>({
3 ,
2 ,
1 ,
3 ,
3 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that FpSemigroupByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size() == 88);
// REQUIRE(S.number_of_rules() == 18);
FpSemigroupByPairs<decltype(S)> p(S);
p.add_rule(word_type({
0 ,
1 ,
0 ,
0 ,
0 ,
1 ,
1 ,
0 ,
0 }),
word_type({
1 ,
0 ,
0 ,
0 ,
1 }));
REQUIRE(p.equal_to(word_type({
0 ,
0 ,
0 ,
1 }), word_type({
0 ,
0 ,
1 ,
0 ,
0 })));
// REQUIRE(p.finished());
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.size() ==
21 );
REQUIRE(p.size() ==
21 );
// number_of_classes requires p.parent_froidure_pin().size();
REQUIRE(!S.started());
// p copies S
REQUIRE(!S.finished());
}
LIBSEMIGROUPS_TEST_CASE(
"FpSemigroupByPairs" ,
"030" ,
"(fpsemi) 2-sided congruence on finite semigroup" ,
"[quick][fpsemi][cong-pair]" ) {
auto rg = ReportGuard(REPORT);
std::vector<Transf<>> gens
= {Transf<>({
1 ,
3 ,
4 ,
2 ,
3 }), Transf<>({
3 ,
2 ,
1 ,
3 ,
3 })};
FroidurePin<Transf<>> S(gens);
// The following lines are intentionally commented out so that we can
// check that FpSemigroupByPairs does not enumerate the semigroup, they
// remain to remind us of the size and number of rules of the semigroups.
// REQUIRE(S.size() == 88);
// REQUIRE(S.number_of_rules() == 18);
FpSemigroupByPairs<decltype(S)> p(S);
p.add_rule(word_type({
0 ,
1 ,
0 ,
0 ,
0 ,
1 ,
1 ,
0 ,
0 }),
word_type({
1 ,
0 ,
0 ,
0 ,
1 }));
REQUIRE(p.equal_to(word_type({
0 ,
0 ,
0 ,
1 }), word_type({
0 ,
0 ,
1 ,
0 ,
0 })));
REQUIRE(!p.finished());
REQUIRE(!S.started());
REQUIRE(!S.finished());
REQUIRE(p.size() ==
21 );
REQUIRE(p.size() ==
21 );
REQUIRE(!S.finished());
// S is copied into p
}
// This test is commented out because it does not and should not compile,
// the FpSemigroupByPairs class requires a base semigroup over which to
// compute, in the example below there is no such base semigroup.
//
// LIBSEMIGROUPS_TEST_CASE("FpSemigroupByPairs", "031",
// "infinite fp semigroup from GAP library ",
// "[quick][fpsemi][cong-pair]") {
// auto rg = ReportGuard(REPORT);
// FpSemigroupByPairs<decltype(S)::element_type> p;
// p.set_alphabet(2);
// p.add_rule(word_type({0, 0}), word_type({0, 0}));
// p.add_rule(word_type({0, 1}), {1, 0});
// p.add_rule(word_type({0, 2}), {2, 0});
// p.add_rule(word_type({0, 0}), {0});
// p.add_rule(word_type({0, 2}), {0});
// p.add_rule(word_type({2, 0}), {0});
// p.add_rule(word_type({1, 0}), {0, 1});
// p.add_rule(word_type({1, 1}), {1, 1});
// p.add_rule(word_type({1, 2}), {2, 1});
// p.add_rule(word_type({1, 1, 1}), {1});
// p.add_rule(word_type({1, 2}), word_type({1}));
// p.add_rule(word_type({2, 1}), word_type({1}));
// p.add_rule(word_type({0}), word_type({1}));
// REQUIRE(!p.finished());
// }
}
// namespace libsemigroups
Messung V0.5 in Prozent C=88 H=97 G=92
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-06-06)
¤
*© Formatika GbR, Deutschland