1use super::normal_subgroup::*;
2use super::partition::*;
3use super::subgroup::*;
4use super::subset::*;
5use rayon::prelude::*;
6use std::collections::BTreeSet;
7use std::collections::HashMap;
8use std::collections::HashSet;
9use std::fmt::Debug;
10use std::hash::Hash;
11
12pub struct Group {
13 n: usize,
14 ident: usize,
15 inv: Vec<usize>,
16 mul: Vec<Vec<usize>>,
17 conjugacy_classes: Option<PartitionState>,
18 is_abelian: Option<bool>,
19 is_simple: Option<bool>,
20}
21
22impl Group {
23 pub fn check_state(&self) -> Result<(), &'static str> {
24 if !(self.ident < self.n) {
26 return Err("bad ident elem");
27 }
28 if !(self.inv.len() == self.n) {
30 return Err("bad inv len");
31 }
32 for x in &self.inv {
33 if !(*x < self.n) {
34 return Err("bad inv elem");
35 }
36 }
37 if !(self.mul.len() == self.n) {
39 return Err("bad mul left len");
40 }
41 for m in &self.mul {
42 if !(m.len() == self.n) {
43 return Err("bad mul right len");
44 }
45 for x in m {
46 if !(*x < self.n) {
47 return Err("bad mul elem");
48 }
49 }
50 }
51 for x in 0..self.n {
53 if !(x == self.mul[x][self.ident] && x == self.mul[self.ident][x]) {
54 return Err("identity axiom failed");
55 }
56 }
57 for x in 0..self.n {
59 if !(self.ident == self.mul[self.inv[x]][x] && self.ident == self.mul[x][self.inv[x]]) {
60 return Err("inverse axiom failed");
61 }
62 }
63 for x in 0..self.n {
65 for y in 0..self.n {
66 for z in 0..self.n {
67 if !(self.mul[x][self.mul[y][z]] == self.mul[self.mul[x][y]][z]) {
68 return Err("assoc axiom failed");
69 }
70 }
71 }
72 }
73
74 match self.is_abelian {
76 Some(claimed_is_abelian) => {
77 if claimed_is_abelian != self.compute_is_abelian() {
78 return Err("incorrect is_abelian flag");
79 }
80 }
81 None => {}
82 };
83
84 match &self.conjugacy_classes {
86 Some(conj_state) => {
87 conj_state.check_state(self).unwrap();
88 }
89 None => {}
90 };
91
92 match &self.is_simple {
94 Some(is_simple) => {
95 if (self.normal_subgroups().len() == 2) != *is_simple {
96 return Err("is_simple flag is incorrect");
97 }
98 }
99 None => {}
100 }
101
102 Ok(())
103 }
104
105 pub fn new(
106 n: usize,
107 ident: usize,
108 inv: Vec<usize>,
109 mul: Vec<Vec<usize>>,
110 ) -> Result<Self, &'static str> {
111 let grp = Group {
112 n,
113 ident,
114 inv,
115 mul,
116 is_abelian: None,
117 conjugacy_classes: None,
118 is_simple: None,
119 };
120 match grp.check_state() {
121 Ok(()) => Ok(grp),
122 Err(msg) => Err(msg),
123 }
124 }
125
126 pub fn new_unchecked(
127 n: usize,
128 ident: usize,
129 inv: Vec<usize>,
130 mul: Vec<Vec<usize>>,
131 is_abelian: Option<bool>,
132 is_simple: Option<bool>,
133 ) -> Self {
134 Self {
135 n,
136 ident,
137 inv,
138 mul,
139 is_abelian,
140 conjugacy_classes: None,
141 is_simple,
142 }
143 }
144
145 pub fn mul(&self, x: usize, y: usize) -> usize {
146 self.mul[x][y]
147 }
148
149 pub fn inv(&self, x: usize) -> usize {
150 self.inv[x]
151 }
152
153 pub fn ident(&self) -> usize {
154 self.ident
155 }
156
157 pub fn from_raw_model<T: PartialEq + Eq + Hash + Clone + Debug>(
158 elems: Vec<T>,
159 ident: impl Fn() -> T,
160 inv: impl Fn(T) -> T,
161 mul: impl Fn(T, T) -> T,
162 ) -> Result<Self, &'static str> {
163 let grp = Self::from_raw_model_unchecked(elems, ident, inv, mul, None, None);
164 match grp.check_state() {
165 Ok(()) => Ok(grp),
166 Err(msg) => Err(msg),
167 }
168 }
169
170 pub fn from_raw_model_unchecked<T: PartialEq + Eq + Hash + Clone + Debug>(
171 elems: Vec<T>,
172 ident: impl Fn() -> T,
173 inv: impl Fn(T) -> T,
174 mul: impl Fn(T, T) -> T,
175 is_abelian: Option<bool>,
176 is_simple: Option<bool>,
177 ) -> Self {
178 let n = elems.len();
179 let mut idx_map: HashMap<T, usize> = HashMap::new();
180 for (i, x) in elems.iter().enumerate() {
181 idx_map.insert(x.clone(), i);
182 }
183
184 Self::new_unchecked(
185 n,
186 idx_map[&ident()],
187 {
188 let mut inv_lookup = vec![];
189 for x in &elems {
190 inv_lookup.push(idx_map[&inv(x.clone())]);
191 }
192 inv_lookup
193 },
194 {
195 let mut mul_lookup = vec![];
196 for (i, x) in elems.iter().enumerate() {
197 mul_lookup.push(vec![]);
198 for y in &elems {
199 mul_lookup[i].push(idx_map[&mul(x.clone(), y.clone())]);
200 }
201 }
202 mul_lookup
203 },
204 is_abelian,
205 is_simple,
206 )
207 }
208
209 pub fn size(&self) -> usize {
210 self.n
211 }
212
213 pub fn elems(&self) -> std::ops::Range<usize> {
214 0..self.n
215 }
216
217 pub fn mul_many(&self, elems: &Vec<usize>) -> usize {
218 if elems.len() == 0 {
219 return self.ident;
220 }
221 let mut prod = elems[0];
222 for i in 1..elems.len() {
223 prod = self.mul[prod][elems[i]];
224 }
225 prod
226 }
227
228 pub fn order(&self, x: usize) -> Result<usize, ()> {
229 if !(x < self.n) {
230 return Err(());
231 }
232 let mut y = x;
233 let mut ord = 1;
234 while y != self.ident {
235 y = self.mul[x][y];
236 ord += 1;
237 debug_assert!(ord <= self.n);
238 }
239 return Ok(ord);
240 }
241
242 fn compute_is_abelian(&self) -> bool {
243 for x in 0..self.n {
244 for y in 0..x {
245 if self.mul[x][y] != self.mul[y][x] {
246 return false;
247 }
248 }
249 }
250 true
251 }
252
253 pub fn is_abelian(&self) -> bool {
254 match &self.is_abelian {
255 Some(flag) => {
256 return *flag;
257 }
258 None => {
259 return self.compute_is_abelian();
260 }
261 }
262 }
263
264 fn compute_conjugacy_classes(&self) -> PartitionState {
265 let mut unclassified_elems = HashSet::<_>::from_iter(self.elems());
266 let mut classes = vec![];
267 let mut lookup = vec![0; self.n];
268 while unclassified_elems.len() > 0 {
269 let x = unclassified_elems.iter().next().unwrap();
270 let mut class = BTreeSet::new();
271 for g in self.elems() {
272 class.insert(self.mul[self.mul[g][*x]][self.inv[g]]);
273 }
274 for y in &class {
275 unclassified_elems.remove(y);
276 lookup[*y] = classes.len();
277 }
278 classes.push(class);
279 }
280 PartitionState::new_unchecked(classes, lookup)
281 }
282
283 pub fn cache_conjugacy_classes(&mut self) {
284 self.conjugacy_classes = Some(self.compute_conjugacy_classes())
285 }
286
287 pub fn conjugacy_class(&mut self, x: usize) -> Result<Subset, ()> {
288 if !(x < self.n) {
289 return Err(());
290 }
291 self.cache_conjugacy_classes();
292 match &self.conjugacy_classes {
293 Some(conj_state) => Ok(Subset::new_unchecked(self, conj_state.project(x).clone())),
294 None => panic!(),
295 }
296 }
297
298 pub fn conjugacy_classes(&self) -> Partition {
299 Partition {
300 group: self,
301 state: match &self.conjugacy_classes {
302 Some(state) => state.clone(),
303 None => self.compute_conjugacy_classes(),
304 },
305 }
306 }
307
308 fn subgroups_impl(&self, only_normal: bool) -> Vec<(Subgroup, Subset)> {
310 let mut distinguished_gens = vec![];
313 let mut subgroups: HashMap<Subgroup, Subset> = HashMap::new();
314 for x in self.elems() {
315 let singleton_x_subset = Subset::new_unchecked(&self, BTreeSet::from_iter(vec![x]));
316 let cyclic_sg = match only_normal {
317 false => singleton_x_subset.generated_subgroup().unwrap(),
318 true => singleton_x_subset.normal_closure().unwrap().to_subgroup(),
319 };
320 if !subgroups.contains_key(&cyclic_sg) {
321 subgroups.insert(cyclic_sg, singleton_x_subset.clone());
322 distinguished_gens.push(x);
323 }
324 }
325
326 let mut boundary = HashMap::new();
328 for (sg, gens) in subgroups.clone().into_iter() {
329 boundary.insert(sg, gens);
330 }
331 let mut next_boundary = HashMap::new();
332 while boundary.len() > 0 {
333 for (_sg, sg_gens) in &boundary {
334 for (new_sg, new_gens) in distinguished_gens
336 .par_iter()
337 .map(|dgen| {
338 let mut new_gens = sg_gens.clone();
339 new_gens.add_elem(*dgen).unwrap();
340 let new_sg = match only_normal {
341 false => new_gens.generated_subgroup().unwrap(),
342 true => new_gens.normal_closure().unwrap().to_subgroup(),
343 };
344 return (new_sg, new_gens);
345 })
346 .collect::<Vec<(Subgroup, Subset)>>()
347 {
348 if !subgroups.contains_key(&new_sg) {
350 next_boundary.insert(new_sg.clone(), new_gens.clone());
351 subgroups.insert(new_sg, new_gens);
352 }
353 }
354 }
355 boundary = next_boundary;
356 next_boundary = HashMap::new();
357 }
358 return subgroups
359 .into_iter()
360 .map(|(elems, gens)| (elems, gens))
361 .collect();
362 }
363
364 pub fn subgroups(&self) -> Vec<(Subgroup, Subset)> {
365 self.subgroups_impl(false)
366 }
367
368 pub fn normal_subgroups(&self) -> Vec<(NormalSubgroup, Subset)> {
369 self.subgroups_impl(true)
370 .into_iter()
371 .map(|(subgroup, gens)| (NormalSubgroup::new_unchecked(subgroup), gens))
372 .collect()
373 }
374}
375
376pub fn direct_product_structure(group_one: &Group, group_two: &Group) -> Group {
377 let m = group_one.n;
378 let n = group_two.n;
379
380 let single_to_pair = |i: usize| -> (usize, usize) { (i % m, i / m) };
381 let pair_to_single = |i: usize, j: usize| -> usize { i + j * m };
382
383 Group {
384 n: m * n,
385 ident: pair_to_single(group_one.ident, group_two.ident),
386 inv: (0..m * n)
387 .map(|x| {
388 let (i, j) = single_to_pair(x);
389 pair_to_single(group_one.inv[i], group_two.inv[j])
390 })
391 .collect(),
392 mul: (0..m * n)
393 .map(|x| {
394 (0..m * n)
395 .map(|y| {
396 let (ix, jx) = single_to_pair(x);
397 let (iy, jy) = single_to_pair(y);
398 pair_to_single(group_one.mul[ix][iy], group_two.mul[jx][jy])
399 })
400 .collect()
401 })
402 .collect(),
403 conjugacy_classes: None,
404 is_abelian: None,
405 is_simple: None,
406 }
407}
408
409pub mod examples {
410 use crate::free_group::todd_coxeter::FinitelyGeneratedGroupPresentation;
411
412 use super::*;
413
414 pub fn trivial_group_structure() -> Group {
415 cyclic_group_structure(1)
416 }
417
418 pub fn cyclic_group_structure(n: usize) -> Group {
419 Group::from_raw_model_unchecked(
420 (0..n).collect(),
421 || 0,
422 |x: usize| (n - x) % n,
423 |x: usize, y: usize| (x + y) % n,
424 Some(true),
425 None,
426 )
427 }
428
429 pub fn klein_four_structure() -> Group {
430 direct_product_structure(&cyclic_group_structure(2), &cyclic_group_structure(2))
431 }
432
433 pub fn dihedral_group_structure(n: usize) -> Group {
434 assert!(1 <= n);
437
438 let mut grp = FinitelyGeneratedGroupPresentation::new();
439 let a = grp.add_generator();
440 let b = grp.add_generator();
441 grp.add_relation(a.pow(2));
442 grp.add_relation(b.pow(2));
443 grp.add_relation((&a * &b).pow(n as isize));
444 let mut grp = grp.into_finite_group();
445 grp.is_abelian = Some(n <= 2);
446 grp.is_simple = Some(n <= 1);
447 grp
448 }
449
450 pub fn quaternion_group_structure() -> Group {
451 let mut grp = FinitelyGeneratedGroupPresentation::new();
454 let a = grp.add_generator();
455 let i = grp.add_generator();
456 let j = grp.add_generator();
457 let k = grp.add_generator();
458 grp.add_relation(a.pow(2));
459 grp.add_two_sided_relation(i.pow(2), a.clone());
460 grp.add_two_sided_relation(j.pow(2), a.clone());
461 grp.add_two_sided_relation(k.pow(2), a.clone());
462 grp.add_two_sided_relation(&i * &j * &k, a.clone());
463 let mut grp = grp.into_finite_group();
464 grp.is_abelian = Some(false);
465 grp.is_simple = Some(false);
466 grp
467 }
468
469 pub fn symmetric_group_structure(n: usize) -> Group {
470 super::super::super::permutation::Permutation::symmetric_composition_table(n).0
471 }
472
473 pub fn alternating_group_structure(n: usize) -> Group {
474 super::super::super::permutation::Permutation::alternating_composition_table(n).0
475 }
476}
477
478#[cfg(test)]
479mod group_tests {
480 use super::*;
481
482 #[test]
483 fn test_cyclic() {
484 for k in vec![1, 2, 3, 81, 91, 97, 100, 128] {
485 let mut grp = examples::cyclic_group_structure(k);
486 grp.cache_conjugacy_classes();
487 grp.check_state().unwrap();
488 assert_eq!(grp.elems().len(), k);
489 }
490 }
491
492 #[test]
493 fn test_dihedral() {
494 for k in vec![1, 2, 3, 16, 17, 18, 50] {
495 let mut grp = examples::dihedral_group_structure(k);
496 grp.cache_conjugacy_classes();
497 grp.check_state().unwrap();
498 assert_eq!(grp.elems().len(), 2 * k);
499 }
500 }
501
502 #[test]
503 fn test_quaternion() {
504 let mut grp = examples::quaternion_group_structure();
505 assert_eq!(grp.size(), 8);
506 grp.cache_conjugacy_classes();
507 grp.check_state().unwrap();
508 }
509
510 #[test]
511 fn test_direct_product() {
512 let grp1 = examples::dihedral_group_structure(5);
513 let grp2 = examples::dihedral_group_structure(4);
514 let grp3 = direct_product_structure(&grp1, &grp2);
515 grp3.check_state().unwrap();
516 }
517
518 #[test]
519 fn test_conjugacy_class_count() {
520 for (grp, num_ccls) in vec![
521 (examples::cyclic_group_structure(12), 12),
522 (examples::cyclic_group_structure(13), 13),
523 (examples::dihedral_group_structure(1), 2),
524 (examples::dihedral_group_structure(12), 9),
525 (examples::dihedral_group_structure(13), 8),
526 (examples::symmetric_group_structure(1), 1), (examples::symmetric_group_structure(2), 2), (examples::symmetric_group_structure(3), 3), (examples::symmetric_group_structure(4), 5), (examples::symmetric_group_structure(5), 7), ] {
533 assert_eq!(grp.conjugacy_classes().size(), num_ccls);
534 }
535 }
536}