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