algebraeon_groups/composition_table/
iso_rep.rs

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