//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2022 James D. Mitchell
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
#include <cstddef>
// for size_t
#include <stdexcept>
// for runtime_error
#include <unordered_set>
// for unordered_set
#include <vector>
// for vector
#include "catch.hpp" // for REQUIRE, REQUIRE_THROWS_AS, REQUI...
#include "libsemigroups/bipart.hpp" // for Bipartition
#include "libsemigroups/containers.hpp" // for StaticVector1
#include "libsemigroups/froidure-pin.hpp" // for FroidurePin
#include "libsemigroups/knuth-bendix.hpp" // for redundant_rule
#include "libsemigroups/make-present.hpp" // for make
#include "libsemigroups/present.hpp" // for Presentation
#include "libsemigroups/types.hpp" // for word_type
#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE
namespace libsemigroups {
namespace {
template <
typename W>
void check_make_from_froidure_pin() {
FroidurePin<Bipartition> S;
S.add_generator(Bipartition({{
1, -
1}, {
2, -
2}, {
3, -
3}, {
4, -
4}}));
S.add_generator(Bipartition({{
1, -
2}, {
2, -
3}, {
3, -
4}, {
4, -
1}}));
S.add_generator(Bipartition({{
1, -
2}, {
2, -
1}, {
3, -
3}, {
4, -
4}}));
S.add_generator(Bipartition({{
1,
2}, {
3, -
3}, {
4, -
4}, {-
1, -
2}}));
REQUIRE(S.size() ==
105);
auto p = make<Presentation<W>>(S);
REQUIRE(p.alphabet().size() ==
4);
REQUIRE(p.rules.size() ==
86);
REQUIRE(presentation::length(p) ==
359);
REQUIRE(std::max_element(p.rules.cbegin(),
p.rules.cend(),
[](
auto const& x,
auto const& y) {
return x.size() < y.size();
})
->size()
==
8);
p.validate();
}
template <
typename W1,
typename W2>
void check_make_from_presentation() {
Presentation<W1> p;
p.alphabet(
2);
p.contains_empty_word(
false);
presentation::add_rule(p, {
0,
1,
2}, {
0,
1});
presentation::add_rule(p, {
0,
1,
2}, {});
// intentionally bad
REQUIRE_THROWS_AS(p.validate(), LibsemigroupsException);
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p),
LibsemigroupsException);
p.alphabet_from_rules();
REQUIRE(p.alphabet() == W1({
0,
1,
2}));
p.validate();
REQUIRE(p.contains_empty_word());
auto q = make<Presentation<W2>>(p);
REQUIRE(q.contains_empty_word());
q.validate();
}
}
// namespace
struct LibsemigroupsException;
// forward decl
using detail::StaticVector1;
LIBSEMIGROUPS_TEST_CASE(
"make",
"000",
"from FroidurePin",
"[quick][make]") {
auto rg = ReportGuard(
false);
check_make_from_froidure_pin<word_type>();
check_make_from_froidure_pin<StaticVector1<uint16_t,
8>>();
check_make_from_froidure_pin<std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"make",
"001",
"from FroidurePin and alphabet",
"[quick][make]") {
FroidurePin<Bipartition> S;
S.add_generator(Bipartition({{
1, -
1}, {
2, -
2}, {
3, -
3}, {
4, -
4}}));
S.add_generator(Bipartition({{
1, -
2}, {
2, -
3}, {
3, -
4}, {
4, -
1}}));
S.add_generator(Bipartition({{
1, -
2}, {
2, -
1}, {
3, -
3}, {
4, -
4}}));
S.add_generator(Bipartition({{
1,
2}, {
3, -
3}, {
4, -
4}, {-
1, -
2}}));
REQUIRE(S.size() ==
105);
// Alphabet too small
REQUIRE_THROWS_AS(make(S,
"abc"), LibsemigroupsException);
// Alphabet contains repeats
REQUIRE_THROWS_AS(make(S,
"abca"), LibsemigroupsException);
// Alphabet too long
REQUIRE_THROWS_AS(make(S,
"abcde"), LibsemigroupsException);
auto p = make(S,
"abcd");
REQUIRE(p.alphabet().size() ==
4);
REQUIRE(p.rules[
8] ==
"ba");
REQUIRE(p.rules[
9] ==
"b");
REQUIRE(presentation::longest_common_subword(p) ==
"bcb");
}
LIBSEMIGROUPS_TEST_CASE(
"make",
"002",
"presentation from presentation",
"[quick][make]") {
check_make_from_presentation<std::string, word_type>();
check_make_from_presentation<std::string, StaticVector1<uint8_t,
3>>();
check_make_from_presentation<word_type, std::string>();
check_make_from_presentation<word_type, StaticVector1<uint8_t,
3>>();
check_make_from_presentation<StaticVector1<uint8_t,
3>, word_type>();
check_make_from_presentation<StaticVector1<uint8_t,
3>, std::string>();
}
LIBSEMIGROUPS_TEST_CASE(
"make",
"003",
"presentation from presentation and alphabet",
"[quick][make]") {
Presentation<word_type> p;
p.alphabet(
2);
p.contains_empty_word(
false);
presentation::add_rule(p, {
0,
1,
2}, {
0,
1});
presentation::add_rule(p, {
0,
1,
2}, {});
// intentionally bad
REQUIRE_THROWS_AS(p.validate(), LibsemigroupsException);
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p),
LibsemigroupsException);
p.alphabet_from_rules();
REQUIRE(p.alphabet() == word_type({
0,
1,
2}));
p.validate();
REQUIRE(p.contains_empty_word());
auto q = make<Presentation<std::string>>(p,
"abc");
REQUIRE(q.alphabet() ==
"abc");
REQUIRE(q.contains_empty_word());
REQUIRE(q.rules == std::vector<std::string>({
"abc",
"ab",
"abc",
""}));
q.validate();
}
LIBSEMIGROUPS_TEST_CASE(
"make",
"004",
"argument checks issue",
"[quick][presentation]") {
Presentation<word_type> p;
p.contains_empty_word(
true);
presentation::add_rule(p, {
0,
0}, {
1});
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p,
"a"),
LibsemigroupsException);
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p,
"aa"),
LibsemigroupsException);
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p,
"aaa"),
LibsemigroupsException);
// Alphabet not set for p, so can't know if the 2nd arg has the correct
// size or not.
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p,
"ab"),
LibsemigroupsException);
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p,
"abc"),
LibsemigroupsException);
p.alphabet_from_rules();
REQUIRE_NOTHROW(make<Presentation<std::string>>(p,
"ab"));
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p,
"a"),
LibsemigroupsException);
REQUIRE_THROWS_AS(make<Presentation<std::string>>(p,
"aa"),
LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE(
"make",
"005",
"use human readable alphabet for make",
"[quick][presentation]") {
Presentation<word_type> p;
p.alphabet(
2);
p.contains_empty_word(
true);
presentation::add_rule_and_check(p, {
0,
1}, {});
auto q = make<Presentation<std::string>>(p);
REQUIRE(q.alphabet() ==
"ab");
REQUIRE(q.rules == std::vector<std::string>({
"ab",
""}));
q = make<Presentation<std::string>>(p,
"xy");
REQUIRE(q.alphabet() ==
"xy");
REQUIRE(q.rules == std::vector<std::string>({
"xy",
""}));
}
}
// namespace libsemigroups