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