use core::marker::PhantomData;
use peano;
use peano::Natural;
// The indices of the basis elements have a bit for each element of the
// generating set, which are in such an order, from LSB to MSB:
// hyperbolic, elliptic, flat
pub struct Basis<DimFlat: Natural = peano::Zero, DimElliptic: Natural = peano::Succ<peano::Zero>, DimHyperbolic: Natural = peano::Zero>(PhantomData<(DimFlat, DimElliptic, DimHyperbolic)>, usize);
impl<DimFlat: Natural, DimElliptic: Natural, DimHyperbolic: Natural> Basis<DimFlat, DimElliptic, DimHyperbolic> {
#[inline] pub fn flat(i: usize) -> Self { Basis(PhantomData, i<<(DimElliptic::to_usize()+DimHyperbolic::to_usize())) }
#[inline] pub fn elliptic(i: usize) -> Self { Basis(PhantomData, i<<DimHyperbolic::to_usize()) }
#[inline] pub fn hyperbolic(i: usize) -> Self { Basis(PhantomData, i) }
fn mul(Basis(_, i): Self, Basis(_, j): Self) -> Option<(bool /* answer is negative */, Self)> {
mul::<DimFlat, DimElliptic, DimHyperbolic>(i, j).map(|(p, k)| (p, Basis(PhantomData, k)))
}
}
// Multiplies 2 basis elements.
// Returns None if product is zero.
pub fn mul<DimFlat: Natural, DimElliptic: Natural, DimHyperbolic: Natural>(i: usize, j: usize) -> Option<(bool /* answer is negative */, usize)> {
let nul_mask = !0<<(DimElliptic::to_usize()+DimHyperbolic::to_usize());
let neg_mask = !0<<DimHyperbolic::to_usize();
if i&j&nul_mask != 0 { None } else { Some((((i&j&neg_mask).count_ones()&1 != 0) ^ parity_to_bring_together_like_factors(i, j), i^j)) }
}
#[inline] pub fn parity_to_bring_together_like_factors(mut i: usize, mut j: usize) -> bool {
let mut p = 0;
while i&j != 0 {
let n = (i&j).trailing_zeros();
i ^= 1<<n;
j ^= 1<<n;
p ^= (j&((1<<n)-1)).count_ones()^(i&(!0<<n)).count_ones();
}
p&1 != 0
}
//#[inline] pub fn reversal_parity(k: usize) -> bool { k.count_ones()&2 != 0 }
#[cfg(test)] mod tests {
use peano;
use quickcheck as qc;
use std::marker::PhantomData;
use super::*;
#[quickcheck] fn multiplication_complex() -> bool {
[(0, 0), (0, 1), (1, 0), (1, 1)].iter().all(|&(i, j)| mul::<peano::Zero, peano::Succ<peano::Zero>, peano::Zero>(i, j) == Some((i&j&1 != 0, i^j)))
}
#[quickcheck] fn multiplication_quaternion() -> bool {
(0..4).all(|i| (0..4).all(|j| {
let c = Some((match (i, j) {
(0, _) => false,
(_, 0) => false,
(1, 2) => false,
(2, 1) => true,
(2, 3) => false,
(3, 2) => true,
(3, 1) => false,
(1, 3) => true,
(_, _) => true,
}, i^j));
let d = mul::<peano::Zero, peano::Succ<peano::Succ<peano::Zero>>, peano::Zero>(i, j);
println!("{}, {}, {}, {:?}, {:?}", i, j, parity_to_bring_together_like_factors(i, j), c, d);
c == d
}))
}
}