use core::mem::{align_of, size_of, MaybeUninit};
use core::ptr::write_bytes;
use crate::algorithm;
use crate::bits_enum::BitsEnum;
use crate::common::{Common, PathsStatus, Splittability};
use crate::macros::{common_strct, for_current_bitness};
use crate::storage::{ArrayStorage, Storage};
use crate::tag_type::TagType;
use crate::{ChunkMut, DisjointSetRoView, Root, UnionResult};
#[derive(Clone, Copy)]
enum Impossible {}
macro_rules! implement_inconstructible_storage {
($tag:ty) => {
impl Storage<$tag> for Impossible {
#[inline]
fn lower_bound(&self) -> usize {
unreachable!("Impossible cannot be constructed!")
}
#[inline]
fn upper_bound(&self) -> usize {
unreachable!("Impossible cannot be constructed!")
}
#[inline]
fn roots_and_ranks(&mut self) -> (&mut [$tag], &mut [$tag]) {
unreachable!("Impossible cannot be constructed!")
}
#[inline]
fn roots_readonly(&self) -> &[$tag] {
unreachable!("Impossible cannot be constructed!")
}
}
};
}
implement_inconstructible_storage!(u8);
implement_inconstructible_storage!(u16);
implement_inconstructible_storage!(u32);
implement_inconstructible_storage!(u64);
trait CompileTest {
const COMPILE_TEST: ();
}
const fn test_struct_size<T, Stor>() {
let extra_place_for_flags = if align_of::<Stor>() > 1 {
align_of::<Stor>()
} else {
2
};
assert!(
size_of::<T>() ==
size_of::<Stor>() + extra_place_for_flags
);
}
impl<T: TagType, const SIZE: usize> CompileTest for [T; SIZE] {
#[allow(clippy::cast_possible_truncation)]
const COMPILE_TEST: () = {
let max_val = match T::BIT_SIZE {
8 => u8::MAX as usize,
16 => u16::MAX as usize,
32 => u32::MAX as usize,
64 => u64::MAX as usize,
_ => unreachable!(),
};
assert!(
SIZE <= max_val,
"SIZE must not be bigger than tag type max."
);
test_struct_size::<DisjointSetArrayU8<128>, ArrayStorage<u8, 128>>();
test_struct_size::<DisjointSetArrayU16<128>, ArrayStorage<u16, 128>>();
test_struct_size::<DisjointSetArrayU32<128>, ArrayStorage<u32, 128>>();
test_struct_size::<DisjointSetArrayU64<128>, ArrayStorage<u64, 128>>();
};
}
macro_rules! impl_array_djs {
($name:ident,$ro_type:ident,
$common_decl:ty,$storage:ty,
$tag:ty,$enm:path
) => {
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Copy)]
pub struct $name<const SIZE: usize>(
$common_decl
);
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Copy)]
pub struct $ro_type<const SIZE: usize>($storage);
impl<const SIZE: usize> $name<SIZE> {
#[must_use]
pub fn new() -> Self {
#[allow(clippy::let_unit_value)]
let _: () = <[$tag; SIZE] as CompileTest>::COMPILE_TEST;
let mut storage = ArrayStorage::ZEROED;
algorithm::initialize_storage::<_, false>(&mut storage);
Self(unsafe {
Common::new(
$enm(storage),
Splittability::Allowed,
PathsStatus::Compressed,
)
})
}
pub fn initialize_inplace(memory: &mut MaybeUninit<Self>) -> &mut Self {
#[allow(clippy::let_unit_value)]
let _: () = <[$tag; SIZE] as CompileTest>::COMPILE_TEST;
unsafe {
let ptr = memory.as_mut_ptr();
write_bytes(ptr, 0, 1);
let me = &mut *ptr;
let (s, is_splittable, is_compressed) = me.0.access_internals();
for_current_bitness!(s; s; algorithm::initialize_storage::<$tag, true>(s));
*is_splittable = Splittability::Allowed;
*is_compressed = PathsStatus::Compressed;
me
}
}
}
impl<const SIZE: usize> $name<SIZE> {
#[must_use]
pub fn get_root(&mut self, idx: usize) -> Root {
self.0.get_root(idx)
}
pub fn union(&mut self, idx0: usize, idx1: usize) -> UnionResult {
self.0.union(idx0, idx1)
}
#[must_use]
pub fn is_united(&mut self, idx0: usize, idx1: usize) -> bool {
self.0.is_united(idx0, idx1)
}
pub fn detach(&mut self, idx: usize) {
self.0.detach(idx);
}
#[must_use]
pub fn split_at(&mut self, split_idx: usize) -> (ChunkMut, ChunkMut) {
unsafe {
self.0.split_at(split_idx)
}
}
#[inline]
#[must_use]
pub fn make_ro_view(&mut self) -> DisjointSetRoView {
self.0.make_ro_view()
}
#[inline]
#[must_use]
pub fn into_readonly(self) -> $ro_type<SIZE> {
let s = self.0.into_compressed_storage();
match s {
$enm(s) => $ro_type(s),
_ => unreachable!(),
}
}
#[inline]
pub fn compress_paths(&mut self) {
self.0.compress_paths();
}
pub fn reset(&mut self) {
self.0.reset();
}
}
impl<const SIZE: usize> Default for $name<SIZE> {
fn default() -> Self {
Self::new()
}
}
impl<const SIZE: usize> From<$ro_type<SIZE>> for $name<SIZE> {
fn from(value: $ro_type<SIZE>) -> Self {
Self(unsafe {
Common::new(
$enm(value.0),
Splittability::Disallowed,
PathsStatus::Compressed,
)
})
}
}
impl<const SIZE: usize> $ro_type<SIZE> {
#[inline]
#[must_use]
pub fn view(&self)->DisjointSetRoView{
DisjointSetRoView($enm(self.0.roots_readonly()))
}
}
};
}
impl_array_djs!(
DisjointSetArrayU8,
RoDisjointSetArrayU8,
common_strct!(ArrayStorage<u8, SIZE>, Impossible, Impossible,Impossible),
ArrayStorage<u8, SIZE>,
u8,
BitsEnum::U8
);
impl_array_djs!(
DisjointSetArrayU16,
RoDisjointSetArrayU16,
common_strct!(Impossible, ArrayStorage<u16, SIZE>, Impossible,Impossible),
ArrayStorage<u16, SIZE>,
u16,
BitsEnum::U16
);
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
impl_array_djs!(
DisjointSetArrayU32,
RoDisjointSetArrayU32,
common_strct!(Impossible,Impossible, ArrayStorage<u32, SIZE>, Impossible),
ArrayStorage<u32, SIZE>,
u32,
BitsEnum::U32
);
#[cfg(target_pointer_width = "64")]
impl_array_djs!(
DisjointSetArrayU64,
RoDisjointSetArrayU64,
common_strct!(Impossible,Impossible,Impossible, ArrayStorage<u64, SIZE>),
ArrayStorage<u64, SIZE>,
u64,
BitsEnum::U64
);
#[allow(clippy::let_underscore_untyped)]
#[cfg(test)]
mod tests {
use core::mem::MaybeUninit;
use crate::{DisjointSetArrayU16, DisjointSetArrayU8};
#[test]
fn test_initialize() {
let mut djs: DisjointSetArrayU8<20> = DisjointSetArrayU8::new();
for i in 0..20 {
assert_eq!(djs.get_root(i).into_inner(), i);
}
let _ = djs.union(5, 7);
assert!(djs.is_united(7, 5));
let mut mem: MaybeUninit<DisjointSetArrayU8<20>> = MaybeUninit::uninit();
let djs = DisjointSetArrayU8::initialize_inplace(&mut mem);
for i in 0..20 {
assert_eq!(djs.get_root(i).into_inner(), i);
}
let _ = djs.union(5, 7);
assert!(djs.is_united(7, 5));
}
#[test]
fn test_initialize2() {
const NUM: usize = 500;
let mut djs: DisjointSetArrayU16<NUM> = DisjointSetArrayU16::new();
for i in 0..NUM {
assert_eq!(djs.get_root(i).into_inner(), i);
}
let _ = djs.union(5, 7);
assert!(djs.is_united(7, 5));
let mut mem: MaybeUninit<DisjointSetArrayU16<NUM>> = MaybeUninit::uninit();
let djs = DisjointSetArrayU16::initialize_inplace(&mut mem);
for i in 0..NUM {
assert_eq!(djs.get_root(i).into_inner(), i);
}
let _ = djs.union(5, 7);
assert!(djs.is_united(7, 5));
}
}