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