Skip to content

Add support for slicing with inclusive ranges #467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sudo: required
dist: trusty
matrix:
include:
- rust: 1.26.0
- rust: 1.27.0
env:
- FEATURES='test docs'
- rust: stable
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ Recent Changes (ndarray)

- Add ``var_axis`` method for computing variance by @LukeMathWalker.
- Add support for 128-bit integer scalars (``i128`` and ``u128``).
- Add support for slicing with inclusive ranges (``start..=end`` and ``..=end``).
- Bump ``num-traits`` and ``num-complex`` to version ``0.2``.
- Bump ``blas-src`` to version ``0.2``.
- Bump minimum required Rust version to 1.26.
- Bump minimum required Rust version to 1.27.
- Additional contributors to this release: @ExpHP, @jturner314, @alexbool, @messense, @danmack, @nbro

- 0.11.2
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
//! + Efficient floating point matrix multiplication even for very large
//! matrices; can optionally use BLAS to improve it further.
//! + See also the [`ndarray-parallel`] crate for integration with rayon.
//! - **Requires Rust 1.26**
//! - **Requires Rust 1.27**
//!
//! [`ndarray-parallel`]: https://docs.rs/ndarray-parallel
//!
Expand Down
227 changes: 103 additions & 124 deletions src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use error::{ShapeError, ErrorKind};
use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeTo};
use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
use std::fmt;
use std::marker::PhantomData;
use super::Dimension;

/// 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.
/// `end` is an exclusive index. 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.s.html) macro.
///
Expand Down Expand Up @@ -66,58 +67,6 @@ impl Slice {
}
}

macro_rules! impl_slice_from_index_type {
($index:ty) => {
impl From<Range<$index>> for Slice {
#[inline]
fn from(r: Range<$index>) -> Slice {
Slice {
start: r.start as isize,
end: Some(r.end as isize),
step: 1,
}
}
}

impl From<RangeFrom<$index>> for Slice {
#[inline]
fn from(r: RangeFrom<$index>) -> Slice {
Slice {
start: r.start as isize,
end: None,
step: 1,
}
}
}

impl From<RangeTo<$index>> for Slice {
#[inline]
fn from(r: RangeTo<$index>) -> Slice {
Slice {
start: 0,
end: Some(r.end as isize),
step: 1,
}
}
}
}
}

impl_slice_from_index_type!(isize);
impl_slice_from_index_type!(usize);
impl_slice_from_index_type!(i32);

impl From<RangeFull> 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
Expand All @@ -144,9 +93,9 @@ impl From<RangeFull> for Slice {
/// 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.
/// A range with step size. `end` is an exclusive index. 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: isize,
end: Option<isize>,
Expand Down Expand Up @@ -219,64 +168,83 @@ impl fmt::Display for SliceOrIndex {
}
}

impl From<Slice> for SliceOrIndex {
#[inline]
fn from(s: Slice) -> SliceOrIndex {
SliceOrIndex::Slice {
start: s.start,
end: s.end,
step: s.step,
}
}
}

macro_rules! impl_sliceorindex_from_index_type {
($index:ty) => {
impl From<$index> for SliceOrIndex {
macro_rules! impl_slice_variant_from_range {
($self:ty, $constructor:path, $index:ty) => {
impl From<Range<$index>> for $self {
#[inline]
fn from(r: $index) -> SliceOrIndex {
SliceOrIndex::Index(r as isize)
fn from(r: Range<$index>) -> $self {
$constructor {
start: r.start as isize,
end: Some(r.end as isize),
step: 1,
}
}
}

impl From<Range<$index>> for SliceOrIndex {
impl From<RangeInclusive<$index>> for $self {
#[inline]
fn from(r: Range<$index>) -> SliceOrIndex {
SliceOrIndex::Slice {
start: r.start as isize,
end: Some(r.end as isize),
fn from(r: RangeInclusive<$index>) -> $self {
let end = *r.end() as isize;
$constructor {
start: *r.start() as isize,
end: if end == -1 { None } else { Some(end + 1) },
step: 1,
}
}
}

impl From<RangeFrom<$index>> for SliceOrIndex {
impl From<RangeFrom<$index>> for $self {
#[inline]
fn from(r: RangeFrom<$index>) -> SliceOrIndex {
SliceOrIndex::Slice {
fn from(r: RangeFrom<$index>) -> $self {
$constructor {
start: r.start as isize,
end: None,
step: 1,
}
}
}

impl From<RangeTo<$index>> for SliceOrIndex {
impl From<RangeTo<$index>> for $self {
#[inline]
fn from(r: RangeTo<$index>) -> SliceOrIndex {
SliceOrIndex::Slice {
fn from(r: RangeTo<$index>) -> $self {
$constructor {
start: 0,
end: Some(r.end as isize),
step: 1,
}
}
}
}

impl From<RangeToInclusive<$index>> for $self {
#[inline]
fn from(r: RangeToInclusive<$index>) -> $self {
let end = r.end as isize;
$constructor {
start: 0,
end: if end == -1 { None } else { Some(end + 1) },
step: 1,
}
}
}
};
}
impl_slice_variant_from_range!(Slice, Slice, isize);
impl_slice_variant_from_range!(Slice, Slice, usize);
impl_slice_variant_from_range!(Slice, Slice, i32);
impl_slice_variant_from_range!(SliceOrIndex, SliceOrIndex::Slice, isize);
impl_slice_variant_from_range!(SliceOrIndex, SliceOrIndex::Slice, usize);
impl_slice_variant_from_range!(SliceOrIndex, SliceOrIndex::Slice, i32);

impl_sliceorindex_from_index_type!(isize);
impl_sliceorindex_from_index_type!(usize);
impl_sliceorindex_from_index_type!(i32);
impl From<RangeFull> for Slice {
#[inline]
fn from(_: RangeFull) -> Slice {
Slice {
start: 0,
end: None,
step: 1,
}
}
}

impl From<RangeFull> for SliceOrIndex {
#[inline]
Expand All @@ -289,6 +257,31 @@ impl From<RangeFull> for SliceOrIndex {
}
}

impl From<Slice> for SliceOrIndex {
#[inline]
fn from(s: Slice) -> SliceOrIndex {
SliceOrIndex::Slice {
start: s.start,
end: s.end,
step: s.step,
}
}
}

macro_rules! impl_sliceorindex_from_index {
($index:ty) => {
impl From<$index> for SliceOrIndex {
#[inline]
fn from(r: $index) -> SliceOrIndex {
SliceOrIndex::Index(r as isize)
}
}
};
}
impl_sliceorindex_from_index!(isize);
impl_sliceorindex_from_index!(usize);
impl_sliceorindex_from_index!(i32);

/// Represents all of the necessary information to perform a slice.
///
/// The type `T` is typically `[SliceOrIndex; n]`, `[SliceOrIndex]`, or
Expand Down Expand Up @@ -427,49 +420,35 @@ 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
}
}

macro_rules! impl_slicenextdim_for_index_type {
($index:ty) => {
impl<D1: Dimension> SliceNextDim<D1, D1> for $index {
macro_rules! impl_slicenextdim_equal {
($self:ty) => {
impl<D1: Dimension> SliceNextDim<D1, D1> for $self {
fn next_dim(&self, _: PhantomData<D1>) -> PhantomData<D1> {
PhantomData
}
}
}
}

impl_slicenextdim_for_index_type!(isize);
impl_slicenextdim_for_index_type!(usize);
impl_slicenextdim_for_index_type!(i32);

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

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

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

impl<D1: Dimension> SliceNextDim<D1, D1::Larger> for RangeFull {
fn next_dim(&self, _: PhantomData<D1>) -> PhantomData<D1::Larger> {
PhantomData
impl_slicenextdim_equal!(isize);
impl_slicenextdim_equal!(usize);
impl_slicenextdim_equal!(i32);

macro_rules! impl_slicenextdim_larger {
(($($generics:tt)*), $self:ty) => {
impl<D1: Dimension, $($generics),*> SliceNextDim<D1, D1::Larger> for $self {
fn next_dim(&self, _: PhantomData<D1>) -> PhantomData<D1::Larger> {
PhantomData
}
}
}
}
impl_slicenextdim_larger!((T), Range<T>);
impl_slicenextdim_larger!((T), RangeInclusive<T>);
impl_slicenextdim_larger!((T), RangeFrom<T>);
impl_slicenextdim_larger!((T), RangeTo<T>);
impl_slicenextdim_larger!((T), RangeToInclusive<T>);
impl_slicenextdim_larger!((), RangeFull);
impl_slicenextdim_larger!((), Slice);

/// Slice argument constructor.
///
Expand Down
8 changes: 8 additions & 0 deletions tests/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ fn test_slice()
assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b));
}

#[test]
fn test_slice_inclusive_range() {
let arr = array![[1, 2, 3], [4, 5, 6]];
assert_eq!(arr.slice(s![1..=1, 1..=2]), array![[5, 6]]);
assert_eq!(arr.slice(s![1..=-1, -2..=2;-1]), array![[6, 5]]);
assert_eq!(arr.slice(s![0..=-1, 0..=2;2]), array![[1, 3], [4, 6]]);
}

/// Test that the compiler can infer a type for a sliced array from the
/// arguments to `s![]`.
///
Expand Down