algebraeon_groups/composition_table/
subset.rs

1use super::group::*;
2use super::normal_subgroup::*;
3use super::subgroup::*;
4use std::collections::HashSet;
5
6pub struct Subset<'a> {
7    group: &'a FiniteGroupMultiplicationTable,
8    elems: HashSet<usize>,
9}
10
11impl<'a> Subset<'a> {
12    pub fn check_state(&self) -> Result<(), &'static str> {
13        for x in &self.elems {
14            if !(*x < self.group.size()) {
15                return Err("invalid subset element");
16            }
17        }
18
19        Ok(())
20    }
21
22    pub fn new_unchecked(group: &'a FiniteGroupMultiplicationTable, elems: HashSet<usize>) -> Self {
23        Self { group, elems }
24    }
25
26    pub fn group(&self) -> &FiniteGroupMultiplicationTable {
27        &self.group
28    }
29
30    pub fn elems(&self) -> &HashSet<usize> {
31        &self.elems
32    }
33
34    pub fn size(&self) -> usize {
35        self.elems.len()
36    }
37
38    pub fn add_elem(&mut self, x: usize) -> Result<(), ()> {
39        if self.elems.contains(&x) {
40            return Ok(());
41        } else if !(x < self.group.size()) {
42            return Err(());
43        }
44        self.elems.insert(x);
45        Ok(())
46    }
47
48    pub fn left_mul(&self, g: usize) -> Result<Subset, ()> {
49        if !(g < self.group.size()) {
50            return Err(());
51        }
52        Ok(Subset {
53            group: self.group,
54            elems: self.elems.iter().map(|h| self.group.mul(g, *h)).collect(),
55        })
56    }
57
58    pub fn right_mul(&self, g: usize) -> Result<Subset, ()> {
59        if !(g < self.group.size()) {
60            return Err(());
61        }
62        Ok(Subset {
63            group: self.group,
64            elems: self.elems.iter().map(|h| self.group.mul(*h, g)).collect(),
65        })
66    }
67
68    pub fn is_subgroup(&self) -> bool {
69        for x in &self.elems {
70            for y in &self.elems {
71                if !self.elems.contains(&self.group.mul(*x, *y)) {
72                    return false;
73                }
74            }
75        }
76        true
77    }
78
79    pub fn to_subgroup(&self) -> Option<Subgroup> {
80        match self.is_subgroup() {
81            true => Some(Subgroup {
82                subset: self.clone(),
83            }),
84            false => None,
85        }
86    }
87
88    pub fn generated_subgroup(&self) -> Result<Subgroup<'a>, ()> {
89        for g in &self.elems {
90            if !(*g < self.group.size()) {
91                return Err(());
92            }
93        }
94
95        //generate subgroup by adding all generated elements
96        let mut sg = HashSet::new();
97        sg.insert(self.group.ident());
98
99        let mut boundary = vec![self.group.ident()];
100        let mut next_boundary = vec![];
101        let mut y;
102        while boundary.len() > 0 {
103            for x in &boundary {
104                for g in &self.elems {
105                    y = self.group.mul(*x, *g);
106                    if !sg.contains(&y) {
107                        sg.insert(y);
108                        next_boundary.push(y);
109                    }
110                }
111            }
112            boundary = next_boundary.clone();
113            next_boundary = vec![];
114        }
115        Ok(Subgroup {
116            subset: Subset {
117                group: self.group,
118                elems: sg,
119            },
120        })
121    }
122
123    pub fn normal_closure(&self) -> Result<NormalSubgroup<'a>, &'static str> {
124        for g in &self.elems {
125            if !(*g < self.group.size()) {
126                return Err("gen out of range");
127            }
128        }
129
130        let conj_info = self.group.conjugacy_classes().partition;
131
132        //generate subgroup by adding all generated elements
133        let mut sg = HashSet::new();
134        sg.insert(self.group.ident());
135
136        let mut boundary = vec![self.group.ident()];
137        let mut next_boundary = vec![];
138        while boundary.len() > 0 {
139            for x in &boundary {
140                for g in &self.elems {
141                    for y in conj_info.class_containing(self.group.mul(*x, *g)) {
142                        if !sg.contains(y) {
143                            sg.insert(*y);
144                            next_boundary.push(*y);
145                        }
146                    }
147                }
148            }
149            boundary = next_boundary.clone();
150            next_boundary = vec![];
151        }
152        Ok(NormalSubgroup::new_unchecked(Subgroup {
153            subset: Subset {
154                group: self.group,
155                elems: sg,
156            },
157        }))
158    }
159}
160
161impl<'a> IntoIterator for Subset<'a> {
162    type Item = usize;
163    type IntoIter = <HashSet<usize> as IntoIterator>::IntoIter;
164
165    fn into_iter(self) -> Self::IntoIter {
166        self.elems.into_iter()
167    }
168}
169
170impl<'a> PartialEq for Subset<'a> {
171    fn eq(&self, other: &Self) -> bool {
172        self.elems == other.elems
173    }
174}
175
176impl<'a> Eq for Subset<'a> {}
177
178// impl<'a> Hash for Subset<'a> {
179//     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
180//         self.elems.hash(state);
181//     }
182// }
183
184impl<'a> Clone for Subset<'a> {
185    fn clone(&self) -> Self {
186        Self {
187            group: self.group,
188            elems: self.elems.clone(),
189        }
190    }
191}
192
193#[cfg(test)]
194mod subset_tests {
195    use super::*;
196
197    #[test]
198    fn subset_left_mul() {
199        use crate::examples::symmetric::Permutation;
200
201        let (grp, _perms, elems) = Permutation::<4>::symmetric_composition_table();
202
203        let subset = Subset {
204            group: &grp,
205            elems: vec![
206                elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
207                elems[&Permutation::new([0, 1, 3, 2]).unwrap()],
208                elems[&Permutation::new([3, 2, 1, 0]).unwrap()],
209            ]
210            .into_iter()
211            .collect(),
212        };
213        let left_mul_subset = subset
214            .left_mul(elems[&Permutation::new([1, 2, 0, 3]).unwrap()])
215            .unwrap();
216        assert_eq!(
217            left_mul_subset.elems,
218            Subset {
219                group: &grp,
220                elems: vec![
221                    elems[&Permutation::new([2, 0, 3, 1]).unwrap()],
222                    elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
223                    elems[&Permutation::new([3, 0, 2, 1]).unwrap()]
224                ]
225                .into_iter()
226                .collect()
227            }
228            .elems
229        );
230    }
231
232    #[test]
233    fn subset_right_mul() {
234        use crate::examples::symmetric::Permutation;
235
236        let (grp, _perms, elems) = Permutation::<4>::symmetric_composition_table();
237
238        let subset = Subset {
239            group: &grp,
240            elems: vec![
241                elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
242                elems[&Permutation::new([0, 1, 3, 2]).unwrap()],
243                elems[&Permutation::new([3, 2, 1, 0]).unwrap()],
244            ]
245            .into_iter()
246            .collect(),
247        };
248        let left_mul_subset = subset
249            .right_mul(elems[&Permutation::new([1, 2, 0, 3]).unwrap()])
250            .unwrap();
251        assert_eq!(
252            left_mul_subset.elems,
253            Subset {
254                group: &grp,
255                elems: vec![
256                    elems[&Permutation::new([2, 3, 1, 0]).unwrap()],
257                    elems[&Permutation::new([1, 3, 0, 2]).unwrap()],
258                    elems[&Permutation::new([2, 1, 3, 0]).unwrap()]
259                ]
260                .into_iter()
261                .collect()
262            }
263            .elems
264        );
265    }
266}