1 /+
2  + License:
3  + The MIT License (MIT)
4  + 
5  +     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
6  + 
7  +     Permission is hereby granted, free of charge, to any person obtaining a copy
8  +     of this software and associated documentation files (the "Software"), to deal
9  +     in the Software without restriction, including without limitation the rights
10  +     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  +     copies of the Software, and to permit persons to whom the Software is
12  +     furnished to do so, subject to the following conditions:
13  + 
14  +     The above copyright notice and this permission notice shall be included in
15  +     all copies or substantial portions of the Software.
16  + 
17  +     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  +     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  +     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  +     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  +     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  +     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  +     THE SOFTWARE.
24  +/
25 
26 /++
27     Provides work with vector and some aliases and functions.
28 +/
29 
30 module des.math.linear.vector;
31 
32 import std.math;
33 import std.algorithm;
34 import std.array;
35 import std.traits;
36 import std.exception;
37 import std.string;
38 
39 import des.util.testsuite;
40 
41 import des.math.util;
42 
43 version(unittest) import std.stdio;
44 
45 /// checks type is vector
46 template isVector(E)
47 {
48     enum isVector = is( typeof(impl(E.init)) );
49     void impl(size_t N,T,alias string AS)( in Vector!(N,T,AS) ) {}
50 }
51 
52 /// checks type is static vector
53 template isStaticVector(E)
54 {
55     static if( !isVector!E )
56         enum isStaticVector = false;
57     else enum isStaticVector = E.isStatic;
58 }
59 
60 /// checks type is dynamic vector
61 template isDynamicVector(E)
62 {
63     static if( !isVector!E )
64         enum isDynamicVector = false;
65     else enum isDynamicVector = E.isDynamic;
66 }
67 
68 unittest
69 {
70     static assert( !isStaticVector!float );
71     static assert( !isDynamicVector!float );
72 }
73 
74 /// сhecks type E is vector and E.dims == N and E.datatype can casted to T
75 template isCompatibleVector(size_t N,T,E)
76 {
77     static if( !isVector!E )
78         enum isCompatibleVector = false;
79     else enum isCompatibleVector = E.dims == N && is( E.datatype : T );
80 }
81 
82 /++
83     validate operation between types `T` and `E`
84 code:
85     mixin( `return is( typeof( T.init ` ~ op ~ ` E.init ) : K );` );
86  +/
87 bool isValidOp(string op,T,E,K=T)() pure
88 { mixin( `return is( typeof( T.init ` ~ op ~ ` E.init ) : K );` ); }
89 
90 bool hasCompMltAndSum(T,E)() pure
91 { return is( typeof( T(T.init * E.init) ) ) && is( typeof( T.init + T.init ) == T ); }
92 
93 /// vector value access string element separator = `" "`
94 enum VVASES = " ";
95 /// vector value access string variant separator = `"|"`
96 enum VVASVS = "|";
97 
98 private @property string zerosVectorData(size_t N)()
99 {
100     string[] ret;
101     foreach( j; 0 .. N )
102         ret ~= format( "%d", 0 );
103     return "[" ~ ret.join(",") ~ "]";
104 }
105 
106 /++
107 Params:
108     N   = Number of dimmensions. 
109           Vector can be dynamic (`N==0`) or static (`N>0`).
110     T   = Vector data type.
111     AS  = Access string.
112           Must be valid access string ( see: [isCompatibleArrayAccessStrings](des/math/util/accessstring/isCompatibleArrayAccessStrings.html) )
113 +/
114 struct Vector( size_t N, T, alias string AS="")
115     if( isCompatibleArrayAccessStrings(N,AS,VVASES,VVASVS) || AS.length == 0 )
116 {
117     /// `N == 0`
118     enum bool isDynamic = N == 0;
119     /// `N != 0`
120     enum bool isStatic = N != 0;
121     /// equals N
122     enum size_t dims = N;
123 
124     /// 
125     static if( isStatic ) 
126     {
127         static if( !isNumeric!T )
128             /// if `isStatic` ( fills by zeros if `isNumeric!T` )
129             T[N] data;
130         else T[N] data = mixin( zerosVectorData!N );
131     }
132     else T[] data; /// if `isDynamic`
133 
134     ///
135     alias data this;
136 
137     ///
138     alias datatype = T;
139 
140     ///
141     alias access_string = AS;
142 
143     ///
144     alias selftype = Vector!(N,T,AS);
145 
146 pure:
147 
148     static if( isDynamic )
149     {
150         /++
151             Length of elements. 
152             Enum, if vector is static.
153          +/
154         pure @property auto length() const { return data.length; }
155 
156         ///ditto
157         pure @property auto length( size_t nl )
158         {
159             data.length = nl;
160             return data.length;
161         }
162     }
163     else enum length = N;
164 
165     /++
166      + Vector can be constructed with different ways:
167 
168      + * from single values
169      + * from arrays
170      + * from other vectors
171      +/
172     this(E...)( in E vals )
173     {
174         // not work with dynamic vectors
175         static assert( is(typeof(flatData!T(vals))), "args not compatible" );
176 
177         static if( isStatic )
178         {
179             static if( hasNoDynamic!E && E.length > 1 )
180             {
181                 static assert( getElemCount!E == N * getElemCount!T, "wrong args count" );
182                 static assert( isConvertable!(T,E), "wrong args type" );
183                 mixin( vectorStaticFill!("T","data","vals",T,E) );
184             }
185             else
186             {
187                 auto buf = flatData!T(vals);
188 
189                 if( buf.length == length )
190                     data[] = buf[];
191                 else if( buf.length == 1 )
192                     data[] = buf[0];
193                 else enforce( false, "bad args length" );
194             }
195         }
196         else data = flatData!T(vals);
197     }
198 
199     static if( isDynamic ) this(this) { data = this.data.dup; }
200 
201     ///
202     auto opAssign( size_t K, E, alias string oas )( in Vector!(K,E,oas) b )
203         if( (K==N||K==0||N==0) && is( typeof(T(E.init)) ) )
204     {
205         static if( isDynamic ) length = b.length;
206         foreach( i; 0 .. length ) data[i] = T(b[i]);
207         return this;
208     }
209 
210     /// 
211     auto opUnary(string op)() const
212         if( op == "-" && is( typeof( T.init * (-1) ) : T ) )
213     {
214         selftype ret;
215         static if( isDynamic ) ret.length = length;
216         foreach( i; 0 .. length )
217             ret[i] = this[i] * -1;
218         return ret;
219     }
220 
221     /++
222      + Any binary operations execs per element
223      + See_Also:
224      + [isValidOp](des/math/linear/vector/isValidOp.html)
225      +/
226     auto opBinary(string op, size_t K, E, alias string oas )
227         ( in Vector!(K,E,oas) b ) const
228         if( isValidOp!(op,T,E) && (K==N||K==0||N==0) )
229     {
230         selftype ret;
231         static if( isDynamic || b.isDynamic )
232             enforce( length == b.length, "wrong length" );
233         static if( isDynamic ) ret.length = length;
234         foreach( i; 0 .. length )
235             mixin( `ret[i] = this[i] ` ~ op ~ ` b[i];` );
236         return ret;
237     }
238 
239     /// ditto
240     auto opBinary(string op, E)( in E b ) const
241         if( isValidOp!(op,T,E) && op != "+" && op != "-" )
242     {
243         selftype ret;
244         static if( isDynamic ) ret.length = length;
245         foreach( i; 0 .. length )
246             mixin( `ret[i] = this[i] ` ~ op ~ ` b;` );
247         return ret;
248     }
249 
250     /// ditto
251     auto opOpAssign(string op, E)( in E b )
252         if( mixin( `is( typeof( this ` ~ op ~ ` b ) )` ) )
253     { mixin( `return this = this ` ~ op ~ ` b;` ); }
254 
255     /// only mul allowed
256     auto opBinaryRight(string op, E)( in E b ) const
257         if( isValidOp!(op,E,T,T) && op == "*" )
258     { mixin( "return this " ~ op ~ " b;" ); }
259 
260     /// checks all elements is finite
261     bool opCast(E)() const if( is( E == bool ) )
262     { return all!isFinite( data.dup ); }
263 
264     const @property
265     {
266         static if( is( typeof( dot(selftype.init,selftype.init) ) ) )
267         {
268             /++ Square of euclidean length of the vector.
269 
270                 only:
271                 if( is( typeof( dot(selftype.init,selftype.init) ) ) )
272             +/
273             auto len2() { return dot(this,this); }
274 
275             static if( is( typeof( sqrt(CommonType!(T,float)(this.len2)) ) ) )
276             {
277                 /++ Euclidean length of the vector
278 
279                     only:
280                     if( is( typeof( sqrt(CommonType!(T,float)(this.len2)) ) ) )
281                 +/
282                 auto len(E=CommonType!(T,float))() { return sqrt( E(len2) ); }
283             }
284 
285             static if( is( typeof( this / len ) == typeof(this) ) )
286             {
287                 /++ Unit length of the vector
288 
289                     only:
290                     if( is( typeof( this / len ) == typeof(this) ) )
291                 +/
292                 auto e() { return this / len; }
293             }
294         }
295     }
296 
297     version(todos) pragma(msg, __FILE__, "TODO: rework rebase for all dimension vectors" );
298 
299     static if( N == 2 )
300     {
301         auto rebase(I,J)( in I x, in J y ) const
302             if( isCompatibleVector!(2,T,I) &&
303                 isCompatibleVector!(2,T,J) )
304         {
305             alias this m;
306 
307             auto  d = x[0] * y[1] - y[0] * x[1];
308             auto rx = m[0] * y[1] - y[0] * m[1];
309             auto ry = x[0] * m[1] - m[0] * x[1];
310 
311             return selftype( rx/d, ry/d );
312         }
313     }
314 
315     static if( N == 3 )
316     {
317         auto rebase(I,J,K)( in I x, in J y, in K z ) const
318             if( isCompatibleVector!(3,T,I) &&
319                 isCompatibleVector!(3,T,J) &&
320                 isCompatibleVector!(3,T,K) )
321         {
322             alias this m;
323 
324             auto a1 =  (y[1] * z[2] - z[1] * y[2]);
325             auto a2 = -(x[1] * z[2] - z[1] * x[2]);
326             auto a3 =  (x[1] * y[2] - y[1] * x[2]);
327 
328             auto x2 = -(m[1] * z[2] - z[1] * m[2]);
329             auto x3 =  (m[1] * y[2] - y[1] * m[2]);
330 
331             auto y1 =  (m[1] * z[2] - z[1] * m[2]);
332             auto y3 =  (x[1] * m[2] - m[1] * x[2]);
333 
334             auto z1 =  (y[1] * m[2] - m[1] * y[2]);
335             auto z2 = -(x[1] * m[2] - m[1] * x[2]);
336 
337             auto ad1 = x[0] * a1;
338             auto ad2 = y[0] * a2;
339             auto ad3 = z[0] * a3;
340 
341             auto d  = x[0] * a1 + y[0] * a2 + z[0] * a3;
342 
343             auto nxd = m[0] * a1 + y[0] * x2 + z[0] * x3;
344             auto nyd = x[0] * y1 + m[0] * a2 + z[0] * y3;
345             auto nzd = x[0] * z1 + y[0] * z2 + m[0] * a3;
346 
347             return selftype( nxd/d, nyd/d, nzd/d );
348         }
349     }
350 
351     static if( AS.length > 0 )
352     {
353         @property
354         {
355             /++
356                 Get/set element data by access string.
357 
358                 only:
359                 if( AS.length > 0 )
360             +/
361             ref T opDispatch(string v)()
362                 if( getIndex(AS,v,VVASES,VVASVS) != -1 )
363             { mixin( format( "return data[%d];", getIndex(AS,v,VVASES,VVASVS) ) ); }
364 
365             ///ditto
366             T opDispatch(string v)() const
367                 if( getIndex(AS,v,VVASES,VVASVS) != -1 )
368             { mixin( format( "return data[%d];", getIndex(AS,v,VVASES,VVASVS) ) ); }
369 
370             static if( isOneSymbolPerFieldForAnyAccessString(AS,VVASES,VVASVS) )
371             {
372                 /++
373                     Get/set vector by access string.
374 
375                     only:
376                     if( AS.length > 0 && isOneSymbolPerFieldForAnyAccessString(AS,VVASES,VVASVS) )
377                 +/
378                 auto opDispatch(string v)() const
379                     if( v.length > 1 && oneOfAnyAccessAll(AS,v,VVASES,VVASVS) )
380                 {
381                     mixin( format( `return Vector!(v.length,T,"%s")(%s);`,
382                                 isCompatibleArrayAccessString(v.length,v)?v.split("").join(VVASES):"",
383                                 array( map!(a=>format( `data[%d]`,getIndex(AS,a,VVASES,VVASVS)))(v.split("")) ).join(",")
384                                 ));
385                 }
386 
387                 ///ditto
388                 auto opDispatch( string v, U )( in U b )
389                     if( v.length > 1 && oneOfAnyAccessAll(AS,v,VVASES,VVASVS) && isCompatibleArrayAccessString(v.length,v) &&
390                             ( isCompatibleVector!(v.length,T,U) || ( isDynamicVector!U && is(typeof(T(U.datatype.init))) ) ) )
391                 {
392                     static if( b.isDynamic ) enforce( v.length == b.length );
393                     foreach( i; 0 .. v.length )
394                         data[getIndex(AS,""~v[i],VVASES,VVASVS)] = T( b[i] );
395                     return opDispatch!v;
396                 }
397             }
398         }
399     }
400 }
401 
402 /// `"x y|u v"`
403 private enum string AS2D = "x y|w h|u v";
404 /// `"x y z|u v t|r g b"`
405 private enum string AS3D = "x y z|u v t|r g b";
406 /// `"x y z w|r g b a"`
407 private enum string AS4D = "x y z w|r g b a";
408 
409 ///
410 alias Vector2(T) = Vector!(2,T,AS2D);
411 ///
412 alias Vector3(T) = Vector!(3,T,AS3D);
413 ///
414 alias Vector4(T) = Vector!(4,T,AS4D);
415 
416 ///
417 alias Vector2!float vec2;
418 ///
419 alias Vector3!float vec3;
420 ///
421 alias Vector4!float vec4;
422 
423 ///
424 alias Vector2!double dvec2;
425 ///
426 alias Vector3!double dvec3;
427 ///
428 alias Vector4!double dvec4;
429 
430 ///
431 alias Vector2!real rvec2;
432 ///
433 alias Vector3!real rvec3;
434 ///
435 alias Vector4!real rvec4;
436 
437 ///
438 alias Vector2!byte bvec2;
439 ///
440 alias Vector3!byte bvec3;
441 ///
442 alias Vector4!byte bvec4;
443 
444 ///
445 alias Vector2!ubyte ubvec2;
446 ///
447 alias Vector3!ubyte ubvec3;
448 ///
449 alias Vector4!ubyte ubvec4;
450 
451 ///
452 alias Vector2!int ivec2;
453 ///
454 alias Vector3!int ivec3;
455 ///
456 alias Vector4!int ivec4;
457 
458 ///
459 alias Vector2!uint uivec2;
460 ///
461 alias Vector3!uint uivec3;
462 ///
463 alias Vector4!uint uivec4;
464 
465 ///
466 alias Vector2!long lvec2;
467 ///
468 alias Vector3!long lvec3;
469 ///
470 alias Vector4!long lvec4;
471 
472 ///
473 alias Vector2!ulong ulvec2;
474 ///
475 alias Vector3!ulong ulvec3;
476 ///
477 alias Vector4!ulong ulvec4;
478 
479 ///
480 alias Vector!(3,float,"r g b") col3;
481 ///
482 alias Vector!(4,float,"r g b a") col4;
483 
484 ///
485 alias Vector!(3,ubyte,"r g b") ubcol3;
486 ///
487 alias Vector!(4,ubyte,"r g b a") ubcol4;
488 
489 unittest
490 {
491     static assert( is( Vector2!float == vec2 ) );
492     static assert( is( Vector3!real == rvec3 ) );
493     static assert( is( Vector4!float == vec4 ) );
494 }
495 
496 ///
497 alias Vector!(0,byte)   bvecD;
498 ///
499 alias Vector!(0,ubyte) ubvecD;
500 ///
501 alias Vector!(0,int)    ivecD;
502 ///
503 alias Vector!(0,uint)  uivecD;
504 ///
505 alias Vector!(0,short)  svecD;
506 ///
507 alias Vector!(0,ushort)usvecD;
508 ///
509 alias Vector!(0,long)   lvecD;
510 ///
511 alias Vector!(0,ulong) ulvecD;
512 ///
513 alias Vector!(0,float)   vecD;
514 ///
515 alias Vector!(0,double) dvecD;
516 ///
517 alias Vector!(0,real)   rvecD;
518 
519 unittest
520 {
521     static assert(  isVector!vec2 );
522     static assert(  isVector!vec3 );
523     static assert(  isVector!vec4 );
524     static assert(  isVector!dvec2 );
525     static assert(  isVector!dvec3 );
526     static assert(  isVector!dvec4 );
527     static assert(  isVector!ivec2 );
528     static assert(  isVector!ivec3 );
529     static assert(  isVector!ivec4 );
530     static assert(  isVector!col3 );
531     static assert(  isVector!col4 );
532     static assert(  isVector!ubcol3 );
533     static assert(  isVector!ubcol4 );
534     static assert(  isVector!vecD );
535     static assert(  isVector!ivecD );
536     static assert(  isVector!dvecD );
537 
538     static assert(  isCompatibleVector!(2,float,vec2) );
539     static assert(  isCompatibleVector!(3,float,vec3) );
540     static assert(  isCompatibleVector!(4,float,vec4) );
541     static assert(  isCompatibleVector!(2,double,dvec2) );
542     static assert(  isCompatibleVector!(3,double,dvec3) );
543     static assert(  isCompatibleVector!(4,double,dvec4) );
544     static assert(  isCompatibleVector!(2,int,ivec2) );
545     static assert(  isCompatibleVector!(3,int,ivec3) );
546     static assert(  isCompatibleVector!(4,int,ivec4) );
547     static assert(  isCompatibleVector!(3,float,col3) );
548     static assert(  isCompatibleVector!(4,float,col4) );
549     static assert(  isCompatibleVector!(3,ubyte,ubcol3) );
550     static assert(  isCompatibleVector!(4,ubyte,ubcol4) );
551     static assert( !isCompatibleVector!(2,ubyte,ubcol3) );
552     static assert( !isCompatibleVector!(3,ubyte,ubcol4) );
553 }
554 
555 ///
556 unittest
557 {
558     static assert( Vector!(3,float).isStatic == true );
559     static assert( Vector!(3,float).isDynamic == false );
560 
561     static assert( Vector!(0,float).isStatic == false );
562     static assert( Vector!(0,float).isDynamic == true );
563 
564     static assert( isVector!(Vector!(3,float)) );
565     static assert( isVector!(Vector!(0,float)) );
566 
567     static assert( !__traits(compiles,Vector!(3,float,"x y")) );
568     static assert( !__traits(compiles,Vector!(3,float,"x y")) );
569     static assert(  __traits(compiles,Vector!(3,float,"x y z")) );
570 
571     static assert( Vector!(3,float,"x y z").sizeof == float.sizeof * 3 );
572     static assert( Vector!(0,float).sizeof == (float[]).sizeof );
573 
574     static assert( Vector!(3,float,"x y z").length == 3 );
575 }
576 
577 ///
578 unittest
579 {
580     assert( eq( Vector!(3,float)(1,2,3), [1,2,3] ) );
581 
582     auto a = Vector!(3,float)(1,2,3);
583     assert( eq( Vector!(5,int)(0,a,4), [0,1,2,3,4] ) );
584 
585     static assert( !__traits(compiles, { auto v = Vector!(2,int)(1,2,3); } ) );
586     assert( !mustExcept( { auto v = Vector!(0,int)(1,2,3); } ) );
587     assert( !mustExcept( { auto v = Vector!(3,int)(1); } ) );
588     auto b = Vector!(0,float)(1,2,3);
589     assert( b.length == 3 );
590     
591     auto c = Vector!(3,float)(1);
592     assert( eq( c, [1,1,1] ) );
593     auto d = c;
594     assert( eq( c, d ) );
595 }
596 
597 ///
598 unittest
599 {
600     static struct Test1 { float x,y,z; }
601     static assert( !__traits(compiles,Vector!(3,float)(Test1.init)) );
602 
603     static struct Test2 { float[3] data; }
604     static assert( __traits(compiles,Vector!(3,float)(Test2.init)) );
605 }
606 
607 unittest
608 {
609     auto a = vec3(1,2,3);
610 
611     auto a1 = const vec3(a);
612     auto a2 = const vec3(1,2,3);
613     auto a3 = const vec3(1);
614 
615     auto a4 = shared vec3(a);
616     auto a5 = shared vec3(1,2,3);
617     auto a6 = shared vec3(1);
618 
619     auto a7 = immutable vec3(a);
620     auto a8 = immutable vec3(1,2,3);
621     auto a9 = immutable vec3(1);
622 
623     auto a10 = shared const vec3(a);
624     auto a11 = shared const vec3(1,2,3);
625     auto a12 = shared const vec3(1);
626 
627     assert( eq( a, a1 ) );
628     assert( eq( a, a4 ) );
629     assert( eq( a, a7 ) );
630     assert( eq( a, a10 ) );
631 
632     a = vec3(a4.data);
633 }
634 
635 /// convert vectors
636 unittest
637 {
638     auto a = ivec2(1,2);
639     auto b = vec2(a);
640     assert( eq( a, b ) );
641     auto c = ivec2(b);
642     assert( eq( a, c ) );
643 }
644 
645 unittest
646 {
647     auto a = vec3(2);
648     assert( eq( -a, [-2,-2,-2] ) );
649 }
650 
651 ///
652 unittest
653 {
654     auto a = Vector!(3,int,"x y z|u v t|r g b")(1,2,3);
655     assert( a.x == a.r );
656     assert( a.y == a.g );
657     assert( a.z == a.b );
658     assert( a.x == a.u );
659     assert( a.y == a.v );
660     assert( a.z == a.t );
661 
662     auto b = Vector!(2,int,"near far|n f")(1,100);
663     assert( b.near == b.n );
664     assert( b.far  == b.f );
665 
666     b.nf = ivec2( 10,20 );
667     assert( b.near == 10 );
668     assert( b.far == 20 );
669 }
670 
671 ///
672 unittest
673 {
674     auto a = vec3(1,2,3);
675     auto b = vecD(1,2,3);
676     auto c = a + b;
677     assert( is( typeof(c) == vec3 ) );
678     auto d = b + a;
679     assert( is( typeof(d) == vecD ) );
680     assert( eq(c,d) );
681     auto f = ivec3(1,2,3);
682     auto c1 = a + f;
683     assert( is( typeof(c1) == vec3 ) );
684     auto d1 = ivec3(f) + ivec3(a);
685     assert( is( typeof(d1) == ivec3 ) );
686     assert( eq(c1,d) );
687     assert( eq(c,d1) );
688 
689     a *= 2;
690     b *= 2;
691     auto e = b *= 2;
692     assert( eq(a,[2,4,6]) );
693     assert( eq(b,a*2) );
694 
695     auto x = 2 * a;
696     assert( eq(x,[4,8,12]) );
697 
698     assert( !!x );
699     x[0] = float.nan;
700     assert( !x );
701 }
702 
703 ///
704 unittest
705 {
706     auto a = vecD(1,2,3);
707 
708     auto b = vec3(a);
709     auto c = vecD(b);
710 
711     assert( eq( a, b ) );
712     assert( eq( a, c ) );
713 }
714 ///
715 unittest
716 {
717     auto a = vec3(2,2,1);
718     assert( eq(a.rebase(vec3(2,0,0),vec3(0,2,0),vec3(0,0,2)), [1,1,.5] ) );
719 }
720 
721 ///
722 unittest
723 {
724     auto a = vec3(1,2,3);
725 
726     assert( a.opDispatch!"x" == 1 );
727     assert( a.y == 2 );
728     assert( a.z == 3 );
729 
730     a.x = 2;
731     assert( a.x == 2 );
732 }
733 
734 ///
735 unittest
736 {
737     alias Vector!(4,float,"x y dx dy") vec2p;
738 
739     auto a = vec2p(1,2,0,0);
740 
741     assert( a.opDispatch!"x" == 1 );
742     assert( a.dx == 0 );
743 }
744 
745 ///
746 unittest
747 {
748     auto a = vec3(1,2,3);
749 
750     auto b = a.opDispatch!"xy";
751     auto c = a.xx;
752     auto d = a.xxxyyzyx;
753 
754     static assert( is(typeof(b) == Vector!(2,float,"x y") ) );
755     static assert( is(typeof(c) == Vector!(2,float) ) );
756     static assert( is(typeof(d) == Vector!(8,float) ) );
757 
758     assert( eq( b, [1,2] ) );
759     assert( eq( c, [1,1] ) );
760     assert( eq( d, [1,1,1,2,2,3,2,1] ) );
761 }
762 
763 ///
764 unittest
765 {
766     auto a = vec3(1,2,3);
767     auto b = dvec4(4,5,6,7);
768     auto c = vecD( 9, 10 );
769     a.xz = b.yw;
770     assert( eq( a, [5,2,7] ) );
771     a.zy = c;
772     assert( eq( a, [5,10,9] ) );
773     static assert( !__traits(compiles, a.xy=vec3(1,2,3)) );
774     static assert( !__traits(compiles, a.xx=vec2(1,2)) );
775     auto d = a.zxy = b.wyx;
776     static assert( d.access_string == "z x y" );
777     static assert( is( d.datatype == float ) );
778     assert( eq( d, [ 7,5,4 ] ) );
779     assert( eq( a, [ 5,4,7 ] ) );
780     a.yzx = a.zxz;
781     assert( eq( a, [ 7,7,5 ] ) );
782 }
783 
784 ///
785 unittest
786 {
787     auto a = vec3(1,2,3);
788     auto b = ivec3(1,2,3);
789     auto k = a.len2;
790     assert( is( typeof(k) == float ) );
791 
792     auto l = b.len2;
793     assert( is( typeof(l) == int ) );
794 
795     auto m = b.len;
796     assert( is( typeof(m) == float ) );
797 
798     auto n = b.len!real;
799     assert( is( typeof(n) == real ) );
800 
801     assert( is( typeof( vec3( 1, 2, 3 ).e ) == vec3 ) );
802     assert( abs( a.e.len - 1 ) < float.epsilon );
803 }
804 
805 ///
806 unittest
807 {
808     alias Vector!(3,cfloat) cvec3;
809 
810     auto a = cvec3( 1-1i, 2, 3i );
811     static assert( __traits(compiles, a.e) );
812     assert( !mustExcept({ auto k = a.e; }) );
813 }
814 
815 ///
816 unittest
817 {
818     alias Vector!(3,Vector!(3,float)) mat3;
819     auto a = mat3( vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) );
820 
821     a *= 2;
822     a += a;
823 
824     assert( a[0][0] == 4 );
825     assert( a[1][1] == 4 );
826     assert( a[2][2] == 4 );
827 
828     assert( a[0][1] == 0 );
829     assert( a[1][2] == 0 );
830     assert( a[2][1] == 0 );
831 
832     a ^^= 2;
833 
834     assert( a[0][0] == 16 );
835     assert( a[1][1] == 16 );
836     assert( a[2][2] == 16 );
837 
838     auto b = -a;
839 
840     assert( b[0][0] == -16 );
841     assert( b[1][1] == -16 );
842     assert( b[2][2] == -16 );
843 }
844 
845 unittest
846 {
847     auto a = vecD(1,2,3);
848     auto b = a;
849     assert( eq(a,b) );
850     b[0] = 111;
851     assert( !eq(a,b) );
852 
853     vecD c;
854     c = b;
855     assert( eq(c,b) );
856     b[0] = 222;
857     assert( !eq(c,b) );
858 }
859 
860 unittest
861 {
862     auto a = vec3(1,2,3);
863     auto b = a;
864     assert( eq(a,b) );
865     b[0] = 111;
866     assert( !eq(a,b) );
867 }
868 
869 /// dot multiplication for compaitable vectors.
870 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 )
871     if( (N==K||K==0||N==0) && hasCompMltAndSum!(T,E) )
872 {
873     static if( a.isDynamic || b.isDynamic )
874     {
875         enforce( a.length == b.length, "wrong length" );
876         enforce( a.length > 0, "zero length" );
877     }
878     T ret = a[0] * b[0];
879     foreach( i; 1 .. a.length )
880         ret = ret + T( a[i] * b[i] );
881     return ret;
882 }
883 
884 ///
885 unittest
886 {
887     auto a = vec3(1,2,3);
888     auto b = vecD(1,2,3);
889 
890     assert( eq( dot(a,b), 1+4+9 ) );
891 }
892 
893 /// cross multiplication for compaitable vectors.
894 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 )
895     if( ((K==3||K==0)&&(N==3||N==0)) && hasCompMltAndSum!(T,E) )
896 {
897     static if( a.isDynamic ) enforce( a.length == 3, "wrong length a" );
898     static if( b.isDynamic ) enforce( b.length == 3, "wrong length b" );
899 
900     a.selftype ret;
901     static if( a.isDynamic ) ret.length = 3;
902     ret[0] = T(a[1] * b[2]) - T(a[2] * b[1]);
903     ret[1] = T(a[2] * b[0]) - T(a[0] * b[2]);
904     ret[2] = T(a[0] * b[1]) - T(a[1] * b[0]);
905     return ret;
906 }
907 
908 ///
909 unittest
910 {
911     auto x = vec3(1,0,0);
912     auto y = vecD(0,1,0);
913     auto z = vecD(0,0,1);
914 
915     assert( eq( cross(x,y), z ) );
916     assert( eq( cross(y,z), x ) );
917     assert( eq( cross(y,x), -z ) );
918     assert( eq( cross(x,z), -y ) );
919     assert( eq( cross(z,x), y ) );
920 
921     auto fy = vecD(0,1,0,0);
922     assert( mustExcept({ auto fz = x * fy; }) );
923     auto cfy = vec4(0,1,0,0);
924     static assert( !__traits(compiles,x*cfy) );
925 }