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' 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/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index bc288e178..5401ca00e 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -7,13 +7,14 @@ // except according to those terms. +use std::convert::AsRef; use std::fmt::Debug; use std::ops::{Index, IndexMut}; 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}; @@ -41,21 +42,24 @@ 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. /// /// For the fixed size dimensions it is a fixed size array of the correct - /// size, which you pass by reference. For the dynamic dimension it is - /// a slice. + /// size. 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`, @@ -152,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 { @@ -240,65 +253,57 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + } #[doc(hidden)] - /// Modify dimension, strides and return data pointer offset + /// Modify dimension, stride 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(); + /// **Panics** if any stride is 0 or if any index is out of bounds. + fn do_slice(dim: &mut Ix, stride: &mut Ix, start: isize, end: Option, step: isize) -> isize { 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 dr = dim; + let sr = stride; + + let m = *dr; + let mi = m as Ixs; + let (b1, opt_e1, s1) = (start, end, step); + 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, + "Slice begin {} is past end of axis of length {}", + b1, m); + ndassert!(e1 <= m, + "Slice end {} is past end of axis of length {}", + e1, m); + + let m = e1 - b1; + // stride + let s = (*sr) as Ixs; + + // Data pointer offset + offset += stride_offset(b1, *sr); + // Adjust for strides + ndassert!(s1 != 0, "Slice stride must not be none"); + // 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 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 }; + 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; - // Update dimension and stride coordinate - *dr = m_prim; - *sr = s_prim as Ix; - } offset } @@ -401,7 +406,7 @@ pub trait Dimension : Clone + Eq + Debug + Send + Sync + Default + // utility functions #[inline] -fn abs_index(len: Ixs, index: Ixs) -> Ix { +pub fn abs_index(len: Ixs, index: Ixs) -> Ix { if index < 0 { (len + index) as Ix } else { @@ -425,7 +430,8 @@ macro_rules! impl_insert_axis_array( ); impl Dimension for Dim<[Ix; 0]> { - type SliceArg = [Si; 0]; + const NDIM: Option = Some(0); + type SliceArg = [SliceOrIndex; 0]; type Pattern = (); type Smaller = Self; type Larger = Ix1; @@ -441,6 +447,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 } @@ -456,7 +467,8 @@ impl Dimension for Dim<[Ix; 0]> { impl Dimension for Dim<[Ix; 1]> { - type SliceArg = [Si; 1]; + const NDIM: Option = Some(1); + type SliceArg = [SliceOrIndex; 1]; type Pattern = Ix; type Smaller = Ix0; type Larger = Ix2; @@ -471,6 +483,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) { @@ -544,7 +561,8 @@ impl Dimension for Dim<[Ix; 1]> { } impl Dimension for Dim<[Ix; 2]> { - type SliceArg = [Si; 2]; + const NDIM: Option = Some(2); + type SliceArg = [SliceOrIndex; 2]; type Pattern = (Ix, Ix); type Smaller = Ix1; type Larger = Ix3; @@ -559,6 +577,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); @@ -674,7 +697,8 @@ impl Dimension for Dim<[Ix; 2]> { } impl Dimension for Dim<[Ix; 3]> { - type SliceArg = [Si; 3]; + const NDIM: Option = Some(3); + type SliceArg = [SliceOrIndex; 3]; type Pattern = (Ix, Ix, Ix); type Smaller = Ix2; type Larger = Ix4; @@ -697,6 +721,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); @@ -785,7 +815,8 @@ 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]> { - type SliceArg = [Si; $n]; + const NDIM: Option = Some($n); + type SliceArg = [SliceOrIndex; $n]; type Pattern = $pattern; type Smaller = Dim<[Ix; $n - 1]>; type Larger = $larger; @@ -800,6 +831,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 { @@ -831,7 +867,8 @@ 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 { - type SliceArg = [Si]; + const NDIM: Option = None; + type SliceArg = [SliceOrIndex]; type Pattern = Self; type Smaller = Self; type Larger = Self; @@ -851,6 +888,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()); diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 541f96429..41e192f24 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -12,7 +12,7 @@ use error::{from_kind, ErrorKind, ShapeError}; pub use self::dim::*; pub use self::axis::Axis; pub use self::conversion::IntoDimension; -pub use self::dimension_trait::Dimension; +pub use self::dimension_trait::{abs_index, Dimension}; pub use self::ndindex::NdIndex; pub use self::remove_axis::RemoveAxis; pub use self::axes::{axes_of, Axes, AxisDescription}; diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 052c31cec..6987b3e62 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::convert::AsRef; use std::cmp; use std::ptr as std_ptr; use std::slice; @@ -19,7 +20,7 @@ use dimension; use iterators; use error::{self, ShapeError, ErrorKind}; use dimension::IntoDimension; -use dimension::{axes_of, Axes, merge_axes, stride_offset}; +use dimension::{abs_index, axes_of, Axes, merge_axes, stride_offset}; use iterators::{ new_lanes, new_lanes_mut, @@ -31,6 +32,8 @@ use zip::Zip; use { NdIndex, + SliceInfo, + SliceOrIndex }; use iter::{ AxisChunksIter, @@ -206,47 +209,167 @@ 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.islice(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, { - let mut arr = self.view_mut(); - arr.islice(indexes); - arr + 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, + { + // 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 islice(&mut self, indexes: &D::SliceArg) { - let offset = D::do_slices(&mut self.dim, &mut self.strides, indexes); + /// **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_inplace(&mut self, indices: &D::SliceArg) { + let indices: &[SliceOrIndex] = indices.as_ref(); + assert_eq!(indices.len(), self.ndim()); + indices + .iter() + .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) + } + &SliceOrIndex::Index(i) => { + let i_usize = abs_index(self.shape()[axis] as Ixs, i); + self.subview_inplace(Axis(axis), i_usize) + } + }); + } + + /// 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 arr = self.view(); + arr.slice_axis_inplace(axis, start, end, step); + arr + } + + /// 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 arr = self.view_mut(); + arr.slice_axis_inplace(axis, start, end, step); + arr + } + + /// 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 = D::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); } @@ -429,7 +552,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 +564,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 +595,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 5dac23cff..1fbc71e22 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::{SliceInfo, SliceNextDim, SliceOrIndex}; 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; @@ -421,20 +421,31 @@ 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()`], +/// [`.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.) -/// [`Si`]: struct.Si.html +/// which will be used in all examples. (The explicit form is an instance of +/// [`&SliceInfo`]; see its docs for more information.) +/// +/// [`&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 /// /// ``` /// // import the s![] macro /// #[macro_use(s)] /// extern crate ndarray; /// -/// use ndarray::arr3; +/// use ndarray::{arr2, arr3}; /// /// fn main() { /// @@ -455,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); @@ -471,19 +480,42 @@ 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]); /// } /// ``` /// /// ## 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()`]. You +/// can also take a subview by using a single index instead of a range when +/// slicing. /// /// 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}; +/// #[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]`. /// @@ -513,10 +545,15 @@ 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])); +/// # } /// ``` /// -/// `.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) diff --git a/src/si.rs b/src/si.rs deleted file mode 100644 index 2e6bc80a1..000000000 --- a/src/si.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2014-2016 bluss and ndarray developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , 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::fmt; -use super::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. -/// -/// See also the [`s![] macro`](macro.s!.html), a convenient way to specify -/// an array of `Si`. -/// -/// ## Examples -/// -/// `Si(0, None, 1)` is the full range of an axis. -/// Python equivalent is `[:]`. 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]`. -/// -/// `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); - -impl fmt::Debug for Si { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Si(0, _, _) => { } - Si(i, _, _) => { try!(write!(f, "{}", i)); } - } - try!(write!(f, "..")); - match *self { - Si(_, None, _) => { } - Si(_, Some(i), _) => { try!(write!(f, "{}", i)); } - } - match *self { - Si(_, _, 1) => { } - Si(_, _, s) => { try!(write!(f, ";{}", s)); } - } - Ok(()) - } -} - -impl From> for Si { - #[inline] - fn from(r: Range) -> Si { - Si(r.start, Some(r.end), 1) - } -} - -impl From> for Si { - #[inline] - fn from(r: RangeFrom) -> Si { - Si(r.start, None, 1) - } -} - -impl From> for Si { - #[inline] - fn from(r: RangeTo) -> Si { - Si(0, Some(r.end), 1) - } -} - -impl From for Si { - #[inline] - fn from(_: RangeFull) -> Si { - S - } -} - - -impl Si { - #[inline] - pub fn step(self, step: Ixs) -> Self { - Si(self.0, self.1, self.2 * step) - } -} - -copy_and_clone!{Si} - -/// Slice value for the full range of an axis. -pub const S: Si = Si(0, None, 1); - -/// 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]`. -/// -/// 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. -/// -/// 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. -/// -/// 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]`. -/// -/// 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)]`. -/// -/// ``` -/// #[macro_use] -/// extern crate ndarray; -/// -/// use ndarray::{Array2, ArrayView2}; -/// -/// fn laplacian(v: &ArrayView2) -> Array2 { -/// -4. * &v.slice(s![1..-1, 1..-1]) -/// + v.slice(s![ ..-2, 1..-1]) -/// + v.slice(s![1..-1, ..-2]) -/// + v.slice(s![1..-1, 2.. ]) -/// + v.slice(s![2.. , 1..-1]) -/// } -/// # fn main() { } -/// ``` -#[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 into @step(a..b, 1), final item - (@parse [$($stack:tt)*] $r:expr) => { - &[$($stack)* s!(@step $r, 1)] - }; - // 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 into @step(a..b, 1), final item, trailing comma - (@parse [$($stack:tt)*] $r:expr ,) => { - &[$($stack)* s!(@step $r, 1)] - }; - // 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 @step(a..b, 1) - (@parse [$($stack:tt)*] $r:expr, $($t:tt)*) => { - s![@parse [$($stack)* s!(@step $r, 1),] $($t)*] - }; - // convert range, step into Si - (@step $r:expr, $s:expr) => { - <$crate::Si as ::std::convert::From<_>>::from($r).step($s) - }; - ($($t:tt)*) => { - s![@parse [] $($t)*] - }; -); diff --git a/src/slice.rs b/src/slice.rs new file mode 100644 index 000000000..8cca262b8 --- /dev/null +++ b/src/slice.rs @@ -0,0 +1,403 @@ +// Copyright 2014-2016 bluss and ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeTo}; +use std::fmt; +use std::marker::PhantomData; +use super::{Dimension, Ixs}; + +/// A slice (range with step) or an index. +/// +/// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a +/// `&SliceInfo<[SliceOrIndex; n], D>`. +/// +/// ## Examples +/// +/// `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(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(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), +} + +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, + } + } + + /// 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 { + 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 SliceOrIndex { + #[inline] + fn from(r: Range) -> SliceOrIndex { + SliceOrIndex::Slice(r.start, Some(r.end), 1) + } +} + +impl From for SliceOrIndex { + #[inline] + fn from(r: Ixs) -> SliceOrIndex { + SliceOrIndex::Index(r) + } +} + +impl From> for SliceOrIndex { + #[inline] + fn from(r: RangeFrom) -> SliceOrIndex { + SliceOrIndex::Slice(r.start, None, 1) + } +} + +impl From> for SliceOrIndex { + #[inline] + fn from(r: RangeTo) -> SliceOrIndex { + SliceOrIndex::Slice(0, Some(r.end), 1) + } +} + +impl From for SliceOrIndex { + #[inline] + 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 +#[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() + }) + } +} + +impl AsRef<[SliceOrIndex]> for SliceInfo +where + T: AsRef<[SliceOrIndex]>, + D: Dimension, +{ + fn as_ref(&self) -> &[SliceOrIndex] { + self.indices.as_ref() + } +} + +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/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. +/// +/// The syntax is `s![` *[ axis-slice-or-index [, axis-slice-or-index [ , ... ] +/// ] ]* `]`, where *axis-slice-or-index* is any of the following: +/// +/// * *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 +/// +/// 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] +/// extern crate ndarray; +/// +/// use ndarray::{Array2, ArrayView2}; +/// +/// fn laplacian(v: &ArrayView2) -> Array2 { +/// -4. * &v.slice(s![1..-1, 1..-1]) +/// + v.slice(s![ ..-2, 1..-1]) +/// + v.slice(s![1..-1, ..-2]) +/// + v.slice(s![1..-1, 2.. ]) +/// + v.slice(s![2.. , 1..-1]) +/// } +/// # fn main() { } +/// ``` +#[macro_export] +macro_rules! s( + // convert a..b;c into @step(a..b, c), final item + (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@step $r, $s)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } + }; + // convert a..b into @step(a..b, 1), final item + (@parse $dim:expr, [$($stack:tt)*] $r:expr) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@step $r, 1)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } + }; + // convert a..b;c into @step(a..b, c), final item, trailing comma + (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr ,) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@step $r, $s)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } + }; + // convert a..b into @step(a..b, 1), final item, trailing comma + (@parse $dim:expr, [$($stack:tt)*] $r:expr ,) => { + unsafe { + &$crate::SliceInfo::new_unchecked( + [$($stack)* s!(@step $r, 1)], + $crate::SliceNextDim::next_dim(&$r, $dim), + ) + } + }; + // convert a..b;c into @step(a..b, c) + (@parse $dim:expr, [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => { + s![@parse $crate::SliceNextDim::next_dim(&$r, $dim), [$($stack)* s!(@step $r, $s),] $($t)*] + }; + // convert a..b into @step(a..b, 1) + (@parse $dim:expr, [$($stack:tt)*] $r:expr, $($t:tt)*) => { + s![@parse $crate::SliceNextDim::next_dim(&$r, $dim), [$($stack)* s!(@step $r, 1),] $($t)*] + }; + // convert range, step into SliceOrIndex + (@step $r:expr, $s:expr) => { + <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r).step($s) + }; + ($($t:tt)*) => { + s![@parse ::std::marker::PhantomData::<$crate::Ix0>, [] $($t)*] + }; +); diff --git a/tests/array.rs b/tests/array.rs index 740b6f7e0..444f47da6 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -6,7 +6,7 @@ extern crate ndarray; extern crate defmac; extern crate itertools; -use ndarray::{S, Si}; +use ndarray::{SliceInfo, SliceOrIndex}; use ndarray::prelude::*; use ndarray::{ rcarr2, @@ -62,11 +62,146 @@ 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)); } +#[test] +fn test_slice_array_fixed() +{ + let mut arr = Array2::::zeros((5, 5)); + let info = s![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 = Array2::::zeros((5, 5)).into_dyn(); + let info = s![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 = Array2::::zeros((5, 5)); + let info = &SliceInfo::<_, IxDyn>::new([ + 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 = Array2::::zeros((5, 5)).into_dyn(); + let info = &SliceInfo::<_, IxDyn>::new([ + 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 = Array2::::zeros((5, 5)).into_dyn(); + let info = &SliceInfo::<_, Ix2>::new(vec![ + 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 = Array2::::zeros((5, 5)).into_dyn(); + let info = &SliceInfo::<_, IxDyn>::new(vec![ + 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 A = RcArray::::zeros((3, 5, 4)); + for (i, elt) in A.iter_mut().enumerate() { + *elt = i; + } + + let vi = A.slice(s![1.., 2, ..;2]); + assert_eq!(vi.shape(), &[2, 2]); + assert!( + vi.iter() + .zip(A.subview(Axis(1), 2).slice(s![1.., ..;2]).iter()) + .all(|(a, b)| a == b) + ); + + let vi = A.slice(s![1, 2, ..;2]); + assert_eq!(vi.shape(), &[2]); + assert!( + vi.iter() + .zip( + A.subview(Axis(0), 1) + .subview(Axis(0), 2) + .slice(s![..;2]) + .iter() + ) + .all(|(a, b)| a == b) + ); +} + +#[test] +fn test_slice_inplace_with_subview_inplace() +{ + let mut A = RcArray::::zeros((3, 5, 4)); + for (i, elt) in A.iter_mut().enumerate() { + *elt = i; + } + + let mut vi = A.view(); + vi.slice_inplace(s![1.., 2, ..;2]); + assert_eq!(vi.shape(), &[2, 1, 2]); + assert!( + vi.iter() + .zip(A.slice(s![1.., 2..3, ..;2]).iter()) + .all(|(a, b)| a == b) + ); + + let mut vi = A.view(); + vi.slice_inplace(s![1, 2, ..;2]); + assert_eq!(vi.shape(), &[1, 1, 2]); + assert!( + vi.iter() + .zip(A.slice(s![1..2, 2..3, ..;2]).iter()) + .all(|(a, b)| a == b) + ); +} + #[should_panic] #[test] fn index_out_of_bounds() { @@ -79,7 +214,14 @@ 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] +#[test] +fn slice_axis_oob() { + let a = RcArray::::zeros((3, 4)); + let _vi = a.slice_axis(Axis(0), 0, Some(10), 1); } #[should_panic] @@ -102,7 +244,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]]); @@ -171,7 +313,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.]; @@ -203,7 +345,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(s![..;-1]); assert_eq!(rev[0], 4); assert_eq!(rev[1], 3); assert_eq!(rev[2], 2); @@ -228,7 +370,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 +385,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 +509,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(s![..1, ..]); v.fill(0); } assert_eq!(a, arr2(&[[0, 0], [3, 4]])); @@ -667,7 +809,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]])); @@ -675,14 +817,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], @@ -1073,7 +1215,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 +1380,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 +1399,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); } 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 fb6515285..bb4a6fc2e 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 num_traits::Float; @@ -561,16 +560,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);