use crate::error::InvalidArrFnRepr;
use crate::reprs::FunctionCompositionOrder;
use crate::sign::PermutationSign;
use crate::util::range_array;
use crate::Permutation;
impl<const N: usize> Permutation<N> {
pub fn identity() -> Self {
Self {
arr_repr: range_array(),
}
}
pub fn is_identity(&self) -> bool {
self.arr_repr.iter().enumerate().all(|(i, x)| *x == i)
}
pub fn compose(self, rhs: Self) -> Self {
let f1 = self.as_fn();
let f2 = rhs.as_fn();
Self::from_fn(move |i| f1(f2(i))).expect("composed permutation is valid")
}
pub fn inverse(self) -> Self {
let mut arr: [_; N] = core::array::from_fn(|i| (i, self.arr_repr[i]));
arr.sort_unstable_by_key(|tup| tup.1);
Self {
arr_repr: arr.map(|tup| tup.0),
}
}
}
impl<const N: usize> Permutation<N> {
pub fn rotate_left(n: usize) -> Self {
let mut arr_repr = range_array();
arr_repr.rotate_left(n);
Self { arr_repr }
}
pub fn rotate_right(n: usize) -> Self {
let mut arr_repr = range_array();
arr_repr.rotate_right(n);
Self { arr_repr }
}
pub fn min_subperm_len(&self) -> usize {
self.arr_repr
.iter()
.enumerate()
.rposition(|(i, x)| *x != i)
.map_or(0, |x| x + 1)
}
pub fn from_subperm<const M: usize>(sub: Permutation<M>) -> Self {
if M > N {
panic!("Permutation::from_subperm called with permutation of more objects");
} else {
let mut arr_repr = range_array();
arr_repr[..M].copy_from_slice(&sub.arr_repr[..]);
Self { arr_repr }
}
}
pub fn from_fn<F: FnMut(usize) -> usize>(f: F) -> Result<Self, InvalidArrFnRepr> {
let arr_repr = core::array::from_fn(f);
Self::from_array_repr(arr_repr)
}
pub fn as_fn(&self) -> impl Fn(usize) -> usize + '_ {
|i| self.arr_repr.get(i).copied().unwrap_or(i)
}
pub fn to_fn(self) -> impl Fn(usize) -> usize + 'static {
move |i| self.arr_repr.get(i).copied().unwrap_or(i)
}
pub fn apply<T>(&self, array: [T; N]) -> [T; N] {
let mut opts = array.map(Some);
core::array::from_fn(|i| {
let j = self.arr_repr[i];
opts[j].take().unwrap_or_else(|| unreachable!())
})
}
pub fn apply_in_place<T>(&self, slice: &mut [T]) {
self.to_swaps_repr()
.into_equivalent_with_order::<FunctionCompositionOrder>()
.apply_in_place(slice);
}
pub fn sign(&self) -> PermutationSign {
let num_swaps = self.to_swaps_repr().len();
match num_swaps % 2 {
0 => PermutationSign::Positive,
1 => PermutationSign::Negative,
_ => unreachable!(),
}
}
pub fn is_even(&self) -> bool {
self.sign().is_positive()
}
pub fn is_odd(&self) -> bool {
self.sign().is_negative()
}
}
#[cfg(test)]
mod tests {
use crate::sign::PermutationSign;
use crate::util::{range_array, with_random_perms};
use crate::Permutation;
#[test]
fn compose() {
with_random_perms::<100>(10, |p1| {
with_random_perms::<100>(10, |p2| {
let pc = p1.compose(p2);
let pfc = {
let pf1 = p1.as_fn();
let pf2 = p2.as_fn();
move |i| pf1(pf2(i))
};
let pcf = pc.as_fn();
for i in 0..100 {
assert_eq!(pfc(i), pcf(i));
}
let arr = range_array();
let tmp = p1.apply(arr);
let r1 = p2.apply(tmp);
let r2 = pc.apply(arr);
assert_eq!(r1, r2);
})
})
}
#[test]
fn inverse() {
with_random_perms::<100>(100, |p| {
let pi = p.inverse();
assert!(pi.compose(p).is_identity());
assert!(p.compose(pi).is_identity());
assert_eq!(pi.inverse(), p);
})
}
#[test]
fn apply() {
with_random_perms::<100>(100, |p| {
let arr = range_array();
assert_eq!(p.apply(arr), p.arr_repr);
let mut arr_inplace = arr;
p.apply_in_place(&mut arr_inplace);
assert_eq!(arr_inplace, p.arr_repr);
let arr2 = range_array::<200>();
let mut arr2_inplace = arr2;
p.apply_in_place(&mut arr2_inplace[..100]);
assert_eq!(arr2_inplace[..100], p.arr_repr[..]);
assert_eq!(arr2_inplace[100..], arr2[100..]);
})
}
#[test]
fn sign() {
assert_eq!(
Permutation::<10>::identity().sign(),
PermutationSign::Positive
);
let p1 = Permutation::from_array_repr([2, 1, 0]).unwrap();
assert_eq!(p1.sign(), PermutationSign::Negative);
let p2 = Permutation::from_array_repr([4, 3, 1, 2, 0]).unwrap();
assert_eq!(p2.sign(), PermutationSign::Negative);
}
}