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