wedged/base/
alloc.rs

1//!
2//! Traits for picking the types to store the internal data of the algebraic structs
3//!
4//! Ideally, this would be done using entirely `const` generics, but this is impossible for a
5//! couple reasons:
6//! 1. Unlike for simple vectors and matrices, the number of elements in blades, evens, etc
7//!    is not simply the dimension or grade but instead a more involved function of them. Hence,
8//!    since generic `const` expressions aren't currently supported, we
9//!    cannot use them to construct the backing array.
10//! 2. We need to *also* support non-array storage for [`Dyn`] dimensions and grades
11//!
12//! Instead, we use a trait based system with associated types to select what the backing buffer
13//! should be.
14//!
15//! The core of this system are the [`AllocBlade<N,G>`], [`AllocEven<N>`], [`AllocOdd<N>`], and
16//! [`AllocMultivector<N>`] traits. These are all given blanket `impl`s on all types for all
17//! supported of dimensions and grades `N` and `G`. Then, in order to retrieve the storage the
18//! for a particular algebraic struct, we can use the corresponding `Alloc*` trait and its
19//! associated type `Buffer`, adding the `Alloc*` bound to the current scalar being used.
20//!
21//! At the moment, for static array allocation, only dimensions and grades up to 16 are supported.
22//! This is both due to the fact we don't have generic const expressions *and* out of practical
23//! considerations regarding sizes in high numbers of dimensions.
24//! (`f32` Multivectors in 16D already use ~**260KB**!!) However,
25//! if a high number of dimensions are required, using a [`Dyn`] dimension or grade will allow
26//! that number of dimensions to be used, just using the heap instead
27//!
28//!
29
30use std::mem::MaybeUninit;
31use std::iter::Iterator;
32
33use crate::base::*;
34
35use std::ops::{ Add, BitOr };
36use typenum::{
37    IsLessOrEqual, Sum, LeEq, True, U1
38};
39
40#[cfg(doc)] use crate::algebra::*;
41#[cfg(doc)] use crate::subspace::*;
42
43/// Implements [`Alloc`] and makes the default choice for what storage types to use
44pub struct DefaultAllocator;
45
46/// The storage type to use for `M`
47pub type Allocate<M> = <DefaultAllocator as Alloc<M>>::Buffer;
48
49/// The backing buffer type for a [`Blade`] with scalar `T`, dimension `N`, and grade `G`
50pub type AllocateBlade<T,N,G> = <T as AllocBlade<N,G>>::Buffer;
51
52/// The backing buffer type for an [`Even`] with scalar `T` and dimension `N`
53pub type AllocateEven<T,N> = <T as AllocEven<N>>::Buffer;
54
55/// The backing buffer type for an [`Odd`] with scalar `T` and dimension `N`
56pub type AllocateOdd<T,N> = <T as AllocOdd<N>>::Buffer;
57
58/// The backing buffer type for a [`Multivector`] with scalar `T` and dimension `N`
59pub type AllocateMultivector<T,N> = <T as AllocMultivector<N>>::Buffer;
60
61///
62/// Picks the buffer type to use for the generic type `M` and provides extra helper methods
63///
64/// The intent is that this is implemented onto a ZST for every supported algebraic struct
65/// in order to represent a particular allocation system. At the moment, only one exists
66/// ([`DefaultAllocator`]), but more may be added in the future.
67///
68/// Additionally, this allows us to make the [`Allocate`] type alias so that every algebraic
69/// struct uses `Allocate<Self>` for it's internal buffer.
70///
71/// Finally, this trait includes some helper methods for actually creating and managing the backing
72/// buffer.
73///
74pub trait Alloc<M> {
75
76    ///The type to store in the backing buffer
77    type Scalar: Sized;
78
79    ///A type representing the dimension and/or grade of this structure
80    type Shape: Copy;
81
82    ///The type to store the data in
83    type Buffer: Storage<Self::Scalar, Uninit=Self::Uninit>;
84
85    ///The type to store uninitialized data in
86    type Uninit: UninitStorage<Self::Scalar, Init=Self::Buffer>;
87
88    ///Returns the dimension and potentially grade depending on what type `M` is
89    fn shape(this: &M) -> Self::Shape;
90    ///Makes an uninitialized buffer
91    fn uninit(shape: Self::Shape) -> Self::Uninit;
92    
93    /// Creates an `M` from an uninitialized buffer assuming it is initialized
94    /// # Safety
95    /// Same requirements as for [MaybeUninit::assume_init]
96    unsafe fn assume_init(uninit: Self::Uninit) -> M;
97
98}
99
100///Picks the buffer to use for a [`Blade`] of dimension `N`, grade `G`, and using `Self` as the scalar
101pub trait AllocBlade<N:Dim,G:Dim>: Sized {
102    type Buffer: BladeStorage<Self,N,G>;
103}
104
105///Picks the buffer to use for an [`Even`] of dimension `N` and using `Self` as the scalar
106pub trait AllocEven<N:Dim>: Sized {
107    type Buffer: EvenStorage<Self,N>;
108}
109
110///Picks the buffer to use for an [`Odd`] of dimension `N` and using `Self` as the scalar
111pub trait AllocOdd<N:Dim>: Sized {
112    type Buffer: OddStorage<Self,N>;
113}
114
115///A marker trait for when a dimension `N` is supported for both [`Even`]s and [`Odd`]s
116pub trait AllocVersor<N:Dim>: AllocEven<N> + AllocOdd<N> {}
117impl<T:AllocEven<N>+AllocOdd<N>, N:Dim> AllocVersor<N> for T {}
118
119///Picks the buffer to use for an [`Multivector`] of dimension `N`, grade `G`, and using `Self` as the scalar
120pub trait AllocMultivector<N:Dim>: Sized {
121    type Buffer: MultivectorStorage<Self,N>;
122}
123
124///
125/// Marks if a [Blade] of a given dimension and grade is guaranteed to always be [simple](SimpleBlade)
126///
127/// This trait is implemented for scalars, vectors, psuedovectors, and psuedoscalars by bounding the
128/// grade to be 0, 1, N, or N-1.
129///
130/// It is used in a number of locations to allow for certain operations that are only possible with
131/// simple blades, ie mutating `SimpleBlade`s, [projecting](Blade::project) onto a Blade, etc.
132///
133/// At the moment, the implementation uses `typenum` expressions to determine type eligibility,
134/// which may be lead to some provability issues when using generic types, especially
135/// for psuedoscalars and psuedovectors. However, this will be simplified dramatically once the
136/// `#[feature(marker_trait_attr)]` is stabilized,
137///
138pub trait AllocSimpleBlade<N:Dim,G:Dim>: AllocBlade<N,G> {}
139
140impl<T:AllocBlade<Dyn,Const<0>>> AllocSimpleBlade<Dyn,Const<0>> for T {}
141impl<T:AllocBlade<Dyn,Const<1>>> AllocSimpleBlade<Dyn,Const<1>> for T {}
142impl<T:AllocBlade<N,G>,N:Dim,G:Dim> AllocSimpleBlade<N,G> for T where
143    //establish that `N` and `G` are constant numbers
144    N: DimName+ToTypenum,
145    G: DimName+ToTypenum,
146
147    //establish that we can compare `G` with `1` and `N` with `G+1`
148    G::Typenum: Add<U1> + IsLessOrEqual<U1>,
149    N::Typenum: IsLessOrEqual<Sum<G::Typenum,U1>>,
150
151    //`or` together the two comparisons
152    LeEq<G::Typenum, U1>: BitOr<LeEq<N::Typenum, Sum<G::Typenum,U1>>, Output=True>
153{}
154
155impl<T, const N: usize> AllocBlade<Const<N>, Dyn> for T {
156    type Buffer = DynBladeStorage<T, Const<N>, Dyn>;
157}
158
159impl<T, const G: usize> AllocBlade<Dyn, Const<G>> for T {
160    type Buffer = DynBladeStorage<T, Dyn, Const<G>>;
161}
162
163impl<T> AllocBlade<Dyn, Dyn> for T {
164    type Buffer = DynBladeStorage<T, Dyn, Dyn>;
165}
166
167impl<T> AllocEven<Dyn> for T {
168    type Buffer = DynEvenStorage<T, Dyn>;
169}
170
171impl<T> AllocOdd<Dyn> for T {
172    type Buffer = DynOddStorage<T, Dyn>;
173}
174
175impl<T> AllocMultivector<Dyn> for T {
176    type Buffer = DynMultivectorStorage<T, Dyn>;
177}
178
179#[inline(always)]
180fn uninit_array<T, const L: usize>() -> [MaybeUninit<T>; L] {
181    //TODO: use `MaybeUninit::uninit_array()` when stabilized
182    unsafe {
183        //the outer MaybeUninit wraps the [MaybeUninit<T>; L] array
184        MaybeUninit::uninit().assume_init()
185    }
186}
187
188#[inline(always)]
189fn array_from_iter<T, I: IntoIterator<Item=T>, const L: usize>(iter:I, kind:&str) -> [T;L] {
190    //TODO: use `MaybeUninit::uninit_array()` when stabilized
191    let mut uninit: [MaybeUninit<T>;L] = uninit_array();
192
193    //fill the array and count how many spaces we actually fill
194    let mut count = 0;
195    for (i, x) in (0..L).zip(iter) {
196        uninit[i] = MaybeUninit::new(x);
197        count = i+1;
198    }
199
200    //panic if not enough elements
201    if count!=L {
202        panic!("Not enough elements to fill {}", kind);
203    }
204
205    unsafe { <[MaybeUninit<T>;L] as UninitStorage<T>>::assume_init(uninit) }
206}
207
208macro_rules! impl_alloc{
209
210    ($n:literal $($N:literal)*; $($G:literal)*; @$cmd:ident $($pairs:tt)*) => {
211        impl_alloc!($($N)*; $($G)*; @$cmd $($pairs)* $(($n, $G))*);
212
213        impl_alloc!($n @$cmd);
214
215    };
216
217    //reuse the macro loop to produce tests for the given dimension
218    ($N:literal @tests) => {
219        assert_eq!(
220            std::mem::size_of::<AllocateEven<f32, Const<$N>>>(),
221            //this has some weird behavior
222            std::mem::size_of::<f32>() * even_elements($N)
223        );
224
225        assert_eq!(
226            std::mem::size_of::<AllocateOdd<f32, Const<$N>>>(),
227            //this has some weird behavior
228            std::mem::size_of::<f32>() * odd_elements($N)
229        );
230
231        assert_eq!(
232            std::mem::size_of::<AllocateMultivector<f32, Const<$N>>>(),
233            std::mem::size_of::<f32>() * 2usize.pow($N)
234        );
235    };
236
237    ($N:literal @impl) => {
238        impl<T> AllocEven<Const<$N>> for T {
239            type Buffer = [T; even_elements($N)];
240        }
241
242        unsafe impl<T> EvenStorage<T, Const<$N>> for [T; even_elements($N) ] {
243            fn dim(&self) -> Const<$N> { Const::<$N> }
244            fn uninit(_: Const<$N>,) -> Self::Uninit { uninit_array() }
245            fn from_iterator<I:IntoIterator<Item=T>>(_: Const<$N>, iter: I) -> Self {
246                array_from_iter(iter, "value")
247            }
248        }
249
250        impl<T> AllocOdd<Const<$N>> for T {
251            type Buffer = [T; odd_elements($N)];
252        }
253
254        unsafe impl<T> OddStorage<T, Const<$N>> for [T; odd_elements($N) ] {
255            fn dim(&self) -> Const<$N> { Const::<$N> }
256            fn uninit(_: Const<$N>,) -> Self::Uninit { uninit_array() }
257            fn from_iterator<I:IntoIterator<Item=T>>(_: Const<$N>, iter: I) -> Self {
258                array_from_iter(iter, "value")
259            }
260        }
261
262        impl<T> AllocMultivector<Const<$N>> for T {
263            type Buffer = [T; 2usize.pow($N)];
264        }
265
266        unsafe impl<T> MultivectorStorage<T, Const<$N>> for [T; 2usize.pow($N)] {
267            fn dim(&self) -> Const<$N> { Const::<$N> }
268            fn uninit(_: Const<$N>,) -> Self::Uninit { uninit_array() }
269            fn from_iterator<I:IntoIterator<Item=T>>(_: Const<$N>, iter: I) -> Self {
270                array_from_iter(iter, "multivector")
271            }
272        }
273
274    };
275
276    (; $($_G:literal)*; @impl $(($N:literal, $G:literal))*) => {
277        $(
278            impl<T> AllocBlade<Const<$N>, Const<$G>> for T {
279                type Buffer = [T; binom($N, $G)];
280            }
281
282            unsafe impl<T> BladeStorage<T, Const<$N>, Const<$G>> for [T; binom($N, $G)] {
283                fn dim(&self) -> Const<$N> { Const::<$N> }
284                fn grade(&self) -> Const<$G> { Const::<$G> }
285
286                fn uninit(_: Const<$N>, _: Const<$G>) -> Self::Uninit { uninit_array() }
287
288                fn from_iterator<I:IntoIterator<Item=T>>(_: Const<$N>, _: Const<$G>, iter: I) -> Self {
289                    array_from_iter(iter, "blade")
290                }
291            }
292
293        )*
294    };
295
296    //reuse the macro loop to produce tests for the given dimension and grade
297    (; $($_G:literal)*; @tests $(($N:literal, $G:literal))*) => {
298        $(
299            assert_eq!(
300                std::mem::size_of::<AllocateBlade<f32, Const<$N>, Const<$G>>>(),
301                std::mem::size_of::<f32>() * binom($N, $G)
302            );
303        )*
304    };
305}
306
307impl_alloc!(
308    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; @impl
309);
310
311#[test]
312#[cfg(test)]
313fn buffer_sizes() {
314    impl_alloc!(
315        0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; @tests
316    );
317}