1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
//! Computational group theory: **permutation groups**.
//!
//! A [`Permutation`] is a bijection of the point set `{0, 1, …, n−1}` stored as
//! an images array; a [`PermutationGroup`] is the subgroup of `S_n` generated by
//! a list of them. From a list of generators this module computes orbits with
//! Schreier vectors, a base and strong generating set by the Schreier–Sims
//! algorithm, and from that BSGS the exact group order as an arbitrary-precision
//! [`rug::Integer`], membership testing by sifting, uniform random elements, and
//! (below a hard cap) full element enumeration.
//!
//! # Two conventions to read before anything else
//!
//! **Points are 0-based.** `Permutation::from_images(vec![1, 0, 2])` is the
//! transposition swapping points `0` and `1`. Group-theory literature, GAP and
//! the ATLAS are all 1-based, so transcribing a generator by hand is the most
//! likely way to get a wrong answer here;
//! [`Permutation::from_cycles_one_based`] exists so that a cycle copied out of a
//! paper can be typed in unchanged.
//!
//! **Composition is left-to-right.** `p.compose(&q)` applies `p` *first* and `q`
//! *second*:
//!
//! ```text
//! (p ∘ q)(i) = q(p(i)) equivalently i^(p·q) = (i^p)^q
//! ```
//!
//! This is GAP's convention and the one that makes the exponential notation
//! `i^g` used throughout the stabilizer-chain literature read left to right. It
//! is the **opposite** of the `f ∘ g = f(g(x))` convention used for functions in
//! analysis, and the opposite of what `nalgebra`-style matrix code would do.
//! Every product in this module — Schreier generators `u_β · x · u_{β^x}^{-1}`,
//! the coset factorisation of an element, the transversal walk — is written in
//! that order, and [`Permutation::compose`] is the only place the order is
//! decided. If you mix conventions you get a group of the right *order* whose
//! elements are the inverses of the ones you wanted, which is exactly the kind
//! of bug the Mathieu-group order tests in this module exist to catch.
//!
//! # What is here
//!
//! * [`Permutation`] — images array, [`compose`](Permutation::compose),
//! [`inverse`](Permutation::inverse), [`identity`](Permutation::identity),
//! [`order`](Permutation::order) (arbitrary precision — Landau's function
//! outruns `u64` by degree ≈ 180), [`cycles`](Permutation::cycles),
//! [`cycle_type`](Permutation::cycle_type), [`sign`](Permutation::sign),
//! [`degree`](Permutation::degree).
//! * [`PermutationGroup`] — built from generators; [`orbit`](PermutationGroup::orbit)
//! and [`orbits`](PermutationGroup::orbits), [`stabilizer_chain`](PermutationGroup::stabilizer_chain),
//! [`order`](PermutationGroup::order), [`contains`](PermutationGroup::contains),
//! [`sift`](PermutationGroup::sift), [`random_element`](PermutationGroup::random_element),
//! [`elements`](PermutationGroup::elements).
//! * [`Orbit`] — the orbit of a point together with its **Schreier vector** and
//! the transversal derived from it.
//! * [`StabilizerChain`] — the base, the strong generators at each level, and
//! each level's basic orbit, all exposed rather than hidden behind `order()`.
//! * Standard constructors: [`symmetric`], [`alternating`], [`cyclic`],
//! [`dihedral`], [`trivial`].
//!
//! # What is *not* here, and why
//!
//! This pass is deliberately the permutation-group foundation and nothing else.
//! None of the following are implemented, and no entry point silently
//! approximates them:
//!
//! * **Finitely-presented groups and Todd–Coxeter coset enumeration.** A
//! presentation `⟨X | R⟩` is a different object with different algorithms
//! (and no termination guarantee); there is no `FpGroup` type here at all.
//! * **Ordinary character tables** are no longer absent, but they are not
//! *here*: conjugacy classes and the Dixon–Schneider table live in
//! [`crate::character`], which takes a [`PermutationGroup`] as its input and
//! caps `|G|` because it enumerates. Modular representation theory and Brauer
//! characters remain unimplemented.
//! * **Matrix groups over `GF(q)`** are no longer absent either, and likewise
//! not *here*: [`crate::matgroup`] builds a subgroup of `GL(d, q)` from
//! arbitrary generators over [`crate::ffield::FiniteField`], with its own
//! Schreier–Sims on the action on vectors. This module stays permutation-only,
//! and the two agree where both can answer — `MatGroup`'s tests check its order
//! against `permutation_action_on_vectors().order()` computed here.
//! * **Group cohomology** in degrees 0, 1 and 2 is implemented, in
//! [`crate::fpgroup`], for a finite group with coefficients in a finitely
//! generated abelian module — not here, and not in higher degrees. Group
//! extensions beyond what `H²` classifies, and the solvable/polycyclic
//! machinery (`PcGroup`), remain unimplemented.
//! * **Group-theoretic structure beyond the chain**: no Sylow subgroups, no
//! normal-closure/derived-series/composition-series computations, no subgroup
//! lattice, no automorphism groups, no backtrack search (set/partition
//! stabilizers, centralizers, intersections). `contains` and `order` come
//! from the chain; everything that needs backtracking is absent. Conjugacy
//! classes are in [`crate::character`] and are found by enumerating the
//! group, not by backtrack search, which is why they are capped there.
//! * **Randomised (Monte-Carlo) Schreier–Sims.** The implementation here is the
//! deterministic Schreier-generator algorithm: always correct, and
//! comfortably fast at the degrees this module admits, but not the
//! near-linear-time algorithm used for degrees in the millions.
//!
//! # Scope limits that are enforced rather than documented
//!
//! * The stabilizer chain stores an **explicit transversal permutation per
//! orbit point**, so its memory is `O(Σ_i |Δ_i| · n)` words — for `S_n` that
//! is about `4n³` bytes. Degrees above [`MAX_BSGS_DEGREE`] are therefore
//! refused ([`GroupError::DegreeTooLargeForBsgs`]) rather than allowed to
//! exhaust memory. Orbit computation itself has no such limit; only the chain
//! does. Lifting the cap means storing Schreier vectors alone and
//! reconstructing `u_β` on demand, which trades the memory for a factor of
//! `|Δ_i|` in time.
//! * Element enumeration refuses above [`DEFAULT_ELEMENT_CAP`] (and at all
//! above [`MAX_ELEMENT_CAP`]) with [`GroupError::EnumerationTooLarge`]. A
//! group of order `10^20` has a perfectly computable order and no listable
//! element set, and those two facts must not be confused.
pub use ;
pub use ;
pub use Permutation;
use fmt;
// ---------------------------------------------------------------------------
// Errors
// ---------------------------------------------------------------------------
/// Why a permutation-group operation refused.
///
/// Every variant is a refusal, never a degraded answer: this module has no
/// "best effort" path. See the module docs for the standing scope limits that
/// produce [`GroupError::DegreeTooLargeForBsgs`] and
/// [`GroupError::EnumerationTooLarge`].