libsemigroups  v3.0.0
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-2025 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 "ranges.hpp" // for is_input_or_sink_v, iterator_...
44
45#include "detail/string.hpp" // for throw_if_nullptr
46
47namespace libsemigroups {
48
49 // Forward decls
50 template <typename Word>
51 class Congruence;
52
53 template <typename Word>
54 class ToddCoxeter;
55
56 enum class tril;
57
58 namespace detail {
59 class ToddCoxeterImpl;
60 class CongruenceCommon;
61 struct CongruenceBase;
62
63 } // namespace detail
64
65 namespace congruence_common {
66
75
77 // Interface helpers - add_generating_pair
79
90
107 // NOTE: we use native_word_type and not another template param to avoid
108 // unexpected behaviour, if for example we add words which are strings to a
109 // ToddCoxeter<word_type>, then unexpected things might happen.
110 template <typename Thing>
111 void
113 typename Thing::native_word_type const& u,
114 typename Thing::native_word_type const& v) {
115 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
116 thing.add_generating_pair_no_checks(
117 std::begin(u), std::end(u), std::begin(v), std::end(v));
118 }
119
129 template <typename Thing, typename Int>
133 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
134 static_assert(std::is_integral_v<Int>);
135 thing.add_generating_pair_no_checks(
136 std::begin(u), std::end(u), std::begin(v), std::end(v));
137 }
138
146 template <typename Thing>
148 char const* u,
149 char const* v) {
150 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
151 LIBSEMIGROUPS_ASSERT(u != nullptr);
152 LIBSEMIGROUPS_ASSERT(v != nullptr);
153 // We could static_assert that Thing::native_word_type == std::string,
154 // but it doesn't seem that adding this restriction would gain us
155 // anything, so it is not currently done.
156 thing.add_generating_pair_no_checks(
157 u, u + std::strlen(u), v, v + std::strlen(v));
158 }
159
160 // This version of the function catches the cases when u & v are not of
161 // the same type but both convertible to string_view
168 template <typename Thing>
170 std::string_view u,
171 std::string_view v) {
172 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
173 // We could static_assert that Thing::native_word_type == std::string,
174 // but it doesn't seem that adding this restriction would gain us
175 // anything, so it is not currently done.
176 thing.add_generating_pair_no_checks(
177 std::begin(u), std::end(u), std::begin(v), std::end(v));
178 }
179
196 // NOTE: we use native_word_type and not another template param to avoid
197 // unexpected behaviour, if for example we add words which are strings to a
198 // ToddCoxeter<word_type>, then unexpected things might happen.
199 template <typename Thing>
200 void add_generating_pair(Thing& thing,
201 typename Thing::native_word_type const& u,
202 typename Thing::native_word_type const& v) {
203 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
204 thing.add_generating_pair(
205 std::begin(u), std::end(u), std::begin(v), std::end(v));
206 }
207
216 template <typename Thing, typename Int>
217 void add_generating_pair(Thing& thing,
220 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
221 static_assert(std::is_integral_v<Int>);
222 thing.add_generating_pair(
223 std::begin(u), std::end(u), std::begin(v), std::end(v));
224 }
225
232 template <typename Thing>
233 void add_generating_pair(Thing& thing, char const* u, char const* v) {
234 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
235 detail::throw_if_nullptr(u, "2nd");
236 detail::throw_if_nullptr(v, "3rd");
237 // We could static_assert that Thing::native_word_type == std::string,
238 // but it doesn't seem that adding this restriction would gain us
239 // anything, so it is not currently done.
240 thing.add_generating_pair(u, u + std::strlen(u), v, v + std::strlen(v));
241 }
242
243 // This version of the function catches the cases when u & v are not of
244 // the same type but both convertible to string_view
251 template <typename Thing>
252 void add_generating_pair(Thing& thing,
253 std::string_view u,
254 std::string_view v) {
255 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
256 // We could static_assert that Thing::native_word_type == std::string,
257 // but it doesn't seem that adding this restriction would gain us
258 // anything, so it is not currently done.
259 thing.add_generating_pair_no_checks(
260 std::begin(u), std::end(u), std::begin(v), std::end(v));
261 }
262
264
266 // Interface helpers - currently_contains_no_checks
268
292
317 // NOTE: we use native_word_type and not another template param to avoid
318 // unexpected behaviour, if for example we add words which are strings to a
319 // ToddCoxeter<word_type>, then unexpected things might happen.
320 template <typename Thing>
321 [[nodiscard]] tril
323 typename Thing::native_word_type const& u,
324 typename Thing::native_word_type const& v) {
325 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
326 return thing.currently_contains_no_checks(
327 std::begin(u), std::end(u), std::begin(v), std::end(v));
328 }
329
339 template <typename Thing, typename Int>
340 [[nodiscard]] tril
344 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
345 static_assert(std::is_integral_v<Int>);
346 return thing.currently_contains_no_checks(
347 std::begin(u), std::end(u), std::begin(v), std::end(v));
348 }
349
357 template <typename Thing>
358 [[nodiscard]] tril currently_contains_no_checks(Thing const& thing,
359 char const* u,
360 char const* v) {
361 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
362 LIBSEMIGROUPS_ASSERT(u != nullptr);
363 LIBSEMIGROUPS_ASSERT(v != nullptr);
364 // We could static_assert that Thing::native_word_type == std::string,
365 // but it doesn't seem that adding this restriction would gain us
366 // anything, so it is not currently done.
367 return thing.currently_contains_no_checks(
368 u, u + std::strlen(u), v, v + std::strlen(v));
369 }
370
378 // NOTE: This version of the function catches the cases when u & v are not
379 // of the same type but both convertible to string_view
380 template <typename Thing>
381 [[nodiscard]] tril currently_contains_no_checks(Thing const& thing,
382 std::string_view u,
383 std::string_view v) {
384 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
385 // We could static_assert that Thing::native_word_type == std::string,
386 // but it doesn't seem that adding this restriction would gain us
387 // anything, so it is not currently done.
388 return thing.currently_contains_no_checks(
389 std::begin(u), std::end(u), std::begin(v), std::end(v));
390 }
391
393 // Interface helpers - currently_contains
395
420 // NOTE: we use native_word_type and not another template param to avoid
421 // unexpected behaviour, if for example we add words which are strings to a
422 // ToddCoxeter<word_type>, then unexpected things might happen.
423 template <typename Thing>
424 [[nodiscard]] tril
425 currently_contains(Thing const& thing,
426 typename Thing::native_word_type const& u,
427 typename Thing::native_word_type const& v) {
428 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
429 return thing.currently_contains(
430 std::begin(u), std::end(u), std::begin(v), std::end(v));
431 }
432
441 template <typename Thing, typename Int>
442 [[nodiscard]] tril currently_contains(Thing const& thing,
445 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
446 static_assert(std::is_integral_v<Int>);
447 return thing.currently_contains(
448 std::begin(u), std::end(u), std::begin(v), std::end(v));
449 }
450
457 template <typename Thing>
458 [[nodiscard]] tril currently_contains(Thing const& thing,
459 char const* u,
460 char const* v) {
461 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
462 detail::throw_if_nullptr(u, "2nd");
463 detail::throw_if_nullptr(v, "3rd");
464 return thing.currently_contains(
465 u, u + std::strlen(u), v, v + std::strlen(v));
466 }
467
468 // This version of the function catches the cases when u & v are not of
469 // the same type but both convertible to string_view
476 template <typename Thing>
477 [[nodiscard]] tril currently_contains(Thing const& thing,
478 std::string_view u,
479 std::string_view v) {
480 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
481 return thing.currently_contains(
482 std::begin(u), std::end(u), std::begin(v), std::end(v));
483 }
484
486 // Interface helpers - contains_no_checks
488
508 // NOTE: we use native_word_type and not another template param to avoid
509 // unexpected behaviour, if for example we add words which are strings to a
510 // ToddCoxeter<word_type>, then unexpected things might happen.
511 template <typename Thing>
512 [[nodiscard]] bool
513 contains_no_checks(Thing& thing,
514 typename Thing::native_word_type const& u,
515 typename Thing::native_word_type const& v) {
516 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
517 return thing.contains_no_checks(
518 std::begin(u), std::end(u), std::begin(v), std::end(v));
519 }
520
529 template <typename Thing, typename Int>
530 [[nodiscard]] bool contains_no_checks(Thing& thing,
533 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
534 static_assert(std::is_integral_v<Int>);
536 }
537
544 template <typename Thing>
545 [[nodiscard]] bool contains_no_checks(Thing& thing,
546 char const* u,
547 char const* v) {
548 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
549 LIBSEMIGROUPS_ASSERT(u != nullptr);
550 LIBSEMIGROUPS_ASSERT(v != nullptr);
551 // We could static_assert that Thing::native_word_type == std::string,
552 // but it doesn't seem that adding this restriction would gain us
553 // anything, so it is not currently done.
554 return thing.contains_no_checks(
555 u, u + std::strlen(u), v, v + std::strlen(v));
556 }
557
558 // This version of the function catches the cases when u & v are not of
559 // the same type but both convertible to string_view
566 template <typename Thing>
567 [[nodiscard]] bool contains_no_checks(Thing& thing,
568 std::string_view u,
569 std::string_view v) {
570 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
571 // We could static_assert that Thing::native_word_type == std::string,
572 // but it doesn't seem that adding this restriction would gain us
573 // anything, so it is not currently done.
574 return thing.contains_no_checks(
575 std::begin(u), std::end(u), std::begin(v), std::end(v));
576 }
577
579 // Interface helpers - contains
581
601 // NOTE: we use native_word_type and not another template param to avoid
602 // unexpected behaviour, if for example we add words which are strings to a
603 // ToddCoxeter<word_type>, then unexpected things might happen.
604 template <typename Thing>
605 [[nodiscard]] bool contains(Thing& thing,
606 typename Thing::native_word_type const& u,
607 typename Thing::native_word_type const& v) {
608 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
609 return thing.contains(
610 std::begin(u), std::end(u), std::begin(v), std::end(v));
611 }
612
621 template <typename Thing, typename Int>
622 [[nodiscard]] bool contains(Thing& thing,
625 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
626 static_assert(std::is_integral_v<Int>);
627 return thing.contains(
628 std::begin(u), std::end(u), std::begin(v), std::end(v));
629 }
630
631 // This version of the function catches the cases when u & v are not of
632 // the same type but both convertible to string_view
639 template <typename Thing>
640 [[nodiscard]] bool contains(Thing& thing,
641 std::string_view u,
642 std::string_view v) {
643 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
644 // We could static_assert that Thing::native_word_type == std::string,
645 // but it doesn't seem that adding this restriction would gain us
646 // anything, so it is not currently done.
647 return thing.contains(
648 std::begin(u), std::end(u), std::begin(v), std::end(v));
649 }
650
657 template <typename Thing>
658 [[nodiscard]] bool contains(Thing& thing, char const* u, char const* v) {
659 static_assert(std::is_base_of_v<detail::CongruenceCommon, Thing>);
660 detail::throw_if_nullptr(u, "2nd");
661 detail::throw_if_nullptr(v, "3rd");
662 // We could static_assert that Thing::native_word_type == std::string,
663 // but it doesn't seem that adding this restriction would gain us
664 // anything, so it is not currently done.
665 return thing.contains(u, u + std::strlen(u), v, v + std::strlen(v));
666 }
667
669
671 // Interface helpers - reduce_no_run_no_checks
673
697
720 // NOTE: we use native_word_type and not another template param to avoid
721 // unexpected behaviour, if for example we add words which are strings to a
722 // ToddCoxeter<word_type>, then unexpected things might happen.
723 template <typename Thing>
724 [[nodiscard]] typename Thing::native_word_type
725 reduce_no_run_no_checks(Thing const& thing,
726 typename Thing::native_word_type const& w);
727
728 // No string_view version is required because there is only a single
729 // "word" parameter, and so the first template will catch every case
730 // except initializer_list and char const*
731
740 template <typename Thing, typename Int>
741 [[nodiscard]] typename Thing::native_word_type
742 reduce_no_run_no_checks(Thing const& thing,
744
751 template <typename Thing>
752 [[nodiscard]] typename Thing::native_word_type
753 reduce_no_run_no_checks(Thing const& thing, char const* w);
754
756 // Interface helpers - reduce_no_run
758
781 // NOTE: we use native_word_type and not another template param to avoid
782 // unexpected behaviour, if for example we add words which are strings to a
783 // ToddCoxeter<word_type>, then unexpected things might happen.
784 template <typename Thing>
785 [[nodiscard]] typename Thing::native_word_type
786 reduce_no_run(Thing const& thing,
787 typename Thing::native_word_type const& w);
788
789 // No string_view version is required because there is only a single
790 // "w" parameter, and so the first template will catch every case
791 // except initializer_list and char const*
792
800 template <typename Thing, typename Int>
801 [[nodiscard]] typename Thing::native_word_type
802 reduce_no_run(Thing const& thing, std::initializer_list<Int> const& w);
803
809 template <typename Thing>
810 [[nodiscard]] typename Thing::native_word_type
811 reduce_no_run(Thing const& thing, char const* w);
812
814 // Interface helpers - reduce_no_checks
816
836 // NOTE: we use native_word_type and not another template param to avoid
837 // unexpected behaviour, if for example we add words which are strings to a
838 // ToddCoxeter<word_type>, then unexpected things might happen.
839 template <typename Thing>
840 [[nodiscard]] typename Thing::native_word_type
841 reduce_no_checks(Thing const& thing,
842 typename Thing::native_word_type const& w);
843
844 // No string_view version is required because there is only a single
845 // "word" parameter, and so the first template will catch every case
846 // except initializer_list and char const*
847
855 template <typename Thing, typename Int>
856 [[nodiscard]] typename Thing::native_word_type
857 reduce_no_checks(Thing const& thing, std::initializer_list<Int> const& w);
858
864 template <typename Thing>
865 [[nodiscard]] typename Thing::native_word_type
866 reduce_no_checks(Thing const& thing, char const* w);
867
869 // Interface helpers - reduce
890 // NOTE: we use native_word_type and not another template param to avoid
891 // unexpected behaviour, if for example we add words which are strings to a
892 // ToddCoxeter<word_type>, then unexpected things might happen.
893 template <typename Thing>
894 [[nodiscard]] typename Thing::native_word_type
895 reduce(Thing const& thing, typename Thing::native_word_type const& w);
896
897 // No string_view version is required because there is only a single
898 // "word" parameter, and so the first template will catch every case
899 // except initializer_list and char const*
900
908 template <typename Thing, typename Int>
909 [[nodiscard]] typename Thing::native_word_type
910 reduce(Thing const& thing, std::initializer_list<Int> const& w);
911
917 template <typename Thing>
918 [[nodiscard]] typename Thing::native_word_type reduce(Thing const& thing,
919 char const* w);
920
922
924 // Interface helpers - normal_forms
926
927 // There's nothing in common to implement in this file.
928
930 // Interface helpers - partition
932
945
946#ifndef LIBSEMIGROUPS_PARSED_BY_DOXYGEN
947 // Forward decls defined in todd-coxeter-helpers.tpp and cong-helpers.tpp
948 template <typename Word,
949 typename Range,
950 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
951 [[nodiscard]] std::vector<std::vector<Word>>
952 partition(ToddCoxeter<Word>& thing, Range r);
953
954 template <typename Word,
955 typename Range,
956 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
957 [[nodiscard]] std::vector<std::vector<Word>>
958 partition(Congruence<Word>& thing, Range r);
959#endif
960
983 template <typename Thing,
984 typename Range,
985 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
987 partition(Thing& thing, Range r);
988
994 template <typename Thing, typename Iterator1, typename Iterator2>
996 partition(Thing& thing, Iterator1 first, Iterator2 last) {
997 // static asserts are in done in the next call to partition
998 return partition(thing, rx::iterator_range(first, last));
999 }
1000
1002 // Interface helpers - non_trivial_classes
1004
1028 template <typename Thing,
1029 typename Range,
1030 typename = std::enable_if_t<rx::is_input_or_sink_v<Range>>>
1032 non_trivial_classes(Thing& thing, Range r);
1033
1040 template <typename Thing, typename Iterator1, typename Iterator2>
1042 non_trivial_classes(Thing& thing, Iterator1 first, Iterator2 last) {
1043 return non_trivial_classes(thing, rx::iterator_range(first, last));
1044 }
1045
1047 } // namespace congruence_common
1048} // namespace libsemigroups
1049
1050#include "cong-common-helpers.tpp"
1051
1052#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:200
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:112
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:513
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:605
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:425
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:322
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:56
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
T strlen(T... args)