From 8d10f1dc6daaa2ac5050b79942c7d35949889a14 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sat, 26 Mar 2016 13:19:06 +1100 Subject: [PATCH] Remove extraneous operator overloads on vectors, and add Elementwise trait --- src/array.rs | 16 ++++++++++ src/lib.rs | 2 -- src/num.rs | 21 +++++++++++-- src/prelude.rs | 1 + src/vector.rs | 80 ++++++++++++++++++++----------------------------- tests/vector.rs | 29 ------------------ 6 files changed, 67 insertions(+), 82 deletions(-) diff --git a/src/array.rs b/src/array.rs index 3536371..f71c37f 100644 --- a/src/array.rs +++ b/src/array.rs @@ -57,3 +57,19 @@ pub trait Array where /// The maximum element of the array. fn max(self) -> Self::Element where Self::Element: PartialOrd; } + +/// Element-wise arithmetic operations. These are supplied for pragmatic +/// reasons, but will usually fall outside of traditional algebraic properties. +pub trait ElementWise { + fn add_element_wise(self, rhs: Rhs) -> Self; + fn sub_element_wise(self, rhs: Rhs) -> Self; + fn mul_element_wise(self, rhs: Rhs) -> Self; + fn div_element_wise(self, rhs: Rhs) -> Self; + fn rem_element_wise(self, rhs: Rhs) -> Self; + + #[cfg(feature = "unstable")] fn add_assign_element_wise(&mut self, rhs: Rhs); + #[cfg(feature = "unstable")] fn sub_assign_element_wise(&mut self, rhs: Rhs); + #[cfg(feature = "unstable")] fn mul_assign_element_wise(&mut self, rhs: Rhs); + #[cfg(feature = "unstable")] fn div_assign_element_wise(&mut self, rhs: Rhs); + #[cfg(feature = "unstable")] fn rem_assign_element_wise(&mut self, rhs: Rhs); +} diff --git a/src/lib.rs b/src/lib.rs index de3cb52..cedfe42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,8 +27,6 @@ //! `look_at`, `from_angle`, `from_euler`, and `from_axis_angle` methods. //! These are provided for convenience. -#![cfg_attr(feature = "unstable", feature(augmented_assignments, op_assign_traits))] - extern crate num as rust_num; extern crate rustc_serialize; extern crate rand; diff --git a/src/num.rs b/src/num.rs index f6e74d0..c29a07e 100644 --- a/src/num.rs +++ b/src/num.rs @@ -17,6 +17,8 @@ use approx::ApproxEq; use std::cmp; use std::fmt; +#[cfg(feature = "unstable")] +use std::ops::*; use rust_num::{Float, Num, NumCast}; @@ -60,9 +62,22 @@ partial_ord_float!(f64); /// Base numeric types with partial ordering -pub trait BaseNum: - Copy + NumCast + Clone + Num - + PartialOrd + cmp::PartialOrd + fmt::Debug +#[cfg(not(feature = "unstable"))] +pub trait BaseNum where + Self: Copy + Clone + fmt::Debug, + Self: Num + NumCast, + Self: PartialOrd + cmp::PartialOrd, +{} + + +/// Base numeric types with partial ordering +#[cfg(feature = "unstable")] +pub trait BaseNum where + Self: Copy + Clone + fmt::Debug, + Self: Num + NumCast, + Self: PartialOrd + cmp::PartialOrd, + Self: AddAssign + SubAssign, + Self: MulAssign + DivAssign + RemAssign, {} diff --git a/src/prelude.rs b/src/prelude.rs index a7ad11c..edbfc0f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -5,6 +5,7 @@ pub use angle::Angle; pub use array::Array; +pub use array::ElementWise; pub use matrix::Matrix; pub use matrix::SquareMatrix; diff --git a/src/vector.rs b/src/vector.rs index b7b9ed8..956c185 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -40,7 +40,7 @@ //! let b: Vector2 = Vector2::new(-3.0, -4.0); //! //! assert_eq!(a + b, Vector2::zero()); -//! assert_eq!(-(a * b), Vector2::new(9.0f64, 16.0f64)); +//! assert_eq!(-(a * 2.0), Vector2::new(-6.0, -8.0)); //! //! // As with Rust's `int` and `f32` types, Vectors of different types cannot //! // be added and so on with impunity. The following will fail to compile: @@ -97,24 +97,17 @@ use rust_num::{NumCast, Zero, One}; use angle::{Angle, Rad}; use approx::ApproxEq; -use array::Array; +use array::{Array, ElementWise}; use num::{BaseNum, BaseFloat, PartialOrd}; -/// A trait that specifies a range of numeric operations for vectors. Not all -/// of these make sense from a linear algebra point of view, but are included -/// for pragmatic reasons. +/// A trait that specifies a range of numeric operations for vectors. pub trait Vector: Copy + Clone where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: Array::Scalar>, Self: Add, Self: Sub, - Self: Mul, - Self: Div, - Self: Rem, - Self: Add<::Scalar, Output = Self>, - Self: Sub<::Scalar, Output = Self>, Self: Mul<::Scalar, Output = Self>, Self: Div<::Scalar, Output = Self>, Self: Rem<::Scalar, Output = Self>, @@ -216,7 +209,7 @@ macro_rules! impl_vector { #[inline] fn from_value(scalar: S) -> $VectorN { $VectorN { $($field: scalar),+ } } - #[inline] fn dot(self, other: $VectorN) -> S { (self * other).sum() } + #[inline] fn dot(self, other: $VectorN) -> S { $VectorN::mul_element_wise(self, other).sum() } } impl> Neg for $VectorN { @@ -242,28 +235,16 @@ macro_rules! impl_vector { } } - impl_operator!( Add for $VectorN { - fn add(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field + scalar),+) } - }); impl_operator!( Add<$VectorN > for $VectorN { fn add(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field + rhs.$field),+) } }); - impl_assignment_operator!( AddAssign for $VectorN { - fn add_assign(&mut self, scalar) { $(self.$field += scalar);+ } - }); impl_assignment_operator!( AddAssign<$VectorN > for $VectorN { fn add_assign(&mut self, other) { $(self.$field += other.$field);+ } }); - impl_operator!( Sub for $VectorN { - fn sub(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field - scalar),+) } - }); impl_operator!( Sub<$VectorN > for $VectorN { fn sub(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field - rhs.$field),+) } }); - impl_assignment_operator!( SubAssign for $VectorN { - fn sub_assign(&mut self, scalar) { $(self.$field -= scalar);+ } - }); impl_assignment_operator!( SubAssign<$VectorN > for $VectorN { fn sub_assign(&mut self, other) { $(self.$field -= other.$field);+ } }); @@ -271,42 +252,51 @@ macro_rules! impl_vector { impl_operator!( Mul for $VectorN { fn mul(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field * scalar),+) } }); - impl_operator!( Mul<$VectorN > for $VectorN { - fn mul(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field * rhs.$field),+) } - }); - impl_assignment_operator!( MulAssign for $VectorN { fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } }); - impl_assignment_operator!( MulAssign<$VectorN > for $VectorN { - fn mul_assign(&mut self, other) { $(self.$field *= other.$field);+ } - }); impl_operator!( Div for $VectorN { fn div(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field / scalar),+) } }); - impl_operator!( Div<$VectorN > for $VectorN { - fn div(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field / rhs.$field),+) } - }); impl_assignment_operator!( DivAssign for $VectorN { fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ } }); - impl_assignment_operator!( DivAssign<$VectorN > for $VectorN { - fn div_assign(&mut self, other) { $(self.$field /= other.$field);+ } - }); impl_operator!( Rem for $VectorN { fn rem(vector, scalar) -> $VectorN { $VectorN::new($(vector.$field % scalar),+) } }); - impl_operator!( Rem<$VectorN > for $VectorN { - fn rem(lhs, rhs) -> $VectorN { $VectorN::new($(lhs.$field % rhs.$field),+) } - }); impl_assignment_operator!( RemAssign for $VectorN { fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } }); - impl_assignment_operator!( RemAssign<$VectorN > for $VectorN { - fn rem_assign(&mut self, other) { $(self.$field %= other.$field);+ } - }); + + impl ElementWise for $VectorN { + #[inline] fn add_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field + rhs.$field),+) } + #[inline] fn sub_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field - rhs.$field),+) } + #[inline] fn mul_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field * rhs.$field),+) } + #[inline] fn div_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field / rhs.$field),+) } + #[inline] fn rem_element_wise(self, rhs: $VectorN) -> $VectorN { $VectorN::new($(self.$field % rhs.$field),+) } + + #[cfg(feature = "unstable")] #[inline] fn add_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field += rhs.$field);+ } + #[cfg(feature = "unstable")] #[inline] fn sub_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field -= rhs.$field);+ } + #[cfg(feature = "unstable")] #[inline] fn mul_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field *= rhs.$field);+ } + #[cfg(feature = "unstable")] #[inline] fn div_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field /= rhs.$field);+ } + #[cfg(feature = "unstable")] #[inline] fn rem_assign_element_wise(&mut self, rhs: $VectorN) { $(self.$field %= rhs.$field);+ } + } + + impl ElementWise for $VectorN { + #[inline] fn add_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field + rhs),+) } + #[inline] fn sub_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field - rhs),+) } + #[inline] fn mul_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field * rhs),+) } + #[inline] fn div_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field / rhs),+) } + #[inline] fn rem_element_wise(self, rhs: S) -> $VectorN { $VectorN::new($(self.$field % rhs),+) } + + #[cfg(feature = "unstable")] #[inline] fn add_assign_element_wise(&mut self, rhs: S) { $(self.$field += rhs);+ } + #[cfg(feature = "unstable")] #[inline] fn sub_assign_element_wise(&mut self, rhs: S) { $(self.$field -= rhs);+ } + #[cfg(feature = "unstable")] #[inline] fn mul_assign_element_wise(&mut self, rhs: S) { $(self.$field *= rhs);+ } + #[cfg(feature = "unstable")] #[inline] fn div_assign_element_wise(&mut self, rhs: S) { $(self.$field /= rhs);+ } + #[cfg(feature = "unstable")] #[inline] fn rem_assign_element_wise(&mut self, rhs: S) { $(self.$field %= rhs);+ } + } impl_scalar_ops!($VectorN { $($field),+ }); impl_scalar_ops!($VectorN { $($field),+ }); @@ -331,12 +321,6 @@ macro_rules! impl_vector { macro_rules! impl_scalar_ops { ($VectorN:ident<$S:ident> { $($field:ident),+ }) => { - impl_operator!(Add<$VectorN<$S>> for $S { - fn add(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar + vector.$field),+) } - }); - impl_operator!(Sub<$VectorN<$S>> for $S { - fn sub(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar - vector.$field),+) } - }); impl_operator!(Mul<$VectorN<$S>> for $S { fn mul(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar * vector.$field),+) } }); diff --git a/tests/vector.rs b/tests/vector.rs index b076f8b..7a41889 100644 --- a/tests/vector.rs +++ b/tests/vector.rs @@ -40,13 +40,6 @@ macro_rules! impl_test_add { assert_eq!(&$v + &$v, $v + $v); assert_eq!(&$v + $v, $v + $v); assert_eq!($v + &$v, $v + $v); - // vector + scalar ops - assert_eq!($v + $s, $VectorN::new($($v.$field + $s),+)); - assert_eq!($s + $v, $VectorN::new($($s + $v.$field),+)); - assert_eq!(&$v + $s, $v + $s); - assert_eq!($s + &$v, $s + $v); - // commutativity - assert_eq!($v + $s, $s + $v); ) } @@ -57,23 +50,11 @@ macro_rules! impl_test_sub { assert_eq!(&$v - &$v, $v - $v); assert_eq!(&$v - $v, $v - $v); assert_eq!($v - &$v, $v - $v); - // vector - scalar ops - assert_eq!($v - $s, $VectorN::new($($v.$field - $s),+)); - assert_eq!($s - $v, $VectorN::new($($s - $v.$field),+)); - assert_eq!(&$v - $s, $v - $s); - assert_eq!($s - &$v, $s - $v); - // commutativity - assert_eq!($v - $s, -($s - $v)); ) } macro_rules! impl_test_mul { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( - // vector * vector ops - assert_eq!($v * $v, $VectorN::new($($v.$field * $v.$field),+)); - assert_eq!(&$v * &$v, $v * $v); - assert_eq!(&$v * $v, $v * $v); - assert_eq!($v * &$v, $v * $v); // vector * scalar ops assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+)); assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+)); @@ -86,11 +67,6 @@ macro_rules! impl_test_mul { macro_rules! impl_test_div { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( - // vector / vector ops - assert_eq!($v / $v, $VectorN::new($($v.$field / $v.$field),+)); - assert_eq!(&$v / &$v, $v / $v); - assert_eq!(&$v / $v, $v / $v); - assert_eq!($v / &$v, $v / $v); // vector / scalar ops assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+)); assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+)); @@ -101,11 +77,6 @@ macro_rules! impl_test_div { macro_rules! impl_test_rem { ($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( - // vector % vector ops - assert_eq!($v % $v, $VectorN::new($($v.$field % $v.$field),+)); - assert_eq!(&$v % &$v, $v % $v); - assert_eq!(&$v % $v, $v % $v); - assert_eq!($v % &$v, $v % $v); // vector % scalar ops assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+)); assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+));