cplx 0.3.3

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

#![no_std]

extern crate typenum;

use core::cmp::*;
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
#[derive(Debug)]
pub struct Complex<A, S: Sign<A> = N1>(PhantomData<S>, A, A);

impl<S: Sign<A>, A> Complex<A, S> {
    #[inline] pub const fn from_rect(re: A, im: A) -> Self { Complex(PhantomData, re, im) }
    #[inline] pub const fn to_rect(self) -> (A, A) { (self.1, self.2) }
}

#[inline] pub const 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 { Complex(PhantomData, self.1.clone(), self.2.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> {}

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::num::*;
    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_eq!(-T::one(), i*i);
    }

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

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

    #[test] fn quaternion_basis() {
        type T = Complex<Complex<isize>>;
        let one = T::one();
        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_eq!((i*j, j*k, k*i, k*j, j*i, i*k,  i*i,  j*j,  k*k),
                   ( k,   i,   j,  -i,  -k,  -j,  -one, -one, -one));
    }
}