permoot 0.2.1

General-purpose no_std permutation library
Documentation
use crate::error::TooLongSliceForPermutation;
use crate::util::AlwaysEq;
use crate::Permutation;

#[derive(Ord, PartialOrd, Eq, PartialEq)]
enum SortHelper<'a, T> {
	ActualValue(AlwaysEq<usize>, &'a T),
	Lengthening(usize),
}

impl<'a, T> SortHelper<'a, T> {
	fn index(&self) -> usize {
		match *self {
			Self::ActualValue(i, _) => i.0,
			Self::Lengthening(i) => i,
		}
	}
}

/// An abstraction that allows implementing custom sorting algorithms
pub trait SortArray<const N: usize> {
	#[allow(missing_docs)] // the function name says it all
	fn sort<T: Ord>(arr: &mut [T; N]);
}

/// An abstraction that allows implementing custom sorting algorithms
pub trait SortSlice {
	#[allow(missing_docs)] // the function name says it all
	fn sort<T: Ord>(sl: &mut [T]);
}

/// Sort an array or slice using [slice::sort_unstable](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable)
pub struct CoreSortUnstable;

impl<const N: usize> SortArray<N> for CoreSortUnstable {
	fn sort<T: Ord>(arr: &mut [T; N]) {
		arr.sort_unstable();
	}
}

impl SortSlice for CoreSortUnstable {
	fn sort<T: Ord>(sl: &mut [T]) {
		sl.sort_unstable();
	}
}

#[cfg(feature = "std")]
/// Sort an array or slice using [slice::sort](https://doc.rust-lang.org/std/primitive.slice.html#method.sort)
pub struct StdSort;

#[cfg(feature = "std")]
impl<const N: usize> SortArray<N> for StdSort {
	fn sort<T: Ord>(arr: &mut [T; N]) {
		arr.sort();
	}
}

#[cfg(feature = "std")]
impl SortSlice for StdSort {
	fn sort<T: Ord>(sl: &mut [T]) {
		sl.sort();
	}
}

/// Construct a permutations that would sort a slice/array when [`apply`](crate::Permutation::apply)ed
impl<const N: usize> Permutation<N> {
	/// Construct a permutation that would sort `arr` according to the sort method given by `S`
	pub fn from_custom_sort<T: Ord, S: SortArray<N>>(arr: &[T; N]) -> Self {
		let mut to_sort: [_; N] =
			core::array::from_fn(|i| SortHelper::ActualValue(AlwaysEq(i), &arr[i]));

		S::sort(&mut to_sort);

		Self {
			arr_repr: to_sort.map(|s| s.index()),
		}
	}

	/// Construct a permutation that would sort `arr` according to the sort method given by [`CoreSortUnstable`]
	#[inline]
	pub fn from_sort_unstable<T: Ord>(arr: &[T; N]) -> Self {
		Self::from_custom_sort::<T, CoreSortUnstable>(arr)
	}

	/// Construct a permutation that would sort `arr` according to the sort method given by [`StdSort`]
	#[cfg(feature = "std")]
	#[inline]
	pub fn from_sort<T: Ord>(arr: &[T; N]) -> Self {
		Self::from_custom_sort::<T, StdSort>(arr)
	}

	/// Construct a permutation that would sort `sl` according to the sort method given by `S`
	pub fn from_custom_sort_slice<T: Ord, S: SortSlice>(
		sl: &[T],
	) -> Result<Self, TooLongSliceForPermutation> {
		if sl.len() > N {
			return Err(TooLongSliceForPermutation {
				length: sl.len(),
				max: N,
			});
		}

		let mut arr: [_; N] = core::array::from_fn(|i| match sl.get(i) {
			Some(x) => SortHelper::ActualValue(AlwaysEq(i), x),
			None => SortHelper::Lengthening(i),
		});

		S::sort(&mut arr[..]);

		Ok(Self {
			arr_repr: arr.map(|s| s.index()),
		})
	}

	/// Construct a permutation that would sort `sl` according to the sort method given by [`CoreSortUnstable`]
	#[inline]
	pub fn from_sort_unstable_slice<T: Ord>(sl: &[T]) -> Result<Self, TooLongSliceForPermutation> {
		Self::from_custom_sort_slice::<T, CoreSortUnstable>(sl)
	}

	/// Construct a permutation that would sort `sl` according to the sort method given by [`StdSort`]
	#[cfg(feature = "std")]
	#[inline]
	pub fn from_sort_slice<T: Ord>(sl: &[T]) -> Result<Self, TooLongSliceForPermutation> {
		Self::from_custom_sort_slice::<T, StdSort>(sl)
	}
}