use rand::thread_rng;
use rand::seq::SliceRandom;
#[derive(Clone, PartialEq, Debug)]
pub struct Permutation {
moves_to : Vec<usize>,
moves_from : Vec<usize>
}
impl Permutation {
pub fn identity(dimensions : usize) -> Self {
let mut p = Permutation {
moves_to : Vec::with_capacity(dimensions),
moves_from : Vec::with_capacity(dimensions),
};
for dimension in 0 .. dimensions {
p.moves_to[dimension] = dimension;
p.moves_from[dimension] = dimension;
}
p
}
pub fn new(moves : &[usize]) -> Self {
let mut p = Permutation {
moves_to : moves.to_vec(),
moves_from : vec![0; moves.len()]
};
if !p.is_valid() { panic!("Permutation has missing, duplicate and/or out of range coordinate indices"); }
p.invert();
p
}
pub fn random(dimensions : usize) -> Self {
let mut p = Self::identity(dimensions);
let mut rng = thread_rng();
p.moves_to.shuffle(&mut rng);
p.invert();
p
}
pub fn swap(&self, first_dimension : usize, second_dimension : usize) -> Self {
let mut p = self.clone();
let i_inverse = self.moves_to[first_dimension];
let j_inverse = self.moves_to[second_dimension];
p.moves_to.swap(first_dimension, second_dimension);
p.moves_from.swap(i_inverse, j_inverse);
p
}
pub fn dimensions(&self) -> usize {
self.moves_to.len()
}
fn is_valid(&self) -> bool {
let n : u64 = (self.dimensions() - 1) as u64;
let mut sum : u64 = 0;
let mut squares : u64 = 0;
let mut expected_xor : usize = 0;
let mut xor : usize = 0;
for (i, dim) in self.moves_to.iter().enumerate() {
if dim.clone() as u64 > n { return false; }
xor ^= dim;
expected_xor ^= i;
let dim64 : u64 = dim.clone() as u64;
sum += dim64;
squares += dim64 * dim64;
}
(xor == expected_xor)
&& (sum == n * (n + 1) / 2)
&& (squares == n * (n + 1) * (2*n + 1) / 6)
}
fn invert(&mut self) {
for i in 0 .. self.dimensions() {
self.moves_from[self.moves_to[i]] = i;
}
}
pub fn apply<T>(&self, unpermuted_data : &[T]) -> Vec<T>
where T : Clone
{
let mut permuted_values = Vec::with_capacity(self.dimensions());
for dim in 0 .. self.dimensions() {
permuted_values.push(unpermuted_data[self.moves_from[dim]].clone());
}
permuted_values
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use spectral::prelude::*;
use crate::permutation::Permutation;
#[test]
fn apply() {
let data : [i32; 5] = [2,4,6,8,10];
let moves : [usize; 5] = [2,0,1,4,3];
let permutation = Permutation::new(&moves);
let actual_permuted_data = permutation.apply(&data);
let expected_permuted_data = vec![4,6,2,10,8];
asserting("Correct permutation").that(&actual_permuted_data).is_equal_to(expected_permuted_data);
}
#[test]
#[should_panic]
fn is_valid_fails() {
let data : [i32; 5] = [2,4,6,8,10];
let moves : [usize; 5] = [2,0,1,4,999];
let permutation = Permutation::new(&moves);
let _actual_permuted_data = permutation.apply(&data);
}
#[test]
fn swap() {
let data : [i32; 5] = [2,4,6,8,10];
let permutation = Permutation::new(&[2,0,1,4,3]);
let swapped = permutation.swap(2, 3); let actual_permuted_data = swapped.apply(&data);
let expected_permuted_data = vec![4,8,2,10,6];
asserting("swapped permutation").that(&actual_permuted_data).is_equal_to(expected_permuted_data);
}
}