//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2019 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 "catch.hpp"      // for TEST_CASE
#include "test-main.hpp"  // for LIBSEMIGROUPS_TEST_CASE

#include "libsemigroups/cong-pair.hpp"     // for KnuthBendixCongruenceByPairs
#include "libsemigroups/cong.hpp"          // for Congruence
#include "libsemigroups/fastest-bmat.hpp"  // for FastestBMat
#include "libsemigroups/fpsemi-examples.hpp"  // for rook_monoid
#include "libsemigroups/fpsemi.hpp"           // for FpSemigroup
#include "libsemigroups/froidure-pin.hpp"     // for FroidurePin
#include "libsemigroups/pbr.hpp"              // for PBR
#include "libsemigroups/report.hpp"           // for ReportGuard
#include "libsemigroups/transf.hpp"           // for Transf<>
#include "libsemigroups/types.hpp"            // for word_type

CATCH_REGISTER_ENUM(libsemigroups::tril,
                    libsemigroups::tril::TRUE,
                    libsemigroups::tril::FALSE,
                    libsemigroups::tril::unknown)

namespace libsemigroups {
  // Forward declarations
  struct LibsemigroupsException;

  constexpr bool REPORT = false;

  constexpr congruence_kind twosided = congruence_kind::twosided;
  constexpr congruence_kind left     = congruence_kind::left;
  constexpr congruence_kind right    = congruence_kind::right;

  using fpsemigroup::rook_monoid;
  using fpsemigroup::stellar_monoid;

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "000",
                          "left congruence on fp semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule(word_type({0, 0, 0}), word_type({0}));
    S.add_rule(word_type({0}), word_type({1, 1}));

    Congruence cong(left, S);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "001",
                          "2-sided congruence on fp semigroup",
                          "[quick][cong]") {
    auto rg = ReportGuard(REPORT);

    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule({0, 0, 0}, {0});
    S.add_rule({0}, {1, 1});
    REQUIRE(!S.is_obviously_infinite());

    Congruence cong(twosided, S);

    REQUIRE(cong.number_of_generating_pairs() == 0);
    REQUIRE(cong.number_of_classes() == 5);

    REQUIRE(cong.word_to_class_index({0, 0, 1})
            == cong.word_to_class_index({0, 0, 0, 0, 1}));
    REQUIRE(cong.contains({0, 0, 1}, {0, 0, 1}));
    REQUIRE(cong.contains({0, 0, 1}, {0, 0, 0, 0, 1}));
    REQUIRE(cong.word_to_class_index({0, 0, 0, 0, 1})
            == cong.word_to_class_index({0, 1, 1, 0, 0, 1}));
    REQUIRE(cong.contains({0, 0, 0, 0, 1}, {0, 1, 1, 0, 0, 1}));
    REQUIRE(cong.word_to_class_index({0, 0, 0})
            != cong.word_to_class_index({0, 0, 1}));
    REQUIRE(!cong.contains({0, 0, 0}, {0, 0, 1}));
    REQUIRE(cong.word_to_class_index({1})
            != cong.word_to_class_index({0, 0, 0}));
    REQUIRE(!cong.contains({1}, {0, 0, 0}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "002",
                          "left congruence on fp semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule({0, 0, 0}, {0});  // (a^3, a)
    S.add_rule({0}, {1, 1});     // (a, b^2)

    Congruence cong(left, S);
    REQUIRE(cong.number_of_classes() == 5);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "003",
                          "word_to_class_index for cong. on fp semigroup",
                          "[quick][cong]") {
    auto rg = ReportGuard(REPORT);

    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule({0, 0, 0}, {0});  // (a^3, a)
    S.add_rule({0}, {1, 1});     // (a, b^2)

    Congruence cong(left, S);
    REQUIRE(cong.number_of_classes() == 5);
    REQUIRE(cong.word_to_class_index({0, 1, 1, 0, 0, 1})
            == cong.word_to_class_index({0, 0, 1}));
    REQUIRE(cong.word_to_class_index({0, 0, 1})
            == cong.word_to_class_index({0, 0, 0, 0, 1}));
    REQUIRE(cong.contains({0, 1, 1, 0, 0, 1}, {0, 0, 1}));
    REQUIRE(cong.word_to_class_index({0, 0, 0})
            != cong.word_to_class_index({0, 0, 1}));
    REQUIRE(cong.word_to_class_index({1})
            != cong.word_to_class_index({0, 0, 0, 0}));
    REQUIRE(!cong.contains({0, 0, 0, 0}, {0, 0, 1}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "004",
                          "word_to_class_index for cong. on fp semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule({0, 0, 0}, {0});  // (a^3, a)
    S.add_rule({0}, {1, 1});     // (a, b^2)

    Congruence cong1(twosided, S);

    REQUIRE(cong1.word_to_class_index({0, 0, 1})
            == cong1.word_to_class_index({0, 0, 0, 0, 1}));
    REQUIRE(cong1.word_to_class_index({0, 1, 1, 0, 0, 1})
            == cong1.word_to_class_index({0, 0, 0, 0, 1}));
    REQUIRE(cong1.word_to_class_index({0, 0, 0})
            == cong1.word_to_class_index({1, 1}));
    REQUIRE(cong1.word_to_class_index({1}) != cong1.word_to_class_index({0}));

    Congruence cong2(twosided, S);

    REQUIRE(cong2.word_to_class_index({0, 0, 0, 0})
            == cong2.word_to_class_index({0, 0}));
    REQUIRE(cong2.contains({0, 0, 0, 0}, {0, 1, 1, 0, 1, 1}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "005",
                          "trivial congruence on non-fp semigroup",
                          "[quick][cong]") {
    auto rg = ReportGuard(REPORT);

    using Transf = LeastTransf<5>;
    FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3, 3})});
    REQUIRE(S.size() == 88);

    Congruence cong(twosided, S);
    REQUIRE(cong.is_quotient_obviously_finite());
    REQUIRE(cong.number_of_classes() == 88);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "006",
                          "2-sided congruence on non-fp semigroup",
                          "[quick][cong]") {
    auto rg = ReportGuard(REPORT);

    using Transf = LeastTransf<5>;
    FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3, 3})});
    REQUIRE(S.size() == 88);

    Congruence cong(twosided, S);
    cong.add_pair(S.factorisation(Transf({3, 4, 4, 4, 4})),
                  S.factorisation(Transf({3, 1, 3, 3, 3})));
    REQUIRE(cong.number_of_classes() == 21);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "007",
                          "2-sided congruence on fp semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    S.add_rule({0, 1}, {1, 0});
    S.add_rule({0, 2}, {2, 2});
    S.add_rule({0, 2}, {0});
    S.add_rule({0, 2}, {0});
    S.add_rule({2, 2}, {0});
    S.add_rule({1, 2}, {1, 2});
    S.add_rule({1, 2}, {2, 2});
    S.add_rule({1, 2, 2}, {1});
    S.add_rule({1, 2}, {1});
    S.add_rule({2, 2}, {1});
    S.add_rule({0}, {1});

    REQUIRE(S.size() == 2);
    REQUIRE(!S.is_obviously_infinite());
    REQUIRE(S.is_obviously_finite());

    REQUIRE(S.froidure_pin()->number_of_generators() == 3);
    REQUIRE(S.froidure_pin()->size() == 2);
    REQUIRE(S.froidure_pin()->is_finite() == tril::TRUE);

    Congruence cong1(twosided, S.froidure_pin());
    cong1.add_pair({0}, {1});
    REQUIRE(cong1.number_of_classes() == 2);

    Congruence cong2(twosided, S);
    cong2.add_pair({0}, {1});
    REQUIRE(cong2.number_of_classes() == 2);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "008",
                          "2-sided congruence on infinite fp semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    S.add_rule({0, 1}, {1, 0});
    S.add_rule({0, 2}, {2, 2});
    S.add_rule({0, 2}, {0});
    S.add_rule({0, 2}, {0});
    S.add_rule({2, 2}, {0});
    S.add_rule({1, 2}, {1, 2});
    S.add_rule({1, 2}, {2, 2});
    S.add_rule({1, 2, 2}, {1});
    S.add_rule({1, 2}, {1});
    S.add_rule({2, 2}, {1});

    Congruence cong(twosided, S);
    cong.add_pair({0}, {1});

    REQUIRE(cong.word_to_class_index({0}) == cong.word_to_class_index({1}));
    REQUIRE(cong.word_to_class_index({0}) == cong.word_to_class_index({1, 0}));
    REQUIRE(cong.word_to_class_index({0}) == cong.word_to_class_index({1, 1}));
    REQUIRE(cong.word_to_class_index({0})
            == cong.word_to_class_index({1, 0, 1}));

    REQUIRE(cong.contains({1}, {1, 1}));
    REQUIRE(cong.contains({1, 0, 1}, {1, 0}));
    REQUIRE(cong.number_of_classes() == 2);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "009",
                          "2-sided congruence on infinite fp semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    S.add_rule({0, 1}, {1, 0});
    S.add_rule({0, 2}, {2, 0});
    S.add_rule({0, 0}, {0});
    S.add_rule({0, 2}, {0});
    S.add_rule({2, 0}, {0});
    S.add_rule({1, 2}, {2, 1});
    S.add_rule({1, 1, 1}, {1});
    S.add_rule({1, 2}, {1});
    S.add_rule({2, 1}, {1});

    Congruence cong(twosided, S);
    cong.add_pair({0}, {1});

    // Requires KnuthBendixCongruenceByPairs to work
    REQUIRE(cong.word_to_class_index({0}) == cong.word_to_class_index({1}));
    REQUIRE(cong.word_to_class_index({0}) == cong.word_to_class_index({1, 0}));
    REQUIRE(cong.word_to_class_index({0}) == cong.word_to_class_index({1, 1}));
    REQUIRE(cong.word_to_class_index({0})
            == cong.word_to_class_index({1, 0, 1}));

    REQUIRE(cong.contains({1}, {1, 1}));
    REQUIRE(cong.contains({1, 0, 1}, {1, 0}));

    REQUIRE(!cong.less({1, 0, 1}, {1, 0}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "010",
                          "2-sided congruence on finite semigroup",
                          "[quick][cong][no-valgrind]") {
    auto rg      = ReportGuard(REPORT);
    using Transf = LeastTransf<8>;
    FroidurePin<Transf> S({Transf({7, 3, 5, 3, 4, 2, 7, 7}),
                           Transf({1, 2, 4, 4, 7, 3, 0, 7}),
                           Transf({0, 6, 4, 2, 2, 6, 6, 4}),
                           Transf({3, 6, 3, 4, 0, 6, 0, 7})});

    // The following lines are intentionally commented out.
    // REQUIRE(S.size() == 11804);
    // REQUIRE(S.number_of_rules() == 2460);

    Congruence cong(twosided, S);
    cong.add_pair({0, 3, 2, 1, 3, 2, 2}, {3, 2, 2, 1, 3, 3});

    REQUIRE(cong.word_to_class_index({0, 0, 0, 1})
            == cong.word_to_class_index({0, 0, 1, 0, 0}));
    REQUIRE(cong.word_to_class_index({0, 0, 1, 0, 1})
            == cong.word_to_class_index({1, 1, 0, 1}));
    REQUIRE(cong.word_to_class_index({1, 1, 0, 0})
            != cong.word_to_class_index({0, 0, 0, 1}));
    REQUIRE(cong.word_to_class_index({0, 0, 3})
            != cong.word_to_class_index({0, 0, 0, 1}));
    REQUIRE(cong.word_to_class_index({1, 1, 0, 0})
            != cong.word_to_class_index({0, 0, 3}));
    REQUIRE(cong.word_to_class_index({1, 2, 1, 3, 3, 2, 1, 2})
            == cong.word_to_class_index({2, 1, 3, 3, 2, 1, 0}));
    REQUIRE(cong.word_to_class_index({0, 3, 1, 1, 1, 3, 2, 2, 1, 0})
            == cong.word_to_class_index({0, 3, 2, 2, 1}));
    REQUIRE(cong.word_to_class_index({0, 3, 2, 1, 3, 3, 3})
            != cong.word_to_class_index({0, 0, 3}));
    REQUIRE(cong.word_to_class_index({1, 1, 0})
            != cong.word_to_class_index({1, 3, 3, 2, 2, 1, 0}));

    REQUIRE(cong.contains({1, 2, 1, 3, 3, 2, 1, 2}, {2, 1, 3, 3, 2, 1, 0}));
    REQUIRE(!cong.contains({1, 1, 0}, {1, 3, 3, 2, 2, 1, 0}));

    REQUIRE(cong.less({1, 3, 3, 2, 2, 1, 0}, {1, 1, 0}));
    REQUIRE(!cong.less({1, 1, 0, 0}, {0, 0, 3}));

    REQUIRE(cong.number_of_classes() == 525);
    REQUIRE(cong.number_of_classes() == 525);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "011",
                          "congruence on full PBR monoid on 2 points",
                          "[extreme][cong]") {
    auto             rg = ReportGuard(true);
    FroidurePin<PBR> S({PBR({{2}, {3}, {0}, {1}}),
                        PBR({{}, {2}, {1}, {0, 3}}),
                        PBR({{0, 3}, {2}, {1}, {}}),
                        PBR({{1, 2}, {3}, {0}, {1}}),
                        PBR({{2}, {3}, {0}, {1, 3}}),
                        PBR({{3}, {1}, {0}, {1}}),
                        PBR({{3}, {2}, {0}, {0, 1}}),
                        PBR({{3}, {2}, {0}, {1}}),
                        PBR({{3}, {2}, {0}, {3}}),
                        PBR({{3}, {2}, {1}, {0}}),
                        PBR({{3}, {2, 3}, {0}, {1}})});

    // REQUIRE(S.size() == 65536);
    // REQUIRE(S.number_of_rules() == 45416);

    Congruence cong(twosided, S);
    cong.add_pair({7, 10, 9, 3, 6, 9, 4, 7, 9, 10}, {9, 3, 6, 6, 10, 9, 4, 7});
    cong.add_pair({8, 7, 5, 8, 9, 8}, {6, 3, 8, 6, 1, 2, 4});

    REQUIRE(cong.number_of_classes() == 19009);
    REQUIRE(cong.number_of_non_trivial_classes() == 577);
    REQUIRE(cong.cend_ntc() - cong.cbegin_ntc() == 577);

    std::vector<size_t> v(577, 0);
    std::transform(cong.cbegin_ntc(),
                   cong.cend_ntc(),
                   v.begin(),
                   std::mem_fn(&std::vector<word_type>::size));
    REQUIRE(std::count(v.cbegin(), v.cend(), 4) == 384);
    REQUIRE(std::count(v.cbegin(), v.cend(), 16) == 176);
    REQUIRE(std::count(v.cbegin(), v.cend(), 96) == 16);
    REQUIRE(std::count(v.cbegin(), v.cend(), 41216) == 1);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "012",
                          "2-sided congruence on finite semigroup",
                          "[quick][cong][no-valgrind]") {
    auto rg = ReportGuard(REPORT);

    FroidurePin<LeastPPerm<6>> S(
        {LeastPPerm<6>({0, 1, 2}, {4, 0, 1}, 6),
         LeastPPerm<6>({0, 1, 2, 3, 5}, {2, 5, 3, 0, 4}, 6),
         LeastPPerm<6>({0, 1, 2, 3}, {5, 0, 3, 1}, 6),
         LeastPPerm<6>({0, 2, 5}, {3, 4, 1}, 6),
         LeastPPerm<6>({0, 2, 5}, {0, 2, 5}, 6),
         LeastPPerm<6>({0, 1, 4}, {1, 2, 0}, 6),
         LeastPPerm<6>({0, 2, 3, 4, 5}, {3, 0, 2, 5, 1}, 6),
         LeastPPerm<6>({0, 1, 3, 5}, {1, 3, 2, 0}, 6),
         LeastPPerm<6>({1, 3, 4}, {5, 0, 2}, 6)});

    // REQUIRE(S.size() == 712);
    // REQUIRE(S.number_of_rules() == 1121);

    Congruence cong(twosided, S);
    cong.add_pair({2, 7}, {1, 6, 6, 1});
    REQUIRE(cong.number_of_classes() == 32);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "013",
                          "trivial 2-sided congruence on bicyclic monoid",
                          "[quick][cong][no-valgrind]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    S.set_identity(0);
    S.add_rule({1, 2}, {0});
    Congruence cong(twosided, S);
    REQUIRE(cong.word_to_class_index({0})
            == cong.word_to_class_index({1, 2, 1, 1, 2, 2}));
    REQUIRE(cong.word_to_class_index({0})
            == cong.word_to_class_index({1, 0, 2, 0, 1, 2}));
    REQUIRE(cong.word_to_class_index({2, 1})
            == cong.word_to_class_index({1, 2, 0, 2, 1, 1, 2}));
    REQUIRE(cong.contains({2, 1}, {1, 2, 0, 2, 1, 1, 2}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "014",
                          "non-trivial 2-sided congruence on bicyclic monoid",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    S.set_identity(0);
    S.add_rule({1, 2}, {0});

#ifdef LIBSEMIGROUPS_EIGEN_ENABLED
    REQUIRE(S.is_obviously_infinite());
#else
    REQUIRE(!S.is_obviously_infinite());
#endif

    Congruence cong(twosided, S);
    cong.add_pair({1, 1, 1}, {0});
    REQUIRE(cong.number_of_classes() == 3);
    // The next test currently runs forever because
    // number_of_non_trivial_classes attempts to enumerate all elements in the
    // non_trivial_classes (and there are infinitely many).
    // REQUIRE_THROWS_AS(cong.number_of_non_trivial_classes() == 3,
    //                  LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "015",
                          "2-sided congruence on free abelian monoid",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    S.add_rule({1, 2}, {2, 1});
    S.set_identity(0);

    Congruence cong(twosided, S);
    cong.add_pair({1, 1, 1, 1, 1}, {1});
    cong.add_pair({2, 2, 2}, {2});

    REQUIRE(cong.number_of_classes() == 15);
  }

  // The previous Congruence 17 test was identical to Congruence 12

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "016",
                          "example where TC works but KB doesn't",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet("abBe");
    REQUIRE_THROWS_AS(S.add_rule("aa", ""), LibsemigroupsException);
    S.set_identity("e");
    S.add_rule("aa", "e");
    S.add_rule("BB", "b");
    S.add_rule("BaBaBaB", "abababa");
    S.add_rule("aBabaBabaBabaBab", "BabaBabaBabaBaba");

    Congruence cong(twosided, S);
    cong.add_pair({0}, {1});

    REQUIRE(cong.number_of_classes() == 4);
    REQUIRE(!cong.quotient_froidure_pin()->is_monoid());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "017",
                          "2-sided congruence on finite semigroup",
                          "[quick][cong]") {
    auto rg      = ReportGuard(REPORT);
    using Transf = LeastTransf<5>;
    FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3, 3})});

    REQUIRE(S.size() == 88);
    REQUIRE(S.number_of_rules() == 18);

    word_type w1 = S.factorisation(Transf({3, 4, 4, 4, 4}));
    word_type w2 = S.factorisation(Transf({3, 4, 4, 4, 4}));

    Congruence cong(twosided, S);
    cong.add_pair(S.factorisation(Transf({3, 4, 4, 4, 4})),
                  S.factorisation(Transf({3, 1, 3, 3, 3})));
    REQUIRE(cong.number_of_classes() == 21);

    word_type u = S.factorisation(Transf({1, 3, 1, 3, 3}));
    word_type v = S.factorisation(Transf({4, 2, 4, 4, 2}));
    REQUIRE(cong.word_to_class_index(u) == cong.word_to_class_index(v));
    REQUIRE(cong.contains(u, v));
  }

  // The next test behaves as expected but runs forever, since the
  // number_of_classes method requires to know the size of the semigroup S, and
  // we cannot currently work that out.
  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "018",
                          "infinite fp semigroup from GAP library ",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    S.add_rule({0, 0}, {0, 0});
    S.add_rule({0, 1}, {1, 0});
    S.add_rule({0, 2}, {2, 0});
    S.add_rule({0, 0}, {0});
    S.add_rule({0, 2}, {0});
    S.add_rule({2, 0}, {0});
    S.add_rule({1, 0}, {0, 1});
    S.add_rule({1, 1}, {1, 1});
    S.add_rule({1, 2}, {2, 1});
    S.add_rule({1, 1, 1}, {1});
    S.add_rule({1, 2}, {1});
    S.add_rule({2, 1}, {1});

    REQUIRE(S.is_obviously_infinite());

    Congruence cong(twosided, S);
    cong.add_pair({0}, {1});
    REQUIRE(cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_classes() == POSITIVE_INFINITY);
    REQUIRE(!cong.finished());
    REQUIRE(cong.number_of_non_trivial_classes() == 1);
    REQUIRE(cong.cbegin_ntc()->size() == 5);
    REQUIRE(std::vector<word_type>(cong.cbegin_ntc()->cbegin(),
                                   cong.cbegin_ntc()->cend())
            == std::vector<word_type>({{0}, {1}, {0, 1}, {1, 1}, {0, 1, 1}}));
    REQUIRE(cong.finished());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "019",
                          "2-sided cong. on fp semigroup with infinite classes",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule({0, 1}, {1, 0});
    S.add_rule({0, 0, 0}, {0, 0});

    Congruence cong(twosided, S);
    cong.add_pair({0}, {1});

    word_type x
        = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
    word_type y
        = {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
    REQUIRE(std::count(x.cbegin(), x.cend(), 1) == 20);
    REQUIRE(std::count(y.cbegin(), y.cend(), 1) == 20);
    REQUIRE(cong.contains(x, y));
    REQUIRE(!cong.less({0, 0, 0}, {1}));
    REQUIRE(cong.less({1}, {0, 0, 0}));
    REQUIRE(!cong.less(x, y));
    REQUIRE(!cong.less(y, x));
    REQUIRE(cong.contains(x, y));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "020",
                          "trivial cong. on an fp semigroup",
                          "[quick][cong][no-valgrind]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet("ab");
    S.add_rule("ab", "ba");
    S.add_rule("a", "b");
    SECTION("compute size") {
      REQUIRE(S.size() == POSITIVE_INFINITY);
    }
    SECTION("don't compute size") {}

    Congruence cong(left, S);

    // No generating pairs for the congruence (not the fp semigroup) means no
    // non-trivial classes.
    REQUIRE(cong.number_of_non_trivial_classes() == 0);
    REQUIRE(cong.finished());
    REQUIRE(cong.started());
    REQUIRE_THROWS_AS(cong.add_pair({0, 0}, {0}), LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "021",
                          "duplicate generators",
                          "[quick][cong]") {
    auto rg      = ReportGuard(REPORT);
    using Transf = LeastTransf<8>;
    FroidurePin<Transf> S({Transf({7, 3, 5, 3, 4, 2, 7, 7}),
                           Transf({7, 3, 5, 3, 4, 2, 7, 7}),
                           Transf({7, 3, 5, 3, 4, 2, 7, 7}),
                           Transf({3, 6, 3, 4, 0, 6, 0, 7})});
    Congruence          cong(twosided, S);
    REQUIRE(cong.number_of_classes() == S.size());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "022",
                          "non-trivial classes",
                          "[quick][cong]") {
    auto rg = ReportGuard(REPORT);

    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule({0, 0, 0}, {0});
    S.add_rule({1, 0, 0}, {1, 0});
    S.add_rule({1, 0, 1, 1, 1}, {1, 0});
    S.add_rule({1, 1, 1, 1, 1}, {1, 1});
    S.add_rule({1, 1, 0, 1, 1, 0}, {1, 0, 1, 0, 1, 1});
    S.add_rule({0, 0, 1, 0, 1, 1, 0}, {0, 1, 0, 1, 1, 0});
    S.add_rule({0, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 0, 1, 0});
    S.add_rule({0, 1, 0, 1, 0, 1, 0}, {1, 0, 1, 0, 1, 0});
    S.add_rule({1, 0, 1, 0, 1, 0, 1}, {1, 0, 1, 0, 1, 0});
    S.add_rule({1, 0, 1, 0, 1, 1, 0}, {1, 0, 1, 0, 1, 1});
    S.add_rule({1, 0, 1, 1, 0, 1, 0}, {1, 0, 1, 1, 0, 1});
    S.add_rule({1, 1, 0, 1, 0, 1, 0}, {1, 0, 1, 0, 1, 0});
    S.add_rule({1, 1, 1, 1, 0, 1, 0}, {1, 0, 1, 0});
    S.add_rule({0, 0, 1, 1, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0});

    // TODO(later) this test fails if we don't run the next line, since the
    // congruence below has no parent
    REQUIRE(S.froidure_pin()->size() == 78);

    Congruence cong(twosided, S);
    cong.add_pair({0}, {1});

    REQUIRE(cong.number_of_non_trivial_classes() == 1);
    REQUIRE(cong.cbegin_ntc()->size() == 78);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "023",
                          "right congruence on finite semigroup",
                          "[quick][cong][no-valgrind]") {
    auto rg      = ReportGuard(REPORT);
    using Transf = LeastTransf<8>;
    FroidurePin<Transf> S({Transf({0, 1, 2, 3, 4, 5, 6, 7}),
                           Transf({1, 2, 3, 4, 5, 0, 6, 7}),
                           Transf({1, 0, 2, 3, 4, 5, 6, 7}),
                           Transf({0, 1, 2, 3, 4, 0, 6, 7}),
                           Transf({0, 1, 2, 3, 4, 5, 7, 6})});
    REQUIRE(S.size() == 93312);
    std::vector<Transf> elms = {Transf({0, 0, 0, 0, 0, 0, 7, 6}),
                                Transf({0, 0, 0, 0, 0, 0, 6, 7}),
                                Transf({0, 0, 0, 0, 0, 0, 6, 7}),
                                Transf({1, 1, 1, 1, 1, 1, 6, 7}),
                                Transf({0, 0, 0, 0, 0, 0, 6, 7}),
                                Transf({2, 2, 2, 2, 2, 2, 6, 7}),
                                Transf({0, 0, 0, 0, 0, 0, 6, 7}),
                                Transf({3, 3, 3, 3, 3, 3, 6, 7}),
                                Transf({0, 0, 0, 0, 0, 0, 6, 7}),
                                Transf({4, 4, 4, 4, 4, 4, 6, 7}),
                                Transf({0, 0, 0, 0, 0, 0, 6, 7}),
                                Transf({5, 5, 5, 5, 5, 5, 6, 7}),
                                Transf({0, 0, 0, 0, 0, 0, 7, 6}),
                                Transf({0, 1, 2, 3, 4, 5, 7, 6})};
    REQUIRE(
        std::all_of(elms.cbegin(), elms.cend(), [&S](Transf const& x) -> bool {
          return S.contains(x);
        }));

    Congruence cong(right, S);
    word_type  w1, w2;
    for (size_t i = 0; i < elms.size(); i += 2) {
      S.factorisation(w1, S.position(elms[i]));
      S.factorisation(w2, S.position(elms[i + 1]));
      cong.add_pair(w1, w2);
    }
    REQUIRE(cong.number_of_classes() == 1);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "024",
                          "redundant generating pairs",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(1);

    Congruence cong(twosided, S);
    cong.add_pair({0, 0}, {0, 0});
    REQUIRE(cong.contains({0, 0}, {0, 0}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "025",
                          "2-sided cong. on free semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet("a");
    Congruence cong(twosided, S);
    REQUIRE(cong.contains({0, 0}, {0, 0}));
    REQUIRE(!cong.contains({0, 0}, {0}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "026",
                          "is_quotient_obviously_(in)finite",
                          "[quick][cong]") {
    auto rg = ReportGuard(REPORT);
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      Congruence cong(twosided, S);
      cong.add_pair({2, 2}, {2});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(cong.number_of_classes() == POSITIVE_INFINITY);
      // REQUIRE(cong.class_index_to_word(0) == word_type({}));
      // TODO(later) the above doesn't currently work, but it should, and there
      // is no reason it can't
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      S.add_rule({0, 0}, {0});
      Congruence cong(twosided, S);
      cong.add_pair({1, 1}, {1});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      S.add_rule({0, 0}, {0});
      Congruence cong(twosided, S);
      cong.add_pair({1, 2}, {1});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      Congruence cong(right, S);
      cong.add_pair({2, 2}, {2});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      S.add_rule({0, 0}, {0});
      Congruence cong(right, S);
      cong.add_pair({1, 1}, {1});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      S.add_rule({0, 0}, {0});
      Congruence cong(right, S);
      cong.add_pair({1, 2}, {1});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      Congruence cong(left, S);
      cong.add_pair({2, 2}, {2});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      S.add_rule({0, 0}, {0});
      Congruence cong(left, S);
      cong.add_pair({1, 1}, {1});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }
    {
      FpSemigroup S;
      S.set_alphabet(3);
      S.add_rule({0, 1}, {0});
      S.add_rule({0, 0}, {0});
      Congruence cong(left, S);
      cong.add_pair({1, 2}, {1});
      REQUIRE(cong.is_quotient_obviously_infinite());
      REQUIRE(!cong.is_quotient_obviously_finite());
    }

    using Transf = LeastTransf<3>;
    FroidurePin<Transf> S({Transf({0, 1, 0}), Transf({0, 1, 2})});
    REQUIRE(S.size() == 2);
    {
      Congruence cong(twosided, S);
      cong.add_pair({1}, {0});
      REQUIRE(!cong.is_quotient_obviously_infinite());
      REQUIRE(cong.is_quotient_obviously_finite());
      REQUIRE(cong.number_of_classes() == 1);
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence", "027", "less", "[quick][cong]") {
    FpSemigroup S;
    S.set_alphabet(2);
    S.add_rule({0, 0}, {0});

    Congruence cong(twosided, S);
    REQUIRE(!cong.less({0, 0}, {0}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "028",
                          "2-sided congruences of BMat8 semigroup",
                          "[quick][cong][no-valgrind]") {
#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Winline"
#endif
    auto rg    = ReportGuard(REPORT);
    using BMat = FastestBMat<4>;
    std::vector<BMat> gens
        = {BMat({{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}),
           BMat({{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {1, 0, 0, 0}}),
           BMat({{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {1, 0, 0, 1}}),
           BMat({{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}})};
    {
      FroidurePin<BMat> S(gens);

      Congruence cong(twosided, S);
      cong.add_pair({1}, {0});

      REQUIRE(cong.number_of_classes() == 3);
      REQUIRE(cong.word_to_class_index({1}) == 0);
      REQUIRE(cong.number_of_non_trivial_classes() == 3);

      std::vector<size_t> v(cong.number_of_non_trivial_classes(), 0);
      std::transform(cong.cbegin_ntc(),
                     cong.cend_ntc(),
                     v.begin(),
                     std::mem_fn(&std::vector<word_type>::size));
      std::sort(v.begin(), v.end());
      REQUIRE(v == std::vector<size_t>({12, 12, 63880}));
      REQUIRE(cong.cbegin_ntc()->size() == 12);
      REQUIRE(std::vector<word_type>(cong.cbegin_ntc()->cbegin(),
                                     cong.cbegin_ntc()->cend())
              == std::vector<word_type>({{0},
                                         {1},
                                         {0, 1, 0},
                                         {0, 1, 1},
                                         {1, 0, 1},
                                         {1, 1, 0},
                                         {1, 1, 1},
                                         {0, 1, 0, 1, 1},
                                         {0, 1, 1, 0, 1},
                                         {1, 0, 1, 1, 0},
                                         {1, 0, 1, 1, 1},
                                         {1, 1, 0, 1, 1}}));
    }
    {
      FroidurePin<BMat> S({gens[0], gens[2], gens[3]});
      Congruence        cong(twosided, S);
      cong.add_pair({1}, {0});

      REQUIRE(cong.number_of_classes() == 2);
      REQUIRE(cong.word_to_class_index({1}) == 0);
      REQUIRE(cong.number_of_non_trivial_classes() == 2);

      std::vector<size_t> v(cong.number_of_non_trivial_classes(), 0);
      std::transform(cong.cbegin_ntc(),
                     cong.cend_ntc(),
                     v.begin(),
                     std::mem_fn(&std::vector<word_type>::size));
      std::sort(v.begin(), v.end());
      REQUIRE(v == std::vector<size_t>({8, 8}));
      REQUIRE(cong.cbegin_ntc()->size() == 8);
      REQUIRE(std::vector<word_type>(cong.cbegin_ntc()->cbegin(),
                                     cong.cbegin_ntc()->cend())
              == std::vector<word_type>({{0},
                                         {1},
                                         {0, 0},
                                         {0, 1},
                                         {1, 0},
                                         {0, 1, 0},
                                         {1, 0, 1},
                                         {0, 1, 0, 1}}));
    }
#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
#pragma GCC diagnostic pop
#endif
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "029",
                          "left congruence on finite semigroup",
                          "[quick][cong]") {
    auto                  rg = ReportGuard(REPORT);
    FroidurePin<Transf<>> S;
    S.add_generator(Transf<>({1, 3, 4, 2, 3}));
    S.add_generator(Transf<>({3, 2, 1, 3, 3}));

    // REQUIRE(S.size() == 88);
    // REQUIRE(S.degree() == 5);
    Congruence cong(left, S);
    cong.add_pair({0, 1, 0, 0, 0, 1, 1, 0, 0}, {1, 0, 0, 0, 1});

    REQUIRE(cong.number_of_classes() == 69);
    REQUIRE(cong.number_of_classes() == 69);

    word_type w3 = S.factorisation(Transf<>({1, 3, 1, 3, 3}));
    word_type w4 = S.factorisation(Transf<>({4, 2, 4, 4, 2}));
    REQUIRE(cong.word_to_class_index(w3) != cong.word_to_class_index(w4));
    REQUIRE(cong.word_to_class_index(w3)
            == cong.word_to_class_index({0, 0, 1, 0, 1}));
    REQUIRE(cong.word_to_class_index({1, 0, 0, 1, 0, 1})
            == cong.word_to_class_index({0, 0, 1, 0, 0, 0, 1}));
    REQUIRE(cong.word_to_class_index({0, 1, 1, 0, 0, 0})
            != cong.word_to_class_index({1, 1}));
    REQUIRE(cong.word_to_class_index({1, 0, 0, 0, 1, 0, 0, 0})
            != cong.word_to_class_index({1, 0, 0, 1}));

    REQUIRE(cong.contains({1, 0, 0, 1, 0, 1}, {0, 0, 1, 0, 0, 0, 1}));
    REQUIRE(!cong.contains({1, 0, 0, 0, 1, 0, 0, 0}, {1, 0, 0, 1}));

    REQUIRE(!cong.less({1, 0, 0, 0, 1, 0, 0, 0}, {1, 0, 0, 1}));
    REQUIRE(cong.less({1, 0, 0, 1}, {1, 0, 0, 0, 1, 0, 0, 0}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "030",
                          "right congruence on finite semigroup",
                          "[quick][cong]") {
    auto                  rg = ReportGuard(REPORT);
    FroidurePin<Transf<>> S;
    S.add_generator(Transf<>({1, 3, 4, 2, 3}));
    S.add_generator(Transf<>({3, 2, 1, 3, 3}));

    // REQUIRE(S.size() == 88);
    // REQUIRE(S.degree() == 5);
    Congruence cong(right, S);
    cong.add_pair({0, 1, 0, 0, 0, 1, 1, 0, 0}, {1, 0, 0, 0, 1});

    REQUIRE(cong.number_of_classes() == 72);
    REQUIRE(cong.number_of_classes() == 72);

    word_type w3 = S.factorisation(Transf<>({1, 3, 1, 3, 3}));
    word_type w4 = S.factorisation(Transf<>({4, 2, 4, 4, 2}));
    REQUIRE(cong.word_to_class_index(w3) != cong.word_to_class_index(w4));
    REQUIRE(cong.word_to_class_index(w3)
            != cong.word_to_class_index({0, 0, 1, 0, 1}));
    REQUIRE(cong.word_to_class_index({1, 0, 0, 1, 0, 1})
            != cong.word_to_class_index({0, 0, 1, 0, 0, 0, 1}));
    REQUIRE(cong.word_to_class_index({0, 1, 1, 0, 0, 0})
            != cong.word_to_class_index({1, 1}));
    REQUIRE(cong.word_to_class_index({1, 0, 0, 0, 1, 0, 0, 0})
            != cong.word_to_class_index({1, 0, 0, 1}));

    REQUIRE(!cong.contains({1, 0, 0, 1, 0, 1}, {0, 0, 1, 0, 0, 0, 1}));
    REQUIRE(!cong.contains({1, 0, 0, 0, 1, 0, 0, 0}, {1, 0, 0, 1}));

    if (!cong.less({1, 0, 0, 0, 1, 0, 0, 0}, {1, 0, 0, 1})) {
      // This depends on which method for cong wins!
      REQUIRE(!cong.less({1, 0, 0, 0, 1, 0, 0, 0}, {1, 0, 0, 1}));
      REQUIRE(cong.less({1, 0, 0, 1}, {1, 0, 0, 0, 1, 0, 0, 0}));
    } else {
      REQUIRE(cong.less({1, 0, 0, 0, 1, 0, 0, 0}, {1, 0, 0, 1}));
      REQUIRE(!cong.less({1, 0, 0, 1}, {1, 0, 0, 0, 1, 0, 0, 0}));
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "031",
                          "right congruence on finite semigroup",
                          "[quick][cong]") {
    auto                  rg = ReportGuard(REPORT);
    FroidurePin<Transf<>> S;
    S.add_generator(Transf<>({1, 3, 4, 2, 3}));
    S.add_generator(Transf<>({3, 2, 1, 3, 3}));

    REQUIRE(S.size() == 88);
    REQUIRE(S.number_of_rules() == 18);
    REQUIRE(S.degree() == 5);
    word_type w1, w2;
    S.factorisation(w1, S.position(Transf<>({3, 4, 4, 4, 4})));
    S.factorisation(w2, S.position(Transf<>({3, 1, 3, 3, 3})));
    Congruence cong(right, S);
    cong.add_pair(w1, w2);

    REQUIRE(cong.number_of_classes() == 72);
    REQUIRE(cong.number_of_classes() == 72);
    word_type w3, w4, w5, w6;
    S.factorisation(w3, S.position(Transf<>({1, 3, 3, 3, 3})));
    S.factorisation(w4, S.position(Transf<>({4, 2, 4, 4, 2})));
    S.factorisation(w5, S.position(Transf<>({2, 3, 2, 2, 2})));
    S.factorisation(w6, S.position(Transf<>({2, 3, 3, 3, 3})));
    REQUIRE(cong.word_to_class_index(w3) != cong.word_to_class_index(w4));
    REQUIRE(cong.word_to_class_index(w5) == cong.word_to_class_index(w6));
    REQUIRE(cong.word_to_class_index(w3) != cong.word_to_class_index(w6));

    REQUIRE(cong.contains(w1, w2));
    REQUIRE(cong.contains(w5, w6));
    REQUIRE(!cong.contains(w3, w5));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence", "032", "contains", "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(2);
    Congruence cong(twosided, S);
    cong.add_pair({0, 0}, {0});
    cong.add_pair({0, 1}, {0});
    cong.add_pair({1, 0}, {0});
    REQUIRE(cong.contains({0, 0}, {0}));
    REQUIRE(cong.contains({0, 1}, {0}));
    REQUIRE(cong.contains({1, 0}, {0}));
    REQUIRE(cong.contains({1, 0}, {0, 1, 0, 1}));
    REQUIRE(!cong.contains({1, 1}, {1}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "033",
                          "stellar_monoid S2",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);
    for (relation_type const& rl : rook_monoid(2, 0)) {
      S.add_rule(rl);
    }

    REQUIRE(S.number_of_rules() == 9);
    REQUIRE(!S.is_obviously_infinite());
    REQUIRE(S.knuth_bendix()->confluent());
    REQUIRE(S.size() == 7);
    REQUIRE(S.froidure_pin()->size() == 7);

    Congruence cong(twosided, S);
    for (relation_type const& rl : stellar_monoid(2)) {
      cong.add_pair(rl.first, rl.second);
    }
    REQUIRE(!cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_classes() == 5);
    REQUIRE(cong.number_of_non_trivial_classes() == 1);

    std::vector<word_type> v(cong.cbegin_ntc()->cbegin(),
                             cong.cbegin_ntc()->cend());
    std::sort(v.begin(), v.end());
    REQUIRE(v == std::vector<word_type>({{0, 1, 0}, {1, 0}, {1, 0, 1}}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "034",
                          "stellar_monoid S3",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(4);
    for (relation_type const& rl : rook_monoid(3, 0)) {
      S.add_rule(rl);
    }

    REQUIRE(S.number_of_rules() == 15);
    REQUIRE(!S.is_obviously_infinite());
    REQUIRE(!S.knuth_bendix()->confluent());
    REQUIRE(S.size() == 34);
    REQUIRE(S.froidure_pin()->size() == 34);

    Congruence cong(twosided, S);
    for (relation_type const& rl : stellar_monoid(3)) {
      cong.add_pair(rl.first, rl.second);
    }
    REQUIRE(!cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_classes() == 16);
    REQUIRE(cong.number_of_non_trivial_classes() == 4);

    using non_trivial_classes_type = Congruence::non_trivial_classes_type;

    non_trivial_classes_type v;
    v.reserve(cong.number_of_non_trivial_classes());
    for (auto it = cong.cbegin_ntc(); it < cong.cend_ntc(); ++it) {
      v.push_back(std::vector<word_type>(it->cbegin(), it->cend()));
      std::sort(v.back().begin(), v.back().end());
    }
    std::sort(v.begin(), v.end());

    REQUIRE(v
            == non_trivial_classes_type(
                {{{0, 1, 0}, {1, 0}, {1, 0, 1}},
                 {{0, 1, 0, 2}, {1, 0, 1, 2}, {1, 0, 2}},
                 {{0, 1, 0, 2, 1}, {1, 0, 1, 2, 1}, {1, 0, 2, 1}},
                 {{0, 1, 0, 2, 1, 0},
                  {0, 1, 2, 1, 0},
                  {0, 1, 2, 1, 0, 1},
                  {0, 2, 1, 0},
                  {1, 0, 1, 2, 1, 0},
                  {1, 0, 1, 2, 1, 0, 1},
                  {1, 0, 2, 1, 0},
                  {1, 2, 1, 0},
                  {1, 2, 1, 0, 1},
                  {1, 2, 1, 0, 1, 2},
                  {2, 1, 0},
                  {2, 1, 0, 1},
                  {2, 1, 0, 1, 2}}}));
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "035",
                          "stellar_monoid S4",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(5);
    for (relation_type const& rl : rook_monoid(4, 0)) {
      S.add_rule(rl);
    }

    REQUIRE(S.number_of_rules() == 23);
    REQUIRE(!S.is_obviously_infinite());
    REQUIRE(!S.knuth_bendix()->confluent());
    REQUIRE(S.size() == 209);
    REQUIRE(S.froidure_pin()->size() == 209);

    Congruence cong(twosided, S);
    for (relation_type const& rl : stellar_monoid(4)) {
      cong.add_pair(rl.first, rl.second);
    }
    REQUIRE(!cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_classes() == 65);
    REQUIRE(cong.number_of_non_trivial_classes() == 17);

    std::vector<size_t> v(cong.number_of_non_trivial_classes(), 0);
    std::transform(cong.cbegin_ntc(),
                   cong.cend_ntc(),
                   v.begin(),
                   std::mem_fn(&std::vector<word_type>::size));
    std::sort(v.begin(), v.end());
    REQUIRE(v
            == std::vector<size_t>(
                {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 13, 13, 13, 13, 73}));
    REQUIRE(
        std::accumulate(v.cbegin(), v.cend(), 0)
            + (cong.number_of_classes() - cong.number_of_non_trivial_classes())
        == 209);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "036",
                          "stellar_monoid S5",
                          "[quick][cong][no-valgrind]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(6);
    for (relation_type const& rl : rook_monoid(5, 0)) {
      S.add_rule(rl);
    }

    REQUIRE(S.number_of_rules() == 33);
    REQUIRE(!S.is_obviously_infinite());
    REQUIRE(!S.knuth_bendix()->confluent());
    REQUIRE(S.size() == 1546);
    REQUIRE(S.froidure_pin()->size() == 1546);

    Congruence cong(twosided, S);
    for (relation_type const& rl : stellar_monoid(5)) {
      cong.add_pair(rl.first, rl.second);
    }
    REQUIRE(!cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_classes() == 326);
    REQUIRE(cong.number_of_non_trivial_classes() == 86);

    std::vector<size_t> v(cong.number_of_non_trivial_classes(), 0);
    std::transform(cong.cbegin_ntc(),
                   cong.cend_ntc(),
                   v.begin(),
                   std::mem_fn(&std::vector<word_type>::size));
    REQUIRE(std::count(v.cbegin(), v.cend(), 3) == 60);
    REQUIRE(std::count(v.cbegin(), v.cend(), 13) == 20);
    REQUIRE(std::count(v.cbegin(), v.cend(), 73) == 5);
    REQUIRE(std::count(v.cbegin(), v.cend(), 501) == 1);
    REQUIRE(
        std::accumulate(v.cbegin(), v.cend(), 0)
            + (cong.number_of_classes() - cong.number_of_non_trivial_classes())
        == S.size());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "037",
                          "stellar_monoid S6",
                          "[quick][cong][no-valgrind]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(7);
    for (relation_type const& rl : rook_monoid(6, 0)) {
      S.add_rule(rl);
    }

    REQUIRE(S.number_of_rules() == 45);
    REQUIRE(!S.is_obviously_infinite());
    REQUIRE(!S.knuth_bendix()->confluent());
    REQUIRE(S.size() == 13327);

    Congruence cong(twosided, S);
    for (relation_type const& rl : stellar_monoid(6)) {
      cong.add_pair(rl.first, rl.second);
    }
    REQUIRE(!cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_classes() == 1957);
    REQUIRE(cong.number_of_non_trivial_classes() == 517);

    std::vector<size_t> v(cong.number_of_non_trivial_classes(), 0);
    std::transform(cong.cbegin_ntc(),
                   cong.cend_ntc(),
                   v.begin(),
                   std::mem_fn(&std::vector<word_type>::size));
    REQUIRE(
        std::accumulate(v.cbegin(), v.cend(), 0)
            + (cong.number_of_classes() - cong.number_of_non_trivial_classes())
        == S.size());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "038",
                          "stellar_monoid S7",
                          "[quick][cong][no-valgrind]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(8);
    for (relation_type const& rl : rook_monoid(7, 0)) {
      S.add_rule(rl);
    }

    REQUIRE(S.number_of_rules() == 59);
    REQUIRE(!S.is_obviously_infinite());
    REQUIRE(!S.knuth_bendix()->confluent());
    REQUIRE(S.size() == 130922);

    Congruence cong(twosided, S);
    for (relation_type const& rl : stellar_monoid(7)) {
      cong.add_pair(rl.first, rl.second);
    }
    REQUIRE(!cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_classes() == 13700);
    REQUIRE(cong.number_of_non_trivial_classes() == 3620);

    std::vector<size_t> v(cong.number_of_non_trivial_classes(), 0);
    std::transform(cong.cbegin_ntc(),
                   cong.cend_ntc(),
                   v.begin(),
                   std::mem_fn(&std::vector<word_type>::size));
    REQUIRE(
        std::accumulate(v.cbegin(), v.cend(), 0)
            + (cong.number_of_classes() - cong.number_of_non_trivial_classes())
        == S.size());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "039",
                          "left cong. on an f.p. semigroup",
                          "[quick][cong]") {
    auto rg = ReportGuard(REPORT);

    FpSemigroup S;
    S.set_alphabet("abe");
    S.set_identity("e");
    S.add_rule("abb", "bb");
    S.add_rule("bbb", "bb");
    S.add_rule("aaaa", "a");
    S.add_rule("baab", "bb");
    S.add_rule("baaab", "b");
    S.add_rule("babab", "b");
    S.add_rule("bbaaa", "bb");
    S.add_rule("bbaba", "bbaa");

    REQUIRE(S.knuth_bendix()->confluent());
    REQUIRE(S.knuth_bendix()->number_of_rules() == 13);

    KnuthBendixCongruenceByPairs kbp(left, S.knuth_bendix());
    // kbp.add_pair({0}, {1, 1, 1});
    kbp.add_pair({1, 1}, {0, 0, 0, 0, 0, 0, 0});

    REQUIRE(kbp.number_of_classes() == 11);

    Congruence cong1(left, S);
    cong1.add_pair({0}, {1, 1, 1});
    REQUIRE(cong1.number_of_classes() == 11);

    Congruence cong2(left, S);
    cong2.add_pair({1, 1}, {0, 0, 0, 0, 0, 0, 0});
    REQUIRE(cong1.number_of_classes() == cong2.number_of_classes());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "040",
                          "2-sided cong. on infinite f.p. semigroup",
                          "[quick][cong]") {
    auto        rg = ReportGuard(REPORT);
    FpSemigroup S;
    S.set_alphabet(3);

    Congruence cong(twosided, S);
    cong.add_pair({1}, {2});
    cong.add_pair({0, 0}, {0});
    cong.add_pair({0, 1}, {1, 0});
    cong.add_pair({0, 1}, {1});
    cong.add_pair({0, 2}, {2, 0});
    cong.add_pair({0, 2}, {2});

    REQUIRE(!cong.contains({1}, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}));
  }
  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "041",
                          "2-sided congruence constructed from type only",
                          "[quick][cong]") {
    auto       rg = ReportGuard(REPORT);
    Congruence cong(twosided);
    REQUIRE(cong.number_of_generators() == UNDEFINED);
    REQUIRE_THROWS_AS(cong.set_number_of_generators(0), LibsemigroupsException);
    REQUIRE_THROWS_AS(cong.contains({1}, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(cong.const_contains({1}, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}),
                      LibsemigroupsException);
    REQUIRE(cong.number_of_classes() == UNDEFINED);
    REQUIRE_THROWS_AS(cong.word_to_class_index({2, 2, 2, 2}),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(cong.class_index_to_word(0), LibsemigroupsException);
    REQUIRE_THROWS_AS(cong.class_index_to_word(1), LibsemigroupsException);
    REQUIRE_THROWS_AS(cong.class_index_to_word(2), LibsemigroupsException);
    REQUIRE_THROWS_AS(cong.run(), LibsemigroupsException);
    REQUIRE(!cong.started());
    REQUIRE(!cong.finished());

    REQUIRE_NOTHROW(cong.set_number_of_generators(2));

    REQUIRE_NOTHROW(cong.add_pair({0, 0, 0}, {0}));
    REQUIRE_NOTHROW(cong.add_pair({0}, {1, 1}));
    REQUIRE_THROWS_AS(!cong.contains({1}, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}),
                      LibsemigroupsException);
    REQUIRE_THROWS_AS(cong.const_contains({1}, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}),
                      LibsemigroupsException);
    REQUIRE(cong.const_contains({0}, {1, 1}) == tril::TRUE);
    REQUIRE_NOTHROW(cong.run());
    REQUIRE(!cong.contains({1}, {0}));
    REQUIRE(cong.contains({0}, {1, 1}));
    REQUIRE(cong.number_of_classes() == 5);
    REQUIRE_THROWS_AS(cong.word_to_class_index({2, 2, 2, 2}),
                      LibsemigroupsException);
    REQUIRE(cong.word_to_class_index({1, 1, 1, 1}) == 2);
    REQUIRE(cong.class_index_to_word(0) == word_type({0}));
    REQUIRE(cong.class_index_to_word(1) == word_type({1}));
    REQUIRE(cong.class_index_to_word(2) == word_type({0, 0}));
    REQUIRE(cong.started());
    REQUIRE(cong.finished());
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "042",
                          "const_contains",
                          "[quick][cong]") {
    auto       rg = ReportGuard(REPORT);
    Congruence cong(twosided);
    cong.set_number_of_generators(2);
    cong.add_pair({0, 0, 0}, {0});
    cong.add_pair({1, 1, 1, 1}, {1});
    cong.add_pair({0, 1, 1, 1, 0}, {0, 0});
    cong.add_pair({1, 0, 0, 1}, {1, 1});
    cong.add_pair({0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0});

    REQUIRE(cong.const_contains({1, 1, 1, 1}, {1}) == tril::TRUE);
    REQUIRE(cong.const_contains({1, 1, 1, 1}, {1, 1}) == tril::unknown);
    REQUIRE(!cong.contains({1, 1, 1, 1}, {1, 1}));
    if (cong.has_todd_coxeter()) {
      REQUIRE_NOTHROW(cong.todd_coxeter());
    }
    if (cong.has_knuth_bendix()) {
      REQUIRE_NOTHROW(cong.knuth_bendix());
    }
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence", "043", "no winner", "[quick][cong]") {
    auto       rg = ReportGuard(REPORT);
    Congruence cong(twosided);
    cong.set_number_of_generators(4);
    cong.max_threads(2);
    // Required in case of using a 1 core computer, otherwise the tests below
    // fail.
    REQUIRE(cong.word_to_class_index({2, 2, 2, 2}) == 254);
    REQUIRE(cong.class_index_to_word(2) == word_type({2}));
    REQUIRE_NOTHROW(cong.quotient_froidure_pin());
    REQUIRE_THROWS_AS(cong.cbegin_ntc(), LibsemigroupsException);
  }

  LIBSEMIGROUPS_TEST_CASE("Congruence",
                          "044",
                          "congruence over smalloverlap",
                          "[quick][cong]") {
    FpSemigroup k;
    k.set_alphabet("abcdefg");
    k.add_rule("abcd", "aaaeaa");
    Congruence cong(twosided, k);
    cong.add_pair({4, 5}, {3, 6});
    REQUIRE(cong.is_quotient_obviously_infinite());
    REQUIRE(cong.number_of_generating_pairs() == 1);
    REQUIRE(cong.number_of_classes() == POSITIVE_INFINITY);
    REQUIRE(cong.class_index_to_word(100) == word_type({0, 6, 4}));
  }

  // The next 3 test cases are commented out because they test features we
  // decided not to include in v1.0.0.

  // LIBSEMIGROUPS_TEST_CASE("Congruence",
  //                         "044",
  //                         "Policy::none edge cases",
  //                         "[quick][cong]") {
  //   auto       rg = ReportGuard(REPORT);
  //   std::unique_ptr<Congruence> cong;
  //   SECTION("Congruence(congruence_kind)") {
  //     cong = detail::make_unique<Congruence>(twosided);
  //   }
  //   SECTION("Congruence(congruence_kind, FpSemigroup const&)") {
  //     FpSemigroup S;
  //     S.set_alphabet(2);
  //     cong = detail::make_unique<Congruence>(twosided, S);
  //   }
  //   REQUIRE(!cong->empty());
  //   REQUIRE_THROWS_AS(cong->set_number_of_generators(0),
  //   LibsemigroupsException); REQUIRE_THROWS_AS(!cong->contains({1}, {2, 2, 2,
  //   2, 2, 2, 2, 2, 2, 2}),
  //                     LibsemigroupsException);
  //   REQUIRE(cong->const_contains({1}, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2})
  //           == tril::unknown);
  //   REQUIRE_THROWS_AS(cong->number_of_classes(), LibsemigroupsException);
  //   REQUIRE_THROWS_AS(cong->word_to_class_index({2, 2, 2, 2}),
  //                     LibsemigroupsException);
  //   REQUIRE_THROWS_AS(cong->class_index_to_word(0), LibsemigroupsException);
  //   REQUIRE_THROWS_AS(cong->class_index_to_word(1), LibsemigroupsException);
  //   REQUIRE_THROWS_AS(cong->class_index_to_word(2), LibsemigroupsException);
  //   REQUIRE_THROWS_AS(cong->run(), LibsemigroupsException);
  // }

  // LIBSEMIGROUPS_TEST_CASE("Congruence",
  //                         "045",
  //                         "Policy::none",
  //                         "[quick][cong]") {
  //   auto rg      = ReportGuard(REPORT);

  //   using Transf = LeastTransf<5>;
  //   FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3,
  //   3})}); REQUIRE(S.size() == 88);

  //   Congruence cong(twosided, S, Congruence::Policy::none);
  //   REQUIRE(!cong.is_quotient_obviously_infinite());

  //   SECTION("ToddCoxeter") {
  //     congruence::ToddCoxeter tc(twosided, S);
  //     REQUIRE(tc.is_quotient_obviously_finite());
  //     REQUIRE(!tc.is_quotient_obviously_infinite());

  //     SECTION("add pair before copy") {
  //       tc.add_pair({0}, {0, 0});
  //       cong.add_runner(tc);
  //       REQUIRE(cong.number_of_classes() == 6);
  //     }
  //     SECTION("add pair after copy") {
  //       cong.add_runner(tc);
  //       tc.add_pair({0}, {0, 0});
  //       cong.add_pair({0}, {0, 0});
  //       REQUIRE(cong.number_of_classes() == 6);
  //     }
  //     SECTION("no added pairs") {
  //       cong.add_runner(tc);
  //       tc.add_pair({0}, {0, 0});
  //       REQUIRE(cong.number_of_classes() == 88);
  //     }
  //     REQUIRE(tc.number_of_classes() == 6);
  //   }
  //   SECTION("KnuthBendix") {
  //     congruence::KnuthBendix kb(S);
  //     REQUIRE(kb.is_quotient_obviously_finite());
  //     REQUIRE(!kb.is_quotient_obviously_infinite());

  //     SECTION("add pair before copy") {
  //       kb.add_pair({0}, {0, 0});
  //       cong.add_runner(kb);
  //       REQUIRE(cong.number_of_classes() == 6);
  //     }
  //     SECTION("add pair after copy") {
  //       cong.add_runner(kb);
  //       kb.add_pair({0}, {0, 0});
  //       cong.add_pair({0}, {0, 0});
  //       REQUIRE(cong.number_of_classes() == 6);
  //     }
  //     SECTION("no added pairs") {
  //       cong.add_runner(kb);
  //       kb.add_pair({0}, {0, 0});
  //       REQUIRE(cong.number_of_classes() == 88);
  //     }
  //     REQUIRE(kb.number_of_classes() == 6);
  //   }
  //   SECTION("ToddCoxeter + KnuthBendix") {
  //     congruence::ToddCoxeter tc(twosided, S);
  //     congruence::KnuthBendix kb(S);

  //     SECTION("add pair before copy") {
  //       tc.add_pair({0}, {0, 0});
  //       kb.add_pair({0}, {0, 0});
  //       cong.add_runner(tc);
  //       cong.add_runner(kb);
  //       REQUIRE(cong.number_runners() == 2);
  //       REQUIRE(cong.number_of_classes() == 6);
  //     }
  //     SECTION("add pair after copy") {
  //       cong.add_runner(tc);
  //       cong.add_runner(kb);
  //       REQUIRE(cong.number_runners() == 2);
  //       tc.add_pair({0}, {0, 0});
  //       kb.add_pair({0}, {0, 0});
  //       cong.add_pair({0}, {0, 0});
  //       REQUIRE(cong.number_of_classes() == 6);
  //     }
  //     SECTION("no added pairs") {
  //       cong.add_runner(tc);
  //       cong.add_runner(kb);
  //       REQUIRE(cong.number_runners() == 2);
  //       tc.add_pair({0}, {0, 0});
  //       kb.add_pair({0}, {0, 0});
  //       REQUIRE(cong.number_of_classes() == 88);
  //     }
  //     REQUIRE(tc.number_of_classes() == 6);
  //     REQUIRE(kb.number_of_classes() == 6);
  //   }
  //   REQUIRE(!cong.is_quotient_obviously_infinite());
  //   REQUIRE(cong.is_quotient_obviously_finite());
  // }

  // LIBSEMIGROUPS_TEST_CASE("Congruence",
  //                         "046",
  //                         "add_runner exceptions",
  //                         "[quick][cong]") {
  //   auto rg      = ReportGuard(REPORT);
  //   using Transf = LeastTransf<5>;
  //   FroidurePin<Transf> S({Transf({1, 3, 4, 2, 3}), Transf({3, 2, 1, 3,
  //   3})}); REQUIRE(S.size() == 88);

  //   SECTION("add runner after finished") {
  //     Congruence cong(twosided, S);
  //     REQUIRE(cong.number_of_classes() == 88);
  //     congruence::ToddCoxeter tc(twosided, S);
  //     REQUIRE_THROWS_AS(cong.add_runner(tc), LibsemigroupsException);
  //   }
  //   SECTION("add before number_of_generators is set") {
  //     Congruence cong(twosided);
  //     congruence::ToddCoxeter tc(twosided, S);
  //     REQUIRE_THROWS_AS(cong.add_runner(tc), LibsemigroupsException);
  //   }
  //   SECTION("add_runner when initialized with generators") {
  //     Congruence cong(twosided);
  //     cong.set_number_of_generators(2);
  //     REQUIRE(!cong.is_quotient_obviously_finite());
  //     REQUIRE(cong.is_quotient_obviously_infinite());

  //     congruence::ToddCoxeter tc(twosided, S);
  //     REQUIRE_THROWS_AS(cong.add_runner(tc), LibsemigroupsException);
  //     REQUIRE(tc.number_of_classes() == 88);
  //     REQUIRE(cong.number_of_classes() == POSITIVE_INFINITY);
  //   }
  //   SECTION("add_runner when initialized with generators + pairs") {
  //     Congruence cong(twosided);
  //     cong.set_number_of_generators(2);
  //     cong.add_pair({0, 0}, {0});
  //     REQUIRE(!cong.is_quotient_obviously_finite());
  //     REQUIRE(cong.is_quotient_obviously_infinite());

  //     congruence::ToddCoxeter tc(twosided, S);
  //     REQUIRE_THROWS_AS(cong.add_runner(tc), LibsemigroupsException);
  //     REQUIRE(tc.number_of_classes() == 88);
  //     REQUIRE(cong.number_of_classes() == POSITIVE_INFINITY);
  //   }
  //   SECTION("add_runner when not initialized + Policy::none") {
  //     Congruence cong(twosided, Congruence::Policy::none);

  //     size_t     old_nr_runners = cong.number_runners();
  //     REQUIRE(cong.number_runners() == 0);
  //     REQUIRE(!cong.is_quotient_obviously_finite());
  //     REQUIRE(!cong.is_quotient_obviously_infinite());
  //     REQUIRE(cong.number_of_generators() == UNDEFINED);
  //     {
  //       congruence::ToddCoxeter tc(twosided, S);
  //       REQUIRE_THROWS_AS(cong.add_runner(tc), LibsemigroupsException);
  //       REQUIRE(tc.number_of_classes() == 88);
  //     }
  //     {
  //       congruence::ToddCoxeter tc(twosided);
  //       REQUIRE_NOTHROW(cong.add_runner(tc));
  //       REQUIRE(cong.number_runners() == old_nr_runners + 1);
  //       congruence::KnuthBendix kb;
  //       REQUIRE_NOTHROW(cong.add_runner(kb));
  //       REQUIRE(cong.number_runners() == old_nr_runners + 2);
  //     }
  //     REQUIRE(!cong.is_quotient_obviously_finite());
  //     REQUIRE(!cong.is_quotient_obviously_infinite());
  //     REQUIRE(cong.number_of_generators() == S.number_of_generators());
  //     cong.add_pair({0, 0}, {0});
  //     REQUIRE(cong.number_of_classes() == 6);
  //   }
  //   SECTION("add_runner when not initialized + Policy::standard") {
  //     Congruence cong(twosided, Congruence::Policy::standard);
  //     size_t     old_nr_runners = cong.number_runners();
  //     REQUIRE(cong.number_runners() > 0);
  //     REQUIRE(!cong.is_quotient_obviously_finite());
  //     REQUIRE(!cong.is_quotient_obviously_infinite());
  //     REQUIRE(cong.number_of_generators() == UNDEFINED);
  //     {
  //       congruence::ToddCoxeter tc(twosided, S);
  //       REQUIRE_THROWS_AS(cong.add_runner(tc), LibsemigroupsException);
  //       REQUIRE(tc.number_of_classes() == 88);
  //     }
  //     {
  //       congruence::ToddCoxeter tc(twosided);
  //       REQUIRE_THROWS_AS(cong.add_runner(tc), LibsemigroupsException);
  //       REQUIRE(cong.number_runners() == old_nr_runners);
  //     }
  //     REQUIRE(!cong.is_quotient_obviously_finite());
  //     REQUIRE(!cong.is_quotient_obviously_infinite());
  //     REQUIRE(cong.number_of_generators() == S.number_of_generators());
  //     cong.add_pair({0, 0}, {0});
  //     REQUIRE(cong.number_of_classes() == 6);
  //   }
  // }

}  // namespace libsemigroups
