1 module sbylib.math.vector;
2 
3 import std.traits;
4 import std.algorithm;
5 import std.range : isInputRange, RangeElementType = ElementType;
6 
7 alias vec2  = Vector!(float, 2);
8 alias vec3  = Vector!(float, 3);
9 alias vec4  = Vector!(float, 4);
10 alias ivec2 = Vector!(int,   2);
11 alias ivec3 = Vector!(int,   3);
12 alias ivec4 = Vector!(int,   4);
13 
14 enum isVector(T) = isInstanceOf!(Vector, T);
15 
16 /**
17 Vector type
18 */
19 struct Vector(T, uint S) 
20 if (__traits(isArithmetic, T)) 
21 {
22 
23     private T[S] elements;
24 
25     /**
26     Dimension of this vector type
27     */
28     enum Dimension = S;
29 
30     /**
31     Element type of this vector type
32     */
33     alias ElementType = T;
34 
35     alias array this;
36 
37     /**
38     Constructor by element type.
39     This vector's each element is filled by given value.
40 
41     Params:
42         e = a value which fills the vector
43     */
44     this(T e) {
45         assignSingle(e);
46     }
47 
48     unittest {
49         import std.random : uniform;
50 
51         foreach (k; 0..100) {
52             const e = uniform(-1.0f, +1.0f);
53             const v = vec3(e);
54             static foreach (i; 0..3) {
55                 assert(v[i] == e);
56             }
57         }
58     }
59 
60     /**
61     Constructor by InputRange.
62     This vector's all elements are filled by given range.
63 
64     Params:
65         elements = values which fills the vector
66     */
67     this(Range)(Range r)
68     if (isInputRange!(Range) && is(RangeElementType!(Range) : T))
69     {
70         import std.range : front, popFront, empty;
71 
72         static foreach (i; 0..S) {
73             this.elements[i] = r.front;
74             r.popFront;
75         }
76         assert(r.empty);
77     }
78 
79     unittest {
80         import std.range : iota;
81         assert(vec3(iota(3)) == vec3(0,1,2));
82     }
83 
84     /**
85     Constructor by combination of scalar, array, or vector.
86 
87     Params:
88         args = combination of values, whose total element count must be the same as S
89     */
90     this(Args...)(Args args) {
91         assignAll(args);
92     }
93 
94     unittest {
95         const v = Vector!(float,5)(1,2,3, vec2(4,5));
96         assert(v == Vector!(float,5)(1,2,3,4,5));
97     }
98 
99     /**
100     Unary operation for "+".
101     This function is identity.
102 
103     Returns: a vector equals to this vector.
104     */
105     Vector opUnary(string op)() const 
106     if (op == "+")
107     {
108         return this;
109     }
110 
111     unittest {
112         import std.algorithm : map;
113         import std.range : iota;
114         import std.random : uniform;
115 
116         foreach (i; 0..100) {
117             const v = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
118             assert(approxEqual(v * +1, +v));
119         }
120     }
121 
122     /**
123     Unary operation for "-".
124 
125     Returns: an inverted vector
126     */
127     Vector opUnary(string op)() const 
128     if (op == "-")
129     {
130         return makeVector!(op ~ "elements[]");
131     }
132 
133     unittest {
134         import std.algorithm : map;
135         import std.range : iota;
136         import std.random : uniform;
137 
138         foreach (i; 0..100) {
139             const v = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
140             assert(approxEqual(v * -1, -v));
141         }
142     }
143 
144     /**
145     Binary operation between scalar, array or vector type.
146 
147     Params:
148         value = operation target
149 
150     Returns: calculated vector
151     */
152     Vector opBinary(string op, AnotherType)(auto ref AnotherType value) const 
153     { 
154         return makeVector!("elements[]" ~ op ~ Expression!(AnotherType))(value);
155     }
156 
157     unittest {
158         import std.algorithm : map;
159         import std.range : iota;
160         import std.array : array;
161         import std.random : uniform;
162         import std.math : approxEqual;
163 
164         foreach (k; 0..100) {
165             const a = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
166             const b = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
167 
168             const s = uniform(-1.0f, +1.0f);
169             const r = iota(4).map!(_ => uniform(-1.0f, +1.0f)).array;
170 
171             static foreach (i; 0..4) {
172                 assert(approxEqual(a[i] + b[i], (a + b)[i]));
173                 assert(approxEqual(a[i] - b[i], (a - b)[i]));
174                 assert(approxEqual(a[i] * b[i], (a * b)[i]));
175                 assert(approxEqual(a[i] / b[i], (a / b)[i]));
176 
177                 assert(approxEqual(a[i] + s, (a + s)[i]));
178                 assert(approxEqual(a[i] - s, (a - s)[i]));
179                 assert(approxEqual(a[i] * s, (a * s)[i]));
180                 assert(approxEqual(a[i] / s, (a / s)[i]));
181 
182                 assert(approxEqual(a[i] + r[i], (a + r)[i]));
183                 assert(approxEqual(a[i] - r[i], (a - r)[i]));
184                 assert(approxEqual(a[i] * r[i], (a * r)[i]));
185                 assert(approxEqual(a[i] / r[i], (a / r)[i]));
186             }
187         }
188     }
189 
190     /**
191     Binary operation between matrix, scalar, array or vector type.
192 
193     Params:
194         value = operation target
195 
196     Returns: calculated vector
197     */
198     Vector opBinaryRight(string op, AnotherType)(auto ref AnotherType value) const 
199     {
200         import sbylib.math.matrix : isMatrix;
201 
202         static if (isPotentially!(AnotherType, isMatrix)) {
203             return mixin("value"~PotentialExpression!(AnotherType, isMatrix) ~ ".opBinary!(\""~op~"\")(this)");
204         } else {
205             return makeVector!(Expression!(AnotherType) ~ op ~ "elements[]")(value);
206         }
207     }
208 
209     unittest {
210         import std.algorithm : map;
211         import std.range : iota;
212         import std.array : array;
213         import std.random : uniform;
214         import std.math : approxEqual;
215 
216         foreach (k; 0..100) {
217             const a = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
218 
219             const s = uniform(-1.0f, +1.0f);
220             const r = iota(4).map!(_ => uniform(-1.0f, +1.0f)).array;
221 
222             static foreach (i; 0..4) {
223 
224                 assert(approxEqual(s + a[i], (s + a)[i]));
225                 assert(approxEqual(s - a[i], (s - a)[i]));
226                 assert(approxEqual(s * a[i], (s * a)[i]));
227                 assert(approxEqual(s / a[i], (s / a)[i]));
228 
229                 assert(approxEqual(r[i] + a[i], (r + a)[i]));
230                 assert(approxEqual(r[i] - a[i], (r - a)[i]));
231                 assert(approxEqual(r[i] * a[i], (r * a)[i]));
232                 assert(approxEqual(r[i] / a[i], (r / a)[i]));
233             }
234         }
235     }
236 
237     /**
238     Assign operation for vector or array.
239 
240     Params:
241         value = assigned value
242 
243     Returns: this vector after assigned.
244     */
245     Vector opAssign(AnotherType)(const AnotherType value) 
246         if (isVector!(AnotherType) || isArray!(AnotherType))
247     {
248         this.assignAll(value);
249         return this;
250     }
251 
252     /**
253     Assign operation for scalar.
254     The value fills each element of this vector.
255 
256     Params:
257         value = assigned value
258 
259     Returns: this vector after assigned.
260     */
261     Vector opAssign(AnotherType)(const AnotherType value) 
262         if (!isVector!(AnotherType) && !isArray!(AnotherType) && is(typeof({ T v; v = AnotherType.init; })))
263     {
264         this.assignSingle(value);
265         return this;
266     }
267 
268     unittest {
269         vec3 v;
270 
271         v = 3;
272         assert(approxEqual(v, vec3(3)));
273 
274         int[3] v2 = [1,2,3];
275         v = v2;
276         assert(approxEqual(v, vec3(1,2,3)));
277 
278         v = vec3(3,2,1);
279         assert(approxEqual(v, vec3(3,2,1)));
280     }
281 
282     /**
283     Operator assign for vector, array, or scalar type.
284     If the argument type is scalar, the value affects each element of this vector.
285 
286     Params:
287         value = assigned value
288 
289     Returns: this vector after assigned.
290     */
291     Vector opOpAssign(string op, AnotherType)(AnotherType value)
292     {
293         mixin("elements[] " ~ op ~ "=" ~ Expression!(AnotherType) ~ ";");
294         return this;
295     }
296 
297     unittest {
298         import std.algorithm : map;
299         import std.range : iota;
300         import std.random : uniform;
301 
302         foreach (i; 0..100) {
303             auto v = vec4(4.iota.map!(_ => uniform(-1.0f, +1.0f)));
304             const v2 = vec4(4.iota.map!(_ => uniform(-1.0f, +1.0f)));
305             const v3 = v + v2;
306             v += v2;
307 
308             assert(approxEqual(v, v3));
309         }
310     }
311 
312     /**
313     Cast operation.
314 
315     Returns: a vector whose elements are indivisually casted.
316     */
317     V2 opCast(V2)() const if (isVector!V2 && Dimension == V2.Dimension) {
318         import std : to;
319 
320         return V2(this.elements[].map!(to!(V2.ElementType)));
321     }
322 
323     unittest {
324         import std.algorithm : map;
325         import std.range : iota;
326         import std.random : uniform;
327         import std.array : array;
328 
329         foreach (k; 0..100) {
330             float[4] e;
331             static foreach (i; 0..4) {
332                 e[i] = uniform(-1.0f, +1.0f);
333             }
334             const v = vec4(e.array);
335 
336             const v2 = cast(ivec4)v;
337 
338             static foreach (i; 0..4) {
339                 assert(cast(int)v[i] == v2[i]);
340             }
341         }
342     }
343 
344     /**
345     Indexing operation.
346     
347     Params:
348         idx = index
349 
350     Returns: i-th element of this vector.
351     */
352     T opIndex(size_t i) const 
353     in (i < S)
354     {
355         return this.elements[i];
356     }
357 
358     unittest {
359         import std.algorithm : map;
360         import std.range : iota;
361         import std.random : uniform;
362         import std.array : array;
363 
364         foreach (k; 0..100) {
365             float[4] e;
366             static foreach (i; 0..4) {
367                 e[i] = uniform(-1.0f, +1.0f);
368             }
369             const v = vec4(e.array);
370 
371             static foreach (i; 0..4) {
372                 assert(v[i] == e[i]);
373             }
374         }
375     }
376 
377     /**
378     Indexing operation.
379     
380     Params:
381         idx = index
382 
383     Returns: i-th element of this vector.
384     */
385     ref T opIndex(size_t i) 
386     in (i < S)
387     {
388         return this.elements[i];
389     }
390 
391     unittest {
392         import std.algorithm : map;
393         import std.range : iota;
394         import std.random : uniform;
395 
396         foreach (k; 0..100) {
397             vec4 v;
398 
399             static foreach (i; 0..4) {{
400                 const e = uniform(-1.0f, +1.0f);
401                 v[i] = e;
402                 assert(v[i] == e);
403             }}
404         }
405     }
406 
407     /**
408     Get raw array of this vector.
409 
410     Returns: raw array of this vector.
411     */
412     auto array() inout {
413         return elements;
414     }
415 
416     private enum XYZW = "xyzw";
417     private enum RGBA = "rgba";
418 
419     private static size_t[] createIndex(string propertyString, string s) {
420         size_t[] index = new size_t[s.length];
421         foreach (i; 0..s.length) {
422             index[i] = propertyString.countUntil(s[i]);
423         }
424         return index;
425     }
426 
427     private mixin template Gen(string s) {
428         enum isXYZW = s.all!(a => XYZW.canFind(a));
429         enum isRGBA = s.all!(a => RGBA.canFind(a));
430         static assert(isXYZW || isRGBA);
431         enum propertyString = isXYZW ? XYZW : isRGBA ? RGBA : "";
432         enum index = createIndex(propertyString, s);
433     }
434 
435     /**
436     Single element access by name.
437     The property is 'x', 'y', 'z', 'w', 'r', 'g', 'b' or 'a'.
438 
439     Returns: selected element
440     */
441     auto ref opDispatch(string s)() inout
442     if (s.length == 1)
443     {
444         mixin Gen!(s);
445         return elements[index[0]];
446     }
447 
448     /**
449     Multiple element access by name.
450     The property is combination of 'x', 'y', 'z', 'w', 'r', 'g', 'b' or 'a'.
451 
452     Returns: a vector made by selected elements
453     */
454     const(Vector!(T,s.length)) opDispatch(string s)() inout
455     if (s.length > 1)
456     {
457         mixin Gen!(s);
458         Vector!(T, s.length) result;
459         static foreach (i,idx; index) {
460             result[i] = elements[idx];
461         }
462         return result;
463     }
464 
465     /**
466     Property assign for scalar value.
467     This function supports both single and multiple property reference.
468 
469     Params:
470         value = assigned scalar value
471 
472     Returns: assigned value
473     */
474     T opDispatch(string s)(T value)
475     if (s.all!(a =>XYZW.canFind(a)) || s.all!(a => RGBA.canFind(a)))
476     {
477         mixin Gen!(s);
478         static if(s.length == 1) {
479             return this[index[0]] = value;
480         } else {
481             static foreach (i,idx; index) {
482                 this[idx] = value;
483             }
484             return value;
485         }
486     }
487 
488     /**
489     Property assign for vector value.
490     This function supports both single and multiple property reference.
491 
492     Params:
493         value = assigned vector value
494 
495     Returns: assigned value
496     */
497     const(Vector!(T,s.length)) opDispatch(string s)(Vector!(T,s.length) value)
498     if (s.all!(a =>XYZW.canFind(a)) || s.all!(a => RGBA.canFind(a)))
499     {
500         mixin Gen!(s);
501         static foreach (i,idx; index) {
502             this[idx] = value[i];
503         }
504         return value;
505     }
506 
507     string toString() const {
508         import std.format : format;
509         import std.array : array, join;
510         import std.algorithm : map;
511         import std.conv : to;
512 
513         return format!"(%s)"(this.elements.array.map!(to!string).join(","));
514     }
515 
516     static if (isFloatingPoint!T) {
517         /**
518         Returns whether this vector contains NaN.
519 
520         Returns: true if this vector contains NaN
521         */
522         bool hasNaN() const {
523             import std.algorithm : any;
524             import std.array : array;
525             import std.math : isNaN;
526 
527             return this.elements.array.any!(isNaN);
528         }
529 
530         unittest {
531             assert(vec3().hasNaN);
532             assert(!vec3(0).hasNaN);
533         }
534     }
535 
536     private void assignAll(Args...)(Args args) {
537         assignAll!(0)(args);
538     }
539 
540     private void assignAll(size_t offset, Args...)(Args args) 
541         if (Args.length > 0 && offset + Count!(Args) == Dimension)
542     {
543         assign!(offset)(args[0]);
544         assignAll!(offset + Count!(Args[0]))(args[1..$]);
545     }
546 
547     private void assignAll(size_t offset)() {
548         static assert(offset == Dimension);
549     }
550 
551     private void assign(size_t offset, AnotherType)(AnotherType value) 
552         if (isVector!(AnotherType) && offset + Count!(AnotherType) <= Dimension)
553     {
554         assign!(offset)(value.elements);
555     }
556 
557     private void assign(size_t offset, AnotherType)(AnotherType value) 
558         if (isStaticArray!(AnotherType) && offset + Count!(AnotherType) <= Dimension)
559     {
560         static foreach (i; 0..Count!(AnotherType)) {
561             assign!(offset+i)(value[i]);
562         }
563     }
564 
565     private void assign(size_t index, AnotherType : T)(AnotherType value) {
566         this[index] = value;
567     }
568 
569     private void assignSingle(AnotherType)(AnotherType value) {
570         this.elements[] = value;
571     }
572 
573     private template Count(Types...) 
574         if (Types.length > 1)
575     {
576         enum Count = Count!(Types[0]) + Count!(Types[1..$]);
577     }
578 
579     private template Count(Type) {
580         static if (isVector!(Type)) {
581             enum Count = Count!(typeof(Type.elements));
582         } else static if (isStaticArray!(Type)) {
583             enum Count = Type.length * Count!(ForeachType!(Type));
584         } else static if (is(Unqual!Type : T)) {
585             enum Count = 1;
586         } else {
587             static assert(false, "Invalid type: " ~ Type.stringof);
588         }
589     }
590 
591     private template Expression(AnotherType) {
592         static if (isPotentially!(AnotherType, isArray)) 
593             enum Expression = "value" ~ PotentialExpression!(AnotherType, isArray) ~ "[]";
594         else 
595             enum Expression = "value";
596     }
597 
598     private Vector makeVector(string expr, AnotherType...)(auto ref AnotherType values) const {
599         static if (AnotherType.length > 0) alias value = values[0];
600         else alias value = values;
601 
602         Vector result;
603         result.elements[] = mixin(expr);
604         return result;
605     }
606 }
607 
608 
609 /**
610 Get a vector whose each element is minimum value of the same indexed element of two vectors.
611 
612 Params:
613     v = left hand value of calculation
614     v2 = right hand value of calculation
615 
616 Returns: minimum vector of two vectors
617 */
618 Vector!(CommonType!(T,S),U) min(T,S,uint U)(const Vector!(T,U) v, const Vector!(S,U) v2) {
619     import std.algorithm : fmin = min;
620     return reduceV!(fmin)(v, v2);
621 }
622 
623 unittest {
624     import std.algorithm : map, fmin = min;
625     import std.range : iota;
626     import std.random : uniform;
627 
628     foreach (k; 0..100) {
629         const a = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
630         const b = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
631         static foreach (i; 0..4) {
632             assert(fmin(a[i], b[i]) == min(a,b)[i]);
633         }
634     }
635 }
636 
637 /**
638 Get a vector whose each element is maximum value of the same indexed element of two vectors.
639 
640 Params:
641     v = left hand value of calculation
642     v2 = right hand value of calculation
643 
644 Returns: maximum vector of two vectors
645 */
646 Vector!(CommonType!(T,S),U) max(T,S,uint U)(const Vector!(T,U) v, const Vector!(S,U) v2) {
647     import std.algorithm : fmax = max;
648     return reduceV!(fmax)(v, v2);
649 }
650 
651 unittest {
652     import std.algorithm : map, fmax = max;
653     import std.range : iota;
654     import std.random : uniform;
655 
656     foreach (k; 0..100) {
657         const a = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
658         const b = vec4(iota(4).map!(_ => uniform(-1.0f, +1.0f)));
659         static foreach (i; 0..4) {
660             assert(fmax(a[i], b[i]) == max(a,b)[i]);
661         }
662     }
663 }
664 
665 /**
666 Calculates dot product.
667 
668 Params:
669     v = left hand value of dot product
670     v2 = right hand value of dot product
671 
672 Returns: dot product of two vectors
673 */
674 CommonType!(T,S) dot(T,S,uint U)(const Vector!(T,U) v, const Vector!(S,U) v2) {
675     CommonType!(T,S) result = 0;
676     static foreach (i; 0..U) {
677         result += v[i] * v2[i];
678     }
679     return result;
680 }
681 
682 unittest {
683     import std.algorithm : map;
684     import std.random : uniform;
685     import std.range : iota;
686     import std.math : abs, approxEqual;
687     import sbylib.math.angle : deg, cos;
688     import sbylib.math.matrix : mat3;
689 
690     foreach (i; 0..100) {
691         const point = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
692         const axis = point.ortho.normalize;
693         const angle = uniform(0,180.0f).deg;
694         const r = mat3.axisAngle(axis, angle);
695         const point2 = r * point;
696         assert(approxEqual(dot(point, point2), length(point) * length(point2) * cos(angle)));
697     }
698 }
699 
700 /**
701 Calclates cross product of two vectors in 2D.
702 Returned value is |v||v2| sin a
703 
704 Params:
705     v = left hand value of calculation
706     v2 = right hand value of calculation
707 
708 Returns: cross product of two vectors
709 */
710 CommonType!(T,S) cross(T,S)(const Vector!(T,2) v, const Vector!(S,2) v2) {
711     return v.x * v2.y - v.y * v2.x;
712 }
713 
714 unittest {
715     import std.algorithm : map;
716     import std.random : uniform;
717     import std.range : iota;
718     import std.math : approxEqual, abs;
719     import sbylib.math.angle : deg, acos, sin;
720 
721     foreach (i; 0..100) {
722         const a = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
723         const b = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
724 
725         const la = length(a);
726         const lb = length(b);
727 
728         const angle = acos(dot(a,b) / (la * lb));
729 
730         const c = cross(a,b);
731 
732         assert(approxEqual(abs(c), la * lb * sin(angle), 1e-3));
733     }
734 }
735 
736 /**
737 Calclates cross product of two vectors in 3D.
738 
739 Params:
740     v = left hand value of calculation
741     v2 = right hand value of calculation
742 
743 Returns: cross product of two vectors
744 */
745 Vector!(CommonType!(T,S),3) cross(T,S)(const Vector!(T,3) v, const Vector!(S,3) v2) {
746     Vector!(CommonType!(T,S),3) result;
747     static foreach (i; 0..3) {
748         result[i] = v[(i+1)%3] * v2[(i+2)%3] - v[(i+2)%3] * v2[(i+1)%3];
749     }
750     return result;
751 }
752 
753 unittest {
754     import std.algorithm : map;
755     import std.random : uniform;
756     import std.range : iota;
757     import std.math : approxEqual;
758     import sbylib.math.angle : deg, acos, sin;
759 
760     foreach (i; 0..100) {
761         const a = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
762         const b = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
763 
764         const la = length(a);
765         const lb = length(b);
766 
767         const angle = acos(dot(a,b) / (la * lb));
768 
769         const c = cross(a,b);
770 
771         assert(approxEqual(dot(a,c), 0));
772         assert(approxEqual(dot(b,c), 0));
773         assert(approxEqual(c.length, la * lb * sin(angle)));
774     }
775 }
776 
777 /**
778 Returns a vector which is orthognal to the given vector.
779 
780 Params:
781     v = target vector
782 
783 Returns: orthognal vector
784 */
785 Vector!(T,2) ortho(T)(const Vector!(T,2) v) {
786     return Vector!(T,2)(-v.y, v.x);
787 }
788 
789 unittest {
790     import std.algorithm : map;
791     import std.random : uniform;
792     import std.range : iota;
793     import std.math : approxEqual;
794 
795     foreach (i; 0..100) {
796         const a = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
797         assert(approxEqual(dot(a, a.ortho), 0));
798     }
799 }
800 
801 /**
802 Returns a vector which is orthognal to the given vector.
803 
804 Params:
805     v = target vector
806 
807 Returns: orthognal vector
808 */
809 Vector!(T,3) ortho(T)(const Vector!(T,3) v) 
810 if (__traits(isFloating, T))
811 {
812     if (v.x == 0 && v.z == 0) {
813         return normalize(cross(v, Vector!(T,3)(1,0,0)));
814     } else {
815         return normalize(cross(v, Vector!(T,3)(0,1,0)));
816     }
817 }
818 
819 unittest {
820     import std.algorithm : map;
821     import std.random : uniform;
822     import std.range : iota;
823     import std.math : approxEqual;
824 
825     foreach (i; 0..100) {
826         const a = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
827         assert(approxEqual(dot(a, a.ortho), 0));
828     }
829 }
830 
831 /**
832 Returns squared length of the given vector.
833 
834 Params:
835     v = target vector
836 
837 Returns: squared length of the given vector
838 */
839 T lengthSq(T,uint U)(const Vector!(T,U) v) {
840     T result = 0;
841     static foreach (i; 0..U) {
842         result += v[i] * v[i];
843     }
844     return result;
845 }
846 
847 /**
848 Returns length of the given vector.
849 
850 Params:
851     v = target vector
852 
853 Returns: length of the given vector
854 */
855 T length(T,uint U)(const Vector!(T,U) v) 
856 if (__traits(isFloating, T))
857 {
858     import std.math : sqrt;
859     return sqrt(lengthSq(v));
860 }
861 
862 /**
863 Returns a vector whose length 1 from target vector.
864 
865 Params:
866     v = target vector
867 
868 Returns: normalized target vector
869 */
870 Vector!(T,U) normalize(T,uint U)(const Vector!(T,U) v) 
871 if (__traits(isFloating, T))
872 {
873     return v / length(v);
874 }
875 
876 /**
877 Returns a vector whose length 1 from target vector.
878 When the length is 0, returns zero vector.
879 
880 Params:
881     v = target vector
882 
883 Returns: normalized target vector if the length is greater than 0, otherwise zero vector
884 */
885 Vector!(T,U) safeNormalize(T,uint U)(const Vector!(T,U) v) 
886 if (__traits(isFloating, T))
887 {
888     const l = length(v);
889     if (l == 0) return v;
890     return v / l;
891 }
892 
893 unittest {
894     import std.algorithm : map;
895     import std.random : uniform;
896     import std.range : iota;
897     import std.math : approxEqual;
898 
899     foreach (i; 0..100) {
900         const v = vec4(4.iota.map!(_ => uniform(-1.0f, +1.0f)));
901         assert(v == vec4(0) || safeNormalize(v).length.approxEqual(1));
902     }
903 }
904 
905 /**
906 Returns a linear interpolated vector
907 
908 Params:
909     v1 = target vector
910     v2 = target vector
911     t  = interpolation coefficient
912 
913 Returns: interpolated vector
914 */
915 Vector!(T,U) mix(T,uint U)(const Vector!(T,U) v1, const Vector!(T,U) v2, T t) {
916     return (1-t) * v1 + t * v2;
917 }
918 
919 unittest {
920     import std.algorithm : map;
921     import std.random : uniform;
922     import std.range : iota;
923     import std.math : approxEqual;
924 
925     foreach (i; 0..100) {
926         const v1 = vec4(4.iota.map!(_ => uniform(-1.0f, +1.0f)));
927         const v2 = vec4(4.iota.map!(_ => uniform(-1.0f, +1.0f)));
928         foreach (j; 1..100) {
929             const t = j * 0.01;
930             const v = mix(v1, v2, t);
931             assert(dot(normalize(v1-v), normalize(v2-v)).approxEqual(-1));
932         }
933     }
934 }
935 
936 /**
937 Reduces an array of vector by the given function.
938 
939 Params:
940     v = array of vector reduced
941 
942 Returns: reduced vector
943 */
944 Vector!(T,U) reduceV(alias pred, T, int U)(const Vector!(T,U)[] v...) {
945     Vector!(T,U) result = v[0];
946     static foreach (i; 0..U) {
947         foreach (j; 1..v.length) {
948             result[i] = pred(result[i], v[j][i]);
949         }
950     }
951     return result;
952 }
953 
954 /**
955 Calculates an unsigned area of a triangle which is composed of the given points.
956 
957 Params:
958     positions = array of position vectors which compose a triangle
959 
960 Returns: unsigned area of triangle
961 */
962 T computeUnSignedArea(T)(const Vector!(T,3)[3] positions...) {
963     return length(cross(positions[2] - positions[0], positions[1] - positions[0])) / 2;
964 }
965 
966 unittest {
967     import std.algorithm : map;
968     import std.random : uniform;
969     import std.range : iota;
970     import std.math : approxEqual;
971 
972     foreach (i; 0..100) {
973         const a = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
974         const b = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
975 
976         const area = computeUnSignedArea(a,b,vec3(0));
977 
978         const base = length(a);
979         const height = length(b-normalize(a) * dot(normalize(a),b));
980 
981         assert(approxEqual(area, base * height / 2));
982     }
983 }
984 
985 /**
986 Calculates a signed volume of a tetrahedron which is composed of the given points.
987 
988 Params:
989     positions = array of position vectors which compose a tetrahedron
990 
991 Returns: signed volume of tetrahedron
992 */
993 T computeSignedVolume(T)(const Vector!(T,3)[4] positions...) {
994     Vector!(T,3)[3] v;
995     static foreach (i; 0..3) {
996         v[i] = positions[i+1] - positions[0];
997     }
998     T result = 0;
999     static foreach (i; 0..3) {
1000         result += v[i].x * v[(i+1)%3].y * v[(i+2)%3].z;
1001         result -= v[i].x * v[(i+2)%3].y * v[(i+1)%3].z;
1002     }
1003     return result / 6;
1004 }
1005 
1006 unittest {
1007     import std.algorithm : map;
1008     import std.random : uniform;
1009     import std.range : iota;
1010     import std.math : approxEqual, abs;
1011 
1012     foreach (i; 0..100) {
1013         const a = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1014         const b = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1015         const c = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1016 
1017         const volume = computeSignedVolume(a,b,c,vec3(0));
1018 
1019         const area = computeUnSignedArea(a,b,vec3(0));
1020         const height = dot(normalize(cross(a,b)), c);
1021 
1022         assert(approxEqual(abs(volume), abs(area * height / 3)));
1023     }
1024 }
1025 
1026 //与えられた点列に対し、分散が最大化するベクトルを含む正規直交基底を返す
1027 /**
1028 Returns normalized orthognal basis which maximize the dispersion of the given points
1029 
1030 Params:
1031     vertices = array of points
1032 
1033 Returns: 3 normalized orthognal basis
1034 */
1035 Vector!(T,3)[3] mostDispersionBasis(T)(const Vector!(T,3)[] vertices...)
1036     in(vertices.length > 0)
1037 {
1038     import std.algorithm : sum;
1039     import sbylib.math.matrix : mat3, Matrix, diagonalizeForRealSym;
1040 
1041     const c = vertices.sum / vertices.length;
1042     mat3 vcm = mat3(0);
1043     foreach (ref v; vertices) {
1044         const r = v - c;
1045         vcm += Matrix!(float,3,1)(r.array) * Matrix!(float,1,3)(r.array);
1046     }
1047     vcm /= vertices.length;
1048     const diagonal = diagonalizeForRealSym(vcm);
1049     Vector!(T,3)[3] result;
1050     static foreach (i; 0..3) {
1051         result[i] = diagonal.column[i].normalize;
1052     }
1053     return result;
1054 }
1055 
1056 unittest {
1057     import std.algorithm : map, sum, max;
1058     import std.random : uniform;
1059     import std.range : iota;
1060     import std.math : abs, approxEqual;
1061     import sbylib.math.angle : deg, cos;
1062     import sbylib.math.matrix : mat3;
1063 
1064     vec3[] points;
1065     foreach (i; 0..100) {
1066         points ~= vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1067     }
1068     const center = points.sum / 100;
1069     foreach (i; 0..100) {
1070         points[i] -= center;
1071     }
1072 
1073     float maxDispersion = 0;
1074 
1075     foreach (i; 0..10) {
1076         const axis = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize;
1077         const angle = uniform(0,180.0f).deg;
1078         const r = mat3.axisAngle(axis, angle);
1079         static foreach (j; 0..3) {
1080             maxDispersion = max(maxDispersion, points.map!(p => (r * p)[j]^^2).sum);
1081         }
1082     }
1083 
1084     const basis = mostDispersionBasis(points);
1085     float idealMaxDispersion = 0;
1086     static foreach (j; 0..3) {
1087         idealMaxDispersion = max(idealMaxDispersion, points.map!(p => dot(basis[j],p)^^2).sum);
1088     }
1089 
1090     assert(idealMaxDispersion >= maxDispersion);
1091 }
1092 
1093 /**
1094 Returns true if the distance between the given two point is less than eps.
1095 
1096 Params:
1097     a = target point
1098     b = target point
1099     eps = criteria of approximation
1100 
1101 Returns: true if 2 vectors are approximately equal.
1102 */
1103 bool approxEqual(T,uint N)(const Vector!(T,N) a, const Vector!(T,N) b, T eps = 1e-5) {
1104     return length(a-b) < eps;
1105 }
1106 
1107 private template PotentialExpression(T, alias pred) {
1108     static if (pred!(T)) {
1109         enum PotentialExpression = "";
1110     } else static if (isAggregateType!(T)) {
1111         enum AliasThis = __traits(getAliasThis, T);
1112         static assert(AliasThis.length > 0);
1113         enum Expr = "T."~AliasThis[0];
1114         static if (isFunction!(mixin(Expr))) {
1115             alias Type = ReturnType!(mixin(Expr));
1116         } else {
1117             alias Type = typeof(mixin(Expr));
1118         }
1119         enum PotentialExpression = "." ~ AliasThis[0] ~ PotentialExpression!(Type, pred);
1120     }
1121 }
1122 
1123 private template isPotentially(T, alias pred) {
1124     static if (pred!(T)) {
1125         enum isPotentially = true;
1126     } else static if (isAggregateType!(T)) {
1127         enum AliasThis = __traits(getAliasThis, T);
1128         static if (AliasThis.length == 0) {
1129             enum isPotentially = false;
1130         } else {
1131             enum Expr = "T."~AliasThis[0];
1132             static if (isFunction!(mixin(Expr))) {
1133                 alias Type = ReturnType!(mixin(Expr));
1134             } else {
1135                 alias Type = typeof(mixin(Expr));
1136             }
1137             enum isPotentially = isPotentially!(Type, pred);
1138         }
1139     } else {
1140         enum isPotentially = false;
1141     }
1142 }