//
// 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 "catch.hpp"      // for REQUIRE, REQUIRE_THROWS_AS, REQUI...
#include "test-main.hpp"  // for LIBSEMIGROUPS_TEST_CASE

#include "libsemigroups/bipart.hpp"        // for Bipartition
#include "libsemigroups/containers.hpp"    // for StaticVector1
#include "libsemigroups/froidure-pin.hpp"  // for FroidurePin
#include "libsemigroups/int-range.hpp"     // for IntegralRange
#include "libsemigroups/knuth-bendix.hpp"  // for redundant_rule
#include "libsemigroups/make-present.hpp"  // for make
#include "libsemigroups/present.hpp"       // for Presentation
#include "libsemigroups/siso.hpp"          // for Sislo
#include "libsemigroups/types.hpp"         // for word_type

namespace libsemigroups {

  struct LibsemigroupsException;  // forward decl

  namespace {
    template <typename W>
    void check_constructors(Presentation<W>& p) {
      p.validate();
      Presentation<W> pp(p);
      pp.validate();
      REQUIRE(pp.alphabet() == p.alphabet());
      REQUIRE(pp.rules == p.rules);

      Presentation<W> q(std::move(p));
      q.validate();
      REQUIRE(q.alphabet() == pp.alphabet());
      REQUIRE(q.rules == pp.rules);

      p = q;
      p.validate();
      REQUIRE(q.alphabet() == p.alphabet());
      REQUIRE(q.rules == p.rules);

      p = std::move(q);
      p.validate();
      REQUIRE(pp.alphabet() == p.alphabet());
      REQUIRE(pp.rules == p.rules);
    }

    template <typename W>
    void check_alphabet_letters() {
      Presentation<W> p;
      p.alphabet({0, 1, 2});
      REQUIRE(p.alphabet() == W({0, 1, 2}));
      REQUIRE(p.letter(0) == 0);
      REQUIRE(p.letter(1) == 1);
      REQUIRE(p.letter(2) == 2);
      p.alphabet(4);
      REQUIRE(p.alphabet() == W({0, 1, 2, 3}));
      p.validate();
      REQUIRE_THROWS_AS(p.alphabet({0, 1, 1}), LibsemigroupsException);

      presentation::add_rule(p, {0, 1, 2, 1}, {0, 0});
      presentation::add_rule(p, {4, 1}, {0, 5});
      presentation::add_rule(p, {4, 1}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1});
      p.alphabet_from_rules();
      REQUIRE(p.alphabet() == W({0, 1, 2, 4, 5}));
      REQUIRE(p.index(0) == 0);
      REQUIRE(p.index(1) == 1);
      REQUIRE(p.index(2) == 2);
      REQUIRE(p.index(4) == 3);
      REQUIRE(p.index(5) == 4);

      REQUIRE(!p.contains_empty_word());
      presentation::add_rule(p, {4, 1}, {});
      p.alphabet_from_rules();
      REQUIRE(p.contains_empty_word());

      p.alphabet({0, 1, 2, 3});
      REQUIRE(p.alphabet() == W({0, 1, 2, 3}));
    }

    template <typename W>
    void check_contains_empty_word() {
      Presentation<W> p;
      REQUIRE(!p.contains_empty_word());
      p.contains_empty_word(true);
      REQUIRE(p.contains_empty_word());
      p.contains_empty_word(false);
      REQUIRE(!p.contains_empty_word());
    }

    template <typename W>
    void check_validate_rules_throws() {
      Presentation<W> p;
      p.rules.emplace_back();
      REQUIRE_THROWS_AS(p.validate_rules(), LibsemigroupsException);
    }

    template <typename W>
    void check_add_rules() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 1, 2, 1}, {0, 0});
      Presentation<W> q;
      presentation::add_rule(q, {4, 1}, {0, 5});
      presentation::add_rule(q, {4, 1}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1});
      presentation::add_rules(p, q);
      REQUIRE(p.rules
              == std::vector<W>({{0, 1, 2, 1},
                                 {0, 0},
                                 {4, 1},
                                 {0, 5},
                                 {4, 1},
                                 {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}));
      REQUIRE(q.rules
              == std::vector<W>(
                  {{4, 1}, {0, 5}, {4, 1}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}));
      REQUIRE_THROWS_AS(p.validate(), LibsemigroupsException);
      REQUIRE_THROWS_AS(q.validate(), LibsemigroupsException);
    }

    template <typename W>
    void check_add_identity_rules() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 1, 2, 1}, {0, 0});
      REQUIRE_THROWS_AS(presentation::add_identity_rules(p, 0),
                        LibsemigroupsException);
      p.alphabet_from_rules();
      presentation::add_identity_rules(p, 0);
      REQUIRE(p.rules
              == std::vector<W>({{0, 1, 2, 1},
                                 {0, 0},
                                 {0, 0},
                                 {0},
                                 {1, 0},
                                 {1},
                                 {0, 1},
                                 {1},
                                 {2, 0},
                                 {2},
                                 {0, 2},
                                 {2}}));
    }

    template <typename W>
    void check_add_zero_rules() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 1, 2, 1}, {0, 0});
      REQUIRE_THROWS_AS(presentation::add_zero_rules(p, 0),
                        LibsemigroupsException);
      p.alphabet_from_rules();
      presentation::add_zero_rules(p, 0);
      REQUIRE(p.rules
              == std::vector<W>({{0, 1, 2, 1},
                                 {0, 0},
                                 {0, 0},
                                 {0},
                                 {1, 0},
                                 {0},
                                 {0, 1},
                                 {0},
                                 {2, 0},
                                 {0},
                                 {0, 2},
                                 {0}}));
    }

    template <typename W>
    void check_add_inverse_rules() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 1, 2, 1}, {0, 0});
      p.alphabet_from_rules();

      REQUIRE_THROWS_AS(presentation::add_inverse_rules(p, {0, 1, 1}, 0),
                        LibsemigroupsException);
      REQUIRE_THROWS_AS(presentation::add_inverse_rules(p, {1, 2, 0}, 0),
                        LibsemigroupsException);
      p.alphabet({0, 1, 2, 3});
      REQUIRE_THROWS_AS(presentation::add_inverse_rules(p, {0, 2, 3, 1}, 0),
                        LibsemigroupsException);
      REQUIRE_THROWS_AS(presentation::add_inverse_rules(p, {0, 2, 1}, 0),
                        LibsemigroupsException);
      p.alphabet({0, 1, 2});
      presentation::add_inverse_rules(p, {0, 2, 1}, 0);

      REQUIRE(
          p.rules
          == std::vector<W>({{0, 1, 2, 1}, {0, 0}, {1, 2}, {0}, {2, 1}, {0}}));
      // When id is UNDEFINED
      presentation::add_inverse_rules(p, {0, 2, 1});
      REQUIRE(p.rules
              == std::vector<W>({{0, 1, 2, 1},
                                 {0, 0},
                                 {1, 2},
                                 {0},
                                 {2, 1},
                                 {0},
                                 {0, 0},
                                 {},
                                 {1, 2},
                                 {},
                                 {2, 1},
                                 {}}));
    }

    template <typename W>
    void check_remove_duplicate_rules() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::remove_duplicate_rules(p),
                        LibsemigroupsException);
      p.rules.push_back(W({0, 0}));
      presentation::add_rule(p, {0, 0}, {0, 1, 2, 1});
      p.alphabet_from_rules();
      REQUIRE(p.rules.size() == 4);
      presentation::remove_duplicate_rules(p);
      REQUIRE(p.rules.size() == 2);
    }

    template <typename W>
    void check_reduce_complements() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::reduce_complements(p),
                        LibsemigroupsException);
      p.rules.push_back(W({1, 2, 1}));

      presentation::add_rule(p, {1, 2, 1}, {1, 1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      p.alphabet_from_rules();
      presentation::reduce_complements(p);
      presentation::sort_each_rule(p);
      presentation::sort_rules(p);
      REQUIRE(p.rules
              == std::vector<W>({{1, 1},
                                 {0},
                                 {1, 2, 1},
                                 {0},
                                 {0, 1, 2, 1},
                                 {0},
                                 {1, 1, 2, 1},
                                 {0}}));
    }

    template <typename W>
    void check_sort_each_rule() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::sort_each_rule(p),
                        LibsemigroupsException);
      p.rules.push_back(W({1, 2, 1}));

      presentation::add_rule(p, {1, 2, 1}, {1, 1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      p.alphabet_from_rules();
      presentation::sort_each_rule(p);
      REQUIRE(p.rules
              == std::vector<W>({{0, 1, 2, 1},
                                 {1, 2, 1},
                                 {1, 1, 2, 1},
                                 {1, 2, 1},
                                 {1, 1, 2, 1},
                                 {1, 1},
                                 {1, 2, 1},
                                 {1, 1},
                                 {1, 2, 1},
                                 {0}}));
    }

    template <typename W>
    void check_sort_rules() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::sort_rules(p), LibsemigroupsException);
      p.rules.push_back(W({1, 2, 1}));
      presentation::add_rule(p, {1, 2, 1}, {1, 1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      p.alphabet_from_rules();
      presentation::sort_rules(p);
      REQUIRE(p.rules
              == std::vector<W>({{1, 2, 1},
                                 {0},
                                 {1, 1},
                                 {1, 2, 1},
                                 {1, 1, 2, 1},
                                 {1, 1},
                                 {0, 1, 2, 1},
                                 {1, 2, 1},
                                 {1, 2, 1},
                                 {1, 1, 2, 1}}));
      REQUIRE(presentation::are_rules_sorted(p));
    }

    template <typename W>
    void check_longest_common_subword() {
      {
        // Normalized alphabet
        Presentation<W> p;
        p.rules.push_back(W({0, 1, 2, 1}));
        REQUIRE_NOTHROW(presentation::longest_common_subword(p));
        p.rules.push_back(W({1, 2, 1}));
        presentation::add_rule(p, {1, 2, 1}, {1, 1, 2, 1});
        presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
        presentation::add_rule(p, {1, 1}, {1, 2, 1});
        presentation::add_rule(p, {1, 2, 1}, {0});
        p.alphabet_from_rules();
        REQUIRE(presentation::longest_common_subword(p) == W({1, 2, 1}));
        presentation::replace_subword(p, W({1, 2, 1}), W({3}));
        presentation::add_rule(p, {3}, {1, 2, 1});
        REQUIRE(p.rules
                == std::vector<W>({{0, 3},
                                   {3},
                                   {3},
                                   {1, 3},
                                   {1, 3},
                                   {1, 1},
                                   {1, 1},
                                   {3},
                                   {3},
                                   {0},
                                   {3},
                                   {1, 2, 1}}));
      }
      {
        // Non-normalized alphabet
        Presentation<W> p;
        presentation::add_rule(p, {1, 2, 4, 2}, {2, 4, 2});
        presentation::add_rule(p, {2, 4, 2}, {2, 2, 4, 2});
        presentation::add_rule(p, {2, 2, 4, 2}, {2, 2});
        presentation::add_rule(p, {2, 2}, {2, 4, 2});
        presentation::add_rule(p, {2, 4, 2}, {1});
        p.alphabet_from_rules();
        REQUIRE(presentation::longest_common_subword(p) == W({2, 4, 2}));
        presentation::replace_subword(p, W({2, 4, 2}), W({0}));
        presentation::add_rule(p, W({0}), W({2, 4, 2}));
        REQUIRE(p.rules
                == std::vector<W>({{1, 0},
                                   {0},
                                   {0},
                                   {2, 0},
                                   {2, 0},
                                   {2, 2},
                                   {2, 2},
                                   {0},
                                   {0},
                                   {1},
                                   {0},
                                   {2, 4, 2}}));
      }
    }

    template <typename W>
    void check_redundant_rule() {
      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(presentation::length(p) == 359);
      presentation::remove_duplicate_rules(p);
      REQUIRE(presentation::length(p) == 359);
      presentation::reduce_complements(p);
      REQUIRE(presentation::length(p) == 359);
      presentation::sort_each_rule(p);
      presentation::sort_rules(p);

      REQUIRE(presentation::length(p) == 359);
      REQUIRE(p.rules.size() == 86);

      p.alphabet_from_rules();
      auto it = presentation::redundant_rule(p, std::chrono::milliseconds(100));
      REQUIRE(*it == W({2, 1, 3, 1, 1, 2, 1, 2}));
      REQUIRE(*(it + 1) == W({1, 1, 2, 1, 3, 1, 2, 1}));
      p.rules.erase(it, it + 2);
      p.validate();
      // while (it != p.rules.cend()) { // Too time consuming and indeterminant
      //   REQUIRE(std::distance(it, p.rules.cend()) % 2 == 0);
      //   p.rules.erase(it, it + 2);
      //   p.validate();
      //   it = presentation::redundant_rule(p, std::chrono::milliseconds(8));
      // }
      REQUIRE(presentation::length(p) == 343);
      REQUIRE(p.rules.size() == 84);
    }

    template <typename W>
    void check_shortlex_compare_concat() {
      REQUIRE(detail::shortlex_compare_concat(
          W({0, 1, 2, 1}), W({0}), W({1, 1, 2, 1}), W({0})));
    }

    template <typename W>
    void check_remove_trivial_rules() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::remove_trivial_rules(p),
                        LibsemigroupsException);
      p.rules.push_back(W({1, 2, 1}));
      presentation::add_rule(p, {1, 2, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 2, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      presentation::add_rule(p, {0}, {0});
      presentation::add_rule(p, {1}, {1});
      presentation::add_rule(p, {2}, {2});

      presentation::remove_trivial_rules(p);
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{0, 1, 2, 1}, {1, 2, 1}, {1, 1, 2, 1}, {1, 1}, {1, 2, 1}, {0}}));
      presentation::remove_trivial_rules(p);
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{0, 1, 2, 1}, {1, 2, 1}, {1, 1, 2, 1}, {1, 1}, {1, 2, 1}, {0}}));
    }

    template <typename W>
    void check_replace_subword() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_NOTHROW(presentation::replace_subword(p, W({0}), W({1})));
      p.rules.push_back(W({1, 2, 1}));
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});

      presentation::replace_subword(p, W({0}), W({1}));
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{1, 1, 2, 1}, {1, 2, 1}, {1, 1, 2, 1}, {1, 1}, {1, 2, 1}, {1}}));

      presentation::replace_subword(p, W({0}), W({1}));
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{1, 1, 2, 1}, {1, 2, 1}, {1, 1, 2, 1}, {1, 1}, {1, 2, 1}, {1}}));

      presentation::replace_subword(p, W({1, 2, 1}), W({0}));
      REQUIRE(p.rules
              == std::vector<W>({{1, 0}, {0}, {1, 0}, {1, 1}, {0}, {1}}));

      presentation::replace_subword(p, W({42, 42}), W({0}));
      REQUIRE(p.rules
              == std::vector<W>({{1, 0}, {0}, {1, 0}, {1, 1}, {0}, {1}}));

      p.rules.clear();
      presentation::add_rule(
          p, {1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1}, {1, 2, 1, 1, 2, 1, 2, 1});
      presentation::replace_subword(p, W({1, 2, 1}), W({1}));
      REQUIRE(p.rules == std::vector<W>({{1, 2, 1, 1, 2, 1, 1}, {1, 1, 2, 1}}));
      presentation::replace_subword(p, W({1, 2, 1}), W({1}));
      REQUIRE(p.rules == std::vector<W>({{1, 1, 1}, {1, 1}}));
      // Test for when existing is a suffix of replacement
      p.rules.clear();
      presentation::add_rule(
          p, {1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1}, {1, 2, 1, 1, 2, 1, 2, 1});
      presentation::replace_subword(p, W({1, 2}), W({1, 1, 2}));
      REQUIRE(p.rules
              == std::vector<W>(
                  {{1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1},
                   {1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1}}));
    }

    template <typename W>
    void check_replace_word() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 1, 0}, {});
      p.alphabet_from_rules();
      presentation::replace_word(p, W({}), W({2}));
      REQUIRE(p.rules == std::vector<W>{{0, 1, 0}, {2}});

      p.rules.clear();
      presentation::add_rule(p, {0, 1, 0}, {2, 1});
      presentation::add_rule(p, {1, 1, 2}, {1, 2, 1});
      presentation::add_rule(p, {2, 1, 2, 1}, {2, 2});
      presentation::add_rule(p, {2, 1}, {0, 1, 1});
      p.alphabet_from_rules();
      presentation::replace_word(p, W({2, 1}), W({1, 2}));
      REQUIRE(p.rules
              == std::vector<W>{{0, 1, 0},
                                {1, 2},
                                {1, 1, 2},
                                {1, 2, 1},
                                {2, 1, 2, 1},
                                {2, 2},
                                {1, 2},
                                {0, 1, 1}});

      p.rules.clear();
      presentation::add_rule(p, {0, 1, 0}, {1, 0, 1});
      presentation::add_rule(p, {0, 1, 1}, {1, 0, 1, 0});
      p.alphabet_from_rules();
      presentation::replace_word(p, W({1, 0, 1}), W({}));
      REQUIRE(p.rules
              == std::vector<W>{{0, 1, 0}, {}, {0, 1, 1}, {1, 0, 1, 0}});
    }

    template <typename W>
    void check_longest_rule() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::longest_rule(p), LibsemigroupsException);
      p.rules.push_back(W({1, 2, 1}));
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      REQUIRE(*presentation::longest_rule(p) == W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(*presentation::longest_rule(
                            presentation::longest_rule(p) + 1, p.rules.cend()),
                        LibsemigroupsException);
      REQUIRE(*presentation::longest_rule(presentation::longest_rule(p) + 2,
                                          p.rules.cend())
              == W({1, 1, 2, 1}));
      REQUIRE(*presentation::shortest_rule(p) == W({1, 2, 1}));
      REQUIRE(*presentation::shortest_rule(p.rules.cbegin(),
                                           presentation::shortest_rule(p))
              == W({1, 1, 2, 1}));
      REQUIRE_THROWS_AS(
          *presentation::shortest_rule(p.rules.cbegin(),
                                       presentation::shortest_rule(p) - 1),
          LibsemigroupsException);
    }

    template <typename W>
    void check_longest_rule_length() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::longest_rule_length(p),
                        LibsemigroupsException);
      p.rules.push_back(W({1, 2, 1}));
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      REQUIRE(presentation::longest_rule_length(p) == 7);
      REQUIRE_THROWS_AS(presentation::longest_rule_length(
                            presentation::longest_rule(p) + 1, p.rules.cend()),
                        LibsemigroupsException);
      REQUIRE(presentation::longest_rule_length(
                  presentation::longest_rule(p) + 2, p.rules.cend())
              == 6);

      REQUIRE(presentation::shortest_rule_length(p) == 4);
      REQUIRE_THROWS_AS(presentation::shortest_rule_length(
                            presentation::shortest_rule(p) + 1, p.rules.cend()),
                        LibsemigroupsException);
      REQUIRE(presentation::shortest_rule_length(p.rules.cbegin(),
                                                 p.rules.cend() - 2)
              == 6);
    }

    template <typename W>
    void check_remove_redundant_generators() {
      Presentation<W> p;
      p.rules.push_back(W({0, 1, 2, 1}));
      REQUIRE_THROWS_AS(presentation::remove_redundant_generators(p),
                        LibsemigroupsException);
      p.rules.push_back(W({1, 2, 1}));

      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});

      presentation::remove_redundant_generators(p);
      REQUIRE(p.rules
              == std::vector<W>(
                  {{1, 2, 1, 1, 2, 1}, {1, 2, 1}, {1, 1, 2, 1}, {1, 1}}));
      presentation::remove_redundant_generators(p);
      REQUIRE(p.rules
              == std::vector<W>(
                  {{1, 2, 1, 1, 2, 1}, {1, 2, 1}, {1, 1, 2, 1}, {1, 1}}));

      p.rules.clear();
      presentation::add_rule(p, {0, 1, 2, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1}, {0});
      presentation::add_rule(p, {1, 2, 1}, {0});
      presentation::remove_redundant_generators(p);
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{0, 0, 2, 0}, {0, 2, 0}, {0, 0, 2, 0}, {0, 0}, {0, 2, 0}, {0}}));

      p.rules.clear();
      presentation::add_rule(p, {0, 1, 2, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {0}, {1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      presentation::remove_redundant_generators(p);
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{0, 0, 2, 0}, {0, 2, 0}, {0, 0, 2, 0}, {0, 0}, {0, 2, 0}, {0}}));

      p.rules.clear();
      presentation::add_rule(p, {0, 1, 2, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1}, {0});
      presentation::add_rule(p, {1, 2, 1}, {0});
      presentation::remove_redundant_generators(p);
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{0, 0, 2, 0}, {0, 2, 0}, {0, 0, 2, 0}, {0, 0}, {0, 2, 0}, {0}}));
    }

    template <typename W>
    void check_reverse() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 1, 2, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});

      presentation::reverse(p);
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{1, 2, 1, 0}, {1, 2, 1}, {1, 2, 1, 1}, {1, 1}, {1, 2, 1}, {0}}));

      presentation::reverse(p);
      REQUIRE(
          p.rules
          == std::vector<W>(
              {{0, 1, 2, 1}, {1, 2, 1}, {1, 1, 2, 1}, {1, 1}, {1, 2, 1}, {0}}));
    }

    template <typename W>
    void check_in_alphabet() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 1, 2, 1}, {1, 2, 1});
      presentation::add_rule(p, {1, 1, 2, 1}, {1, 1});
      presentation::add_rule(p, {1, 2, 1}, {0});
      // Alphabet not set, so everything false
      REQUIRE(!p.in_alphabet(0));
      REQUIRE(!p.in_alphabet(1));
      REQUIRE(!p.in_alphabet(2));
      REQUIRE(!p.in_alphabet(3));
      REQUIRE(!p.in_alphabet(42));

      p.alphabet_from_rules();
      REQUIRE(p.in_alphabet(0));
      REQUIRE(p.in_alphabet(1));
      REQUIRE(p.in_alphabet(2));
      REQUIRE(!p.in_alphabet(3));
      REQUIRE(!p.in_alphabet(42));
    }

    template <typename W>
    void check_make_semigroup() {
      Presentation<W> p;
      presentation::add_rule(p, {0, 0}, {});
      presentation::add_rule(p, {1, 1}, {});
      presentation::add_rule(p, {2, 2}, {});
      presentation::add_rule(p, {0, 1, 0, 1, 0, 1}, {});
      presentation::add_rule(p, {1, 2, 1, 0, 1, 2, 1, 0}, {});
      presentation::add_rule(p, {2, 0, 2, 1, 2, 0, 2, 1}, {0, 3});

      p.alphabet_from_rules();
      auto e = presentation::make_semigroup(p);
      REQUIRE(p.rules
              == std::vector<W>({{0, 0},
                                 {e},
                                 {1, 1},
                                 {e},
                                 {2, 2},
                                 {e},
                                 {0, 1, 0, 1, 0, 1},
                                 {e},
                                 {1, 2, 1, 0, 1, 2, 1, 0},
                                 {e},
                                 {2, 0, 2, 1, 2, 0, 2, 1},
                                 {0, 3},
                                 {0, e},
                                 {0},
                                 {e, 0},
                                 {0},
                                 {1, e},
                                 {1},
                                 {e, 1},
                                 {1},
                                 {2, e},
                                 {2},
                                 {e, 2},
                                 {2},
                                 {3, e},
                                 {3},
                                 {e, 3},
                                 {3},
                                 {e, e},
                                 {e}}));
      REQUIRE(presentation::make_semigroup(p) == UNDEFINED);
    }

  }  // namespace

  using detail::StaticVector1;

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "000",
                          "vectors of ints",
                          "[quick][presentation]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.alphabet({0, 1, 2});
    REQUIRE(p.alphabet() == word_type({0, 1, 2}));
    REQUIRE_THROWS_AS(p.alphabet({0, 0}), LibsemigroupsException);
    REQUIRE(p.alphabet() == word_type({0, 1, 2}));
    presentation::add_rule(p, {0, 0, 0}, {0});
    REQUIRE(std::distance(p.rules.cbegin(), p.rules.cend()) == 2);
    REQUIRE(std::vector<word_type>(p.rules.cbegin(), p.rules.cend())
            == std::vector<word_type>({{0, 0, 0}, {0}}));
    presentation::add_rule_and_check(p, {0, 0, 0}, {0});
    REQUIRE_THROWS_AS(presentation::add_rule_and_check(p, {0, 5, 0}, {0}),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(presentation::add_rule_and_check(p, {}, {0}),
                      LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "001",
                          "strings",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.alphabet("abc");
    REQUIRE(p.alphabet() == "abc");
    REQUIRE_THROWS_AS(p.alphabet("aa"), LibsemigroupsException);
    REQUIRE(p.alphabet() == "abc");
    presentation::add_rule(p, "aaa", "a");
    REQUIRE(std::distance(p.rules.cbegin(), p.rules.cend()) == 2);
    REQUIRE(std::vector<std::string>(p.rules.cbegin(), p.rules.cend())
            == std::vector<std::string>({"aaa", "a"}));
    REQUIRE_THROWS_AS(presentation::add_rule_and_check(p, "abz", "a"),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(presentation::add_rule_and_check(p, "", "a"),
                      LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "002",
                          "constructors (word_type)",
                          "[quick][presentation]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.alphabet({0, 1, 2});
    presentation::add_rule(p, {0, 0, 0}, {0});
    REQUIRE(p.rules.size() == 2);
    presentation::add_rule_and_check(p, {0, 0, 0}, {0});
    p.validate();
    check_constructors(p);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "003",
                          "constructors (StaticVector1)",
                          "[quick][presentation]") {
    auto                                      rg = ReportGuard(false);
    Presentation<StaticVector1<uint16_t, 16>> p;
    p.alphabet({0, 1, 2});
    presentation::add_rule(p, {0, 0, 0}, {0});
    REQUIRE(p.rules.size() == 2);
    presentation::add_rule_and_check(p, {0, 0, 0}, {0});
    p.validate();
    check_constructors(p);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "004",
                          "constructors (std::string)",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.alphabet("abc");
    presentation::add_rule(p, "aaaa", "aa");
    REQUIRE(p.rules.size() == 2);
    presentation::add_rule_and_check(p, "aaa", "aa");
    p.validate();
    check_constructors(p);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "005",
                          "alphabet + letters (word_type)",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_alphabet_letters<word_type>();
    check_alphabet_letters<StaticVector1<uint16_t, 16>>();
    check_alphabet_letters<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "006",
                          "alphabet + letters (std::string)",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.alphabet("abc");
    REQUIRE(p.alphabet() == "abc");
    REQUIRE(p.letter(0) == 'a');
    REQUIRE(p.letter(1) == 'b');
    REQUIRE(p.letter(2) == 'c');
    p.alphabet(4);
    REQUIRE(p.alphabet().size() == 4);
    p.validate();
    REQUIRE_THROWS_AS(p.alphabet("abb"), LibsemigroupsException);

    presentation::add_rule(p, "abca", "aa");
    presentation::add_rule(p, "eb", "af");
    presentation::add_rule(p, "eb", "abbbbbb");
    p.alphabet_from_rules();
    REQUIRE(p.alphabet() == "abcef");
    REQUIRE(p.index('a') == 0);
    REQUIRE(p.index('b') == 1);
    REQUIRE(p.index('c') == 2);
    REQUIRE(p.index('e') == 3);
    REQUIRE(p.index('f') == 4);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "007",
                          "contains_empty_word",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_contains_empty_word<word_type>();
    check_contains_empty_word<StaticVector1<uint16_t, 16>>();
    check_contains_empty_word<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "008",
                          "validate_rules throws",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_validate_rules_throws<word_type>();
    check_validate_rules_throws<StaticVector1<uint16_t, 16>>();
    check_validate_rules_throws<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "009",
                          "helpers add_rule(s)",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_add_rules<word_type>();
    check_add_rules<StaticVector1<uint16_t, 10>>();
    check_add_rules<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "010",
                          "helpers add_rule(s) (std::string)",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    presentation::add_rule(p, "abcb", "aa");
    Presentation<std::string> q;
    presentation::add_rule(q, "eb", "af");
    presentation::add_rule(q, "eb", "abbbbbbbbb");
    presentation::add_rules(p, q);
    REQUIRE(p.rules
            == std::vector<std::string>(
                {"abcb", "aa", "eb", "af", "eb", "abbbbbbbbb"}));
    REQUIRE(q.rules
            == std::vector<std::string>({"eb", "af", "eb", "abbbbbbbbb"}));
    REQUIRE_THROWS_AS(p.validate(), LibsemigroupsException);
    REQUIRE_THROWS_AS(q.validate(), LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE(
      "Presentation",
      "011",
      "helpers add_identity_rules (std::vector/StaticVector1)",
      "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_add_identity_rules<word_type>();
    check_add_identity_rules<StaticVector1<uint16_t, 10>>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "035",
                          "helpers add_zero_rules (std::vector/StaticVector1)",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_add_zero_rules<word_type>();
    check_add_zero_rules<StaticVector1<uint16_t, 10>>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "012",
                          "helpers add_identity_rules (std::string)",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    presentation::add_rule(p, "abcb", "aa");
    REQUIRE_THROWS_AS(presentation::add_identity_rules(p, 'a'),
                      LibsemigroupsException);
    p.alphabet_from_rules();
    presentation::add_identity_rules(p, 'a');
    REQUIRE(p.rules
            == std::vector<std::string>({"abcb",
                                         "aa",
                                         "aa",
                                         "a",
                                         "ba",
                                         "b",
                                         "ab",
                                         "b",
                                         "ca",
                                         "c",
                                         "ac",
                                         "c"}));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "036",
                          "helpers add_zero_rules (std::string)",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    presentation::add_rule(p, "abcb", "aa");
    REQUIRE_THROWS_AS(presentation::add_zero_rules(p, '0'),
                      LibsemigroupsException);
    p.alphabet("abc0");
    presentation::add_zero_rules(p, '0');
    REQUIRE(p.rules
            == std::vector<std::string>({"abcb",
                                         "aa",
                                         "a0",
                                         "0",
                                         "0a",
                                         "0",
                                         "b0",
                                         "0",
                                         "0b",
                                         "0",
                                         "c0",
                                         "0",
                                         "0c",
                                         "0",
                                         "00",
                                         "0"}));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "013",
                          "helpers add_inverse_rules (all)",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_add_inverse_rules<word_type>();
    check_add_inverse_rules<StaticVector1<uint16_t, 10>>();
    check_add_inverse_rules<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "014",
                          "helpers add_inverse_rules (std::string)",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.contains_empty_word(false);

    p.alphabet("aAbBcCe");
    presentation::add_identity_rules(p, 'e');

    presentation::add_inverse_rules(p, "AaBbCce", 'e');
    presentation::add_rule_and_check(p, "aaCac", "e");
    presentation::add_rule_and_check(p, "acbbACb", "e");
    presentation::add_rule_and_check(p, "ABabccc", "e");
    REQUIRE(
        p.rules
        == std::vector<std::string>(
            {"ae", "a",  "ea",    "a",  "Ae",      "A",  "eA",      "A",  "be",
             "b",  "eb", "b",     "Be", "B",       "eB", "B",       "ce", "c",
             "ec", "c",  "Ce",    "C",  "eC",      "C",  "ee",      "e",  "aA",
             "e",  "Aa", "e",     "bB", "e",       "Bb", "e",       "cC", "e",
             "Cc", "e",  "aaCac", "e",  "acbbACb", "e",  "ABabccc", "e"}));
    REQUIRE(!presentation::are_rules_sorted(p));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "015",
                          "helpers remove_duplicate_rules",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_remove_duplicate_rules<word_type>();
    check_remove_duplicate_rules<StaticVector1<uint16_t, 10>>();
    check_remove_duplicate_rules<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "016",
                          "helpers reduce_complements",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_reduce_complements<word_type>();
    check_reduce_complements<StaticVector1<uint16_t, 10>>();
    Presentation<std::string> p;
    presentation::add_rule(p, "abcb", "bcb");
    presentation::add_rule(p, "bcb", "bbcb");
    presentation::add_rule(p, "bbcb", "bb");
    presentation::add_rule(p, "bb", "bcb");
    presentation::add_rule(p, "bcb", "a");
    p.alphabet_from_rules();
    presentation::reduce_complements(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);
    REQUIRE(p.rules
            == std::vector<std::string>(
                {"bb", "a", "bcb", "a", "abcb", "a", "bbcb", "a"}));
    REQUIRE(p.alphabet() == "abc");
    presentation::normalize_alphabet(p);
    REQUIRE(p.letter(0) == presentation::letter(p, 0));
    REQUIRE(p.letter(1) == presentation::letter(p, 1));
    REQUIRE(p.letter(2) == presentation::letter(p, 2));
    p.validate();

    presentation::add_rule(p, "abcb", "ecb");
    REQUIRE(!p.in_alphabet('e'));
    // Not valid
    REQUIRE_THROWS_AS(presentation::normalize_alphabet(p),
                      LibsemigroupsException);
    p.alphabet_from_rules();
    presentation::add_rule(p, "abcd", "bcb");
    REQUIRE_THROWS_AS(presentation::normalize_alphabet(p),
                      LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "017",
                          "helpers sort_each_rule",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_sort_each_rule<word_type>();
    check_sort_each_rule<StaticVector1<uint16_t, 10>>();
    check_sort_each_rule<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "018",
                          "helpers sort_rules",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_sort_rules<word_type>();
    check_sort_rules<StaticVector1<uint16_t, 10>>();
    check_sort_rules<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "019",
                          "helpers longest_common_subword/replace_subword",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_longest_common_subword<word_type>();
    check_longest_common_subword<StaticVector1<uint16_t, 10>>();
    check_longest_common_subword<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "020",
                          "helpers redundant_rule",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_redundant_rule<word_type>();
    check_redundant_rule<StaticVector1<uint16_t, 10>>();
    check_redundant_rule<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "021",
                          "helpers shortlex_compare_concat",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_shortlex_compare_concat<word_type>();
    check_shortlex_compare_concat<StaticVector1<uint16_t, 10>>();
    check_shortlex_compare_concat<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "022",
                          "helpers remove_trivial_rules",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_remove_trivial_rules<word_type>();
    check_remove_trivial_rules<StaticVector1<uint16_t, 10>>();
    check_remove_trivial_rules<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "023",
                          "helpers replace_subword (existing, replacement)",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_replace_subword<word_type>();
    check_replace_subword<StaticVector1<uint16_t, 64>>();
    check_replace_subword<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "030",
                          "helpers replace_word",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_replace_word<word_type>();
    check_replace_word<StaticVector1<uint16_t, 10>>();
    check_replace_word<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "024",
                          "helpers longest_rule",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_longest_rule<word_type>();
    check_longest_rule<StaticVector1<uint16_t, 10>>();
    check_longest_rule<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "025",
                          "helpers longest_rule_length",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_longest_rule_length<word_type>();
    check_longest_rule_length<StaticVector1<uint16_t, 10>>();
    check_longest_rule_length<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "026",
                          "helpers remove_redundant_generators",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_remove_redundant_generators<word_type>();
    check_remove_redundant_generators<StaticVector1<uint16_t, 64>>();
    check_remove_redundant_generators<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "027",
                          "helpers reverse",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_reverse<word_type>();
    check_reverse<StaticVector1<uint16_t, 10>>();
    check_reverse<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "028",
                          "in_alphabet",
                          "[quick][presentation]") {
    auto rg = ReportGuard(false);
    check_in_alphabet<word_type>();
    check_in_alphabet<StaticVector1<uint16_t, 10>>();
    check_in_alphabet<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "029",
                          "replace_subword with empty word",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.alphabet(2);
    p.contains_empty_word(true);
    presentation::add_rule(p, {0, 0, 0}, {});
    p.validate();
    REQUIRE_THROWS_AS(presentation::replace_subword(p, {}, {2}),
                      LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "031",
                          "clear",
                          "[quick][presentation]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.alphabet(2);
    p.contains_empty_word(true);
    presentation::add_rule(p, {0, 0, 0}, {});
    p.validate();
    p.clear();
    REQUIRE(p.alphabet().empty());
    REQUIRE(p.rules.empty());
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "040",
                          "change_alphabet",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("ab");
    presentation::add_rule_and_check(p, "ba", "abaaabaa");
    presentation::replace_subword(p, "ba");
    presentation::change_alphabet(p, "abc");
    REQUIRE(p.rules == std::vector<std::string>({"c", "acaaca", "c", "ba"}));
    REQUIRE(p.alphabet() == "abc");
    REQUIRE_NOTHROW(p.validate());
    // Alphabet wrong size
    REQUIRE_THROWS_AS(presentation::change_alphabet(p, "ab"),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(presentation::change_alphabet(p, "aab"),
                      LibsemigroupsException);
    REQUIRE(p.alphabet() == "abc");
    REQUIRE(p.rules == std::vector<std::string>({"c", "acaaca", "c", "ba"}));
    presentation::change_alphabet(p, "bac");
    REQUIRE(p.rules == std::vector<std::string>({"c", "bcbbcb", "c", "ab"}));
    REQUIRE(p.alphabet() == "bac");

    presentation::change_alphabet(p, "xyz");
    REQUIRE(p.rules == std::vector<std::string>({"z", "xzxxzx", "z", "yx"}));
    REQUIRE(p.alphabet() == "xyz");

    presentation::change_alphabet(p, "xyt");
    REQUIRE(p.rules == std::vector<std::string>({"t", "xtxxtx", "t", "yx"}));
    REQUIRE(p.alphabet() == "xyt");
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "032",
                          "letter",
                          "[quick][presentation]") {
    Presentation<std::vector<uint16_t>> p;
    REQUIRE_THROWS_AS(presentation::letter(p, 65536), LibsemigroupsException);
    REQUIRE(presentation::letter(p, 10) == 10);
    REQUIRE_THROWS_AS(presentation::character(65536), LibsemigroupsException);
    REQUIRE(presentation::character(0) == 'a');
    REQUIRE(presentation::character(10) == 'k');

    IntegralRange<size_t>     ir(0, 255);
    Presentation<std::string> q;

    REQUIRE(std::all_of(ir.cbegin(), ir.cend(), [&q](size_t i) {
      return presentation::character(i) == presentation::letter(q, i);
    }));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "033",
                          "normalize_alphabet",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("axy");
    presentation::normalize_alphabet(p);
    REQUIRE(p.alphabet() == "abc");
    Presentation<word_type> q;
    q.alphabet({0, 10, 12});
    presentation::normalize_alphabet(q);
    REQUIRE(q.alphabet() == word_type({0, 1, 2}));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "042",
                          "first_unused_letter/letter",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("ab");

    presentation::add_rule_and_check(p, "baabaa", "ababa");
    REQUIRE(presentation::first_unused_letter(p) == 'c');
    p.alphabet("abcdefghijklmnopq");
    REQUIRE(presentation::first_unused_letter(p) == 'r');
    p.alphabet("abcdefghijklmnopqrstuvwxyz");
    REQUIRE(presentation::first_unused_letter(p) == 'A');
    p.alphabet("abcdefgijklmnopqrstuvwxyz");
    REQUIRE(presentation::first_unused_letter(p) == 'h');
    p.alphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
    REQUIRE(presentation::first_unused_letter(p) == '0');
    p.alphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ02");
    REQUIRE(presentation::first_unused_letter(p) == '1');
    std::string const letters
        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    std::unordered_set<letter_type> set;
    for (size_t i = 0; i < letters.size(); ++i) {
      REQUIRE(letters[i] == presentation::letter(p, i));
      REQUIRE(set.insert(letters[i]).second);
    }
    for (size_t i = letters.size(); i < 255; ++i) {
      REQUIRE(set.insert(presentation::letter(p, i)).second);
    }
    REQUIRE_THROWS_AS(presentation::letter(p, 255), LibsemigroupsException);
    p.alphabet(255);
    REQUIRE_THROWS_AS(presentation::first_unused_letter(p),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(p.alphabet(256), LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "034",
                          "longest_common_subword issue",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("a");
    presentation::add_rule_and_check(p, "aaaaaaaaaaaaaaaaaaa", "a");
    REQUIRE(presentation::longest_common_subword(p) == "aaaaaa");
    presentation::replace_subword(p, "aaaaaa");
    REQUIRE(presentation::longest_common_subword(p) == "");
    REQUIRE(p.rules == std::vector<std::string>({"bbba", "a", "b", "aaaaaa"}));
    REQUIRE(presentation::length(p) == 12);
    p.rules = std::vector<std::string>({"bba", "a", "b", "aaaaaaaa"});
    REQUIRE(presentation::length(p) == 13);

    p.alphabet("ab");
    presentation::add_rule_and_check(p, "baaaaaaaaaaaaaaaaaaa", "a");
    REQUIRE(presentation::longest_common_subword(p) == "aaaaaa");

    p.alphabet("ab");
    p.rules.clear();
    presentation::add_rule_and_check(p, "aaaaaaaaaaaaaaaa", "a");
    presentation::add_rule_and_check(p, "bbbbbbbbbbbbbbbb", "b");
    presentation::add_rule_and_check(p, "abb", "baa");
    REQUIRE(presentation::length(p) == 40);
    auto w = presentation::longest_common_subword(p);
    REQUIRE(w == "bbbb");
    presentation::replace_subword(p, w);
    REQUIRE(presentation::length(p) == 33);
    REQUIRE(
        p.rules
        == std::vector<std::string>(
            {"aaaaaaaaaaaaaaaa", "a", "cccc", "b", "abb", "baa", "c", "bbbb"}));
    w = presentation::longest_common_subword(p);
    REQUIRE(w == "aaaa");
    presentation::replace_subword(p, w);
    REQUIRE(presentation::length(p) == 26);
    REQUIRE(p.rules
            == std::vector<std::string>({"dddd",
                                         "a",
                                         "cccc",
                                         "b",
                                         "abb",
                                         "baa",
                                         "c",
                                         "bbbb",
                                         "d",
                                         "aaaa"}));
    w = presentation::longest_common_subword(p);
    REQUIRE(w == "");
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "037",
                          "make_semigroup",
                          "[quick][presentation]") {
    check_make_semigroup<word_type>();
    check_make_semigroup<StaticVector1<uint16_t, 10>>();
    check_make_semigroup<std::string>();
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "038",
                          "greedy_reduce_length",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("ab");
    p.rules.clear();
    presentation::add_rule_and_check(p, "aaaaaaaaaaaaaaaa", "a");
    presentation::add_rule_and_check(p, "bbbbbbbbbbbbbbbb", "b");
    presentation::add_rule_and_check(p, "abb", "baa");
    REQUIRE(presentation::length(p) == 40);
    presentation::greedy_reduce_length(p);
    REQUIRE(presentation::length(p) == 26);
    REQUIRE(p.rules
            == std::vector<std::string>({"dddd",
                                         "a",
                                         "cccc",
                                         "b",
                                         "abb",
                                         "baa",
                                         "c",
                                         "bbbb",
                                         "d",
                                         "aaaa"}));
    REQUIRE(presentation::longest_common_subword(p) == "");
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "039",
                          "aaaaaaaab = aaaaaaaaab strong compression",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("ab");
    presentation::add_rule_and_check(p, "aaaaaaaab", "aaaaaaaaab");
    REQUIRE(presentation::strongly_compress(p));
    REQUIRE(p.rules == decltype(p.rules)({"a", "aa"}));

    p.rules = {"adadnadnasnamdnamdna", "akdjskadjksajdaldja"};
    p.alphabet_from_rules();

    REQUIRE(presentation::strongly_compress(p));
    REQUIRE(presentation::reduce_to_2_generators(p));
    REQUIRE(
        p.rules
        == decltype(p.rules)({"aaaaaaaaaaaaaaaaaaa", "baaaaaaaaaaaaaaaaa"}));

    // Only works for 1-relation monoids at present
    p.alphabet("ab");
    presentation::add_rule_and_check(p, "aaaaaaaab", "aaaaaaaaab");
    presentation::add_rule_and_check(p, "aaaaaaaab", "aaaaaaaaab");
    REQUIRE(!presentation::strongly_compress(p));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "043",
                          "case where strong compression doesn't work",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("ab");
    presentation::add_rule_and_check(p, "abaaaabab", "abbabaaaab");
    REQUIRE(presentation::strongly_compress(p));
    REQUIRE(p.rules == decltype(p.rules)({"abccdae", "fgeabccd"}));

    auto q = p;
    REQUIRE(presentation::reduce_to_2_generators(q));
    REQUIRE(q.rules == decltype(q.rules)({"aaaaaaa", "baaaaaaa"}));

    q = p;
    REQUIRE(presentation::reduce_to_2_generators(q, 1));
    REQUIRE(q.rules == decltype(q.rules)({"abbbbab", "bbbabbbb"}));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "044",
                          "proof that",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("ab");
    presentation::add_rule_and_check(p, "aabb", "aaabaaab");
    REQUIRE(presentation::strongly_compress(p));
    presentation::reverse(p);
    REQUIRE(p.rules == decltype(p.rules)({"cba", "baadbaa"}));

    auto q = p;
    REQUIRE(presentation::reduce_to_2_generators(q));
    REQUIRE(q.rules == decltype(q.rules)({"aba", "baaabaa"}));

    q = p;
    REQUIRE(presentation::reduce_to_2_generators(q, 1));
    REQUIRE(q.rules == decltype(q.rules)({"abb", "bbbbbbb"}));

    // Wrong index
    REQUIRE_THROWS_AS(presentation::reduce_to_2_generators(q, 2),
                      LibsemigroupsException);
    q = p;
    presentation::add_rule_and_check(q, "aabb", "aaabaaab");
    // not 1-relation
    REQUIRE(!presentation::reduce_to_2_generators(q, 1));

    q.rules = {"aaaaa", "a"};
    REQUIRE(!presentation::reduce_to_2_generators(q));

    q.rules = {"aaaaa", ""};
    REQUIRE(!presentation::reduce_to_2_generators(q));

    q.rules = {"abcacbabab", ""};
    REQUIRE(!presentation::reduce_to_2_generators(q));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "045",
                          "decompression",
                          "[quick][presentation]") {
    Presentation<std::string> p;
    p.alphabet("ab");
    p.rules = {"aabb", "aaabaab"};
    REQUIRE(presentation::strongly_compress(p));
    REQUIRE(p.rules == decltype(p.rules)({"abc", "aabdab"}));
    REQUIRE(!presentation::reduce_to_2_generators(p));
    presentation::reverse(p);
    REQUIRE(presentation::reduce_to_2_generators(p));
    REQUIRE(p.rules == decltype(p.rules)({"aba", "baabaa"}));
  }

  LIBSEMIGROUPS_TEST_CASE("Presentation",
                          "041",
                          "sort_rules bug",
                          "[quick][presentation]") {
    std::string prefix1 = "dabd", suffix1 = "cbb", prefix2 = "abbaba",
                suffix2 = "c";

    REQUIRE(
        !detail::shortlex_compare_concat(prefix1, suffix1, prefix2, suffix2));

    Presentation<std::string> p;
    p.alphabet("bacd");
    p.rules = {"baabbabaa",
               "abaaba",
               "abbaba",
               "c",
               "abb",
               "d",
               "daba",
               "c",
               "dabd",
               "cbb",
               "dbaca",
               "caba",
               "dbacc",
               "cabc",
               "dbacd",
               "cabd",
               "abaaba",
               "baca",
               "abaabc",
               "bacc",
               "abaabd",
               "bacd",
               "cbaaba",
               "ddaca",
               "cbaabc",
               "ddacc",
               "cbaabd",
               "ddacd",
               "cbbaba",
               "dabc",
               "dabcbb",
               "cbbabd",
               "bacaaba",
               "ababaca",
               "bacaabc",
               "ababacc",
               "bacaabd",
               "ababacd",
               "bacbaca",
               "abadaca",
               "bacbacc",
               "abadacc",
               "bacbacd",
               "abadacd",
               "dabcaba",
               "cbbbaca",
               "dabcabc",
               "cbbbacc",
               "dabcabd",
               "cbbbacd",
               "ddacaaba",
               "cbabaca",
               "ddacaabc",
               "cbabacc",
               "ddacaabd",
               "cbabacd",
               "ddacbaca",
               "cbadaca",
               "ddacbacc",
               "cbadacc",
               "ddacbacd",
               "cbadacd",
               "abababaca",
               "dacaaba",
               "abababacc",
               "dacaabc",
               "abababacd",
               "dacaabd",
               "ababadaca",
               "dacbaca",
               "ababadacc",
               "dacbacc",
               "ababadacd",
               "dacbacd",
               "daababaca",
               "ccaaba",
               "daababacc",
               "ccaabc",
               "daababacd",
               "ccaabd",
               "daabadaca",
               "ccbaca",
               "daabadacc",
               "ccbacc",
               "daabadacd",
               "ccbacd",
               "bacababaca",
               "abadacaaba",
               "bacababacc",
               "abadacaabc",
               "bacababacd",
               "abadacaabd",
               "bacabadaca",
               "abadacbaca",
               "bacabadacc",
               "abadacbacc",
               "bacabadacd",
               "abadacbacd",
               "dabcbabaca",
               "cbbdacaaba",
               "dabcbabacc",
               "cbbdacaabc",
               "dabcbabacd",
               "cbbdacaabd",
               "dabcbadaca",
               "cbbdacbaca",
               "dabcbadacc",
               "cbbdacbacc",
               "dabcbadacd",
               "cbbdacbacd",
               "abaaababaca",
               "bacacaaba",
               "abaaababacc",
               "bacacaabc",
               "abaaababacd",
               "bacacaabd",
               "abaaabadaca",
               "bacacbaca",
               "abaaabadacc",
               "bacacbacc",
               "abaaabadacd",
               "bacacbacd",
               "cbaaababaca",
               "ddacacaaba",
               "cbaaababacc",
               "ddacacaabc",
               "cbaaababacd",
               "ddacacaabd",
               "cbaaabadaca",
               "ddacacbaca",
               "cbaaabadacc",
               "ddacacbacc",
               "cbaaabadacd",
               "ddacacbacd",
               "cbbaababaca",
               "dabccaaba",
               "cbbaababacc",
               "dabccaabc",
               "cbbaababacd",
               "dabccaabd",
               "cbbaabadaca",
               "dabccbaca",
               "cbbaabadacc",
               "dabccbacc",
               "cbbaabadacd",
               "dabccbacd",
               "ddacababaca",
               "cbadacaaba",
               "ddacababacc",
               "cbadacaabc",
               "ddacababacd",
               "cbadacaabd",
               "ddacabadaca",
               "cbadacbaca",
               "ddacabadacc",
               "cbadacbacc",
               "ddacabadacd",
               "cbadacbacd",
               "ababadacbaca",
               "dacabadaca",
               "ababadacbacc",
               "dacabadacc",
               "ababadacbacd",
               "dacabadacd",
               "bacaaababaca",
               "ababacacaaba",
               "bacaaababacc",
               "ababacacaabc",
               "bacaaababacd",
               "ababacacaabd",
               "bacaaabadaca",
               "ababacacbaca",
               "bacaaabadacc",
               "ababacacbacc",
               "bacaaabadacd",
               "ababacacbacd",
               "daabadacbaca",
               "ccabadaca",
               "daabadacbacc",
               "ccabadacc",
               "daabadacbacd",
               "ccabadacd",
               "bacabadacbaca",
               "abadacabadaca",
               "bacabadacbacc",
               "abadacabadacc",
               "bacabadacbacd",
               "abadacabadacd",
               "dabcbadacaaba",
               "cbbdacababaca",
               "dabcbadacaabc",
               "cbbdacababacc",
               "dabcbadacaabd",
               "cbbdacababacd",
               "dabcbadacbaca",
               "cbbdacabadaca",
               "dabcbadacbacc",
               "cbbdacabadacc",
               "dabcbadacbacd",
               "cbbdacabadacd",
               "ddacaaababaca",
               "cbabacacaaba",
               "ddacaaababacc",
               "cbabacacaabc",
               "ddacaaababacd",
               "cbabacacaabd",
               "ddacaaabadaca",
               "cbabacacbaca",
               "ddacaaabadacc",
               "cbabacacbacc",
               "ddacaaabadacd",
               "cbabacacbacd",
               "abaaabadacbaca",
               "bacacabadaca",
               "abaaabadacbacc",
               "bacacabadacc",
               "abaaabadacbacd",
               "bacacabadacd",
               "cbaaabadacbaca",
               "ddacacabadaca",
               "cbaaabadacbacc",
               "ddacacabadacc",
               "cbaaabadacbacd",
               "ddacacabadacd",
               "cbbaabadacbaca",
               "dabccabadaca",
               "cbbaabadacbacc",
               "dabccabadacc",
               "cbbaabadacbacd",
               "dabccabadacd",
               "ddacabadacbaca",
               "cbadacabadaca",
               "ddacabadacbacc",
               "cbadacabadacc",
               "ddacabadacbacd",
               "cbadacabadacd",
               "bacaaabadacbaca",
               "ababacacabadaca",
               "bacaaabadacbacc",
               "ababacacabadacc",
               "bacaaabadacbacd",
               "ababacacabadacd",
               "dabcbabacacaaba",
               "cbbdacaaababaca",
               "dabcbabacacaabc",
               "cbbdacaaababacc",
               "dabcbabacacaabd",
               "cbbdacaaababacd",
               "dabcbabacacbaca",
               "cbbdacaaabadaca",
               "dabcbabacacbacc",
               "cbbdacaaabadacc",
               "dabcbabacacbacd",
               "cbbdacaaabadacd",
               "dabcbadacabadaca",
               "cbbdacabadacbaca",
               "dabcbadacabadacc",
               "cbbdacabadacbacc"};
    REQUIRE(p.rules.size() == 258);

    p.validate();
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);
    REQUIRE(presentation::are_rules_sorted(p));
    REQUIRE(p.rules
            == std::vector<std::string>({"abb",
                                         "d",
                                         "daba",
                                         "c",
                                         "abbaba",
                                         "c",
                                         "dabd",
                                         "cbb",
                                         "dbaca",
                                         "caba",
                                         "dbacc",
                                         "cabc",
                                         "dbacd",
                                         "cabd",
                                         "abaaba",
                                         "baca",
                                         "abaabc",
                                         "bacc",
                                         "abaabd",
                                         "bacd",
                                         "cbbaba",
                                         "dabc",
                                         "cbaaba",
                                         "ddaca",
                                         "cbaabc",
                                         "ddacc",
                                         "cbaabd",
                                         "ddacd",
                                         "dabcbb",
                                         "cbbabd",
                                         "bacaaba",
                                         "ababaca",
                                         "bacaabc",
                                         "ababacc",
                                         "bacaabd",
                                         "ababacd",
                                         "bacbaca",
                                         "abadaca",
                                         "bacbacc",
                                         "abadacc",
                                         "bacbacd",
                                         "abadacd",
                                         "dabcaba",
                                         "cbbbaca",
                                         "dabcabc",
                                         "cbbbacc",
                                         "dabcabd",
                                         "cbbbacd",
                                         "baabbabaa",
                                         "abaaba",
                                         "daababaca",
                                         "ccaaba",
                                         "daababacc",
                                         "ccaabc",
                                         "daababacd",
                                         "ccaabd",
                                         "daabadaca",
                                         "ccbaca",
                                         "daabadacc",
                                         "ccbacc",
                                         "daabadacd",
                                         "ccbacd",
                                         "ddacaaba",
                                         "cbabaca",
                                         "ddacaabc",
                                         "cbabacc",
                                         "ddacaabd",
                                         "cbabacd",
                                         "ddacbaca",
                                         "cbadaca",
                                         "ddacbacc",
                                         "cbadacc",
                                         "ddacbacd",
                                         "cbadacd",
                                         "abababaca",
                                         "dacaaba",
                                         "abababacc",
                                         "dacaabc",
                                         "abababacd",
                                         "dacaabd",
                                         "ababadaca",
                                         "dacbaca",
                                         "ababadacc",
                                         "dacbacc",
                                         "ababadacd",
                                         "dacbacd",
                                         "abaaababaca",
                                         "bacacaaba",
                                         "abaaababacc",
                                         "bacacaabc",
                                         "abaaababacd",
                                         "bacacaabd",
                                         "abaaabadaca",
                                         "bacacbaca",
                                         "abaaabadacc",
                                         "bacacbacc",
                                         "abaaabadacd",
                                         "bacacbacd",
                                         "bacababaca",
                                         "abadacaaba",
                                         "bacababacc",
                                         "abadacaabc",
                                         "bacababacd",
                                         "abadacaabd",
                                         "bacabadaca",
                                         "abadacbaca",
                                         "bacabadacc",
                                         "abadacbacc",
                                         "bacabadacd",
                                         "abadacbacd",
                                         "cbbaababaca",
                                         "dabccaaba",
                                         "cbbaababacc",
                                         "dabccaabc",
                                         "cbbaababacd",
                                         "dabccaabd",
                                         "cbbaabadaca",
                                         "dabccbaca",
                                         "cbbaabadacc",
                                         "dabccbacc",
                                         "cbbaabadacd",
                                         "dabccbacd",
                                         "dabcbabaca",
                                         "cbbdacaaba",
                                         "dabcbabacc",
                                         "cbbdacaabc",
                                         "dabcbabacd",
                                         "cbbdacaabd",
                                         "dabcbadaca",
                                         "cbbdacbaca",
                                         "dabcbadacc",
                                         "cbbdacbacc",
                                         "dabcbadacd",
                                         "cbbdacbacd",
                                         "cbaaababaca",
                                         "ddacacaaba",
                                         "cbaaababacc",
                                         "ddacacaabc",
                                         "cbaaababacd",
                                         "ddacacaabd",
                                         "cbaaabadaca",
                                         "ddacacbaca",
                                         "cbaaabadacc",
                                         "ddacacbacc",
                                         "cbaaabadacd",
                                         "ddacacbacd",
                                         "daabadacbaca",
                                         "ccabadaca",
                                         "daabadacbacc",
                                         "ccabadacc",
                                         "daabadacbacd",
                                         "ccabadacd",
                                         "ddacababaca",
                                         "cbadacaaba",
                                         "ddacababacc",
                                         "cbadacaabc",
                                         "ddacababacd",
                                         "cbadacaabd",
                                         "ddacabadaca",
                                         "cbadacbaca",
                                         "ddacabadacc",
                                         "cbadacbacc",
                                         "ddacabadacd",
                                         "cbadacbacd",
                                         "ababadacbaca",
                                         "dacabadaca",
                                         "ababadacbacc",
                                         "dacabadacc",
                                         "ababadacbacd",
                                         "dacabadacd",
                                         "bacaaababaca",
                                         "ababacacaaba",
                                         "bacaaababacc",
                                         "ababacacaabc",
                                         "bacaaababacd",
                                         "ababacacaabd",
                                         "bacaaabadaca",
                                         "ababacacbaca",
                                         "bacaaabadacc",
                                         "ababacacbacc",
                                         "bacaaabadacd",
                                         "ababacacbacd",
                                         "ddacaaababaca",
                                         "cbabacacaaba",
                                         "ddacaaababacc",
                                         "cbabacacaabc",
                                         "ddacaaababacd",
                                         "cbabacacaabd",
                                         "ddacaaabadaca",
                                         "cbabacacbaca",
                                         "ddacaaabadacc",
                                         "cbabacacbacc",
                                         "ddacaaabadacd",
                                         "cbabacacbacd",
                                         "abaaabadacbaca",
                                         "bacacabadaca",
                                         "abaaabadacbacc",
                                         "bacacabadacc",
                                         "abaaabadacbacd",
                                         "bacacabadacd",
                                         "bacabadacbaca",
                                         "abadacabadaca",
                                         "bacabadacbacc",
                                         "abadacabadacc",
                                         "bacabadacbacd",
                                         "abadacabadacd",
                                         "cbbaabadacbaca",
                                         "dabccabadaca",
                                         "cbbaabadacbacc",
                                         "dabccabadacc",
                                         "cbbaabadacbacd",
                                         "dabccabadacd",
                                         "dabcbadacaaba",
                                         "cbbdacababaca",
                                         "dabcbadacaabc",
                                         "cbbdacababacc",
                                         "dabcbadacaabd",
                                         "cbbdacababacd",
                                         "dabcbadacbaca",
                                         "cbbdacabadaca",
                                         "dabcbadacbacc",
                                         "cbbdacabadacc",
                                         "dabcbadacbacd",
                                         "cbbdacabadacd",
                                         "cbaaabadacbaca",
                                         "ddacacabadaca",
                                         "cbaaabadacbacc",
                                         "ddacacabadacc",
                                         "cbaaabadacbacd",
                                         "ddacacabadacd",
                                         "ddacabadacbaca",
                                         "cbadacabadaca",
                                         "ddacabadacbacc",
                                         "cbadacabadacc",
                                         "ddacabadacbacd",
                                         "cbadacabadacd",
                                         "bacaaabadacbaca",
                                         "ababacacabadaca",
                                         "bacaaabadacbacc",
                                         "ababacacabadacc",
                                         "bacaaabadacbacd",
                                         "ababacacabadacd",
                                         "dabcbabacacaaba",
                                         "cbbdacaaababaca",
                                         "dabcbabacacaabc",
                                         "cbbdacaaababacc",
                                         "dabcbabacacaabd",
                                         "cbbdacaaababacd",
                                         "dabcbabacacbaca",
                                         "cbbdacaaabadaca",
                                         "dabcbabacacbacc",
                                         "cbbdacaaabadacc",
                                         "dabcbabacacbacd",
                                         "cbbdacaaabadacd",
                                         "dabcbadacabadaca",
                                         "cbbdacabadacbaca",
                                         "dabcbadacabadacc",
                                         "cbbdacabadacbacc"}));

    std::vector<std::string> expected;
    for (auto it = p.rules.cbegin(); it < p.rules.cend(); it += 2) {
      expected.push_back(*it + *(it + 1));
    }
    std::vector<std::string> found = expected;
    std::sort(found.begin(), found.end(), ShortLexCompare<std::string>());
    REQUIRE(found == expected);
    for (size_t i = 0; i != found.size(); ++i) {
      REQUIRE(std::make_pair(found[i], i) == std::make_pair(expected[i], i));
    }
  }

}  // namespace libsemigroups
