1 module gfm.math.vector;
2 
3 import std.traits,
4        std.math,
5        std.conv,
6        std.array,
7        std.string;
8 
9 import gfm.math.funcs;
10 
11 /**
12  * Generic 1D small vector.
13  * Params:
14  *    N = number of elements
15  *    T = type of elements
16  */
17 struct Vector(T, int N)
18 {
19 nothrow:
20     public
21     {
22         static assert(N >= 1);
23 
24         // fields definition
25         union
26         {
27             T[N] v;
28             struct
29             {
30                 static if (N >= 1)
31                 {
32                     T x;
33                     alias x r;
34                 }
35                 static if (N >= 2)
36                 {
37                     T y;
38                     alias y g;
39                 }
40                 static if (N >= 3)
41                 {
42                     T z;
43                     alias z b;
44                 }
45                 static if (N >= 4)
46                 {
47                     T w;
48                     alias w a;
49                 }
50             }
51         }
52 
53         @nogc this(Args...)(Args args) pure nothrow
54         {
55             static if (args.length == 1)
56             {
57                 // Construct a Vector from a single value.
58                 opAssign!(Args[0])(args[0]);
59             }
60             else
61             {
62                 // validate the total argument count across scalars and vectors
63                 template argCount(T...) {
64                     static if(T.length == 0)
65                         enum argCount = 0; // done recursing
66                     else static if(isVector!(T[0]))
67                         enum argCount = T[0]._N + argCount!(T[1..$]);
68                     else
69                         enum argCount = 1 + argCount!(T[1..$]);
70                 }
71 
72                 static assert(argCount!Args <= N, "Too many arguments in vector constructor");
73 
74                 int index = 0;
75                 foreach(arg; args)
76                 {
77                     static if (isAssignable!(T, typeof(arg)))
78                     {
79                         v[index] = arg;
80                         index++; // has to be on its own line (DMD 2.068)
81                     }
82                     else static if (isVector!(typeof(arg)) && isAssignable!(T, arg._T))
83                     {
84                         mixin(generateLoopCode!("v[index + @] = arg[@];", arg._N)());
85                         index += arg._N;
86                     }
87                     else
88                         static assert(false, "Unrecognized argument in Vector constructor");
89                 }
90                 assert(index == N, "Bad arguments in Vector constructor");
91             }
92         }
93 
94         /// Assign a Vector from a compatible type.
95         @nogc ref Vector opAssign(U)(U x) pure nothrow if (isAssignable!(T, U))
96         {
97             mixin(generateLoopCode!("v[@] = x;", N)()); // copy to each component
98             return this;
99         }
100 
101         /// Assign a Vector with a static array type.
102         @nogc ref Vector opAssign(U)(U arr) pure nothrow if ((isStaticArray!(U) && isAssignable!(T, typeof(arr[0])) && (arr.length == N)))
103         {
104             mixin(generateLoopCode!("v[@] = arr[@];", N)());
105             return this;
106         }
107 
108         /// Assign with a dynamic array.
109         /// Size is checked in debug-mode.
110         @nogc ref Vector opAssign(U)(U arr) pure nothrow if (isDynamicArray!(U) && isAssignable!(T, typeof(arr[0])))
111         {
112             assert(arr.length == N);
113             mixin(generateLoopCode!("v[@] = arr[@];", N)());
114             return this;
115         }
116 
117         /// Assign from a samey Vector.
118         @nogc ref Vector opAssign(U)(U u) pure nothrow if (is(U : Vector))
119         {
120             v[] = u.v[];
121             return this;
122         }
123 
124         /// Assign from other vectors types (same size, compatible type).
125         @nogc ref Vector opAssign(U)(U x) pure nothrow if (isVector!U
126                                                        && isAssignable!(T, U._T)
127                                                        && (!is(U: Vector))
128                                                        && (U._N == _N))
129         {
130             mixin(generateLoopCode!("v[@] = x.v[@];", N)());
131             return this;
132         }
133 
134         /// Returns: a pointer to content.
135         @nogc inout(T)* ptr() pure inout nothrow @property
136         {
137             return v.ptr;
138         }
139 
140         /// Converts to a pretty string.
141         string toString() const nothrow
142         {
143             try
144                 return format("%s", v);
145             catch (Exception e)
146                 assert(false); // should not happen since format is right
147         }
148 
149         @nogc bool opEquals(U)(U other) pure const nothrow
150             if (is(U : Vector))
151         {
152             for (int i = 0; i < N; ++i)
153             {
154                 if (v[i] != other.v[i])
155                 {
156                     return false;
157                 }
158             }
159             return true;
160         }
161 
162         @nogc bool opEquals(U)(U other) pure const nothrow
163             if (isConvertible!U)
164         {
165             Vector conv = other;
166             return opEquals(conv);
167         }
168 
169         @nogc Vector opUnary(string op)() pure const nothrow
170             if (op == "+" || op == "-" || op == "~" || op == "!")
171         {
172             Vector res = void;
173             mixin(generateLoopCode!("res.v[@] = " ~ op ~ " v[@];", N)());
174             return res;
175         }
176 
177         @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow
178             if (is(U : Vector))
179         {
180             mixin(generateLoopCode!("v[@] " ~ op ~ "= operand.v[@];", N)());
181             return this;
182         }
183 
184         @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow if (isConvertible!U)
185         {
186             Vector conv = operand;
187             return opOpAssign!op(conv);
188         }
189 
190         @nogc Vector opBinary(string op, U)(U operand) pure const nothrow
191             if (is(U: Vector) || (isConvertible!U))
192         {
193             Vector result = void;
194             static if (is(U: T))
195                 mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " operand);", N)());
196             else
197             {
198                 Vector other = operand;
199                 mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " other.v[@]);", N)());
200             }
201             return result;
202         }
203 
204         @nogc Vector opBinaryRight(string op, U)(U operand) pure const nothrow if (isConvertible!U)
205         {
206             Vector result = void;
207             static if (is(U: T))
208                 mixin(generateLoopCode!("result.v[@] = cast(T)(operand " ~ op ~ " v[@]);", N)());
209             else
210             {
211                 Vector other = operand;
212                 mixin(generateLoopCode!("result.v[@] = cast(T)(other.v[@] " ~ op ~ " v[@]);", N)());
213             }
214             return result;
215         }
216 
217         @nogc ref T opIndex(size_t i) pure nothrow
218         {
219             return v[i];
220         }
221 
222         @nogc ref const(T) opIndex(size_t i) pure const nothrow
223         {
224             return v[i];
225         }
226 
227         @nogc T opIndexAssign(U : T)(U x, size_t i) pure nothrow
228         {
229             return v[i] = x;
230         }
231 
232 
233         /// Implements swizzling.
234         ///
235         /// Example:
236         /// ---
237         /// vec4i vi = [4, 1, 83, 10];
238         /// assert(vi.zxxyw == [83, 4, 4, 1, 10]);
239         /// ---
240         @nogc @property auto opDispatch(string op, U = void)() pure const nothrow if (isValidSwizzle!(op))
241         {
242             alias Vector!(T, op.length) returnType;
243             returnType res = void;
244             enum indexTuple = swizzleTuple!op;
245             foreach(i, index; indexTuple)
246                 res.v[i] = v[index];
247             return res;
248         }
249 
250         /// Support swizzling assignment like in shader languages.
251         ///
252         /// Example:
253         /// ---
254         /// vec3f v = [0, 1, 2];
255         /// v.yz = v.zx;
256         /// assert(v == [0, 2, 0]);
257         /// ---
258         @nogc @property void opDispatch(string op, U)(U x) pure
259             if ((op.length >= 2)
260                 && (isValidSwizzleUnique!op)                   // v.xyy will be rejected
261                 && is(typeof(Vector!(T, op.length)(x)))) // can be converted to a small vector of the right size
262         {
263             Vector!(T, op.length) conv = x;
264             enum indexTuple = swizzleTuple!op;
265             foreach(i, index; indexTuple)
266                 v[index] = conv[i];
267         }
268 
269         /// Casting to small vectors of the same size.
270         /// Example:
271         /// ---
272         /// vec4f vf;
273         /// vec4d vd = cast!(vec4d)vf;
274         /// ---
275         @nogc U opCast(U)() pure const nothrow if (isVector!U && (U._N == _N))
276         {
277             U res = void;
278             mixin(generateLoopCode!("res.v[@] = cast(U._T)v[@];", N)());
279             return res;
280         }
281 
282         /// Implement slices operator overloading.
283         /// Allows to go back to slice world.
284         /// Returns: length.
285         @nogc int opDollar() pure const nothrow
286         {
287             return N;
288         }
289 
290         /// Returns: a slice which covers the whole Vector.
291         @nogc T[] opSlice() pure nothrow
292         {
293             return v[];
294         }
295 
296         // vec[a..b]
297         @nogc T[] opSlice(int a, int b) pure nothrow
298         {
299             return v[a..b];
300         }
301 
302         /// Returns: squared length.
303         @nogc T squaredLength() pure const nothrow
304         {
305             T sumSquares = 0;
306             mixin(generateLoopCode!("sumSquares += v[@] * v[@];", N)());
307             return sumSquares;
308         }
309 
310         // Returns: squared Euclidean distance.
311         @nogc T squaredDistanceTo(Vector v) pure const nothrow
312         {
313             return (v - this).squaredLength();
314         }
315 
316         static if (isFloatingPoint!T)
317         {
318             /// Returns: Euclidean length
319             @nogc T length() pure const nothrow
320             {
321                 return sqrt(squaredLength());
322             }
323 
324             /// Returns: Inverse of Euclidean length.
325             @nogc T inverseLength() pure const nothrow
326             {
327                 return 1 / sqrt(squaredLength());
328             }
329 
330             /// Faster but less accurate inverse of Euclidean length.
331             /// Returns: Inverse of Euclidean length.
332             @nogc T fastInverseLength() pure const nothrow
333             {
334                 return inverseSqrt(squaredLength());
335             }
336 
337             /// Returns: Euclidean distance between this and other.
338             @nogc T distanceTo(Vector other) pure const nothrow
339             {
340                 return (other - this).length();
341             }
342 
343             /// In-place normalization.
344             @nogc void normalize() pure nothrow
345             {
346                 auto invLength = inverseLength();
347                 mixin(generateLoopCode!("v[@] *= invLength;", N)());
348             }
349 
350             /// Returns: Normalized vector.
351             @nogc Vector normalized() pure const nothrow
352             {
353                 Vector res = this;
354                 res.normalize();
355                 return res;
356             }
357 
358             /// Faster but less accurate in-place normalization.
359             @nogc void fastNormalize() pure nothrow
360             {
361                 auto invLength = fastInverseLength();
362                 mixin(generateLoopCode!("v[@] *= invLength;", N)());
363             }
364 
365             /// Faster but less accurate vector normalization.
366             /// Returns: Normalized vector.
367             @nogc Vector fastNormalized() pure const nothrow
368             {
369                 Vector res = this;
370                 res.fastNormalize();
371                 return res;
372             }
373 
374             static if (N == 3)
375             {
376                 /// Gets an orthogonal vector from a 3-dimensional vector.
377                 /// Doesn’t normalize the output.
378                 /// Authors: Sam Hocevar
379                 /// See_also: Source at $(WEB lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts).
380                 @nogc Vector getOrthogonalVector() pure const nothrow
381                 {
382                     return abs(x) > abs(z) ? Vector(-y, x, 0.0) : Vector(0.0, -z, y);
383                 }
384             }
385         }
386     }
387 
388     private
389     {
390         enum _N = N;
391         alias T _T;
392 
393         // define types that can be converted to this, but are not the same type
394         template isConvertible(T)
395         {
396             enum bool isConvertible = (!is(T : Vector))
397             && is(typeof(
398                 {
399                     T x;
400                     Vector v = x;
401                 }()));
402         }
403 
404         // define types that can't be converted to this
405         template isForeign(T)
406         {
407             enum bool isForeign = (!isConvertible!T) && (!is(T: Vector));
408         }
409 
410         template isValidSwizzle(string op, int lastSwizzleClass = -1)
411         {
412             static if (op.length == 0)
413                 enum bool isValidSwizzle = true;
414             else
415             {
416                 enum len = op.length;
417                 enum int swizzleClass = swizzleClassify!(op[0]);
418                 enum bool swizzleClassValid = (lastSwizzleClass == -1 || (swizzleClass == lastSwizzleClass));
419                 enum bool isValidSwizzle = (swizzleIndex!(op[0]) != -1)
420                                          && swizzleClassValid
421                                          && isValidSwizzle!(op[1..len], swizzleClass);
422             }
423         }
424 
425         template searchElement(char c, string s)
426         {
427             static if (s.length == 0)
428             {
429                 enum bool result = false;
430             }
431             else
432             {
433                 enum string tail = s[1..s.length];
434                 enum bool result = (s[0] == c) || searchElement!(c, tail).result;
435             }
436         }
437 
438         template hasNoDuplicates(string s)
439         {
440             static if (s.length == 1)
441             {
442                 enum bool result = true;
443             }
444             else
445             {
446                 enum tail = s[1..s.length];
447                 enum bool result = !(searchElement!(s[0], tail).result) && hasNoDuplicates!(tail).result;
448             }
449         }
450 
451         // true if the swizzle has at the maximum one time each letter
452         template isValidSwizzleUnique(string op)
453         {
454             static if (isValidSwizzle!op)
455                 enum isValidSwizzleUnique = hasNoDuplicates!op.result;
456             else
457                 enum bool isValidSwizzleUnique = false;
458         }
459 
460         template swizzleIndex(char c)
461         {
462             static if((c == 'x' || c == 'r') && N >= 1)
463                 enum swizzleIndex = 0;
464             else static if((c == 'y' || c == 'g') && N >= 2)
465                 enum swizzleIndex = 1;
466             else static if((c == 'z' || c == 'b') && N >= 3)
467                 enum swizzleIndex = 2;
468             else static if ((c == 'w' || c == 'a') && N >= 4)
469                 enum swizzleIndex = 3;
470             else
471                 enum swizzleIndex = -1;
472         }
473 
474         template swizzleClassify(char c)
475         {
476             static if(c == 'x' || c == 'y' || c == 'z' || c == 'w')
477                 enum swizzleClassify = 0;
478             else static if(c == 'r' || c == 'g' || c == 'b' || c == 'a')
479                 enum swizzleClassify = 1;
480             else
481                 enum swizzleClassify = -1;
482         }
483 
484         template swizzleTuple(string op)
485         {
486             enum opLength = op.length;
487             static if (op.length == 0)
488                 enum swizzleTuple = [];
489             else
490                 enum swizzleTuple = [ swizzleIndex!(op[0]) ] ~ swizzleTuple!(op[1..op.length]);
491         }
492     }
493 }
494 
495 /// True if `T` is some kind of `Vector`
496 enum isVector(T) = is(T : Vector!U, U...);
497 
498 // Previous name, but the alias doesn't seem to show deprecation messages
499 deprecated("Use isVector instead") alias isVectorInstantiation(T) = isVector!T;
500 
501 ///
502 unittest
503 {
504     static assert(isVector!vec2f);
505     static assert(isVector!vec3d);
506     static assert(isVector!(vec4!real));
507     static assert(!isVector!float);
508 }
509 
510 /// Get the numeric type used to measure a vectors's coordinates.
511 alias DimensionType(T : Vector!U, U...) = U[0];
512 
513 ///
514 unittest
515 {
516     static assert(is(DimensionType!vec2f == float));
517     static assert(is(DimensionType!vec3d == double));
518 }
519 
520 template vec2(T) { alias Vector!(T, 2) vec2; }
521 template vec3(T) { alias Vector!(T, 3) vec3; }
522 template vec4(T) { alias Vector!(T, 4) vec4; }
523 
524 alias vec2!byte   vec2b;
525 alias vec2!ubyte  vec2ub;
526 alias vec2!short  vec2s;
527 alias vec2!ushort vec2us;
528 alias vec2!int    vec2i;
529 alias vec2!uint   vec2ui;
530 alias vec2!long   vec2l;
531 alias vec2!ulong  vec2ul;
532 alias vec2!float  vec2f;
533 alias vec2!double vec2d;
534 
535 alias vec3!byte   vec3b;
536 alias vec3!ubyte  vec3ub;
537 alias vec3!short  vec3s;
538 alias vec3!ushort vec3us;
539 alias vec3!int    vec3i;
540 alias vec3!uint   vec3ui;
541 alias vec3!long   vec3l;
542 alias vec3!ulong  vec3ul;
543 alias vec3!float  vec3f;
544 alias vec3!double vec3d;
545 
546 alias vec4!byte   vec4b;
547 alias vec4!ubyte  vec4ub;
548 alias vec4!short  vec4s;
549 alias vec4!ushort vec4us;
550 alias vec4!int    vec4i;
551 alias vec4!uint   vec4ui;
552 alias vec4!long   vec4l;
553 alias vec4!ulong  vec4ul;
554 alias vec4!float  vec4f;
555 alias vec4!double vec4d;
556 
557 private
558 {
559     static string generateLoopCode(string formatString, int N)() pure nothrow
560     {
561         string result;
562         for (int i = 0; i < N; ++i)
563         {
564             string index = ctIntToString(i);
565             // replace all @ by indices
566             result ~= formatString.replace("@", index);
567         }
568         return result;
569     }
570 
571     // Speed-up CTFE conversions
572     static string ctIntToString(int n) pure nothrow
573     {
574         static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
575         if (n < 10)
576             return table[n];
577         else
578             return to!string(n);
579     }
580 }
581 
582 
583 /// Element-wise minimum.
584 @nogc Vector!(T, N) minByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
585 {
586     import std.algorithm: min;
587     Vector!(T, N) res = void;
588     mixin(generateLoopCode!("res.v[@] = min(a.v[@], b.v[@]);", N)());
589     return res;
590 }
591 
592 /// Element-wise maximum.
593 @nogc Vector!(T, N) maxByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
594 {
595     import std.algorithm: max;
596     Vector!(T, N) res = void;
597     mixin(generateLoopCode!("res.v[@] = max(a.v[@], b.v[@]);", N)());
598     return res;
599 }
600 
601 /// Element-wise absolute value.
602 @nogc Vector!(T, N) absByElem(T, int N)(const Vector!(T, N) a) pure nothrow
603 {
604     Vector!(T, N) res = void;
605     mixin(generateLoopCode!("res.v[@] = abs(a.v[@]);", N)());
606     return res;
607 }
608 
609 /// Returns: Dot product.
610 @nogc T dot(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
611 {
612     T sum = 0;
613     mixin(generateLoopCode!("sum += a.v[@] * b.v[@];", N)());
614     return sum;
615 }
616 
617 /// Returns: 3D cross product.
618 /// Thanks to vuaru for corrections.
619 @nogc Vector!(T, 3) cross(T)(const Vector!(T, 3) a, const Vector!(T, 3) b) pure nothrow
620 {
621     return Vector!(T, 3)(a.y * b.z - a.z * b.y,
622                          a.z * b.x - a.x * b.z,
623                          a.x * b.y - a.y * b.x);
624 }
625 
626 /// 3D reflect, like the GLSL function.
627 /// Returns: a reflected by normal b.
628 @nogc Vector!(T, N) reflect(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
629 {
630     return a - (2 * dot(b, a)) * b;
631 }
632 
633 ///
634 @nogc unittest
635 {
636     // reflect a 2D vector across the x axis (the normal points along the y axis)
637     assert(vec2f(1,1).reflect(vec2f(0,1)) == vec2f(1,-1));
638     assert(vec2f(1,1).reflect(vec2f(0,-1)) == vec2f(1,-1));
639 
640     // note that the normal must be, well, normalized:
641     assert(vec2f(1,1).reflect(vec2f(0,20)) != vec2f(1,-1));
642 
643     // think of this like a ball hitting a flat floor at an angle.
644     // the x and y components remain unchanged, and the z inverts
645     assert(vec3f(2,3,-0.5).reflect(vec3f(0,0,1)) == vec3f(2,3,0.5));
646 }
647 
648 /// Returns: angle between vectors.
649 /// See_also: "The Right Way to Calculate Stuff" at $(WEB www.plunk.org/~hatch/rightway.php)
650 @nogc T angleBetween(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow
651 {
652     auto aN = a.normalized();
653     auto bN = b.normalized();
654     auto dp = dot(aN, bN);
655 
656     if (dp < 0)
657         return PI - 2 * asin((-bN-aN).length / 2);
658     else
659         return 2 * asin((bN-aN).length / 2);
660 }
661 
662 static assert(vec2f.sizeof == 8);
663 static assert(vec3d.sizeof == 24);
664 static assert(vec4i.sizeof == 16);
665 
666 
667 unittest
668 {
669     static assert(vec2i.isValidSwizzle!"xyx");
670     static assert(!vec2i.isValidSwizzle!"xyz");
671     static assert(vec4i.isValidSwizzle!"brra");
672     static assert(!vec4i.isValidSwizzle!"rgyz");
673     static assert(vec2i.isValidSwizzleUnique!"xy");
674     static assert(vec2i.isValidSwizzleUnique!"yx");
675     static assert(!vec2i.isValidSwizzleUnique!"xx");
676 
677     assert(vec2l(0, 1) == vec2i(0, 1));
678 
679     int[2] arr = [0, 1];
680     int[] arr2 = new int[2];
681     arr2[] = arr[];
682     vec2i a = vec2i([0, 1]);
683     vec2i a2 = vec2i(0, 1);
684     immutable vec2i b = vec2i(0);
685     assert(b[0] == 0 && b[1] == 0);
686     vec2i c = arr;
687     vec2l d = arr2;
688     assert(a == a2);
689     assert(a == c);
690     assert(vec2l(a) == vec2l(a));
691     assert(vec2l(a) == d);
692 
693     vec4i x = [4, 5, 6, 7];
694     assert(x == x);
695     --x[0];
696     assert(x[0] == 3);
697     ++x[0];
698     assert(x[0] == 4);
699     x[1] &= 1;
700     x[2] = 77 + x[2];
701     x[3] += 3;
702     assert(x == [4, 1, 83, 10]);
703     assert(x.xxywz == [4, 4, 1, 10, 83]);
704     assert(x.xxxxxxx == [4, 4, 4, 4, 4, 4, 4]);
705     assert(x.abgr == [10, 83, 1, 4]);
706     assert(a != b);
707     x = vec4i(x.xyz, 166);
708     assert(x == [4, 1, 83, 166]);
709 
710     vec2l e = a;
711     vec2l f = a + b;
712     assert(f == vec2l(a));
713 
714     vec3ui g = vec3i(78,9,4);
715     g ^= vec3i(78,9,4);
716     assert(g == vec3ui(0));
717     //g[0..2] = 1u;
718     //assert(g == [2, 1, 0]);
719 
720     assert(vec2i(4, 5) + 1 == vec2i(5,6));
721     assert(vec2i(4, 5) - 1 == vec2i(3,4));
722     assert(1 + vec2i(4, 5) == vec2i(5,6));
723     assert(vec3f(1,1,1) * 0 == 0);
724     assert(1.0 * vec3d(4,5,6) == vec3f(4,5.0f,6.0));
725 
726     auto dx = vec2i(1,2);
727     auto dy = vec2i(4,5);
728     auto dp = dot(dx, dy);
729     assert(dp == 14 );
730 
731     vec3i h = cast(vec3i)(vec3d(0.5, 1.1, -2.2));
732     assert(h == [0, 1, -2]);
733     assert(h[] == [0, 1, -2]);
734     assert(h[1..3] == [1, -2]);
735     assert(h.zyx == [-2, 1, 0]);
736 
737     h.yx = vec2i(5, 2); // swizzle assignment
738 
739     assert(h.xy == [2, 5]);
740     assert(-h[1] == -5);
741     assert(++h[0] == 3);
742 
743     //assert(h == [-2, 1, 0]);
744     assert(!__traits(compiles, h.xx = h.yy));
745     vec4ub j;
746 
747     assert(lerp(vec2f(-10, -1), vec2f(10, 1), 0.5) == vec2f(0, 0));
748 
749     // larger vectors
750     alias Vector!(float, 5) vec5f;
751     vec5f l = vec5f(1, 2.0f, 3.0, 4u, 5.0L);
752     l = vec5f(l.xyz, vec2i(1, 2));
753 
754     // the ctor should not compile if given too many arguments
755     static assert(!is(typeof(vec2f(1, 2, 3))));
756     static assert(!is(typeof(vec2f(vec2f(1, 2), 3))));
757     static assert( is(typeof(vec3f(vec2f(1, 2), 3))));
758     static assert( is(typeof(vec3f(1, 2, 3))));
759 
760     assert(absByElem(vec3i(-1, 0, 2)) == vec3i(1, 0, 2));
761 }
762