use std::{mem, num, ptr};
use crate::{base::num::bounded, depot::array};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct Index<const N: usize>(num::NonZero<u8>);
impl<const N: usize> Index<N> {
pub const fn new(index: usize) -> Option<Self> {
const { assert!(N <= u8::MAX as usize) };
if index >= N {
return None;
}
Some(Self(num::NonZero::<u8>::MIN.saturating_add(index as u8)))
}
pub const fn mask(value: u8) -> Self {
const { assert!(N <= u8::MAX as usize && N.is_power_of_two()) };
Self(num::NonZero::<u8>::MIN.saturating_add(value & (N - 1) as u8))
}
pub fn from_bit(index: impl crate::depot::raw::BitIndex<N>) -> Self {
const { assert!(N <= u8::MAX as usize) };
let index = index.index();
Self(unsafe { num::NonZero::new_unchecked((index as u8).wrapping_add(1)) })
}
pub fn iter() -> impl DoubleEndedIterator<Item = Self> + ExactSizeIterator {
const { assert!(N <= u8::MAX as usize) };
(0..N).map(|index| {
Self(unsafe { num::NonZero::new_unchecked(index as u8 + 1) })
})
}
pub const fn at<const I: usize>() -> Self {
const { assert!(N <= u8::MAX as usize) };
const { assert!(I < N) };
Self(num::NonZero::<u8>::MIN.saturating_add(I as u8))
}
pub fn partition<T, const M: usize>(
self,
values: &[T; M],
test: impl FnMut(&T) -> bool,
) -> Self {
const { assert!(M < N && M + 1 == N && N <= u8::MAX as usize) };
let prefix = unsafe { values.get_unchecked(..self.index()) };
let index = prefix.partition_point(test);
Self(unsafe { num::NonZero::new_unchecked(index as u8 + 1) })
}
pub const fn widen<const M: usize>(self) -> Index<M> {
const { assert!(N <= M && M <= u8::MAX as usize) };
Index(self.0)
}
pub fn previous<const M: usize>(self) -> Option<Index<M>> {
const { assert!(M < N && M + 1 == N && N <= u8::MAX as usize) };
num::NonZero::new(self.0.get() - 1).map(Index)
}
pub fn next<const M: usize>(self) -> Index<M> {
const { assert!(N < M && M <= u8::MAX as usize) };
let slot =
array::Vacancy::<M>(num::NonZero::<u32>::MIN.saturating_add(self.0.get() as u32));
Index::from(slot)
}
pub fn insert<'a, T: Copy, const M: usize>(
self,
values: &[T; M],
value: T,
out: &'a mut mem::MaybeUninit<[T; N]>,
) -> &'a mut [T; N] {
const { assert!(M < N && M + 1 == N) };
let at = self.index();
unsafe {
let target = out.as_mut_ptr().cast::<T>();
ptr::copy_nonoverlapping(values.as_ptr(), target, at);
target.add(at).write(value);
ptr::copy_nonoverlapping(values.as_ptr().add(at), target.add(at + 1), M - at);
out.assume_init_mut()
}
}
pub const fn index(self) -> usize {
self.0.get() as usize - 1
}
pub const fn into_u32<const MAX: u32>(self) -> bounded::U32<0, MAX> {
const { assert!(N <= u8::MAX as usize && N <= MAX as usize + 1) };
unsafe { bounded::raw::u32(self.index() as u32) }
}
pub fn get<T>(self, values: &[T; N]) -> &T {
unsafe { values.get_unchecked(self.index()) }
}
pub fn get_mut<T>(self, values: &mut [T; N]) -> &mut T {
unsafe { values.get_unchecked_mut(self.index()) }
}
}
impl<const N: usize> From<array::Vacancy<N>> for Index<N> {
fn from(slot: array::Vacancy<N>) -> Self {
const { assert!(N <= u8::MAX as usize) };
Self(unsafe { num::NonZero::new_unchecked(slot.0.get() as u8) })
}
}
const _: () = {
assert!(mem::size_of::<Index<1>>() == 1);
assert!(mem::size_of::<Option<Index<1>>>() == 1);
};