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