wedged 0.1.1

A robust and generalized library for Geometric Algebra in Rust.
Documentation

use super::*;

#[inline]
const fn upcast_dst_index(n:usize, dn:usize, g:usize) -> usize {
    if 2*g <= n+1 || dn==0 || g>n+1 {
        0
    } else {
        binom(n, n+1-g) + upcast_dst_index(n+1, dn-1, g)
    }
}

#[inline]
const fn downcast_src_index(n:usize, dn:usize, g:usize) -> usize {
    if n==0 || dn==0 {
        0
    } else if 2*g <= n || g>n {
        downcast_src_index(n-1, dn-1, g)
    } else {
        binom(n-1, n-g) + downcast_src_index(n-1, dn-1, g)
    }
}

#[inline]
#[allow(clippy::needless_range_loop)]
fn cast_blade<T:Zero, B:Iterator<Item=T>>(
    b: &mut B, n1:usize, n2:usize, g:usize, e1:usize, e2:usize, dst: &mut [MaybeUninit<T>]
) {
    if n2 > n1 {
        let start = upcast_dst_index(n1, n2-n1, g);

        //any zeroes at the beginning
        for i in 0..start {
            dst[i] = MaybeUninit::new(T::zero())
        }

        //copy the data into the right spot
        for i in 0..e1 {
            dst[i+start] = MaybeUninit::new(b.next().unwrap());
        }

        //the remaining zeroes
        for i in (start+e1)..e2 {
            dst[i] = MaybeUninit::new(T::zero())
        }
    } else {

        let start = downcast_src_index(n1, n1-n2, g);

        //skip "start" indices
        for _ in 0..start { b.next(); }

        //extract the correct number of values from b
        for i in 0..e2 {
            dst[i] = MaybeUninit::new(b.next().unwrap());
        }

        //skip the remaining amount in this blade
        for _ in (start+e2)..e1 { b.next(); }

    }
}

#[inline]
fn cast_subspace<T:Zero, B:Iterator<Item=T>, I:Iterator<Item=(usize, usize, usize)>>(
    b: &mut B, n1:usize, n2:usize, grades:I, dst: &mut [MaybeUninit<T>]
) {
    let x = b;
    let mut dst = dst;
    for (g, e1, e2) in grades {
        cast_blade(x, n1, n2, g, e1, e2, dst);
        dst = &mut dst[e2..];
    }
}

impl<T:AllocBlade<N1,G>+Zero, N1:Dim, G:Dim> Blade<T,N1,G> {

    #[doc = cast_dim_doc!()]
    pub fn cast_dim_generic<N2:Dim>(self, n:N2) -> Blade<T,N2,G> where T:AllocBlade<N2,G> {
        //the destination value
        let mut uninit = AllocateBlade::<T,N2,G>::uninit(n, self.grade_generic());

        //copy the right amount of values from the source to the destination at the right spot
        let (n1, n2, g, e) = (self.dim(), n.value(), self.grade(), self.elements());
        cast_blade(&mut self.into_iter(), n1, n2, g, e, uninit.elements(), uninit.borrow_mut());

        //assume init
        Blade { data: unsafe { uninit.assume_init() } }
    }

    #[doc = cast_dim_doc!()]
    pub fn cast_dim_dyn(self, n:usize) -> Blade<T,Dyn,G> where T:AllocBlade<Dyn,G> {
        self.cast_dim_generic(Dyn(n))
    }

    #[doc = cast_dim_doc!()]
    pub fn cast_dim<N2:DimName>(self) -> Blade<T,N2,G> where T:AllocBlade<N2,G> {
        self.cast_dim_generic(N2::name())
    }

}

macro_rules! impl_dim_cast {
    ($($Ty:ident<T:$Alloc:ident,N1>, |$n1:ident, $n2:ident| $iter:expr;)*) => {

        $(
            impl<T:$Alloc<N1>+Zero, N1:Dim> $Ty<T,N1> {

                #[doc = cast_dim_doc!()]
                pub fn cast_dim_generic<N2:Dim>(self, n:N2) -> $Ty<T,N2> where T:$Alloc<N2> {
                    //the destination value
                    let mut uninit = Allocate::<$Ty<T,N2>>::uninit(n);

                    //copy the right amount of values from the source to the destination at the right spot
                    let ($n1, $n2) = (self.dim(), n.value());
                    cast_subspace(&mut self.into_iter(), $n1, $n2, $iter, uninit.borrow_mut());

                    //assume init
                    $Ty { data: unsafe { uninit.assume_init() } }
                }

                #[doc = cast_dim_doc!()]
                pub fn cast_dim_dyn(self, n:usize) -> $Ty<T,Dyn> where T:$Alloc<Dyn> {
                    self.cast_dim_generic(Dyn(n))
                }

                #[doc = cast_dim_doc!()]
                pub fn cast_dim<N2:DimName>(self) -> $Ty<T,N2> where T:$Alloc<N2> {
                    self.cast_dim_generic(N2::name())
                }
            }
        )*

    }
}

impl_dim_cast!(
    Even<T:AllocEven,N1>, |n1,n2| {
        components_of(n1).zip(components_of(n2)).enumerate().map(|(g,(e1,e2))| (g,e1,e2)).step_by(2)
    };

    Odd<T:AllocOdd,N1>, |n1,n2| {
        components_of(n1).zip(components_of(n2)).enumerate().map(|(g,(e1,e2))| (g,e1,e2)).skip(1).step_by(2)
    };

    Multivector<T:AllocMultivector,N1>, |n1,n2| {
        components_of(n1).zip(components_of(n2)).enumerate().map(|(g,(e1,e2))| (g,e1,e2))
    };
);

#[cfg(test)]
mod tests {

    use super::*;/// Embeds `self` into a different dimension by either removing elements or inserting zeros


    const N: usize = TEST_DIM;

    #[test]
    fn upcast_downcast_idempotence() {

        for n1 in 0..=N {
            for n2 in n1..=N {

                for g in 0..=N {
                    let b1 = BladeD::from_iterator(n1, g, 1..);
                    let b2 = b1.clone().cast_dim_dyn(n2).cast_dim_dyn(n1);
                    assert_eq!(b1, b2);
                }

                let m1 = EvenD::from_iterator(n1, 1..);
                let m2 = m1.clone().cast_dim_dyn(n2).cast_dim_dyn(n1);
                assert_eq!(m1, m2);

                let m1 = OddD::from_iterator(n1, 1..);
                let m2 = m1.clone().cast_dim_dyn(n2).cast_dim_dyn(n1);
                assert_eq!(m1, m2);

                let m1 = MultivectorD::from_iterator(n1, 1..);
                let m2 = m1.clone().cast_dim_dyn(n2).cast_dim_dyn(n1);
                assert_eq!(m1, m2);


            }

        }

        dim_name_test_loop!(
            |$N1, $N2| if $N2::dim() >= $N1::dim() {

                dim_name_test_loop!(
                    |$G| {
                        let b1 = Blade::<_,$N1,$G>::from_iterator(1..);
                        let b2 = b1.clone().cast_dim::<$N2>().cast_dim::<$N1>();
                        assert_eq!(b1, b2);
                    }
                );

                let m1 = Even::<_,$N1>::from_iterator(1..);
                let m2 = m1.clone().cast_dim::<$N2>().cast_dim::<$N1>();
                assert_eq!(m1, m2);

                let m1 = Odd::<_,$N1>::from_iterator(1..);
                let m2 = m1.clone().cast_dim::<$N2>().cast_dim::<$N1>();
                assert_eq!(m1, m2);

                let m1 = Multivector::<_,$N1>::from_iterator(1..);
                let m2 = m1.clone().cast_dim::<$N2>().cast_dim::<$N1>();
                assert_eq!(m1, m2);

            }
        );

    }

    #[test]
    fn vector_dim_cast() {

        for n1 in 0..=N {
            for n2 in 0..=N {

                let v1 = VecD::from_element(n1, 6.28);
                let v2 = v1.clone().cast_dim_dyn(n2);

                if n1 <= n2 {
                    assert_eq!(v1.as_slice(), &v2.as_slice()[0..n1]);
                } else {
                    assert_eq!(&v1.as_slice()[0..n2], v2.as_slice());
                }

            }
        }

        dim_name_test_loop!(
            |$N1, $N2| {
                let (n1, n2) = ($N1::dim(), $N2::dim());

                let v1 = VecN::<_, $N1>::from_element(6.28);
                let v2 = v1.clone().cast_dim::<$N2>();

                if n1 <= n2 {
                    assert_eq!(v1.as_slice(), &v2.as_slice()[0..n1]);
                } else {
                    assert_eq!(&v1.as_slice()[0..n2], v2.as_slice());
                }
            }
        );

    }

}