From 0aa775eb0285f5510b8b0620f72b781a0038158a Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 14:07:51 -0400 Subject: [PATCH 01/11] Remove CI for Rust 1.18.0 `ndarray` guarantees support for only the latest stable version of Rust, so removing Rust 1.18.0 from continuous integration testing allows us to take advantage of new features. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f258a3e46..e7a3e26a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,6 @@ sudo: required dist: trusty matrix: include: - - rust: 1.18.0 - env: - - FEATURES='test' - rust: stable env: - FEATURES='test' From e93845ee12a1703aa8fbceed0f3d6da1dd36c7d8 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 14:13:42 -0400 Subject: [PATCH 02/11] Rename .isubview() -> .subview_inplace() --- src/impl_methods.rs | 6 +++--- src/lib.rs | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index e3f3e2dea..659d40387 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -429,7 +429,7 @@ impl ArrayBase where S: Data, D: Dimension /// and select the subview of `index` along that axis. /// /// **Panics** if `index` is past the length of the axis. - pub fn isubview(&mut self, axis: Axis, index: Ix) { + pub fn subview_inplace(&mut self, axis: Axis, index: Ix) { dimension::do_sub(&mut self.dim, &mut self.ptr, &self.strides, axis.index(), index) } @@ -441,7 +441,7 @@ impl ArrayBase where S: Data, D: Dimension pub fn into_subview(mut self, axis: Axis, index: Ix) -> ArrayBase where D: RemoveAxis, { - self.isubview(axis, index); + self.subview_inplace(axis, index); self.remove_axis(axis) } @@ -472,7 +472,7 @@ impl ArrayBase where S: Data, D: Dimension { let mut subs = vec![self.view(); indices.len()]; for (&i, sub) in zip(indices, &mut subs[..]) { - sub.isubview(axis, i); + sub.subview_inplace(axis, i); } if subs.is_empty() { let mut dim = self.raw_dim(); diff --git a/src/lib.rs b/src/lib.rs index 484a8840c..4f9f766d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -477,12 +477,17 @@ pub type Ixs = isize; /// /// ## Subviews /// -/// Subview methods allow you to restrict the array view while removing -/// one axis from the array. Subview methods include `.subview()`, -/// `.isubview()`, `.subview_mut()`. +/// Subview methods allow you to restrict the array view while removing one +/// axis from the array. Subview methods include [`.subview()`], +/// [`.subview_mut()`], [`.into_subview()`], and [`.subview_inplace()`]. /// /// Subview takes two arguments: `axis` and `index`. /// +/// [`.subview()`]: #method.subview +/// [`.subview_mut()`]: #method.subview_mut +/// [`.into_subview()`]: #method.into_subview +/// [`.subview_inplace()`]: #method.subview_inplace +/// /// ``` /// use ndarray::{arr3, aview2, Axis}; /// @@ -516,8 +521,8 @@ pub type Ixs = isize; /// [ 7, 10]])); /// ``` /// -/// `.isubview()` modifies the view in the same way as `subview()`, but -/// since it is *in place*, it cannot remove the collapsed axis. It becomes +/// [`.subview_inplace()`] modifies the view in the same way as [`.subview()`], +/// but since it is *in place*, it cannot remove the collapsed axis. It becomes /// an axis of length 1. /// /// `.outer_iter()` is an iterator of every subview along the zeroth (outer) From 68d0efd9b3517d334ba772b5d37295397b4f3f9b Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 14:24:54 -0400 Subject: [PATCH 03/11] Rename .islice() -> .slice_inplace() --- examples/axis_ops.rs | 4 ++-- serialization-tests/tests/serialize.rs | 8 ++++---- src/impl_methods.rs | 6 +++--- src/lib.rs | 8 ++++++-- tests/array.rs | 16 ++++++++-------- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/examples/axis_ops.rs b/examples/axis_ops.rs index a5632e7a8..452dbba88 100644 --- a/examples/axis_ops.rs +++ b/examples/axis_ops.rs @@ -47,7 +47,7 @@ fn main() { } a.swap_axes(0, 1); a.swap_axes(0, 2); - a.islice(s![.., ..;-1, ..]); + a.slice_inplace(s![.., ..;-1, ..]); regularize(&mut a).ok(); let mut b = Array::::zeros((2, 3, 4)); @@ -64,6 +64,6 @@ fn main() { for (i, elt) in (0..).zip(&mut a) { *elt = i; } - a.islice(s![..;-1, ..;2, ..]); + a.slice_inplace(s![..;-1, ..;2, ..]); regularize(&mut a).ok(); } diff --git a/serialization-tests/tests/serialize.rs b/serialization-tests/tests/serialize.rs index 0355981eb..99d9f8191 100644 --- a/serialization-tests/tests/serialize.rs +++ b/serialization-tests/tests/serialize.rs @@ -53,7 +53,7 @@ fn serial_many_dim() { // Test a sliced array. let mut a = RcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.islice(s![..;-1, .., .., ..2]); + a.slice_inplace(s![..;-1, .., .., ..2]); let serial = json::encode(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); let res = json::decode::>(&serial); @@ -114,7 +114,7 @@ fn serial_many_dim_serde() { // Test a sliced array. let mut a = RcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.islice(s![..;-1, .., .., ..2]); + a.slice_inplace(s![..;-1, .., .., ..2]); let serial = serde_json::to_string(&a).unwrap(); println!("Encode {:?} => {:?}", a, serial); let res = serde_json::from_str::>(&serial); @@ -221,7 +221,7 @@ fn serial_many_dim_serde_msgpack() { // Test a sliced array. let mut a = RcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.islice(s![..;-1, .., .., ..2]); + a.slice_inplace(s![..;-1, .., .., ..2]); let mut buf = Vec::new(); serde::Serialize::serialize(&a, &mut rmp_serde::Serializer::new(&mut buf)).ok().unwrap(); @@ -273,7 +273,7 @@ fn serial_many_dim_ron() { // Test a sliced array. let mut a = RcArray::linspace(0., 31., 32).reshape((2, 2, 2, 4)); - a.islice(s![..;-1, .., .., ..2]); + a.slice_inplace(s![..;-1, .., .., ..2]); let a_s = ron_serialize(&a).unwrap(); diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 659d40387..82eae2f8a 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -217,7 +217,7 @@ impl ArrayBase where S: Data, D: Dimension /// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.) pub fn slice(&self, indexes: &D::SliceArg) -> ArrayView { let mut arr = self.view(); - arr.islice(indexes); + arr.slice_inplace(indexes); arr } @@ -233,7 +233,7 @@ impl ArrayBase where S: Data, D: Dimension where S: DataMut { let mut arr = self.view_mut(); - arr.islice(indexes); + arr.slice_inplace(indexes); arr } @@ -245,7 +245,7 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Panics** if an index is out of bounds or stride is zero.
/// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.) - pub fn islice(&mut self, indexes: &D::SliceArg) { + pub fn slice_inplace(&mut self, indexes: &D::SliceArg) { let offset = D::do_slices(&mut self.dim, &mut self.strides, indexes); unsafe { self.ptr = self.ptr.offset(offset); diff --git a/src/lib.rs b/src/lib.rs index 4f9f766d6..9677a36d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -421,8 +421,8 @@ pub type Ixs = isize; /// ## Slicing /// /// You can use slicing to create a view of a subset of the data in -/// the array. Slicing methods include `.slice()`, `.islice()`, -/// `.slice_mut()`. +/// the array. Slicing methods include [`.slice()`], [`.slice_mut()`], +/// and [`.slice_inplace()`]. /// /// The slicing argument can be passed using the macro [`s![]`](macro.s!.html), /// which will be used in all examples. (The explicit form is a reference @@ -430,6 +430,10 @@ pub type Ixs = isize; /// /// [`Si`]: struct.Si.html /// +/// [`.slice()`]: #method.slice +/// [`.slice_mut()`]: #method.slice_mut +/// [`.slice_inplace()`]: #method.slice_inplace +/// /// ``` /// // import the s![] macro /// #[macro_use(s)] diff --git a/tests/array.rs b/tests/array.rs index 740b6f7e0..f60d86bb0 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -203,7 +203,7 @@ fn test_cow() assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); let mut rev = mat.reshape(4); - rev.islice(&[Si(0, None, -1)]); + rev.slice_inplace(&[Si(0, None, -1)]); assert_eq!(rev[0], 4); assert_eq!(rev[1], 3); assert_eq!(rev[2], 2); @@ -228,7 +228,7 @@ fn test_cow_shrink() // mutation shrinks the array and gives it different strides // let mut mat = RcArray::zeros((2, 3)); - //mat.islice(s![.., ..;2]); + //mat.slice_inplace(s![.., ..;2]); mat[[0, 0]] = 1; let n = mat.clone(); mat[[0, 1]] = 2; @@ -243,7 +243,7 @@ fn test_cow_shrink() assert_eq!(n.get((0, 1)), Some(&0)); // small has non-C strides this way let mut small = mat.reshape(6); - small.islice(s![4..;-1]); + small.slice_inplace(s![4..;-1]); assert_eq!(small[0], 6); assert_eq!(small[1], 5); let before = small.clone(); @@ -367,7 +367,7 @@ fn assign() let mut a = arr2(&[[1, 2], [3, 4]]); { let mut v = a.view_mut(); - v.islice(&[Si(0, Some(1), 1), S]); + v.slice_inplace(&[Si(0, Some(1), 1), S]); v.fill(0); } assert_eq!(a, arr2(&[[0, 0], [3, 4]])); @@ -1073,7 +1073,7 @@ fn to_owned_memory_order() { fn to_owned_neg_stride() { let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); - c.islice(s![.., ..;-1]); + c.slice_inplace(s![.., ..;-1]); let co = c.to_owned(); assert_eq!(c, co); } @@ -1238,10 +1238,10 @@ fn test_to_vec() { [7, 8, 9], [10,11,12]]); - a.islice(s![..;-1, ..]); + a.slice_inplace(s![..;-1, ..]); assert_eq!(a.row(3).to_vec(), vec![1, 2, 3]); assert_eq!(a.column(2).to_vec(), vec![12, 9, 6, 3]); - a.islice(s![.., ..;-1]); + a.slice_inplace(s![.., ..;-1]); assert_eq!(a.row(3).to_vec(), vec![3, 2, 1]); } @@ -1257,7 +1257,7 @@ fn test_array_clone_unalias() { #[test] fn test_array_clone_same_view() { let mut a = Array::from_iter(0..9).into_shape((3, 3)).unwrap(); - a.islice(s![..;-1, ..;-1]); + a.slice_inplace(s![..;-1, ..;-1]); let b = a.clone(); assert_eq!(a, b); } From d2750d10ec4f36bba74da745353facff20979f0a Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 14:29:21 -0400 Subject: [PATCH 04/11] Rename ndarray::si module -> ndarray::slice --- src/lib.rs | 4 ++-- src/{si.rs => slice.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{si.rs => slice.rs} (100%) diff --git a/src/lib.rs b/src/lib.rs index 9677a36d5..bad645909 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,7 +105,7 @@ pub use dimension::NdIndex; pub use dimension::IxDynImpl; pub use indexes::{indices, indices_of}; pub use error::{ShapeError, ErrorKind}; -pub use si::{Si, S}; +pub use slice::{Si, S}; use iterators::Baseiter; use iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; @@ -143,7 +143,7 @@ mod free_functions; pub use free_functions::*; pub use iterators::iter; -mod si; +mod slice; mod layout; mod indexes; mod iterators; diff --git a/src/si.rs b/src/slice.rs similarity index 100% rename from src/si.rs rename to src/slice.rs From 5de94ef3133647ad20a9914ce3adb1a0cdf2b2da Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 18:34:53 -0400 Subject: [PATCH 05/11] Add .slice_axis*() --- src/dimension/dimension_trait.rs | 75 -------------------------------- src/dimension/mod.rs | 71 ++++++++++++++++++++++++++++++ src/impl_methods.rs | 67 +++++++++++++++++++++++++++- tests/array.rs | 7 +++ 4 files changed, 143 insertions(+), 77 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index bc288e178..81a39078f 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -239,69 +239,6 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + self.slice_mut()[nd - 1] = i; } - #[doc(hidden)] - /// Modify dimension, strides and return data pointer offset - /// - /// **Panics** if `slices` does not correspond to the number of axes, - /// if any stride is 0, or if any index is out of bounds. - fn do_slices(dim: &mut Self, strides: &mut Self, slices: &Self::SliceArg) -> isize { - let slices = slices.as_ref(); - let mut offset = 0; - ndassert!(slices.len() == dim.slice().len(), - "SliceArg {:?}'s length does not match dimension {:?}", - slices, dim); - for (dr, sr, &slc) in izip!(dim.slice_mut(), strides.slice_mut(), slices) { - let m = *dr; - let mi = m as Ixs; - let Si(b1, opt_e1, s1) = slc; - let e1 = opt_e1.unwrap_or(mi); - - let b1 = abs_index(mi, b1); - let mut e1 = abs_index(mi, e1); - if e1 < b1 { e1 = b1; } - - ndassert!(b1 <= m, - concat!("Slice begin {} is past end of axis of length {}", - " (for SliceArg {:?})"), - b1, m, slices); - ndassert!(e1 <= m, - concat!("Slice end {} is past end of axis of length {}", - " (for SliceArg {:?})"), - e1, m, slices); - - let m = e1 - b1; - // stride - let s = (*sr) as Ixs; - - // Data pointer offset - offset += stride_offset(b1, *sr); - // Adjust for strides - ndassert!(s1 != 0, - concat!("Slice stride must not be none", - "(for SliceArg {:?})"), - slices); - // How to implement negative strides: - // - // Increase start pointer by - // old stride * (old dim - 1) - // to put the pointer completely in the other end - if s1 < 0 { - offset += stride_offset(m - 1, *sr); - } - - let s_prim = s * s1; - - let d = m / s1.abs() as Ix; - let r = m % s1.abs() as Ix; - let m_prim = d + if r > 0 { 1 } else { 0 }; - - // Update dimension and stride coordinate - *dr = m_prim; - *sr = s_prim as Ix; - } - offset - } - #[doc(hidden)] fn is_contiguous(dim: &Self, strides: &Self) -> bool { let defaults = dim.default_strides(); @@ -398,18 +335,6 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + private_decl!{} } -// utility functions - -#[inline] -fn abs_index(len: Ixs, index: Ixs) -> Ix { - if index < 0 { - (len + index) as Ix - } else { - index as Ix - } -} - - // Dimension impls macro_rules! impl_insert_axis_array( diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 541f96429..319b6155e 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -212,6 +212,77 @@ pub fn do_sub(dims: &mut D, ptr: &mut *mut A, strides: &D, } } +/// Compute the equivalent unsigned index given the axis length and signed index. +#[inline] +fn abs_index(len: Ix, index: Ixs) -> Ix { + if index < 0 { + len - (-index as Ix) + } else { + index as Ix + } +} + +/// Modify dimension, stride and return data pointer offset +/// +/// **Panics** if stride is 0 or if any index is out of bounds. +pub fn do_slice( + dim: &mut Ix, + stride: &mut Ix, + start: Ixs, + end: Option, + step: Ixs, +) -> isize { + let mut offset = 0; + + let axis_len = *dim; + let start = abs_index(axis_len, start); + let mut end = abs_index(axis_len, end.unwrap_or(axis_len as Ixs)); + if end < start { + end = start; + } + + ndassert!( + start <= axis_len, + "Slice begin {} is past end of axis of length {}", + start, + axis_len, + ); + ndassert!( + end <= axis_len, + "Slice end {} is past end of axis of length {}", + end, + axis_len, + ); + + let m = end - start; + // stride + let s = (*stride) as Ixs; + + // Data pointer offset + offset += stride_offset(start, *stride); + // Adjust for strides + ndassert!(step != 0, "Slice stride must not be zero"); + // How to implement negative strides: + // + // Increase start pointer by + // old stride * (old dim - 1) + // to put the pointer completely in the other end + if step < 0 { + offset += stride_offset(m - 1, *stride); + } + + let s_prim = s * step; + + let d = m / step.abs() as Ix; + let r = m % step.abs() as Ix; + let m_prim = d + if r > 0 { 1 } else { 0 }; + + // Update dimension and stride coordinate + *dim = m_prim; + *stride = s_prim as Ix; + + offset +} pub fn merge_axes(dim: &mut D, strides: &mut D, take: Axis, into: Axis) -> bool where D: Dimension, diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 82eae2f8a..397275055 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -19,7 +19,7 @@ use dimension; use iterators; use error::{self, ShapeError, ErrorKind}; use dimension::IntoDimension; -use dimension::{axes_of, Axes, merge_axes, stride_offset}; +use dimension::{axes_of, Axes, do_slice, merge_axes, stride_offset}; use iterators::{ new_lanes, new_lanes_mut, @@ -31,6 +31,7 @@ use zip::Zip; use { NdIndex, + Si, }; use iter::{ AxisChunksIter, @@ -246,7 +247,69 @@ impl ArrayBase where S: Data, D: Dimension /// **Panics** if an index is out of bounds or stride is zero.
/// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.) pub fn slice_inplace(&mut self, indexes: &D::SliceArg) { - let offset = D::do_slices(&mut self.dim, &mut self.strides, indexes); + let indexes: &[Si] = indexes.as_ref(); + assert_eq!(indexes.len(), self.ndim()); + indexes + .iter() + .enumerate() + .for_each(|(axis, &Si(start, end, step))| { + self.slice_axis_inplace(Axis(axis), start, end, step) + }); + } + + /// Return a view of the array, sliced along the specified axis. + /// + /// **Panics** if an index is out of bounds or step size is zero.
+ /// **Panics** if `axis` is out of bounds. + pub fn slice_axis( + &self, + axis: Axis, + start: Ixs, + end: Option, + step: Ixs, + ) -> ArrayView { + let mut view = self.view(); + view.slice_axis_inplace(axis, start, end, step); + view + } + + /// Return a mutable view of the array, sliced along the specified axis. + /// + /// **Panics** if an index is out of bounds or step size is zero.
+ /// **Panics** if `axis` is out of bounds. + pub fn slice_axis_mut( + &mut self, + axis: Axis, + start: Ixs, + end: Option, + step: Ixs, + ) -> ArrayViewMut + where + S: DataMut, + { + let mut view_mut = self.view_mut(); + view_mut.slice_axis_inplace(axis, start, end, step); + view_mut + } + + /// Slice the array in place along the specified axis. + /// + /// **Panics** if an index is out of bounds or step size is zero.
+ /// **Panics** if `axis` is out of bounds. + pub fn slice_axis_inplace( + &mut self, + axis: Axis, + start: Ixs, + end: Option, + step: Ixs, + ) { + let offset = do_slice( + &mut self.dim.slice_mut()[axis.index()], + &mut self.strides.slice_mut()[axis.index()], + start, + end, + step, + ); unsafe { self.ptr = self.ptr.offset(offset); } diff --git a/tests/array.rs b/tests/array.rs index f60d86bb0..40b3b7bc6 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -82,6 +82,13 @@ fn slice_oob() let _vi = a.slice(&[Si(0, Some(10), 1), S]); } +#[should_panic] +#[test] +fn slice_axis_oob() { + let a = RcArray::::zeros((3, 4)); + let _vi = a.slice_axis(Axis(0), 0, Some(10), 1); +} + #[should_panic] #[test] fn slice_wrong_dim() From d2837d11c2aecb564b9a9280d195852544d54e36 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 18:13:32 -0400 Subject: [PATCH 06/11] Add Dimension::NDIM associated constant --- src/dimension/dimension_trait.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 81a39078f..5725a5139 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -41,6 +41,10 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + MulAssign + for<'x> MulAssign<&'x Self> + MulAssign { + /// For fixed-size dimension representations (e.g. `Ix2`), this should be + /// `Some(ndim)`, and for variable-size dimension representations (e.g. + /// `IxDyn`), this should be `None`. + const NDIM: Option; /// `SliceArg` is the type which is used to specify slicing for this /// dimension. /// @@ -350,6 +354,7 @@ macro_rules! impl_insert_axis_array( ); impl Dimension for Dim<[Ix; 0]> { + const NDIM: Option = Some(0); type SliceArg = [Si; 0]; type Pattern = (); type Smaller = Self; @@ -381,6 +386,7 @@ impl Dimension for Dim<[Ix; 0]> { impl Dimension for Dim<[Ix; 1]> { + const NDIM: Option = Some(1); type SliceArg = [Si; 1]; type Pattern = Ix; type Smaller = Ix0; @@ -469,6 +475,7 @@ impl Dimension for Dim<[Ix; 1]> { } impl Dimension for Dim<[Ix; 2]> { + const NDIM: Option = Some(2); type SliceArg = [Si; 2]; type Pattern = (Ix, Ix); type Smaller = Ix1; @@ -599,6 +606,7 @@ impl Dimension for Dim<[Ix; 2]> { } impl Dimension for Dim<[Ix; 3]> { + const NDIM: Option = Some(3); type SliceArg = [Si; 3]; type Pattern = (Ix, Ix, Ix); type Smaller = Ix2; @@ -710,6 +718,7 @@ impl Dimension for Dim<[Ix; 3]> { macro_rules! large_dim { ($n:expr, $name:ident, $pattern:ty, $larger:ty, { $($insert_axis:tt)* }) => ( impl Dimension for Dim<[Ix; $n]> { + const NDIM: Option = Some($n); type SliceArg = [Si; $n]; type Pattern = $pattern; type Smaller = Dim<[Ix; $n - 1]>; @@ -756,6 +765,7 @@ large_dim!(6, Ix6, (Ix, Ix, Ix, Ix, Ix, Ix), IxDyn, { /// and memory wasteful, but it allows an arbitrary and dynamic number of axes. impl Dimension for IxDyn { + const NDIM: Option = None; type SliceArg = [Si]; type Pattern = Self; type Smaller = Self; From 13c92158c173f12308eff7b15365b2750bb43dd0 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 18:15:53 -0400 Subject: [PATCH 07/11] Add Dimension::zero_index_with_ndim() --- src/dimension/dimension_trait.rs | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 5725a5139..6993b38f1 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -156,6 +156,15 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + Self::default() } + #[doc(hidden)] + /// Return an index of same type and with the specified dimensionality. + /// + /// This method is useful for generalizing over fixed-size and + /// variable-size dimension representations. + /// + /// **Panics** if `Self` has a fixed size that is not `ndim`. + fn zero_index_with_ndim(ndim: usize) -> Self; + #[doc(hidden)] #[inline] fn first_index(&self) -> Option { @@ -371,6 +380,11 @@ impl Dimension for Dim<[Ix; 0]> { #[inline] fn into_pattern(self) -> Self::Pattern { } #[inline] + fn zero_index_with_ndim(ndim: usize) -> Self { + assert_eq!(ndim, 0); + Self::default() + } + #[inline] fn next_for(&self, _index: Self) -> Option { None } @@ -402,6 +416,11 @@ impl Dimension for Dim<[Ix; 1]> { get!(&self, 0) } #[inline] + fn zero_index_with_ndim(ndim: usize) -> Self { + assert_eq!(ndim, 1); + Self::default() + } + #[inline] fn next_for(&self, mut index: Self) -> Option { getm!(index, 0) += 1; if get!(&index, 0) < get!(self, 0) { @@ -491,6 +510,11 @@ impl Dimension for Dim<[Ix; 2]> { #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] + fn zero_index_with_ndim(ndim: usize) -> Self { + assert_eq!(ndim, 2); + Self::default() + } + #[inline] fn next_for(&self, index: Self) -> Option { let mut i = get!(&index, 0); let mut j = get!(&index, 1); @@ -630,6 +654,12 @@ impl Dimension for Dim<[Ix; 3]> { m as usize * n as usize * o as usize } + #[inline] + fn zero_index_with_ndim(ndim: usize) -> Self { + assert_eq!(ndim, 3); + Self::default() + } + #[inline] fn next_for(&self, index: Self) -> Option { let mut i = get!(&index, 0); @@ -734,6 +764,11 @@ macro_rules! large_dim { #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] + fn zero_index_with_ndim(ndim: usize) -> Self { + assert_eq!(ndim, $n); + Self::default() + } + #[inline] $($insert_axis)* #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { @@ -786,6 +821,11 @@ impl Dimension for IxDyn IxDyn::zeros(self.ndim()) } + #[inline] + fn zero_index_with_ndim(ndim: usize) -> Self { + IxDyn::zeros(ndim) + } + #[inline] fn insert_axis(&self, axis: Axis) -> Self::Larger { debug_assert!(axis.index() <= self.ndim()); From 860366d9faebea98790729c9748e0e2c51fd08d0 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 18:54:00 -0400 Subject: [PATCH 08/11] Add support for individual indices when slicing --- src/dimension/dimension_trait.rs | 26 +- src/dimension/mod.rs | 2 +- src/impl_methods.rs | 113 ++++++-- src/lib.rs | 47 +++- src/slice.rs | 433 ++++++++++++++++++++++++------- tests/array.rs | 19 +- tests/iterators.rs | 14 +- tests/oper.rs | 12 +- 8 files changed, 496 insertions(+), 170 deletions(-) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 6993b38f1..8673f3791 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -13,7 +13,7 @@ use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign}; use itertools::{enumerate, zip}; -use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, Dim, Si, IxDynImpl}; +use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, Dim, SliceOrIndex, IxDynImpl}; use IntoDimension; use RemoveAxis; use {ArrayView1, ArrayViewMut1}; @@ -52,14 +52,14 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + /// size, which you pass by reference. For the dynamic dimension it is /// a slice. /// - /// - For `Ix1`: `[Si; 1]` - /// - For `Ix2`: `[Si; 2]` + /// - For `Ix1`: `[SliceOrIndex; 1]` + /// - For `Ix2`: `[SliceOrIndex; 2]` /// - and so on.. - /// - For `IxDyn`: `[Si]` + /// - For `IxDyn`: `[SliceOrIndex]` /// - /// The easiest way to create a `&SliceArg` is using the macro - /// [`s![]`](macro.s!.html). - type SliceArg: ?Sized + AsRef<[Si]>; + /// The easiest way to create a `&SliceInfo` is using the + /// [`s![]`](macro.s!.html) macro. + type SliceArg: ?Sized + AsRef<[SliceOrIndex]>; /// Pattern matching friendly form of the dimension value. /// /// - For `Ix1`: `usize`, @@ -364,7 +364,7 @@ macro_rules! impl_insert_axis_array( impl Dimension for Dim<[Ix; 0]> { const NDIM: Option = Some(0); - type SliceArg = [Si; 0]; + type SliceArg = [SliceOrIndex; 0]; type Pattern = (); type Smaller = Self; type Larger = Ix1; @@ -401,7 +401,7 @@ impl Dimension for Dim<[Ix; 0]> { impl Dimension for Dim<[Ix; 1]> { const NDIM: Option = Some(1); - type SliceArg = [Si; 1]; + type SliceArg = [SliceOrIndex; 1]; type Pattern = Ix; type Smaller = Ix0; type Larger = Ix2; @@ -495,7 +495,7 @@ impl Dimension for Dim<[Ix; 1]> { impl Dimension for Dim<[Ix; 2]> { const NDIM: Option = Some(2); - type SliceArg = [Si; 2]; + type SliceArg = [SliceOrIndex; 2]; type Pattern = (Ix, Ix); type Smaller = Ix1; type Larger = Ix3; @@ -631,7 +631,7 @@ impl Dimension for Dim<[Ix; 2]> { impl Dimension for Dim<[Ix; 3]> { const NDIM: Option = Some(3); - type SliceArg = [Si; 3]; + type SliceArg = [SliceOrIndex; 3]; type Pattern = (Ix, Ix, Ix); type Smaller = Ix2; type Larger = Ix4; @@ -749,7 +749,7 @@ macro_rules! large_dim { ($n:expr, $name:ident, $pattern:ty, $larger:ty, { $($insert_axis:tt)* }) => ( impl Dimension for Dim<[Ix; $n]> { const NDIM: Option = Some($n); - type SliceArg = [Si; $n]; + type SliceArg = [SliceOrIndex; $n]; type Pattern = $pattern; type Smaller = Dim<[Ix; $n - 1]>; type Larger = $larger; @@ -801,7 +801,7 @@ large_dim!(6, Ix6, (Ix, Ix, Ix, Ix, Ix, Ix), IxDyn, { impl Dimension for IxDyn { const NDIM: Option = None; - type SliceArg = [Si]; + type SliceArg = [SliceOrIndex]; type Pattern = Self; type Smaller = Self; type Larger = Self; diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 319b6155e..fee9e9c37 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -214,7 +214,7 @@ pub fn do_sub(dims: &mut D, ptr: &mut *mut A, strides: &D, /// Compute the equivalent unsigned index given the axis length and signed index. #[inline] -fn abs_index(len: Ix, index: Ixs) -> Ix { +pub fn abs_index(len: Ix, index: Ixs) -> Ix { if index < 0 { len - (-index as Ix) } else { diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 397275055..0f2552497 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -19,7 +19,7 @@ use dimension; use iterators; use error::{self, ShapeError, ErrorKind}; use dimension::IntoDimension; -use dimension::{axes_of, Axes, do_slice, merge_axes, stride_offset}; +use dimension::{abs_index, axes_of, Axes, do_slice, merge_axes, stride_offset}; use iterators::{ new_lanes, new_lanes_mut, @@ -31,7 +31,8 @@ use zip::Zip; use { NdIndex, - Si, + SliceInfo, + SliceOrIndex }; use iter::{ AxisChunksIter, @@ -207,53 +208,111 @@ impl ArrayBase where S: Data, D: Dimension } - /// Return a sliced array. + /// Return a sliced view of the array. /// /// See [*Slicing*](#slicing) for full documentation. - /// See also [`D::SliceArg`]. + /// See also [`SliceInfo`] and [`D::SliceArg`]. /// + /// [`SliceInfo`]: struct.SliceInfo.html /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg /// - /// **Panics** if an index is out of bounds or stride is zero.
- /// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.) - pub fn slice(&self, indexes: &D::SliceArg) -> ArrayView { - let mut arr = self.view(); - arr.slice_inplace(indexes); - arr + /// **Panics** if an index is out of bounds or step size is zero.
+ /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + pub fn slice(&self, info: &SliceInfo) -> ArrayView + where + Do: Dimension, + { + self.view().slice_move(info) } /// Return a sliced read-write view of the array. /// - /// See also [`D::SliceArg`]. + /// See [*Slicing*](#slicing) for full documentation. + /// See also [`SliceInfo`] and [`D::SliceArg`]. /// + /// [`SliceInfo`]: struct.SliceInfo.html /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg /// - /// **Panics** if an index is out of bounds or stride is zero.
- /// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.) - pub fn slice_mut(&mut self, indexes: &D::SliceArg) -> ArrayViewMut - where S: DataMut + /// **Panics** if an index is out of bounds or step size is zero.
+ /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + pub fn slice_mut(&mut self, info: &SliceInfo) -> ArrayViewMut + where + Do: Dimension, + S: DataMut, + { + self.view_mut().slice_move(info) + } + + /// Slice the array, possibly changing the number of dimensions. + /// + /// See [*Slicing*](#slicing) for full documentation. + /// See also [`SliceInfo`] and [`D::SliceArg`]. + /// + /// [`SliceInfo`]: struct.SliceInfo.html + /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg + /// + /// **Panics** if an index is out of bounds or step size is zero.
+ /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) + pub fn slice_move(mut self, info: &SliceInfo) -> ArrayBase + where + Do: Dimension, { - let mut arr = self.view_mut(); - arr.slice_inplace(indexes); - arr + // Slice and subview in-place without changing the number of dimensions. + self.slice_inplace(&*info); + + let indices: &[SliceOrIndex] = (**info).as_ref(); + + // Copy the dim and strides that remain after removing the subview axes. + let out_ndim = info.out_ndim(); + let mut new_dim = Do::zero_index_with_ndim(out_ndim); + let mut new_strides = Do::zero_index_with_ndim(out_ndim); + izip!(self.dim.slice(), self.strides.slice(), indices) + .filter_map(|(d, s, slice_or_index)| match slice_or_index { + &SliceOrIndex::Slice(..) => Some((d, s)), + &SliceOrIndex::Index(_) => None, + }) + .zip(izip!(new_dim.slice_mut(), new_strides.slice_mut())) + .for_each(|((d, s), (new_d, new_s))| { + *new_d = *d; + *new_s = *s; + }); + + ArrayBase { + ptr: self.ptr, + data: self.data, + dim: new_dim, + strides: new_strides, + } } - /// Slice the array’s view in place. + /// Slice the array in place without changing the number of dimensions. + /// + /// Note that [`&SliceInfo`](struct.SliceInfo.html) (produced by the + /// [`s![]`](macro.s!.html) macro) will usually coerce into `&D::SliceArg` + /// automatically, but in some cases (e.g. if `D` is `IxDyn`), you may need + /// to call `.as_ref()`. /// + /// See [*Slicing*](#slicing) for full documentation. /// See also [`D::SliceArg`]. /// /// [`D::SliceArg`]: trait.Dimension.html#associatedtype.SliceArg /// - /// **Panics** if an index is out of bounds or stride is zero.
- /// (**Panics** if `D` is `IxDyn` and `indexes` does not match the number of array axes.) - pub fn slice_inplace(&mut self, indexes: &D::SliceArg) { - let indexes: &[Si] = indexes.as_ref(); - assert_eq!(indexes.len(), self.ndim()); - indexes + /// **Panics** if an index is out of bounds or step size is zero.
+ /// (**Panics** if `D` is `IxDyn` and `indices` does not match the number of array axes.) + pub fn slice_inplace(&mut self, indices: &D::SliceArg) { + let indices: &[SliceOrIndex] = indices.as_ref(); + assert_eq!(indices.len(), self.ndim()); + indices .iter() .enumerate() - .for_each(|(axis, &Si(start, end, step))| { - self.slice_axis_inplace(Axis(axis), start, end, step) + .for_each(|(axis, slice_or_index)| match slice_or_index { + &SliceOrIndex::Slice(start, end, step) => { + self.slice_axis_inplace(Axis(axis), start, end, step) + } + &SliceOrIndex::Index(index) => { + let i_usize = abs_index(self.len_of(Axis(axis)), index); + self.subview_inplace(Axis(axis), i_usize) + } }); } diff --git a/src/lib.rs b/src/lib.rs index bad645909..c9487c2a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,7 +105,7 @@ pub use dimension::NdIndex; pub use dimension::IxDynImpl; pub use indexes::{indices, indices_of}; pub use error::{ShapeError, ErrorKind}; -pub use slice::{Si, S}; +pub use slice::{SliceInfo, SliceNextDim, SliceOrIndex}; use iterators::Baseiter; use iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; @@ -422,16 +422,22 @@ pub type Ixs = isize; /// /// You can use slicing to create a view of a subset of the data in /// the array. Slicing methods include [`.slice()`], [`.slice_mut()`], -/// and [`.slice_inplace()`]. +/// [`.slice_move()`], and [`.slice_inplace()`]. /// /// The slicing argument can be passed using the macro [`s![]`](macro.s!.html), -/// which will be used in all examples. (The explicit form is a reference -/// to a fixed size array of [`Si`]; see its docs for more information.) +/// which will be used in all examples. (The explicit form is an instance of +/// [`&SliceInfo`]; see its docs for more information.) /// -/// [`Si`]: struct.Si.html +/// [`&SliceInfo`]: struct.SliceInfo.html +/// +/// If a range is used, the axis is preserved. If an index is used, a subview +/// is taken with respect to the axis. See [*Subviews*](#subviews) for more +/// information about subviews. Note that [`.slice_inplace()`] behaves like +/// [`.subview_inplace()`] by preserving the number of dimensions. /// /// [`.slice()`]: #method.slice /// [`.slice_mut()`]: #method.slice_mut +/// [`.slice_move()`]: #method.slice_move /// [`.slice_inplace()`]: #method.slice_inplace /// /// ``` @@ -439,7 +445,7 @@ pub type Ixs = isize; /// #[macro_use(s)] /// extern crate ndarray; /// -/// use ndarray::arr3; +/// use ndarray::{arr2, arr3}; /// /// fn main() { /// @@ -460,8 +466,6 @@ pub type Ixs = isize; /// // - Every element in each row: `..` /// /// let b = a.slice(s![.., 0..1, ..]); -/// // without the macro, the explicit argument is `&[S, Si(0, Some(1), 1), S]` -/// /// let c = arr3(&[[[ 1, 2, 3]], /// [[ 7, 8, 9]]]); /// assert_eq!(b, c); @@ -476,6 +480,18 @@ pub type Ixs = isize; /// let e = arr3(&[[[ 6, 5, 4]], /// [[12, 11, 10]]]); /// assert_eq!(d, e); +/// assert_eq!(d.shape(), &[2, 1, 3]); +/// +/// // Let’s create a slice while taking a subview with +/// // +/// // - Both submatrices of the greatest dimension: `..` +/// // - The last row in each submatrix, removing that axis: `-1` +/// // - Row elements in reverse order: `..;-1` +/// let f = a.slice(s![.., -1, ..;-1]); +/// let g = arr2(&[[ 6, 5, 4], +/// [12, 11, 10]]); +/// assert_eq!(f, g); +/// assert_eq!(f.shape(), &[2, 3]); /// } /// ``` /// @@ -483,7 +499,9 @@ pub type Ixs = isize; /// /// Subview methods allow you to restrict the array view while removing one /// axis from the array. Subview methods include [`.subview()`], -/// [`.subview_mut()`], [`.into_subview()`], and [`.subview_inplace()`]. +/// [`.subview_mut()`], [`.into_subview()`], and [`.subview_inplace()`]. You +/// can also take a subview by using a single index instead of a range when +/// slicing. /// /// Subview takes two arguments: `axis` and `index`. /// @@ -493,7 +511,11 @@ pub type Ixs = isize; /// [`.subview_inplace()`]: #method.subview_inplace /// /// ``` -/// use ndarray::{arr3, aview2, Axis}; +/// #[macro_use(s)] extern crate ndarray; +/// +/// use ndarray::{arr3, aview1, aview2, Axis}; +/// +/// # fn main() { /// /// // 2 submatrices of 2 rows with 3 elements per row, means a shape of `[2, 2, 3]`. /// @@ -523,6 +545,11 @@ pub type Ixs = isize; /// /// assert_eq!(sub_col, aview2(&[[ 1, 4], /// [ 7, 10]])); +/// +/// // You can take multiple subviews at once (and slice at the same time) +/// let double_sub = a.slice(s![1, .., 0]); +/// assert_eq!(double_sub, aview1(&[7, 10])); +/// # } /// ``` /// /// [`.subview_inplace()`] modifies the view in the same way as [`.subview()`], diff --git a/src/slice.rs b/src/slice.rs index 2b4c45b07..a42e60941 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -5,126 +5,333 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::ops::{Range, RangeFrom, RangeTo, RangeFull}; +use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeTo}; use std::fmt; -use super::Ixs; +use std::marker::PhantomData; +use super::{Dimension, Ixs}; -// [a:b:s] syntax for example [:3], [::-1] -// [0,:] -- first row of matrix -// [:,0] -- first column of matrix - -#[derive(PartialEq, Eq, Hash)] -/// A slice, a description of a range of an array axis. -/// -/// Fields are `begin`, `end` and `stride`, where -/// negative `begin` or `end` indexes are counted from the back -/// of the axis. -/// -/// If `end` is `None`, the slice extends to the end of the axis. +/// A slice (range with step) or an index. /// -/// See also the [`s![] macro`](macro.s!.html), a convenient way to specify -/// an array of `Si`. +/// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a +/// `&SliceInfo<[SliceOrIndex; n], D>`. /// /// ## Examples /// -/// `Si(0, None, 1)` is the full range of an axis. -/// Python equivalent is `[:]`. Macro equivalent is `s![..]`. +/// `SliceOrIndex::Slice(0, None, 1)` is the full range of an axis. It can also +/// be created with `SliceOrIndex::from(..)`. The Python equivalent is `[:]`. +/// The macro equivalent is `s![..]`. /// -/// `Si(a, Some(b), 2)` is every second element from `a` until `b`. -/// Python equivalent is `[a:b:2]`. Macro equivalent is `s![a..b;2]`. +/// `SliceOrIndex::Slice(a, Some(b), 2)` is every second element from `a` until +/// `b`. It can also be created with `SliceOrIndex::from(a..b).step(2)`. The +/// Python equivalent is `[a:b:2]`. The macro equivalent is `s![a..b;2]`. /// -/// `Si(a, None, -1)` is every element, from `a` -/// until the end, in reverse order. Python equivalent is `[a::-1]`. -/// Macro equivalent is `s![a..;-1]`. -/// -/// The constant [`S`] is a shorthand for the full range of an axis. -/// -/// [`S`]: constant.S.html -pub struct Si(pub Ixs, pub Option, pub Ixs); +/// `SliceOrIndex::Slice(a, None, -1)` is every element, from `a` until the +/// end, in reverse order. It can also be created with +/// `SliceOrIndex::from(a..).step(-1)`. The Python equivalent is `[a::-1]`. The +/// macro equivalent is `s![a..;-1]`. +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum SliceOrIndex { + /// A range with step size. The fields are `begin`, `end`, and `step`, + /// where negative `begin` or `end` indexes are counted from the back of + /// the axis. If `end` is `None`, the slice extends to the end of the axis. + Slice(Ixs, Option, Ixs), + /// A single index. + Index(Ixs), +} -impl fmt::Debug for Si { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Si(0, _, _) => { } - Si(i, _, _) => { try!(write!(f, "{}", i)); } +copy_and_clone!{SliceOrIndex} + +impl SliceOrIndex { + /// Returns `true` if `self` is a `Slice` value. + pub fn is_slice(&self) -> bool { + match self { + &SliceOrIndex::Slice(..) => true, + _ => false, } - try!(write!(f, "..")); - match *self { - Si(_, None, _) => { } - Si(_, Some(i), _) => { try!(write!(f, "{}", i)); } + } + + /// Returns `true` if `self` is an `Index` value. + pub fn is_index(&self) -> bool { + match self { + &SliceOrIndex::Index(_) => true, + _ => false, + } + } + + /// Returns a new `SliceOrIndex` with the given step size. + #[inline] + pub fn step(self, step: Ixs) -> Self { + match self { + SliceOrIndex::Slice(start, end, _) => SliceOrIndex::Slice(start, end, step), + SliceOrIndex::Index(s) => SliceOrIndex::Index(s), } + } +} + +impl fmt::Display for SliceOrIndex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Si(_, _, 1) => { } - Si(_, _, s) => { try!(write!(f, ";{}", s)); } + SliceOrIndex::Index(index) => write!(f, "{}", index)?, + SliceOrIndex::Slice(start, end, step) => { + if start != 0 { + write!(f, "{}", start)?; + } + write!(f, "..")?; + if let Some(i) = end { + write!(f, "{}", i)?; + } + if step != 1 { + write!(f, ";{}", step)?; + } + } } Ok(()) } } -impl From> for Si { +impl From> for SliceOrIndex { #[inline] - fn from(r: Range) -> Si { - Si(r.start, Some(r.end), 1) + fn from(r: Range) -> SliceOrIndex { + SliceOrIndex::Slice(r.start, Some(r.end), 1) } } -impl From> for Si { +impl From for SliceOrIndex { #[inline] - fn from(r: RangeFrom) -> Si { - Si(r.start, None, 1) + fn from(r: Ixs) -> SliceOrIndex { + SliceOrIndex::Index(r) } } -impl From> for Si { +impl From> for SliceOrIndex { #[inline] - fn from(r: RangeTo) -> Si { - Si(0, Some(r.end), 1) + fn from(r: RangeFrom) -> SliceOrIndex { + SliceOrIndex::Slice(r.start, None, 1) } } -impl From for Si { +impl From> for SliceOrIndex { #[inline] - fn from(_: RangeFull) -> Si { - S + fn from(r: RangeTo) -> SliceOrIndex { + SliceOrIndex::Slice(0, Some(r.end), 1) } } - -impl Si { +impl From for SliceOrIndex { #[inline] - pub fn step(self, step: Ixs) -> Self { - Si(self.0, self.1, self.2 * step) + fn from(_: RangeFull) -> SliceOrIndex { + SliceOrIndex::Slice(0, None, 1) + } +} + +/// Represents all of the necessary information to perform a slice. +/// +/// The type `T` is typically `[SliceOrIndex; n]`, `[SliceOrIndex]`, or +/// `Vec`. The type `D` is the output dimension after calling +/// [`.slice()`]. +/// +/// [`.slice()`]: struct.ArrayBase.html#method.slice +#[derive(Debug)] +#[repr(C)] +pub struct SliceInfo { + out_dim: PhantomData, + indices: T, +} + +impl Deref for SliceInfo +where + D: Dimension, +{ + type Target = T; + fn deref(&self) -> &Self::Target { + &self.indices + } +} + +impl SliceInfo +where + D: Dimension, +{ + /// Returns a new `SliceInfo` instance. + /// + /// If you call this method, you are guaranteeing that `out_dim` and + /// `out_ndim` are consistent with `indices`. + #[doc(hidden)] + pub unsafe fn new_unchecked(indices: T, out_dim: PhantomData) -> SliceInfo { + SliceInfo { + out_dim: out_dim, + indices: indices, + } + } +} + +impl SliceInfo +where + T: AsRef<[SliceOrIndex]>, + D: Dimension, +{ + /// Returns a new `SliceInfo` instance. + /// + /// **Panics** if `D` is not consistent with `indices`. + pub fn new(indices: T) -> SliceInfo { + let out_ndim = indices.as_ref().iter().filter(|s| s.is_slice()).count(); + if let Some(ndim) = D::NDIM { + assert_eq!(out_ndim, ndim); + } + SliceInfo { + out_dim: PhantomData, + indices: indices, + } + } +} + +impl SliceInfo +where + T: AsRef<[SliceOrIndex]>, + D: Dimension, +{ + /// Returns the number of dimensions after calling + /// [`.slice()`](struct.ArrayBase.html#method.slice) (including taking + /// subviews). + /// + /// If `D` is a fixed-size dimension type, then this is equivalent to + /// `D::NDIM.unwrap()`. Otherwise, the value is calculated by iterating + /// over the ranges/indices. + pub fn out_ndim(&self) -> usize { + D::NDIM.unwrap_or_else(|| { + self.indices + .as_ref() + .iter() + .filter(|s| s.is_slice()) + .count() + }) } } -copy_and_clone!{Si} +impl AsRef<[SliceOrIndex]> for SliceInfo +where + T: AsRef<[SliceOrIndex]>, + D: Dimension, +{ + fn as_ref(&self) -> &[SliceOrIndex] { + self.indices.as_ref() + } +} -/// Slice value for the full range of an axis. -pub const S: Si = Si(0, None, 1); +impl AsRef> for SliceInfo +where + T: AsRef<[SliceOrIndex]>, + D: Dimension, +{ + fn as_ref(&self) -> &SliceInfo<[SliceOrIndex], D> { + unsafe { + // This is okay because the only non-zero-sized member of + // `SliceInfo` is `indices`, so `&SliceInfo<[SliceOrIndex], D>` + // should have the same bitwise representation as + // `&[SliceOrIndex]`. + &*(self.indices.as_ref() as *const [SliceOrIndex] + as *const SliceInfo<[SliceOrIndex], D>) + } + } +} + +impl Copy for SliceInfo +where + T: Copy, + D: Dimension, +{ +} + +impl Clone for SliceInfo +where + T: Clone, + D: Dimension, +{ + fn clone(&self) -> Self { + SliceInfo { + out_dim: PhantomData, + indices: self.indices.clone(), + } + } +} + + +#[doc(hidden)] +pub trait SliceNextDim { + fn next_dim(&self, PhantomData) -> PhantomData; +} + +impl SliceNextDim for Range { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } +} + +impl SliceNextDim for RangeFrom { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } +} + +impl SliceNextDim for RangeTo { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } +} + +impl SliceNextDim for RangeFull { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } +} + +impl SliceNextDim for Ixs { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } +} /// Slice argument constructor. /// -/// `s![]` takes a list of ranges, separated by comma, with optional strides -/// that are separated from the range by a semicolon. -/// It is converted into a slice argument with type `&[Si; N]`. +/// `s![]` takes a list of ranges/indices, separated by comma, with optional +/// step sizes that are separated from the range by a semicolon. It is +/// converted into a [`&SliceInfo`] instance. +/// +/// [`&SliceInfo`]: struct.SliceInfo.html /// -/// Each range uses signed indices, where a negative value is counted from -/// the end of the axis. Strides are also signed and may be negative, but -/// must not be zero. +/// Each range/index uses signed indices, where a negative value is counted +/// from the end of the axis. Step sizes are also signed and may be negative, +/// but must not be zero. /// -/// The syntax is `s![` *[ axis-slice [, axis-slice [ , ... ] ] ]* `]`. -/// Where *axis-slice* is either *i* `..` *j* or *i* `..` *j* `;` *step*, -/// and *i* is the start index, *j* end index and *step* the element step -/// size (which defaults to 1). The number of *axis-slice* must match the -/// number of axes in the array. +/// The syntax is `s![` *[ axis-slice-or-index [, axis-slice-or-index [ , ... ] +/// ] ]* `]`, where *axis-slice-or-index* is any of the following: /// -/// For example `s![0..4;2, 1..5]` is a slice of rows 0..4 with step size 2, -/// and columns 1..5 with default step size 1. The slice would have -/// shape `[2, 4]`. +/// * *index*: an index to use for taking a subview with respect to that axis +/// * *range*: a range with step size 1 to use for slicing that axis +/// * *range* `;` *step*: a range with step size *step* to use for slicing that axis /// -/// If an array has two axes, the slice argument is passed as -/// type `&[Si; 2]`. The macro expansion of `s![a..b;c, d..e]` -/// is equivalent to `&[Si(a, Some(b), c), Si(d, Some(e), 1)]`. +/// The number of *axis-slice-or-index* must match the number of axes in the +/// array. *index*, *range*, and *step* can be expressions. *index* and *step* +/// must be of type [`Ixs`]. *range* can be of type `Range`, +/// `RangeTo`, `RangeFrom`, or `RangeFull`. +/// +/// [`Ixs`]: type.Ixs.html +/// +/// For example `s![0..4;2, 6, 1..5]` is a slice of the first axis for 0..4 +/// with step size 2, a subview of the second axis at index 6, and a slice of +/// the third axis for 1..5 with default step size 1. The input array must have +/// 3 dimensions. The resulting slice would have shape `[2, 4]` for +/// [`.slice()`], [`.slice_mut()`], and [`.slice_move()`], and shape +/// `[2, 1, 4]` for [`.slice_inplace()`]. +/// +/// [`.slice()`]: struct.ArrayBase.html#method.slice +/// [`.slice_mut()`]: struct.ArrayBase.html#method.slice_mut +/// [`.slice_move()`]: struct.ArrayBase.html#method.slice_move +/// [`.slice_inplace()`]: struct.ArrayBase.html#method.slice_inplace +/// +/// See also [*Slicing*](struct.ArrayBase.html#slicing). +/// +/// # Example /// /// ``` /// #[macro_use] @@ -143,35 +350,67 @@ pub const S: Si = Si(0, None, 1); /// ``` #[macro_export] macro_rules! s( - // convert a..b;c into @step(a..b, c), final item - (@parse [$($stack:tt)*] $r:expr;$s:expr) => { - &[$($stack)* s!(@step $r, $s)] + // convert a..b;c into @convert(a..b, c), final item + (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@convert $r, $s)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } }; - // convert a..b into @step(a..b, 1), final item - (@parse [$($stack:tt)*] $r:expr) => { - &[$($stack)* s!(@step $r, 1)] + // convert a..b into @convert(a..b), final item + (@parse $dim:expr, [$($stack:tt)*] $r:expr) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@convert $r)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } }; - // convert a..b;c into @step(a..b, c), final item, trailing comma - (@parse [$($stack:tt)*] $r:expr;$s:expr ,) => { - &[$($stack)* s!(@step $r, $s)] + // convert a..b;c into @convert(a..b, c), final item, trailing comma + (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr ,) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@convert $r, $s)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } + }; + // convert a..b into @convert(a..b), final item, trailing comma + (@parse $dim:expr, [$($stack:tt)*] $r:expr ,) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@convert $r)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } }; - // convert a..b into @step(a..b, 1), final item, trailing comma - (@parse [$($stack:tt)*] $r:expr ,) => { - &[$($stack)* s!(@step $r, 1)] + // convert a..b;c into @convert(a..b, c) + (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => { + s![@parse + $crate::SliceNextDim::next_dim(&$r, $dim), + [$($stack)* s!(@convert $r, $s),] + $($t)* + ] }; - // convert a..b;c into @step(a..b, c) - (@parse [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => { - s![@parse [$($stack)* s!(@step $r, $s),] $($t)*] + // convert a..b into @convert(a..b) + (@parse $dim:expr, [$($stack:tt)*] $r:expr, $($t:tt)*) => { + s![@parse + $crate::SliceNextDim::next_dim(&$r, $dim), + [$($stack)* s!(@convert $r),] + $($t)* + ] }; - // convert a..b into @step(a..b, 1) - (@parse [$($stack:tt)*] $r:expr, $($t:tt)*) => { - s![@parse [$($stack)* s!(@step $r, 1),] $($t)*] + // convert range/index into SliceOrIndex + (@convert $r:expr) => { + <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r) }; - // convert range, step into Si - (@step $r:expr, $s:expr) => { - <$crate::Si as ::std::convert::From<_>>::from($r).step($s) + // convert range/index and step into SliceOrIndex + (@convert $r:expr, $s:expr) => { + <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r).step($s) }; ($($t:tt)*) => { - s![@parse [] $($t)*] + s![@parse ::std::marker::PhantomData::<$crate::Ix0>, [] $($t)*] }; ); diff --git a/tests/array.rs b/tests/array.rs index 40b3b7bc6..1419a91bc 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -6,7 +6,6 @@ extern crate ndarray; extern crate defmac; extern crate itertools; -use ndarray::{S, Si}; use ndarray::prelude::*; use ndarray::{ rcarr2, @@ -62,7 +61,7 @@ fn test_slice() let vi = A.slice(s![1.., ..;2]); assert_eq!(vi.shape(), &[2, 2]); - let vi = A.slice(&[S, S]); + let vi = A.slice(s![.., ..]); assert_eq!(vi.shape(), A.shape()); assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b)); } @@ -79,7 +78,7 @@ fn index_out_of_bounds() { fn slice_oob() { let a = RcArray::::zeros((3, 4)); - let _vi = a.slice(&[Si(0, Some(10), 1), S]); + let _vi = a.slice(s![..10, ..]); } #[should_panic] @@ -109,7 +108,7 @@ fn test_index() assert_eq!(*a, A[[i, j]]); } - let vi = A.slice(&[Si(1, None, 1), Si(0, None, 2)]); + let vi = A.slice(s![1.., ..;2]); let mut it = vi.iter(); for ((i, j), x) in zip(indices((1, 2)), &mut it) { assert_eq!(*x, vi[[i, j]]); @@ -178,7 +177,7 @@ fn test_negative_stride_rcarray() } { - let vi = mat.slice(&[S, Si(0, None, -1), Si(0, None, -1)]); + let vi = mat.slice(s![.., ..;-1, ..;-1]); assert_eq!(vi.shape(), &[2, 4, 2]); // Test against sequential iterator let seq = [7f32,6., 5.,4.,3.,2.,1.,0.,15.,14.,13., 12.,11., 10., 9., 8.]; @@ -210,7 +209,7 @@ fn test_cow() assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); let mut rev = mat.reshape(4); - rev.slice_inplace(&[Si(0, None, -1)]); + rev.slice_inplace(s![..;-1]); assert_eq!(rev[0], 4); assert_eq!(rev[1], 3); assert_eq!(rev[2], 2); @@ -374,7 +373,7 @@ fn assign() let mut a = arr2(&[[1, 2], [3, 4]]); { let mut v = a.view_mut(); - v.slice_inplace(&[Si(0, Some(1), 1), S]); + v.slice_inplace(s![..1, ..]); v.fill(0); } assert_eq!(a, arr2(&[[0, 0], [3, 4]])); @@ -674,7 +673,7 @@ fn view_mut() { #[test] fn slice_mut() { let mut a = RcArray::from_vec(vec![1, 2, 3, 4]).reshape((2, 2)); - for elt in a.slice_mut(&[S, S]) { + for elt in a.slice_mut(s![.., ..]) { *elt = 0; } assert_eq!(a, aview2(&[[0, 0], [0, 0]])); @@ -682,14 +681,14 @@ fn slice_mut() { let mut b = arr2(&[[1, 2, 3], [4, 5, 6]]); let c = b.clone(); // make sure we can mutate b even if it has to be unshared first - for elt in b.slice_mut(&[S, Si(0, Some(1), 1)]) { + for elt in b.slice_mut(s![.., ..1]) { *elt = 0; } assert_eq!(b, aview2(&[[0, 2, 3], [0, 5, 6]])); assert!(c != b); - for elt in b.slice_mut(&[S, Si(0, None, 2)]) { + for elt in b.slice_mut(s![.., ..;2]) { *elt = 99; } assert_eq!(b, aview2(&[[99, 2, 99], diff --git a/tests/iterators.rs b/tests/iterators.rs index a75eefbb8..41197eafa 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -4,7 +4,7 @@ extern crate itertools; use ndarray::{Array0, Array2}; use ndarray::RcArray; -use ndarray::{Ix, Si, S}; +use ndarray::Ix; use ndarray::{ ArrayBase, Data, @@ -99,22 +99,22 @@ fn as_slice() { assert_slice_correct(&v.subview(Axis(0), 0)); assert_slice_correct(&v.subview(Axis(0), 1)); - assert!(v.slice(&[S, Si(0, Some(1), 1)]).as_slice().is_none()); - println!("{:?}", v.slice(&[Si(0, Some(1), 2), S])); - assert!(v.slice(&[Si(0, Some(1), 2), S]).as_slice().is_some()); + assert!(v.slice(s![.., ..1]).as_slice().is_none()); + println!("{:?}", v.slice(s![..1;2, ..])); + assert!(v.slice(s![..1;2, ..]).as_slice().is_some()); // `u` is contiguous, because the column stride of `2` doesn't matter // when the result is just one row anyway -- length of that dimension is 1 - let u = v.slice(&[Si(0, Some(1), 2), S]); + let u = v.slice(s![..1;2, ..]); println!("{:?}", u.shape()); println!("{:?}", u.strides()); - println!("{:?}", v.slice(&[Si(0, Some(1), 2), S])); + println!("{:?}", v.slice(s![..1;2, ..])); assert!(u.as_slice().is_some()); assert_slice_correct(&u); let a = a.reshape((8, 1)); assert_slice_correct(&a); - let u = a.slice(&[Si(0, None, 2), S]); + let u = a.slice(s![..;2, ..]); println!("u={:?}, shape={:?}, strides={:?}", u, u.shape(), u.strides()); assert!(u.as_slice().is_none()); } diff --git a/tests/oper.rs b/tests/oper.rs index 7c7918372..1eb427675 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -7,8 +7,7 @@ use ndarray::{rcarr1, rcarr2}; use ndarray::{LinalgScalar, Data}; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; -use ndarray::Si; -use ndarray::{Ix, Ixs}; +use ndarray::{Ix, Ixs, SliceInfo, SliceOrIndex}; use std::fmt; use std::ops::Neg; @@ -578,16 +577,19 @@ fn scaled_add_3() { vec![n, q] }; let cslice = if n == 1 { - vec![Si(0, None, s2)] + vec![SliceOrIndex::from(..).step(s2)] } else { - vec![Si(0, None, s1), Si(0, None, s2)] + vec![ + SliceOrIndex::from(..).step(s1), + SliceOrIndex::from(..).step(s2), + ] }; let c = range_mat64(n, q).into_shape(cdim).unwrap(); { let mut av = a.slice_mut(s![..;s1, ..;s2]); - let c = c.slice(&cslice); + let c = c.slice(SliceInfo::<_, IxDyn>::new(cslice).as_ref()); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); From 8413825be9d4bcdbd2232b8e0a0ba51dfbd10e96 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Sun, 29 Oct 2017 18:55:22 -0400 Subject: [PATCH 09/11] Add more tests for slicing --- tests/array.rs | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/tests/array.rs b/tests/array.rs index 1419a91bc..38ac07f25 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -6,6 +6,7 @@ extern crate ndarray; extern crate defmac; extern crate itertools; +use ndarray::{SliceInfo, SliceOrIndex}; use ndarray::prelude::*; use ndarray::{ rcarr2, @@ -66,6 +67,172 @@ fn test_slice() assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b)); } +#[test] +fn test_slice_with_many_dim() { + let mut A = RcArray::::zeros(&[3, 1, 4, 1, 3, 2, 1][..]); + for (i, elt) in A.iter_mut().enumerate() { + *elt = i; + } + + let vi = A.slice(s![..2, .., ..;2, ..1, ..1, 1.., ..]); + let new_shape = &[2, 1, 2, 1, 1, 1, 1][..]; + assert_eq!(vi.shape(), new_shape); + let correct = array![ + [A[&[0, 0, 0, 0, 0, 1, 0][..]], A[&[0, 0, 2, 0, 0, 1, 0][..]]], + [A[&[1, 0, 0, 0, 0, 1, 0][..]], A[&[1, 0, 2, 0, 0, 1, 0][..]]] + ].into_shape(new_shape) + .unwrap(); + assert_eq!(vi, correct); + + let vi = A.slice(s![..2, 0, ..;2, 0, 0, 1, 0]); + assert_eq!(vi.shape(), &[2, 2][..]); + let correct = array![ + [A[&[0, 0, 0, 0, 0, 1, 0][..]], A[&[0, 0, 2, 0, 0, 1, 0][..]]], + [A[&[1, 0, 0, 0, 0, 1, 0][..]], A[&[1, 0, 2, 0, 0, 1, 0][..]]] + ]; + assert_eq!(vi, correct); +} + +#[test] +fn test_slice_array_fixed() { + let mut arr = Array3::::zeros((5, 2, 5)); + let info = s![1.., 1, ..;2]; + arr.slice(info); + arr.slice_mut(info); + arr.view().slice_move(info); + arr.view().slice_inplace(info); +} + +#[test] +fn test_slice_dyninput_array_fixed() { + let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); + let info = s![1.., 1, ..;2]; + arr.slice(info); + arr.slice_mut(info); + arr.view().slice_move(info); + arr.view().slice_inplace(info.as_ref()); +} + +#[test] +fn test_slice_array_dyn() { + let mut arr = Array3::::zeros((5, 2, 5)); + let info = &SliceInfo::<_, IxDyn>::new([ + SliceOrIndex::from(1..), + SliceOrIndex::from(1), + SliceOrIndex::from(..).step(2), + ]); + arr.slice(info); + arr.slice_mut(info); + arr.view().slice_move(info); + arr.view().slice_inplace(info); +} + +#[test] +fn test_slice_dyninput_array_dyn() { + let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); + let info = &SliceInfo::<_, IxDyn>::new([ + SliceOrIndex::from(1..), + SliceOrIndex::from(1), + SliceOrIndex::from(..).step(2), + ]); + arr.slice(info); + arr.slice_mut(info); + arr.view().slice_move(info); + arr.view().slice_inplace(info.as_ref()); +} + +#[test] +fn test_slice_dyninput_vec_fixed() { + let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); + let info = &SliceInfo::<_, Ix2>::new(vec![ + SliceOrIndex::from(1..), + SliceOrIndex::from(1), + SliceOrIndex::from(..).step(2), + ]); + arr.slice(info.as_ref()); + arr.slice_mut(info.as_ref()); + arr.view().slice_move(info.as_ref()); + arr.view().slice_inplace(info.as_ref()); +} + +#[test] +fn test_slice_dyninput_vec_dyn() { + let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); + let info = &SliceInfo::<_, IxDyn>::new(vec![ + SliceOrIndex::from(1..), + SliceOrIndex::from(1), + SliceOrIndex::from(..).step(2), + ]); + arr.slice(info.as_ref()); + arr.slice_mut(info.as_ref()); + arr.view().slice_move(info.as_ref()); + arr.view().slice_inplace(info.as_ref()); +} + +#[test] +fn test_slice_with_subview() { + let mut arr = RcArray::::zeros((3, 5, 4)); + for (i, elt) in arr.iter_mut().enumerate() { + *elt = i; + } + + let vi = arr.slice(s![1.., 2, ..;2]); + assert_eq!(vi.shape(), &[2, 2]); + assert!( + vi.iter() + .zip(arr.subview(Axis(1), 2).slice(s![1.., ..;2]).iter()) + .all(|(a, b)| a == b) + ); + + let vi = arr.slice(s![1, 2, ..;2]); + assert_eq!(vi.shape(), &[2]); + assert!( + vi.iter() + .zip( + arr.subview(Axis(0), 1) + .subview(Axis(0), 2) + .slice(s![..;2]) + .iter() + ) + .all(|(a, b)| a == b) + ); + + let vi = arr.slice(s![1, 2, 3]); + assert_eq!(vi.shape(), &[]); + assert_eq!(vi, Array0::from_elem((), arr[(1, 2, 3)])); +} + +#[test] +fn test_slice_inplace_with_subview_inplace() { + let mut arr = RcArray::::zeros((3, 5, 4)); + for (i, elt) in arr.iter_mut().enumerate() { + *elt = i; + } + + let mut vi = arr.view(); + vi.slice_inplace(s![1.., 2, ..;2]); + assert_eq!(vi.shape(), &[2, 1, 2]); + assert!( + vi.iter() + .zip(arr.slice(s![1.., 2..3, ..;2]).iter()) + .all(|(a, b)| a == b) + ); + + let mut vi = arr.view(); + vi.slice_inplace(s![1, 2, ..;2]); + assert_eq!(vi.shape(), &[1, 1, 2]); + assert!( + vi.iter() + .zip(arr.slice(s![1..2, 2..3, ..;2]).iter()) + .all(|(a, b)| a == b) + ); + + let mut vi = arr.view(); + vi.slice_inplace(s![1, 2, 3]); + assert_eq!(vi.shape(), &[1, 1, 1]); + assert_eq!(vi, Array3::from_elem((1, 1, 1), arr[(1, 2, 3)])); +} + #[should_panic] #[test] fn index_out_of_bounds() { From 2d4ceab838af08cf36aa3879d13338a5d2f3d1ce Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 3 Nov 2017 18:37:15 -0400 Subject: [PATCH 10/11] Add Slice type and use it for .slice_axis*() --- src/impl_methods.rs | 37 +++++-------------- src/lib.rs | 2 +- src/slice.rs | 89 +++++++++++++++++++++++++++++++++++++++++---- tests/array.rs | 12 +++--- 4 files changed, 99 insertions(+), 41 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 0f2552497..f63a83ca0 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -31,6 +31,7 @@ use zip::Zip; use { NdIndex, + Slice, SliceInfo, SliceOrIndex }; @@ -307,7 +308,7 @@ impl ArrayBase where S: Data, D: Dimension .enumerate() .for_each(|(axis, slice_or_index)| match slice_or_index { &SliceOrIndex::Slice(start, end, step) => { - self.slice_axis_inplace(Axis(axis), start, end, step) + self.slice_axis_inplace(Axis(axis), Slice(start, end, step)) } &SliceOrIndex::Index(index) => { let i_usize = abs_index(self.len_of(Axis(axis)), index); @@ -320,15 +321,9 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. - pub fn slice_axis( - &self, - axis: Axis, - start: Ixs, - end: Option, - step: Ixs, - ) -> ArrayView { + pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView { let mut view = self.view(); - view.slice_axis_inplace(axis, start, end, step); + view.slice_axis_inplace(axis, indices); view } @@ -336,18 +331,12 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. - pub fn slice_axis_mut( - &mut self, - axis: Axis, - start: Ixs, - end: Option, - step: Ixs, - ) -> ArrayViewMut + pub fn slice_axis_mut(&mut self, axis: Axis, indices: Slice) -> ArrayViewMut where S: DataMut, { let mut view_mut = self.view_mut(); - view_mut.slice_axis_inplace(axis, start, end, step); + view_mut.slice_axis_inplace(axis, indices); view_mut } @@ -355,19 +344,13 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. - pub fn slice_axis_inplace( - &mut self, - axis: Axis, - start: Ixs, - end: Option, - step: Ixs, - ) { + pub fn slice_axis_inplace(&mut self, axis: Axis, indices: Slice) { let offset = do_slice( &mut self.dim.slice_mut()[axis.index()], &mut self.strides.slice_mut()[axis.index()], - start, - end, - step, + indices.0, + indices.1, + indices.2, ); unsafe { self.ptr = self.ptr.offset(offset); diff --git a/src/lib.rs b/src/lib.rs index c9487c2a0..1b4f44515 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,7 +105,7 @@ pub use dimension::NdIndex; pub use dimension::IxDynImpl; pub use indexes::{indices, indices_of}; pub use error::{ShapeError, ErrorKind}; -pub use slice::{SliceInfo, SliceNextDim, SliceOrIndex}; +pub use slice::{Slice, SliceInfo, SliceNextDim, SliceOrIndex}; use iterators::Baseiter; use iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; diff --git a/src/slice.rs b/src/slice.rs index a42e60941..153b5fd73 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -10,6 +10,59 @@ use std::fmt; use std::marker::PhantomData; use super::{Dimension, Ixs}; +/// A slice (range with step size). +/// +/// ## Examples +/// +/// `Slice(0, None, 1)` is the full range of an axis. It can also be created +/// with `Slice::from(..)`. The Python equivalent is `[:]`. +/// +/// `Slice(a, Some(b), 2)` is every second element from `a` until `b`. It can +/// also be created with `Slice::from(a..b).step(2)`. The Python equivalent is +/// `[a:b:2]`. +/// +/// `Slice(a, None, -1)` is every element, from `a` until the end, in reverse +/// order. It can also be created with `Slice::from(a..).step(-1)`. The Python +/// equivalent is `[a::-1]`. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Slice(pub Ixs, pub Option, pub Ixs); + +impl Slice { + /// Returns a new `Slice` with the given step size. + #[inline] + pub fn step(self, step: Ixs) -> Self { + Slice(self.0, self.1, step) + } +} + +impl From> for Slice { + #[inline] + fn from(r: Range) -> Slice { + Slice(r.start, Some(r.end), 1) + } +} + +impl From> for Slice { + #[inline] + fn from(r: RangeFrom) -> Slice { + Slice(r.start, None, 1) + } +} + +impl From> for Slice { + #[inline] + fn from(r: RangeTo) -> Slice { + Slice(0, Some(r.end), 1) + } +} + +impl From for Slice { + #[inline] + fn from(_: RangeFull) -> Slice { + Slice(0, None, 1) + } +} + /// A slice (range with step) or an index. /// /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a @@ -17,6 +70,10 @@ use super::{Dimension, Ixs}; /// /// ## Examples /// +/// `SliceOrIndex::Index(a)` is the index `a`. It can also be created with +/// `SliceOrIndex::from(a)`. The Python equivalent is `[a]`. The macro +/// equivalent is `s![a]`. +/// /// `SliceOrIndex::Slice(0, None, 1)` is the full range of an axis. It can also /// be created with `SliceOrIndex::from(..)`. The Python equivalent is `[:]`. /// The macro equivalent is `s![..]`. @@ -89,6 +146,13 @@ impl fmt::Display for SliceOrIndex { } } +impl From for SliceOrIndex { + #[inline] + fn from(s: Slice) -> SliceOrIndex { + SliceOrIndex::Slice(s.0, s.1, s.2) + } +} + impl From> for SliceOrIndex { #[inline] fn from(r: Range) -> SliceOrIndex { @@ -261,6 +325,12 @@ pub trait SliceNextDim { fn next_dim(&self, PhantomData) -> PhantomData; } +impl SliceNextDim for Slice { + fn next_dim(&self, _: PhantomData) -> PhantomData { + PhantomData + } +} + impl SliceNextDim for Range { fn next_dim(&self, _: PhantomData) -> PhantomData { PhantomData @@ -293,15 +363,15 @@ impl SliceNextDim for Ixs { /// Slice argument constructor. /// -/// `s![]` takes a list of ranges/indices, separated by comma, with optional -/// step sizes that are separated from the range by a semicolon. It is +/// `s![]` takes a list of ranges/slices/indices, separated by comma, with +/// optional step sizes that are separated from the range by a semicolon. It is /// converted into a [`&SliceInfo`] instance. /// /// [`&SliceInfo`]: struct.SliceInfo.html /// -/// Each range/index uses signed indices, where a negative value is counted -/// from the end of the axis. Step sizes are also signed and may be negative, -/// but must not be zero. +/// Each range/slice/index uses signed indices, where a negative value is +/// counted from the end of the axis. Step sizes are also signed and may be +/// negative, but must not be zero. /// /// The syntax is `s![` *[ axis-slice-or-index [, axis-slice-or-index [ , ... ] /// ] ]* `]`, where *axis-slice-or-index* is any of the following: @@ -309,10 +379,15 @@ impl SliceNextDim for Ixs { /// * *index*: an index to use for taking a subview with respect to that axis /// * *range*: a range with step size 1 to use for slicing that axis /// * *range* `;` *step*: a range with step size *step* to use for slicing that axis +/// * *slice*: a [`Slice`] instance to use for slicing that axis +/// * *slice* `;` *step*: a range constructed from the start and end of a [`Slice`] +/// instance, with new step size *step*, to use for slicing that axis +/// +/// [`Slice`]: struct.Slice.html /// /// The number of *axis-slice-or-index* must match the number of axes in the -/// array. *index*, *range*, and *step* can be expressions. *index* and *step* -/// must be of type [`Ixs`]. *range* can be of type `Range`, +/// array. *index*, *range*, *slice*, and *step* can be expressions. *index* +/// and *step* must be of type [`Ixs`]. *range* can be of type `Range`, /// `RangeTo`, `RangeFrom`, or `RangeFull`. /// /// [`Ixs`]: type.Ixs.html diff --git a/tests/array.rs b/tests/array.rs index 38ac07f25..b542f9fae 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -6,7 +6,7 @@ extern crate ndarray; extern crate defmac; extern crate itertools; -use ndarray::{SliceInfo, SliceOrIndex}; +use ndarray::{Slice, SliceInfo, SliceOrIndex}; use ndarray::prelude::*; use ndarray::{ rcarr2, @@ -55,14 +55,14 @@ fn test_mat_mul() { #[test] fn test_slice() { - let mut A = RcArray::::zeros((3, 4)); + let mut A = RcArray::::zeros((3, 4, 5)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; } - let vi = A.slice(s![1.., ..;2]); - assert_eq!(vi.shape(), &[2, 2]); - let vi = A.slice(s![.., ..]); + let vi = A.slice(s![1.., ..;2, Slice(0, None, 2)]); + assert_eq!(vi.shape(), &[2, 2, 3]); + let vi = A.slice(s![.., .., ..]); assert_eq!(vi.shape(), A.shape()); assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b)); } @@ -252,7 +252,7 @@ fn slice_oob() #[test] fn slice_axis_oob() { let a = RcArray::::zeros((3, 4)); - let _vi = a.slice_axis(Axis(0), 0, Some(10), 1); + let _vi = a.slice_axis(Axis(0), Slice(0, Some(10), 1)); } #[should_panic] From 79659c9e5376da1aadea041976c50a8cdb1f0657 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Fri, 3 Nov 2017 20:08:24 -0400 Subject: [PATCH 11/11] Add field names to Slice and SliceOrIndex::Slice Also rename .step() to .step_by(). --- src/impl_methods.rs | 12 ++-- src/slice.rs | 137 +++++++++++++++++++++++++++++++------------- tests/array.rs | 12 ++-- tests/oper.rs | 6 +- 4 files changed, 113 insertions(+), 54 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f63a83ca0..171670893 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -269,7 +269,7 @@ impl ArrayBase where S: Data, D: Dimension let mut new_strides = Do::zero_index_with_ndim(out_ndim); izip!(self.dim.slice(), self.strides.slice(), indices) .filter_map(|(d, s, slice_or_index)| match slice_or_index { - &SliceOrIndex::Slice(..) => Some((d, s)), + &SliceOrIndex::Slice {..} => Some((d, s)), &SliceOrIndex::Index(_) => None, }) .zip(izip!(new_dim.slice_mut(), new_strides.slice_mut())) @@ -307,8 +307,8 @@ impl ArrayBase where S: Data, D: Dimension .iter() .enumerate() .for_each(|(axis, slice_or_index)| match slice_or_index { - &SliceOrIndex::Slice(start, end, step) => { - self.slice_axis_inplace(Axis(axis), Slice(start, end, step)) + &SliceOrIndex::Slice { start, end, step } => { + self.slice_axis_inplace(Axis(axis), Slice { start, end, step }) } &SliceOrIndex::Index(index) => { let i_usize = abs_index(self.len_of(Axis(axis)), index); @@ -348,9 +348,9 @@ impl ArrayBase where S: Data, D: Dimension let offset = do_slice( &mut self.dim.slice_mut()[axis.index()], &mut self.strides.slice_mut()[axis.index()], - indices.0, - indices.1, - indices.2, + indices.start, + indices.end, + indices.step, ); unsafe { self.ptr = self.ptr.offset(offset); diff --git a/src/slice.rs b/src/slice.rs index 153b5fd73..41788ca6d 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -12,54 +12,88 @@ use super::{Dimension, Ixs}; /// A slice (range with step size). /// +/// Negative `begin` or `end` indexes are counted from the back of the axis. If +/// `end` is `None`, the slice extends to the end of the axis. +/// /// ## Examples /// -/// `Slice(0, None, 1)` is the full range of an axis. It can also be created -/// with `Slice::from(..)`. The Python equivalent is `[:]`. +/// `Slice::new(0, None, 1)` is the full range of an axis. It can also be +/// created with `Slice::from(..)`. The Python equivalent is `[:]`. /// -/// `Slice(a, Some(b), 2)` is every second element from `a` until `b`. It can -/// also be created with `Slice::from(a..b).step(2)`. The Python equivalent is -/// `[a:b:2]`. +/// `Slice::new(a, b, 2)` is every second element from `a` until `b`. It can +/// also be created with `Slice::from(a..b).step_by(2)`. The Python equivalent +/// is `[a:b:2]`. /// -/// `Slice(a, None, -1)` is every element, from `a` until the end, in reverse -/// order. It can also be created with `Slice::from(a..).step(-1)`. The Python -/// equivalent is `[a::-1]`. +/// `Slice::new(a, None, -1)` is every element, from `a` until the end, in +/// reverse order. It can also be created with `Slice::from(a..).step_by(-1)`. +/// The Python equivalent is `[a::-1]`. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct Slice(pub Ixs, pub Option, pub Ixs); +pub struct Slice { + pub start: Ixs, + pub end: Option, + pub step: Ixs, +} impl Slice { + pub fn new(start: Ixs, end: I, step: Ixs) -> Slice + where + I: Into>, + { + Slice { + start, + end: end.into(), + step, + } + } + /// Returns a new `Slice` with the given step size. #[inline] - pub fn step(self, step: Ixs) -> Self { - Slice(self.0, self.1, step) + pub fn step_by(self, step: Ixs) -> Self { + Slice { step, ..self } } } impl From> for Slice { #[inline] fn from(r: Range) -> Slice { - Slice(r.start, Some(r.end), 1) + Slice { + start: r.start, + end: Some(r.end), + step: 1, + } } } impl From> for Slice { #[inline] fn from(r: RangeFrom) -> Slice { - Slice(r.start, None, 1) + Slice { + start: r.start, + end: None, + step: 1, + } } } impl From> for Slice { #[inline] fn from(r: RangeTo) -> Slice { - Slice(0, Some(r.end), 1) + Slice { + start: 0, + end: Some(r.end), + step: 1, + } } } impl From for Slice { #[inline] fn from(_: RangeFull) -> Slice { - Slice(0, None, 1) + Slice { + start: 0, + end: None, + step: 1, + } } } @@ -74,24 +108,29 @@ impl From for Slice { /// `SliceOrIndex::from(a)`. The Python equivalent is `[a]`. The macro /// equivalent is `s![a]`. /// -/// `SliceOrIndex::Slice(0, None, 1)` is the full range of an axis. It can also -/// be created with `SliceOrIndex::from(..)`. The Python equivalent is `[:]`. -/// The macro equivalent is `s![..]`. +/// `SliceOrIndex::Slice { start: 0, end: None, step: 1 }` is the full range of +/// an axis. It can also be created with `SliceOrIndex::from(..)`. The Python +/// equivalent is `[:]`. The macro equivalent is `s![..]`. /// -/// `SliceOrIndex::Slice(a, Some(b), 2)` is every second element from `a` until -/// `b`. It can also be created with `SliceOrIndex::from(a..b).step(2)`. The -/// Python equivalent is `[a:b:2]`. The macro equivalent is `s![a..b;2]`. +/// `SliceOrIndex::Slice { start: a, end: Some(b), step: 2 }` is every second +/// element from `a` until `b`. It can also be created with +/// `SliceOrIndex::from(a..b).step_by(2)`. The Python equivalent is `[a:b:2]`. +/// The macro equivalent is `s![a..b;2]`. /// -/// `SliceOrIndex::Slice(a, None, -1)` is every element, from `a` until the -/// end, in reverse order. It can also be created with -/// `SliceOrIndex::from(a..).step(-1)`. The Python equivalent is `[a::-1]`. The -/// macro equivalent is `s![a..;-1]`. +/// `SliceOrIndex::Slice { start: a, end: None, step: -1 }` is every element, +/// from `a` until the end, in reverse order. It can also be created with +/// `SliceOrIndex::from(a..).step_by(-1)`. The Python equivalent is `[a::-1]`. +/// The macro equivalent is `s![a..;-1]`. #[derive(Debug, PartialEq, Eq, Hash)] pub enum SliceOrIndex { - /// A range with step size. The fields are `begin`, `end`, and `step`, - /// where negative `begin` or `end` indexes are counted from the back of - /// the axis. If `end` is `None`, the slice extends to the end of the axis. - Slice(Ixs, Option, Ixs), + /// A range with step size. Negative `begin` or `end` indexes are counted + /// from the back of the axis. If `end` is `None`, the slice extends to the + /// end of the axis. + Slice { + start: Ixs, + end: Option, + step: Ixs, + }, /// A single index. Index(Ixs), } @@ -102,7 +141,7 @@ impl SliceOrIndex { /// Returns `true` if `self` is a `Slice` value. pub fn is_slice(&self) -> bool { match self { - &SliceOrIndex::Slice(..) => true, + &SliceOrIndex::Slice { .. } => true, _ => false, } } @@ -117,9 +156,9 @@ impl SliceOrIndex { /// Returns a new `SliceOrIndex` with the given step size. #[inline] - pub fn step(self, step: Ixs) -> Self { + pub fn step_by(self, step: Ixs) -> Self { match self { - SliceOrIndex::Slice(start, end, _) => SliceOrIndex::Slice(start, end, step), + SliceOrIndex::Slice { start, end, .. } => SliceOrIndex::Slice { start, end, step }, SliceOrIndex::Index(s) => SliceOrIndex::Index(s), } } @@ -129,7 +168,7 @@ impl fmt::Display for SliceOrIndex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SliceOrIndex::Index(index) => write!(f, "{}", index)?, - SliceOrIndex::Slice(start, end, step) => { + SliceOrIndex::Slice { start, end, step } => { if start != 0 { write!(f, "{}", start)?; } @@ -149,14 +188,22 @@ impl fmt::Display for SliceOrIndex { impl From for SliceOrIndex { #[inline] fn from(s: Slice) -> SliceOrIndex { - SliceOrIndex::Slice(s.0, s.1, s.2) + SliceOrIndex::Slice { + start: s.start, + end: s.end, + step: s.step, + } } } impl From> for SliceOrIndex { #[inline] fn from(r: Range) -> SliceOrIndex { - SliceOrIndex::Slice(r.start, Some(r.end), 1) + SliceOrIndex::Slice { + start: r.start, + end: Some(r.end), + step: 1, + } } } @@ -170,21 +217,33 @@ impl From for SliceOrIndex { impl From> for SliceOrIndex { #[inline] fn from(r: RangeFrom) -> SliceOrIndex { - SliceOrIndex::Slice(r.start, None, 1) + SliceOrIndex::Slice { + start: r.start, + end: None, + step: 1, + } } } impl From> for SliceOrIndex { #[inline] fn from(r: RangeTo) -> SliceOrIndex { - SliceOrIndex::Slice(0, Some(r.end), 1) + SliceOrIndex::Slice { + start: 0, + end: Some(r.end), + step: 1, + } } } impl From for SliceOrIndex { #[inline] fn from(_: RangeFull) -> SliceOrIndex { - SliceOrIndex::Slice(0, None, 1) + SliceOrIndex::Slice { + start: 0, + end: None, + step: 1, + } } } @@ -483,7 +542,7 @@ macro_rules! s( }; // convert range/index and step into SliceOrIndex (@convert $r:expr, $s:expr) => { - <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r).step($s) + <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r).step_by($s) }; ($($t:tt)*) => { s![@parse ::std::marker::PhantomData::<$crate::Ix0>, [] $($t)*] diff --git a/tests/array.rs b/tests/array.rs index b542f9fae..73b7eedca 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -60,7 +60,7 @@ fn test_slice() *elt = i; } - let vi = A.slice(s![1.., ..;2, Slice(0, None, 2)]); + let vi = A.slice(s![1.., ..;2, Slice::new(0, None, 2)]); assert_eq!(vi.shape(), &[2, 2, 3]); let vi = A.slice(s![.., .., ..]); assert_eq!(vi.shape(), A.shape()); @@ -119,7 +119,7 @@ fn test_slice_array_dyn() { let info = &SliceInfo::<_, IxDyn>::new([ SliceOrIndex::from(1..), SliceOrIndex::from(1), - SliceOrIndex::from(..).step(2), + SliceOrIndex::from(..).step_by(2), ]); arr.slice(info); arr.slice_mut(info); @@ -133,7 +133,7 @@ fn test_slice_dyninput_array_dyn() { let info = &SliceInfo::<_, IxDyn>::new([ SliceOrIndex::from(1..), SliceOrIndex::from(1), - SliceOrIndex::from(..).step(2), + SliceOrIndex::from(..).step_by(2), ]); arr.slice(info); arr.slice_mut(info); @@ -147,7 +147,7 @@ fn test_slice_dyninput_vec_fixed() { let info = &SliceInfo::<_, Ix2>::new(vec![ SliceOrIndex::from(1..), SliceOrIndex::from(1), - SliceOrIndex::from(..).step(2), + SliceOrIndex::from(..).step_by(2), ]); arr.slice(info.as_ref()); arr.slice_mut(info.as_ref()); @@ -161,7 +161,7 @@ fn test_slice_dyninput_vec_dyn() { let info = &SliceInfo::<_, IxDyn>::new(vec![ SliceOrIndex::from(1..), SliceOrIndex::from(1), - SliceOrIndex::from(..).step(2), + SliceOrIndex::from(..).step_by(2), ]); arr.slice(info.as_ref()); arr.slice_mut(info.as_ref()); @@ -252,7 +252,7 @@ fn slice_oob() #[test] fn slice_axis_oob() { let a = RcArray::::zeros((3, 4)); - let _vi = a.slice_axis(Axis(0), Slice(0, Some(10), 1)); + let _vi = a.slice_axis(Axis(0), Slice::new(0, 10, 1)); } #[should_panic] diff --git a/tests/oper.rs b/tests/oper.rs index 1eb427675..5dbc5b923 100644 --- a/tests/oper.rs +++ b/tests/oper.rs @@ -577,11 +577,11 @@ fn scaled_add_3() { vec![n, q] }; let cslice = if n == 1 { - vec![SliceOrIndex::from(..).step(s2)] + vec![SliceOrIndex::from(..).step_by(s2)] } else { vec![ - SliceOrIndex::from(..).step(s1), - SliceOrIndex::from(..).step(s2), + SliceOrIndex::from(..).step_by(s1), + SliceOrIndex::from(..).step_by(s2), ] };