algebraeon_groups/composition_table/
iso_rep.rs

1use super::group::{FiniteGroupMultiplicationTable, direct_product_structure, examples};
2use super::homomorphism::find_isomorphism;
3use std::collections::BTreeMap;
4use std::fmt::Debug;
5use std::hash::Hash;
6
7#[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Ord, Clone)]
8pub enum IsomorphismClass {
9    Trivial,
10    Cyclic(usize),
11    Dihedral(usize),
12    Quaternion,
13    Alternating(usize),
14    Symmetric(usize),
15    DirectProduct(Box<BTreeMap<IsomorphismClass, usize>>), //count how many of each isomorphic factor
16    Unknown(usize),
17}
18
19pub fn isomorphism_class(group: &FiniteGroupMultiplicationTable) -> IsomorphismClass {
20    IsomorphismClass::from_group(group)
21}
22
23impl IsomorphismClass {
24    fn check_state(&self) -> Result<(), &'static str> {
25        match self {
26            Self::Trivial | Self::Quaternion => {}
27            Self::Cyclic(n) => {
28                if *n == 0 {
29                    return Err("C0 is not a group");
30                }
31            }
32            Self::Dihedral(n) => {
33                if *n == 0 {
34                    return Err("D0 is not a group");
35                }
36            }
37            Self::Symmetric(_n) | Self::Alternating(_n) => {}
38            Self::DirectProduct(_factors) => {
39                todo!();
40            }
41            Self::Unknown(n) => {
42                if *n == 0 {
43                    return Err("Unknown group with 0 elements is not valid");
44                }
45            }
46        }
47
48        Ok(())
49    }
50
51    pub fn from_group(group: &FiniteGroupMultiplicationTable) -> Self {
52        let n = group.size();
53
54        //trivial
55        if n == 1 {
56            return Self::Trivial;
57        }
58
59        //cyclic
60        if let Some(_f) = find_isomorphism(group, &examples::cyclic_group_structure(n)) {
61            return Self::Cyclic(n);
62        }
63
64        //direct products
65        let mut nsgs = group.normal_subgroups();
66        nsgs.sort_by_key(|(nsg, _gens)| nsg.size());
67        for (nsg, _gens) in &nsgs {
68            nsg.check_state().unwrap();
69            if nsg.size() != 1 && nsg.size() != group.size() {
70                //non-trivial normal subgroup
71                let nsg_group = nsg.subgroup().to_group();
72                let quo_group = nsg.quotient_group();
73                let prod_group = direct_product_structure(&nsg_group, &quo_group);
74                nsg_group.check_state().unwrap();
75                quo_group.check_state().unwrap();
76                let isom_result = find_isomorphism(&prod_group, group);
77                if let Some(_f) = isom_result {
78                    return IsomorphismClass::from_group(&nsg_group)
79                        * IsomorphismClass::from_group(&quo_group);
80                }
81            }
82        }
83
84        debug_assert!(!group.is_abelian());
85
86        //quaternion
87        if let Some(_f) = find_isomorphism(group, &examples::quaternion_group_structure()) {
88            return Self::Quaternion;
89        }
90
91        //symmetric
92        let mut k = 0;
93        let mut k_fact = 1;
94        while k_fact < n {
95            k += 1;
96            k_fact *= k;
97        }
98        if n == k_fact
99            && let Some(_f) = find_isomorphism(group, &examples::symmetric_group_structure(k))
100        {
101            return Self::Symmetric(k);
102        }
103
104        //alternating
105        let mut k = 2;
106        let mut half_k_fact = 1;
107        while half_k_fact < n {
108            k += 1;
109            half_k_fact *= k;
110        }
111        if n == half_k_fact
112            && let Some(_f) = find_isomorphism(group, &examples::alternating_group_structure(k))
113        {
114            return Self::Alternating(k);
115        }
116
117        //dihedral
118        if n.is_multiple_of(2)
119            && let Some(_f) = find_isomorphism(group, &examples::dihedral_group_structure(n / 2))
120        {
121            return Self::Dihedral(n / 2);
122        }
123
124        IsomorphismClass::Unknown(n)
125    }
126
127    pub fn to_group(&self) -> Result<FiniteGroupMultiplicationTable, ()> {
128        match self {
129            Self::Trivial => Ok(examples::cyclic_group_structure(1)),
130            Self::Cyclic(n) => Ok(examples::cyclic_group_structure(*n)),
131            Self::Dihedral(n) => Ok(examples::dihedral_group_structure(*n)),
132            Self::Quaternion => Ok(examples::quaternion_group_structure()),
133            Self::Alternating(n) => Ok(examples::alternating_group_structure(*n)),
134            Self::Symmetric(n) => Ok(examples::symmetric_group_structure(*n)),
135            Self::DirectProduct(factors) => {
136                let mut factors_list = vec![];
137                for (factor, power) in factors.iter() {
138                    for _i in 0..*power {
139                        factors_list.push(factor);
140                    }
141                }
142                let mut prod_group = examples::trivial_group_structure();
143                for factor in factors_list {
144                    match factor.to_group() {
145                        Ok(factor_group) => {
146                            prod_group = direct_product_structure(&factor_group, &prod_group);
147                        }
148                        Err(()) => {
149                            return Err(());
150                        }
151                    }
152                }
153                Ok(prod_group)
154            }
155            Self::Unknown(_n) => Err(()),
156        }
157    }
158
159    #[allow(clippy::inherent_to_string)]
160    pub fn to_string(&self) -> String {
161        match self {
162            Self::Trivial => "T".to_owned(),
163            Self::Cyclic(n) => "C".to_owned() + &n.to_string(),
164            Self::Dihedral(n) => "D".to_owned() + &n.to_string(),
165            Self::Quaternion => "Q8".to_owned(),
166            Self::Alternating(n) => "A".to_owned() + &n.to_string(),
167            Self::Symmetric(n) => "S".to_owned() + &n.to_string(),
168            Self::DirectProduct(factors) => {
169                let mut ans = String::new();
170                let mut first = true;
171                for (factor, power) in factors.iter() {
172                    for _i in 0..*power {
173                        if !first {
174                            ans += "x";
175                        }
176                        ans += &factor.to_string();
177                        first = false;
178                    }
179                }
180                ans
181            }
182            Self::Unknown(n) => "Unknown".to_owned() + &n.to_string(),
183        }
184    }
185}
186
187impl std::ops::Mul<IsomorphismClass> for IsomorphismClass {
188    type Output = IsomorphismClass;
189
190    fn mul(self, other: IsomorphismClass) -> Self::Output {
191        if self == IsomorphismClass::Trivial {
192            return other;
193        }
194        if other == IsomorphismClass::Trivial {
195            return other;
196        }
197
198        let mut factors: BTreeMap<IsomorphismClass, usize> = BTreeMap::new();
199
200        match self {
201            IsomorphismClass::DirectProduct(fs) => {
202                for (f, p) in fs.iter() {
203                    *factors.entry(f.clone()).or_insert(0) += p;
204                }
205            }
206            _ => {
207                *factors.entry(self).or_insert(0) += 1;
208            }
209        }
210
211        match other {
212            IsomorphismClass::DirectProduct(fs) => {
213                for (f, p) in fs.iter() {
214                    *factors.entry(f.clone()).or_insert(0) += p;
215                }
216            }
217            _ => {
218                *factors.entry(other).or_insert(0) += 1;
219            }
220        }
221
222        IsomorphismClass::DirectProduct(Box::new(factors))
223    }
224}
225
226#[cfg(test)]
227mod isom_class_tests {
228    use super::*;
229
230    #[test]
231    fn test() {
232        let g = examples::klein_four_structure();
233        let i = IsomorphismClass::from_group(&g);
234        println!("{:?}", i.to_string());
235        assert_eq!(
236            i,
237            IsomorphismClass::DirectProduct(Box::new(BTreeMap::from([(
238                IsomorphismClass::Cyclic(2),
239                2
240            )])))
241        );
242    }
243}