use num::{Zero, One}; use std::cmp; use std::ptr; use core::{DefaultAllocator, Scalar, Matrix, DMatrix, MatrixMN, Vector}; use core::dimension::{Dim, DimName, DimSub, DimDiff, DimAdd, DimSum, DimMin, DimMinimum, U1, Dynamic}; use core::constraint::{ShapeConstraint, DimEq}; use core::allocator::{Allocator, Reallocator}; use core::storage::{Storage, StorageMut}; impl> Matrix { /// Extracts the upper triangular part of this matrix (including the diagonal). #[inline] pub fn upper_triangle(&self) -> MatrixMN where DefaultAllocator: Allocator { let mut res = self.clone_owned(); res.fill_lower_triangle(N::zero(), 1); res } /// Extracts the upper triangular part of this matrix (including the diagonal). #[inline] pub fn lower_triangle(&self) -> MatrixMN where DefaultAllocator: Allocator { let mut res = self.clone_owned(); res.fill_upper_triangle(N::zero(), 1); res } } impl> Matrix { /// Sets all the elements of this matrix to `val`. #[inline] pub fn fill(&mut self, val: N) { for e in self.iter_mut() { *e = val } } /// Fills `self` with the identity matrix. #[inline] pub fn fill_with_identity(&mut self) where N: Zero + One { self.fill(N::zero()); self.fill_diagonal(N::one()); } /// Sets all the diagonal elements of this matrix to `val`. #[inline] pub fn fill_diagonal(&mut self, val: N) { let (nrows, ncols) = self.shape(); let n = cmp::min(nrows, ncols); for i in 0 .. n { unsafe { *self.get_unchecked_mut(i, i) = val } } } /// Sets all the elements of the selected row to `val`. #[inline] pub fn fill_row(&mut self, i: usize, val: N) { assert!(i < self.nrows(), "Row index out of bounds."); for j in 0 .. self.ncols() { unsafe { *self.get_unchecked_mut(i, j) = val } } } /// Sets all the elements of the selected column to `val`. #[inline] pub fn fill_column(&mut self, j: usize, val: N) { assert!(j < self.ncols(), "Row index out of bounds."); for i in 0 .. self.nrows() { unsafe { *self.get_unchecked_mut(i, j) = val } } } /// Fills the diagonal of this matrix with the content of the given vector. #[inline] pub fn set_diagonal(&mut self, diag: &Vector) where R: DimMin, S2: Storage, ShapeConstraint: DimEq, R2> { let (nrows, ncols) = self.shape(); let min_nrows_ncols = cmp::min(nrows, ncols); assert_eq!(diag.len(), min_nrows_ncols, "Mismatched dimensions."); for i in 0 .. min_nrows_ncols { unsafe { *self.get_unchecked_mut(i, i) = *diag.vget_unchecked(i) } } } /// Sets all the elements of the lower-triangular part of this matrix to `val`. /// /// The parameter `shift` allows some subdiagonals to be left untouched: /// * If `shift = 0` then the diagonal is overwritten as well. /// * If `shift = 1` then the diagonal is left untouched. /// * If `shift > 1`, then the diagonal and the first `shift - 1` subdiagonals are left /// untouched. #[inline] pub fn fill_lower_triangle(&mut self, val: N, shift: usize) { for j in 0 .. self.ncols() { for i in (j + shift) .. self.nrows() { unsafe { *self.get_unchecked_mut(i, j) = val } } } } /// Sets all the elements of the lower-triangular part of this matrix to `val`. /// /// The parameter `shift` allows some superdiagonals to be left untouched: /// * If `shift = 0` then the diagonal is overwritten as well. /// * If `shift = 1` then the diagonal is left untouched. /// * If `shift > 1`, then the diagonal and the first `shift - 1` superdiagonals are left /// untouched. #[inline] pub fn fill_upper_triangle(&mut self, val: N, shift: usize) { for j in shift .. self.ncols() { // FIXME: is there a more efficient way to avoid the min ? // (necessary for rectangular matrices) for i in 0 .. cmp::min(j + 1 - shift, self.nrows()) { unsafe { *self.get_unchecked_mut(i, j) = val } } } } /// Swaps two rows in-place. #[inline] pub fn swap_rows(&mut self, irow1: usize, irow2: usize) { assert!(irow1 < self.nrows() && irow2 < self.nrows()); if irow1 != irow2 { // FIXME: optimize that. for i in 0 .. self.ncols() { unsafe { self.swap_unchecked((irow1, i), (irow2, i)) } } } // Otherwise do nothing. } /// Swaps two columns in-place. #[inline] pub fn swap_columns(&mut self, icol1: usize, icol2: usize) { assert!(icol1 < self.ncols() && icol2 < self.ncols()); if icol1 != icol2 { // FIXME: optimize that. for i in 0 .. self.nrows() { unsafe { self.swap_unchecked((i, icol1), (i, icol2)) } } } // Otherwise do nothing. } } impl> Matrix { /// Copies the upper-triangle of this matrix to its lower-triangular part. /// /// This makes the matrix symmetric. Panics if the matrix is not square. pub fn fill_lower_triangle_with_upper_triangle(&mut self) { assert!(self.is_square(), "The input matrix should be square."); let dim = self.nrows(); for j in 0 .. dim { for i in j + 1 .. dim { unsafe { *self.get_unchecked_mut(i, j) = *self.get_unchecked(j, i); } } } } /// Copies the upper-triangle of this matrix to its upper-triangular part. /// /// This makes the matrix symmetric. Panics if the matrix is not square. pub fn fill_upper_triangle_with_lower_triangle(&mut self) { assert!(self.is_square(), "The input matrix should be square."); for j in 1 .. self.ncols() { for i in 0 .. j { unsafe { *self.get_unchecked_mut(i, j) = *self.get_unchecked(j, i); } } } } } /* * * FIXME: specialize all the following for slices. * */ impl> Matrix { /* * * Column removal. * */ /// Removes the `i`-th column from this matrix. #[inline] pub fn remove_column(self, i: usize) -> MatrixMN> where C: DimSub, DefaultAllocator: Reallocator> { self.remove_fixed_columns::(i) } /// Removes a fixed number − `D::dim()` − of consecutive columns from this matrix, starting /// with the `i`-th (included). #[inline] pub fn remove_fixed_columns(self, i: usize) -> MatrixMN> where D: DimName, C: DimSub, DefaultAllocator: Reallocator> { self.remove_columns_generic(i, D::name()) } /// Removes `n` consecutive columns from this matrix, starting with the `i`-th (included). #[inline] pub fn remove_columns(self, i: usize, n: usize) -> MatrixMN where C: DimSub, DefaultAllocator: Reallocator { self.remove_columns_generic(i, Dynamic::new(n)) } /// Removes `nremove.value()` columns from this matrix, starting with the `i`-th (included). /// /// This is the generic implementation of `.remove_columns(...)` and /// `.remove_fixed_columns(...)` which have nicer API interfaces. #[inline] pub fn remove_columns_generic(self, i: usize, nremove: D) -> MatrixMN> where D: Dim, C: DimSub, DefaultAllocator: Reallocator> { let mut m = self.into_owned(); let (nrows, ncols) = m.data.shape(); assert!(i + nremove.value() <= ncols.value(), "Column index out of range."); if nremove.value() != 0 && i + nremove.value() < ncols.value() { // The first `deleted_i * nrows` are left untouched. let copied_value_start = i + nremove.value(); unsafe { let ptr_in = m.data.ptr().offset((copied_value_start * nrows.value()) as isize); let ptr_out = m.data.ptr_mut().offset((i * nrows.value()) as isize); ptr::copy(ptr_in, ptr_out, (ncols.value() - copied_value_start) * nrows.value()); } } unsafe { Matrix::from_data(DefaultAllocator::reallocate_copy(nrows, ncols.sub(nremove), m.data)) } } /* * * Row removal. * */ /// Removes the `i`-th row from this matrix. #[inline] pub fn remove_row(self, i: usize) -> MatrixMN, C> where R: DimSub, DefaultAllocator: Reallocator, C> { self.remove_fixed_rows::(i) } /// Removes a fixed number − `D::dim()` − of consecutive rows from this matrix, starting /// with the `i`-th (included). #[inline] pub fn remove_fixed_rows(self, i: usize) -> MatrixMN, C> where D: DimName, R: DimSub, DefaultAllocator: Reallocator, C> { self.remove_rows_generic(i, D::name()) } /// Removes `n` consecutive rows from this matrix, starting with the `i`-th (included). #[inline] pub fn remove_rows(self, i: usize, n: usize) -> MatrixMN where R: DimSub, DefaultAllocator: Reallocator { self.remove_rows_generic(i, Dynamic::new(n)) } /// Removes `nremove.value()` rows from this matrix, starting with the `i`-th (included). /// /// This is the generic implementation of `.remove_rows(...)` and `.remove_fixed_rows(...)` /// which have nicer API interfaces. #[inline] pub fn remove_rows_generic(self, i: usize, nremove: D) -> MatrixMN, C> where D: Dim, R: DimSub, DefaultAllocator: Reallocator, C> { let mut m = self.into_owned(); let (nrows, ncols) = m.data.shape(); assert!(i + nremove.value() <= nrows.value(), "Row index out of range."); if nremove.value() != 0 { unsafe { compress_rows(&mut m.data.as_mut_slice(), nrows.value(), ncols.value(), i, nremove.value()); } } unsafe { Matrix::from_data(DefaultAllocator::reallocate_copy(nrows.sub(nremove), ncols, m.data)) } } /* * * Columns insertion. * */ /// Inserts a column filled with `val` at the `i-th` position. #[inline] pub fn insert_column(self, i: usize, val: N) -> MatrixMN> where C: DimAdd, DefaultAllocator: Reallocator> { self.insert_fixed_columns::(i, val) } /// Inserts `D` column filled with `val` at the `i-th` position. #[inline] pub fn insert_fixed_columns(self, i: usize, val: N) -> MatrixMN> where D: DimName, C: DimAdd, DefaultAllocator: Reallocator> { let mut res = unsafe { self.insert_columns_generic_uninitialized(i, D::name()) }; res.fixed_columns_mut::(i).fill(val); res } /// Inserts `n` column filled with `val` at the `i-th` position. #[inline] pub fn insert_columns(self, i: usize, n: usize, val: N) -> MatrixMN where C: DimAdd, DefaultAllocator: Reallocator { let mut res = unsafe { self.insert_columns_generic_uninitialized(i, Dynamic::new(n)) }; res.columns_mut(i, n).fill(val); res } /// Inserts `ninsert.value()` columns at the `i-th` place of this matrix. /// /// The added column values are not initialized. #[inline] pub unsafe fn insert_columns_generic_uninitialized(self, i: usize, ninsert: D) -> MatrixMN> where D: Dim, C: DimAdd, DefaultAllocator: Reallocator> { let m = self.into_owned(); let (nrows, ncols) = m.data.shape(); let mut res = Matrix::from_data(DefaultAllocator::reallocate_copy(nrows, ncols.add(ninsert), m.data)); assert!(i <= ncols.value(), "Column insertion index out of range."); if ninsert.value() != 0 && i != ncols.value() { let ptr_in = res.data.ptr().offset((i * nrows.value()) as isize); let ptr_out = res.data.ptr_mut().offset(((i + ninsert.value()) * nrows.value()) as isize); ptr::copy(ptr_in, ptr_out, (ncols.value() - i) * nrows.value()) } res } /* * * Rows insertion. * */ /// Inserts a row filled with `val` at the `i-th` position. #[inline] pub fn insert_row(self, i: usize, val: N) -> MatrixMN, C> where R: DimAdd, DefaultAllocator: Reallocator, C> { self.insert_fixed_rows::(i, val) } /// Inserts `D` row filled with `val` at the `i-th` position. #[inline] pub fn insert_fixed_rows(self, i: usize, val: N) -> MatrixMN, C> where D: DimName, R: DimAdd, DefaultAllocator: Reallocator, C> { let mut res = unsafe { self.insert_rows_generic_uninitialized(i, D::name()) }; res.fixed_rows_mut::(i).fill(val); res } /// Inserts `n` rows filled with `val` at the `i-th` position. #[inline] pub fn insert_rows(self, i: usize, n: usize, val: N) -> MatrixMN where R: DimAdd, DefaultAllocator: Reallocator { let mut res = unsafe { self.insert_rows_generic_uninitialized(i, Dynamic::new(n)) }; res.rows_mut(i, n).fill(val); res } /// Inserts `ninsert.value()` rows at the `i-th` place of this matrix. /// /// The added rows values are not initialized. #[inline] pub unsafe fn insert_rows_generic_uninitialized(self, i: usize, ninsert: D) -> MatrixMN, C> where D: Dim, R: DimAdd, DefaultAllocator: Reallocator, C> { let m = self.into_owned(); let (nrows, ncols) = m.data.shape(); let mut res = Matrix::from_data(DefaultAllocator::reallocate_copy(nrows.add(ninsert), ncols, m.data)); assert!(i <= nrows.value(), "Row insertion index out of range."); if ninsert.value() != 0 { extend_rows(&mut res.data.as_mut_slice(), nrows.value(), ncols.value(), i, ninsert.value()); } res } /* * * Resizing. * */ pub fn resize(self, new_nrows: usize, new_ncols: usize, val: N) -> DMatrix where DefaultAllocator: Reallocator { self.resize_generic(Dynamic::new(new_nrows), Dynamic::new(new_ncols), val) } pub fn fixed_resize(self, val: N) -> MatrixMN where DefaultAllocator: Reallocator { self.resize_generic(R2::name(), C2::name(), val) } /// Resizes `self` such that it has dimensions `new_nrows × now_ncols`. The values are copied /// such that `self[(i, j)] == result[(i, j)]`. If the result has more rows and/or columns than /// `self`, then the extra rows or columns are filled with `val`. #[inline] pub fn resize_generic(self, new_nrows: R2, new_ncols: C2, val: N) -> MatrixMN where DefaultAllocator: Reallocator { let (nrows, ncols) = self.shape(); let mut data = self.data.into_owned(); if new_nrows.value() == nrows { let res = unsafe { DefaultAllocator::reallocate_copy(new_nrows, new_ncols, data) }; Matrix::from_data(res) } else { let mut res; unsafe { if new_nrows.value() < nrows { compress_rows(&mut data.as_mut_slice(), nrows, ncols, new_nrows.value(), nrows - new_nrows.value()); res = Matrix::from_data(DefaultAllocator::reallocate_copy(new_nrows, new_ncols, data)); } else { res = Matrix::from_data(DefaultAllocator::reallocate_copy(new_nrows, new_ncols, data)); extend_rows(&mut res.data.as_mut_slice(), nrows, ncols, nrows, new_nrows.value() - nrows); } } if new_ncols.value() > ncols { res.columns_range_mut(ncols ..).fill(val); } if new_nrows.value() > nrows { res.slice_range_mut(nrows .., .. cmp::min(ncols, new_ncols.value())).fill(val); } res } } } unsafe fn compress_rows(data: &mut [N], nrows: usize, ncols: usize, i: usize, nremove: usize) { let new_nrows = nrows - nremove; let ptr_in = data.as_ptr(); let ptr_out = data.as_mut_ptr(); let mut curr_i = i; for k in 0 .. ncols - 1 { ptr::copy(ptr_in.offset((curr_i + (k + 1) * nremove) as isize), ptr_out.offset(curr_i as isize), new_nrows); curr_i += new_nrows; } // Deal with the last column from which less values have to be copied. let remaining_len = nrows - i - nremove; ptr::copy(ptr_in.offset((nrows * ncols - remaining_len) as isize), ptr_out.offset(curr_i as isize), remaining_len); } unsafe fn extend_rows(data: &mut [N], nrows: usize, ncols: usize, i: usize, ninsert: usize) { let new_nrows = nrows + ninsert; let ptr_in = data.as_ptr(); let ptr_out = data.as_mut_ptr(); let remaining_len = nrows - i; let mut curr_i = new_nrows * ncols - remaining_len; // Deal with the last column from which less values have to be copied. ptr::copy(ptr_in.offset((nrows * ncols - remaining_len) as isize), ptr_out.offset(curr_i as isize), remaining_len); for k in (0 .. ncols - 1).rev() { curr_i -= new_nrows; ptr::copy(ptr_in.offset((k * nrows + i) as isize), ptr_out.offset(curr_i as isize), nrows); } }