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