#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![doc(html_root_url = "https://docs.rs/importunate/0.1.2")]
#![deny(missing_docs)]
#![deny(warnings, dead_code, unused_imports, unused_mut)]
#![warn(clippy::pedantic)]
#![allow(clippy::cast_possible_truncation)]
//! [![github]](https://github.com/wainwrightmark/importunate) [![crates-io]](https://crates.io/crates/importunate) [![docs-rs]](https://docs.rs/importunate)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
//!
//!
//!
//! Methods for choosing random elements from an iterator.
//!
//!
//!
//! ## Usage
//!
//! ```
//! use importunate::*;
//!
//! let arr1 = [2,0,1,3];
//! let mut arr2 = ["zero", "one", "two", "three"];
//! let perm = Permutation::::calculate_unchecked(arr1, |&x|x);
//! perm.apply(&mut arr2);
//!
//! assert_eq!(arr2,["two","zero", "one", "three"] );
//!
//!
//! ```
//!
//! ## Readme Docs
//!
//! You can find the crate's readme documentation on the
//! [crates.io] page, or alternatively in the [`README.md`] file on the GitHub project repo.
//!
//! [crates.io]: https://crates.io/crates/importunate
//! [`README.md`]: https://github.com/wainwrightmark/importunate
// TODO
// documentation
// optional rand
// errors
mod cyclic_generator;
mod decomposer;
/// Inner types that Permutations can use
pub mod inner;
mod swaps_iterator;
#[cfg(any(test, feature = "std"))]
/// Allows you to solve permutations - finding the shortest sequence of permutations that lead to it
pub mod solver;
use core::fmt::Display;
use core::hash::Hash;
use core::{cmp::Ordering, fmt::Debug};
use inner::Inner;
use num_integer::Integer;
#[cfg(any(test, feature = "serde"))]
use serde::{Deserialize, Serialize};
/// A permutation of a fixed length array
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[must_use]
#[cfg_attr(any(test, feature = "serde"), derive(Serialize), serde(transparent))]
pub struct Permutation(I);
#[cfg(any(test, feature = "serde"))]
impl<'de, I: Inner + Deserialize<'de>, const ELEMENTS: usize> Deserialize<'de>
for Permutation
{
fn deserialize(deserializer: D) -> Result
where
D: serde::Deserializer<'de>,
{
debug_assert!(ELEMENTS <= I::MAX_ELEMENTS);
let i = I::deserialize(deserializer)?;
if i > Self::get_last().0 {
return Err(serde::de::Error::custom("Permutation out of range"));
}
Ok(Self(i))
}
}
#[cfg(any(test, feature = "arbitrary"))]
use arbitrary::Arbitrary;
use swaps_iterator::SwapsIterator;
#[cfg(any(test, feature = "arbitrary"))]
impl<'a, I: Inner, const ELEMENTS: usize> Arbitrary<'a> for Permutation {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result {
debug_assert!(ELEMENTS <= I::MAX_ELEMENTS);
let bytes = u.bytes(Self::REQUIRED_BYTES)?;
let inner = I::from_le_byte_array(bytes);
let inner = inner.mod_floor(&Self::get_last().0);
Ok(Self(inner))
}
fn arbitrary_take_rest(mut u: arbitrary::Unstructured<'a>) -> arbitrary::Result {
debug_assert!(ELEMENTS <= I::MAX_ELEMENTS);
Self::arbitrary(&mut u)
}
fn size_hint(_depth: usize) -> (usize, Option) {
(Self::REQUIRED_BYTES, Some(Self::REQUIRED_BYTES))
}
}
impl Default for Permutation {
fn default() -> Self {
debug_assert!(ELEMENTS <= I::MAX_ELEMENTS);
Self(Default::default())
}
}
impl Display for Permutation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if ELEMENTS <= 10 {
write!(f, "{:01?}", self.get_array())
} else {
write!(f, "{:02?}", self.get_array())
}
}
}
impl Permutation {
/// The inner value of this permutation
pub fn inner(&self) -> I {
self.0
}
/// Create the permuation associated with a particular number, if it is in range.
pub fn try_from_inner(i: &I) -> Option {
if *i <= I::get_factorial(ELEMENTS) {
Some(Self(*i))
} else {
None
}
}
/// Apply this permutation to an array, reordering the first `ELEMENTS` elements
pub fn apply(&self, arr: &mut [T]) {
for (i, swap) in self.swaps().enumerate() {
arr.swap(i, usize::from(swap) + i);
}
}
/// Apply the inverse of this permutation to an array, reordering the first `ELEMENTS` elements
pub fn apply_inverse(&self, arr: &mut [T]) {
for (i, swap) in self.swaps_array().into_iter().enumerate().rev() {
arr.swap(i, usize::from(swap) + i);
}
}
/// The range of all possible permutations of this number of elements
#[must_use]
pub fn all() -> impl DoubleEndedIterator- {
let range = I::get_permutation_range(ELEMENTS);
range.map(|x| Self(x))
}
/// Is this the default permutation which does not reorder elements
pub fn is_default(&self) -> bool {
self.0.is_zero()
}
/// The swaps represented by this permutation
/// ```
/// let permutation = importunate::Permutation::::try_from_inner(&42).unwrap();
/// let mut arr = [0,1,2,3,4];
///
/// for (i, swap) in permutation.swaps().enumerate() {
/// arr.swap(i, usize::from(swap) + i);
/// }
///
/// assert_eq!(arr, [2, 1, 4, 3, 0])
/// ```
pub fn swaps(&self) -> SwapsIterator {
SwapsIterator::new(self)
}
/// The array of swaps represented by this permutation
/// ```
/// let permutation = importunate::Permutation::::try_from_inner(&42).unwrap();
/// let mut arr = [0,1,2,3,4];
///
/// for (i, swap) in permutation.swaps_array().into_iter().enumerate() {
/// arr.swap(i, usize::from(swap) + i);
/// }
///
/// assert_eq!(arr, [2, 1, 4, 3, 0])
/// ```
pub fn swaps_array(&self) -> [u8; ELEMENTS] {
let mut swaps = [0; ELEMENTS];
for (i, swap) in self.swaps().enumerate() {
swaps[i] = swap;
}
swaps
}
fn from_swaps(swaps: impl Iterator
- ) -> Self {
let mut inner: I = I::zero();
let mut mult: I = I::one();
for (i, swap) in swaps.enumerate() {
let r = mult * swap.into();
inner = inner + r;
mult = mult * (ELEMENTS - i).try_into().ok().unwrap();
}
Self(inner)
}
fn test_unique(iterator: impl Iterator
- ) -> bool {
let mut test = 0u64;
for x in iterator.take(ELEMENTS) {
test |= 1 << x;
}
test.count_ones() as usize == ELEMENTS
}
/// Calculate the permutation for any list, even one containing duplicates.
/// There is a performance penalty for using this - it will make n * n comparisons
pub fn calculate_incomplete(slice: &[T]) -> Self {
let mut arr = Self::DEFAULT_ARRAY;
for (index, element) in slice.iter().take(ELEMENTS).enumerate() {
let mut c = 0;
for (jindex, el) in slice.iter().take(ELEMENTS).enumerate() {
match element.cmp(el) {
Ordering::Greater => c += 1,
Ordering::Equal => {
if index > jindex {
c += 1;
}
}
Ordering::Less => {}
}
}
arr[index] = c;
}
Self::calculate_unchecked(arr, |&x| x)
}
/// *DO NOT USE THIS FUNCTION ON USER INPUT*
/// Calculate the permutation of an array.
/// # Panics
///
/// This will panic or loop forever if the array's elements contain duplicates or elements outsize `0..ELEMENTS`
pub fn calculate_unchecked u8>(mut arr: [T; ELEMENTS], mut f: F) -> Self {
debug_assert!(Self::test_unique(arr.iter().map(&mut f)));
let mut slot_multiplier: I = I::one();
let mut inner: I = I::zero();
for index in 0..(ELEMENTS as u8) {
let mut swap_element = f(&arr[usize::from(index)]);
'inner: loop {
match swap_element.checked_sub(index) {
None => unreachable!(), //Array is invalid
Some(0) => break 'inner,
Some(diff) => {
swap_element = f(&arr[usize::from(swap_element)]);
if swap_element == index {
let amount = f(&arr[usize::from(index)]) - index;
arr.swap(index.into(), (index + diff).into());
let change = slot_multiplier * (I::from(amount));
inner = inner + change;
break 'inner;
}
}
}
}
slot_multiplier = slot_multiplier * I::from((ELEMENTS as u8) - index);
}
Self(inner)
}
/// Calculate the permutation of an array
/// This will return `None` if the array's elements contain duplicates or elements outsize `0..ELEMENTS`
pub fn try_calculate u8>(arr: [T; ELEMENTS], mut f: F) -> Option {
if !Self::test_unique(arr.iter().map(&mut f)) {
return None;
}
Some(Self::calculate_unchecked(arr, f))
}
/// Get the element at the given index of the permutation
pub fn element_at_index T>(&self, new_index: u8, f: F) -> T {
debug_assert!((new_index as usize) < ELEMENTS);
let mut swaps = [0; ELEMENTS];
for (i, swap) in self.swaps().enumerate().take((new_index + 1) as usize) {
swaps[i] = swap;
}
let old_index = Self::element_at_index_from_swaps(&swaps, new_index);
f(old_index)
}
fn element_at_index_from_swaps(swaps: &[u8], index: u8) -> u8 {
let mut current = swaps[usize::from(index)] + index;
for j in (0..index).rev() {
if swaps[usize::from(j)] + j == current {
current = j;
}
}
current
}
/// Get the new index of the given element from the permutation
pub fn index_of u8>(&self, element: &T, f: F) -> u8 {
let old_index = f(element);
debug_assert!(usize::from(old_index) < ELEMENTS);
Self::index_of_element_from_swaps(self.swaps(), old_index)
}
fn index_of_element_from_swaps(swaps_iter: impl Iterator
- , mut index: u8) -> u8 {
for (j, diff) in swaps_iter.enumerate() {
let j = j as u8;
match j.cmp(&index) {
Ordering::Less => {
if j + diff == index {
return j;
}
}
Ordering::Equal => {
index += diff;
}
Ordering::Greater => {
break;
}
}
}
index
}
const DEFAULT_ARRAY: [u8; ELEMENTS] = {
let mut arr = [0u8; ELEMENTS];
let mut i: u8 = 0;
while i < (ELEMENTS as u8) {
arr[i as usize] = i;
i += 1;
}
arr
};
/// Get the complete array of this permutation's elements
pub fn get_array(&self) -> [u8; ELEMENTS] {
let mut arr = Self::DEFAULT_ARRAY;
self.apply(&mut arr);
arr
}
/// Write this permutation to a byte array
/// Panics if `BYTES` is too small for permutations of this many elements
/// See `REQUIRED_BYTES`
pub fn to_le_byte_array(&self) -> [u8; BYTES] {
debug_assert!(BYTES >= Self::REQUIRED_BYTES);
self.0.to_le_byte_array()
}
/// Read this permutation from a byte array
/// Panics if `BYTES` is too small for permutations of this many elements
/// See `REQUIRED_BYTES`
#[must_use]
pub fn try_from_le_byte_array(bytes: &[u8]) -> Option {
debug_assert!(bytes.len() >= Self::REQUIRED_BYTES);
let inner = I::from_le_byte_array(bytes);
if inner <= Self::get_last().0 {
Self(inner).into()
} else {
None
}
}
/// The number of bytes required to store a permutation of this many elements
pub const REQUIRED_BYTES: usize = {
match ELEMENTS {
0..=5 => 1,
6..=8 => 2,
9..=10 => 3,
11..=12 => 4,
13..=14 => 5,
15..=16 => 6,
17..=18 => 7,
19..=20 => 8,
21..=22 => 9,
23..=24 => 10,
25 => 11,
26..=27 => 12,
28..=29 => 13,
30 => 14,
31..=32 => 15,
33..=34 => 16,
_ => {
panic!("ELEMENTS is too big")
}
}
};
/// Invert this permutation
/// This produces the permutation that will reorder the array back to its original order
pub fn invert(&self) -> Self {
let mut swaps = self.swaps_array();
let mut is_different = false;
for i in (0..(ELEMENTS as u8)).rev() {
let swap: u8 = swaps[usize::from(i)];
if swap != 0 {
let i2 = i + swap;
for j in (0..i).rev() {
if swaps[usize::from(j)] + j == i {
swaps[usize::from(j)] = i2 - j;
is_different = true;
} else if swaps[usize::from(j)] + j == i2 {
swaps[usize::from(j)] = i - j;
is_different = true;
}
}
}
}
if !is_different {
//A great many permutations are their own inverses, so we can skip calculating if that is the case
return *self;
}
Self::from_swaps(swaps.into_iter())
}
/// Gets the permutation of this many elements with the highest inner value
pub fn get_last() -> Self {
let inner = I::get_factorial(ELEMENTS);
Self(inner - I::one())
}
/// Combine this permutation with another. Producing a permutation equivalent to performing this and then the other.
/// Note that this operation is neither commutative nor associative
pub fn combine(&self, rhs: &Self) -> Self {
let mut arr = self.get_array();
rhs.apply(&mut arr);
Self::calculate_unchecked(arr, |&x| x)
}
/// Gets the reverse permutation for this number of elements.
/// ```
/// use importunate::Permutation;
/// assert_eq!(Permutation::::reverse().get_array(), [3,2,1,0]);
/// assert_eq!(Permutation::::reverse().get_array(), [4,3,2,1,0]);
/// ```
pub fn reverse() -> Self {
let mut swaps = [0; ELEMENTS];
for (i, swap) in swaps.iter_mut().enumerate().take(ELEMENTS / 2) {
*swap = (ELEMENTS - ((2 * i) + 1)) as u8;
}
Self::from_swaps(swaps.into_iter())
}
/// Gets the rotate right permutation for this number of elements.
/// ```
/// use importunate::Permutation;
/// assert_eq!(Permutation::::rotate_right().get_array(), [3,0,1,2]);
/// ```
pub fn rotate_right() -> Self {
let mut swaps = [0; ELEMENTS];
for (i, swap) in swaps.iter_mut().enumerate().take(ELEMENTS) {
*swap = (ELEMENTS - (i + 1)) as u8;
}
Self::from_swaps(swaps.into_iter())
}
/// Gets the rotate left permutation for this number of elements.
/// ```
/// use importunate::Permutation;
/// assert_eq!(Permutation::::rotate_left().get_array(), [1,2,3,0]);
/// ```
pub fn rotate_left() -> Self {
let mut swaps = [1; ELEMENTS];
swaps[ELEMENTS - 1] = 0;
Self::from_swaps(swaps.into_iter())
}
/// Rotate right n times
/// ```
/// use importunate::Permutation;
/// assert_eq!(Permutation::::rotate_n(2).get_array(), [3,4,0,1,2]);
/// ```
pub fn rotate_n(n: usize) -> Self {
let rhs = Self::rotate_right();
let mut p = Self::default();
for _ in 0..n {
p = p.combine(&rhs);
}
p
}
/// Gets the permutation corresponding to interleaving elements.
/// This is the inverse of the pile shuffle
/// ```
/// use importunate::Permutation;
/// assert_eq!(Permutation::::interleave(3).get_array(), [0, 5, 10, 1, 6, 11, 2, 7, 12, 3, 8, 4, 9]);
/// ```
pub fn interleave(groups: u8) -> Self {
debug_assert!(groups >= 1);
let (div, rem) = (ELEMENTS as u8).div_rem(&groups);
let group_size = if rem == 0 { div } else { div + 1 };
let piles = group_size;
debug_assert!(piles >= 1);
let mut arr = [0u8; ELEMENTS];
let mut current = 0;
let mut pile_number = 0;
for element in &mut arr {
*element = current;
current += piles;
if current >= ELEMENTS as u8 {
pile_number += 1;
current = pile_number;
}
}
Self::calculate_unchecked(arr, |&x| x)
}
/// Generate the cycle with this permutation as the operator
pub fn generate_cycle(self) -> impl Iterator
- {
cyclic_generator::CyclicGenerator::from(self)
}
/// Decompose this permutation into disjoint cycles
pub fn decompose(self) -> impl Iterator
- {
decomposer::Decomposer::from(self)
}
// /// The slow (but oh so elegant) version of combine
// pub fn combine_2(&self, rhs: &Self) -> Self {
// let mut left_swaps = self.swaps_array();
// for (i, diff) in rhs.swaps().enumerate() {
// Self::swap_swaps(&mut left_swaps[i..], diff);
// }
// Self::from_swaps(left_swaps.into_iter())
// }
// fn swap_swaps(mut swaps_arr: &mut [u8], mut diff: u8) {
// while diff > 0 {
// let index_of_zero =
// Self::index_of_element_from_swaps(swaps_arr.iter().map(|x| *x), 0);
// let element_at_n = Self::element_at_index_from_swaps(swaps_arr, diff);
// swaps_arr[0] = element_at_n;
// if index_of_zero > 0 {
// let min = diff.min(index_of_zero) as usize;
// diff = diff.abs_diff(index_of_zero);
// swaps_arr = &mut swaps_arr[min..];
// } else {
// break;
// }
// }
// }
}
#[cfg(test)]
mod tests {
use crate::Permutation;
use arbitrary::*;
use arbtest::{arbitrary::{self, Unstructured}, arbtest};
use itertools::Itertools;
use ntest::test_case;
use std::collections::HashSet;
// pub fn test_arbitrary(){
// type Perm = Permutation;
// ::arbitrary(u)
// }
#[test]
pub fn test_decompose() {
type Perm = Permutation;
for perm in Perm::all() {
let decomposed = perm.decompose().collect_vec();
let product_left = decomposed.iter().fold(Perm::default(), |x, b| x.combine(b));
let product_right = decomposed
.iter()
.fold(Perm::default(), |x, b| b.combine(&x));
println!(
"{:?}: {:?}",
perm.get_array(),
decomposed
.iter()
.map(|x| format!("({:?})", x.get_array()))
.join("")
);
assert_eq!(perm, product_left);
assert_eq!(perm, product_right);
}
}
#[test]
pub fn test_display() {
let perm5 = Permutation::::rotate_right();
assert_eq!(perm5.to_string(), "[4, 0, 1, 2, 3]");
let perm10 = Permutation::::rotate_n(2);
assert_eq!(perm10.to_string(), "[8, 9, 0, 1, 2, 3, 4, 5, 6, 7]");
let perm11 = Permutation::::rotate_n(3);
assert_eq!(
perm11.to_string(),
"[08, 09, 10, 00, 01, 02, 03, 04, 05, 06, 07]"
);
}
#[test]
pub fn test_cycle() {
let perm = Permutation::::rotate_right();
let generator = perm.generate_cycle();
let vec = generator.map(|x| x.0).collect_vec();
assert_eq!(vec![119, 98, 112, 86, 0], vec);
}
#[test]
pub fn test_combine() {
let mut combinations = [Permutation::::default(); 576];
let mut i = 0;
for p_left in Permutation::::all() {
for p_right in Permutation::::all() {
let combined1 = p_left.combine(&p_right);
combinations[i] = combined1;
i += 1;
}
}
insta::assert_snapshot!(combinations
.map(|x| format!("{:?}", x.swaps_array()))
.join("\n"));
}
#[test]
pub fn test_calculate_incomplete() {
let actual = Permutation::::calculate_incomplete(&[1, 2, 2, 3, 2]);
let expected = Permutation::::calculate_unchecked([0, 1, 2, 4, 3], |x| *x);
assert_eq!(actual, expected);
}
#[test]
pub fn find_anagrams() {
let anagram = Permutation::::calculate_incomplete("anagram".as_bytes());
let admiral = Permutation::::calculate_incomplete("admiral".as_bytes());
assert_eq!(anagram, admiral.invert());
let anagrams = Permutation::::calculate_incomplete("anagrams".as_bytes());
assert_eq!(anagram, anagrams); //extra characters are ignored
let anagr = Permutation::::calculate_incomplete("anagr".as_bytes());
let anag = Permutation::::calculate_incomplete("anag".as_bytes());
assert_eq!(anagr, anag); //extra characters are ignored
}
#[test]
pub fn test_bytes() {
fn test_bytes1(u: &mut Unstructured<'_>) -> Result<(), arbitrary::Error> {
let perm = u.arbitrary::>()?;
let bytes: [u8; 3] = perm.to_le_byte_array();
let perm2 = Permutation::::try_from_le_byte_array(&bytes).unwrap();
assert_eq!(perm, perm2);
Ok(())
}
arbtest(|u|test_bytes1(u));
}
#[test]
pub fn test_inner() {
fn test_inner1(u: &mut Unstructured<'_>) -> Result<(), arbitrary::Error> {
let perm = u.arbitrary::>()?;
assert_eq!(perm.0, perm.inner());
assert_eq!(perm.0 == 0, perm.is_default());
assert_eq!(perm, Permutation::::try_from_inner(&perm.0).unwrap());
Ok(())
}
arbtest(|u|test_inner1(u));
}
#[test]
pub fn test_swaps() {
fn test_swaps1(u: &mut Unstructured<'_>) -> Result<(), arbitrary::Error> {
let perm = u.arbitrary::>()?;
let swaps = perm.swaps_array();
assert_eq!(
swaps.into_iter().collect_vec(),
perm.swaps().pad_using(4, |_| 0).collect_vec()
);
let ordering2 = Permutation::::from_swaps(swaps.into_iter());
assert_eq!(perm, ordering2);
Ok(())
}
arbtest(|u|test_swaps1(u));
}
#[test]
pub fn test_invert() {
for permutation in Permutation::::all() {
let arr = permutation.get_array();
let inverse = permutation.invert();
let mut arr2 = arr;
inverse.apply(&mut arr2);
let new_perm = Permutation::::try_calculate(arr2, |&x| x).unwrap();
assert_eq!(0, new_perm.0);
}
}
#[test]
pub fn test_apply_inverse() {
for permutation in Permutation::::all() {
let mut arr = permutation.get_array();
permutation.apply_inverse(&mut arr);
assert_eq!(arr, Permutation::::DEFAULT_ARRAY);
}
}
#[test]
pub fn test_element_at_index() {
type Perm = Permutation;
for perm in Perm::all() {
let mut arr = Perm::DEFAULT_ARRAY;
perm.apply(&mut arr);
let mut arr2 = Perm::DEFAULT_ARRAY;
for index in 0..4 {
let element = perm.element_at_index(index, |x| x);
arr2[index as usize] = element;
}
assert_eq!(arr, arr2);
}
}
#[test]
pub fn test_index_of() {
for perm in Permutation::::all() {
let mut arr = [0, 1, 2, 3];
perm.apply(&mut arr);
let mut arr1 = [0, 1, 2, 3];
let mut arr2 = [0, 1, 2, 3];
for index in 0..4u8 {
arr1[index as usize] = arr
.iter()
.enumerate()
.find(|(_, x)| x == &&index)
.unwrap()
.0 as u8;
arr2[index as usize] = perm.index_of(&index, |&x| x);
}
assert_eq!(arr1, arr2);
}
}
#[test]
pub fn all_possible_orderings_are_unique() {
let mut set: HashSet<[u8; 4]> = std::collections::HashSet::default();
for perm in Permutation::::all() {
let arr = perm.get_array();
let added = set.insert(arr);
assert!(added);
}
assert_eq!(set.len(), 24);
}
#[test]
pub fn test_calculate() {
for perm in Permutation::::all() {
let arr = perm.get_array();
let calculated = Permutation::::try_calculate(arr, |x| *x).unwrap();
assert_eq!(perm, calculated);
}
}
#[test]
pub fn test_calculate_with_duplicate() {
let r = Permutation::::try_calculate([0, 1, 2, 2], |x| *x);
assert_eq!(r, None)
}
#[test_case(0, "0123")]
#[test_case(1, "1023")]
#[test_case(2, "2103")]
#[test_case(3, "3120")]
#[test_case(4, "0213")]
#[test_case(5, "1203")]
pub fn should_order_correctly(o: u8, expected: &str) -> Result<(), anyhow::Error> {
let permutation: Permutation = Permutation(o);
let mut arr = [0, 1, 2, 3];
permutation.apply(&mut arr);
let actual = arr.into_iter().map(|x| x.to_string()).join("");
assert_eq!(expected, actual);
Ok(())
}
macro_rules! test_max {
($name: ident, $inner:ty, $max_elements:tt) => {
#[test]
pub fn $name() {
let permutation = Permutation::<$inner, $max_elements>::get_last();
//This orders arrays like [3,0,1,2] - effectively rotating them
let index_of_0 = permutation.index_of(&0, |&x| x);
assert_eq!(index_of_0, 1);
let element_at_0 = permutation.element_at_index(0, |x| x);
assert_eq!(element_at_0, $max_elements - 1);
}
};
}
test_max!(test_max_u8, u8, 5);
test_max!(test_max_u16, u16, 8);
test_max!(test_max_u32, u32, 12);
test_max!(test_max_u64, u64, 20);
test_max!(test_max_u128, u128, 34);
#[test]
fn test_ser_de() {
use serde_test::{assert_tokens, Token};
let perm = Permutation::::calculate_incomplete(&[2, 0, 1, 3]);
assert_tokens(&perm, &[Token::U8(6)]);
}
}