use std::{mem::MaybeUninit, vec::Vec};
pub trait Collate {
    type Collated;
    fn collated(self) -> Self::Collated;
}
impl<A, B, const N: usize> Collate for [(A, B); N] {
    type Collated = ([A; N], [B; N]);
    fn collated(self) -> Self::Collated {
        let mut a_n = [(); N].map(|_| MaybeUninit::uninit());
        let mut b_n = [(); N].map(|_| MaybeUninit::uninit());
        for (i, (a, b)) in self.into_iter().enumerate() {
            a_n[i].write(a);
            b_n[i].write(b);
        }
        let a_n = unsafe { a_n.map(|a| a.assume_init()) };
        let b_n = unsafe { b_n.map(|b| b.assume_init()) };
        (a_n, b_n)
    }
}
impl<'a, A, B, const N: usize> Collate for [&'a (A, B); N] {
    type Collated = ([&'a A; N], [&'a B; N]);
    fn collated(self) -> Self::Collated {
        let mut a_n = [(); N].map(|_| MaybeUninit::uninit());
        let mut b_n = [(); N].map(|_| MaybeUninit::uninit());
        for (i, (a, b)) in self.into_iter().enumerate() {
            a_n[i].write(a);
            b_n[i].write(b);
        }
        let a_n = unsafe { a_n.map(|a| a.assume_init()) };
        let b_n = unsafe { b_n.map(|b| b.assume_init()) };
        (a_n, b_n)
    }
}
impl<A, B> Collate for Vec<(A, B)> {
    type Collated = (Vec<A>, Vec<B>);
    fn collated(self) -> Self::Collated {
        self.into_iter().unzip()
    }
}
impl<'a, A, B> Collate for Vec<&'a (A, B)> {
    type Collated = (Vec<&'a A>, Vec<&'a B>);
    fn collated(self) -> Self::Collated {
        self.into_iter().map(|(a, b)| (a, b)).unzip()
    }
}
pub struct Collator<I> {
    iter: I,
}
impl<I: Iterator> Iterator for Collator<I>
where
    I::Item: Collate,
{
    type Item = <I::Item as Collate>::Collated;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|i| i.collated())
    }
}
impl<I: ExactSizeIterator> ExactSizeIterator for Collator<I>
where
    Self: Iterator,
{
    fn len(&self) -> usize {
        self.iter.len()
    }
}
pub trait IteratorCollateExt: Iterator {
    fn collate(self) -> Collator<Self>
    where
        Self: Sized,
    {
        Collator { iter: self }
    }
}
impl<I: Iterator> IteratorCollateExt for I {}
#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_collate_array() {
        let items = [(1, 2), (3, 4), (5, 6)];
        assert_eq!(items.collated(), ([1, 3, 5], [2, 4, 6]));
        let items = [&(1, 2), &(3, 4), &(5, 6)];
        assert_eq!(items.collated(), ([&1, &3, &5], [&2, &4, &6]));
    }
    #[test]
    fn test_collate_vec() {
        let items = std::vec![(1, 2), (3, 4), (5, 6)];
        let (a, b): (Vec<i32>, Vec<i32>) = items.collated();
        assert_eq!(a, [1, 3, 5]);
        assert_eq!(b, [2, 4, 6]);
        let items = std::vec![&(1, 2), &(3, 4), &(5, 6)];
        let (a, b): (Vec<&i32>, Vec<&i32>) = items.collated();
        assert_eq!(a, [&1, &3, &5]);
        assert_eq!(b, [&2, &4, &6]);
    }
}