Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/semigroups/libsemigroups/tests/   (GAP Algebra Version 4.15.1©)  Datei vom 18.5.2025 mit Größe 85 kB image not shown  

Quelle  test-sims1.cpp

  Sprache: C
 

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

#include <cstddef>        // for size_t
#include <iostream>       // for cout
#include <stdexcept>      // for number_of_congruencestime_error
#include <unordered_set>  // for unordered_set
#include <vector>         // for vector

#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/config.hpp"           // for LIBSEMIGROUPS_ENABLE_STATS
#include "libsemigroups/digraph-helper.hpp"   // for action_digraph_helper
#include "libsemigroups/fpsemi-examples.hpp"  // for brauer_monoid etc
#include "libsemigroups/froidure-pin.hpp"     // for FroidurePin
#include "libsemigroups/knuth-bendix.hpp"     // for redundant_rule
#include "libsemigroups/make-froidure-pin.hpp"  // for make
#include "libsemigroups/make-present.hpp"       // for make
#include "libsemigroups/sims1.hpp"              // for Sims1
#include "libsemigroups/transf.hpp"             // for Transf
#include "libsemigroups/types.hpp"              // for word_type
                                                //
namespace libsemigroups {

  using Sims1_       = Sims1<uint32_t>;
  using digraph_type = typename Sims1_::digraph_type;
  using node_type    = typename digraph_type::node_type;

  using fpsemigroup::author;
  using fpsemigroup::make;

  using fpsemigroup::brauer_monoid;
  using fpsemigroup::chinese_monoid;
  using fpsemigroup::fibonacci_semigroup;
  using fpsemigroup::full_transformation_monoid;
  using fpsemigroup::monogenic_semigroup;
  using fpsemigroup::partition_monoid;
  using fpsemigroup::plactic_monoid;
  using fpsemigroup::rectangular_band;
  using fpsemigroup::rook_monoid;
  using fpsemigroup::singular_brauer_monoid;
  using fpsemigroup::stellar_monoid;
  using fpsemigroup::stylic_monoid;
  using fpsemigroup::temperley_lieb_monoid;

  namespace {
    template <typename P>
    void check_extra(congruence_kind ck, P const& p, P const& e, size_t n) {
      P f = e;
      if (ck == congruence_kind::left) {
        presentation::reverse(f);
      }
      auto foo = [&f](auto const& ad) {
        using action_digraph_helper::follow_path_nc;
        for (auto it = f.rules.cbegin(); it != f.rules.cend(); it += 2) {
          if (follow_path_nc(ad, 0, *it) != follow_path_nc(ad, 0, *(it + 1))) {
            return false;
          }
        }
        return true;
      };
      Sims1_ S(ck);
      S.short_rules(p);

      Sims1_ T(ck);
      T.short_rules(p).extra(e);

      REQUIRE(static_cast<uint64_t>(std::count_if(S.cbegin(n), S.cend(n), foo))
              == T.number_of_congruences(n));
    }

    constexpr unsigned int factorial(unsigned int n) {
      return n > 1 ? n * factorial(n - 1) : 1;
    }
  }  // namespace

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "000",
                          "fp example 1",
                          "[quick][low-index]") {
    auto rg = ReportGuard(false);

    Presentation<word_type> p;
    p.contains_empty_word(true);
    p.alphabet({01});
    presentation::add_rule_and_check(p, {000}, {0});
    presentation::add_rule_and_check(p, {11}, {1});
    presentation::add_rule_and_check(p, {0101}, {0});

    {
      Sims1_ S(congruence_kind::right);
      REQUIRE(S.short_rules(p).number_of_threads(2).number_of_congruences(5)
              == 6);
      REQUIRE_THROWS_AS(S.number_of_congruences(0), LibsemigroupsException);
      REQUIRE_THROWS_AS(S.find_if(0, [](auto) { return false; }),
                        LibsemigroupsException);
      REQUIRE_THROWS_AS(S.for_each(0, [](auto) { return false; }),
                        LibsemigroupsException);
      REQUIRE_THROWS_AS(S.cbegin(0), LibsemigroupsException);
      REQUIRE_THROWS_AS(S.cend(0), LibsemigroupsException);
      REQUIRE(S.number_of_congruences(1) == 1);

      auto it = S.cbegin(1);
      REQUIRE(*it == action_digraph_helper::make<node_type>(1, {{00}}));

      it = S.cbegin(5);
      REQUIRE(*(it++) == action_digraph_helper::make<node_type>(5, {{00}}));
      REQUIRE(*(it++)
              == action_digraph_helper::make<node_type>(5, {{10}, {11}}));
      REQUIRE(*(it++)
              == action_digraph_helper::make<node_type>(5, {{11}, {11}}));
      REQUIRE(*(it++)
              == action_digraph_helper::make<node_type>(
                  5, {{12}, {11}, {12}}));
      REQUIRE(*(it++)
              == action_digraph_helper::make<node_type>(
                  5, {{12}, {11}, {22}}));
      REQUIRE(*(it++)
              == action_digraph_helper::make<node_type>(
                  5, {{12}, {11}, {32}, {33}}));
      REQUIRE(*(it++) == ActionDigraph<node_type>(02));
      REQUIRE(*(it++) == ActionDigraph<node_type>(02));
      REQUIRE(*(it++) == ActionDigraph<node_type>(02));

      it = S.cbegin(3);
      REQUIRE(*it == action_digraph_helper::make<node_type>(3, {{00}}));
    }
    // [[[0, 0]],
    // [[1, 2], [1, 1], [3, 2], [3, 3]],
    // [[1, 2], [1, 1], [2, 2]],
    // [[1, 2], [1, 1], [1, 2]],
    // [[1, 1], [1, 1]],
    // [[1, 0], [1, 1]]]
    {
      Sims1_ S(congruence_kind::left);
      REQUIRE(S.short_rules(p).number_of_congruences(5) == 9);
      for (auto it = S.cbegin(5); it != S.cend(5); ++it) {
        REQUIRE(action_digraph_helper::follow_path_nc(*it, 0, {1010})
                == action_digraph_helper::follow_path_nc(*it, 0, {0}));
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "001",
                          "fp example 2",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    p.alphabet({012});
    presentation::add_rule_and_check(p, {010}, {00});
    presentation::add_rule_and_check(p, {22}, {00});
    presentation::add_rule_and_check(p, {000}, {00});
    presentation::add_rule_and_check(p, {21}, {12});
    presentation::add_rule_and_check(p, {20}, {00});
    presentation::add_rule_and_check(p, {11}, {1});
    presentation::add_rule_and_check(p, {02}, {00});

    {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p);
      REQUIRE(S.number_of_congruences(1) == 1);
      REQUIRE(S.number_of_congruences(2) == 3);
      REQUIRE(S.number_of_congruences(3) == 13);
      REQUIRE(S.number_of_congruences(4) == 36);
      REQUIRE(S.number_of_congruences(5) == 82);
      REQUIRE(S.number_of_congruences(6) == 135);
      REQUIRE(S.number_of_congruences(7) == 166);
      REQUIRE(S.number_of_congruences(8) == 175);
      REQUIRE(S.number_of_congruences(9) == 176);
      REQUIRE(S.number_of_congruences(10) == 176);

      auto it = S.cbegin(2);
      REQUIRE(*(it++)
              == action_digraph_helper::make<node_type>(2, {{000}}));
      REQUIRE(
          *(it++)
          == action_digraph_helper::make<node_type>(2, {{101}, {111}}));
      REQUIRE(
          *(it++)
          == action_digraph_helper::make<node_type>(2, {{111}, {111}}));
      REQUIRE(*(it++) == ActionDigraph<node_type>(03));
      REQUIRE(*(it++) == ActionDigraph<node_type>(03));
    }
    {
      Sims1_ S(congruence_kind::left);
      S.short_rules(p);
      REQUIRE(S.number_of_congruences(11) == 176);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "002",
                          "ToddCoxeter failing example",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(false);

    //              a  A  b  B  c  C  e
    p.alphabet({0123456});
    presentation::add_identity_rules(p, 6);
    presentation::add_inverse_rules(p, {1032546}, 6);
    presentation::add_rule_and_check(p, {00504}, {6});
    presentation::add_rule_and_check(p, {0422152}, {6});
    presentation::add_rule_and_check(p, {1302444}, {6});
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);
    REQUIRE(S.number_of_congruences(1) == 1);
    REQUIRE(S.number_of_congruences(3) == 14);
    REQUIRE(S.number_of_congruences(4) == 14);
    REQUIRE(S.number_of_congruences(5) == 14);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "003",
                          "ToddCoxeter failing example",
                          "[quick][low-index]") {
    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");
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);
    REQUIRE(S.number_of_congruences(3) == 14);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "004",
                          "partition_monoid(2) right",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(false);

    p.alphabet({0123});
    presentation::add_identity_rules(p, 0);
    presentation::add_rule_and_check(p, {11}, {0});
    presentation::add_rule_and_check(p, {13}, {3});
    presentation::add_rule_and_check(p, {22}, {2});
    presentation::add_rule_and_check(p, {31}, {3});
    presentation::add_rule_and_check(p, {33}, {3});
    presentation::add_rule_and_check(p, {232}, {2});
    presentation::add_rule_and_check(p, {323}, {3});
    presentation::add_rule_and_check(p, {1212}, {212});
    presentation::add_rule_and_check(p, {2121}, {212});

    Sims1_ S(congruence_kind::right);
    S.short_rules(p);
    REQUIRE(S.number_of_congruences(2) == 4);
    REQUIRE(S.number_of_congruences(3) == 7);
    REQUIRE(S.number_of_congruences(4) == 14);
    REQUIRE(S.number_of_congruences(5) == 23);
    REQUIRE(S.number_of_congruences(6) == 36);
    REQUIRE(S.number_of_congruences(7) == 51);
    REQUIRE(S.number_of_congruences(8) == 62);
    REQUIRE(S.number_of_congruences(9) == 74);
    REQUIRE(S.number_of_congruences(10) == 86);
    REQUIRE(S.number_of_congruences(11) == 95);
    REQUIRE(S.number_of_congruences(12) == 100);
    REQUIRE(S.number_of_congruences(13) == 102);
    REQUIRE(S.number_of_congruences(14) == 104);
    REQUIRE(S.number_of_congruences(15) == 105);
    REQUIRE(S.number_of_congruences(16) == 105);
    REQUIRE(S.number_of_congruences(17) == 105);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "005",
                          "partition_monoid(3)",
                          "[standard][low-index][no-coverage]") {
    auto rg = ReportGuard(false);
    auto p
        = make<Presentation<word_type>>(partition_monoid(3, author::Machine));
    REQUIRE(!p.contains_empty_word());
    REQUIRE(p.alphabet() == word_type({01234}));

    Sims1_ S(congruence_kind::right);
    S.short_rules(p).long_rule_length(11).number_of_threads(
        4);  // This actually helps here!
    REQUIRE(S.number_of_congruences(17) == 1589);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "006",
                          "full_transformation_monoid(3) right",
                          "[quick][low-index]") {
    auto                   rg = ReportGuard(false);
    FroidurePin<Transf<3>> S({Transf<3>::make({120}),
                              Transf<3>::make({102}),
                              Transf<3>::make({010})});
    REQUIRE(S.size() == 27);
    REQUIRE(S.number_of_generators() == 3);
    REQUIRE(S.number_of_rules() == 16);
    auto p = make<Presentation<word_type>>(S);
    REQUIRE(static_cast<size_t>(std::distance(p.rules.cbegin(), p.rules.cend()))
            == 2 * S.number_of_rules());
    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(27) == 287);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "007",
                          "full_transformation_monoid(3) left",
                          "[quick][low-index]") {
    auto                   rg = ReportGuard(false);
    FroidurePin<Transf<3>> S(
        {Transf<3>({120}), Transf<3>({102}), Transf<3>({010})});
    REQUIRE(S.size() == 27);
    auto   p = make<Presentation<word_type>>(S);
    Sims1_ C(congruence_kind::left);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(27) == 120);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "008",
                          "full_transformation_monoid(4) left",
                          "[fail][low-index][babbage]") {
    auto rg = ReportGuard(true);

    auto p = make<Presentation<word_type>>(
        full_transformation_monoid(4, author::Iwahori));

    REQUIRE(p.alphabet().size() == 4);

    auto w = presentation::longest_common_subword(p);
    while (!w.empty()) {
      presentation::replace_subword(p, w);
      w = presentation::longest_common_subword(p);
    }

    presentation::sort_each_rule(p);
    presentation::sort_rules(p);
    presentation::remove_duplicate_rules(p);
    presentation::reduce_complements(p);
    presentation::remove_trivial_rules(p);

    do {
      auto it = presentation::redundant_rule(p, std::chrono::milliseconds(100));
      p.rules.erase(it, it + 2);
    } while (presentation::length(p) > 700);

    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    // Takes about 1h31m to run!
    REQUIRE(C.number_of_threads(6).number_of_congruences(256) == 22'069'828);
    // Sims1_ C(congruence_kind::left);
    // C.short_rules(p);
    // REQUIRE(C.number_of_threads(6).number_of_congruences(256) == 120'121);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "009",
                          "rook_monoid(2, 1)",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(false);

    p.alphabet(3);
    for (auto const& rel : rook_monoid(21)) {
      p.add_rule_and_check(rel.first.cbegin(),
                           rel.first.cend(),
                           rel.second.cbegin(),
                           rel.second.cend());
    }
    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(7) == 10);  // Should be 10
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "010",
                          "symmetric_inverse_monoid(2) from FroidurePin",
                          "[quick][low-index]") {
    auto                  rg = ReportGuard(false);
    FroidurePin<PPerm<2>> S({PPerm<2>({10}), PPerm<2>({0}, {0}, 2)});
    REQUIRE(S.size() == 7);
    auto   p = make<Presentation<word_type>>(S);
    Sims1_ C(congruence_kind::left);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(7) == 10);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "011",
                          "symmetric_inverse_monoid(3)",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(false);

    p.alphabet(4);
    for (auto const& rel : rook_monoid(31)) {
      p.add_rule_and_check(rel.first.cbegin(),
                           rel.first.cend(),
                           rel.second.cbegin(),
                           rel.second.cend());
    }
    Sims1_ C(congruence_kind::left);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(34) == 274);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "012",
                          "symmetric_inverse_monoid(4)",
                          "[extreme][low-index]") {
    auto                    rg = ReportGuard(true);
    Presentation<word_type> p;
    p.contains_empty_word(false);

    p.alphabet(5);
    for (auto const& rel : rook_monoid(41)) {
      p.add_rule_and_check(rel.first.cbegin(),
                           rel.first.cend(),
                           rel.second.cbegin(),
                           rel.second.cend());
    }
    presentation::remove_duplicate_rules(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);

    REQUIRE(presentation::length(p) == 78);
    REQUIRE(p.alphabet().size() == 5);
    REQUIRE(*presentation::shortest_rule(p) == word_type({00}));
    REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({0}));
    REQUIRE(presentation::longest_rule_length(p) == 8);

    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
                .number_of_congruences(209)
            == 195'709);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "013",
                          "symmetric_inverse_monoid(5)",
                          "[fail][low-index]") {
    // This might take an extremely long time to terminate
    auto                    rg = ReportGuard(true);
    Presentation<word_type> p;
    p.contains_empty_word(false);

    p.alphabet(6);
    for (auto const& rel : rook_monoid(51)) {
      p.add_rule_and_check(rel.first.cbegin(),
                           rel.first.cend(),
                           rel.second.cbegin(),
                           rel.second.cend());
    }
    Sims1_ C(congruence_kind::left);
    C.short_rules(p);
    REQUIRE(C.number_of_threads(6).number_of_congruences(1'546) == 0);
    // On 24/08/2022 JDM ran this for approx. 16 hours overnight on his laptop,
    // the last line of output was:
    // #4: Sims1: found 63'968'999 congruences in 52156s!
    // #21: Sims1: found 759'256'468 congruences in 244617.546875
    // #12: Sims1: found 943'233'501 congruences in 321019.531250!
    // #30: Sims1: found 1'005'857'634 congruences in 350411.000000!
    // #45: Sims1: found 1'314'588'296 congruences in 487405.000000!
    // #20: Sims1: found 4'619'043'843 congruences in 2'334'184.500000!
    // #49: Sims1: found 5'582'499'404 congruences in 2'912'877.000000!
    // #10: Sims1: found 6'825'113'083 congruences in 3705611.250000!
    //
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "014",
                          "temperley_lieb_monoid(3) from presentation",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    p.alphabet(2);
    for (auto const& rel : temperley_lieb_monoid(3)) {
      p.add_rule_and_check(rel.first.cbegin(),
                           rel.first.cend(),
                           rel.second.cbegin(),
                           rel.second.cend());
    }
    {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p);
      REQUIRE(S.number_of_congruences(14) == 9);
    }
    {
      Sims1_ S(congruence_kind::left);
      S.short_rules(p);
      REQUIRE(S.number_of_congruences(14) == 9);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "015",
                          "temperley_lieb_monoid(4) from presentation",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    p.alphabet(3);
    for (auto const& rel : temperley_lieb_monoid(4)) {
      p.add_rule_and_check(rel.first.cbegin(),
                           rel.first.cend(),
                           rel.second.cbegin(),
                           rel.second.cend());
    }
    {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p);
      REQUIRE(S.number_of_congruences(14) == 79);
    }
    {
      Sims1_ S(congruence_kind::left);
      S.short_rules(p);
      REQUIRE(S.number_of_congruences(14) == 79);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "016",
                          "fp semigroup containing given pairs #1",
                          "[quick][low-index]") {
    auto rg = ReportGuard(false);

    Presentation<word_type> p;
    p.contains_empty_word(true);

    p.alphabet({01});
    presentation::add_rule_and_check(p, {000}, {0});
    presentation::add_rule_and_check(p, {11}, {1});
    presentation::add_rule_and_check(p, {0101}, {0});
    Presentation<word_type> e;
    e.contains_empty_word(true);

    e.alphabet({01});
    presentation::add_rule_and_check(e, {0}, {1});
    Sims1_ S(congruence_kind::right);
    S.short_rules(p).extra(e);
    REQUIRE(S.number_of_congruences(5) == 2);
    check_extra(congruence_kind::right, p, e, 5);
    check_extra(congruence_kind::left, p, e, 5);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "017",
                          "fp semigroup containing given pairs #2",
                          "[quick][low-index]") {
    auto rg = ReportGuard(false);

    Presentation<word_type> p;
    p.contains_empty_word(true);

    p.alphabet({01});
    presentation::add_rule_and_check(p, {000}, {0});
    presentation::add_rule_and_check(p, {11}, {1});
    presentation::add_rule_and_check(p, {0101}, {0});
    Presentation<word_type> e;
    e.contains_empty_word(true);

    e.alphabet({01});
    presentation::add_rule_and_check(e, {01}, {1});
    Sims1_ T(congruence_kind::right);
    T.short_rules(p).extra(e);
    REQUIRE(T.number_of_congruences(5) == 2);
    check_extra(congruence_kind::right, p, e, 5);
    check_extra(congruence_kind::left, p, e, 5);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "018",
                          "fp semigroup containing given pairs #3",
                          "[quick][low-index]") {
    auto rg = ReportGuard(false);

    Presentation<word_type> p;
    p.contains_empty_word(true);

    p.alphabet({01});
    presentation::add_rule_and_check(p, {000}, {0});
    presentation::add_rule_and_check(p, {11}, {1});
    presentation::add_rule_and_check(p, {0101}, {0});
    Presentation<word_type> e;
    e.contains_empty_word(true);

    e.alphabet({01});
    presentation::add_rule_and_check(e, {0101}, {0});
    {
      Sims1_ T(congruence_kind::right);
      T.short_rules(p).extra(e);
      REQUIRE(T.number_of_congruences(5) == 6);
    }
    {
      Sims1_ T(congruence_kind::left);
      T.short_rules(p).extra(e);
      REQUIRE(T.number_of_congruences(5) == 9);  // Verified with GAP
    }
    check_extra(congruence_kind::right, p, e, 5);
    check_extra(congruence_kind::left, p, e, 5);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "019",
                          "ToddCoxeter failing example",
                          "[quick][low-index]") {
    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");

    Presentation<std::string> e;
    e.alphabet(p.alphabet());
    presentation::add_rule_and_check(p, "a""A");
    presentation::add_rule_and_check(p, "a""b");

    Sims1_ S(congruence_kind::right);
    S.short_rules(p).extra(e);
    REQUIRE(S.number_of_congruences(3) == 2);

    check_extra(congruence_kind::right, S.short_rules(), S.extra(), 3);
    check_extra(congruence_kind::left, S.short_rules(), S.extra(), 3);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "020",
                          "fp example 2",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    p.alphabet({012});
    presentation::add_rule_and_check(p, {010}, {00});
    presentation::add_rule_and_check(p, {22}, {00});
    presentation::add_rule_and_check(p, {000}, {00});
    presentation::add_rule_and_check(p, {21}, {12});
    presentation::add_rule_and_check(p, {20}, {00});
    presentation::add_rule_and_check(p, {11}, {1});
    presentation::add_rule_and_check(p, {02}, {00});

    Presentation<word_type> e;
    e.alphabet(p.alphabet());
    presentation::add_rule_and_check(e, {1}, {00});
    check_extra(congruence_kind::right, p, e, 11);
    check_extra(congruence_kind::left, p, e, 11);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1""021""exceptions""[quick][low-index]") {
    Presentation<word_type> p;
    p.alphabet({012});
    presentation::add_rule_and_check(p, {010}, {00});

    Presentation<word_type> e;
    e.alphabet({01});
    REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).short_rules(p).extra(e),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(
        Sims1_(congruence_kind::right).short_rules(p).long_rules(e),
        LibsemigroupsException);
    REQUIRE_THROWS_AS(
        Sims1_(congruence_kind::right).long_rules(p).short_rules(e),
        LibsemigroupsException);
    REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).long_rules(p).extra(e),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).extra(p).short_rules(e),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(Sims1_(congruence_kind::right).extra(p).long_rules(e),
                      LibsemigroupsException);
    REQUIRE_NOTHROW(Sims1_(congruence_kind::right).extra(p).extra(e));
    REQUIRE_NOTHROW(
        Sims1_(congruence_kind::right).short_rules(p).short_rules(e));
    REQUIRE_NOTHROW(Sims1_(congruence_kind::right).long_rules(p).long_rules(e));
    REQUIRE_THROWS_AS(Sims1_(congruence_kind::twosided),
                      LibsemigroupsException);
    Sims1_ S(congruence_kind::right);
    REQUIRE_THROWS_AS(S.number_of_threads(0), LibsemigroupsException);
    RepOrc ro;
    REQUIRE_THROWS_AS(ro.number_of_threads(0), LibsemigroupsException);
    MinimalRepOrc mro;
    REQUIRE_THROWS_AS(mro.number_of_threads(0), LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "022",
                          "singular_brauer_monoid(4) (Maltcev-Mazorchuk)",
                          "[extreme][sims1]") {
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(singular_brauer_monoid(4));
    REQUIRE(p.alphabet().size() == 12);
    REQUIRE(presentation::length(p) == 660);

    REQUIRE(*presentation::shortest_rule(p) == word_type({0}));
    REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({3}));

    presentation::remove_redundant_generators(p);

    presentation::remove_duplicate_rules(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);

    REQUIRE(presentation::shortest_rule_length(p) == 3);
    REQUIRE(*presentation::shortest_rule(p) == word_type({00}));
    REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({0}));

    REQUIRE(presentation::longest_rule_length(p) == 6);
    REQUIRE(*presentation::longest_rule(p) == word_type({048}));
    REQUIRE(*(presentation::longest_rule(p) + 1) == word_type({028}));

    REQUIRE(p.alphabet().size() == 6);
    REQUIRE(presentation::length(p) == 462);
    REQUIRE(p.rules.size() == 186);

    p.contains_empty_word(true);
    p.validate();

    MinimalRepOrc orc;
    auto          d = orc.short_rules(p)
                 .target_size(82)
                 .number_of_threads(std::thread::hardware_concurrency())
                 .report_interval(1'999)
                 .digraph();
    REQUIRE(d.number_of_nodes() == 18);
    REQUIRE(orc.target_size() == 82);

    p.contains_empty_word(false);

    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.short_rules().rules.size() == 186);

    REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
                .number_of_congruences(81)
            == 601'265);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "023",
                          "Brauer(4) from FroidurePin",
                          "[extreme][sims1]") {
    auto                     rg = ReportGuard(true);
    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({{12}, {3, -3}, {4, -4}, {-1, -2}}));
    REQUIRE(S.size() == 105);

    auto p = make<Presentation<word_type>>(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(p.rules.size() == 86);
    do {
      auto it = presentation::redundant_rule(p, std::chrono::milliseconds(100));
      p.rules.erase(it, it + 2);
    } while (presentation::length(p) > 300);
    presentation::replace_subword(p, presentation::longest_common_subword(p));

    Sims1_ C(congruence_kind::right);
    C.short_rules(p).long_rule_length(8);
    REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
                .number_of_congruences(105)
            == 103'406);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "024",
                          "brauer_monoid(4) (Kudryavtseva-Mazorchuk)",
                          "[extreme][sims1]") {
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(brauer_monoid(4));
    REQUIRE(p.alphabet().size() == 7);
    REQUIRE(presentation::length(p) == 182);
    presentation::remove_duplicate_rules(p);
    REQUIRE(presentation::length(p) == 162);
    presentation::reduce_complements(p);
    REQUIRE(presentation::length(p) == 159);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);
    REQUIRE(p.rules.size() == 86);

    auto d = MinimalRepOrc().short_rules(p).target_size(105).digraph();
    REQUIRE(d.number_of_nodes() == 22);
    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
    REQUIRE(
        d
        == action_digraph_helper::make<uint32_t>(
            22, {{0010232},        {1405637},
                 {2222222},        {3839637},
                 {414106211},      {510511227},
                 {668126313},      {711971337},
                 {836146311},      {914731237},
                 {105154121611},   {1171711131611},
                 {1212186121613},  {13131913132013},
                 {149218122011},   {151510152162},
                 {16181617121611}, {1721111661611},
                 {1816122112167},  {19201320132013},
                 {20192019132013}, {211714186207}}));

    auto S = make<FroidurePin<Transf<0, node_type>>>(d);
    REQUIRE(S.size() == 105);
    REQUIRE(S.generator(0) == Transf<0, node_type>::identity(22));
    REQUIRE(
        S.generator(1)
        == Transf<0, node_type>({04,  2,  81,  106,  113,  145,
                                 71213915182116201917}));
    REQUIRE(
        S.generator(2)
        == Transf<0, node_type>({1,  0,  2,  3,  4,  5,  8,  9,  6,  7,  15,
                                 1718192110161112132014}));
    REQUIRE(S.generator(3)
            == Transf<0, node_type>({05,  29,  101,  127,  143411,
                                     613815171621201918}));
    REQUIRE(S.generator(4)
            == Transf<0, node_type>({2,  6,  2,  6,  6126136,  1212,
                                     1312131221261213136}));
    REQUIRE(
        S.generator(5)
        == Transf<0, node_type>({3,  3,  2,  3,  2,  2,  3,  3,  3,  3,  16,
                                 1616202016161616202020}));
    REQUIRE(
        S.generator(6)
        == Transf<0, node_type>({2,  7,  2,  7,  117,  137117,  11,
                                 111313112,  1111713137}));

    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
                .number_of_congruences(105)
            == 103'406);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "025",
                          "brauer_monoid(5) (Kudryavtseva-Mazorchuk)",
                          "[extreme][sims1]") {
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(brauer_monoid(5));

    REQUIRE(presentation::length(p) == 295);

    presentation::remove_duplicate_rules(p);
    presentation::reduce_complements(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);
    REQUIRE(presentation::length(p) == 249);

    REQUIRE(presentation::shortest_rule_length(p) == 3);
    REQUIRE(*presentation::shortest_rule(p) == word_type({00}));
    REQUIRE(*(presentation::shortest_rule(p) + 1) == word_type({0}));

    REQUIRE(p.alphabet().size() == 9);

    presentation::remove_redundant_generators(p);
    REQUIRE(p.alphabet() == word_type({12345678}));
    REQUIRE(p.alphabet().size() == 8);
    REQUIRE(presentation::length(p) == 268);

    REQUIRE(*presentation::longest_rule(p) == word_type({1111}));
    REQUIRE(*(presentation::longest_rule(p) + 1) == word_type({11}));

    REQUIRE(presentation::longest_common_subword(p) == word_type({11}));
    p.alphabet(9);
    presentation::replace_subword(p, {11}, {0});
    REQUIRE(presentation::length(p) == 246);

    // This is just very long running (without e!) and I haven't managed to run
    // it to completion.
    Presentation<word_type> e;
    e.alphabet(9);
    presentation::add_rule_and_check(e, {0}, {1});

    auto d = MinimalRepOrc()
                 .short_rules(p)
                 .extra(e)
                 .target_size(945)
                 .number_of_threads(8)
                 .report_interval(100)
                 .digraph();
    // WARNING: the number below is not necessarily the minimal degree of an
    // action on right congruences, only the minimal degree of an action on
    // right congruences containing the pair {0}, {1}.
    REQUIRE(d.number_of_nodes() == 46);
    auto S = make<FroidurePin<Transf<0, node_type>>>(d);
    REQUIRE(S.size() == 945);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "026",
                          "uniform_block_bijection_monoid(4) (Fitzgerald)",
                          "[extreme][sims1]") {
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(
        uniform_block_bijection_monoid(4, author::FitzGerald));
    presentation::remove_duplicate_rules(p);
    presentation::reduce_complements(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);

    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_threads(std::thread::hardware_concurrency())
                .number_of_congruences(131)
            == 280'455);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "027",
                          "from https://mathoverflow.net/questions/423541/",
                          "[quick][sims1]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.contains_empty_word(false);
    p.alphabet("aAbBe");
    presentation::add_identity_rules(p, 'e');
    presentation::add_inverse_rules(p, "AaBbe"'e');
    presentation::add_rule_and_check(p, "aaa""e");
    presentation::add_rule_and_check(p, "baBBBABA""e");
    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(10) == 3);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "028",
                          "from https://mathoverflow.net/questions/423541/",
                          "[quick][sims1]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.contains_empty_word(true);
    p.alphabet("aAbB");
    presentation::add_inverse_rules(p, "AaBb");
    presentation::add_rule_and_check(p, "aaa""");
    presentation::add_rule_and_check(p, "baBBBABA""");
    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(10) == 3);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "029",
                          "fibonacci_semigroup(4, 6)",
                          "[standard][sims1][no-valgrind]") {
    auto rg = ReportGuard(true);  // for code coverage
    auto p  = make<Presentation<word_type>>(fibonacci_semigroup(46));
    presentation::remove_duplicate_rules(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);
    REQUIRE(presentation::length(p) == 30);
    REQUIRE(p.rules.size() == 12);
    REQUIRE(p.rules[0].size() + p.rules[1].size() == 5);

    Sims1_ C(congruence_kind::right);
    C.short_rules(p).report_interval(1);
    REQUIRE(C.number_of_congruences(3) == 5);

    C.number_of_threads(2);
    REQUIRE(C.number_of_congruences(3) == 5);
    REQUIRE_THROWS_AS(C.find_if(0, [](auto const&) { return true; }),
                      LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "030",
                          "presentation with one free generator",
                          "[quick][sims1]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.alphabet(4);
    presentation::add_rule(p, {121}, {11});
    presentation::add_rule(p, {33}, {11});
    presentation::add_rule(p, {111}, {11});
    presentation::add_rule(p, {32}, {23});
    presentation::add_rule(p, {31}, {11});
    presentation::add_rule(p, {22}, {2});
    presentation::add_rule(p, {13}, {11});
    p.validate();
    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(2) == 67);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "031",
                          "presentation with non-zero index generators",
                          "[quick][sims1]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    presentation::add_rule(p, {121}, {11});
    presentation::add_rule(p, {33}, {11});
    presentation::add_rule(p, {111}, {11});
    presentation::add_rule(p, {32}, {23});
    presentation::add_rule(p, {31}, {11});
    presentation::add_rule(p, {22}, {2});
    presentation::add_rule(p, {13}, {11});
    p.alphabet_from_rules();
    p.validate();

    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(2) == 7);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "032",
                          "presentation with empty word",
                          "[quick][sims1]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    //          a  A  b  B  c  C
    p.alphabet({012345});
    presentation::add_inverse_rules(p, {103254});
    presentation::add_rule_and_check(p, {00504}, {});
    presentation::add_rule_and_check(p, {0422152}, {});
    presentation::add_rule_and_check(p, {1302444}, {});
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);
    REQUIRE(S.number_of_congruences(3) == 14);
    REQUIRE(S.number_of_congruences(4) == 14);
    REQUIRE(S.number_of_congruences(5) == 14);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1""033""constructors""[quick][sims1]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    //          a  A  b  B  c  C
    p.alphabet({012345});
    presentation::add_inverse_rules(p, {103254});
    presentation::add_rule_and_check(p, {00504}, {});
    presentation::add_rule_and_check(p, {0422152}, {});
    presentation::add_rule_and_check(p, {1302444}, {});
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);

    Sims1_ T(S);
    REQUIRE(S.number_of_congruences(3) == 14);
    REQUIRE(T.number_of_congruences(3) == 14);

    Sims1_ U(std::move(S));
    REQUIRE(U.number_of_congruences(3) == 14);
    REQUIRE(T.number_of_congruences(3) == 14);

    S = U;
    REQUIRE(S.number_of_congruences(3) == 14);

    S = std::move(U);
    REQUIRE(S.number_of_congruences(3) == 14);

    Presentation<word_type> e;
    e.alphabet({0125});

    Sims1_ C(congruence_kind::right);
    REQUIRE_THROWS_AS(C.short_rules(p).extra(e), LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1""034""split_at""[quick][sims1]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    //          a  A  b  B  c  C
    p.alphabet({012345});
    presentation::add_inverse_rules(p, {103254});
    presentation::add_rule_and_check(p, {00504}, {});
    presentation::add_rule_and_check(p, {0422152}, {});
    presentation::add_rule_and_check(p, {1302444}, {});
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);

    REQUIRE_THROWS_AS(S.split_at(10), LibsemigroupsException);
    S.split_at(0);

    REQUIRE(S.short_rules().rules.empty());

    for (size_t i = 0; i <= p.rules.size() / 2; ++i) {
      S.split_at(i);
      REQUIRE(S.short_rules().rules.size() == 2 * i);
    }
    REQUIRE(S.short_rules().rules.size() == p.rules.size());
    for (size_t i = p.rules.size() / 2; i > 0; --i) {
      S.split_at(i);
      REQUIRE(S.short_rules().rules.size() == 2 * i);
    }
    S.split_at(7);
    REQUIRE(S.number_of_congruences(3) == 14);
  }

#ifdef LIBSEMIGROUPS_ENABLE_STATS
  LIBSEMIGROUPS_TEST_CASE("Sims1""035""stats""[quick][sims1]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);

    //          a  A  b  B  c  C
    p.alphabet({012345});
    presentation::add_inverse_rules(p, {103254});
    presentation::add_rule_and_check(p, {00504}, {});
    presentation::add_rule_and_check(p, {0422152}, {});
    presentation::add_rule_and_check(p, {1302444}, {});
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);

    std::stringbuf buff;
    std::ostream   os(&buff);
    S.number_of_congruences(2);
    // REQUIRE(S.stats().max_pending != 0);
    os << S.cbegin(3).stats();  // Also does not do anything visible
  }
#endif

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "036",
                          "check iterator requirements",
                          "[quick][sims1]") {
    Presentation<word_type> p;
    p.contains_empty_word(true);
    p.alphabet({01});
    presentation::add_rule_and_check(p, {000}, {0});
    presentation::add_rule_and_check(p, {11}, {1});
    presentation::add_rule_and_check(p, {0101}, {0});

    {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p);
      verify_forward_iterator_requirements(S.cbegin(10));
      auto it = S.cbegin(10);
      REQUIRE(it->number_of_nodes() == 10);
    }
    {
      Sims1_ S(congruence_kind::left);
      S.short_rules(p);
      verify_forward_iterator_requirements(S.cbegin(10));
      auto it = S.cbegin(10);
      REQUIRE(it->number_of_nodes() == 10);
    }
  }

  // Takes about 30s
  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "037",
                          "rectangular_band(9, 2)",
                          "[extreme][sims1]") {
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(rectangular_band(92));
    presentation::remove_duplicate_rules(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);

    REQUIRE(MinimalRepOrc()
                .short_rules(p)
                .target_size(18)
                .number_of_threads(std::thread::hardware_concurrency())
                .digraph()
                .number_of_nodes()
            == 0);
    p.contains_empty_word(true);
    auto mro = MinimalRepOrc().short_rules(p).target_size(19).number_of_threads(
        std::thread::hardware_concurrency());
    auto d = mro.digraph();
    REQUIRE(d.number_of_nodes() == 11);
    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
    auto S = make<FroidurePin<Transf<0, node_type>>>(d);
    S.add_generator(S.generator(0).identity());
    REQUIRE(S.size() == 19);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "038",
                          "partition_monoid(3) - minimal o.r.c. rep",
                          "[extreme][sims1]") {
    auto rg = ReportGuard(true);
    auto p
        = make<Presentation<word_type>>(partition_monoid(3, author::Machine));
    REQUIRE(!p.contains_empty_word());
    REQUIRE(p.alphabet() == word_type({01234}));

    auto d = RepOrc()
                 .short_rules(p)
                 .target_size(203)
                 .min_nodes(1)
                 .max_nodes(22)
                 .number_of_threads(2)
                 .digraph();
    REQUIRE(d.number_of_nodes() == 22);

    auto mro
        = MinimalRepOrc().short_rules(p).target_size(203).number_of_threads(4);
    d = mro.digraph();

    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
    auto S = make<FroidurePin<Transf<0, node_type>>>(d);
    REQUIRE(S.size() == 203);
    // The actual digraph obtained is non-deterministic because we just take
    // whichever one is found first.
    // REQUIRE(
    //     d
    //     == action_digraph_helper::make<node_type>(
    //         22,
    //         {{0, 1, 0, 2, 0},      {1, 3, 3, 4, 5},      {2, 6, 6, 2, 0},
    //          {3, 0, 1, 2, 5},      {4, 4, 4, 4, 4},      {5, 5, 5, 7, 5},
    //          {6, 8, 2, 4, 0},      {7, 9, 9, 7, 5},      {8, 2, 8, 4, 10},
    //          {9, 10, 7, 11, 5},    {10, 7, 10, 12, 10},  {11, 13, 11, 11,
    //          14}, {12, 11, 13, 12, 10}, {13, 12, 12, 15, 10}, {14, 16, 14,
    //          11, 14}, {15, 15, 15, 15, 17}, {16, 18, 18, 19, 5},  {17, 19,
    //          17, 15, 17}, {18, 14, 16, 12, 5},  {19, 20, 20, 19, 21}, {20,
    //          17, 19, 15, 21}, {21, 21, 21, 19, 21}}));
    REQUIRE(d.number_of_nodes() == 22);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "039",
                          "temperley_lieb_monoid(n) - n = 3 .. 6, minimal rep",
                          "[standard][sims1]") {
    auto rg = ReportGuard(false);

    std::array<uint64_t, 11const sizes
        = {012514421324291'430, 4'86216'796};
    std::array<uint64_t, 11const min_degrees
        = {0024710202963910};
    // The values 63 and 91 are not verified

    for (size_t n = 3; n <= 6; ++n) {
      auto p = make<Presentation<word_type>>(temperley_lieb_monoid(n));
      // There are no relations containing the empty word so we just manually
      // add it.
      p.contains_empty_word(true);
      auto orc
          = MinimalRepOrc().short_rules(p).number_of_threads(2).target_size(
              sizes[n]);

      auto d = orc.digraph();
      REQUIRE(orc.target_size() == sizes[n]);
      REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
      auto S = make<FroidurePin<Transf<0, node_type>>>(d);
      S.add_generator(S.generator(0).identity());
      REQUIRE(S.size() == sizes[n]);
      REQUIRE(d.number_of_nodes() == min_degrees[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "040",
                          "TransitiveGroup(10, 32) - minimal rep",
                          "[quick][sims1]") {
    auto                    rg = ReportGuard(false);
    Presentation<word_type> p;
    p.contains_empty_word(true);
    p.alphabet({01234});
    presentation::add_rule_and_check(p, {00}, {});
    presentation::add_rule_and_check(p, {11}, {});
    presentation::add_rule_and_check(p, {22}, {});
    presentation::add_rule_and_check(p, {33}, {});
    presentation::add_rule_and_check(p, {44}, {});
    presentation::add_rule_and_check(p, {010101}, {});
    presentation::add_rule_and_check(p, {0202}, {});
    presentation::add_rule_and_check(p, {0303}, {});
    presentation::add_rule_and_check(p, {0404}, {});
    presentation::add_rule_and_check(p, {121212}, {});
    presentation::add_rule_and_check(p, {1313}, {});
    presentation::add_rule_and_check(p, {1414}, {});
    presentation::add_rule_and_check(p, {232323}, {});
    presentation::add_rule_and_check(p, {2424}, {});
    presentation::add_rule_and_check(p, {343434}, {});
    REQUIRE(MinimalRepOrc()
                .short_rules(p)
                .target_size(0)
                .digraph()
                .number_of_nodes()
            == 0);

    REQUIRE(RepOrc()
                .short_rules(p)
                .min_nodes(0)
                .max_nodes(0)
                .target_size(0)
                .digraph()
                .number_of_nodes()
            == 0);

    auto d = MinimalRepOrc().short_rules(p).target_size(720).digraph();
    REQUIRE(d.number_of_nodes() == 6);
    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "041",
                          "rectangular_band(4, 4) - minimal o.r.c. rep",
                          "[standard][sims1]") {
    auto rg = ReportGuard(false);
    auto p  = make<Presentation<word_type>>(rectangular_band(44));
    p.contains_empty_word(true);
    auto d = MinimalRepOrc()
                 .short_rules(p)
                 .number_of_threads(2)
                 .target_size(17)
                 .digraph();
    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
    auto S = make<FroidurePin<Transf<0, node_type>>>(d);
    REQUIRE(S.size() == 16);
    REQUIRE(d.number_of_nodes() == 7);

    p.contains_empty_word(false);
    d = MinimalRepOrc()
            .short_rules(p)
            .target_size(16)
            .number_of_threads(2)
            .digraph();
    REQUIRE(d.number_of_nodes() == 0);
  }

  // unbuffer -p ./test_sims1 "[042]" | ag -v FroidurePin
  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "042",
                          "rectangular_band(m, n) - m = 1 .. 5, n = 1 .. 5",
                          "[fail][sims1]") {
    // This doesn't fail it's just very extreme
    std::vector<std::array<size_t, 6>> results = {{000000},
                                                  {022345},
                                                  {034556},
                                                  {045667},
                                                  {056778},
                                                  {067889}};

    auto rg = ReportGuard(true);
    for (size_t m = 1; m <= 5; ++m) {
      for (size_t n = 1; n <= 5; ++n) {
        std::cout << std::string(72'#') << "\n"
                  << "CASE m, n = " << m << ", " << n << "\n"
                  << std::string(72'#') << std::endl;

        auto p = make<Presentation<word_type>>(rectangular_band(m, n));
        p.contains_empty_word(true);
        auto d = MinimalRepOrc()
                     .short_rules(p)
                     .target_size(m * n + 1)
                     .number_of_threads(6)
                     .digraph();
        REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
        auto S = make<FroidurePin<Transf<0, node_type>>>(d);
        REQUIRE(S.size() == m * n);
        REQUIRE(d.number_of_nodes() == results[m][n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "043",
                          "rectangular_band(2, 2) - with and without identity",
                          "[quick][sims1]") {
    auto   rg = ReportGuard(false);
    auto   p  = make<Presentation<word_type>>(rectangular_band(22));
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);

    REQUIRE(S.number_of_congruences(4) == 6);

    p.contains_empty_word(true);

    Sims1_ T(congruence_kind::right);
    T.short_rules(p);
    REQUIRE(T.number_of_congruences(5) == 9);

    auto it = S.cbegin(4);

    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1111}, {1111}}));  // Good
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1112}, {1112}, {1112}}));  // Good
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1121}, {1111}, {2222}}));  // Good
    REQUIRE(
        *it++
        == action_digraph_helper::make<node_type>(
            5,
            {{1121}, {1111}, {2223}, {2223}}));  // Good
    REQUIRE(
        *it++
        == action_digraph_helper::make<node_type>(
            5,
            {{1123}, {1113}, {2222}, {1113}}));  // Good
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(5,
                                                      {{1123},
                                                       {1113},
                                                       {2224},
                                                       {1113},
                                                       {2224}}));  // Good
    REQUIRE(it->number_of_nodes() == 0);

    it = T.cbegin(5);

    REQUIRE(*it++ == action_digraph_helper::make<node_type>(5, {{0000}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{0001}, {0001}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1110}, {1110}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1111}, {1111}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1112}, {1112}, {1112}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1121}, {1111}, {2222}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1121}, {1111}, {2223}, {2223}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(
                5, {{1123}, {1113}, {2222}, {1113}}));
    REQUIRE(*it++
            == action_digraph_helper::make<node_type>(5,
                                                      {{1123},
                                                       {1113},
                                                       {2224},
                                                       {1113},
                                                       {2224}}));
    REQUIRE(it->number_of_nodes() == 0);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "044",
                          "trivial group - minimal o.r.c. rep",
                          "[quick][sims1]") {
    auto                      rg = ReportGuard(false);
    Presentation<std::string> p;
    p.alphabet("aAbB");
    p.contains_empty_word(true);
    presentation::add_inverse_rules(p, "AaBb");
    presentation::add_rule_and_check(p, "ab""");
    presentation::add_rule_and_check(p, "abb""");

    Sims1_ S(congruence_kind::right);
    S.short_rules(p);

    REQUIRE(S.number_of_congruences(10) == 1);
    auto d = MinimalRepOrc().short_rules(p).target_size(1).digraph();
    REQUIRE(d.number_of_nodes() == 1);
    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "045",
                          "right zero semigroup - minimal o.r.c. rep",
                          "[quick][sims1]") {
    // This is an example of a semigroup with a strictly cyclic faithful
    // right representation.
    auto         rg = ReportGuard(false);
    size_t const n  = 5;
    auto         p  = make<Presentation<word_type>>(rectangular_band(1, n));
    auto         d  = MinimalRepOrc().short_rules(p).target_size(n).digraph();
    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
    auto S = make<FroidurePin<Transf<0, node_type>>>(d);
    REQUIRE(S.size() == n);
    REQUIRE(d.number_of_nodes() == 5);
  }

  LIBSEMIGROUPS_TEST_CASE(
      "Sims1",
      "046",
      "semigroup with faithful non-strictly cyclic action of right congruence",
      "[quick][sims1]") {
    // Found with Smallsemi, this example is minimal wrt size of the semigroup.

    auto rg = ReportGuard(false);

    FroidurePin<Transf<6>> S({Transf<6>::make({002141}),
                              Transf<6>::make({002343}),
                              Transf<6>::make({022044})});

    REQUIRE(S.size() == 5);
    auto p = make<Presentation<word_type>>(S);
    auto d = MinimalRepOrc().short_rules(p).target_size(5).digraph();
    REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
    REQUIRE(d.number_of_nodes() == 4);
    REQUIRE(d
            == action_digraph_helper::make<uint32_t>(
                4, {{223}, {012}, {222}, {333}}));
    auto T = make<FroidurePin<Transf<4>>>(d);
    REQUIRE(T.generator(0) == Transf<4>({2023}));
    REQUIRE(T.generator(1) == Transf<4>({2123}));
    REQUIRE(T.generator(2) == Transf<4>({3223}));
    REQUIRE(T.size() == 5);

    auto dd = action_digraph_helper::make<uint8_t>(5,
                                                   {{00000},
                                                    {00002},
                                                    {22222},
                                                    {01230},
                                                    {44444}});

    REQUIRE(!action_digraph_helper::is_strictly_cyclic(dd));
    REQUIRE(dd.number_of_nodes() == 5);
    auto U = make<FroidurePin<Transf<5>>>(dd);
    REQUIRE(U.size() == 5);

    Sims1_ C(congruence_kind::right);
    C.short_rules(p);
    REQUIRE(C.number_of_congruences(5) == 9);
    uint64_t strictly_cyclic_count     = 0;
    uint64_t non_strictly_cyclic_count = 0;

    for (auto it = C.cbegin(5); it != C.cend(5); ++it) {
      auto W = make<FroidurePin<Transf<0, node_type>>>(
          *it, 1, it->number_of_active_nodes());
      if (p.contains_empty_word()) {
        auto one = W.generator(0).identity();
        if (!W.contains(one)) {
          W.add_generator(one);
        }
      }
      if (W.size() == 5) {
        auto result = *it;
        result.induced_subdigraph(1, result.number_of_active_nodes());
        result.number_of_active_nodes(result.number_of_active_nodes() - 1);
        if (action_digraph_helper::is_strictly_cyclic(result)) {
          strictly_cyclic_count++;
        } else {
          REQUIRE(W.generator(0) == Transf<0, node_type>({30234}));
          REQUIRE(W.generator(1) == Transf<0, node_type>({31234}));
          REQUIRE(W.generator(2) == Transf<0, node_type>({43234}));
          REQUIRE(
              result
              == action_digraph_helper::make<uint32_t>(
                  5, {{334}, {013}, {222}, {333}, {444}}));
          non_strictly_cyclic_count++;
        }
      }
    }
    REQUIRE(strictly_cyclic_count == 2);
    REQUIRE(non_strictly_cyclic_count == 1);
  }

  // Takes about 3 to 4 minutes
  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "047",
                          "rectangular_band(m, n) - m = 1 .. 5, n = 1 .. 5",
                          "[fail][sims1]") {
    // This doesn't fail it's just very extreme
    std::vector<std::array<size_t, 7>> left
        = {{000000},
           {000000},
           {00622944542'430},
           {00302051'555, 12'880},
           {002404'065, 72'4651'353'390},
           {002'756, 148'7728'174'244456'876'004}};

    // Seems like the m,n-th entry of the table above is:
    // {m, n} ->  Sum([0 .. n], k -> Bell(m)^k*Stirling2(n, k));

    auto rg = ReportGuard(true);
    for (size_t m = 2; m <= 5; ++m) {
      for (size_t n = 2; n <= 6; ++n) {
        std::cout << std::string(72'#') << "\n"
                  << "CASE m, n = " << m << ", " << n << "\n"
                  << std::string(72'#') << std::endl;

        auto   p = make<Presentation<word_type>>(rectangular_band(m, n));
        Sims1_ S(congruence_kind::left);
        S.short_rules(p);
        REQUIRE(S.number_of_threads(4).number_of_congruences(m * n)
                == left[m][n]);
        Sims1_ T(congruence_kind::right);
        T.short_rules(p);
        REQUIRE(T.number_of_threads(4).number_of_congruences(m * n)
                == left[n][m]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "048",
                          "stellar_monoid(n) n = 3 .. 4",
                          "[fail][sims1][babbage]") {
    std::array<uint64_t, 10const size      = {0001665};
    std::array<uint64_t, 10const num_left  = {0001'550, 0};
    std::array<uint64_t, 10const num_right = {0001'521, 0};

    for (size_t n = 3; n < 5; ++n) {
      auto p = make<Presentation<word_type>>(rook_monoid(n, 0));
      auto q = make<Presentation<word_type>>(stellar_monoid(n));
      p.rules.insert(p.rules.end(), q.rules.cbegin(), q.rules.cend());
      REQUIRE(p.alphabet().size() == n + 1);
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(size[n]) == num_left[n]);
      }
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(size[n]) == num_right[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "049",
                          "stylic_monoid(n) n = 3, 4",
                          "[fail][sims1]") {
    auto                           rg   = ReportGuard(true);
    std::array<uint64_t, 10const size = {0001451};
    //               1505s
    std::array<uint64_t, 10const num_left  = {0001'214, 1'429'447'174};
    std::array<uint64_t, 10const num_right = {0001'214, 1'429'455'689};

    for (size_t n = 3; n < 5; ++n) {
      auto p = make<Presentation<word_type>>(stylic_monoid(n));
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(size[n]) == num_right[n]);
      }
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(size[n]) == num_left[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "050",
                          "(2, 3, 7)-triangle group - index 50",
                          "[extreme][sims1]") {
    auto                      rg = ReportGuard(true);
    Presentation<std::string> p;
    p.contains_empty_word(true);
    p.alphabet("xy");
    presentation::add_rule_and_check(p, "xx""");
    presentation::add_rule_and_check(p, "yyy""");
    presentation::add_rule_and_check(p, "xyxyxyxyxyxyxy""");
    Sims1_ S(congruence_kind::right);
    S.short_rules(p).number_of_threads(4);
    REQUIRE(S.number_of_congruences(50) == 75'971);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "051",
                          "Heineken group - index 10",
                          "[extreme][sims1]") {
    auto                      rg = ReportGuard(true);
    Presentation<std::string> p;
    p.contains_empty_word(true);
    p.alphabet("xXyY");
    presentation::add_inverse_rules(p, "XxYy");
    presentation::add_rule_and_check(p, "yXYYxyYYxyyXYYxyyXyXYYxy""x");

    Presentation<std::string> q;
    q.alphabet("xXyY");
    presentation::add_rule_and_check(
        q, "YxyyXXYYxyxYxyyXYXyXYYxxyyXYXyXYYxyx""y");

    Sims1_ S(congruence_kind::right);
    S.short_rules(p).long_rules(q).number_of_threads(8);
    REQUIRE(S.number_of_congruences(10) == 1);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "052",
                          "temperley_lieb_monoid(n) - n = 3 .. 6",
                          "[extreme][low-index][babbage]") {
    std::array<uint64_t, 10const size = {00051442132429};
    std::array<uint64_t, 10const num_right
        = {0009792'157, 4'326'459};

    auto rg = ReportGuard(true);
    for (size_t n = 3; n < 7; ++n) {
      auto p = make<Presentation<word_type>>(temperley_lieb_monoid(n));
      p.contains_empty_word(true);
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(size[n]) == num_right[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "053",
                          "partial_transformation_monoid(3)",
                          "[extreme][low-index]") {
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(
        partial_transformation_monoid(3, author::Machine));
    {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(64) == 92'703);
    }
    {
      Sims1_ S(congruence_kind::left);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(64) == 371);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "054",
                          "partial_transformation_monoid(4) from FroidurePin",
                          "[fail][low-index]") {
    using Transf_ = Transf<5>;
    auto rg       = ReportGuard(true);

    FroidurePin<Transf_> S({Transf_({10234}),
                            Transf_({30124}),
                            Transf_({41234}),
                            Transf_({11234})});
    REQUIRE(S.size() == 625);
    auto   p = make<Presentation<word_type>>(S);
    Sims1_ C(congruence_kind::left);
    REQUIRE(presentation::longest_rule_length(p) == 18);
    REQUIRE(presentation::shortest_rule_length(p) == 3);

    presentation::remove_duplicate_rules(p);
    presentation::remove_trivial_rules(p);
    presentation::sort_each_rule(p);
    presentation::sort_rules(p);

    word_type w = presentation::longest_common_subword(p);
    while (!w.empty()) {
      presentation::replace_subword(p, w);
      w = presentation::longest_common_subword(p);
    }

    REQUIRE(presentation::length(p) == 1414);
    REQUIRE(presentation::longest_rule_length(p) == 6);

    C.short_rules(p).long_rule_length(6).number_of_threads(8).report_interval(
        100);
    REQUIRE(C.number_of_congruences(625) == 10);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "055",
                          "plactic_monoid(3) up to index 8",
                          "[extreme][low-index][plactic]") {
    std::array<uint64_t, 9const num
        = {01294846'896, 103'2041'773'36035'874'182849'953'461};
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(plactic_monoid(3));
    for (size_t n = 2; n < 9; ++n) {
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "056",
                          "plactic_monoid(4) up to index 6",
                          "[extreme][low-index][plactic]") {
    std::array<uint64_t, 8const num
        = {01672'794, 106'2644'795'980278'253'84120'855'970'290};
    // Last value too 1h34m to compute so is not included.
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(plactic_monoid(4));
    for (size_t n = 2; n < 7; ++n) {
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "057",
                          "plactic_monoid(5) up to index 5",
                          "[extreme][low-index][plactic]") {
    std::array<uint64_t, 7const num
        = {0114514'851, 1'496'113, 198'996'912, 37'585'675'984};
    // Last value took 5h11m to compute
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(plactic_monoid(5));
    for (size_t n = 3; n < 6; ++n) {
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "058",
                          "plactic_monoid(6) up to index 4",
                          "[extreme][low-index][plactic]") {
    std::array<uint64_t, 6const num
        = {0130377'409, 20'526'128, 7'778'840'717};
    // The last value took 4h5m to run and is omitted.
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(plactic_monoid(6));
    for (size_t n = 2; n < 5; ++n) {
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "059",
                          "plactic_monoid(7) up to index 3",
                          "[extreme][low-index][plactic]") {
    std::array<uint64_t, 5const num = {01621408'024, 281'600'130};
    // The last value took approx. 12m34s to run and is omitted from the
    // extreme test 12m34s to run and is omitted from the extreme test.
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(plactic_monoid(7));
    for (size_t n = 2; n < 4; ++n) {
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "060",
                          "plactic_monoid(8) up to index 3",
                          "[extreme][low-index][plactic]") {
    std::array<uint64_t, 4const num = {011'259, 2'201'564};
    auto                          rg  = ReportGuard(true);
    auto p = make<Presentation<word_type>>(plactic_monoid(8));
    for (size_t n = 2; n < 4; ++n) {
      {
        Sims1_ S(congruence_kind::right);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
      {
        Sims1_ S(congruence_kind::left);
        S.short_rules(p).number_of_threads(4);
        REQUIRE(S.number_of_congruences(n) == num[n]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "061",
                          "chinese_monoid(3) up to index 8",
                          "[extreme][low-index][chinese]") {
    std::array<uint64_t, 9const num
        = {01315598'904, 149'5292'860'01863'828'9381'654'488'307};
    // index 8 is doable and the value is included above, but it took about X
    // minutes to run, so isn't included in the loop below.
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(chinese_monoid(3));
    for (size_t n = 2; n < 8; ++n) {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(n) == num[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "062",
                          "chinese_monoid(4) up to index 6",
                          "[extreme][low-index][chinese]") {
    // n = 6 took between 3 and 4 minutes
    // n = 7 took 6h16m
    // both are omitted
    std::array<uint64_t, 8const num
        = {01793'809, 183'99510'759'706804'802'04577'489'765'654};
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(chinese_monoid(4));
    for (size_t n = 3; n < 7; ++n) {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(n) == num[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "063",
                          "chinese_monoid(5) up to index 5",
                          "[extreme][low-index][chinese]") {
    std::array<uint64_t, 7const num
        = {0119123'504, 3'382'921, 685'523'226, 199'011'439'587};

    // The last value took 21h32m and so is omitted
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(chinese_monoid(5));
    for (size_t n = 3; n < 6; ++n) {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(n) == num[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "064",
                          "chinese_monoid(6) up to index 4",
                          "[extreme][low-index][chinese]") {
    // 0 1 2 3 4
    std::array<uint64_t, 6const num
        = {01447137'694, 58'624'384, 40'823'448'867};
    // The last value took 9h54m to compute, and is omitted!
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(chinese_monoid(6));
    for (size_t n = 3; n < 5; ++n) {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(n) == num[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "065",
                          "chinese_monoid(7) up to index 4",
                          "[extreme][low-index][chinese]") {
    // Last value took about 50m to compute
    std::array<uint64_t, 5const num = {011'023, 786'949988'827'143};
    auto                          rg  = ReportGuard(true);
    auto p = make<Presentation<word_type>>(chinese_monoid(7));
    for (size_t n = 2; n < 4; ++n) {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(n) == num[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "066",
                          "chinese_monoid(8) up to index 3",
                          "[extreme][low-index][chinese]") {
    std::array<uint64_t, 4const num = {012'303, 4'459'599};
    auto                          rg  = ReportGuard(true);
    auto p = make<Presentation<word_type>>(chinese_monoid(8));
    for (size_t n = 2; n < 4; ++n) {
      Sims1_ S(congruence_kind::right);
      S.short_rules(p).number_of_threads(4);
      REQUIRE(S.number_of_congruences(n) == num[n]);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "067",
                          "FreeSemigroup(n) up to index 3",
                          "[extreme][low-index]") {
    // (27^n - 9^n)/2 - 12^n + 6^n
    std::array<uint64_t, 10const num = {0222980222432416904866};
    // 4 ^ n - 2 ^ n
    //  {0, 2, 13, 57, 241, 993, 4033};
    //    = {0, 1, 29, 249, 2'033, 16'353, 131'009};
    // = {0, 1, 830, 81'762, 7'008'614};
    auto rg = ReportGuard(true);
    for (size_t n = 2; n < 8; ++n) {
      Presentation<word_type> p;
      p.contains_empty_word(true);
      p.alphabet(n);
      Sims1_ S(congruence_kind::right);
      S.short_rules(p);
      REQUIRE(S.number_of_congruences(3) == num[n]);
    }

    // For n >= 1, a(n) is the number of deterministic, completely-defined,
    // initially-connected finite automata with n inputs and 3 unlabeled states.
    // A020522 counts similar automata with n inputs and 2 unlabeled states.

    // According to a comment by Nelma Moreira in A006689 and A006690, the
    // number of such automata with N inputs and M unlabeled states is Sum
    // (Product_{i=1..M-1} i^(f_i - f_{i-1} - 1)) * M^(M*N - f_{M-1} - 1), where
    // the sum is taken over integers f_1, ..., f_{M-1} satisfying 0 <= f_1 < N
    // and f_{i-1} < f_{i} < i*N for i = 2..M-1. (See Theorem 8 in Almeida,
    // Moreira, and Reis (2007). The value of f_0 is not relevant.) For this
    // sequence we have M = 3 unlabeled states, for A020522 we have M = 2
    // unlabeled states, for A006689 we have N = 2 inputs, and for A006690 we
    // have N = 3 inputs.
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1""068""RepOrc""[quick][low-index]") {
    auto rg = ReportGuard(false);

    auto p = make<Presentation<word_type>>(temperley_lieb_monoid(9));
    // There are no relations containing the empty word so we just manually
    // add it.
    p.contains_empty_word(true);
    RepOrc orc;
    // Check bad input
    auto d = orc.short_rules(p)
                 .min_nodes(100)
                 .max_nodes(90)
                 .target_size(4'862)
                 .digraph();
    REQUIRE(d.number_of_nodes() == 0);

    d = orc.short_rules(p)
            .min_nodes(80)
            .max_nodes(100)
            .target_size(4'862)
            .digraph();

    auto S = make<FroidurePin<Transf<0, node_type>>>(d);
    S.add_generator(S.generator(0).identity());
    REQUIRE(S.size() == 4'862);
    REQUIRE(orc.min_nodes() == 80);
    REQUIRE(orc.max_nodes() == 100);
    REQUIRE(orc.target_size() == 4'862);
    REQUIRE(orc.short_rules().rules.size() == 128);
    REQUIRE(orc.long_rules().rules.size() == 0);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "069",
                          "fp example 1",
                          "[quick][low-index]") {
    auto rg = ReportGuard(false);

    Presentation<word_type> p;
    p.contains_empty_word(true);
    p.alphabet({01});
    presentation::add_rule_and_check(p, {000}, {0});
    presentation::add_rule_and_check(p, {11}, {1});

    Presentation<word_type> q;
    q.contains_empty_word(true);
    q.alphabet({01});

    presentation::add_rule_and_check(q, {0101}, {0});

    Sims1_ S(congruence_kind::right);
    REQUIRE(S.short_rules(p)
                .long_rules(q)
                .number_of_threads(1)
                .number_of_congruences(5)
            == 6);
    S.long_rule_length(5);
    REQUIRE(S.long_rules().rules.size() == 2);
    REQUIRE(S.short_rules().rules.size() == 4);
    S.long_rule_length(4);
    REQUIRE(S.long_rules().rules.size() == 4);
    REQUIRE(S.short_rules().rules.size() == 2);

    S = Sims1_(congruence_kind::left);
    REQUIRE(S.short_rules(p)
                .long_rules(q)
                .number_of_threads(1)
                .number_of_congruences(5)
            == 9);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "070",
                          "temperley_lieb_monoid(3) - n = minimal rep "
                          "(single-threaded, reporting on)",
                          "[standard][sims1]") {
    auto rg = ReportGuard(true);

    for (size_t n = 3; n <= 3; ++n) {
      auto p = make<Presentation<word_type>>(temperley_lieb_monoid(n));
      // There are no relations containing the empty word so we just manually
      // add it.
      p.contains_empty_word(true);
      auto d = MinimalRepOrc()
                   .short_rules(p)
                   .number_of_threads(1)
                   .target_size(5)
                   .digraph();
      REQUIRE(action_digraph_helper::is_strictly_cyclic(d));
      auto S = make<FroidurePin<Transf<0, node_type>>>(d);
      S.add_generator(S.generator(0).identity());
      REQUIRE(S.size() == 5);
      REQUIRE(d.number_of_nodes() == 4);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "071",
                          "FreeSemigroup(2) up to index 4",
                          "[quick][low-index]") {
    auto                    rg = ReportGuard(true);
    Presentation<word_type> p;
    p.contains_empty_word(true);
    p.alphabet(2);
    Sims1_ S(congruence_kind::right);
    S.short_rules(p);
    REQUIRE(S.number_of_congruences(4) == 5'477);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "072",
                          "symmetric_group(n) for n = 4",
                          "[quick][low-index]") {
    std::array<uint64_t, 10const num = {0006301561'455};
    auto                           rg  = ReportGuard(false);
    size_t                         n   = 4;
    auto                           p
        = make<Presentation<word_type>>(symmetric_group(n, author::Carmichael));
    Sims1_ C(congruence_kind::right);
    C.short_rules(p).number_of_threads(4);
    REQUIRE(C.number_of_congruences(factorial(n)) == num[n]);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "073",
                          "corner case no generators + no relations",
                          "[quick][low-index]") {
    Presentation<word_type> p;
    p.alphabet(0);
    Sims1_ S(congruence_kind::right);
    REQUIRE_THROWS_AS(S.short_rules(p), LibsemigroupsException);
    REQUIRE_THROWS_AS(S.number_of_congruences(1), LibsemigroupsException);
    REQUIRE_THROWS_AS(S.cbegin(2), LibsemigroupsException);
    REQUIRE_THROWS_AS(S.cend(2), LibsemigroupsException);
    REQUIRE_THROWS_AS(S.find_if(2, [](auto) { return true; }),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(S.for_each(2, [](auto) {}), LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "074",
                          "monogenic_semigroup(m, r) for m, r = 1 .. 10",
                          "[fail][low-index]") {
    auto                                        rg = ReportGuard(false);
    std::vector<std::array<uint64_t, 11>> const num
        = {{1223242434},
           {2446484868},
           {3669612612912},
           {488128168161216},
           {5101015102010201520},
           {6121218122412241824},
           {7141421142814282128},
           {8161624163216322432},
           {9181827183618362736},
           {10202030204020403040}};

    // m * number of divisors of r

    for (size_t m = 1; m <= 10; ++m) {
      for (size_t r = 1; r <= 10; ++r) {
        // Cyclic groups
        auto p = make<Presentation<word_type>>(monogenic_semigroup(m, r));

        Sims1_ C(congruence_kind::right);
        C.short_rules(p);
        // std::cout << C.number_of_congruences(m + r) << ", ";
        REQUIRE(C.number_of_congruences(m + r) == num[m - 1][r - 1]);
      }
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "075",
                          "partial_transformation_monoid(4)",
                          "[fail][low-index]") {
    auto rg = ReportGuard(true);
    auto p  = make<Presentation<word_type>>(
        partial_transformation_monoid(4, author::Sutov));
    auto w = presentation::longest_common_subword(p);
    while (!w.empty()) {
      presentation::replace_subword(p, presentation::longest_common_subword(p));
      w = presentation::longest_common_subword(p);
    }

    presentation::sort_each_rule(p);
    presentation::sort_rules(p);
    presentation::remove_duplicate_rules(p);
    presentation::reduce_complements(p);
    presentation::remove_trivial_rules(p);

    do {
      auto it = presentation::redundant_rule(p, std::chrono::milliseconds(100));
      p.rules.erase(it, it + 2);
    } while (presentation::length(p) > 800);
    Sims1_ C(congruence_kind::left);
    C.short_rules(p).number_of_threads(4).report_interval(10);
    REQUIRE(C.number_of_congruences(624) == 0);
  }

  LIBSEMIGROUPS_TEST_CASE("Sims1",
                          "076",
                          "uninitialized RepOrc",
                          "[quick][low-index]") {
    auto rg = ReportGuard(false);

    Presentation<std::string> p;
    p.alphabet("abc");

    presentation::add_rule_and_check(p, "cc""c");
    presentation::add_rule_and_check(p, "abb""a");
    presentation::add_rule_and_check(p, "aca""aba");

    RepOrc orc;
    orc.short_rules(p).number_of_threads(std::thread::hardware_concurrency());
    REQUIRE(orc.min_nodes() == 0);
    REQUIRE(orc.max_nodes() == 0);
    REQUIRE(orc.target_size() == 0);
    REQUIRE(orc.digraph().number_of_nodes() == 0);
  }

}  // namespace libsemigroups

// [[[0, 0, 0]],            #1#
// [[0, 0, 1], [1, 0, 1]],  #2#
// [[0, 1, 0], [1, 1, 0]],
// [[0, 1, 1], [1, 1, 1]]]  #3#

// [[[0, 0, 0]],                        #1#
//   [[1, 0, 1], [1, 1, 1]],
//   [[1, 0, 2], [1, 1, 1], [1, 2, 1]],
//   [[1, 1, 1], [1, 1, 1]],            #2#
//   [[1, 1, 2], [1, 1, 1], [1, 1, 1]], #4#
//   [[1, 0, 1], [2, 1, 2], [2, 2, 2]],
//   [[1, 0, 2], [2, 1, 2], [2, 2, 2]],
//   [[1, 1, 2], [2, 1, 2], [2, 2, 2]], #3#
//   [[1, 2, 2], [2, 1, 2], [2, 2, 2]],
//   [[1, 0, 2], [2, 2, 2], [2, 2, 2]],
//   [[1, 2, 1], [2, 2, 2], [2, 2, 2]],
//   [[1, 2, 2], [2, 2, 2], [2, 2, 2]],
//   [[1, 2, 1], [1, 1, 1], [1, 2, 1]]]

Messung V0.5 in Prozent
C=66 H=94 G=80

¤ Dauer der Verarbeitung: 0.52 Sekunden  (vorverarbeitet am  2026-06-17) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.