cplx 0.3.3

Complex and hypercomplex numbers
#![feature(zero_one)]

#![cfg_attr(test, feature(plugin))]

#![no_std]

#![cfg_attr(test, plugin(quickcheck_macros))]

extern crate typenum;

#[cfg(test)] extern crate quickcheck;
#[cfg(test)] #[macro_use] extern crate std;

use core::cmp::*;
use core::fmt;
use core::marker::PhantomData;
use core::num::*;
use core::ops::*;
use typenum::consts::{ P1, N1 };
use typenum::int::{ Integer, Z0 };

pub trait Sign<A> : Integer { fn sign(A) -> A; }
impl<A: Zero>            Sign<A> for Z0 { fn sign(_: A) -> A { Zero::zero() } }
impl<A>                  Sign<A> for P1 { fn sign(a: A) -> A { a } }
impl<A: Neg<Output = A>> Sign<A> for N1 { fn sign(a: A) -> A { a.neg() } }

/// Cayley-Dickson construction
pub struct Complex<A, S: Sign<A> = N1>(PhantomData<S>, A, A);

impl<S: Sign<A>, A> Complex<A, S> {
    #[inline] pub fn from_rect(re: A, im: A) -> Self { Complex(PhantomData, re, im) }
    #[inline] pub fn to_rect(self) -> (A, A) { let Complex(_, re, im) = self; (re, im) }
}

#[inline] pub fn from_rect<S: Sign<A>, A>(re: A, im: A) -> Complex<A, S> { Complex::<A, S>::from_rect(re, im) }

impl<S: Sign<A>, A: Clone> Clone for Complex<A, S> {
    #[inline] fn clone(&self) -> Self {
        let &Complex(_, ref a, ref b) = self;
        Complex(PhantomData, a.clone(), b.clone())
    }
}

impl<S: Sign<A>, A: Copy> Copy for Complex<A, S> {}

impl<S: Sign<A>, A: PartialEq> PartialEq for Complex<A, S> {
    #[inline] fn eq(&self, &Complex(_, ref c, ref d): &Self) -> bool {
        let &Complex(_, ref a, ref b) = self;
        (a, b) == (c, d)
    }
}

impl<S: Sign<A>, A: Eq> Eq for Complex<A, S> {}

impl<S: Sign<A>, A: fmt::Debug> fmt::Debug for Complex<A, S> {
    #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let &Complex(_, ref a, ref b) = self;
        fmt.write_fmt(format_args!("({:?})+i({:?})", a, b))
    }
}

pub trait Conjugable {
    fn conjugate(self) -> Self;
}

impl<S: Sign<A>, A: Add<Output = A> + Neg<Output = A> + Conjugable> Conjugable for Complex<A, S> {
    #[inline] fn conjugate(self) -> Self {
        let Complex(_, a, b) = self;
        Complex(PhantomData, a.conjugate(), b.neg())
    }
}

macro_rules! impl_Conjugable_id { ($t: ty) => (impl Conjugable for $t { fn conjugate(self) -> Self { self } }) }
impl_Conjugable_id!(());
impl_Conjugable_id!(isize);
impl_Conjugable_id!(i8);
impl_Conjugable_id!(i16);
impl_Conjugable_id!(i32);
impl_Conjugable_id!(i64);
impl_Conjugable_id!(f32);
impl_Conjugable_id!(f64);

impl<S: Sign<A>, A: Zero + Add<Output = A>> Zero for Complex<A, S> {
    #[inline] fn zero() -> Self { Complex(PhantomData, Zero::zero(), Zero::zero()) }
}

impl<S: Sign<A>, A: Zero + Add<Output = A>> Add for Complex<A, S> {
    type Output = Self;
    #[inline] fn add(self, Complex(_, c, d): Self) -> Self {
        let Complex(_, a, b) = self;
        Complex(PhantomData, a+c, b+d)
    }
}

impl<S: Sign<A>, A: Zero + Sub<Output = A>> Sub for Complex<A, S> {
    type Output = Self;
    #[inline] fn sub(self, Complex(_, c, d): Self) -> Self {
        let Complex(_, a, b) = self;
        Complex(PhantomData, a-c, b-d)
    }
}

impl<S: Sign<A>, A: Zero + Neg<Output = A>> Neg for Complex<A, S> {
    type Output = Self;
    #[inline] fn neg(self) -> Self {
        let Complex(_, a, b) = self;
        Complex(PhantomData, -a, -b)
    }
}

impl<S: Sign<A>, A: Zero + Add<Output = A> + One> One for Complex<A, S> {
    #[inline] fn one() -> Self { Complex(PhantomData, One::one(), Zero::zero()) }
}

impl<S: Sign<A>, A: Copy + Zero + Add<Output = A> + Conjugable + Mul<Output = A>> Mul for Complex<A, S> {
    type Output = Self;
    #[inline] fn mul(self, Complex(_, c, d): Self) -> Self {
        let Complex(_, a, b) = self;
        Complex(PhantomData, a*c+S::sign(d.conjugate()*b), d*a+b*c.conjugate())
    }
}

impl<S: Sign<A>, A: Copy + Zero + Add<Output = A> + Neg<Output = A> + Conjugable + Mul<Output = A> + Div<Output = A>> Div for Complex<A, S> {
    type Output = Self;
    #[inline] fn div(self, other: Self) -> Self {
        let Complex(_, a, b) =  self*other.conjugate();
        let Complex(_, c, _) = other*other.conjugate();
        Complex(PhantomData, a/c, b/c)
    }
}

#[cfg(test)] mod tests {
    use core::marker::PhantomData;
    use core::num::*;
    use quickcheck as qc;
    use typenum::consts::P1;
    use typenum::int::Z0;

    use super::*;

    #[test] fn complex_basis() {
        type T = Complex<isize>;
        let i: T = from_rect(0, 1);
        assert!(i*i == -T::one());
    }

    #[test] fn split_complex_basis() {
        type T = Complex<isize, P1>;
        let i: T = from_rect(0, 1);
        assert!(i*i == T::one());
    }

    #[test] fn dual_basis() {
        type T = Complex<isize, Z0>;
        let i: T = from_rect(0, 1);
        assert!(i*i == T::zero());
    }

    #[test] fn quaternion_basis() {
        type T = Complex<Complex<isize>>;
        let i: T = from_rect(from_rect(0, 1), from_rect(0, 0));
        let j: T = from_rect(from_rect(0, 0), from_rect(1, 0));
        let k: T = from_rect(from_rect(0, 0), from_rect(0, 1));
        assert!(i*j ==  k && j*k ==  i && k*i ==  j &&
                k*j == -i && j*i == -k && i*k == -j &&
                i*i == -T::one() && j*j == -T::one() && k*k == -T::one());
    }

    impl<S: Sign<A> + Send + 'static, A: qc::Arbitrary> qc::Arbitrary for Complex<A, S> {
        fn arbitrary<G: qc::Gen>(g: &mut G) -> Self { Complex(PhantomData, A::arbitrary(g), A::arbitrary(g)) }
    }

    #[quickcheck] fn complex_mul(x@Complex(_, a, b): Complex<isize>, y@Complex(_, c, d): Complex<isize>) -> bool {
        Complex(PhantomData, a*c-b*d, a*d+b*c) == x*y
    }
}