Relax constraints for some operations

Many operations expected the numeric type `N` to implement `ClosedAdd`
and/or `ClosedSub`.
This commit replaces some of these with the less restrictive
`Simple{Add,Sub}`, which does not require `{Add,Sub}Assign`.
This commit is contained in:
Alexander 'z33ky' Hirsch 2020-05-01 22:14:55 +02:00
parent 66957980cc
commit 788c261df8
9 changed files with 329 additions and 97 deletions

View File

@ -739,7 +739,7 @@ impl_constructors_from_data!(data; Dynamic, Dynamic;
*/ */
impl<N, R: DimName, C: DimName> Zero for MatrixMN<N, R, C> impl<N, R: DimName, C: DimName> Zero for MatrixMN<N, R, C>
where where
N: Scalar + Zero + ClosedAdd, N: Scalar + Zero,
DefaultAllocator: Allocator<N, R, C>, DefaultAllocator: Allocator<N, R, C>,
{ {
#[inline] #[inline]

View File

@ -16,7 +16,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "abomonation-serialize")] #[cfg(feature = "abomonation-serialize")]
use abomonation::Abomonation; use abomonation::Abomonation;
use simba::scalar::{ClosedAdd, ClosedMul, ClosedSub, Field, RealField}; use simba::scalar::{ClosedAdd, ClosedMul, Field, RealField};
use simba::simd::SimdPartialOrd; use simba::simd::SimdPartialOrd;
use crate::base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR}; use crate::base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR};
@ -28,7 +28,7 @@ use crate::base::iter::{
use crate::base::storage::{ use crate::base::storage::{
ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut, ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut,
}; };
use crate::base::{DefaultAllocator, MatrixMN, MatrixN, Scalar, Unit, VectorN}; use crate::base::{DefaultAllocator, MatrixMN, MatrixN, Scalar, Unit, VectorN, ops::SimpleSub};
use crate::SimdComplexField; use crate::SimdComplexField;
/// A square matrix. /// A square matrix.
@ -1572,7 +1572,7 @@ fn lower_exp() {
) )
} }
impl<N: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<N, R, C>> impl<N: Scalar + SimpleSub + ClosedMul, R: Dim, C: Dim, S: Storage<N, R, C>>
Matrix<N, R, C, S> Matrix<N, R, C, S>
{ {
/// The perpendicular product between two 2D column vectors, i.e. `a.x * b.y - a.y * b.x`. /// The perpendicular product between two 2D column vectors, i.e. `a.x * b.y - a.y * b.x`.
@ -1715,7 +1715,7 @@ impl<N: SimdComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S
} }
} }
impl<N: Scalar + Zero + One + ClosedAdd + ClosedSub + ClosedMul, D: Dim, S: Storage<N, D>> impl<N: Scalar + Zero + One + ClosedAdd + SimpleSub + ClosedMul, D: Dim, S: Storage<N, D>>
Vector<N, D, S> Vector<N, D, S>
{ {
/// Returns `self * (1.0 - t) + rhs * t`, i.e., the linear blend of the vectors x and y using the scalar value a. /// Returns `self * (1.0 - t) + rhs * t`, i.e., the linear blend of the vectors x and y using the scalar value a.

View File

@ -5,10 +5,11 @@ use num::{One, Zero};
use alga::general::{ use alga::general::{
AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule,
AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, ClosedAdd, ClosedMul, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, ClosedMul, ClosedNeg,
ClosedNeg, ComplexField, Field, Identity, JoinSemilattice, Lattice, MeetSemilattice, Module, ComplexField, Field, Identity, JoinSemilattice, Lattice, MeetSemilattice, Module,
Multiplicative, RingCommutative, TwoSidedInverse, Multiplicative, RingCommutative, TwoSidedInverse,
}; };
use crate::base::{SimpleAdd, SimpleSub};
use alga::linear::{ use alga::linear::{
FiniteDimInnerSpace, FiniteDimVectorSpace, InnerSpace, NormedSpace, VectorSpace, FiniteDimInnerSpace, FiniteDimVectorSpace, InnerSpace, NormedSpace, VectorSpace,
}; };
@ -36,7 +37,7 @@ where
impl<N, R: DimName, C: DimName> AbstractMagma<Additive> for MatrixMN<N, R, C> impl<N, R: DimName, C: DimName> AbstractMagma<Additive> for MatrixMN<N, R, C>
where where
N: Scalar + ClosedAdd, N: Scalar + SimpleAdd,
DefaultAllocator: Allocator<N, R, C>, DefaultAllocator: Allocator<N, R, C>,
{ {
#[inline] #[inline]
@ -71,12 +72,12 @@ macro_rules! inherit_additive_structure(
); );
inherit_additive_structure!( inherit_additive_structure!(
AbstractSemigroup<Additive> + ClosedAdd, AbstractSemigroup<Additive> + SimpleAdd,
AbstractMonoid<Additive> + Zero + ClosedAdd, AbstractMonoid<Additive> + Zero + SimpleAdd,
AbstractQuasigroup<Additive> + ClosedAdd + ClosedNeg, AbstractQuasigroup<Additive> + SimpleAdd + ClosedNeg,
AbstractLoop<Additive> + Zero + ClosedAdd + ClosedNeg, AbstractLoop<Additive> + Zero + SimpleAdd + ClosedNeg,
AbstractGroup<Additive> + Zero + ClosedAdd + ClosedNeg, AbstractGroup<Additive> + Zero + SimpleAdd + ClosedNeg,
AbstractGroupAbelian<Additive> + Zero + ClosedAdd + ClosedNeg AbstractGroupAbelian<Additive> + Zero + SimpleAdd + ClosedNeg
); );
impl<N, R: DimName, C: DimName> AbstractModule for MatrixMN<N, R, C> impl<N, R: DimName, C: DimName> AbstractModule for MatrixMN<N, R, C>
@ -371,7 +372,7 @@ where
impl<N, D: DimName> AbstractMagma<Multiplicative> for MatrixN<N, D> impl<N, D: DimName> AbstractMagma<Multiplicative> for MatrixN<N, D>
where where
N: Scalar + Zero + One + ClosedAdd + ClosedMul, N: Scalar + Zero + One + ClosedMul,
DefaultAllocator: Allocator<N, D, D>, DefaultAllocator: Allocator<N, D, D>,
{ {
#[inline] #[inline]
@ -383,7 +384,7 @@ where
macro_rules! impl_multiplicative_structure( macro_rules! impl_multiplicative_structure(
($($marker: ident<$operator: ident> $(+ $bounds: ident)*),* $(,)*) => {$( ($($marker: ident<$operator: ident> $(+ $bounds: ident)*),* $(,)*) => {$(
impl<N, D: DimName> $marker<$operator> for MatrixN<N, D> impl<N, D: DimName> $marker<$operator> for MatrixN<N, D>
where N: Scalar + Zero + One + ClosedAdd + ClosedMul + $marker<$operator> $(+ $bounds)*, where N: Scalar + Zero + One + ClosedMul + $marker<$operator> $(+ $bounds)*,
DefaultAllocator: Allocator<N, D, D> { } DefaultAllocator: Allocator<N, D, D> { }
)*} )*}
); );

View File

@ -8,6 +8,7 @@ pub mod default_allocator;
pub mod dimension; pub mod dimension;
pub mod iter; pub mod iter;
mod ops; mod ops;
pub use ops::{SimpleAdd, SimpleSub};
pub mod storage; pub mod storage;
mod alias; mod alias;

View File

@ -132,9 +132,9 @@ where
*/ */
macro_rules! componentwise_binop_impl( macro_rules! componentwise_binop_impl(
($Trait: ident, $method: ident, $bound: ident; ($Trait: ident, $method: ident, $bound: ident, $bound_assign: ident;
$TraitAssign: ident, $method_assign: ident, $method_assign_statically_unchecked: ident, $TraitAssign: ident, $method_assign: ident, $method_assign_statically_unchecked: ident,
$method_assign_statically_unchecked_rhs: ident; $method_assign_statically_unchecked_rhs: ident, $method_assign_statically_unchecked_lhs: ident;
$method_to: ident, $method_to_statically_unchecked: ident) => { $method_to: ident, $method_to_statically_unchecked: ident) => {
impl<N, R1: Dim, C1: Dim, SA: Storage<N, R1, C1>> Matrix<N, R1, C1, SA> impl<N, R1: Dim, C1: Dim, SA: Storage<N, R1, C1>> Matrix<N, R1, C1, SA>
@ -182,34 +182,24 @@ macro_rules! componentwise_binop_impl(
} }
/*
*
* Methods without dimension checking at compile-time.
* This is useful for code reuse because the sum representative system does not plays
* easily with static checks.
*
*/
/// Equivalent to `self + rhs` but stores the result into `out` to avoid allocations.
#[inline] #[inline]
fn $method_assign_statically_unchecked<R2, C2, SB>(&mut self, rhs: &Matrix<N, R2, C2, SB>) pub fn $method_to<R2: Dim, C2: Dim, SB,
where R2: Dim, R3: Dim, C3: Dim, SC>(&self,
C2: Dim, rhs: &Matrix<N, R2, C2, SB>,
SA: StorageMut<N, R1, C1>, out: &mut Matrix<N, R3, C3, SC>)
SB: Storage<N, R2, C2> { where SB: Storage<N, R2, C2>,
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch."); SC: StorageMut<N, R3, C3>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> +
// This is the most common case and should be deduced at compile-time. SameNumberOfRows<R1, R3> + SameNumberOfColumns<C1, C3> {
// FIXME: use specialization instead? self.$method_to_statically_unchecked(rhs, out)
if self.data.is_contiguous() && rhs.data.is_contiguous() {
let arr1 = self.data.as_mut_slice();
let arr2 = rhs.data.as_slice();
for i in 0 .. arr2.len() {
unsafe {
arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).inlined_clone());
}
}
}
else {
for j in 0 .. rhs.ncols() {
for i in 0 .. rhs.nrows() {
unsafe {
self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).inlined_clone())
}
}
}
}
} }
@ -245,24 +235,71 @@ macro_rules! componentwise_binop_impl(
} }
/*
*
* Methods without dimension checking at compile-time.
* This is useful for code reuse because the sum representative system does not plays
* easily with static checks.
*
*/
/// Equivalent to `self + rhs` but stores the result into `out` to avoid allocations.
#[inline] #[inline]
pub fn $method_to<R2: Dim, C2: Dim, SB, fn $method_assign_statically_unchecked_lhs<R2, C2, SB>(&mut self, rhs: &Matrix<N, R2, C2, SB>)
R3: Dim, C3: Dim, SC>(&self, where R2: Dim,
rhs: &Matrix<N, R2, C2, SB>, C2: Dim,
out: &mut Matrix<N, R3, C3, SC>) SA: StorageMut<N, R1, C1>,
where SB: Storage<N, R2, C2>, SB: Storage<N, R2, C2> {
SC: StorageMut<N, R3, C3>, assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> +
SameNumberOfRows<R1, R3> + SameNumberOfColumns<C1, C3> { // This is the most common case and should be deduced at compile-time.
self.$method_to_statically_unchecked(rhs, out) // FIXME: use specialization instead?
if self.data.is_contiguous() && rhs.data.is_contiguous() {
let arr1 = self.data.as_mut_slice();
let arr2 = rhs.data.as_slice();
for i in 0 .. arr2.len() {
unsafe {
let res = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone());
*arr1.get_unchecked_mut(i) = res;
}
}
}
else {
for j in 0 .. rhs.ncols() {
for i in 0 .. rhs.nrows() {
unsafe {
let r = self.get_unchecked_mut((i, j));
*r = r.inlined_clone().$method(r.inlined_clone())
}
}
}
}
}
}
impl<N, R1: Dim, C1: Dim, SA: Storage<N, R1, C1>> Matrix<N, R1, C1, SA>
where N: Scalar + $bound_assign {
#[inline]
fn $method_assign_statically_unchecked<R2, C2, SB>(&mut self, rhs: &Matrix<N, R2, C2, SB>)
where R2: Dim,
C2: Dim,
SA: StorageMut<N, R1, C1>,
SB: Storage<N, R2, C2> {
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
// This is the most common case and should be deduced at compile-time.
// FIXME: use specialization instead?
if self.data.is_contiguous() && rhs.data.is_contiguous() {
let arr1 = self.data.as_mut_slice();
let arr2 = rhs.data.as_slice();
for i in 0 .. arr2.len() {
unsafe {
arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).inlined_clone());
}
}
}
else {
for j in 0 .. rhs.ncols() {
for i in 0 .. rhs.nrows() {
unsafe {
self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).inlined_clone())
}
}
}
}
} }
} }
@ -279,7 +316,7 @@ macro_rules! componentwise_binop_impl(
fn $method(self, rhs: &'b Matrix<N, R2, C2, SB>) -> Self::Output { fn $method(self, rhs: &'b Matrix<N, R2, C2, SB>) -> Self::Output {
assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch."); assert!(self.shape() == rhs.shape(), "Matrix addition/subtraction dimensions mismatch.");
let mut res = self.into_owned_sum::<R2, C2>(); let mut res = self.into_owned_sum::<R2, C2>();
res.$method_assign_statically_unchecked(rhs); res.$method_assign_statically_unchecked_lhs(rhs);
res res
} }
} }
@ -342,7 +379,7 @@ macro_rules! componentwise_binop_impl(
impl<'b, N, R1, C1, R2, C2, SA, SB> $TraitAssign<&'b Matrix<N, R2, C2, SB>> for Matrix<N, R1, C1, SA> impl<'b, N, R1, C1, R2, C2, SA, SB> $TraitAssign<&'b Matrix<N, R2, C2, SB>> for Matrix<N, R1, C1, SA>
where R1: Dim, C1: Dim, R2: Dim, C2: Dim, where R1: Dim, C1: Dim, R2: Dim, C2: Dim,
N: Scalar + $bound, N: Scalar + $bound_assign,
SA: StorageMut<N, R1, C1>, SA: StorageMut<N, R1, C1>,
SB: Storage<N, R2, C2>, SB: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> { ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
@ -355,7 +392,7 @@ macro_rules! componentwise_binop_impl(
impl<N, R1, C1, R2, C2, SA, SB> $TraitAssign<Matrix<N, R2, C2, SB>> for Matrix<N, R1, C1, SA> impl<N, R1, C1, R2, C2, SA, SB> $TraitAssign<Matrix<N, R2, C2, SB>> for Matrix<N, R1, C1, SA>
where R1: Dim, C1: Dim, R2: Dim, C2: Dim, where R1: Dim, C1: Dim, R2: Dim, C2: Dim,
N: Scalar + $bound, N: Scalar + $bound_assign,
SA: StorageMut<N, R1, C1>, SA: StorageMut<N, R1, C1>,
SB: Storage<N, R2, C2>, SB: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> { ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
@ -368,16 +405,24 @@ macro_rules! componentwise_binop_impl(
} }
); );
componentwise_binop_impl!(Add, add, ClosedAdd; /// Trait __alias__ for `Add` with result of type `Self`.
AddAssign, add_assign, add_assign_statically_unchecked, add_assign_statically_unchecked_mut; pub trait SimpleAdd<Right = Self>: Sized + Add<Right, Output = Self> {}
impl<T, Right> SimpleAdd<Right> for T where T: Add<Right, Output = T> {}
/// Trait __alias__ for `Sub` with result of type `Self`.
pub trait SimpleSub<Right = Self>: Sized + Sub<Right, Output = Self> {}
impl<T, Right> SimpleSub<Right> for T where T: Sub<Right, Output = T> {}
componentwise_binop_impl!(Add, add, SimpleAdd, ClosedAdd;
AddAssign, add_assign, add_assign_statically_unchecked, add_assign_statically_unchecked_mut, add_assign_statically_unchecked_lhs;
add_to, add_to_statically_unchecked); add_to, add_to_statically_unchecked);
componentwise_binop_impl!(Sub, sub, ClosedSub; componentwise_binop_impl!(Sub, sub, SimpleSub, ClosedSub;
SubAssign, sub_assign, sub_assign_statically_unchecked, sub_assign_statically_unchecked_mut; SubAssign, sub_assign, sub_assign_statically_unchecked, sub_assign_statically_unchecked_mut, sub_assign_statically_unchecked_lhs;
sub_to, sub_to_statically_unchecked); sub_to, sub_to_statically_unchecked);
impl<N, R: DimName, C: DimName> iter::Sum for MatrixMN<N, R, C> impl<N, R: DimName, C: DimName> iter::Sum for MatrixMN<N, R, C>
where where
N: Scalar + ClosedAdd + Zero, N: Scalar + SimpleAdd + Zero,
DefaultAllocator: Allocator<N, R, C>, DefaultAllocator: Allocator<N, R, C>,
{ {
fn sum<I: Iterator<Item = MatrixMN<N, R, C>>>(iter: I) -> MatrixMN<N, R, C> { fn sum<I: Iterator<Item = MatrixMN<N, R, C>>>(iter: I) -> MatrixMN<N, R, C> {
@ -387,7 +432,7 @@ where
impl<N, C: Dim> iter::Sum for MatrixMN<N, Dynamic, C> impl<N, C: Dim> iter::Sum for MatrixMN<N, Dynamic, C>
where where
N: Scalar + ClosedAdd + Zero, N: Scalar + SimpleAdd + Zero,
DefaultAllocator: Allocator<N, Dynamic, C>, DefaultAllocator: Allocator<N, Dynamic, C>,
{ {
/// # Example /// # Example
@ -417,7 +462,7 @@ where
impl<'a, N, R: DimName, C: DimName> iter::Sum<&'a MatrixMN<N, R, C>> for MatrixMN<N, R, C> impl<'a, N, R: DimName, C: DimName> iter::Sum<&'a MatrixMN<N, R, C>> for MatrixMN<N, R, C>
where where
N: Scalar + ClosedAdd + Zero, N: Scalar + SimpleAdd + Zero,
DefaultAllocator: Allocator<N, R, C>, DefaultAllocator: Allocator<N, R, C>,
{ {
fn sum<I: Iterator<Item = &'a MatrixMN<N, R, C>>>(iter: I) -> MatrixMN<N, R, C> { fn sum<I: Iterator<Item = &'a MatrixMN<N, R, C>>>(iter: I) -> MatrixMN<N, R, C> {
@ -427,7 +472,7 @@ where
impl<'a, N, C: Dim> iter::Sum<&'a MatrixMN<N, Dynamic, C>> for MatrixMN<N, Dynamic, C> impl<'a, N, C: Dim> iter::Sum<&'a MatrixMN<N, Dynamic, C>> for MatrixMN<N, Dynamic, C>
where where
N: Scalar + ClosedAdd + Zero, N: Scalar + SimpleAdd + Zero,
DefaultAllocator: Allocator<N, Dynamic, C>, DefaultAllocator: Allocator<N, Dynamic, C>,
{ {
/// # Example /// # Example

View File

@ -77,65 +77,66 @@ where
* *
*/ */
use crate::base::{SimpleAdd, SimpleSub};
// Point - Point // Point - Point
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D, U1), (D, U1) for D: DimName; (D, U1), (D, U1) for D: DimName;
self: &'a Point<N, D>, right: &'b Point<N, D>, Output = VectorSum<N, D, D>; self: &'a Point<N, D>, right: &'b Point<N, D>, Output = VectorSum<N, D, D>;
&self.coords - &right.coords; 'a, 'b); &self.coords - &right.coords; 'a, 'b);
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D, U1), (D, U1) for D: DimName; (D, U1), (D, U1) for D: DimName;
self: &'a Point<N, D>, right: Point<N, D>, Output = VectorSum<N, D, D>; self: &'a Point<N, D>, right: Point<N, D>, Output = VectorSum<N, D, D>;
&self.coords - right.coords; 'a); &self.coords - right.coords; 'a);
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D, U1), (D, U1) for D: DimName; (D, U1), (D, U1) for D: DimName;
self: Point<N, D>, right: &'b Point<N, D>, Output = VectorSum<N, D, D>; self: Point<N, D>, right: &'b Point<N, D>, Output = VectorSum<N, D, D>;
self.coords - &right.coords; 'b); self.coords - &right.coords; 'b);
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D, U1), (D, U1) for D: DimName; (D, U1), (D, U1) for D: DimName;
self: Point<N, D>, right: Point<N, D>, Output = VectorSum<N, D, D>; self: Point<N, D>, right: Point<N, D>, Output = VectorSum<N, D, D>;
self.coords - right.coords; ); self.coords - right.coords; );
// Point - Vector // Point - Vector
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: &'a Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>; self: &'a Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(&self.coords - right); 'a, 'b); Self::Output::from(&self.coords - right); 'a, 'b);
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: &'a Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>; self: &'a Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(&self.coords - &right); 'a); // FIXME: should not be a ref to `right`. Self::Output::from(&self.coords - &right); 'a); // FIXME: should not be a ref to `right`.
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>; self: Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(self.coords - right); 'b); Self::Output::from(self.coords - right); 'b);
add_sub_impl!(Sub, sub, ClosedSub; add_sub_impl!(Sub, sub, SimpleSub;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>; self: Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(self.coords - right); ); Self::Output::from(self.coords - right); );
// Point + Vector // Point + Vector
add_sub_impl!(Add, add, ClosedAdd; add_sub_impl!(Add, add, SimpleAdd;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: &'a Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>; self: &'a Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(&self.coords + right); 'a, 'b); Self::Output::from(&self.coords + right); 'a, 'b);
add_sub_impl!(Add, add, ClosedAdd; add_sub_impl!(Add, add, SimpleAdd;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: &'a Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>; self: &'a Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(&self.coords + &right); 'a); // FIXME: should not be a ref to `right`. Self::Output::from(&self.coords + &right); 'a); // FIXME: should not be a ref to `right`.
add_sub_impl!(Add, add, ClosedAdd; add_sub_impl!(Add, add, SimpleAdd;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>; self: Point<N, D1>, right: &'b Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(self.coords + right); 'b); Self::Output::from(self.coords + right); 'b);
add_sub_impl!(Add, add, ClosedAdd; add_sub_impl!(Add, add, SimpleAdd;
(D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>; (D1, U1), (D2, U1) -> (D1) for D1: DimName, D2: Dim, SB: Storage<N, D2>;
self: Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>; self: Point<N, D1>, right: Vector<N, D2, SB>, Output = Point<N, D1>;
Self::Output::from(self.coords + right); ); Self::Output::from(self.coords + right); );

View File

@ -9,45 +9,46 @@ use crate::base::{DefaultAllocator, Scalar};
use crate::geometry::{Point, Translation}; use crate::geometry::{Point, Translation};
use crate::base::{SimpleAdd, SimpleSub};
// Translation × Translation // Translation × Translation
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>; self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
Translation::from(&self.vector + &right.vector); 'a, 'b); Translation::from(&self.vector + &right.vector); 'a, 'b);
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>; self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
Translation::from(&self.vector + right.vector); 'a); Translation::from(&self.vector + right.vector); 'a);
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>; self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
Translation::from(self.vector + &right.vector); 'b); Translation::from(self.vector + &right.vector); 'b);
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>; self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
Translation::from(self.vector + right.vector); ); Translation::from(self.vector + right.vector); );
// Translation ÷ Translation // Translation ÷ Translation
// FIXME: instead of calling inverse explicitly, could we just add a `mul_tr` or `mul_inv` method? // FIXME: instead of calling inverse explicitly, could we just add a `mul_tr` or `mul_inv` method?
add_sub_impl!(Div, div, ClosedSub; add_sub_impl!(Div, div, SimpleSub;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>; self: &'a Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
Translation::from(&self.vector - &right.vector); 'a, 'b); Translation::from(&self.vector - &right.vector); 'a, 'b);
add_sub_impl!(Div, div, ClosedSub; add_sub_impl!(Div, div, SimpleSub;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>; self: &'a Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
Translation::from(&self.vector - right.vector); 'a); Translation::from(&self.vector - right.vector); 'a);
add_sub_impl!(Div, div, ClosedSub; add_sub_impl!(Div, div, SimpleSub;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>; self: Translation<N, D>, right: &'b Translation<N, D>, Output = Translation<N, D>;
Translation::from(self.vector - &right.vector); 'b); Translation::from(self.vector - &right.vector); 'b);
add_sub_impl!(Div, div, ClosedSub; add_sub_impl!(Div, div, SimpleSub;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>; self: Translation<N, D>, right: Translation<N, D>, Output = Translation<N, D>;
Translation::from(self.vector - right.vector); ); Translation::from(self.vector - right.vector); );
@ -55,22 +56,22 @@ add_sub_impl!(Div, div, ClosedSub;
// Translation × Point // Translation × Point
// FIXME: we don't handle properly non-zero origins here. Do we want this to be the intended // FIXME: we don't handle properly non-zero origins here. Do we want this to be the intended
// behavior? // behavior?
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: &'a Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>; self: &'a Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>;
right + &self.vector; 'a, 'b); right + &self.vector; 'a, 'b);
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: &'a Translation<N, D>, right: Point<N, D>, Output = Point<N, D>; self: &'a Translation<N, D>, right: Point<N, D>, Output = Point<N, D>;
right + &self.vector; 'a); right + &self.vector; 'a);
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>; self: Translation<N, D>, right: &'b Point<N, D>, Output = Point<N, D>;
right + self.vector; 'b); right + self.vector; 'b);
add_sub_impl!(Mul, mul, ClosedAdd; add_sub_impl!(Mul, mul, SimpleAdd;
(D, U1), (D, U1) -> (D) for D: DimName; (D, U1), (D, U1) -> (D) for D: DimName;
self: Translation<N, D>, right: Point<N, D>, Output = Point<N, D>; self: Translation<N, D>, right: Point<N, D>, Output = Point<N, D>;
right + self.vector; ); right + self.vector; );

View File

@ -1107,3 +1107,98 @@ fn partial_eq_different_types() {
// assert_ne!(static_mat, typenum_static_mat); // assert_ne!(static_mat, typenum_static_mat);
//assert_ne!(typenum_static_mat, static_mat); //assert_ne!(typenum_static_mat, static_mat);
} }
#[test]
fn add_without_add_assign() {
// Ensure adding matrices works without implementing AddAssign
#[derive(Clone, Copy, Debug, PartialEq)]
struct Value(f32);
impl std::ops::Add<&Value> for Value {
type Output = Self;
fn add(self, rhs: &Self) -> Self {
Value(self.0 + rhs.0)
}
}
impl std::ops::Add<Value> for Value {
type Output = Self;
fn add(self, rhs: Self) -> Self {
self.add(&rhs)
}
}
impl std::ops::Sub<&Value> for Value {
type Output = Self;
fn sub(self, rhs: &Self) -> Self {
Value(self.0 - rhs.0)
}
}
impl std::ops::Sub<Value> for Value {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self.sub(&rhs)
}
}
let a = Matrix2x3::new(
Value(1.0),
Value(2.0),
Value(3.0),
Value(4.0),
Value(5.0),
Value(6.0),
);
let b = Matrix2x3::new(
Value(10.0),
Value(20.0),
Value(30.0),
Value(40.0),
Value(50.0),
Value(60.0),
);
let c = DMatrix::from_row_slice(2, 3, &[
Value(10.0),
Value(20.0),
Value(30.0),
Value(40.0),
Value(50.0),
Value(60.0),
]);
let expected_add = Matrix2x3::new(
Value(11.0),
Value(22.0),
Value(33.0),
Value(44.0),
Value(55.0),
Value(66.0)
);
let expected_sub = Matrix2x3::new(
Value(-9.0),
Value(-18.0),
Value(-27.0),
Value(-36.0),
Value(-45.0),
Value(-54.0)
);
assert_eq!(expected_add, &a + &b);
assert_eq!(expected_add, &a + b);
assert_eq!(expected_add, a + &b);
assert_eq!(expected_add, a + b);
// Sum of a static matrix with a dynamic one.
assert_eq!(expected_add, &a + &c);
assert_eq!(expected_add, a + &c);
assert_eq!(expected_add, &c + &a);
assert_eq!(expected_add, &c + a);
assert_eq!(expected_sub, &a - &b);
assert_eq!(expected_sub, &a - b);
assert_eq!(expected_sub, a - &b);
assert_eq!(expected_sub, a - b);
// Difference of a static matrix with a dynamic one.
assert_eq!(expected_sub, &a - &c);
assert_eq!(expected_sub, a - &c);
}

View File

@ -93,6 +93,94 @@ fn to_homogeneous() {
assert_eq!(a.to_homogeneous(), expected); assert_eq!(a.to_homogeneous(), expected);
} }
#[test]
fn point_ops_without_assign() {
// Ensure adding matrices works without implementing AddAssign
#[derive(Clone, Copy, Debug, PartialEq)]
struct Value(f32);
impl std::ops::Add<&Value> for Value {
type Output = Self;
fn add(self, rhs: &Self) -> Self {
Value(self.0 + rhs.0)
}
}
impl std::ops::Add<Value> for Value {
type Output = Self;
fn add(self, rhs: Self) -> Self {
self.add(&rhs)
}
}
impl std::ops::Sub<&Value> for Value {
type Output = Self;
fn sub(self, rhs: &Self) -> Self {
Value(self.0 - rhs.0)
}
}
impl std::ops::Sub<Value> for Value {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self.sub(&rhs)
}
}
impl std::ops::Mul<Value> for f32 {
type Output = Value;
fn mul(self, rhs: Value) -> Self::Output {
Value(self * rhs.0)
}
}
impl std::ops::Mul<f32> for Value {
type Output = Value;
fn mul(self, rhs: f32) -> Self::Output {
Value(self.0 * rhs)
}
}
impl Zero for Value {
fn zero() -> Self {
Value(Zero::zero())
}
fn is_zero(&self) -> bool {
self.0.is_zero()
}
}
let a = Point3::new(
Value(1.0),
Value(2.0),
Value(3.0),
);
let b = Point3::new(
Value(1.0),
Value(2.0),
Value(3.0),
);
let c = Vector3::new(
Value(1.0),
Value(2.0),
Value(3.0),
);
assert_eq!(a - b, Vector3::zero());
assert_eq!(&a - &b, Vector3::zero());
assert_eq!(a - &b, Vector3::zero());
assert_eq!(&a - b, Vector3::zero());
assert_eq!(b - c, Point3::origin());
assert_eq!(&b - &c, Point3::origin());
assert_eq!(b - &c, Point3::origin());
assert_eq!(&b - c, Point3::origin());
let a2 = Point3::new(
Value(2.0),
Value(4.0),
Value(6.0),
);
assert_eq!(b + c, a2);
assert_eq!(&b + &c, a2);
assert_eq!(b + &c, a2);
assert_eq!(&b + c, a2);
}
#[cfg(feature = "arbitrary")] #[cfg(feature = "arbitrary")]
quickcheck!( quickcheck!(
fn point_sub(pt1: Point3<f64>, pt2: Point3<f64>) -> bool { fn point_sub(pt1: Point3<f64>, pt2: Point3<f64>) -> bool {