1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#![doc = include_str!("../README.md")]
#![recursion_limit = "1024"]
#![cfg_attr(test, allow(soft_unstable))]
#![cfg_attr(test, feature(test))]
#![cfg_attr(feature = "fn_traits", feature(fn_traits, unboxed_closures))]


#[cfg(test)] extern crate test;

#[macro_use]
extern crate derivative;
extern crate approx;
extern crate num_traits;
extern crate nalgebra as na;

//16D should work ok... a 16D multivector takes *only* 65K components, but if this takes
//too much memory, we may need to lower it a little :/
#[cfg(test)] pub(crate) const TEST_DIM: usize = 16;
#[cfg(test)] pub(crate) const SHORT_TEST_DIM: usize = 6;

macro_rules! cast_dim_doc {
    () => {
        "Embeds `self` into a different dimension by either removing elements or inserting zeros"
    }
}

macro_rules! new_docs {
    //starts the loop
    ($ty:ident::new($($arg:ident),*);) => { new_docs!(@vals 0 $($arg)*; $ty) };

    //picks out the numbers that will be used in the doc test
    (@vals 0  $arg:ident $($tt:tt)*) => { new_docs!(@vals 1  $($tt)* $arg 6) };
    (@vals 1  $arg:ident $($tt:tt)*) => { new_docs!(@vals 2  $($tt)* $arg 2) };
    (@vals 2  $arg:ident $($tt:tt)*) => { new_docs!(@vals 3  $($tt)* $arg 8) };
    (@vals 3  $arg:ident $($tt:tt)*) => { new_docs!(@vals 4  $($tt)* $arg 3) };
    (@vals 4  $arg:ident $($tt:tt)*) => { new_docs!(@vals 5  $($tt)* $arg 1) };
    (@vals 5  $arg:ident $($tt:tt)*) => { new_docs!(@vals 6  $($tt)* $arg 8) };
    (@vals 6  $arg:ident $($tt:tt)*) => { new_docs!(@vals 7  $($tt)* $arg 5) };
    (@vals 7  $arg:ident $($tt:tt)*) => { new_docs!(@vals 8  $($tt)* $arg 3) };
    (@vals 8  $arg:ident $($tt:tt)*) => { new_docs!(@vals 9  $($tt)* $arg 0) };
    (@vals 9  $arg:ident $($tt:tt)*) => { new_docs!(@vals 10 $($tt)* $arg 7) };
    (@vals 10 $arg:ident $($tt:tt)*) => { new_docs!(@vals 11 $($tt)* $arg 1) };
    (@vals 11 $arg:ident $($tt:tt)*) => { new_docs!(@vals 12 $($tt)* $arg 7) };
    (@vals 12 $arg:ident $($tt:tt)*) => { new_docs!(@vals 13 $($tt)* $arg 9) };
    (@vals 13 $arg:ident $($tt:tt)*) => { new_docs!(@vals 14 $($tt)* $arg 5) };
    (@vals 14 $arg:ident $($tt:tt)*) => { new_docs!(@vals 15 $($tt)* $arg 8) };
    (@vals 15 $arg:ident $($tt:tt)*) => { new_docs!(@vals 16 $($tt)* $arg 6) };
    (@vals 16 $arg:ident $($tt:tt)*) => { new_docs!(@vals 17 $($tt)* $arg 4) };
    (@vals 17 $arg:ident $($tt:tt)*) => { new_docs!(@vals 18 $($tt)* $arg 7) };
    (@vals 18 $arg:ident $($tt:tt)*) => { new_docs!(@vals 19 $($tt)* $arg 6) };
    (@vals 19 $arg:ident $($tt:tt)*) => { new_docs!(@vals 20 $($tt)* $arg 9) };

    //creates the function at the end of the vals loop
    (@vals $n:literal; $ty:ident $($arg:ident $val:literal)*) => {
        concat!(
            //yes, this is kinda gross, but it *does* work
            "Constructs a [`", stringify!($ty), "`] directly from components\n",
            "\n",
            " # Examples\n",
            "```\n",
            " # #[allow(unused_imports)] use wedged::algebra::*;\n",
            " # #[allow(unused_imports)] use wedged::subspace::*;\n",
            "let arr = [", stringify!($($val),*), "];\n",
            "let x = ", stringify!($ty), "::new(", stringify!($($val),*), ");\n",
            "\n",
            "assert_eq!(x.as_slice(), &arr);\n",
            "```"
        )
    }
}

//Takes in normal rust code and quotes it but with a basic impl of trait aliases added
macro_rules! auto {

    //doing this means that we can ignore the trailing semicolon
    (
        @lines {
            $(#[$attr:meta])*
            $vis:vis trait $trait:ident$(<$($T:ident),*>)? = $($bound:tt)*
        } ;
        $($rest:tt)*
    ) => {

        $(#[$attr])*
        $vis trait $trait$(<$($T),*>)?: $($bound)* {}

        impl<X $(, $($T),*)?> $trait$(<$($T),*>)? for X where X: $($bound)* {}

        auto!($($rest)*);
    };

    //munches through the code until we have a full line or block
    (@lines {$($line:tt)*} ) => { $($line)* };
    (@lines {$($line:tt)*} ; $($rest:tt)*) => { $($line)*; auto!(@lines {} $($rest)*); };
    (@lines {$($line:tt)*} {} $($rest:tt)*) => { $($line)*{} auto!(@lines {} $($rest)*); };
    (@lines {$($line:tt)*} $tt:tt $($rest:tt)*) => { auto!(@lines {$($line)* $tt} $($rest)*); };

    //ends the macro if we have nothing left
    () => {};

    //starts the loop to split off each line (we gotta be careful tho since this can lead to really
    //nasty errors)
    ($($stuff:tt)*) => { auto!(@lines {} $($stuff)*); };

}

macro_rules! impl_forward_scalar_binops {
    (
        @impl
        $prim:ty; impl<T:$Alloc:ident,$($N:ident),*> $Op:ident.$op:ident() for $Ty:ident;
        $($a:lifetime)?; $($b:lifetime)?
    ) => {
        impl<$($a,)? $($b,)? $($N:Dim),*> $Op<$(&$b)? $Ty<$prim,$($N),*>> for $(&$a)? $prim where $prim:$Alloc<$($N),*> {
            type Output = $Ty<$prim,$($N),*>;
            fn $op(self, rhs: $(&$b)? $Ty<$prim,$($N),*>) -> $Ty<$prim,$($N),*> {
                rhs.$op(self)
            }
        }
    };

    (@loop ; $($rest:tt)*) => {};

    (@loop $p:ty, $($prim:ty,)*; $($tt:tt)*) => {

        impl_forward_scalar_binops!(@impl $p; $($tt)*;   ;   );
        impl_forward_scalar_binops!(@impl $p; $($tt)*;   ; 'b);
        impl_forward_scalar_binops!(@impl $p; $($tt)*; 'a;   );
        impl_forward_scalar_binops!(@impl $p; $($tt)*; 'a; 'b);

        impl_forward_scalar_binops!(@loop $($prim,)*; $($tt)*);
    };

    ($($tt:tt)*) => {
        impl_forward_scalar_binops!(
            @loop
            u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
            // ::std::num::Wrapping<u8>,    ::std::num::Wrapping<i8>,
            // ::std::num::Wrapping<u16>,   ::std::num::Wrapping<i16>,
            // ::std::num::Wrapping<u32>,   ::std::num::Wrapping<i32>,
            // ::std::num::Wrapping<u64>,   ::std::num::Wrapping<i64>,
            // ::std::num::Wrapping<u128>,  ::std::num::Wrapping<i128>,
            // ::std::num::Wrapping<usize>, ::std::num::Wrapping<isize>,
            ;$($tt)*
        );
    };
}

macro_rules! impl_eq {

    () => {};

    (
        $Ty1:ident<T:$Alloc1:ident $(, $N1:ident)*> == $Ty2:ident<T:$Alloc2:ident $(, $N2:ident)*>
        with |$self:ident, $rhs:ident| $cond:expr, $field1:expr, $field2:expr;
        $($rest:tt)*
    ) => {

        impl<T1, T2 $(, $N1:Dim)* $(, $N2:Dim)*> PartialEq<$Ty2<T2 $(,$N2)*>> for $Ty1<T1 $(,$N1)*>
        where
            T1: $Alloc1<$($N1),*> + PartialEq<T2>,
            T2: $Alloc2<$($N2),*>
        {
            fn eq(&$self, $rhs: &$Ty2<T2 $(,$N2)*>) -> bool { $cond && $field1.eq($field2) }
            fn ne(&$self, $rhs: &$Ty2<T2 $(,$N2)*>) -> bool { !$cond || $field1.ne($field2) }
        }

        impl<T1, T2 $(, $N1:Dim)* $(, $N2:Dim)*> AbsDiffEq<$Ty2<T2 $(,$N2)*>> for $Ty1<T1 $(,$N1)*>
        where
            T1: $Alloc1<$($N1),*> + AbsDiffEq<T2>,
            T2: $Alloc2<$($N2),*>,
            T1::Epsilon: Clone
        {

            type Epsilon = T1::Epsilon;

            fn default_epsilon() -> T1::Epsilon { T1::default_epsilon() }

            fn abs_diff_eq(&$self, $rhs: &$Ty2<T2 $(,$N2)*>, epsilon: T1::Epsilon) -> bool {
                $cond && $field1.abs_diff_eq($field2, epsilon)
            }

            fn abs_diff_ne(&$self, $rhs: &$Ty2<T2 $(,$N2)*>, epsilon: T1::Epsilon) -> bool {
                !$cond || $field1.abs_diff_ne($field2, epsilon)
            }
        }

        impl<T1, T2 $(, $N1:Dim)* $(, $N2:Dim)*> RelativeEq<$Ty2<T2 $(,$N2)*>> for $Ty1<T1 $(,$N1)*>
        where
            T1: $Alloc1<$($N1),*> + RelativeEq<T2>,
            T2: $Alloc2<$($N2),*>,
            T1::Epsilon: Clone
        {

            fn default_max_relative() -> T1::Epsilon { T1::default_max_relative() }

            fn relative_eq(&$self, $rhs: &$Ty2<T2 $(,$N2)*>, epsilon: T1::Epsilon, max_relative: T1::Epsilon) -> bool {
                $cond && $field1.relative_eq($field2, epsilon, max_relative)
            }

            fn relative_ne(&$self, $rhs: &$Ty2<T2 $(,$N2)*>, epsilon: T1::Epsilon, max_relative: T1::Epsilon) -> bool {
                !$cond || $field1.relative_ne($field2, epsilon, max_relative)
            }
        }

        impl<T1, T2 $(, $N1:Dim)* $(, $N2:Dim)*> UlpsEq<$Ty2<T2 $(,$N2)*>> for $Ty1<T1 $(,$N1)*>
        where
            T1: $Alloc1<$($N1),*> + UlpsEq<T2>,
            T2: $Alloc2<$($N2),*>,
            T1::Epsilon: Clone
        {

            fn default_max_ulps() -> u32 { T1::default_max_ulps() }

            fn ulps_eq(&$self, $rhs: &$Ty2<T2 $(,$N2)*>, epsilon: T1::Epsilon, max_ulps: u32) -> bool {
                $cond && $field1.ulps_eq($field2, epsilon, max_ulps)
            }

            fn ulps_ne(&$self, $rhs: &$Ty2<T2 $(,$N2)*>, epsilon: T1::Epsilon, max_ulps: u32) -> bool {
                !$cond || $field1.ulps_ne($field2, epsilon, max_ulps)
            }
        }

        impl_eq!($($rest)*);
    }

}

//implements Sum and/or Product using fold()
macro_rules! impl_fold {
    (
        impl<T:$Alloc2:ident,U:$Alloc1:ident,$($N:ident),*>
        $Op:ident<$Ty2:ident<T,$($N2:ident),*>>.$op:ident() for $Ty1:ident<U,$($N1:ident),*>
        with $Op2:ident.$op2:ident(), $Id:ident.$id:ident()
        ; $($a:lifetime)? $(where $($tt:tt)*)?
    ) => {

        //
        // There were a couple options for how to do this.
        // One was to use Add and Mul with fold, and the other would be to split the
        // iterator by index, use Sum, and recombine. Theoretically, there could be benefits to
        // the latter in efficiency with more complex types and in correctness. But the added
        // implementation and API complexity didn't quite seem worth it for probably only a very
        // minor gain
        //

        impl<$($a,)? T,U,$($N:Dim),*> $Op<$(&$a)? $Ty2<T,$($N2),*>> for $Ty1<U,$($N1),*>
        where
            T: $Alloc2<$($N2),*>,
            U: $Alloc1<$($N1),*>,
            $Ty1<U,$($N1),*>: $Op2<$(&$a)? $Ty2<T,$($N2),*>, Output=$Ty1<U,$($N1),*>> + $Id
            $(, $($tt)*)?
        {
            fn $op<I>(i:I) -> $Ty1<U,$($N1),*> where I: Iterator<Item=$(&$a)? $Ty2<T,$($N2),*>> {
                i.fold($Id::$id(), |c,x| c.$op2(x))
            }
        }

    };

    (
        impl<T:$Alloc1:ident,U:$Alloc2:ident,$($N:ident),*>
        Sum<$Ty2:ident<T,$($N2:ident),*>> for $Ty1:ident<U,$($N1:ident),*>
        $(where $($tt:tt)*)?
    ) => {
        impl_fold!(
            impl<T:$Alloc1,U:$Alloc2,$($N),*> Sum<$Ty2<T,$($N2),*>>.sum() for $Ty1<U,$($N1),*>
            with Add.add(), Zero.zero() ; $(where $($tt)*)?
        );
        impl_fold!(
            impl<T:$Alloc1,U:$Alloc2,$($N),*> Sum<$Ty2<T,$($N2),*>>.sum() for $Ty1<U,$($N1),*>
            with Add.add(), Zero.zero() ; 'a $(where $($tt)*)?
        );
    };

    (
        impl<T:$Alloc1:ident,U:$Alloc2:ident,$($N:ident),*>
        Product<$Ty2:ident<T,$($N2:ident),*>> for $Ty1:ident<U,$($N1:ident),*>
        $(where $($tt:tt)*)?
    ) => {
        impl_fold!(
            impl<T:$Alloc1,U:$Alloc2,$($N),*> Product<$Ty2<T,$($N2),*>>.product() for $Ty1<U,$($N1),*>
            with Mul.mul(), One.one() ; $(where $($tt)*)?
        );
        impl_fold!(
            impl<T:$Alloc1,U:$Alloc2,$($N),*> Product<$Ty2<T,$($N2),*>>.product() for $Ty1<U,$($N1),*>
            with Mul.mul(), One.one() ; 'a $(where $($tt)*)?
        );
    };

}

#[cfg(test)]
macro_rules! dim_name_test_loop {
    (@run ; $callback:ident) => {};
    (@run $N0:ident $($N:ident)*; $callback:ident) => {
        $callback!($N0);
        dim_name_test_loop!(@run $($N)*; $callback);
    };

    (|$d:tt $N:ident| $($expr:tt)*) => {
        dim_name_test_loop!({U0 U1 U2 U3 U4 U5 U6 U7 U8 U9 U10} |$d $N| $($expr)*);
    };

    (@short |$d:tt $N:ident| $($expr:tt)*) => {
        dim_name_test_loop!({U0 U1 U2 U3 U4 U5 U6} |$d $N| $($expr)*);
    };

    //$d must be the $ token
    ({$($U:ident)*} |$d:tt $N:ident| $($expr:tt)*) => {
        {

            #[allow(unused_imports)]
            use na::dimension::{
                DimName,
                U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10
            };

            macro_rules! _dim_name_test_loop_callback {
                ($d $N:ident) => {
                    $($expr)*
                };
            }

            //if we do any more than U14, then we get stack overflows from the values being too large
            //for 64bit scalars
            //Also, compilation times get pretty long for this stuff, so we lower it down to U10
            //instead
            dim_name_test_loop!(
                @run $($U)*;
                _dim_name_test_loop_callback
            );

        }
    };

    (|$d0:tt $N0:ident $(, $d:tt $N:ident)+| $($expr:tt)*) => {
        dim_name_test_loop!(
            |$d0 $N0| dim_name_test_loop!(|$($d $N),+| $($expr)*);
        );
    }


}


pub mod base;

pub mod algebra;
pub mod subspace;