use std::ops::*;
use crate::traits::{ComplexSqrt, Zero};
use crate::vec::{EucVecf2, EucVecd2};
macro_rules! declare {
($($name:ident, $og:ident, $ogbig:ident, $ty:ident, $($tag:ident)?),+) => {
$(
#[derive(Default, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct $name (pub(crate) $og);
impl_arith!($name, $ty);
impl_assign!(
$name,
AddAssign, add_assign, add,
SubAssign, sub_assign, sub,
MulAssign, mul_assign, mul,
DivAssign, div_assign, div
);
impl_assign!(
1, $name, $ty,
AddAssign, add_assign, add,
SubAssign, sub_assign, sub,
MulAssign, mul_assign, mul,
DivAssign, div_assign, div
);
impl $name {
#[inline(always)]
pub fn new (re: $ty, im: $ty) -> Self {
Self($og::new([re, im]))
}
#[inline(always)]
pub fn from_re (re: $ty) -> Self {
Self($og::new([re, 0.]))
}
#[inline(always)]
pub fn from_im (im: $ty) -> Self {
Self($og::new([0., im]))
}
#[inline(always)]
pub fn unzip (self) -> ($ty, $ty) {
(self.re(), self.im())
}
#[inline(always)]
pub fn re (&self) -> $ty {
self.0.x()
}
#[inline(always)]
pub fn im (&self) -> $ty {
self.0.y()
}
#[inline(always)]
pub fn conj (self) -> Self {
Self::new(self.re(), -self.im())
}
#[inline(always)]
pub fn radius (self) -> $ty {
self.re().hypot(self.im())
}
#[inline(always)]
pub fn angle (self) -> $ty {
self.im().atan2(self.re())
}
#[inline(always)]
pub fn polar (self) -> ($ty, $ty) {
(self.radius(), self.angle())
}
#[inline(always)]
pub fn inv (self) -> Self {
Self(self.conj().0 / self.0.dot(self.0))
}
#[inline(always)]
pub fn sqrt (self) -> Self {
if self.im().is_zero() {
return self.re().sqrtc()
}
let alpha = self.radius();
let res = (($og::new([self.re(), -self.re()]) + alpha) / 2.).sqrt();
Self::new(res.x(), self.im().signum() * res.y())
}
#[inline(always)]
pub fn exp (self) -> Self {
self.re().exp() * Self::expi(self.im())
}
#[inline(always)]
pub fn ln (self) -> Self {
let (radius, angle) = self.polar();
Self::new(radius.ln(), angle)
}
#[inline(always)]
pub fn powi (self, exp: i32) -> Self {
let (radius, angle) = self.polar();
radius.powi(exp) * Self::expi(angle * (exp as $ty))
}
#[inline(always)]
pub fn powf (self, exp: $ty) -> Self {
let (radius, angle) = self.polar();
radius.powf(exp) * Self::expi(angle * exp)
}
#[inline(always)]
pub fn powc (self, exp: Self) -> Self {
let (r, theta) = self.polar();
let (x, y) = exp.unzip();
let v1 = $og::new(exp.into());
let v2 = $og::new([theta, r.ln()]);
r.powf(x) * Self::expi(v1.dot(v2)) / (y * theta).exp()
}
#[inline(always)]
pub fn sin (self) -> Self {
let sin_cos = self.re().sin_cos();
let alpha = $og::new([sin_cos.0, sin_cos.1]);
let beta = $og::new([self.im().cosh(), self.im().sinh()]);
Self(alpha * beta)
}
#[inline(always)]
pub fn cos (self) -> Self {
let sin_cos = self.re().sin_cos();
let alpha = $og::new([sin_cos.1, -sin_cos.0]);
let beta = $og::new([self.im().cosh(), self.im().sinh()]);
Self(alpha * beta)
}
#[inline(always)]
pub fn tan (self) -> Self {
let (re, im) = (2. * self).unzip();
let div = re.cos() + im.cosh();
Self::new(re.sin(), im.sinh()) / div
}
#[inline(always)]
pub fn sqrtc (x: $ty) -> Self {
if (x >= 0.) { return Self::from_re(x.sqrt()) }
Self::from_im((-x).sqrt())
}
#[inline(always)]
pub fn expi (x: $ty) -> Self {
let sin_cos = x.sin_cos();
Self::new(sin_cos.1, sin_cos.0)
}
#[inline(always)]
pub fn powci (x: $ty, rhs: $ty) -> Self {
Self::expi(rhs * x.ln())
}
}
impl Neg for $name {
type Output = Self;
#[inline(always)]
fn neg (self) -> Self::Output {
Self(-self.0)
}
}
impl Div for $name {
type Output = Self;
#[inline(always)]
fn div (self, rhs: Self) -> Self::Output {
self * rhs.inv()
}
}
impl Into<[$ty;2]> for $name {
#[inline(always)]
fn into (self) -> [$ty;2] {
self.0.into()
}
}
impl Into<$og> for $name {
#[inline(always)]
fn into (self) -> $og {
self.0
}
}
)*
};
}
macro_rules! impl_arith {
($($target:ident, $ty:ident),+) => {
$(
impl_arith!(
$target, $ty,
Add, add, +,
Sub, sub, -
);
impl_arith_scal!(
$target, $ty,
Mul, mul, *,
Div, div, /
);
)*
};
($target:ident, $ty:ident, $($trait:ident, $fun:ident, $sy:tt),+) => {
$(
impl $trait for $target {
type Output = Self;
#[inline(always)]
fn $fun (self, rhs: Self) -> Self::Output {
Self(self.0 $sy rhs.0)
}
}
impl_arith_scal!($target, $ty, $trait, $fun, $sy);
)*
};
}
macro_rules! impl_arith_scal {
($target:ident, $ty:ident, $($trait:ident, $fun:ident, $sy:tt),+) => {
$(
impl $trait<$ty> for $target {
type Output = Self;
#[inline(always)]
fn $fun (self, rhs: $ty) -> Self::Output {
Self(self.0 $sy rhs)
}
}
impl $trait<$target> for $ty {
type Output = $target;
#[inline(always)]
fn $fun (self, rhs: $target) -> Self::Output {
$target(self $sy rhs.0)
}
}
)*
};
}
declare!(
Complxf, EucVecf2, Matf2, f32, ,
Complxd, EucVecd2, Matd2, f64, q
);
impl Into<Complxd> for Complxf {
#[inline(always)]
fn into(self) -> Complxd {
Complxd(Into::<EucVecd2>::into(self.0))
}
}
impl Into<Complxf> for Complxd {
#[inline(always)]
fn into(self) -> Complxf {
Complxf(self.0.into())
}
}