use std::marker::PhantomData;
use num_complex::Complex as NumComplex;
use crate::domain::Domain;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Complex<D: Domain> {
inner: NumComplex<D::Element>,
}
impl<D: Domain> Complex<D> {
pub fn new(real: D::Element, imag: D::Element) -> Self {
Self {
inner: NumComplex::new(real, imag),
}
}
pub fn re(&self) -> &D::Element {
&self.inner.re
}
pub fn im(&self) -> &D::Element {
&self.inner.im
}
pub fn inner(&self) -> &NumComplex<D::Element> {
&self.inner
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ComplexDomain<D: Domain> {
base: D,
_marker: PhantomData<D>,
}
impl<D: Domain> ComplexDomain<D> {
pub fn new(base: D) -> Self {
Self {
base,
_marker: PhantomData,
}
}
pub fn base(&self) -> &D {
&self.base
}
pub fn real_element(&self, re: D::Element) -> Complex<D> {
Complex::new(re, self.base.zero())
}
pub fn imag_element(&self, im: D::Element) -> Complex<D> {
Complex::new(self.base.zero(), im)
}
}
impl<D: Domain> Domain for ComplexDomain<D> {
type Element = Complex<D>;
fn zero(&self) -> Self::Element {
Complex::new(self.base.zero(), self.base.zero())
}
fn one(&self) -> Self::Element {
Complex::new(self.base.one(), self.base.zero())
}
fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
Complex::new(self.base.add(a.re(), b.re()), self.base.add(a.im(), b.im()))
}
fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
Complex::new(self.base.sub(a.re(), b.re()), self.base.sub(a.im(), b.im()))
}
fn neg(&self, a: &Self::Element) -> Self::Element {
Complex::new(self.base.neg(a.re()), self.base.neg(a.im()))
}
fn mul(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
let ac = self.base.mul(a.re(), b.re());
let bd = self.base.mul(a.im(), b.im());
let ad = self.base.mul(a.re(), b.im());
let bc = self.base.mul(a.im(), b.re());
Complex::new(self.base.sub(&ac, &bd), self.base.add(&ad, &bc))
}
fn div(&self, a: &Self::Element, b: &Self::Element) -> Option<Self::Element> {
let c2 = self.base.mul(b.re(), b.re());
let d2 = self.base.mul(b.im(), b.im());
let denom = self.base.add(&c2, &d2);
let ac = self.base.mul(a.re(), b.re());
let bd = self.base.mul(a.im(), b.im());
let bc = self.base.mul(a.im(), b.re());
let ad = self.base.mul(a.re(), b.im());
let real_num = self.base.add(&ac, &bd);
let imag_num = self.base.sub(&bc, &ad);
let real = self.base.div(&real_num, &denom)?;
let imag = self.base.div(&imag_num, &denom)?;
Some(Complex::new(real, imag))
}
fn inv(&self, a: &Self::Element) -> Option<Self::Element> {
self.div(&self.one(), a)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{IntegerDomain, Rational, RationalDomain};
#[test]
fn complex_addition() {
let base = IntegerDomain;
let domain = ComplexDomain::new(base);
let a = Complex::new(1.into(), 2.into());
let b = Complex::new(3.into(), 4.into());
let sum = domain.add(&a, &b);
assert_eq!(sum.re(), &4.into());
assert_eq!(sum.im(), &6.into());
}
#[test]
fn complex_multiplication() {
let base = IntegerDomain;
let domain = ComplexDomain::new(base);
let a = Complex::new(1.into(), 2.into());
let b = Complex::new(3.into(), 4.into());
let prod = domain.mul(&a, &b);
assert_eq!(prod.re(), &(-5).into());
assert_eq!(prod.im(), &10.into());
}
#[test]
fn complex_i_squared_is_minus_one() {
let base = IntegerDomain;
let domain = ComplexDomain::new(base);
let i = domain.imag_element(1.into());
let i2 = domain.mul(&i, &i);
assert_eq!(i2.re(), &(-1).into());
assert_eq!(i2.im(), &0.into());
}
#[test]
fn complex_inverse_over_rationals() {
let base = RationalDomain;
let domain = ComplexDomain::new(base);
let z = Complex::new(Rational::new(1, 1), Rational::new(1, 1));
let inv = domain.inv(&z).expect("non-zero complex is invertible");
let prod = domain.mul(&z, &inv);
assert_eq!(prod, domain.one());
}
}