use std::collections::HashMap;
use crate::symbolic::core::Expr;
use crate::symbolic::group_theory::Group;
use crate::symbolic::group_theory::GroupElement;
#[must_use]
pub fn cyclic_group(n: usize) -> Group {
let elements: Vec<_> = (0..n)
.map(|i| {
if i == 0 {
GroupElement(Expr::Variable("e".to_string()))
} else {
GroupElement(Expr::new_pow(
Expr::Variable("g".to_string()),
Expr::Variable(i.to_string()),
))
}
})
.collect();
let mut multiplication_table = HashMap::new();
for i in 0..n {
for j in 0..n {
let result_idx = (i + j) % n;
multiplication_table.insert(
(elements[i].clone(), elements[j].clone()),
elements[result_idx].clone(),
);
}
}
Group::new(elements.clone(), multiplication_table, elements[0].clone())
}
#[allow(clippy::cast_possible_wrap)]
#[must_use]
pub fn dihedral_group(n: usize) -> Group {
let mut elements = Vec::with_capacity(2 * n);
let rotations: Vec<_> = (0..n)
.map(|i| GroupElement(Expr::Variable(format!("r{i}"))))
.collect();
let reflections: Vec<_> = (0..n)
.map(|i| GroupElement(Expr::Variable(format!("s{i}"))))
.collect();
elements.extend(rotations.clone());
elements.extend(reflections.clone());
let mut multiplication_table = HashMap::new();
for i in 0..n {
for j in 0..n {
let res_idx = (i + j) % n;
multiplication_table.insert(
(rotations[i].clone(), rotations[j].clone()),
rotations[res_idx].clone(),
);
let res_idx = (j as isize - i as isize).rem_euclid(n as isize) as usize;
multiplication_table.insert(
(rotations[i].clone(), reflections[j].clone()),
reflections[res_idx].clone(),
);
let res_idx = (i + j) % n;
multiplication_table.insert(
(reflections[i].clone(), rotations[j].clone()),
reflections[res_idx].clone(),
);
let res_idx = (j as isize - i as isize).rem_euclid(n as isize) as usize;
multiplication_table.insert(
(reflections[i].clone(), reflections[j].clone()),
rotations[res_idx].clone(),
);
}
}
Group::new(elements.clone(), multiplication_table, rotations[0].clone())
}
pub(crate) fn generate_permutations(n: usize) -> Vec<Vec<usize>> {
if n == 0 {
return vec![vec![]];
}
let mut result = Vec::new();
let mut p: Vec<usize> = (0..n).collect();
let mut c: Vec<usize> = vec![0; n];
result.push(p.clone());
let mut i = 1;
while i < n {
if c[i] < i {
if i % 2 == 0 {
p.swap(0, i);
} else {
p.swap(c[i], i);
}
result.push(p.clone());
c[i] += 1;
i = 1;
} else {
c[i] = 0;
i += 1;
}
}
result
}
pub(crate) fn compose_permutations(
p1: &[usize],
p2: &[usize],
) -> Vec<usize> {
p2.iter().map(|&x| p1[x]).collect()
}
pub fn symmetric_group(n: usize) -> Result<Group, String> {
let perms_as_indices = generate_permutations(n);
let elements: Vec<_> = perms_as_indices
.iter()
.map(|p| {
let expr_vec = p.iter().map(|&i| Expr::Variable(i.to_string())).collect();
GroupElement(Expr::Vector(expr_vec))
})
.collect();
let mut multiplication_table = HashMap::new();
for (i, p1_indices) in perms_as_indices.iter().enumerate() {
for (j, p2_indices) in perms_as_indices.iter().enumerate() {
let result_indices = compose_permutations(p1_indices, p2_indices);
let result_idx = match perms_as_indices.iter().position(|p| p == &result_indices) {
| Some(idx) => idx,
| None => {
return Err("Composed permutation not found in the group elements.".to_string());
},
};
multiplication_table.insert(
(elements[i].clone(), elements[j].clone()),
elements[result_idx].clone(),
);
}
}
let identity_element = match elements.iter().find(|el| {
if let GroupElement(Expr::Vector(v)) = el {
v.iter().enumerate().all(|(idx, val)| {
if let Expr::Variable(s) = val {
s.parse::<usize>().unwrap_or(n + 1) == idx
} else {
false
}
})
} else {
false
}
}) {
| Some(el) => el.clone(),
| None => {
return Err("Identity element not found in the symmetric group.".to_string());
},
};
Ok(Group::new(elements, multiplication_table, identity_element))
}
#[must_use]
pub fn klein_four_group() -> Group {
let e = GroupElement(Expr::Variable("e".to_string()));
let a = GroupElement(Expr::Variable("a".to_string()));
let b = GroupElement(Expr::Variable("b".to_string()));
let c = GroupElement(Expr::Variable("c".to_string()));
let elements = vec![e.clone(), a.clone(), b.clone(), c.clone()];
let mut table = HashMap::new();
for x in &elements {
table.insert((e.clone(), x.clone()), x.clone());
table.insert((x.clone(), e.clone()), x.clone());
}
table.insert((a.clone(), a.clone()), e.clone());
table.insert((b.clone(), b.clone()), e.clone());
table.insert((c.clone(), c.clone()), e.clone());
table.insert((a.clone(), b.clone()), c.clone());
table.insert((b.clone(), a.clone()), c.clone());
table.insert((b.clone(), c.clone()), a.clone());
table.insert((c.clone(), b.clone()), a.clone());
table.insert((c.clone(), a.clone()), b.clone());
table.insert((a, c), b);
Group::new(elements, table, e)
}