1 /** 
2  * This module contains the `Mat` templated struct representing matrices and
3  * their operations.
4  */
5 module dvec.matrix;
6 
7 import std.traits : isNumeric, isFloatingPoint;
8 import dvec.vector;
9 import dvec.vector_types;
10 
11 /** 
12  * Generic struct that represents a matrix with `rowCount` rows and `colCount`
13  * columns, holding elements of type `T`. A matrix must be at least 1x1.
14  */
15 struct Mat(T, size_t rowCount, size_t colCount) if (isNumeric!T && rowCount > 0 && colCount > 0) {
16     public static const size_t WIDTH = colCount;
17     public static const size_t HEIGHT = rowCount;
18 
19     /** 
20      * The internal static array storing the elements in row-major form.
21      */
22     public T[rowCount * colCount] data;
23 
24     /** 
25      * Constructs a matrix from the given elements.
26      * Params:
27      *   elements = The elements to place in the matrix. Should be in row-major
28      *              form.
29      */
30     public this(T[rowCount * colCount] elements) {
31         setData(elements);
32     }
33 
34     /** 
35      * Constructs a matrix from the given elements.
36      * Params:
37      *   elements = The elements to place in the matrix. Should be in row-major
38      *              form.
39      */
40     public this(T[] elements...) {
41         data[0 .. $] = elements[0 .. data.length];
42     }
43 
44     /** 
45      * Constructs a matrix from the given 2-dimensional array.
46      * Params:
47      *   elements = The elements to place in the matrix.
48      */
49     public this(T[colCount][rowCount] elements) {
50         static foreach (i; 0 .. rowCount) {
51             static foreach (j; 0 .. colCount) {
52                 this[i, j] = elements[i][j];
53             }
54         }
55     }
56 
57     /** 
58      * Constructs a matrix as a copy of another.
59      * Params:
60      *   other = The matrix to copy.
61      */
62     public this(Mat!(T, rowCount, colCount) other) {
63         setData(other.data);
64     }
65 
66     /** 
67      * Constructs a matrix with all elements initialized to the given value.
68      * Params:
69      *   value = The value to set all elements to.
70      */
71     public this(T value) {
72         static foreach (i; 0 .. data.length) data[i] = value;
73     }
74 
75     /** 
76      * Helper method to convert a 2-dimensional `[i, j]` index into a
77      * 1-dimensional `[i]` index.
78      * Params:
79      *   i = The row number.
80      *   j = The column number.
81      * Returns: A 1-dimensional index.
82      */
83     private static size_t convertToIndex(size_t i, size_t j) {
84         return colCount * i + j;
85     }
86 
87     /** 
88      * Gets the value at a specified location in the matrix. For example,
89      * with a `Mat!(float, 2, 2) m`, we can say `float v = m[0, 1];`
90      * Params:
91      *   i = The row number, starting from 0.
92      *   j = The column number, starting from 0.
93      * Returns: The value at the specified location.
94      */
95     public T opIndex(size_t i, size_t j) const {
96         return data[convertToIndex(i, j)];
97     }
98 
99     /** 
100      * Sets the value at a specified location in the matrix.
101      * Params:
102      *   value = The value to assign.
103      *   i = The row number, starting from 0.
104      *   j = The column number, starting from 0.
105      */
106     public void opIndexAssign(T value, size_t i, size_t j) {
107         data[convertToIndex(i, j)] = value;
108     }
109 
110     /** 
111      * Gets a specified row as a vector.
112      * Params:
113      *   row = The row number, starting from 0.
114      * Returns: The row.
115      */
116     public Vec!(T, colCount) getRow(size_t row) {
117         size_t idx = convertToIndex(row, 0);
118         return Vec!(T, colCount)(data[idx .. idx + colCount]);
119     }
120 
121     /** 
122      * Sets a specified row to the given vector of elements.
123      * Params:
124      *   row = The row number, starting from 0.
125      *   vector = The elements to set in the row.
126      * Returns: A reference to this matrix, for method chaining.
127      */
128     public ref Mat!(T, rowCount, colCount) setRow(size_t row, Vec!(T, colCount) vector) {
129         size_t idx = convertToIndex(row, 0);
130         data[idx .. idx + colCount] = vector.data;
131         return this;
132     }
133 
134     /** 
135      * Gets a specified column as a vector.
136      * Params:
137      *   col = The column number, starting from 0.
138      * Returns: The column.
139      */
140     public Vec!(T, rowCount) getCol(size_t col) const {
141         Vec!(T, rowCount) v;
142         static foreach (i; 0 .. rowCount) {
143             v[i] = this[i, col];
144         }
145         return v;
146     }
147 
148     /** 
149      * Sets a specified column to the given vector of elements.
150      * Params:
151      *   col = The column number, starting from 0.
152      *   vector = The elements to set in the column.
153      * Returns: A reference to this matrix, for method chaining.
154      */
155     public ref Mat!(T, rowCount, colCount) setCol(size_t col, Vec!(T, rowCount) vector) {
156         static foreach (i; 0 .. rowCount) {
157             this[i, col] = vector[i];
158         }
159         return this;
160     }
161 
162     /** 
163      * Sets all elements of this matrix using the given elements.
164      * Params:
165      *   elements = The elements to set.
166      * Returns: A reference to this matrix, for method chaining.
167      */
168     public ref Mat!(T, rowCount, colCount) setData(T[rowCount * colCount] elements) {
169         data[0 .. $] = elements[0 .. $];
170         return this;
171     }
172 
173     /** 
174      * Gets a copy of this matrix.
175      * Returns: A copy of this matrix.
176      */
177     public Mat!(T, rowCount, colCount) copy() const {
178         return Mat!(T, rowCount, colCount)(this);
179     }
180 
181     /** 
182      * Adds a given matrix to this one.
183      * Params:
184      *   other = The other matrix to add to this one.
185      * Returns: A reference to this matrix, for method chaining.
186      */
187     public ref Mat!(T, rowCount, colCount) add(Mat!(T, rowCount, colCount) other) {
188         static foreach (i; 0 .. data.length) data[i] += other.data[i];
189         return this;
190     }
191 
192     /** 
193      * Subtracts a given matrix from this one.
194      * Params:
195      *   other = The other matrix to subtract from this one.
196      * Returns: A reference to this matrix, for method chaining.
197      */
198     public ref Mat!(T, rowCount, colCount) sub(Mat!(T, rowCount, colCount) other) {
199         static foreach (i; 0 .. data.length) data[i] -= other.data[i];
200         return this;
201     }
202 
203     /** 
204      * Multiplies this matrix by a factor.
205      * Params:
206      *   factor = The factor to muliply by.
207      * Returns: A reference to this matrix, for method chaining.
208      */
209     public ref Mat!(T, rowCount, colCount) mul(T factor) {
210         static foreach (i; 0 .. data.length) data[i] *= factor;
211         return this;
212     }
213 
214     /** 
215      * Divides this matrix by a factor.
216      * Params:
217      *   factor = The factor to divide by.
218      * Returns: A reference to this matrix, for method chaining.
219      */
220     public ref Mat!(T, rowCount, colCount) div(T factor) {
221         static foreach (i; 0 .. data.length) data[i] /= factor;
222         return this;
223     }
224 
225     /** 
226      * Gets the [transpose](https://en.wikipedia.org/wiki/Transpose) of this
227      * matrix, and stores it in this matrix.
228      * Returns: A reference to this matrix, for method chaining.
229      */
230     public ref Mat!(T, rowCount, colCount) transpose() {
231         Mat!(T, colCount, rowCount) m;
232         static foreach (i; 0 .. rowCount) {
233             static foreach (j; 0 .. colCount) {
234                 m[j, i] = this[i, j];
235             }
236         }
237         return setData(m.data);
238     }
239 
240     /** 
241      * Computes the [matrix multiplication](https://en.wikipedia.org/wiki/Matrix_multiplication)
242      * of `this * other`.
243      * Params:
244      *   other = The matrix to multiply with this one.
245      * Returns: A new matrix of size `this.rowCount x other.colCount`.
246      */
247     public Mat!(T, rowCount, otherColCount) mul(T, size_t otherRowCount, size_t otherColCount)
248         (Mat!(T, otherRowCount, otherColCount) other) const {
249         Mat!(T, rowCount, otherColCount) m;
250         T sum;
251         static foreach (i; 0 .. rowCount) {
252             static foreach (j; 0 .. otherColCount) {
253                 sum = 0;
254                 static foreach (k; 0 .. colCount) {
255                     sum += this[i, k] * other[k, j];
256                 }
257                 m[i, j] = sum;
258             }
259         }
260         return m;
261     }
262 
263     /** 
264      * Multiplies a vector against this matrix.
265      * Params:
266      *   vector = The vector to multiply.
267      * Returns: The resultant transformed vector.
268      */
269     public Vec!(T, rowCount) mul(Vec!(T, colCount) vector) const {
270         Vec!(T, rowCount) result;
271         T sum;
272         static foreach (i; 0 .. rowCount) {
273             sum = 0;
274             static foreach (j; 0 .. colCount) {
275                 sum += this[i, j] * vector[j];
276             }
277             result[i] = sum;
278         }
279         return result;
280     }
281 
282     /** 
283      * Switches two rows in this matrix.
284      * Params:
285      *   rowI = A row to swap, starting from 0.
286      *   rowJ = A row to swap, starting from 0.
287      * Returns: A reference to this matrix, for method chaining.
288      */
289     public ref Mat!(T, rowCount, colCount) rowSwitch(size_t rowI, size_t rowJ) {
290         auto r = getRow(rowI);
291         setRow(rowI, getRow(rowJ));
292         setRow(rowJ, r);
293         return this;
294     }
295 
296     /** 
297      * Multiplies a row by a factor.
298      * Params:
299      *   row = The row number, starting from 0.
300      *   factor = The factor to multiply by.
301      * Returns: A reference to this matrix, for method chaining.
302      */
303     public ref Mat!(T, rowCount, colCount) rowMultiply(size_t row, T factor) {
304         size_t idx = convertToIndex(row, 0);
305         static foreach (i; 0 .. colCount) {
306             data[idx + i] *= factor;
307         }
308         return this;
309     }
310 
311     /** 
312      * Adds a row, multiplied by a factor, to another row.
313      * Params:
314      *   rowI = The row number to add to.
315      *   factor = The factor to multiply `rowJ` by.
316      *   rowJ = The row to add to `rowI`.
317      * Returns: A reference to this matrix, for method chaining.
318      */
319     public ref Mat!(T, rowCount, colCount) rowAdd(size_t rowI, T factor, size_t rowJ) {
320         auto row = getRow(rowJ);
321         row.mul(factor);
322         setRow(rowI, row);
323         return this;
324     }
325 
326     /** 
327      * Gets a submatrix of this matrix, with the given rows and columns removed.
328      * Params:
329      *   rows = The set of rows to remove.
330      *   cols = The set of columns to remove.
331      * Returns: A matrix with the given rows and columns removed.
332      */
333     public Mat!(T, rowCount - n, colCount - m) subMatrix(size_t n, size_t m)(size_t[n] rows, size_t[m] cols) const
334         if (rowCount - n > 0 && colCount - m > 0) {
335         // TODO: Improve efficiency with static stuff.
336         Mat!(T, rowCount - n, colCount - m) sub;
337         size_t subIdx = 0;
338         foreach (idx; 0 .. data.length) {
339             size_t row = idx / colCount;
340             size_t col = idx % colCount;
341             bool skip = false;
342             foreach (r; rows) {
343                 if (r == row) {
344                     skip = true;
345                     break;
346                 }
347             }
348             if (!skip) {
349                 foreach (c; cols) {
350                     if (c == col) {
351                         skip = true;
352                         break;
353                     }
354                 }
355             }
356             if (!skip) {
357                 sub.data[subIdx++] = this[row, col];
358             }
359         }
360         return sub;
361     }
362 
363     /** 
364      * Converts this matrix to a string.
365      * Returns: A string representation of the matrix.
366      */
367     public string toString() const {
368         import std.conv : to;
369         import std.algorithm : max;
370         import std.array : appender, replicate;
371         string[data.length] values;
372         size_t[colCount] columnWidths;
373         foreach (i; 0 .. data.length) {
374             values[i] = data[i].to!string;
375             size_t colIdx = i % colCount;
376             columnWidths[colIdx] = max(columnWidths[colIdx], values[i].length + 1);
377         }
378         auto s = appender!string;
379         foreach (r; 0 .. rowCount) {
380             s ~= "| ";
381             foreach (c; 0 .. colCount) {
382                 size_t idx = convertToIndex(r, c);
383                 size_t padAmount = columnWidths[c] - values[idx].length;
384                 string padding = replicate(" ", padAmount);
385                 s ~= padding ~ values[idx];
386                 if (c < colCount - 1) s ~= ", ";
387             }
388             s ~= " |";
389             if (r < rowCount - 1) s ~= "\n";
390         }
391         return s[];
392     }
393 
394     // Special methods for square matrices.
395     static if (rowCount == colCount) {
396         alias N = rowCount;
397 
398         /** 
399          * Gets an [identity matrix](https://en.wikipedia.org/wiki/Identity_matrix).
400          * Returns: An identity matrix.
401          */
402         public static Mat!(T, N, N) identity() {
403             Mat!(T, N, N) m;
404             static foreach (i; 0 .. N) {
405                 static foreach (j; 0 .. N) {
406                     m[i, j] = i == j ? 1 : 0;
407                 }
408             }
409             return m;
410         }
411 
412         /** 
413          * Gets the [determinant](https://en.wikipedia.org/wiki/Determinant)
414          * of this matrix.
415          * Returns: The determinant of this matrix.
416          */
417         public T det() const {
418             static if (N == 1) {
419                 return data[0];
420             } else static if (N == 2) {
421                 return data[0] * data[3] - data[1] * data[2];
422             } else {
423                 // Laplace expansion, taking i = 0.
424                 T sum = 0;
425                 static foreach (j; 0 .. N) {
426                     sum += (j % 2 == 0 ? 1 : -1) * this[0, j] * this.subMatrix([0], [j]).det();
427                 }
428                 return sum;
429             }
430         }
431 
432         /** 
433          * Determines if this matrix is invertible, which simply means a
434          * non-zero determinant.
435          * Returns: True if this matrix is invertible.
436          */
437         public bool invertible() const {
438             return det() != 0;
439         }
440 
441         /** 
442          * Gets a [cofactor matrix](https://en.wikipedia.org/wiki/Minor_(linear_algebra)#Cofactor_expansion_of_the_determinant).
443          * Returns: A reference to this matrix, for method chaining.
444          */
445         public ref Mat!(T, N, N) cofactor() {
446             static if (N > 1) {
447                 Mat!(T, N, N) c;
448                 static foreach (i; 0 .. N) {
449                     static foreach (j; 0 .. N) {
450                         c[i, j] = ((i + j) % 2 == 0 ? 1 : -1) * this.subMatrix([i], [j]).det();
451                     }
452                 }
453                 setData(c.data);
454             }
455             return this;
456         }
457 
458         /** 
459          * Gets an [adjugate matrix](https://en.wikipedia.org/wiki/Adjugate_matrix).
460          * Returns: A reference to this matrix, for method chaining.
461          */
462         public ref Mat!(T, N, N) adjugate() {
463             cofactor();
464             transpose();
465             return this;
466         }
467 
468         /** 
469          * Gets the inverse of this matrix.
470          * Returns: A reference to this matrix, for method chaining.
471          */
472         public ref Mat!(T, N, N) inv() {
473             T d = det();
474             adjugate();
475             div(d);
476             return this;
477         }
478 
479         // Special case for 3x3 floating-point matrices: linear transformations
480         static if (N == 3 && isFloatingPoint!T) {
481             /** 
482              * Applies a 2D rotation to this matrix.
483              * Params:
484              *   theta = The angle in radians.
485              * Returns: A reference to this matrix, for method chaining.
486              */
487             public ref Mat!(T, N, N) rotate(T theta) {
488                 import std.math : cos, sin;
489                 this = mul(Mat!(T, N, N)(
490                     cos(theta), -sin(theta), 0,
491                     sin(theta), cos(theta),  0,
492                     0,          0,           1
493                 ));
494                 return this;
495             }
496 
497             /** 
498              * Applies a 2D translation to this matrix.
499              * Params:
500              *   dx = The translation on the x-axis.
501              *   dy = The translation on the y-axis.
502              * Returns: A reference to this matrix, for method chaining.
503              */
504             public ref Mat!(T, N, N) translate(T dx, T dy) {
505                 this = mul(Mat!(T, N, N)(
506                     1, 0, dx,
507                     0, 1, dy,
508                     0, 0, 1
509                 ));
510                 return this;
511             }
512 
513             /** 
514              * Applies a 2D scaling to this matrix.
515              * Params:
516              *   sx = The scale factor on the x-axis.
517              *   sy = The scale factor on the y-axis.
518              * Returns: A reference to this matrix, for method chaining.
519              */
520             public ref Mat!(T, N, N) scale(T sx, T sy) {
521                 this = mul(Mat!(T, N, N)(
522                     sx, 0,  0,
523                     0,  sy, 0,
524                     0,  0,  1
525                 ));
526                 return this;
527             }
528 
529             /** 
530              * Applies a uniform 2D scaling to this matrix on all axes.
531              * Params:
532              *   s = The scale factor to apply.
533              * Returns: A reference to this matrix, for method chaining.
534              */
535             public ref Mat!(T, N, N) scale(T s) {
536                 return scale(s, s);
537             }
538 
539             /** 
540              * Applies a 2D shear to this matrix.
541              * Params:
542              *   sx = The shear factor on the x-axis.
543              *   sy = The shear factor on the y-axis.
544              * Returns: A reference to this matrix, for method chaining.
545              */
546             public ref Mat!(T, N, N) shear(T sx, T sy) {
547                 this = mul(Mat!(T, N, N)(
548                     1,  sx, 0,
549                     sy, 1,  0,
550                     0,  0,  1
551                 ));
552                 return this;
553             }
554 
555             /** 
556              * Maps the given 2D vector using this matrix.
557              * Params:
558              *   v = The vector to map.
559              * Returns: The resultant vector.
560              */
561             public Vec!(T, 2) map(Vec!(T, 2) v) {
562                 Vec!(T, 3) v1 = this.mul(Vec!(T, 3)(v[0], v[1], 1));
563                 return Vec!(T, 2)(v1[0], v1[1]);
564             }
565         }
566 
567         // Special case for 4x4 floating-point matrices: linear transformations
568         static if (N == 4 && isFloatingPoint!T) {
569             /** 
570              * Applies a 3D translation to this matrix.
571              * Params:
572              *   dx = The translation on the x-axis.
573              *   dy = The translation on the y-axis.
574              *   dz = The translation on the z-axis.
575              * Returns: A reference to this matrix, for method chaining.
576              */
577             public ref Mat!(T, N, N) translate(T dx, T dy, T dz) {
578                 this = mul(Mat!(T, N, N)(
579                     1, 0, 0, dx,
580                     0, 1, 0, dy,
581                     0, 0, 1, dz,
582                     0, 0, 0, 1
583                 ));
584                 return this;
585             }
586 
587             /** 
588              * Applies a 3D scaling to this matrix.
589              * Params:
590              *   sx = The scale factor on the x-axis.
591              *   sy = The scale factor on the y-axis.
592              *   sz = The scale factor on the z-axis.
593              * Returns: A reference to this matrix, for method chaining.
594              */
595             public ref Mat!(T, N, N) scale(T sx, T sy, T sz) {
596                 this = mul(Mat!(T, N, N)(
597                     sx, 0,  0,  0,
598                     0,  sy, 0,  0,
599                     0,  0,  sz, 0,
600                     0,  0,  0,  1
601                 ));
602                 return this;
603             }
604 
605             /** 
606              * Applies a uniform 3D scaling to this matrix on all axes.
607              * Params:
608              *   s = The scale factor to apply.
609              * Returns: A reference to this matrix, for method chaining.
610              */
611             public ref Mat!(T, N, N) scale(T s) {
612                 return scale(s, s, s);
613             }
614 
615             /** 
616              * Applies a rotation to this matrix about the x-axis.
617              * Params:
618              *   theta = The angle in radians.
619              * Returns: A reference to this matrix, for method chaining.
620              */
621             public ref Mat!(T, N, N) rotateX(T theta) {
622                 import std.math : cos, sin;
623                 this = mul(Mat!(T, N, N)(
624                     1, 0,          0,           0,
625                     0, cos(theta), -sin(theta), 0,
626                     0, sin(theta), cos(theta),  0,
627                     0, 0,          0,           1
628                 ));
629                 return this;
630             }
631 
632             /** 
633              * Applies a rotation to this matrix about the y-axis.
634              * Params:
635              *   theta = The angle in radians.
636              * Returns: A reference to this matrix, for method chaining.
637              */
638             public ref Mat!(T, N, N) rotateY(T theta) {
639                 import std.math : cos, sin;
640                 this = mul(Mat!(T, N, N)(
641                     cos(theta),  0, sin(theta), 0,
642                     0,           1, 0,          0,
643                     -sin(theta), 0, cos(theta), 0,
644                     0,           0, 0,          1
645                 ));
646                 return this;
647             }
648 
649             /** 
650              * Applies a rotation to this matrix about the z-axis.
651              * Params:
652              *   theta = The angle in radians.
653              * Returns: A reference to this matrix, for method chaining.
654              */
655             public ref Mat!(T, N, N) rotateZ(T theta) {
656                 import std.math : cos, sin;
657                 this = mul(Mat!(T, N, N)(
658                     cos(theta), -sin(theta), 0, 0,
659                     sin(theta), cos(theta),  0, 0,
660                     0,          0,           1, 0,
661                     0,          0,           0, 1
662                 ));
663                 return this;
664             }
665 
666             /** 
667              * Applies a 3D rotation to this matrix about the x, y, and then z
668              * axes.
669              * Params:
670              *   x = The angle to rotate about the x-axis.
671              *   y = The angle to rotate about the y-axis.
672              *   z = The angle to rotate about the z-axis.
673              * Returns: A reference to this matrix, for method chaining.
674              */
675             public ref Mat!(T, N, N) rotate(T x, T y, T z) {
676                 rotateX(x);
677                 rotateY(y);
678                 rotateZ(z);
679                 return this;
680             }
681         }
682     }
683 }
684 
685 // Aliases for common matrix types.
686 alias Mat2f = Mat!(float, 2, 2);
687 alias Mat3f = Mat!(float, 3, 3);
688 alias Mat4f = Mat!(float, 4, 4);
689 
690 alias Mat2d = Mat!(double, 2, 2);
691 alias Mat3d = Mat!(double, 3, 3);
692 alias Mat4d = Mat!(double, 4, 4);
693 
694 unittest {
695     import std.stdio;
696     import dvec.vector;
697 
698     auto m1 = Mat3d();
699     assert(m1.data.length == 9);
700 
701     auto m2 = Mat!(double, 2, 3)([1, 2, 3, 0, -6, 7]);
702     assert(m2.transpose.data == [1, 0, 2, -6, 3, 7]);
703 
704     auto m4 = Mat2d([1, 2, 3, 4]);
705     assert(m4.getRow(0).data == [1, 2]);
706     assert(m4.getRow(1).data == [3, 4]);
707     assert(m4.getCol(0).data == [1, 3]);
708     assert(m4.getCol(1).data == [2, 4]);
709     auto m5 = m4.mul(Mat2d([0, 1, 0, 0]));
710     assert(m5.data == [0, 1, 0, 3]);
711 
712     assert(Mat2d([[1, 2], [3, 4]]).data == [1, 2, 3, 4]);
713     assert(Mat!(double, 2, 3)([[1, 2, 3], [-1, -2, -3]]).data == [1, 2, 3, -1, -2, -3]);
714 
715     assert(Mat2d.identity.data == [1, 0, 0, 1]);
716     assert(Mat3d.identity.data == [1, 0, 0, 0, 1, 0, 0, 0, 1]);
717 
718     auto m8 = Mat!(double, 2, 3)([1, -1, 2, 0, -3, 1]);
719     Vec3d v1 = Vec3d(2, 1, 0);
720     assert(m8.mul(v1).data == [1, -3]);
721 
722     Vec3f p = Vec3f(0, 0, 1);
723     Mat3f tx = Mat3f([
724         [1, 0, 42],
725         [0, 1, 64],
726         [0, 0, 1]
727     ]);
728     auto transformed = tx.mul(p);
729     assert(transformed.data == [42, 64, 1]);
730 
731     auto p2 = Vec2f(0, 0);
732     auto tx2 = Mat3f.identity();
733     tx2.translate(42, 64);
734     auto transformed2 = tx2.map(p2);
735     assert(transformed2.data == [42, 64]);
736 
737     auto m9 = Mat!(double, 3, 4)([
738         1, 2, 3, 4,
739         5, 6, 7, 8,
740         9, 10, 11, 12
741     ]);
742     auto m10 = m9.subMatrix([2], [1]);
743     assert(m10.data == [
744         1, 3, 4,
745         5, 7, 8
746     ]);
747     assert(m10.subMatrix(cast(size_t[])[], [0]).data == [
748         3, 4,
749         7, 8
750     ]);
751 
752     assert(Mat2d([3, 7, 1, -4]).det == -19);
753     assert(Mat2d([1, 2, 3, 4]).det == -2);
754     assert(Mat3d([1, 2, 3, 4, 5, 6, 7, 8, 9]).det == 0);
755 
756     assert(Mat2d.identity.inv() == Mat2d.identity());
757     assert(Mat3f.identity.inv() == Mat3f.identity());
758     assert(Mat2d(4, 7, 2, 6).inv() == Mat2d(0.6, -0.7, -0.2, 0.4));
759     assert(Mat2d(-3, 1, 5, -2).inv() == Mat2d(-2, -1, -5, -3));
760     assert(Mat3d(1, 3, 3, 1, 4, 3, 1, 3, 4).inv() == Mat3d(7, -3, -3, -1, 1, 0, -1, 0, 1));
761 }