use super::GroupError;
use rug::Integer;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Permutation {
images: Vec<usize>,
}
impl Permutation {
pub fn identity(degree: usize) -> Permutation {
Permutation {
images: (0..degree).collect(),
}
}
pub fn from_images(images: Vec<usize>) -> Result<Permutation, GroupError> {
let degree = images.len();
let mut seen = vec![false; degree];
for (point, &image) in images.iter().enumerate() {
if image >= degree {
return Err(GroupError::NotAPermutation {
degree,
reason: format!("image of {point} is {image}, which is not below {degree}"),
});
}
if seen[image] {
return Err(GroupError::NotAPermutation {
degree,
reason: format!("{image} is the image of two different points"),
});
}
seen[image] = true;
}
Ok(Permutation { images })
}
pub fn from_cycles(degree: usize, cycles: &[Vec<usize>]) -> Result<Permutation, GroupError> {
let mut images: Vec<usize> = (0..degree).collect();
let mut used = vec![false; degree];
for cycle in cycles {
for &point in cycle {
if point >= degree {
return Err(GroupError::NotAPermutation {
degree,
reason: format!("cycle names point {point}, which is not below {degree}"),
});
}
if used[point] {
return Err(GroupError::NotAPermutation {
degree,
reason: format!("point {point} appears in more than one cycle position"),
});
}
used[point] = true;
}
if cycle.len() < 2 {
continue;
}
for window in cycle.windows(2) {
images[window[0]] = window[1];
}
images[cycle[cycle.len() - 1]] = cycle[0];
}
Permutation::from_images(images)
}
pub fn from_cycles_one_based(
degree: usize,
cycles: &[Vec<usize>],
) -> Result<Permutation, GroupError> {
let mut zero_based = Vec::with_capacity(cycles.len());
for cycle in cycles {
let mut c = Vec::with_capacity(cycle.len());
for &point in cycle {
if point == 0 || point > degree {
return Err(GroupError::NotAPermutation {
degree,
reason: format!(
"1-based cycle names point {point}, which is outside 1..={degree}"
),
});
}
c.push(point - 1);
}
zero_based.push(c);
}
Permutation::from_cycles(degree, &zero_based)
}
pub fn degree(&self) -> usize {
self.images.len()
}
pub fn images(&self) -> &[usize] {
&self.images
}
pub fn apply(&self, point: usize) -> Result<usize, GroupError> {
self.images
.get(point)
.copied()
.ok_or(GroupError::PointOutOfRange {
point,
degree: self.degree(),
})
}
#[inline]
pub(crate) fn img(&self, point: usize) -> usize {
self.images[point]
}
pub fn extend_degree(&self, degree: usize) -> Result<Permutation, GroupError> {
if degree < self.degree() {
return Err(GroupError::DegreeMismatch {
left: self.degree(),
right: degree,
});
}
let mut images = self.images.clone();
images.extend(self.degree()..degree);
Ok(Permutation { images })
}
pub fn compose(&self, other: &Permutation) -> Result<Permutation, GroupError> {
if self.degree() != other.degree() {
return Err(GroupError::DegreeMismatch {
left: self.degree(),
right: other.degree(),
});
}
Ok(self.compose_unchecked(other))
}
#[inline]
pub(crate) fn compose_unchecked(&self, other: &Permutation) -> Permutation {
Permutation {
images: self.images.iter().map(|&i| other.images[i]).collect(),
}
}
pub fn inverse(&self) -> Permutation {
let mut images = vec![0usize; self.degree()];
for (point, &image) in self.images.iter().enumerate() {
images[image] = point;
}
Permutation { images }
}
pub fn pow(&self, exponent: i64) -> Permutation {
let mut base = if exponent < 0 {
self.inverse()
} else {
self.clone()
};
let mut k = exponent.unsigned_abs();
let mut acc = Permutation::identity(self.degree());
while k > 0 {
if k & 1 == 1 {
acc = acc.compose_unchecked(&base);
}
base = base.compose_unchecked(&base);
k >>= 1;
}
acc
}
pub fn is_identity(&self) -> bool {
self.images.iter().enumerate().all(|(i, &j)| i == j)
}
pub fn support(&self) -> Vec<usize> {
self.images
.iter()
.enumerate()
.filter(|(i, &j)| *i != j)
.map(|(i, _)| i)
.collect()
}
pub fn first_moved_point(&self) -> Option<usize> {
self.images
.iter()
.enumerate()
.find(|(i, &j)| *i != j)
.map(|(i, _)| i)
}
pub fn cycles(&self) -> Vec<Vec<usize>> {
let mut seen = vec![false; self.degree()];
let mut out = Vec::new();
for start in 0..self.degree() {
if seen[start] {
continue;
}
let mut cycle = vec![start];
seen[start] = true;
let mut next = self.images[start];
while next != start {
seen[next] = true;
cycle.push(next);
next = self.images[next];
}
if cycle.len() > 1 {
out.push(cycle);
}
}
out
}
pub fn cycle_type(&self) -> Vec<usize> {
let mut seen = vec![false; self.degree()];
let mut lengths = Vec::new();
for start in 0..self.degree() {
if seen[start] {
continue;
}
let mut len = 1;
seen[start] = true;
let mut next = self.images[start];
while next != start {
seen[next] = true;
len += 1;
next = self.images[next];
}
lengths.push(len);
}
lengths.sort_unstable_by(|a, b| b.cmp(a));
lengths
}
pub fn order(&self) -> Integer {
let mut order = Integer::from(1);
for length in self.cycle_type() {
if length > 1 {
order.lcm_mut(&Integer::from(length));
}
}
order
}
pub fn sign(&self) -> i32 {
let mut parity = 0usize;
for length in self.cycle_type() {
parity += length - 1;
}
if parity % 2 == 0 {
1
} else {
-1
}
}
pub fn is_even(&self) -> bool {
self.sign() == 1
}
}
impl fmt::Display for Permutation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let cycles = self.cycles();
if cycles.is_empty() {
return write!(f, "()");
}
for cycle in &cycles {
write!(f, "(")?;
for (i, point) in cycle.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{point}")?;
}
write!(f, ")")?;
}
Ok(())
}
}