libsemigroups  v3.5.2
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
cong-common-helpers.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2024-2026 James D. Mitchell
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17//
18
19// This file contains helper functions for the derived classes of
20// detail::CongruenceCommon. This is a separate file so that we can declare all
21// the derived classes of detail::CongruenceCommon prior to declaring the
22// functions in this file.
23//
24// The implementation of all helpers must go into the namespace
25// congruence_common, and then be aliased into the, e.g., todd_coxeter
26// namespace. This makes it possible to generically use, e.g.,
27// congruence_common::normal_forms in, e.g., the python bindings.
28
29#ifndef LIBSEMIGROUPS_CONG_COMMON_HELPERS_HPP_
30#define LIBSEMIGROUPS_CONG_COMMON_HELPERS_HPP_
31
32#include <algorithm> // for remove_if
33#include <cstring> // for strlen, size_t
34#include <initializer_list> // for initializer_list, begin, end
35#include <iterator> // for begin, end, back_inserter
36#include <string> // for string
37#include <string_view> // for basic_string_view, string_view
38#include <type_traits> // for is_base_of_v, decay_t, is_same_v
39#include <unordered_map> // for unordered_map
40#include <vector> // for vector
41
42#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
43#include "is_specialization_of.hpp" // for is_specialization_of
44#include "ranges.hpp" // for is_input_or_sink_v, iterator_...
45
46#include "detail/string.hpp" // for throw_if_nullptr
47
48namespace libsemigroups {
49
50 // Forward decls
51 template <typename Word>
52 class Congruence;
53
54 template <typename Word>
55 class ToddCoxeter;
56
57 enum class tril;
58
59 namespace detail {
60 class ToddCoxeterImpl;
61 class CongruenceCommon;
62 } // namespace detail
63
64 namespace congruence_common {
65
74
76 // Interface helpers - add_generating_pair
78
89
106 // NOTE: we use native_word_type and not another template param to avoid
107 // unexpected behaviour, if for example we add words which are strings to a
108 // ToddCoxeter<word_type>, then unexpected things might happen.
109 template <typename Thing>
110 void
112 typename Thing::native_word_type const& u,
113 typename Thing::native_word_type const& v) {
114 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
115 thing.add_generating_pair_no_checks(
116 std::begin(u), std::end(u), std::begin(v), std::end(v));
117 }
118
128 template <typename Thing, typename Int>
132 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
133 static_assert(std::is_integral_v<Int>);
134 thing.add_generating_pair_no_checks(
135 std::begin(u), std::end(u), std::begin(v), std::end(v));
136 }
137
145 template <typename Thing>
147 char const* u,
148 char const* v) {
149 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
150 LIBSEMIGROUPS_ASSERT(u != nullptr);
151 LIBSEMIGROUPS_ASSERT(v != nullptr);
152 // We could static_assert that Thing::native_word_type == std::string,
153 // but it doesn't seem that adding this restriction would gain us
154 // anything, so it is not currently done.
155 thing.add_generating_pair_no_checks(
156 u, u + std::strlen(u), v, v + std::strlen(v));
157 }
158
159 // This version of the function catches the cases when u & v are not of
160 // the same type but both convertible to string_view
167 template <typename Thing>
169 std::string_view u,
170 std::string_view v) {
171 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
172 // We could static_assert that Thing::native_word_type == std::string,
173 // but it doesn't seem that adding this restriction would gain us
174 // anything, so it is not currently done.
175 thing.add_generating_pair_no_checks(
176 std::begin(u), std::end(u), std::begin(v), std::end(v));
177 }
178
195 // NOTE: we use native_word_type and not another template param to avoid
196 // unexpected behaviour, if for example we add words which are strings to a
197 // ToddCoxeter<word_type>, then unexpected things might happen.
198 template <typename Thing>
199 void add_generating_pair(Thing& thing,
200 typename Thing::native_word_type const& u,
201 typename Thing::native_word_type const& v) {
202 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
203 thing.add_generating_pair(
204 std::begin(u), std::end(u), std::begin(v), std::end(v));
205 }
206
215 template <typename Thing, typename Int>
216 void add_generating_pair(Thing& thing,
219 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
220 static_assert(std::is_integral_v<Int>);
221 thing.add_generating_pair(
222 std::begin(u), std::end(u), std::begin(v), std::end(v));
223 }
224
231 template <typename Thing>
232 void add_generating_pair(Thing& thing, char const* u, char const* v) {
233 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
234 detail::throw_if_nullptr(u, "2nd");
235 detail::throw_if_nullptr(v, "3rd");
236 // We could static_assert that Thing::native_word_type == std::string,
237 // but it doesn't seem that adding this restriction would gain us
238 // anything, so it is not currently done.
239 thing.add_generating_pair(u, u + std::strlen(u), v, v + std::strlen(v));
240 }
241
242 // This version of the function catches the cases when u & v are not of
243 // the same type but both convertible to string_view
250 template <typename Thing>
251 void add_generating_pair(Thing& thing,
252 std::string_view u,
253 std::string_view v) {
254 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
255 // We could static_assert that Thing::native_word_type == std::string,
256 // but it doesn't seem that adding this restriction would gain us
257 // anything, so it is not currently done.
258 thing.add_generating_pair_no_checks(
259 std::begin(u), std::end(u), std::begin(v), std::end(v));
260 }
261
263
265 // Interface helpers - currently_contains_no_checks
267
291
316 // NOTE: we use native_word_type and not another template param to avoid
317 // unexpected behaviour, if for example we add words which are strings to a
318 // ToddCoxeter<word_type>, then unexpected things might happen.
319 template <typename Thing>
320 [[nodiscard]] tril
322 typename Thing::native_word_type const& u,
323 typename Thing::native_word_type const& v) {
324 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
325 return thing.currently_contains_no_checks(
326 std::begin(u), std::end(u), std::begin(v), std::end(v));
327 }
328
338 template <typename Thing, typename Int>
339 [[nodiscard]] tril
343 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
344 static_assert(std::is_integral_v<Int>);
345 return thing.currently_contains_no_checks(
346 std::begin(u), std::end(u), std::begin(v), std::end(v));
347 }
348
356 template <typename Thing>
357 [[nodiscard]] tril currently_contains_no_checks(Thing const& thing,
358 char const* u,
359 char const* v) {
360 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
361 LIBSEMIGROUPS_ASSERT(u != nullptr);
362 LIBSEMIGROUPS_ASSERT(v != nullptr);
363 // We could static_assert that Thing::native_word_type == std::string,
364 // but it doesn't seem that adding this restriction would gain us
365 // anything, so it is not currently done.
366 return thing.currently_contains_no_checks(
367 u, u + std::strlen(u), v, v + std::strlen(v));
368 }
369
377 // NOTE: This version of the function catches the cases when u & v are not
378 // of the same type but both convertible to string_view
379 template <typename Thing>
380 [[nodiscard]] tril currently_contains_no_checks(Thing const& thing,
381 std::string_view u,
382 std::string_view v) {
383 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
384 // We could static_assert that Thing::native_word_type == std::string,
385 // but it doesn't seem that adding this restriction would gain us
386 // anything, so it is not currently done.
387 return thing.currently_contains_no_checks(
388 std::begin(u), std::end(u), std::begin(v), std::end(v));
389 }
390
392 // Interface helpers - currently_contains
394
419 // NOTE: we use native_word_type and not another template param to avoid
420 // unexpected behaviour, if for example we add words which are strings to a
421 // ToddCoxeter<word_type>, then unexpected things might happen.
422 template <typename Thing>
423 [[nodiscard]] tril
424 currently_contains(Thing const& thing,
425 typename Thing::native_word_type const& u,
426 typename Thing::native_word_type const& v) {
427 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
428 return thing.currently_contains(
429 std::begin(u), std::end(u), std::begin(v), std::end(v));
430 }
431
440 template <typename Thing, typename Int>
441 [[nodiscard]] tril currently_contains(Thing const& thing,
444 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
445 static_assert(std::is_integral_v<Int>);
446 return thing.currently_contains(
447 std::begin(u), std::end(u), std::begin(v), std::end(v));
448 }
449
456 template <typename Thing>
457 [[nodiscard]] tril currently_contains(Thing const& thing,
458 char const* u,
459 char const* v) {
460 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
461 detail::throw_if_nullptr(u, "2nd");
462 detail::throw_if_nullptr(v, "3rd");
463 return thing.currently_contains(
464 u, u + std::strlen(u), v, v + std::strlen(v));
465 }
466
467 // This version of the function catches the cases when u & v are not of
468 // the same type but both convertible to string_view
475 template <typename Thing>
476 [[nodiscard]] tril currently_contains(Thing const& thing,
477 std::string_view u,
478 std::string_view v) {
479 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
480 return thing.currently_contains(
481 std::begin(u), std::end(u), std::begin(v), std::end(v));
482 }
483
485 // Interface helpers - contains_no_checks
487
507 // NOTE: we use native_word_type and not another template param to avoid
508 // unexpected behaviour, if for example we add words which are strings to a
509 // ToddCoxeter<word_type>, then unexpected things might happen.
510 template <typename Thing>
511 [[nodiscard]] bool
512 contains_no_checks(Thing& thing,
513 typename Thing::native_word_type const& u,
514 typename Thing::native_word_type const& v) {
515 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
516 return thing.contains_no_checks(
517 std::begin(u), std::end(u), std::begin(v), std::end(v));
518 }
519
528 template <typename Thing, typename Int>
529 [[nodiscard]] bool contains_no_checks(Thing& thing,
532 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
533 static_assert(std::is_integral_v<Int>);
535 }
536
543 template <typename Thing>
544 [[nodiscard]] bool contains_no_checks(Thing& thing,
545 char const* u,
546 char const* v) {
547 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
548 LIBSEMIGROUPS_ASSERT(u != nullptr);
549 LIBSEMIGROUPS_ASSERT(v != nullptr);
550 // We could static_assert that Thing::native_word_type == std::string,
551 // but it doesn't seem that adding this restriction would gain us
552 // anything, so it is not currently done.
553 return thing.contains_no_checks(
554 u, u + std::strlen(u), v, v + std::strlen(v));
555 }
556
557 // This version of the function catches the cases when u & v are not of
558 // the same type but both convertible to string_view
565 template <typename Thing>
566 [[nodiscard]] bool contains_no_checks(Thing& thing,
567 std::string_view u,
568 std::string_view v) {
569 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
570 // We could static_assert that Thing::native_word_type == std::string,
571 // but it doesn't seem that adding this restriction would gain us
572 // anything, so it is not currently done.
573 return thing.contains_no_checks(
574 std::begin(u), std::end(u), std::begin(v), std::end(v));
575 }
576
578 // Interface helpers - contains
580
600 // NOTE: we use native_word_type and not another template param to avoid
601 // unexpected behaviour, if for example we add words which are strings to a
602 // ToddCoxeter<word_type>, then unexpected things might happen.
603 template <typename Thing>
604 [[nodiscard]] bool contains(Thing& thing,
605 typename Thing::native_word_type const& u,
606 typename Thing::native_word_type const& v) {
607 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
608 return thing.contains(
609 std::begin(u), std::end(u), std::begin(v), std::end(v));
610 }
611
620 template <typename Thing, typename Int>
621 [[nodiscard]] bool contains(Thing& thing,
624 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
625 static_assert(std::is_integral_v<Int>);
626 return thing.contains(
627 std::begin(u), std::end(u), std::begin(v), std::end(v));
628 }
629
630 // This version of the function catches the cases when u & v are not of
631 // the same type but both convertible to string_view
638 template <typename Thing>
639 [[nodiscard]] bool contains(Thing& thing,
640 std::string_view u,
641 std::string_view v) {
642 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
643 // We could static_assert that Thing::native_word_type == std::string,
644 // but it doesn't seem that adding this restriction would gain us
645 // anything, so it is not currently done.
646 return thing.contains(
647 std::begin(u), std::end(u), std::begin(v), std::end(v));
648 }
649
656 template <typename Thing>
657 [[nodiscard]] bool contains(Thing& thing, char const* u, char const* v) {
658 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
659 detail::throw_if_nullptr(u, "2nd");
660 detail::throw_if_nullptr(v, "3rd");
661 // We could static_assert that Thing::native_word_type == std::string,
662 // but it doesn't seem that adding this restriction would gain us
663 // anything, so it is not currently done.
664 return thing.contains(u, u + std::strlen(u), v, v + std::strlen(v));
665 }
666
668
670 // Interface helpers - reduce_no_run_no_checks
672
696
719 // NOTE: we use native_word_type and not another template param to avoid
720 // unexpected behaviour, if for example we add words which are strings to a
721 // ToddCoxeter<word_type>, then unexpected things might happen.
722 template <typename Thing>
723 [[nodiscard]] typename Thing::native_word_type
724 reduce_no_run_no_checks(Thing const& thing,
725 typename Thing::native_word_type const& w);
726
727 // No string_view version is required because there is only a single
728 // "word" parameter, and so the first template will catch every case
729 // except initializer_list and char const*
730
739 template <typename Thing, typename Int>
740 [[nodiscard]] typename Thing::native_word_type
741 reduce_no_run_no_checks(Thing const& thing,
743
750 template <typename Thing>
751 [[nodiscard]] typename Thing::native_word_type
752 reduce_no_run_no_checks(Thing const& thing, char const* w);
753
755 // Interface helpers - reduce_no_run
757
780 // NOTE: we use native_word_type and not another template param to avoid
781 // unexpected behaviour, if for example we add words which are strings to a
782 // ToddCoxeter<word_type>, then unexpected things might happen.
783 template <typename Thing>
784 [[nodiscard]] typename Thing::native_word_type
785 reduce_no_run(Thing const& thing,
786 typename Thing::native_word_type const& w);
787
788 // No string_view version is required because there is only a single
789 // "w" parameter, and so the first template will catch every case
790 // except initializer_list and char const*
791
799 template <typename Thing, typename Int>
800 [[nodiscard]] typename Thing::native_word_type
801 reduce_no_run(Thing const& thing, std::initializer_list<Int> const& w);
802
808 template <typename Thing>
809 [[nodiscard]] typename Thing::native_word_type
810 reduce_no_run(Thing const& thing, char const* w);
811
813 // Interface helpers - reduce_no_checks
815
835 // NOTE: we use native_word_type and not another template param to avoid
836 // unexpected behaviour, if for example we add words which are strings to a
837 // ToddCoxeter<word_type>, then unexpected things might happen.
838 template <typename Thing>
839 [[nodiscard]] typename Thing::native_word_type
840 reduce_no_checks(Thing const& thing,
841 typename Thing::native_word_type const& w);
842
843 // No string_view version is required because there is only a single
844 // "word" parameter, and so the first template will catch every case
845 // except initializer_list and char const*
846
854 template <typename Thing, typename Int>
855 [[nodiscard]] typename Thing::native_word_type
856 reduce_no_checks(Thing const& thing, std::initializer_list<Int> const& w);
857
863 template <typename Thing>
864 [[nodiscard]] typename Thing::native_word_type
865 reduce_no_checks(Thing const& thing, char const* w);
866
868 // Interface helpers - reduce
889 // NOTE: we use native_word_type and not another template param to avoid
890 // unexpected behaviour, if for example we add words which are strings to a
891 // ToddCoxeter<word_type>, then unexpected things might happen.
892 template <typename Thing>
893 [[nodiscard]] typename Thing::native_word_type
894 reduce(Thing const& thing, typename Thing::native_word_type const& w);
895
896 // No string_view version is required because there is only a single
897 // "word" parameter, and so the first template will catch every case
898 // except initializer_list and char const*
899
907 template <typename Thing, typename Int>
908 [[nodiscard]] typename Thing::native_word_type
909 reduce(Thing const& thing, std::initializer_list<Int> const& w);
910
916 template <typename Thing>
917 [[nodiscard]] typename Thing::native_word_type reduce(Thing const& thing,
918 char const* w);
919
921
923 // Interface helpers - normal_forms
925
926 // There's nothing in common to implement in this file.
927
929 // Interface helpers - partition
931
944
945#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
946 // Forward decls defined in todd-coxeter-helpers.tpp and cong-helpers.tpp
947 template <typename Word,
948 typename Range,
949 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
950 [[nodiscard]] std::vector<std::vector<Word>>
951 partition(ToddCoxeter<Word>& thing, Range r);
952
953 template <typename Word,
954 typename Range,
955 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
956 [[nodiscard]] std::vector<std::vector<Word>>
957 partition(Congruence<Word>& thing, Range r);
958#endif
959
982 template <typename Thing,
983 typename Range,
984 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
986 partition(Thing& thing, Range r);
987
993 template <typename Thing, typename Iterator1, typename Iterator2>
995 partition(Thing& thing, Iterator1 first, Iterator2 last) {
996 // static asserts are in done in the next call to partition
997 return partition(thing, rx::iterator_range(first, last));
998 }
999
1001 // Interface helpers - non_trivial_classes
1003
1027 template <typename Thing,
1028 typename Range,
1029 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
1031 non_trivial_classes(Thing& thing, Range r);
1032
1039 template <typename Thing, typename Iterator1, typename Iterator2>
1041 non_trivial_classes(Thing& thing, Iterator1 first, Iterator2 last) {
1042 return non_trivial_classes(thing, rx::iterator_range(first, last));
1043 }
1044
1046 } // namespace congruence_common
1047} // namespace libsemigroups
1048
1049#include "cong-common-helpers.tpp"
1050
1051#endif // LIBSEMIGROUPS_CONG_COMMON_HELPERS_HPP_
T begin(T... args)
T end(T... args)
void add_generating_pair(Thing &thing, typename Thing::native_word_type const &u, typename Thing::native_word_type const &v)
Helper for adding a generating pair of words.
Definition cong-common-helpers.hpp:199
void add_generating_pair_no_checks(Thing &thing, typename Thing::native_word_type const &u, typename Thing::native_word_type const &v)
Helper for adding a generating pair of words.
Definition cong-common-helpers.hpp:111
bool contains_no_checks(Thing &thing, typename Thing::native_word_type const &u, typename Thing::native_word_type const &v)
Check containment of a pair of words.
Definition cong-common-helpers.hpp:512
bool contains(Thing &thing, typename Thing::native_word_type const &u, typename Thing::native_word_type const &v)
Check containment of a pair of words.
Definition cong-common-helpers.hpp:604
tril currently_contains(Thing const &thing, typename Thing::native_word_type const &u, typename Thing::native_word_type const &v)
Check containment of a pair of words.
Definition cong-common-helpers.hpp:424
tril currently_contains_no_checks(Thing const &thing, typename Thing::native_word_type const &u, typename Thing::native_word_type const &v)
Check containment of a pair of words.
Definition cong-common-helpers.hpp:321
std::vector< std::vector< typename Thing::native_word_type > > non_trivial_classes(Thing &thing, Range r)
Find the non-trivial classes in the partition of a range of words.
std::vector< std::vector< typename Thing::native_word_type > > partition(Thing &thing, Range r)
Partition a range of words.
Thing::native_word_type reduce(Thing const &thing, typename Thing::native_word_type const &w)
Reduce a word.
Thing::native_word_type reduce_no_run_no_checks(Thing const &thing, typename Thing::native_word_type const &w)
Reduce a word with no enumeration or checks.
Thing::native_word_type reduce_no_checks(Thing const &thing, typename Thing::native_word_type const &w)
Reduce a word with no checks.
Thing::native_word_type reduce_no_run(Thing const &thing, typename Thing::native_word_type const &w)
Reduce a word with no enumeration.
tril
Enum to indicate true, false or not currently knowable.
Definition types.hpp:54
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
T strlen(T... args)