macro_rules! impls {
    ($($T: ident {$([$kind:tt] $($kinded: ident)*)*})*) => {$(
        pub mod $T {$(
            inner!{$T $kind $($kinded)*}
        )*}
    )*}
}

macro_rules! inner {
    ($T: ident $kind: tt $($U: ident)*) => {$(
        pub mod $U {
            kinded!{$kind $T $U}
        }
    )*}
}

macro_rules! kinded {
    (perfect $T: ident $U: ident) => {
        cast_fns!{$T $U cast #[doc = "this cast perfectly preserves the value"]}
        impl crate::traits::PerfectCast<$U> for $T {
            fn cast(self) -> $U { cast(self) }
        }
    };
    (wrap $T: ident $U: ident) => {
        cast_fns!{$T $U cast_wrapping #[doc = "this cast deals with out-of-range values via wrapping"]}
        impl crate::traits::WrappingCast<$U> for $T {
            fn cast(self) -> $U { cast_wrapping(self) }
        }
    };
    (round0 $T: ident $U: ident) => {
        cast_fns!{$T $U cast_round_toward_zero #[doc = "this cast rounds unrepresentable values to the next representable value towards zero\nnan becomes zero"]}
        impl crate::traits::RoundTowardZeroCast<$U> for $T {
            fn cast(self) -> $U { cast_round_toward_zero(self) }
        }
    };
    (round $T: ident $U: ident) => {
        cast_fns!{$T $U cast_round_ties_even #[doc = "this cast rounds unrepresentable values to the nearest, with ties going to whichever option is even"]}
        impl crate::traits::RoundTiesEvenCast<$U> for $T {
            fn cast(self) -> $U { cast_round_ties_even(self) }
        }
    };
    (bit $T: ident $U: ident) => {
        cast_fns!{$T $U to_bit}
        impl crate::traits::ToBit<$U> for $T {
            fn to(self) -> $U { to_bit(self) }
        }
    };
    (codepoint $T: ident $U: ident) => {
        cast_fns!{$T $U to_codepoint}
        impl crate::traits::ToCodepoint<$U> for $T {
            fn to(self) -> $U { to_codepoint(self) }
        }
    };
    (wrap_codepoint $T: ident $U: ident) => {
        cast_fns!{$T $U to_codepoint_wrapping}
        impl crate::traits::ToCodepointWrapping<$U> for $T {
            fn to(self) -> $U { to_codepoint_wrapping(self) }
        }
    };
    (char $T: ident $U: ident) => {
        cast_fns!{$T $U codepoint_to_char}
    };
}

macro_rules! cast_fns {
    ($T: ident $U: ident $func: ident $(#[$docs: meta])?) => {
        $(#[$docs])?
        #[inline]
        pub const fn $func(value: $T) -> $U {
            value as _
        }
    }
}

impls! {
    u8 {
        [wrap]
        i8
        [perfect]
        u16 u32 u64 u128 usize
        i16 i32 i64 i128 isize
        f32 f64

        [char]
        char
    }
    u16 {
        [wrap]
        u8
        i8 i16 isize
        [perfect]
        u32 u64 u128 usize
        i32 i64 i128
        f32 f64
    }
    u32 {
        [wrap]
        u8 u16 usize
        i8 i16 i32 isize
        [perfect]
        u64 u128
        i64 i128
        f64
        [round]
        f32
    }
    u64 {
        [wrap]
        u8 u16 u32 usize
        i8 i16 i32 i64 isize
        [perfect]
        u128
        i128
        [round]
        f32 f64
    }
    u128 {
        [wrap]
        u8 u16 u32 u64 usize
        i8 i16 i32 i64 isize i128
        [round]
        f32 f64
    }
    usize {
        [wrap]
        u8 u16 u32 u64 u128
        i8 i16 i32 i64 i128 isize
        [round]
        f32 f64
    }

    i8 {
        [wrap]
        u8 u16 u32 u64 u128 usize
        [perfect]
        i16 i32 i64 i128 isize
        f32 f64
    }
    i16 {
        [wrap]
        u8 u16 u32 u64 u128 usize
        i8
        [perfect]
        i32 i64 i128 isize
        f32 f64
    }
    i32 {
        [wrap]
        u8 u16 u32 u64 u128 usize
        i8 i16 isize
        [perfect]
        i64 i128
        f32
        [round]
        f64
    }
    i64 {
        [wrap]
        u8 u16 u32 u64 u128 usize
        i8 i16 i32 isize
        [perfect]
        i128
        [round]
        f32 f64
    }
    i128 {
        [wrap]
        u8 u16 u32 u64 u128 usize
        i8 i16 i32 i64 isize
        [round]
        f32 f64
    }
    isize {
        [wrap]
        u8 u16 u32 u64 u128 usize
        i8 i16 i32 i64 i128
        [round]
        f32 f64
    }

    f32 {
        [round0]
        u8 u16 u32 u64 u128 usize
        i8 i16 i32 i64 i128 isize
        [perfect]
        f64
    }
    f64 {
        [round0]
        u8 u16 u32 u64 u128 usize
        i8 i16 i32 i64 i128 isize
        [round]
        f32
    }

    bool {
        [bit]
        u8 u16 u32 u64 u128 usize
        i8 i16 i32 i64 i128 isize
    }

    char {
        [wrap_codepoint]
        u8 u16 usize
        i8 i16 isize
        [codepoint]
        u32 u64 u128
        i32 i64 i128
    }
}


#[inline]
pub fn ptr_to_addr<T>(ptr: *const T) -> usize {
    ptr as _
}

#[inline]
pub fn addr_to_ptr<T>(addr: usize) -> *const T {
    addr as _
}

#[inline]
pub fn addr_to_ptr_mut<T>(addr: usize) -> *mut T {
    addr as _
}

#[inline]
pub fn array_start_ptr<T, const N: usize>(array: *const [T; N]) -> *const T {
    array as _
}

#[inline]
pub fn array_start_ptr_mut<T, const N: usize>(array: *mut [T; N]) -> *mut T {
    array as _
}
