#![feature(test)]
extern crate test;
extern crate itertools;

use itertools::Stride;

#[cfg(not(feature = "unstable"))]
use itertools::Zip;

#[cfg(feature = "unstable")]
use itertools::{Zip, ZipTrusted};

use std::iter::repeat;
use std::marker::PhantomData;

#[bench]
fn slice_iter(b: &mut test::Bencher)
{
    let xs: Vec<_> = repeat(1i32).take(20).collect();
    b.iter(|| for elt in xs.iter() {
        test::black_box(elt);
    })
}

#[bench]
fn slice_iter_rev(b: &mut test::Bencher)
{
    let xs: Vec<_> = repeat(1i32).take(20).collect();
    b.iter(|| for elt in xs.iter().rev() {
        test::black_box(elt);
    })
}

#[bench]
fn stride_iter(b: &mut test::Bencher)
{
    let xs: Vec<_> = repeat(1i32).take(20).collect();
    b.iter(|| for elt in Stride::from_slice(&xs, 1) {
        test::black_box(elt);
    })
}

#[bench]
fn stride_iter_rev(b: &mut test::Bencher)
{
    let xs: Vec<_> = repeat(1i32).take(20).collect();
    b.iter(|| for elt in Stride::from_slice(&xs, 1).rev() {
        test::black_box(elt);
    })
}

#[derive(Copy, Clone)]
struct ZipSlices<'a, T: 'a, U :'a>
{
    t_ptr: *const T,
    t_end: *const T,
    u_ptr: *const U,
    mark: PhantomData<&'a (T, U)>,
}

impl<'a, T, U> ZipSlices<'a, T, U>
{
    pub fn new(t: &'a [T], u: &'a [U]) -> Self
    {
        assert!(::std::mem::size_of::<T>() != 0);
        assert!(::std::mem::size_of::<U>() != 0);
        let minl = std::cmp::min(t.len(), u.len());
        let end_ptr = unsafe {
            t.as_ptr().offset(minl as isize)
        };
        ZipSlices {
            t_ptr: t.as_ptr(),
            t_end: end_ptr,
            u_ptr: u.as_ptr(),
            mark: PhantomData,
        }
    }
}

impl<'a, T, U> Iterator for ZipSlices<'a, T, U>
{
    type Item = (&'a T, &'a U);

    #[inline]
    fn next(&mut self) -> Option<(&'a T, &'a U)>
    {
        if self.t_ptr == self.t_end {
            return None
        }
        let t_elt: &T;
        let u_elt: &U;
        unsafe {
            t_elt = ::std::mem::transmute(self.t_ptr);
            self.t_ptr = self.t_ptr.offset(1);
            u_elt = ::std::mem::transmute(self.u_ptr);
            self.u_ptr = self.u_ptr.offset(1);
        }
        Some((t_elt, u_elt))
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>)
    {
        let len = self.t_end as usize - self.t_ptr as usize;
        (len, Some(len))
    }
}

#[bench]
fn zip_slices_default_zip(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];

    b.iter(|| for (&x, &y) in xs.iter().zip(ys.iter()) {
        test::black_box(x);
        test::black_box(y);
    })
}

#[bench]
fn zip_slices_default_zip3(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];
    let zs = vec![0; 766];

    b.iter(|| for ((&x, &y), &z) in xs.iter().zip(ys.iter()).zip(zs.iter()) {
        test::black_box(x);
        test::black_box(y);
        test::black_box(z);
    })
}

#[bench]
fn zip_slices_ziptuple(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];

    b.iter(|| for (&x, &y) in Zip::new((xs.iter(), ys.iter())) {
        test::black_box(x);
        test::black_box(y);
    })
}

#[bench]
fn zipslices(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];

    b.iter(|| for (&x, &y) in ZipSlices::new(&xs, &ys) {
        test::black_box(x);
        test::black_box(y);
    })
}

#[cfg(feature = "unstable")]
#[bench]
fn ziptrusted(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];

    b.iter(|| for (&x, &y) in ZipTrusted::new((xs.iter(), ys.iter())) {
        test::black_box(x);
        test::black_box(y);
    })
}

#[cfg(feature = "unstable")]
#[bench]
fn ziptrusted3(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];
    let zs = vec![0; 766];

    b.iter(|| for (&x, &y, &z) in ZipTrusted::new((xs.iter(), ys.iter(), zs.iter())) {
        test::black_box(x);
        test::black_box(y);
        test::black_box(z);
    })
}

#[bench]
fn zip_loop(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];

    b.iter(|| {
        let len = ::std::cmp::min(xs.len(), ys.len());
        for i in 0..len {
            unsafe {
            let x = *xs.get_unchecked(i);
            let y = *ys.get_unchecked(i);
            test::black_box(x);
            test::black_box(y);
            }
        }
    })
}

#[bench]
fn zip_loop3(b: &mut test::Bencher)
{
    let xs = vec![0; 1024];
    let ys = vec![0; 768];
    let zs = vec![0; 766];

    b.iter(|| {
        let len = ::std::cmp::min(xs.len(), ::std::cmp::min(ys.len(), zs.len()));
        for i in 0..len {
            unsafe {
            let x = *xs.get_unchecked(i);
            let y = *ys.get_unchecked(i);
            let z = *zs.get_unchecked(i);
            test::black_box(x);
            test::black_box(y);
            test::black_box(z);
            }
        }
    })
}
