use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg, Index, IndexMut}; use num::Zero; use alga::general::{ClosedMul, ClosedDiv, ClosedAdd, ClosedSub, ClosedNeg}; use core::{Scalar, Matrix, OwnedMatrix, MatrixSum, MatrixMul, MatrixTrMul}; use core::dimension::Dim; use core::constraint::{ShapeConstraint, SameNumberOfRows, SameNumberOfColumns, AreMultipliable}; use core::storage::{Storage, StorageMut, OwnedStorage}; use core::allocator::{SameShapeAllocator, Allocator, OwnedAllocator}; /* * * Indexing. * */ impl> Index for Matrix { type Output = N; #[inline] fn index(&self, i: usize) -> &N { let ij = self.vector_to_matrix_index(i); &self[ij] } } impl Index<(usize, usize)> for Matrix where N: Scalar, S: Storage { type Output = N; #[inline] fn index(&self, ij: (usize, usize)) -> &N { assert!(ij < self.shape(), "Matrix index out of bounds."); unsafe { self.get_unchecked(ij.0, ij.1) } } } // Mutable versions. impl> IndexMut for Matrix { #[inline] fn index_mut(&mut self, i: usize) -> &mut N { let ij = self.vector_to_matrix_index(i); &mut self[ij] } } impl IndexMut<(usize, usize)> for Matrix where N: Scalar, S: StorageMut { #[inline] fn index_mut(&mut self, ij: (usize, usize)) -> &mut N { assert!(ij < self.shape(), "Matrix index out of bounds."); unsafe { self.get_unchecked_mut(ij.0, ij.1) } } } /* * * Neg * */ impl Neg for Matrix where N: Scalar + ClosedNeg, S: Storage { type Output = OwnedMatrix; #[inline] fn neg(self) -> Self::Output { let mut res = self.into_owned(); res.neg_mut(); res } } impl<'a, N, R: Dim, C: Dim, S> Neg for &'a Matrix where N: Scalar + ClosedNeg, S: Storage { type Output = OwnedMatrix; #[inline] fn neg(self) -> Self::Output { -self.clone_owned() } } impl Matrix where N: Scalar + ClosedNeg, S: StorageMut { /// Negates `self` in-place. #[inline] pub fn neg_mut(&mut self) { for e in self.iter_mut() { *e = -*e } } } /* * * Addition & Substraction * */ macro_rules! componentwise_binop_impl( ($Trait: ident, $method: ident, $bound: ident; $TraitAssign: ident, $method_assign: ident) => { impl<'b, N, R1, C1, R2, C2, SA, SB> $Trait<&'b Matrix> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, N: Scalar + $bound, SA: Storage, SB: Storage, SA::Alloc: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, right: &'b Matrix) -> Self::Output { assert!(self.shape() == right.shape(), "Matrix addition/subtraction dimensions mismatch."); let mut res = self.into_owned_sum::(); for (left, right) in res.iter_mut().zip(right.iter()) { *left = left.$method(*right) } res } } impl<'a, N, R1, C1, R2, C2, SA, SB> $Trait> for &'a Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, N: Scalar + $bound, SA: Storage, SB: Storage, SB::Alloc: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, right: Matrix) -> Self::Output { assert!(self.shape() == right.shape(), "Matrix addition/subtraction dimensions mismatch."); let mut res = right.into_owned_sum::(); for (left, right) in self.iter().zip(res.iter_mut()) { *right = left.$method(*right) } res } } impl $Trait> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, N: Scalar + $bound, SA: Storage, SB: Storage, SA::Alloc: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, right: Matrix) -> Self::Output { self.$method(&right) } } impl<'a, 'b, N, R1, C1, R2, C2, SA, SB> $Trait<&'b Matrix> for &'a Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, N: Scalar + $bound, SA: Storage, SB: Storage, SA::Alloc: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, right: &'b Matrix) -> Self::Output { self.clone_owned().$method(right) } } impl<'b, N, R1, C1, R2, C2, SA, SB> $TraitAssign<&'b Matrix> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, N: Scalar + $bound, SA: StorageMut, SB: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { #[inline] fn $method_assign(&mut self, right: &'b Matrix) { assert!(self.shape() == right.shape(), "Matrix addition/subtraction dimensions mismatch."); for (left, right) in self.iter_mut().zip(right.iter()) { left.$method_assign(*right) } } } impl $TraitAssign> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, N: Scalar + $bound, SA: StorageMut, SB: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { #[inline] fn $method_assign(&mut self, right: Matrix) { self.$method_assign(&right) } } } ); componentwise_binop_impl!(Add, add, ClosedAdd; AddAssign, add_assign); componentwise_binop_impl!(Sub, sub, ClosedSub; SubAssign, sub_assign); /* * * Multiplication * */ // Matrix × Scalar // Matrix / Scalar macro_rules! componentwise_scalarop_impl( ($Trait: ident, $method: ident, $bound: ident; $TraitAssign: ident, $method_assign: ident) => { impl $Trait for Matrix where N: Scalar + $bound, S: Storage { type Output = OwnedMatrix; #[inline] fn $method(self, rhs: N) -> Self::Output { let mut res = self.into_owned(); for left in res.iter_mut() { *left = left.$method(rhs) } res } } impl<'a, N, R: Dim, C: Dim, S> $Trait for &'a Matrix where N: Scalar + $bound, S: Storage { type Output = OwnedMatrix; #[inline] fn $method(self, rhs: N) -> Self::Output { self.clone_owned().$method(rhs) } } impl $TraitAssign for Matrix where N: Scalar + $bound, S: StorageMut { #[inline] fn $method_assign(&mut self, right: N) { for left in self.iter_mut() { left.$method_assign(right) } } } } ); componentwise_scalarop_impl!(Mul, mul, ClosedMul; MulAssign, mul_assign); componentwise_scalarop_impl!(Div, div, ClosedDiv; DivAssign, div_assign); macro_rules! left_scalar_mul_impl( ($($T: ty),* $(,)*) => {$( impl Mul> for $T where S: Storage<$T, R, C> { type Output = OwnedMatrix<$T, R, C, S::Alloc>; #[inline] fn mul(self, right: Matrix<$T, R, C, S>) -> Self::Output { let mut res = right.into_owned(); for right in res.iter_mut() { *right = self * *right } res } } impl<'b, R: Dim, C: Dim, S> Mul<&'b Matrix<$T, R, C, S>> for $T where S: Storage<$T, R, C> { type Output = OwnedMatrix<$T, R, C, S::Alloc>; #[inline] fn mul(self, right: &'b Matrix<$T, R, C, S>) -> Self::Output { self * right.clone_owned() } } )*} ); left_scalar_mul_impl!( u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64 ); // Matrix × Matrix impl<'a, 'b, N, R1: Dim, C1: Dim, R2: Dim, C2: Dim, SA, SB> Mul<&'b Matrix> for &'a Matrix where N: Scalar + Zero + ClosedAdd + ClosedMul, SB: Storage, SA: Storage, SA::Alloc: Allocator, ShapeConstraint: AreMultipliable { type Output = MatrixMul; #[inline] fn mul(self, right: &'b Matrix) -> Self::Output { let (nrows1, ncols1) = self.shape(); let (nrows2, ncols2) = right.shape(); assert!(ncols1 == nrows2, "Matrix multiplication dimensions mismatch."); let mut res: MatrixMul = unsafe { Matrix::new_uninitialized_generic(self.data.shape().0, right.data.shape().1) }; for i in 0 .. nrows1 { for j in 0 .. ncols2 { let mut acc = N::zero(); unsafe { for k in 0 .. ncols1 { acc = acc + *self.get_unchecked(i, k) * *right.get_unchecked(k, j); } *res.get_unchecked_mut(i, j) = acc; } } } res } } impl<'a, N, R1: Dim, C1: Dim, R2: Dim, C2: Dim, SA, SB> Mul> for &'a Matrix where N: Scalar + Zero + ClosedAdd + ClosedMul, SB: Storage, SA: Storage, SA::Alloc: Allocator, ShapeConstraint: AreMultipliable { type Output = MatrixMul; #[inline] fn mul(self, right: Matrix) -> Self::Output { self * &right } } impl<'b, N, R1: Dim, C1: Dim, R2: Dim, C2: Dim, SA, SB> Mul<&'b Matrix> for Matrix where N: Scalar + Zero + ClosedAdd + ClosedMul, SB: Storage, SA: Storage, SA::Alloc: Allocator, ShapeConstraint: AreMultipliable { type Output = MatrixMul; #[inline] fn mul(self, right: &'b Matrix) -> Self::Output { &self * right } } impl Mul> for Matrix where N: Scalar + Zero + ClosedAdd + ClosedMul, SB: Storage, SA: Storage, SA::Alloc: Allocator, ShapeConstraint: AreMultipliable { type Output = MatrixMul; #[inline] fn mul(self, right: Matrix) -> Self::Output { &self * &right } } // FIXME: this is too restrictive: // − we can't use `a *= b` when `a` is a mutable slice. // − we can't use `a *= b` when C2 is not equal to C1. impl MulAssign> for Matrix where R1: Dim, C1: Dim, R2: Dim, N: Scalar + Zero + ClosedAdd + ClosedMul, SB: Storage, SA: OwnedStorage, ShapeConstraint: AreMultipliable, SA::Alloc: OwnedAllocator { #[inline] fn mul_assign(&mut self, right: Matrix) { *self = &*self * right } } impl<'b, N, R1, C1, R2, SA, SB> MulAssign<&'b Matrix> for Matrix where R1: Dim, C1: Dim, R2: Dim, N: Scalar + Zero + ClosedAdd + ClosedMul, SB: Storage, SA: OwnedStorage, ShapeConstraint: AreMultipliable, // FIXME: this is too restrictive. See comments for the non-ref version. SA::Alloc: OwnedAllocator { #[inline] fn mul_assign(&mut self, right: &'b Matrix) { *self = &*self * right } } impl Matrix where N: Scalar + Zero + ClosedAdd + ClosedMul, SA: Storage { /// Equivalent to `self.transpose() * right`. #[inline] pub fn tr_mul(&self, right: &Matrix) -> MatrixTrMul where SB: Storage, SA::Alloc: Allocator, ShapeConstraint: AreMultipliable { let (nrows1, ncols1) = self.shape(); let (nrows2, ncols2) = right.shape(); assert!(nrows1 == nrows2, "Matrix multiplication dimensions mismatch."); let mut res: MatrixTrMul = unsafe { Matrix::new_uninitialized_generic(self.data.shape().1, right.data.shape().1) }; for i in 0 .. ncols1 { for j in 0 .. ncols2 { let mut acc = N::zero(); unsafe { for k in 0 .. nrows1 { acc = acc + *self.get_unchecked(k, i) * *right.get_unchecked(k, j); } *res.get_unchecked_mut(i, j) = acc; } } } res } }