use crate::adaptor::{InputAdaptor, OutputAdaptor};
use crate::Result;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use std::fmt::Debug;
use sys::{mcomplex, mreal};
#[repr(C)]
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Default)]
pub struct Complex<T> {
pub re: T,
pub im: T,
}
impl<T> Complex<T> {
#[inline]
pub const fn new(re: T, im: T) -> Self {
Complex { re, im }
}
}
impl<T> From<T> for Complex<T>
where
T: Default,
{
#[inline]
fn from(re: T) -> Self {
Self::new(re, T::default())
}
}
impl<'a, T> From<&'a T> for Complex<T>
where
T: Clone + Default,
{
#[inline]
fn from(re: &T) -> Self {
From::from(re.clone())
}
}
impl<T> Complex<T>
where
T: Clone + Add<Output = T> + Mul<Output = T>,
{
#[inline]
pub fn norm_sqr(&self) -> T {
self.re.clone() * self.re.clone() + self.im.clone() * self.im.clone()
}
}
impl<T> Complex<T>
where
T: Clone + Neg<Output = T>,
{
#[inline]
pub fn conj(&self) -> Self {
Self::new(self.re.clone(), -self.im.clone())
}
}
impl<T> Neg for Complex<T>
where
T: Neg<Output = T>,
{
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self::Output::new(-self.re, -self.im)
}
}
impl<'a, T> Neg for &'a Complex<T>
where
T: Clone + Neg<Output = T>,
{
type Output = Complex<T>;
#[inline]
fn neg(self) -> Self::Output {
-self.clone()
}
}
impl<T> Add<Complex<T>> for Complex<T>
where
T: Clone + Add<Output = T>,
{
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self::Output::new(self.re + rhs.re, self.im + rhs.im)
}
}
impl<T> Sub<Complex<T>> for Complex<T>
where
T: Clone + Sub<Output = T>,
{
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self::Output::new(self.re - rhs.re, self.im - rhs.im)
}
}
impl<T> Mul<Complex<T>> for Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
let re = self.re.clone() * rhs.re.clone() - self.im.clone() * rhs.im.clone();
let im = self.re * rhs.im + self.im * rhs.re;
Self::Output::new(re, im)
}
}
impl<T> Div<Complex<T>> for Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
let deno = rhs.norm_sqr();
let re = self.re.clone() * rhs.re.clone() + self.im.clone() * rhs.im.clone();
let im = self.im * rhs.re - self.re * rhs.im;
Self::Output::new(re / deno.clone(), im / deno)
}
}
macro_rules! forward_ref_ref_binop {
(impl $tr:ident => $method:ident where $($reqs:ident),+) => {
impl<'a, 'b, T> $tr<&'b Complex<T>> for &'a Complex<T>
where
T: Clone $(+ $reqs<Output = T>)+,
{
type Output = Complex<T>;
#[inline]
fn $method(self, rhs: &'b Complex<T>) -> Self::Output {
self.clone().$method(rhs.clone())
}
}
};
}
macro_rules! forward_val_ref_binop {
(impl $tr:ident => $method:ident where $($reqs:ident),+) => {
impl<'a, T> $tr<Complex<T>> for &'a Complex<T>
where
T: Clone $(+ $reqs<Output = T>)+,
{
type Output = Complex<T>;
#[inline]
fn $method(self, rhs: Complex<T>) -> Self::Output {
self.clone().$method(rhs)
}
}
};
}
macro_rules! forward_ref_val_binop {
(impl $tr:ident => $method:ident where $($reqs:ident),+) => {
impl<'a, T> $tr<&'a Complex<T>> for Complex<T>
where
T: Clone $(+ $reqs<Output = T>)+,
{
type Output = Complex<T>;
#[inline]
fn $method(self, rhs: &'a Complex<T>) -> Self::Output {
self.$method(rhs.clone())
}
}
};
}
macro_rules! forward_all_binop {
(impl $tr:ident => $method:ident where $($reqs:ident),+) => {
forward_ref_ref_binop!(impl $tr => $method where $($reqs),+);
forward_val_ref_binop!(impl $tr => $method where $($reqs),+);
forward_ref_val_binop!(impl $tr => $method where $($reqs),+);
};
}
forward_all_binop!(impl Add => add where Add);
forward_all_binop!(impl Sub => sub where Sub);
forward_all_binop!(impl Mul => mul where Add, Sub, Mul);
forward_all_binop!(impl Div => div where Add, Sub, Mul, Div);
impl<T> AddAssign for Complex<T>
where
T: AddAssign,
{
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.re += rhs.re;
self.im += rhs.im;
}
}
impl<T> SubAssign for Complex<T>
where
T: SubAssign,
{
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.re -= rhs.re;
self.im -= rhs.im;
}
}
impl<T> MulAssign for Complex<T>
where
T: Clone + AddAssign + SubAssign + MulAssign,
{
#[allow(clippy::suspicious_op_assign_impl)]
#[inline]
fn mul_assign(&mut self, rhs: Self) {
let mut imim = self.im.clone();
imim *= rhs.im.clone();
self.re *= rhs.re.clone();
self.re -= imim;
let mut reim = self.re.clone();
reim *= rhs.im;
self.im *= rhs.re;
self.im += reim;
}
}
impl<T> DivAssign for Complex<T>
where
T: Clone + AddAssign + SubAssign + MulAssign + DivAssign,
{
#[allow(clippy::suspicious_op_assign_impl)]
#[inline]
fn div_assign(&mut self, rhs: Self) {
let mut rhs_im2 = rhs.im.clone();
rhs_im2 *= rhs.im.clone();
let mut deno = rhs.re.clone();
deno *= rhs.re.clone();
deno += rhs_im2;
let mut imim = self.im.clone();
imim *= rhs.im.clone();
self.re *= rhs.re.clone();
self.re += imim;
self.re /= deno.clone();
let mut reim = self.re.clone();
reim *= rhs.im;
self.im *= rhs.re;
self.im -= reim;
self.im /= deno;
}
}
macro_rules! impl_complex_real_adaptor {
($($t:ty),+) => {
$(
impl InputAdaptor for Complex<$t> {
type Input = mcomplex;
#[inline]
fn mtype_try_from(input: Self::Input) -> Result<Self> {
Ok(Self::new(input.ri[0] as $t, input.ri[1] as $t))
}
}
impl OutputAdaptor for Complex<$t> {
type Output = mcomplex;
#[inline]
fn try_into_mtype(self) -> Result<Self::Output> {
Ok(Self::Output {
ri: [self.re as mreal, self.im as mreal],
})
}
}
)+
};
}
macro_rules! impl_complex_int_adaptor {
($($t:ty),+) => {
$(
impl OutputAdaptor for Complex<$t> {
type Output = mcomplex;
#[inline]
fn try_into_mtype(self) -> Result<Self::Output> {
Ok(Self::Output {
ri: [self.re as mreal, self.im as mreal],
})
}
}
)+
};
}
impl_complex_real_adaptor!(f32, f64);
impl_complex_int_adaptor!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
#[cfg(test)]
mod tests {
use super::*;
const EPS: f64 = 1e-10;
#[test]
fn convert_int() {
assert_eq!(Complex::from(1), Complex::new(1, 0));
}
#[test]
fn convert_real() {
assert_eq!(Complex::from(1.), Complex::new(1., 0.));
}
#[test]
fn norm_sqr_int() {
assert_eq!(Complex::new(-5, 12).norm_sqr(), 169);
}
#[test]
fn norm_sqr_real() {
assert!((Complex::new(3.0, -4.0).norm_sqr() - 25f64).abs() <= EPS)
}
#[test]
fn conj_int() {
assert_eq!(Complex::new(-2, 1).conj(), Complex::new(-2, -1));
}
#[test]
fn conj_real() {
let a = Complex::new(1.2, -3.1);
let ca = a.conj();
let b = Complex::new(1.2f64, 3.1f64);
assert!((ca.re - b.re).abs() <= EPS && (ca.im - b.im).abs() <= EPS)
}
#[test]
fn add_complex_int() {
let a = Complex::new(2, 1);
let b = Complex::new(3, -7);
let c = Complex::new(5, -6);
assert_eq!(a + b, c);
}
#[test]
fn add_complex_real() {
let a = Complex::new(0.1, -3.7);
let b = Complex::new(2.4, 1.48);
let c = Complex::new(2.5f64, -2.22f64);
let ab = a + b;
assert!((ab.re - c.re).abs() <= EPS && (ab.im - c.im).abs() <= EPS);
}
#[test]
fn sub_complex_int() {
let a = Complex::new(2, 1);
let b = Complex::new(3, -7);
let c = Complex::new(-1, 8);
assert_eq!(a - b, c);
}
#[test]
fn sub_complex_real() {
let a = Complex::new(0.1, -3.7);
let b = Complex::new(2.4, 1.48);
let c = Complex::new(-2.3f64, -5.18f64);
let ab = a - b;
assert!((ab.re - c.re).abs() <= EPS && (ab.im - c.im).abs() <= EPS);
}
#[test]
fn mul_complex_int() {
let a = Complex::new(2, 1);
let b = Complex::new(3, -7);
let c = Complex::new(13, -11);
assert_eq!(a * b, c);
}
#[test]
fn mul_complex_real() {
let a = Complex::new(0.1, -3.7);
let b = Complex::new(2.4, 1.48);
let c = Complex::new(5.716f64, -8.732f64);
let ab = a * b;
assert!((ab.re - c.re).abs() <= EPS && (ab.im - c.im).abs() <= EPS);
}
#[test]
fn div_complex_int() {
let a = Complex::new(2, 1);
let b = Complex::new(3, -7);
let c = Complex::new(0, -3);
assert_eq!(b / a, c);
}
#[test]
fn div_complex_real() {
let a = Complex::new(0.5, -1.2);
let b = Complex::new(4.6, -0.9);
let c = Complex::new(2f64, 3f64);
let ab = b / a;
assert!((ab.re - c.re).abs() <= EPS && (ab.im - c.im).abs() <= EPS);
}
}