algebraeon_groups/composition_table/
subset.rs1use super::group::FiniteGroupMultiplicationTable;
2use super::normal_subgroup::NormalSubgroup;
3use super::subgroup::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 if self.is_subgroup() {
81 Some(Subgroup {
82 subset: self.clone(),
83 })
84 } else {
85 None
86 }
87 }
88
89 pub fn generated_subgroup(&self) -> Result<Subgroup<'a>, ()> {
90 for g in &self.elems {
91 if *g >= self.group.size() {
92 return Err(());
93 }
94 }
95
96 let mut sg = HashSet::new();
98 sg.insert(self.group.ident());
99
100 let mut boundary = vec![self.group.ident()];
101 let mut next_boundary = vec![];
102 let mut y;
103 #[allow(clippy::assigning_clones)]
104 while !boundary.is_empty() {
105 for x in &boundary {
106 for g in &self.elems {
107 y = self.group.mul(*x, *g);
108 if !sg.contains(&y) {
109 sg.insert(y);
110 next_boundary.push(y);
111 }
112 }
113 }
114 boundary = next_boundary.clone();
115 next_boundary = vec![];
116 }
117 Ok(Subgroup {
118 subset: Subset {
119 group: self.group,
120 elems: sg,
121 },
122 })
123 }
124
125 pub fn normal_closure(&self) -> Result<NormalSubgroup<'a>, &'static str> {
126 for g in &self.elems {
127 if *g >= self.group.size() {
128 return Err("gen out of range");
129 }
130 }
131
132 let conj_info = self.group.conjugacy_classes().partition;
133
134 let mut sg = HashSet::new();
136 sg.insert(self.group.ident());
137
138 let mut boundary = vec![self.group.ident()];
139 let mut next_boundary = vec![];
140 #[allow(clippy::assigning_clones)]
141 while !boundary.is_empty() {
142 for x in &boundary {
143 for g in &self.elems {
144 for y in conj_info.class_containing(self.group.mul(*x, *g)) {
145 if !sg.contains(y) {
146 sg.insert(*y);
147 next_boundary.push(*y);
148 }
149 }
150 }
151 }
152 boundary = next_boundary.clone();
153 next_boundary = vec![];
154 }
155 Ok(NormalSubgroup::new_unchecked(Subgroup {
156 subset: Subset {
157 group: self.group,
158 elems: sg,
159 },
160 }))
161 }
162}
163
164impl<'a> IntoIterator for Subset<'a> {
165 type Item = usize;
166 type IntoIter = <HashSet<usize> as IntoIterator>::IntoIter;
167
168 fn into_iter(self) -> Self::IntoIter {
169 self.elems.into_iter()
170 }
171}
172
173impl<'a> PartialEq for Subset<'a> {
174 fn eq(&self, other: &Self) -> bool {
175 self.elems == other.elems
176 }
177}
178
179impl<'a> Eq for Subset<'a> {}
180
181impl<'a> Clone for Subset<'a> {
188 fn clone(&self) -> Self {
189 Self {
190 group: self.group,
191 elems: self.elems.clone(),
192 }
193 }
194}
195
196#[cfg(test)]
197mod subset_tests {
198 use super::*;
199
200 #[test]
201 fn subset_left_mul() {
202 use crate::examples::symmetric::Permutation;
203
204 let (grp, _perms, elems) = Permutation::<4>::symmetric_composition_table();
205
206 let subset = Subset {
207 group: &grp,
208 elems: vec![
209 elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
210 elems[&Permutation::new([0, 1, 3, 2]).unwrap()],
211 elems[&Permutation::new([3, 2, 1, 0]).unwrap()],
212 ]
213 .into_iter()
214 .collect(),
215 };
216 let left_mul_subset = subset
217 .left_mul(elems[&Permutation::new([1, 2, 0, 3]).unwrap()])
218 .unwrap();
219 assert_eq!(
220 left_mul_subset.elems,
221 Subset {
222 group: &grp,
223 elems: vec![
224 elems[&Permutation::new([2, 0, 3, 1]).unwrap()],
225 elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
226 elems[&Permutation::new([3, 0, 2, 1]).unwrap()]
227 ]
228 .into_iter()
229 .collect()
230 }
231 .elems
232 );
233 }
234
235 #[test]
236 fn subset_right_mul() {
237 use crate::examples::symmetric::Permutation;
238
239 let (grp, _perms, elems) = Permutation::<4>::symmetric_composition_table();
240
241 let subset = Subset {
242 group: &grp,
243 elems: vec![
244 elems[&Permutation::new([1, 2, 3, 0]).unwrap()],
245 elems[&Permutation::new([0, 1, 3, 2]).unwrap()],
246 elems[&Permutation::new([3, 2, 1, 0]).unwrap()],
247 ]
248 .into_iter()
249 .collect(),
250 };
251 let left_mul_subset = subset
252 .right_mul(elems[&Permutation::new([1, 2, 0, 3]).unwrap()])
253 .unwrap();
254 assert_eq!(
255 left_mul_subset.elems,
256 Subset {
257 group: &grp,
258 elems: vec![
259 elems[&Permutation::new([2, 3, 1, 0]).unwrap()],
260 elems[&Permutation::new([1, 3, 0, 2]).unwrap()],
261 elems[&Permutation::new([2, 1, 3, 0]).unwrap()]
262 ]
263 .into_iter()
264 .collect()
265 }
266 .elems
267 );
268 }
269}