use core::num::NonZeroUsize;
use arrayvec::ArrayVec;
use crate::error::{CycleElementLocation, InvalidArrFnRepr, InvalidCycleRepr, InvalidSwapRepr};
use crate::util::{copy_to_array, cycle_iter, range_array};
use crate::Permutation;
#[derive(Debug, Clone)]
pub struct Cycles<const N: usize> {
data: [usize; N],
cycle_starts: ArrayVec<usize, N>,
}
impl<const N: usize> Cycles<N> {
pub fn cycle_count(&self) -> usize {
self.cycle_starts.len()
}
pub fn get(&self, i: usize) -> Option<&[usize]> {
let i0 = *self.cycle_starts.get(i)?;
let i1 = i
.checked_add(1)
.and_then(|j| self.cycle_starts.get(j))
.copied();
match i1 {
None => self.data.get(i0..),
Some(i1) => self.data.get(i0..i1),
}
}
pub fn iter(&self) -> impl Iterator<Item = &[usize]> + '_ {
self.cycle_starts
.windows(2)
.map(|w| {
let [i0, i1] = copy_to_array(w).unwrap_or_else(|| unreachable!());
self.data.get(i0..i1).unwrap_or_else(|| unreachable!())
})
.chain(
self.cycle_starts
.last()
.copied()
.map(|i0| self.data.get(i0..).unwrap_or_else(|| unreachable!())),
)
}
fn convert_index(&self, i: usize) -> Option<CycleElementLocation> {
let (cycle_i, &cycle_start) = self
.cycle_starts
.iter()
.enumerate()
.rfind(|&(_, &s)| s < i)?;
let sub_i = i - cycle_start;
Some(CycleElementLocation {
cycle_index: cycle_i,
index: sub_i,
})
}
fn deconvert_index(&self, loc: CycleElementLocation) -> Option<usize> {
let res = *self.cycle_starts.get(loc.cycle_index)? + loc.index;
(res < N).then_some(res)
}
}
#[derive(Default)]
struct CyclesBuilder<const N: usize> {
data: ArrayVec<usize, N>,
cycle_starts: ArrayVec<usize, N>,
curr_cycle_outer_values: Option<(usize, usize)>,
}
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
struct PushResult {
pub cycle_closed: bool,
}
impl<const N: usize> CyclesBuilder<N> {
fn new() -> Self {
Self::default()
}
fn curr_cycle_starts_with(&self, i: usize) -> bool {
matches!(self.curr_cycle_outer_values, Some(t) if t.0 == i)
}
fn push(&mut self, i: usize) -> PushResult {
if self.curr_cycle_starts_with(i) {
self.curr_cycle_outer_values = None;
PushResult { cycle_closed: true }
} else if let Some((_, last)) = &mut self.curr_cycle_outer_values {
self.data.push(i);
*last = i;
PushResult {
cycle_closed: false,
}
} else {
self.cycle_starts.push(self.data.len());
self.data.push(i);
self.curr_cycle_outer_values = Some((i, i));
PushResult {
cycle_closed: false,
}
}
}
fn unwrap_finished(self) -> Cycles<N> {
debug_assert!(
self.curr_cycle_outer_values.is_none(),
"tried to unwrap unfinished `CyclesBuilder`"
);
Cycles {
data: self.data.into_inner().unwrap(),
cycle_starts: self.cycle_starts,
}
}
}
#[allow(missing_docs)] pub trait SwapsReprOrder: private::Sealed {
fn new() -> Self;
const IS_FCO: bool;
type Reverse: SwapsReprOrder;
}
mod private {
pub trait Sealed {}
impl Sealed for super::FunctionCompositionOrder {}
impl Sealed for super::SequentialApplicationOrder {}
}
pub struct FunctionCompositionOrder;
pub struct SequentialApplicationOrder;
impl SwapsReprOrder for FunctionCompositionOrder {
fn new() -> Self {
Self
}
const IS_FCO: bool = true;
type Reverse = SequentialApplicationOrder;
}
impl SwapsReprOrder for SequentialApplicationOrder {
fn new() -> Self {
Self
}
const IS_FCO: bool = false;
type Reverse = FunctionCompositionOrder;
}
pub struct Swaps<const N: usize, O: SwapsReprOrder> {
left: [usize; N],
right: [usize; N],
len: usize,
_order: O,
}
impl<const N: usize, O: SwapsReprOrder> Swaps<N, O> {
fn new() -> Self {
Self {
left: [0; N],
right: [0; N],
len: 0,
_order: O::new(),
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn get(&self, i: usize) -> Option<(&usize, &usize)> {
self.left
.get(i)
.and_then(|l| self.right.get(i).map(|r| (l, r)))
}
pub fn iter(&self) -> impl Iterator<Item = (&usize, &usize)> + '_ {
self.left.iter().zip(self.right.iter()).take(self.len)
}
pub fn interpret_as_order<O2: SwapsReprOrder>(self) -> Swaps<N, O2> {
Swaps {
left: self.left,
right: self.right,
len: self.len,
_order: O2::new(),
}
}
#[inline]
pub fn inverse(self) -> Swaps<N, <O as SwapsReprOrder>::Reverse> {
self.interpret_as_order()
}
pub fn into_equivalent_with_order<O2: SwapsReprOrder>(mut self) -> Swaps<N, O2> {
if O::IS_FCO != O2::IS_FCO {
self.left[..self.len].reverse();
self.right[..self.len].reverse();
}
self.interpret_as_order::<O2>()
}
}
impl<const N: usize> Swaps<N, FunctionCompositionOrder> {
pub fn apply_in_place<T>(&self, slice: &mut [T]) {
for (&i, &j) in self.iter() {
slice.swap(i, j);
}
}
}
impl<const N: usize> Swaps<N, SequentialApplicationOrder> {
fn swap(&mut self, i: usize, j: usize) {
if self.len >= N {
panic!("ran out of capacity while constructing `Swaps`");
}
self.left[self.len] = i;
self.right[self.len] = j;
self.len += 1;
}
}
impl<const N: usize> Permutation<N> {
pub fn from_array_repr(arr: [usize; N]) -> Result<Self, InvalidArrFnRepr> {
for (i, x) in arr.iter().enumerate() {
if *x >= N {
return Err(InvalidArrFnRepr::InvalidValue {
input: i,
value: *x,
max: N - 1,
});
}
if let Some(j) = arr[..i].iter().position(|y| y == x) {
return Err(InvalidArrFnRepr::RepeatedValue {
first_input: j,
second_input: i,
value: *x,
});
}
}
Ok(Self { arr_repr: arr })
}
pub fn from_one_based_array_repr(arr: [NonZeroUsize; N]) -> Result<Self, InvalidArrFnRepr> {
Self::from_array_repr(arr.map(|x| x.get() - 1))
}
pub fn to_array_repr(self) -> [usize; N] {
self.arr_repr
}
pub fn from_cycles_repr(cycles: Cycles<N>) -> Result<Self, InvalidCycleRepr> {
let mut arr_repr = [0; N];
for (i, c) in cycles.iter().enumerate() {
for (j, (curr, next)) in cycle_iter(c).enumerate() {
let loc = CycleElementLocation {
cycle_index: i,
index: j,
};
let data_i = cycles
.deconvert_index(loc)
.unwrap_or_else(|| unreachable!());
if let Some(prev_data_i) = cycles.data[..data_i].iter().position(|&x| x == curr) {
let prev_loc = cycles
.convert_index(prev_data_i)
.unwrap_or_else(|| unreachable!());
return Err(InvalidCycleRepr::RepeatedValue {
first_occurrence: prev_loc,
second_occurrence: loc,
value: curr,
});
}
match arr_repr.get_mut(curr) {
None => {
return Err(InvalidCycleRepr::InvalidValue {
location: loc,
value: curr,
max: N - 1,
});
}
Some(x) => *x = next,
}
}
}
Ok(Self { arr_repr })
}
pub fn to_cycles_repr(self) -> Cycles<N> {
let mut builder = CyclesBuilder::new();
#[derive(Copy, Clone, Eq, PartialEq)]
enum ToVisit {
NotVisited,
Visited,
}
let mut visited = [ToVisit::NotVisited; N];
let mut curr = 0;
loop {
visited[curr] = ToVisit::Visited;
if builder.push(curr).cycle_closed {
match visited.iter().position(|&v| v == ToVisit::NotVisited) {
None => {
break;
}
Some(i) => curr = i,
}
} else {
curr = self.arr_repr[curr]
}
}
builder.unwrap_finished()
}
pub fn from_swaps_repr<const M: usize, O: SwapsReprOrder>(
swaps: Swaps<M, O>,
) -> Result<Self, InvalidSwapRepr> {
let mut arr_repr = range_array();
for (i, (&a, &b)) in swaps
.into_equivalent_with_order::<FunctionCompositionOrder>()
.iter()
.enumerate()
{
if a >= N {
return Err(InvalidSwapRepr::InvalidLeftValue {
index: i,
value: a,
max: N - 1,
});
}
if b >= N {
return Err(InvalidSwapRepr::InvalidRightValue {
index: i,
value: b,
max: N - 1,
});
}
arr_repr.swap(a, b);
}
Ok(Self { arr_repr })
}
pub fn to_swaps_repr(self) -> Swaps<N, SequentialApplicationOrder> {
let mut swaps = Swaps::<N, SequentialApplicationOrder>::new();
let mut remaining = ArrayVec::from(self.arr_repr);
while let Some(i) = remaining.iter().position(|x| *x == remaining.len() - 1) {
if i == remaining.len() - 1 {
remaining.pop();
continue;
}
swaps.swap(i, remaining.len() - 1);
remaining.swap_remove(i);
}
swaps
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::with_random_perms;
use crate::Permutation;
#[test]
fn roundtrip_cycle() {
with_random_perms::<100>(100, |p| {
assert_eq!(Permutation::from_cycles_repr(p.to_cycles_repr()), Ok(p));
})
}
#[test]
fn roundtrip_swap() {
with_random_perms::<100>(100, |p| {
assert_eq!(Permutation::from_swaps_repr(p.to_swaps_repr()), Ok(p));
})
}
#[test]
fn reverse_order_is_inerse() {
with_random_perms::<100>(100, |p| {
let sw: Swaps<100, SequentialApplicationOrder> = p.to_swaps_repr();
let opp =
sw.interpret_as_order::<<SequentialApplicationOrder as SwapsReprOrder>::Reverse>();
let opp_p = Permutation::from_swaps_repr(opp).unwrap();
assert_eq!(opp_p, p.inverse());
})
}
}