1 module sbylib.math.matrix;
2 
3 import sbylib.math;
4 import std.traits;
5 import std.range : isInputRange, RangeElementType = ElementType;
6 import std.format : format;
7 
8 alias mat1x1 = Matrix!(float, 1, 1);
9 alias mat1x2 = Matrix!(float, 1, 2);
10 alias mat1x3 = Matrix!(float, 1, 3);
11 alias mat1x4 = Matrix!(float, 1, 4);
12 alias mat2x1 = Matrix!(float, 2, 1);
13 alias mat2x2 = Matrix!(float, 2, 2);
14 alias mat2x3 = Matrix!(float, 2, 3);
15 alias mat2x4 = Matrix!(float, 2, 4);
16 alias mat3x1 = Matrix!(float, 3, 1);
17 alias mat3x2 = Matrix!(float, 3, 2);
18 alias mat3x3 = Matrix!(float, 3, 3);
19 alias mat3x4 = Matrix!(float, 3, 4);
20 alias mat4x1 = Matrix!(float, 4, 1);
21 alias mat4x2 = Matrix!(float, 4, 2);
22 alias mat4x3 = Matrix!(float, 4, 3);
23 alias mat4x4 = Matrix!(float, 4, 4);
24 
25 alias mat2 = mat2x2;
26 alias mat3 = mat3x3;
27 alias mat4 = mat4x4;
28 
29 enum isMatrix(T) = isInstanceOf!(Matrix, T);
30 
31 /**
32 Matrix type
33 */
34 struct Matrix(T, uint U, uint V)
35 if (__traits(isArithmetic, T)) 
36 {
37     private T[U*V] element;
38 
39     /**
40     Row number of this matrix type
41     */
42     enum Row = U;
43 
44     /**
45     Column number of this matrix type
46     */
47     enum Column = V;
48 
49     /**
50     Element type of this matrix type
51     */
52     alias ElementType = T;
53 
54     alias array this;
55 
56     /**
57     Constructor by element type.
58     This matrix's each element is filled by given value.
59 
60     Params:
61         e = a value which fills the matrix
62     */
63     this(T e) {
64         foreach (ref el; this.element) el = e;
65     }
66 
67     unittest {
68         import std.random : uniform;
69 
70         foreach (k; 0..100) {
71             const e = uniform(-1.0f, +1.0f);
72             const m = mat3(e);
73             static foreach (i; 0..3) {
74                 static foreach (j; 0..3) {
75                     assert(m[i,j] == e);
76                 }
77             }
78         }
79     }
80 
81     /**
82     Constructor by array.
83     This matrix's all elements are filled by given array.
84 
85     Params:
86         elements = values which fills the matrix
87     */
88     this(T[U*V] elements...) {
89         static foreach (i; 0..U*V) {
90             this[i/V,i%V] = elements[i];
91         }
92     }
93 
94     /**
95     Constructor by InputRange.
96     This matrix's all elements are filled by given range.
97 
98     Params:
99         elements = values which fills the matrix
100     */
101     this(Range)(Range r)
102     if (isInputRange!(Range) && is(RangeElementType!(Range) : T))
103     {
104         import std.range : front, popFront, empty;
105 
106         static foreach (i; 0..U*V) {
107             this.element[i] = r.front;
108             r.popFront;
109         }
110         assert(r.empty);
111     }
112 
113     unittest {
114         import std.range : iota;
115         const a = mat2(iota(4));
116         assert(a[0,0] == 0);
117         assert(a[1,0] == 1);
118         assert(a[0,1] == 2);
119         assert(a[1,1] == 3);
120     }
121 
122 
123     /**
124     Constructor by array of array.
125     This matrix's all elements are filled by given array.
126 
127     Params:
128         elements = values which fills the matrix
129     */
130     this(T[U][V] elements...) {
131         static foreach (i; 0..U) {
132             static foreach (j; 0..V) {
133                 this[i,j] = elements[j][i];
134             }
135         }
136     }
137 
138     unittest {
139         const a = mat2([1,3],[2,4]);
140         assert(a[0,0] == 1);
141         assert(a[0,1] == 2);
142         assert(a[1,0] == 3);
143         assert(a[1,1] == 4);
144     }
145 
146     static if (V > 1) {
147         /**
148         Constructor by array of vector.
149         This matrix's all elements are filled by given
150         Each vectors are dealed as column.
151 
152         Params:
153         vectors = values which fills the matrix
154         */
155         this(Vector!(T, U)[V] vectors...) {
156             static foreach (i; 0..U) {
157                 static foreach (j; 0..V) {
158                     this[i,j] = vectors[j][i];
159                 }
160             }
161         }
162 
163         unittest {
164             const a = mat2(vec2(1,3),vec2(2,4));
165             assert(a[0,0] == 1);
166             assert(a[0,1] == 2);
167             assert(a[1,0] == 3);
168             assert(a[1,1] == 4);
169         }
170     }
171 
172     /**
173     Unary operation for "+".
174 
175     this is identity function
176 
177     Returns: a matrix which is equal to this matrix
178     */
179     Matrix!(T,U,V) opUnary(string op)() const 
180     if (op == "+")
181     {
182         return this;
183     }
184 
185     unittest {
186         import std.algorithm : map;
187         import std.range : iota;
188         import std.random : uniform;
189 
190         foreach (i; 0..100) {
191             const m = mat4(iota(16).map!(_ => uniform(-1.0f, +1.0f)));
192             assert(approxEqual(m * +1, +m));
193         }
194     }
195 
196     /**
197     Unary operation for "-".
198 
199     Returns: a matrix whose sign is inverted
200     */
201     Matrix!(T,U,V) opUnary(string op)() const 
202     if (op == "-")
203     {
204         Matrix!(T,U,V) result;
205         static foreach (i; 0..U) {
206             static foreach (j; 0..V) {
207                 result[i,j] = -this[i,j];
208             }
209         }
210         return result;
211     }
212 
213     unittest {
214         import std.algorithm : map;
215         import std.range : iota;
216         import std.random : uniform;
217 
218         foreach (i; 0..100) {
219             const m = mat4(iota(16).map!(_ => uniform(-1.0f, +1.0f)));
220             assert(approxEqual(m * -1, -m));
221         }
222     }
223 
224     /**
225     Binary operator between the same size matrix ("+" or "-").
226     Another type matrix is allowed.
227 
228     Params: 
229         m = target matrix
230 
231     Returns: calculated matrix
232     */
233     Matrix!(CommonType!(T,S), U,V) opBinary(string op, S)(Matrix!(S,U,V) m) const
234     if (op == "+" || op == "-")
235     {
236         Matrix!(CommonType!(T,S),U,V) result;
237         static foreach (i; 0..U) {
238             static foreach (j; 0..V) {
239                 mixin(format!q{
240                     result[i,j] = this[i,j] %s m[i,j];
241                 }(op));
242             }
243         }
244         return result;
245     }
246 
247     unittest {
248         import std.algorithm : map;
249         import std.range : iota;
250         import std.random : uniform;
251         import std.math : approxEqual;
252 
253         foreach (k; 0..100) {
254             const a = mat4(iota(16).map!(_ => uniform(-1.0f, +1.0f)));
255             const b = mat4(iota(16).map!(_ => uniform(-1.0f, +1.0f)));
256 
257             const c = a + b;
258             const d = a - b;
259 
260             static foreach (i; 0..4) {
261                 static foreach (j; 0..4) {
262                     assert(approxEqual(a[i,j] + b[i,j], c[i,j]));
263                     assert(approxEqual(a[i,j] - b[i,j], d[i,j]));
264                 }
265             }
266         }
267     }
268 
269     /**
270     Multiply operator between matrix.
271     Another type matrix is allowed.
272 
273     Params: 
274         m = target matrix
275 
276     Returns: calculated matrix
277     */
278     Matrix!(CommonType!(T,S),U,P) opBinary(string op, S, uint P)(Matrix!(S,V,P) m) const
279     if (op == "*") 
280     {
281         Matrix!(CommonType!(T,S),U,P) result;
282         static foreach (i; 0..U) {
283             static foreach (j; 0..P) {
284                 result[i,j] = 0;
285                 static foreach (k; 0..V) {
286                     result[i,j] += this[i,k] * m[k,j];
287                 }
288             }
289         }
290         return result;
291     }
292 
293     unittest {
294         import std.algorithm : map;
295         import std.range : iota;
296         import std.random : uniform;
297 
298         foreach (i; 0..100) {
299             const a = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
300             const b = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
301             const c = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
302             assert(approxEqual((a * b) * c, a * (b * c)));
303         }
304     }
305 
306     /**
307     Binary operation between scalar type ("*" or "/").
308 
309     Params: 
310         e = target scalar value
311 
312     Returns: calculated matrix
313     */
314     Matrix!(CommonType!(T,S),U,V) opBinary(string op, S)(S e) const 
315     if (__traits(isArithmetic, S) && (op == "*" || op == "/"))
316     {
317         Matrix!(CommonType!(T,S),U,V) result;
318         static foreach (i; 0..U) {
319             static foreach (j; 0..V) {
320                 result[i,j] = mixin(format!q{
321                     this[i,j] %s e
322                 }(op));
323             }
324         }
325         return result;
326     }
327 
328     unittest {
329         import std.algorithm : map;
330         import std.range : iota;
331         import std.random : uniform;
332         import std.math : approxEqual;
333 
334         foreach (k; 0..100) {
335             const m = mat4(16.iota.map!(_ => uniform(-1.0f, +1.0f)));
336             const s = uniform(-1.0f, +1.0f);
337             const m2 = m * s;
338             const m3 = m / s;
339             static foreach (i; 0..4) {
340                 static foreach (j; 0..4) {
341                     assert(approxEqual(m[i,j] * s, m2[i,j]));
342                     assert(approxEqual(m[i,j] / s, m3[i,j]));
343                 }
344             }
345         }
346     }
347 
348     /**
349     Binary operation between vector type ("*" only).
350 
351     Params: 
352         e = target scalar value
353 
354     Returns: calculated vector
355     */
356     Vector!(CommonType!(T,S),U) opBinary(string op, S)(Vector!(S,V) v) const 
357     if (op == "*")
358     {
359         Vector!(CommonType!(T,S),U) result;
360         static foreach (i; 0..U) {
361             result[i] = 0;
362             static foreach (j; 0..V) {
363                 result[i] += this[i,j] * v[j];
364             }
365         }
366         return result;
367     }
368 
369     unittest {
370         import std.algorithm : map;
371         import std.range : iota;
372         import std.random : uniform;
373         import sbylib.math.vector : dot, approxEqual;
374         foreach (i; 0..100) {
375             const m = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
376             const v = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
377             const v2 = m * v;
378 
379             assert(approxEqual(vec3(dot(m.row[0], v), dot(m.row[1], v), dot(m.row[2], v)), v2));
380         }
381     }
382 
383     /**
384     operator assign between matrix type ("+" or "-")
385 
386     Params: 
387         m = target matrix
388 
389     Returns: calculated this matrix
390     */
391     Matrix opOpAssign(string op, S)(Matrix!(S,U,V) m) 
392     if (op == "+" || op == "-")
393     {
394         static foreach (i; 0..U) {
395             static foreach (j; 0..V) {
396                 mixin(format!q{
397                     this[i,j] %s= m[i,j];
398                 }(op));
399             }
400         }
401         return this;
402     }
403 
404     unittest {
405         import std.algorithm : map;
406         import std.range : iota;
407         import std.random : uniform;
408 
409         foreach (i; 0..100) {
410             auto m = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
411             const m2 = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
412             const m3 = m + m2;
413             m += m2;
414 
415             assert(approxEqual(m, m3));
416         }
417     }
418 
419     /**
420     operator assign between matrix type (only "*")
421 
422     Params: 
423         m = target matrix
424 
425     Returns: calculated this matrix
426     */
427     Matrix opOpAssign(string op, S)(Matrix!(S,U,V) m) 
428     if (op == "*")
429     {
430         Matrix result = this * m;
431         static foreach (i; 0..U) {
432             static foreach (j; 0..V) {
433                 this[i,j] = result[i,j];
434             }
435         }
436         return this;
437     }
438 
439     unittest {
440         import std.algorithm : map;
441         import std.range : iota;
442         import std.random : uniform;
443         foreach (i; 0..100) {
444             auto m = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
445             const m2 = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
446             const m3 = m * m2;
447             m *= m2;
448 
449             assert(approxEqual(m, m3));
450         }
451     }
452 
453     /**
454     operator assign between scalar type ("*" or "/")
455 
456     Params: 
457         e = target scalar value
458 
459     Returns: calculated this matrix
460     */
461     Matrix opOpAssign(string op, S)(S e) 
462     if (__traits(isArithmetic, S) && (op == "*" || op == "/"))
463     {
464         static foreach (i; 0..U) {
465             static foreach (j; 0..V) {
466                 mixin(format!q{
467                     this[i,j] %s= e;
468                 }(op));
469             }
470         }
471         return this;
472     }
473 
474     unittest {
475         import std.algorithm : map;
476         import std.range : iota;
477         import std.random : uniform;
478 
479         foreach (i; 0..100) {
480             auto m = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
481             const s = uniform(-1.0f, +1.0f);
482             const m2 = m * s;
483             m *= s;
484 
485             assert(approxEqual(m, m2));
486         }
487     }
488 
489     /**
490     indexing operation
491 
492     Params: 
493         i = column index
494         j = row index
495 
496     Returns: selected value
497     */
498     T opIndex(size_t i, size_t j) const 
499     in (i < U && j < V)
500     {
501         return element[i+j*U];
502     }
503 
504     unittest {
505         import std.algorithm : map;
506         import std.range : iota;
507         import std.random : uniform;
508 
509         foreach (k; 0..100) {
510             vec3[3] v;
511             static foreach (i; 0..3) {
512                 v[i] = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
513             }
514             const m = mat3(v);
515 
516             static foreach (i; 0..3) {
517                 static foreach (j; 0..3) {
518                     assert(m[i,j] == v[j][i]);
519                 }
520             }
521         }
522     }
523 
524     /**
525     indexing operation
526 
527     Params: 
528         i = column index
529         j = row index
530 
531     Returns: selected value
532     */
533     ref T opIndex(size_t i, size_t j) 
534     in(i < U && j < V)
535     {
536         return element[i+j*U];
537     }
538 
539     unittest {
540         import std.algorithm : map;
541         import std.range : iota;
542         import std.random : uniform;
543 
544         foreach (k; 0..100) {
545             mat3 m;
546 
547             static foreach (i; 0..3) {{
548                 static foreach (j; 0..3) {{
549                     const v = uniform(-1.0f, +1.0f);
550                     m[i,j] = v;
551                     assert(m[i,j] == v);
552                 }}
553             }}
554         }
555     }
556 
557     /**
558     Get raw array data.
559 
560     Returns: raw array data
561     */
562     T[U*V] array() inout {
563         return element;
564     }
565 
566     unittest {
567         import std.range : iota;
568         const a = mat2(iota(4));
569         assert(a.array == [0,1,2,3]);
570     }
571 
572     /**
573     Get column array as array of vector
574 
575     Returns: column array
576     */
577     Vector!(T,U)[V] column() const {
578         Vector!(T,U)[V] result;
579         static foreach (j; 0..V) {
580             static foreach (i; 0..U) {
581                 result[j][i] = this[i,j];
582             }
583         }
584         return result;
585     }
586 
587     unittest {
588         import std.range : iota;
589         const a = mat2(iota(4));
590         assert(a.column[0] == vec2(0,1));
591         assert(a.column[1] == vec2(2,3));
592     }
593 
594     /**
595     Get row array as array of vector
596 
597     Returns: row array
598     */
599     Vector!(T,V)[U] row() const {
600         Vector!(T,V)[U] result;
601         static foreach (i; 0..U) {
602             static foreach (j; 0..V) {
603                 result[i][j] = this[i,j];
604             }
605         }
606         return result;
607     }
608 
609     unittest {
610         import std.range : iota;
611         const a = mat2(iota(4));
612         assert(a.row[0] == vec2(0,2));
613         assert(a.row[1] == vec2(1,3));
614     }
615 
616     /**
617     Converts to string
618 
619     Returns: string representation of this matrix
620     */
621     string toString() const {
622         string res;
623         static foreach (i; 0..U) {
624             res ~= "\n\t";
625             static foreach (j; 0..V) {
626                 static if (__traits(isFloating, T))
627                     res ~= format!"%10f,"(this[i,j]);
628                 else
629                     res ~= format!"%d,"(this[i,j]);
630             }
631         }
632         return res;
633     }
634 
635     static if (U == V) {
636         /**
637         Returns identity matrix.
638 
639         Returns: identity matrix
640         */
641         static Matrix identity() {
642             Matrix result;
643             static foreach (i; 0..U) {
644                 static foreach (j; 0..U) {
645                     static if (i == j)
646                         result[i,j] = 1;
647                     else
648                         result[i,j] = 0;
649                 }
650             }
651             return result;
652         }
653 
654         unittest {
655             import std.algorithm : map;
656             import std.range : iota;
657             import std.random : uniform;
658             import sbylib.math.vector : approxEqual;
659 
660             foreach (i; 0..100) {
661                 const v2 = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
662                 assert(approxEqual(v2, mat2.identity * v2));
663 
664                 const v3 = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
665                 assert(approxEqual(v3, mat3.identity * v3));
666 
667                 const v4 = vec4(4.iota.map!(_ => uniform(-1.0f, +1.0f)));
668                 assert(approxEqual(v4, mat4.identity * v4));
669             }
670         }
671 
672         /**
673         Returns scale matrix.
674 
675         Params:
676             vec = scale vector
677 
678         Returns: scale matrix
679         */
680         static Matrix scale(const Vector!(T,U) vec) {
681             Matrix result;
682             static foreach (i; 0..U) {
683                 static foreach (j; 0..U) {
684                     static if (i == j)
685                         result[i,j] = vec[i];
686                     else
687                         result[i,j] = 0;
688                 }
689             }
690             return result;
691         }
692 
693         unittest {
694             import std.algorithm : map;
695             import std.range : iota;
696             import std.random : uniform;
697             import sbylib.math.vector : approxEqual;
698 
699             foreach (i; 0..100) {
700                 const v2 = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
701                 const s2 = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
702                 assert(approxEqual(v2 * s2, mat2.scale(s2) * v2));
703 
704                 const v3 = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
705                 const s3 = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
706                 assert(approxEqual(v3 * s3, mat3.scale(s3) * v3));
707             }
708         }
709         static if (U == 3 || U == 4) {
710             /**
711             Returns translation matrix.
712 
713             Params:
714                 vec = translation vector
715 
716             Returns: translation matrix
717             */
718             static Matrix translate(Vector!(T,U-1) vec) {
719                 Matrix result;
720                 static foreach (i; 0..U) {
721                     static foreach (j; 0..U) {
722                         static if (i == j) {
723                             result[i,j] = 1;
724                         } else static if (j == U-1) {
725                             static if (i < U-1) {
726                                 result[i,j] = vec[i];
727                             } else {
728                                 result[i,j] = 1;
729                             }
730                         } else {
731                             result[i,j] = 0;
732                         }
733                     }
734                 }
735                 return result;
736             }
737 
738             unittest {
739                 import std.algorithm : map;
740                 import std.range : iota;
741                 import std.random : uniform;
742                 import sbylib.math.vector : approxEqual;
743 
744                 foreach (i; 0..100) {
745                     const p2 = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
746                     const t2 = vec2(2.iota.map!(_ => uniform(-1.0f, +1.0f)));
747                     assert(approxEqual(p2 + t2, (mat3.translate(t2) * vec3(p2,1)).xy));
748 
749                     const p3 = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
750                     const t3 = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
751                     assert(approxEqual(p3 + t3, (mat4.translate(t3) * vec4(p3,1)).xyz));
752                 }
753             }
754 
755         }
756         static if (U == 3) {
757             /**
758             Returns rotation matrix decided by its rotation axis and angle.
759 
760             Params:
761                 axis = rotation axis vector, which must be normalized
762                 angle = rotation angle
763 
764             Returns: rotation matrix
765             */
766             static Matrix axisAngle(const Vector!(T,3) axis, const Angle angle) {
767                 const c = cos(angle);
768                 const s = sin(angle);
769                 Matrix result;
770                 static foreach (i; 0..3) {
771                     static foreach (j; 0..3) {
772                         static if (i == j) {
773                             result[i,j] = axis[i]*axis[j]*(1-c)+c;
774                         } else static if (j == (i+1)%3) {
775                             result[i,j] = axis[i]*axis[j]*(1-c)+axis[(i+2)%3]*s;
776                         } else {
777                             result[i,j] = axis[i]*axis[j]*(1-c)-axis[(i+1)%3]*s;
778                         }
779                     }
780                 }
781                 return result;
782             }
783 
784             unittest {
785                 import std.algorithm : map;
786                 import std.random : uniform;
787                 import std.range : iota;
788                 import std.math : abs;
789                 import sbylib.math.vector : dot;
790 
791                 foreach (i; 0..100) {
792                     const axis = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize;
793                     auto point = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
794                     point = normalize(point - dot(point, axis) * axis);
795                     const r = mat3.axisAngle(axis, 90.deg);
796                     const point2 = r * point;
797                     assert(abs(dot(point, point2)) < 1e-5);
798                 }
799             }
800 
801             /**
802             Returns rotation matrix which converts a vector to another vector.
803 
804             Params:
805                 from = before vector, which must be normalized
806                 to = after vector, which must be normalized
807 
808             Returns: rotation matrix
809             */
810             static Matrix rotFromTo(const Vector!(T,3) from, const Vector!(T,3) to) {
811                 import std.algorithm : clamp;
812 
813                 const v = cross(from, to).normalize;
814                 const c = dot(from, to);
815 
816                 if (v.hasNaN) {
817                     if (c > 0) return Matrix.identity;
818                     else return Matrix.scale(vec3(-1));
819                 }
820                 const angle = -acos(clamp(c, -1, +1));
821                 return axisAngle(v, angle);
822             }
823 
824             unittest {
825                 import std.algorithm : map;
826                 import std.random : uniform;
827                 import std.range : iota;
828                 import sbylib.math.vector : approxEqual;
829 
830                 foreach (i; 0..100) {
831                     const a = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize;
832                     const b = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize;
833                     const c = rotFromTo(a,b) * a;
834                     assert(approxEqual(b,c), format!"b = %s, b' = %s"(b,c));
835                 }
836             }
837 
838             /**
839             Returns view transform matrix.
840 
841             Params:
842                 vec = backward vector
843                 up = up vector
844 
845             Returns: view transformation matrix
846             */
847             static Matrix lookAt(const Vector!(T,3) vec, const Vector!(T,3) up) {
848                 const side = normalize(cross(up, vec));
849                 const up2 = normalize(cross(vec, side));
850                 return Matrix(side, up2, vec);
851             }
852 
853         } else static if (U == 4) {
854 
855             /**
856             Returns rotation matrix decided by its rotation axis and angle.
857 
858             Params:
859                 axis = rotation axis vector, which must be normalized
860                 angle = rotation angle
861 
862             Returns: rotation matrix
863             */
864             static Matrix axisAngle(const Vector!(T,3) v, const Angle angle) {
865                 return Matrix!(T,3,3).axisAngle(v, angle).toMatrix4();
866             }
867 
868             unittest {
869                 import std.algorithm : map;
870                 import std.random : uniform;
871                 import std.range : iota;
872                 import std.math : abs;
873                 import sbylib.math.vector : dot;
874                 foreach (i; 0..100) {
875                     const axis = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize;
876                     auto point = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
877                     point = normalize(point - dot(point, axis) * axis);
878                     const r = mat4.axisAngle(axis, 90.deg);
879                     const point2 = (r * vec4(point, 1)).xyz;
880                     assert(abs(dot(point, point2)) < 1e-5);
881                 }
882             }
883 
884             /**
885             Returns rotation matrix which converts a vector to another vector.
886 
887             Params:
888                 from = before vector, which must be normalized
889                 to = after vector, which must be normalized
890 
891             Returns: rotation matrix
892             */
893             static Matrix rotFromTo(const Vector!(T,3) from, const Vector!(T,3) to) {
894                 return Matrix!(T,3,3).rotFromTo(from, to).toMatrix4();
895             }
896 
897             unittest {
898                 import std.algorithm : map;
899                 import std.random : uniform;
900                 import std.range : iota;
901                 import sbylib.math.vector : approxEqual;
902 
903                 foreach (i; 0..100) {
904                     const a = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize;
905                     const b = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize;
906                     const c = (mat4.rotFromTo(a,b) * vec4(a,1)).xyz;
907                     assert(approxEqual(b,c), format!"b = %s, b' = %s"(b,c));
908                 }
909             }
910 
911             /**
912             Returns view transform matrix.
913 
914             Params:
915                 eye = point of view
916                 vec = backward vector
917                 up = up vector
918 
919             Returns: view transformation matrix
920             */
921             static Matrix lookAt(const Vector!(T,3) eye, const Vector!(T,3) vec, const Vector!(T,3) up) {
922                 const side = normalize(cross(up, vec));
923                 const up2 = normalize(cross(vec, side));
924                 return Matrix(Vector!(T,4)(side, 0), Vector!(T,4)(up2, 0), Vector!(T,4)(vec, 0),
925                         Vector!(T,4)(-dot(eye,side),-dot(eye,up2),-dot(eye,vec),1));
926             }
927 
928             /**
929             Returns orthographic projection transform matrix.
930 
931             Params:
932                 width = width of orthographic view area
933                 height = height of orthographic view area
934                 nearZ = near side z value of orthographic view area
935                 farZ = far side z value of orthographic view area
936 
937             Returns: orthographic projection transformation matrix
938             */
939             static Matrix ortho(T width, T height, T nearZ, T farZ) {
940                 return Matrix(
941                         2 / width, 0,          0,                  0,
942                         0,         2 / height, 0,                  0,
943                         0,         0,          1 / (nearZ - farZ), nearZ / (nearZ - farZ),
944                         0,         0,          0,                  1
945                         );
946             }
947 
948             /**
949             Returns perspective projection transform matrix.
950 
951             Params:
952                 aspectWperH = bottom side aspect retio
953                 fovy = FOV of y direction
954                 nearZ = near side z value of orthographic view area
955                 farZ = far side z value of orthographic view area
956 
957             Returns: perspective projection transform matrix
958             */
959             static Matrix perspective(T aspectWperH, Angle fovy, T nearZ, T farZ) {
960                 return Matrix(
961                         1 / (aspectWperH * tan(fovy/2)), 0, 0, 0,
962                         0, 1 / (tan(fovy/2)), 0, 0,
963                         0, 0, (nearZ+farZ)/(nearZ-farZ), 2 * nearZ * farZ / (nearZ - farZ),
964                         0, 0, -1, 0);
965             }
966 
967             /**
968             Returns TRS transform matrix.
969 
970             Params:
971                 pos = translation vector, which represents T
972                 rot = rotation matrix, which represents R
973                 scale = scale vector, which represents S
974 
975             Returns: perspective projection transform matrix
976             */
977             static Matrix makeTRS(const Vector!(T,3) trans, const Matrix!(T,3,3) rot, const Vector!(T,3) scale) {
978                 return Matrix(
979                         vec4(scale[0] * rot.column[0],0), 
980                         vec4(scale[1] * rot.column[1],0), 
981                         vec4(scale[2] * rot.column[2],0), 
982                         vec4(trans,1));
983             }
984 
985             unittest {
986                 import std.algorithm : map;
987                 import std.random : uniform;
988                 import std.range : iota;
989                 import sbylib.math.vector : approxEqual;
990 
991                 foreach (i; 0..100) {
992                     const p = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
993                     const t = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
994                     const r = mat3.axisAngle(vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize,
995                             uniform(0,180.0).deg);
996                     const s = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
997 
998                     assert(approxEqual((mat4.makeTRS(t,r,s) * vec4(p,1)).xyz, r * (s * p) + t));
999                 }
1000             }
1001             
1002             /**
1003             Returns inverse TRS transform matrix.
1004 
1005             Params:
1006                 pos = translation vector, which represents T
1007                 rot = rotation matrix, which represents R
1008                 scale = scale vector, which represents S
1009 
1010             Returns: perspective projection transform matrix
1011             */
1012             static Matrix makeInvertTRS(const Vector!(T,3) pos, const Matrix!(T,3,3) rot, const Vector!(T, 3) scale) {
1013                 return Matrix(
1014                         rot[0,0] / scale[0], rot[1,0] / scale[0], rot[2,0] / scale[0], -dot(rot.column[0], pos) / scale[0],
1015                         rot[0,1] / scale[1], rot[1,1] / scale[1], rot[2,1] / scale[1], -dot(rot.column[1], pos) / scale[1],
1016                         rot[0,2] / scale[2], rot[1,2] / scale[2], rot[2,2] / scale[2], -dot(rot.column[2], pos) / scale[2],
1017                         0,0,0, 1);
1018             }
1019 
1020             unittest {
1021                 import std.algorithm : map;
1022                 import std.random : uniform;
1023                 import std.range : iota;
1024 
1025                 foreach (i; 0..100) {
1026                     const t = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1027                     const r = mat3.axisAngle(vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize, uniform(0,180.0).deg);
1028                     const s = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1029 
1030                     assert(approxEqual(mat4.makeTRS(t,r,s) * mat4.makeInvertTRS(t,r,s), mat4.identity));
1031                 }
1032             }
1033         }
1034     }
1035 }
1036 
1037 /**
1038 Returns transposed matrix.
1039 
1040 Params:
1041     m = target matrix
1042 
1043 Returns: transposed matrix
1044 */
1045 Matrix!(T,V,U) transpose(T, uint U, uint V)(const Matrix!(T,U,V) m) {
1046     Matrix!(T,V,U) r;
1047     static foreach (i;0..V) {
1048         static foreach (j;0..U) {
1049             r[i,j] = m[j,i];
1050         }
1051     }
1052     return r;
1053 }
1054 
1055 unittest {
1056     assert(approxEqual(transpose(mat2x3(1,2,3, 4,5,6)), mat3x2(1,4, 2,5, 3,6)));
1057 }
1058 
1059 /**
1060 Returns shrinked 3x3 matrix
1061 The last row and column of the target are removed.
1062 
1063 Params:
1064     m = target matrix
1065 
1066 Returns: 3x3 matrix
1067 */
1068 Matrix!(T,3,3) toMatrix3(T)(const Matrix!(T,4,4) m) {
1069     return Matrix!(T,3,3)(m.column[0].xyz, m.column[1].xyz, m.column[2].xyz);
1070 }
1071 
1072 unittest {
1073     import std.algorithm : map;
1074     import std.random : uniform;
1075     import std.range : iota;
1076     import sbylib.math.vector : approxEqual;
1077 
1078     foreach (i; 0..100) {
1079         const p = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1080         const m = mat4(16.iota.map!(_ => uniform(-1.0f, +1.0f)));
1081 
1082         assert(approxEqual((m * vec4(p,0)).xyz, m.toMatrix3() * p));
1083     }
1084 }
1085 
1086 /**
1087 Returns expanded 4x4 matrix
1088 The last row and column of the result matrix are almost 0, but 3,3 element is 1.
1089 
1090 Params:
1091     m = target matrix
1092 
1093 Returns: 4x4 matrix
1094 */
1095 Matrix!(T,4,4) toMatrix4(T)(const Matrix!(T,3,3) m) {
1096     Matrix!(T,4,4) result;
1097     static foreach (i; 0..3) {
1098         static foreach (j; 0..3) {
1099             result[i,j] = m[i,j];
1100         }
1101     }
1102     static foreach (i; 0..3) {
1103         result[i,3] = 0;
1104         result[3,i] = 0;
1105     }
1106     result[3,3] = 1;
1107     return result;
1108 }
1109 
1110 unittest {
1111     import std.algorithm : map;
1112     import std.random : uniform;
1113     import std.range : iota;
1114     import sbylib.math.vector : approxEqual;
1115 
1116     foreach (i; 0..100) {
1117         const p = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1118         const m = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
1119 
1120         assert(approxEqual(m * p, (m.toMatrix4() * vec4(p,1)).xyz));
1121     }
1122 }
1123 
1124 /**
1125 Converts to quaternion.
1126 
1127 Returns: converted quaternion
1128 */
1129 Quaternion!T toQuaternion(T)(const Matrix!(T,3,3) m) {
1130     import std.math : sqrt, sgn;
1131 
1132     auto q0 = ( m[0,0] + m[1,1] + m[2,2] + 1.0f) / 4.0f;
1133     auto q1 = ( m[0,0] - m[1,1] - m[2,2] + 1.0f) / 4.0f;
1134     auto q2 = (-m[0,0] + m[1,1] - m[2,2] + 1.0f) / 4.0f;
1135     auto q3 = (-m[0,0] - m[1,1] + m[2,2] + 1.0f) / 4.0f;
1136     if(q0 < 0.0f) q0 = 0.0f;
1137     if(q1 < 0.0f) q1 = 0.0f;
1138     if(q2 < 0.0f) q2 = 0.0f;
1139     if(q3 < 0.0f) q3 = 0.0f;
1140     q0 = sqrt(q0);
1141     q1 = sqrt(q1);
1142     q2 = sqrt(q2);
1143     q3 = sqrt(q3);
1144     if(q0 >= q1 && q0 >= q2 && q0 >= q3) {
1145         q1 *= sgn(m[2,1] - m[1,2]);
1146         q2 *= sgn(m[0,2] - m[2,0]);
1147         q3 *= sgn(m[1,0] - m[0,1]);
1148     } else if(q1 >= q0 && q1 >= q2 && q1 >= q3) {
1149         q0 *= sgn(m[2,1] - m[1,2]);
1150         q2 *= sgn(m[1,0] + m[0,1]);
1151         q3 *= sgn(m[0,2] + m[2,0]);
1152     } else if(q2 >= q0 && q2 >= q1 && q2 >= q3) {
1153         q0 *= sgn(m[0,2] - m[2,0]);
1154         q1 *= sgn(m[1,0] + m[0,1]);
1155         q3 *= sgn(m[2,1] + m[1,2]);
1156     } else if(q3 >= q0 && q3 >= q1 && q3 >= q2) {
1157         q0 *= sgn(m[1,0] - m[0,1]);
1158         q1 *= sgn(m[2,0] + m[0,2]);
1159         q2 *= sgn(m[2,1] + m[1,2]);
1160     } else {
1161         assert(false);
1162     }
1163 
1164     auto result = Quaternion!T(q1, q2, q3, q0);
1165     return result.normalize;
1166 }
1167 
1168 unittest {
1169     import std.algorithm : map;
1170     import std.random : uniform;
1171     import std.range : iota;
1172     import sbylib.math.vector : approxEqual;
1173     import sbylib.math.quaternion : rotate;
1174 
1175     foreach (i; 0..100) {
1176         const p = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1177         const r = mat3.axisAngle(vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f))).normalize,
1178                 uniform(0,180.0).deg);
1179 
1180         assert(approxEqual(r * p, rotate(r.toQuaternion(), p), 1e-3));
1181     }
1182 }
1183 
1184 /**
1185 Get diagonal component from matrix
1186 
1187 Returns: diagonal vector of the matrix
1188 */
1189 Vector!(T,U) diagonal(T,uint U)(const Matrix!(T,U,U) m) {
1190     Vector!(T,U) result;
1191     static foreach (i; 0..U) {
1192         result[i] = m[i,i];
1193     }
1194     return result;
1195 }
1196 
1197 unittest {
1198     import std.range : iota;
1199     import sbylib.math.vector : approxEqual;
1200 
1201     assert(approxEqual(diagonal(mat3(iota(9))), vec3(0,4,8)));
1202 }
1203 
1204 /**
1205 Get translation information from matrix
1206 
1207 Returns: translation vector for the matrix
1208 */
1209 Vector!(T,U-1) getTranslation(T,uint U)(const Matrix!(T,U,U) m) 
1210 if (U > 0)
1211 {
1212     Vector!(T,U-1) result;
1213     static foreach (i; 0..U-1) {
1214         result[i] = m[i,U-1];
1215     }
1216     return result;
1217 }
1218 
1219 unittest {
1220     import std.algorithm : map;
1221     import std.random : uniform;
1222     import std.range : iota;
1223     import sbylib.math.vector : approxEqual;
1224 
1225     foreach (i; 0..100) {
1226         const t = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1227         const r = mat3.identity();
1228         const s = vec3(3.iota.map!(_ => uniform(-1.0f, +1.0f)));
1229 
1230         const m = mat4.makeTRS(t,r,s);
1231 
1232         assert(approxEqual(diagonal(m).xyz, s));
1233         assert(approxEqual(getTranslation(m), t));
1234     }
1235 }
1236 
1237 /**
1238 Get the determinant value of 2x2 matrix
1239 
1240 Params:
1241     m = target matrix
1242 
1243 Returns: determinant value of the target matrix
1244 */
1245 T determinant(T)(const Matrix!(T,2,2) m) {
1246     return m[0,0]*m[1,1] - m[0,1]*m[1,0];
1247 }
1248 
1249 /**
1250 Get the determinant value of 3x3 matrix
1251 
1252 Params:
1253     m = target matrix
1254 
1255 Returns: determinant value of the target matrix
1256 */
1257 T determinant(T)(const Matrix!(T,3,3) m) {
1258     return
1259         + m[0,0]*m[1,1]*m[2,2]
1260         + m[0,1]*m[1,2]*m[2,0]
1261         + m[0,2]*m[1,0]*m[2,1]
1262         - m[0,0]*m[1,2]*m[2,1]
1263         - m[0,1]*m[1,0]*m[2,2]
1264         - m[0,2]*m[1,1]*m[2,0];
1265 }
1266 
1267 /**
1268 Get the determinant value of 4x4 matrix
1269 
1270 Params:
1271     m = target matrix
1272 
1273 Returns: determinant value of the target matrix
1274 */
1275 T determinant(T)(const Matrix!(T,4,4) m) {
1276     const e2233_2332 = m[2,2] * m[3,3] - m[2,3] * m[3,2];
1277     const e2133_2331 = m[2,1] * m[3,3] - m[2,3] * m[3,1];
1278     const e2132_2231 = m[2,1] * m[3,2] - m[2,2] * m[3,1];
1279     const e2033_2330 = m[2,0] * m[3,3] - m[2,3] * m[3,0];
1280     const e2032_2230 = m[2,0] * m[3,2] - m[2,2] * m[3,0];
1281     const e2031_2130 = m[2,0] * m[3,1] - m[2,1] * m[3,0];
1282     return
1283         m[0,0] * (m[1,1] * e2233_2332 - m[1,2] * e2133_2331 + m[1,3] * e2132_2231) -
1284         m[0,1] * (m[1,0] * e2233_2332 - m[1,2] * e2033_2330 + m[1,3] * e2032_2230) +
1285         m[0,2] * (m[1,0] * e2133_2331 - m[1,1] * e2033_2330 + m[1,3] * e2031_2130) -
1286         m[0,3] * (m[1,0] * e2132_2231 - m[1,1] * e2032_2230 + m[1,2] * e2031_2130)
1287     ;
1288 }
1289 
1290 /**
1291 Get 2x2 inverse matrix
1292 
1293 Params:
1294     m = target matrix
1295 
1296 Returns: inverse matrix of the target matrix
1297 */
1298 Matrix!(T,2,2) invert(T)(const Matrix!(T,2,2) m) {
1299     auto det = determinant(m);
1300     if (det != 0) det = 1 / det;
1301     Matrix!(T,2,2) r;
1302     r[0,0] = +m[1,1] * det;
1303     r[0,1] = -m[0,1] * det;
1304     r[1,0] = -m[1,0] * det;
1305     r[1,1] = +m[0,0] * det;
1306     return r;
1307 }
1308 
1309 unittest {
1310     import std.algorithm : map;
1311     import std.range : iota;
1312     import std.random : uniform;
1313     foreach (i; 0..100) {
1314         const m = mat2(4.iota.map!(_ => uniform(-1.0f, +1.0f)));
1315         assert(determinant(m) == 0 || approxEqual(m * invert(m), mat2.identity, 1e-3));
1316     }
1317 }
1318 
1319 /**
1320 Get 3x3 inverse matrix
1321 
1322 Params:
1323     m = target matrix
1324 
1325 Returns: inverse matrix of the target matrix
1326 */
1327 Matrix!(T,3,3) invert(T)(const Matrix!(T,3,3) m) {
1328     auto det = determinant(m);
1329     if (det != 0) det = 1 / det;
1330     Matrix!(T,3,3) r;
1331     r[0,0] = (m[1,1]*m[2,2] - m[1,2]*m[2,1]) * det;
1332     r[0,1] = (m[0,2]*m[2,1] - m[0,1]*m[2,2]) * det;
1333     r[0,2] = (m[0,1]*m[1,2] - m[0,2]*m[1,1]) * det;
1334     r[1,0] = (m[1,2]*m[2,0] - m[1,0]*m[2,2]) * det;
1335     r[1,1] = (m[0,0]*m[2,2] - m[0,2]*m[2,0]) * det;
1336     r[1,2] = (m[0,2]*m[1,0] - m[0,0]*m[1,2]) * det;
1337     r[2,0] = (m[1,0]*m[2,1] - m[1,1]*m[2,0]) * det;
1338     r[2,1] = (m[0,1]*m[2,0] - m[0,0]*m[2,1]) * det;
1339     r[2,2] = (m[0,0]*m[1,1] - m[0,1]*m[1,0]) * det;
1340     return r;
1341 }
1342 
1343 unittest {
1344     import std.algorithm : map;
1345     import std.range : iota;
1346     import std.random : uniform;
1347     foreach (i; 0..100) {
1348         const m = mat3(9.iota.map!(_ => uniform(-1.0f, +1.0f)));
1349         assert(determinant(m) == 0 || approxEqual(m * invert(m), mat3.identity, 1e-3));
1350     }
1351 }
1352 
1353 /**
1354 Get 4x4 inverse matrix
1355 
1356 Params:
1357     m = target matrix
1358 
1359 Returns: inverse matrix of the target matrix
1360 */
1361 Matrix!(T,4,4) invert(T)(const Matrix!(T,4,4) m) {
1362     auto det = determinant(m);
1363     if (det != 0) det = 1 / det;
1364     const e2233_2332 = m[2,2] * m[3,3] - m[2,3] * m[3,2];
1365     const e2133_2331 = m[2,1] * m[3,3] - m[2,3] * m[3,1];
1366     const e2132_2231 = m[2,1] * m[3,2] - m[2,2] * m[3,1];
1367     const e1233_1332 = m[1,2] * m[3,3] - m[1,3] * m[3,2];
1368     const e1133_1331 = m[1,1] * m[3,3] - m[1,3] * m[3,1];
1369     const e1132_1231 = m[1,1] * m[3,2] - m[1,2] * m[3,1];
1370     const e1322_1223 = m[1,3] * m[2,2] - m[1,2] * m[2,3];
1371     const e1123_1321 = m[1,1] * m[2,3] - m[1,3] * m[2,1];
1372     const e1122_1221 = m[1,1] * m[2,2] - m[1,2] * m[2,1];
1373     const e2033_2330 = m[2,0] * m[3,3] - m[2,3] * m[3,0];
1374     const e2032_2230 = m[2,0] * m[3,2] - m[2,2] * m[3,0];
1375     const e1033_1330 = m[1,0] * m[3,3] - m[1,3] * m[3,0];
1376     const e1032_1230 = m[1,0] * m[3,2] - m[1,2] * m[3,0];
1377     const e1023_1320 = m[1,0] * m[2,3] - m[1,3] * m[2,0];
1378     const e1022_1220 = m[1,0] * m[2,2] - m[1,2] * m[2,0];
1379     const e2031_2130 = m[2,0] * m[3,1] - m[2,1] * m[3,0];
1380     const e1031_1130 = m[1,0] * m[3,1] - m[1,1] * m[3,0];
1381     const e1021_1120 = m[1,0] * m[2,1] - m[1,1] * m[2,0];
1382     const t00 =  m[1,1] * e2233_2332 - m[1,2] * e2133_2331 + m[1,3] * e2132_2231;
1383     const t01 = -m[0,1] * e2233_2332 + m[0,2] * e2133_2331 - m[0,3] * e2132_2231;
1384     const t02 =  m[0,1] * e1233_1332 - m[0,2] * e1133_1331 + m[0,3] * e1132_1231;
1385     const t03 =  m[0,1] * e1322_1223 + m[0,2] * e1123_1321 - m[0,3] * e1122_1221;
1386     const t10 = -m[1,0] * e2233_2332 + m[1,2] * e2033_2330 - m[1,3] * e2032_2230;
1387     const t11 =  m[0,0] * e2233_2332 - m[0,2] * e2033_2330 + m[0,3] * e2032_2230;
1388     const t12 = -m[0,0] * e1233_1332 + m[0,2] * e1033_1330 - m[0,3] * e1032_1230;
1389     const t13 = -m[0,0] * e1322_1223 - m[0,2] * e1023_1320 + m[0,3] * e1022_1220;
1390     const t20 =  m[1,0] * e2133_2331 - m[1,1] * e2033_2330 + m[1,3] * e2031_2130;
1391     const t21 = -m[0,0] * e2133_2331 + m[0,1] * e2033_2330 - m[0,3] * e2031_2130;
1392     const t22 =  m[0,0] * e1133_1331 - m[0,1] * e1033_1330 + m[0,3] * e1031_1130;
1393     const t23 = -m[0,0] * e1123_1321 + m[0,1] * e1023_1320 - m[0,3] * e1021_1120;
1394     const t30 = -m[1,0] * e2132_2231 + m[1,1] * e2032_2230 - m[1,2] * e2031_2130;
1395     const t31 =  m[0,0] * e2132_2231 - m[0,1] * e2032_2230 + m[0,2] * e2031_2130;
1396     const t32 = -m[0,0] * e1132_1231 + m[0,1] * e1032_1230 - m[0,2] * e1031_1130;
1397     const t33 =  m[0,0] * e1122_1221 - m[0,1] * e1022_1220 + m[0,2] * e1021_1120;
1398     Matrix!(T,4,4) r;
1399     r[0,0] =  det * t00;
1400     r[0,1] =  det * t01;
1401     r[0,2] =  det * t02;
1402     r[0,3] =  det * t03;
1403     r[1,0] =  det * t10;
1404     r[1,1] =  det * t11;
1405     r[1,2] =  det * t12;
1406     r[1,3] =  det * t13;
1407     r[2,0] =  det * t20;
1408     r[2,1] =  det * t21;
1409     r[2,2] =  det * t22;
1410     r[2,3] =  det * t23;
1411     r[3,0] =  det * t30;
1412     r[3,1] =  det * t31;
1413     r[3,2] =  det * t32;
1414     r[3,3] =  det * t33;
1415     return r;
1416 }
1417 
1418 unittest {
1419     import std.algorithm : map;
1420     import std.range : iota;
1421     import std.random : uniform;
1422     foreach (i; 0..100) {
1423         const m = mat4(16.iota.map!(_ => uniform(-1.0f, +1.0f)));
1424         assert(determinant(m) == 0 || approxEqual(m * invert(m), mat4.identity, 1e-3));
1425     }
1426 }
1427 
1428 /**
1429 Calculate the diagonalize matrix of the target matrix.
1430 
1431 Params:
1432     m = target matrix, which must be real symmetric
1433 
1434 Returns: the diagonalize matrix
1435 */
1436 Matrix!(T,U,U) diagonalizeForRealSym(T, uint U)(Matrix!(T,U,U) m, T eps = 1e-5) 
1437 if (__traits(isFloating, T))
1438 {
1439     import std.math : abs, sqrt;
1440 
1441     T getMaxValue(ref Matrix!(T,U,U) m, out uint p, out uint q) {
1442         T max = 0;
1443         foreach (i; 0..U) {
1444             foreach (j; 0..U) {
1445                 if (i == j) continue;
1446                 if (m[i,j].abs > max) {
1447                     max = m[i,j].abs;
1448                     p = i;
1449                     q = j;
1450                 }
1451             }
1452         }
1453         return max;
1454     }
1455 
1456     auto result = Matrix!(T,U,U).identity;
1457     T max;
1458     uint p, q;
1459     uint bp = 114514, bq = 114514;
1460     while (true) {
1461         if ((max = getMaxValue(m, p, q)) < eps) break;
1462         if (p == bp && q == bq) break;
1463         const pp = m[p,p];
1464         const pq = m[p,q];
1465         const qq = m[q,q];
1466         T alpha = (pp - qq) / 2.0;
1467         T beta = -pq;
1468         T gamma = abs(alpha) / sqrt(alpha*alpha+beta*beta);
1469         T s = sqrt((1.0-gamma) / 2.0);
1470         const c = sqrt((1.0+gamma) / 2.0);
1471         if (alpha * beta < 0) s = -s;
1472         static foreach (i; 0..U) {{
1473             const tmp = c * m[p, i] - s * m[q, i];
1474             m[q, i] = s * m[p, i] + c * m[q, i];
1475             m[p, i] = tmp;
1476         }}
1477         static foreach (i; 0..U) {
1478             m[i,p] = m[p,i];
1479             m[i,q] = m[q,i];
1480         }
1481         m[p,p] = c*c*pp + s*s*qq - 2*s*c*pq;
1482         m[p,q] = s*c*(pp-qq) + (c*c-s*s) * pq;
1483         m[q,p] = m[p,q];
1484         m[q,q] = s*s*pp + c*c*qq + 2*s*c*pq;
1485         static foreach (i; 0..U) {{
1486             const tmp = c*result[i,p]-s*result[i,q];
1487             result[i,q] = s*result[i,p] + c*result[i,q];
1488             result[i,p] = tmp;
1489         }}
1490         bp = p;
1491         bq = q;
1492     }
1493     return result;
1494 }
1495 
1496 unittest {
1497     import std.algorithm : map;
1498     import std.array : array;
1499     import std.range : iota;
1500     import std.random : uniform;
1501     import sbylib.math.vector : vapproxEqual = approxEqual;
1502     foreach (k; 0..100) {
1503         // m is real symmetric matrix
1504         auto m = mat4(16.iota.map!(_ => uniform(-1.0f, +1.0f)));
1505         static foreach (i; 0..4) {
1506             static foreach (j; 0..4) {
1507                 m[i,j] = m[j,i];
1508             }
1509         }
1510         assert(m.isSymmetric);
1511 
1512         // p is diagonalize matrix of m
1513         const p = diagonalizeForRealSym(m);
1514 
1515         // a is diagonalized matrix
1516         const a = transpose(p) * m * p;
1517 
1518         // successfully diagonalized
1519         assert(approxEqual(a, mat4.scale(diagonal(a)), 1e-3));
1520 
1521         // if m is real symmetric, p is orthognal matrix
1522         assert(p.isOrthogonal);
1523 
1524         static foreach (i; 0..4) {{
1525             const eigenVector = p.column[i];
1526             const eigenValue = a[i,i];
1527 
1528             assert(vapproxEqual(m * eigenVector, eigenVector * eigenValue, 1e-3));
1529         }}
1530     }
1531 }
1532 
1533 /**
1534 Returns true if the given matrix is symmetric.
1535 
1536 Params:
1537     m = target matrix
1538     eps = criteria of approximation
1539 
1540 Returns: true if the given matrix is symmetric
1541 */
1542 bool isSymmetric(T, uint U)(const Matrix!(T,U,U) m, T eps = 1e-5) {
1543     return approxEqual(m, transpose(m), eps);
1544 }
1545 
1546 /**
1547 Returns true if the given matrix is orthogonal.
1548 
1549 Params:
1550     m = target matrix
1551     eps = criteria of approximation
1552 
1553 Returns: true if the given matrix is orthogonal
1554 */
1555 bool isOrthogonal(T, uint U)(const Matrix!(T,U,U) m, T eps = 1e-5) {
1556     return approxEqual(invert(m), transpose(m), eps);
1557 }
1558 
1559 /**
1560 Returns true if the distance of each element is less than eps.
1561 
1562 Params:
1563     a = target point
1564     b = target point
1565     eps = criteria of approximation
1566 
1567 Returns: true if 2 vectors are approximately equal.
1568 */
1569 bool approxEqual(T,uint U,uint V)(const Matrix!(T,U,V) a, const Matrix!(T,U,V) b, T eps = 1e-5) {
1570     import std.math : abs;
1571 
1572     static foreach (i; 0..U) {
1573         static foreach (j; 0..V) {
1574             if (abs(a[i,j] - b[i,j]) > eps) return false;
1575         }
1576     }
1577     return true;
1578 }