1 /+
2 The MIT License (MIT)
3 
4     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
5 
6     Permission is hereby granted, free of charge, to any person obtaining a copy
7     of this software and associated documentation files (the "Software"), to deal
8     in the Software without restriction, including without limitation the rights
9     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10     copies of the Software, and to permit persons to whom the Software is
11     furnished to do so, subject to the following conditions:
12 
13     The above copyright notice and this permission notice shall be included in
14     all copies or substantial portions of the Software.
15 
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22     THE SOFTWARE.
23 +/
24 
25 module des.math.linear.vector;
26 
27 import std.math;
28 import std.algorithm;
29 import std.array;
30 import std.traits;
31 import std.exception;
32 import std.string;
33 
34 import des.util.testsuite;
35 
36 import des.math.util;
37 
38 version(unittest) import std.stdio;
39 
40 private pure void isVectorImpl(size_t N,T,alias string AS)( in Vector!(N,T,AS) ) {}
41 pure bool isVector(E)() { return is( typeof(isVectorImpl(E.init)) ); }
42 
43 pure bool isStaticVector(E)()
44 {
45     static if( !isVector!E ) return false;
46     else return E.isStatic;
47 }
48 
49 pure bool isDynamicVector(E)()
50 {
51     static if( !isVector!E ) return false;
52     else return E.isDynamic;
53 }
54 
55 unittest
56 {
57     static assert( !isStaticVector!float );
58     static assert( !isDynamicVector!float );
59 }
60 
61 pure bool isCompatibleVector(size_t N,T,E)()
62 {
63     static if( !isVector!E ) return false;
64     else return E.init.length == N && is( E.datatype : T );
65 }
66 
67 pure bool isValidOp(string op,T,E,K=T)()
68 { mixin( `return is( typeof( T.init ` ~ op ~ ` E.init ) : K );` ); }
69 
70 pure bool hasCompMltAndSum(T,E)()
71 { return is( typeof( T(T.init * E.init) ) ) && is( typeof( T.init + T.init ) == T ); }
72 
73 private enum SEP = " ";
74 
75 pure @property string spaceSep(string str) { return str.split("").join(SEP); }
76 
77 private @property string zerosVectorData(size_t N)()
78 {
79     string[] ret;
80     foreach( j; 0 .. N )
81         ret ~= format( "%d", 0 );
82     return "[" ~ ret.join(",") ~ "]";
83 }
84 
85 struct Vector( size_t N, T, alias string AS="")
86     if( isCompatibleArrayAccessString(N,AS,SEP) || AS.length == 0 )
87 {
88     enum isDynamic = N == 0;
89     enum isStatic = N != 0;
90     enum dims = N;
91 
92     static if( isStatic ) 
93     {
94         static if( isNumeric!T )
95             T[N] data = mixin( zerosVectorData!N );
96         else
97             T[N] data;
98     }
99     else T[] data;
100 
101     alias data this;
102 
103     alias T datatype;
104     alias AS access_string;
105     alias Vector!(N,T,AS) selftype;
106 
107 pure:
108     static if( isDynamic )
109     {
110         pure @property auto length() const { return data.length; }
111 
112         pure @property auto length( size_t nl )
113         {
114             data.length = nl;
115             return data.length;
116         }
117     }
118     else enum length = N;
119 
120     this(E...)( in E vals )
121     {
122         // not in limitation of signature because
123         // not work with dynamic vectors
124         static if( E.length == 0 )
125             static assert( 0, "args length == 0" );
126         static if( !is(typeof(flatData!T(vals))) )
127             static assert( 0, "args not compatible" );
128 
129         auto buf = flatData!T(vals);
130 
131         static if( isStatic )
132         {
133             if( buf.length == length )
134                 data[] = buf[];
135             else if( buf.length == 1 )
136                 data[] = buf[0];
137             else enforce( false, "bad args length" );
138         }
139         else
140         {
141             length = buf.length;
142             data[] = buf[];
143         }
144     }
145 
146     static if( isDynamic )
147         this(this) { data = this.data.dup; }
148 
149     auto opAssign( size_t K, E, alias string oas )( in Vector!(K,E,oas) b )
150         if( (K==N||K==0||N==0) && is( typeof(T(E.init)) ) )
151     {
152         static if( isDynamic ) length = b.length;
153         foreach( i; 0 .. length ) data[i] = T(b[i]);
154         return this;
155     }
156 
157     auto opUnary(string op)() const
158         if( op == "-" && is( typeof( T.init * (-1) ) : T ) )
159     {
160         selftype ret;
161         static if( isDynamic ) ret.length = length;
162         foreach( i; 0 .. length )
163             ret[i] = this[i] * -1;
164         return ret;
165     }
166 
167     auto opBinary(string op, size_t K, E, alias string oas )
168         ( in Vector!(K,E,oas) b ) const
169         if( isValidOp!(op,T,E) && (K==N||K==0||N==0) )
170     {
171         selftype ret;
172         static if( isDynamic || b.isDynamic )
173             enforce( length == b.length, "wrong length" );
174         static if( isDynamic ) ret.length = length;
175         foreach( i; 0 .. length )
176             mixin( `ret[i] = this[i] ` ~ op ~ ` b[i];` );
177         return ret;
178     }
179 
180     auto opBinary(string op, E)( in E b ) const
181         if( isValidOp!(op,T,E) && op != "+" && op != "-" )
182     {
183         selftype ret;
184         static if( isDynamic ) ret.length = length;
185         foreach( i; 0 .. length )
186             mixin( `ret[i] = this[i] ` ~ op ~ ` b;` );
187         return ret;
188     }
189 
190     auto opOpAssign(string op, E)( in E b )
191         if( mixin( `is( typeof( this ` ~ op ~ ` b ) )` ) )
192     { mixin( `return this = this ` ~ op ~ ` b;` ); }
193 
194     auto opBinaryRight(string op, E)( in E b ) const
195         if( isValidOp!(op,E,T,T) && op == "*" )
196     { mixin( "return this " ~ op ~ " b;" ); }
197 
198     bool opCast(E)() const if( is( E == bool ) )
199     { 
200         foreach( v; data ) if( !isFinite(v) ) return false;
201         return true;
202     }
203 
204     const(T[]) opCast(E)() const if( is( E == T[] ) ) { return data; }
205 
206     const @property
207     {
208         static if( is( typeof( dot(selftype.init,selftype.init) ) ) )
209         {
210             auto len2() { return dot(this,this); }
211 
212             static if( is( typeof( sqrt(CommonType!(T,float)(this.len2)) ) ) )
213                 auto len(E=CommonType!(T,float))() { return sqrt( E(len2) ); }
214 
215             static if( is( typeof( this / len ) == typeof(this) ) )
216                 auto e() { return this / len; }
217         }
218     }
219 
220     static if( N == 2 )
221     {
222         auto rebase(I,J)( in I x, in J y ) const
223             if( isCompatibleVector!(2,T,I) &&
224                 isCompatibleVector!(2,T,J) )
225         {
226             alias this m;
227 
228             auto  d = x[0] * y[1] - y[0] * x[1];
229             auto rx = m[0] * y[1] - y[0] * m[1];
230             auto ry = x[0] * m[1] - m[0] * x[1];
231 
232             return selftype( rx/d, ry/d );
233         }
234     }
235 
236     static if( N == 3 )
237     {
238         auto rebase(I,J,K)( in I x, in J y, in K z ) const
239             if( isCompatibleVector!(3,T,I) &&
240                 isCompatibleVector!(3,T,J) &&
241                 isCompatibleVector!(3,T,K) )
242         {
243             alias this m;
244 
245             auto a1 =  (y[1] * z[2] - z[1] * y[2]);
246             auto a2 = -(x[1] * z[2] - z[1] * x[2]);
247             auto a3 =  (x[1] * y[2] - y[1] * x[2]);
248 
249             auto x2 = -(m[1] * z[2] - z[1] * m[2]);
250             auto x3 =  (m[1] * y[2] - y[1] * m[2]);
251 
252             auto y1 =  (m[1] * z[2] - z[1] * m[2]);
253             auto y3 =  (x[1] * m[2] - m[1] * x[2]);
254 
255             auto z1 =  (y[1] * m[2] - m[1] * y[2]);
256             auto z2 = -(x[1] * m[2] - m[1] * x[2]);
257 
258             auto ad1 = x[0] * a1;
259             auto ad2 = y[0] * a2;
260             auto ad3 = z[0] * a3;
261 
262             auto d  = x[0] * a1 + y[0] * a2 + z[0] * a3;
263 
264             auto nxd = m[0] * a1 + y[0] * x2 + z[0] * x3;
265             auto nyd = x[0] * y1 + m[0] * a2 + z[0] * y3;
266             auto nzd = x[0] * z1 + y[0] * z2 + m[0] * a3;
267 
268             return selftype( nxd/d, nyd/d, nzd/d );
269         }
270     }
271 
272     static if( AS.length > 0 )
273     {
274         @property
275         {
276             ref T opDispatch(string v)()
277                 if( oneOfAccess(AS,v,SEP) )
278             { mixin( format( "return data[%d];", getIndex(AS,v,SEP) ) ); }
279 
280             T opDispatch(string v)() const
281                 if( oneOfAccess(AS,v,SEP) )
282             { mixin( format( "return data[%d];", getIndex(AS,v,SEP) ) ); }
283         }
284 
285         static if( isOneSymbolPerFieldAccessString(AS,SEP) )
286         {
287             @property auto opDispatch(string v)() const
288                 if( v.length > 1 && oneOfAccessAll(AS,v,SEP) )
289             {
290                 mixin( format( `return Vector!(v.length,T,"%s")(%s);`,
291                             isCompatibleArrayAccessString(v.length,v)?v.split("").join(SEP):"",
292                             array( map!(a=>format( `data[%d]`,getIndex(AS,a,SEP)))(v.split("")) ).join(",")
293                             ));
294             }
295 
296             @property auto opDispatch( string v, U )( in U b )
297                 if( v.length > 1 && oneOfAccessAll(AS,v,SEP) && isCompatibleArrayAccessString(v.length,v) &&
298                         ( isCompatibleVector!(v.length,T,U) || ( isDynamicVector!U && is(typeof(T(U.datatype.init))) ) ) )
299             {
300                 static if( b.isDynamic ) enforce( v.length == b.length );
301                 foreach( i; 0 .. v.length ) data[getIndex(AS,""~v[i],SEP)] = T( b[i] );
302                 return opDispatch!v;
303             }
304         }
305     }
306 
307     /++ для кватернионов +/
308     static if( AS == "i j k a" )
309     {
310         static assert( isFloatingPoint!T, "quaterni must be floating point vector" );
311 
312         static selftype fromAngle(E,alias string bs)( T alpha, in Vector!(3,E,bs) axis )
313             if( isFloatingPoint!E )
314         { 
315             T a = alpha / cast(T)(2.0);
316             return selftype( axis * sin(a), cos(a) );
317         }
318 
319         /++ quaterni mul +/
320         auto quatMlt(E)( in Vector!(4,E,AS) b ) const
321             if( is( CommonType!(T,E) : T ) )
322         {
323             alias this a;
324             auto aijk = a.ijk;
325             auto bijk = b.ijk;
326             return selftype( cross(aijk, bijk) + aijk * b.a + bijk * a.a,
327                     a.a * b.a - dot(aijk, bijk) );
328         }
329 
330         auto rot(size_t K,E,alias string bs)( in Vector!(K,E,bs) b ) const
331             if( (K==0||K==3) && is( CommonType!(T,E) : T ) )
332         {
333             static if( K==0 ) enforce( b.length == 3, "wrong length" );
334             auto res = (this.quatMlt( selftype(b,0).quatMlt(inv) ));
335             return Vector!(K,T,bs)( res.ijk );
336         }
337 
338         const @property
339         {
340             T norm() { return dot(this,this); }
341             T mag() { return sqrt( norm ); }
342             auto con() { return selftype( -this.ijk, this.a ); }
343             auto inv() { return con / norm; }
344         }
345     }
346 }
347 
348 auto dot( size_t N, size_t K, T,E, alias string S1, alias string S2 )( in Vector!(N,T,S1) a, in Vector!(K,E,S2) b )
349     if( (N==K||K==0||N==0) && hasCompMltAndSum!(T,E) )
350 {
351     static if( a.isDynamic || b.isDynamic )
352     {
353         enforce( a.length == b.length, "wrong length" );
354         enforce( a.length > 0, "zero length" );
355     }
356     T ret = a[0] * b[0];
357     foreach( i; 1 .. a.length )
358         ret = ret + T( a[i] * b[i] );
359     return ret;
360 }
361 
362 auto cross( size_t N, size_t K, T,E, alias string S1, alias string S2 )( in Vector!(N,T,S1) a, in Vector!(K,E,S2) b )
363     if( ((K==3||K==0)&&(N==3||N==0)) && hasCompMltAndSum!(T,E) )
364 {
365     static if( a.isDynamic ) enforce( a.length == 3, "wrong length a" );
366     static if( b.isDynamic ) enforce( b.length == 3, "wrong length b" );
367 
368     a.selftype ret;
369     static if( a.isDynamic ) ret.length = 3;
370     ret[0] = T(a[1] * b[2]) - T(a[2] * b[1]);
371     ret[1] = T(a[2] * b[0]) - T(a[0] * b[2]);
372     ret[2] = T(a[0] * b[1]) - T(a[1] * b[0]);
373     return ret;
374 }
375 
376 alias Vector!(2,float,"x y") vec2;
377 alias Vector!(3,float,"x y z") vec3;
378 alias Vector!(4,float,"x y z w") vec4;
379 
380 alias Vector!(4,float,"i j k a") quat;
381 alias Vector!(4,double,"i j k a") dquat;
382 
383 alias Vector!(2,double,"x y") dvec2;
384 alias Vector!(3,double,"x y z") dvec3;
385 alias Vector!(4,double,"x y z w") dvec4;
386 
387 alias Vector!(2,int,"x y") ivec2;
388 alias Vector!(3,int,"x y z") ivec3;
389 alias Vector!(4,int,"x y z w") ivec4;
390 
391 alias Vector!(2,uint,"x y") uivec2;
392 alias Vector!(3,uint,"x y z") uivec3;
393 alias Vector!(4,uint,"x y z w") uivec4;
394 
395 alias Vector!(3,float,"r g b") col3;
396 alias Vector!(4,float,"r g b a") col4;
397 
398 alias Vector!(3,ubyte,"r g b") ubcol3;
399 alias Vector!(4,ubyte,"r g b a") ubcol4;
400 
401 alias Vector!(0,byte)   bvecD;
402 alias Vector!(0,ubyte) ubvecD;
403 alias Vector!(0,int)    ivecD;
404 alias Vector!(0,uint)  uivecD;
405 alias Vector!(0,long)   lvecD;
406 alias Vector!(0,ulong) ulvecD;
407 alias Vector!(0,float)   vecD;
408 alias Vector!(0,double) dvecD;
409 alias Vector!(0,real)   rvecD;
410 
411 unittest
412 {
413     static assert( isVector!vec2 );
414     static assert( isVector!vec3 );
415     static assert( isVector!vec4 );
416     static assert( isVector!quat );
417     static assert( isVector!dquat );
418     static assert( isVector!dvec2 );
419     static assert( isVector!dvec3 );
420     static assert( isVector!dvec4 );
421     static assert( isVector!ivec2 );
422     static assert( isVector!ivec3 );
423     static assert( isVector!ivec4 );
424     static assert( isVector!col3 );
425     static assert( isVector!col4 );
426     static assert( isVector!ubcol3 );
427     static assert( isVector!ubcol4 );
428     static assert( isVector!vecD );
429     static assert( isVector!ivecD );
430     static assert( isVector!dvecD );
431 }
432 
433 unittest
434 {
435     static assert( Vector!(3,float).isStatic == true );
436     static assert( Vector!(3,float).isDynamic == false );
437 
438     static assert( Vector!(0,float).isStatic == false );
439     static assert( Vector!(0,float).isDynamic == true );
440 
441     static assert( isVector!(Vector!(3,float)) );
442     static assert( isVector!(Vector!(0,float)) );
443 
444     static assert( !__traits(compiles,Vector!(3,float,"x y")) );
445     static assert( !__traits(compiles,Vector!(3,float,"x y")) );
446     static assert(  __traits(compiles,Vector!(3,float,"x y z")) );
447 
448     static assert( Vector!(3,float,"x y z").sizeof == float.sizeof * 3 );
449     static assert( Vector!(0,float).sizeof == (float[]).sizeof );
450 
451     static assert( Vector!(3,float,"x y z").length == 3 );
452 }
453 
454 unittest
455 {
456     assert( eq( Vector!(3,float)(1,2,3), [1,2,3] ) );
457 
458     auto a = Vector!(3,float)(1,2,3);
459     assert( eq( Vector!(5,int)(0,a,4), [0,1,2,3,4] ) );
460 
461     assert( mustExcept( { auto v = Vector!(2,int)(1,2,3); } ) );
462     assert( !mustExcept( { auto v = Vector!(0,int)(1,2,3); } ) );
463     assert( !mustExcept( { auto v = Vector!(3,int)(1); } ) );
464     auto b = Vector!(0,float)(1,2,3);
465     assert( b.length == 3 );
466     
467     auto c = Vector!(3,float)(1);
468     assert( eq( c, [1,1,1] ) );
469     auto d = c;
470     assert( eq( c, d ) );
471 }
472 
473 unittest
474 {
475     static struct Test1 { float x,y,z; }
476     static assert( !__traits(compiles,Vector!(3,float)(Test1.init)) );
477 
478     static struct Test2 { float[3] data; }
479     static assert( __traits(compiles,Vector!(3,float)(Test2.init)) );
480 }
481 
482 unittest
483 {
484     auto a = vec3(1,2,3);
485 
486     auto a1 = const vec3(a);
487     auto a2 = const vec3(1,2,3);
488     auto a3 = const vec3(1);
489 
490     auto a4 = shared vec3(a);
491     auto a5 = shared vec3(1,2,3);
492     auto a6 = shared vec3(1);
493 
494     auto a7 = immutable vec3(a);
495     auto a8 = immutable vec3(1,2,3);
496     auto a9 = immutable vec3(1);
497 
498     auto a10 = shared const vec3(a);
499     auto a11 = shared const vec3(1,2,3);
500     auto a12 = shared const vec3(1);
501 
502     assert( eq( a, a1 ) );
503     assert( eq( a, a4 ) );
504     assert( eq( a, a7 ) );
505     assert( eq( a, a10 ) );
506 
507     a = vec3(a4.data);
508 }
509 
510 unittest
511 {
512     auto a = vec3(2);
513     assert( eq( -a, [-2,-2,-2] ) );
514 }
515 
516 unittest
517 {
518     auto a = vec3(1,2,3);
519     auto b = vecD(1,2,3);
520     auto c = a + b;
521     assert( is( typeof(c) == vec3 ) );
522     auto d = b + a;
523     assert( is( typeof(d) == vecD ) );
524     assert( eq(c,d) );
525     auto f = ivec3(1,2,3);
526     auto c1 = a + f;
527     assert( is( typeof(c1) == vec3 ) );
528     auto d1 = ivec3(f) + ivec3(a);
529     assert( is( typeof(d1) == ivec3 ) );
530     assert( eq(c1,d) );
531     assert( eq(c,d1) );
532 
533     a *= 2;
534     b *= 2;
535     auto e = b *= 2;
536     assert( eq(a,[2,4,6]) );
537     assert( eq(b,a*2) );
538 
539     auto x = 2 * a;
540     assert( eq(x,[4,8,12]) );
541 
542     assert( !!x );
543     x[0] = float.nan;
544     assert( !x );
545 }
546 
547 unittest
548 {
549     auto a = vecD(1,2,3);
550 
551     auto b = vec3(a);
552     auto c = vecD(b);
553 
554     assert( eq( a, b ) );
555     assert( eq( a, c ) );
556 }
557 
558 unittest
559 {
560     auto a = vec3(1,2,3);
561     auto b = vecD(1,2,3);
562 
563     assert( eq( dot(a,b), 1+4+9 ) );
564 }
565 
566 unittest
567 {
568     auto x = vec3(1,0,0);
569     auto y = vecD(0,1,0);
570     auto z = vecD(0,0,1);
571 
572     assert( eq( cross(x,y), z ) );
573     assert( eq( cross(y,z), x ) );
574     assert( eq( cross(y,x), -z ) );
575     assert( eq( cross(x,z), -y ) );
576     assert( eq( cross(z,x), y ) );
577 
578     auto fy = vecD(0,1,0,0);
579     assert( mustExcept({ auto fz = x * fy; }) );
580     auto cfy = vec4(0,1,0,0);
581     static assert( !__traits(compiles,x*cfy) );
582 }
583 
584 unittest
585 {
586     auto a = vec3(2,2,1);
587     assert( eq(a.rebase(vec3(2,0,0),vec3(0,2,0),vec3(0,0,2)), [1,1,.5] ) );
588 }
589 
590 unittest
591 {
592     auto a = vec3(1,2,3);
593 
594     assert( a.opDispatch!"x" == 1 );
595     assert( a.y == 2 );
596     assert( a.z == 3 );
597 
598     a.x = 2;
599     assert( a.x == 2 );
600 }
601 
602 unittest
603 {
604     alias Vector!(4,float,"x y dx dy") vec2p;
605 
606     auto a = vec2p(1,2,0,0);
607 
608     assert( a.opDispatch!"x" == 1 );
609     assert( a.dx == 0 );
610 }
611 
612 unittest
613 {
614     auto a = vec3(1,2,3);
615 
616     auto b = a.opDispatch!"xy";
617     auto c = a.xx;
618     auto d = a.xxxyyzyx;
619 
620     static assert( is(typeof(b) == Vector!(2,float,"x y") ) );
621     static assert( is(typeof(c) == Vector!(2,float) ) );
622     static assert( is(typeof(d) == Vector!(8,float) ) );
623 
624     assert( eq( b, [1,2] ) );
625     assert( eq( c, [1,1] ) );
626     assert( eq( d, [1,1,1,2,2,3,2,1] ) );
627 }
628 
629 unittest
630 {
631     auto a = vec3(1,2,3);
632     auto b = dvec4(4,5,6,7);
633     auto c = vecD( 9, 10 );
634     a.xz = b.yw;
635     assert( eq( a, [5,2,7] ) );
636     a.zy = c;
637     assert( eq( a, [5,10,9] ) );
638     static assert( !__traits(compiles, a.xy=vec3(1,2,3)) );
639     static assert( !__traits(compiles, a.xx=vec2(1,2)) );
640     auto d = a.zxy = b.wyx;
641     static assert( d.access_string == "z x y" );
642     static assert( is( d.datatype == float ) );
643     assert( eq( d, [ 7,5,4 ] ) );
644     assert( eq( a, [ 5,4,7 ] ) );
645     a.yzx = a.zxz;
646     assert( eq( a, [ 7,7,5 ] ) );
647 }
648 
649 unittest
650 {
651     auto r = quat.fromAngle( PI_2, vec3(0,0,1) );
652 
653     auto a = vec3( 1,0,0 );
654     auto b = r.rot(a);
655     assert( is( typeof(b) == vec3 ) );
656     assert( eq( b.data, [ 0, 1, 0 ] ) );
657 }
658 
659 unittest
660 {
661     auto a = vec3(1,2,3);
662     auto b = ivec3(1,2,3);
663     auto k = a.len2;
664     assert( is( typeof(k) == float ) );
665 
666     auto l = b.len2;
667     assert( is( typeof(l) == int ) );
668 
669     auto m = b.len;
670     assert( is( typeof(m) == float ) );
671 
672     auto n = b.len!real;
673     assert( is( typeof(n) == real ) );
674 
675     assert( is( typeof( vec3( 1, 2, 3 ).e ) == vec3 ) );
676     assert( abs( a.e.len - 1 ) < float.epsilon );
677 }
678 
679 unittest
680 {
681     alias Vector!(3,cfloat) cvec3;
682 
683     auto a = cvec3( 1-1i, 2, 3i );
684     static assert( __traits(compiles, a.e) );
685     assert( !mustExcept({ auto k = a.e; }) );
686 }
687 
688 unittest
689 {
690     alias Vector!(3,Vector!(3,float)) mat3;
691     auto a = mat3( vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) );
692 
693     a *= 2;
694     a += a;
695 
696     assert( a[0][0] == 4 );
697     assert( a[1][1] == 4 );
698     assert( a[2][2] == 4 );
699 
700     assert( a[0][1] == 0 );
701     assert( a[1][2] == 0 );
702     assert( a[2][1] == 0 );
703 
704     a ^^= 2;
705 
706     assert( a[0][0] == 16 );
707     assert( a[1][1] == 16 );
708     assert( a[2][2] == 16 );
709 
710     auto b = -a;
711 
712     assert( b[0][0] == -16 );
713     assert( b[1][1] == -16 );
714     assert( b[2][2] == -16 );
715 }
716 
717 unittest
718 {
719     auto a = vecD(1,2,3);
720     auto b = a;
721     assert( eq(a,b) );
722     b[0] = 111;
723     assert( !eq(a,b) );
724 
725     vecD c;
726     c = b;
727     assert( eq(c,b) );
728     b[0] = 222;
729     assert( !eq(c,b) );
730 }
731 
732 unittest
733 {
734     auto a = vec3(1,2,3);
735     auto b = a;
736     assert( eq(a,b) );
737     b[0] = 111;
738     assert( !eq(a,b) );
739 }