1 //
2 // libsemigroups - C++ library for semigroups and monoids
3 // Copyright (C) 2019-20 Finn Smith
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 // This file contains a generic implementation of Konieczny's algorithm,
19 // originally for computing subsemigroups of the boolean matrix monoid.
20 
21 // TODO(later):
22 // 1) exception safety!
23 // 3) expose iterators to relevant things in D-classes, in particular elements
24 
25 #ifndef LIBSEMIGROUPS_KONIECZNY_HPP_
26 #define LIBSEMIGROUPS_KONIECZNY_HPP_
27 
28 #include <algorithm>      // for binary_search
29 #include <cstddef>        // for size_t
30 #include <set>            // for set
31 #include <type_traits>    // for is_pointer
32 #include <unordered_map>  // for unordered_map
33 #include <unordered_set>  // for unordered_set
34 #include <utility>        // for pair, make_pair
35 #include <vector>         // for vector
36 
37 #include "action.hpp"                   // for LeftAction, RightAction
38 #include "adapters.hpp"                 // for Lambda, etc
39 #include "bruidhinn-traits.hpp"         // for BruidhinnTraits
40 #include "constants.hpp"                // for UNDEFINED
41 #include "element-adapters.hpp"         // for Rank
42 #include "libsemigroups-debug.hpp"      // for LIBSEMIGROUPS_ASSERT
43 #include "libsemigroups-exception.hpp"  // for LIBSEMIGROUPS_EXCEPTION
44 #include "pool.hpp"                     // for detail::Pool
45 #include "report.hpp"                   // for REPORT_DEFAULT
46 #include "runner.hpp"                   // for Runner
47 #include "timer.hpp"                    // for Timer
48 
49 namespace libsemigroups {
50   //! Defined in ``konieczny.hpp``.
51   //!
52   //! This is a traits class for use with Konieczny.
53   //!
54   //! \sa Konieczny
55   template <typename TElementType>
56   struct KoniecznyTraits {
57     //! The type of the elements of a Konieczny instance with const removed.
58     using element_type =
59         typename detail::BruidhinnTraits<TElementType>::value_type;
60 
61     //! The type of const elements of a Konieczny instance.
62     using const_element_type =
63         typename detail::BruidhinnTraits<TElementType>::const_value_type;
64 
65     //! \copydoc LambdaValue
66     using lambda_value_type =
67         typename ::libsemigroups::LambdaValue<element_type>::type;
68 
69     //! \copydoc RhoValue
70     using rho_value_type =
71         typename ::libsemigroups::RhoValue<element_type>::type;
72 
73     //! \copydoc RankState
74     using rank_state_type = typename ::libsemigroups::RankState<element_type>;
75 
76     //! The orbit of the lambda values under LambdaAction
77     //! \sa LambdaAction and RightAction
78     using lambda_orb_type
79         = RightAction<element_type,
80                       lambda_value_type,
81                       ImageRightAction<element_type, lambda_value_type>>;
82 
83     //! The orbit of the rho values under RhoAction
84     //! \sa RhoAction and LeftAction
85     using rho_orb_type
86         = LeftAction<element_type,
87                      rho_value_type,
88                      ImageLeftAction<element_type, rho_value_type>>;
89 
90     //! \copydoc libsemigroups::Lambda
91     using Lambda = ::libsemigroups::Lambda<element_type, lambda_value_type>;
92 
93     //! \copydoc libsemigroups::Rho
94     using Rho = ::libsemigroups::Rho<element_type, rho_value_type>;
95 
96     //! \copydoc libsemigroups::Product
97     using Product = ::libsemigroups::Product<element_type>;
98 
99     //! \copydoc libsemigroups::Rank
100     using Rank = ::libsemigroups::Rank<element_type, rank_state_type>;
101 
102     //! \copydoc libsemigroups::One
103     using One = ::libsemigroups::One<element_type>;
104 
105     //! \copydoc libsemigroups::Hash
106     using ElementHash = ::libsemigroups::Hash<element_type>;
107 
108     //! \copydoc libsemigroups::EqualTo
109     using EqualTo = ::libsemigroups::EqualTo<element_type>;
110 
111     //! \copydoc libsemigroups::Swap
112     using Swap = ::libsemigroups::Swap<element_type>;
113 
114     //! \copydoc libsemigroups::Less
115     using Less = ::libsemigroups::Less<element_type>;
116 
117     //! \copydoc libsemigroups::Degree
118     using Degree = ::libsemigroups::Degree<element_type>;
119   };
120 
121   //! Defined in ``konieczny.hpp``.
122   //!
123   //! The class template Konieczny implements %Konieczny's algorithm as
124   //! described in the article 'Green's equivalences in finite semigroups of
125   //! binary relations' by Janusz %Konieczny; see [here] for more details.
126   //! This algorithm is similar to that of Lallement and McFadden; see [this]
127   //! paper for more details. It differs in being applicable to subsemigroups of
128   //! a non-regular semigroup, though is essentially the same algorithm for
129   //! elements which happen to be regular.
130   //!
131   //! A Konieczny instance is defined by a generating set, and the main
132   //! member function is Konieczny::run, which implements
133   //! %Konieczny's Algorithm. If Konieczny::run is invoked and
134   //! Konieczny::finished returns \c true, then the size, partial order of
135   //! \f$\mathscr{D}\f$-classes, and complete frames for each
136   //! \f$\mathscr{D}\f$-class are known.
137   //!
138   //! \tparam TElementType the type of the elements of the semigroup.
139   //!
140   //! \tparam TTraits the type of a traits class with the requirements of
141   //! libsemigroups::KoniecznyTraits.
142   //!
143   //! \sa KoniecznyTraits and DClass
144   //!
145   //! [here]:  https://link.springer.com/article/10.1007/BF02573672
146   //! [this]:
147   //! https://www.sciencedirect.com/science/article/pii/S0747717108800570
148   template <typename TElementType,
149             typename TTraits = KoniecznyTraits<TElementType>>
150   class Konieczny final : public Runner,
151                           private detail::BruidhinnTraits<TElementType> {
152     // pointers are not currently supported
153     static_assert(!std::is_pointer<TElementType>::value,
154                   "Pointer types are not currently supported by Konieczny");
155     ////////////////////////////////////////////////////////////////////////
156     // Konieczny - aliases - private
157     ////////////////////////////////////////////////////////////////////////
158 
159     using internal_element_type =
160         typename detail::BruidhinnTraits<TElementType>::internal_value_type;
161     using internal_const_element_type = typename detail::BruidhinnTraits<
162         TElementType>::internal_const_value_type;
163     using internal_const_reference = typename detail::BruidhinnTraits<
164         TElementType>::internal_const_reference;
165     using internal_reference =
166         typename detail::BruidhinnTraits<TElementType>::internal_reference;
167 
168     using PairHash = Hash<std::pair<size_t, size_t>>;
169 
170    public:
171     ////////////////////////////////////////////////////////////////////////
172     // Konieczny - aliases - public
173     ////////////////////////////////////////////////////////////////////////
174 
175     //! The type of the elements of the semigroup represented by \c this.
176     using element_type = typename TTraits::element_type;
177 
178     //! The type of the elements of the semigroup represented by \c this, with
179     //! const added.
180     using const_element_type = typename TTraits::const_element_type;
181 
182     //! The type of a const reference to an element of the semigroup represented
183     //! by \c this.
184     using const_reference =
185         typename detail::BruidhinnTraits<TElementType>::const_reference;
186 
187     //! The type of indices of \f$\mathscr{D}\f$-classes in the semigroup
188     //! represented by \c this. \sa cbegin_D_classes and
189     //! cbegin_regular_D_classes
190     using D_class_index_type = size_t;
191 
192     //! The type of the lambda values that the semigroup represented by \c this
193     //! acts on.
194     using lambda_value_type = typename TTraits::lambda_value_type;
195 
196     //! The type of the orbit of the lambda values under the action of the
197     //! semigroup represented by \c this.
198     using lambda_orb_type = typename TTraits::lambda_orb_type;
199 
200     //! The type of the rho values that the semigroup represented by \c this
201     //! acts on.
202     using rho_value_type = typename TTraits::rho_value_type;
203 
204     //! The type of the orbit of the rho values under the action of the
205     //! semigroup represented by \c this.
206     using rho_orb_type = typename TTraits::rho_orb_type;
207 
208     //! \copydoc libsemigroups::Degree
209     using Degree = typename TTraits::Degree;
210 
211     //! \copydoc libsemigroups::EqualTo
212     using EqualTo = typename TTraits::EqualTo;
213 
214     //! \copydoc libsemigroups::Lambda
215     using Lambda = typename TTraits::Lambda;
216 
217     //! \copydoc libsemigroups::Less
218     using Less = typename TTraits::Less;
219 
220     //! \copydoc libsemigroups::One
221     using One = typename TTraits::One;
222 
223     //! \copydoc libsemigroups::Product
224     using Product = typename TTraits::Product;
225 
226     //! \copydoc libsemigroups::Rank
227     using Rank = typename TTraits::Rank;
228 
229     //! \copydoc libsemigroups::Rho
230     using Rho = typename TTraits::Rho;
231 
232     //! \copydoc libsemigroups::Swap
233     using Swap = typename TTraits::Swap;
234 
235    private:
236     ////////////////////////////////////////////////////////////////////////
237     // Konieczny - aliases - private
238     ////////////////////////////////////////////////////////////////////////
239     using lambda_orb_index_type     = typename lambda_orb_type::index_type;
240     using lambda_orb_scc_index_type = typename lambda_orb_type::scc_index_type;
241     using rho_orb_index_type        = typename rho_orb_type::index_type;
242     using rho_orb_scc_index_type    = typename rho_orb_type::scc_index_type;
243     using rank_type                 = size_t;
244     using rank_state_type           = typename TTraits::rank_state_type;
245     using left_indices_index_type   = size_t;
246     using right_indices_index_type  = size_t;
247 
248     ////////////////////////////////////////////////////////////////////////
249     // Konieczny - internal structs - private
250     ////////////////////////////////////////////////////////////////////////
251 
252     struct InternalHash : private detail::BruidhinnTraits<TElementType> {
operator ()libsemigroups::Konieczny::InternalHash253       size_t operator()(internal_const_element_type x) const {
254         return Hash<TElementType>()(this->to_external_const(x));
255       }
256     };
257 
258     struct InternalEqualTo : private detail::BruidhinnTraits<TElementType> {
operator ()libsemigroups::Konieczny::InternalEqualTo259       bool operator()(internal_const_reference x,
260                       internal_const_reference y) const {
261         return EqualTo()(this->to_external_const(x),
262                          this->to_external_const(y));
263       }
264     };
265 
266     struct InternalLess : private detail::BruidhinnTraits<TElementType> {
operator ()libsemigroups::Konieczny::InternalLess267       bool operator()(internal_const_reference x,
268                       internal_const_reference y) const {
269         return Less()(this->to_external_const(x), this->to_external_const(y));
270       }
271     };
272 
273     struct InternalVecEqualTo : private detail::BruidhinnTraits<TElementType> {
operator ()libsemigroups::Konieczny::InternalVecEqualTo274       size_t operator()(std::vector<internal_element_type> const& x,
275                         std::vector<internal_element_type> const& y) const {
276         LIBSEMIGROUPS_ASSERT(x.size() == y.size());
277         return std::equal(x.cbegin(), x.cend(), y.cbegin(), InternalEqualTo());
278       }
279     };
280 
281     struct InternalVecFree : private detail::BruidhinnTraits<TElementType> {
operator ()libsemigroups::Konieczny::InternalVecFree282       void operator()(std::vector<internal_element_type> const& x) {
283         for (auto it = x.cbegin(); it != x.cend(); ++it) {
284           this->internal_free(*it);
285         }
286       }
287     };
288 
289     struct OneParamLambda {
operator ()libsemigroups::Konieczny::OneParamLambda290       lambda_value_type operator()(const_reference x) const {
291         lambda_value_type lval;
292         Lambda()(lval, x);
293         return lval;
294       }
295     };
296 
297     struct OneParamRho {
operator ()libsemigroups::Konieczny::OneParamRho298       rho_value_type operator()(const_reference x) const {
299         rho_value_type rval;
300         Rho()(rval, x);
301         return rval;
302       }
303     };
304 
305     struct InternalRank {
306       template <typename SFINAE = size_t>
operator ()libsemigroups::Konieczny::InternalRank307       auto operator()(void*, const_reference x) -> typename std::enable_if<
308           std::is_void<typename rank_state_type::type>::value,
309           SFINAE>::type {
310         return Rank()(x);
311       }
312 
313       template <typename SFINAE = size_t>
operator ()libsemigroups::Konieczny::InternalRank314       auto operator()(rank_state_type* state, const_reference x) ->
315           typename std::enable_if<
316               !std::is_void<typename rank_state_type::type>::value,
317               SFINAE>::type {
318         return Rank()(*state, x);
319       }
320     };
321 
322    public:
323     ////////////////////////////////////////////////////////////////////////
324     // Konieczny - constructor and destructor - public
325     ////////////////////////////////////////////////////////////////////////
326 
327     //! 0-parameter constructor.
328     //!
329     //! This is the standard constructor for a Konieczny instance with
330     //! unspecified generators.
331     //!
332     //! \exceptions
333     //! \no_libsemigroups_except
334     //!
335     //! \sa add_generator and add_generators
Konieczny()336     Konieczny()
337         : _adjoined_identity_contained(false),
338           _D_classes(),
339           _D_rels(),
340           _data_initialised(false),
341           _degree(UNDEFINED),
342           _element_pool(),
343           _gens(),
344           _group_indices(),
345           _group_indices_rev(),
346           _lambda_orb(),
347           _lambda_to_D_map(),
348           _nonregular_reps(),
349           _one(),
350           _rank_state(nullptr),
351           _ranks(),
352           _regular_D_classes(),
353           _reg_reps(),
354           _reps_processed(0),
355           _rho_orb(),
356           _rho_to_D_map(),
357           _run_initialised(false),
358           _tmp_lambda_value1(),
359           _tmp_lambda_value2(),
360           _tmp_rho_value1(),
361           _tmp_rho_value2() {}
362 
363     //! Deleted.
364     //!
365     //! Konieczny does not support a copy constructor.
366     Konieczny(Konieczny const&) = delete;
367 
368     //! Deleted.
369     //!
370     //! Konieczny does not support a move constructor.
371     Konieczny(Konieczny&&) = delete;
372 
373     //! Deleted.
374     //!
375     //! Konieczny does not support a copy assignment operator.
376     Konieczny& operator=(Konieczny const&) = delete;
377 
378     //! Deleted.
379     //!
380     //! Konieczny does not support a move assignment operator.
381     Konieczny& operator=(Konieczny&&) = delete;
382 
383     //! Construct from generators.
384     //!
385     //! This is the standard constructor for a Konieczny instance generated by a
386     //! vector of generators. There can be duplicate generators and although
387     //! they do not count as distinct elements, they do count as distinct
388     //! generators. In other words, the generators of the semigroup are
389     //! precisely (a copy of) \p gens in the same order they occur in \p gens.
390     //!
391     //! The generators \p gens are copied by the constructor, and so it is the
392     //! responsibility of the caller to delete \p gens.
393     //!
394     //! \param gens the generators of the semigroup represented by \c this.
395     //!
396     //! \throws LibsemigroupsException if \p gens is empty, or
397     //! Konieczny::Degree()(x) != Konieczny::Degree()(y) for \c x, \c y in
398     //! \p gens.
Konieczny(std::vector<element_type> const & gens)399     explicit Konieczny(std::vector<element_type> const& gens) : Konieczny() {
400       if (gens.empty()) {
401         LIBSEMIGROUPS_EXCEPTION(
402             "expected a positive number of generators, but got 0");
403       }
404       add_generators(gens.cbegin(), gens.cend());
405       init_data();
406     }
407 
408     ~Konieczny();
409 
410     ////////////////////////////////////////////////////////////////////////
411     // Konieczny - forward class declarations - public/private
412     ////////////////////////////////////////////////////////////////////////
413 
414     class DClass;
415 
416    private:
417     class RegularDClass;
418     class NonRegularDClass;
419 
420    public:
421     ////////////////////////////////////////////////////////////////////////
422     // Konieczny - member functions - public
423     ////////////////////////////////////////////////////////////////////////
424 
425     //! Returns the number of generators of \c this.
426     //!
427     //! This member function returns the number of generators given to \c this.
428     //! Note that there may be duplicate generators, and so \c this may have
429     //! more generators than unique generators.
430     //!
431     //! \noexcept
432     //!
433     //! \sa add_generator and add_generators
number_of_generators() const434     size_t number_of_generators() const noexcept {
435       return _gens.size() - 1;
436     }
437 
438     //! Returns the number of \f$\mathscr{D}\f$-classes of \c this.
439     //!
440     //! This involves computing complete frames for every
441     //! \f$\mathscr{D}\f$-class of \c this.
number_of_D_classes()442     size_t number_of_D_classes() {
443       run();
444       return std::distance(cbegin_D_classes(), cend_D_classes());
445     }
446 
447     //! Returns the current number of \f$\mathscr{D}\f$-classes of \c this.
448     //!
449     //! This member function does not perform any enumeration of the semigroup.
current_number_of_D_classes() const450     size_t current_number_of_D_classes() const {
451       return std::distance(cbegin_D_classes(), cend_D_classes());
452     }
453 
454     //! Returns the number of regular \f$\mathscr{D}\f$-classes of \c this.
455     //!
456     //! This involves computing complete frames for every
457     //! \f$\mathscr{D}\f$-class of \c this.
number_of_regular_D_classes()458     size_t number_of_regular_D_classes() {
459       run();
460       return current_number_of_regular_D_classes();
461     }
462 
463     //! Returns the current number of regular \f$\mathscr{D}\f$-classes of \c
464     //! this.
465     //!
466     //! This member function does not perform any enumeration of the semigroup.
current_number_of_regular_D_classes() const467     size_t current_number_of_regular_D_classes() const {
468       return std::distance(cbegin_regular_D_classes(),
469                            cend_regular_D_classes());
470     }
471 
472     //! Returns the number of \f$\mathscr{L}\f$-classes of \c this.
473     //!
474     //! This involves computing complete frames for every
475     //! \f$\mathscr{D}\f$-class of \c this.
number_of_L_classes()476     size_t number_of_L_classes() {
477       run();
478       return current_number_of_L_classes();
479     }
480 
481     //! Returns the current number of \f$\mathscr{L}\f$-classes of \c this.
482     //!
483     //! This member function does not perform any enumeration of the semigroup.
current_number_of_L_classes() const484     size_t current_number_of_L_classes() const {
485       size_t val = 0;
486       std::for_each(
487           cbegin_D_classes(), cend_D_classes(), [&val](DClass const& D) {
488             val += D.number_of_L_classes();
489           });
490       return val;
491     }
492 
493     //! Returns the number of regular \f$\mathscr{L}\f$-classes of \c this.
494     //!
495     //! This involves computing complete frames for every
496     //! \f$\mathscr{D}\f$-class of \c this.
number_of_regular_L_classes()497     size_t number_of_regular_L_classes() {
498       run();
499       return current_number_of_regular_L_classes();
500     }
501 
502     //! Returns the number of regular \f$\mathscr{L}\f$-classes of \c this.
503     //!
504     //! This member function does not perform any enumeration of the semigroup.
current_number_of_regular_L_classes() const505     size_t current_number_of_regular_L_classes() const {
506       size_t val = 0;
507       std::for_each(
508           cbegin_regular_D_classes(),
509           cend_regular_D_classes(),
510           [&val](DClass const& D) { val += D.number_of_L_classes(); });
511       return val;
512     }
513 
514     //! Returns the number of \f$\mathscr{R}\f$-classes of \c this.
515     //!
516     //! This involves computing complete frames for every
517     //! \f$\mathscr{D}\f$-class of \c this.
number_of_R_classes()518     size_t number_of_R_classes() {
519       run();
520       return current_number_of_R_classes();
521     }
522 
523     //! Returns the current number of \f$\mathscr{R}\f$-classes of \c this.
524     //!
525     //! This member function does not perform any enumeration of the semigroup.
current_number_of_R_classes() const526     size_t current_number_of_R_classes() const {
527       size_t val = 0;
528       std::for_each(
529           cbegin_D_classes(), cend_D_classes(), [&val](DClass const& D) {
530             val += D.number_of_R_classes();
531           });
532       return val;
533     }
534 
535     //! Returns the number of regular \f$\mathscr{R}\f$-classes of \c this.
536     //!
537     //! This involves computing complete frames for every
538     //! \f$\mathscr{D}\f$-class of \c this.
number_of_regular_R_classes()539     size_t number_of_regular_R_classes() {
540       run();
541       return current_number_of_regular_R_classes();
542     }
543 
544     //! Returns the current number of regular \f$\mathscr{R}\f$-classes of \c
545     //! this.
546     //!
547     //! This member function does not perform any enumeration of the semigroup.
current_number_of_regular_R_classes() const548     size_t current_number_of_regular_R_classes() const {
549       size_t val = 0;
550       std::for_each(
551           cbegin_regular_D_classes(),
552           cend_regular_D_classes(),
553           [&val](DClass const& D) { val += D.number_of_R_classes(); });
554       return val;
555     }
556 
557     //! Returns the number of \f$\mathscr{H}\f$-classes of \c this.
558     //!
559     //! This involves computing complete frames for every
560     //! \f$\mathscr{D}\f$-class of \c this.
number_of_H_classes()561     size_t number_of_H_classes() {
562       run();
563       return current_number_of_H_classes();
564     }
565 
566     //! Returns the current number of \f$\mathscr{H}\f$-classes of \c this.
567     //!
568     //! This member function does not perform any enumeration of the semigroup.
current_number_of_H_classes() const569     size_t current_number_of_H_classes() const {
570       size_t val = 0;
571       std::for_each(
572           cbegin_D_classes(), cend_D_classes(), [&val](DClass const& D) {
573             val += (D.number_of_R_classes() * D.number_of_L_classes());
574           });
575       return val;
576     }
577 
578     //! Returns the number of idempotents in \c this.
579     //!
580     //! This involves computing complete frames for every
581     //! \f$\mathscr{D}\f$-class of \c this.
number_of_idempotents()582     size_t number_of_idempotents() {
583       run();
584       return current_number_of_idempotents();
585     }
586 
587     //! Returns the current number of idempotents in \c this.
588     //!
589     //! This member function does not perform any enumeration of the semigroup.
current_number_of_idempotents() const590     size_t current_number_of_idempotents() const {
591       size_t val = 0;
592       std::for_each(
593           cbegin_regular_D_classes(),
594           cend_regular_D_classes(),
595           [&val](RegularDClass const& D) { val += D.number_of_idempotents(); });
596       return val;
597     }
598 
599     //! Returns the number of regular elements in \c this.
600     //!
601     //! This involves computing complete frames for every
602     //! \f$\mathscr{D}\f$-class of \c this.
number_of_regular_elements()603     size_t number_of_regular_elements() {
604       run();
605       return current_number_of_regular_elements();
606     }
607 
608     //! Returns the current number of regular elements in \c this.
609     //!
610     //! This member function does not perform any enumeration of the semigroup.
current_number_of_regular_elements() const611     size_t current_number_of_regular_elements() const {
612       size_t val = 0;
613       std::for_each(cbegin_regular_D_classes(),
614                     cend_regular_D_classes(),
615                     [&val](DClass const& D) { val += D.size(); });
616       return val;
617     }
618 
619     //! Returns the size of \c this.
620     //!
621     //! This involves computing complete frames for every
622     //! \f$\mathscr{D}\f$-class of \c this.
623     //!
624     //! \sa current_size
size()625     size_t size() {
626       run();
627       return current_size();
628     }
629 
630     //! Returns the current size of \c this.
631     //!
632     //! This member function does not perform any enumeration of the semigroup.
633     //!
634     //! \sa size
current_size()635     size_t current_size() {
636       size_t val = 0;
637       std::for_each(cbegin_D_classes(),
638                     cend_D_classes(),
639                     [&val](DClass const& D) { val += D.size(); });
640       return val;
641     }
642 
643     //! Returns the degree of elements of \c this.
644     //!
645     //! All elements of \c this must have the same degree; this member function
646     //! returns that degree.
647     //!
648     //! \noexcept
649     //!
650     //! \sa Degree
degree() const651     size_t degree() const noexcept {
652       return _degree;
653     }
654 
655     //! Returns whether \p x is contained in \c this.
656     //!
657     //! This involves computing as many complete frames for
658     //! \f$\mathscr{D}\f$-classes of \c this as necessary.
contains(const_reference x)659     bool contains(const_reference x) {
660       return Degree()(x) == degree()
661              && get_containing_D_class(this->to_internal_const(x), true)
662                     != UNDEFINED;
663     }
664 
665     //! Returns the \f$\mathscr{D}\f$-class of \c this which contains \p x.
666     //!
667     //! This involves computing as many complete frames for
668     //! \f$\mathscr{D}\f$-classes of \c this as necessary.
669     //!
670     //! \throws LibsemigroupsException if \p x does not belong to \c this.
D_class_of_element(const_reference x)671     DClass& D_class_of_element(const_reference x) {
672       D_class_index_type i
673           = get_containing_D_class(this->to_internal_const(x), true);
674       if (i == UNDEFINED) {
675         LIBSEMIGROUPS_EXCEPTION(
676             "the argument does not belong to this semigroup!");
677       }
678       return *_D_classes[i];
679     }
680 
681     //! Returns whether \p x is regular in \c this.
682     //!
683     //! This involves computing the orbits of the Lambda and Rho values under
684     //! the action of \c this, if they are not already computed.
is_regular_element(const_reference x)685     bool is_regular_element(const_reference x) {
686       return contains(x) && is_regular_element_NC(this->to_internal_const(x));
687     }
688 
689     //! Add a copy of the generator \p x to the generators of \c this.
690     //!
691     //! This member function may result in \c this having duplicate generators.
692     //!
693     //! \throws LibsemigroupsException if enumeration of \c this has already
694     //! begun
add_generator(const_reference gen)695     void add_generator(const_reference gen) {
696       add_generators(&gen, &gen + 1);
697     }
698 
699     //! Add copies of the generators \p coll to the generators of \c this.
700     //!
701     //! See Konieczny::add_generator for more details.
702     template <typename T>
add_generators(T const & coll)703     void add_generators(T const& coll) {
704       static_assert(!std::is_pointer<T>::value,
705                     "the template parameter T must not be a pointer");
706       add_generators(coll.begin(), coll.end());
707     }
708 
709     //! Add copies of the generators in the range \p first to \p last to \c
710     //! this.
711     //!
712     //! See Konieczny::add_generator for more details.
add_generators(std::initializer_list<const_element_type> coll)713     void add_generators(std::initializer_list<const_element_type> coll) {
714       add_generators<std::initializer_list<const_element_type>>(coll);
715     }
716 
717     //! Add copies of the generators in the range \p first to \p last to \c
718     //! this.
719     //!
720     //! See Konieczny::add_generator for more details.
721     template <typename T>
add_generators(T const & first,T const & last)722     void add_generators(T const& first, T const& last) {
723       if (started()) {
724         LIBSEMIGROUPS_EXCEPTION(
725             "cannot add generators after the algorithm has begun!");
726       }
727       validate_element_collection(first, last);
728       for (auto it = first; it < last; ++it) {
729         _gens.push_back(this->internal_copy(this->to_internal_const(*it)));
730       }
731     }
732 
733     ////////////////////////////////////////////////////////////////////////
734     // Konieczny - iterators - public
735     ////////////////////////////////////////////////////////////////////////
736 
737     //! A type for const iterators through elements of \c this.
738     using const_iterator
739         = detail::BruidhinnConstIterator<element_type,
740                                          std::vector<internal_element_type>>;
741 
742     //! Returns a const iterator pointing to the first generator of the
743     //! semigroup.
744     //!
745     //! This member function does not perform any enumeration of the
746     //! semigroup; the iterator returned may be invalidated by any call to a
747     //! non-const member function of the Konieczny class.
748     //!
749     //! \noexcept
750     //!
751     //! \sa cend_generators
cbegin_generators() const752     const_iterator cbegin_generators() const noexcept {
753       return const_iterator(_gens.cbegin());
754     }
755 
756     //! Returns a const iterator pointing to past the last generator of the
757     //! semigroup.
758     //!
759     //! This member function does not perform any enumeration of the
760     //! semigroup; the iterator returned may be invalidated by any call to a
761     //! non-const member function of the Konieczny class.
762     //!
763     //! \noexcept
764     //!
765     //! \sa cbegin_generators
cend_generators() const766     const_iterator cend_generators() const noexcept {
767       return const_iterator(_gens.cend() - 1);
768     }
769 
770     // This is a traits class for ConstIteratorStateless in iterator.hpp
771     //! No doc
772     template <typename T>
773     struct DClassIteratorTraits : detail::ConstIteratorTraits<std::vector<T*>> {
774       using base_traits_type = detail::ConstIteratorTraits<std::vector<T*>>;
775 
776       using internal_iterator_type =
777           typename base_traits_type::internal_iterator_type;
778 
779       using value_type      = T;
780       using reference       = value_type&;
781       using const_reference = value_type const&;
782       using const_pointer   = value_type const*;
783       using pointer         = value_type*;
784 
785       //! No doc
786       struct Deref {
787         //! No doc
788         const_reference
operator ()libsemigroups::Konieczny::DClassIteratorTraits::Deref789         operator()(internal_iterator_type const& it) const noexcept {
790           return **it;
791         }
792       };
793 
794       //! No doc
795       struct AddressOf {
796         //! No doc
797         const_pointer
operator ()libsemigroups::Konieczny::DClassIteratorTraits::AddressOf798         operator()(internal_iterator_type const& it) const noexcept {
799           return &(**it);
800         }
801       };
802     };
803 
804     //! A type for const iterators through the \f$\mathscr{D}\f$-classes of \c
805     //! this, in the order they were enumerated.
806     //!
807     //! \sa const_regular_d_class_iterator.
808     using const_d_class_iterator
809         = detail::ConstIteratorStateless<DClassIteratorTraits<DClass>>;
810 
811     //! Returns a const iterator referring to a pointer to the first
812     //! \f$\mathscr{D}\f$-class of the semigroup.
813     //!
814     //! This member function does not perform any enumeration of the
815     //! semigroup; the iterator returned may be invalidated by any call to a
816     //! non-const member function of the Konieczny class.
817     // not noexcept because operator++ isn't necessarily
cbegin_D_classes() const818     const_d_class_iterator cbegin_D_classes() const {
819       auto it = _D_classes.cbegin();
820       return const_d_class_iterator(it)
821              + ((_D_classes.size() > 0 && _adjoined_identity_contained) ? 0
822                                                                         : 1);
823     }
824 
825     //! Returns a const iterator referring to past the pointer to the last
826     //! \f$\mathscr{D}\f$-class of the semigroup.
827     //!
828     //! This member function does not perform any enumeration of the
829     //! semigroup; the iterator returned may be invalidated by any call to a
830     //! non-const member function of the Konieczny class.
cend_D_classes() const831     const_d_class_iterator cend_D_classes() const noexcept {
832       return const_d_class_iterator(_D_classes.cend());
833     }
834 
835     //! A type for const iterators through the regular \f$\mathscr{D}\f$-classes
836     //! of \c this, in the order they were enumerated.
837     //!
838     //! \sa const_d_class_iterator.
839     using const_regular_d_class_iterator
840         = detail::ConstIteratorStateless<DClassIteratorTraits<RegularDClass>>;
841 
842     //! Returns a const iterator referring to a pointer to the first regular
843     //! \f$\mathscr{D}\f$-class of the semigroup.
844     //!
845     //! This member function does not perform any enumeration of the
846     //! semigroup; the iterator returned may be invalidated by any call to a
847     //! non-const member function of the Konieczny class.
848     //!
849     //! \sa cbegin_rdc
850     // not noexcept because operator++ isn't necessarily
cbegin_regular_D_classes() const851     const_regular_d_class_iterator cbegin_regular_D_classes() const {
852       auto it = _regular_D_classes.cbegin();
853       return const_regular_d_class_iterator(it)
854              + ((_regular_D_classes.size() > 0 && _adjoined_identity_contained)
855                     ? 0
856                     : 1);
857     }
858 
859     //! Alias for Konieczny::cbegin_regular_D_classes.
cbegin_rdc() const860     const_regular_d_class_iterator cbegin_rdc() const noexcept {
861       return cbegin_regular_D_classes();
862     }
863 
864     //! Returns a const iterator referring to past the pointer to the last
865     //! regular \f$\mathscr{D}\f$-class of the semigroup.
866     //!
867     //! This member function does not perform any enumeration of the
868     //! semigroup; the iterator returned may be invalidated by any call to a
869     //! non-const member function of the Konieczny class.
870     //!
871     //! \sa cend_rdc
cend_regular_D_classes() const872     const_regular_d_class_iterator cend_regular_D_classes() const noexcept {
873       return const_regular_d_class_iterator(_regular_D_classes.cend());
874     }
875 
876     //! Alias for Konieczny::cend_regular_D_classes.
cend_rdc() const877     const_regular_d_class_iterator cend_rdc() const {
878       return cend_regular_D_classes();
879     }
880 
881    private:
882     using PoolGuard = detail::PoolGuard<internal_element_type>;
883 
884     ////////////////////////////////////////////////////////////////////////
885     // Konieczny - utility methods - private
886     ////////////////////////////////////////////////////////////////////////
887 
888     // assumes its argument has valid lambda/rho values
is_regular_element_NC(internal_const_reference x)889     bool is_regular_element_NC(internal_const_reference x) {
890       LIBSEMIGROUPS_ASSERT(_lambda_orb.finished() && _rho_orb.finished());
891       return get_lambda_group_index(x) != UNDEFINED;
892     }
893 
894     // Returns a lambda orb index corresponding to a group H-class in the R-
895     // class of \p x.
896     // asserts its argument has lambda/rho values in the orbits.
897     // modifies _tmp_lambda_value1
898     // modifies _tmp_rho_value1
get_lambda_group_index(internal_const_reference x)899     lambda_orb_index_type get_lambda_group_index(internal_const_reference x) {
900       Rho()(_tmp_rho_value1, this->to_external_const(x));
901       Lambda()(_tmp_lambda_value1, this->to_external_const(x));
902       lambda_orb_index_type lpos = _lambda_orb.position(_tmp_lambda_value1);
903       LIBSEMIGROUPS_ASSERT(lpos != UNDEFINED);
904 
905       lambda_orb_scc_index_type lval_scc_id
906           = _lambda_orb.digraph().scc_id(lpos);
907 
908       std::pair<rho_orb_index_type, lambda_orb_scc_index_type> key(
909           _rho_orb.position(_tmp_rho_value1), lval_scc_id);
910 
911       if (_group_indices.find(key) != _group_indices.end()) {
912         return _group_indices.at(key);
913       } else {
914         PoolGuard             cg1(_element_pool);
915         PoolGuard             cg2(_element_pool);
916         internal_element_type tmp1 = cg1.get();
917         internal_element_type tmp2 = cg2.get();
918 
919         Product()(this->to_external(tmp1),
920                   this->to_external_const(x),
921                   _lambda_orb.multiplier_to_scc_root(lpos));
922         for (auto it = _lambda_orb.digraph().cbegin_scc(lval_scc_id);
923              it < _lambda_orb.digraph().cend_scc(lval_scc_id);
924              it++) {
925           Product()(this->to_external(tmp2),
926                     this->to_external(tmp1),
927                     _lambda_orb.multiplier_from_scc_root(*it));
928           if (is_group_index(x, tmp2)) {
929             _group_indices.emplace(key, *it);
930             return *it;
931           }
932         }
933       }
934       _group_indices.emplace(key, UNDEFINED);
935       return UNDEFINED;
936     }
937 
938     // Finds a group index of a H-class in the L-class of \p x.
939     // modifies _tmp_lambda_value1
940     // modifies _tmp_rho_value1
get_rho_group_index(internal_const_reference x)941     rho_orb_index_type get_rho_group_index(internal_const_reference x) {
942       Rho()(_tmp_rho_value1, this->to_external_const(x));
943       Lambda()(_tmp_lambda_value1, this->to_external_const(x));
944       rho_orb_index_type rpos = _rho_orb.position(_tmp_rho_value1);
945       LIBSEMIGROUPS_ASSERT(rpos != UNDEFINED);
946       rho_orb_scc_index_type rval_scc_id = _rho_orb.digraph().scc_id(rpos);
947 
948       std::pair<rho_orb_scc_index_type, lambda_orb_index_type> key(
949           rval_scc_id, _lambda_orb.position(_tmp_lambda_value1));
950 
951       if (_group_indices_rev.find(key) != _group_indices_rev.end()) {
952         return _group_indices_rev.at(key);
953       } else {
954         PoolGuard             cg1(_element_pool);
955         internal_element_type tmp1 = cg1.get();
956         PoolGuard             cg2(_element_pool);
957         internal_element_type tmp2 = cg2.get();
958 
959         Product()(this->to_external(tmp1),
960                   _rho_orb.multiplier_to_scc_root(rpos),
961                   this->to_external_const(x));
962         for (auto it = _rho_orb.digraph().cbegin_scc(rval_scc_id);
963              it < _rho_orb.digraph().cend_scc(rval_scc_id);
964              it++) {
965           Product()(this->to_external(tmp2),
966                     _rho_orb.multiplier_from_scc_root(*it),
967                     this->to_external(tmp1));
968           if (is_group_index(tmp2, x)) {
969             _group_indices_rev.emplace(key, *it);
970             return *it;
971           }
972         }
973       }
974       _group_indices_rev.emplace(key, UNDEFINED);
975       return UNDEFINED;
976     }
977 
978     //! Finds the idempotent in the H-class of \p x. Note that it is assumed
979     //! that \p x is in a group H-class.
980     // TODO(later): it must be possible to do better than this
idem_in_H_class(internal_reference res,internal_const_reference x) const981     void idem_in_H_class(internal_reference       res,
982                          internal_const_reference x) const {
983       this->to_external(res) = this->to_external_const(x);
984       PoolGuard             cg(_element_pool);
985       internal_element_type tmp = cg.get();
986       do {
987         Swap()(this->to_external(res), this->to_external(tmp));
988         Product()(this->to_external(res),
989                   this->to_external_const(tmp),
990                   this->to_external_const(x));
991         Product()(this->to_external(tmp),
992                   this->to_external_const(res),
993                   this->to_external_const(res));
994       } while (!InternalEqualTo()(res, tmp));
995     }
996 
997     //! Finds an idempotent in the \f$\mathscr{D}\f$-class of \c x, if \c x is
998     //! regular, and modifies \c x in place to be this idempotent
999     // modifies _tmp_lambda_value1
make_idem(internal_reference x)1000     void make_idem(internal_reference x) {
1001       LIBSEMIGROUPS_ASSERT(is_regular_element_NC(x));
1002       PoolGuard             cg1(_element_pool);
1003       internal_element_type tmp1 = cg1.get();
1004 
1005       Product()(this->to_external(tmp1),
1006                 this->to_external_const(x),
1007                 this->to_external_const(x));
1008       if (EqualTo()(this->to_external(tmp1), this->to_external_const(x))) {
1009         return;
1010       }
1011 
1012       lambda_orb_index_type i = get_lambda_group_index(x);
1013       Lambda()(_tmp_lambda_value1, this->to_external_const(x));
1014       lambda_orb_index_type pos = _lambda_orb.position(_tmp_lambda_value1);
1015 
1016       PoolGuard             cg2(_element_pool);
1017       internal_element_type tmp2 = cg2.get();
1018       Product()(this->to_external(tmp1),
1019                 this->to_external_const(x),
1020                 _lambda_orb.multiplier_to_scc_root(pos));
1021       Product()(this->to_external(tmp2),
1022                 this->to_external(tmp1),
1023                 _lambda_orb.multiplier_from_scc_root(i));
1024 
1025       idem_in_H_class(tmp1, tmp2);
1026       this->to_external(x) = this->to_external_const(tmp1);
1027     }
1028 
1029     //! Finds the group inverse of \p x in its H-class; i.e. the element \c y
1030     //! in the H-class of \p x such that <tt> xy = \p id</tt>. Will run
1031     //! forever if no such element exists.
group_inverse(internal_element_type & res,internal_const_reference id,internal_const_reference x) const1032     void group_inverse(internal_element_type&   res,
1033                        internal_const_reference id,
1034                        internal_const_reference x) const {
1035       PoolGuard             cg(_element_pool);
1036       internal_element_type tmp = cg.get();
1037       this->to_external(tmp)    = this->to_external_const(x);
1038       do {
1039         Swap()(this->to_external(res), this->to_external(tmp));
1040         Product()(this->to_external(tmp),
1041                   this->to_external_const(res),
1042                   this->to_external_const(x));
1043       } while (!InternalEqualTo()(tmp, id));
1044     }
1045 
1046     //! Determines whether <tt>(x, y)</tt> forms a group index.
1047     // modifies _tmp_lambda_value and _tmp_rho_value
is_group_index(internal_const_reference x,internal_const_reference y) const1048     bool is_group_index(internal_const_reference x,
1049                         internal_const_reference y) const {
1050       PoolGuard             cg(_element_pool);
1051       internal_element_type tmp = cg.get();
1052 
1053       Product()(this->to_external(tmp),
1054                 this->to_external_const(y),
1055                 this->to_external_const(x));
1056       Lambda()(_tmp_lambda_value1, this->to_external(tmp));
1057       Rho()(_tmp_rho_value1, this->to_external(tmp));
1058       Lambda()(_tmp_lambda_value2, this->to_external_const(x));
1059       Rho()(_tmp_rho_value2, this->to_external_const(y));
1060 
1061       return _tmp_lambda_value1 == _tmp_lambda_value2
1062              && _tmp_rho_value1 == _tmp_rho_value2;
1063     }
1064 
1065     // pass full_check = true to use the contains method of the D-classes
1066     // instead of the contains_NC
get_containing_D_class(internal_const_reference x,bool const full_check=false)1067     D_class_index_type get_containing_D_class(internal_const_reference x,
1068                                               bool const full_check = false) {
1069       if (full_check) {
1070         rank_type const rnk
1071             = InternalRank()(_rank_state, this->to_external_const(x));
1072         run_until([this, rnk]() -> bool { return max_rank() < rnk; });
1073       }
1074 
1075       Lambda()(_tmp_lambda_value1, this->to_external_const(x));
1076       Rho()(_tmp_rho_value1, this->to_external_const(x));
1077       lambda_orb_index_type lpos = _lambda_orb.position(_tmp_lambda_value1);
1078       lambda_orb_index_type rpos = _rho_orb.position(_tmp_rho_value1);
1079       if (lpos == UNDEFINED || rpos == UNDEFINED) {
1080         // this should only be possible if this function was called from a
1081         // public function, and hence full_check is true.
1082         LIBSEMIGROUPS_ASSERT(full_check);
1083         return UNDEFINED;
1084       }
1085       auto l_it = _lambda_to_D_map.find(lpos);
1086       auto r_it = _rho_to_D_map.find(rpos);
1087       if (l_it != _lambda_to_D_map.end() && r_it != _rho_to_D_map.end()) {
1088         auto l_D_it  = l_it->second.cbegin();
1089         auto l_D_end = l_it->second.cend();
1090         auto r_D_it  = r_it->second.cbegin();
1091         auto r_D_end = r_it->second.cend();
1092         // the vectors should already be sorted given how we create them
1093         LIBSEMIGROUPS_ASSERT(std::is_sorted(l_D_it, l_D_end));
1094         LIBSEMIGROUPS_ASSERT(std::is_sorted(r_D_it, r_D_end));
1095         while (l_D_it != l_D_end && r_D_it != r_D_end) {
1096           if (*l_D_it < *r_D_it) {
1097             ++l_D_it;
1098           } else {
1099             if (*r_D_it == *l_D_it) {
1100               if (full_check) {
1101                 if (_D_classes[*l_D_it]->contains(
1102                         this->to_external_const(x), lpos, rpos)) {
1103                   return *l_D_it;
1104                 }
1105               } else {
1106                 if (_D_classes[*l_D_it]->contains_NC(x, lpos, rpos)) {
1107                   return *l_D_it;
1108                 }
1109               }
1110             }
1111             ++r_D_it;
1112           }
1113         }
1114       }
1115       return UNDEFINED;
1116     }
1117 
add_to_D_maps(D_class_index_type d)1118     void add_to_D_maps(D_class_index_type d) {
1119       LIBSEMIGROUPS_ASSERT(d < _D_classes.size());
1120       DClass* D = _D_classes[d];
1121       for (auto it = D->cbegin_left_indices(); it < D->cend_left_indices();
1122            ++it) {
1123         _lambda_to_D_map[*it].push_back(d);
1124       }
1125       for (auto it = D->cbegin_right_indices(); it < D->cend_right_indices();
1126            ++it) {
1127         _rho_to_D_map[*it].push_back(d);
1128       }
1129     }
1130 
1131     ////////////////////////////////////////////////////////////////////////
1132     // Konieczny - accessor member functions - private
1133     ////////////////////////////////////////////////////////////////////////
1134     void add_D_class(Konieczny::RegularDClass* D);
1135     void add_D_class(Konieczny::NonRegularDClass* D);
1136 
1137     typename std::vector<internal_element_type>::const_iterator
cbegin_internal_generators() const1138     cbegin_internal_generators() const noexcept {
1139       return _gens.cbegin();
1140     }
1141 
1142     typename std::vector<internal_element_type>::const_iterator
cend_internal_generators() const1143     cend_internal_generators() const noexcept {
1144       return _gens.cend();
1145     }
1146 
element_pool() const1147     detail::Pool<internal_element_type>& element_pool() const {
1148       return _element_pool;
1149     }
1150 
max_rank() const1151     size_t max_rank() const noexcept {
1152       if (_ranks.empty()) {
1153         return UNDEFINED;
1154       }
1155       return *_ranks.rbegin();
1156     }
1157 
1158     ////////////////////////////////////////////////////////////////////////
1159     // Konieczny - initialisation member functions - private
1160     ////////////////////////////////////////////////////////////////////////
1161     void init_run();
1162 
init_data()1163     void init_data() {
1164       if (_data_initialised) {
1165         return;
1166       }
1167       if (_gens.empty()) {
1168         LIBSEMIGROUPS_EXCEPTION("no generators have been added!");
1169       }
1170       LIBSEMIGROUPS_ASSERT(
1171           _degree == UNDEFINED
1172           || _degree == Degree()(this->to_external_const(_gens[0])));
1173       _degree = Degree()(this->to_external_const(_gens[0]));
1174 
1175       element_type x = this->to_external_const(_gens[0]);
1176 
1177       _tmp_lambda_value1 = OneParamLambda()(x);
1178       _tmp_lambda_value2 = OneParamLambda()(x);
1179 
1180       _tmp_rho_value1 = OneParamRho()(x);
1181       _tmp_rho_value2 = OneParamRho()(x);
1182 
1183       // if _one is created but not immediately push into _gens
1184       // it won't be freed if there are exceptions thrown!
1185       _one = this->to_internal(One()(x));
1186       _gens.push_back(_one);  // TODO(later): maybe not this
1187 
1188       _element_pool.init(_one);
1189 
1190       _rank_state = new rank_state_type(cbegin_generators(), cend_generators());
1191       LIBSEMIGROUPS_ASSERT((_rank_state == nullptr)
1192                            == (std::is_same<void, rank_state_type>::value));
1193       _nonregular_reps = std::vector<
1194           std::vector<std::pair<internal_element_type, D_class_index_type>>>(
1195           InternalRank()(_rank_state, this->to_external_const(_one)) + 1,
1196           std::vector<std::pair<internal_element_type, D_class_index_type>>());
1197       _reg_reps = std::vector<
1198           std::vector<std::pair<internal_element_type, D_class_index_type>>>(
1199           InternalRank()(_rank_state, this->to_external_const(_one)) + 1,
1200           std::vector<std::pair<internal_element_type, D_class_index_type>>());
1201 
1202       _data_initialised = true;
1203     }
1204 
compute_orbs()1205     void compute_orbs() {
1206       if (_lambda_orb.finished() && _rho_orb.finished()) {
1207         return;
1208       }
1209       REPORT_DEFAULT("Computing orbits . . .\n");
1210       detail::Timer t;
1211       if (!_lambda_orb.started()) {
1212         _lambda_orb.add_seed(OneParamLambda()(this->to_external_const(_one)));
1213         for (internal_const_element_type g : _gens) {
1214           _lambda_orb.add_generator(this->to_external_const(g));
1215         }
1216       }
1217       if (!_rho_orb.started()) {
1218         _rho_orb.add_seed(OneParamRho()(this->to_external_const(_one)));
1219         for (internal_const_element_type g : _gens) {
1220           _rho_orb.add_generator(this->to_external_const(g));
1221         }
1222       }
1223       _lambda_orb.run_until([this]() -> bool { return this->stopped(); });
1224       _rho_orb.run_until([this]() -> bool { return this->stopped(); });
1225       REPORT_DEFAULT("found %llu lambda-values and %llu rho-values in %s\n",
1226                      static_cast<uint64_t>(_lambda_orb.current_size()),
1227                      static_cast<uint64_t>(_rho_orb.current_size()),
1228                      t.string().c_str());
1229     }
1230 
1231     ////////////////////////////////////////////////////////////////////////
1232     // Konieczny - validation member functions - private
1233     ////////////////////////////////////////////////////////////////////////
1234 
validate_element(const_reference x) const1235     void validate_element(const_reference x) const {
1236       size_t const n = Degree()(x);
1237       if (degree() != UNDEFINED && n != degree()) {
1238         LIBSEMIGROUPS_EXCEPTION(
1239             "element has degree %d but should have degree %d", n, degree());
1240       }
1241     }
1242 
1243     template <typename T>
validate_element_collection(T const & first,T const & last) const1244     void validate_element_collection(T const& first, T const& last) const {
1245       if (degree() == UNDEFINED && std::distance(first, last) != 0) {
1246         auto const n = Degree()(*first);
1247         for (auto it = first + 1; it < last; ++it) {
1248           auto const m = Degree()(*it);
1249           if (m != n) {
1250             LIBSEMIGROUPS_EXCEPTION(
1251                 "element has degree %d but should have degree %d", n, m);
1252           }
1253         }
1254       } else {
1255         for (auto it = first; it < last; ++it) {
1256           validate_element(*it);
1257         }
1258       }
1259     }
1260 
1261     ////////////////////////////////////////////////////////////////////////
1262     // Konieczny - Runner methods - private
1263     ////////////////////////////////////////////////////////////////////////
1264     bool finished_impl() const override;
1265     void run_impl() override;
1266     void run_report();
1267 
1268     ////////////////////////////////////////////////////////////////////////
1269     // Konieczny - data - private
1270     ////////////////////////////////////////////////////////////////////////
1271     bool                                         _adjoined_identity_contained;
1272     std::vector<DClass*>                         _D_classes;
1273     std::vector<std::vector<D_class_index_type>> _D_rels;
1274     bool                                         _data_initialised;
1275     size_t                                       _degree;
1276     mutable detail::Pool<internal_element_type>  _element_pool;
1277     std::vector<internal_element_type>           _gens;
1278     std::unordered_map<std::pair<rho_orb_index_type, lambda_orb_scc_index_type>,
1279                        lambda_orb_index_type,
1280                        PairHash>
1281         _group_indices;
1282     std::unordered_map<std::pair<rho_orb_scc_index_type, lambda_orb_index_type>,
1283                        rho_orb_index_type,
1284                        PairHash>
1285                     _group_indices_rev;
1286     lambda_orb_type _lambda_orb;
1287     std::unordered_map<lambda_orb_index_type, std::vector<D_class_index_type>>
1288         _lambda_to_D_map;
1289     std::vector<
1290         std::vector<std::pair<internal_element_type, D_class_index_type>>>
1291                                 _nonregular_reps;
1292     internal_element_type       _one;
1293     rank_state_type*            _rank_state;
1294     std::set<rank_type>         _ranks;
1295     std::vector<RegularDClass*> _regular_D_classes;
1296     std::vector<
1297         std::vector<std::pair<internal_element_type, D_class_index_type>>>
1298                  _reg_reps;
1299     size_t       _reps_processed;
1300     rho_orb_type _rho_orb;
1301     std::unordered_map<rho_orb_index_type, std::vector<D_class_index_type>>
1302                               _rho_to_D_map;
1303     bool                      _run_initialised;
1304     mutable lambda_value_type _tmp_lambda_value1;
1305     mutable lambda_value_type _tmp_lambda_value2;
1306     mutable rho_value_type    _tmp_rho_value1;
1307     mutable rho_value_type    _tmp_rho_value2;
1308   };
1309 
1310   /////////////////////////////////////////////////////////////////////////////
1311   // DClass
1312   /////////////////////////////////////////////////////////////////////////////
1313 
1314   //! Defined in ``konieczny.hpp``.
1315   //!
1316   //! The nested abstract class Konieczny::DClass represents a
1317   //! \f$\mathscr{D}\f$-class via a complete frame as computed in %Konieczny's
1318   //! algorithm. See [here] for more details.
1319   //!
1320   //! As an abstract class, DClass cannot be directly constructed; instead you
1321   //! should obtain a \f$\mathscr{D}\f$-class by calling Konieczny::get_D_class
1322   //! on the parent semigroup.
1323   //!
1324   //! \sa Konieczny.
1325   //!
1326   //! [here]:  https://link.springer.com/article/10.1007/BF02573672
1327   template <typename TElementType, typename TTraits>
1328   class Konieczny<TElementType, TTraits>::DClass
1329       : protected detail::BruidhinnTraits<TElementType> {
1330     // This friend is only here so that the virtual contains(x, lpos, rpos)
1331     // method and the cbegin_left_indices etc. methods can be private.
1332     friend class Konieczny<TElementType, TTraits>;
1333 
1334    protected:
1335     ////////////////////////////////////////////////////////////////////////
1336     // DClass - aliases - protected
1337     ////////////////////////////////////////////////////////////////////////
1338     using konieczny_type    = Konieczny<TElementType, TTraits>;
1339     using internal_set_type = std::
1340         unordered_set<internal_element_type, InternalHash, InternalEqualTo>;
1341 
1342     ////////////////////////////////////////////////////////////////////////
1343     // DClass - constructors - public
1344     ////////////////////////////////////////////////////////////////////////
1345 
1346     //! Deleted.
1347     //!
1348     //! DClass does not support a copy constructor.
1349     DClass(DClass const&) = delete;
1350 
1351     //! Deleted.
1352     //!
1353     //! DClass does not support a copy assignment operator to avoid accidental
1354     //! copying.
1355     DClass& operator=(DClass const&) = delete;
1356 
1357     //! Deleted.
1358     //!
1359     //! DClass does not support a move constructor.
1360     DClass(DClass&&) = delete;
1361 
1362     //! Deleted.
1363     //!
1364     //! DClass does not support a move assignment operator.
1365     DClass& operator=(DClass&&) = delete;
1366 
1367     ////////////////////////////////////////////////////////////////////////
1368     // DClass - constructor - protected
1369     ////////////////////////////////////////////////////////////////////////
1370 
DClass(Konieczny * parent,internal_reference rep)1371     DClass(Konieczny* parent, internal_reference rep)
1372         : _class_computed(false),
1373           _H_class(),
1374           _H_class_computed(false),
1375           _left_indices(),
1376           _left_mults(),
1377           _left_mults_inv(),
1378           _left_reps(),
1379           _mults_computed(false),
1380           _parent(parent),
1381           _rank(InternalRank()(parent->_rank_state,
1382                                this->to_external_const(rep))),
1383           _rep(rep),  // note that rep is not copied and is now owned by this
1384           _reps_computed(false),
1385           _right_indices(),
1386           _right_mults(),
1387           _right_mults_inv(),
1388           _right_reps(),
1389           _tmp_internal_set(),
1390           _tmp_internal_vec(),
1391           _tmp_lambda_value(OneParamLambda()(this->to_external_const(rep))),
1392           _tmp_rho_value(OneParamRho()(this->to_external_const(rep))) {
1393       _is_regular_D_class = _parent->is_regular_element_NC(rep);
1394     }
1395 
1396    public:
1397     ////////////////////////////////////////////////////////////////////////
1398     // DClass - destructor - public
1399     ////////////////////////////////////////////////////////////////////////
~DClass()1400     virtual ~DClass() {
1401       // the user of _tmp_internal_vec/_tmp_internal_set is responsible for
1402       // freeing any necessary elements
1403       InternalVecFree()(_H_class);
1404       InternalVecFree()(_left_mults);
1405       InternalVecFree()(_left_mults_inv);
1406       InternalVecFree()(_left_reps);
1407       this->internal_free(_rep);
1408       InternalVecFree()(_right_mults);
1409       InternalVecFree()(_right_mults_inv);
1410       InternalVecFree()(_right_reps);
1411     }
1412 
1413     ////////////////////////////////////////////////////////////////////////
1414     // DClass - member functions - public
1415     ////////////////////////////////////////////////////////////////////////
1416 
1417     //! Returns the representative which defines \c this.
1418     //!
1419     //! The complete frame computed for \c this depends on the choice of
1420     //! representative. This function returns the representative used by \c
1421     //! this. This may not be the same representative as used to construct \c
1422     //! this, but is guaranteed to not change.
rep() const1423     const_reference rep() const {
1424       return this->to_external_const(_rep);
1425     }
1426 
1427     //! Returns the size of \c this.
1428     //!
1429     //! This member function involves computing most of the complete frame for
1430     //! \c this, if it is not already known.
size() const1431     size_t size() const {
1432       // init();
1433       LIBSEMIGROUPS_ASSERT(this->class_computed());
1434       return number_of_L_classes() * number_of_R_classes() * size_H_class();
1435     }
1436 
1437     //! Returns the number of \f$L\f$-classes in \c this.
1438     //!
1439     //! This member function involves computing some of the complete frame for
1440     //! \c this, if it is not already known.
number_of_L_classes() const1441     size_t number_of_L_classes() const {
1442       LIBSEMIGROUPS_ASSERT(_left_mults.size() > 0);
1443       LIBSEMIGROUPS_ASSERT(this->class_computed());
1444       return _left_mults.size();
1445     }
1446 
1447     //! Returns the number of \f$R\f$-classes in \c this.
1448     //!
1449     //! This member function involves computing some of the complete frame for
1450     //! \c this, if it is not already known.
number_of_R_classes() const1451     size_t number_of_R_classes() const {
1452       // compute_right_mults();
1453       LIBSEMIGROUPS_ASSERT(_right_mults.size() > 0);
1454       LIBSEMIGROUPS_ASSERT(this->class_computed());
1455       return _right_mults.size();
1456     }
1457 
1458     //! Returns the size of the \f$H\f$-classes in \c this.
1459     //!
1460     //! This member function involves computing some of the complete frame for
1461     //! \c this, if it is not already known.
size_H_class() const1462     size_t size_H_class() const {
1463       // compute_H_class();
1464       LIBSEMIGROUPS_ASSERT(_H_class.size() > 0);
1465       LIBSEMIGROUPS_ASSERT(this->class_computed());
1466       return _H_class.size();
1467     }
1468 
1469     //! Returns whether \c this is a regular \f$\mathscr{D}\f$-class.
1470     //!
1471     //! This member function is guaranteed to not throw an exception.
is_regular_D_class() const1472     bool is_regular_D_class() const noexcept {
1473       return _is_regular_D_class;
1474     }
1475 
1476     //! Returns whether the element \p x belongs to this
1477     //! \f$\mathscr{D}\f$-class.
1478     //!
1479     //! Given an element \p x which may or may not belong to \c parent, this
1480     //! function returns whether \p x is an element of the
1481     //! \f$\mathscr{D}\f$-class represented by \c this.
1482     //! This member function involves computing most of the complete frame for
1483     //! \c this, if it is not already known.
contains(const_reference x)1484     bool contains(const_reference x) {
1485       Lambda()(_tmp_lambda_value, x);
1486       Rho()(_tmp_rho_value, x);
1487       lambda_orb_index_type lpos
1488           = this->parent()->_lambda_orb.position(_tmp_lambda_value);
1489       rho_orb_index_type rpos
1490           = this->parent()->_rho_orb.position(_tmp_rho_value);
1491       return contains(x, lpos, rpos);
1492     }
1493 
1494     //! Returns the number of idempotents in \c this.
1495     //!
1496     //! This member function involves computing most of the complete frame for
1497     //! \c this, if it is not already known.
number_of_idempotents() const1498     virtual size_t number_of_idempotents() const {
1499       return 0;
1500     }
1501 
1502    protected:
1503     ////////////////////////////////////////////////////////////////////////
1504     // DClass - iterators - protected
1505     ////////////////////////////////////////////////////////////////////////
1506     using const_iterator =
1507         typename std::vector<internal_element_type>::const_iterator;
1508 
cbegin_left_reps()1509     const_iterator cbegin_left_reps() {
1510       compute_left_reps();
1511       return _left_reps.cbegin();
1512     }
1513 
cend_left_reps()1514     const_iterator cend_left_reps() {
1515       compute_left_reps();
1516       return _left_reps.cend();
1517     }
1518 
cbegin_right_reps()1519     const_iterator cbegin_right_reps() {
1520       compute_right_reps();
1521       return _right_reps.cbegin();
1522     }
1523 
cend_right_reps()1524     const_iterator cend_right_reps() {
1525       compute_right_reps();
1526       return _right_reps.cend();
1527     }
1528 
cbegin_left_mults()1529     const_iterator cbegin_left_mults() {
1530       compute_left_mults();
1531       return _left_mults.cbegin();
1532     }
1533 
cend_left_mults()1534     const_iterator cend_left_mults() {
1535       compute_left_mults();
1536       return _left_mults.cend();
1537     }
1538 
cbegin_right_mults()1539     const_iterator cbegin_right_mults() {
1540       compute_right_mults();
1541       return _right_mults.cbegin();
1542     }
1543 
cend_right_mults()1544     const_iterator cend_right_mults() {
1545       compute_right_mults();
1546       return _right_mults.cend();
1547     }
1548 
cbegin_H_class()1549     const_iterator cbegin_H_class() {
1550       compute_H_class();
1551       return _H_class.cbegin();
1552     }
1553 
cend_H_class()1554     const_iterator cend_H_class() {
1555       compute_H_class();
1556       return _H_class.cend();
1557     }
1558 
left_mults_inv(size_t i)1559     internal_element_type left_mults_inv(size_t i) {
1560       compute_left_mults_inv();
1561       return _left_mults_inv[i];
1562     }
1563 
right_mults_inv(size_t i)1564     internal_element_type right_mults_inv(size_t i) {
1565       compute_right_mults_inv();
1566       return _right_mults_inv[i];
1567     }
1568 
H_class_NC(size_t i) const1569     internal_element_type H_class_NC(size_t i) const {
1570       return _H_class[i];
1571     }
1572 
1573     ////////////////////////////////////////////////////////////////////////
1574     // DClass - initialisation member functions - protected
1575     ////////////////////////////////////////////////////////////////////////
1576     virtual void init()                    = 0;
1577     virtual void compute_left_indices()    = 0;
1578     virtual void compute_left_mults()      = 0;
1579     virtual void compute_left_mults_inv()  = 0;
1580     virtual void compute_left_reps()       = 0;
1581     virtual void compute_right_indices()   = 0;
1582     virtual void compute_right_mults()     = 0;
1583     virtual void compute_right_mults_inv() = 0;
1584     virtual void compute_right_reps()      = 0;
1585     virtual void compute_H_class()         = 0;
1586 
1587     ////////////////////////////////////////////////////////////////////////
1588     // DClass - containment - protected
1589     ////////////////////////////////////////////////////////////////////////
1590 
1591     // Returns whether the element \p x belongs to this \f$\mathscr{D}\f$-class.
1592     //
1593     // Given an element \p x of the semigroup represented by \c parent, this
1594     // function returns whether \p x is an element of the
1595     // \f$\mathscr{D}\f$-class represented by \c this. If \p x is not an
1596     // element of the semigroup, then the behaviour is undefined.
1597     // This member function involved computing most of the complete frame for
1598     // \c this, if it is not already known.
contains_NC(internal_const_reference x)1599     bool contains_NC(internal_const_reference x) {
1600       Lambda()(_tmp_lambda_value, this->to_external_const(x));
1601       Rho()(_tmp_rho_value, this->to_external_const(x));
1602       LIBSEMIGROUPS_ASSERT(
1603           this->parent()->_lambda_orb.position(_tmp_lambda_value) != UNDEFINED);
1604       LIBSEMIGROUPS_ASSERT(this->parent()->_rho_orb.position(_tmp_rho_value)
1605                            != UNDEFINED);
1606       return contains_NC(
1607           x,
1608           this->parent()->_lambda_orb.position(_tmp_lambda_value),
1609           this->parent()->_rho_orb.position(_tmp_rho_value));
1610     }
1611 
1612     // Returns whether the element \p x belongs to this \f$\mathscr{D}\f$-class.
1613     //
1614     // Given an element \p x of the semigroup represented by \c parent, this
1615     // function returns whether \p x is an element of the
1616     // \f$\mathscr{D}\f$-class represented by \c this. If \p x is not an
1617     // element of the semigroup, then the behaviour is undefined. This overload
1618     // of DClass::contains_NC is provided in order to avoid recalculating the
1619     // rank of \p x when it is already known.
1620     // This member function involves computing most of the complete frame for
1621     // \c this, if it is not already known.
contains_NC(internal_const_reference x,size_t rank)1622     bool contains_NC(internal_const_reference x, size_t rank) {
1623       LIBSEMIGROUPS_ASSERT(this->parent()->InternalRank()(_rank_state, x)
1624                            == rank);
1625       return (rank == _rank && contains_NC(x));
1626     }
1627 
1628     // Returns whether the element \p x belongs to this
1629     // \f$\mathscr{D}\f$-class.
1630     //
1631     // Given an element \p x of the semigroup represented by \c parent, this
1632     // function returns whether \p x is an element of the
1633     // \f$\mathscr{D}\f$-class represented by \c this. If \p x is not an
1634     // element of the semigroup, then the behaviour is undefined. This overload
1635     // of DClass::contains_NC is provided in order to avoid recalculating the
1636     // rank, lambda value, and rho value of \p x when they are already known.
1637     // This member function involves computing most of the complete frame for
1638     // \c this, if it is not already known.
contains_NC(internal_const_reference x,size_t rank,lambda_orb_index_type lpos,rho_orb_index_type rpos)1639     bool contains_NC(internal_const_reference x,
1640                      size_t                   rank,
1641                      lambda_orb_index_type    lpos,
1642                      rho_orb_index_type       rpos) {
1643       LIBSEMIGROUPS_ASSERT(this->parent()->InternalRank()(_rank_state, x)
1644                            == rank);
1645       return (rank == _rank && contains_NC(x, lpos, rpos));
1646     }
1647 
1648     // Returns whether the element \p x belongs to this \f$\mathscr{D}\f$-class.
1649     //
1650     // Given an element \p x of the semigroup represented by \c parent, this
1651     // function returns whether \p x is an element of the
1652     // \f$\mathscr{D}\f$-class represented by \c this. If \p x is not an
1653     // element of the semigroup, then the behaviour is undefined. This overload
1654     // of DClass::contains_NC is provided in order to avoid recalculating the
1655     // lambda value and rho value of \p x  when they are already known.
1656     // This member function involves computing most of the complete frame for
1657     // \c this, if it is not already known.
1658     virtual bool contains_NC(internal_const_reference x,
1659                              lambda_orb_index_type    lpos,
1660                              rho_orb_scc_index_type   rpos)
1661         = 0;
1662 
1663     virtual bool contains(const_reference        x,
1664                           lambda_orb_index_type  lpos,
1665                           rho_orb_scc_index_type rpos)
1666         = 0;
1667 
1668     ////////////////////////////////////////////////////////////////////////
1669     // DClass - accessor member functions - protected
1670     ////////////////////////////////////////////////////////////////////////
1671 
number_of_left_reps_NC() const1672     size_t number_of_left_reps_NC() const noexcept {
1673       return _left_reps.size();
1674     }
1675 
number_of_right_reps_NC() const1676     size_t number_of_right_reps_NC() const noexcept {
1677       return _right_reps.size();
1678     }
1679 
size_H_class_NC() const1680     size_t size_H_class_NC() const noexcept {
1681       return _H_class.size();
1682     }
1683 
push_left_mult(internal_const_reference x)1684     void push_left_mult(internal_const_reference x) {
1685       _left_mults.push_back(this->internal_copy(x));
1686 #ifdef LIBSEMIGROUPS_DEBUG
1687       PoolGuard             cg1(_parent->element_pool());
1688       PoolGuard             cg2(_parent->element_pool());
1689       internal_element_type tmp1 = cg1.get();
1690       internal_element_type tmp2 = cg2.get();
1691       if (_left_reps.size() >= _left_mults.size()) {
1692         Product()(this->to_external(tmp1),
1693                   this->to_external_const(_rep),
1694                   this->to_external_const(x));
1695         LIBSEMIGROUPS_ASSERT(OneParamLambda()(this->to_external(tmp1))
1696                              == OneParamLambda()(this->to_external_const(
1697                                  _left_reps[_left_mults.size() - 1])));
1698       }
1699       if (_left_mults_inv.size() >= _left_mults.size()) {
1700         Product()(this->to_external(tmp1),
1701                   this->to_external_const(_rep),
1702                   this->to_external_const(x));
1703         Product()(this->to_external(tmp2),
1704                   this->to_external(tmp1),
1705                   this->to_external(_left_mults_inv[_left_mults.size() - 1]));
1706         LIBSEMIGROUPS_ASSERT(OneParamLambda()(this->to_external_const(_rep))
1707                              == OneParamLambda()(this->to_external(tmp2)));
1708       }
1709 #endif
1710     }
1711 
push_left_mult_inv(internal_const_reference x)1712     void push_left_mult_inv(internal_const_reference x) {
1713       _left_mults_inv.push_back(this->internal_copy(x));
1714 #ifdef LIBSEMIGROUPS_DEBUG
1715       PoolGuard             cg1(_parent->element_pool());
1716       PoolGuard             cg2(_parent->element_pool());
1717       internal_element_type tmp1 = cg1.get();
1718       internal_element_type tmp2 = cg2.get();
1719       if (_left_reps.size() >= _left_mults_inv.size()) {
1720         Product()(this->to_external(tmp1),
1721                   this->to_external(_left_reps[_left_mults.size() - 1]),
1722                   this->to_external_const(x));
1723         LIBSEMIGROUPS_ASSERT(OneParamLambda()(this->to_external_const(_rep))
1724                              == OneParamLambda()(this->to_external(tmp1)));
1725       }
1726       if (_left_mults.size() >= _left_mults_inv.size()) {
1727         Product()(this->to_external(tmp1),
1728                   this->to_external_const(_rep),
1729                   this->to_external(_left_mults[_left_mults_inv.size() - 1]));
1730         Product()(this->to_external(tmp2),
1731                   this->to_external(tmp1),
1732                   this->to_external_const(x));
1733         LIBSEMIGROUPS_ASSERT(OneParamLambda()(this->to_external_const(_rep))
1734                              == OneParamLambda()(this->to_external(tmp2)));
1735       }
1736 #endif
1737     }
1738 
push_right_mult(internal_const_reference x)1739     void push_right_mult(internal_const_reference x) {
1740       _right_mults.push_back(this->internal_copy(x));
1741 #ifdef LIBSEMIGROUPS_DEBUG
1742       PoolGuard             cg1(_parent->element_pool());
1743       PoolGuard             cg2(_parent->element_pool());
1744       internal_element_type tmp1 = cg1.get();
1745       internal_element_type tmp2 = cg2.get();
1746       if (_right_reps.size() >= _right_mults.size()) {
1747         Product()(this->to_external(tmp1),
1748                   this->to_external_const(x),
1749                   this->to_external_const(_rep));
1750         LIBSEMIGROUPS_ASSERT(OneParamRho()(this->to_external(tmp1))
1751                              == OneParamRho()(this->to_external_const(
1752                                  _right_reps[_right_mults.size() - 1])));
1753       }
1754       if (_right_mults_inv.size() >= _right_mults.size()) {
1755         Product()(this->to_external(tmp1),
1756                   this->to_external(_right_mults_inv[_right_mults.size() - 1]),
1757                   this->to_external_const(x));
1758         Product()(this->to_external(tmp2),
1759                   this->to_external(tmp1),
1760                   this->to_external_const(_rep));
1761         LIBSEMIGROUPS_ASSERT(OneParamRho()(this->to_external_const(_rep))
1762                              == OneParamRho()(this->to_external(tmp2)));
1763       }
1764 #endif
1765     }
1766 
push_right_mult_inv(internal_const_reference x)1767     void push_right_mult_inv(internal_const_reference x) {
1768       _right_mults_inv.push_back(this->internal_copy(x));
1769 #ifdef LIBSEMIGROUPS_DEBUG
1770       PoolGuard             cg1(_parent->element_pool());
1771       PoolGuard             cg2(_parent->element_pool());
1772       internal_element_type tmp1 = cg1.get();
1773       internal_element_type tmp2 = cg2.get();
1774       if (_right_reps.size() >= _right_mults_inv.size()) {
1775         Product()(this->to_external(tmp1),
1776                   this->to_external_const(x),
1777                   this->to_external(_right_reps[_right_mults.size() - 1]));
1778         LIBSEMIGROUPS_ASSERT(OneParamRho()(this->to_external_const(_rep))
1779                              == OneParamRho()(this->to_external(tmp1)));
1780       }
1781       if (_right_mults.size() >= _right_mults_inv.size()) {
1782         Product()(this->to_external(tmp1),
1783                   this->to_external_const(x),
1784                   this->to_external(_right_mults[_right_mults_inv.size() - 1]));
1785         Product()(this->to_external(tmp2),
1786                   this->to_external(tmp1),
1787                   this->to_external_const(_rep));
1788         LIBSEMIGROUPS_ASSERT(OneParamRho()(this->to_external_const(_rep))
1789                              == OneParamRho()(this->to_external(tmp2)));
1790       }
1791 #endif
1792     }
1793 
push_left_rep(internal_const_reference x)1794     void push_left_rep(internal_const_reference x) {
1795       _left_reps.push_back(this->internal_copy(x));
1796 #ifdef LIBSEMIGROUPS_DEBUG
1797       PoolGuard             cg1(_parent->element_pool());
1798       internal_element_type tmp = cg1.get();
1799       if (_left_mults.size() >= _left_reps.size()) {
1800         Product()(this->to_external(tmp),
1801                   this->to_external_const(_rep),
1802                   this->to_external(_left_mults[_left_reps.size() - 1]));
1803         LIBSEMIGROUPS_ASSERT(OneParamLambda()(this->to_external(tmp))
1804                              == OneParamLambda()(this->to_external_const(x)));
1805       }
1806       if (_left_mults_inv.size() >= _left_reps.size()) {
1807         Product()(this->to_external(tmp),
1808                   this->to_external_const(x),
1809                   this->to_external(_left_mults_inv[_left_reps.size() - 1]));
1810         LIBSEMIGROUPS_ASSERT(OneParamLambda()(this->to_external_const(_rep))
1811                              == OneParamLambda()(this->to_external(tmp)));
1812       }
1813 #endif
1814     }
1815 
push_right_rep(internal_const_reference x)1816     void push_right_rep(internal_const_reference x) {
1817       _right_reps.push_back(this->internal_copy(x));
1818 #ifdef LIBSEMIGROUPS_DEBUG
1819       PoolGuard             cg1(_parent->element_pool());
1820       internal_element_type tmp = cg1.get();
1821       if (_right_mults.size() >= _right_reps.size()) {
1822         Product()(this->to_external(tmp),
1823                   this->to_external(_right_mults[_right_reps.size() - 1]),
1824                   this->to_external_const(_rep));
1825         LIBSEMIGROUPS_ASSERT(OneParamRho()(this->to_external(tmp))
1826                              == OneParamRho()(this->to_external_const(x)));
1827       }
1828       if (_right_mults_inv.size() >= _right_reps.size()) {
1829         Product()(this->to_external(tmp),
1830                   this->to_external(_right_mults_inv[_right_reps.size() - 1]),
1831                   this->to_external_const(x));
1832         LIBSEMIGROUPS_ASSERT(OneParamRho()(this->to_external_const(_rep))
1833                              == OneParamRho()(this->to_external(tmp)));
1834       }
1835 #endif
1836     }
1837 
class_computed() const1838     bool class_computed() const noexcept {
1839       return _class_computed;
1840     }
1841 
mults_computed() const1842     bool mults_computed() const noexcept {
1843       return _mults_computed;
1844     }
1845 
reps_computed() const1846     bool reps_computed() const noexcept {
1847       return _reps_computed;
1848     }
1849 
H_class_computed() const1850     bool H_class_computed() const noexcept {
1851       return _H_class_computed;
1852     }
1853 
set_class_computed(bool x)1854     void set_class_computed(bool x) noexcept {
1855       _class_computed = x;
1856     }
1857 
set_mults_computed(bool x)1858     void set_mults_computed(bool x) noexcept {
1859       _mults_computed = x;
1860     }
1861 
set_reps_computed(bool x)1862     void set_reps_computed(bool x) noexcept {
1863       _reps_computed = x;
1864     }
1865 
set_H_class_computed(bool x)1866     void set_H_class_computed(bool x) noexcept {
1867       _H_class_computed = x;
1868     }
1869 
parent() const1870     Konieczny* parent() const noexcept {
1871       return _parent;
1872     }
1873 
1874     // Watch out! Doesn't copy its argument
push_back_H_class(internal_element_type x)1875     void push_back_H_class(internal_element_type x) {
1876       _H_class.push_back(x);
1877     }
1878 
H_class()1879     std::vector<internal_element_type>& H_class() {
1880       return _H_class;
1881     }
1882 
tmp_lambda_value() const1883     lambda_value_type& tmp_lambda_value() const noexcept {
1884       return _tmp_lambda_value;
1885     }
1886 
tmp_rho_value() const1887     rho_value_type& tmp_rho_value() const noexcept {
1888       return _tmp_rho_value;
1889     }
1890 
rank() const1891     rank_type rank() const noexcept {
1892       return _rank;
1893     }
1894 
internal_set() const1895     internal_set_type& internal_set() const noexcept {
1896       return _tmp_internal_set;
1897     }
1898 
internal_vec() const1899     std::vector<internal_element_type>& internal_vec() const noexcept {
1900       return _tmp_internal_vec;
1901     }
1902 
unsafe_rep()1903     internal_reference unsafe_rep() noexcept {
1904       return _rep;
1905     }
1906 
left_indices()1907     std::vector<lambda_orb_index_type>& left_indices() {
1908       return _left_indices;
1909     }
1910 
right_indices()1911     std::vector<rho_orb_index_type>& right_indices() {
1912       return _right_indices;
1913     }
1914 
1915    protected:
1916     ////////////////////////////////////////////////////////////////////////
1917     // DClass - index iterators - protected
1918     ////////////////////////////////////////////////////////////////////////
1919 
1920     typename std::vector<left_indices_index_type>::const_iterator
cbegin_left_indices()1921     cbegin_left_indices() {
1922       compute_left_indices();
1923       return _left_indices.cbegin();
1924     }
1925 
1926     typename std::vector<left_indices_index_type>::const_iterator
cend_left_indices()1927     cend_left_indices() {
1928       compute_left_indices();
1929       return _left_indices.cend();
1930     }
1931 
1932     typename std::vector<right_indices_index_type>::const_iterator
cbegin_right_indices()1933     cbegin_right_indices() {
1934       compute_right_indices();
1935       return _right_indices.cbegin();
1936     }
1937 
1938     typename std::vector<right_indices_index_type>::const_iterator
cend_right_indices()1939     cend_right_indices() {
1940       compute_right_indices();
1941       return _right_indices.cend();
1942     }
1943 
1944    private:
1945     ////////////////////////////////////////////////////////////////////////
1946     // DClass - member functions - private
1947     ////////////////////////////////////////////////////////////////////////
1948 
1949     // Returns a set of representatives of L- or R-classes covered by \c this.
1950     //
1951     // The \f$\mathscr{D}\f$-classes of the parent semigroup are enumerated
1952     // either by finding representatives of all L-classes or all R-classes. This
1953     // member function returns the representatives obtainable by multipliying
1954     // the representatives of \c this by generators on either the left or right.
covering_reps()1955     std::vector<internal_element_type>& covering_reps() {
1956       init();
1957       _tmp_internal_vec.clear();
1958       _tmp_internal_set.clear();
1959       // TODO(later): how to best decide which side to calculate? One is often
1960       // faster
1961       if (_parent->_lambda_orb.size() < _parent->_rho_orb.size()) {
1962         PoolGuard             cg(_parent->element_pool());
1963         internal_element_type tmp = cg.get();
1964         for (left_indices_index_type i = 0; i < _left_reps.size(); ++i) {
1965           internal_element_type w = _left_reps[i];
1966           size_t                j = 0;
1967           for (auto it = _parent->cbegin_internal_generators();
1968                it < _parent->cend_internal_generators();
1969                ++it, ++j) {
1970             Product()(this->to_external(tmp),
1971                       this->to_external_const(w),
1972                       this->to_external_const(*it));
1973             // TODO(later) this is fragile
1974             lambda_orb_index_type lpos
1975                 = _parent->_lambda_orb.digraph().neighbor(_left_indices[i], j);
1976             Rho()(_tmp_rho_value, this->to_external_const(tmp));
1977             rho_orb_index_type rpos
1978                 = _parent->_rho_orb.position(_tmp_rho_value);
1979             if (!contains_NC(tmp, lpos, rpos)) {
1980               if (_tmp_internal_set.find(tmp) == _tmp_internal_set.end()) {
1981                 internal_element_type x = this->internal_copy(tmp);
1982                 _tmp_internal_set.insert(x);
1983                 _tmp_internal_vec.push_back(x);
1984               }
1985             }
1986           }
1987         }
1988       } else {
1989         PoolGuard             cg(_parent->element_pool());
1990         internal_element_type tmp = cg.get();
1991         for (right_indices_index_type i = 0; i < _right_reps.size(); ++i) {
1992           internal_element_type z = _right_reps[i];
1993           size_t                j = 0;
1994           for (auto it = _parent->cbegin_internal_generators();
1995                it < _parent->cend_internal_generators();
1996                ++it, ++j) {
1997             Product()(this->to_external(tmp),
1998                       this->to_external_const(*it),
1999                       this->to_external_const(z));
2000             // TODO(later) this is fragile
2001             rho_orb_index_type rpos
2002                 = _parent->_rho_orb.digraph().neighbor(_right_indices[i], j);
2003             Lambda()(_tmp_lambda_value, this->to_external_const(tmp));
2004             lambda_orb_index_type lpos
2005                 = _parent->_lambda_orb.position(_tmp_lambda_value);
2006             if (!contains_NC(tmp, lpos, rpos)) {
2007               if (_tmp_internal_set.find(tmp) == _tmp_internal_set.end()) {
2008                 internal_element_type x = this->internal_copy(tmp);
2009                 _tmp_internal_set.insert(x);
2010                 _tmp_internal_vec.push_back(x);
2011               }
2012             }
2013           }
2014         }
2015       }
2016       return _tmp_internal_vec;
2017     }
2018 
2019     ////////////////////////////////////////////////////////////////////////
2020     // DClass - data - private
2021     ////////////////////////////////////////////////////////////////////////
2022     bool                                       _class_computed;
2023     std::vector<internal_element_type>         _H_class;
2024     bool                                       _H_class_computed;
2025     bool                                       _is_regular_D_class;
2026     std::vector<lambda_orb_index_type>         _left_indices;
2027     std::vector<internal_element_type>         _left_mults;
2028     std::vector<internal_element_type>         _left_mults_inv;
2029     std::vector<internal_element_type>         _left_reps;
2030     bool                                       _mults_computed;
2031     Konieczny*                                 _parent;
2032     rank_type                                  _rank;
2033     internal_element_type                      _rep;
2034     bool                                       _reps_computed;
2035     std::vector<rho_orb_index_type>            _right_indices;
2036     std::vector<internal_element_type>         _right_mults;
2037     std::vector<internal_element_type>         _right_mults_inv;
2038     std::vector<internal_element_type>         _right_reps;
2039     mutable internal_set_type                  _tmp_internal_set;
2040     mutable std::vector<internal_element_type> _tmp_internal_vec;
2041     mutable lambda_value_type                  _tmp_lambda_value;
2042     mutable rho_value_type                     _tmp_rho_value;
2043   };
2044 
2045   /////////////////////////////////////////////////////////////////////////////
2046   // RegularDClass
2047   /////////////////////////////////////////////////////////////////////////////
2048 
2049   // Defined in ``konieczny.hpp``.
2050   //
2051   // The nested class Konieczny::RegularDClass inherits from DClass and
2052   // represents a regular \f$\mathscr{D}\f$-class via a complete frame as
2053   // computed in %Konieczny's algorithm. See [here] for more details.
2054   //
2055   // A RegularDClass cannot be constructed directly, but may be returned by
2056   // member functions of the parent semigroup.
2057   //
2058   // \sa Konieczny, DClass, and NonRegularDClass
2059   //
2060   // [here]:  https://link.springer.com/article/10.1007/BF02573672
2061   template <typename TElementType, typename TTraits>
2062   class Konieczny<TElementType, TTraits>::RegularDClass final
2063       : public Konieczny<TElementType, TTraits>::DClass {
2064     // Konieczny is only a friend of RegularDClass so it can call the private
2065     // constructor
2066     friend class Konieczny<TElementType, TTraits>;
2067     // NonRegularDClass is a friend of RegularDClass so it can access private
2068     // iterators
2069     friend class Konieczny<TElementType, TTraits>::NonRegularDClass;
2070 
2071    private:
2072     ////////////////////////////////////////////////////////////////////////
2073     // RegularDClass - aliases - private
2074     ////////////////////////////////////////////////////////////////////////
2075     using left_indices_index_type =
2076         typename RegularDClass::konieczny_type::left_indices_index_type;
2077     using right_indices_index_type =
2078         typename RegularDClass::konieczny_type::right_indices_index_type;
2079     using const_index_iterator =
2080         typename std::vector<lambda_orb_index_type>::const_iterator;
2081     using const_internal_iterator =
2082         typename std::vector<internal_element_type>::const_iterator;
2083 
2084     ////////////////////////////////////////////////////////////////////////
2085     // RegularDClass - constructor - private
2086     ////////////////////////////////////////////////////////////////////////
2087 
2088     // Deleted.
2089     //
2090     // RegularDClass does not support a copy constructor.
2091     RegularDClass(RegularDClass const&) = delete;
2092 
2093     // Deleted.
2094     //
2095     // The RegularDClass class does not support a copy assignment operator to
2096     // avoid accidental copying.
2097     RegularDClass& operator=(RegularDClass const&) = delete;
2098 
2099     // Deleted.
2100     //
2101     // RegularDClass does not support a move constructor.
2102     RegularDClass(RegularDClass&&) = delete;
2103 
2104     // Deleted.
2105     //
2106     // RegularDClass does not support a move assignment operator.
2107     RegularDClass& operator=(RegularDClass&&) = delete;
2108 
2109     // Construct from a pointer to a Konieczny object and an element of the
2110     // semigroup represented by the Konieczny object.
2111     //
2112     // The representative \p rep is not copied by the constructor, and so must
2113     // not be modified by the user after constructing the RegularDClass. The
2114     // behaviour of RegularDClass when \p rep is not an element of the
2115     // semigroup represented by \p parent is undefined.
2116     //
2117     // \param parent a pointer to the Konieczny object representing the
2118     // semigroup of which \c this represents a \f$\mathscr{D}\f$-class.
2119     //
2120     // \param rep a regular element of the semigroup represented by \p parent.
2121     //
2122     // \throws LibsemigroupsException if \p rep is an element of the semigroup
2123     // represented by \p parent but is not regular.
RegularDClass(Konieczny * parent,internal_reference rep)2124     RegularDClass(Konieczny* parent, internal_reference rep)
2125         : Konieczny::DClass(parent, rep),
2126           _H_gens(),
2127           _H_gens_computed(false),
2128           _idem_reps_computed(false),
2129           _lambda_index_positions(),
2130           _left_idem_reps(),
2131           _left_indices_computed(false),
2132           _rho_index_positions(),
2133           _right_idem_reps(),
2134           _right_indices_computed(false) {
2135       if (!parent->is_regular_element_NC(rep)) {
2136         LIBSEMIGROUPS_EXCEPTION("the representative given should be regular");
2137       }
2138       parent->make_idem(this->unsafe_rep());
2139       init();
2140 #ifdef LIBSEMIGROUPS_DEBUG
2141       PoolGuard cg(this->parent()->element_pool());
2142       auto      tmp = cg.get();
2143       Product()(this->to_external(tmp), this->rep(), this->rep());
2144       LIBSEMIGROUPS_ASSERT(EqualTo()(this->to_external(tmp), this->rep()));
2145 #endif
2146     }
2147 
2148    public:
2149     ////////////////////////////////////////////////////////////////////////
2150     // RegularDClass - destructor - public
2151     ////////////////////////////////////////////////////////////////////////
~RegularDClass()2152     virtual ~RegularDClass() {
2153       // _H_gens is contained in _H_class which is freed in ~DClass
2154       InternalVecFree()(_left_idem_reps);
2155       InternalVecFree()(_right_idem_reps);
2156     }
2157 
2158     ////////////////////////////////////////////////////////////////////////
2159     // RegularDClass - member functions - public
2160     ////////////////////////////////////////////////////////////////////////
2161     using DClass::contains;
2162 
2163     // \copydoc DClass::contains
contains(const_reference x,lambda_orb_index_type lpos,rho_orb_index_type rpos)2164     bool contains(const_reference       x,
2165                   lambda_orb_index_type lpos,
2166                   rho_orb_index_type    rpos) override {
2167       LIBSEMIGROUPS_ASSERT(
2168           this->parent()->_lambda_orb.position(OneParamLambda()(x)) == lpos);
2169       LIBSEMIGROUPS_ASSERT(this->parent()->_rho_orb.position(OneParamRho()(x))
2170                            == rpos);
2171       auto l_it = _lambda_index_positions.find(lpos);
2172       auto r_it = _rho_index_positions.find(rpos);
2173       if (l_it == _lambda_index_positions.end()
2174           || r_it == _rho_index_positions.end()) {
2175         return false;
2176       }
2177       PoolGuard cg1(this->parent()->element_pool());
2178       PoolGuard cg2(this->parent()->element_pool());
2179       auto      tmp1 = cg1.get();
2180       auto      tmp2 = cg2.get();
2181       Product()(this->to_external(tmp1),
2182                 x,
2183                 this->to_external_const(this->left_mults_inv(l_it->second)));
2184       Product()(this->to_external(tmp2),
2185                 this->to_external_const(this->right_mults_inv(r_it->second)),
2186                 this->to_external(tmp1));
2187       std::sort(this->H_class().begin(), this->H_class().end(), InternalLess());
2188       return std::binary_search(this->H_class().cbegin(),
2189                                 this->H_class().cend(),
2190                                 tmp2,
2191                                 InternalLess());
2192     }
2193 
number_of_idempotents() const2194     size_t number_of_idempotents() const override {
2195       size_t count = 0;
2196       for (auto it = cbegin_left_idem_reps(); it < cend_left_idem_reps();
2197            ++it) {
2198         for (auto it2 = cbegin_right_idem_reps(); it2 < cend_right_idem_reps();
2199              ++it2) {
2200           if (this->parent()->is_group_index(*it2, *it)) {
2201             count++;
2202           }
2203         }
2204       }
2205       LIBSEMIGROUPS_ASSERT(count > 0);
2206       return count;
2207     }
2208 
2209    private:
2210     ////////////////////////////////////////////////////////////////////////
2211     // RegularDClass - containment - private
2212     ////////////////////////////////////////////////////////////////////////
2213     using DClass::contains_NC;
2214 
2215     // \copydoc DClass::contains_NC
contains_NC(internal_const_reference x,lambda_orb_index_type lpos,rho_orb_index_type rpos)2216     bool contains_NC(internal_const_reference x,
2217                      lambda_orb_index_type    lpos,
2218                      rho_orb_index_type       rpos) override {
2219       // the next line is to suppress compiler warnings
2220       (void) x;
2221       LIBSEMIGROUPS_ASSERT(this->parent()->_lambda_orb.position(
2222                                OneParamLambda()(this->to_external_const(x)))
2223                            == lpos);
2224       LIBSEMIGROUPS_ASSERT(this->parent()->_rho_orb.position(
2225                                OneParamRho()(this->to_external_const(x)))
2226                            == rpos);
2227       compute_left_indices();
2228       compute_right_indices();
2229       return (_lambda_index_positions.find(lpos)
2230               != _lambda_index_positions.end())
2231              && (_rho_index_positions.find(rpos) != _rho_index_positions.end());
2232     }
2233 
2234     // Returns the indices of the L- and R-classes of \c this that \p bm is in.
2235     //
2236     // Returns the indices of the L- and R-classes of \c this that \p bm is in,
2237     // unless bm is not in \c this, in which case returns the pair (UNDEFINED,
2238     // UNDEFINED). Requires computing part of the complete frame of \c this.
2239     std::pair<lambda_orb_index_type, rho_orb_index_type>
index_positions(const_reference bm)2240     index_positions(const_reference bm) {
2241       compute_left_indices();
2242       compute_right_indices();
2243       Lambda()(this->tmp_lambda_value(), bm);
2244       auto l_it = _lambda_index_positions.find(
2245           this->parent()->_lambda_orb.position(this->tmp_lambda_value()));
2246       if (l_it != _lambda_index_positions.end()) {
2247         Rho()(this->tmp_rho_value(), bm);
2248         auto r_it = _rho_index_positions.find(
2249             this->parent()->_rho_orb.position(this->tmp_rho_value()));
2250         if (r_it != _rho_index_positions.end()) {
2251           return std::make_pair(l_it->second, r_it->second);
2252         }
2253       }
2254       return std::make_pair(UNDEFINED, UNDEFINED);
2255     }
2256 
2257     ////////////////////////////////////////////////////////////////////////
2258     // RegularDClass - initialisation member functions - private
2259     ////////////////////////////////////////////////////////////////////////
2260 
compute_left_indices()2261     void compute_left_indices() override {
2262       if (_left_indices_computed) {
2263         return;
2264       }
2265 
2266       Lambda()(this->tmp_lambda_value(), this->rep());
2267       lambda_orb_index_type lval_pos
2268           = this->parent()->_lambda_orb.position(this->tmp_lambda_value());
2269       lambda_orb_scc_index_type lval_scc_id
2270           = this->parent()->_lambda_orb.digraph().scc_id(lval_pos);
2271       for (auto it
2272            = this->parent()->_lambda_orb.digraph().cbegin_scc(lval_scc_id);
2273            it < this->parent()->_lambda_orb.digraph().cend_scc(lval_scc_id);
2274            ++it) {
2275         _lambda_index_positions.emplace(*it, this->left_indices().size());
2276         this->left_indices().push_back(*it);
2277         // TODO(later) prove this works
2278 #ifdef LIBSEMIGROUPS_DEBUG
2279         PoolGuard cg(this->parent()->element_pool());
2280         PoolGuard cg2(this->parent()->element_pool());
2281         auto      tmp  = cg.get();
2282         auto      tmp2 = cg2.get();
2283         Product()(this->to_external(tmp),
2284                   this->parent()->_lambda_orb.multiplier_to_scc_root(lval_pos),
2285                   this->parent()->_lambda_orb.multiplier_from_scc_root(*it));
2286 
2287         Product()(this->to_external(tmp2), this->rep(), this->to_external(tmp));
2288         LIBSEMIGROUPS_ASSERT(this->parent()->get_lambda_group_index(tmp2)
2289                              != UNDEFINED);
2290 #endif
2291       }
2292 #ifdef LIBSEMIGROUPS_DEBUG
2293       for (lambda_orb_index_type i : this->left_indices()) {
2294         LIBSEMIGROUPS_ASSERT(i < this->parent()->_lambda_orb.size());
2295       }
2296 #endif
2297       this->_left_indices_computed = true;
2298     }
2299 
compute_right_indices()2300     void compute_right_indices() override {
2301       if (_right_indices_computed) {
2302         return;
2303       }
2304 
2305       Rho()(this->tmp_rho_value(), this->rep());
2306       rho_orb_index_type rval_pos
2307           = this->parent()->_rho_orb.position(this->tmp_rho_value());
2308       rho_orb_scc_index_type rval_scc_id
2309           = this->parent()->_rho_orb.digraph().scc_id(rval_pos);
2310       for (auto it = this->parent()->_rho_orb.digraph().cbegin_scc(rval_scc_id);
2311            it < this->parent()->_rho_orb.digraph().cend_scc(rval_scc_id);
2312            it++) {
2313         _rho_index_positions.emplace(*it, this->right_indices().size());
2314         this->right_indices().push_back(*it);
2315 #ifdef LIBSEMIGROUPS_DEBUG
2316         PoolGuard cg(this->parent()->element_pool());
2317         PoolGuard cg2(this->parent()->element_pool());
2318         auto      tmp  = cg.get();
2319         auto      tmp2 = cg2.get();
2320         Product()(this->to_external(tmp),
2321                   this->parent()->_rho_orb.multiplier_from_scc_root(*it),
2322                   this->parent()->_rho_orb.multiplier_to_scc_root(rval_pos));
2323 
2324         Product()(this->to_external(tmp2), this->to_external(tmp), this->rep());
2325         LIBSEMIGROUPS_ASSERT(this->parent()->get_lambda_group_index(tmp2)
2326                              != UNDEFINED);
2327 #endif
2328       }
2329 #ifdef LIBSEMIGROUPS_DEBUG
2330       for (rho_orb_index_type i : this->right_indices()) {
2331         LIBSEMIGROUPS_ASSERT(i < this->parent()->_rho_orb.size());
2332       }
2333 #endif
2334       this->_right_indices_computed = true;
2335     }
2336 
compute_left_mults()2337     void compute_left_mults() override {
2338       compute_mults();
2339     }
2340 
compute_left_mults_inv()2341     void compute_left_mults_inv() override {
2342       compute_mults();
2343     }
2344 
compute_left_reps()2345     void compute_left_reps() override {
2346       compute_reps();
2347     }
2348 
compute_right_mults()2349     void compute_right_mults() override {
2350       compute_mults();
2351     }
2352 
compute_right_mults_inv()2353     void compute_right_mults_inv() override {
2354       compute_reps();
2355     }
2356 
compute_right_reps()2357     void compute_right_reps() override {
2358       compute_reps();
2359     }
2360 
compute_mults()2361     void compute_mults() {
2362       if (this->mults_computed()) {
2363         return;
2364       }
2365 
2366       Lambda()(this->tmp_lambda_value(), this->rep());
2367       Rho()(this->tmp_rho_value(), this->rep());
2368       lambda_value_type&    lval = this->tmp_lambda_value();
2369       lambda_orb_index_type lval_pos
2370           = this->parent()->_lambda_orb.position(lval);
2371       rho_value_type     rval     = this->tmp_rho_value();
2372       rho_orb_index_type rval_pos = this->parent()->_rho_orb.position(rval);
2373 
2374       PoolGuard cg(this->parent()->element_pool());
2375       auto      tmp = cg.get();
2376 
2377       for (auto lidx_it = this->cbegin_left_indices();
2378            lidx_it < this->cend_left_indices();
2379            ++lidx_it) {
2380         Product()(
2381             this->to_external(tmp),
2382             this->parent()->_lambda_orb.multiplier_to_scc_root(lval_pos),
2383             this->parent()->_lambda_orb.multiplier_from_scc_root(*lidx_it));
2384         this->push_left_mult(tmp);
2385         Product()(
2386             this->to_external(tmp),
2387             this->parent()->_lambda_orb.multiplier_to_scc_root(*lidx_it),
2388             this->parent()->_lambda_orb.multiplier_from_scc_root(lval_pos));
2389         this->push_left_mult_inv(tmp);
2390       }
2391 
2392       for (auto ridx_it = this->cbegin_right_indices();
2393            ridx_it < this->cend_right_indices();
2394            ++ridx_it) {
2395         // push_right_mult and push_right_mult_inv use tmp_element and
2396         // tmp_element2
2397         Product()(this->to_external(tmp),
2398                   this->parent()->_rho_orb.multiplier_from_scc_root(*ridx_it),
2399                   this->parent()->_rho_orb.multiplier_to_scc_root(rval_pos));
2400         this->push_right_mult(tmp);
2401         Product()(this->to_external(tmp),
2402                   this->parent()->_rho_orb.multiplier_from_scc_root(rval_pos),
2403                   this->parent()->_rho_orb.multiplier_to_scc_root(*ridx_it));
2404         this->push_right_mult_inv(tmp);
2405       }
2406       this->set_mults_computed(true);
2407     }
2408 
compute_reps()2409     void compute_reps() {
2410       if (this->reps_computed()) {
2411         return;
2412       }
2413 
2414       compute_mults();
2415 
2416       PoolGuard cg(this->parent()->element_pool());
2417       auto      tmp = cg.get();
2418 
2419       for (auto it = this->cbegin_left_mults(); it < this->cend_left_mults();
2420            ++it) {
2421         Product()(
2422             this->to_external(tmp), this->rep(), this->to_external_const(*it));
2423         this->push_left_rep(tmp);
2424       }
2425 
2426       for (auto it = this->cbegin_right_mults(); it < this->cend_right_mults();
2427            ++it) {
2428         Product()(
2429             this->to_external(tmp), this->to_external_const(*it), this->rep());
2430         this->push_right_rep(tmp);
2431       }
2432       this->set_reps_computed(true);
2433     }
2434 
compute_H_gens()2435     void compute_H_gens() {
2436       if (_H_gens_computed) {
2437         return;
2438       }
2439 
2440       // internal vec represents right inverses
2441       this->internal_vec().clear();
2442 
2443       PoolGuard cg1(this->parent()->element_pool());
2444       PoolGuard cg2(this->parent()->element_pool());
2445       PoolGuard cg3(this->parent()->element_pool());
2446       auto      tmp1 = cg1.get();
2447       auto      tmp2 = cg2.get();
2448       auto      tmp3 = cg3.get();
2449 
2450       for (auto lrep_it = this->cbegin_left_reps();
2451            lrep_it < this->cend_left_reps();
2452            ++lrep_it) {
2453         LIBSEMIGROUPS_ASSERT(OneParamRho()(this->to_external_const(*lrep_it))
2454                              == OneParamRho()(this->rep()));
2455         rho_orb_index_type k = this->parent()->get_rho_group_index(*lrep_it);
2456         LIBSEMIGROUPS_ASSERT(k != UNDEFINED);
2457         LIBSEMIGROUPS_ASSERT(_rho_index_positions.find(k)
2458                              != _rho_index_positions.end());
2459         right_indices_index_type j = _rho_index_positions.at(k);
2460 
2461         // for p a left rep and q an appropriate right rep,
2462         // find the product of q with the inverse of pq in H_rep
2463         Product()(this->to_external(tmp1),
2464                   this->to_external_const(*lrep_it),
2465                   this->to_external_const(this->cbegin_right_reps()[j]));
2466 
2467         this->parent()->group_inverse(
2468             tmp3, this->to_internal_const(this->rep()), tmp1);
2469         Product()(this->to_external(tmp2),
2470                   this->to_external_const(this->cbegin_right_reps()[j]),
2471                   this->to_external_const(tmp3));
2472         this->internal_vec().push_back(this->internal_copy(tmp2));
2473       }
2474 
2475       this->internal_set().clear();
2476       for (size_t i = 0; i < this->left_indices().size(); ++i) {
2477         for (internal_const_reference g : this->parent()->_gens) {
2478           Product()(this->to_external(tmp1),
2479                     this->to_external_const(this->cbegin_left_reps()[i]),
2480                     this->to_external_const(g));
2481           Lambda()(this->tmp_lambda_value(), this->to_external(tmp1));
2482           lambda_orb_index_type lpos
2483               = this->parent()->_lambda_orb.position(this->tmp_lambda_value());
2484           LIBSEMIGROUPS_ASSERT(lpos != UNDEFINED);
2485           if (_lambda_index_positions.find(lpos)
2486               != _lambda_index_positions.end()) {
2487             left_indices_index_type j = _lambda_index_positions.at(lpos);
2488             // TODO(later): this j and internal_vec are a bit mysterious
2489             Product()(this->to_external(tmp2),
2490                       this->to_external(tmp1),
2491                       this->to_external_const(this->internal_vec()[j]));
2492             if (this->internal_set().find(tmp2) == this->internal_set().end()) {
2493               internal_element_type x = this->internal_copy(tmp2);
2494               this->internal_set().insert(x);
2495               _H_gens.push_back(x);
2496             }
2497           }
2498         }
2499       }
2500       InternalVecFree()(this->internal_vec());
2501       this->_H_gens_computed = true;
2502     }
2503 
compute_idem_reps()2504     void compute_idem_reps() {
2505       if (_idem_reps_computed) {
2506         return;
2507       }
2508       compute_left_indices();
2509       compute_right_indices();
2510 
2511       PoolGuard cg1(this->parent()->element_pool());
2512       PoolGuard cg2(this->parent()->element_pool());
2513       PoolGuard cg3(this->parent()->element_pool());
2514       auto      tmp1 = cg1.get();
2515       auto      tmp2 = cg2.get();
2516       auto      tmp3 = cg3.get();
2517 
2518       // TODO(later): use information from the looping through the left indices
2519       // in the loop through the right indices
2520       for (auto lmult_it = this->cbegin_left_mults();
2521            lmult_it < this->cend_left_mults();
2522            ++lmult_it) {
2523         Product()(this->to_external(tmp1),
2524                   this->rep(),
2525                   this->to_external_const(*lmult_it));
2526         rho_orb_index_type k = this->parent()->get_rho_group_index(tmp1);
2527         LIBSEMIGROUPS_ASSERT(k != UNDEFINED);
2528         LIBSEMIGROUPS_ASSERT(_rho_index_positions.find(k)
2529                              != _rho_index_positions.end());
2530         size_t rmult_pos = _rho_index_positions.at(k);
2531         Product()(
2532             this->to_external(tmp2),
2533             this->to_external_const(this->cbegin_right_mults()[rmult_pos]),
2534             this->to_external_const(tmp1));
2535         this->parent()->idem_in_H_class(tmp3, tmp2);
2536 
2537         _left_idem_reps.push_back(this->internal_copy(tmp3));
2538       }
2539 
2540       for (auto rmult_it = this->cbegin_right_mults();
2541            rmult_it < this->cend_right_mults();
2542            ++rmult_it) {
2543         Product()(this->to_external(tmp1),
2544                   this->to_external_const(*rmult_it),
2545                   this->rep());
2546         lambda_orb_index_type k = this->parent()->get_lambda_group_index(tmp1);
2547         LIBSEMIGROUPS_ASSERT(k != UNDEFINED);
2548         LIBSEMIGROUPS_ASSERT(_lambda_index_positions.find(k)
2549                              != _lambda_index_positions.end());
2550         size_t lmult_pos = _lambda_index_positions.at(k);
2551         Product()(
2552             this->to_external(tmp2),
2553             this->to_external(tmp1),
2554             this->to_external_const(this->cbegin_left_mults()[lmult_pos]));
2555 
2556         this->parent()->idem_in_H_class(tmp3, tmp2);
2557 
2558         _right_idem_reps.push_back(this->internal_copy(tmp3));
2559       }
2560       this->_idem_reps_computed = true;
2561     }
2562 
2563     // there should be some way of getting rid of this
compute_H_class()2564     void compute_H_class() override {
2565       if (this->H_class_computed()) {
2566         return;
2567       }
2568       compute_H_gens();
2569 
2570       LIBSEMIGROUPS_ASSERT(_H_gens.begin() != _H_gens.end());
2571 
2572       this->internal_set().clear();
2573 
2574       for (auto it = _H_gens.begin(); it < _H_gens.end(); ++it) {
2575         this->internal_set().insert(*it);
2576         this->push_back_H_class(*it);
2577       }
2578 
2579       PoolGuard cg(this->parent()->element_pool());
2580       auto      tmp = cg.get();
2581 
2582       for (size_t i = 0; i < this->size_H_class_NC(); ++i) {
2583         for (internal_const_reference g : _H_gens) {
2584           Product()(this->to_external(tmp),
2585                     this->to_external_const(this->H_class_NC(i)),
2586                     this->to_external_const(g));
2587           if (this->internal_set().find(tmp) == this->internal_set().end()) {
2588             internal_element_type x = this->internal_copy(tmp);
2589             this->internal_set().insert(x);
2590             this->push_back_H_class(x);
2591           }
2592         }
2593       }
2594       this->set_H_class_computed(true);
2595     }
2596 
init()2597     void init() override {
2598       if (this->class_computed()) {
2599         return;
2600       }
2601       compute_left_indices();
2602       compute_right_indices();
2603       compute_mults();
2604       compute_reps();
2605       compute_idem_reps();
2606       compute_H_gens();
2607       compute_H_class();
2608       this->set_class_computed(true);
2609     }
2610 
2611     ////////////////////////////////////////////////////////////////////////
2612     // RegularDClass - accessor member functions - private (friend NonRegular)
2613     ////////////////////////////////////////////////////////////////////////
cbegin_left_idem_reps() const2614     const_internal_iterator cbegin_left_idem_reps() const {
2615       LIBSEMIGROUPS_ASSERT(this->class_computed());
2616       return _left_idem_reps.cbegin();
2617     }
2618 
cend_left_idem_reps() const2619     const_internal_iterator cend_left_idem_reps() const {
2620       LIBSEMIGROUPS_ASSERT(this->class_computed());
2621       return _left_idem_reps.cend();
2622     }
2623 
cbegin_right_idem_reps() const2624     const_internal_iterator cbegin_right_idem_reps() const {
2625       LIBSEMIGROUPS_ASSERT(this->class_computed());
2626       return _right_idem_reps.cbegin();
2627     }
2628 
cend_right_idem_reps() const2629     const_internal_iterator cend_right_idem_reps() const {
2630       LIBSEMIGROUPS_ASSERT(this->class_computed());
2631       return _right_idem_reps.cend();
2632     }
2633 
2634     ////////////////////////////////////////////////////////////////////////
2635     // RegularDClass - data - private
2636     ////////////////////////////////////////////////////////////////////////
2637     std::vector<internal_element_type> _H_gens;
2638     bool                               _H_gens_computed;
2639     bool                               _idem_reps_computed;
2640     std::unordered_map<lambda_orb_index_type, left_indices_index_type>
2641                                        _lambda_index_positions;
2642     std::vector<internal_element_type> _left_idem_reps;
2643     bool                               _left_indices_computed;
2644     std::unordered_map<rho_orb_index_type, right_indices_index_type>
2645                                        _rho_index_positions;
2646     std::vector<internal_element_type> _right_idem_reps;
2647     bool                               _right_indices_computed;
2648   };
2649 
2650   /////////////////////////////////////////////////////////////////////////////
2651   // NonRegularDClass
2652   /////////////////////////////////////////////////////////////////////////////
2653 
2654   // Defined in ``konieczny.hpp``.
2655   //
2656   // The nested class Konieczny::NonRegularDClass inherits from DClass
2657   // and represents a regular \f$\mathscr{D}\f$-class via a complete frame as
2658   // computed in %Konieczny's algorithm. See [here] for more details.
2659   //
2660   // A NonRegularDClass is defined by a pointer to the corresponding
2661   // Konieczny object, together with a representative of the
2662   // \f$\mathscr{D}\f$-class.
2663   //
2664   // \sa Konieczny, DClass, and RegularDClass
2665   //
2666   // [here]:  https://link.springer.com/article/10.1007/BF02573672
2667   template <typename TElementType, typename TTraits>
2668   class Konieczny<TElementType, TTraits>::NonRegularDClass final
2669       : public Konieczny<TElementType, TTraits>::DClass {
2670     // Konieczny is only a friend of NonRegularDClass so it can call the private
2671     // constructor
2672     friend class Konieczny<TElementType, TTraits>;
2673 
2674    private:
2675     ////////////////////////////////////////////////////////////////////////
2676     // NonRegularDClass - aliases - private
2677     ////////////////////////////////////////////////////////////////////////
2678     using left_indices_index_type =
2679         typename NonRegularDClass::konieczny_type::left_indices_index_type;
2680     using right_indices_index_type =
2681         typename NonRegularDClass::konieczny_type::right_indices_index_type;
2682     using internal_set_type = typename DClass::internal_set_type;
2683 
2684     ////////////////////////////////////////////////////////////////////////
2685     // NonRegularDClass - constructor - private
2686     ////////////////////////////////////////////////////////////////////////
2687 
2688     // Deleted.
2689     //
2690     // NonRegularDClass does not support a copy constructor.
2691     NonRegularDClass(NonRegularDClass const&) = delete;
2692 
2693     // Deleted.
2694     //
2695     // The NonRegularDClass class does not support a copy assignment operator
2696     // to avoid accidental copying.
2697     NonRegularDClass& operator=(NonRegularDClass const&) = delete;
2698 
2699     // Deleted.
2700     //
2701     // NonRegularDClass does not support a move constructor.
2702     NonRegularDClass(NonRegularDClass&&) = delete;
2703 
2704     // Deleted.
2705     //
2706     // NonRegularDClass does not support a move assignment operator.
2707     NonRegularDClass& operator=(NonRegularDClass&&) = delete;
2708 
2709     // Construct from a pointer to a Konieczny object and an element of the
2710     // semigroup represented by the Konieczny object.
2711     //
2712     // A NonRegularDClass cannot be constructed directly, but may be returned
2713     // by member functions of the parent semigroup.
2714     //
2715     // \param rep an element of the semigroup represented by \p parent.
2716     //
2717     // \throws LibsemigroupsException if \p rep is a regular element of the
2718     // semigroup represented by \p parent.
NonRegularDClass(Konieczny * parent,internal_reference rep)2719     NonRegularDClass(Konieczny* parent, internal_reference rep)
2720         : Konieczny::DClass(parent, rep),
2721           _H_set(),
2722           _idems_above_computed(false),
2723           _lambda_index_positions(),
2724           _left_idem_above(rep),
2725           _left_idem_class(),
2726           _left_idem_H_class(),
2727           _left_idem_left_reps(),
2728           _left_indices_computed(false),
2729           _rho_index_positions(),
2730           _right_idem_above(rep),
2731           _right_idem_class(),
2732           _right_idem_H_class(),
2733           _right_idem_right_reps(),
2734           _right_indices_computed(false) {
2735       if (parent->is_regular_element_NC(rep)) {
2736         LIBSEMIGROUPS_EXCEPTION("NonRegularDClass: the representative "
2737                                 "given should not be idempotent");
2738       }
2739       init();
2740     }
2741 
2742    public:
2743     ////////////////////////////////////////////////////////////////////////
2744     // NonRegularDClass - destructor - public
2745     ////////////////////////////////////////////////////////////////////////
~NonRegularDClass()2746     virtual ~NonRegularDClass() {
2747       InternalVecFree()(_left_idem_H_class);
2748       InternalVecFree()(_right_idem_H_class);
2749       InternalVecFree()(_left_idem_left_reps);
2750       InternalVecFree()(_right_idem_right_reps);
2751     }
2752 
2753     ////////////////////////////////////////////////////////////////////////
2754     // NonRegularDClass - member functions - public
2755     ////////////////////////////////////////////////////////////////////////
2756     using DClass::contains;
2757 
2758     // \copydoc DClass::contains
contains(const_reference x,lambda_orb_index_type lpos,rho_orb_index_type rpos)2759     bool contains(const_reference       x,
2760                   lambda_orb_index_type lpos,
2761                   rho_orb_index_type    rpos) override {
2762       return contains_NC(this->to_internal_const(x), lpos, rpos);
2763     }
2764 
2765     ////////////////////////////////////////////////////////////////////////
2766     // NonRegularDClass - member functions - private
2767     ////////////////////////////////////////////////////////////////////////
2768     using DClass::contains_NC;
contains_NC(internal_const_reference x,lambda_orb_index_type lpos,rho_orb_index_type rpos)2769     bool contains_NC(internal_const_reference x,
2770                      lambda_orb_index_type    lpos,
2771                      rho_orb_index_type       rpos) override {
2772       if (_lambda_index_positions.find(lpos) == _lambda_index_positions.end()) {
2773         return false;
2774       }
2775       if (_rho_index_positions.find(rpos) == _rho_index_positions.end()) {
2776         return false;
2777       }
2778       PoolGuard cg1(this->parent()->element_pool());
2779       PoolGuard cg2(this->parent()->element_pool());
2780       auto      tmp1 = cg1.get();
2781       auto      tmp2 = cg2.get();
2782       for (left_indices_index_type i : _lambda_index_positions[lpos]) {
2783         Product()(this->to_external(tmp1),
2784                   this->to_external_const(x),
2785                   this->to_external_const(this->left_mults_inv(i)));
2786         for (right_indices_index_type j : _rho_index_positions[rpos]) {
2787           Product()(this->to_external(tmp2),
2788                     this->to_external_const(this->right_mults_inv(j)),
2789                     this->to_external(tmp1));
2790           if (_H_set.find(tmp2) != _H_set.end()) {
2791             return true;
2792           }
2793         }
2794       }
2795       return false;
2796     }
2797 
2798    private:
2799     ////////////////////////////////////////////////////////////////////////
2800     // NonRegularDClass - initialisation member functions - private
2801     ////////////////////////////////////////////////////////////////////////
init()2802     void init() override {
2803       if (this->class_computed()) {
2804         return;
2805       }
2806       find_idems_above();
2807       compute_H_class();
2808       compute_mults();
2809       compute_left_indices();
2810       compute_right_indices();
2811       construct_H_set();
2812       this->set_class_computed(true);
2813     }
2814 
find_idems_above()2815     void find_idems_above() {
2816       if (_idems_above_computed) {
2817         return;
2818       }
2819       // assumes that all D-classes above this have already been calculated!
2820       bool      left_found  = false;
2821       bool      right_found = false;
2822       PoolGuard cg(this->parent()->element_pool());
2823       auto      tmp = cg.get();
2824 
2825       for (auto it = this->parent()->_regular_D_classes.rbegin();
2826 
2827            (!left_found || !right_found)
2828            && it != this->parent()->_regular_D_classes.rend();
2829            it++) {
2830         RegularDClass* D = *it;
2831         if (!left_found) {
2832           for (auto idem_it = D->cbegin_left_idem_reps();
2833                idem_it < D->cend_left_idem_reps();
2834                idem_it++) {
2835             Product()(this->to_external(tmp),
2836                       this->rep(),
2837                       this->to_external_const(*idem_it));
2838             if (this->to_external(tmp) == this->rep()) {
2839               _left_idem_above = *idem_it;
2840               _left_idem_class = D;
2841               left_found       = true;
2842               break;
2843             }
2844           }
2845         }
2846 
2847         if (!right_found) {
2848           for (auto idem_it = D->cbegin_right_idem_reps();
2849                idem_it < D->cend_right_idem_reps();
2850                idem_it++) {
2851             Product()(this->to_external(tmp),
2852                       this->to_external_const(*idem_it),
2853                       this->rep());
2854             if (this->to_external(tmp) == this->rep()) {
2855               _right_idem_above = *idem_it;
2856               _right_idem_class = D;
2857               right_found       = true;
2858               break;
2859             }
2860           }
2861         }
2862       }
2863       LIBSEMIGROUPS_ASSERT(_left_idem_class != NULL);
2864       LIBSEMIGROUPS_ASSERT(_right_idem_class != NULL);
2865       _idems_above_computed = true;
2866 #ifdef LIBSEMIGROUPS_DEBUG
2867       LIBSEMIGROUPS_ASSERT(_left_idem_class->contains_NC(_left_idem_above));
2868       LIBSEMIGROUPS_ASSERT(_right_idem_class->contains_NC(_right_idem_above));
2869       LIBSEMIGROUPS_ASSERT(left_found && right_found);
2870       Product()(this->to_external(tmp),
2871                 this->rep(),
2872                 this->to_external(_left_idem_above));
2873       LIBSEMIGROUPS_ASSERT(EqualTo()(this->to_external(tmp), this->rep()));
2874       Product()(this->to_external(tmp),
2875                 this->to_external(_right_idem_above),
2876                 this->rep());
2877       LIBSEMIGROUPS_ASSERT(EqualTo()(this->to_external(tmp), this->rep()));
2878 #endif
2879     }
2880 
compute_H_class()2881     void compute_H_class() override {
2882       if (this->H_class_computed()) {
2883         return;
2884       }
2885       find_idems_above();
2886       std::pair<lambda_orb_index_type, rho_orb_index_type> left_idem_indices
2887           = _left_idem_class->index_positions(
2888               this->to_external(_left_idem_above));
2889       internal_const_element_type left_idem_left_mult
2890           = _left_idem_class->cbegin_left_mults()[left_idem_indices.first];
2891       internal_const_element_type left_idem_right_mult
2892           = _left_idem_class->cbegin_right_mults()[left_idem_indices.second];
2893 
2894       std::pair<lambda_orb_index_type, rho_orb_index_type> right_idem_indices
2895           = _right_idem_class->index_positions(
2896               this->to_external(_right_idem_above));
2897       internal_const_element_type right_idem_left_mult
2898           = _right_idem_class->cbegin_left_mults()[right_idem_indices.first];
2899       internal_const_element_type right_idem_right_mult
2900           = _right_idem_class->cbegin_right_mults()[right_idem_indices.second];
2901 
2902       PoolGuard cg1(this->parent()->element_pool());
2903       PoolGuard cg2(this->parent()->element_pool());
2904       auto      tmp1 = cg1.get();
2905       auto      tmp2 = cg2.get();
2906       for (auto it = _left_idem_class->cbegin_H_class();
2907            it < _left_idem_class->cend_H_class();
2908            it++) {
2909         Product()(this->to_external(tmp1),
2910                   this->to_external_const(left_idem_right_mult),
2911                   this->to_external_const(*it));
2912         Product()(this->to_external(tmp2),
2913                   this->to_external(tmp1),
2914                   this->to_external_const(left_idem_left_mult));
2915         _left_idem_H_class.push_back(this->internal_copy(tmp2));
2916       }
2917 
2918       for (auto it = _right_idem_class->cbegin_H_class();
2919            it < _right_idem_class->cend_H_class();
2920            it++) {
2921         Product()(this->to_external(tmp1),
2922                   this->to_external_const(right_idem_right_mult),
2923                   this->to_external_const(*it));
2924         Product()(this->to_external(tmp2),
2925                   this->to_external(tmp1),
2926                   this->to_external_const(right_idem_left_mult));
2927         _right_idem_H_class.push_back(this->internal_copy(tmp2));
2928       }
2929 
2930       for (auto it = _left_idem_class->cbegin_left_mults();
2931            it < _left_idem_class->cend_left_mults();
2932            it++) {
2933         Product()(this->to_external(tmp1),
2934                   this->to_external_const(left_idem_right_mult),
2935                   _left_idem_class->rep());
2936         Product()(this->to_external(tmp2),
2937                   this->to_external(tmp1),
2938                   this->to_external_const(*it));
2939         _left_idem_left_reps.push_back(this->internal_copy((tmp2)));
2940       }
2941 
2942       for (auto it = _right_idem_class->cbegin_right_mults();
2943            it < _right_idem_class->cend_right_mults();
2944            it++) {
2945         Product()(this->to_external(tmp1),
2946                   this->to_external_const(*it),
2947                   _right_idem_class->rep());
2948         Product()(this->to_external(tmp2),
2949                   this->to_external(tmp1),
2950                   this->to_external_const(right_idem_left_mult));
2951         _right_idem_right_reps.push_back(this->internal_copy(tmp2));
2952       }
2953 
2954       static std::vector<internal_element_type> Hex;
2955       static std::vector<internal_element_type> xHf;
2956 
2957       for (internal_const_reference s : _left_idem_H_class) {
2958         Product()(
2959             this->to_external(tmp1), this->rep(), this->to_external_const(s));
2960         xHf.push_back(this->internal_copy(tmp1));
2961       }
2962 
2963       for (internal_const_reference t : _right_idem_H_class) {
2964         Product()(
2965             this->to_external(tmp1), this->to_external_const(t), this->rep());
2966         Hex.push_back(this->internal_copy(tmp1));
2967       }
2968 
2969       static internal_set_type s;
2970       this->internal_set().clear();
2971       for (auto it = Hex.begin(); it < Hex.end(); ++it) {
2972         if (!this->internal_set().insert(*it).second) {
2973           this->internal_free(*it);
2974         }
2975       }
2976       Hex.clear();
2977       Hex.assign(this->internal_set().begin(), this->internal_set().end());
2978 
2979       this->internal_set().clear();
2980       for (auto it = xHf.begin(); it < xHf.end(); ++it) {
2981         if (!this->internal_set().insert(*it).second) {
2982           this->internal_free(*it);
2983         }
2984       }
2985       xHf.clear();
2986       xHf.assign(this->internal_set().begin(), this->internal_set().end());
2987 
2988       std::sort(Hex.begin(), Hex.end(), InternalLess());
2989       std::sort(xHf.begin(), xHf.end(), InternalLess());
2990 
2991       this->internal_vec().clear();
2992       std::set_intersection(Hex.begin(),
2993                             Hex.end(),
2994                             xHf.begin(),
2995                             xHf.end(),
2996                             std::back_inserter(this->internal_vec()),
2997                             InternalLess());
2998 
2999       for (auto it = this->internal_vec().cbegin();
3000            it < this->internal_vec().cend();
3001            ++it) {
3002         this->push_back_H_class(this->internal_copy(*it));
3003       }
3004 
3005       InternalVecFree()(xHf);
3006       InternalVecFree()(Hex);
3007       Hex.clear();
3008       xHf.clear();
3009 
3010       this->set_H_class_computed(true);
3011     }
3012 
compute_mults()3013     void compute_mults() {
3014       if (this->mults_computed()) {
3015         return;
3016       }
3017       find_idems_above();
3018       compute_H_class();
3019       std::pair<lambda_orb_index_type, rho_orb_index_type> left_idem_indices
3020           = _left_idem_class->index_positions(
3021               this->to_external(_left_idem_above));
3022       LIBSEMIGROUPS_ASSERT(left_idem_indices.first != UNDEFINED
3023                            && left_idem_indices.second != UNDEFINED);
3024       internal_const_element_type left_idem_left_mult
3025           = _left_idem_class->cbegin_left_mults()[left_idem_indices.first];
3026 
3027       std::pair<lambda_orb_index_type, rho_orb_index_type> right_idem_indices
3028           = _right_idem_class->index_positions(
3029               this->to_external(_right_idem_above));
3030       LIBSEMIGROUPS_ASSERT(right_idem_indices.first != UNDEFINED
3031                            && right_idem_indices.second != UNDEFINED);
3032       internal_const_element_type right_idem_right_mult
3033           = _right_idem_class->cbegin_right_mults()[right_idem_indices.second];
3034 
3035       static std::unordered_set<std::vector<internal_element_type>,
3036                                 Hash<std::vector<internal_element_type>>,
3037                                 InternalVecEqualTo>
3038           Hxhw_set;
3039       Hxhw_set.clear();
3040 
3041       static std::unordered_set<std::vector<internal_element_type>,
3042                                 Hash<std::vector<internal_element_type>>,
3043                                 InternalVecEqualTo>
3044           Hxh_set;
3045       Hxh_set.clear();
3046 
3047       static std::unordered_set<std::vector<internal_element_type>,
3048                                 Hash<std::vector<internal_element_type>>,
3049                                 InternalVecEqualTo>
3050           zhHx_set;
3051       zhHx_set.clear();
3052 
3053       static std::unordered_set<std::vector<internal_element_type>,
3054                                 Hash<std::vector<internal_element_type>>,
3055                                 InternalVecEqualTo>
3056           hHx_set;
3057       hHx_set.clear();
3058 
3059       PoolGuard cg1(this->parent()->element_pool());
3060       PoolGuard cg2(this->parent()->element_pool());
3061       PoolGuard cg3(this->parent()->element_pool());
3062       PoolGuard cg4(this->parent()->element_pool());
3063       auto      tmp1 = cg1.get();
3064       auto      tmp2 = cg2.get();
3065       auto      tmp3 = cg3.get();
3066       auto      tmp4 = cg4.get();
3067       for (internal_const_reference h : _left_idem_H_class) {
3068         static std::vector<internal_element_type> Hxh;
3069         LIBSEMIGROUPS_ASSERT(Hxh.empty());
3070         for (auto it = this->cbegin_H_class(); it < this->cend_H_class();
3071              ++it) {
3072           Product()(this->to_external(tmp1),
3073                     this->to_external_const(*it),
3074                     this->to_external_const(h));
3075           Hxh.push_back(this->internal_copy(tmp1));
3076         }
3077 
3078         std::sort(Hxh.begin(), Hxh.end(), InternalLess());
3079         if (Hxh_set.find(Hxh) == Hxh_set.end()) {
3080           for (size_t i = 0; i < _left_idem_left_reps.size(); ++i) {
3081             static std::vector<internal_element_type> Hxhw;
3082             Hxhw.clear();
3083 
3084             for (auto it = Hxh.cbegin(); it < Hxh.cend(); ++it) {
3085               Product()(this->to_external(tmp1),
3086                         this->to_external_const(*it),
3087                         this->to_external_const(_left_idem_left_reps[i]));
3088               Hxhw.push_back(this->internal_copy(tmp1));
3089             }
3090             std::sort(Hxhw.begin(), Hxhw.end(), InternalLess());
3091             if (Hxhw_set.find(Hxhw) == Hxhw_set.end()) {
3092               Hxhw_set.insert(std::move(Hxhw));
3093 
3094               Product()(this->to_external(tmp3),
3095                         this->to_external_const(h),
3096                         this->to_external_const(_left_idem_left_reps[i]));
3097               Product()(this->to_external(tmp4),
3098                         this->rep(),
3099                         this->to_external(tmp3));
3100               Lambda()(this->tmp_lambda_value(), this->to_external(tmp4));
3101               lambda_orb_index_type lpos = this->parent()->_lambda_orb.position(
3102                   this->tmp_lambda_value());
3103               if (_lambda_index_positions.find(lpos)
3104                   == _lambda_index_positions.end()) {
3105                 _lambda_index_positions.emplace(
3106                     lpos, std::vector<left_indices_index_type>());
3107               }
3108               _lambda_index_positions[lpos].push_back(
3109                   this->number_of_left_reps_NC());
3110 
3111               // push_left_rep and push_left_mult use tmp_element and
3112               // tmp_element2
3113               this->push_left_rep(tmp4);
3114               this->push_left_mult(tmp3);
3115 
3116               Product()(
3117                   this->to_external(tmp1),
3118                   this->to_external_const(_left_idem_left_reps[i]),
3119                   this->to_external_const(_left_idem_class->left_mults_inv(i)));
3120               Product()(this->to_external(tmp2),
3121                         this->to_external(tmp1),
3122                         this->to_external_const(left_idem_left_mult));
3123               this->parent()->group_inverse(tmp3, _left_idem_above, tmp2);
3124               this->parent()->group_inverse(tmp4, _left_idem_above, h);
3125               Product()(this->to_external(tmp1),
3126                         this->to_external(tmp3),
3127                         this->to_external(tmp4));
3128 
3129               Product()(
3130                   this->to_external(tmp2),
3131                   this->to_external_const(_left_idem_class->left_mults_inv(i)),
3132                   this->to_external_const(left_idem_left_mult));
3133               Product()(this->to_external(tmp3),
3134                         this->to_external(tmp2),
3135                         this->to_external(tmp1));
3136               this->push_left_mult_inv(tmp3);
3137             } else {
3138               InternalVecFree()(Hxhw);
3139             }
3140           }
3141           Hxh_set.insert(std::move(Hxh));
3142         } else {
3143           InternalVecFree()(Hxh);
3144         }
3145         Hxh.clear();
3146       }
3147 
3148       for (auto it = Hxh_set.begin(); it != Hxh_set.end(); ++it) {
3149         InternalVecFree()(*it);
3150       }
3151 
3152       for (auto it = Hxhw_set.begin(); it != Hxhw_set.end(); ++it) {
3153         InternalVecFree()(*it);
3154       }
3155 
3156       for (internal_const_reference h : _right_idem_H_class) {
3157         static std::vector<internal_element_type> hHx;
3158         LIBSEMIGROUPS_ASSERT(hHx.empty());
3159 
3160         for (auto it = this->cbegin_H_class(); it < this->cend_H_class();
3161              ++it) {
3162           Product()(this->to_external(tmp1),
3163                     this->to_external_const(h),
3164                     this->to_external_const(*it));
3165           hHx.push_back(this->internal_copy(tmp1));
3166         }
3167 
3168         std::sort(hHx.begin(), hHx.end(), InternalLess());
3169         if (hHx_set.find(hHx) == hHx_set.end()) {
3170           for (size_t i = 0; i < _right_idem_right_reps.size(); ++i) {
3171             static std::vector<internal_element_type> zhHx;
3172             zhHx.clear();
3173             for (auto it = hHx.cbegin(); it < hHx.cend(); ++it) {
3174               Product()(this->to_external(tmp1),
3175                         this->to_external_const(_right_idem_right_reps[i]),
3176                         this->to_external_const(*it));
3177               zhHx.push_back(this->internal_copy(tmp1));
3178             }
3179 
3180             std::sort(zhHx.begin(), zhHx.end(), InternalLess());
3181             if (zhHx_set.find(zhHx) == zhHx_set.end()) {
3182               zhHx_set.insert(std::move(zhHx));
3183               // push_right_rep and push_right_mult use tmp_element and
3184               // tmp_element2
3185               Product()(this->to_external(tmp3),
3186                         this->to_external_const(_right_idem_right_reps[i]),
3187                         this->to_external_const(h));
3188               Product()(this->to_external(tmp4),
3189                         this->to_external(tmp3),
3190                         this->rep());
3191 
3192               Rho()(this->tmp_rho_value(), this->to_external(tmp4));
3193               rho_orb_index_type rpos
3194                   = this->parent()->_rho_orb.position(this->tmp_rho_value());
3195               if (_rho_index_positions.find(rpos)
3196                   == _rho_index_positions.end()) {
3197                 _rho_index_positions.emplace(
3198                     rpos, std::vector<right_indices_index_type>());
3199               }
3200               _rho_index_positions[rpos].push_back(
3201                   this->number_of_right_reps_NC());
3202               this->push_right_rep(tmp4);
3203               this->push_right_mult(tmp3);
3204 
3205               Product()(this->to_external(tmp1),
3206                         this->to_external_const(right_idem_right_mult),
3207                         this->to_external_const(
3208                             _right_idem_class->right_mults_inv(i)));
3209               Product()(this->to_external(tmp2),
3210                         this->to_external(tmp1),
3211                         this->to_external_const(_right_idem_right_reps[i]));
3212 
3213               this->parent()->group_inverse(tmp3, _right_idem_above, h);
3214               this->parent()->group_inverse(tmp4, _right_idem_above, tmp2);
3215               Product()(this->to_external(tmp1),
3216                         this->to_external(tmp3),
3217                         this->to_external(tmp4));
3218 
3219               Product()(this->to_external(tmp2),
3220                         this->to_external_const(right_idem_right_mult),
3221                         this->to_external_const(
3222                             _right_idem_class->right_mults_inv(i)));
3223               Product()(this->to_external(tmp3),
3224                         this->to_external(tmp1),
3225                         this->to_external(tmp2));
3226               this->push_right_mult_inv(tmp3);
3227             } else {
3228               InternalVecFree()(zhHx);
3229             }
3230           }
3231           hHx_set.insert(std::move(hHx));
3232         } else {
3233           InternalVecFree()(hHx);
3234         }
3235         hHx.clear();
3236       }
3237 
3238       for (auto it = hHx_set.begin(); it != hHx_set.end(); ++it) {
3239         InternalVecFree()(*it);
3240       }
3241 
3242       for (auto it = zhHx_set.begin(); it != zhHx_set.end(); ++it) {
3243         InternalVecFree()(*it);
3244       }
3245       this->set_mults_computed(true);
3246     }
3247 
construct_H_set()3248     void construct_H_set() {
3249       for (auto it = this->cbegin_H_class(); it < this->cend_H_class(); ++it) {
3250         _H_set.insert(*it);
3251       }
3252     }
3253 
compute_left_mults()3254     void compute_left_mults() override {
3255       compute_mults();
3256     }
3257 
compute_left_mults_inv()3258     void compute_left_mults_inv() override {
3259       compute_mults();
3260     }
3261 
compute_left_reps()3262     void compute_left_reps() override {
3263       compute_mults();
3264     }
3265 
compute_right_mults()3266     void compute_right_mults() override {
3267       compute_mults();
3268     }
3269 
compute_right_mults_inv()3270     void compute_right_mults_inv() override {
3271       compute_mults();
3272     }
3273 
compute_right_reps()3274     void compute_right_reps() override {
3275       compute_mults();
3276     }
3277 
compute_left_indices()3278     void compute_left_indices() override {
3279       if (_left_indices_computed) {
3280         return;
3281       }
3282       for (auto it = this->cbegin_left_reps(); it != this->cend_left_reps();
3283            ++it) {
3284         Lambda()(this->tmp_lambda_value(), this->to_external_const(*it));
3285         LIBSEMIGROUPS_ASSERT(
3286             this->parent()->_lambda_orb.position(this->tmp_lambda_value())
3287             != UNDEFINED);
3288         this->left_indices().push_back(
3289             this->parent()->_lambda_orb.position(this->tmp_lambda_value()));
3290       }
3291       _left_indices_computed = true;
3292     }
3293 
compute_right_indices()3294     void compute_right_indices() override {
3295       if (_right_indices_computed) {
3296         return;
3297       }
3298       for (auto it = this->cbegin_right_reps(); it != this->cend_right_reps();
3299            ++it) {
3300         Rho()(this->tmp_rho_value(), this->to_external_const(*it));
3301         LIBSEMIGROUPS_ASSERT(
3302             this->parent()->_rho_orb.position(this->tmp_rho_value())
3303             != UNDEFINED);
3304         this->right_indices().push_back(
3305             this->parent()->_rho_orb.position(this->tmp_rho_value()));
3306       }
3307       _right_indices_computed = true;
3308     }
3309 
3310     ////////////////////////////////////////////////////////////////////////
3311     // NonRegularDClass - data - private
3312     ////////////////////////////////////////////////////////////////////////
3313     internal_set_type _H_set;
3314     bool              _idems_above_computed;
3315     std::unordered_map<lambda_orb_index_type,
3316                        std::vector<left_indices_index_type>>
3317                                        _lambda_index_positions;
3318     internal_element_type              _left_idem_above;
3319     RegularDClass*                     _left_idem_class;
3320     std::vector<internal_element_type> _left_idem_H_class;
3321     std::vector<internal_element_type> _left_idem_left_reps;
3322     bool                               _left_indices_computed;
3323     std::unordered_map<rho_orb_index_type,
3324                        std::vector<right_indices_index_type>>
3325                                        _rho_index_positions;
3326     internal_element_type              _right_idem_above;
3327     RegularDClass*                     _right_idem_class;
3328     std::vector<internal_element_type> _right_idem_H_class;
3329     std::vector<internal_element_type> _right_idem_right_reps;
3330     bool                               _right_indices_computed;
3331   };
3332 
3333   template <typename TElementType, typename TTraits>
~Konieczny()3334   Konieczny<TElementType, TTraits>::~Konieczny() {
3335     for (DClass* D : _D_classes) {
3336       delete D;
3337     }
3338     // _one is included in _gens
3339     InternalVecFree()(_gens);
3340     while (!_ranks.empty()) {
3341       for (auto x : _reg_reps[max_rank()]) {
3342         this->internal_free(x.first);
3343       }
3344       for (auto x : _nonregular_reps[max_rank()]) {
3345         this->internal_free(x.first);
3346       }
3347       _ranks.erase(max_rank());
3348     }
3349     delete _rank_state;
3350   }
3351 
3352   template <typename TElementType, typename TTraits>
3353   void
add_D_class(Konieczny::RegularDClass * D)3354   Konieczny<TElementType, TTraits>::add_D_class(Konieczny::RegularDClass* D) {
3355     _regular_D_classes.push_back(D);
3356     _D_classes.push_back(D);
3357     add_to_D_maps(_D_classes.size() - 1);
3358     _D_rels.push_back(std::vector<D_class_index_type>());
3359   }
3360 
3361   template <typename TElementType, typename TTraits>
add_D_class(Konieczny<TElementType,TTraits>::NonRegularDClass * D)3362   void Konieczny<TElementType, TTraits>::add_D_class(
3363       Konieczny<TElementType, TTraits>::NonRegularDClass* D) {
3364     _D_classes.push_back(D);
3365     add_to_D_maps(_D_classes.size() - 1);
3366     _D_rels.push_back(std::vector<D_class_index_type>());
3367   }
3368 
3369   template <typename TElementType, typename TTraits>
finished_impl() const3370   bool Konieczny<TElementType, TTraits>::finished_impl() const {
3371     return _ranks.empty() && _run_initialised;
3372   }
3373 
3374   template <typename TElementType, typename TTraits>
init_run()3375   void Konieczny<TElementType, TTraits>::init_run() {
3376     if (_run_initialised) {
3377       return;
3378     }
3379     // ensure the data is set up correctly
3380     init_data();
3381     // compute orbits (can stop during these enumerations)
3382     compute_orbs();
3383     // we might have stopped; then we shouldn't attempt to anything else since
3384     // the orbs may not have been fully enumerated and hence we cannot compute D
3385     // classes
3386     if (stopped()) {
3387       return;
3388     }
3389     // compute the D-class of the adjoined identity and its covering reps
3390     internal_element_type y   = this->internal_copy(_one);
3391     RegularDClass*        top = new RegularDClass(this, y);
3392     add_D_class(top);
3393     for (internal_reference x : top->covering_reps()) {
3394       size_t rnk = InternalRank()(_rank_state, this->to_external_const(x));
3395       _ranks.insert(rnk);
3396       if (is_regular_element_NC(x)) {
3397         _reg_reps[rnk].emplace_back(x, 0);
3398       } else {
3399         _nonregular_reps[rnk].emplace_back(x, 0);
3400       }
3401     }
3402     _reps_processed++;
3403     // Set whether the adjoined one is in the semigroup or not
3404     // i.e. whether the generators contain multiple elements in the top D
3405     // class
3406     bool flag = false;
3407     for (internal_const_element_type x : _gens) {
3408       if (_D_classes[0]->contains_NC(x)) {
3409         if (flag) {
3410           _adjoined_identity_contained = true;
3411           break;
3412         } else {
3413           flag = true;
3414         }
3415       }
3416     }
3417 
3418     _run_initialised = true;
3419   }
3420 
3421   template <typename TElementType, typename TTraits>
run_report()3422   void Konieczny<TElementType, TTraits>::run_report() {
3423     if (!report()) {
3424       return;
3425     }
3426     size_t number_of_reps_remaining = 0;
3427     std::for_each(_ranks.cbegin(),
3428                   _ranks.cend(),
3429                   [this, &number_of_reps_remaining](rank_type x) {
3430                     number_of_reps_remaining
3431                         += _reg_reps[x].size() + _nonregular_reps[x].size();
3432                   });
3433     // TODO(later) add layers to report
3434     REPORT_DEFAULT(
3435         "found %d elements in %d D-classes (%d regular), %d R-classes (%d "
3436         "regular), %d L-classes (%d regular)\n",
3437         current_size(),
3438         current_number_of_D_classes(),
3439         current_number_of_regular_D_classes(),
3440         current_number_of_R_classes(),
3441         current_number_of_regular_R_classes(),
3442         current_number_of_L_classes(),
3443         current_number_of_regular_L_classes());
3444     REPORT_DEFAULT("there are %d unprocessed reps with ranks in [%d, %d]\n",
3445                    number_of_reps_remaining,
3446                    *_ranks.cbegin(),
3447                    max_rank());
3448   }
3449 
3450   template <typename TElementType, typename TTraits>
run_impl()3451   void Konieczny<TElementType, TTraits>::run_impl() {
3452     detail::Timer t;
3453     // initialise the required data
3454     init_run();
3455     // if we haven't initialised, it should be because we stopped() during
3456     // init(), and hence we should not continue
3457     if (!_run_initialised) {
3458       LIBSEMIGROUPS_ASSERT(stopped());
3459       return;
3460     }
3461 
3462     std::vector<std::pair<internal_element_type, D_class_index_type>> next_reps;
3463     std::vector<std::pair<internal_element_type, D_class_index_type>> tmp_next;
3464 
3465     while (!stopped() && !_ranks.empty()) {
3466       LIBSEMIGROUPS_ASSERT(next_reps.empty());
3467       bool         reps_are_reg = false;
3468       size_t const mx_rank      = max_rank();
3469       if (!_reg_reps[mx_rank].empty()) {
3470         reps_are_reg = true;
3471         std::swap(next_reps, _reg_reps[mx_rank]);
3472         _reg_reps[mx_rank].clear();
3473       } else {
3474         std::swap(next_reps, _nonregular_reps[mx_rank]);
3475         _nonregular_reps[mx_rank].clear();
3476       }
3477 
3478       tmp_next.clear();
3479       for (auto it = next_reps.begin(); it < next_reps.end(); it++) {
3480         D_class_index_type i = get_containing_D_class(it->first);
3481         if (i != UNDEFINED) {
3482           _D_rels[i].push_back(it->second);
3483           this->internal_free(it->first);
3484           _reps_processed++;
3485         } else {
3486           tmp_next.push_back(*it);
3487         }
3488       }
3489       std::swap(next_reps, tmp_next);
3490 
3491       while (!next_reps.empty()) {
3492         run_report();
3493         auto& tup = next_reps.back();
3494         if (reps_are_reg) {
3495           add_D_class(new RegularDClass(this, tup.first));
3496         } else {
3497           add_D_class(new NonRegularDClass(this, tup.first));
3498         }
3499         for (internal_reference x : _D_classes.back()->covering_reps()) {
3500           size_t rnk = InternalRank()(_rank_state, this->to_external_const(x));
3501           _ranks.insert(rnk);
3502           if (is_regular_element_NC(x)) {
3503             LIBSEMIGROUPS_ASSERT(rnk < mx_rank);
3504             _reg_reps[rnk].emplace_back(x, _D_classes.size() - 1);
3505           } else {
3506             _nonregular_reps[rnk].emplace_back(x, _D_classes.size() - 1);
3507           }
3508         }
3509         next_reps.pop_back();
3510         _reps_processed++;
3511 
3512         tmp_next.clear();
3513         for (auto& x : next_reps) {
3514           if (_D_classes.back()->contains_NC(x.first)) {
3515             _D_rels.back().push_back(x.second);
3516             this->internal_free(x.first);
3517             _reps_processed++;
3518           } else {
3519             tmp_next.push_back(std::move(x));
3520           }
3521         }
3522         std::swap(next_reps, tmp_next);
3523       }
3524       LIBSEMIGROUPS_ASSERT(_reg_reps[mx_rank].empty());
3525       if (_nonregular_reps[mx_rank].empty()) {
3526         _ranks.erase(mx_rank);
3527       }
3528     }
3529 
3530     REPORT_TIME(t);
3531     report_why_we_stopped();
3532   }
3533 }  // namespace libsemigroups
3534 #endif  // LIBSEMIGROUPS_KONIECZNY_HPP_
3535