use smallvec::SmallVec;
pub fn permutation_swaps(n: usize) -> PermutationSwaps {
let mut swaps = permutation_swaps_cyclic(n);
swaps.remaining_length = swaps.remaining_length.saturating_sub(1);
swaps
}
pub fn permutation_swaps_cyclic(n: usize) -> PermutationSwaps {
let elements = (0..n)
.map(|i| Element {
id: i as u8,
direction: if i == 0 {
Direction::None
} else {
Direction::Down
},
})
.collect();
let num_permutations: usize = (1..n + 1).product();
PermutationSwaps {
state: elements,
remaining_length: num_permutations,
}
}
#[derive(Clone)]
pub struct PermutationSwaps {
state: SmallVec<[Element; 16]>,
remaining_length: usize,
}
impl Iterator for PermutationSwaps {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {
let elements = &mut self.state;
let n = elements.len();
if let Some((idx, greatest_nonzero)) = elements
.iter()
.enumerate()
.filter(|(_pos, e)| e.direction != Direction::None)
.max_by_key(|(_pos, e)| e.id)
{
assert!(self.remaining_length > 0);
let output;
let chosen_id = greatest_nonzero.id;
let move_direction = greatest_nonzero.direction.to_isize();
let chosen_index = {
let other_idx = ((idx as isize) + move_direction) as usize; elements.swap(idx, other_idx);
output = (idx, other_idx);
other_idx
};
{
let reached_end = chosen_index == 0 || chosen_index == n - 1;
let next_index = ((chosen_index as isize) + move_direction) as usize;
let next_element_is_greater = || elements[next_index].id > chosen_id;
if reached_end || next_element_is_greater() {
elements[chosen_index].direction = Direction::None;
}
}
elements
.iter_mut()
.enumerate()
.filter(|(_i, e)| e.id > chosen_id)
.for_each(|(i, e)| {
let direction_towards_chosen =
((chosen_index as isize) - (i as isize)).signum();
e.direction = Direction::from_isize(direction_towards_chosen);
});
self.remaining_length -= 1;
Some(output)
} else {
assert!(self.remaining_length <= 1);
if self.remaining_length == 1 {
self.remaining_length -= 1;
Some((0, 1))
} else {
None
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining_length, Some(self.remaining_length))
}
}
#[derive(Clone, PartialEq, Eq)]
enum Direction {
None,
Down,
Up,
}
impl Direction {
fn to_isize(&self) -> isize {
match self {
Direction::None => 0,
Direction::Down => -1,
Direction::Up => 1,
}
}
fn from_isize(d: isize) -> Self {
match d {
-1 => Direction::Down,
0 => Direction::None,
1 => Direction::Up,
_ => panic!("invalid value for direction"),
}
}
}
#[derive(Clone)]
struct Element {
id: u8,
direction: Direction,
}
#[test]
fn test_shimon_even_algorithm() {
let mut arr = [1, 2, 3, 4, 5, 6];
let num_permutations: usize = (1..arr.len() + 1).product();
let mut all_elements = std::collections::HashSet::new();
all_elements.insert(arr.clone());
let swaps = permutation_swaps(arr.len());
for (i, j) in swaps {
arr.swap(i, j);
all_elements.insert(arr.clone());
}
assert_eq!(all_elements.len(), num_permutations);
}
#[test]
fn test_permutation_swaps_cyclic() {
let mut arr = [1, 2, 3, 4, 5];
let swaps = permutation_swaps_cyclic(arr.len());
for (i, j) in swaps {
arr.swap(i, j);
}
assert_eq!(arr, [1, 2, 3, 4, 5]);
}