use super::*;
use core::ops::{Add, Sub};
use core::mem::MaybeUninit;
use core::ptr;
use typenum::operator_aliases::*;
pub unsafe trait GenericSequence<T>: Sized + IntoIterator {
    
    type Length: ArrayLength<T>;
    
    type Sequence: GenericSequence<T, Length = Self::Length> + FromIterator<T>;
    
    
    
    
    fn generate<F>(f: F) -> Self::Sequence
    where
        F: FnMut(usize) -> T;
    #[doc(hidden)]
    fn inverted_zip<B, U, F>(
        self,
        lhs: GenericArray<B, Self::Length>,
        mut f: F,
    ) -> MappedSequence<GenericArray<B, Self::Length>, B, U>
    where
        GenericArray<B, Self::Length>: GenericSequence<B, Length = Self::Length>
            + MappedGenericSequence<B, U>,
        Self: MappedGenericSequence<T, U>,
        Self::Length: ArrayLength<B> + ArrayLength<U>,
        F: FnMut(B, Self::Item) -> U,
    {
        unsafe {
            let mut left = ArrayConsumer::new(lhs);
            let (left_array_iter, left_position) = left.iter_position();
            FromIterator::from_iter(left_array_iter.zip(self.into_iter()).map(
                |(l, right_value)| {
                        let left_value = ptr::read(l);
                        *left_position += 1;
                        f(left_value, right_value)
                },
            ))
        }
    }
    #[doc(hidden)]
    fn inverted_zip2<B, Lhs, U, F>(self, lhs: Lhs, mut f: F) -> MappedSequence<Lhs, B, U>
    where
        Lhs: GenericSequence<B, Length = Self::Length> + MappedGenericSequence<B, U>,
        Self: MappedGenericSequence<T, U>,
        Self::Length: ArrayLength<B> + ArrayLength<U>,
        F: FnMut(Lhs::Item, Self::Item) -> U,
    {
        FromIterator::from_iter(lhs.into_iter().zip(self.into_iter()).map(|(l, r)| f(l, r)))
    }
}
pub type SequenceItem<T> = <T as IntoIterator>::Item;
unsafe impl<'a, T: 'a, S: GenericSequence<T>> GenericSequence<T> for &'a S
where
    &'a S: IntoIterator,
{
    type Length = S::Length;
    type Sequence = S::Sequence;
    #[inline]
    fn generate<F>(f: F) -> Self::Sequence
    where
        F: FnMut(usize) -> T,
    {
        S::generate(f)
    }
}
unsafe impl<'a, T: 'a, S: GenericSequence<T>> GenericSequence<T> for &'a mut S
where
    &'a mut S: IntoIterator,
{
    type Length = S::Length;
    type Sequence = S::Sequence;
    #[inline]
    fn generate<F>(f: F) -> Self::Sequence
    where
        F: FnMut(usize) -> T,
    {
        S::generate(f)
    }
}
pub unsafe trait Lengthen<T>: Sized + GenericSequence<T> {
    
    type Longer: Shorten<T, Shorter = Self>;
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    fn append(self, last: T) -> Self::Longer;
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    fn prepend(self, first: T) -> Self::Longer;
}
pub unsafe trait Shorten<T>: Sized + GenericSequence<T> {
    
    type Shorter: Lengthen<T, Longer = Self>;
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    fn pop_back(self) -> (Self::Shorter, T);
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    fn pop_front(self) -> (T, Self::Shorter);
}
unsafe impl<T, N: ArrayLength<T>> Lengthen<T> for GenericArray<T, N>
where
    N: Add<B1>,
    Add1<N>: ArrayLength<T>,
    Add1<N>: Sub<B1, Output = N>,
    Sub1<Add1<N>>: ArrayLength<T>,
{
    type Longer = GenericArray<T, Add1<N>>;
    fn append(self, last: T) -> Self::Longer {
        let mut longer: MaybeUninit<Self::Longer> = MaybeUninit::uninit();
        
        let out_ptr = longer.as_mut_ptr() as *mut Self;
        unsafe {
            
            ptr::write(out_ptr, self);
            
            ptr::write(out_ptr.add(1) as *mut T, last);
            longer.assume_init()
        }
    }
    fn prepend(self, first: T) -> Self::Longer {
        let mut longer: MaybeUninit<Self::Longer> = MaybeUninit::uninit();
        
        let out_ptr = longer.as_mut_ptr() as *mut T;
        unsafe {
            
            ptr::write(out_ptr, first);
            
            ptr::write(out_ptr.add(1) as *mut Self, self);
            longer.assume_init()
        }
    }
}
unsafe impl<T, N: ArrayLength<T>> Shorten<T> for GenericArray<T, N>
where
    N: Sub<B1>,
    Sub1<N>: ArrayLength<T>,
    Sub1<N>: Add<B1, Output = N>,
    Add1<Sub1<N>>: ArrayLength<T>,
{
    type Shorter = GenericArray<T, Sub1<N>>;
    fn pop_back(self) -> (Self::Shorter, T) {
        let whole = ManuallyDrop::new(self);
        unsafe {
            let init = ptr::read(whole.as_ptr() as _);
            let last = ptr::read(whole.as_ptr().add(Sub1::<N>::USIZE) as _);
            (init, last)
        }
    }
    fn pop_front(self) -> (T, Self::Shorter) {
        
        let whole = ManuallyDrop::new(self);
        unsafe {
            let head = ptr::read(whole.as_ptr() as _);
            let tail = ptr::read(whole.as_ptr().offset(1) as _);
            (head, tail)
        }
    }
}
pub unsafe trait Split<T, K>: GenericSequence<T>
where
    K: ArrayLength<T>,
{
    
    type First: GenericSequence<T>;
    
    type Second: GenericSequence<T>;
    
    fn split(self) -> (Self::First, Self::Second);
}
unsafe impl<T, N, K> Split<T, K> for GenericArray<T, N>
where
    N: ArrayLength<T>,
    K: ArrayLength<T>,
    N: Sub<K>,
    Diff<N, K>: ArrayLength<T>,
{
    type First = GenericArray<T, K>;
    type Second = GenericArray<T, Diff<N, K>>;
    fn split(self) -> (Self::First, Self::Second) {
        unsafe {
            
            let whole = ManuallyDrop::new(self);
            let head = ptr::read(whole.as_ptr() as *const _);
            let tail = ptr::read(whole.as_ptr().add(K::USIZE) as *const _);
            (head, tail)
        }
    }
}
unsafe impl<'a, T, N, K> Split<T, K> for &'a GenericArray<T, N>
where
    N: ArrayLength<T>,
    K: ArrayLength<T> + 'static,
    N: Sub<K>,
    Diff<N, K>: ArrayLength<T>,
{
    type First = &'a GenericArray<T, K>;
    type Second = &'a GenericArray<T, Diff<N, K>>;
    fn split(self) -> (Self::First, Self::Second) {
        unsafe {
            let ptr_to_first: *const T = self.as_ptr();
            let head = &*(ptr_to_first as *const _);
            let tail = &*(ptr_to_first.add(K::USIZE) as *const _);
            (head, tail)
        }
    }
}
unsafe impl<'a, T, N, K> Split<T, K> for &'a mut GenericArray<T, N>
where
    N: ArrayLength<T>,
    K: ArrayLength<T> + 'static,
    N: Sub<K>,
    Diff<N, K>: ArrayLength<T>,
{
    type First = &'a mut GenericArray<T, K>;
    type Second = &'a mut GenericArray<T, Diff<N, K>>;
    fn split(self) -> (Self::First, Self::Second) {
        unsafe {
            let ptr_to_first: *mut T = self.as_mut_ptr();
            let head = &mut *(ptr_to_first as *mut _);
            let tail = &mut *(ptr_to_first.add(K::USIZE) as *mut _);
            (head, tail)
        }
    }
}
pub unsafe trait Concat<T, M>: GenericSequence<T>
where
    M: ArrayLength<T>,
{
    
    type Rest: GenericSequence<T, Length = M>;
    
    type Output: GenericSequence<T>;
    
    fn concat(self, rest: Self::Rest) -> Self::Output;
}
unsafe impl<T, N, M> Concat<T, M> for GenericArray<T, N>
where
    N: ArrayLength<T> + Add<M>,
    M: ArrayLength<T>,
    Sum<N, M>: ArrayLength<T>,
{
    type Rest = GenericArray<T, M>;
    type Output = GenericArray<T, Sum<N, M>>;
    fn concat(self, rest: Self::Rest) -> Self::Output {
        let mut output: MaybeUninit<Self::Output> = MaybeUninit::uninit();
        let out_ptr = output.as_mut_ptr() as *mut Self;
        unsafe {
            
            ptr::write(out_ptr, self);
            
            ptr::write(out_ptr.add(1) as *mut _, rest);
            output.assume_init()
        }
    }
}