diff --git a/benches/bench1.rs b/benches/bench1.rs index 3860b7e21..a8596c648 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::::zeros((64 * 1, 64 * 1)); + let b = Array::::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::::zeros((64 * 1, 64 * 1)); + let b = Array::::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::::zeros((64 * 1, 64 * 1)); + let b = Array::::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 000000000..ee221ed5d --- /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::::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::(); + }); + }); +} + +#[bench] +fn chunk2x2_scalar_sum(bench: &mut Bencher) +{ + let a = Array::::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 3ce83ad05..81a516630 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 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 ca6e7f120..5af1bfc61 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 ArrayBase where S: Data, 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` + /// The produced element is a `ArrayView` with exactly the dimension + /// `chunk_size`. /// - /// **Panics** if any dimension of `chunk_size` is zero + /// **Panics** if any dimension of `chunk_size` is zero
+ /// (**Panics** if `D` is `IxDyn` and `chunk_size` does not match the + /// number of array axes.) pub fn whole_chunks(&self, chunk_size: E) -> WholeChunks where E: IntoDimension, { 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` with exactly + /// the dimension `chunk_size`. + /// + /// **Panics** if any dimension of `chunk_size` is zero
+ /// (**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(&mut self, chunk_size: E) -> WholeChunksMut + where E: IntoDimension, + 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 35a355adb..c26a4ef1b 100644 --- a/src/impl_views.rs +++ b/src/impl_views.rs @@ -75,7 +75,7 @@ impl<'a, A, D> ArrayBase, 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, 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 3b02a2cfa..04ca80431 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
-pub fn whole_chunks_of(a: ArrayView, chunk: E) -> WholeChunks +pub fn whole_chunks_of(mut a: ArrayView, chunk: E) -> WholeChunks where D: Dimension, E: IntoDimension, { - 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 = ::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
+pub fn whole_chunks_mut_of(mut a: ArrayViewMut, chunk: E) + -> WholeChunksMut + where D: Dimension, + E: IntoDimension, +{ + 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.iter.next().map(|elt| { + type Item = ::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_.$base.next().map(|$elt| { + $refexpr + }) + } + + fn size_hint(&self) -> (usize, Option) { + 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 000000000..bcc5bb4f2 --- /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 e363219b3..9208f36b6 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 { ptr: *mut A, } +clone_bounds!( + [A, D: Clone] + OuterIterCore[A, D] { + @copy { + index, + len, + stride, + ptr, + } + inner_dim, + inner_strides, + } +); + fn new_outer_core(v: ArrayBase, axis: usize) -> OuterIterCore 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(v: ArrayView, axis: usize, size: usize) -> (OuterIterCore, *mut A, D) { @@ -986,7 +1012,7 @@ fn chunk_iter_parts(v: ArrayView, 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(v: ArrayView, 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(v: ArrayView, axis: usize, size: usize) -> AxisChunksIter 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(v: ArrayViewMut, axis: usize, size: usize) -> AxisChunksIterMut 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 db1537654..980c9a014 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 ArrayBase } } + 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, 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 ZipExt for I where I: Iterator { } +#[derive(Clone)] enum ElementsRepr { Slice(S), Counted(C), diff --git a/src/macro_utils.rs b/src/macro_utils.rs index 86eecbaeb..70f746832 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 7f8310893..12a833bc4 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),*> 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. /// diff --git a/tests/iterator_chunks.rs b/tests/iterator_chunks.rs index d4865edf4..965837fa4 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::::zeros(vec![2, 3]); + a.whole_chunks(vec![2]); +} + +#[test] +fn chunks_ok_size() { + let mut a = Array::::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::::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::::zeros(vec![2, 3]); + a.whole_chunks_mut(vec![2, 3, 4]); +}