diff --git a/src/impl_ops.rs b/src/impl_ops.rs index 4804356e8..23ccd6ddb 100644 --- a/src/impl_ops.rs +++ b/src/impl_ops.rs @@ -53,9 +53,7 @@ macro_rules! impl_binary_op( /// Perform elementwise #[doc=$doc] /// between `self` and `rhs`, -/// and return the result (based on `self`). -/// -/// `self` must be an `Array` or `ArcArray`. +/// and return the result. /// /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// @@ -64,13 +62,13 @@ impl $trt> for ArrayBase where A: Clone + $trt, B: Clone, - S: DataOwned + DataMut, + S: Data, S2: Data, D: Dimension, E: Dimension, { - type Output = ArrayBase; - fn $mth(self, rhs: ArrayBase) -> ArrayBase + type Output = Array; + fn $mth(self, rhs: ArrayBase) -> Array { self.$mth(&rhs) } @@ -79,7 +77,7 @@ where /// Perform elementwise #[doc=$doc] /// between `self` and reference `rhs`, -/// and return the result (based on `self`). +/// and return the result. /// /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// @@ -88,18 +86,19 @@ impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase where A: Clone + $trt, B: Clone, - S: DataOwned + DataMut, + S: Data, S2: Data, D: Dimension, E: Dimension, { - type Output = ArrayBase; - fn $mth(mut self, rhs: &ArrayBase) -> ArrayBase + type Output = Array; + fn $mth(self, rhs: &ArrayBase) -> Array { - self.zip_mut_with(rhs, |x, y| { + let mut lhs = self.into_owned(); + lhs.zip_mut_with(rhs, |x, y| { *x = x.clone() $operator y.clone(); }); - self + lhs } } @@ -129,22 +128,45 @@ where /// Perform elementwise #[doc=$doc] -/// between `self` and the scalar `x`, -/// and return the result (based on `self`). +/// between `self` and `rhs`, +/// and return the result as a new `Array`. +/// +/// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// -/// `self` must be an `Array` or `ArcArray`. +/// **Panics** if broadcasting isn’t possible. +impl<'a, A, B, S, S2, D, E> $trt> for &'a ArrayBase +where + A: Clone + $trt, + B: Clone, + S: Data, + S2: Data, + D: Dimension, + E: Dimension, +{ + type Output = Array; + fn $mth(self, rhs: ArrayBase) -> Array { + // FIXME: Can we co-broadcast arrays here? And how? + self.to_owned().$mth(rhs) + } +} + +/// Perform elementwise +#[doc=$doc] +/// between `self` and the scalar `x`, +/// and return the result. impl $trt for ArrayBase where A: Clone + $trt, - S: DataOwned + DataMut, + S: Data, D: Dimension, B: ScalarOperand, { - type Output = ArrayBase; - fn $mth(mut self, x: B) -> ArrayBase { - self.unordered_foreach_mut(move |elt| { + type Output = Array; + fn $mth(self, x: B) -> Array { + let mut lhs = self.into_owned(); + lhs.unordered_foreach_mut(move |elt| { *elt = elt.clone() $operator x.clone(); }); - self + lhs } } @@ -183,17 +205,17 @@ macro_rules! impl_scalar_lhs_op { // these have no doc -- they are not visible in rustdoc // Perform elementwise // between the scalar `self` and array `rhs`, -// and return the result (based on `self`). +// and return the result. impl $trt> for $scalar - where S: DataOwned + DataMut, + where S: Data, D: Dimension, { - type Output = ArrayBase; - fn $mth(self, rhs: ArrayBase) -> ArrayBase { + type Output = Array<$scalar, D>; + fn $mth(self, rhs: ArrayBase) -> Array<$scalar, D> { if_commutative!($commutative { rhs.$mth(self) } or {{ - let mut rhs = rhs; + let mut rhs = rhs.into_owned(); rhs.unordered_foreach_mut(move |elt| { *elt = self $operator *elt; }); @@ -293,16 +315,17 @@ mod arithmetic_ops { impl Neg for ArrayBase where A: Clone + Neg, - S: DataOwned + DataMut, + S: Data, D: Dimension, { - type Output = Self; + type Output = Array; /// Perform an elementwise negation of `self` and return the result. - fn neg(mut self) -> Self { - self.unordered_foreach_mut(|elt| { + fn neg(self) -> Array { + let mut array = self.into_owned(); + array.unordered_foreach_mut(|elt| { *elt = -elt.clone(); }); - self + array } } @@ -323,16 +346,17 @@ mod arithmetic_ops { impl Not for ArrayBase where A: Clone + Not, - S: DataOwned + DataMut, + S: Data, D: Dimension, { - type Output = Self; + type Output = Array; /// Perform an elementwise unary not of `self` and return the result. - fn not(mut self) -> Self { - self.unordered_foreach_mut(|elt| { + fn not(self) -> Array { + let mut array = self.into_owned(); + array.unordered_foreach_mut(|elt| { *elt = !elt.clone(); }); - self + array } } @@ -359,6 +383,23 @@ mod assign_ops { ($trt:ident, $method:ident, $doc:expr) => { use std::ops::$trt; + #[doc=$doc] + /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. + /// + /// **Panics** if broadcasting isn’t possible. + impl $trt> for ArrayBase + where + A: Clone + $trt, + S: DataMut, + S2: Data, + D: Dimension, + E: Dimension, + { + fn $method(&mut self, rhs: ArrayBase) { + self.$method(&rhs) + } + } + #[doc=$doc] /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// diff --git a/src/lib.rs b/src/lib.rs index 35d1e1aab..ca63b8dd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -607,18 +607,14 @@ pub type Ixs = isize; /// /// ### Binary Operators with Two Arrays /// -/// Let `A` be an array or view of any kind. Let `B` be an array -/// with owned storage (either `Array` or `ArcArray`). -/// Let `C` be an array with mutable data (either `Array`, `ArcArray` -/// or `ArrayViewMut`). -/// The following combinations of operands -/// are supported for an arbitrary binary operator denoted by `@` (it can be -/// `+`, `-`, `*`, `/` and so on). -/// -/// - `&A @ &A` which produces a new `Array` -/// - `B @ A` which consumes `B`, updates it with the result, and returns it -/// - `B @ &A` which consumes `B`, updates it with the result, and returns it -/// - `C @= &A` which performs an arithmetic operation in place +/// Let `A` be an array or view of any kind. Let `M` be an array with mutable +/// data (either `Array`, `ArcArray` or `ArrayViewMut`). The following +/// combinations of operands are supported for an arbitrary binary operator +/// denoted by `@` (it can be `+`, `-`, `*`, `/` and so on). +/// +/// - `&A @ &A` or `&A @ A` which produce a new `Array` +/// - `A @ &A` or `A @ A` which may reuse the allocation of the LHS if it's an owned array +/// - `M @= &A` or `M @= A` which performs an arithmetic operation in place on `M` /// /// Note that the element type needs to implement the operator trait and the /// `Clone` trait. @@ -647,17 +643,16 @@ pub type Ixs = isize; /// `ScalarOperand` docs has the detailed condtions). /// /// - `&A @ K` or `K @ &A` which produces a new `Array` -/// - `B @ K` or `K @ B` which consumes `B`, updates it with the result and returns it -/// - `C @= K` which performs an arithmetic operation in place +/// - `A @ K` or `K @ A` which may reuse the allocation of the array if it's an owned array +/// - `M @= K` which performs an arithmetic operation in place /// /// ### Unary Operators /// -/// Let `A` be an array or view of any kind. Let `B` be an array with owned -/// storage (either `Array` or `ArcArray`). The following operands are supported -/// for an arbitrary unary operator denoted by `@` (it can be `-` or `!`). +/// The following operands are supported for an arbitrary unary operator +/// denoted by `@` (it can be `-` or `!`). /// /// - `@&A` which produces a new `Array` -/// - `@B` which consumes `B`, updates it with the result, and returns it +/// - `@A` which may reuse the allocation of the array if it's an owned array /// /// ## Broadcasting /// diff --git a/tests/array.rs b/tests/array.rs index ea374bc7c..4c997798d 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -394,11 +394,11 @@ fn test_add() { } let B = A.clone(); - A = A + &B; - assert_eq!(A[[0, 0]], 0); - assert_eq!(A[[0, 1]], 2); - assert_eq!(A[[1, 0]], 4); - assert_eq!(A[[1, 1]], 6); + let C = A + &B; + assert_eq!(C[[0, 0]], 0); + assert_eq!(C[[0, 1]], 2); + assert_eq!(C[[1, 0]], 4); + assert_eq!(C[[1, 1]], 6); } #[test]