1 //
2 // libsemigroups - C++ library for semigroups and monoids
3 // Copyright (C) 2019 James D. Mitchell
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 // This file contains an implementation of the Todd-Coxeter algorithm for
20 // semigroups.
21 
22 #include "libsemigroups/todd-coxeter.hpp"
23 
24 #include <algorithm>  // for reverse
25 #include <chrono>     // for nanoseconds etc
26 #include <cstddef>    // for size_t
27 #include <memory>     // for shared_ptr
28 #include <numeric>    // for iota
29 #include <random>     // for mt19937
30 #include <string>     // for operator+, basic_string
31 #include <utility>    // for pair
32 
33 #ifdef LIBSEMIGROUPS_DEBUG
34 #include <set>  // for set
35 #endif
36 
37 #include "libsemigroups/cong-intf.hpp"          // for CongruenceInterface
38 #include "libsemigroups/coset.hpp"              // for CosetManager
39 #include "libsemigroups/froidure-pin-base.hpp"  // for FroidurePinBase
40 #include "libsemigroups/froidure-pin.hpp"       // for FroidurePin
41 #include "libsemigroups/knuth-bendix.hpp"       // for fpsemigroup::KnuthBendix
42 #include "libsemigroups/libsemigroups-config.hpp"  // for LIBSEMIGROUPS_DEBUG
43 #include "libsemigroups/libsemigroups-debug.hpp"   // for LIBSEMIGROUPS_ASSERT
44 #include "libsemigroups/libsemigroups-exception.hpp"  // for LIBSEMIGROUPS_EXCEPTION
45 #include "libsemigroups/obvinf.hpp"                   // for IsObviouslyInfinite
46 #include "libsemigroups/report.hpp"                   // for REPORT
47 #include "libsemigroups/stl.hpp"                      // for apply_permutation
48 #include "libsemigroups/tce.hpp"                      // for TCE
49 #include "libsemigroups/timer.hpp"                    // for detail::Timer
50 #include "libsemigroups/types.hpp"                    // for letter_type
51 
52 // TODO(later)
53 //
54 // 1. Explore whether row-filling is useful when performing HLT. I think the
55 //    just means making sure that there are no undefined values in the row of
56 //    the current coset, this is an option from ACE.
57 //
58 // 2. Allow there to be a limit to the number of deductions that are stacked.
59 //    this is an option from ACE. There are 4 options as described:
60 //
61 //    https://magma.maths.usyd.edu.au/magma/handbook/text/833
62 //
63 // 3. Explore whether deductions can be useful in HLT.
64 //
65 // 4. Make make_deductions_dfs non-recursive, this will likely only be an issue
66 //    for presentations with extremely long relations.
67 //
68 // 5. Use path compression in _ident, or other techniques from union-find, see:
69 //
70 //      https://www.boost.org/doc/libs/1_70_0/boost/pending/disjoint_sets.hpp
71 //
72 // 6. Wreath product standardize mem fn.
73 //
74 // 7. ACE stacks deductions when processing coincidences, we don't
75 
76 ////////////////////////////////////////////////////////////////////////////////
77 // COSET TABLES:
78 //
79 // We use these three tables to store all a coset's images and preimages.
80 //   _table[c][i] is coset c's image under generator i.
81 //   _preim_init[c][i] is ONE of coset c's preimages under generator i.
82 //   _preim_next[c][i] is a coset that has THE SAME IMAGE as coset c
83 //   (under i)
84 //
85 // Hence to find all the preimages of c under i:
86 //   - Let u = _preim_init[c][i] ONCE.
87 //   - Let u = _preim_next[u][i] REPEATEDLY until it becomes UNDEFINED.
88 // Each u is one preimage.
89 //
90 // To add v, a new preimage of c under i:
91 //   - Set _preim_next[v][i] to point to the current _preim_init[c][i].
92 //   - Then change _preim_init[c][i] to point to v.
93 // Now the new preimage and all the old preimages are stored.
94 ////////////////////////////////////////////////////////////////////////////////
95 
96 ////////////////////////////////////////////////////////////////////////
97 // Reporting macros
98 ////////////////////////////////////////////////////////////////////////
99 
100 #define TODD_COXETER_REPORT_COSETS()                                \
101   REPORT_DEFAULT("%d defined, %d max, %d active, %d killed (%s)\n", \
102                  nr_cosets_defined(),                               \
103                  coset_capacity(),                                  \
104                  nr_cosets_active(),                                \
105                  nr_cosets_killed(),                                \
106                  __func__);
107 
108 #define TODD_COXETER_REPORT_SWITCH(t, r)                                 \
109   REPORT_VERBOSE_DEFAULT("switching %*d %s and %*d %s\n",                \
110                          detail::to_string(nr_cosets_active()).length()  \
111                              - detail::to_string(t).length() + 1,        \
112                          t,                                              \
113                          (is_active_coset(t) ? "(active)" : "(free)  "), \
114                          detail::to_string(nr_cosets_active()).length()  \
115                              - detail::to_string(r).length() + 1,        \
116                          r,                                              \
117                          (is_active_coset(r) ? "(active)" : "(free)  "));
118 
119 #ifdef LIBSEMIGROUPS_DEBUG
120 #define TODD_COXETER_REPORT_OK() REPORT_DEBUG(" ok\n").flush_right().flush();
121 #else
122 #define TODD_COXETER_REPORT_OK()
123 #endif
124 
125 // Helper functions
126 namespace {
127   using class_index_type = libsemigroups::CongruenceInterface::class_index_type;
128   using word_type        = libsemigroups::word_type;
129 
sort_generating_pairs(std::vector<class_index_type> & perm,std::vector<word_type> & vec)130   void sort_generating_pairs(std::vector<class_index_type>& perm,
131                              std::vector<word_type>&        vec) {
132     // Apply the permutation (adapted from
133     // stl.hpp:apply_permutation)
134     size_t const n = perm.size();
135     for (size_t i = 0; i < n; ++i) {
136       size_t current = i;
137       while (i != perm[current]) {
138         size_t next = perm[current];
139         std::swap(vec[2 * current], vec[2 * next]);
140         std::swap(vec[2 * current + 1], vec[2 * next + 1]);
141         perm[current] = current;
142         current       = next;
143       }
144       perm[current] = current;
145     }
146   }
147 
sort_generating_pairs(std::function<bool (word_type const &,word_type const &)> func,std::vector<class_index_type> & perm,std::vector<word_type> & vec)148   void sort_generating_pairs(
149       std::function<bool(word_type const&, word_type const&)> func,
150       std::vector<class_index_type>&                          perm,
151       std::vector<word_type>&                                 vec) {
152     // Sort each relation so that the lhs is greater than the rhs according
153     // to func.
154     for (auto it = vec.begin(); it < vec.end(); it += 2) {
155       if (func(*it, *(it + 1))) {
156         std::swap(*it, *(it + 1));
157       }
158     }
159 
160     // Create a permutation of the even indexed entries in vec
161     perm.resize(vec.size() / 2);
162     std::iota(perm.begin(), perm.end(), 0);
163     std::sort(perm.begin(),
164               perm.end(),
165               [&func, &vec](class_index_type x, class_index_type y) -> bool {
166                 return func(vec[2 * x], vec[2 * y]);
167               });
168     sort_generating_pairs(perm, vec);
169   }
170 }  // namespace
171 
172 namespace libsemigroups {
173   namespace congruence {
174     ////////////////////////////////////////////////////////////////////////
175     // Aliases
176     ////////////////////////////////////////////////////////////////////////
177 
178     using coset_type  = congruence::ToddCoxeter::coset_type;
179     using Coincidence = std::pair<coset_type, coset_type>;
180     using Deduction   = std::pair<coset_type, letter_type>;
181 
182     ////////////////////////////////////////////////////////////////////////
183     // Helper structs
184     ////////////////////////////////////////////////////////////////////////
185 
186     struct StackDeductions {
operator ()libsemigroups::congruence::StackDeductions187       inline void operator()(std::stack<Deduction>& stck,
188                              coset_type             c,
189                              letter_type            a) const noexcept {
190         stck.emplace(c, a);
191       }
192     };
193 
194     struct DoNotStackDeductions {
operator ()libsemigroups::congruence::DoNotStackDeductions195       inline void operator()(std::stack<Deduction>&,
196                              coset_type,
197                              letter_type) const noexcept {}
198     };
199 
200     struct ProcessCoincidences {
201       template <typename TStackDeductions>
operator ()libsemigroups::congruence::ProcessCoincidences202       void operator()(congruence::ToddCoxeter* tc) const noexcept {
203         tc->process_coincidences<TStackDeductions>();
204       }
205     };
206 
207     struct DoNotProcessCoincidences {
208       template <typename TStackDeductions>
operator ()libsemigroups::congruence::DoNotProcessCoincidences209       void operator()(congruence::ToddCoxeter*) const noexcept {}
210     };
211 
212     ////////////////////////////////////////////////////////////////////////
213     // Helper free functions
214     ////////////////////////////////////////////////////////////////////////
215 
ff(coset_type c,coset_type d,coset_type r)216     static inline coset_type ff(coset_type c, coset_type d, coset_type r) {
217       return (r == c ? d : (r == d ? c : r));
218     }
219 
220     ////////////////////////////////////////////////////////////////////////
221     // ToddCoxeter - inner classes - private
222     ////////////////////////////////////////////////////////////////////////
223 
224     struct ToddCoxeter::Settings {
Settingslibsemigroups::congruence::ToddCoxeter::Settings225       Settings()
226           :
227 #ifdef LIBSEMIGROUPS_DEBUG
228             enable_debug_verify_no_missing_deductions(true),
229 #endif
230             lookahead(policy::lookahead::partial),
231             lower_bound(UNDEFINED),
232             next_lookahead(5000000),
233             froidure_pin(policy::froidure_pin::none),
234             random_interval(200000000),
235             save(false),
236             standardize(false),
237             strategy(policy::strategy::hlt) {
238       }
239 
240       Settings(Settings const& copy) = default;
241 
242 #ifdef LIBSEMIGROUPS_DEBUG
243       bool enable_debug_verify_no_missing_deductions;
244 #endif
245       policy::lookahead        lookahead;
246       size_t                   lower_bound;
247       size_t                   next_lookahead;
248       policy::froidure_pin     froidure_pin;
249       std::chrono::nanoseconds random_interval;
250       bool                     save;
251       bool                     standardize;
252       policy::strategy         strategy;
253     };
254 
255     class ToddCoxeter::FelschTree {
256      public:
257       using index_type     = size_t;
258       using state_type     = size_t;
259       using const_iterator = std::vector<index_type>::const_iterator;
260       static constexpr state_type initial_state = 0;
261       static constexpr state_type final_state   = UNDEFINED;
262 
FelschTree(ToddCoxeter const * tc)263       explicit FelschTree(ToddCoxeter const* tc)
264           : _automata(tc->nr_generators(), 1, final_state),
265             _index(1, std::vector<index_type>({})),
266             _parent(1, state_type(UNDEFINED)) {}
267 
268       FelschTree(FelschTree const&) = default;
269 
add_relations(std::vector<word_type> const & rels)270       void add_relations(std::vector<word_type> const& rels) {
271         size_t nr_words = 0;
272         LIBSEMIGROUPS_ASSERT(rels.size() % 2 == 0);
273         for (auto const& w : rels) {
274           // For every prefix [w.cbegin(), last)
275           for (auto last = w.cend(); last > w.cbegin(); --last) {
276             // For every suffix [first, last) of the prefix [w.cbegin(), last)
277             for (auto first = w.cbegin(); first < last; ++first) {
278               // Find the maximal suffix of [first, last) that corresponds to
279               // an existing state . . .
280               auto       it = last - 1;
281               state_type s  = initial_state;
282               while (_automata.get(s, *it) != final_state && it > first) {
283                 s = _automata.get(s, *it);
284                 --it;
285               }
286               if (_automata.get(s, *it) == final_state) {
287                 // [it + 1, last) is the maximal suffix of [first, last) that
288                 // corresponds to the existing state s
289                 size_t nr_states = _automata.nr_rows();
290                 _automata.add_rows((it + 1) - first);
291                 _index.resize(_index.size() + ((it + 1) - first), {});
292                 _parent.resize(_parent.size() + ((it + 1) - first), UNDEFINED);
293                 while (it >= first) {
294                   // Add [it, last) as a new state
295                   _automata.set(s, *it, nr_states);
296                   _parent[nr_states] = s;
297                   s                  = nr_states;
298                   nr_states++;
299                   it--;
300                 }
301               }
302             }
303             // Find the state corresponding to the prefix [w.cbegin(), last)
304             auto       it = last - 1;
305             state_type s  = initial_state;
306             while (it >= w.cbegin()) {
307               s = _automata.get(s, *it);
308               LIBSEMIGROUPS_ASSERT(s != final_state);
309               --it;
310             }
311             index_type m = ((nr_words % 2) == 0 ? nr_words : nr_words - 1);
312             if (!std::binary_search(_index[s].cbegin(), _index[s].cend(), m)) {
313               _index[s].push_back(m);
314             }
315           }
316           nr_words++;
317         }
318       }
319 
push_back(letter_type x)320       void push_back(letter_type x) {
321         _current_state = _automata.get(initial_state, x);
322       }
323 
push_front(letter_type x)324       bool push_front(letter_type x) {
325         LIBSEMIGROUPS_ASSERT(x < _automata.nr_cols());
326         if (_automata.get(_current_state, x) != final_state) {
327           _current_state = _automata.get(_current_state, x);
328           return true;
329         } else {
330           return false;
331         }
332       }
333 
pop_front()334       void pop_front() {
335         _current_state = _parent[_current_state];
336       }
337 
cbegin() const338       const_iterator cbegin() const {
339         LIBSEMIGROUPS_ASSERT(_current_state != final_state);
340         return _index[_current_state].cbegin();
341       }
342 
cend() const343       const_iterator cend() const {
344         LIBSEMIGROUPS_ASSERT(_current_state != final_state);
345         return _index[_current_state].cend();
346       }
347 
348      private:
349       using StateTable = detail::DynamicArray2<state_type>;
350       StateTable                           _automata;
351       state_type                           _current_state;
352       std::vector<std::vector<index_type>> _index;
353       std::vector<state_type>              _parent;
354     };
355 
356     struct ToddCoxeter::TreeNode {
TreeNodelibsemigroups::congruence::ToddCoxeter::TreeNode357       TreeNode() : parent(UNDEFINED), gen(UNDEFINED) {}
TreeNodelibsemigroups::congruence::ToddCoxeter::TreeNode358       TreeNode(coset_type p, letter_type g) : parent(p), gen(g) {}
359       coset_type  parent;
360       letter_type gen;
361     };
362 
363     ////////////////////////////////////////////////////////////////////////
364     // ToddCoxeter - constructors and destructor - public
365     ////////////////////////////////////////////////////////////////////////
366 
ToddCoxeter(congruence_type type)367     ToddCoxeter::ToddCoxeter(congruence_type type)
368         : CongruenceInterface(type),
369           CosetManager(),
370           _coinc(),
371           _deduct(),
372           _extra(),
373           _felsch_tree(nullptr),
374           _nr_pairs_added_earlier(0),
375           _prefilled(false),
376           _preim_init(0, 0, UNDEFINED),
377           _preim_next(0, 0, UNDEFINED),
378           _relations(),
379           _settings(new Settings()),
380           _standardized(order::none),
381           _state(state::constructed),
382           _table(0, 0, UNDEFINED),
383           _tree(nullptr) {}
384 
ToddCoxeter(ToddCoxeter const & copy)385     ToddCoxeter::ToddCoxeter(ToddCoxeter const& copy)
386         : CongruenceInterface(copy),
387           CosetManager(copy),
388           _coinc(copy._coinc),
389           _deduct(copy._deduct),
390           _extra(copy._extra),
391           _felsch_tree(nullptr),
392           _nr_pairs_added_earlier(copy._nr_pairs_added_earlier),
393           _prefilled(copy._prefilled),
394           _preim_init(copy._preim_init),
395           _preim_next(copy._preim_next),
396           _relations(copy._relations),
397           _settings(detail::make_unique<Settings>(*copy._settings)),
398           _standardized(copy._standardized),
399           _state(copy._state),
400           _table(copy._table),
401           _tree(nullptr) {
402       if (copy._felsch_tree != nullptr) {
403         _felsch_tree = detail::make_unique<FelschTree>(*copy._felsch_tree);
404       }
405       if (copy._tree != nullptr) {
406         _tree = detail::make_unique<Tree>(*copy._tree);
407       }
408     }
409 
ToddCoxeter(congruence_type type,std::shared_ptr<FroidurePinBase> S,policy::froidure_pin p)410     ToddCoxeter::ToddCoxeter(congruence_type                  type,
411                              std::shared_ptr<FroidurePinBase> S,
412                              policy::froidure_pin             p)
413         : ToddCoxeter(type) {
414       _settings->froidure_pin = p;
415       set_parent_froidure_pin(S);
416       set_nr_generators(S->nr_generators());
417     }
418 
419     // Construct a ToddCoxeter object representing a congruence over the
420     // semigroup defined by copy (the quotient that is).
ToddCoxeter(congruence_type typ,ToddCoxeter & copy)421     ToddCoxeter::ToddCoxeter(congruence_type typ, ToddCoxeter& copy)
422         : ToddCoxeter(typ) {
423       if (copy.kind() != congruence_type::twosided && typ != copy.kind()) {
424         LIBSEMIGROUPS_EXCEPTION("incompatible types of congruence, found ("
425                                 + congruence_type_to_string(copy.kind()) + " / "
426                                 + congruence_type_to_string(typ)
427                                 + ") but only (left / left), (right / "
428                                   "right), (two-sided / *) are valid");
429       }
430       copy_relations_for_quotient(copy);
431     }
432 
ToddCoxeter(congruence_type typ,fpsemigroup::ToddCoxeter & copy)433     ToddCoxeter::ToddCoxeter(congruence_type           typ,
434                              fpsemigroup::ToddCoxeter& copy)
435         : ToddCoxeter(typ) {
436       set_parent_froidure_pin(copy);
437       if (copy.finished()) {
438         set_nr_generators(copy.froidure_pin()->nr_generators());
439         _settings->froidure_pin = policy::froidure_pin::use_cayley_graph;
440       } else {
441         copy_relations_for_quotient(copy.congruence());
442         _settings->froidure_pin = policy::froidure_pin::use_relations;
443       }
444     }
445 
ToddCoxeter(congruence_type typ,fpsemigroup::KnuthBendix & kb)446     ToddCoxeter::ToddCoxeter(congruence_type typ, fpsemigroup::KnuthBendix& kb)
447         : ToddCoxeter(typ) {
448       set_nr_generators(kb.alphabet().size());
449       // TODO(later) use active rules when available
450       for (auto it = kb.cbegin_rules(); it < kb.cend_rules(); ++it) {
451         add_pair(kb.string_to_word(it->first), kb.string_to_word(it->second));
452       }
453       // Note that something goes horribly wrong if the next line is above the
454       // for loop above.
455       set_parent_froidure_pin(kb);
456       if (kb.finished() && kb.is_obviously_finite()) {
457         LIBSEMIGROUPS_ASSERT(_settings->froidure_pin
458                              == policy::froidure_pin::none);
459         _settings->froidure_pin = policy::froidure_pin::use_cayley_graph;
460       }
461     }
462 
463     ToddCoxeter::~ToddCoxeter() = default;
464 
465     ////////////////////////////////////////////////////////////////////////
466     // CongruenceInterface - non-pure virtual member functions - public
467     ////////////////////////////////////////////////////////////////////////
468 
contains(word_type const & lhs,word_type const & rhs)469     bool ToddCoxeter::contains(word_type const& lhs, word_type const& rhs) {
470       validate_word(lhs);
471       validate_word(rhs);
472       init();
473       if (empty()) {
474         // Note that it's possible to be not _prefilled, and have _relations,
475         // and _extra empty, because shrink_to_fit clears _relations and
476         // _extra. That's why we use empty() here instead of checking
477         // _prefilled && _relations.empty() && _extra.empty(), as used to be the
478         // case.
479         // This defines the free semigroup
480         return lhs == rhs;
481       }
482       return CongruenceInterface::contains(lhs, rhs);
483     }
484 
485     ////////////////////////////////////////////////////////////////////////
486     // ToddCoxeter - member functions (init + settings) - public
487     ////////////////////////////////////////////////////////////////////////
488 
489     // Init
prefill(table_type const & table)490     void ToddCoxeter::prefill(table_type const& table) {
491       prefill_and_validate(table, true);
492       init_preimages_from_table();
493     }
494 
495     // Settings
496     ToddCoxeter&
froidure_pin_policy(policy::froidure_pin x)497     ToddCoxeter::froidure_pin_policy(policy::froidure_pin x) noexcept {
498       _settings->froidure_pin = x;
499       return *this;
500     }
501 
502     ToddCoxeter::policy::froidure_pin
froidure_pin_policy() const503     ToddCoxeter::froidure_pin_policy() const noexcept {
504       return _settings->froidure_pin;
505     }
506 
lookahead(policy::lookahead x)507     ToddCoxeter& ToddCoxeter::lookahead(policy::lookahead x) noexcept {
508       _settings->lookahead = x;
509       return *this;
510     }
511 
lower_bound(size_t n)512     ToddCoxeter& ToddCoxeter::lower_bound(size_t n) noexcept {
513       _settings->lower_bound = n;
514       return *this;
515     }
516 
next_lookahead(size_t n)517     ToddCoxeter& ToddCoxeter::next_lookahead(size_t n) noexcept {
518       _settings->next_lookahead = n;
519       return *this;
520     }
521 
standardize(bool x)522     ToddCoxeter& ToddCoxeter::standardize(bool x) noexcept {
523       _settings->standardize = x;
524       return *this;
525     }
526 
save(bool x)527     ToddCoxeter& ToddCoxeter::save(bool x) {
528       if ((_prefilled
529            || (has_parent_froidure_pin()
530                && parent_froidure_pin()->is_finite() == tril::TRUE
531                && (_settings->froidure_pin == policy::froidure_pin::none
532                    || _settings->froidure_pin
533                           == policy::froidure_pin::use_cayley_graph)))
534           && x) {
535         LIBSEMIGROUPS_EXCEPTION("cannot use the save setting with a "
536                                 "prefilled ToddCoxeter instance");
537       }
538       _settings->save = x;
539       return *this;
540     }
541 
strategy(policy::strategy x)542     ToddCoxeter& ToddCoxeter::strategy(policy::strategy x) {
543       if ((_prefilled
544            || (has_parent_froidure_pin()
545                && parent_froidure_pin()->is_finite() == tril::TRUE
546                && (_settings->froidure_pin == policy::froidure_pin::none
547                    || _settings->froidure_pin
548                           == policy::froidure_pin::use_cayley_graph)))
549           && x == policy::strategy::felsch) {
550         LIBSEMIGROUPS_EXCEPTION("cannot use the Felsch strategy with a "
551                                 "prefilled ToddCoxeter instance");
552       }
553       _settings->strategy = x;
554       return *this;
555     }
556 
strategy() const557     ToddCoxeter::policy::strategy ToddCoxeter::strategy() const noexcept {
558       return _settings->strategy;
559     }
560 
561     ToddCoxeter&
random_interval(std::chrono::nanoseconds x)562     ToddCoxeter::random_interval(std::chrono::nanoseconds x) noexcept {
563       _settings->random_interval = x;
564       return *this;
565     }
566 
sort_generating_pairs(std::function<bool (word_type const &,word_type const &)> func)567     ToddCoxeter& ToddCoxeter::sort_generating_pairs(
568         std::function<bool(word_type const&, word_type const&)> func) {
569       if (started()) {
570         LIBSEMIGROUPS_EXCEPTION(
571             "Cannot sort relations, the coset enumeration has started!")
572       }
573       init();
574       std::vector<class_index_type> perm;
575       ::sort_generating_pairs(func, perm, _relations);
576       ::sort_generating_pairs(func, perm, _extra);
577       return *this;
578     }
579 
random_shuffle_generating_pairs()580     ToddCoxeter& ToddCoxeter::random_shuffle_generating_pairs() {
581       if (started()) {
582         LIBSEMIGROUPS_EXCEPTION(
583             "Cannot shuffle relations, the coset enumeration has started!")
584       }
585       init();
586       std::vector<class_index_type> perm(0, _relations.size());
587       std::iota(perm.begin(), perm.end(), 0);
588       std::random_device rd;
589       std::mt19937       g(rd());
590       std::shuffle(perm.begin(), perm.end(), g);
591       ::sort_generating_pairs(perm, _relations);
592       ::sort_generating_pairs(perm, _extra);
593       return *this;
594     }
595 
596     ////////////////////////////////////////////////////////////////////////
597     // ToddCoxeter - member functions (container-like) - public
598     ////////////////////////////////////////////////////////////////////////
599 
empty() const600     bool ToddCoxeter::empty() const {
601       return _relations.empty() && _extra.empty() && nr_cosets_active() == 1;
602     }
603 
reserve(size_t n)604     void ToddCoxeter::reserve(size_t n) {
605       size_t m = coset_capacity();
606       if (n > m) {
607         m = n - m;
608         _table.add_rows(m);
609         _preim_init.add_rows(m);
610         _preim_next.add_rows(m);
611         add_free_cosets(m);
612       }
613     }
614 
shrink_to_fit()615     void ToddCoxeter::shrink_to_fit() {
616       if (!finished()) {
617         return;
618       }
619       if (!is_standardized()) {
620         standardize(order::shortlex);
621       }
622 
623       _table.shrink_rows_to(nr_cosets_active());
624       // Cannot delete _preim_init or _preim_next because they are required by
625       // standardize
626       _preim_init.shrink_rows_to(nr_cosets_active());
627       _preim_next.shrink_rows_to(nr_cosets_active());
628       _relations.clear();
629       _relations.shrink_to_fit();
630       _extra.clear();
631       _extra.shrink_to_fit();
632       erase_free_cosets();
633     }
634 
635     ////////////////////////////////////////////////////////////////////////
636     // ToddCoxeter - member functions (state) - public
637     ////////////////////////////////////////////////////////////////////////
638 
complete() const639     bool ToddCoxeter::complete() const noexcept {
640       size_t const n = nr_generators();
641       coset_type   c = _id_coset;
642       while (c != first_free_coset()) {
643         for (size_t a = 0; a < n; ++a) {
644           if (_table.get(c, a) == UNDEFINED) {
645             return false;
646           }
647         }
648         c = next_active_coset(c);
649       }
650       return true;
651     }
652 
compatible() const653     bool ToddCoxeter::compatible() const noexcept {
654       coset_type c = _id_coset;
655       while (c != first_free_coset()) {
656         for (auto it = _relations.cbegin(); it < _relations.cend(); it += 2) {
657           coset_type x = tau(c, (*it).cbegin(), (*it).cend());
658           LIBSEMIGROUPS_ASSERT(is_active_coset(x) || x == UNDEFINED);
659           coset_type y = tau(c, (*(it + 1)).cbegin(), (*(it + 1)).cend());
660           LIBSEMIGROUPS_ASSERT(is_active_coset(y) || y == UNDEFINED);
661           if (x != y) {
662             return false;
663           }
664         }
665         c = next_active_coset(c);
666       }
667       return true;
668     }
669 
670     ////////////////////////////////////////////////////////////////////////
671     // ToddCoxeter - member functions (standardization) - public
672     ////////////////////////////////////////////////////////////////////////
673 
is_standardized() const674     bool ToddCoxeter::is_standardized() const noexcept {
675       return _standardized != order::none;
676     }
677 
standardize(order rdr)678     void ToddCoxeter::standardize(order rdr) {
679       if (_standardized == rdr || empty()) {
680         return;
681       }
682       switch (rdr) {
683         case order::shortlex:
684           init_standardize();
685           shortlex_standardize();
686           break;
687         case order::lex:
688           init_standardize();
689           lex_standardize();
690           break;
691         case order::recursive:
692           init_standardize();
693           recursive_standardize();
694           break;
695         case order::none: {
696         }
697         default: {
698         }
699       }
700       if (finished()) {
701         _standardized = rdr;
702       }
703     }
704 
705     ////////////////////////////////////////////////////////////////////////
706     // CongruenceInterface - pure virtual member functions - private
707     ////////////////////////////////////////////////////////////////////////
708 
class_index_to_word_impl(coset_type i)709     word_type ToddCoxeter::class_index_to_word_impl(coset_type i) {
710       run();
711       if (!is_standardized()) {
712         standardize(order::shortlex);
713       }
714       LIBSEMIGROUPS_ASSERT(finished());
715       word_type w;
716       TreeNode  tn = (*_tree)[i + 1];
717       while (tn.parent != UNDEFINED) {
718         w.push_back(tn.gen);
719         tn = (*_tree)[tn.parent];
720       }
721       if (kind() != congruence_type::left) {
722         std::reverse(w.begin(), w.end());
723       }
724       return w;
725     }
726 
nr_classes_impl()727     size_t ToddCoxeter::nr_classes_impl() {
728       run();
729       LIBSEMIGROUPS_ASSERT(finished());
730       return nr_cosets_active() - 1;
731     }
732 
quotient_impl()733     std::shared_ptr<FroidurePinBase> ToddCoxeter::quotient_impl() {
734       using detail::TCE;
735       run();
736       standardize(order::shortlex);
737       shrink_to_fit();
738       // Ensure class indices and letters are equal!
739       auto   table = std::make_shared<table_type>(_table);
740       size_t n     = nr_generators();
741       for (letter_type a = 0; a < n;) {
742         if (table->get(0, a) != a + 1) {
743           table->erase_column(a);
744           n--;
745         } else {
746           ++a;
747         }
748       }
749       auto ptr = std::make_shared<
750           FroidurePin<TCE, FroidurePinTraits<TCE, table_type>>>(table);
751       for (size_t i = 0; i < nr_generators(); ++i) {
752         // We use table.get(0, i) instead of just i, because there might be
753         // more generators than cosets.
754         ptr->add_generator(TCE(_table.get(0, i)));
755       }
756       return ptr;
757     }
758 
run_impl()759     void ToddCoxeter::run_impl() {
760       if (is_quotient_obviously_infinite()) {
761         LIBSEMIGROUPS_EXCEPTION(
762             "there are infinitely many classes in the congruence and "
763             "Todd-Coxeter will never terminate");
764       }
765       if (_settings->lower_bound != UNDEFINED) {
766         size_t const bound     = _settings->lower_bound;
767         _settings->lower_bound = UNDEFINED;
768         run_until([this, &bound]() -> bool {
769           return (nr_cosets_active() == bound) && complete();
770         });
771       } else if (_settings->strategy == policy::strategy::felsch) {
772         felsch();
773       } else if (_settings->strategy == policy::strategy::hlt) {
774         hlt();
775       } else if (_settings->strategy == policy::strategy::random) {
776         sims();
777       }
778     }
779 
finished_impl() const780     bool ToddCoxeter::finished_impl() const {
781       return _state == state::finished;
782     }
783 
word_to_class_index_impl(word_type const & w)784     coset_type ToddCoxeter::word_to_class_index_impl(word_type const& w) {
785       run();
786       LIBSEMIGROUPS_ASSERT(finished());
787       if (!is_standardized()) {
788         standardize(order::shortlex);
789       }
790       coset_type c = const_word_to_class_index(w);
791       // c is in the range 1, ..., nr_cosets_active() because 0 represents the
792       // identity coset, and does not correspond to an element.
793       return c;
794     }
795 
796     ////////////////////////////////////////////////////////////////////////
797     // CongruenceInterface - non-pure virtual member functions - private
798     ////////////////////////////////////////////////////////////////////////
799 
800     coset_type
const_word_to_class_index(word_type const & w) const801     ToddCoxeter::const_word_to_class_index(word_type const& w) const {
802       validate_word(w);
803       coset_type c = _id_coset;
804 
805       if (kind() == congruence_type::left) {
806         c = tau(c, w.crbegin(), w.crend());
807       } else {
808         c = tau(c, w.cbegin(), w.cend());
809       }
810       return (c == UNDEFINED ? c : c - 1);
811     }
812 
is_quotient_obviously_finite_impl()813     bool ToddCoxeter::is_quotient_obviously_finite_impl() {
814       if (finished()) {
815         return true;
816       }
817       init();
818       return _prefilled;
819       // _prefilled means that either we were created from a FroidurePinBase*
820       // with _settings->froidure_pin = use_cayley_graph and we successfully
821       // prefilled the table, or we manually prefilled the table.  In this case
822       // the semigroup defined by _relations must be finite.
823     }
824 
is_quotient_obviously_infinite_impl()825     bool ToddCoxeter::is_quotient_obviously_infinite_impl() {
826       if (finished()) {
827         return false;
828       }
829       init();
830       if (_prefilled) {
831         return false;
832       } else if (nr_generators() > _relations.size() + _extra.size()) {
833         return true;
834       }
835       detail::IsObviouslyInfinite ioi(nr_generators());
836       ioi.add_rules(_relations.cbegin(), _relations.cend());
837       ioi.add_rules(_extra.cbegin(), _extra.cend());
838       return ioi.result();
839     }
840 
set_nr_generators_impl(size_t n)841     void ToddCoxeter::set_nr_generators_impl(size_t n) {
842       // TODO(later) add columns to make it up to n?
843       _preim_init = table_type(n, 1, UNDEFINED);
844       _preim_next = table_type(n, 1, UNDEFINED);
845       _table      = table_type(n, 1, UNDEFINED);
846     }
847 
848     ////////////////////////////////////////////////////////////////////////
849     // ToddCoxeter - member functions (validation) - private
850     ////////////////////////////////////////////////////////////////////////
851 
validate_table(table_type const & table,size_t const first,size_t const last) const852     void ToddCoxeter::validate_table(table_type const& table,
853                                      size_t const      first,
854                                      size_t const      last) const {
855       REPORT_DEBUG_DEFAULT("validating coset table...\n");
856       if (nr_generators() == UNDEFINED) {
857         LIBSEMIGROUPS_EXCEPTION("no generators have been defined");
858       } else if (table.nr_cols() != nr_generators()) {
859         LIBSEMIGROUPS_EXCEPTION("invalid table, expected %d columns, found %d",
860                                 nr_generators(),
861                                 table.nr_cols());
862       }
863       if (last - first <= 0) {
864         LIBSEMIGROUPS_EXCEPTION(
865             "invalid table, expected at least 1 rows, found %d",
866             table.nr_rows());
867       }
868       for (size_t i = first; i < last; ++i) {
869         for (size_t j = 0; j < table.nr_cols(); ++j) {
870           coset_type c = table.get(i, j);
871           if (c < first || c >= last) {
872             LIBSEMIGROUPS_EXCEPTION(
873                 "invalid table, expected entries in the range [%d, %d), found "
874                 "%d in row %d, column %d",
875                 i,
876                 j,
877                 first,
878                 last,
879                 c);
880           }
881         }
882       }
883       REPORT_DEBUG_DEFAULT("...coset table ok\n");
884     }
885 
886     ////////////////////////////////////////////////////////////////////////
887     // ToddCoxeter - member functions (initialisation) - private
888     ////////////////////////////////////////////////////////////////////////
889 
890     //! Copy all _relations and _extra from copy into _relations of this
copy_relations_for_quotient(ToddCoxeter & copy)891     void ToddCoxeter::copy_relations_for_quotient(ToddCoxeter& copy) {
892       REPORT_DEBUG_DEFAULT("copying relations for quotient...\n");
893       LIBSEMIGROUPS_ASSERT(empty());
894       copy.init();
895       set_nr_generators(copy.nr_generators());
896       _state     = state::initialized;
897       _relations = copy._relations;
898       _relations.insert(
899           _relations.end(), copy._extra.cbegin(), copy._extra.cend());
900       if (kind() == congruence_type::left
901           && copy.kind() != congruence_type::left) {
902         for (auto& w : _relations) {
903           std::reverse(w.begin(), w.end());
904         }
905       }
906       _nr_pairs_added_earlier = 0;
907     }
908 
init()909     void ToddCoxeter::init() {
910       if (_state == state::constructed) {
911         REPORT_DEBUG_DEFAULT("initializing...\n");
912         // Add the relations/Cayley graph from parent() if any.
913         if (has_parent_froidure_pin()
914             && parent_froidure_pin()->is_finite() == tril::TRUE) {
915           if (_settings->froidure_pin == policy::froidure_pin::use_cayley_graph
916               || _settings->froidure_pin == policy::froidure_pin::none) {
917             REPORT_DEBUG_DEFAULT("using Cayley graph...\n");
918             LIBSEMIGROUPS_ASSERT(_relations.empty());
919             prefill(*parent_froidure_pin());
920 #ifdef LIBSEMIGROUPS_DEBUG
921             // This is a check of program logic, since we use parent() to fill
922             // the table, so we only validate in debug mode.
923             validate_table(_table, 1, parent_froidure_pin()->size() + 1);
924 #endif
925           } else {
926             REPORT_DEBUG_DEFAULT("using presentation...\n");
927             LIBSEMIGROUPS_ASSERT(_settings->froidure_pin
928                                  == policy::froidure_pin::use_relations);
929             auto fp = parent_froidure_pin();
930             fp->run();
931             for (auto it = fp->cbegin_rules(); it != fp->cend_rules(); ++it) {
932               reverse_if_necessary_and_push_back(it->first, _relations);
933               reverse_if_necessary_and_push_back(it->second, _relations);
934             }
935 #ifdef LIBSEMIGROUPS_DEBUG
936             // This is a check of program logic, since we use parent() to
937             // obtain the relations, so we only validate in debug mode.
938             for (auto const& rel : _relations) {
939               validate_word(rel);
940             }
941             // We don't add anything to _extra here so no need to check.
942 #endif
943           }
944         }
945         _state = state::initialized;
946       }
947 
948       // Get new generating pairs (if any) from CongruenceInterface (if any)
949       auto it = cbegin_generating_pairs() + _nr_pairs_added_earlier;
950       if (kind() != congruence_type::twosided) {
951         for (; it < cend_generating_pairs(); ++it) {
952           reverse_if_necessary_and_push_back(it->first, _extra);
953           reverse_if_necessary_and_push_back(it->second, _extra);
954         }
955       } else {
956         for (; it < cend_generating_pairs(); ++it) {
957           reverse_if_necessary_and_push_back(it->first, _relations);
958           reverse_if_necessary_and_push_back(it->second, _relations);
959         }
960       }
961       _nr_pairs_added_earlier
962           = cend_generating_pairs() - cbegin_generating_pairs();
963     }
964 
init_felsch_tree()965     void ToddCoxeter::init_felsch_tree() {
966       LIBSEMIGROUPS_ASSERT(_state >= state::initialized);
967       if (_felsch_tree == nullptr) {
968         REPORT_DEFAULT("initializing the Felsch tree...\n");
969         detail::Timer tmr;
970         _felsch_tree = detail::make_unique<FelschTree>(this);
971         _felsch_tree->add_relations(_relations);
972         REPORT_TIME(tmr);
973       }
974     }
975 
init_preimages_from_table()976     void ToddCoxeter::init_preimages_from_table() {
977       REPORT_DEBUG("initializing preimages...\n");
978       LIBSEMIGROUPS_ASSERT(_table.nr_cols() == nr_generators());
979       LIBSEMIGROUPS_ASSERT(_table.nr_rows() >= nr_cosets_active());
980       LIBSEMIGROUPS_ASSERT(_prefilled);
981       LIBSEMIGROUPS_ASSERT(_state == state::constructed);
982       LIBSEMIGROUPS_ASSERT(_relations.empty());
983 
984       for (coset_type c = 0; c < nr_cosets_active(); c++) {
985         for (size_t i = 0; i < nr_generators(); i++) {
986           coset_type b = _table.get(c, i);
987           _preim_next.set(c, i, _preim_init.get(b, i));
988           _preim_init.set(b, i, c);
989         }
990       }
991     }
992 
prefill(FroidurePinBase & S)993     void ToddCoxeter::prefill(FroidurePinBase& S) {
994       REPORT_DEBUG_DEFAULT("prefilling the coset table from FroidurePin...\n");
995       LIBSEMIGROUPS_ASSERT(_state == state::constructed);
996       LIBSEMIGROUPS_ASSERT(
997           _settings->froidure_pin == policy::froidure_pin::use_cayley_graph
998           || _settings->froidure_pin == policy::froidure_pin::none);
999       LIBSEMIGROUPS_ASSERT(S.nr_generators() == nr_generators());
1000       if (kind() == congruence_type::left) {
1001         prefill_and_validate(S.left_cayley_graph(), false);
1002       } else {
1003         prefill_and_validate(S.right_cayley_graph(), false);
1004       }
1005       for (size_t i = 0; i < nr_generators(); i++) {
1006         _table.set(0, i, S.letter_to_pos(i) + 1);
1007       }
1008       init_preimages_from_table();
1009     }
1010 
prefill_and_validate(table_type const & table,bool validate)1011     void ToddCoxeter::prefill_and_validate(table_type const& table,
1012                                            bool              validate) {
1013       if (_settings->strategy == policy::strategy::felsch) {
1014         LIBSEMIGROUPS_EXCEPTION(
1015             "it is not possible to prefill when using the Felsch strategy");
1016       }
1017       if (!empty()) {
1018         LIBSEMIGROUPS_EXCEPTION("cannot prefill a non-empty instance")
1019       }
1020       if (validate) {
1021         validate_table(table, 0, table.nr_rows());
1022       }
1023 
1024       REPORT_DEBUG("prefilling the coset table...\n");
1025       _prefilled = true;
1026       size_t m   = table.nr_rows() + 1;
1027       _table.add_rows(m - _table.nr_rows());
1028       for (size_t i = 0; i < _table.nr_cols(); i++) {
1029         _table.set(0, i, i + 1);
1030       }
1031       for (size_t row = 0; row < _table.nr_rows() - 1; ++row) {
1032         for (size_t col = 0; col < _table.nr_cols(); ++col) {
1033           _table.set(row + 1, col, table.get(row, col) + 1);
1034         }
1035       }
1036       add_active_cosets(m - nr_cosets_active());
1037       _preim_init.add_rows(m - _preim_init.nr_rows());
1038       _preim_next.add_rows(m - _preim_next.nr_rows());
1039     }
1040 
1041     void
reverse_if_necessary_and_push_back(word_type w,std::vector<word_type> & v)1042     ToddCoxeter::reverse_if_necessary_and_push_back(word_type               w,
1043                                                     std::vector<word_type>& v) {
1044       if (kind() == congruence_type::left) {
1045         std::reverse(w.begin(), w.end());
1046       }
1047       v.push_back(std::move(w));
1048     }
1049 
1050     ////////////////////////////////////////////////////////////////////////
1051     // ToddCoxeter - member functions (cosets) - private
1052     ////////////////////////////////////////////////////////////////////////
1053 
new_coset()1054     coset_type ToddCoxeter::new_coset() {
1055       if (!has_free_cosets()) {
1056         reserve(2 * coset_capacity());
1057         return new_active_coset();
1058       } else {
1059         coset_type const c = new_active_coset();
1060         // Clear the new coset's row in each table
1061         for (letter_type i = 0; i < nr_generators(); i++) {
1062           _table.set(c, i, UNDEFINED);
1063           _preim_init.set(c, i, UNDEFINED);
1064         }
1065         return c;
1066       }
1067     }
1068 
remove_preimage(coset_type const cx,letter_type const x,coset_type const d)1069     void ToddCoxeter::remove_preimage(coset_type const  cx,
1070                                       letter_type const x,
1071                                       coset_type const  d) {
1072       LIBSEMIGROUPS_ASSERT(is_active_coset(cx));
1073       LIBSEMIGROUPS_ASSERT(is_valid_coset(d));
1074       coset_type e = _preim_init.get(cx, x);
1075       if (e == d) {
1076         _preim_init.set(cx, x, _preim_next.get(d, x));
1077       } else {
1078         while (_preim_next.get(e, x) != d) {
1079           e = _preim_next.get(e, x);
1080         }
1081         LIBSEMIGROUPS_ASSERT(_preim_next.get(e, x) == d);
1082         _preim_next.set(e, x, _preim_next.get(d, x));
1083       }
1084     }
1085 
1086     // Perform a DFS in _felsch_tree
make_deductions_dfs(coset_type const c)1087     void ToddCoxeter::make_deductions_dfs(coset_type const c) {
1088       for (auto it = _felsch_tree->cbegin(); it < _felsch_tree->cend(); ++it) {
1089         push_definition_felsch<StackDeductions, DoNotProcessCoincidences>(
1090             c, _relations[*it], _relations[*it + 1]);
1091       }
1092 
1093       size_t const n = nr_generators();
1094       for (size_t x = 0; x < n; ++x) {
1095         if (_felsch_tree->push_front(x)) {
1096           coset_type e = _preim_init.get(c, x);
1097           while (e != UNDEFINED) {
1098             make_deductions_dfs(e);
1099             e = _preim_next.get(e, x);
1100           }
1101           _felsch_tree->pop_front();
1102         }
1103       }
1104     }
1105 
process_deductions()1106     void ToddCoxeter::process_deductions() {
1107       LIBSEMIGROUPS_ASSERT(!_prefilled);
1108 #ifdef LIBSEMIGROUPS_VERBOSE
1109       if (!_deduct.empty()) {
1110         REPORT_VERBOSE_DEFAULT("processing %llu deductions . . .\n",
1111                                static_cast<uint64_t>(_deduct.size()));
1112       }
1113 #endif
1114       while (!_deduct.empty()) {
1115         auto d = _deduct.top();
1116         _deduct.pop();
1117         if (is_active_coset(d.first)) {
1118           _felsch_tree->push_back(d.second);
1119           make_deductions_dfs(d.first);
1120           process_coincidences<StackDeductions>();
1121         }
1122       }
1123       process_coincidences<StackDeductions>();
1124       if (!_deduct.empty()) {
1125         process_deductions();
1126       }
1127     }
1128 
1129     ////////////////////////////////////////////////////////////////////////
1130     // ToddCoxeter - member functions (main strategies) - private
1131     ////////////////////////////////////////////////////////////////////////
1132 
felsch()1133     void ToddCoxeter::felsch() {
1134       REPORT_DEFAULT("performing Felsch %s standardization...\n",
1135                      _settings->standardize ? "with" : "without");
1136       detail::Timer tmr;
1137       init();
1138       coset_type   t = 0;
1139       size_t const n = nr_generators();
1140       // Can only initialise _felsch_tree here because we require _relations
1141       // to be complete.
1142       init_felsch_tree();
1143       if (_state == state::initialized) {
1144         LIBSEMIGROUPS_ASSERT(_settings->strategy == policy::strategy::felsch);
1145         for (auto it = _extra.cbegin(); it < _extra.cend(); it += 2) {
1146           push_definition_hlt<StackDeductions, ProcessCoincidences>(
1147               _id_coset, *it, *(it + 1));
1148         }
1149         if (_settings->standardize) {
1150           for (letter_type x = 0; x < n; ++x) {
1151             standardize_immediate(_id_coset, t, x);
1152           }
1153         }
1154         if (!_prefilled) {
1155           if (_relations.empty()) {
1156             _felsch_tree->add_relations(_extra);
1157             _extra.swap(_relations);
1158           }
1159           process_deductions();
1160           // process_deductions doesn't work if the table is prefilled
1161         }
1162       } else if (_state == state::hlt) {
1163         _current = _id_coset;
1164       }
1165       _state = state::felsch;
1166       while (_current != first_free_coset() && !stopped()) {
1167         for (letter_type a = 0; a < n; ++a) {
1168           if (_table.get(_current, a) == UNDEFINED) {
1169             define<StackDeductions>(_current, a, new_coset());
1170             process_deductions();
1171 #ifdef LIBSEMIGROUPS_DEBUG
1172             if (_settings->enable_debug_verify_no_missing_deductions) {
1173               debug_verify_no_missing_deductions();
1174             }
1175 #endif
1176           }
1177           if (_settings->standardize) {
1178             standardize_immediate(_current, t, a);
1179           }
1180         }
1181         if (report()) {
1182           TODD_COXETER_REPORT_COSETS()
1183         }
1184         _current = next_active_coset(_current);
1185       }
1186       LIBSEMIGROUPS_ASSERT(_coinc.empty());
1187       LIBSEMIGROUPS_ASSERT(_deduct.empty());
1188       if (!stopped()) {
1189         LIBSEMIGROUPS_ASSERT(_current == first_free_coset());
1190         _state = state::finished;
1191       }
1192       TODD_COXETER_REPORT_COSETS()
1193       REPORT_TIME(tmr);
1194       report_why_we_stopped();
1195     }
1196 
1197     // Walker's Strategy 1 = HLT = ACE style-R
hlt()1198     void ToddCoxeter::hlt() {
1199       REPORT_DEFAULT("performing HLT %s standardization, %s lookahead, and%s"
1200                      "deduction processing...\n",
1201                      _settings->standardize ? "with" : "without",
1202                      _settings->lookahead == policy::lookahead::partial
1203                          ? "partial"
1204                          : "full",
1205                      _settings->save ? " " : " no ");
1206       detail::Timer tmr;
1207       init();
1208 
1209       coset_type t = 0;
1210       if (_state == state::initialized) {
1211         for (auto it = _extra.cbegin(); it < _extra.cend(); it += 2) {
1212           push_definition_hlt<DoNotStackDeductions, ProcessCoincidences>(
1213               _id_coset, *it, *(it + 1));
1214         }
1215         if (_settings->standardize) {
1216           size_t const n = nr_generators();
1217           for (letter_type x = 0; x < n; ++x) {
1218             standardize_immediate(_id_coset, t, x);
1219           }
1220         }
1221         if (!_prefilled) {
1222           if (_relations.empty()) {
1223             _extra.swap(_relations);
1224           }
1225         }
1226       } else if (_state == state::felsch) {
1227         _current = _id_coset;
1228       }
1229       _state = state::hlt;
1230 
1231       if (_settings->save) {
1232         init_felsch_tree();
1233       }
1234       // size_t const n = nr_generators();
1235       while (_current != first_free_coset() && !stopped()) {
1236         if (!_settings->save) {
1237           for (auto it = _relations.cbegin(); it < _relations.cend(); it += 2) {
1238             push_definition_hlt<DoNotStackDeductions, ProcessCoincidences>(
1239                 _current, *it, *(it + 1));
1240           }
1241           // Row filling
1242           // for (letter_type x = 0; x < n; ++x) {
1243           //   if (tau(_current, x) == UNDEFINED) {
1244           //     define<DoNotStackDeductions>(_current, x, new_coset());
1245           //   }
1246           // }
1247         } else {
1248           for (auto it = _relations.cbegin(); it < _relations.cend(); it += 2) {
1249             push_definition_hlt<StackDeductions, DoNotProcessCoincidences>(
1250                 _current, *it, *(it + 1));
1251             process_deductions();
1252           }
1253           // Row filling
1254           // for (letter_type x = 0; x < n; ++x) {
1255           //   if (tau(_current, x) == UNDEFINED) {
1256           //     define<StackDeductions>(_current, x, new_coset());
1257           //   }
1258           // }
1259         }
1260         if (nr_cosets_active() > _settings->next_lookahead) {
1261           perform_lookahead();
1262         }
1263         if (_settings->standardize) {
1264           size_t const n = nr_generators();
1265           for (letter_type x = 0; x < n; ++x) {
1266             standardize_immediate(_current, t, x);
1267           }
1268         }
1269         if (report()) {
1270           TODD_COXETER_REPORT_COSETS()
1271         }
1272         _current = next_active_coset(_current);
1273       }
1274       LIBSEMIGROUPS_ASSERT(_coinc.empty());
1275       LIBSEMIGROUPS_ASSERT(_deduct.empty());
1276       if (!stopped()) {
1277         LIBSEMIGROUPS_ASSERT(_current == first_free_coset());
1278         _state = state::finished;
1279       }
1280       TODD_COXETER_REPORT_COSETS();
1281       REPORT_TIME(tmr);
1282       report_why_we_stopped();
1283     }
1284 
1285     // This is not exactly Sim's TEN_CE, since all of the variants of
1286     // Todd-Coxeter represented in TEN_CE (that apply to semigroups/monoids)
1287     // are already accounted for in the above.
sims()1288     void ToddCoxeter::sims() {
1289       REPORT_DEFAULT("performing random Sims' TEN_CE strategy...\n");
1290       using int_dist_type
1291           = std::uniform_int_distribution<std::mt19937::result_type>;
1292       static int_dist_type dist(0, 9);
1293       static std::mt19937  mt;
1294 
1295       static constexpr std::array<bool, 8> full
1296           = {true, true, true, true, false, false, false, false};
1297       static constexpr std::array<bool, 10> stand
1298           = {true, true, false, false, true, true, false, false, true, false};
1299       static constexpr std::array<bool, 8> save_it
1300           = {true, false, true, false, true, false, true, false};
1301 
1302       static const std::string line = std::string(79, '#') + '\n';
1303 #ifdef LIBSEMIGROUPS_DEBUG
1304       // Don't check for missing deductions, because when changing from HLT to
1305       // Felsch and back repeatedly, there can be missing deductions after
1306       // deduction processing in Felsch (caused by not pushing all relations
1307       // through all cosets in HLT).
1308       _settings->enable_debug_verify_no_missing_deductions = false;
1309 #endif
1310       while (!finished()) {
1311         size_t m = dist(mt);
1312         if (m < 8) {
1313           strategy(policy::strategy::hlt);
1314           lookahead(
1315               (full[m] ? policy::lookahead::full : policy::lookahead::partial));
1316           try {
1317             save(save_it[m]);
1318           } catch (...) {
1319             // It isn't always possible to use the save option (when this is
1320             // created from a Cayley table, for instance), and
1321             // ToddCoxeter::save throws if this is the case.
1322           }
1323         } else {
1324           try {
1325             strategy(policy::strategy::felsch);
1326           } catch (...) {
1327             // It isn't always possible to use the Felsch strategy (when this
1328             // is created from a Cayley table, for instance), and
1329             // ToddCoxeter::save throws if this is the case.
1330           }
1331         }
1332         standardize(stand[m]);
1333 
1334         REPORT(line).prefix().flush();
1335         run_for(_settings->random_interval);
1336       }
1337       LIBSEMIGROUPS_ASSERT(_coinc.empty());
1338       LIBSEMIGROUPS_ASSERT(_deduct.empty());
1339       // The next 2 lines are necessary because if we are changing from HLT to
1340       // Felsch repeatedly, we can be in the situation where the table is
1341       // complete but not compatible. Test [ToddCoxeter][048] is a good example
1342       // of this.
1343       lookahead(policy::lookahead::full);
1344       perform_lookahead();
1345     }
1346 
1347     // TODO(later) we could use deduction processing here instead of this,
1348     // where appropriate?
perform_lookahead()1349     void ToddCoxeter::perform_lookahead() {
1350       state const old_state = _state;
1351       _state                = state::lookahead;
1352       if (_settings->lookahead == policy::lookahead::partial) {
1353         REPORT_DEFAULT("performing partial lookahead...\n");
1354         // Start lookahead from the coset after _current
1355         _current_la = next_active_coset(_current);
1356       } else {
1357         LIBSEMIGROUPS_ASSERT(_settings->lookahead == policy::lookahead::full);
1358         REPORT_DEFAULT("performing full lookahead...\n");
1359         // Start from the first coset
1360         _current_la = _id_coset;
1361       }
1362       TODD_COXETER_REPORT_COSETS()
1363 
1364       size_t nr_killed = nr_cosets_killed();
1365       while (_current_la != first_free_coset()
1366              // when running the random sims method the state is finished at
1367              // this point, and so stopped() == true, but we anyway want to
1368              // perform a full lookahead, which is why "_state ==
1369              // state::finished" is in the next line.
1370              && (old_state == state::finished || !stopped())) {
1371         for (auto it = _relations.cbegin(); it < _relations.cend(); it += 2) {
1372           push_definition_felsch<DoNotStackDeductions, ProcessCoincidences>(
1373               _current_la, *it, *(it + 1));
1374         }
1375         _current_la = next_active_coset(_current_la);
1376         if (report()) {
1377           TODD_COXETER_REPORT_COSETS()
1378         }
1379       }
1380       nr_killed = nr_cosets_killed() - nr_killed;
1381       if (nr_cosets_active() > _settings->next_lookahead
1382           || nr_killed < (nr_cosets_active() / 4)) {
1383         _settings->next_lookahead *= 2;
1384       }
1385       REPORT_DEFAULT("%2d cosets killed\n", nr_killed);
1386       _state = old_state;
1387     }
1388 
1389     ////////////////////////////////////////////////////////////////////////
1390     // ToddCoxeter - member functions (standardize) - private
1391     ////////////////////////////////////////////////////////////////////////
1392 
init_standardize()1393     void ToddCoxeter::init_standardize() {
1394       if (_tree == nullptr) {
1395         _tree = detail::make_unique<std::vector<TreeNode>>(nr_cosets_active(),
1396                                                            TreeNode());
1397       } else {
1398         _tree->resize(nr_cosets_active());
1399       }
1400       LIBSEMIGROUPS_ASSERT(_coinc.empty());
1401     }
1402 
1403     // Returns true if t is incremented (i.e. it's the first time we are
1404     // seeing and t as a coset in a standardization) and false otherwise.
standardize_immediate(coset_type const s,coset_type & t,letter_type const x)1405     bool ToddCoxeter::standardize_immediate(coset_type const  s,
1406                                             coset_type&       t,
1407                                             letter_type const x) {
1408       LIBSEMIGROUPS_ASSERT(!finished());
1409       coset_type const r = _table.get(s, x);
1410       if (r != UNDEFINED) {
1411         if (r > t) {
1412           t++;
1413           if (r > t) {
1414             swap(t, r);
1415           }
1416           return true;
1417         }
1418       }
1419       return false;
1420     }
1421 
standardize_deferred(std::vector<coset_type> & p,std::vector<coset_type> & q,coset_type const s,coset_type & t,letter_type const x)1422     bool ToddCoxeter::standardize_deferred(std::vector<coset_type>& p,
1423                                            std::vector<coset_type>& q,
1424                                            coset_type const         s,
1425                                            coset_type&              t,
1426                                            letter_type const        x) {
1427       // p : new -> old and q : old -> new
1428       coset_type r = _table.get(p[s], x);
1429       if (r != UNDEFINED) {
1430         r = q[r];  // new
1431         if (r > t) {
1432           t++;
1433           if (r > t) {
1434             TODD_COXETER_REPORT_SWITCH(r, t);
1435             std::swap(p[t], p[r]);
1436             std::swap(q[p[t]], q[p[r]]);
1437           }
1438           (*_tree)[t].parent = (s == t ? r : s);
1439           (*_tree)[t].gen    = x;
1440           return true;
1441         }
1442       }
1443       return false;
1444     }
1445 
lex_standardize()1446     void ToddCoxeter::lex_standardize() {
1447       REPORT_DEFAULT("standardizing (lex)... ");
1448       detail::Timer tmr;
1449 
1450       coset_type              s = 0;
1451       coset_type              t = 0;
1452       letter_type             x = 0;
1453       size_t const            n = nr_generators();
1454       std::vector<coset_type> p(coset_capacity(), 0);
1455       std::iota(p.begin(), p.end(), 0);
1456       std::vector<coset_type> q(coset_capacity(), 0);
1457       std::iota(q.begin(), q.end(), 0);
1458 
1459       // Perform a DFS through the _table
1460       while (s <= t) {
1461         if (standardize_deferred(p, q, s, t, x)) {
1462           s = t;
1463           x = 0;
1464           continue;
1465         }
1466         x++;
1467         if (x == n) {  // backtrack
1468           x = (*_tree)[s].gen;
1469           s = (*_tree)[s].parent;
1470         }
1471       }
1472       apply_permutation(p, q);
1473 
1474       REPORT("%s\n", tmr.string().c_str()).prefix().flush_right().flush();
1475 #ifdef LIBSEMIGROUPS_DEBUG
1476       debug_validate_forwd_bckwd();
1477       debug_validate_table();
1478       // debug_validate_preimages();
1479 #endif
1480     }
1481 
shortlex_standardize()1482     void ToddCoxeter::shortlex_standardize() {
1483       REPORT_DEFAULT("standardizing (shortlex)... ");
1484       detail::Timer           tmr;
1485       coset_type              t = 0;
1486       size_t const            n = nr_generators();
1487       std::vector<coset_type> p(coset_capacity(), 0);
1488       std::iota(p.begin(), p.end(), 0);
1489       std::vector<coset_type> q(coset_capacity(), 0);
1490       std::iota(q.begin(), q.end(), 0);
1491 
1492       for (coset_type s = 0; s <= t; ++s) {
1493         for (letter_type x = 0; x < n; ++x) {
1494           standardize_deferred(p, q, s, t, x);
1495         }
1496       }
1497       apply_permutation(p, q);
1498       REPORT("%s\n", tmr.string().c_str()).prefix().flush_right().flush();
1499 #ifdef LIBSEMIGROUPS_DEBUG
1500       debug_validate_forwd_bckwd();
1501       debug_validate_table();
1502       // debug_validate_preimages();
1503 #endif
1504     }
1505 
1506     // This is how the recursive words up to a given length M, and on an
1507     // arbitrary finite alphabet are generated.  On a single letter alphabet,
1508     // this order is just increasing powers of the only generator:
1509     //
1510     //   a < aa < aaa < aaaa < ... < aa...a (M times)
1511     //
1512     // With an n-letter alphabet A = {a_1, a_2, ..., a_n}, suppose we have
1513     // already obtained all of the words W_{n - 1} containing {a_1, ..., a_{n -
1514     // 1}}.  Every word in W_{n - 1} is less than any word containing a_n, and
1515     // the least word greater than every word in W_{n - 1} is a_n. Words greater
1516     // than a_n are obtain in the follow way, where:
1517     //
1518     // x: is the maximum word in W_{n - 1}, this is constant, in the description
1519     //    that follows.
1520     // u: the first word obtained in point (1), the first time it is applied
1521     //    after (2) has been applied, starting with u = a_{n - 1}.
1522     // v: a word with one fewer letters than u, starting with the empty word.
1523     // w: a word such that w < u, also starting with the empty word.
1524     //
1525     // 1. If v < x, then v is replaced by the next word in the order. If |uv| <=
1526     //    M, then the next word is uv. Otherwise, goto 1.
1527     //
1528     // 2. If v = x, then and there exists a word w' in the set of words obtained
1529     //    so far such that w' > w and |w'| <= M - 1, then replace w with w',
1530     //    replace u by wa_n, replace v by the empty word, and the next word is
1531     //    wa_n.
1532     //
1533     //    If no such word w' exists, then we have enumerated all the required
1534     //    words, and we can stop.
1535     //
1536     // For example, if A = {a, b} and M = 4, then the initial elements in the
1537     // order are:
1538     //
1539     //   e < a < aa < aaa < aaaa (e is the empty word)
1540     //
1541     // Set b > aaaa. At this point, x = aaaa, u = b, v = e, w = e, and so
1542     // (1) applies, v <- a, and since |uv| = ba <= 4 = M, the next word is
1543     // ba.  Repeatedly applying (1), until it fails to hold, we obtain the
1544     // following:
1545     //
1546     //   aaaa < b < ba < baa < baaa
1547     //
1548     // After defining baa < baaa, x = aaaa, u = b, v = aaaa, and w = e. Hence v
1549     // = x, and so (2) applies. The next w' in the set of words so far
1550     // enumerated is a, and |a| = 1 <= 3 = M - 1, and so w <- a, u <- ab, v <-
1551     // e, and the next word is ab. We repeatedly apply (1), until it fails, to
1552     // obtain
1553     //
1554     //   baaa < ab < aba < abaa
1555     //
1556     // At which point u = b, v = aaaa = x, and w = a. Hence (2) applies, w <-
1557     // aa, v <- e, u <- aab, and the next word is: aab. And so on ...
1558 
1559     // TODO(later): improve this, it is currently very slow.
recursive_standardize()1560     void ToddCoxeter::recursive_standardize() {
1561       REPORT_DEFAULT("standardizing (recursive)... ");
1562       detail::Timer          tmr;
1563       std::vector<word_type> out;
1564       size_t const           n = nr_generators();
1565       letter_type            a = 0;
1566       coset_type             s = 0;
1567       coset_type             t = 0;
1568 
1569       std::vector<coset_type> p(coset_capacity(), 0);
1570       std::iota(p.begin(), p.end(), 0);
1571       std::vector<coset_type> q(coset_capacity(), 0);
1572       std::iota(q.begin(), q.end(), 0);
1573 
1574       while (s <= t) {
1575         if (standardize_deferred(p, q, s, t, 0)) {
1576           out.push_back(word_type(t, a));
1577         }
1578         s++;
1579       }
1580       a++;
1581       bool new_generator = true;
1582       int  x, u, w;
1583       while (a < n && t < nr_cosets_active() - 1) {
1584         if (new_generator) {
1585           w = -1;  // -1 is the empty word
1586           if (standardize_deferred(p, q, 0, t, a)) {
1587             out.push_back({a});
1588           }
1589           x             = out.size() - 1;
1590           u             = out.size() - 1;
1591           new_generator = false;
1592         }
1593 
1594         coset_type const uu = tau(0, out[u].begin(), out[u].end());
1595         if (uu != UNDEFINED) {
1596           for (int v = 0; v < x; v++) {
1597             coset_type const uuv = tau(uu, out[v].begin(), out[v].end() - 1);
1598             if (uuv != UNDEFINED) {
1599               s = q[uuv];
1600               if (standardize_deferred(p, q, s, t, out[v].back())) {
1601                 word_type nxt = out[u];
1602                 nxt.insert(nxt.end(), out[v].begin(), out[v].end());
1603                 out.push_back(std::move(nxt));
1604               }
1605             }
1606           }
1607         }
1608         w++;
1609         if (static_cast<size_t>(w) < out.size()) {
1610           coset_type const ww = tau(0, out[w].begin(), out[w].end());
1611           if (ww != UNDEFINED) {
1612             s = q[ww];
1613             if (standardize_deferred(p, q, s, t, a)) {
1614               u             = out.size();
1615               word_type nxt = out[w];
1616               nxt.push_back(a);
1617               out.push_back(std::move(nxt));
1618             }
1619           }
1620         } else {
1621           a++;
1622           new_generator = true;
1623         }
1624       }
1625       apply_permutation(p, q);
1626       REPORT("%s\n", tmr.string().c_str()).prefix().flush_right().flush();
1627 #ifdef LIBSEMIGROUPS_DEBUG
1628       debug_validate_forwd_bckwd();
1629       debug_validate_table();
1630       debug_validate_preimages();
1631 #endif
1632     }
1633 
1634     // The permutation q must map the active cosets to the [0, ..
1635     // , nr_cosets_active())
apply_permutation(std::vector<coset_type> & p,std::vector<coset_type> & q)1636     void ToddCoxeter::apply_permutation(std::vector<coset_type>& p,
1637                                         std::vector<coset_type>& q) {
1638       // p : new -> old, q = p ^ -1
1639 #ifdef LIBSEMIGROUPS_DEBUG
1640       for (size_t c = 0; c < q.size(); ++c) {
1641         LIBSEMIGROUPS_ASSERT(
1642             (is_active_coset(c) && q[c] < nr_cosets_active())
1643             || (!is_active_coset(c) && q[c] >= nr_cosets_active()));
1644         LIBSEMIGROUPS_ASSERT(p[q[c]] == c);
1645         LIBSEMIGROUPS_ASSERT(q[p[c]] == c);
1646       }
1647 #endif
1648       {
1649         coset_type   c = _id_coset;
1650         size_t const n = nr_generators();
1651         // Permute all the values in the _table, and pre-images, that relate
1652         // to active cosets
1653         while (c < nr_cosets_active()) {
1654           for (letter_type x = 0; x < n; ++x) {
1655             coset_type i = _table.get(p[c], x);
1656             _table.set(p[c], x, (i == UNDEFINED ? i : q[i]));
1657             i = _preim_init.get(p[c], x);
1658             _preim_init.set(p[c], x, (i == UNDEFINED ? i : q[i]));
1659             i = _preim_next.get(p[c], x);
1660             _preim_next.set(p[c], x, (i == UNDEFINED ? i : q[i]));
1661           }
1662           c++;
1663         }
1664         // Permute the rows themselves
1665         _table.apply_row_permutation(p);
1666         _preim_init.apply_row_permutation(p);
1667         _preim_next.apply_row_permutation(p);
1668       }
1669       {
1670         // Permute the cosets in the CosetManager using p . . .
1671         size_t const n = p.size();
1672         for (coset_type i = 0; i < n; ++i) {
1673           coset_type current = i;
1674           while (i != p[current]) {
1675             size_t next = p[current];
1676             switch_cosets(current, next);
1677             p[current] = current;
1678             current    = next;
1679           }
1680           p[current] = current;
1681         }
1682       }
1683     }
1684 
1685     // Based on the procedure SWITCH in Sims' book, p193
1686     // Swaps an active coset and another coset in the table.
swap(coset_type const c,coset_type const d)1687     void ToddCoxeter::swap(coset_type const c, coset_type const d) {
1688       TODD_COXETER_REPORT_SWITCH(c, d)
1689 
1690       LIBSEMIGROUPS_ASSERT(_coinc.empty());
1691       LIBSEMIGROUPS_ASSERT(c != _id_coset);
1692       LIBSEMIGROUPS_ASSERT(d != _id_coset);
1693       LIBSEMIGROUPS_ASSERT(c != d);
1694       LIBSEMIGROUPS_ASSERT(is_valid_coset(c));
1695       LIBSEMIGROUPS_ASSERT(is_valid_coset(d));
1696       LIBSEMIGROUPS_ASSERT(is_active_coset(c) || is_active_coset(d));
1697 
1698       size_t const n = nr_generators();
1699       for (letter_type x = 0; x < n; ++x) {
1700         coset_type cx = _table.get(c, x);
1701         coset_type dx = _table.get(d, x);
1702 
1703         if (is_active_coset(c)) {
1704           // Replace c <-- d in the coset table _table
1705           coset_type e = _preim_init.get(c, x);
1706           while (e != UNDEFINED) {
1707             LIBSEMIGROUPS_ASSERT(_table.get(e, x) == c);
1708             _table.set(e, x, d);
1709             e = _preim_next.get(e, x);
1710           }
1711           _table.set(c, x, ff(c, d, cx));
1712         }
1713         if (is_active_coset(d)) {
1714           // Replace d <-- c in the coset table _table
1715           coset_type e = _preim_init.get(d, x);
1716           while (e != UNDEFINED) {
1717             _table.set(e, x, c);
1718             e = _preim_next.get(e, x);
1719           }
1720           _table.set(d, x, ff(c, d, dx));
1721         }
1722         if (is_active_coset(c) && is_active_coset(d) && cx == dx
1723             && cx != UNDEFINED) {
1724           // Swap c <--> d in preimages of cx = dx
1725           size_t     found = 0;
1726           coset_type e     = _preim_init.get(cx, x);
1727           if (e == c) {
1728             _preim_init.set(cx, x, d);
1729             found++;
1730           } else if (e == d) {
1731             _preim_init.set(cx, x, c);
1732             found++;
1733           }
1734           while (e != UNDEFINED && found < 2) {
1735             LIBSEMIGROUPS_ASSERT(ff(c, d, _table.get(e, x)) == cx);
1736             coset_type f = _preim_next.get(e, x);
1737             if (f == c) {
1738               _preim_next.set(e, x, d);
1739               found++;
1740             } else if (f == d) {
1741               _preim_next.set(e, x, c);
1742               found++;
1743             }
1744             e = f;
1745           }
1746         } else {
1747           if (is_active_coset(c) && cx != UNDEFINED) {
1748             // Replace c <-- d in preimages of cx, and d is not a preimage of
1749             // cx under x
1750             coset_type e = _preim_init.get(cx, x);
1751             if (e == c) {
1752               _preim_init.set(cx, x, d);
1753               e = UNDEFINED;  // To prevent going into the next loop
1754             }
1755             while (e != UNDEFINED) {
1756               LIBSEMIGROUPS_ASSERT(ff(c, d, _table.get(e, x)) == cx);
1757               coset_type f = _preim_next.get(e, x);
1758               if (f == c) {
1759                 _preim_next.set(e, x, d);
1760                 break;
1761               }
1762               e = f;
1763             }
1764           }
1765           if (is_active_coset(d) && dx != UNDEFINED) {
1766             // Replace d <-- c in preimages of dx, and c is not a preimage of
1767             // dx under x
1768             coset_type e = _preim_init.get(dx, x);
1769             if (e == d) {
1770               _preim_init.set(dx, x, c);
1771               e = UNDEFINED;  // To prevent going into the next loop
1772             }
1773             while (e != UNDEFINED) {
1774               LIBSEMIGROUPS_ASSERT(ff(c, d, _table.get(e, x)) == dx);
1775               coset_type f = _preim_next.get(e, x);
1776               if (f == d) {
1777                 _preim_next.set(e, x, c);
1778                 break;
1779               }
1780               e = f;
1781             }
1782           }
1783         }
1784         _table.swap(c, x, d, x);
1785         _preim_init.swap(c, x, d, x);
1786         _preim_next.swap(c, x, d, x);
1787       }
1788       switch_cosets(c, d);
1789     }
1790 
1791     ////////////////////////////////////////////////////////////////////////
1792     // ToddCoxeter - member functions (debug) - private
1793     ////////////////////////////////////////////////////////////////////////
1794 
1795 #ifdef LIBSEMIGROUPS_DEBUG
1796     // Validates the coset table.
debug_validate_table() const1797     void ToddCoxeter::debug_validate_table() const {
1798       REPORT_DEBUG_DEFAULT("validating the coset table... ");
1799       size_t const n = nr_generators();
1800       coset_type   c = _id_coset;
1801       while (c != first_free_coset()) {
1802         if (!is_active_coset(c)) {
1803           LIBSEMIGROUPS_EXCEPTION(
1804               "invalid table, coset %d is both free and active!", c);
1805         }
1806         for (letter_type x = 0; x < n; ++x) {
1807           if (_table.get(c, x) != UNDEFINED
1808               && !is_active_coset(_table.get(c, x))) {
1809             LIBSEMIGROUPS_EXCEPTION("invalid table, _table.get(%d, %d) = %d"
1810                                     " is not an active coset or UNDEFINED!",
1811                                     c,
1812                                     x,
1813                                     _table.get(c, x));
1814           }
1815         }
1816         c = next_active_coset(c);
1817       }
1818       TODD_COXETER_REPORT_OK();
1819     }
1820 
1821     // Validates the preimages, this is very expensive.
debug_validate_preimages() const1822     void ToddCoxeter::debug_validate_preimages() const {
1823       REPORT_DEBUG_DEFAULT("validating preimages... ");
1824       size_t const n = nr_generators();
1825       coset_type   c = _id_coset;
1826       while (c != first_free_coset()) {
1827         for (letter_type x = 0; x < n; ++x) {
1828           coset_type           e = _preim_init.get(c, x);
1829           std::set<coset_type> stored_preimages;
1830           while (e != UNDEFINED) {
1831             if (!is_active_coset(e)) {
1832               LIBSEMIGROUPS_EXCEPTION("invalid preimage e = %d of c = %d, e = "
1833                                       "%d is not an active coset or UNDEFINED!",
1834                                       e,
1835                                       c,
1836                                       e);
1837             }
1838             if (!stored_preimages.insert(e).second) {
1839               LIBSEMIGROUPS_EXCEPTION(
1840                   "duplicate preimage e = %d of c = %d under x = %d!", e, c, x);
1841             }
1842             if (_table.get(e, x) != c) {
1843               LIBSEMIGROUPS_EXCEPTION(
1844                   "invalid preimage, _table.get(%d, %d) != %d but found e = %d "
1845                   "in the preimages of %d under x = %d",
1846                   e,
1847                   x,
1848                   c,
1849                   e,
1850                   c,
1851                   x);
1852             }
1853             e = _preim_next.get(e, x);
1854           }
1855           // Check there are no missing preimages!
1856           coset_type d = 0;
1857           while (d != first_free_coset()) {
1858             if (d != c && _table.get(d, x) == c
1859                 && stored_preimages.insert(d).second) {
1860               LIBSEMIGROUPS_EXCEPTION(
1861                   "missing preimage, _table.get(%d, %d) == %d but d = %d "
1862                   "wasn't found in the stored preimages",
1863                   d,
1864                   x,
1865                   c,
1866                   d);
1867             }
1868             d = next_active_coset(d);
1869           }
1870         }
1871         c = next_active_coset(c);
1872       }
1873       TODD_COXETER_REPORT_OK();
1874     }
1875 
debug_verify_no_missing_deductions() const1876     void ToddCoxeter::debug_verify_no_missing_deductions() const {
1877       REPORT_DEBUG_DEFAULT("verifying no missing deductions or "
1878                            "coincidences...");
1879       if (!_deduct.empty()) {
1880         LIBSEMIGROUPS_EXCEPTION("the deduction stack is not empty");
1881       }
1882       if (!_coinc.empty()) {
1883         LIBSEMIGROUPS_EXCEPTION("the coincidence stack is not empty");
1884       }
1885       for (coset_type c = _id_coset; c != first_free_coset();
1886            c            = next_active_coset(c)) {
1887         for (auto it = _relations.cbegin(); it < _relations.cend(); it += 2) {
1888           word_type const& u = *it;
1889           word_type const& v = *(it + 1);
1890           LIBSEMIGROUPS_ASSERT(is_valid_coset(c));
1891           LIBSEMIGROUPS_ASSERT(!u.empty());
1892           LIBSEMIGROUPS_ASSERT(!v.empty());
1893           coset_type x = tau(c, u.cbegin(), u.cend() - 1);
1894           if (x == UNDEFINED) {
1895             goto end;
1896           }
1897           LIBSEMIGROUPS_ASSERT(is_valid_coset(x));
1898           coset_type y = tau(c, v.cbegin(), v.cend() - 1);
1899           if (y == UNDEFINED) {
1900             goto end;
1901           }
1902           LIBSEMIGROUPS_ASSERT(is_valid_coset(y));
1903           letter_type a  = u.back();
1904           letter_type b  = v.back();
1905           coset_type  xx = _table.get(x, a);
1906           coset_type  yy = _table.get(y, b);
1907           if (xx == UNDEFINED && yy != UNDEFINED) {
1908             LIBSEMIGROUPS_EXCEPTION("missing deduction tau(%d, %d) = %d when "
1909                                     "pushing coset %d through %s = %s",
1910                                     x,
1911                                     a,
1912                                     yy,
1913                                     c,
1914                                     detail::to_string(u).c_str(),
1915                                     detail::to_string(v).c_str());
1916           } else if (xx != UNDEFINED && yy == UNDEFINED) {
1917             // tau(y, b) <- xx
1918             LIBSEMIGROUPS_EXCEPTION("missing deduction tau(%d, %d) = %d when "
1919                                     "pushing coset %d through %s = %s",
1920                                     y,
1921                                     b,
1922                                     xx,
1923                                     c,
1924                                     detail::to_string(u).c_str(),
1925                                     detail::to_string(v).c_str());
1926           } else if (xx != UNDEFINED && yy != UNDEFINED) {
1927             // tau(x, a) and tau(y, b) are defined
1928             if (xx != yy) {
1929               LIBSEMIGROUPS_EXCEPTION("missing coincidence %d = %d when "
1930                                       "pushing coset %d through %s = %s",
1931                                       xx,
1932                                       yy,
1933                                       c,
1934                                       detail::to_string(u).c_str(),
1935                                       detail::to_string(v).c_str());
1936             }
1937           }
1938         }
1939       }
1940     end:
1941       TODD_COXETER_REPORT_OK();
1942     }
1943 
1944 #endif
1945   }  // namespace congruence
1946 }  // namespace libsemigroups
1947