aph_disjoint_set 0.1.1

Disjoint set implementation with optimized memory usage and ability to detach elements.
Documentation
// It is not possible to parametrise
// type by another generic so I resort to macros.
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
macro_rules! bits_enum{
    ($t:ident) => {
        crate::bits_enum::BitsEnum<
            $t<u8>,
            $t<u16>,
        >
    };
    ($t:ident, $extra_lifetime:lifetime) => {
        crate::bits_enum::BitsEnum<
            $t<$extra_lifetime, u8>,
            $t<$extra_lifetime, u16>,
        >
    };
    (impl $t:ident) => {
        crate::bits_enum::BitsEnum<
            impl $t<u8>,
            impl $t<u16>,
        >
    };
    ($tu8:ty, $tu16:ty, $tu32:ty, $tu64:ty) => {
        crate::bits_enum::BitsEnum<
            $tu8,
            $tu16,
        >
    };
}
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
macro_rules! common_strct{
    ($t:ident) => {
        crate::common::Common<
            $t<u8>,
            $t<u16>,
        >
    };
    ($t:ident, $extra_lifetime:lifetime) => {
        crate::common::Common<
            $t<$extra_lifetime, u8>,
            $t<$extra_lifetime, u16>,
        >
    };
    ($tu8:ty, $tu16:ty, $tu32:ty, $tu64:ty) => {
        crate::common::Common<
            $tu8,
            $tu16,
        >
    };
}
#[cfg(target_pointer_width = "32")]
macro_rules! bits_enum{
    ($t:ident) => {
        crate::bits_enum::BitsEnum<
            $t<u8>,
            $t<u16>,
            $t<u32>,
        >
    };
    ($t:ident, $extra_lifetime:lifetime) => {
        crate::bits_enum::BitsEnum<
            $t<$extra_lifetime, u8>,
            $t<$extra_lifetime, u16>,
            $t<$extra_lifetime, u32>,
        >
    };
    (impl $t:ident) => {
        crate::bits_enum::BitsEnum<
            impl $t<u8>,
            impl $t<u16>,
            impl $t<u32>,
        >
    };
    ($tu8:ty, $tu16:ty, $tu32:ty, $tu64:ty) => {
        crate::bits_enum::BitsEnum<
            $tu8,
            $tu16,
            $tu32,
        >
    };
}
#[cfg(target_pointer_width = "32")]
macro_rules! common_strct{
    ($t:ident) => {
        crate::common::Common<
            $t<u8>,
            $t<u16>,
            $t<u32>,
        >
    };
    ($t:ident, $extra_lifetime:lifetime) => {
        crate::common::Common<
            $t<$extra_lifetime, u8>,
            $t<$extra_lifetime, u16>,
            $t<$extra_lifetime, u32>,
        >
    };
    ($tu8:ty, $tu16:ty, $tu32:ty, $tu64:ty) => {
        crate::common::Common<
            $tu8,
            $tu16,
            $tu32,
        >
    };
}
#[cfg(target_pointer_width = "64")]
macro_rules! bits_enum{
    ($t:ident) => {
        crate::bits_enum::BitsEnum<
            $t<u8>,
            $t<u16>,
            $t<u32>,
            $t<u64>,
        >
    };
    ($t:ident, $extra_lifetime:lifetime) => {
        crate::bits_enum::BitsEnum<
            $t<$extra_lifetime, u8>,
            $t<$extra_lifetime, u16>,
            $t<$extra_lifetime, u32>,
            $t<$extra_lifetime, u64>,
        >
    };
    (impl $t:ident) => {
        crate::bits_enum::BitsEnum<
            impl $t<u8>,
            impl $t<u16>,
            impl $t<u32>,
            impl $t<u64>,
        >
    };
    ($tu8:ty, $tu16:ty, $tu32:ty, $tu64:ty) => {
        crate::bits_enum::BitsEnum<
            $tu8,
            $tu16,
            $tu32,
            $tu64,
        >
    };
}
#[cfg(target_pointer_width = "64")]
macro_rules! common_strct{
    ($t:ident) => {
        crate::common::Common<
            $t<u8>,
            $t<u16>,
            $t<u32>,
            $t<u64>,
        >
    };
    ($t:ident, $extra_lifetime:lifetime) => {
        crate::common::Common<
            $t<$extra_lifetime, u8>,
            $t<$extra_lifetime, u16>,
            $t<$extra_lifetime, u32>,
            $t<$extra_lifetime, u64>,
        >
    };
    ($tu8:ty, $tu16:ty, $tu32:ty, $tu64:ty) => {
        crate::common::Common<
            $tu8,
            $tu16,
            $tu32,
            $tu64,
        >
    };
}

// Basically, visitor macro.
macro_rules! for_current_bitness {
    ($this:expr; $matched:ident; $e:expr) => {
        match $this {
            crate::bits_enum::BitsEnum::U8($matched) => {
                #[allow(unused)]
                use crate::bits_enum::BitsEnum::U8 as CurrentBitness;
                $e
            }
            crate::bits_enum::BitsEnum::U16($matched) => {
                #[allow(unused)]
                use crate::bits_enum::BitsEnum::U16 as CurrentBitness;
                $e
            }
            #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
            crate::bits_enum::BitsEnum::U32($matched) => {
                #[allow(unused)]
                use crate::bits_enum::BitsEnum::U32 as CurrentBitness;
                $e
            }
            #[cfg(target_pointer_width = "64")]
            crate::bits_enum::BitsEnum::U64($matched) => {
                #[allow(unused)]
                use crate::bits_enum::BitsEnum::U64 as CurrentBitness;
                $e
            }
        }
    };
}

// Choses `BitsEnum` variant by size.
// Chosen type can be accessed as `ChosenTagType`.
macro_rules! choose_by_size {
    ($size: expr, $e:expr) => {
        {
            #[allow(clippy::assertions_on_constants, clippy::cast_possible_truncation)]
            const MAX_U8: usize = {
                assert!(u8::BITS <= usize::BITS);
                u8::MAX as usize
            };
            #[allow(clippy::assertions_on_constants, clippy::cast_possible_truncation)]
            const MAX_U16: usize = {
                assert!(u16::BITS <= usize::BITS);
                u16::MAX as usize
            };
            #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
            #[allow(clippy::assertions_on_constants, clippy::cast_possible_truncation)]
            const MAX_U32: usize = {
                assert!(u32::BITS <= usize::BITS);
                u32::MAX as usize
            };
            #[cfg(target_pointer_width = "64")]
            #[allow(clippy::assertions_on_constants, clippy::cast_possible_truncation)]
            const MAX_U64: usize = {
                assert!(u64::BITS <= usize::BITS);
                u64::MAX as usize
            };

            #[allow(clippy::match_overlapping_arm)]
            match $size {
                ..=MAX_U8 => {
                    #[allow(unused)]
                    type ChosenTagType = u8;
                    crate::bits_enum::BitsEnum::U8($e)
                },
                ..=MAX_U16 => {
                    #[allow(unused)]
                    type ChosenTagType = u16;
                    crate::bits_enum::BitsEnum::U16($e)
                },
                #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
                ..=MAX_U32 => {
                    #[allow(unused)]
                    type ChosenTagType = u32;
                    crate::bits_enum::BitsEnum::U32($e)
                },
                #[cfg(target_pointer_width = "64")]
                ..=MAX_U64 => {
                    #[allow(unused)]
                    type ChosenTagType = u64;
                    crate::bits_enum::BitsEnum::U64($e)
                },
                _ => unreachable!("We checked all ranges of usize"),
            }
        }
    };
}

macro_rules! check_bounds {
    ($storage:expr; $($idx:ident),+) => {
        {
            let lower_bound: usize = (& $storage).lower_bound();
            let upper_bound: usize = (& $storage).upper_bound();
            $(
                assert!(
                    lower_bound <= $idx && $idx < upper_bound,
                    "{} ({}) is out of bounds {}..{}",
                    stringify!($idx),
                    $idx,
                    lower_bound,
                    upper_bound
                );
            )+
        }
    };
    (multibit $storage:expr; $($idx:ident),+) => {
        {
            let (lower_bound, upper_bound): (usize, usize) =
                crate::macros::for_current_bitness!(
                    (& $storage); s; (s.lower_bound(), s.upper_bound())
                );
            $(
                assert!(
                    lower_bound <= $idx && $idx < upper_bound,
                    "{} ({}) is out of bounds {}..{}",
                    stringify!($idx),
                    $idx,
                    lower_bound,
                    upper_bound
                );
            )+
        }
    };
}

pub(crate) use bits_enum;
pub(crate) use check_bounds;
pub(crate) use choose_by_size;
pub(crate) use common_strct;
pub(crate) use for_current_bitness;

#[cfg(test)]
mod tests {
    use crate::bits_enum::BitsEnum;
    use crate::tag_type::TagType;

    #[test]
    fn choose_by_size() {
        let v = choose_by_size!(255, ());
        assert!(matches!(v, BitsEnum::U8(())));

        let v = choose_by_size!(256, ());
        assert!(matches!(v, BitsEnum::U16(())));

        let v = choose_by_size!(65535, ());
        assert!(matches!(v, BitsEnum::U16(())));

        #[cfg(target_pointer_width = "32")]
        {
            let v = choose_by_size!(65536, ());
            assert!(matches!(v, BitsEnum::U32(())));

            let v = choose_by_size!(u32::MAX.as_u(), ());
            assert!(matches!(v, BitsEnum::U32(())));
        }

        #[cfg(target_pointer_width = "64")]
        {
            let v = choose_by_size!(1 + u32::MAX.as_u(), ());
            assert!(matches!(v, BitsEnum::U64(())));

            let v = choose_by_size!(u64::MAX.as_u(), ());
            assert!(matches!(v, BitsEnum::U64(())));
        }
    }
}