use arrow::array::*;
use crate::prelude::*;
#[cfg(feature = "dtype-struct")]
use crate::series::iterator::SeriesIter;
pub mod par;
impl<T> ChunkedArray<T>
where
    T: PolarsDataType,
{
    #[inline]
    pub fn iter(&self) -> impl PolarsIterator<Item = Option<T::Physical<'_>>> {
        unsafe {
            self.downcast_iter()
                .flat_map(|arr| arr.iter())
                .trust_my_length(self.len())
        }
    }
}
pub trait PolarsIterator:
    ExactSizeIterator + DoubleEndedIterator + Send + Sync + TrustedLen
{
}
unsafe impl<'a, I> TrustedLen for Box<dyn PolarsIterator<Item = I> + 'a> {}
impl<T: ?Sized> PolarsIterator for T where
    T: ExactSizeIterator + DoubleEndedIterator + Send + Sync + TrustedLen
{
}
impl<'a, T> IntoIterator for &'a ChunkedArray<T>
where
    T: PolarsNumericType,
{
    type Item = Option<T::Native>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        Box::new(
            unsafe {
                self.downcast_iter()
                    .flatten()
                    .map(|x| x.copied())
                    .trust_my_length(self.len())
            },
        )
    }
}
impl<'a> IntoIterator for &'a BooleanChunked {
    type Item = Option<bool>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        unsafe { Box::new(self.downcast_iter().flatten().trust_my_length(self.len())) }
    }
}
pub struct BoolIterNoNull<'a> {
    array: &'a BooleanArray,
    current: usize,
    current_end: usize,
}
impl<'a> BoolIterNoNull<'a> {
    pub fn new(array: &'a BooleanArray) -> Self {
        BoolIterNoNull {
            array,
            current: 0,
            current_end: array.len(),
        }
    }
}
impl<'a> Iterator for BoolIterNoNull<'a> {
    type Item = bool;
    fn next(&mut self) -> Option<Self::Item> {
        if self.current == self.current_end {
            None
        } else {
            let old = self.current;
            self.current += 1;
            unsafe { Some(self.array.value_unchecked(old)) }
        }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        (
            self.array.len() - self.current,
            Some(self.array.len() - self.current),
        )
    }
}
impl<'a> DoubleEndedIterator for BoolIterNoNull<'a> {
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.current_end == self.current {
            None
        } else {
            self.current_end -= 1;
            unsafe { Some(self.array.value_unchecked(self.current_end)) }
        }
    }
}
impl<'a> ExactSizeIterator for BoolIterNoNull<'a> {}
impl BooleanChunked {
    #[allow(clippy::wrong_self_convention)]
    #[doc(hidden)]
    pub fn into_no_null_iter(
        &self,
    ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = bool> + DoubleEndedIterator + TrustedLen
    {
        unsafe {
            self.downcast_iter()
                .flat_map(BoolIterNoNull::new)
                .trust_my_length(self.len())
        }
    }
}
impl<'a> IntoIterator for &'a StringChunked {
    type Item = Option<&'a str>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        unsafe { Box::new(self.downcast_iter().flatten().trust_my_length(self.len())) }
    }
}
impl StringChunked {
    #[allow(clippy::wrong_self_convention)]
    #[doc(hidden)]
    pub fn into_no_null_iter(
        &self,
    ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = &str> + DoubleEndedIterator + TrustedLen
    {
        unsafe {
            self.downcast_iter()
                .flat_map(|arr| arr.values_iter())
                .trust_my_length(self.len())
        }
    }
}
impl<'a> IntoIterator for &'a BinaryChunked {
    type Item = Option<&'a [u8]>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        unsafe { Box::new(self.downcast_iter().flatten().trust_my_length(self.len())) }
    }
}
impl BinaryChunked {
    #[allow(clippy::wrong_self_convention)]
    #[doc(hidden)]
    pub fn into_no_null_iter(
        &self,
    ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = &[u8]> + DoubleEndedIterator + TrustedLen
    {
        unsafe {
            self.downcast_iter()
                .flat_map(|arr| arr.values_iter())
                .trust_my_length(self.len())
        }
    }
}
impl<'a> IntoIterator for &'a BinaryOffsetChunked {
    type Item = Option<&'a [u8]>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        unsafe { Box::new(self.downcast_iter().flatten().trust_my_length(self.len())) }
    }
}
impl BinaryOffsetChunked {
    #[allow(clippy::wrong_self_convention)]
    #[doc(hidden)]
    pub fn into_no_null_iter(
        &self,
    ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = &[u8]> + DoubleEndedIterator + TrustedLen
    {
        unsafe {
            self.downcast_iter()
                .flat_map(|arr| arr.values_iter())
                .trust_my_length(self.len())
        }
    }
}
impl<'a> IntoIterator for &'a ListChunked {
    type Item = Option<Series>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        let dtype = self.inner_dtype();
        if self.null_count() == 0 {
            unsafe {
                Box::new(
                    self.downcast_iter()
                        .flat_map(|arr| arr.iter().unwrap_required())
                        .trust_my_length(self.len())
                        .map(move |arr| {
                            Some(Series::from_chunks_and_dtype_unchecked(
                                "",
                                vec![arr],
                                dtype,
                            ))
                        }),
                )
            }
        } else {
            unsafe {
                Box::new(
                    self.downcast_iter()
                        .flat_map(|arr| arr.iter())
                        .trust_my_length(self.len())
                        .map(move |arr| {
                            arr.map(|arr| {
                                Series::from_chunks_and_dtype_unchecked("", vec![arr], dtype)
                            })
                        }),
                )
            }
        }
    }
}
impl ListChunked {
    #[allow(clippy::wrong_self_convention)]
    #[doc(hidden)]
    pub fn into_no_null_iter(
        &self,
    ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = Series> + DoubleEndedIterator + TrustedLen
    {
        let inner_type = self.inner_dtype();
        unsafe {
            self.downcast_iter()
                .flat_map(|arr| arr.values_iter())
                .map(move |arr| Series::from_chunks_and_dtype_unchecked("", vec![arr], inner_type))
                .trust_my_length(self.len())
        }
    }
}
#[cfg(feature = "dtype-array")]
impl<'a> IntoIterator for &'a ArrayChunked {
    type Item = Option<Series>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        let dtype = self.inner_dtype();
        if self.null_count() == 0 {
            unsafe {
                Box::new(
                    self.downcast_iter()
                        .flat_map(|arr| arr.iter().unwrap_required())
                        .trust_my_length(self.len())
                        .map(move |arr| {
                            Some(Series::from_chunks_and_dtype_unchecked(
                                "",
                                vec![arr],
                                dtype,
                            ))
                        }),
                )
            }
        } else {
            unsafe {
                Box::new(
                    self.downcast_iter()
                        .flat_map(|arr| arr.iter())
                        .trust_my_length(self.len())
                        .map(move |arr| {
                            arr.map(|arr| {
                                Series::from_chunks_and_dtype_unchecked("", vec![arr], dtype)
                            })
                        }),
                )
            }
        }
    }
}
#[cfg(feature = "dtype-array")]
pub struct FixedSizeListIterNoNull<'a> {
    array: &'a FixedSizeListArray,
    inner_type: DataType,
    current: usize,
    current_end: usize,
}
#[cfg(feature = "dtype-array")]
impl<'a> FixedSizeListIterNoNull<'a> {
    pub fn new(array: &'a FixedSizeListArray, inner_type: DataType) -> Self {
        FixedSizeListIterNoNull {
            array,
            inner_type,
            current: 0,
            current_end: array.len(),
        }
    }
}
#[cfg(feature = "dtype-array")]
impl<'a> Iterator for FixedSizeListIterNoNull<'a> {
    type Item = Series;
    fn next(&mut self) -> Option<Self::Item> {
        if self.current == self.current_end {
            None
        } else {
            let old = self.current;
            self.current += 1;
            unsafe {
                Some(Series::from_chunks_and_dtype_unchecked(
                    "",
                    vec![self.array.value_unchecked(old)],
                    &self.inner_type,
                ))
            }
        }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        (
            self.array.len() - self.current,
            Some(self.array.len() - self.current),
        )
    }
}
#[cfg(feature = "dtype-array")]
impl<'a> DoubleEndedIterator for FixedSizeListIterNoNull<'a> {
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.current_end == self.current {
            None
        } else {
            self.current_end -= 1;
            unsafe {
                Some(Series::try_from(("", self.array.value_unchecked(self.current_end))).unwrap())
            }
        }
    }
}
#[cfg(feature = "dtype-array")]
impl<'a> ExactSizeIterator for FixedSizeListIterNoNull<'a> {}
#[cfg(feature = "dtype-array")]
impl ArrayChunked {
    #[allow(clippy::wrong_self_convention)]
    #[doc(hidden)]
    pub fn into_no_null_iter(
        &self,
    ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = Series> + DoubleEndedIterator + TrustedLen
    {
        let inner_type = self.inner_dtype();
        unsafe {
            self.downcast_iter()
                .flat_map(move |arr| FixedSizeListIterNoNull::new(arr, inner_type.clone()))
                .trust_my_length(self.len())
        }
    }
}
#[cfg(feature = "object")]
impl<'a, T> IntoIterator for &'a ObjectChunked<T>
where
    T: PolarsObject,
{
    type Item = Option<&'a T>;
    type IntoIter = Box<dyn PolarsIterator<Item = Self::Item> + 'a>;
    fn into_iter(self) -> Self::IntoIter {
        unsafe { Box::new(self.downcast_iter().flatten().trust_my_length(self.len())) }
    }
}
#[cfg(feature = "object")]
impl<T: PolarsObject> ObjectChunked<T> {
    #[allow(clippy::wrong_self_convention)]
    #[doc(hidden)]
    pub fn into_no_null_iter(
        &self,
    ) -> impl '_ + Send + Sync + ExactSizeIterator<Item = &T> + DoubleEndedIterator + TrustedLen
    {
        unsafe {
            self.downcast_iter()
                .flat_map(|arr| arr.values().iter())
                .trust_my_length(self.len())
        }
    }
}
#[cfg(feature = "dtype-struct")]
impl<'a> IntoIterator for &'a StructChunked {
    type Item = &'a [AnyValue<'a>];
    type IntoIter = StructIter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        let field_iter = self.fields().iter().map(|s| s.iter()).collect();
        StructIter {
            field_iter,
            buf: vec![],
        }
    }
}
#[cfg(feature = "dtype-struct")]
pub struct StructIter<'a> {
    field_iter: Vec<SeriesIter<'a>>,
    buf: Vec<AnyValue<'a>>,
}
#[cfg(feature = "dtype-struct")]
impl<'a> Iterator for StructIter<'a> {
    type Item = &'a [AnyValue<'a>];
    fn next(&mut self) -> Option<Self::Item> {
        self.buf.clear();
        for it in &mut self.field_iter {
            self.buf.push(it.next()?);
        }
        unsafe {
            Some(std::mem::transmute::<&'_ [AnyValue], &'a [AnyValue]>(
                &self.buf,
            ))
        }
    }
}
pub struct SomeIterator<I>(I)
where
    I: Iterator;
impl<I> Iterator for SomeIterator<I>
where
    I: Iterator,
{
    type Item = Option<I::Item>;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(Some)
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}
impl<I> DoubleEndedIterator for SomeIterator<I>
where
    I: DoubleEndedIterator,
{
    fn next_back(&mut self) -> Option<Self::Item> {
        self.0.next_back().map(Some)
    }
}
impl<I> ExactSizeIterator for SomeIterator<I> where I: ExactSizeIterator {}
#[cfg(test)]
mod test {
    use crate::prelude::*;
    #[test]
    fn out_of_bounds() {
        let mut a = UInt32Chunked::from_slice("a", &[1, 2, 3]);
        let b = UInt32Chunked::from_slice("a", &[1, 2, 3]);
        a.append(&b);
        let v = a.into_iter().collect::<Vec<_>>();
        assert_eq!(
            vec![Some(1u32), Some(2), Some(3), Some(1), Some(2), Some(3)],
            v
        )
    }
    macro_rules! impl_test_iter_single_chunk {
        ($test_name:ident, $ca_type:ty, $first_val:expr, $second_val:expr, $third_val:expr) => {
            #[test]
            fn $test_name() {
                let a = <$ca_type>::from_slice("test", &[$first_val, $second_val, $third_val]);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some(Some($first_val)));
                assert_eq!(it.next(), Some(Some($second_val)));
                assert_eq!(it.next(), Some(Some($third_val)));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some(Some($third_val)));
                assert_eq!(it.next_back(), Some(Some($second_val)));
                assert_eq!(it.next_back(), Some(Some($first_val)));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some(Some($third_val)));
                assert_eq!(it.next(), Some(Some($first_val)));
                assert_eq!(it.next(), Some(Some($second_val)));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some(Some($first_val)));
                assert_eq!(it.next_back(), Some(Some($third_val)));
                assert_eq!(it.next_back(), Some(Some($second_val)));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
            }
        };
    }
    impl_test_iter_single_chunk!(num_iter_single_chunk, UInt32Chunked, 1, 2, 3);
    impl_test_iter_single_chunk!(utf8_iter_single_chunk, StringChunked, "a", "b", "c");
    impl_test_iter_single_chunk!(bool_iter_single_chunk, BooleanChunked, true, true, false);
    macro_rules! impl_test_iter_single_chunk_null_check {
        ($test_name:ident, $ca_type:ty, $first_val:expr, $second_val:expr, $third_val:expr) => {
            #[test]
            fn $test_name() {
                let a = <$ca_type>::new("test", &[$first_val, $second_val, $third_val]);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), Some($third_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), Some($first_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
            }
        };
    }
    impl_test_iter_single_chunk_null_check!(
        num_iter_single_chunk_null_check,
        UInt32Chunked,
        Some(1),
        None,
        Some(3)
    );
    impl_test_iter_single_chunk_null_check!(
        utf8_iter_single_chunk_null_check,
        StringChunked,
        Some("a"),
        None,
        Some("c")
    );
    impl_test_iter_single_chunk_null_check!(
        bool_iter_single_chunk_null_check,
        BooleanChunked,
        Some(true),
        None,
        Some(false)
    );
    macro_rules! impl_test_iter_many_chunk {
        ($test_name:ident, $ca_type:ty, $first_val:expr, $second_val:expr, $third_val:expr) => {
            #[test]
            fn $test_name() {
                let mut a = <$ca_type>::from_slice("test", &[$first_val, $second_val]);
                let a_b = <$ca_type>::from_slice("", &[$third_val]);
                a.append(&a_b);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some(Some($first_val)));
                assert_eq!(it.next(), Some(Some($second_val)));
                assert_eq!(it.next(), Some(Some($third_val)));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some(Some($third_val)));
                assert_eq!(it.next_back(), Some(Some($second_val)));
                assert_eq!(it.next_back(), Some(Some($first_val)));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some(Some($third_val)));
                assert_eq!(it.next(), Some(Some($first_val)));
                assert_eq!(it.next(), Some(Some($second_val)));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some(Some($first_val)));
                assert_eq!(it.next_back(), Some(Some($third_val)));
                assert_eq!(it.next_back(), Some(Some($second_val)));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
            }
        };
    }
    impl_test_iter_many_chunk!(num_iter_many_chunk, UInt32Chunked, 1, 2, 3);
    impl_test_iter_many_chunk!(utf8_iter_many_chunk, StringChunked, "a", "b", "c");
    impl_test_iter_many_chunk!(bool_iter_many_chunk, BooleanChunked, true, true, false);
    macro_rules! impl_test_iter_many_chunk_null_check {
        ($test_name:ident, $ca_type:ty, $first_val:expr, $second_val:expr, $third_val:expr) => {
            #[test]
            fn $test_name() {
                let mut a = <$ca_type>::new("test", &[$first_val, $second_val]);
                let a_b = <$ca_type>::new("", &[$third_val]);
                a.append(&a_b);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), Some($third_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), Some($first_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
            }
        };
    }
    impl_test_iter_many_chunk_null_check!(
        num_iter_many_chunk_null_check,
        UInt32Chunked,
        Some(1),
        None,
        Some(3)
    );
    impl_test_iter_many_chunk_null_check!(
        utf8_iter_many_chunk_null_check,
        StringChunked,
        Some("a"),
        None,
        Some("c")
    );
    impl_test_iter_many_chunk_null_check!(
        bool_iter_many_chunk_null_check,
        BooleanChunked,
        Some(true),
        None,
        Some(false)
    );
    macro_rules! impl_test_no_null_iter_single_chunk {
        ($test_name:ident, $ca_type:ty, $first_val:expr, $second_val:expr, $third_val:expr) => {
            #[test]
            fn $test_name() {
                let a = <$ca_type>::from_slice("test", &[$first_val, $second_val, $third_val]);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), Some($third_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), Some($first_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
            }
        };
    }
    impl_test_no_null_iter_single_chunk!(num_no_null_iter_single_chunk, UInt32Chunked, 1, 2, 3);
    impl_test_no_null_iter_single_chunk!(
        utf8_no_null_iter_single_chunk,
        StringChunked,
        "a",
        "b",
        "c"
    );
    impl_test_no_null_iter_single_chunk!(
        bool_no_null_iter_single_chunk,
        BooleanChunked,
        true,
        true,
        false
    );
    macro_rules! impl_test_no_null_iter_many_chunk {
        ($test_name:ident, $ca_type:ty, $first_val:expr, $second_val:expr, $third_val:expr) => {
            #[test]
            fn $test_name() {
                let mut a = <$ca_type>::from_slice("test", &[$first_val, $second_val]);
                let a_b = <$ca_type>::from_slice("", &[$third_val]);
                a.append(&a_b);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), Some($third_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), Some($first_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next(), Some($second_val));
                assert_eq!(it.next(), None);
                assert_eq!(it.next_back(), None);
                let mut it = a.into_no_null_iter();
                assert_eq!(it.next(), Some($first_val));
                assert_eq!(it.next_back(), Some($third_val));
                assert_eq!(it.next_back(), Some($second_val));
                assert_eq!(it.next_back(), None);
                assert_eq!(it.next(), None);
            }
        };
    }
    impl_test_no_null_iter_many_chunk!(num_no_null_iter_many_chunk, UInt32Chunked, 1, 2, 3);
    impl_test_no_null_iter_many_chunk!(utf8_no_null_iter_many_chunk, StringChunked, "a", "b", "c");
    impl_test_no_null_iter_many_chunk!(
        bool_no_null_iter_many_chunk,
        BooleanChunked,
        true,
        true,
        false
    );
    const SKIP_ITERATOR_SIZE: usize = 10;
    macro_rules! impl_test_iter_skip {
        ($test_name:ident, $skip_values:expr, $first_val:expr, $second_val:expr, $ca_init_block:block) => {
            #[test]
            fn $test_name() {
                let a = $ca_init_block;
                let mut it = a.into_iter();
                assert_eq!(it.next(), Some($first_val));
                let mut it = it.skip($skip_values);
                assert_eq!(it.next(), Some($second_val));
                let mut it = it.skip(SKIP_ITERATOR_SIZE);
                assert_eq!(it.next(), None);
            }
        };
    }
    fn generate_utf8_vec(size: usize) -> Vec<String> {
        (0..size).map(|n| n.to_string()).collect()
    }
    fn generate_opt_utf8_vec(size: usize) -> Vec<Option<String>> {
        (0..size)
            .map(|n| {
                if n % 2 == 0 {
                    Some(n.to_string())
                } else {
                    None
                }
            })
            .collect()
    }
    impl_test_iter_skip!(utf8_iter_single_chunk_skip, 8, Some("0"), Some("9"), {
        StringChunked::from_slice("test", &generate_utf8_vec(SKIP_ITERATOR_SIZE))
    });
    impl_test_iter_skip!(
        utf8_iter_single_chunk_null_check_skip,
        8,
        Some("0"),
        None,
        { StringChunked::new("test", &generate_opt_utf8_vec(SKIP_ITERATOR_SIZE)) }
    );
    impl_test_iter_skip!(utf8_iter_many_chunk_skip, 18, Some("0"), Some("9"), {
        let mut a = StringChunked::from_slice("test", &generate_utf8_vec(SKIP_ITERATOR_SIZE));
        let a_b = StringChunked::from_slice("test", &generate_utf8_vec(SKIP_ITERATOR_SIZE));
        a.append(&a_b);
        a
    });
    impl_test_iter_skip!(utf8_iter_many_chunk_null_check_skip, 18, Some("0"), None, {
        let mut a = StringChunked::new("test", &generate_opt_utf8_vec(SKIP_ITERATOR_SIZE));
        let a_b = StringChunked::new("test", &generate_opt_utf8_vec(SKIP_ITERATOR_SIZE));
        a.append(&a_b);
        a
    });
    fn generate_boolean_vec(size: usize) -> Vec<bool> {
        (0..size).map(|n| n % 2 == 0).collect()
    }
    fn generate_opt_boolean_vec(size: usize) -> Vec<Option<bool>> {
        (0..size)
            .map(|n| if n % 3 == 0 { None } else { Some(n % 2 == 0) })
            .collect()
    }
    impl_test_iter_skip!(bool_iter_single_chunk_skip, 8, Some(true), Some(false), {
        BooleanChunked::from_slice("test", &generate_boolean_vec(SKIP_ITERATOR_SIZE))
    });
    impl_test_iter_skip!(bool_iter_single_chunk_null_check_skip, 8, None, None, {
        BooleanChunked::new("test", &generate_opt_boolean_vec(SKIP_ITERATOR_SIZE))
    });
    impl_test_iter_skip!(bool_iter_many_chunk_skip, 18, Some(true), Some(false), {
        let mut a = BooleanChunked::from_slice("test", &generate_boolean_vec(SKIP_ITERATOR_SIZE));
        let a_b = BooleanChunked::from_slice("test", &generate_boolean_vec(SKIP_ITERATOR_SIZE));
        a.append(&a_b);
        a
    });
    impl_test_iter_skip!(bool_iter_many_chunk_null_check_skip, 18, None, None, {
        let mut a = BooleanChunked::new("test", &generate_opt_boolean_vec(SKIP_ITERATOR_SIZE));
        let a_b = BooleanChunked::new("test", &generate_opt_boolean_vec(SKIP_ITERATOR_SIZE));
        a.append(&a_b);
        a
    });
}