+
diff --git a/docs/examples/atlas/mathieu/m22/m22-176-i.mp4 b/docs/examples/atlas/mathieu/m22/m22-176-i.mp4
new file mode 100644
index 0000000..91b3f4e
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-176-i.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22-176-ii.mp4 b/docs/examples/atlas/mathieu/m22/m22-176-ii.mp4
new file mode 100644
index 0000000..cc1daf0
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-176-ii.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22-22.mp4 b/docs/examples/atlas/mathieu/m22/m22-22.mp4
new file mode 100644
index 0000000..17af033
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-22.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22-231.mp4 b/docs/examples/atlas/mathieu/m22/m22-231.mp4
new file mode 100644
index 0000000..ad3f32c
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-231.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22-330.mp4 b/docs/examples/atlas/mathieu/m22/m22-330.mp4
new file mode 100644
index 0000000..08572d2
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-330.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22-616.mp4 b/docs/examples/atlas/mathieu/m22/m22-616.mp4
new file mode 100644
index 0000000..04f2d1e
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-616.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22-672.mp4 b/docs/examples/atlas/mathieu/m22/m22-672.mp4
new file mode 100644
index 0000000..1601ed8
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-672.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22-77.mp4 b/docs/examples/atlas/mathieu/m22/m22-77.mp4
new file mode 100644
index 0000000..41c9cda
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22-77.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m22/m22.mp4 b/docs/examples/atlas/mathieu/m22/m22.mp4
new file mode 100644
index 0000000..59a0baf
Binary files /dev/null and b/docs/examples/atlas/mathieu/m22/m22.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/1771.md b/docs/examples/atlas/mathieu/m23/1771.md
new file mode 100644
index 0000000..883714f
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m23/1771.md
@@ -0,0 +1,148 @@
+# $2^4:(3 \times A_5):2$ as a subgroup of Mathieu group M~23~ (Presentation 1)
+
+
+
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max6W1).
+
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 2.497 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong 2^4:(3 \times A_5):2,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{23}:H] = 1,771.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses. The presentation replaces `A` by `a`,
+since $a^2=1$, and `B` represents $b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import (
+ Presentation,
+ ToddCoxeter,
+ congruence_kind,
+ presentation,
+)
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
+presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abab^2ab^2)^6"), ""
+) # Is redundant, but very useful.
+presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
+presentation.replace_subword(p, "A", "a")
+p.alphabet("Bab")
+
+# 2^4:(3 x A5):2, order 5760, expected index 1771.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max6W1
+# ATLAS output words simplified using a^2 = b^4 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+for word in (
+ "BaBaBaBaBaBabababababab",
+ "aBaBaBababababa",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(milliseconds=35))
+
+actual_index = tc.number_of_classes()
+print(f"The index of 2^4:(3 x A5):2 is {actual_index}")
+if actual_index != 1771:
+ raise RuntimeError(f"expected index 1771, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = hlt)
+ #0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: HLT 0.0 START
+ #0: ToddCoxeter: HLT 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 37 | 0 | 37
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 38 | 73 | 34.2%
+ #0: ToddCoxeter: time | run 0 = 26µs | all runs = 26µs | elapsed = 85µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: HLT 0.0.1 | active | killed | defined
+ [... lines omitted ...]
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 5,313 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.7.2 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.7.0 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: phase 0.7 = 25ms | run 0 = 2.469s | all runs = 2.469s | elapsed = 2.469s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 3 | 0 | 4 | 0
+ #0: ToddCoxeter: time spent in phases | 2.135s (85%) | - (0%) | 309ms (12%) | - (0%)
+ #0: ToddCoxeter: phase 0.7 = 52ms | run 0 = 2.497s | all runs = 2.497s | elapsed = 2.497s
+ The index of 2^4:(3 x A5):2 is 1771
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index:
+$1,771$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
+
diff --git a/docs/examples/atlas/mathieu/m23/253-i.md b/docs/examples/atlas/mathieu/m23/253-i.md
new file mode 100644
index 0000000..2ca0a87
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m23/253-i.md
@@ -0,0 +1,148 @@
+# $L_3(4):2_2$ as a subgroup of Mathieu group M~23~ (Presentation 1)
+
+
+
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max2W1).
+
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 4.852 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong L_3(4):2_2,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{23}:H] = 253.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses. The presentation replaces `A` by `a`,
+since $a^2=1$, and `B` represents $b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import (
+ Presentation,
+ ToddCoxeter,
+ congruence_kind,
+ presentation,
+)
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
+presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abab^2ab^2)^6"), ""
+) # Is redundant, but very useful.
+presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
+presentation.replace_subword(p, "A", "a")
+p.alphabet("Bab")
+
+# L3(4):2, order 40320, expected index 253.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max2W1
+# ATLAS output words simplified using a^2 = b^4 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+for word in (
+ "BaBaBaBaBaBaBaBaBaBaBaBabababababababababababab",
+ "bbabbaBababbabababbabababbabbabbabb",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(milliseconds=80))
+
+actual_index = tc.number_of_classes()
+print(f"The index of L3(4):2 is {actual_index}")
+if actual_index != 253:
+ raise RuntimeError(f"expected index 253, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = hlt)
+ #0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: HLT 0.0 START
+ #0: ToddCoxeter: HLT 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 81 | 0 | 81
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 82 | 161 | 33.7%
+ #0: ToddCoxeter: time | run 0 = 27µs | all runs = 27µs | elapsed = 88µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: HLT 0.0.1 | active | killed | defined
+ [... lines omitted ...]
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 759 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.11.2 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.11.0 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: phase 0.11 = 66ms | run 0 = 4.817s | all runs = 4.817s | elapsed = 4.817s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 5 | 0 | 6 | 0
+ #0: ToddCoxeter: time spent in phases | 4.093s (84%) | - (0%) | 657ms (14%) | - (0%)
+ #0: ToddCoxeter: phase 0.11 = 101ms | run 0 = 4.852s | all runs = 4.852s | elapsed = 4.852s
+ The index of L3(4):2 is 253
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index:
+$253$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
+
diff --git a/docs/examples/atlas/mathieu/m23/253-ii.md b/docs/examples/atlas/mathieu/m23/253-ii.md
new file mode 100644
index 0000000..21756ce
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m23/253-ii.md
@@ -0,0 +1,148 @@
+# $2^4:A_7$ as a subgroup of Mathieu group M~23~ (Presentation 1)
+
+
+
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max3W1).
+
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 3.157 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong 2^4:A_7,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{23}:H] = 253.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses. The presentation replaces `A` by `a`,
+since $a^2=1$, and `B` represents $b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import (
+ Presentation,
+ ToddCoxeter,
+ congruence_kind,
+ presentation,
+)
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
+presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abab^2ab^2)^6"), ""
+) # Is redundant, but very useful.
+presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
+presentation.replace_subword(p, "A", "a")
+p.alphabet("Bab")
+
+# 2^4:A7, order 40320, expected index 253.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max3W1
+# ATLAS output words simplified using a^2 = b^4 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+for word in (
+ "BaBaBaBaBaBaBaBaBababababababababab",
+ "bbabbaBababbabababbabababbabbabbabb",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(milliseconds=50))
+
+actual_index = tc.number_of_classes()
+print(f"The index of 2^4:A7 is {actual_index}")
+if actual_index != 253:
+ raise RuntimeError(f"expected index 253, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = hlt)
+ #0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: HLT 0.0 START
+ #0: ToddCoxeter: HLT 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 69 | 0 | 69
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 70 | 137 | 33.8%
+ #0: ToddCoxeter: time | run 0 = 25µs | all runs = 25µs | elapsed = 85µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: HLT 0.0.1 | active | killed | defined
+ [... lines omitted ...]
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 759 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.9.2 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.9.0 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: phase 0.9 = 18ms | run 0 = 3.117s | all runs = 3.117s | elapsed = 3.117s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 4 | 0 | 5 | 0
+ #0: ToddCoxeter: time spent in phases | 2.707s (86%) | - (0%) | 391ms (12%) | - (0%)
+ #0: ToddCoxeter: phase 0.9 = 59ms | run 0 = 3.157s | all runs = 3.157s | elapsed = 3.157s
+ The index of 2^4:A7 is 253
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index:
+$253$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
+
diff --git a/docs/examples/atlas/mathieu/m23/40320.md b/docs/examples/atlas/mathieu/m23/40320.md
new file mode 100644
index 0000000..ca4be1d
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m23/40320.md
@@ -0,0 +1,149 @@
+# $23:11$ as a subgroup of Mathieu group M~23~ (Presentation 1)
+
+
+
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max7W1).
+
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 5.402 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong 23:11,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{23}:H] = 40,320.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses. The presentation replaces `A` by `a`,
+since $a^2=1$, and `B` represents $b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import (
+ Presentation,
+ ToddCoxeter,
+ congruence_kind,
+ presentation,
+)
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
+presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abab^2ab^2)^6"), ""
+) # Is redundant, but very useful.
+presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
+presentation.replace_subword(p, "A", "a")
+p.alphabet("Bab")
+
+# 23:11, order 253, expected index 40320.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max7W1
+# ATLAS output words simplified using a^2 = b^4 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+for word in (
+ "BaBaBaBaBaBabababbababbabababababab",
+ "abababbababababbababbabababbababababbababbabababbababababbabababbaBaBaBabbaB"
+ "aBabbaBabbaBaBaBabbaBaBa",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(milliseconds=100))
+
+actual_index = tc.number_of_classes()
+print(f"The index of 23:11 is {actual_index}")
+if actual_index != 40320:
+ raise RuntimeError(f"expected index 40320, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = hlt)
+ #0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: HLT 0.0 START
+ #0: ToddCoxeter: HLT 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 134 | 0 | 134
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 135 | 267 | 33.6%
+ #0: ToddCoxeter: time | run 0 = 30µs | all runs = 30µs | elapsed = 94µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: HLT 0.0.1 | active | killed | defined
+ [... lines omitted ...]
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 120,960 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.11.2 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.11.0 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: phase 0.11 = 54ms | run 0 = 5.346s | all runs = 5.346s | elapsed = 5.347s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 5 | 0 | 6 | 0
+ #0: ToddCoxeter: time spent in phases | 4.662s (86%) | - (0%) | 639ms (12%) | - (0%)
+ #0: ToddCoxeter: phase 0.11 = 110ms | run 0 = 5.402s | all runs = 5.402s | elapsed = 5.402s
+ The index of 23:11 is 40320
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index:
+$40,320$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
+
diff --git a/docs/examples/atlas/mathieu/m23/506.md b/docs/examples/atlas/mathieu/m23/506.md
new file mode 100644
index 0000000..f7a8c44
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m23/506.md
@@ -0,0 +1,148 @@
+# $A_8$ as a subgroup of Mathieu group M~23~ (Presentation 1)
+
+
+
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max4W1).
+
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 720 milliseconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong A_8,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{23}:H] = 506.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses. The presentation replaces `A` by `a`,
+since $a^2=1$, and `B` represents $b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import (
+ Presentation,
+ ToddCoxeter,
+ congruence_kind,
+ presentation,
+)
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
+presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abab^2ab^2)^6"), ""
+) # Is redundant, but very useful.
+presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
+presentation.replace_subword(p, "A", "a")
+p.alphabet("Bab")
+
+# A8, order 20160, expected index 506.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max4W1
+# ATLAS output words simplified using a^2 = b^4 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+for word in (
+ "BaBaBababab",
+ "aBaBaBaBabababababa",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(milliseconds=10))
+
+actual_index = tc.number_of_classes()
+print(f"The index of A8 is {actual_index}")
+if actual_index != 506:
+ raise RuntimeError(f"expected index 506, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = hlt)
+ #0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: HLT 0.0 START
+ #0: ToddCoxeter: HLT 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 29 | 0 | 29
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 30 | 57 | 34.5%
+ #0: ToddCoxeter: time | run 0 = 24µs | all runs = 24µs | elapsed = 91µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: HLT 0.0.1 | active | killed | defined
+ [... lines omitted ...]
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 1,518 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.5.3 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.5.0 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: phase 0.5 = 20ms | run 0 = 712ms | all runs = 712ms | elapsed = 712ms
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 2 | 0 | 3 | 0
+ #0: ToddCoxeter: time spent in phases | 528ms (73%) | - (0%) | 162ms (23%) | - (0%)
+ #0: ToddCoxeter: phase 0.5 = 28ms | run 0 = 720ms | all runs = 720ms | elapsed = 720ms
+ The index of A8 is 506
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index:
+$506$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
+
diff --git a/docs/examples/atlas/mathieu/m23/index.md b/docs/examples/atlas/mathieu/m23/index.md
index ba46a36..32c770b 100644
--- a/docs/examples/atlas/mathieu/m23/index.md
+++ b/docs/examples/atlas/mathieu/m23/index.md
@@ -1,25 +1,44 @@
# Mathieu group M~23~
-On this page, we provide links to verifications of two presentations of the
-Mathieu group M~23~, and to attempts to verify the indices of two of its maximal
-subgroups.
+
+
+The presentations and maximal subgroup data come from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/spor/M23/).
+The claimed order is $|\mathrm{M}_{23}| = 10,200,960$.
## Presentations
-- [Presentation 1](p1.md)
- :material-checkbox-marked-circle-outline:{ style="color: #00c853" }
-- [Presentation 2](p2.md)
- :material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The full-group enumerations use [Presentation 1](p1.md) and
+[Presentation 2](p2.md). Their pages include the defining relations,
+self-contained code, recorded output, and a corresponding animation showing
+the number of nodes, and the percentage completeness, of the graph.
+The scripts include additional relations; we do not verify that these
+relations are redundant here.
+
+## Subgroup enumerations
+
+The trivial subgroup is shown in one row for both presentations. All seven
+maximal-subgroup videos supplied with the verification files use Presentation 1.
+The Presentation 2 subgroup links retain the earlier recorded computations.
+
+
diff --git a/docs/examples/atlas/mathieu/m23/m23-1288.mp4 b/docs/examples/atlas/mathieu/m23/m23-1288.mp4
new file mode 100644
index 0000000..f77d959
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-1288.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-1771.mp4 b/docs/examples/atlas/mathieu/m23/m23-1771.mp4
new file mode 100644
index 0000000..c427064
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-1771.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-23.mp4 b/docs/examples/atlas/mathieu/m23/m23-23.mp4
new file mode 100644
index 0000000..daf9f2d
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-23.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-253-i.mp4 b/docs/examples/atlas/mathieu/m23/m23-253-i.mp4
new file mode 100644
index 0000000..60d0b68
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-253-i.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-253-ii.mp4 b/docs/examples/atlas/mathieu/m23/m23-253-ii.mp4
new file mode 100644
index 0000000..84ac1cb
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-253-ii.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-40320.mp4 b/docs/examples/atlas/mathieu/m23/m23-40320.mp4
new file mode 100644
index 0000000..c5f34c3
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-40320.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-506.mp4 b/docs/examples/atlas/mathieu/m23/m23-506.mp4
new file mode 100644
index 0000000..25d43a1
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-506.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-P1.mp4 b/docs/examples/atlas/mathieu/m23/m23-P1.mp4
new file mode 100644
index 0000000..f7b47f5
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-P1.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/m23-P2.mp4 b/docs/examples/atlas/mathieu/m23/m23-P2.mp4
new file mode 100644
index 0000000..5490968
Binary files /dev/null and b/docs/examples/atlas/mathieu/m23/m23-P2.mp4 differ
diff --git a/docs/examples/atlas/mathieu/m23/p1.md b/docs/examples/atlas/mathieu/m23/p1.md
index a58528f..8ea2795 100644
--- a/docs/examples/atlas/mathieu/m23/p1.md
+++ b/docs/examples/atlas/mathieu/m23/p1.md
@@ -1,11 +1,31 @@
-# Mathieu group M~23~ (Presentation 1)
+# The trivial subgroup of Mathieu group M~23~ (Presentation 1)
-The claims on this page come from
-[https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+
-The Python script includes the additional relation
-$(abab^2ab^2)^6 = 1$, which its source marks as redundant and useful. We do
-not verify that this relation is redundant here.
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 2 minutes 34 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
## Claim
@@ -16,24 +36,23 @@ $$
= (abab^2)^3(ab^2ab^{-1})^2abab^2abab^{-1}ab^2 = 1 \rangle
$$
-with
+Let $H$ be the trivial subgroup. Including the additional relation described
+above, the presentation used by the script defines a group of order
$$
- |\mathrm{M}_{23}| = 10,200,960.
+ [\mathrm{M}_{23}:H] = |\mathrm{M}_{23}| = 10,200,960.
$$
-On this page, we verify that the presentation used by the script defines a
-group of order $10,200,960$.
-
## The code
-In [libsemigroups_pybind11][], the following script constructs the presentation
-for M~23~ and runs the Todd-Coxeter algorithm.
+The following script includes the full presentation and can be run on its own.
Code
```python
+from datetime import timedelta
+
from libsemigroups_pybind11 import (
Presentation,
ToddCoxeter,
@@ -42,7 +61,7 @@ from libsemigroups_pybind11 import (
)
from libsemigroups_pybind11.words import parse_relations
-# Setup the presentation object with the empty and inverses, so it can represent a group
+# Enable the empty word and inverses so the presentation defines a group.
p = Presentation("abAB")
p.contains_empty_word(True)
presentation.add_inverse_rules(p, "ABab")
@@ -52,7 +71,7 @@ presentation.add_rule(p, parse_relations("a^2"), "")
presentation.add_rule(p, parse_relations("b^4"), "")
presentation.add_rule(p, parse_relations("(ab)^23"), "")
presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
-presentation.add_rule(p, parse_relations("(a,b)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
presentation.add_rule(
@@ -62,25 +81,23 @@ presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"),
presentation.replace_subword(p, "A", "a")
p.alphabet("Bab")
-# Run the Todd-Coxeter algorithm
tc = ToddCoxeter(congruence_kind.twosided, p)
tc.strategy(ToddCoxeter.options.strategy.felsch)
+tc.report_every(timedelta(seconds=1.6))
-# Takes approx. 2.5 minutes
-
-print(f"The size of the group defined by the presentation is {tc.number_of_classes()}")
+actual_order = tc.number_of_classes()
+print(f"The size of the group defined by the presentation is {actual_order}")
+if actual_order != 10200960:
+ raise RuntimeError(f"expected order 10200960, got {actual_order}")
```
## The output
-The truncated output of the enumeration is below:
-
??? info "Truncated output from the Python script"
```
- Bab
++++++++++++++++++++++++++++++++
#0: ToddCoxeter: RUN 0 START (strategy() = felsch)
#0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
@@ -90,83 +107,29 @@ The truncated output of the enumeration is below:
#0: ToddCoxeter: nodes | 1 | 0 | 1
#0: ToddCoxeter: | active | missing | % complete
#0: ToddCoxeter: edges | 0 | 3 | 0.0%
- #0: ToddCoxeter: time | run 0 = 22µs | all runs = 22µs | elapsed = 66µs
+ #0: ToddCoxeter: time | run 0 = 21µs | all runs = 21µs | elapsed = 83µs
++++++++++++++++++++++++++++++++
#1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
- #1: ToddCoxeter: nodes | 1,822,250 | 36,043 | 1,858,318
- #1: ToddCoxeter: diff 0.0.0 | +1,822,249 | +36,043 | +1,858,317
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 4,253,812 | 1,212,938 | 77.8%
- #1: ToddCoxeter: diff 0.0.0 | +4,253,812 | +1,212,935 | +77.8%
- #1: ToddCoxeter: phase 0.0 = 1.000s | run 0 = 1.000s | all runs = 1.000s | elapsed = 1.000s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.2 | active | killed | defined
- #1: ToddCoxeter: nodes | 3,655,607 | 83,517 | 3,739,139
- #1: ToddCoxeter: diff 0.0.1 | +1,833,357 | +47,474 | +1,880,821
- #1: ToddCoxeter: diff 0.0.0 | +3,655,606 | +83,517 | +3,739,138
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 8,538,514 | 2,428,307 | 77.9%
- #1: ToddCoxeter: diff 0.0.1 | +4,284,702 | +1,215,369 | +0.0%
- #1: ToddCoxeter: diff 0.0.0 | +8,538,514 | +2,428,304 | +77.9%
- #1: ToddCoxeter: phase 0.0 = 2.001s | run 0 = 2.001s | all runs = 2.001s | elapsed = 2.001s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.3 | active | killed | defined
- #1: ToddCoxeter: nodes | 5,466,932 | 137,547 | 5,604,495
- #1: ToddCoxeter: diff 0.0.2 | +1,811,325 | +54,030 | +1,865,356
- #1: ToddCoxeter: diff 0.0.0 | +5,466,931 | +137,547 | +5,604,494
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 12,774,618 | 3,626,178 | 77.9%
- #1: ToddCoxeter: diff 0.0.2 | +4,236,104 | +1,197,871 | +0.0%
- #1: ToddCoxeter: diff 0.0.0 | +12,774,618 | +3,626,175 | +77.9%
- #1: ToddCoxeter: phase 0.0 = 3.006s | run 0 = 3.006s | all runs = 3.006s | elapsed = 3.006s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.4 | active | killed | defined
- #1: ToddCoxeter: nodes | 7,294,223 | 190,832 | 7,485,071
- #1: ToddCoxeter: diff 0.0.3 | +1,827,291 | +53,285 | +1,880,576
- #1: ToddCoxeter: diff 0.0.0 | +7,294,222 | +190,832 | +7,485,070
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 17,048,446 | 4,834,223 | 77.9%
- #1: ToddCoxeter: diff 0.0.3 | +4,273,828 | +1,208,045 | +0.0%
- #1: ToddCoxeter: diff 0.0.0 | +17,048,446 | +4,834,220 | +77.9%
- #1: ToddCoxeter: phase 0.0 = 4.011s | run 0 = 4.011s | all runs = 4.011s | elapsed = 4.011s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.5 | active | killed | defined
- #1: ToddCoxeter: nodes | 9,065,055 | 235,680 | 9,300,749
- #1: ToddCoxeter: diff 0.0.4 | +1,770,832 | +44,848 | +1,815,678
- #1: ToddCoxeter: diff 0.0.0 | +9,065,054 | +235,680 | +9,300,748
- #1: ToddCoxeter: | active | missing | % complete
[... lines omitted ...]
- #0: ToddCoxeter: diff 0.1.51 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 30,602,880 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.1.51 | +0 | +0 | +0.0%
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.1 = 51.591s | run 0 = 2min29s | all runs = 2min29s | elapsed = 2min29s
- #0: ToddCoxeter: lookahead_next() is now max(f x a = 20,401,920, m = 10,000) (+15,401,920)
- #0: ToddCoxeter: because a > n
- #0: ToddCoxeter: where: a = number_of_nodes_active() = 10,200,960
- #0: ToddCoxeter: f = lookahead_growth_factor() = 2
- #0: ToddCoxeter: m = lookahead_min() = 10,000
- #0: ToddCoxeter: n = lookahead_next() = 5,000,000
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.2.53 | active | killed | defined
- #0: ToddCoxeter: nodes | 10,200,960 | 33,207,284 | 43,408,244
- #0: ToddCoxeter: diff 0.2.52 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0
#0: ToddCoxeter: | active | missing | % complete
#0: ToddCoxeter: edges | 30,602,880 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.2.52 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.2.35 | +0 | +0 | +0.0%
#0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.2 = 51.591s | run 0 = 2min29s | all runs = 2min29s | elapsed = 2min29s
+ #0: ToddCoxeter: phase 0.2 = 53.212s | run 0 = 2min34s | all runs = 2min34s | elapsed = 2min34s
++++++++++++++++++++++++++++++++
#0: ToddCoxeter: RUN 0 STOP (finished)
#0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
- #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
- #0: ToddCoxeter: time spent in phases | 51.591s (35%) | - (0%) | - (0%) | 1min37s (65%)
- #0: ToddCoxeter: phase 0.2 = 51.708s | run 0 = 2min29s | all runs = 2min29s | elapsed = 2min29s
+ #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | 53.212s (34%) | - (0%) | - (0%) | 1min41s (65%)
+ #0: ToddCoxeter: phase 0.2 = 53.475s | run 0 = 2min34s | all runs = 2min34s | elapsed = 2min34s
The size of the group defined by the presentation is 10200960
```
-:material-checkbox-marked-circle-outline: The computed size of the group
-matches the claimed size: $10,200,960$.
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed order is the claimed order:
+$10,200,960$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m23/p1_m11.md b/docs/examples/atlas/mathieu/m23/p1_m11.md
index bfc2755..0ae1366 100644
--- a/docs/examples/atlas/mathieu/m23/p1_m11.md
+++ b/docs/examples/atlas/mathieu/m23/p1_m11.md
@@ -1,36 +1,62 @@
-# Mathieu group M~11~ as a subgroup of Mathieu group M~23~ (Presentation 1)
+# $\mathrm{M}_{11}$ as a subgroup of Mathieu group M~23~ (Presentation 1)
-The claims on this page come from
-[https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1](https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1).
+
-## Claim
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max5W1).
-$$
- \mathrm{M}_{23} = \langle a, b \mid a^2 = b^4 = (ab)^{23} = (ab^2)^6 = [a, b]^6 = (abab^{−1}ab^2)^4 = (ab)^3ab^{−1}ab^2(abab^{−1})^2(ab)^3(ab^{−1})^3 = (abab^2)^3(ab^2ab^{−1})^2abab^2abab^{−1}ab^2 = 1\rangle
-$$
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 203 milliseconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
-and
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
$$
- \mathrm{M}_{11} = {\mathrm{M}_{23}} \mathbin{/} \langle a, b \mid b = bbabbababbbabbabab = 1 \rangle
+ H = \langle u,v \rangle \cong \mathrm{M}_{11},
$$
-with
+then
$$
- [\mathrm{M}_{23} : \mathrm{M}_{11}] = 1,288.
+ [\mathrm{M}_{23}:H] = 1,288.
$$
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses. The presentation replaces `A` by `a`,
+since $a^2=1$, and `B` represents $b^{-1}$.
+
## The code
-In [libsemigroups_pybind11][], the following script constructs the presentation
-for M~23~, adds the generating pairs that define the subgroup M~11~, and runs
-the Todd-Coxeter algorithm.
+The following script includes the full presentation and can be run on its own.
Code
```python
+from datetime import timedelta
+
from libsemigroups_pybind11 import (
Presentation,
ToddCoxeter,
@@ -39,7 +65,7 @@ from libsemigroups_pybind11 import (
)
from libsemigroups_pybind11.words import parse_relations
-# Setup the presentation object with the empty word and inverses, so it can represent a group
+# Enable the empty word and inverses so the presentation defines a group.
p = Presentation("abAB")
p.contains_empty_word(True)
presentation.add_inverse_rules(p, "ABab")
@@ -49,116 +75,73 @@ presentation.add_rule(p, parse_relations("a^2"), "")
presentation.add_rule(p, parse_relations("b^4"), "")
presentation.add_rule(p, parse_relations("(ab)^23"), "")
presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
-presentation.add_rule(p, parse_relations("(a,b)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abab^2ab^2)^6"), ""
+) # Is redundant, but very useful.
presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
+presentation.replace_subword(p, "A", "a")
+p.alphabet("Bab")
-
-# Add generating pairs for the maximal subgroup
+# M11, order 7920, expected index 1288.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max5W1
+# ATLAS output words simplified using a^2 = b^4 = 1; B denotes b^-1.
tc = ToddCoxeter(congruence_kind.onesided, p)
-tc.add_generating_pair("b", "")
-tc.add_generating_pair("bbabbababbbabbabab", "")
-
-# Run the Todd-Coxeter algorithm
-tc.strategy(tc.options.strategy.felsch)
-tc.def_version(tc.options.def_version.two)
-tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
-tc.use_relations_in_extra(True)
-tc.def_max(10000)
-tc.run()
-
-print(f"The index of the subgroup is {tc.number_of_classes()}")
+for word in (
+ "bbabbabaBabbabab",
+ "b",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(milliseconds=2.2))
+
+actual_index = tc.number_of_classes()
+print(f"The index of M11 is {actual_index}")
+if actual_index != 1288:
+ raise RuntimeError(f"expected index 1288, got {actual_index}")
```
## The output
-??? info "Output from the Python script"
+??? info "Truncated output from the Python script"
```
++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
- #0: ToddCoxeter: |A| = 4, |R| = 12, |u| + |v| ∈ [2, 46], ∑(|u| + |v|) = 198
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.0 START
- #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
- #0: ToddCoxeter: nodes | 119 | 56 | 175
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 181 | 295 | 38.0%
- #0: ToddCoxeter: time | run 0 = 248µs | all runs = 248µs | elapsed = 323µs
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
- #1: ToddCoxeter: nodes | 1,258,648 | 24,628 | 1,283,314
- #1: ToddCoxeter: diff 0.0.0 | +1,258,529 | +24,572 | +1,283,139
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 3,915,350 | 1,119,242 | 77.8%
- #1: ToddCoxeter: diff 0.0.0 | +3,915,169 | +1,118,947 | +39.7%
- #1: ToddCoxeter: phase 0.0 = 1.000s | run 0 = 1.000s | all runs = 1.000s | elapsed = 1.000s
- #0: ToddCoxeter: large collapse, number of coincidences 100,000 >= 100,000 = large_collapse()!
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.2 | active | killed | defined
- #1: ToddCoxeter: nodes | 273,941 | 2,129,053 | 2,402,478
- #1: ToddCoxeter: diff 0.0.1 | -984,707 | +2,104,425 | +1,119,164
- #1: ToddCoxeter: diff 0.0.0 | +273,822 | +2,128,997 | +2,402,303
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 0 | 1,095,764 | 0.0%
- #1: ToddCoxeter: diff 0.0.1 | -3,915,350 | -23,478 | -77.8%
- #1: ToddCoxeter: diff 0.0.0 | -181 | +1,095,469 | -38.0%
- #1: ToddCoxeter: phase 0.0 = 2.000s | run 0 = 2.001s | all runs = 2.001s | elapsed = 2.001s
+ #0: ToddCoxeter: RUN 0 START (strategy() = hlt)
+ #0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.0 STOP
- #0: ToddCoxeter: FELSCH 0.0.3 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 2,401,190 | 2,402,478
- #0: ToddCoxeter: diff 0.0.2 | -272,653 | +272,137 | +0
- #0: ToddCoxeter: diff 0.0.0 | +1,169 | +2,401,134 | +2,402,303
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.0.2 | +5,152 | -1,095,764 | +100.0%
- #0: ToddCoxeter: diff 0.0.0 | +4,971 | -295 | +62.0%
- #0: ToddCoxeter: phase 0.0 = 2.013s | run 0 = 2.013s | all runs = 2.013s | elapsed = 2.013s
+ #0: ToddCoxeter: HLT 0.0 START
+ #0: ToddCoxeter: HLT 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 14 | 2 | 16
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 15 | 27 | 35.7%
+ #0: ToddCoxeter: time | run 0 = 26µs | all runs = 26µs | elapsed = 96µs
++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: LOOKAHEAD 0.1 START (lookahead_extent() = full, lookahead_style() = hlt)
- #0: ToddCoxeter: LOOKAHEAD 0.1.0 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 2,401,190 | 2,402,478
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: time | run 0 = 2.013s | all runs = 2.013s | elapsed = 2.013s
- #0: ToddCoxeter: triggered because there are skipped definitions (1,288 active nodes)!
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: LOOKAHEAD 0.1 STOP
- #0: ToddCoxeter: LOOKAHEAD 0.1.1 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 2,401,190 | 2,402,478
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.1 = 391µs | run 0 = 2.014s | all runs = 2.014s | elapsed = 2.014s
- #0: ToddCoxeter: lookahead_next() is now max(f x a = 2,576, m = 10,000) (-4,990,000)
- #0: ToddCoxeter: because f x a < n
- #0: ToddCoxeter: where: a = number_of_nodes_active() = 1,288
- #0: ToddCoxeter: f = lookahead_growth_factor() = 2
- #0: ToddCoxeter: m = lookahead_min() = 10,000
- #0: ToddCoxeter: n = lookahead_next() = 5,000,000
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.2.2 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 2,401,190 | 2,402,478
- #0: ToddCoxeter: diff 0.2.1 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.2.1 | +0 | +0 | +0.0%
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.2 = 418µs | run 0 = 2.014s | all runs = 2.014s | elapsed = 2.014s
+ #1: ToddCoxeter: HLT 0.0.1 | active | killed | defined
+ [... lines omitted ...]
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 3,864 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.74 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +3,849 | -27 | +64.3%
+ #1: ToddCoxeter: phase 0.1 = 202ms | run 0 = 202ms | all runs = 202ms | elapsed = 202ms
++++++++++++++++++++++++++++++++
#0: ToddCoxeter: RUN 0 STOP (finished)
- #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
- #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
- #0: ToddCoxeter: time spent in phases | 414µs (0%) | - (0%) | - (0%) | 2.013s (100%)
- #0: ToddCoxeter: phase 0.2 = 437µs | run 0 = 2.014s | all runs = 2.014s | elapsed = 2.014s
- The index of the subgroup is 1288
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 1 | 0
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | 191ms (94%) | - (0%)
+ #0: ToddCoxeter: phase 0.1 = 203ms | run 0 = 203ms | all runs = 203ms | elapsed = 203ms
+ The index of M11 is 1288
```
-:material-checkbox-marked-circle-outline: The computed index is the same as
-the claimed index: $1,288$.
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index:
+$1,288$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m23/p1_m22.md b/docs/examples/atlas/mathieu/m23/p1_m22.md
index b6a71e5..b6aeaa0 100644
--- a/docs/examples/atlas/mathieu/m23/p1_m22.md
+++ b/docs/examples/atlas/mathieu/m23/p1_m22.md
@@ -1,272 +1,159 @@
-# Mathieu group M~22~ as a subgroup of Mathieu group M~23~ (Presentation 1)
+# $\mathrm{M}_{22}$ as a subgroup of Mathieu group M~23~ (Presentation 1)
-The claims on this page come from
-[https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1](https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1).
+
-## Claim
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P1).
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max1W1).
-$$
- \mathrm{M}_{23} = \langle a, b \mid a^2 = b^4 = (ab)^{23} = (ab^2)^6 = [a, b]^6 = (abab^{−1}ab^2)^4 = (ab)^3ab^{−1}ab^2(abab^{−1})^2(ab)^3(ab^{−1})^3 = (abab^2)^3(ab^2ab^{−1})^2abab^2abab^{−1}ab^2 = 1\rangle
-$$
+The script includes the additional relation $(abab^2ab^2)^6 = 1$, which
+its source marks as redundant and useful. We do not verify that this relation
+is redundant here.
+
+## The computation
+
+The recorded run took about 282 milliseconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
-and
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
$$
- \mathrm{M}_{22} = {\mathrm{M}_{23}} \mathbin{/} \langle a, b \mid a = bababbab = 1 \rangle
+ H = \langle u,v \rangle \cong \mathrm{M}_{22},
$$
-with
+then
$$
- [\mathrm{M}_{23} : \mathrm{M}_{22}] = 23.
+ [\mathrm{M}_{23}:H] = 23.
$$
-!!! warning
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses. The presentation replaces `A` by `a`,
+since $a^2=1$, and `B` represents $b^{-1}$.
- The word $bababbab$ in plain text on the page
+!!! warning "ATLAS subgroup generators for M~22~"
- [https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1](https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1)
+ The [ATLAS subgroup page](https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1)
+ gives `a` and `bababbab` as generators for M~22~, but the second word is
+ incorrect. Expanding the corresponding
+ [straightline program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max1W1)
+ gives `bababbbab`, with one extra `b`. The correct pair is therefore `a`
+ and `bababbbab`, generating a subgroup of index $23$ in M~23~.
- does not seem to generate a subgroup of the correct index. Expanding
- the [GAP][] straightline programme at:
+ The word `babaBab` used in the code is equivalent to `bababbbab`:
+ `B` denotes $b^{-1}$, which equals $b^3$ because $b^4=1$.
- [http://atlas.math.rwth-aachen.de/Atlas/v3/scripts/slp2.php%3Fformat=GAP&filename=M23G1-max1W1&filename_path=spor%252FM23&sub_dir=words](http://atlas.math.rwth-aachen.de/Atlas/v3/scripts/slp2.php%3Fformat=GAP&filename=M23G1-max1W1&filename_path=spor%252FM23&sub_dir=words)
+## The code
- yields the word $bababbbab$ which does generate a subgroup of the correct index.
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import (
+ Presentation,
+ ToddCoxeter,
+ congruence_kind,
+ presentation,
+)
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
+presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
+presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abab^2ab^2)^6"), ""
+) # Is redundant, but very useful.
+presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
+presentation.replace_subword(p, "A", "a")
+p.alphabet("Bab")
+
+# M22, order 443520, expected index 23.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M23/words/M23G1-max1W1
+# ATLAS output words simplified using a^2 = b^4 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+for word in (
+ "a",
+ "babaBab",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(milliseconds=3.3))
+
+actual_index = tc.number_of_classes()
+print(f"The index of M22 is {actual_index}")
+if actual_index != 23:
+ raise RuntimeError(f"expected index 23, got {actual_index}")
+```
+
+
-## The code
+## The output
-=== "Plain text"
-
- In [libsemigroups_pybind11][], the following script constructs the
- presentation for M~23~, adds the generating pairs that define the subgroup
- M~22~, and runs the Todd-Coxeter algorithm. This is based on the plain text
- implementation from the ATLAS.
-
- ```python
- from libsemigroups_pybind11 import (
- Presentation,
- ToddCoxeter,
- congruence_kind,
- presentation,
- )
- from libsemigroups_pybind11.words import parse_relations
-
- # Setup the presentation object with the empty word and inverses, so it can represent a group
- p = Presentation("abAB")
- p.contains_empty_word(True)
- presentation.add_inverse_rules(p, "ABab")
-
- # Add the defining relations
- presentation.add_rule(p, parse_relations("a^2"), "")
- presentation.add_rule(p, parse_relations("b^4"), "")
- presentation.add_rule(p, parse_relations("(ab)^23"), "")
- presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
- presentation.add_rule(p, parse_relations("(a,b)^6"), "")
- presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
- presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
- presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
-
-
- # Add generating pairs for the maximal subgroup
- tc = ToddCoxeter(congruence_kind.onesided, p)
- tc.add_generating_pair("a", "")
- tc.add_generating_pair("bababbab", "")
-
- # Run the Todd-Coxeter algorithm
- tc.strategy(tc.options.strategy.felsch)
- tc.def_version(tc.options.def_version.two)
- tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
- tc.use_relations_in_extra(True)
- tc.def_max(10000)
- tc.run()
-
- print(f"The index of the group is {tc.number_of_classes()}")
- ```
+??? info "Truncated output from the Python script"
-=== "Straightline program"
-
- In [libsemigroups_pybind11][], the following script constructs the
- presentation for M~23~, adds the generating pairs that define the subgroup
- M~22~, and runs the Todd-Coxeter algorithm. This is based on the
- [GAP][] straightline program hosted on the ATLAS.
-
- ```python
- from libsemigroups_pybind11 import (
- Presentation,
- ToddCoxeter,
- congruence_kind,
- presentation,
- )
- from libsemigroups_pybind11.words import parse_relations
-
- # Setup the presentation object with the empty word and inverses, so it can represent a group
- p = Presentation("abAB")
- p.contains_empty_word(True)
- presentation.add_inverse_rules(p, "ABab")
-
- # Add the defining relations
- presentation.add_rule(p, parse_relations("a^2"), "")
- presentation.add_rule(p, parse_relations("b^4"), "")
- presentation.add_rule(p, parse_relations("(ab)^23"), "")
- presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
- presentation.add_rule(p, parse_relations("(a,b)^6"), "")
- presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
- presentation.add_rule(p, parse_relations("(ab)^3aBab^2(abaB)^2(ab)^3(aB)^3"), "")
- presentation.add_rule(p, parse_relations("(abab^2)^3(ab^2aB)^2abab^2abaBab^2"), "")
-
-
- # Add generating pairs for the maximal subgroup
- tc = ToddCoxeter(congruence_kind.onesided, p)
- tc.add_generating_pair("a", "")
- tc.add_generating_pair("bababbbab", "")
-
- # Run the Todd-Coxeter algorithm
- tc.strategy(tc.options.strategy.felsch)
- tc.def_version(tc.options.def_version.two)
- tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
- tc.use_relations_in_extra(True)
- tc.def_max(10000)
- tc.run()
-
- print(f"The index of the group is {tc.number_of_classes()}")
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = hlt)
+ #0: ToddCoxeter: |A| = 3, |R| = 13, |u| + |v| ∈ [2, 48], ∑(|u| + |v|) = 246
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: HLT 0.0 START
+ #0: ToddCoxeter: HLT 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 7 | 0 | 7
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 8 | 13 | 38.1%
+ #0: ToddCoxeter: time | run 0 = 26µs | all runs = 26µs | elapsed = 90µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: HLT 0.0.1 | active | killed | defined
+ [... lines omitted ...]
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 69 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.69 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +61 | -13 | +61.9%
+ #1: ToddCoxeter: phase 0.1 = 281ms | run 0 = 281ms | all runs = 281ms | elapsed = 281ms
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 1 | 0
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | 275ms (98%) | - (0%)
+ #0: ToddCoxeter: phase 0.1 = 282ms | run 0 = 282ms | all runs = 282ms | elapsed = 282ms
+ The index of M22 is 23
```
-## The output
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index:
+$23$.
+
+!!! note
-=== "Plain text"
-
- ??? info "Output from the Python script"
- ```
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
- #0: ToddCoxeter: |A| = 4, |R| = 12, |u| + |v| ∈ [2, 46], ∑(|u| + |v|) = 198
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.0 START
- #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
- #0: ToddCoxeter: nodes | 112 | 52 | 164
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 184 | 264 | 41.1%
- #0: ToddCoxeter: time | run 0 = 239µs | all runs = 239µs | elapsed = 315µs
- #0: ToddCoxeter: large collapse, number of coincidences 100,000 >= 100,000 = large_collapse()!
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.0 STOP
- #0: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 1,176,014 | 1,177,302
- #0: ToddCoxeter: diff 0.0.0 | +1,176 | +1,175,962 | +1,177,138
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.0.0 | +4,968 | -264 | +58.9%
- #0: ToddCoxeter: phase 0.0 = 980ms | run 0 = 980ms | all runs = 980ms | elapsed = 980ms
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: LOOKAHEAD 0.1 START (lookahead_extent() = full, lookahead_style() = hlt)
- #0: ToddCoxeter: LOOKAHEAD 0.1.0 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 1,176,014 | 1,177,302
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: time | run 0 = 980ms | all runs = 980ms | elapsed = 980ms
- #0: ToddCoxeter: triggered because there are skipped definitions (1,288 active nodes)!
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: LOOKAHEAD 0.1 STOP
- #0: ToddCoxeter: LOOKAHEAD 0.1.1 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 1,176,014 | 1,177,302
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.1 = 436µs | run 0 = 980ms | all runs = 980ms | elapsed = 980ms
- #0: ToddCoxeter: lookahead_next() is now max(f x a = 2,576, m = 10,000) (-4,990,000)
- #0: ToddCoxeter: because f x a < n
- #0: ToddCoxeter: where: a = number_of_nodes_active() = 1,288
- #0: ToddCoxeter: f = lookahead_growth_factor() = 2
- #0: ToddCoxeter: m = lookahead_min() = 10,000
- #0: ToddCoxeter: n = lookahead_next() = 5,000,000
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.2.2 | active | killed | defined
- #0: ToddCoxeter: nodes | 1,288 | 1,176,014 | 1,177,302
- #0: ToddCoxeter: diff 0.2.1 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 5,152 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.2.1 | +0 | +0 | +0.0%
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.2 = 465µs | run 0 = 980ms | all runs = 980ms | elapsed = 980ms
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: RUN 0 STOP (finished)
- #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
- #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
- #0: ToddCoxeter: time spent in phases | 460µs (0%) | - (0%) | - (0%) | 980ms (100%)
- #0: ToddCoxeter: phase 0.2 = 498µs | run 0 = 980ms | all runs = 980ms | elapsed = 980ms
- The index of the subgroup is 1288
- ```
-
- :material-close-circle-outline: The computed index is $1,288$ which is not the
- same as the claimed index: $23$.
-
-=== "Straightline program"
-
- ??? info "Output from the Python script"
-
- ```
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
- #0: ToddCoxeter: |A| = 4, |R| = 12, |u| + |v| ∈ [2, 46], ∑(|u| + |v|) = 198
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.0 START
- #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
- #0: ToddCoxeter: nodes | 113 | 52 | 165
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 178 | 274 | 39.4%
- #0: ToddCoxeter: time | run 0 = 251µs | all runs = 251µs | elapsed = 428µs
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.0 STOP
- #0: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
- #0: ToddCoxeter: nodes | 23 | 880,964 | 880,987
- #0: ToddCoxeter: diff 0.0.0 | -90 | +880,912 | +880,822
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 92 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.0.0 | -86 | -274 | +60.6%
- #0: ToddCoxeter: phase 0.0 = 937ms | run 0 = 937ms | all runs = 937ms | elapsed = 937ms
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: LOOKAHEAD 0.1 START (lookahead_extent() = full, lookahead_style() = hlt)
- #0: ToddCoxeter: LOOKAHEAD 0.1.0 | active | killed | defined
- #0: ToddCoxeter: nodes | 23 | 880,964 | 880,987
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 92 | 0 | 100.0%
- #0: ToddCoxeter: time | run 0 = 937ms | all runs = 937ms | elapsed = 937ms
- #0: ToddCoxeter: triggered because there are skipped definitions (23 active nodes)!
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: LOOKAHEAD 0.1 STOP
- #0: ToddCoxeter: LOOKAHEAD 0.1.1 | active | killed | defined
- #0: ToddCoxeter: nodes | 23 | 880,964 | 880,987
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 92 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.1 = 44µs | run 0 = 937ms | all runs = 937ms | elapsed = 937ms
- #0: ToddCoxeter: lookahead_next() is now max(f x a = 46, m = 10,000) (-4,990,000)
- #0: ToddCoxeter: because f x a < n
- #0: ToddCoxeter: where: a = number_of_nodes_active() = 23
- #0: ToddCoxeter: f = lookahead_growth_factor() = 2
- #0: ToddCoxeter: m = lookahead_min() = 10,000
- #0: ToddCoxeter: n = lookahead_next() = 5,000,000
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.2.2 | active | killed | defined
- #0: ToddCoxeter: nodes | 23 | 880,964 | 880,987
- #0: ToddCoxeter: diff 0.2.1 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 92 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.2.1 | +0 | +0 | +0.0%
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.2 = 94µs | run 0 = 937ms | all runs = 937ms | elapsed = 937ms
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: RUN 0 STOP (finished)
- #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
- #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
- #0: ToddCoxeter: time spent in phases | 87µs (0%) | - (0%) | - (0%) | 937ms (100%)
- #0: ToddCoxeter: phase 0.2 = 119µs | run 0 = 937ms | all runs = 937ms | elapsed = 937ms
- The index of the group is 23
- ```
- :material-checkbox-marked-circle-outline: The computed index is the same as
- the claimed index: $23$.
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m23/p2.md b/docs/examples/atlas/mathieu/m23/p2.md
index cd4795a..14aaafc 100644
--- a/docs/examples/atlas/mathieu/m23/p2.md
+++ b/docs/examples/atlas/mathieu/m23/p2.md
@@ -1,11 +1,31 @@
-# Mathieu group M~23~ (Presentation 2)
+# The trivial subgroup of Mathieu group M~23~ (Presentation 2)
-The claims on this page come from
-[https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P2](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P2).
+
-The Python script includes the additional relation
-$[a,b]^6 = 1$, which does not appear in the presentation displayed on the
-ATLAS page. We do not verify that this relation is redundant here.
+The presentation comes from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/pres/M23G1-P2).
+
+The script includes the additional relation $[a,b]^6 = 1$, which does
+not appear in the presentation displayed on the ATLAS page. We do not verify
+that this relation is redundant here.
+
+## The computation
+
+The recorded run took about 4 minutes 35 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
## Claim
@@ -19,24 +39,23 @@ $$
= 1 \rangle
$$
-with
+Let $H$ be the trivial subgroup. Including the additional relation described
+above, the presentation used by the script defines a group of order
$$
- |\mathrm{M}_{23}| = 10,200,960.
+ [\mathrm{M}_{23}:H] = |\mathrm{M}_{23}| = 10,200,960.
$$
-On this page, we verify that the presentation used by the script defines a
-group of order $10,200,960$.
-
## The code
-In [libsemigroups_pybind11][], the following script constructs the presentation
-for M~23~ and runs the Todd-Coxeter algorithm.
+The following script includes the full presentation and can be run on its own.
Code
```python
+from datetime import timedelta
+
from libsemigroups_pybind11 import (
Presentation,
ToddCoxeter,
@@ -45,7 +64,7 @@ from libsemigroups_pybind11 import (
)
from libsemigroups_pybind11.words import parse_relations
-# Setup the presentation object with the empty and inverses, so it can represent a group
+# Enable the empty word and inverses so the presentation defines a group.
p = Presentation("abAB")
p.contains_empty_word(True)
presentation.add_inverse_rules(p, "ABab")
@@ -54,7 +73,8 @@ presentation.add_inverse_rules(p, "ABab")
presentation.add_rule(p, parse_relations("a^2"), "")
presentation.add_rule(p, parse_relations("b^4"), "")
presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
-presentation.add_rule(p, parse_relations("(a,b)^6"), "")
+# [a,b] = a^-1 b^-1 a b.
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
presentation.add_rule(p, parse_relations("abababaBab^2abaBabaBabababaBaBaB"), "")
presentation.add_rule(
@@ -69,19 +89,20 @@ presentation.add_rule(
p, parse_relations("ababababab^2abaBabab^2ababaBab^2abab^2ab^2abaBaBabab^2"), ""
)
-# Run the Todd-Coxeter algorithm
tc = ToddCoxeter(congruence_kind.twosided, p)
-tc.strategy(tc.options.strategy.felsch)
+tc.strategy(ToddCoxeter.options.strategy.felsch)
+tc.report_every(timedelta(seconds=3))
-print(f"The size of the group defined by the presentation is {tc.number_of_classes()}")
+actual_order = tc.number_of_classes()
+print(f"The size of the group defined by the presentation is {actual_order}")
+if actual_order != 10200960:
+ raise RuntimeError(f"expected order 10200960, got {actual_order}")
```
## The output
-The truncated output of the enumeration is below:
-
??? info "Truncated output from the Python script"
```
@@ -94,84 +115,29 @@ The truncated output of the enumeration is below:
#0: ToddCoxeter: nodes | 1 | 0 | 1
#0: ToddCoxeter: | active | missing | % complete
#0: ToddCoxeter: edges | 0 | 4 | 0.0%
- #0: ToddCoxeter: time | run 0 = 26µs | all runs = 26µs | elapsed = 72µs
+ #0: ToddCoxeter: time | run 0 = 20µs | all runs = 20µs | elapsed = 80µs
++++++++++++++++++++++++++++++++
#1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
- #1: ToddCoxeter: nodes | 1,275,486 | 25,934 | 1,301,428
- #1: ToddCoxeter: diff 0.0.0 | +1,275,485 | +25,934 | +1,301,427
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 3,967,993 | 1,133,951 | 77.8%
- #1: ToddCoxeter: diff 0.0.0 | +3,967,993 | +1,133,947 | +77.8%
- #1: ToddCoxeter: phase 0.0 = 1.005s | run 0 = 1.005s | all runs = 1.005s | elapsed = 1.005s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.2 | active | killed | defined
- #1: ToddCoxeter: nodes | 2,506,185 | 60,138 | 2,566,339
- #1: ToddCoxeter: diff 0.0.1 | +1,230,699 | +34,204 | +1,264,911
- #1: ToddCoxeter: diff 0.0.0 | +2,506,184 | +60,138 | +2,566,338
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 7,801,046 | 2,223,694 | 77.8%
- #1: ToddCoxeter: diff 0.0.1 | +3,833,053 | +1,089,743 | +0.0%
- #1: ToddCoxeter: diff 0.0.0 | +7,801,046 | +2,223,690 | +77.8%
- #1: ToddCoxeter: phase 0.0 = 2.010s | run 0 = 2.010s | all runs = 2.010s | elapsed = 2.010s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.3 | active | killed | defined
- #1: ToddCoxeter: nodes | 3,727,047 | 96,273 | 3,823,324
- #1: ToddCoxeter: diff 0.0.2 | +1,220,862 | +36,135 | +1,256,985
- #1: ToddCoxeter: diff 0.0.0 | +3,727,046 | +96,273 | +3,823,323
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 11,606,914 | 3,301,274 | 77.9%
- #1: ToddCoxeter: diff 0.0.2 | +3,805,868 | +1,077,580 | +0.0%
- #1: ToddCoxeter: diff 0.0.0 | +11,606,914 | +3,301,270 | +77.9%
- #1: ToddCoxeter: phase 0.0 = 3.011s | run 0 = 3.011s | all runs = 3.011s | elapsed = 3.011s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.4 | active | killed | defined
- #1: ToddCoxeter: nodes | 4,908,390 | 131,357 | 5,039,763
- #1: ToddCoxeter: diff 0.0.3 | +1,181,343 | +35,084 | +1,216,439
- #1: ToddCoxeter: diff 0.0.0 | +4,908,389 | +131,357 | +5,039,762
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 15,288,906 | 4,344,654 | 77.9%
- #1: ToddCoxeter: diff 0.0.3 | +3,681,992 | +1,043,380 | +0.0%
- #1: ToddCoxeter: diff 0.0.0 | +15,288,906 | +4,344,650 | +77.9%
- #1: ToddCoxeter: phase 0.0 = 4.015s | run 0 = 4.015s | all runs = 4.015s | elapsed = 4.015s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.5 | active | killed | defined
- #1: ToddCoxeter: nodes | 6,098,758 | 169,799 | 6,268,568
- #1: ToddCoxeter: diff 0.0.4 | +1,190,368 | +38,442 | +1,228,805
- #1: ToddCoxeter: diff 0.0.0 | +6,098,757 | +169,799 | +6,268,567
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 18,999,097 | 5,395,935 | 77.9%
[... lines omitted ...]
- #0: ToddCoxeter: diff 0.1.53 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 40,803,840 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.1.53 | +0 | +0 | +0.0%
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.1 = 53.174s | run 0 = 4min38s | all runs = 4min38s | elapsed = 4min38s
- #0: ToddCoxeter: lookahead_next() is now max(f x a = 20,401,920, m = 10,000) (+15,401,920)
- #0: ToddCoxeter: because a > n
- #0: ToddCoxeter: where: a = number_of_nodes_active() = 10,200,960
- #0: ToddCoxeter: f = lookahead_growth_factor() = 2
- #0: ToddCoxeter: m = lookahead_min() = 10,000
- #0: ToddCoxeter: n = lookahead_next() = 5,000,000
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.2.55 | active | killed | defined
- #0: ToddCoxeter: nodes | 10,200,960 | 15,538,858 | 25,739,818
- #0: ToddCoxeter: diff 0.2.54 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0
#0: ToddCoxeter: | active | missing | % complete
#0: ToddCoxeter: edges | 40,803,840 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.2.54 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.2.19 | +0 | +0 | +0.0%
#0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.2 = 53.174s | run 0 = 4min38s | all runs = 4min38s | elapsed = 4min38s
+ #0: ToddCoxeter: phase 0.2 = 53.555s | run 0 = 4min35s | all runs = 4min35s | elapsed = 4min35s
++++++++++++++++++++++++++++++++
#0: ToddCoxeter: RUN 0 STOP (finished)
#0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
- #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
- #0: ToddCoxeter: time spent in phases | 53.174s (19%) | - (0%) | - (0%) | 3min45s (81%)
- #0: ToddCoxeter: phase 0.2 = 53.410s | run 0 = 4min38s | all runs = 4min38s | elapsed = 4min38s
+ #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | 53.555s (19%) | - (0%) | - (0%) | 3min41s (80%)
+ #0: ToddCoxeter: phase 0.2 = 53.873s | run 0 = 4min35s | all runs = 4min35s | elapsed = 4min35s
The size of the group defined by the presentation is 10200960
```
-:material-checkbox-marked-circle-outline: The computed size of the group
-matches the claimed size: $10,200,960$.
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed order is the claimed order:
+$10,200,960$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m23/p2_m11.md b/docs/examples/atlas/mathieu/m23/p2_m11.md
index 99c11ab..b13969c 100644
--- a/docs/examples/atlas/mathieu/m23/p2_m11.md
+++ b/docs/examples/atlas/mathieu/m23/p2_m11.md
@@ -1,7 +1,19 @@
# Mathieu group M~11~ as a subgroup of Mathieu group M~23~ (Presentation 2)
+
+
+!!! info "Earlier Presentation 2 enumeration"
+
+ This page retains the earlier computation. The new verification files
+ include a [Presentation 1 animation](p1_m11.md), but no corresponding
+ Presentation 2 subgroup video.
+
The claims on this page come from
-[https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1](https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1).
+[https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max5W1](https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max5W1).
+
+The script includes the additional relation $[a,b]^6 = 1$, which does not
+appear in Presentation 2 as displayed on the ATLAS page. We do not verify
+that this relation is redundant here.
## Claim
@@ -48,7 +60,8 @@ presentation.add_inverse_rules(p, "ABab")
presentation.add_rule(p, parse_relations("a^2"), "")
presentation.add_rule(p, parse_relations("b^4"), "")
presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
-presentation.add_rule(p, parse_relations("(a,b)^6"), "")
+# [a,b] = a^-1 b^-1 a b.
+presentation.add_rule(p, parse_relations("(ABab)^6"), "")
presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
presentation.add_rule(p, parse_relations("abababaBab^2abaBabaBabababaBaBaB"), "")
presentation.add_rule(p, parse_relations("abab^2abab^2abab^2ab^2aBab^2aBabab^2abaBab^2"), "")
diff --git a/docs/examples/atlas/mathieu/m23/p2_m22.md b/docs/examples/atlas/mathieu/m23/p2_m22.md
index 1720e64..9fb123e 100644
--- a/docs/examples/atlas/mathieu/m23/p2_m22.md
+++ b/docs/examples/atlas/mathieu/m23/p2_m22.md
@@ -1,8 +1,20 @@
# Mathieu group M~22~ as a subgroup of Mathieu group M~23~ (Presentation 2)
+
+
+!!! info "Earlier Presentation 2 enumeration"
+
+ This page retains the earlier computations. The new verification files
+ include a [Presentation 1 animation](p1_m22.md), but no corresponding
+ Presentation 2 subgroup video.
+
The claims on this page come from
[https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1](https://brauer.maths.qmul.ac.uk/Atlas/v3/subgroup/M23G1-max1W1).
+The scripts include the additional relation $[a,b]^6 = 1$, which does not
+appear in Presentation 2 as displayed on the ATLAS page. We do not verify
+that this relation is redundant here.
+
## Claim
$$
@@ -59,7 +71,8 @@ $$
presentation.add_rule(p, parse_relations("a^2"), "")
presentation.add_rule(p, parse_relations("b^4"), "")
presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
- presentation.add_rule(p, parse_relations("(a,b)^6"), "")
+ # [a,b] = a^-1 b^-1 a b.
+ presentation.add_rule(p, parse_relations("(ABab)^6"), "")
presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
presentation.add_rule(p, parse_relations("abababaBab^2abaBabaBabababaBaBaB"), "")
presentation.add_rule(p, parse_relations("abab^2abab^2abab^2ab^2aBab^2aBabab^2abaBab^2"), "")
@@ -112,7 +125,8 @@ $$
presentation.add_rule(p, parse_relations("a^2"), "")
presentation.add_rule(p, parse_relations("b^4"), "")
presentation.add_rule(p, parse_relations("(ab^2)^6"), "")
- presentation.add_rule(p, parse_relations("(a,b)^6"), "")
+ # [a,b] = a^-1 b^-1 a b.
+ presentation.add_rule(p, parse_relations("(ABab)^6"), "")
presentation.add_rule(p, parse_relations("(abaBab^2)^4"), "")
presentation.add_rule(p, parse_relations("abababaBab^2abaBabaBabababaBaBaB"), "")
presentation.add_rule(p, parse_relations("abab^2abab^2abab^2ab^2aBab^2aBabab^2abaBab^2"), "")
@@ -188,7 +202,7 @@ $$
:material-close-circle-outline: The computed index is $1,288$ which is not the
same as the claimed index: $23$.
-=== "Plain text"
+=== "Straightline program"
??? info "Output from the Python script"
@@ -254,5 +268,5 @@ $$
The index of the subgroup is 23
```
- :material-close-circle-outline: The computed index is the same as
+ :material-checkbox-marked-circle-outline:{ style="color: #00c853" } The computed index is the same as
the claimed index: $23$.
diff --git a/docs/examples/atlas/mathieu/m24.md b/docs/examples/atlas/mathieu/m24.md
deleted file mode 100644
index 639e4f3..0000000
--- a/docs/examples/atlas/mathieu/m24.md
+++ /dev/null
@@ -1,163 +0,0 @@
-# Mathieu group M~24~
-
-The claims on this page come from
-[https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M).
-This is a link to a Magma file that contains a presentation (amongst other
-things). This presentation differs from the presentation provided in plain-text
-on the ATLAS webpage due to the inclusion of an additional relation. The
-comments in the Magma file claim that this additional relation is redundant. We
-do not verify that this relation is redundant here.
-
-## Claim
-
-$$
- \mathrm{M}_{24} = \langle a, b \mid a^2 = b^3 = (ab)^{23} = [a, b]^{12} = [a, bab]^5 = (ababab^{−1})^3(abab^{−1}ab^{−1})^3 = [a,b^{-1}abab]^5 = (ab(abab^{−1})^3)^4 = 1 \rangle
-$$
-
-with
-
-$$
- [\mathrm{M}_{23} : \langle ab \rangle] = 10,644,480.
-$$
-
-## The code
-
-In [libsemigroups_pybind11][], the following script constructs the presentation
-for M~24~, adds the generating pairs that define the subgroup
-$\langle ab \rangle$, and runs the Todd-Coxeter algorithm.
-
-
-Code
-
-```python
-from libsemigroups_pybind11 import (
- Presentation,
- ToddCoxeter,
- congruence_kind,
- presentation,
-)
-from libsemigroups_pybind11.words import parse_relations
-
-# Setup the presentation object with the empty word and inverses, so it can represent a group
-p = Presentation("abAB")
-p.contains_empty_word(True)
-presentation.add_inverse_rules(p, "ABab")
-
-# Add the defining relations
-presentation.add_rule(p, parse_relations("a^2"), "")
-presentation.add_rule(p, parse_relations("b^3"), "")
-presentation.add_rule(p, parse_relations("(ab)^23"), "")
-presentation.add_rule(p, parse_relations("(a,b)^12"), "")
-presentation.add_rule(p, parse_relations("(a,bab)^5"), "")
-presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
-presentation.add_rule(p, parse_relations("(a,Babab)^5"), "") # the claimed redundant relation
-presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
-
-# Make the presentation a bit shorter
-presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
-presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
-presentation.sort_each_rule(p)
-presentation.sort_rules(p)
-
-# Add generating pairs for the subgroup
-tc = ToddCoxeter(congruence_kind.onesided, p)
-tc.add_generating_pair("ab", "")
-
-# Run the Todd-Coxeter algorithm
-tc.strategy(tc.options.strategy.felsch)
-tc.def_version(tc.options.def_version.two)
-tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
-tc.use_relations_in_extra(True)
-tc.def_max(10000)
-tc.run()
-
-print(f"The index of the subgroup is {tc.number_of_classes()}")
-```
-
-
-
-## The output
-
-The truncated output of the enumeration is below:
-
-??? info "Truncated output from the Python script"
-
- ```
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
- #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.0 START
- #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
- #0: ToddCoxeter: nodes | 101 | 35 | 136
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 157 | 449 | 25.9%
- #0: ToddCoxeter: time | run 0 = 180µs | all runs = 180µs | elapsed = 344µs
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
- #1: ToddCoxeter: nodes | 1,415,661 | 175,779 | 1,591,465
- #1: ToddCoxeter: diff 0.0.0 | +1,415,560 | +175,744 | +1,591,329
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 5,654,857 | 2,839,109 | 66.6%
- #1: ToddCoxeter: diff 0.0.0 | +5,654,700 | +2,838,660 | +40.7%
- #1: ToddCoxeter: phase 0.0 = 1.000s | run 0 = 1.000s | all runs = 1.000s | elapsed = 1.000s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.2 | active | killed | defined
- #1: ToddCoxeter: nodes | 2,802,886 | 379,414 | 3,182,324
- #1: ToddCoxeter: diff 0.0.1 | +1,387,225 | +203,635 | +1,590,859
- #1: ToddCoxeter: diff 0.0.0 | +2,802,785 | +379,379 | +3,182,188
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 11,202,682 | 5,614,634 | 66.6%
- #1: ToddCoxeter: diff 0.0.1 | +5,547,825 | +2,775,525 | +0.0%
- #1: ToddCoxeter: diff 0.0.0 | +11,202,525 | +5,614,185 | +40.7%
- #1: ToddCoxeter: phase 0.0 = 2.000s | run 0 = 2.000s | all runs = 2.000s | elapsed = 2.001s
- ++++++++++++++++++++++++++++++++
- #1: ToddCoxeter: FELSCH 0.0.3 | active | killed | defined
- #1: ToddCoxeter: nodes | 4,194,304 | 561,668 | 4,755,972
- #1: ToddCoxeter: diff 0.0.2 | +1,391,418 | +182,254 | +1,573,648
- #1: ToddCoxeter: diff 0.0.0 | +4,194,203 | +561,633 | +4,755,836
- #1: ToddCoxeter: | active | missing | % complete
- #1: ToddCoxeter: edges | 16,763,867 | 8,401,957 | 66.6%
- #1: ToddCoxeter: diff 0.0.2 | +5,561,185 | +2,787,323 | -0.0%
- #1: ToddCoxeter: diff 0.0.0 | +16,763,710 | +8,401,508 | +40.7%
- #1: ToddCoxeter: phase 0.0 = 3.000s | run 0 = 3.001s | all runs = 3.001s | elapsed = 3.001s
- ++++++++++++++++++++++++++++++++
- [... lines omitted ...]
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: LOOKAHEAD 0.1 STOP
- #0: ToddCoxeter: LOOKAHEAD 0.1.65 | active | killed | defined
- #0: ToddCoxeter: nodes | 10,644,480 | 52,857,227 | 63,501,707
- #0: ToddCoxeter: diff 0.1.64 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 63,866,880 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.1.64 | +0 | +0 | +0.0%
- #0: ToddCoxeter: diff 0.1.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.1 = 1min4s | run 0 = 3min37s | all runs = 3min37s | elapsed = 3min37s
- #0: ToddCoxeter: lookahead_next() is now max(f x a = 21,288,960, m = 10,000) (+16,288,960)
- #0: ToddCoxeter: because a > n
- #0: ToddCoxeter: where: a = number_of_nodes_active() = 10,644,480
- #0: ToddCoxeter: f = lookahead_growth_factor() = 2
- #0: ToddCoxeter: m = lookahead_min() = 10,000
- #0: ToddCoxeter: n = lookahead_next() = 5,000,000
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: FELSCH 0.2.66 | active | killed | defined
- #0: ToddCoxeter: nodes | 10,644,480 | 52,857,227 | 63,501,707
- #0: ToddCoxeter: diff 0.2.65 | +0 | +0 | +0
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0
- #0: ToddCoxeter: | active | missing | % complete
- #0: ToddCoxeter: edges | 63,866,880 | 0 | 100.0%
- #0: ToddCoxeter: diff 0.2.65 | +0 | +0 | +0.0%
- #0: ToddCoxeter: diff 0.2.0 | +0 | +0 | +0.0%
- #0: ToddCoxeter: phase 0.2 = 1min4s | run 0 = 3min37s | all runs = 3min37s | elapsed = 3min37s
- ++++++++++++++++++++++++++++++++
- #0: ToddCoxeter: RUN 0 STOP (finished)
- #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
- #0: ToddCoxeter: num. phases | 1 | 0 | 0 | 1
- #0: ToddCoxeter: time spent in phases | 1min4s (29%) | - (0%) | - (0%) | 2min33s (70%)
- #0: ToddCoxeter: phase 0.2 = 1min4s | run 0 = 3min37s | all runs = 3min37s | elapsed = 3min38s
- The index of the subgroup is 10644480
- ```
-
-:material-checkbox-marked-circle-outline: The computed index is the same as the
-claimed index: $10,644,480$.
diff --git a/docs/examples/atlas/mathieu/m24/10644480.md b/docs/examples/atlas/mathieu/m24/10644480.md
new file mode 100644
index 0000000..21e924f
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/10644480.md
@@ -0,0 +1,155 @@
+# The cyclic subgroup $\langle ab \rangle$ of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+## The computation
+
+The recorded run took about 2 minutes 5 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $H=\langle ab \rangle$, claimed to be cyclic of order $23$. Then
+
+$$
+ [\mathrm{M}_{24}:H] = 10,644,480.
+$$
+
+We verify this index. The enumeration is over $H$, so its result is an index
+rather than the order $244,823,040$ of M~24~.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# Run the Todd-Coxeter algorithm
+tc = ToddCoxeter(congruence_kind.onesided, p)
+
+tc.add_generating_pair("ab", "")
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(10644480)
+# Cap the reporting interval at one second.
+tc.report_every(timedelta(seconds=1))
+
+tc.run_for(timedelta(minutes=10))
+
+if not tc.finished():
+ raise RuntimeError("The enumeration did not finish within 10 minutes")
+
+actual_index = tc.number_of_classes()
+print(f"The index of the subgroup is {actual_index}")
+if actual_index != 10644480:
+ raise RuntimeError(f"expected index 10644480, got {actual_index}")
+```
+
+
+
+## The output
+
+The original log calls the final value “the size of the group defined by the
+presentation”. Since the script enumerates over $\langle ab \rangle$, that value
+is the subgroup index. The code above uses the corrected label.
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: running for approx. 10min0s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 101 | 35 | 136
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 157 | 449 | 25.9%
+ #0: ToddCoxeter: time | run 0 = 136µs | all runs = 136µs | elapsed = 205µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 1,946,556 | 253,428 | 2,199,994
+ #1: ToddCoxeter: diff 0.0.0 | +1,946,455 | +253,393 | +2,199,858
+ [... lines omitted ...]
+ #0: ToddCoxeter: diff 0.1.124 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +63,866,723 | -449 | +74.1%
+ #0: ToddCoxeter: phase 0.1 = 2min4s | run 0 = 2min4s | all runs = 2min4s | elapsed = 2min4s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.126 | active | killed | defined
+ #1: ToddCoxeter: nodes | 10,644,480 | 52,857,227 | 63,501,707
+ #1: ToddCoxeter: diff 0.1.125 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +10,644,379 | +52,857,192 | +63,501,571
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 63,866,880 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.125 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +63,866,723 | -449 | +74.1%
+ #1: ToddCoxeter: phase 0.1 = 2min5s | run 0 = 2min5s | all runs = 2min5s | elapsed = 2min5s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 2min4s (99%)
+ #0: ToddCoxeter: phase 0.1 = 2min5s | run 0 = 2min5s | all runs = 2min5s | elapsed = 2min5s
+ The size of the group defined by the presentation is 10644480
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $10,644,480$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/1288.md b/docs/examples/atlas/mathieu/m24/1288.md
new file mode 100644
index 0000000..8839950
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/1288.md
@@ -0,0 +1,163 @@
+# M~12~:2 as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max4W1).
+
+## The computation
+
+The recorded run took about 2 minutes 46 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong \mathrm{M}_{12}:2,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 1,288.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# M12.2, order 190080, expected index 1288.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max4W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(1288)
+for word in (
+ "a",
+ "baBabaBabaBabaBabaBaBaBaBaBaBaBaBaBaBaBaBaBababababababababababababababaBaba"
+ "BabaBabaBabaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=1))
+
+actual_index = tc.number_of_classes()
+print(f"The index of M12.2 is {actual_index}")
+if actual_index != 1288:
+ raise RuntimeError(f"expected index 1288, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 178 | 55 | 233
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 295 | 773 | 27.6%
+ #0: ToddCoxeter: time | run 0 = 104µs | all runs = 104µs | elapsed = 181µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 2,224,295 | 270,182 | 2,494,494
+ #1: ToddCoxeter: diff 0.0.0 | +2,224,117 | +270,127 | +2,494,261
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 8,880,625 | 4,465,145 | 66.5%
+ [... lines omitted ...]
+ #1: ToddCoxeter: diff 0.1.165 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +7,433 | -773 | +72.4%
+ #1: ToddCoxeter: phase 0.1 = 2min44s | run 0 = 2min44s | all runs = 2min44s | elapsed = 2min44s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.167 | active | killed | defined
+ #1: ToddCoxeter: nodes | 1,288 | 308,581,440 | 308,582,728
+ #1: ToddCoxeter: diff 0.1.166 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +1,110 | +308,581,385 | +308,582,495
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 7,728 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.166 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +7,433 | -773 | +72.4%
+ #1: ToddCoxeter: phase 0.1 = 2min45s | run 0 = 2min45s | all runs = 2min45s | elapsed = 2min45s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 2min41s (97%)
+ #0: ToddCoxeter: phase 0.1 = 2min46s | run 0 = 2min46s | all runs = 2min46s | elapsed = 2min46s
+ The index of M12.2 is 1288
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $1,288$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/1457280.md b/docs/examples/atlas/mathieu/m24/1457280.md
new file mode 100644
index 0000000..a225ae4
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/1457280.md
@@ -0,0 +1,162 @@
+# $L_2(7)$ as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max9W1).
+
+## The computation
+
+The recorded run took about 41.647 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong L_2(7),
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 1,457,280.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# L2(7), order 168, expected index 1457280.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max9W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(1457280)
+for word in (
+ "a",
+ "baBabaBabaBabaBaBababaBabaBababababaBabaBabaBabaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=1))
+
+actual_index = tc.number_of_classes()
+print(f"The index of L2(7) is {actual_index}")
+if actual_index != 1457280:
+ raise RuntimeError(f"expected index 1457280, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 164 | 29 | 193
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 232 | 752 | 23.6%
+ #0: ToddCoxeter: time | run 0 = 107µs | all runs = 107µs | elapsed = 175µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 2,083,767 | 254,278 | 2,338,059
+ #1: ToddCoxeter: diff 0.0.0 | +2,083,603 | +254,249 | +2,337,866
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 8,320,554 | 4,182,048 | 66.6%
+ [... lines omitted ...]
+ #1: ToddCoxeter: diff 0.1.41 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +8,743,448 | -752 | +76.4%
+ #1: ToddCoxeter: phase 0.1 = 41.192s | run 0 = 41.192s | all runs = 41.192s | elapsed = 41.192s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.1.43 | active | killed | defined
+ #0: ToddCoxeter: nodes | 1,457,280 | 74,443,085 | 75,900,365
+ #0: ToddCoxeter: diff 0.1.42 | +0 | +0 | +0
+ #0: ToddCoxeter: diff 0.1.0 | +1,457,116 | +74,443,056 | +75,900,172
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 8,743,680 | 0 | 100.0%
+ #0: ToddCoxeter: diff 0.1.42 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +8,743,448 | -752 | +76.4%
+ #0: ToddCoxeter: phase 0.1 = 41.399s | run 0 = 41.400s | all runs = 41.400s | elapsed = 41.400s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 41.152s (99%)
+ #0: ToddCoxeter: phase 0.1 = 41.647s | run 0 = 41.647s | all runs = 41.647s | elapsed = 41.647s
+ The index of L2(7) is 1457280
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $1,457,280$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/1771.md b/docs/examples/atlas/mathieu/m24/1771.md
new file mode 100644
index 0000000..f705cee
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/1771.md
@@ -0,0 +1,162 @@
+# $2^6:3.S_6$ as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max5W1).
+
+## The computation
+
+The recorded run took about 54.390 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong 2^6:3.S_6,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 1,771.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# 2^6:3.S6, order 138240, expected index 1771.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max5W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(1771)
+for word in (
+ "abaBabaBabaB",
+ "baBaBaBaBaBaBabababaBababaBabababababababababaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=0.5))
+
+actual_index = tc.number_of_classes()
+print(f"The index of 2^6:3.S6 is {actual_index}")
+if actual_index != 1771:
+ raise RuntimeError(f"expected index 1771, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 178 | 26 | 204
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 246 | 822 | 23.0%
+ #0: ToddCoxeter: time | run 0 = 120µs | all runs = 120µs | elapsed = 196µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 1,063,879 | 134,650 | 1,198,537
+ #1: ToddCoxeter: diff 0.0.0 | +1,063,701 | +134,624 | +1,198,333
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 4,248,563 | 2,134,711 | 66.6%
+ [... lines omitted ...]
+ #0: ToddCoxeter: diff 0.1.107 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +10,380 | -822 | +77.0%
+ #0: ToddCoxeter: phase 0.1 = 53.936s | run 0 = 53.936s | all runs = 53.936s | elapsed = 53.936s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.109 | active | killed | defined
+ #1: ToddCoxeter: nodes | 1,771 | 108,469,896 | 108,471,667
+ #1: ToddCoxeter: diff 0.1.108 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +1,593 | +108,469,870 | +108,471,463
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 10,626 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.108 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +10,380 | -822 | +77.0%
+ #1: ToddCoxeter: phase 0.1 = 53.994s | run 0 = 53.994s | all runs = 53.994s | elapsed = 53.994s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 53.471s (98%)
+ #0: ToddCoxeter: phase 0.1 = 54.390s | run 0 = 54.390s | all runs = 54.390s | elapsed = 54.390s
+ The index of 2^6:3.S6 is 1771
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $1,771$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/2024.md b/docs/examples/atlas/mathieu/m24/2024.md
new file mode 100644
index 0000000..08f0d61
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/2024.md
@@ -0,0 +1,163 @@
+# $L_3(4):S_3$ as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max6W1).
+
+## The computation
+
+The recorded run took about 39.055 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong L_3(4):S_3,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 2,024.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# L3(4):S3, order 120960, expected index 2024.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max6W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(2024)
+for word in (
+ "ababaBababaBababaBababaBababaBababaB",
+ "baBabaBabaBabaBabaBabaBabaBabaBaBabaBabababaBabaBabaBabaBabaBabaBabaBabaBaba"
+ "BabaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=0.5))
+
+actual_index = tc.number_of_classes()
+print(f"The index of L3(4):S3 is {actual_index}")
+if actual_index != 2024:
+ raise RuntimeError(f"expected index 2024, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 215 | 44 | 259
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 313 | 977 | 24.3%
+ #0: ToddCoxeter: time | run 0 = 116µs | all runs = 116µs | elapsed = 183µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 1,048,576 | 128,319 | 1,176,895
+ #1: ToddCoxeter: diff 0.0.0 | +1,048,361 | +128,275 | +1,176,636
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 4,187,615 | 2,103,841 | 66.6%
+ [... lines omitted ...]
+ #0: ToddCoxeter: diff 0.1.77 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +11,831 | -977 | +75.7%
+ #0: ToddCoxeter: phase 0.1 = 38.589s | run 0 = 38.589s | all runs = 38.589s | elapsed = 38.590s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.79 | active | killed | defined
+ #1: ToddCoxeter: nodes | 2,024 | 78,084,413 | 78,086,437
+ #1: ToddCoxeter: diff 0.1.78 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +1,809 | +78,084,369 | +78,086,178
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 12,144 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.78 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +11,831 | -977 | +75.7%
+ #1: ToddCoxeter: phase 0.1 = 38.850s | run 0 = 38.850s | all runs = 38.850s | elapsed = 38.851s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 38.111s (98%)
+ #0: ToddCoxeter: phase 0.1 = 39.055s | run 0 = 39.055s | all runs = 39.055s | elapsed = 39.055s
+ The index of L3(4):S3 is 2024
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $2,024$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/24.md b/docs/examples/atlas/mathieu/m24/24.md
new file mode 100644
index 0000000..2659e85
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/24.md
@@ -0,0 +1,162 @@
+# M~23~ as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max1W1).
+
+## The computation
+
+The recorded run took about 1 minute 27 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong \mathrm{M}_{23},
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 24.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# M23, order 10200960, expected index 24.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max1W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(24)
+for word in (
+ "BababababaBabababaBabababaBababababab",
+ "babaBabaBabababaBabaBaBaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=1))
+
+actual_index = tc.number_of_classes()
+print(f"The index of M23 is {actual_index}")
+if actual_index != 24:
+ raise RuntimeError(f"expected index 24, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 191 | 16 | 207
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 231 | 915 | 20.2%
+ #0: ToddCoxeter: time | run 0 = 91µs | all runs = 91µs | elapsed = 159µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 2,097,152 | 256,655 | 2,353,807
+ #1: ToddCoxeter: diff 0.0.0 | +2,096,961 | +256,639 | +2,353,600
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 8,372,906 | 4,210,006 | 66.5%
+ [... lines omitted ...]
+ #0: ToddCoxeter: diff 0.1.86 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | -87 | -915 | +79.8%
+ #0: ToddCoxeter: phase 0.1 = 1min26s | run 0 = 1min26s | all runs = 1min26s | elapsed = 1min26s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.88 | active | killed | defined
+ #1: ToddCoxeter: nodes | 24 | 167,808,875 | 167,808,899
+ #1: ToddCoxeter: diff 0.1.87 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | -167 | +167,808,859 | +167,808,692
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 144 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.87 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | -87 | -915 | +79.8%
+ #1: ToddCoxeter: phase 0.1 = 1min26s | run 0 = 1min26s | all runs = 1min26s | elapsed = 1min26s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 1min25s (98%)
+ #0: ToddCoxeter: phase 0.1 = 1min27s | run 0 = 1min27s | all runs = 1min27s | elapsed = 1min27s
+ The index of M23 is 24
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $24$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/244823040.md b/docs/examples/atlas/mathieu/m24/244823040.md
new file mode 100644
index 0000000..6f46177
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/244823040.md
@@ -0,0 +1,145 @@
+# The trivial subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+## The computation
+
+The recorded run took about 1 hour 45 minutes 42 seconds. The animation shows
+its progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $H$ be the trivial subgroup. We verify that the
+[presentation of M~24~](index.md#presentation), including the additional
+relation described above, defines a group of order
+
+$$
+ [\mathrm{M}_{24}:H] = |\mathrm{M}_{24}| = 244,823,040.
+$$
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+It uses only the generators `a` and `b`; inverses are expressed using
+$a^{-1}=a$ and $b^{-1}=b^2$.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import (
+ Presentation,
+ ToddCoxeter,
+ congruence_kind,
+ presentation,
+)
+from libsemigroups_pybind11.words import parse_relations
+
+# The empty word and a^2 = b^3 = 1 allow this presentation to define a group.
+p = Presentation("ab")
+p.contains_empty_word(True)
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(abbab)^12"), "")
+presentation.add_rule(p, parse_relations("(abbabbabab)^5"), "")
+presentation.add_rule(p, parse_relations("(abababb)^3(ababbabb)^3"), "")
+presentation.add_rule(
+ p, parse_relations("(abbabbababbabab)^5"), ""
+) # redundant, but quite useful.
+presentation.add_rule(p, parse_relations("(ab(ababb)^3)^4"), "")
+
+if __name__ == "__main__":
+ # Enumerate the whole group, without imposing any subgroup generators.
+ tc = ToddCoxeter(congruence_kind.twosided, p)
+ tc.strategy(tc.options.strategy.felsch)
+ tc.def_version(tc.options.def_version.two)
+ tc.use_relations_in_extra(True)
+ tc.def_max(10000)
+ tc.lower_bound(244823040)
+ # Cap the reporting interval at one second.
+ tc.report_every(timedelta(seconds=1))
+
+ actual_order = tc.number_of_classes()
+ print(f"The size of the group defined by the presentation is {actual_order}")
+ if actual_order != 244823040:
+ raise RuntimeError(f"expected order 244823040, got {actual_order}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 2, |R| = 8, |u| + |v| ∈ [2, 75], ∑(|u| + |v|) = 349
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 286 | 21 | 307
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 298 | 274 | 52.1%
+ #0: ToddCoxeter: time | run 0 = 291µs | all runs = 291µs | elapsed = 357µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 1,760,116 | 47,675 | 1,807,814
+ #1: ToddCoxeter: diff 0.0.0 | +1,759,830 | +47,654 | +1,807,507
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 2,961,491 | 558,741 | 84.1%
+ [... lines omitted ...]
+ #1: ToddCoxeter: diff 0.1.6294 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +489,645,782 | -274 | +47.9%
+ #1: ToddCoxeter: phase 0.1 = 1h45min41s | run 0 = 1h45min41s | all runs = 1h45min41s | elapsed = 1h45min41s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.6296 | active | killed | defined
+ #1: ToddCoxeter: nodes | 244,823,040 | 313,710,991 | 558,534,031
+ #1: ToddCoxeter: diff 0.1.6295 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +244,822,754 | +313,710,970 | +558,533,724
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 489,646,080 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.6295 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +489,645,782 | -274 | +47.9%
+ #1: ToddCoxeter: phase 0.1 = 1h45min42s | run 0 = 1h45min42s | all runs = 1h45min42s | elapsed = 1h45min42s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 1h45min14s (100%)
+ #0: ToddCoxeter: phase 0.1 = 1h45min42s | run 0 = 1h45min42s | all runs = 1h45min42s | elapsed = 1h45min42s
+ The size of the group defined by the presentation is 244823040
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed order is the claimed order: $244,823,040$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
+
diff --git a/docs/examples/atlas/mathieu/m24/276.md b/docs/examples/atlas/mathieu/m24/276.md
new file mode 100644
index 0000000..b46162d
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/276.md
@@ -0,0 +1,162 @@
+# M~22~:2 as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max2W1).
+
+## The computation
+
+The recorded run took about 5.165 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong \mathrm{M}_{22}:2,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 276.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# M22.2, order 887040, expected index 276.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max2W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(276)
+for word in (
+ "a",
+ "baBaBabababaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=0.05))
+
+actual_index = tc.number_of_classes()
+print(f"The index of M22.2 is {actual_index}")
+if actual_index != 276:
+ raise RuntimeError(f"expected index 276, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 140 | 17 | 157
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 184 | 656 | 21.9%
+ #0: ToddCoxeter: time | run 0 = 93µs | all runs = 93µs | elapsed = 160µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 110,558 | 16,608 | 127,187
+ #1: ToddCoxeter: diff 0.0.0 | +110,418 | +16,591 | +127,030
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 442,070 | 221,278 | 66.6%
+ [... lines omitted ...]
+ #0: ToddCoxeter: diff 0.1.94 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +1,472 | -656 | +78.1%
+ #0: ToddCoxeter: phase 0.1 = 5.107s | run 0 = 5.107s | all runs = 5.107s | elapsed = 5.107s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.96 | active | killed | defined
+ #1: ToddCoxeter: nodes | 276 | 11,018,629 | 11,018,905
+ #1: ToddCoxeter: diff 0.1.95 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +136 | +11,018,612 | +11,018,748
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 1,656 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.95 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +1,472 | -656 | +78.1%
+ #1: ToddCoxeter: phase 0.1 = 5.121s | run 0 = 5.121s | all runs = 5.121s | elapsed = 5.121s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 5.048s (98%)
+ #0: ToddCoxeter: phase 0.1 = 5.165s | run 0 = 5.165s | all runs = 5.165s | elapsed = 5.165s
+ The index of M22.2 is 276
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $276$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/3795.md b/docs/examples/atlas/mathieu/m24/3795.md
new file mode 100644
index 0000000..cb825bf
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/3795.md
@@ -0,0 +1,163 @@
+# $2^6:(L_3(2) \times S_3)$ as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max7W1).
+
+## The computation
+
+The recorded run took about 6.844 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong 2^6:(L_3(2) \times S_3),
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 3,795.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# 2^6:(L3(2) x S3), order 64512, expected index 3795.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max7W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(3795)
+for word in (
+ "a",
+ "baBabaBabaBabaBabaBabaBabaBabaBabaBabaBabaBaBaBaBaBabababaBababaBabababababa"
+ "bababaBabaBabaBabaBabaBabaBabaBabaBabaBabaBabaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=0.05))
+
+actual_index = tc.number_of_classes()
+print(f"The index of 2^6:(L3(2) x S3) is {actual_index}")
+if actual_index != 3795:
+ raise RuntimeError(f"expected index 3795, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 204 | 63 | 267
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 333 | 891 | 27.2%
+ #0: ToddCoxeter: time | run 0 = 109µs | all runs = 109µs | elapsed = 184µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 116,376 | 14,752 | 131,146
+ #1: ToddCoxeter: diff 0.0.0 | +116,172 | +14,689 | +130,879
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 464,624 | 233,632 | 66.5%
+ [... lines omitted ...]
+ #0: ToddCoxeter: diff 0.1.125 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +22,437 | -891 | +72.8%
+ #0: ToddCoxeter: phase 0.1 = 6.788s | run 0 = 6.788s | all runs = 6.788s | elapsed = 6.788s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.127 | active | killed | defined
+ #1: ToddCoxeter: nodes | 3,795 | 14,242,002 | 14,245,797
+ #1: ToddCoxeter: diff 0.1.126 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +3,591 | +14,241,939 | +14,245,530
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 22,770 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.126 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +22,437 | -891 | +72.8%
+ #1: ToddCoxeter: phase 0.1 = 6.824s | run 0 = 6.824s | all runs = 6.824s | elapsed = 6.824s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 6.728s (98%)
+ #0: ToddCoxeter: phase 0.1 = 6.843s | run 0 = 6.844s | all runs = 6.844s | elapsed = 6.844s
+ The index of 2^6:(L3(2) x S3) is 3795
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $3,795$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/40320.md b/docs/examples/atlas/mathieu/m24/40320.md
new file mode 100644
index 0000000..c216d05
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/40320.md
@@ -0,0 +1,163 @@
+# $L_2(23)$ as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max8W1).
+
+## The computation
+
+The recorded run took about 1 minute 3 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong L_2(23),
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 40,320.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# L2(23), order 6072, expected index 40320.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max8W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(40320)
+for word in (
+ "a",
+ "baBabaBabaBabaBabaBabaBabaBabaBabaBabaBabaBaBaBaBaBaBababaBabaBababababababa"
+ "babaBabaBabaBabaBabaBabaBabaBabaBabaBabaBabaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=1))
+
+actual_index = tc.number_of_classes()
+print(f"The index of L2(23) is {actual_index}")
+if actual_index != 40320:
+ raise RuntimeError(f"expected index 40320, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 200 | 65 | 265
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 333 | 867 | 27.8%
+ #0: ToddCoxeter: time | run 0 = 123µs | all runs = 123µs | elapsed = 184µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 2,185,069 | 264,255 | 2,449,332
+ #1: ToddCoxeter: diff 0.0.0 | +2,184,869 | +264,190 | +2,449,067
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 8,724,695 | 4,385,719 | 66.5%
+ [... lines omitted ...]
+ #1: ToddCoxeter: diff 0.1.62 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +241,587 | -867 | +72.2%
+ #1: ToddCoxeter: phase 0.1 = 1min2s | run 0 = 1min2s | all runs = 1min2s | elapsed = 1min2s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.1.64 | active | killed | defined
+ #0: ToddCoxeter: nodes | 40,320 | 132,052,901 | 132,093,221
+ #0: ToddCoxeter: diff 0.1.63 | +0 | +0 | +0
+ #0: ToddCoxeter: diff 0.1.0 | +40,120 | +132,052,836 | +132,092,956
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 241,920 | 0 | 100.0%
+ #0: ToddCoxeter: diff 0.1.63 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +241,587 | -867 | +72.2%
+ #0: ToddCoxeter: phase 0.1 = 1min3s | run 0 = 1min3s | all runs = 1min3s | elapsed = 1min3s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 1min2s (99%)
+ #0: ToddCoxeter: phase 0.1 = 1min3s | run 0 = 1min3s | all runs = 1min3s | elapsed = 1min3s
+ The index of L2(23) is 40320
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $40,320$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/759.md b/docs/examples/atlas/mathieu/m24/759.md
new file mode 100644
index 0000000..1c31f2d
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/759.md
@@ -0,0 +1,163 @@
+# $2^4:A_8$ as a subgroup of Mathieu group M~24~
+
+
+
+The presentation comes from the
+[ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M),
+with the additional relation $[a,b^{-1}abab]^5=1$ enabled as in the
+[overview](index.md#presentation). The ATLAS source marks this relation as
+redundant. We do not verify that this relation is redundant here.
+
+The subgroup generators come from the corresponding
+[ATLAS program](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max3W1).
+
+## The computation
+
+The recorded run took about 1 minute 39 seconds. The animation shows its
+progress, with the number of active nodes and the proportion of defined edges.
+
+
+
+
+
+
+ Progress of the Todd–Coxeter computation.
+
+
+
+## Claim
+
+Let $u$ and $v$ be the two subgroup words listed in the code below. If
+
+$$
+ H = \langle u,v \rangle \cong 2^4:A_8,
+$$
+
+then
+
+$$
+ [\mathrm{M}_{24}:H] = 759.
+$$
+
+We verify the claimed index, but not the claimed isomorphism type. In the code,
+uppercase letters denote inverses: `A` represents $a^{-1}$ and `B` represents
+$b^{-1}$.
+
+## The code
+
+The following script includes the full presentation and can be run on its own.
+
+
+Code
+
+```python
+from datetime import timedelta
+
+from libsemigroups_pybind11 import Presentation, ToddCoxeter, congruence_kind, presentation
+from libsemigroups_pybind11.words import parse_relations
+
+# Enable the empty word and inverses so the presentation defines a group.
+p = Presentation("abAB")
+p.contains_empty_word(True)
+presentation.add_inverse_rules(p, "ABab")
+
+# Add the defining relations
+presentation.add_rule(p, parse_relations("a^2"), "")
+presentation.add_rule(p, parse_relations("b^3"), "")
+presentation.add_rule(p, parse_relations("(ab)^23"), "")
+presentation.add_rule(p, parse_relations("(ABab)^12"), "")
+# [a, bab] = a^-1 (bab)^-1 a (bab).
+presentation.add_rule(p, parse_relations("(ABABabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ababaB)^3(abaBaB)^3"), "")
+# [a, b^-1 a b a b]^5 = 1, marked redundant in the ATLAS source.
+presentation.add_rule(p, parse_relations("(ABABAbaBabab)^5"), "")
+presentation.add_rule(p, parse_relations("(ab(abaB)^3)^4"), "")
+
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.replace_word_with_new_generator(p, presentation.longest_subword_reducing_length(p))
+presentation.sort_each_rule(p)
+presentation.sort_rules(p)
+
+# 2^4:A8, order 322560, expected index 759.
+# Source: https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/words/M24G1-max3W1
+# ATLAS output words simplified using a^2 = b^3 = 1; B denotes b^-1.
+tc = ToddCoxeter(congruence_kind.onesided, p)
+# Felsch settings from the original m24.py enumeration.
+tc.strategy(tc.options.strategy.felsch)
+tc.def_version(tc.options.def_version.two)
+tc.def_policy(tc.options.def_policy.discard_all_if_no_space)
+tc.use_relations_in_extra(True)
+tc.def_max(10000)
+tc.lower_bound(759)
+for word in (
+ "a",
+ "baBabaBabaBabaBabaBabaBabaBabaBabaBaBaBaBaBaBaBaBaBababaBababaBabaBababababa"
+ "babababababababaBabaBabaBabaBabaBabaBabaBabaBabaB",
+):
+ tc.add_generating_pair(word, "")
+
+tc.report_every(timedelta(seconds=1))
+
+actual_index = tc.number_of_classes()
+print(f"The index of 2^4:A8 is {actual_index}")
+if actual_index != 759:
+ raise RuntimeError(f"expected index 759, got {actual_index}")
+```
+
+
+
+## The output
+
+??? info "Truncated output from the Python script"
+
+ ```
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 START (strategy() = felsch)
+ #0: ToddCoxeter: |A| = 6, |R| = 14, |u| + |v| ∈ [2, 45], ∑(|u| + |v|) = 178
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: FELSCH 0.0 START
+ #0: ToddCoxeter: FELSCH 0.0.0 | active | killed | defined
+ #0: ToddCoxeter: nodes | 206 | 63 | 269
+ #0: ToddCoxeter: | active | missing | % complete
+ #0: ToddCoxeter: edges | 337 | 899 | 27.3%
+ #0: ToddCoxeter: time | run 0 = 121µs | all runs = 121µs | elapsed = 186µs
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.0.1 | active | killed | defined
+ #1: ToddCoxeter: nodes | 2,059,370 | 250,013 | 2,309,403
+ #1: ToddCoxeter: diff 0.0.0 | +2,059,164 | +249,950 | +2,309,134
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 8,221,585 | 4,134,635 | 66.5%
+ [... lines omitted ...]
+ #0: ToddCoxeter: diff 0.1.98 | +0 | +0 | +0.0%
+ #0: ToddCoxeter: diff 0.1.0 | +4,217 | -899 | +72.7%
+ #0: ToddCoxeter: phase 0.1 = 1min38s | run 0 = 1min38s | all runs = 1min38s | elapsed = 1min38s
+ ++++++++++++++++++++++++++++++++
+ #1: ToddCoxeter: FELSCH 0.1.100 | active | killed | defined
+ #1: ToddCoxeter: nodes | 759 | 188,025,671 | 188,026,430
+ #1: ToddCoxeter: diff 0.1.99 | +0 | +0 | +0
+ #1: ToddCoxeter: diff 0.1.0 | +553 | +188,025,608 | +188,026,161
+ #1: ToddCoxeter: | active | missing | % complete
+ #1: ToddCoxeter: edges | 4,554 | 0 | 100.0%
+ #1: ToddCoxeter: diff 0.1.99 | +0 | +0 | +0.0%
+ #1: ToddCoxeter: diff 0.1.0 | +4,217 | -899 | +72.7%
+ #1: ToddCoxeter: phase 0.1 = 1min38s | run 0 = 1min38s | all runs = 1min38s | elapsed = 1min38s
+ ++++++++++++++++++++++++++++++++
+ #0: ToddCoxeter: RUN 0 STOP (finished)
+ #0: ToddCoxeter: run 0 | lookahead | lookbehind | hlt | felsch
+ #0: ToddCoxeter: num. phases | 0 | 0 | 0 | 1
+ #0: ToddCoxeter: time spent in phases | - (0%) | - (0%) | - (0%) | 1min37s (98%)
+ #0: ToddCoxeter: phase 0.1 = 1min39s | run 0 = 1min39s | all runs = 1min39s | elapsed = 1min39s
+ The index of 2^4:A8 is 759
+ ```
+
+:material-checkbox-marked-circle-outline:{ style="color: #00c853" }
+The computed index is the claimed index: $759$.
+
+!!! note
+
+ The output and timings may vary between machines, but the final result
+ should be the same.
diff --git a/docs/examples/atlas/mathieu/m24/index.md b/docs/examples/atlas/mathieu/m24/index.md
new file mode 100644
index 0000000..4fb0e56
--- /dev/null
+++ b/docs/examples/atlas/mathieu/m24/index.md
@@ -0,0 +1,52 @@
+# Mathieu group M~24~
+
+
+
+The presentation and maximal subgroup data come from the
+[ATLAS of Group Representations](https://brauer.maths.qmul.ac.uk/Atlas/v3/spor/M24/).
+The claimed order is $|\mathrm{M}_{24}| = 244,823,040$.
+
+## Presentation
+
+$$
+ \mathrm{M}_{24} = \langle a,b \mid a^2 = b^3 = (ab)^{23}
+ = [a,b]^{12} = [a,bab]^5
+ = (ababab^{-1})^3(abab^{-1}ab^{-1})^3
+ = (ab(abab^{-1})^3)^4 = 1 \rangle
+$$
+
+The scripts also include $[a,b^{-1}abab]^5=1$, which is marked as redundant
+in the [ATLAS Magma file](https://brauer.maths.qmul.ac.uk/Atlas/spor/M24/mag/M24G1-P1.M).
+We do not verify that this relation is redundant here.
+
+## Subgroup enumerations
+
+The supplied files record enumerations over the trivial subgroup, the cyclic
+subgroup generated by $ab$, of claimed order $23$, and all nine maximal subgroups.
+Each linked page includes self-contained code, recorded output, and a
+corresponding animation showing the number of nodes, and the percentage
+completeness, of the graph.
+
+