Skip to content

Commit

Permalink
Add Slice type and use it for .slice_axis*()
Browse files Browse the repository at this point in the history
  • Loading branch information
jturner314 committed Nov 3, 2017
1 parent 8413825 commit 2d4ceab
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 41 deletions.
37 changes: 10 additions & 27 deletions src/impl_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use zip::Zip;

use {
NdIndex,
Slice,
SliceInfo,
SliceOrIndex
};
Expand Down Expand Up @@ -307,7 +308,7 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
.enumerate()
.for_each(|(axis, slice_or_index)| match slice_or_index {
&SliceOrIndex::Slice(start, end, step) => {
self.slice_axis_inplace(Axis(axis), start, end, step)
self.slice_axis_inplace(Axis(axis), Slice(start, end, step))
}
&SliceOrIndex::Index(index) => {
let i_usize = abs_index(self.len_of(Axis(axis)), index);
Expand All @@ -320,54 +321,36 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
///
/// **Panics** if an index is out of bounds or step size is zero.<br>
/// **Panics** if `axis` is out of bounds.
pub fn slice_axis(
&self,
axis: Axis,
start: Ixs,
end: Option<Ixs>,
step: Ixs,
) -> ArrayView<A, D> {
pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView<A, D> {
let mut view = self.view();
view.slice_axis_inplace(axis, start, end, step);
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.<br>
/// **Panics** if `axis` is out of bounds.
pub fn slice_axis_mut(
&mut self,
axis: Axis,
start: Ixs,
end: Option<Ixs>,
step: Ixs,
) -> ArrayViewMut<A, D>
pub fn slice_axis_mut(&mut self, axis: Axis, indices: Slice) -> ArrayViewMut<A, D>
where
S: DataMut,
{
let mut view_mut = self.view_mut();
view_mut.slice_axis_inplace(axis, start, end, step);
view_mut.slice_axis_inplace(axis, indices);
view_mut
}

/// Slice the array in place along the specified axis.
///
/// **Panics** if an index is out of bounds or step size is zero.<br>
/// **Panics** if `axis` is out of bounds.
pub fn slice_axis_inplace(
&mut self,
axis: Axis,
start: Ixs,
end: Option<Ixs>,
step: Ixs,
) {
pub fn slice_axis_inplace(&mut self, axis: Axis, indices: Slice) {
let offset = do_slice(
&mut self.dim.slice_mut()[axis.index()],
&mut self.strides.slice_mut()[axis.index()],
start,
end,
step,
indices.0,
indices.1,
indices.2,
);
unsafe {
self.ptr = self.ptr.offset(offset);
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub use dimension::NdIndex;
pub use dimension::IxDynImpl;
pub use indexes::{indices, indices_of};
pub use error::{ShapeError, ErrorKind};
pub use slice::{SliceInfo, SliceNextDim, SliceOrIndex};
pub use slice::{Slice, SliceInfo, SliceNextDim, SliceOrIndex};

use iterators::Baseiter;
use iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut};
Expand Down
89 changes: 82 additions & 7 deletions src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,70 @@ use std::fmt;
use std::marker::PhantomData;
use super::{Dimension, Ixs};

/// A slice (range with step size).
///
/// ## Examples
///
/// `Slice(0, None, 1)` is the full range of an axis. It can also be created
/// with `Slice::from(..)`. The Python equivalent is `[:]`.
///
/// `Slice(a, Some(b), 2)` is every second element from `a` until `b`. It can
/// also be created with `Slice::from(a..b).step(2)`. The Python equivalent is
/// `[a:b:2]`.
///
/// `Slice(a, None, -1)` is every element, from `a` until the end, in reverse
/// order. It can also be created with `Slice::from(a..).step(-1)`. The Python
/// equivalent is `[a::-1]`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Slice(pub Ixs, pub Option<Ixs>, pub Ixs);

impl Slice {
/// Returns a new `Slice` with the given step size.
#[inline]
pub fn step(self, step: Ixs) -> Self {
Slice(self.0, self.1, step)
}
}

impl From<Range<Ixs>> for Slice {
#[inline]
fn from(r: Range<Ixs>) -> Slice {
Slice(r.start, Some(r.end), 1)
}
}

impl From<RangeFrom<Ixs>> for Slice {
#[inline]
fn from(r: RangeFrom<Ixs>) -> Slice {
Slice(r.start, None, 1)
}
}

impl From<RangeTo<Ixs>> for Slice {
#[inline]
fn from(r: RangeTo<Ixs>) -> Slice {
Slice(0, Some(r.end), 1)
}
}

impl From<RangeFull> for Slice {
#[inline]
fn from(_: RangeFull) -> Slice {
Slice(0, None, 1)
}
}

/// A slice (range with step) or an index.
///
/// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a
/// `&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(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![..]`.
Expand Down Expand Up @@ -89,6 +146,13 @@ impl fmt::Display for SliceOrIndex {
}
}

impl From<Slice> for SliceOrIndex {
#[inline]
fn from(s: Slice) -> SliceOrIndex {
SliceOrIndex::Slice(s.0, s.1, s.2)
}
}

impl From<Range<Ixs>> for SliceOrIndex {
#[inline]
fn from(r: Range<Ixs>) -> SliceOrIndex {
Expand Down Expand Up @@ -261,6 +325,12 @@ pub trait SliceNextDim<D1, D2> {
fn next_dim(&self, PhantomData<D1>) -> PhantomData<D2>;
}

impl<D1: Dimension> SliceNextDim<D1, D1::Larger> for Slice {
fn next_dim(&self, _: PhantomData<D1>) -> PhantomData<D1::Larger> {
PhantomData
}
}

impl<D1: Dimension> SliceNextDim<D1, D1::Larger> for Range<Ixs> {
fn next_dim(&self, _: PhantomData<D1>) -> PhantomData<D1::Larger> {
PhantomData
Expand Down Expand Up @@ -293,26 +363,31 @@ impl<D1: Dimension> SliceNextDim<D1, D1> for Ixs {

/// Slice argument constructor.
///
/// `s![]` takes a list of ranges/indices, separated by comma, with optional
/// step sizes that are separated from the range by a semicolon. It is
/// `s![]` takes a list of ranges/slices/indices, separated by comma, with
/// optional step sizes that are separated from the range by a semicolon. It is
/// converted into a [`&SliceInfo`] instance.
///
/// [`&SliceInfo`]: struct.SliceInfo.html
///
/// Each range/index uses signed indices, where a negative value is counted
/// from the end of the axis. Step sizes are also signed and may be negative,
/// but must not be zero.
/// Each range/slice/index uses signed indices, where a negative value is
/// counted from the end of the axis. Step sizes are also signed and may be
/// negative, but must not be zero.
///
/// The syntax is `s![` *[ axis-slice-or-index [, axis-slice-or-index [ , ... ]
/// ] ]* `]`, where *axis-slice-or-index* is any of the following:
///
/// * *index*: an index to use for taking a subview with respect to that axis
/// * *range*: a range with step size 1 to use for slicing that axis
/// * *range* `;` *step*: a range with step size *step* to use for slicing that axis
/// * *slice*: a [`Slice`] instance to use for slicing that axis
/// * *slice* `;` *step*: a range constructed from the start and end of a [`Slice`]
/// instance, with new step size *step*, to use for slicing that axis
///
/// [`Slice`]: struct.Slice.html
///
/// The number of *axis-slice-or-index* must match the number of axes in the
/// array. *index*, *range*, and *step* can be expressions. *index* and *step*
/// must be of type [`Ixs`]. *range* can be of type `Range<Ixs>`,
/// array. *index*, *range*, *slice*, and *step* can be expressions. *index*
/// and *step* must be of type [`Ixs`]. *range* can be of type `Range<Ixs>`,
/// `RangeTo<Ixs>`, `RangeFrom<Ixs>`, or `RangeFull`.
///
/// [`Ixs`]: type.Ixs.html
Expand Down
12 changes: 6 additions & 6 deletions tests/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extern crate ndarray;
extern crate defmac;
extern crate itertools;

use ndarray::{SliceInfo, SliceOrIndex};
use ndarray::{Slice, SliceInfo, SliceOrIndex};
use ndarray::prelude::*;
use ndarray::{
rcarr2,
Expand Down Expand Up @@ -55,14 +55,14 @@ fn test_mat_mul() {
#[test]
fn test_slice()
{
let mut A = RcArray::<usize, _>::zeros((3, 4));
let mut A = RcArray::<usize, _>::zeros((3, 4, 5));
for (i, elt) in A.iter_mut().enumerate() {
*elt = i;
}

let vi = A.slice(s![1.., ..;2]);
assert_eq!(vi.shape(), &[2, 2]);
let vi = A.slice(s![.., ..]);
let vi = A.slice(s![1.., ..;2, Slice(0, None, 2)]);
assert_eq!(vi.shape(), &[2, 2, 3]);
let vi = A.slice(s![.., .., ..]);
assert_eq!(vi.shape(), A.shape());
assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b));
}
Expand Down Expand Up @@ -252,7 +252,7 @@ fn slice_oob()
#[test]
fn slice_axis_oob() {
let a = RcArray::<i32, _>::zeros((3, 4));
let _vi = a.slice_axis(Axis(0), 0, Some(10), 1);
let _vi = a.slice_axis(Axis(0), Slice(0, Some(10), 1));
}

#[should_panic]
Expand Down

0 comments on commit 2d4ceab

Please sign in to comment.