diff --git a/Makefile.am b/Makefile.am index 22336da7d..04d14a5be 100644 --- a/Makefile.am +++ b/Makefile.am @@ -812,6 +812,7 @@ EXTRA_PROGRAMS += test_cong_all EXTRA_PROGRAMS += test_cong_common EXTRA_PROGRAMS += test_constants EXTRA_PROGRAMS += test_containers +EXTRA_PROGRAMS += test_docs_code_examples EXTRA_PROGRAMS += test_dot EXTRA_PROGRAMS += test_felsch_tree EXTRA_PROGRAMS += test_forest @@ -899,6 +900,7 @@ test_all_SOURCES += tests/test-cong-common.cpp test_all_SOURCES += tests/test-cong.cpp test_all_SOURCES += tests/test-constants.cpp test_all_SOURCES += tests/test-containers.cpp +test_all_SOURCES += tests/test-docs-code-examples.cpp test_all_SOURCES += tests/test-dot.cpp test_all_SOURCES += tests/test-felsch-tree.cpp test_all_SOURCES += tests/test-forest.cpp @@ -1013,6 +1015,10 @@ test_containers_SOURCES = tests/test-containers.cpp test_containers_SOURCES += tests/test-main.cpp test_containers_SOURCES += third_party/Catch2-3.8.0/catch_amalgamated_patch.cpp +test_docs_code_examples_SOURCES = tests/test-docs-code-examples.cpp +test_docs_code_examples_SOURCES += tests/test-main.cpp +test_docs_code_examples_SOURCES += third_party/Catch2-3.8.0/catch_amalgamated_patch.cpp + test_dot_SOURCES = tests/test-dot.cpp test_dot_SOURCES += tests/test-main.cpp test_dot_SOURCES += third_party/Catch2-3.8.0/catch_amalgamated_patch.cpp diff --git a/etc/extract-doc-examples.py b/etc/extract-doc-examples.py new file mode 100644 index 000000000..799440c9f --- /dev/null +++ b/etc/extract-doc-examples.py @@ -0,0 +1,357 @@ +""" +Automated code block extraction from documentation for testing. +""" + +import sys +import argparse +import subprocess +import re +from pathlib import Path + +######################################################################## +# Constants +######################################################################## + +HEADER_TEXT = """// libsemigroups - C++ library for semigroups and monoids +// Copyright (C) 2021-2025 James D. Mitchell + Maria Tsalakou +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "Catch2-3.8.0/catch_amalgamated.hpp" // for REQUIRE, REQUIRE_NOTHROW, REQUIRE_THROWS_AS +#include "libsemigroups/libsemigroups.hpp" // for * +#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE + +using namespace libsemigroups::literals; +""" + +TEST_FILEPATH = "./tests/test-docs-code-examples.cpp" +######################################################################## +# Internal +######################################################################## + + +def __error(msg: str) -> None: + sys.stderr.write(f"\033[0;31m{msg}\n\033[0m") + + +def __bold(msg: str) -> None: + sys.stderr.write(f"\033[1m{msg}\n\033[0m") + + +def __parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Extract code example blocks from documentation.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" + Examples: + python3 extract_code.py <-- Process specific folder + Flags: + -r | Process folder recursively + -e | Exclude the following files + """, + ) + + parser.add_argument( + "folder_path", + nargs="?", + default=".", + help="Path to the folder to process (default: current directory)", + ) + + parser.add_argument( + "-r", + "--recursive", + action="store_true", + help="Process subdirectories recursively", + ) + + parser.add_argument( + "-e", + "--exclude", + nargs="+", + help="Files to exclude from docs code example colleciton", + ) + + parser.add_argument( + "-v", + "--version", + action="version", + version="Documentation Code Block Extraction 0.1.0", + ) + + args = parser.parse_args() + + return args + + +######################################################################## +# Code block extraction +######################################################################## + + +def extract_code_blocks(file_path): + """ + Extract code examples from a file. + + Args: + file_path (Path): Path to the file to process + + Returns: + list: List of code blocks found in the file + [["content", "start_line"], ...] + """ + code_blocks = [] + + try: + with open(file_path, "r", encoding="utf-8", errors="ignore") as file: + in_code_block = False + ignore_this_block = False + current_block = [] + + for line_num, line in enumerate(file, 1): + line = line.lstrip() + line = line.removeprefix("//!") + line = line.rstrip("\n\r") + + # Code Block Open + if "\\code" in line: + if in_code_block: + __error( + f"Warning: Found \\code while already in code block at { + file_path + }:{line_num}" + ) + in_code_block = True + current_block = [] + continue + + #  Code Block Close + if "\\endcode" in line: + if not in_code_block: + __error( + f"Warning: Found \\endcode without matching \\code at { + file_path + }:{line_num}" + ) + elif not ignore_this_block: + code_blocks.append( + { + "content": "\n".join(current_block), + "start_line": line_num - len(current_block), + } + ) + in_code_block = False + ignore_this_block = False + current_block = [] + continue + + if in_code_block: + if "using namespace libsemigroups" in line: + continue + + # If line contains non-executable statements, + # skip this block. + if "\\skip-test" in line: + ignore_this_block = True + current_block = [] + __bold( + f"Note: Example code block skipped in {file_path} at line { + line_num + }" + ) + continue + + # If line contains a function call with a return value + # labelled by '//-> ' + # where ::= | + if "//->" in line: + parts = line.split("//->") + function_call = parts[0].strip().strip(";") + function_returns = parts[1].strip() + + #  validate format of return + #  TODO: This + + #  Add require value eq + current_block.append( + f" REQUIRE({function_call} == { + function_returns});" + ) + continue + + if not ignore_this_block: + current_block.append(line) + + # discard unclosed code blocks + if in_code_block and current_block: + __error( + f"Warning: Unclosed code block at end of file {file_path}") + + except Exception as e: + print(f"Error reading file {file_path}: {e}") + + return code_blocks + + +######################################################################## +# File Iteration +######################################################################## + + +def process_folder(folder_path, recursive=False, exclude=[]): + """ + Process all files in a folder to find code examples. + + Args: + folder_path (string): Path to the folder to process + recursive (bool): Whether to process subdirectories recursively + """ + folder = Path(folder_path) + + if not folder.exists(): + print(f"Error: Directory '{folder_path}' does not exist.") + sys.exit(1) + + if not folder.is_dir(): + print(f"Error: '{folder_path}' is not a directory.") + sys.exit(1) + + print(f"Searching for code blocks in folder: {folder.absolute()}") + if recursive: + print("Processing subdirectories recursively...") + print("=" * 60) + + # Get files + if recursive: + files = folder.rglob("*") + else: + files = folder.glob("*") + + # Filter + files = [f for f in files if f.is_file()] + + if len(files) == 0: + print("No files found in the specified directory.") + return + + total_blocks = 0 + + try: + with open(TEST_FILEPATH, "w") as testfile: + testfile.write(f"{HEADER_TEXT}\n") + testfile.write("namespace libsemigroups {\n") # Open namespace + + for file_path in sorted(files): + # Extract example code + if (Path(file_path).name in exclude) or (file_path in exclude): + code_blocks = [] + __bold( + f"Note: Excluded { + file_path + } from example code block extraction." + ) + else: + code_blocks = extract_code_blocks(file_path) + + # Write to cpp test file + if code_blocks: + for i, block in enumerate(code_blocks, 1): + testfile.write( + f'// {file_path.relative_to(folder)}: Line { + block["start_line"] + }\nLIBSEMIGROUPS_TEST_CASE("docs", "{ + "{:03}".format(total_blocks) + }", "{ + file_path.relative_to(folder) + }", "[docs][quick]") {{\n' + ) + if block["content"].strip(): + testfile.write(" " + block["content"]) + else: + testfile.write("// ~ empty code block ~") + testfile.write("\n}\n\n") + total_blocks += 1 + + testfile.write("\n}") # Close namespace + except IOError as e: + print(f"Could not write to test file: {e}") + + if total_blocks == 0: + print("No code blocks found in any files.") + else: + print(f"Total code blocks found: {total_blocks}") + + +######################################################################## +# File Formatting +######################################################################### + + +def check_clang_format_version() -> bool: + """Check that clang-format@15 is installed.""" + try: + res = subprocess.run( + ["clang-format", "--version"], capture_output=True, text=True, check=True + ) + version_check = re.search(r"version\s+(\d+)", res.stdout) + + if not version_check: + __error("Could not match clang-format --version") + return False + + ver = int(version_check.group(1)) + + if ver != 15: + __error("clang-format@15 is required") + return False + else: + return True + except FileNotFoundError: + __error("clang-format@15 is not installed") + return False + except subprocess.CalledProcessError as e: + __error(f"clang-format --version error: {e}") + return False + + +def format_file(filepath): + if not check_clang_format_version(): + __error("Unable to format file, see clang-format related error") + return + + try: + subprocess.run(["clang-format", "-i", filepath], check=True) + print(f"\n Formatted {filepath}") + except subprocess.CalledProcessError as e: + __error(f"Failed to format file {filepath} : {e}") + except FileNotFoundError: + __error(f"Could not find file {filepath} to format") + + +######################################################################## +# Main +######################################################################### + + +def main(): + args = __parse_args() + exclude = args.exclude if not args.exclude is None else [] + process_folder(args.folder_path, args.recursive, exclude) + format_file(TEST_FILEPATH) + print("\n Docs code block extraction completed successfully. Exiting.") + + +if __name__ == "__main__": + main() diff --git a/include/libsemigroups/action.hpp b/include/libsemigroups/action.hpp index 550dca3fe..0ccd6e0f0 100644 --- a/include/libsemigroups/action.hpp +++ b/include/libsemigroups/action.hpp @@ -59,7 +59,7 @@ namespace libsemigroups { //! using namespace libsemigroups; //! RightAction, PPerm<16>, ImageRightAction, PPerm<16>>> //! o; - //! o.add_seed(PPerm<16>::identity(16)); + //! o.add_seed(PPerm<16>::one(16)); //! o.add_generator( //! PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, //! {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}, @@ -77,7 +77,7 @@ namespace libsemigroups { //! {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, //! 16)); //! o.reserve(70000); - //! o.size(); // returns 65536 + //! o.size(); //-> 65536 //! \endcode //! \ingroup types_group @@ -143,7 +143,7 @@ namespace libsemigroups { //! auto rg = ReportGuard(true); //! RightAction, PPerm<16>, ImageRightAction, PPerm<16>>> //! o; - //! o.add_seed(PPerm<16>::identity(16)); + //! o.add_seed(PPerm<16>::one(16)); //! o.add_generator( //! PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, //! {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}, @@ -161,8 +161,8 @@ namespace libsemigroups { //! {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, //! 16)); //! o.reserve(70000); - //! o.size(); // 65536 - //! o.scc().number_of_components(); // 17 + //! o.size(); //-> 65536 + //! o.scc().number_of_components(); //-> 17 //! \endcode //! //! \par Complexity diff --git a/include/libsemigroups/adapters.hpp b/include/libsemigroups/adapters.hpp index df7ed8d7c..7394d94b2 100644 --- a/include/libsemigroups/adapters.hpp +++ b/include/libsemigroups/adapters.hpp @@ -117,6 +117,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template <> //! struct Complexity { //! constexpr size_t operator()(KBE const&) const noexcept { @@ -155,6 +156,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template <> //! struct Degree { //! constexpr inline size_t operator()(BMat8 const&) const noexcept { @@ -193,6 +195,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template //! struct IncreaseDegree< //! Integral, @@ -220,6 +223,7 @@ namespace libsemigroups { //! 2. `Element operator()(T const&) const` (possibly `noexcept`, //! `inline` and/or `constexpr` also). This could be implemented as: //! \code + //! \skip-test //! Element operator()(Element const& x) const noexcept { //! return this->operator()(Degree()(x)); //! } @@ -236,6 +240,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template //! struct One< //! T, @@ -279,6 +284,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template <> //! struct Product { //! void operator()(size_t& xy, size_t x, size_t y, size_t = 0) const @@ -314,6 +320,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template <> //! struct Inverse { //! inline BMat8 operator()(BMat8 const& x) const noexcept { @@ -346,6 +353,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template <> //! struct ImageLeftAction { //! void operator()(BMat8& res, BMat8 pt, BMat8 x) const noexcept { @@ -388,6 +396,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template <> //! struct ImageLeftAction { //! void operator()(BMat8& res, BMat8 pt, BMat8 x) const noexcept { diff --git a/include/libsemigroups/cong-class.hpp b/include/libsemigroups/cong-class.hpp index b14b56382..4d9414fa0 100644 --- a/include/libsemigroups/cong-class.hpp +++ b/include/libsemigroups/cong-class.hpp @@ -146,12 +146,14 @@ namespace libsemigroups { //! \par Example //! \code //! Presentation p; - //! p.alphabet(2) + //! p.alphabet(2); + //! p.contains_empty_word(true); //! presentation::add_rule(p, {0, 1}, {}); + //! //! Congruence cong(congruence_kind::twosided, p); //! is_obviously_infinite(cong); // true //! congruence_common::add_generating_pair(cong, {0, 0, 0}, {}); - //! cong.number_of_classes(); // 3 + //! cong.number_of_classes(); //-> 3 //! \endcode template class Congruence : public detail::CongruenceCommon, diff --git a/include/libsemigroups/freeband.hpp b/include/libsemigroups/freeband.hpp index 4632221e6..74e7d748a 100644 --- a/include/libsemigroups/freeband.hpp +++ b/include/libsemigroups/freeband.hpp @@ -76,7 +76,7 @@ namespace libsemigroups { //! freeband_equal_to({0, 1, 2, 3, 2, 1, 0}, //! {0, 1, 2, 3, 2, 3, 2, 1, 0}); // true //! freeband_equal_to({1, 2, 3}, {0, 1, 2}); // false - //! freeband_equal_to({1, 4, 2, 3, 10}, {1, 4, 1, 4, 2, 3, 10}) // true + //! freeband_equal_to({1, 4, 2, 3, 10}, {1, 4, 1, 4, 2, 3, 10}); // true //! freeband_equal_to({0, 1, 2, 3, 4, 0, 1, 2, 3, 4}, //! {4, 3, 2, 1, 0, 4, 3, 2, 1, 0}); // false //! freeband_equal_to({0, 1, 2, 1, 0, 1, 2}, {0, 1, 2}); // true diff --git a/include/libsemigroups/froidure-pin-base.hpp b/include/libsemigroups/froidure-pin-base.hpp index 45a82e0ab..6bd6e17be 100644 --- a/include/libsemigroups/froidure-pin-base.hpp +++ b/include/libsemigroups/froidure-pin-base.hpp @@ -1351,7 +1351,7 @@ namespace libsemigroups { //! {0, 0, 0, 1}, //! {0, 0, 0, 1}, //! {0, 0, 0, 1}})); - //! S.size(); // 4 + //! S.size(); //-> 4 //! std::vector(S.cbegin_rules(), S.cend_rules()); //! // {{{0, 0}, {0}}, //! // {{0, 1}, {1}}, @@ -1455,7 +1455,7 @@ namespace libsemigroups { //! {0, 0, 0, 1}, //! {0, 0, 0, 1}, //! {0, 0, 0, 1}})); - //! S.size(); // 4 + //! S.size(); //-> 4 //! std::vector(S.cbegin_rules(), S.cend_rules()); //! // {{{0, 0}, {0}}, //! // {{0, 1}, {1}}, diff --git a/include/libsemigroups/froidure-pin.hpp b/include/libsemigroups/froidure-pin.hpp index 68e0ea400..1900fe86a 100644 --- a/include/libsemigroups/froidure-pin.hpp +++ b/include/libsemigroups/froidure-pin.hpp @@ -134,6 +134,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! \skip-test //! template <> //! struct Complexity { //! constexpr size_t operator()(int) const noexcept { @@ -171,15 +172,18 @@ namespace libsemigroups { //! xy = x * y; //! } //! }; - //! + //! \endcode + //! \code + //! \skip-test This is closer to a style guide. + //! using namespace froidure_pin; //! FroidurePin S({2}); //! S.size(); // 32 - //! S.number_of_idempotents() // 1 + //! S.number_of_idempotents(); // 1 //! *S.cbegin(); // 2 //! //! FroidurePin T({2, 3}); - //! T.size() // 130 - //! T.number_of_idempotents() // 2 + //! T.size(); // 130 + //! T.number_of_idempotents(); // 2 //! *T.cbegin_idempotents(); // 0 //! *T.cbegin_idempotents() + 1; // 1 //! \endcode @@ -313,7 +317,7 @@ namespace libsemigroups { private: template static constexpr bool IsState - = ((!std::is_void_v) &&std::is_same_v); + = ((!std::is_void_v) && std::is_same_v); //////////////////////////////////////////////////////////////////////// // FroidurePin - data - private @@ -1383,8 +1387,8 @@ namespace libsemigroups { void expand(size_type); void is_one(internal_const_element_type x, element_index_type) noexcept( - std::is_nothrow_default_constructible_v&& noexcept( - std::declval()(x, x))); + std::is_nothrow_default_constructible_v + && noexcept(std::declval()(x, x))); void copy_generators_from_elements(size_t); void closure_update(element_index_type, diff --git a/include/libsemigroups/is_specialization_of.hpp b/include/libsemigroups/is_specialization_of.hpp index 33431f689..cb756f4ae 100644 --- a/include/libsemigroups/is_specialization_of.hpp +++ b/include/libsemigroups/is_specialization_of.hpp @@ -41,9 +41,11 @@ namespace libsemigroups { //! //! \par Example //! \code - //! is_specialization_of, std::vector>::value // true - //! is_specialization_of, std::unordered_map>::value - //! // false + //! using iso_1= is_specialization_of, std::vector>; + //! iso_1::value; //-> true + //! using iso_2 = is_specialization_of,std::unordered_map>; + //! iso_2::value; //-> false + //! //! \endcode //! //! \note The template parameters of \p Primary must be types, so, for @@ -71,8 +73,8 @@ namespace libsemigroups { //! //! \par Example //! \code - //! is_specialization_of_v, std::vector> // true - //! is_specialization_of_v, std::unordered_map> // false + //! is_specialization_of_v, std::vector>; //-> true + //! is_specialization_of_v, std::unordered_map>; //-> false //! \endcode //! //! \note The template parameters of \p Primary must be types, so, for diff --git a/include/libsemigroups/knuth-bendix-class.hpp b/include/libsemigroups/knuth-bendix-class.hpp index 01183de73..7e5c092aa 100644 --- a/include/libsemigroups/knuth-bendix-class.hpp +++ b/include/libsemigroups/knuth-bendix-class.hpp @@ -77,13 +77,15 @@ namespace libsemigroups { //! presentation::add_rule_no_checks(p, "cd", ""); //! presentation::add_rule_no_checks(p, "dc", ""); //! - //! KnuthBendix kb(twosided, p); + //! KnuthBendix kb(congruence_kind::twosided, p); //! - //! !kb.confluent(); // true + //! kb.number_of_active_rules(); //-> 0 + //! kb.number_of_pending_rules(); //-> 4 //! kb.run(); - //! kb.number_of_active_rules(); // 8 - //! kb.confluent(); // true - //! kb.number_of_classes(); // POSITIVE_INFINITY + //! kb.number_of_active_rules(); //-> 4 + //! kb.number_of_pending_rules(); //-> 0 + //! kb.confluent(); //-> true + //! kb.number_of_classes(); //-> POSITIVE_INFINITY //! \endcode //! //! \warning At present it is only possible to create KnuthBendix objects from diff --git a/include/libsemigroups/konieczny.hpp b/include/libsemigroups/konieczny.hpp index b97659b4d..1a458687f 100644 --- a/include/libsemigroups/konieczny.hpp +++ b/include/libsemigroups/konieczny.hpp @@ -1905,6 +1905,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! make(gens); //! \endcode //! @@ -1949,6 +1950,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! make(gens); //! \endcode //! diff --git a/include/libsemigroups/matrix.hpp b/include/libsemigroups/matrix.hpp index 0c120690d..f3fef2c25 100644 --- a/include/libsemigroups/matrix.hpp +++ b/include/libsemigroups/matrix.hpp @@ -1524,8 +1524,8 @@ namespace libsemigroups { private: using DynamicMatrix_ = DynamicMatrix; using RowViewCommon = detail::RowViewCommon< - DynamicMatrix_, - DynamicRowView>; + DynamicMatrix_, + DynamicRowView>; friend RowViewCommon; public: @@ -1961,6 +1961,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! using Mat = BMat8; //! Mat m({{1, 1}, {0, 0}}); //! \endcode explicit StaticMatrix( @@ -2780,9 +2781,9 @@ namespace libsemigroups { MatrixStaticArithmetic { using MatrixDynamicDim = ::libsemigroups::detail::MatrixDynamicDim; using MatrixCommon = ::libsemigroups::detail::MatrixCommon< - std::vector, - DynamicMatrix, - DynamicRowView>; + std::vector, + DynamicMatrix, + DynamicRowView>; friend MatrixCommon; public: @@ -2863,6 +2864,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! using Mat = IntMat<>; //! Mat m(2, 3); // construct a 2 x 3 matrix //! \endcode DynamicMatrix(size_t r, size_t c) : MatrixDynamicDim(r, c), MatrixCommon() { @@ -2885,6 +2887,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! using Mat = BMat<>; //! Mat m({1, 1, 0, 0}); //! \endcode explicit DynamicMatrix(std::initializer_list const& c) @@ -2907,6 +2910,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! using Mat = IntMat<>; //! Mat m({{1, 1}, {0, 0}}); //! \endcode explicit DynamicMatrix( @@ -3812,9 +3816,9 @@ namespace libsemigroups { //! \par Example //! \code //! // default construct an uninitialized 3 x 3 static matrix - //! BMat<3> m; + //! [[maybe_unused]] BMat<3> m3; //! // construct an uninitialized 4 x 4 dynamic matrix - //! BMat<> m(4, 4); + //! BMat<> m4(4, 4); //! \endcode //! \ingroup bmat_group @@ -4119,9 +4123,9 @@ namespace libsemigroups { //! \par Example //! \code //! // default construct an uninitialized 3 x 3 static matrix - //! IntMat<3> m; + //! [[maybe_unused]] IntMat<3> m3; //! // construct an uninitialized 4 x 4 dynamic matrix - //! IntMat<> m(4, 4); + //! IntMat<> m4(4, 4); //! \endcode //! \ingroup intmat_group @@ -4409,9 +4413,9 @@ namespace libsemigroups { //! \par Example //! \code //! // default construct an uninitialized 3 x 3 static matrix - //! MaxPlusMat<3> m; + //! [[maybe_unused]] MaxPlusMat<3> m3; //! // construct an uninitialized 4 x 4 dynamic matrix - //! MaxPlusMat<> m(4, 4); + //! MaxPlusMat<> m4(4, 4); //! \endcode //! \ingroup maxplusmat_group @@ -4718,9 +4722,9 @@ namespace libsemigroups { //! //! \code //! // default construct an uninitialized 3 x 3 static matrix - //! MinPlusMat<3> m; + //! [[maybe_unused]] MinPlusMat<3> m3; //! // construct an uninitialized 4 x 4 dynamic matrix - //! MinPlusMat<> m(4, 4); + //! MinPlusMat<> m4(4, 4); //! \endcode //! \ingroup minplusmat_group @@ -5037,14 +5041,14 @@ namespace libsemigroups { //! \par Example //! \code //! // construct an uninitialized 3 x 3 static matrix with threshold 11 - //! MaxPlusTruncMat<11, 3> m; + //! [[maybe_unused]] MaxPlusTruncMat<11, 3> m3_11; //! // construct an uninitialized 4 x 4 dynamic matrix with threshold 11 - //! MaxPlusTruncMat<11> m(4, 4); + //! MaxPlusTruncMat<11> m4_11(4, 4); //! // construct a truncated max-plus semiring with threshold 11 - //! MaxPlusTruncSemiring sr(11); + //! MaxPlusTruncSemiring sr_11(11); //! // construct an uninitialized 5 x 5 dynamic matrix with threshold 11 //! // (defined at run time) - //! MaxPlusTruncMat<> m(sr, 5, 5); + //! MaxPlusTruncMat<> m5_11(&sr_11, 5, 5); //! \endcode //! \ingroup maxplustruncmat_group @@ -5518,14 +5522,14 @@ namespace libsemigroups { //! \par Example //! \code //! // construct an uninitialized 3 x 3 static matrix with threshold 11 - //! MinPlusTruncMat<11, 3> m; + //! [[maybe_unused]] MinPlusTruncMat<11, 3> m3_11; //! // construct an uninitialized 4 x 4 dynamic matrix with threshold 11 - //! MinPlusTruncMat<11> m(4, 4); + //! MinPlusTruncMat<11> m4_11(4, 4); //! // construct a truncated min-plus semiring with threshold 11 - //! MinPlusTruncSemiring sr(11); + //! MinPlusTruncSemiring sr_11(11); //! // construct an uninitialized 5 x 5 dynamic matrix with threshold 11 //! // (defined at run time) - //! MinPlusTruncMat<> m(sr, 5, 5); + //! MinPlusTruncMat<> m5_11(&sr_11, 5, 5); //! \endcode //! \ingroup minplustruncmat_group @@ -6005,15 +6009,15 @@ namespace libsemigroups { //! \code //! // construct an uninitialized 3 x 3 static matrix with threshold //! // 11, period 2 - //! NTPMat<11, 2, 3> m; + //! [[maybe_unused]] NTPMat<11, 2, 3> m3_11_2; //! // construct an uninitialized 4 x 4 dynamic matrix with threshold 11, //! // period 2 - //! NTPMat<11, 2> m(4, 4); + //! NTPMat<11, 2> m4_11_2(4, 4); //! // construct an ntp semiring with threshold 11, period 2 - //! NTPSemiring sr(11, 2); + //! NTPSemiring<> sr_11_2(11, 2); //! // construct an uninitialized 5 x 5 dynamic matrix with threshold 11, //! // period 2 - //! NTPMat<> m(sr, 5, 5); + //! NTPMat<> m_5_11_2(&sr_11_2, 5, 5); //! \endcode namespace detail { @@ -6678,7 +6682,7 @@ namespace libsemigroups { ProjMaxPlusMat( std::initializer_list> const& m) : ProjMaxPlusMat( - std::vector>(m.begin(), m.end())) {} + std::vector>(m.begin(), m.end())) {} ~ProjMaxPlusMat() = default; @@ -6982,9 +6986,9 @@ namespace libsemigroups { //! \par Example //! \code //! // default construct an uninitialized 3 x 3 static matrix - //! ProjMaxPlusMat<3> m; + //! ProjMaxPlusMat<3> m3; //! // construct an uninitialized 4 x 4 dynamic matrix - //! ProjMaxPlusMat<> m(4, 4); + //! ProjMaxPlusMat<> m4(4, 4); //! \endcode //! \ingroup projmaxplus_group @@ -7146,8 +7150,8 @@ namespace libsemigroups { //! \par Example //! //! \code - //! auto x == make>({{-2, 2, 0}, {-1, 0, 0}, {1, -3, - //! 1}})); + //! auto x = make>({{-2, 2, 0}, {-1, 0, 0}, {1, -3, + //! 1}}); //! // returns {{-1, 0, -1}, {-2, -1, -2}, {-1, 0, -1}} //! matrix::pow(x, 100); //! \endcode diff --git a/include/libsemigroups/order.hpp b/include/libsemigroups/order.hpp index e0a3bf97c..07a71d606 100644 --- a/include/libsemigroups/order.hpp +++ b/include/libsemigroups/order.hpp @@ -95,8 +95,11 @@ namespace libsemigroups { //! //! \par Possible Implementation //! \code - //! std::lexicographical_compare( - //! x.cbegin(), x.cend(), y.cbegin(), y.cend()); + //! word_type x = 1101_w; + //! word_type y = 1001_w; + //! + //! // x > y + //! lexicographical_compare(x.cbegin(),x.cend(),y.cbegin(),y.cend());//->false //! \endcode template >> bool lexicographical_compare(T const& x, T const& y) { @@ -128,8 +131,10 @@ namespace libsemigroups { //! //! \par Possible Implementation //! \code - //! lexicographical_compare( - //! x->cbegin(), x->cend(), y->cbegin(), y->cend()); + //! word_type x = 0001_w; + //! word_type y = 0010_w; + //! // x < y + //! lexicographical_compare(x.cbegin(),x.cend(),y.cbegin(),y.cend());//->true //! \endcode template bool lexicographical_compare(T* const x, T* const y) { @@ -258,6 +263,7 @@ namespace libsemigroups { //! //! \par Possible Implementation //! \code + //! \skip-test //! template //! bool shortlex_compare(T const& first1, //! T const& last1, @@ -302,6 +308,8 @@ namespace libsemigroups { //! //! \par Possible Implementation //! \code + //! word_type x = random_word(5, 10); + //! word_type y = random_word(5, 10); //! shortlex_compare( //! x.cbegin(), x.cend(), y.cbegin(), y.cend()); //! \endcode @@ -336,8 +344,10 @@ namespace libsemigroups { //! //! \par Possible Implementation //! \code + //! word_type x = random_word(5, 10); + //! word_type y = random_word(5, 10); //! shortlex_compare( - //! x->cbegin(), x->cend(), y->cbegin(), y->cend()); + //! x.cbegin(), x.cend(), y.cbegin(), y.cend()); //! \endcode //! //! \sa @@ -470,6 +480,8 @@ namespace libsemigroups { //! //! \par Possible Implementation //! \code + //! word_type x = random_word(5, 10); + //! word_type y = random_word(5, 10); //! recursive_path_compare( //! x.cbegin(), x.cend(), y.cbegin(), y.cend()); //! \endcode @@ -502,8 +514,10 @@ namespace libsemigroups { //! //! \par Possible Implementation //! \code + //! word_type x = random_word(5, 10); + //! word_type y = random_word(5, 10); //! recursive_path_compare( - //! x->cbegin(), x->cend(), y->cbegin(), y->cend()); + //! x.cbegin(), x.cend(), y.cbegin(), y.cend()); //! \endcode //! //! \sa diff --git a/include/libsemigroups/presentation.hpp b/include/libsemigroups/presentation.hpp index b8316d43d..6c390a0b7 100644 --- a/include/libsemigroups/presentation.hpp +++ b/include/libsemigroups/presentation.hpp @@ -861,7 +861,7 @@ namespace libsemigroups { //! \par Example //! \code //! Presentation p; - //! presentation::to_report_string(p) + //! presentation::to_report_string(p); //! // "|A| = 0, |R| = 0, |u| + |v| ∈ [0, 0], ∑(|u| + |v|) = 0" //! \endcode template diff --git a/include/libsemigroups/ranges.hpp b/include/libsemigroups/ranges.hpp index b84171537..b290f1a58 100644 --- a/include/libsemigroups/ranges.hpp +++ b/include/libsemigroups/ranges.hpp @@ -84,11 +84,13 @@ namespace libsemigroups { //! //! \par Example //! \code - //! auto wg = make>(4, [[0, 1], [1, 0], [2, 2]]); + //! using rx::operator|; + //! auto wg = make>(4, {{0, 1}, {1, 0}, {2, 2}}); //! Paths p(wg); //! p.source(0).max(10); - //! p.count(); // returns 1023 - //! (p | Random()).get(); // returns random path in p + //! p.count(); //-> 1023 + //! // (p | Random()).get(); // returns random path in p (Pipe operator not + //! // implemented for Paths?) //! \endcode // TODO(2) this should be able to emit any number of random items not only // one. diff --git a/include/libsemigroups/schreier-sims.hpp b/include/libsemigroups/schreier-sims.hpp index 7d9e931a1..85c617fe2 100644 --- a/include/libsemigroups/schreier-sims.hpp +++ b/include/libsemigroups/schreier-sims.hpp @@ -167,7 +167,7 @@ namespace libsemigroups { //! using Perm = decltype(S)::element_type; //! S.add_generator(Perm({1, 0, 2, 3, 4})); //! S.add_generator(Perm({1, 2, 3, 4, 0})); - //! S.size(); // 120 + //! S.size(); //-> 120 //! \endcode template ::type, @@ -933,10 +933,9 @@ namespace libsemigroups { bool internal_equal_to(internal_const_element_type x, internal_const_element_type y) const - noexcept(noexcept( - EqualTo()(this->to_external_const(x), - this->to_external_const( - y)) && noexcept(this->to_external_const(x)))) { + noexcept(noexcept(EqualTo()(this->to_external_const(x), + this->to_external_const(y)) + && noexcept(this->to_external_const(x)))) { return EqualTo()(this->to_external_const(x), this->to_external_const(y)); } diff --git a/include/libsemigroups/sims.hpp b/include/libsemigroups/sims.hpp index fc4e0dacf..a0f4f2001 100644 --- a/include/libsemigroups/sims.hpp +++ b/include/libsemigroups/sims.hpp @@ -1485,7 +1485,7 @@ namespace libsemigroups { uint64_t number_of_congruences(size_type n) const; }; // SimsBase - } // namespace detail + } // namespace detail namespace sims { class const_cgp_iterator; @@ -1967,7 +1967,7 @@ namespace libsemigroups { using SimsBase::IteratorBase::stats; }; // class iterator_base - }; // class Sims2 + }; // class Sims2 //! \ingroup sims_group //! @@ -2250,7 +2250,9 @@ namespace libsemigroups { //! returned (with `0` nodes and `0` edges). //! //! The algorithm implemented by this function repeatedly runs: - //! \code RepOrc(*this) + //! \code + //! \skip-test + //! RepOrc(*this) //! .min_nodes(1) //! .max_nodes(best) //! .target_size(target_size()) diff --git a/include/libsemigroups/to-cong.hpp b/include/libsemigroups/to-cong.hpp index e7cf16320..b457a33ea 100644 --- a/include/libsemigroups/to-cong.hpp +++ b/include/libsemigroups/to-cong.hpp @@ -49,6 +49,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to>(knd, fpb, wg); //! \endcode //! diff --git a/include/libsemigroups/to-froidure-pin.hpp b/include/libsemigroups/to-froidure-pin.hpp index dc316a5b6..43cc2d0e5 100644 --- a/include/libsemigroups/to-froidure-pin.hpp +++ b/include/libsemigroups/to-froidure-pin.hpp @@ -86,6 +86,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to(cong); //! \endcode //! @@ -118,6 +119,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to(k); //! \endcode //! @@ -156,6 +158,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to(kb); //! \endcode //! @@ -190,6 +193,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to(k); //! \endcode //! @@ -223,6 +227,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to(kb); //! \endcode //! @@ -263,6 +268,7 @@ namespace libsemigroups { //! as follows (for example): //! //! \code + //! \skip-test //! to>(wg, 0, 10); //! \endcode //! diff --git a/include/libsemigroups/to-knuth-bendix.hpp b/include/libsemigroups/to-knuth-bendix.hpp index 871de945a..621709582 100644 --- a/include/libsemigroups/to-knuth-bendix.hpp +++ b/include/libsemigroups/to-knuth-bendix.hpp @@ -58,6 +58,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to>(knd, fpb); //! \endcode //! @@ -93,6 +94,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to(knd, tc); //! \endcode //! @@ -128,6 +130,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to>(knd, tc); //! \endcode //! diff --git a/include/libsemigroups/to-presentation.hpp b/include/libsemigroups/to-presentation.hpp index 3785be437..402d616f1 100644 --- a/include/libsemigroups/to-presentation.hpp +++ b/include/libsemigroups/to-presentation.hpp @@ -62,6 +62,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to>(fp); //! \endcode //! @@ -102,6 +103,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to>(kb); //! \endcode //! @@ -150,31 +152,32 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code - //! to(kb); - //! \endcode - //! - //! This function constructs and returns a Presentation object using the - //! currently active rules of \p kb. - //! - //! No enumeration of the argument \p kb is performed, so it might be the - //! case that the resulting presentation does not define the same - //! semigroup/monoid as \p kb. To ensure that the resulting presentation - //! defines the same semigroup as \p kb, run \ref KnuthBendix::run (or any - //! other function that fully enumerates \p kb) prior to calling this - //! function. - //! - //! \tparam Thing used for SFINAE, must be Presentation. - //! \tparam Word the type of the rules in the presentation of the - //! \ref_knuth_bendix object \p kb. - //! \tparam Rewriter the second template parameter for \ref_knuth_bendix. - //! \tparam ReductionOrder the third template parameter for \ref_knuth_bendix. - //! - //! \param kb the \ref_knuth_bendix object from which to obtain the rules. - //! - //! \returns An object of type \c Presentation. - //! - //! \exceptions - //! \no_libsemigroups_except +//! \skip-test +//! to(kb); +//! \endcode +//! +//! This function constructs and returns a Presentation object using the +//! currently active rules of \p kb. +//! +//! No enumeration of the argument \p kb is performed, so it might be the +//! case that the resulting presentation does not define the same +//! semigroup/monoid as \p kb. To ensure that the resulting presentation +//! defines the same semigroup as \p kb, run \ref KnuthBendix::run (or any +//! other function that fully enumerates \p kb) prior to calling this +//! function. +//! +//! \tparam Thing used for SFINAE, must be Presentation. +//! \tparam Word the type of the rules in the presentation of the +//! \ref_knuth_bendix object \p kb. +//! \tparam Rewriter the second template parameter for \ref_knuth_bendix. +//! \tparam ReductionOrder the third template parameter for \ref_knuth_bendix. +//! +//! \param kb the \ref_knuth_bendix object from which to obtain the rules. +//! +//! \returns An object of type \c Presentation. +//! +//! \exceptions +//! \no_libsemigroups_except #ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN // FIXME(1) this is the same hack as elsewhere (deliberately introducing a // typo) because Doxygen conflates functions with trailing return type but the @@ -210,6 +213,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to>(k); //! \endcode //! @@ -263,6 +267,7 @@ namespace libsemigroups { //! follows: //! //! \code + //! \skip-test //! to>(p, func); //! \endcode //! @@ -314,6 +319,7 @@ namespace libsemigroups { //! follows: //! //! \code + //! \skip-test //! to>(p, func); //! \endcode //! @@ -366,6 +372,7 @@ namespace libsemigroups { //! follows: //! //! \code + //! \skip-test //! to>(p, func); //! \endcode //! @@ -433,6 +440,7 @@ namespace libsemigroups { //! follows: //! //! \code + //! \skip-test //! to>(ip); //! \endcode //! @@ -501,24 +509,25 @@ namespace libsemigroups { //! follows: //! //! \code - //! to(p); - //! \endcode - //! - //! This function returns an inverse presentation with rules equivalent to - //! those of the input presentation, but over a normalised alphabet. If the - //! alphabet of \p p is \f$\{a_0, a_1, \dots, a_{n-1}\}\f$, then the - //! alphabet of the returned inverse presentation will be - //! \f$\{0, 1, \dots, n-1, n, \dots, 2n-1\}\f$, where the inverse of letter - //! \f$i\f$ is the letter \f$i + n\, (\text{mod }2n)\f$. - //! - //! \tparam Thing used for SFINAE, must be InversePresentation. - //! \tparam Word the type of the words in the input presentation. - //! \param p the input presentation. - //! - //! \returns A value of type `InversePresentation`. - //! - //! \throws LibsemigroupsException if `p.throw_if_bad_alphabet_or_rules()` - //! throws. +//! \skip-test +//! to(p); +//! \endcode +//! +//! This function returns an inverse presentation with rules equivalent to +//! those of the input presentation, but over a normalised alphabet. If the +//! alphabet of \p p is \f$\{a_0, a_1, \dots, a_{n-1}\}\f$, then the +//! alphabet of the returned inverse presentation will be +//! \f$\{0, 1, \dots, n-1, n, \dots, 2n-1\}\f$, where the inverse of letter +//! \f$i\f$ is the letter \f$i + n\, (\text{mod }2n)\f$. +//! +//! \tparam Thing used for SFINAE, must be InversePresentation. +//! \tparam Word the type of the words in the input presentation. +//! \param p the input presentation. +//! +//! \returns A value of type `InversePresentation`. +//! +//! \throws LibsemigroupsException if `p.throw_if_bad_alphabet_or_rules()` +//! throws. //! //! \note This function will be moved from the header `to-presentation.hpp` to //! `presentation.hpp` in v4 of libsemigroups. diff --git a/include/libsemigroups/to-todd-coxeter.hpp b/include/libsemigroups/to-todd-coxeter.hpp index ad91c88be..8ace14c53 100644 --- a/include/libsemigroups/to-todd-coxeter.hpp +++ b/include/libsemigroups/to-todd-coxeter.hpp @@ -59,28 +59,29 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code - //! to>(knd, fpb, wg); - //! \endcode - //! - //! This function converts the \ref FroidurePin object \p fpb into a - //! \ref_todd_coxeter object using the WordGraph \p wg (which should be either - //! the \ref FroidurePinBase::left_cayley_graph or the - //! \ref FroidurePinBase::right_cayley_graph of \p fpb). - //! - //! \tparam Result used for SFINAE, the return type of this function, must be - //! \c ToddCoxeter for some type \c Word. - //! \tparam Node the type of the nodes in the WordGraph \p wg. - //! - //! \param knd the kind of the congruence being constructed. - //! \param fpb the FroidurePin instance to be converted. - //! \param wg the left or right Cayley graph of \p fpb. - //! - //! \returns A \ref_todd_coxeter object representing the trivial congruence - //! over the semigroup defined by \p fpb. - //! - //! \throws LibsemigroupsException if \p wg is not the - //! \ref FroidurePinBase::left_cayley_graph or the - //! \ref FroidurePinBase::right_cayley_graph of \p fpb. +//! \skip-test +//! to>(knd, fpb, wg); +//! \endcode +//! +//! This function converts the \ref FroidurePin object \p fpb into a +//! \ref_todd_coxeter object using the WordGraph \p wg (which should be either +//! the \ref FroidurePinBase::left_cayley_graph or the +//! \ref FroidurePinBase::right_cayley_graph of \p fpb). +//! +//! \tparam Result used for SFINAE, the return type of this function, must be +//! \c ToddCoxeter for some type \c Word. +//! \tparam Node the type of the nodes in the WordGraph \p wg. +//! +//! \param knd the kind of the congruence being constructed. +//! \param fpb the FroidurePin instance to be converted. +//! \param wg the left or right Cayley graph of \p fpb. +//! +//! \returns A \ref_todd_coxeter object representing the trivial congruence +//! over the semigroup defined by \p fpb. +//! +//! \throws LibsemigroupsException if \p wg is not the +//! \ref FroidurePinBase::left_cayley_graph or the +//! \ref FroidurePinBase::right_cayley_graph of \p fpb. #ifdef LIBSEMIGROUPS_PARSED_BY_DOXYGEN // FIXME(1) doxygen conflates this version of "to" with the one of the same // signature in "to-cong.hpp" and so we misspell one of the types of the @@ -109,6 +110,7 @@ namespace libsemigroups { //! Despite the hideous signature, this function should be invoked as follows: //! //! \code + //! \skip-test //! to(kb); //! \endcode //! diff --git a/include/libsemigroups/todd-coxeter-class.hpp b/include/libsemigroups/todd-coxeter-class.hpp index 39f2dbd7e..05bd86bc3 100644 --- a/include/libsemigroups/todd-coxeter-class.hpp +++ b/include/libsemigroups/todd-coxeter-class.hpp @@ -77,19 +77,26 @@ namespace libsemigroups { //! //! \par Example 1 //! \code + //! using options = detail::ToddCoxeterImpl::options; + //! //! Presentation p; //! p.alphabet(2); //! presentation::add_rule(p, 00_w, 0_w); //! presentation::add_rule(p, 0_w, 1_w); //! ToddCoxeter tc(congruence_kind::onesided, p); //! tc.strategy(options::strategy::felsch); - //! tc.number_of_classes(); - //! tc.contains(0000_w, 00_w); - //! tc.index_of(0000_w); + //! tc.number_of_classes(); //-> 1 + //! + //! auto w1 = 0000_w; + //! auto w2 = 00_w; + //! todd_coxeter::contains(tc, w1, w2); //-> true + //! todd_coxeter::index_of(tc, w1); //-> 0 //! \endcode //! //! \par Example 2 //! \code + //! using options = detail::ToddCoxeterImpl::options; + //! //! Presentation p; //! p.alphabet(4); //! presentation::add_rule(p, 00_w, 0_w); @@ -108,9 +115,9 @@ namespace libsemigroups { //! tc.strategy(options::strategy::hlt) //! .lookahead_extent(options::lookahead_extent::partial) //! .save(false); - //! tc.number_of_classes() // 10'752 - //! tc.standardize(order::recursive); - //! normal_forms(tc) | rx::take(10) | rx::to_vector() + //! tc.number_of_classes(); //-> 10752 + //! tc.standardize(Order::recursive); + //! todd_coxeter::normal_forms(tc) | rx::take(10) | rx::to_vector(); //! // {0_w, //! // 1_w, //! // 2_w, @@ -121,8 +128,8 @@ namespace libsemigroups { //! // 221_w, //! // 212_w, //! // 2121_w} - //! tc.standardize(order::lex); - //! normal_forms(tc) | rx::take(10) | rx::to_vector() + //! tc.standardize(Order::lex); + //! todd_coxeter::normal_forms(tc) | rx::take(10) | rx::to_vector(); //! // {0_w, //! // 01_w, //! // 012_w, diff --git a/include/libsemigroups/todd-coxeter-helpers.hpp b/include/libsemigroups/todd-coxeter-helpers.hpp index 3a3a17c6b..bccba9d42 100644 --- a/include/libsemigroups/todd-coxeter-helpers.hpp +++ b/include/libsemigroups/todd-coxeter-helpers.hpp @@ -92,6 +92,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.current_index_of_no_checks(std::begin(w), std::end(w)); //! \endcode //! @@ -115,6 +116,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.current_index_of(std::begin(w), std::end(w)); //! \endcode //! @@ -138,6 +140,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.index_of_no_checks(std::begin(w), std::end(w)); //! \endcode //! @@ -161,6 +164,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.index_of(std::begin(w), std::end(w)); //! \endcode //! @@ -183,6 +187,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.current_index_of_no_checks(std::begin(w), std::end(w)); //! \endcode //! @@ -208,6 +213,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.current_index_of(std::begin(w), std::end(w)); //! \endcode //! @@ -233,6 +239,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.index_of_no_checks(std::begin(w), std::end(w)); //! \endcode //! @@ -258,6 +265,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.index_of(std::begin(w), std::end(w)); //! \endcode //! @@ -282,6 +290,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.current_index_of_no_checks(w, w + std::strlen(w)); //! \endcode //! @@ -304,6 +313,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.current_index_of(w, w + std::strlen(w)); //! \endcode //! @@ -326,6 +336,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.index_of_no_checks(w, w + std::strlen(w)); //! \endcode //! @@ -348,6 +359,7 @@ namespace libsemigroups { //! //! This function just calls //! \code + //! \skip-test //! tc.index_of(w, w + std::strlen(w)); //! \endcode //! @@ -831,6 +843,10 @@ namespace libsemigroups { //! Neumann for the trivial group: //! //! \code + //! \skip-test + //! \\ TODO : Long running test. Check if meant to be this slow? + //! + //! using options = detail::ToddCoxeterImpl::options; //! Presentation p; //! p.alphabet("abcdef"); //! p.contains_empty_word(true); @@ -838,17 +854,8 @@ namespace libsemigroups { //! presentation::add_rule(p, "bbdeaecbffdbaeeccefbccefb", ""); //! presentation::add_rule(p, "ccefbfacddecbffaafdcaafdc", ""); //! presentation::add_rule(p, "aafdcdbaeefacddbbdeabbdea", ""); - //! ToddCoxeter tc(congruence_kind.twosided, p); - //! \endcode - //! - //! Then running \p tc will simply grow the underlying word graph until - //! your computer runs out of memory. The authors of ``libsemigroups`` were - //! not able to find any combination of the many settings for - //! \ref_todd_coxeter where running \p tc returned an answer. We also tried - //! with GAP and ACE but neither of these seemed able to return an answer - //! either. But doing the following: + //! ToddCoxeter tc(congruence_kind::twosided, p); //! - //! \code //! tc.lookahead_extent(options::lookahead_extent::full) //! .lookahead_style(options::lookahead_style::felsch); //! @@ -861,9 +868,14 @@ namespace libsemigroups { //! tc.perform_lookahead(true); //! tc.number_of_classes(); // returns 1 //! \endcode + //!! + //! Running \p tc will simply grow the underlying word graph until + //! your computer runs out of memory. The authors of ``libsemigroups`` were + //! not able to find any combination of the many settings for + //! \ref_todd_coxeter where running \p tc returned an answer. We also tried + //! with GAP and ACE but neither of these seemed able to return an answer + //! either. But doing the following: //! - //! returns the correct answer in about 22 seconds (on a 2024 Macbook Pro M4 - //! Pro). //! //! \param tc the \ref_todd_coxeter instance. //! diff --git a/include/libsemigroups/word-graph.hpp b/include/libsemigroups/word-graph.hpp index a1f0b0284..926dd6b9d 100644 --- a/include/libsemigroups/word-graph.hpp +++ b/include/libsemigroups/word-graph.hpp @@ -1542,7 +1542,7 @@ namespace libsemigroups { //! wg.add_to_out_degree(1); //! wg.target(0, 0, 1); //! wg.target(1, 0, 0); - //! word_graph::is_acyclic(wg); // returns false + //! word_graph::is_acyclic(wg); //-> false //! \endcode // Not noexcept because detail::is_acyclic isn't template @@ -1586,11 +1586,11 @@ namespace libsemigroups { //! wg.target(0, 0, 1); //! wg.target(1, 0, 0); //! wg.target(2, 0, 3); - //! word_graph::is_acyclic(wg); // returns false - //! word_graph::is_acyclic(wg, 0); // returns false - //! word_graph::is_acyclic(wg, 1); // returns false - //! word_graph::is_acyclic(wg, 2); // returns true - //! word_graph::is_acyclic(wg, 3); // returns true + //! word_graph::is_acyclic(wg); //-> false + //! word_graph::is_acyclic(wg, 0); //-> false + //! word_graph::is_acyclic(wg, 1); //-> false + //! word_graph::is_acyclic(wg, 2); //-> true + //! word_graph::is_acyclic(wg, 3); //-> true //! \endcode // Not noexcept because detail::is_acyclic isn't template @@ -1972,15 +1972,15 @@ namespace libsemigroups { //! \code //! WordGraph wg; //! wg.add_nodes(4); - //! wg.add_to_out_degree(1); + //! wg.add_to_out_degree(4); //! wg.target(0, 1, 0); //! wg.target(1, 0, 0); //! wg.target(2, 3, 0); - //! word_graph::is_reachable_no_checks(wg, 0, 1); // returns true - //! word_graph::is_reachable_no_checks(wg, 1, 0); // returns true - //! word_graph::is_reachable_no_checks(wg, 1, 2); // returns false - //! word_graph::is_reachable_no_checks(wg, 2, 3); // returns true - //! word_graph::is_reachable_no_checks(wg, 3, 2); // returns false + //! word_graph::is_reachable_no_checks(wg, 0, 1); //-> false + //! word_graph::is_reachable_no_checks(wg, 1, 0); //-> true + //! word_graph::is_reachable_no_checks(wg, 1, 2); //-> false + //! word_graph::is_reachable_no_checks(wg, 2, 3); //-> false + //! word_graph::is_reachable_no_checks(wg, 3, 2); //-> false //! \endcode template [[nodiscard]] bool is_reachable_no_checks(WordGraph const& wg, @@ -2052,7 +2052,7 @@ namespace libsemigroups { //! \code //! auto wg = make>( //! 5, {{0, 0}, {1, 1}, {2}, {3, 3}}); - //! word_graph::is_strictly_cyclic(wg); // returns false + //! word_graph::is_strictly_cyclic(wg); //-> false //! \endcode // TODO(1) should have a version that returns the node that everything is // reachable from @@ -2791,7 +2791,7 @@ namespace libsemigroups { //! \par Example //! \code //! // Construct a word graph with 5 nodes and 10 edges (7 specified) - //! make>(5, {{0, 0}, {1, 1}, {2}, {3, 3}}); + //! auto wg = make>(5,{{0, 0},{1, 1},{2},{3, 3}}); //! \endcode // Passing the 2nd parameter "targets" by value disambiguates it from the // other make. @@ -2880,7 +2880,7 @@ namespace libsemigroups { // always have an odd number of arguments, so we check that it's even // here (the argument x and an odd number of further arguments). WordGraph xy; - operator()(xy, x, std::forward(args)...); + operator()(xy, x, std::forward(args)...); return xy; } @@ -2915,7 +2915,7 @@ namespace libsemigroups { return is_subrelation(x, static_cast(0), y, static_cast(0)); } }; // JoinerMeeterCommon - } // namespace detail + } // namespace detail //! \ingroup word_graph_group //! \brief Class for taking joins of word graphs. diff --git a/include/libsemigroups/word-range.hpp b/include/libsemigroups/word-range.hpp index 1d6b8c6f3..8325f3bf5 100644 --- a/include/libsemigroups/word-range.hpp +++ b/include/libsemigroups/word-range.hpp @@ -354,8 +354,7 @@ namespace libsemigroups { //! function could be anything. [[nodiscard]] output_type get() const noexcept { set_iterator(); - return std::visit( - [](auto& it) -> auto const& { return *it; }, _current); + return std::visit([](auto& it) -> auto const& { return *it; }, _current); } //! \brief Advance to the next value. @@ -760,11 +759,11 @@ namespace libsemigroups { //! \par Example //! \code //! ToWord toword("bac"); - //! toword("bac"); // returns {0, 1, 2} - //! toword("bababbbcbc"); // returns { 0, 1, 0, 1, 0, 0, 0, 2, 0, 2} + //! toword("bac"); //->std::vector{0,1,2} + //! toword("bababbbcbc");//->std::vector{0,1,0,1,0,0,0,2,0,2} //! //! toword.init(); - //! toword("bac"); // returns {1, 0, 2} + //! toword("bac"); //-> std::vector{1, 0, 2} //! \endcode // TODO (later) a version that takes a word_type, so that we can permute the // letters in a word @@ -985,7 +984,7 @@ namespace libsemigroups { //! * \ref literals [[nodiscard]] word_type operator()(From const& input) const { word_type output; - operator()(output, input); + operator()(output, input); return output; } @@ -1048,6 +1047,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! using namespace rx; //! StringRange strings; //! strings.alphabet("ab").first("a").last("bbbb"); //! auto words = (strings | ToWord("ba")); @@ -1149,11 +1149,11 @@ namespace libsemigroups { //! \par Example //! \code //! ToString tostring("bac"); - //! tostring(word_type({1, 0, 2})); // returns "abc" - //! tostring(word_type({0, 1, 1, 0, 1, 1, 0, 2})); // returns "baabaabc" + //! tostring(word_type({1, 0, 2})); //-> "abc" + //! tostring(word_type({0, 1, 1, 0, 1, 1, 0, 2})); //-> "baabaabc" //! //! tostring.init(); - //! tostring(word_type({1, 0, 2})); // returns "bac" + //! tostring(word_type({1, 0, 2})); //-> "bac" //! \endcode class ToString { public: @@ -1360,7 +1360,7 @@ namespace libsemigroups { //! * \ref literals [[nodiscard]] std::string operator()(word_type const& input) const { std::string output; - operator()(output, input); + operator()(output, input); return output; } @@ -1385,7 +1385,7 @@ namespace libsemigroups { operator()(std::initializer_list input) const { static_assert(std::is_integral_v); word_type copy(input.begin(), input.end()); - return operator()(copy); + return operator()(copy); } template @@ -1399,6 +1399,7 @@ namespace libsemigroups { //! //! \par Example //! \code + //! using namespace rx; //! WordRange words; //! words.alphabet_size(1).min(0).max(10); //! @@ -2005,6 +2006,7 @@ namespace libsemigroups { //! in a compact form. //! \par Example //! \code + //! \skip-test //! 012_w // same as word_type({0, 1, 2}) //! "abc"_w // also same as word_type({0, 1, 2}) //! "(ab)^3"_p // same as "ababab" @@ -2086,6 +2088,7 @@ namespace libsemigroups { //! in a compact form. //! \par Example //! \code + //! \skip-test //! using namespace words; //! pow("a", 5) // same as "aaaaa" //! 01_w + 2 // same as 012_w @@ -2302,11 +2305,11 @@ namespace libsemigroups { //! \par Examples //! \code //! using namespace words; - //! word_type w = 012345_w - //! prod(w, 0, 5, 2) // {0, 2, 4} - //! prod(w, 1, 9, 2) // {1, 3, 5, 1} - //! prod("abcde", 4, 1, -1) // "edc" - //! prod({"aba", "xyz"}, 0, 4, 1) // "abaxyzabaxyz" + //! word_type w = 012345_w; + //! prod(w, 0, 5, 2); // {0, 2, 4} + //! prod(w, 1, 9, 2); // {1, 3, 5, 1} + //! prod("abcde", 4, 1, -1); // "edc" + //! prod({"aba", "xyz"}, 0, 4, 1); // "abaxyzabaxyz" //! \endcode template Word prod(Container const& elts, int first, int last, int step = 1); diff --git a/tests/test-docs-code-examples.cpp b/tests/test-docs-code-examples.cpp new file mode 100644 index 000000000..ceda60bfb --- /dev/null +++ b/tests/test-docs-code-examples.cpp @@ -0,0 +1,635 @@ +// libsemigroups - C++ library for semigroups and monoids +// Copyright (C) 2021-2025 James D. Mitchell + Maria Tsalakou +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "Catch2-3.8.0/catch_amalgamated.hpp" // for REQUIRE, REQUIRE_NOTHROW, REQUIRE_THROWS_AS +#include "libsemigroups/libsemigroups.hpp" // for * +#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE + +using namespace libsemigroups::literals; + +namespace libsemigroups { + // action.hpp: Line 60 + LIBSEMIGROUPS_TEST_CASE("docs", "000", "action.hpp", "[docs][quick]") { + RightAction, PPerm<16>, ImageRightAction, PPerm<16>>> o; + o.add_seed(PPerm<16>::one(16)); + o.add_generator( + PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}, + 16)); + o.add_generator( + PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + 16)); + o.add_generator( + PPerm<16>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + 16)); + o.add_generator( + PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + 16)); + o.reserve(70000); + REQUIRE(o.size() == 65536); + } + + // action.hpp: Line 143 + LIBSEMIGROUPS_TEST_CASE("docs", "001", "action.hpp", "[docs][quick]") { + auto rg = ReportGuard(true); + RightAction, PPerm<16>, ImageRightAction, PPerm<16>>> o; + o.add_seed(PPerm<16>::one(16)); + o.add_generator( + PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}, + 16)); + o.add_generator( + PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + 16)); + o.add_generator( + PPerm<16>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + 16)); + o.add_generator( + PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + 16)); + o.reserve(70000); + REQUIRE(o.size() == 65536); + REQUIRE(o.scc().number_of_components() == 17); + } + + // cong-class.hpp: Line 148 + LIBSEMIGROUPS_TEST_CASE("docs", "002", "cong-class.hpp", "[docs][quick]") { + Presentation p; + p.alphabet(2); + p.contains_empty_word(true); + presentation::add_rule(p, {0, 1}, {}); + + Congruence cong(congruence_kind::twosided, p); + is_obviously_infinite(cong); // true + congruence_common::add_generating_pair(cong, {0, 0, 0}, {}); + REQUIRE(cong.number_of_classes() == 3); + } + + // freeband.hpp: Line 76 + LIBSEMIGROUPS_TEST_CASE("docs", "003", "freeband.hpp", "[docs][quick]") { + freeband_equal_to({0, 1, 2, 3, 2, 1, 0}, + {0, 1, 2, 3, 2, 3, 2, 1, 0}); // true + freeband_equal_to({1, 2, 3}, {0, 1, 2}); // false + freeband_equal_to({1, 4, 2, 3, 10}, {1, 4, 1, 4, 2, 3, 10}); // true + freeband_equal_to({0, 1, 2, 3, 4, 0, 1, 2, 3, 4}, + {4, 3, 2, 1, 0, 4, 3, 2, 1, 0}); // false + freeband_equal_to({0, 1, 2, 1, 0, 1, 2}, {0, 1, 2}); // true + freeband_equal_to({0, 1, 2, 3, 0, 1}, + {0, 1, 2, 3, 3, 2, 2, 1, 0, 2, 1, 0, 2, 3, + 0, 2, 1, 3, 2, 1, 2, 3, 2, 1, 0, 2, 0, 1, + 0, 2, 0, 3, 2, 0, 1, 2, 2, 3, 0, 1}); // true + } + + // froidure-pin-base.hpp: Line 1337 + LIBSEMIGROUPS_TEST_CASE("docs", + "004", + "froidure-pin-base.hpp", + "[docs][quick]") { + FroidurePin S; + S.add_generator( + BMat8({{1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}})); + S.add_generator( + BMat8({{0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}})); + S.add_generator( + BMat8({{0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}})); + S.add_generator( + BMat8({{0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}})); + REQUIRE(S.size() == 4); + std::vector(S.cbegin_rules(), S.cend_rules()); + // {{{0, 0}, {0}}, + // {{0, 1}, {1}}, + // {{0, 2}, {2}}, + // {{0, 3}, {3}}, + // {{1, 0}, {0}}, + // {{1, 1}, {1}}, + // {{1, 2}, {2}}, + // {{1, 3}, {3}}, + // {{2, 0}, {0}}, + // {{2, 1}, {1}}, + // {{2, 2}, {2}}, + // {{2, 3}, {3}}, + // {{3, 0}, {0}}, + // {{3, 1}, {1}}, + // {{3, 2}, {2}}, + // {{3, 3}, {3}}} + } + + // froidure-pin-base.hpp: Line 1441 + LIBSEMIGROUPS_TEST_CASE("docs", + "005", + "froidure-pin-base.hpp", + "[docs][quick]") { + FroidurePin S; + S.add_generator( + BMat8({{1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}})); + S.add_generator( + BMat8({{0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}})); + S.add_generator( + BMat8({{0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}})); + S.add_generator( + BMat8({{0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}})); + REQUIRE(S.size() == 4); + std::vector(S.cbegin_rules(), S.cend_rules()); + // {{{0, 0}, {0}}, + // {{0, 1}, {1}}, + // {{0, 2}, {2}}, + // {{0, 3}, {3}}, + // {{1, 0}, {0}}, + // {{1, 1}, {1}}, + // {{1, 2}, {2}}, + // {{1, 3}, {3}}, + // {{2, 0}, {0}}, + // {{2, 1}, {1}}, + // {{2, 2}, {2}}, + // {{2, 3}, {3}}, + // {{3, 0}, {0}}, + // {{3, 1}, {1}}, + // {{3, 2}, {2}}, + // {{3, 3}, {3}}} + } + + // is_specialization_of.hpp: Line 44 + LIBSEMIGROUPS_TEST_CASE("docs", + "006", + "is_specialization_of.hpp", + "[docs][quick]") { + using iso_1 = is_specialization_of, std::vector>; + REQUIRE(iso_1::value == true); + using iso_2 = is_specialization_of, std::unordered_map>; + REQUIRE(iso_2::value == false); + } + + // is_specialization_of.hpp: Line 76 + LIBSEMIGROUPS_TEST_CASE("docs", + "007", + "is_specialization_of.hpp", + "[docs][quick]") { + REQUIRE(is_specialization_of_v, std::vector> == true); + REQUIRE(is_specialization_of_v, std::unordered_map> + == false); + } + + // knuth-bendix-class.hpp: Line 72 + LIBSEMIGROUPS_TEST_CASE("docs", + "008", + "knuth-bendix-class.hpp", + "[docs][quick]") { + Presentation p; + p.contains_empty_word(true); + p.alphabet("abcd"); + presentation::add_rule_no_checks(p, "ab", ""); + presentation::add_rule_no_checks(p, "ba", ""); + presentation::add_rule_no_checks(p, "cd", ""); + presentation::add_rule_no_checks(p, "dc", ""); + + KnuthBendix kb(congruence_kind::twosided, p); + + REQUIRE(kb.number_of_active_rules() == 0); + REQUIRE(kb.number_of_pending_rules() == 4); + kb.run(); + REQUIRE(kb.number_of_active_rules() == 4); + REQUIRE(kb.number_of_pending_rules() == 0); + REQUIRE(kb.confluent() == true); + REQUIRE(kb.number_of_classes() == POSITIVE_INFINITY); + } + + // konieczny.hpp: Line 70 + LIBSEMIGROUPS_TEST_CASE("docs", "009", "konieczny.hpp", "[docs][quick]") { + auto S = make( + {BMat8({{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}), + BMat8({{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {1, 0, 0, 0}}), + BMat8({{1, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}), + BMat8({{0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}})}); + S.size(); // returns 63'904 + S.number_of_idempotents(); // returns 2'360 + } + + // matrix.hpp: Line 1964 + LIBSEMIGROUPS_TEST_CASE("docs", "010", "matrix.hpp", "[docs][quick]") { + using Mat = BMat8; + Mat m({{1, 1}, {0, 0}}); + } + + // matrix.hpp: Line 2867 + LIBSEMIGROUPS_TEST_CASE("docs", "011", "matrix.hpp", "[docs][quick]") { + using Mat = IntMat<>; + Mat m(2, 3); // construct a 2 x 3 matrix + } + + // matrix.hpp: Line 2890 + LIBSEMIGROUPS_TEST_CASE("docs", "012", "matrix.hpp", "[docs][quick]") { + using Mat = BMat<>; + Mat m({1, 1, 0, 0}); + } + + // matrix.hpp: Line 2913 + LIBSEMIGROUPS_TEST_CASE("docs", "013", "matrix.hpp", "[docs][quick]") { + using Mat = IntMat<>; + Mat m({{1, 1}, {0, 0}}); + } + + // matrix.hpp: Line 3818 + LIBSEMIGROUPS_TEST_CASE("docs", "014", "matrix.hpp", "[docs][quick]") { + // default construct an uninitialized 3 x 3 static matrix + [[maybe_unused]] BMat<3> m3; + // construct an uninitialized 4 x 4 dynamic matrix + BMat<> m4(4, 4); + } + + // matrix.hpp: Line 4125 + LIBSEMIGROUPS_TEST_CASE("docs", "015", "matrix.hpp", "[docs][quick]") { + // default construct an uninitialized 3 x 3 static matrix + [[maybe_unused]] IntMat<3> m3; + // construct an uninitialized 4 x 4 dynamic matrix + IntMat<> m4(4, 4); + } + + // matrix.hpp: Line 4415 + LIBSEMIGROUPS_TEST_CASE("docs", "016", "matrix.hpp", "[docs][quick]") { + // default construct an uninitialized 3 x 3 static matrix + [[maybe_unused]] MaxPlusMat<3> m3; + // construct an uninitialized 4 x 4 dynamic matrix + MaxPlusMat<> m4(4, 4); + } + + // matrix.hpp: Line 4724 + LIBSEMIGROUPS_TEST_CASE("docs", "017", "matrix.hpp", "[docs][quick]") { + // default construct an uninitialized 3 x 3 static matrix + [[maybe_unused]] MinPlusMat<3> m3; + // construct an uninitialized 4 x 4 dynamic matrix + MinPlusMat<> m4(4, 4); + } + + // matrix.hpp: Line 5043 + LIBSEMIGROUPS_TEST_CASE("docs", "018", "matrix.hpp", "[docs][quick]") { + // construct an uninitialized 3 x 3 static matrix with threshold 11 + [[maybe_unused]] MaxPlusTruncMat<11, 3> m3_11; + // construct an uninitialized 4 x 4 dynamic matrix with threshold 11 + MaxPlusTruncMat<11> m4_11(4, 4); + // construct a truncated max-plus semiring with threshold 11 + MaxPlusTruncSemiring sr_11(11); + // construct an uninitialized 5 x 5 dynamic matrix with threshold 11 + // (defined at run time) + MaxPlusTruncMat<> m5_11(&sr_11, 5, 5); + } + + // matrix.hpp: Line 5524 + LIBSEMIGROUPS_TEST_CASE("docs", "019", "matrix.hpp", "[docs][quick]") { + // construct an uninitialized 3 x 3 static matrix with threshold 11 + [[maybe_unused]] MinPlusTruncMat<11, 3> m3_11; + // construct an uninitialized 4 x 4 dynamic matrix with threshold 11 + MinPlusTruncMat<11> m4_11(4, 4); + // construct a truncated min-plus semiring with threshold 11 + MinPlusTruncSemiring sr_11(11); + // construct an uninitialized 5 x 5 dynamic matrix with threshold 11 + // (defined at run time) + MinPlusTruncMat<> m5_11(&sr_11, 5, 5); + } + + // matrix.hpp: Line 6010 + LIBSEMIGROUPS_TEST_CASE("docs", "020", "matrix.hpp", "[docs][quick]") { + // construct an uninitialized 3 x 3 static matrix with threshold + // 11, period 2 + [[maybe_unused]] NTPMat<11, 2, 3> m3_11_2; + // construct an uninitialized 4 x 4 dynamic matrix with threshold 11, + // period 2 + NTPMat<11, 2> m4_11_2(4, 4); + // construct an ntp semiring with threshold 11, period 2 + NTPSemiring<> sr_11_2(11, 2); + // construct an uninitialized 5 x 5 dynamic matrix with threshold 11, + // period 2 + NTPMat<> m_5_11_2(&sr_11_2, 5, 5); + } + + // matrix.hpp: Line 6988 + LIBSEMIGROUPS_TEST_CASE("docs", "021", "matrix.hpp", "[docs][quick]") { + // default construct an uninitialized 3 x 3 static matrix + ProjMaxPlusMat<3> m3; + // construct an uninitialized 4 x 4 dynamic matrix + ProjMaxPlusMat<> m4(4, 4); + } + + // matrix.hpp: Line 7153 + LIBSEMIGROUPS_TEST_CASE("docs", "022", "matrix.hpp", "[docs][quick]") { + auto x = make>({{-2, 2, 0}, {-1, 0, 0}, {1, -3, 1}}); + // returns {{-1, 0, -1}, {-2, -1, -2}, {-1, 0, -1}} + matrix::pow(x, 100); + } + + // matrix.hpp: Line 7901 + LIBSEMIGROUPS_TEST_CASE("docs", "023", "matrix.hpp", "[docs][quick]") { + auto x = make>({{1, 0, 0}, {0, 0, 1}, {0, 1, 0}}); + matrix::row_space_size(x); // returns 7 + } + + // order.hpp: Line 98 + LIBSEMIGROUPS_TEST_CASE("docs", "024", "order.hpp", "[docs][quick]") { + word_type x = 1101_w; + word_type y = 1001_w; + + // x > y + REQUIRE(lexicographical_compare(x.cbegin(), x.cend(), y.cbegin(), y.cend()) + == false); + } + + // order.hpp: Line 134 + LIBSEMIGROUPS_TEST_CASE("docs", "025", "order.hpp", "[docs][quick]") { + word_type x = 0001_w; + word_type y = 0010_w; + // x < y + REQUIRE(lexicographical_compare(x.cbegin(), x.cend(), y.cbegin(), y.cend()) + == true); + } + + // order.hpp: Line 311 + LIBSEMIGROUPS_TEST_CASE("docs", "026", "order.hpp", "[docs][quick]") { + word_type x = random_word(5, 10); + word_type y = random_word(5, 10); + shortlex_compare(x.cbegin(), x.cend(), y.cbegin(), y.cend()); + } + + // order.hpp: Line 347 + LIBSEMIGROUPS_TEST_CASE("docs", "027", "order.hpp", "[docs][quick]") { + word_type x = random_word(5, 10); + word_type y = random_word(5, 10); + shortlex_compare(x.cbegin(), x.cend(), y.cbegin(), y.cend()); + } + + // order.hpp: Line 483 + LIBSEMIGROUPS_TEST_CASE("docs", "028", "order.hpp", "[docs][quick]") { + word_type x = random_word(5, 10); + word_type y = random_word(5, 10); + recursive_path_compare(x.cbegin(), x.cend(), y.cbegin(), y.cend()); + } + + // order.hpp: Line 517 + LIBSEMIGROUPS_TEST_CASE("docs", "029", "order.hpp", "[docs][quick]") { + word_type x = random_word(5, 10); + word_type y = random_word(5, 10); + recursive_path_compare(x.cbegin(), x.cend(), y.cbegin(), y.cend()); + } + + // presentation.hpp: Line 863 + LIBSEMIGROUPS_TEST_CASE("docs", "030", "presentation.hpp", "[docs][quick]") { + Presentation p; + presentation::to_report_string(p); + // "|A| = 0, |R| = 0, |u| + |v| ∈ [0, 0], ∑(|u| + |v|) = 0" + } + + // ranges.hpp: Line 87 + LIBSEMIGROUPS_TEST_CASE("docs", "031", "ranges.hpp", "[docs][quick]") { + using rx::operator|; + auto wg = make>(4, {{0, 1}, {1, 0}, {2, 2}}); + Paths p(wg); + p.source(0).max(10); + REQUIRE(p.count() == 1023); + // (p | Random()).get(); // returns random path in p (Pipe operator not + // implemented for Paths?) + } + + // schreier-sims.hpp: Line 166 + LIBSEMIGROUPS_TEST_CASE("docs", "032", "schreier-sims.hpp", "[docs][quick]") { + SchreierSims<5> S; + using Perm = decltype(S)::element_type; + S.add_generator(Perm({1, 0, 2, 3, 4})); + S.add_generator(Perm({1, 2, 3, 4, 0})); + REQUIRE(S.size() == 120); + } + + // todd-coxeter-class.hpp: Line 80 + LIBSEMIGROUPS_TEST_CASE("docs", + "033", + "todd-coxeter-class.hpp", + "[docs][quick]") { + using options = detail::ToddCoxeterImpl::options; + + Presentation p; + p.alphabet(2); + presentation::add_rule(p, 00_w, 0_w); + presentation::add_rule(p, 0_w, 1_w); + ToddCoxeter tc(congruence_kind::onesided, p); + tc.strategy(options::strategy::felsch); + REQUIRE(tc.number_of_classes() == 1); + + auto w1 = 0000_w; + auto w2 = 00_w; + REQUIRE(todd_coxeter::contains(tc, w1, w2) == true); + REQUIRE(todd_coxeter::index_of(tc, w1) == 0); + } + + // todd-coxeter-class.hpp: Line 98 + LIBSEMIGROUPS_TEST_CASE("docs", + "034", + "todd-coxeter-class.hpp", + "[docs][quick]") { + using options = detail::ToddCoxeterImpl::options; + + Presentation p; + p.alphabet(4); + presentation::add_rule(p, 00_w, 0_w); + presentation::add_rule(p, 10_w, 1_w); + presentation::add_rule(p, 01_w, 1_w); + presentation::add_rule(p, 20_w, 2_w); + presentation::add_rule(p, 02_w, 2_w); + presentation::add_rule(p, 30_w, 3_w); + presentation::add_rule(p, 03_w, 3_w); + presentation::add_rule(p, 11_w, 0_w); + presentation::add_rule(p, 23_w, 0_w); + presentation::add_rule(p, 222_w, 0_w); + presentation::add_rule(p, 12121212121212_w, 0_w); + presentation::add_rule(p, 12131213121312131213121312131213_w, 0_w); + ToddCoxeter tc(congruence_kind::twosided, p); + tc.strategy(options::strategy::hlt) + .lookahead_extent(options::lookahead_extent::partial) + .save(false); + REQUIRE(tc.number_of_classes() == 10752); + tc.standardize(Order::recursive); + todd_coxeter::normal_forms(tc) | rx::take(10) | rx::to_vector(); + // {0_w, + // 1_w, + // 2_w, + // 21_w, + // 12_w, + // 121_w, + // 22_w, + // 221_w, + // 212_w, + // 2121_w} + tc.standardize(Order::lex); + todd_coxeter::normal_forms(tc) | rx::take(10) | rx::to_vector(); + // {0_w, + // 01_w, + // 012_w, + // 0121_w, + // 01212_w, + // 012121_w, + // 0121212_w, + // 01212121_w, + // 012121212_w, + // 0121212121_w}; + } + + // word-graph.hpp: Line 1540 + LIBSEMIGROUPS_TEST_CASE("docs", "035", "word-graph.hpp", "[docs][quick]") { + WordGraph wg; + wg.add_nodes(2); + wg.add_to_out_degree(1); + wg.target(0, 0, 1); + wg.target(1, 0, 0); + REQUIRE(word_graph::is_acyclic(wg) == false); + } + + // word-graph.hpp: Line 1583 + LIBSEMIGROUPS_TEST_CASE("docs", "036", "word-graph.hpp", "[docs][quick]") { + WordGraph wg; + wg.add_nodes(4); + wg.add_to_out_degree(1); + wg.target(0, 0, 1); + wg.target(1, 0, 0); + wg.target(2, 0, 3); + REQUIRE(word_graph::is_acyclic(wg) == false); + REQUIRE(word_graph::is_acyclic(wg, 0) == false); + REQUIRE(word_graph::is_acyclic(wg, 1) == false); + REQUIRE(word_graph::is_acyclic(wg, 2) == true); + REQUIRE(word_graph::is_acyclic(wg, 3) == true); + } + + // word-graph.hpp: Line 1973 + LIBSEMIGROUPS_TEST_CASE("docs", "037", "word-graph.hpp", "[docs][quick]") { + WordGraph wg; + wg.add_nodes(4); + wg.add_to_out_degree(4); + wg.target(0, 1, 0); + wg.target(1, 0, 0); + wg.target(2, 3, 0); + REQUIRE(word_graph::is_reachable_no_checks(wg, 0, 1) == false); + REQUIRE(word_graph::is_reachable_no_checks(wg, 1, 0) == true); + REQUIRE(word_graph::is_reachable_no_checks(wg, 1, 2) == false); + REQUIRE(word_graph::is_reachable_no_checks(wg, 2, 3) == false); + REQUIRE(word_graph::is_reachable_no_checks(wg, 3, 2) == false); + } + + // word-graph.hpp: Line 2053 + LIBSEMIGROUPS_TEST_CASE("docs", "038", "word-graph.hpp", "[docs][quick]") { + auto wg = make>(5, {{0, 0}, {1, 1}, {2}, {3, 3}}); + REQUIRE(word_graph::is_strictly_cyclic(wg) == false); + } + + // word-graph.hpp: Line 2793 + LIBSEMIGROUPS_TEST_CASE("docs", "039", "word-graph.hpp", "[docs][quick]") { + // Construct a word graph with 5 nodes and 10 edges (7 specified) + auto wg = make>(5, {{0, 0}, {1, 1}, {2}, {3, 3}}); + } + + // word-range.hpp: Line 183 + LIBSEMIGROUPS_TEST_CASE("docs", "040", "word-range.hpp", "[docs][quick]") { + std::vector(cbegin_wilo(2, 3, {0}, {1, 1, 1}), + cend_wilo(2, 3, {0}, {1, 1, 1})); + // {{0}, {0, 0}, {0, 1}, {1}, {1, 0}, {1, 1}}; + } + + // word-range.hpp: Line 253 + LIBSEMIGROUPS_TEST_CASE("docs", "041", "word-range.hpp", "[docs][quick]") { + std::vector(cbegin_wislo(2, {0}, {0, 0, 0}), + cend_wislo(2, {0}, {0, 0, 0})); + // {{0}, {1}, {0, 0}, {0, 1}, {1, 0}, {1, 1}}; + } + + // word-range.hpp: Line 313 + LIBSEMIGROUPS_TEST_CASE("docs", "042", "word-range.hpp", "[docs][quick]") { + WordRange words; + words + .order(Order::shortlex) // words in shortlex order + .alphabet_size(2) // on 2 letters + .min(1) // of length in the range from 1 + .max(5); // to 5 + } + + // word-range.hpp: Line 761 + LIBSEMIGROUPS_TEST_CASE("docs", "043", "word-range.hpp", "[docs][quick]") { + ToWord toword("bac"); + REQUIRE(toword("bac") == std::vector{0, 1, 2}); + REQUIRE(toword("bababbbcbc") + == std::vector{0, 1, 0, 1, 0, 0, 0, 2, 0, 2}); + + toword.init(); + REQUIRE(toword("bac") == std::vector{1, 0, 2}); + } + + // word-range.hpp: Line 1050 + LIBSEMIGROUPS_TEST_CASE("docs", "044", "word-range.hpp", "[docs][quick]") { + using namespace rx; + StringRange strings; + strings.alphabet("ab").first("a").last("bbbb"); + auto words = (strings | ToWord("ba")); + // contains the words + // {1_w, 0_w, 11_w, 10_w, 01_w, 00_w, 111_w, + // 110_w, 101_w, 100_w, 011_w, 010_w, 001_w, 000_w, + // 1111_w, 1110_w, 1101_w, 1100_w, 1011_w, 1010_w, 1001_w, + // 1000_w, 0111_w, 0110_w, 0101_w, 0100_w, 0011_w, 0010_w, + // 0001_w})); + } + + // word-range.hpp: Line 1151 + LIBSEMIGROUPS_TEST_CASE("docs", "045", "word-range.hpp", "[docs][quick]") { + ToString tostring("bac"); + REQUIRE(tostring(word_type({1, 0, 2})) == "abc"); + REQUIRE(tostring(word_type({0, 1, 1, 0, 1, 1, 0, 2})) == "baabaabc"); + + tostring.init(); + REQUIRE(tostring(word_type({1, 0, 2})) == "bac"); + } + + // word-range.hpp: Line 1402 + LIBSEMIGROUPS_TEST_CASE("docs", "046", "word-range.hpp", "[docs][quick]") { + using namespace rx; + WordRange words; + words.alphabet_size(1).min(0).max(10); + + auto strings = (words | ToString("a")); + // Contains the strings + // {"", "a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaa", "aaaaaaa", + // "aaaaaaaa", "aaaaaaaaa"}; + } + + // word-range.hpp: Line 1596 + LIBSEMIGROUPS_TEST_CASE("docs", "047", "word-range.hpp", "[docs][quick]") { + StringRange strings; + strings + .order(Order::shortlex) // strings in shortlex order + .alphabet("ab") // on 2 letters + .min(1) // of length in the range from 1 + .max(5); // to 5 + } + + // word-range.hpp: Line 2307 + LIBSEMIGROUPS_TEST_CASE("docs", "048", "word-range.hpp", "[docs][quick]") { + using namespace words; + word_type w = 012345_w; + prod(w, 0, 5, 2); // {0, 2, 4} + prod(w, 1, 9, 2); // {1, 3, 5, 1} + prod("abcde", 4, 1, -1); // "edc" + prod({"aba", "xyz"}, 0, 4, 1); // "abaxyzabaxyz" + } + +} // namespace libsemigroups \ No newline at end of file