Remove extraneous operator overloads on vectors, and add Elementwise trait

This commit is contained in:
Brendan Zabarauskas 2016-03-26 13:19:06 +11:00
parent bf4637352e
commit 8d10f1dc6d
6 changed files with 67 additions and 82 deletions

View file

@ -57,3 +57,19 @@ pub trait Array where
/// The maximum element of the array. /// The maximum element of the array.
fn max(self) -> Self::Element where Self::Element: PartialOrd; 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<Rhs = Self> {
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);
}

View file

@ -27,8 +27,6 @@
//! `look_at`, `from_angle`, `from_euler`, and `from_axis_angle` methods. //! `look_at`, `from_angle`, `from_euler`, and `from_axis_angle` methods.
//! These are provided for convenience. //! These are provided for convenience.
#![cfg_attr(feature = "unstable", feature(augmented_assignments, op_assign_traits))]
extern crate num as rust_num; extern crate num as rust_num;
extern crate rustc_serialize; extern crate rustc_serialize;
extern crate rand; extern crate rand;

View file

@ -17,6 +17,8 @@ use approx::ApproxEq;
use std::cmp; use std::cmp;
use std::fmt; use std::fmt;
#[cfg(feature = "unstable")]
use std::ops::*;
use rust_num::{Float, Num, NumCast}; use rust_num::{Float, Num, NumCast};
@ -60,9 +62,22 @@ partial_ord_float!(f64);
/// Base numeric types with partial ordering /// Base numeric types with partial ordering
pub trait BaseNum: #[cfg(not(feature = "unstable"))]
Copy + NumCast + Clone + Num pub trait BaseNum where
+ PartialOrd + cmp::PartialOrd + fmt::Debug 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,
{} {}

View file

@ -5,6 +5,7 @@
pub use angle::Angle; pub use angle::Angle;
pub use array::Array; pub use array::Array;
pub use array::ElementWise;
pub use matrix::Matrix; pub use matrix::Matrix;
pub use matrix::SquareMatrix; pub use matrix::SquareMatrix;

View file

@ -40,7 +40,7 @@
//! let b: Vector2<f64> = Vector2::new(-3.0, -4.0); //! let b: Vector2<f64> = Vector2::new(-3.0, -4.0);
//! //!
//! assert_eq!(a + b, Vector2::zero()); //! 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 //! // 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: //! // 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 angle::{Angle, Rad};
use approx::ApproxEq; use approx::ApproxEq;
use array::Array; use array::{Array, ElementWise};
use num::{BaseNum, BaseFloat, PartialOrd}; use num::{BaseNum, BaseFloat, PartialOrd};
/// A trait that specifies a range of numeric operations for vectors. Not all /// A trait that specifies a range of numeric operations for vectors.
/// of these make sense from a linear algebra point of view, but are included
/// for pragmatic reasons.
pub trait Vector: Copy + Clone where pub trait Vector: Copy + Clone where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: Array<Element = <Self as Vector>::Scalar>, Self: Array<Element = <Self as Vector>::Scalar>,
Self: Add<Self, Output = Self>, Self: Add<Self, Output = Self>,
Self: Sub<Self, Output = Self>, Self: Sub<Self, Output = Self>,
Self: Mul<Self, Output = Self>,
Self: Div<Self, Output = Self>,
Self: Rem<Self, Output = Self>,
Self: Add<<Self as Vector>::Scalar, Output = Self>,
Self: Sub<<Self as Vector>::Scalar, Output = Self>,
Self: Mul<<Self as Vector>::Scalar, Output = Self>, Self: Mul<<Self as Vector>::Scalar, Output = Self>,
Self: Div<<Self as Vector>::Scalar, Output = Self>, Self: Div<<Self as Vector>::Scalar, Output = Self>,
Self: Rem<<Self as Vector>::Scalar, Output = Self>, Self: Rem<<Self as Vector>::Scalar, Output = Self>,
@ -216,7 +209,7 @@ macro_rules! impl_vector {
#[inline] fn from_value(scalar: S) -> $VectorN<S> { $VectorN { $($field: scalar),+ } } #[inline] fn from_value(scalar: S) -> $VectorN<S> { $VectorN { $($field: scalar),+ } }
#[inline] fn dot(self, other: $VectorN<S>) -> S { (self * other).sum() } #[inline] fn dot(self, other: $VectorN<S>) -> S { $VectorN::mul_element_wise(self, other).sum() }
} }
impl<S: Neg<Output = S>> Neg for $VectorN<S> { impl<S: Neg<Output = S>> Neg for $VectorN<S> {
@ -242,28 +235,16 @@ macro_rules! impl_vector {
} }
} }
impl_operator!(<S: BaseNum> Add<S> for $VectorN<S> {
fn add(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field + scalar),+) }
});
impl_operator!(<S: BaseNum> Add<$VectorN<S> > for $VectorN<S> { impl_operator!(<S: BaseNum> Add<$VectorN<S> > for $VectorN<S> {
fn add(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field + rhs.$field),+) } fn add(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field + rhs.$field),+) }
}); });
impl_assignment_operator!(<S: BaseNum> AddAssign<S> for $VectorN<S> {
fn add_assign(&mut self, scalar) { $(self.$field += scalar);+ }
});
impl_assignment_operator!(<S: BaseNum> AddAssign<$VectorN<S> > for $VectorN<S> { impl_assignment_operator!(<S: BaseNum> AddAssign<$VectorN<S> > for $VectorN<S> {
fn add_assign(&mut self, other) { $(self.$field += other.$field);+ } fn add_assign(&mut self, other) { $(self.$field += other.$field);+ }
}); });
impl_operator!(<S: BaseNum> Sub<S> for $VectorN<S> {
fn sub(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field - scalar),+) }
});
impl_operator!(<S: BaseNum> Sub<$VectorN<S> > for $VectorN<S> { impl_operator!(<S: BaseNum> Sub<$VectorN<S> > for $VectorN<S> {
fn sub(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field - rhs.$field),+) } fn sub(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field - rhs.$field),+) }
}); });
impl_assignment_operator!(<S: BaseNum> SubAssign<S> for $VectorN<S> {
fn sub_assign(&mut self, scalar) { $(self.$field -= scalar);+ }
});
impl_assignment_operator!(<S: BaseNum> SubAssign<$VectorN<S> > for $VectorN<S> { impl_assignment_operator!(<S: BaseNum> SubAssign<$VectorN<S> > for $VectorN<S> {
fn sub_assign(&mut self, other) { $(self.$field -= other.$field);+ } fn sub_assign(&mut self, other) { $(self.$field -= other.$field);+ }
}); });
@ -271,42 +252,51 @@ macro_rules! impl_vector {
impl_operator!(<S: BaseNum> Mul<S> for $VectorN<S> { impl_operator!(<S: BaseNum> Mul<S> for $VectorN<S> {
fn mul(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field * scalar),+) } fn mul(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field * scalar),+) }
}); });
impl_operator!(<S: BaseNum> Mul<$VectorN<S> > for $VectorN<S> {
fn mul(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field * rhs.$field),+) }
});
impl_assignment_operator!(<S: BaseNum> MulAssign<S> for $VectorN<S> { impl_assignment_operator!(<S: BaseNum> MulAssign<S> for $VectorN<S> {
fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ }
}); });
impl_assignment_operator!(<S: BaseNum> MulAssign<$VectorN<S> > for $VectorN<S> {
fn mul_assign(&mut self, other) { $(self.$field *= other.$field);+ }
});
impl_operator!(<S: BaseNum> Div<S> for $VectorN<S> { impl_operator!(<S: BaseNum> Div<S> for $VectorN<S> {
fn div(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field / scalar),+) } fn div(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field / scalar),+) }
}); });
impl_operator!(<S: BaseNum> Div<$VectorN<S> > for $VectorN<S> {
fn div(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field / rhs.$field),+) }
});
impl_assignment_operator!(<S: BaseNum> DivAssign<S> for $VectorN<S> { impl_assignment_operator!(<S: BaseNum> DivAssign<S> for $VectorN<S> {
fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ } fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ }
}); });
impl_assignment_operator!(<S: BaseNum> DivAssign<$VectorN<S> > for $VectorN<S> {
fn div_assign(&mut self, other) { $(self.$field /= other.$field);+ }
});
impl_operator!(<S: BaseNum> Rem<S> for $VectorN<S> { impl_operator!(<S: BaseNum> Rem<S> for $VectorN<S> {
fn rem(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field % scalar),+) } fn rem(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field % scalar),+) }
}); });
impl_operator!(<S: BaseNum> Rem<$VectorN<S> > for $VectorN<S> {
fn rem(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field % rhs.$field),+) }
});
impl_assignment_operator!(<S: BaseNum> RemAssign<S> for $VectorN<S> { impl_assignment_operator!(<S: BaseNum> RemAssign<S> for $VectorN<S> {
fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ }
}); });
impl_assignment_operator!(<S: BaseNum> RemAssign<$VectorN<S> > for $VectorN<S> {
fn rem_assign(&mut self, other) { $(self.$field %= other.$field);+ } impl<S: BaseNum> ElementWise for $VectorN<S> {
}); #[inline] fn add_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field + rhs.$field),+) }
#[inline] fn sub_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field - rhs.$field),+) }
#[inline] fn mul_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field * rhs.$field),+) }
#[inline] fn div_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field / rhs.$field),+) }
#[inline] fn rem_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field % rhs.$field),+) }
#[cfg(feature = "unstable")] #[inline] fn add_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field += rhs.$field);+ }
#[cfg(feature = "unstable")] #[inline] fn sub_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field -= rhs.$field);+ }
#[cfg(feature = "unstable")] #[inline] fn mul_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field *= rhs.$field);+ }
#[cfg(feature = "unstable")] #[inline] fn div_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field /= rhs.$field);+ }
#[cfg(feature = "unstable")] #[inline] fn rem_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field %= rhs.$field);+ }
}
impl<S: BaseNum> ElementWise<S> for $VectorN<S> {
#[inline] fn add_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field + rhs),+) }
#[inline] fn sub_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field - rhs),+) }
#[inline] fn mul_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field * rhs),+) }
#[inline] fn div_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field / rhs),+) }
#[inline] fn rem_element_wise(self, rhs: S) -> $VectorN<S> { $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<usize> { $($field),+ }); impl_scalar_ops!($VectorN<usize> { $($field),+ });
impl_scalar_ops!($VectorN<u8> { $($field),+ }); impl_scalar_ops!($VectorN<u8> { $($field),+ });
@ -331,12 +321,6 @@ macro_rules! impl_vector {
macro_rules! impl_scalar_ops { macro_rules! impl_scalar_ops {
($VectorN:ident<$S:ident> { $($field:ident),+ }) => { ($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 { impl_operator!(Mul<$VectorN<$S>> for $S {
fn mul(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar * vector.$field),+) } fn mul(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar * vector.$field),+) }
}); });

View file

@ -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); 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); 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 { macro_rules! impl_test_mul {
($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( ($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 // vector * scalar ops
assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+)); assert_eq!($v * $s, $VectorN::new($($v.$field * $s),+));
assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+)); assert_eq!($s * $v, $VectorN::new($($s * $v.$field),+));
@ -86,11 +67,6 @@ macro_rules! impl_test_mul {
macro_rules! impl_test_div { macro_rules! impl_test_div {
($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( ($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 // vector / scalar ops
assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+)); assert_eq!($v / $s, $VectorN::new($($v.$field / $s),+));
assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+)); assert_eq!($s / $v, $VectorN::new($($s / $v.$field),+));
@ -101,11 +77,6 @@ macro_rules! impl_test_div {
macro_rules! impl_test_rem { macro_rules! impl_test_rem {
($VectorN:ident { $($field:ident),+ }, $s:expr, $v:expr) => ( ($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 // vector % scalar ops
assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+)); assert_eq!($v % $s, $VectorN::new($($v.$field % $s),+));
assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+)); assert_eq!($s % $v, $VectorN::new($($s % $v.$field),+));