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