use std::marker::PhantomData; use std::ops::{Range, RangeFrom, RangeTo, RangeFull}; use std::slice; use core::{Scalar, Matrix}; use core::dimension::{Dim, DimName, Dynamic, DimMul, DimProd, U1}; use core::iter::MatrixIter; use core::storage::{Storage, StorageMut, Owned}; use core::allocator::Allocator; use core::default_allocator::DefaultAllocator; macro_rules! slice_storage_impl( ($doc: expr; $Storage: ident as $SRef: ty; $T: ident.$get_addr: ident ($Ptr: ty as $Ref: ty)) => { #[doc = $doc] #[derive(Debug)] pub struct $T<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> { ptr: $Ptr, shape: (R, C), strides: (RStride, CStride), _phantoms: PhantomData<$Ref>, } // Dynamic is arbitrary. It's just to be able to call the constructors with `Slice::` impl<'a, N: Scalar, R: Dim, C: Dim> $T<'a, N, R, C, Dynamic, Dynamic> { /// Create a new matrix slice without bound checking. #[inline] pub unsafe fn new_unchecked(storage: $SRef, start: (usize, usize), shape: (R, C)) -> $T<'a, N, R, C, S::RStride, S::CStride> where RStor: Dim, CStor: Dim, S: $Storage { let strides = storage.strides(); $T::new_with_strides_unchecked(storage, start, shape, strides) } /// Create a new matrix slice without bound checking. #[inline] pub unsafe fn new_with_strides_unchecked(storage: $SRef, start: (usize, usize), shape: (R, C), strides: (RStride, CStride)) -> $T<'a, N, R, C, RStride, CStride> where RStor: Dim, CStor: Dim, S: $Storage, RStride: Dim, CStride: Dim { $T::from_raw_parts(storage.$get_addr(start.0, start.1), shape, strides) } /// Create a new matrix slice without bound checking and from a raw pointer. #[inline] pub unsafe fn from_raw_parts(ptr: $Ptr, shape: (R, C), strides: (RStride, CStride)) -> $T<'a, N, R, C, RStride, CStride> where RStride: Dim, CStride: Dim { $T { ptr: ptr, shape: shape, strides: strides, _phantoms: PhantomData } } } } ); slice_storage_impl!("A matrix data storage for a matrix slice. Only contains an internal reference \ to another matrix data storage."; Storage as &'a S; SliceStorage.get_address_unchecked(*const N as &'a N)); slice_storage_impl!("A mutable matrix data storage for mutable matrix slice. Only contains an \ internal mutable reference to another matrix data storage."; StorageMut as &'a mut S; SliceStorageMut.get_address_unchecked_mut(*mut N as &'a mut N) ); impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Copy for SliceStorage<'a, N, R, C, RStride, CStride> { } impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Clone for SliceStorage<'a, N, R, C, RStride, CStride> { #[inline] fn clone(&self) -> Self { SliceStorage { ptr: self.ptr, shape: self.shape, strides: self.strides, _phantoms: PhantomData, } } } macro_rules! storage_impl( ($($T: ident),* $(,)*) => {$( unsafe impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Storage for $T<'a, N, R, C, RStride, CStride> { type RStride = RStride; type CStride = CStride; #[inline] fn ptr(&self) -> *const N { self.ptr } #[inline] fn shape(&self) -> (R, C) { self.shape } #[inline] fn strides(&self) -> (Self::RStride, Self::CStride) { self.strides } #[inline] fn is_contiguous(&self) -> bool { // Common cases that can be deduced at compile-time even if one of the dimensions // is Dynamic. if (RStride::is::() && C::is::()) || // Column vector. (CStride::is::() && R::is::()) { // Row vector. true } else { let (nrows, _) = self.shape(); let (srows, scols) = self.strides(); srows.value() == 1 && scols.value() == nrows.value() } } #[inline] fn into_owned(self) -> Owned where DefaultAllocator: Allocator { self.clone_owned() } #[inline] fn clone_owned(&self) -> Owned where DefaultAllocator: Allocator { let (nrows, ncols) = self.shape(); let it = MatrixIter::new(self).cloned(); DefaultAllocator::allocate_from_iterator(nrows, ncols, it) } #[inline] fn as_slice(&self) -> &[N] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); unsafe { slice::from_raw_parts(self.ptr, sz + 1) } } else { unsafe { slice::from_raw_parts(self.ptr, 0) } } } } )*} ); storage_impl!(SliceStorage, SliceStorageMut); unsafe impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> StorageMut for SliceStorageMut<'a, N, R, C, RStride, CStride> { #[inline] fn ptr_mut(&mut self) -> *mut N { self.ptr } #[inline] fn as_mut_slice(&mut self) -> &mut [N] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) } } else { unsafe { slice::from_raw_parts_mut(self.ptr, 0) } } } } impl> Matrix { #[inline] fn assert_slice_index(&self, start: (usize, usize), shape: (usize, usize), steps: (usize, usize)) { let my_shape = self.shape(); // NOTE: we previously did: (shape.0 - 1) * steps.0 // which was wrong because underflow may occur for zero-sized matrices. // Istead, we moved the subtraction into an addition on the right hand side. assert!(start.0 + shape.0 * steps.0 <= my_shape.0 + steps.0, "Matrix slicing out of bounds."); assert!(start.1 + shape.1 * steps.1 <= my_shape.1 + steps.1, "Matrix slicing out of bounds."); } } macro_rules! matrix_slice_impl( ($me: ident: $Me: ty, $MatrixSlice: ident, $SliceStorage: ident, $Storage: ident.$get_addr: ident (), $data: expr; $row: ident, $row_part: ident, $rows: ident, $rows_with_step: ident, $fixed_rows: ident, $fixed_rows_with_step: ident, $rows_generic: ident, $rows_generic_with_step: ident, $column: ident, $column_part: ident, $columns: ident, $columns_with_step: ident, $fixed_columns: ident, $fixed_columns_with_step: ident, $columns_generic: ident, $columns_generic_with_step: ident, $slice: ident, $slice_with_steps: ident, $fixed_slice: ident, $fixed_slice_with_steps: ident, $generic_slice: ident, $generic_slice_with_steps: ident, $rows_range_pair: ident, $columns_range_pair: ident) => { /// A matrix slice. pub type $MatrixSlice<'a, N, R, C, RStride, CStride> = Matrix>; impl> Matrix { /* * * Row slicing. * */ /// Returns a slice containing the i-th row of this matrix. #[inline] pub fn $row($me: $Me, i: usize) -> $MatrixSlice { $me.$fixed_rows::(i) } /// Returns a slice containing the `n` first elements of the i-th row of this matrix. #[inline] pub fn $row_part($me: $Me, i: usize, n: usize) -> $MatrixSlice { $me.$generic_slice((i, 0), (U1, Dynamic::new(n))) } /// Extracts from this matrix a set of consecutive rows. #[inline] pub fn $rows($me: $Me, first_row: usize, nrows: usize) -> $MatrixSlice { $me.$rows_generic(first_row, Dynamic::new(nrows)) } /// Extracts from this matrix a set of consecutive rows regularly spaced by `step` rows. #[inline] pub fn $rows_with_step($me: $Me, first_row: usize, nrows: usize, step: usize) -> $MatrixSlice { $me.$rows_generic_with_step(first_row, Dynamic::new(nrows), Dynamic::new(step)) } /// Extracts a compile-time number of consecutive rows from this matrix. #[inline] pub fn $fixed_rows($me: $Me, first_row: usize) -> $MatrixSlice where RSlice: DimName { $me.$rows_generic(first_row, RSlice::name()) } /// Extracts from this matrix a compile-time number of rows regularly spaced by `step` rows. #[inline] pub fn $fixed_rows_with_step($me: $Me, first_row: usize, step: usize) -> $MatrixSlice where RSlice: DimName { $me.$rows_generic_with_step(first_row, RSlice::name(), Dynamic::new(step)) } /// Extracts from this matrix `nrows` rows regularly spaced by `step` rows. Both argument may /// or may not be values known at compile-time. #[inline] pub fn $rows_generic($me: $Me, row_start: usize, nrows: RSlice) -> $MatrixSlice where RSlice: Dim { let my_shape = $me.data.shape(); $me.assert_slice_index((row_start, 0), (nrows.value(), my_shape.1.value()), (1, 1)); let shape = (nrows, my_shape.1); unsafe { let data = $SliceStorage::new_unchecked($data, (row_start, 0), shape); Matrix::from_data_statically_unchecked(data) } } /// Extracts from this matrix `nrows` rows regularly spaced by `step` rows. Both argument may /// or may not be values known at compile-time. #[inline] pub fn $rows_generic_with_step($me: $Me, row_start: usize, nrows: RSlice, step: RStep) -> $MatrixSlice, S::CStride> where RSlice: Dim, RStep: DimMul { let my_shape = $me.data.shape(); let my_strides = $me.data.strides(); $me.assert_slice_index((row_start, 0), (nrows.value(), my_shape.1.value()), (step.value(), 1)); let strides = (step.mul(my_strides.0), my_strides.1); let shape = (nrows, my_shape.1); unsafe { let data = $SliceStorage::new_with_strides_unchecked($data, (row_start, 0), shape, strides); Matrix::from_data_statically_unchecked(data) } } /* * * Column slicing. * */ /// Returns a slice containing the i-th column of this matrix. #[inline] pub fn $column($me: $Me, i: usize) -> $MatrixSlice { $me.$fixed_columns::(i) } /// Returns a slice containing the `n` first elements of the i-th column of this matrix. #[inline] pub fn $column_part($me: $Me, i: usize, n: usize) -> $MatrixSlice { $me.$generic_slice((0, i), (Dynamic::new(n), U1)) } /// Extracts from this matrix a set of consecutive columns. #[inline] pub fn $columns($me: $Me, first_col: usize, ncols: usize) -> $MatrixSlice { $me.$columns_generic(first_col, Dynamic::new(ncols)) } /// Extracts from this matrix a set of consecutive columns regularly spaced by `step` columns. #[inline] pub fn $columns_with_step($me: $Me, first_col: usize, ncols: usize, step: usize) -> $MatrixSlice { $me.$columns_generic_with_step(first_col, Dynamic::new(ncols), Dynamic::new(step)) } /// Extracts a compile-time number of consecutive columns from this matrix. #[inline] pub fn $fixed_columns($me: $Me, first_col: usize) -> $MatrixSlice where CSlice: DimName { $me.$columns_generic(first_col, CSlice::name()) } /// Extracts from this matrix a compile-time number of columns regularly spaced by `step` /// columns. #[inline] pub fn $fixed_columns_with_step($me: $Me, first_col: usize, step: usize) -> $MatrixSlice where CSlice: DimName { $me.$columns_generic_with_step(first_col, CSlice::name(), Dynamic::new(step)) } /// Extracts from this matrix `ncols` columns. The number of columns may or may not be /// known at compile-time. #[inline] pub fn $columns_generic($me: $Me, first_col: usize, ncols: CSlice) -> $MatrixSlice where CSlice: Dim { let my_shape = $me.data.shape(); $me.assert_slice_index((0, first_col), (my_shape.0.value(), ncols.value()), (1, 1)); let shape = (my_shape.0, ncols); unsafe { let data = $SliceStorage::new_unchecked($data, (0, first_col), shape); Matrix::from_data_statically_unchecked(data) } } /// Extracts from this matrix `ncols` columns regularly spaced by `step` columns. Both argument may /// or may not be values known at compile-time. #[inline] pub fn $columns_generic_with_step($me: $Me, first_col: usize, ncols: CSlice, step: CStep) -> $MatrixSlice> where CSlice: Dim, CStep: DimMul { let my_shape = $me.data.shape(); let my_strides = $me.data.strides(); $me.assert_slice_index((0, first_col), (my_shape.0.value(), ncols.value()), (1, step.value())); let strides = (my_strides.0, step.mul(my_strides.1)); let shape = (my_shape.0, ncols); unsafe { let data = $SliceStorage::new_with_strides_unchecked($data, (0, first_col), shape, strides); Matrix::from_data_statically_unchecked(data) } } /* * * General slicing. * */ /// Slices this matrix starting at its component `(irow, icol)` and with `(nrows, ncols)` /// consecutive elements. #[inline] pub fn $slice($me: $Me, start: (usize, usize), shape: (usize, usize)) -> $MatrixSlice { $me.assert_slice_index(start, shape, (1, 1)); let shape = (Dynamic::new(shape.0), Dynamic::new(shape.1)); unsafe { let data = $SliceStorage::new_unchecked($data, start, shape); Matrix::from_data_statically_unchecked(data) } } /// Slices this matrix starting at its component `(start.0, start.1)` and with /// `(shape.0, shape.1)` components. Each row (resp. column) of the sliced matrix is /// separated by `steps.0` (resp. `steps.1`) ignored rows (resp. columns) of the /// original matrix. #[inline] pub fn $slice_with_steps($me: $Me, start: (usize, usize), shape: (usize, usize), steps: (usize, usize)) -> $MatrixSlice { let shape = (Dynamic::new(shape.0), Dynamic::new(shape.1)); let steps = (Dynamic::new(steps.0), Dynamic::new(steps.1)); $me.$generic_slice_with_steps(start, shape, steps) } /// Slices this matrix starting at its component `(irow, icol)` and with `(R::dim(), /// CSlice::dim())` consecutive components. #[inline] pub fn $fixed_slice($me: $Me, irow: usize, icol: usize) -> $MatrixSlice where RSlice: DimName, CSlice: DimName { $me.assert_slice_index((irow, icol), (RSlice::dim(), CSlice::dim()), (1, 1)); let shape = (RSlice::name(), CSlice::name()); unsafe { let data = $SliceStorage::new_unchecked($data, (irow, icol), shape); Matrix::from_data_statically_unchecked(data) } } /// Slices this matrix starting at its component `(start.0, start.1)` and with /// `(R::dim(), CSlice::dim())` components. Each row (resp. column) of the sliced /// matrix is separated by `steps.0` (resp. `steps.1`) ignored rows (resp. columns) of /// the original matrix. #[inline] pub fn $fixed_slice_with_steps($me: $Me, start: (usize, usize), steps: (usize, usize)) -> $MatrixSlice where RSlice: DimName, CSlice: DimName { let shape = (RSlice::name(), CSlice::name()); let steps = (Dynamic::new(steps.0), Dynamic::new(steps.1)); $me.$generic_slice_with_steps(start, shape, steps) } /// Creates a slice that may or may not have a fixed size and stride. #[inline] pub fn $generic_slice($me: $Me, start: (usize, usize), shape: (RSlice, CSlice)) -> $MatrixSlice where RSlice: Dim, CSlice: Dim { $me.assert_slice_index(start, (shape.0.value(), shape.1.value()), (1, 1)); unsafe { let data = $SliceStorage::new_unchecked($data, start, shape); Matrix::from_data_statically_unchecked(data) } } /// Creates a slice that may or may not have a fixed size and stride. #[inline] pub fn $generic_slice_with_steps($me: $Me, start: (usize, usize), shape: (RSlice, CSlice), steps: (RStep, CStep)) -> $MatrixSlice, DimProd> where RSlice: Dim, CSlice: Dim, RStep: DimMul, CStep: DimMul { assert!(steps.0.value() > 0 && steps.1.value() > 0, "Matrix slicing steps must not be zero."); $me.assert_slice_index(start, (shape.0.value(), shape.1.value()), (steps.0.value(), steps.1.value())); let my_strides = $me.data.strides(); let strides = (steps.0.mul(my_strides.0), steps.1.mul(my_strides.1)); unsafe { let data = $SliceStorage::new_with_strides_unchecked($data, start, shape, strides); Matrix::from_data_statically_unchecked(data) } } /* * * Splitting. * */ /// Splits this NxM matrix into two parts delimited by two ranges. /// /// Panics if the ranges overlap or if the first range is empty. #[inline] pub fn $rows_range_pair, Range2: SliceRange>($me: $Me, r1: Range1, r2: Range2) -> ($MatrixSlice, $MatrixSlice) { let (nrows, ncols) = $me.data.shape(); let strides = $me.data.strides(); let start1 = r1.begin(nrows); let start2 = r2.begin(nrows); let end1 = r1.end(nrows); let end2 = r2.end(nrows); let nrows1 = r1.size(nrows); let nrows2 = r2.size(nrows); assert!(start2 >= end1 || start1 >= end2, "Rows range pair: the slice ranges must not overlap."); assert!(end2 <= nrows.value(), "Rows range pair: index out of range."); unsafe { let ptr1 = $data.$get_addr(start1, 0); let ptr2 = $data.$get_addr(start2, 0); let data1 = $SliceStorage::from_raw_parts(ptr1, (nrows1, ncols), strides); let data2 = $SliceStorage::from_raw_parts(ptr2, (nrows2, ncols), strides); let slice1 = Matrix::from_data_statically_unchecked(data1); let slice2 = Matrix::from_data_statically_unchecked(data2); (slice1, slice2) } } /// Splits this NxM matrix into two parts delimited by two ranges. /// /// Panics if the ranges overlap or if the first range is empty. #[inline] pub fn $columns_range_pair, Range2: SliceRange>($me: $Me, r1: Range1, r2: Range2) -> ($MatrixSlice, $MatrixSlice) { let (nrows, ncols) = $me.data.shape(); let strides = $me.data.strides(); let start1 = r1.begin(ncols); let start2 = r2.begin(ncols); let end1 = r1.end(ncols); let end2 = r2.end(ncols); let ncols1 = r1.size(ncols); let ncols2 = r2.size(ncols); assert!(start2 >= end1 || start1 >= end2, "Columns range pair: the slice ranges must not overlap."); assert!(end2 <= ncols.value(), "Columns range pair: index out of range."); unsafe { let ptr1 = $data.$get_addr(0, start1); let ptr2 = $data.$get_addr(0, start2); let data1 = $SliceStorage::from_raw_parts(ptr1, (nrows, ncols1), strides); let data2 = $SliceStorage::from_raw_parts(ptr2, (nrows, ncols2), strides); let slice1 = Matrix::from_data_statically_unchecked(data1); let slice2 = Matrix::from_data_statically_unchecked(data2); (slice1, slice2) } } } } ); matrix_slice_impl!( self: &Self, MatrixSlice, SliceStorage, Storage.get_address_unchecked(), &self.data; row, row_part, rows, rows_with_step, fixed_rows, fixed_rows_with_step, rows_generic, rows_generic_with_step, column, column_part, columns, columns_with_step, fixed_columns, fixed_columns_with_step, columns_generic, columns_generic_with_step, slice, slice_with_steps, fixed_slice, fixed_slice_with_steps, generic_slice, generic_slice_with_steps, rows_range_pair, columns_range_pair); matrix_slice_impl!( self: &mut Self, MatrixSliceMut, SliceStorageMut, StorageMut.get_address_unchecked_mut(), &mut self.data; row_mut, row_part_mut, rows_mut, rows_with_step_mut, fixed_rows_mut, fixed_rows_with_step_mut, rows_generic_mut, rows_generic_with_step_mut, column_mut, column_part_mut, columns_mut, columns_with_step_mut, fixed_columns_mut, fixed_columns_with_step_mut, columns_generic_mut, columns_generic_with_step_mut, slice_mut, slice_with_steps_mut, fixed_slice_mut, fixed_slice_with_steps_mut, generic_slice_mut, generic_slice_with_steps_mut, rows_range_pair_mut, columns_range_pair_mut); pub trait SliceRange { type Size: Dim; fn begin(&self, shape: D) -> usize; // NOTE: this is the index immediatly after the last index. fn end(&self, shape: D) -> usize; fn size(&self, shape: D) -> Self::Size; } impl SliceRange for usize { type Size = U1; #[inline(always)] fn begin(&self, _: D) -> usize { *self } #[inline(always)] fn end(&self, _: D) -> usize { *self + 1 } #[inline(always)] fn size(&self, _: D) -> Self::Size { U1 } } impl SliceRange for Range { type Size = Dynamic; #[inline(always)] fn begin(&self, _: D) -> usize { self.start } #[inline(always)] fn end(&self, _: D) -> usize { self.end } #[inline(always)] fn size(&self, _: D) -> Self::Size { Dynamic::new(self.end - self.start) } } impl SliceRange for RangeFrom { type Size = Dynamic; #[inline(always)] fn begin(&self, _: D) -> usize { self.start } #[inline(always)] fn end(&self, dim: D) -> usize { dim.value() } #[inline(always)] fn size(&self, dim: D) -> Self::Size { Dynamic::new(dim.value() - self.start) } } impl SliceRange for RangeTo { type Size = Dynamic; #[inline(always)] fn begin(&self, _: D) -> usize { 0 } #[inline(always)] fn end(&self, _: D) -> usize { self.end } #[inline(always)] fn size(&self, _: D) -> Self::Size { Dynamic::new(self.end) } } impl SliceRange for RangeFull { type Size = D; #[inline(always)] fn begin(&self, _: D) -> usize { 0 } #[inline(always)] fn end(&self, dim: D) -> usize { dim.value() } #[inline(always)] fn size(&self, dim: D) -> Self::Size { dim } } impl> Matrix { #[inline] pub fn slice_range(&self, rows: RowRange, cols: ColRange) -> MatrixSlice where RowRange: SliceRange, ColRange: SliceRange { let (nrows, ncols) = self.data.shape(); self.generic_slice((rows.begin(nrows), cols.begin(ncols)), (rows.size(nrows), cols.size(ncols))) } #[inline] pub fn rows_range>(&self, rows: RowRange) -> MatrixSlice { self.slice_range(rows, ..) } #[inline] pub fn columns_range>(&self, cols: ColRange) -> MatrixSlice { self.slice_range(.., cols) } } impl> Matrix { pub fn slice_range_mut(&mut self, rows: RowRange, cols: ColRange) -> MatrixSliceMut where RowRange: SliceRange, ColRange: SliceRange { let (nrows, ncols) = self.data.shape(); self.generic_slice_mut((rows.begin(nrows), cols.begin(ncols)), (rows.size(nrows), cols.size(ncols))) } #[inline] pub fn rows_range_mut>(&mut self, rows: RowRange) -> MatrixSliceMut { self.slice_range_mut(rows, ..) } #[inline] pub fn columns_range_mut>(&mut self, cols: ColRange) -> MatrixSliceMut { self.slice_range_mut(.., cols) } }