use core::fmt;
use core::ops::{Add, Div, Mul, Neg, Sub};
#[derive(Clone, Copy, PartialEq, Eq, 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) -> Complex<T> {
Complex { re, im }
}
}
impl<T: Default> Complex<T> {
#[inline]
pub fn from_real(re: T) -> Complex<T> {
Complex {
re,
im: T::default(),
}
}
#[inline]
pub fn imaginary(one: T) -> Complex<T> {
Complex {
re: T::default(),
im: one,
}
}
}
impl<T: Default + PartialEq> Complex<T> {
#[inline]
pub fn is_zero(&self) -> bool {
self.re == T::default() && self.im == T::default()
}
#[inline]
pub fn is_real(&self) -> bool {
self.im == T::default()
}
}
impl<T> Complex<T>
where
T: Clone + Neg<Output = T>,
{
#[inline]
pub fn conj(&self) -> Complex<T> {
Complex {
re: self.re.clone(),
im: -self.im.clone(),
}
}
}
impl<T> Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
pub fn add(&self, rhs: &Complex<T>) -> Complex<T> {
Complex {
re: self.re.clone() + rhs.re.clone(),
im: self.im.clone() + rhs.im.clone(),
}
}
pub fn sub(&self, rhs: &Complex<T>) -> Complex<T> {
Complex {
re: self.re.clone() - rhs.re.clone(),
im: self.im.clone() - rhs.im.clone(),
}
}
pub fn mul(&self, rhs: &Complex<T>) -> Complex<T> {
let ac = self.re.clone() * rhs.re.clone();
let bd = self.im.clone() * rhs.im.clone();
let ad = self.re.clone() * rhs.im.clone();
let bc = self.im.clone() * rhs.re.clone();
Complex {
re: ac - bd,
im: ad + bc,
}
}
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 + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Neg<Output = T>,
{
#[inline]
pub fn neg(&self) -> Complex<T> {
Complex {
re: -self.re.clone(),
im: -self.im.clone(),
}
}
}
impl<T> Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{
pub fn div(&self, rhs: &Complex<T>) -> Complex<T> {
let denom = rhs.re.clone() * rhs.re.clone() + rhs.im.clone() * rhs.im.clone();
let re =
(self.re.clone() * rhs.re.clone() + self.im.clone() * rhs.im.clone()) / denom.clone();
let im = (self.im.clone() * rhs.re.clone() - self.re.clone() * rhs.im.clone()) / denom;
Complex { re, im }
}
}
impl<T> fmt::Display for Complex<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} + {}i", self.re, self.im)
}
}
macro_rules! complex_binop {
($tr:ident, $m:ident, $bound:path, $atr:ident, $am:ident) => {
impl<T> core::ops::$tr for Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + $bound,
{
type Output = Complex<T>;
#[inline]
fn $m(self, rhs: Complex<T>) -> Complex<T> {
Complex::$m(&self, &rhs)
}
}
impl<T> core::ops::$tr<&Complex<T>> for &Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + $bound,
{
type Output = Complex<T>;
#[inline]
fn $m(self, rhs: &Complex<T>) -> Complex<T> {
Complex::$m(self, rhs)
}
}
impl<T> core::ops::$atr for Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + $bound,
{
#[inline]
fn $am(&mut self, rhs: Complex<T>) {
*self = Complex::$m(self, &rhs);
}
}
};
}
complex_binop!(Add, add, Mul<Output = T>, AddAssign, add_assign);
complex_binop!(Sub, sub, Mul<Output = T>, SubAssign, sub_assign);
complex_binop!(Mul, mul, Mul<Output = T>, MulAssign, mul_assign);
complex_binop!(Div, div, Div<Output = T>, DivAssign, div_assign);
impl<T> core::ops::Neg for Complex<T>
where
T: Clone + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Neg<Output = T>,
{
type Output = Complex<T>;
#[inline]
fn neg(self) -> Complex<T> {
Complex::neg(&self)
}
}
#[cfg(feature = "float")]
impl Complex<crate::float::Float> {
fn working_precision(&self) -> u64 {
self.re.precision().max(self.im.precision())
}
pub fn abs(&self) -> crate::float::Float {
use crate::float::{Float, RoundingMode::Nearest};
let p = self.working_precision();
let g = p + 16;
let re2 = Float::mul(&self.re, &self.re, g, Nearest);
let im2 = Float::mul(&self.im, &self.im, g, Nearest);
Float::add(&re2, &im2, g, Nearest).sqrt(p, Nearest)
}
fn abs_at(&self, w: u64) -> crate::float::Float {
use crate::float::{Float, RoundingMode::Nearest};
let re2 = Float::mul(&self.re, &self.re, w, Nearest);
let im2 = Float::mul(&self.im, &self.im, w, Nearest);
Float::add(&re2, &im2, w, Nearest).sqrt(w, Nearest)
}
pub fn arg(&self) -> crate::float::Float {
self.im.atan2(
&self.re,
self.working_precision(),
crate::float::RoundingMode::Nearest,
)
}
pub fn exp(&self) -> Complex<crate::float::Float> {
use crate::float::{Float, RoundingMode::Nearest};
let p = self.working_precision();
let er = self.re.exp(p, Nearest);
Complex {
re: Float::mul(&er, &self.im.cos(p, Nearest), p, Nearest),
im: Float::mul(&er, &self.im.sin(p, Nearest), p, Nearest),
}
}
pub fn ln(&self) -> Complex<crate::float::Float> {
use crate::float::RoundingMode::Nearest;
Complex {
re: self.abs().ln(self.working_precision(), Nearest),
im: self.arg(),
}
}
pub fn sqrt(&self) -> Complex<crate::float::Float> {
use crate::float::{Float, RoundingMode::Nearest};
use crate::int::{Int, Sign};
let p = self.working_precision();
let g = p + 16;
let modulus = self.abs_at(g);
let two = Float::from_int(&Int::from_i64(2), g, Nearest);
let re = Float::div(
&Float::add(&modulus, &self.re, g, Nearest),
&two,
g,
Nearest,
)
.sqrt(p, Nearest);
let mut im = Float::div(
&Float::sub(&modulus, &self.re, g, Nearest),
&two,
g,
Nearest,
)
.sqrt(p, Nearest);
if self.im.sign() == Sign::Negative {
im = im.neg();
}
Complex { re, im }
}
pub fn pow(&self, w: &Complex<crate::float::Float>) -> Complex<crate::float::Float> {
w.mul(&self.ln()).exp()
}
pub fn sin(&self) -> Complex<crate::float::Float> {
use crate::float::{Float, RoundingMode::Nearest};
let p = self.working_precision();
Complex {
re: Float::mul(
&self.re.sin(p, Nearest),
&self.im.cosh(p, Nearest),
p,
Nearest,
),
im: Float::mul(
&self.re.cos(p, Nearest),
&self.im.sinh(p, Nearest),
p,
Nearest,
),
}
}
pub fn cos(&self) -> Complex<crate::float::Float> {
use crate::float::{Float, RoundingMode::Nearest};
let p = self.working_precision();
Complex {
re: Float::mul(
&self.re.cos(p, Nearest),
&self.im.cosh(p, Nearest),
p,
Nearest,
),
im: Float::mul(
&self.re.sin(p, Nearest),
&self.im.sinh(p, Nearest),
p,
Nearest,
)
.neg(),
}
}
}