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