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..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}; @@ -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. /// @@ -48,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`, @@ -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 { @@ -239,69 +252,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 +348,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( @@ -425,7 +363,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 +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 } @@ -456,7 +400,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 +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) { @@ -544,7 +494,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 +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); @@ -674,7 +630,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 +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); @@ -785,7 +748,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 +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 { @@ -831,7 +800,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 +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()); diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 541f96429..fee9e9c37 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] +pub 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 e3f3e2dea..171670893 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::{abs_index, axes_of, Axes, do_slice, merge_axes, stride_offset}; use iterators::{ new_lanes, new_lanes_mut, @@ -31,6 +31,9 @@ use zip::Zip; use { NdIndex, + Slice, + SliceInfo, + SliceOrIndex }; use iter::{ AxisChunksIter, @@ -206,47 +209,149 @@ 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 `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, slice_or_index)| match slice_or_index { + &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); + 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, indices: Slice) -> ArrayView { + let mut view = self.view(); + view.slice_axis_inplace(axis, indices); + 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, indices: Slice) -> ArrayViewMut + where + S: DataMut, + { + let mut view_mut = self.view_mut(); + view_mut.slice_axis_inplace(axis, indices); + 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, indices: Slice) { + let offset = do_slice( + &mut self.dim.slice_mut()[axis.index()], + &mut self.strides.slice_mut()[axis.index()], + indices.start, + indices.end, + indices.step, + ); unsafe { self.ptr = self.ptr.offset(offset); } @@ -429,7 +534,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 +546,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 +577,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..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 si::{Si, S}; +pub use slice::{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,21 +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.) +/// 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 /// /// ``` /// // import the s![] macro /// #[macro_use(s)] /// extern crate ndarray; /// -/// use ndarray::arr3; +/// use ndarray::{arr2, arr3}; /// /// fn main() { /// @@ -456,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); @@ -472,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]`. /// @@ -514,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 2b4c45b07..000000000 --- a/src/si.rs +++ /dev/null @@ -1,177 +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..41788ca6d --- /dev/null +++ b/src/slice.rs @@ -0,0 +1,550 @@ +// 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 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::new(0, None, 1)` is the full range of an axis. It can also be +/// created with `Slice::from(..)`. The Python equivalent is `[:]`. +/// +/// `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::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 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_by(self, step: Ixs) -> Self { + Slice { step, ..self } + } +} + +impl From> for Slice { + #[inline] + fn from(r: Range) -> Slice { + Slice { + start: r.start, + end: Some(r.end), + step: 1, + } + } +} + +impl From> for Slice { + #[inline] + fn from(r: RangeFrom) -> Slice { + Slice { + start: r.start, + end: None, + step: 1, + } + } +} + +impl From> for Slice { + #[inline] + fn from(r: RangeTo) -> Slice { + Slice { + start: 0, + end: Some(r.end), + step: 1, + } + } +} + +impl From for Slice { + #[inline] + fn from(_: RangeFull) -> Slice { + Slice { + start: 0, + end: None, + step: 1, + } + } +} + +/// 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::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 { 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 { 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 { 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. 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), +} + +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_by(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(s: Slice) -> SliceOrIndex { + SliceOrIndex::Slice { + start: s.start, + end: s.end, + step: s.step, + } + } +} + +impl From> for SliceOrIndex { + #[inline] + fn from(r: Range) -> SliceOrIndex { + SliceOrIndex::Slice { + start: r.start, + end: Some(r.end), + step: 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 { + start: r.start, + end: None, + step: 1, + } + } +} + +impl From> for SliceOrIndex { + #[inline] + fn from(r: RangeTo) -> SliceOrIndex { + SliceOrIndex::Slice { + start: 0, + end: Some(r.end), + step: 1, + } + } +} + +impl From for SliceOrIndex { + #[inline] + fn from(_: RangeFull) -> SliceOrIndex { + SliceOrIndex::Slice { + start: 0, + end: None, + step: 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() + }) + } +} + +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 Slice { + fn next_dim(&self, _: PhantomData) -> 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/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/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: +/// +/// * *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*, *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 +/// +/// 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 @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 @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 @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;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 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 range/index into SliceOrIndex + (@convert $r:expr) => { + <$crate::SliceOrIndex as ::std::convert::From<_>>::from($r) + }; + // convert range/index and step into SliceOrIndex + (@convert $r:expr, $s:expr) => { + <$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 740b6f7e0..73b7eedca 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::{Slice, SliceInfo, SliceOrIndex}; use ndarray::prelude::*; use ndarray::{ rcarr2, @@ -55,18 +55,184 @@ 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, S]); + 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()); 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_by(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_by(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_by(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_by(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() { @@ -79,7 +245,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), Slice::new(0, 10, 1)); } #[should_panic] @@ -102,7 +275,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 +344,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 +376,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 +401,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 +416,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 +540,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 +840,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 +848,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 +1246,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 +1411,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 +1430,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 7c7918372..5dbc5b923 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_by(s2)] } else { - vec![Si(0, None, s1), Si(0, None, s2)] + vec![ + SliceOrIndex::from(..).step_by(s1), + SliceOrIndex::from(..).step_by(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);