ndarray/
slice.rs

1// Copyright 2014-2016 bluss and ndarray developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8use crate::dimension::slices_intersect;
9use crate::error::{ErrorKind, ShapeError};
10use crate::{ArrayViewMut, DimAdd, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn};
11use alloc::vec::Vec;
12use std::convert::TryFrom;
13use std::fmt;
14use std::marker::PhantomData;
15use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
16
17/// A slice (range with step size).
18///
19/// `end` is an exclusive index. Negative `begin` or `end` indexes are counted
20/// from the back of the axis. If `end` is `None`, the slice extends to the end
21/// of the axis.
22///
23/// See also the [`s![]`](macro.s.html) macro.
24///
25/// ## Examples
26///
27/// `Slice::new(0, None, 1)` is the full range of an axis. It can also be
28/// created with `Slice::from(..)`. The Python equivalent is `[:]`.
29///
30/// `Slice::new(a, b, 2)` is every second element from `a` until `b`. It can
31/// also be created with `Slice::from(a..b).step_by(2)`. The Python equivalent
32/// is `[a:b:2]`.
33///
34/// `Slice::new(a, None, -1)` is every element, from `a` until the end, in
35/// reverse order. It can also be created with `Slice::from(a..).step_by(-1)`.
36/// The Python equivalent is `[a::-1]`.
37#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
38pub struct Slice {
39    pub start: isize,
40    pub end: Option<isize>,
41    pub step: isize,
42}
43
44impl Slice {
45    /// Create a new `Slice` with the given extents.
46    ///
47    /// See also the `From` impls, converting from ranges; for example
48    /// `Slice::from(i..)` or `Slice::from(j..k)`.
49    ///
50    /// `step` must be nonzero.
51    /// (This method checks with a debug assertion that `step` is not zero.)
52    pub fn new(start: isize, end: Option<isize>, step: isize) -> Slice {
53        debug_assert_ne!(step, 0, "Slice::new: step must be nonzero");
54        Slice { start, end, step }
55    }
56
57    /// Create a new `Slice` with the given step size (multiplied with the
58    /// previous step size).
59    ///
60    /// `step` must be nonzero.
61    /// (This method checks with a debug assertion that `step` is not zero.)
62    #[inline]
63    pub fn step_by(self, step: isize) -> Self {
64        debug_assert_ne!(step, 0, "Slice::step_by: step must be nonzero");
65        Slice {
66            step: self.step * step,
67            ..self
68        }
69    }
70}
71
72/// Token to represent a new axis in a slice description.
73///
74/// See also the [`s![]`](macro.s!.html) macro.
75#[derive(Clone, Copy, Debug)]
76pub struct NewAxis;
77
78/// A slice (range with step), an index, or a new axis token.
79///
80/// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a
81/// `SliceInfo<[SliceInfoElem; n], Din, Dout>`.
82///
83/// ## Examples
84///
85/// `SliceInfoElem::Index(a)` is the index `a`. It can also be created with
86/// `SliceInfoElem::from(a)`. The Python equivalent is `[a]`. The macro
87/// equivalent is `s![a]`.
88///
89/// `SliceInfoElem::Slice { start: 0, end: None, step: 1 }` is the full range
90/// of an axis. It can also be created with `SliceInfoElem::from(..)`. The
91/// Python equivalent is `[:]`. The macro equivalent is `s![..]`.
92///
93/// `SliceInfoElem::Slice { start: a, end: Some(b), step: 2 }` is every second
94/// element from `a` until `b`. It can also be created with
95/// `SliceInfoElem::from(Slice::from(a..b).step_by(2))`. The Python equivalent
96/// is `[a:b:2]`. The macro equivalent is `s![a..b;2]`.
97///
98/// `SliceInfoElem::Slice { start: a, end: None, step: -1 }` is every element,
99/// from `a` until the end, in reverse order. It can also be created with
100/// `SliceInfoElem::from(Slice::from(a..).step_by(-1))`. The Python equivalent
101/// is `[a::-1]`. The macro equivalent is `s![a..;-1]`.
102///
103/// `SliceInfoElem::NewAxis` is a new axis of length 1. It can also be created
104/// with `SliceInfoElem::from(NewAxis)`. The Python equivalent is
105/// `[np.newaxis]`. The macro equivalent is `s![NewAxis]`.
106#[derive(Debug, PartialEq, Eq, Hash)]
107pub enum SliceInfoElem {
108    /// A range with step size. `end` is an exclusive index. Negative `begin`
109    /// or `end` indexes are counted from the back of the axis. If `end` is
110    /// `None`, the slice extends to the end of the axis.
111    Slice {
112        start: isize,
113        end: Option<isize>,
114        step: isize,
115    },
116    /// A single index.
117    Index(isize),
118    /// A new axis of length 1.
119    NewAxis,
120}
121
122copy_and_clone! {SliceInfoElem}
123
124impl SliceInfoElem {
125    /// Returns `true` if `self` is a `Slice` value.
126    pub fn is_slice(&self) -> bool {
127        matches!(self, SliceInfoElem::Slice { .. })
128    }
129
130    /// Returns `true` if `self` is an `Index` value.
131    pub fn is_index(&self) -> bool {
132        matches!(self, SliceInfoElem::Index(_))
133    }
134
135    /// Returns `true` if `self` is a `NewAxis` value.
136    pub fn is_new_axis(&self) -> bool {
137        matches!(self, SliceInfoElem::NewAxis)
138    }
139}
140
141impl fmt::Display for SliceInfoElem {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        match *self {
144            SliceInfoElem::Index(index) => write!(f, "{}", index)?,
145            SliceInfoElem::Slice { start, end, step } => {
146                if start != 0 {
147                    write!(f, "{}", start)?;
148                }
149                write!(f, "..")?;
150                if let Some(i) = end {
151                    write!(f, "{}", i)?;
152                }
153                if step != 1 {
154                    write!(f, ";{}", step)?;
155                }
156            }
157            SliceInfoElem::NewAxis => write!(f, stringify!(NewAxis))?,
158        }
159        Ok(())
160    }
161}
162
163macro_rules! impl_slice_variant_from_range {
164    ($self:ty, $constructor:path, $index:ty) => {
165        impl From<Range<$index>> for $self {
166            #[inline]
167            fn from(r: Range<$index>) -> $self {
168                $constructor {
169                    start: r.start as isize,
170                    end: Some(r.end as isize),
171                    step: 1,
172                }
173            }
174        }
175
176        impl From<RangeInclusive<$index>> for $self {
177            #[inline]
178            fn from(r: RangeInclusive<$index>) -> $self {
179                let end = *r.end() as isize;
180                $constructor {
181                    start: *r.start() as isize,
182                    end: if end == -1 { None } else { Some(end + 1) },
183                    step: 1,
184                }
185            }
186        }
187
188        impl From<RangeFrom<$index>> for $self {
189            #[inline]
190            fn from(r: RangeFrom<$index>) -> $self {
191                $constructor {
192                    start: r.start as isize,
193                    end: None,
194                    step: 1,
195                }
196            }
197        }
198
199        impl From<RangeTo<$index>> for $self {
200            #[inline]
201            fn from(r: RangeTo<$index>) -> $self {
202                $constructor {
203                    start: 0,
204                    end: Some(r.end as isize),
205                    step: 1,
206                }
207            }
208        }
209
210        impl From<RangeToInclusive<$index>> for $self {
211            #[inline]
212            fn from(r: RangeToInclusive<$index>) -> $self {
213                let end = r.end as isize;
214                $constructor {
215                    start: 0,
216                    end: if end == -1 { None } else { Some(end + 1) },
217                    step: 1,
218                }
219            }
220        }
221    };
222}
223impl_slice_variant_from_range!(Slice, Slice, isize);
224impl_slice_variant_from_range!(Slice, Slice, usize);
225impl_slice_variant_from_range!(Slice, Slice, i32);
226impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, isize);
227impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, usize);
228impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, i32);
229
230impl From<RangeFull> for Slice {
231    #[inline]
232    fn from(_: RangeFull) -> Slice {
233        Slice {
234            start: 0,
235            end: None,
236            step: 1,
237        }
238    }
239}
240
241impl From<RangeFull> for SliceInfoElem {
242    #[inline]
243    fn from(_: RangeFull) -> SliceInfoElem {
244        SliceInfoElem::Slice {
245            start: 0,
246            end: None,
247            step: 1,
248        }
249    }
250}
251
252impl From<Slice> for SliceInfoElem {
253    #[inline]
254    fn from(s: Slice) -> SliceInfoElem {
255        SliceInfoElem::Slice {
256            start: s.start,
257            end: s.end,
258            step: s.step,
259        }
260    }
261}
262
263macro_rules! impl_sliceinfoelem_from_index {
264    ($index:ty) => {
265        impl From<$index> for SliceInfoElem {
266            #[inline]
267            fn from(r: $index) -> SliceInfoElem {
268                SliceInfoElem::Index(r as isize)
269            }
270        }
271    };
272}
273impl_sliceinfoelem_from_index!(isize);
274impl_sliceinfoelem_from_index!(usize);
275impl_sliceinfoelem_from_index!(i32);
276
277impl From<NewAxis> for SliceInfoElem {
278    #[inline]
279    fn from(_: NewAxis) -> SliceInfoElem {
280        SliceInfoElem::NewAxis
281    }
282}
283
284/// A type that can slice an array of dimension `D`.
285///
286/// This trait is unsafe to implement because the implementation must ensure
287/// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are
288/// consistent with the `&[SliceInfoElem]` returned by `self.as_ref()` and that
289/// `self.as_ref()` always returns the same value when called multiple times.
290pub unsafe trait SliceArg<D: Dimension>: AsRef<[SliceInfoElem]> {
291    /// Dimensionality of the output array.
292    type OutDim: Dimension;
293
294    /// Returns the number of axes in the input array.
295    fn in_ndim(&self) -> usize;
296
297    /// Returns the number of axes in the output array.
298    fn out_ndim(&self) -> usize;
299
300    private_decl! {}
301}
302
303unsafe impl<T, D> SliceArg<D> for &T
304where
305    T: SliceArg<D> + ?Sized,
306    D: Dimension,
307{
308    type OutDim = T::OutDim;
309
310    fn in_ndim(&self) -> usize {
311        T::in_ndim(self)
312    }
313
314    fn out_ndim(&self) -> usize {
315        T::out_ndim(self)
316    }
317
318    private_impl! {}
319}
320
321macro_rules! impl_slicearg_samedim {
322    ($in_dim:ty) => {
323        unsafe impl<T, Dout> SliceArg<$in_dim> for SliceInfo<T, $in_dim, Dout>
324        where
325            T: AsRef<[SliceInfoElem]>,
326            Dout: Dimension,
327        {
328            type OutDim = Dout;
329
330            fn in_ndim(&self) -> usize {
331                self.in_ndim()
332            }
333
334            fn out_ndim(&self) -> usize {
335                self.out_ndim()
336            }
337
338            private_impl! {}
339        }
340    };
341}
342impl_slicearg_samedim!(Ix0);
343impl_slicearg_samedim!(Ix1);
344impl_slicearg_samedim!(Ix2);
345impl_slicearg_samedim!(Ix3);
346impl_slicearg_samedim!(Ix4);
347impl_slicearg_samedim!(Ix5);
348impl_slicearg_samedim!(Ix6);
349
350unsafe impl<T, Din, Dout> SliceArg<IxDyn> for SliceInfo<T, Din, Dout>
351where
352    T: AsRef<[SliceInfoElem]>,
353    Din: Dimension,
354    Dout: Dimension,
355{
356    type OutDim = Dout;
357
358    fn in_ndim(&self) -> usize {
359        self.in_ndim()
360    }
361
362    fn out_ndim(&self) -> usize {
363        self.out_ndim()
364    }
365
366    private_impl! {}
367}
368
369unsafe impl SliceArg<IxDyn> for [SliceInfoElem] {
370    type OutDim = IxDyn;
371
372    fn in_ndim(&self) -> usize {
373        self.iter().filter(|s| !s.is_new_axis()).count()
374    }
375
376    fn out_ndim(&self) -> usize {
377        self.iter().filter(|s| !s.is_index()).count()
378    }
379
380    private_impl! {}
381}
382
383/// Represents all of the necessary information to perform a slice.
384///
385/// The type `T` is typically `[SliceInfoElem; n]`, `&[SliceInfoElem]`, or
386/// `Vec<SliceInfoElem>`. The type `Din` is the dimension of the array to be
387/// sliced, and `Dout` is the output dimension after calling [`.slice()`]. Note
388/// that if `Din` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the
389/// `SliceInfo` instance can still be used to slice an array with dimension
390/// `IxDyn` as long as the number of axes matches.
391///
392/// [`.slice()`]: struct.ArrayBase.html#method.slice
393#[derive(Debug)]
394pub struct SliceInfo<T, Din: Dimension, Dout: Dimension> {
395    in_dim: PhantomData<Din>,
396    out_dim: PhantomData<Dout>,
397    indices: T,
398}
399
400impl<T, Din, Dout> Deref for SliceInfo<T, Din, Dout>
401where
402    Din: Dimension,
403    Dout: Dimension,
404{
405    type Target = T;
406    fn deref(&self) -> &Self::Target {
407        &self.indices
408    }
409}
410
411fn check_dims_for_sliceinfo<Din, Dout>(indices: &[SliceInfoElem]) -> Result<(), ShapeError>
412where
413    Din: Dimension,
414    Dout: Dimension,
415{
416    if let Some(in_ndim) = Din::NDIM {
417        if in_ndim != indices.in_ndim() {
418            return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape));
419        }
420    }
421    if let Some(out_ndim) = Dout::NDIM {
422        if out_ndim != indices.out_ndim() {
423            return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape));
424        }
425    }
426    Ok(())
427}
428
429impl<T, Din, Dout> SliceInfo<T, Din, Dout>
430where
431    T: AsRef<[SliceInfoElem]>,
432    Din: Dimension,
433    Dout: Dimension,
434{
435    /// Returns a new `SliceInfo` instance.
436    ///
437    /// **Note:** only unchecked for non-debug builds of `ndarray`.
438    ///
439    /// # Safety
440    ///
441    /// The caller must ensure that `in_dim` and `out_dim` are consistent with
442    /// `indices` and that `indices.as_ref()` always returns the same value
443    /// when called multiple times.
444    #[doc(hidden)]
445    pub unsafe fn new_unchecked(
446        indices: T,
447        in_dim: PhantomData<Din>,
448        out_dim: PhantomData<Dout>,
449    ) -> SliceInfo<T, Din, Dout> {
450        if cfg!(debug_assertions) {
451            check_dims_for_sliceinfo::<Din, Dout>(indices.as_ref())
452                .expect("`Din` and `Dout` must be consistent with `indices`.");
453        }
454        SliceInfo {
455            in_dim,
456            out_dim,
457            indices,
458        }
459    }
460
461    /// Returns a new `SliceInfo` instance.
462    ///
463    /// Errors if `Din` or `Dout` is not consistent with `indices`.
464    ///
465    /// For common types, a safe alternative is to use `TryFrom` instead.
466    ///
467    /// # Safety
468    ///
469    /// The caller must ensure `indices.as_ref()` always returns the same value
470    /// when called multiple times.
471    pub unsafe fn new(indices: T) -> Result<SliceInfo<T, Din, Dout>, ShapeError> {
472        check_dims_for_sliceinfo::<Din, Dout>(indices.as_ref())?;
473        Ok(SliceInfo {
474            in_dim: PhantomData,
475            out_dim: PhantomData,
476            indices,
477        })
478    }
479
480    /// Returns the number of dimensions of the input array for
481    /// [`.slice()`](struct.ArrayBase.html#method.slice).
482    ///
483    /// If `Din` is a fixed-size dimension type, then this is equivalent to
484    /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating
485    /// over the `SliceInfoElem` elements.
486    pub fn in_ndim(&self) -> usize {
487        if let Some(ndim) = Din::NDIM {
488            ndim
489        } else {
490            self.indices.as_ref().in_ndim()
491        }
492    }
493
494    /// Returns the number of dimensions after calling
495    /// [`.slice()`](struct.ArrayBase.html#method.slice) (including taking
496    /// subviews).
497    ///
498    /// If `Dout` is a fixed-size dimension type, then this is equivalent to
499    /// `Dout::NDIM.unwrap()`. Otherwise, the value is calculated by iterating
500    /// over the `SliceInfoElem` elements.
501    pub fn out_ndim(&self) -> usize {
502        if let Some(ndim) = Dout::NDIM {
503            ndim
504        } else {
505            self.indices.as_ref().out_ndim()
506        }
507    }
508}
509
510impl<'a, Din, Dout> TryFrom<&'a [SliceInfoElem]> for SliceInfo<&'a [SliceInfoElem], Din, Dout>
511where
512    Din: Dimension,
513    Dout: Dimension,
514{
515    type Error = ShapeError;
516
517    fn try_from(
518        indices: &'a [SliceInfoElem],
519    ) -> Result<SliceInfo<&'a [SliceInfoElem], Din, Dout>, ShapeError> {
520        unsafe {
521            // This is okay because `&[SliceInfoElem]` always returns the same
522            // value for `.as_ref()`.
523            Self::new(indices)
524        }
525    }
526}
527
528impl<Din, Dout> TryFrom<Vec<SliceInfoElem>> for SliceInfo<Vec<SliceInfoElem>, Din, Dout>
529where
530    Din: Dimension,
531    Dout: Dimension,
532{
533    type Error = ShapeError;
534
535    fn try_from(
536        indices: Vec<SliceInfoElem>,
537    ) -> Result<SliceInfo<Vec<SliceInfoElem>, Din, Dout>, ShapeError> {
538        unsafe {
539            // This is okay because `Vec` always returns the same value for
540            // `.as_ref()`.
541            Self::new(indices)
542        }
543    }
544}
545
546macro_rules! impl_tryfrom_array_for_sliceinfo {
547    ($len:expr) => {
548        impl<Din, Dout> TryFrom<[SliceInfoElem; $len]>
549            for SliceInfo<[SliceInfoElem; $len], Din, Dout>
550        where
551            Din: Dimension,
552            Dout: Dimension,
553        {
554            type Error = ShapeError;
555
556            fn try_from(
557                indices: [SliceInfoElem; $len],
558            ) -> Result<SliceInfo<[SliceInfoElem; $len], Din, Dout>, ShapeError> {
559                unsafe {
560                    // This is okay because `[SliceInfoElem; N]` always returns
561                    // the same value for `.as_ref()`.
562                    Self::new(indices)
563                }
564            }
565        }
566    };
567}
568impl_tryfrom_array_for_sliceinfo!(0);
569impl_tryfrom_array_for_sliceinfo!(1);
570impl_tryfrom_array_for_sliceinfo!(2);
571impl_tryfrom_array_for_sliceinfo!(3);
572impl_tryfrom_array_for_sliceinfo!(4);
573impl_tryfrom_array_for_sliceinfo!(5);
574impl_tryfrom_array_for_sliceinfo!(6);
575impl_tryfrom_array_for_sliceinfo!(7);
576impl_tryfrom_array_for_sliceinfo!(8);
577
578impl<T, Din, Dout> AsRef<[SliceInfoElem]> for SliceInfo<T, Din, Dout>
579where
580    T: AsRef<[SliceInfoElem]>,
581    Din: Dimension,
582    Dout: Dimension,
583{
584    fn as_ref(&self) -> &[SliceInfoElem] {
585        self.indices.as_ref()
586    }
587}
588
589impl<'a, T, Din, Dout> From<&'a SliceInfo<T, Din, Dout>>
590    for SliceInfo<&'a [SliceInfoElem], Din, Dout>
591where
592    T: AsRef<[SliceInfoElem]>,
593    Din: Dimension,
594    Dout: Dimension,
595{
596    fn from(info: &'a SliceInfo<T, Din, Dout>) -> SliceInfo<&'a [SliceInfoElem], Din, Dout> {
597        SliceInfo {
598            in_dim: info.in_dim,
599            out_dim: info.out_dim,
600            indices: info.indices.as_ref(),
601        }
602    }
603}
604
605impl<T, Din, Dout> Copy for SliceInfo<T, Din, Dout>
606where
607    T: Copy,
608    Din: Dimension,
609    Dout: Dimension,
610{
611}
612
613impl<T, Din, Dout> Clone for SliceInfo<T, Din, Dout>
614where
615    T: Clone,
616    Din: Dimension,
617    Dout: Dimension,
618{
619    fn clone(&self) -> Self {
620        SliceInfo {
621            in_dim: PhantomData,
622            out_dim: PhantomData,
623            indices: self.indices.clone(),
624        }
625    }
626}
627
628/// Trait for determining dimensionality of input and output for [`s!`] macro.
629#[doc(hidden)]
630pub trait SliceNextDim {
631    /// Number of dimensions that this slicing argument consumes in the input array.
632    type InDim: Dimension;
633    /// Number of dimensions that this slicing argument produces in the output array.
634    type OutDim: Dimension;
635
636    fn next_in_dim<D>(&self, _: PhantomData<D>) -> PhantomData<<D as DimAdd<Self::InDim>>::Output>
637    where
638        D: Dimension + DimAdd<Self::InDim>,
639    {
640        PhantomData
641    }
642
643    fn next_out_dim<D>(&self, _: PhantomData<D>) -> PhantomData<<D as DimAdd<Self::OutDim>>::Output>
644    where
645        D: Dimension + DimAdd<Self::OutDim>,
646    {
647        PhantomData
648    }
649}
650
651macro_rules! impl_slicenextdim {
652    (($($generics:tt)*), $self:ty, $in:ty, $out:ty) => {
653        impl<$($generics)*> SliceNextDim for $self {
654            type InDim = $in;
655            type OutDim = $out;
656        }
657    };
658}
659
660impl_slicenextdim!((), isize, Ix1, Ix0);
661impl_slicenextdim!((), usize, Ix1, Ix0);
662impl_slicenextdim!((), i32, Ix1, Ix0);
663
664impl_slicenextdim!((T), Range<T>, Ix1, Ix1);
665impl_slicenextdim!((T), RangeInclusive<T>, Ix1, Ix1);
666impl_slicenextdim!((T), RangeFrom<T>, Ix1, Ix1);
667impl_slicenextdim!((T), RangeTo<T>, Ix1, Ix1);
668impl_slicenextdim!((T), RangeToInclusive<T>, Ix1, Ix1);
669impl_slicenextdim!((), RangeFull, Ix1, Ix1);
670impl_slicenextdim!((), Slice, Ix1, Ix1);
671
672impl_slicenextdim!((), NewAxis, Ix0, Ix1);
673
674/// Slice argument constructor.
675///
676/// `s![]` takes a list of ranges/slices/indices/new-axes, separated by comma,
677/// with optional step sizes that are separated from the range by a semicolon.
678/// It is converted into a [`SliceInfo`] instance.
679///
680/// Each range/slice/index uses signed indices, where a negative value is
681/// counted from the end of the axis. Step sizes are also signed and may be
682/// negative, but must not be zero.
683///
684/// The syntax is `s![` *[ elem [, elem [ , ... ] ] ]* `]`, where *elem* is any
685/// of the following:
686///
687/// * *index*: an index to use for taking a subview with respect to that axis.
688///   (The index is selected. The axis is removed except with
689///   [`.slice_collapse()`].)
690/// * *range*: a range with step size 1 to use for slicing that axis.
691/// * *range* `;` *step*: a range with step size *step* to use for slicing that axis.
692/// * *slice*: a [`Slice`] instance to use for slicing that axis.
693/// * *slice* `;` *step*: a range constructed from a [`Slice`] instance,
694///   multiplying the step size by *step*, to use for slicing that axis.
695/// * *new-axis*: a [`NewAxis`] instance that represents the creation of a new axis.
696///   (Except for [`.slice_collapse()`], which panics on [`NewAxis`] elements.)
697///
698/// [`Slice`]: struct.Slice.html
699/// [`NewAxis`]: struct.NewAxis.html
700///
701/// The number of *elem*, not including *new-axis*, must match the
702/// number of axes in the array. *index*, *range*, *slice*, *step*, and
703/// *new-axis* can be expressions. *index* must be of type `isize`, `usize`, or
704/// `i32`. *range* must be of type `Range<I>`, `RangeTo<I>`, `RangeFrom<I>`, or
705/// `RangeFull` where `I` is `isize`, `usize`, or `i32`. *step* must be a type
706/// that can be converted to `isize` with the `as` keyword.
707///
708/// For example, `s![0..4;2, 6, 1..5, NewAxis]` is a slice of the first axis
709/// for 0..4 with step size 2, a subview of the second axis at index 6, a slice
710/// of the third axis for 1..5 with default step size 1, and a new axis of
711/// length 1 at the end of the shape. The input array must have 3 dimensions.
712/// The resulting slice would have shape `[2, 4, 1]` for [`.slice()`],
713/// [`.slice_mut()`], and [`.slice_move()`], while [`.slice_collapse()`] would
714/// panic. Without the `NewAxis`, i.e. `s![0..4;2, 6, 1..5]`,
715/// [`.slice_collapse()`] would result in an array of shape `[2, 1, 4]`.
716///
717/// [`.slice()`]: struct.ArrayBase.html#method.slice
718/// [`.slice_mut()`]: struct.ArrayBase.html#method.slice_mut
719/// [`.slice_move()`]: struct.ArrayBase.html#method.slice_move
720/// [`.slice_collapse()`]: struct.ArrayBase.html#method.slice_collapse
721///
722/// See also [*Slicing*](struct.ArrayBase.html#slicing).
723///
724/// # Example
725///
726/// ```
727/// use ndarray::{s, Array2, ArrayView2};
728///
729/// fn laplacian(v: &ArrayView2<f32>) -> Array2<f32> {
730///     -4. * &v.slice(s![1..-1, 1..-1])
731///     + v.slice(s![ ..-2, 1..-1])
732///     + v.slice(s![1..-1,  ..-2])
733///     + v.slice(s![1..-1, 2..  ])
734///     + v.slice(s![2..  , 1..-1])
735/// }
736/// # fn main() { }
737/// ```
738///
739/// # Negative *step*
740///
741/// The behavior of negative *step* arguments is most easily understood with
742/// slicing as a two-step process:
743///
744/// 1. First, perform a slice with *range*.
745///
746/// 2. If *step* is positive, start with the front of the slice; if *step* is
747///    negative, start with the back of the slice. Then, add *step* until
748///    reaching the other end of the slice (inclusive).
749///
750/// An equivalent way to think about step 2 is, "If *step* is negative, reverse
751/// the slice. Start at the front of the (possibly reversed) slice, and add
752/// *step.abs()* until reaching the back of the slice (inclusive)."
753///
754/// For example,
755///
756/// ```
757/// # use ndarray::prelude::*;
758/// #
759/// # fn main() {
760/// let arr = array![0, 1, 2, 3];
761/// assert_eq!(arr.slice(s![1..3;-1]), array![2, 1]);
762/// assert_eq!(arr.slice(s![1..;-2]), array![3, 1]);
763/// assert_eq!(arr.slice(s![0..4;-2]), array![3, 1]);
764/// assert_eq!(arr.slice(s![0..;-2]), array![3, 1]);
765/// assert_eq!(arr.slice(s![..;-2]), array![3, 1]);
766/// # }
767/// ```
768#[macro_export]
769macro_rules! s(
770    // convert a..b;c into @convert(a..b, c), final item
771    (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => {
772        match $r {
773            r => {
774                let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim);
775                let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim);
776                #[allow(unsafe_code)]
777                unsafe {
778                    $crate::SliceInfo::new_unchecked(
779                        [$($stack)* $crate::s!(@convert r, $s)],
780                        in_dim,
781                        out_dim,
782                    )
783                }
784            }
785        }
786    };
787    // convert a..b into @convert(a..b), final item
788    (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr) => {
789        match $r {
790            r => {
791                let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim);
792                let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim);
793                #[allow(unsafe_code)]
794                unsafe {
795                    $crate::SliceInfo::new_unchecked(
796                        [$($stack)* $crate::s!(@convert r)],
797                        in_dim,
798                        out_dim,
799                    )
800                }
801            }
802        }
803    };
804    // convert a..b;c into @convert(a..b, c), final item, trailing comma
805    (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr ,) => {
806        $crate::s![@parse $in_dim, $out_dim, [$($stack)*] $r;$s]
807    };
808    // convert a..b into @convert(a..b), final item, trailing comma
809    (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr ,) => {
810        $crate::s![@parse $in_dim, $out_dim, [$($stack)*] $r]
811    };
812    // convert a..b;c into @convert(a..b, c)
813    (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => {
814        match $r {
815            r => {
816                $crate::s![@parse
817                   $crate::SliceNextDim::next_in_dim(&r, $in_dim),
818                   $crate::SliceNextDim::next_out_dim(&r, $out_dim),
819                   [$($stack)* $crate::s!(@convert r, $s),]
820                   $($t)*
821                ]
822            }
823        }
824    };
825    // convert a..b into @convert(a..b)
826    (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr, $($t:tt)*) => {
827        match $r {
828            r => {
829                $crate::s![@parse
830                   $crate::SliceNextDim::next_in_dim(&r, $in_dim),
831                   $crate::SliceNextDim::next_out_dim(&r, $out_dim),
832                   [$($stack)* $crate::s!(@convert r),]
833                   $($t)*
834                ]
835            }
836        }
837    };
838    // empty call, i.e. `s![]`
839    (@parse ::std::marker::PhantomData::<$crate::Ix0>, ::std::marker::PhantomData::<$crate::Ix0>, []) => {
840        {
841            #[allow(unsafe_code)]
842            unsafe {
843                $crate::SliceInfo::new_unchecked(
844                    [],
845                    ::std::marker::PhantomData::<$crate::Ix0>,
846                    ::std::marker::PhantomData::<$crate::Ix0>,
847                )
848            }
849        }
850    };
851    // Catch-all clause for syntax errors
852    (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") };
853    // convert range/index/new-axis into SliceInfoElem
854    (@convert $r:expr) => {
855        <$crate::SliceInfoElem as ::std::convert::From<_>>::from($r)
856    };
857    // convert range/index/new-axis and step into SliceInfoElem
858    (@convert $r:expr, $s:expr) => {
859        <$crate::SliceInfoElem as ::std::convert::From<_>>::from(
860            <$crate::Slice as ::std::convert::From<_>>::from($r).step_by($s as isize)
861        )
862    };
863    ($($t:tt)*) => {
864        $crate::s![@parse
865              ::std::marker::PhantomData::<$crate::Ix0>,
866              ::std::marker::PhantomData::<$crate::Ix0>,
867              []
868              $($t)*
869        ]
870    };
871);
872
873/// Slicing information describing multiple mutable, disjoint slices.
874///
875/// It's unfortunate that we need `'a` and `A` to be parameters of the trait,
876/// but they're necessary until Rust supports generic associated types.
877pub trait MultiSliceArg<'a, A, D>
878where
879    A: 'a,
880    D: Dimension,
881{
882    /// The type of the slices created by `.multi_slice_move()`.
883    type Output;
884
885    /// Split the view into multiple disjoint slices.
886    ///
887    /// **Panics** if performing any individual slice panics or if the slices
888    /// are not disjoint (i.e. if they intersect).
889    fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output;
890
891    private_decl! {}
892}
893
894impl<'a, A, D> MultiSliceArg<'a, A, D> for ()
895where
896    A: 'a,
897    D: Dimension,
898{
899    type Output = ();
900
901    fn multi_slice_move(&self, _view: ArrayViewMut<'a, A, D>) -> Self::Output {}
902
903    private_impl! {}
904}
905
906impl<'a, A, D, I0> MultiSliceArg<'a, A, D> for (I0,)
907where
908    A: 'a,
909    D: Dimension,
910    I0: SliceArg<D>,
911{
912    type Output = (ArrayViewMut<'a, A, I0::OutDim>,);
913
914    fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output {
915        (view.slice_move(&self.0),)
916    }
917
918    private_impl! {}
919}
920
921macro_rules! impl_multislice_tuple {
922    ([$($but_last:ident)*] $last:ident) => {
923        impl_multislice_tuple!(@def_impl ($($but_last,)* $last,), [$($but_last)*] $last);
924    };
925    (@def_impl ($($all:ident,)*), [$($but_last:ident)*] $last:ident) => {
926        impl<'a, A, D, $($all,)*> MultiSliceArg<'a, A, D> for ($($all,)*)
927        where
928            A: 'a,
929            D: Dimension,
930            $($all: SliceArg<D>,)*
931        {
932            type Output = ($(ArrayViewMut<'a, A, $all::OutDim>,)*);
933
934            fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output {
935                #[allow(non_snake_case)]
936                let ($($all,)*) = self;
937
938                let shape = view.raw_dim();
939                assert!(!impl_multislice_tuple!(@intersects_self &shape, ($($all,)*)));
940
941                let raw_view = view.into_raw_view_mut();
942                unsafe {
943                    (
944                        $(raw_view.clone().slice_move($but_last).deref_into_view_mut(),)*
945                        raw_view.slice_move($last).deref_into_view_mut(),
946                    )
947                }
948            }
949
950            private_impl! {}
951        }
952    };
953    (@intersects_self $shape:expr, ($head:expr,)) => {
954        false
955    };
956    (@intersects_self $shape:expr, ($head:expr, $($tail:expr,)*)) => {
957        $(slices_intersect($shape, $head, $tail)) ||*
958            || impl_multislice_tuple!(@intersects_self $shape, ($($tail,)*))
959    };
960}
961
962impl_multislice_tuple!([I0] I1);
963impl_multislice_tuple!([I0 I1] I2);
964impl_multislice_tuple!([I0 I1 I2] I3);
965impl_multislice_tuple!([I0 I1 I2 I3] I4);
966impl_multislice_tuple!([I0 I1 I2 I3 I4] I5);
967
968impl<'a, A, D, T> MultiSliceArg<'a, A, D> for &T
969where
970    A: 'a,
971    D: Dimension,
972    T: MultiSliceArg<'a, A, D>,
973{
974    type Output = T::Output;
975
976    fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output {
977        T::multi_slice_move(self, view)
978    }
979
980    private_impl! {}
981}