Repository: bluss/rust-ndarray
Pull request #291 (2017-03-30 19:59:56)

Add .whole_chunks_mut()
- Fix bug in .whole_chunks() for IxDyn (did not check chunk dimensionality)
- Add macro for implementing NdProducer based on an array view
- Add `.whole_chunks_mut()` producer and intoiterator
- Show memory layout description in an array's debug fmt


Patch:
diff --git a/benches/bench1.rs b/benches/bench1.rs
index 3860b7e2..a8596c64 100644
--- a/benches/bench1.rs
+++ b/benches/bench1.rs
@@ -348,6 +348,45 @@ fn add_2d_zip_cutout(bench: &mut test::Bencher)
     });
 }
 
+#[bench]
+fn add_2d_cutouts_by_4(bench: &mut test::Bencher)
+{
+    let mut a = Array::<i32, _>::zeros((64 * 1, 64 * 1));
+    let b = Array::<i32, _>::zeros((64 * 1, 64 * 1));
+    let chunksz = (4, 4);
+    bench.iter(|| {
+        Zip::from(a.whole_chunks_mut(chunksz))
+            .and(b.whole_chunks(chunksz))
+            .apply(|mut a, b| a += &b);
+    });
+}
+
+#[bench]
+fn add_2d_cutouts_by_16(bench: &mut test::Bencher)
+{
+    let mut a = Array::<i32, _>::zeros((64 * 1, 64 * 1));
+    let b = Array::<i32, _>::zeros((64 * 1, 64 * 1));
+    let chunksz = (16, 16);
+    bench.iter(|| {
+        Zip::from(a.whole_chunks_mut(chunksz))
+            .and(b.whole_chunks(chunksz))
+            .apply(|mut a, b| a += &b);
+    });
+}
+
+#[bench]
+fn add_2d_cutouts_by_32(bench: &mut test::Bencher)
+{
+    let mut a = Array::<i32, _>::zeros((64 * 1, 64 * 1));
+    let b = Array::<i32, _>::zeros((64 * 1, 64 * 1));
+    let chunksz = (32, 32);
+    bench.iter(|| {
+        Zip::from(a.whole_chunks_mut(chunksz))
+            .and(b.whole_chunks(chunksz))
+            .apply(|mut a, b| a += &b);
+    });
+}
+
 #[bench]
 fn add_2d_broadcast_1_to_2(bench: &mut test::Bencher)
 {
diff --git a/benches/chunks.rs b/benches/chunks.rs
new file mode 100644
index 00000000..ee221ed5
--- /dev/null
+++ b/benches/chunks.rs
@@ -0,0 +1,35 @@
+#![feature(test)]
+
+extern crate test;
+use test::Bencher;
+
+#[macro_use(azip)]
+extern crate ndarray;
+use ndarray::prelude::*;
+use ndarray::NdProducer;
+
+#[bench]
+fn chunk2x2_sum(bench: &mut Bencher)
+{
+    let a = Array::<f32, _>::zeros((256, 256));
+    let chunksz = (2, 2);
+    let mut sum = Array::zeros(a.whole_chunks(chunksz).raw_dim());
+    bench.iter(|| {
+        azip!(ref a (a.whole_chunks(chunksz)), mut sum in {
+            *sum = a.iter().sum::<f32>();
+        });
+    });
+}
+
+#[bench]
+fn chunk2x2_scalar_sum(bench: &mut Bencher)
+{
+    let a = Array::<f32, _>::zeros((256, 256));
+    let chunksz = (2, 2);
+    let mut sum = Array::zeros(a.whole_chunks(chunksz).raw_dim());
+    bench.iter(|| {
+        azip!(ref a (a.whole_chunks(chunksz)), mut sum in {
+            *sum = a.scalar_sum();
+        });
+    });
+}
diff --git a/src/arrayformat.rs b/src/arrayformat.rs
index 3ce83ad0..81a51663 100644
--- a/src/arrayformat.rs
+++ b/src/arrayformat.rs
@@ -10,6 +10,7 @@ use super::{
     ArrayBase,
     Data,
     Dimension,
+    NdProducer,
 };
 use dimension::IntoDimension;
 
@@ -109,7 +110,8 @@ impl<'a, A: fmt::Debug, S, D: Dimension> fmt::Debug for ArrayBase<S, D>
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         // Add extra information for Debug
         try!(format_array(self, f, <_>::fmt));
-        try!(write!(f, " shape={:?}, strides={:?}", self.shape(), self.strides()));
+        try!(write!(f, " shape={:?}, strides={:?}, layout={:?}",
+                    self.shape(), self.strides(), layout=self.view().layout()));
         Ok(())
     }
 }
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index ca6e7f12..5af1bfc6 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -22,10 +22,11 @@ use super::zipsl;
 use super::ZipExt;
 use dimension::IntoDimension;
 use dimension::{axes_of, Axes, merge_axes, stride_offset};
-use iterators::whole_chunks_of;
 use iterators::{
     new_inner_iter_smaller,
     new_inner_iter_smaller_mut,
+    whole_chunks_of,
+    whole_chunks_mut_of,
 };
 
 use {
@@ -41,6 +42,7 @@ use {
     AxisIter,
     AxisIterMut,
     WholeChunks,
+    WholeChunksMut,
 };
 use stacking::stack;
 
@@ -619,15 +621,57 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
     /// It produces the whole chunks of a given n-dimensional chunk size,
     /// skipping the remainder along each dimension that doesn't fit evenly.
     ///
-    /// Iterator element is `ArrayView<A, D>`
+    /// The produced element is a `ArrayView<A, D>` with exactly the dimension
+    /// `chunk_size`.
     ///
-    /// **Panics** if any dimension of `chunk_size` is zero
+    /// **Panics** if any dimension of `chunk_size` is zero<br>
+    /// (**Panics** if `D` is `IxDyn` and `chunk_size` does not match the
+    /// number of array axes.)
     pub fn whole_chunks<E>(&self, chunk_size: E) -> WholeChunks<A, D> 
         where E: IntoDimension<Dim=D>,
     {
         whole_chunks_of(self.view(), chunk_size)
     }
 
+    /// Return a whole chunks producer (and iterable).
+    ///
+    /// It produces the whole chunks of a given n-dimensional chunk size,
+    /// skipping the remainder along each dimension that doesn't fit evenly.
+    ///
+    /// The produced element is a `ArrayViewMut<A, D>` with exactly
+    /// the dimension `chunk_size`.
+    ///
+    /// **Panics** if any dimension of `chunk_size` is zero<br>
+    /// (**Panics** if `D` is `IxDyn` and `chunk_size` does not match the
+    /// number of array axes.)
+    ///
+    /// ```rust
+    /// use ndarray::Array;
+    /// use ndarray::arr2;
+    /// let mut a = Array::zeros((6, 7));
+    ///
+    /// // Fill each 2 × 2 chunk with the index of where it appeared in iteration
+    /// for (i, mut chunk) in a.whole_chunks_mut((2, 2)).into_iter().enumerate() {
+    ///     chunk.fill(i);
+    /// }
+    ///
+    /// // The resulting array is:
+    /// assert_eq!(
+    ///   a,
+    ///   arr2(&[[0, 0, 1, 1, 2, 2, 0],
+    ///          [0, 0, 1, 1, 2, 2, 0],
+    ///          [3, 3, 4, 4, 5, 5, 0],
+    ///          [3, 3, 4, 4, 5, 5, 0],
+    ///          [6, 6, 7, 7, 8, 8, 0],
+    ///          [6, 6, 7, 7, 8, 8, 0]]));
+    /// ```
+    pub fn whole_chunks_mut<E>(&mut self, chunk_size: E) -> WholeChunksMut<A, D> 
+        where E: IntoDimension<Dim=D>,
+              S: DataMut
+    {
+        whole_chunks_mut_of(self.view_mut(), chunk_size)
+    }
+
     // Return (length, stride) for diagonal
     fn diag_params(&self) -> (Ix, Ixs) {
         /* empty shape has len 1 */
diff --git a/src/impl_views.rs b/src/impl_views.rs
index 35a355ad..c26a4ef1 100644
--- a/src/impl_views.rs
+++ b/src/impl_views.rs
@@ -75,7 +75,7 @@ impl<'a, A, D> ArrayBase<ViewRepr<&'a A>, D>
         ArrayView::new_(ptr, dim, strides)
     }
 
-    /// Split the array along `axis` and return one view strictly before the
+    /// Split the array view along `axis` and return one view strictly before the
     /// split and one view after the split.
     ///
     /// **Panics** if `axis` or `index` is out of bounds.
@@ -190,7 +190,7 @@ impl<'a, A, D> ArrayBase<ViewRepr<&'a mut A>, D>
         ArrayViewMut::new_(ptr, dim, strides)
     }
 
-    /// Split the array along `axis` and return one mutable view strictly
+    /// Split the array view along `axis` and return one mutable view strictly
     /// before the split and one mutable view after the split.
     ///
     /// **Panics** if `axis` or `index` is out of bounds.
diff --git a/src/iterators/chunks.rs b/src/iterators/chunks.rs
index 3b02a2cf..04ca8043 100644
--- a/src/iterators/chunks.rs
+++ b/src/iterators/chunks.rs
@@ -1,146 +1,63 @@
 
-use std::marker::PhantomData;
-
 use imp_prelude::*;
 use IntoDimension;
-use {NdProducer, Layout, NdIndex};
-use Iter;
-
-impl<'a, A, D> NdProducer for WholeChunks<'a, A, D>
-    where D: Dimension,
-{
-    type Item = ArrayView<'a, A, D>;
-    type Elem = A;
-    type Dim = D;
+use {NdProducer, Layout};
+use ::ElementsBase;
+use ::ElementsBaseMut;
 
-    #[doc(hidden)]
-    fn raw_dim(&self) -> D {
-        self.size.clone()
+impl_ndproducer! {
+    ['a, A, D: Dimension]
+    [Clone => 'a, A, D: Clone ]
+    WholeChunks {
+        base,
+        chunk,
+        inner_strides,
     }
+    WholeChunks<'a, A, D> {
+        type Dim = D;
+        type Item = ArrayView<'a, A, D>;
 
-    #[doc(hidden)]
-    fn layout(&self) -> Layout {
-        if Dimension::is_contiguous(&self.size, &self.strides) {
-            Layout::c()
-        } else {
-            Layout::none()
+        unsafe fn item(&self, ptr) {
+            ArrayView::new_(ptr, self.chunk.clone(),
+                            self.inner_strides.clone())
         }
     }
-
-    #[doc(hidden)]
-    fn as_ptr(&self) -> *mut A {
-        self.ptr
-    }
-
-    #[doc(hidden)]
-    fn contiguous_stride(&self) -> isize {
-        let n = self.strides.ndim();
-        let s = self.strides[n - 1] as isize;
-        s
-    }
-
-    #[doc(hidden)]
-    unsafe fn as_ref(&self, p: *mut A) -> Self::Item {
-        ArrayView::from_shape_ptr(self.chunk.clone().strides(self.inner_strides.clone()), p)
-    }
-
-    #[doc(hidden)]
-    unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A {
-        self.ptr.offset(i.index_unchecked(&self.strides))
-    }
-
-    #[doc(hidden)]
-    fn stride_of(&self, axis: Axis) -> isize {
-        self.strides[axis.index()] as isize
-    }
-
-    #[doc(hidden)]
-    fn split_at(mut self, axis: Axis, index: usize) -> (Self, Self) {
-        let len = self.size[axis.index()];
-        let right_ptr = if index != len {
-            unsafe { self.ptr.offset(self.stride_of(axis) * index as isize) } 
-        } else {
-            self.ptr
-        };
-        let mut right_size = self.size.clone();
-        self.size[axis.index()] = index;
-        right_size[axis.index()] = len - index;
-        let left = WholeChunks {
-            size: self.size,
-            chunk: self.chunk.clone(),
-            strides: self.strides.clone(),
-            inner_strides: self.inner_strides.clone(),
-            ptr: self.ptr,
-            life: self.life,
-        };
-        let right = WholeChunks {
-            size: right_size,
-            chunk: self.chunk,
-            strides: self.strides,
-            inner_strides: self.inner_strides,
-            ptr: right_ptr,
-            life: self.life,
-        };
-        (left, right)
-    }
-    private_impl!{}
 }
 
+type BaseProducerRef<'a, A, D> = ArrayView<'a, A, D>;
+type BaseProducerMut<'a, A, D> = ArrayViewMut<'a, A, D>;
+
 /// Whole chunks producer and iterable.
 ///
 /// See [`.whole_chunks()`](struct.ArrayBase.html#method.whole_chunks) for more
 /// information.
-#[derive(Debug)]
+//#[derive(Debug)]
 pub struct WholeChunks<'a, A: 'a, D> {
-    size: D,
+    base: BaseProducerRef<'a, A, D>,
     chunk: D,
-    strides: D,
     inner_strides: D,
-    ptr: *mut A,
-    life: PhantomData<&'a A>,
-}
-
-impl<'a, A, D: Clone> Clone for WholeChunks<'a, A, D> {
-    fn clone(&self) -> Self {
-        WholeChunks {
-            size: self.size.clone(),
-            chunk: self.chunk.clone(),
-            strides: self.strides.clone(),
-            inner_strides: self.inner_strides.clone(),
-            ptr: self.ptr,
-            life: self.life,
-        }
-    }
 }
 
 /// **Panics** if any chunk dimension is zero<br>
-pub fn whole_chunks_of<A, D, E>(a: ArrayView<A, D>, chunk: E) -> WholeChunks<A, D>
+pub fn whole_chunks_of<A, D, E>(mut a: ArrayView<A, D>, chunk: E) -> WholeChunks<A, D>
     where D: Dimension,
           E: IntoDimension<Dim=D>,
 {
-    let mut chunk = chunk.into_dimension();
-    let mut size = a.raw_dim();
-    for (sz, ch) in size.slice_mut().iter_mut().zip(chunk.slice_mut()) {
-        assert!(*ch != 0, "Chunk size must not be zero");
-        *sz /= *ch;
-    }
-    let mut strides = a.raw_dim();
-    for (a, b) in strides.slice_mut().iter_mut().zip(a.strides()) {
-        *a = *b as Ix;
-    }
-    
-    let mut mult_strides = strides.clone();
-    for (a, &b) in mult_strides.slice_mut().iter_mut().zip(chunk.slice()) {
-        *a *= b;
+    let chunk = chunk.into_dimension();
+    ndassert!(a.ndim() == chunk.ndim(),
+              concat!("Chunk dimension {} does not match array dimension {} ",
+                      "(with array of shape {:?})"),
+             chunk.ndim(), a.ndim(), a.shape());
+    for i in 0..a.ndim() {
+        a.dim[i] /= chunk[i];
     }
+    let inner_strides = a.raw_strides();
+    a.strides *= &chunk;
 
     WholeChunks {
+        base: a,
         chunk: chunk,
-        inner_strides: strides,
-        strides: mult_strides.clone(),
-        ptr: a.as_ptr() as _,
-        size: size,
-        life: PhantomData,
+        inner_strides: inner_strides,
     }
 }
 
@@ -151,13 +68,10 @@ impl<'a, A, D> IntoIterator for WholeChunks<'a, A, D>
     type Item = <Self::IntoIter as Iterator>::Item;
     type IntoIter = WholeChunksIter<'a, A, D>;
     fn into_iter(self) -> Self::IntoIter {
-        unsafe {
-            WholeChunksIter {
-                iter: ArrayView::from_shape_ptr(
-                    self.size.strides(self.strides), self.ptr).into_iter(),
-                chunk: self.chunk,
-                inner_strides: self.inner_strides,
-            }
+        WholeChunksIter {
+            iter: self.base.into_elements_base(),
+            chunk: self.chunk,
+            inner_strides: self.inner_strides,
         }
     }
 }
@@ -167,33 +81,178 @@ impl<'a, A, D> IntoIterator for WholeChunks<'a, A, D>
 /// See [`.whole_chunks()`](struct.ArrayBase.html#method.whole_chunks) for more
 /// information.
 pub struct WholeChunksIter<'a, A: 'a, D> {
-    iter: Iter<'a, A, D>,
+    iter: ElementsBase<'a, A, D>,
     chunk: D,
     inner_strides: D,
 }
 
-impl<'a, A, D: Clone> Clone for WholeChunksIter<'a, A, D> {
-    fn clone(&self) -> Self {
-        WholeChunksIter {
-            iter: self.iter.clone(),
-            chunk: self.chunk.clone(),
-            inner_strides: self.inner_strides.clone(),
+impl_ndproducer! {
+    ['a, A, D: Dimension]
+    [Clone => ]
+    WholeChunksMut {
+        base,
+        chunk,
+        inner_strides,
+    }
+    WholeChunksMut<'a, A, D> {
+        type Dim = D;
+        type Item = ArrayViewMut<'a, A, D>;
+
+        unsafe fn item(&self, ptr) {
+            ArrayViewMut::new_(ptr,
+                               self.chunk.clone(),
+                               self.inner_strides.clone())
         }
     }
 }
 
-impl<'a, A, D> Iterator for WholeChunksIter<'a, A, D>
+/// Whole chunks producer and iterable.
+///
+/// See [`.whole_chunks_mut()`](struct.ArrayBase.html#method.whole_chunks_mut)
+/// for more information.
+//#[derive(Debug)]
+pub struct WholeChunksMut<'a, A: 'a, D> {
+    base: BaseProducerMut<'a, A, D>,
+    chunk: D,
+    inner_strides: D,
+}
+
+/// **Panics** if any chunk dimension is zero<br>
+pub fn whole_chunks_mut_of<A, D, E>(mut a: ArrayViewMut<A, D>, chunk: E)
+    -> WholeChunksMut<A, D>
+    where D: Dimension,
+          E: IntoDimension<Dim=D>,
+{
+    let chunk = chunk.into_dimension();
+    ndassert!(a.ndim() == chunk.ndim(),
+              concat!("Chunk dimension {} does not match array dimension {} ",
+                      "(with array of shape {:?})"),
+             chunk.ndim(), a.ndim(), a.shape());
+    for i in 0..a.ndim() {
+        a.dim[i] /= chunk[i];
+    }
+    let inner_strides = a.raw_strides();
+    a.strides *= &chunk;
+
+    WholeChunksMut {
+        base: a,
+        chunk: chunk,
+        inner_strides: inner_strides,
+    }
+}
+
+impl<'a, A, D> IntoIterator for WholeChunksMut<'a, A, D>
     where D: Dimension,
+          A: 'a,
 {
-    type Item = ArrayView<'a, A, D>;
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next().map(|elt| {
+    type Item = <Self::IntoIter as Iterator>::Item;
+    type IntoIter = WholeChunksIterMut<'a, A, D>;
+    fn into_iter(self) -> Self::IntoIter {
+        WholeChunksIterMut {
+            iter: self.base.into_elements_base(),
+            chunk: self.chunk,
+            inner_strides: self.inner_strides,
+        }
+    }
+}
+
+macro_rules! impl_iterator {
+    (
+    [$($typarm:tt)*]
+    [Clone => $($cloneparm:tt)*]
+     $typename:ident {
+         $base:ident,
+         $(
+             $fieldname:ident,
+         )*
+     }
+     $fulltype:ty {
+        type Item = $ity:ty;
+
+        fn item(&mut $self_:ident, $elt:pat) {
+            $refexpr:expr
+        }
+    }) => { 
+         expand_if!(@nonempty [$($cloneparm)*] 
+
+            impl<$($cloneparm)*> Clone for $fulltype {
+                fn clone(&self) -> Self {
+                    $typename {
+                        $base: self.$base.clone(),
+                        $(
+                            $fieldname: self.$fieldname.clone(),
+                        )*
+                    }
+                }
+            }
+
+         );
+        impl<$($typarm)*> Iterator for $fulltype {
+            type Item = $ity;
+
+            fn next(&mut $self_) -> Option<Self::Item> {
+                $self_.$base.next().map(|$elt| {
+                    $refexpr
+                })
+            }
+
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                self.$base.size_hint()
+            }
+        }
+    }
+}
+
+impl_iterator!{
+    ['a, A, D: Dimension]
+    [Clone => 'a, A, D: Clone]
+    WholeChunksIter {
+        iter,
+        chunk,
+        inner_strides,
+    }
+    WholeChunksIter<'a, A, D> {
+        type Item = ArrayView<'a, A, D>;
+
+        fn item(&mut self, elt) {
+            unsafe {
+                ArrayView::new_(
+                    elt,
+                    self.chunk.clone(),
+                    self.inner_strides.clone())
+            }
+        }
+    }
+}
+
+impl_iterator!{
+    ['a, A, D: Dimension]
+    [Clone => ]
+    WholeChunksIterMut {
+        iter,
+        chunk,
+        inner_strides,
+    }
+    WholeChunksIterMut<'a, A, D> {
+        type Item = ArrayViewMut<'a, A, D>;
+
+        fn item(&mut self, elt) {
             unsafe {
-                ArrayView::from_shape_ptr(
-                    self.chunk.clone()
-                        .strides(self.inner_strides.clone()),
-                    elt)
+                ArrayViewMut::new_(
+                    elt,
+                    self.chunk.clone(),
+                    self.inner_strides.clone())
             }
-        })
+        }
     }
 }
+
+/// Whole chunks iterator.
+///
+/// See [`.whole_chunks_mut()`](struct.ArrayBase.html#method.whole_chunks_mut)
+/// for more information.
+pub struct WholeChunksIterMut<'a, A: 'a, D> {
+    iter: ElementsBaseMut<'a, A, D>,
+    chunk: D,
+    inner_strides: D,
+}
diff --git a/src/iterators/macros.rs b/src/iterators/macros.rs
new file mode 100644
index 00000000..bcc5bb4f
--- /dev/null
+++ b/src/iterators/macros.rs
@@ -0,0 +1,115 @@
+
+// Send and Sync
+// All the iterators are thread safe the same way the slice's iterator are
+
+// read-only iterators use Sync => Send rules, same as `std::slice::Iter`.
+macro_rules! send_sync_read_only {
+    ($name:ident) => {
+        unsafe impl<'a, A, D> Send for $name<'a, A, D> where A: Sync, D: Send { }
+        unsafe impl<'a, A, D> Sync for $name<'a, A, D> where A: Sync, D: Sync { }
+    }
+}
+
+// read-write iterators use Send => Send rules, same as `std::slice::IterMut`.
+macro_rules! send_sync_read_write {
+    ($name:ident) => {
+        unsafe impl<'a, A, D> Send for $name<'a, A, D> where A: Send, D: Send { }
+        unsafe impl<'a, A, D> Sync for $name<'a, A, D> where A: Sync, D: Sync { }
+    }
+}
+
+macro_rules! impl_ndproducer {
+    (
+    [$($typarm:tt)*]
+    [Clone => $($cloneparm:tt)*]
+     $typename:ident {
+         $base:ident,
+         $(
+             $fieldname:ident,
+         )*
+     }
+     $fulltype:ty {
+        $(
+            type $atyn:ident = $atyv:ty;
+        )*
+
+        unsafe fn item(&$self_:ident, $ptr:pat) {
+            $refexpr:expr
+        }
+    }) => { 
+impl<$($typarm)*> NdProducer for $fulltype {
+    type Elem = A;
+    $(
+        type $atyn = $atyv;
+    )*
+
+    #[doc(hidden)]
+    fn raw_dim(&self) -> D {
+        self.$base.raw_dim()
+    }
+
+    #[doc(hidden)]
+    fn layout(&self) -> Layout {
+        self.$base.layout()
+    }
+
+    #[doc(hidden)]
+    fn as_ptr(&self) -> *mut A {
+        self.$base.as_ptr() as *mut _
+    }
+
+    #[doc(hidden)]
+    fn contiguous_stride(&self) -> isize {
+        self.$base.contiguous_stride()
+    }
+
+    #[doc(hidden)]
+    unsafe fn as_ref(&$self_, $ptr: *mut A) -> Self::Item {
+        $refexpr
+    }
+
+    #[doc(hidden)]
+    unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A {
+        self.$base.uget_ptr(i)
+    }
+
+    #[doc(hidden)]
+    fn stride_of(&self, axis: Axis) -> isize {
+        self.$base.stride_of(axis)
+    }
+
+    #[doc(hidden)]
+    fn split_at(self, axis: Axis, index: usize) -> (Self, Self) {
+        let (a, b) = self.$base.split_at(axis, index);
+        ($typename {
+            $base: a,
+            $(
+            $fieldname: self.$fieldname.clone(),
+            )*
+        },
+        $typename {
+            $base: b,
+            $(
+            $fieldname: self.$fieldname,
+            )*
+        })
+    }
+    private_impl!{}
+}
+
+expand_if!(@nonempty [$($cloneparm)*] 
+    impl<$($cloneparm)*> Clone for $fulltype {
+        fn clone(&self) -> Self {
+            $typename {
+                $base: self.base.clone(),
+                $(
+                $fieldname: self.$fieldname.clone(),
+                )*
+            }
+        }
+    }
+);
+
+    }
+}
+
diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs
index e363219b..9208f36b 100644
--- a/src/iterators/mod.rs
+++ b/src/iterators/mod.rs
@@ -7,6 +7,7 @@
 // except according to those terms.
 
 
+#[macro_use] mod macros;
 mod chunks;
 
 use std::marker::PhantomData;
@@ -26,7 +27,14 @@ use super::{
     NdProducer,
 };
 
-pub use self::chunks::{WholeChunks, WholeChunksIter, whole_chunks_of};
+pub use self::chunks::{
+    WholeChunks,
+    WholeChunksIter,
+    whole_chunks_of,
+    WholeChunksMut,
+    WholeChunksIterMut,
+    whole_chunks_mut_of,
+};
 
 /// Base for array iterators
 ///
@@ -154,23 +162,27 @@ impl<'a, A> Baseiter<'a, A, Ix1> {
     }
 }
 
-impl<'a, A, D: Clone> Clone for Baseiter<'a, A, D> {
-    fn clone(&self) -> Baseiter<'a, A, D> {
-        Baseiter {
-            ptr: self.ptr,
-            dim: self.dim.clone(),
-            strides: self.strides.clone(),
-            index: self.index.clone(),
-            life: self.life,
+clone_bounds!(
+    ['a, A, D: Clone]
+    Baseiter['a, A, D] {
+        @copy {
+            ptr,
+            life,
         }
+        dim,
+        strides,
+        index,
     }
-}
+);
 
-impl<'a, A, D: Clone> Clone for ElementsBase<'a, A, D> {
-    fn clone(&self) -> ElementsBase<'a, A, D> {
-        ElementsBase { inner: self.inner.clone() }
+clone_bounds!(
+    ['a, A, D: Clone]
+    ElementsBase['a, A, D] {
+        @copy {
+        }
+        inner,
     }
-}
+);
 
 impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D> {
     type Item = &'a A;
@@ -226,19 +238,14 @@ macro_rules! either_mut {
     )
 }
 
-
-impl<'a, A, D: Clone> Clone for Iter<'a, A, D> {
-    fn clone(&self) -> Iter<'a, A, D> {
-        Iter {
-            inner: match self.inner {
-                ElementsRepr::Slice(ref iter) => ElementsRepr::Slice(iter.clone()),
-                ElementsRepr::Counted(ref iter) => {
-                    ElementsRepr::Counted(iter.clone())
-                }
-            },
+clone_bounds!(
+    ['a, A, D: Clone]
+    Iter['a, A, D] {
+        @copy {
         }
+        inner,
     }
-}
+);
 
 impl<'a, A, D: Dimension> Iterator for Iter<'a, A, D> {
     type Item = &'a A;
@@ -579,6 +586,20 @@ pub struct OuterIterCore<A, D> {
     ptr: *mut A,
 }
 
+clone_bounds!(
+    [A, D: Clone]
+    OuterIterCore[A, D] {
+        @copy {
+            index,
+            len,
+            stride,
+            ptr,
+        }
+        inner_dim,
+        inner_strides,
+    }
+);
+
 fn new_outer_core<A, S, D>(v: ArrayBase<S, D>, axis: usize)
     -> OuterIterCore<A, D::Smaller>
     where D: RemoveAxis,
@@ -660,6 +681,17 @@ pub struct AxisIter<'a, A: 'a, D> {
     life: PhantomData<&'a A>,
 }
 
+clone_bounds!(
+    ['a, A, D: Clone]
+    AxisIter['a, A, D] {
+        @copy {
+            life,
+        }
+        iter,
+    }
+);
+
+
 macro_rules! outer_iter_split_at_impl {
     ($iter: ident) => (
         impl<'a, A, D> $iter<'a, A, D>
@@ -708,24 +740,6 @@ macro_rules! outer_iter_split_at_impl {
 
 outer_iter_split_at_impl!(AxisIter);
 
-impl<'a, A, D> Clone for AxisIter<'a, A, D>
-    where D: Dimension
-{
-    fn clone(&self) -> Self {
-        AxisIter {
-            iter: OuterIterCore {
-                index: self.iter.index,
-                len: self.iter.len,
-                stride: self.iter.stride,
-                inner_dim: self.iter.inner_dim.clone(),
-                inner_strides: self.iter.inner_strides.clone(),
-                ptr: self.iter.ptr,
-            },
-            life: self.life,
-        }
-    }
-}
-
 impl<'a, A, D> Iterator for AxisIter<'a, A, D>
     where D: Dimension
 {
@@ -973,6 +987,18 @@ pub struct AxisChunksIter<'a, A: 'a, D> {
     life: PhantomData<&'a A>,
 }
 
+clone_bounds!(
+    ['a, A, D: Clone]
+    AxisChunksIter['a, A, D] {
+        @copy {
+            life,
+            last_ptr,
+        }
+        iter,
+        last_dim,
+    }
+);
+
 fn chunk_iter_parts<A, D: Dimension>(v: ArrayView<A, D>, axis: usize, size: usize)
     -> (OuterIterCore<A, D>, *mut A, D)
 {
@@ -986,7 +1012,7 @@ fn chunk_iter_parts<A, D: Dimension>(v: ArrayView<A, D>, axis: usize, size: usiz
     let mut inner_dim = v.dim.clone();
     inner_dim.slice_mut()[axis] = size;
 
-    let mut last_dim = v.dim.clone();
+    let mut last_dim = v.dim;
     last_dim.slice_mut()[axis] = if rem == 0 { size } else { rem };
 
     let last_ptr = if rem != 0 {
@@ -1002,7 +1028,7 @@ fn chunk_iter_parts<A, D: Dimension>(v: ArrayView<A, D>, axis: usize, size: usiz
         len: shape,
         stride: stride,
         inner_dim: inner_dim,
-        inner_strides: v.strides.clone(),
+        inner_strides: v.strides,
         ptr: v.ptr,
     };
 
@@ -1013,7 +1039,7 @@ pub fn new_chunk_iter<A, D>(v: ArrayView<A, D>, axis: usize, size: usize)
     -> AxisChunksIter<A, D>
     where D: Dimension
 {
-    let (iter, last_ptr, last_dim) = chunk_iter_parts(v.view(), axis, size);
+    let (iter, last_ptr, last_dim) = chunk_iter_parts(v, axis, size);
 
     AxisChunksIter {
         iter: iter,
@@ -1102,7 +1128,7 @@ pub fn new_chunk_iter_mut<A, D>(v: ArrayViewMut<A, D>, axis: usize, size: usize)
     -> AxisChunksIterMut<A, D>
     where D: Dimension
 {
-    let (iter, last_ptr, last_dim) = chunk_iter_parts(v.view(), axis, size);
+    let (iter, last_ptr, last_dim) = chunk_iter_parts(v.into_view(), axis, size);
 
     AxisChunksIterMut {
         iter: iter,
@@ -1116,32 +1142,11 @@ chunk_iter_impl!(AxisChunksIter, ArrayView);
 chunk_iter_impl!(AxisChunksIterMut, ArrayViewMut);
 
 
-// Send and Sync
-// All the iterators are thread safe the same way the slice's iterator are
-
-// read-only iterators use Sync => Send rules, same as `std::slice::Iter`.
-macro_rules! send_sync_read_only {
-    ($name:ident) => {
-        unsafe impl<'a, A, D> Send for $name<'a, A, D> where A: Sync, D: Send { }
-        unsafe impl<'a, A, D> Sync for $name<'a, A, D> where A: Sync, D: Sync { }
-    }
-}
-
-// read-write iterators use Send => Send rules, same as `std::slice::IterMut`.
-macro_rules! send_sync_read_write {
-    ($name:ident) => {
-        unsafe impl<'a, A, D> Send for $name<'a, A, D> where A: Send, D: Send { }
-        unsafe impl<'a, A, D> Sync for $name<'a, A, D> where A: Sync, D: Sync { }
-    }
-}
-
 send_sync_read_only!(Iter);
 send_sync_read_only!(IndexedIter);
 send_sync_read_only!(InnerIter);
 send_sync_read_only!(AxisIter);
 send_sync_read_only!(AxisChunksIter);
-send_sync_read_only!(WholeChunks);
-send_sync_read_only!(WholeChunksIter);
 
 send_sync_read_write!(IterMut);
 send_sync_read_write!(IndexedIterMut);
diff --git a/src/lib.rs b/src/lib.rs
index db153765..980c9a01 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -109,6 +109,8 @@ pub use iterators::{
     AxisChunksIterMut,
     WholeChunks,
     WholeChunksIter,
+    WholeChunksMut,
+    WholeChunksIterMut,
 };
 
 pub use arraytraits::AsArray;
@@ -588,6 +590,9 @@ impl<A, S, D> ArrayBase<S, D>
         }
     }
 
+    fn raw_strides(&self) -> D {
+        self.strides.clone()
+    }
 
     /// Apply closure `f` to each element in the array, in whatever
     /// order is the fastest to visit.
@@ -702,6 +707,13 @@ impl<'a, A, D> ArrayBase<ViewRepr<&'a mut A>, D>
         }
     }
 
+    // Convert into a read-only view
+    fn into_view(self) -> ArrayView<'a, A, D> {
+        unsafe {
+            ArrayView::new_(self.ptr, self.dim, self.strides)
+        }
+    }
+
     #[inline]
     fn into_base_iter(self) -> Baseiter<'a, A, D> {
         unsafe {
@@ -801,6 +813,7 @@ trait ZipExt : Iterator {
 
 impl<I> ZipExt for I where I: Iterator { }
 
+#[derive(Clone)]
 enum ElementsRepr<S, C> {
     Slice(S),
     Counted(C),
diff --git a/src/macro_utils.rs b/src/macro_utils.rs
index 86eecbae..70f74683 100644
--- a/src/macro_utils.rs
+++ b/src/macro_utils.rs
@@ -13,6 +13,28 @@ macro_rules! copy_and_clone {
     }
 }
 
+macro_rules! clone_bounds {
+    ([$($parmbounds:tt)*] $typename:ident [$($parm:tt)*] {
+        @copy {
+            $($copyfield:ident,)*
+        }
+        $($field:ident,)*
+    }) => {
+        impl<$($parmbounds)*> Clone for $typename<$($parm)*> {
+            fn clone(&self) -> Self {
+                $typename {
+                $(
+                    $copyfield: self.$copyfield,
+                )*
+                $(
+                    $field: self.$field.clone(),
+                )*
+                }
+            }
+        }
+    };
+}
+
 /// This assertion is always enabled but only verbose (formatting when
 /// debug assertions are enabled).
 #[cfg(debug_assertions)]
@@ -24,3 +46,12 @@ macro_rules! ndassert {
 macro_rules! ndassert {
     ($e:expr, $($_ignore:tt)*) => { assert!($e) }
 }
+
+macro_rules! expand_if {
+    (@bool [true] $($body:tt)*) => { $($body)* };
+    (@bool [false] $($body:tt)*) => { };
+    (@nonempty [$($if_present:tt)+] $($body:tt)*) => {
+        $($body)*
+    };
+    (@nonempty [] $($body:tt)*) => { };
+}
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index 7f831089..12a833bc 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -676,13 +676,8 @@ zipt_impl!{
     [A B C D E F][ a b c d e f],
 }
 
-macro_rules! macroif {
-    (true $($x:tt)*) => { $($x)* };
-    (false $($x:tt)*) => {  };
-}
-
 macro_rules! map_impl {
-    ($([$choice:ident $($p:ident)*],)+) => {
+    ($([$notlast:ident $($p:ident)*],)+) => {
         $(
         #[allow(non_snake_case)]
         impl<D: Dimension, $($p: NdProducer<Dim=D>),*> Zip<($($p,)*), D> {
@@ -712,7 +707,7 @@ macro_rules! map_impl {
                 })
             }
 
-            macroif!{ $choice
+            expand_if!(@bool [$notlast]
 
             /// Include the producer `p` in the Zip.
             ///
@@ -750,8 +745,7 @@ macro_rules! map_impl {
                     dimension: self.dimension,
                 }
             }
-
-            }
+            );
 
             /// Split the `Zip` evenly in two.
             ///


Test patch:
diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs
index d4865edf..965837fa 100644
--- a/tests/iterator_chunks.rs
+++ b/tests/iterator_chunks.rs
@@ -1,5 +1,5 @@
 
-#[macro_use(s)]
+#[macro_use(array, s)]
 extern crate ndarray;
 
 use ndarray::prelude::*;
@@ -35,3 +35,55 @@ fn chunks() {
     assert_eq!(c.raw_dim().size(), 0);
     assert_eq!(c.into_iter().count(), 0);
 }
+
+#[should_panic]
+#[test]
+fn chunks_different_size_1() {
+    let a = Array::<f32, _>::zeros(vec![2, 3]);
+    a.whole_chunks(vec![2]);
+}
+
+#[test]
+fn chunks_ok_size() {
+    let mut a = Array::<f32, _>::zeros(vec![2, 3]);
+    a.fill(1.);
+    let mut c = 0;
+    for elt in a.whole_chunks(vec![2, 1]) {
+        assert!(elt.iter().all(|&x| x == 1.));
+        assert_eq!(elt.dim(), vec![2, 1]);
+        c += 1;
+    }
+    assert_eq!(c, 3);
+}
+
+#[should_panic]
+#[test]
+fn chunks_different_size_2() {
+    let a = Array::<f32, _>::zeros(vec![2, 3]);
+    a.whole_chunks(vec![2, 3, 4]);
+}
+
+#[test]
+fn chunks_mut() {
+    let mut a = Array::zeros((7, 8));
+    for (i, mut chunk) in a.whole_chunks_mut((2, 3)).into_iter().enumerate() {
+        chunk.fill(i);
+    }
+    println!("{:?}", a);
+    let ans = array![
+       [0, 0, 0, 1, 1, 1, 0, 0],
+       [0, 0, 0, 1, 1, 1, 0, 0],
+       [2, 2, 2, 3, 3, 3, 0, 0],
+       [2, 2, 2, 3, 3, 3, 0, 0],
+       [4, 4, 4, 5, 5, 5, 0, 0],
+       [4, 4, 4, 5, 5, 5, 0, 0],
+       [0, 0, 0, 0, 0, 0, 0, 0]];
+    assert_eq!(a, ans);
+}
+
+#[should_panic]
+#[test]
+fn chunks_different_size_3() {
+    let mut a = Array::<f32, _>::zeros(vec![2, 3]);
+    a.whole_chunks_mut(vec![2, 3, 4]);
+}