use super::Complex;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
impl<T: Add<Output = T>> Add for Complex<T> {
type Output = Self;
#[inline(always)]
fn add(self, other: Self) -> Self {
Self {
re: self.re + other.re,
im: self.im + other.im,
}
}
}
impl<T: Sub<Output = T>> Sub for Complex<T> {
type Output = Self;
#[inline(always)]
fn sub(self, other: Self) -> Self {
Self {
re: self.re - other.re,
im: self.im - other.im,
}
}
}
impl<T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Clone> Mul for Complex<T> {
type Output = Self;
#[inline(always)]
fn mul(self, other: Self) -> Self {
Self {
re: self.re.clone() * other.re.clone() - self.im.clone() * other.im.clone(),
im: self.re * other.im + self.im * other.re,
}
}
}
impl<T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + Clone> Div
for Complex<T>
{
type Output = Self;
#[inline(always)]
fn div(self, other: Self) -> Self {
let denom = other.re.clone() * other.re.clone() + other.im.clone() * other.im.clone();
Self {
re: (self.re.clone() * other.re.clone() + self.im.clone() * other.im.clone())
/ denom.clone(),
im: (self.im * other.re.clone() - self.re * other.im) / denom,
}
}
}
impl<T: Neg<Output = T>> Neg for Complex<T> {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self {
Self {
re: -self.re,
im: -self.im,
}
}
}
impl<T: Mul<Output = T> + Clone> Mul<T> for Complex<T> {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: T) -> Self {
Self {
re: self.re * rhs.clone(),
im: self.im * rhs,
}
}
}
impl<T: Div<Output = T> + Clone> Div<T> for Complex<T> {
type Output = Self;
#[inline(always)]
fn div(self, rhs: T) -> Self {
Self {
re: self.re / rhs.clone(),
im: self.im / rhs,
}
}
}
impl<T: Add<Output = T>> Add<T> for Complex<T> {
type Output = Self;
#[inline(always)]
fn add(self, rhs: T) -> Self {
Self {
re: self.re + rhs,
im: self.im,
}
}
}
impl<T: Sub<Output = T>> Sub<T> for Complex<T> {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: T) -> Self {
Self {
re: self.re - rhs,
im: self.im,
}
}
}
impl<T: Mul<Output = T> + Clone + Copy> MulAssign<T> for Complex<T> {
#[inline(always)]
fn mul_assign(&mut self, rhs: T) {
*self = *self * rhs;
}
}
impl<T: Div<Output = T> + Clone + Copy> DivAssign<T> for Complex<T> {
#[inline(always)]
fn div_assign(&mut self, rhs: T) {
*self = *self / rhs;
}
}
impl<T: Add<Output = T> + Copy> AddAssign<T> for Complex<T> {
#[inline(always)]
fn add_assign(&mut self, rhs: T) {
*self = *self + rhs;
}
}
impl<T: Sub<Output = T> + Copy> SubAssign<T> for Complex<T> {
#[inline(always)]
fn sub_assign(&mut self, rhs: T) {
*self = *self - rhs;
}
}
impl<T> AddAssign for Complex<T>
where
Complex<T>: Add<Output = Complex<T>> + Copy,
{
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<T> SubAssign for Complex<T>
where
Complex<T>: Sub<Output = Complex<T>> + Copy,
{
#[inline(always)]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<T> MulAssign for Complex<T>
where
Complex<T>: Mul<Output = Complex<T>> + Copy,
{
#[inline(always)]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<T> DivAssign for Complex<T>
where
Complex<T>: Div<Output = Complex<T>> + Copy,
{
#[inline(always)]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
macro_rules! forward_ref_binop {
($imp:ident, $method:ident) => {
impl<T> $imp<&Complex<T>> for &Complex<T>
where
Complex<T>: $imp<Output = Complex<T>> + Clone,
{
type Output = Complex<T>;
#[inline(always)]
fn $method(self, other: &Complex<T>) -> Complex<T> {
(*self).clone().$method((*other).clone())
}
}
impl<T> $imp<&Complex<T>> for Complex<T>
where
Complex<T>: $imp<Output = Complex<T>> + Clone,
{
type Output = Complex<T>;
#[inline(always)]
fn $method(self, other: &Complex<T>) -> Complex<T> {
self.$method((*other).clone())
}
}
impl<T> $imp<Complex<T>> for &Complex<T>
where
Complex<T>: $imp<Output = Complex<T>> + Clone,
{
type Output = Complex<T>;
#[inline(always)]
fn $method(self, other: Complex<T>) -> Complex<T> {
(*self).clone().$method(other)
}
}
};
}
forward_ref_binop!(Add, add);
forward_ref_binop!(Sub, sub);
forward_ref_binop!(Mul, mul);
forward_ref_binop!(Div, div);
impl<T: Rem<Output = T>> Rem for Complex<T> {
type Output = Self;
#[inline(always)]
fn rem(self, other: Self) -> Self {
Self {
re: self.re % other.re,
im: self.im % other.im,
}
}
}
impl<T: PartialEq> PartialOrd for Complex<T> {
#[inline(always)]
fn partial_cmp(&self, _other: &Self) -> Option<core::cmp::Ordering> {
None
}
}
macro_rules! scalar_left_ops {
($scalar:ty) => {
impl Add<Complex<$scalar>> for $scalar {
type Output = Complex<$scalar>;
#[inline(always)]
fn add(self, rhs: Complex<$scalar>) -> Self::Output {
Complex::new(self + rhs.re, rhs.im)
}
}
impl Sub<Complex<$scalar>> for $scalar {
type Output = Complex<$scalar>;
#[inline(always)]
fn sub(self, rhs: Complex<$scalar>) -> Self::Output {
Complex::new(self - rhs.re, -rhs.im)
}
}
impl Mul<Complex<$scalar>> for $scalar {
type Output = Complex<$scalar>;
#[inline(always)]
fn mul(self, rhs: Complex<$scalar>) -> Self::Output {
Complex::new(self * rhs.re, self * rhs.im)
}
}
impl Div<Complex<$scalar>> for $scalar {
type Output = Complex<$scalar>;
#[inline(always)]
fn div(self, rhs: Complex<$scalar>) -> Self::Output {
Complex::new(self, 0.0) / rhs
}
}
};
}
scalar_left_ops!(f32);
scalar_left_ops!(f64);
#[cfg(test)]
#[allow(clippy::op_ref)]
mod tests {
use super::Complex;
#[test]
fn scalar_real_ops_and_compound_assign() {
assert_eq!(Complex::new(2.0_f64, 3.0) + 5.0, Complex::new(7.0, 3.0));
assert_eq!(Complex::new(2.0_f64, 3.0) - 1.0, Complex::new(1.0, 3.0));
let mut z = Complex::new(2.0_f64, -4.0);
z *= 3.0;
assert_eq!(z, Complex::new(6.0, -12.0));
z /= 2.0;
assert_eq!(z, Complex::new(3.0, -6.0));
z += 1.0;
assert_eq!(z, Complex::new(4.0, -6.0));
z -= 4.0;
assert_eq!(z, Complex::new(0.0, -6.0));
}
#[test]
fn scalar_and_assign_operators() {
let z = Complex::new(2.0_f64, -4.0);
assert_eq!(z * 3.0, Complex::new(6.0, -12.0));
assert_eq!(z / 2.0, Complex::new(1.0, -2.0));
let mut a = Complex::new(1.0_f64, 1.0);
a += Complex::new(2.0, 3.0);
assert_eq!(a, Complex::new(3.0, 4.0));
a -= Complex::new(1.0, 1.0);
assert_eq!(a, Complex::new(2.0, 3.0));
a *= Complex::new(0.0, 1.0); assert_eq!(a, Complex::new(-3.0, 2.0));
a /= Complex::new(0.0, 1.0); assert_eq!(a, Complex::new(2.0, 3.0));
}
#[test]
fn reference_operators_match_by_value() {
let a = Complex::new(2.0_f64, 3.0);
let b = Complex::new(-1.0_f64, 4.0);
assert_eq!(&a + &b, a + b);
assert_eq!(&a - &b, a - b);
assert_eq!(&a * &b, a * b);
assert_eq!(&a / &b, a / b);
assert_eq!(a + &b, a + b);
assert_eq!(&a * b, a * b);
}
#[test]
fn scalar_left_ops_match_right_ops_by_commutativity() {
let z = Complex::new(2.0_f64, -3.0);
let s = 4.0_f64;
assert_eq!(s + z, z + s);
assert_eq!(s * z, z * s);
assert_eq!(s - z, Complex::new(s, 0.0) - z);
assert_eq!(s / z, Complex::new(s, 0.0) / z);
let zf = Complex::new(1.5_f32, 2.5);
let sf = 3.0_f32;
assert_eq!(sf + zf, zf + sf);
assert_eq!(sf * zf, zf * sf);
assert_eq!(sf - zf, Complex::new(sf, 0.0) - zf);
assert_eq!(sf / zf, Complex::new(sf, 0.0) / zf);
}
}