1 /**
2  * Matrix
3  */
4 module d2d.math.Matrix;
5 
6 import std.algorithm;
7 import std.array;
8 import std.math;
9 import std.parallelism;
10 import d2d.math;
11 
12 /**
13  * A matrix is just like a mathematical matrix where it is similar to essentially a 2d array of of the given type
14  * Template parameters are the type, how many rows, and how many columns
15  * TODO: rref, frustums, transformations
16  */
17 class Matrix(T, uint rows, uint columns) {
18 
19     T[columns][rows] elements; ///The elements of the matrix; stored as an array of rows (i.e. row vectors)
20 
21     /**
22      * Recursively finds the determinant of the matrix if the matrix is square
23      * Task is done in O(n!) for an nxn matrix, so determinants of matrices of at most size 3x3 are already defined to be more efficient
24      * Not very efficient for large matrices 
25      */
26     static if (rows == columns) {
27         @property T determinant() {
28             //Degenerate cases:
29             static if (rows == 1) {
30                 return elements.front.front;
31             }
32             else static if (rows == 2) {
33                 return elements[0][0] * elements[1][1] - elements[0][1] * elements[1][0];
34             }
35             else static if (rows == 3) {
36                 return elements[0][0] * elements[1][1] * elements[2][2]
37                     + elements[0][1] * elements[1][2] * elements[2][0]
38                     + elements[0][2] * elements[1][0] * elements[2][1]
39                     - elements[0][2] * elements[1][1] * elements[2][0]
40                     - elements[0][1] * elements[1][0] * elements[2][2]
41                     - elements[0][0] * elements[1][2] * elements[2][1];
42             }
43             else {
44                 T determinant;
45                 foreach (i; 0 .. columns) {
46                     determinant += (-1).pow(i) * this.elements[0][i] * (new Matrix!(T,
47                             rows - 1, columns - 1)(this.elements[1 .. $].map!(a => a[0 .. i] ~ a[i + 1 .. $])
48                             .array).determinant);
49                 }
50                 return determinant;
51             }
52         }
53     }
54 
55     /**
56      * Constructs a matrix from a two-dimensional array of elements
57      */
58     this(T[][] elements) {
59         foreach (i, row; elements) {
60             foreach (j, element; row) {
61                 this.elements[i][j] = element;
62             }
63         }
64     }
65 
66     /**
67      * Constructs a matrix from a two-dimensional array of elements
68      */
69     this(T[columns][rows] elements) {
70         this.elements = elements;
71     }
72 
73     /**
74      * Constructs a matrix that is identically one value
75      */
76     this(T element) {
77         T[columns] row = element;
78         this.elements[] = row;
79     }
80 
81     /**
82      * Constructs a matrix as an identity matrix
83      */
84     this() {
85         T[rows][columns] elements;
86         foreach (index, ref element; (cast(T[]) elements).parallel) {
87             elements[index][index] = 1;
88         }
89     }
90 
91     /**
92      * Copy constructor for a matrix; creates a copy of the given matrix
93      */
94     this(Matrix!(T, rows, columns) toCopy) {
95         this(toCopy.elements);
96     }
97 
98     /**
99      * Sets the nth row of the matrix
100      */
101     void setRow(uint index, Vector!(T, columns) r) {
102         this.elements[index] = r.components;
103     }
104 
105     /**
106      * Returns the nth row of the matrix
107      */
108     Vector!(T, columns) getRow(uint index) {
109         return new Vector!(T, columns)(this.elements[index]);
110     }
111 
112     /**
113      * Sets the nth column of the matrix
114      */
115     void setColumn(uint index, Vector!(T, rows) c) {
116         foreach (i, ref row; this.elements) {
117             row[index] = c[i];
118         }
119     }
120 
121     /**
122      * Returns the nth column of the matrix
123      */
124     Vector!(T, rows) getColumn(uint index) {
125         Vector!(T, rows) c = new Vector!(T, rows)();
126         foreach (i, row; this.elements) {
127             c[i] = row[index];
128         }
129         return c;
130     }
131 
132     /**
133      * Allows assigning the matrix to a static two-dimensional array to set all components of the matrix
134      */
135     void opAssign(T[][] rhs) {
136         foreach (i, row; rhs) {
137             foreach (j, element; row) {
138                 this.elements[i][j] = element;
139             }
140         }
141     }
142 
143     /**
144      * Allows assigning the matrix to a static two-dimensional array to set all components of the matrix
145      */
146     void opAssign(T[columns][rows] rhs) {
147         this.elements = rhs;
148     }
149 
150     /**
151      * Allows assigning the matrix to a single value to set all elements of the matrix to such a value
152      */
153     void opAssign(T rhs) {
154         T[columns] row = rhs;
155         this.elements[] = row;
156     }
157 
158 }