logicaffeine_proof/
two_group.rs1#[derive(Clone)]
18pub struct FiniteGroup {
19 pub mul: Vec<Vec<usize>>,
21 pub id: usize,
23}
24
25impl FiniteGroup {
26 pub fn order(&self) -> usize {
27 self.mul.len()
28 }
29
30 pub fn inverse(&self, a: usize) -> usize {
31 (0..self.order()).find(|&b| self.mul[a][b] == self.id).expect("a group element has an inverse")
32 }
33
34 pub fn trivial() -> FiniteGroup {
36 FiniteGroup { mul: vec![vec![0]], id: 0 }
37 }
38
39 pub fn cyclic(m: usize) -> FiniteGroup {
41 let mul = (0..m).map(|a| (0..m).map(|b| (a + b) % m).collect()).collect();
42 FiniteGroup { mul, id: 0 }
43 }
44
45 pub fn symmetric(n: usize) -> FiniteGroup {
48 let mut perms: Vec<Vec<usize>> = Vec::new();
51 let mut cur: Vec<usize> = (0..n).collect();
52 permute(&mut cur, 0, &mut perms);
53 let index: std::collections::HashMap<Vec<usize>, usize> =
54 perms.iter().cloned().enumerate().map(|(i, p)| (p, i)).collect();
55 let compose = |p: &[usize], q: &[usize]| -> Vec<usize> { (0..n).map(|i| p[q[i]]).collect() };
56 let mul: Vec<Vec<usize>> = perms
57 .iter()
58 .map(|p| perms.iter().map(|q| index[&compose(p, q)]).collect())
59 .collect();
60 let id = index[&(0..n).collect::<Vec<_>>()];
61 FiniteGroup { mul, id }
62 }
63}
64
65fn permute(arr: &mut Vec<usize>, k: usize, out: &mut Vec<Vec<usize>>) {
66 if k == arr.len() {
67 out.push(arr.clone());
68 return;
69 }
70 for i in k..arr.len() {
71 arr.swap(k, i);
72 permute(arr, k + 1, out);
73 arr.swap(k, i);
74 }
75}
76
77pub struct CrossedModule {
80 pub g: FiniteGroup,
81 pub h: FiniteGroup,
82 pub partial: Vec<usize>,
84}
85
86impl CrossedModule {
87 pub fn is_homomorphism(&self) -> bool {
89 (0..self.h.order()).all(|x| {
90 (0..self.h.order()).all(|y| {
91 self.partial[self.h.mul[x][y]] == self.g.mul[self.partial[x]][self.partial[y]]
92 })
93 })
94 }
95
96 pub fn pi_two(&self) -> Vec<usize> {
98 (0..self.h.order()).filter(|&x| self.partial[x] == self.g.id).collect()
99 }
100
101 pub fn image_size(&self) -> usize {
103 let mut img: Vec<usize> = (0..self.h.order()).map(|x| self.partial[x]).collect();
104 img.sort_unstable();
105 img.dedup();
106 img.len()
107 }
108
109 pub fn pi_one_order(&self) -> usize {
111 self.g.order() / self.image_size()
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn finite_groups_are_well_formed() {
121 for g in [FiniteGroup::trivial(), FiniteGroup::cyclic(5), FiniteGroup::symmetric(4)] {
122 let n = g.order();
123 for a in 0..n {
125 assert_eq!(g.mul[g.id][a], a);
126 assert_eq!(g.mul[a][g.id], a);
127 let _ = g.inverse(a);
128 }
129 }
130 assert_eq!(FiniteGroup::symmetric(4).order(), 24, "|S_4| = 24");
131 }
132
133 #[test]
134 fn discrete_symmetry_is_a_one_type_pi_two_vanishes() {
135 let g = FiniteGroup::symmetric(4);
139 let cm = CrossedModule { g, h: FiniteGroup::trivial(), partial: vec![0] };
140 assert!(cm.is_homomorphism(), "∂ : 1 → G is a homomorphism");
141 assert_eq!(cm.pi_two().len(), 1, "π₂ = ker(∂) = trivial ⇒ a 1-type (discrete symmetry)");
142 assert_eq!(cm.pi_one_order(), 24, "π₁ = G = S_4");
143 }
144
145 #[test]
146 fn a_genuine_2_group_populates_pi_two() {
147 for m in [2usize, 3, 5] {
151 let cm = CrossedModule { g: FiniteGroup::trivial(), h: FiniteGroup::cyclic(m), partial: vec![0; m] };
152 assert!(cm.is_homomorphism(), "∂ = 0 : Z/m → 1 is a homomorphism");
153 assert_eq!(cm.pi_two().len(), m, "π₂ = ker(∂) = Z/{m} — genuine higher homotopy");
154 assert_eq!(cm.pi_one_order(), 1, "π₁ = coker = trivial");
155 }
156 }
157
158 #[test]
159 fn an_isomorphism_crossed_module_is_contractible() {
160 let m = 6;
163 let cm = CrossedModule {
164 g: FiniteGroup::cyclic(m),
165 h: FiniteGroup::cyclic(m),
166 partial: (0..m).collect(), };
168 assert!(cm.is_homomorphism());
169 assert_eq!(cm.pi_two().len(), 1, "π₂ = ker(id) = trivial");
170 assert_eq!(cm.pi_one_order(), 1, "π₁ = coker(id) = trivial");
171 }
172}