1 /**
2  * Copyright:       Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved
3  * Authors:         $(Gabriel Gheorghe)
4  * License:         $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007)
5  * Source:			$(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/math/matrix.d, _matrix.d)
6  * Documentation:
7  * Coverage:
8  */
9 module liberty.math.matrix;
10 import liberty.math.vector : Vector;
11 import liberty.core.config : __RowMajor__, __ColumnMajor__;
12 import std.traits : isFloatingPoint;
13 ///
14 enum MatrixOrder : ubyte {
15 	///
16 	RowMajor = 0x00,
17 	///
18 	ColumnMajor = 0x01
19 }
20 static if (__RowMajor__) {
21 	enum CurrentMatrixOrder = MatrixOrder.RowMajor;
22 } else {
23 	enum CurrentMatrixOrder = MatrixOrder.ColumnMajor;
24 }
25 /// T = Type of elements.
26 /// R = Number of rows.
27 /// C = Number of columns.
28 /// O = Matrix order. It can be RowMajor or ColumnMajor.
29 struct Matrix(T, ubyte R, ubyte C = R, MatrixOrder O = CurrentMatrixOrder) if (R >= 2 && R <= 4 && C >= 2 && C <= 4) {
30 	///
31 	alias type = T;
32 	///
33 	enum rowCount = R;
34 	///
35 	enum columnCount = C;
36 	///
37 	enum order = 0;
38 	///
39 	enum bool isSquare = (R == C);
40 	static if (O == MatrixOrder.RowMajor) {
41 		///
42 		alias RowType = Vector!(T, C) ;
43 		///
44 		alias ColumnType = Vector!(T, R);
45 		/// Fields definition.
46 		union {
47 			/// All elements.
48 			T[C * R] v;
49 			/// All rows.
50 			RowType[R] row;
51 			/// Components.
52 			T[C][R] c;
53 		}
54 		///
55 		this(U...)(U values) pure nothrow @safe @nogc {
56 			import std.meta : allSatisfy;
57 			import std.traits : isAssignable;
58 			enum bool isAsgn(U) = isAssignable!(T, U);
59 			static if ((U.length == C * R) && allSatisfy!(isAsgn, U)) {
60 				static foreach (i, x; values) {
61 					v[i] = x;
62 				}
63 			} else static if ((U.length == 1) && isAsgn!U && (!is(U[0] : Matrix))) {
64 				v[] = values[0];
65 			} else static if (U.length == 0) {
66 				v[] = cast(T)0;
67 			} else {
68 				static assert(false, "Cannot create a matrix from given arguments!");
69 			}
70 		}
71 		///
72 		pure nothrow @safe unittest {
73 			// Create a matrix using all values.
74 			auto matrix1 = Matrix!(int, 2)(1, 2, 3, 4);
75 			assert (matrix1.v == [1, 2, /**/ 3, 4]);
76 			// Create a matrix from one value.
77 			auto matrix2 = Matrix!(float, 2)(5.2f);
78 			assert (matrix2.v == [5.2f, 5.2f, /**/ 5.2f, 5.2f]);
79 			// Create a matrix with no value (implicit 0).
80 			auto matrix3 = Matrix!(short, 2)();
81 			assert (matrix3.v == [0, 0, /**/ 0, 0]);
82 		}
83 		/// Construct a matrix from columns.
84 		static Matrix fromColumns(ColumnType[] columns) pure nothrow @safe @nogc
85 		in {
86 			assert(columns.length == C);
87 		} do {
88 			Matrix ret = void;
89 			// todo
90 			return ret;
91 		}
92 		///
93 		pure nothrow @safe unittest {
94 			// todo
95 		}
96 		/// Construct a matrix from rows.
97 		static Matrix fromRows(RowType[] rows) pure nothrow @safe @nogc
98 		in {
99 			assert(rows.length == R);
100 		} do {
101 			Matrix ret = void;
102 			ret.row[] = rows[];
103 			return ret;
104 		}
105 		///
106 		pure nothrow @safe unittest {
107 			auto row = Vector!(double, 2)(2.0, 4.5);
108 			auto matrix = Matrix!(double, 2).fromRows([row, row]);
109 			assert (matrix.v == [2.0, 4.5, /**/ 2.0, 4.5]);
110 		}
111 		/// Returns an identity matrix.
112 		static Matrix identity() pure nothrow @safe @nogc {
113 			Matrix ret = void;
114 			static foreach (i; 0 .. R) {
115 				static foreach (j; 0 .. C) {
116 					static if (i == j) {
117 						ret.c[i][j] = 1;
118 					} else {
119 						ret.c[i][j] = 0;
120 					}
121 				}
122 			}
123 			return ret;
124 		}
125 		///
126 		pure nothrow @safe unittest {
127 			auto matrix = Matrix!(uint, 3).identity();
128 			assert (matrix.v == [1, 0, 0, /**/ 0, 1, 0, /**/ 0, 0, 1]);
129 		}
130 		/// Returns a constant matrix.
131 		static Matrix constant(U)(U x) pure nothrow @safe @nogc {
132 			Matrix ret = void;
133 			static foreach (i; 0 .. R * C) {
134 				ret.v[i] = cast(T)x;
135 			}
136 			return ret;
137 		}
138 		///
139 		pure nothrow @safe unittest {
140 			auto matrix = Matrix!(int, 2, 3).constant(7);
141 			assert (matrix.v == [7, 7, 7, /**/ 7, 7, 7]);
142 		}
143 		///
144 		ColumnType column(int j) pure nothrow const @safe @nogc {
145 			ColumnType ret = void;
146 			static foreach (i; 0..R) {
147 				ret.v[i] = c[i][j];
148 			}
149 			return ret;
150 		}
151 		///
152 		Matrix opBinary(string op)(T factor) pure nothrow const @safe @nogc if (op == "*") {
153 			Matrix ret = void;
154 			static foreach (i; 0..R) {
155 				static foreach (j; 0..C) {
156 					ret.c[i][j] = c[i][j] * factor;
157 				}
158 			}
159 			return ret;
160 		}
161 		///
162 		pure nothrow @safe @nogc unittest {
163 			auto m1 = Matrix2I(3);
164 			auto m2 = m1 * 4;
165 			assert (m2.v == [12, 12, /**/ 12, 12]);
166 		}
167 		///
168 		ColumnType opBinary(string op)(RowType x) pure nothrow const @safe @nogc if (op == "*") {
169 			ColumnType ret = void;
170 			static foreach (i; 0..R) {
171 				T sum = 0;
172 				static foreach (j; 0..C) {
173 					sum += c[i][j] * x.v[j];
174 				}
175 				res.v[i] = sum;
176 			}
177 			return ret;
178 		}
179 		///
180 		auto opBinary(string op, U)(U x) pure nothrow const @safe @nogc if (isMatrixInstance!U && (U.rowCount == C) && (op == "*")) {
181 			Matrix!(T, R, U.columnCount) ret = void;
182 			T sum = 0;
183 			static foreach (i; 0..R) {
184 				static foreach (j; 0..U.columnCount) {
185 					static foreach (k; 0..C) {
186 						sum += c[i][k] * x.c[k][j];
187 					}
188 					ret.c[i][j] = sum;
189 					sum = 0;
190 				}
191 			}
192 			return ret;
193 		}
194 		///
195 		pure nothrow @safe @nogc unittest {
196 			auto m1 = Matrix2I(2, 5, /**/ -1, 2);
197 			auto m2 = Matrix2x3I(3, 3, -2, /**/ 3, 4, 3);
198 			auto m3 = m1 * m2;
199 			assert (m3.v == [21, 26, 11, /**/ 3, 5, 8]);
200 		}
201 		///
202 		Matrix opBinary(string op, U)(U rhs) pure nothrow const @safe @nogc if (op == "+" || op == "-") {
203 			Matrix ret = void;
204 			static if (is (U : Matrix)) {
205 				static foreach (i; 0..R) {
206 					static foreach (j; 0..C) {
207 						mixin("ret.c[i][j] = c[i][j] " ~ op ~ " rhs.c[i][j];");
208 					}
209 				}
210 			} else static if (is (U : T)) {
211 				mixin("ret.v[] = this.v[] " ~ op ~ " rhs;");
212 			} else {
213 				static assert (0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Matrix.stringof);
214 			}
215 			return ret;
216 		}
217 		///
218 		pure nothrow @safe @nogc unittest {
219 			auto m1 = Matrix2I(1, 3, /**/ -1, 4);
220 			auto m2 = Matrix2I(5, 2, /**/ 1, 0);
221 			auto m3 = m1 + m2;
222 			assert (m3.v == [6, 5, /**/ 0, 4]);
223 			auto m4 = m1 - 7;
224 			assert (m4.v == [-6, -4, /**/ -8, -3]);
225 		}
226 		///
227 		ref Matrix opOpAssign(string op, U)(U rhs) pure nothrow @safe @nogc {
228 			static if (is(U : Matrix) || is(U : T)) {
229 				mixin("this = this " ~ op ~ " rhs;");
230 			} else {
231 				static assert (0, "Cannot assign a variable of type " ~ U.stringof ~ " within a variable of type " ~ Matrix.stringof);
232 			}
233 			return this;
234 		}
235 		///
236 		pure nothrow @safe @nogc unittest {
237 			auto m1 = Matrix2I(1, 3, /**/ -1, 4);
238 			auto m2 = Matrix2I(5, 2, /**/ 1, 0);
239 			m1 += m2;
240 			assert (m1.v == [6, 5, /**/ 0, 4]);
241 			m1 -= 2;
242 			assert (m1.v == [4, 3, /**/ -2, 2]);
243 		}
244 		///
245 		Matrix opUnary(string op)() pure nothrow const @safe @nogc if (op == "+" || op == "-" || op == "~" || op == "!") {
246 			Matrix ret = void;
247 			static foreach (i; 0..R * C) {
248 				mixin("ret.v[i] = " ~ op ~ "v[i];");
249 			}
250 			return ret;
251 		}
252 		///
253 		pure nothrow @safe @nogc unittest {
254 			auto m1 = Matrix2I(1, 3, /**/ -1, 0);
255 			assert ((+m1).v == [1, 3, /**/ -1, 0]);
256 			assert ((-m1).v == [-1, -3, /**/ 1, 0]);
257 			assert ((~m1).v == [-2, -4, /**/ 0, -1]);
258 			// *** BUG ***
259 			//auto m2 = Matrix!(bool, 2)(true, true, /**/ false, false);
260 			//assert ((!m2).v == [false, false, /**/ true, true]);
261 		}
262 		///
263 		U opCast(U)() pure nothrow const @safe @nogc if (isMatrixInstance!U && U.rowCount == rowCount && U.columnCount == columnCount) {
264 			U ret = U.identity();
265 			enum min_r = R < U.rowCount ? R : U.rowCount;
266 			enum min_c = C < U.columnCount ? C : U.columnCount;
267 			static foreach (i; 0..min_r) {
268 				static foreach (j; 0..min_c) {
269 					ret.c[i][j] = cast(U.type)(c[i][j]);
270 				}
271 			}
272 			return ret;
273 		}
274 		///
275 		pure nothrow @safe @nogc unittest {
276 			auto m1 = Matrix2F(3.2f, 4.5f, /**/ 3.8f, -7.2f);
277 			Matrix2I m2 = cast(Matrix2I)m1;
278 			assert (m2.v == [3, 4, /**/ 3, -7]);
279 		}
280 		///
281 		bool opEquals(U)(U rhs) pure nothrow const @safe @nogc {
282 			static if (is(U : Matrix)) {
283 				static foreach (i; 0..R * C) {
284 					if (v[i] != rhs.v[i]) {
285 						return false;
286 					}
287 				}
288 			} else static if (is(U : T)) {
289 				static foreach (i; 0..R * C) {
290 					if (v[i] != rhs) {
291 						return false;
292 					}
293 				}
294 			} else {
295 				static assert (0, "Cannot compare a variable of type " ~ U.stringof ~ " with a variable of type " ~ Matrix.stringof);
296 			}
297 			return true;
298 		}
299 		///
300 		pure nothrow @safe @nogc unittest {
301 			auto m1 = Matrix2I(6, 3, /**/ -1, 0);
302 			auto m2 = Matrix2I(6, 3, /**/ -1, 0);
303 			auto m3 = Matrix2I(1, 4, /**/ -1, 7);
304 			assert (m1 == m2);
305 			assert (m1 != m3);
306 			auto m4 = Matrix2I(3, 3, /**/ 3, 3);
307 			assert (m4 == 3);
308 			assert (m4 != -3);
309 		}
310 		/// Returns a pointer to content.
311 		inout(T)* ptr() pure nothrow inout @nogc @property {
312 			return v.ptr;
313 		}
314 		static if (isFloatingPoint!T && R == 2 && C == 2) {
315 			/// Returns inverse of matrix 2x2.
316 			Matrix inverse() pure nothrow const @safe @nogc {
317 				T invDet = 1 / (c[0][0] * c[1][1] - c[0][1] * c[1][0]);
318 				return Matrix(c[1][1] * invDet, -c[0][1] * invDet, -c[1][0] * invDet, c[0][0] * invDet);
319 			}
320 			///
321 			pure nothrow @safe @nogc unittest {
322 				import std.math : approxEqual;
323 				auto m1 = Matrix2D(2.0, 3.0, /**/ -1.0, 5.0);
324 				auto m2 = m1.inverse();
325 				assert (m2.v[0].approxEqual(0.384615));
326 				assert (m2.v[1].approxEqual(-0.230769));
327 				assert (m2.v[2].approxEqual(0.0769231));
328 				assert (m2.v[3].approxEqual(0.153846));
329 			}
330 		}
331 		static if (isFloatingPoint!T && R == 3 && C == 3) {
332 			/// Returns inverse of matrix 3x3.
333 			Matrix inverse() pure nothrow const @safe @nogc {
334 				T det = c[0][0] * (c[1][1] * c[2][2] - c[2][1] * c[1][2]) - c[0][1] * (c[1][0] * c[2][2] - c[1][2] * c[2][0]) + c[0][2] * (c[1][0] * c[2][1] - c[1][1] * c[2][0]);
335 				T invDet = 1 / det;
336 				Matrix ret = void;
337 				ret.c[0][0] =  (c[1][1] * c[2][2] - c[2][1] * c[1][2]) * invDet;
338 				ret.c[0][1] = -(c[0][1] * c[2][2] - c[0][2] * c[2][1]) * invDet;
339 				ret.c[0][2] =  (c[0][1] * c[1][2] - c[0][2] * c[1][1]) * invDet;
340 				ret.c[1][0] = -(c[1][0] * c[2][2] - c[1][2] * c[2][0]) * invDet;
341 				ret.c[1][1] =  (c[0][0] * c[2][2] - c[0][2] * c[2][0]) * invDet;
342 				ret.c[1][2] = -(c[0][0] * c[1][2] - c[1][0] * c[0][2]) * invDet;
343 				ret.c[2][0] =  (c[1][0] * c[2][1] - c[2][0] * c[1][1]) * invDet;
344 				ret.c[2][1] = -(c[0][0] * c[2][1] - c[2][0] * c[0][1]) * invDet;
345 				ret.c[2][2] =  (c[0][0] * c[1][1] - c[1][0] * c[0][1]) * invDet;
346 				return ret;
347 			}
348 			///
349 			pure nothrow @safe @nogc unittest {
350 			}
351 		}
352 		static if (isFloatingPoint!T && R == 4 && C == 4) {
353 			/// Returns inverse of matrix 4x4.
354 			Matrix inverse() pure nothrow const @safe @nogc {
355 				T det2_01_01 = c[0][0] * c[1][1] - c[0][1] * c[1][0];
356 				T det2_01_02 = c[0][0] * c[1][2] - c[0][2] * c[1][0];
357 				T det2_01_03 = c[0][0] * c[1][3] - c[0][3] * c[1][0];
358 				T det2_01_12 = c[0][1] * c[1][2] - c[0][2] * c[1][1];
359 				T det2_01_13 = c[0][1] * c[1][3] - c[0][3] * c[1][1];
360 				T det2_01_23 = c[0][2] * c[1][3] - c[0][3] * c[1][2];
361 				T det3_201_012 = c[2][0] * det2_01_12 - c[2][1] * det2_01_02 + c[2][2] * det2_01_01;
362 				T det3_201_013 = c[2][0] * det2_01_13 - c[2][1] * det2_01_03 + c[2][3] * det2_01_01;
363 				T det3_201_023 = c[2][0] * det2_01_23 - c[2][2] * det2_01_03 + c[2][3] * det2_01_02;
364 				T det3_201_123 = c[2][1] * det2_01_23 - c[2][2] * det2_01_13 + c[2][3] * det2_01_12;
365 				T det = - det3_201_123 * c[3][0] + det3_201_023 * c[3][1] - det3_201_013 * c[3][2] + det3_201_012 * c[3][3];
366 				T invDet = 1 / det;
367 				T det2_03_01 = c[0][0] * c[3][1] - c[0][1] * c[3][0];
368 				T det2_03_02 = c[0][0] * c[3][2] - c[0][2] * c[3][0];
369 				T det2_03_03 = c[0][0] * c[3][3] - c[0][3] * c[3][0];
370 				T det2_03_12 = c[0][1] * c[3][2] - c[0][2] * c[3][1];
371 				T det2_03_13 = c[0][1] * c[3][3] - c[0][3] * c[3][1];
372 				T det2_03_23 = c[0][2] * c[3][3] - c[0][3] * c[3][2];
373 				T det2_13_01 = c[1][0] * c[3][1] - c[1][1] * c[3][0];
374 				T det2_13_02 = c[1][0] * c[3][2] - c[1][2] * c[3][0];
375 				T det2_13_03 = c[1][0] * c[3][3] - c[1][3] * c[3][0];
376 				T det2_13_12 = c[1][1] * c[3][2] - c[1][2] * c[3][1];
377 				T det2_13_13 = c[1][1] * c[3][3] - c[1][3] * c[3][1];
378 				T det2_13_23 = c[1][2] * c[3][3] - c[1][3] * c[3][2];
379 				T det3_203_012 = c[2][0] * det2_03_12 - c[2][1] * det2_03_02 + c[2][2] * det2_03_01;
380 				T det3_203_013 = c[2][0] * det2_03_13 - c[2][1] * det2_03_03 + c[2][3] * det2_03_01;
381 				T det3_203_023 = c[2][0] * det2_03_23 - c[2][2] * det2_03_03 + c[2][3] * det2_03_02;
382 				T det3_203_123 = c[2][1] * det2_03_23 - c[2][2] * det2_03_13 + c[2][3] * det2_03_12;
383 				T det3_213_012 = c[2][0] * det2_13_12 - c[2][1] * det2_13_02 + c[2][2] * det2_13_01;
384 				T det3_213_013 = c[2][0] * det2_13_13 - c[2][1] * det2_13_03 + c[2][3] * det2_13_01;
385 				T det3_213_023 = c[2][0] * det2_13_23 - c[2][2] * det2_13_03 + c[2][3] * det2_13_02;
386 				T det3_213_123 = c[2][1] * det2_13_23 - c[2][2] * det2_13_13 + c[2][3] * det2_13_12;
387 				T det3_301_012 = c[3][0] * det2_01_12 - c[3][1] * det2_01_02 + c[3][2] * det2_01_01;
388 				T det3_301_013 = c[3][0] * det2_01_13 - c[3][1] * det2_01_03 + c[3][3] * det2_01_01;
389 				T det3_301_023 = c[3][0] * det2_01_23 - c[3][2] * det2_01_03 + c[3][3] * det2_01_02;
390 				T det3_301_123 = c[3][1] * det2_01_23 - c[3][2] * det2_01_13 + c[3][3] * det2_01_12;
391 				Matrix ret = void;
392 				ret.c[0][0] = - det3_213_123 * invDet;
393 				ret.c[1][0] = + det3_213_023 * invDet;
394 				ret.c[2][0] = - det3_213_013 * invDet;
395 				ret.c[3][0] = + det3_213_012 * invDet;
396 				ret.c[0][1] = + det3_203_123 * invDet;
397 				ret.c[1][1] = - det3_203_023 * invDet;
398 				ret.c[2][1] = + det3_203_013 * invDet;
399 				ret.c[3][1] = - det3_203_012 * invDet;
400 				ret.c[0][2] = + det3_301_123 * invDet;
401 				ret.c[1][2] = - det3_301_023 * invDet;
402 				ret.c[2][2] = + det3_301_013 * invDet;
403 				ret.c[3][2] = - det3_301_012 * invDet;
404 				ret.c[0][3] = - det3_201_123 * invDet;
405 				ret.c[1][3] = + det3_201_023 * invDet;
406 				ret.c[2][3] = - det3_201_013 * invDet;
407 				ret.c[3][3] = + det3_201_012 * invDet;
408 				return ret;
409 			}
410 			///
411 			pure nothrow @safe @nogc unittest {
412 			}
413 		}
414 		/// Returns transposed matrix.
415 		Matrix!(T, C, R) transposed() pure nothrow const @safe @nogc {
416 			Matrix!(T, C, R) ret = void;
417 			static foreach (i; 0..C) {
418 				static foreach (j; 0..R) {
419 					ret.c[i][j] = c[j][i];
420 				}
421 			}
422 			return ret;
423 		}
424 		///
425 		pure nothrow @safe @nogc unittest {
426 			auto m1 = Matrix2I(4, 5, /**/ -1, 0);
427 			assert (m1.transposed().v == [4, -1, /**/ 5, 0]);
428 		}
429 		static if (isSquare && R > 2) {
430 			/// Makes a diagonal matrix from a vector.
431 			static Matrix diag(Vector!(T, R) v) pure nothrow @safe @nogc {
432 				Matrix ret = void;
433 				static foreach (i; 0..R) {
434 					static foreach (j; 0..C) {
435 						ret.c[i][j] = (i == j) ? v.v[i] : 0;
436 					}
437 				}
438 				return ret;
439 			}
440 			///
441 			pure nothrow @safe @nogc unittest {
442 				auto v1 = Vector!(int, 3)(4, 5, -1);
443 				auto m1 = Matrix!(int, 3).diag(v1);
444 				assert (m1.v == [4, 0, 0, /**/ 0, 5, 0, /**/ 0, 0, -1]);
445 			}
446 			/// In-place translate by (v, 1).
447 			void translate(Vector!(T, R-1) v) pure nothrow @safe @nogc {
448 				T _dot = 0;
449 				static foreach (i; 0..R) {
450 					static foreach (j; 0..C - 1) {
451 						_dot += v.v[j] * c[i][j];
452 					}
453 					c[i][C - 1] += _dot;
454 					_dot = 0;
455 				}
456 			}
457 			///
458 			pure nothrow @safe @nogc unittest {
459 			}
460 			/// Make a translation matrix.
461 			static Matrix translation(Vector!(T, R-1) v) pure nothrow @safe @nogc {
462 				Matrix ret = identity();
463 				static foreach (i; 0..R - 1) {
464 					ret.c[i][C - 1] += v.v[i];
465 				}
466 				return ret;
467 			}
468 			///
469 			pure nothrow @safe @nogc unittest {
470 			}
471 			/// In-place matrix scaling.
472 			void scale(Vector!(T, R-1) v) pure nothrow @safe @nogc {
473 				static foreach (i; 0..R) {
474 					static foreach (j; 0..C - 1) {
475 						c[i][j] *= v.v[j];
476 					}
477 				}
478 			}
479 			///
480 			pure nothrow @safe @nogc unittest {
481 			}
482 			/// Make a scaling matrix.
483 			static Matrix scaling(Vector!(T, R-1) v) pure nothrow @safe @nogc {
484 				Matrix ret = identity();
485 				static foreach (i; 0..R - 1) {
486 					ret.c[i][i] = v.v[i];
487 				}
488 				return ret;
489 			}
490 			///
491 			pure nothrow @safe @nogc unittest {
492 			}
493 		}
494 		static if (isSquare && (R == 3 || R == 4) && isFloatingPoint!T) {
495 			///
496 			static Matrix rotateAxis(int i, int j)(T angle) pure nothrow @safe @nogc {
497 				import liberty.math.functions : sin, cos;
498 				Matrix ret = identity();
499 				const T cosa = cos(angle);
500 				const T sina = sin(angle);
501 				ret.c[i][i] = cosa;
502 				ret.c[i][j] = -sina;
503 				ret.c[j][i] = sina;
504 				ret.c[j][j] = cosa;
505 				return ret;
506 			}
507 			///
508 			pure nothrow @safe @nogc unittest {
509 			}
510 			/// Returns rotation matrix along axis X.
511 			alias rotateXAxix = rotateAxis!(1, 2);
512 			/// Returns rotation matrix along axis Y.
513 			alias rotateYAxis = rotateAxis!(2, 0);
514 			/// Returns rotation matrix along axis Z.
515 			alias rotateZAxis = rotateAxis!(0, 1);
516 			/// In-place rotation by (v, 1)
517 			void rotate(T angle, Vector!(T, 3) axis) pure nothrow @safe @nogc {
518 				this = rotation(angle, axis, this);
519 			}
520 			///
521 			pure nothrow @safe @nogc unittest {
522 			}
523 			///
524 			void rotateX(T angle) pure nothrow @safe @nogc {
525 				this = rotation(angle, Vector!(T, 3)(1, 0, 0), this);
526 			}
527 			///
528 			pure nothrow @safe @nogc unittest {
529 			}
530 			///
531 			void rotateY(T angle) pure nothrow @safe @nogc {
532 				this = rotation(angle, Vector!(T, 3)(0, 1, 0), this);
533 			}
534 			///
535 			pure nothrow @safe @nogc unittest {
536 			}
537 			///
538 			void rotateZ(T angle) pure nothrow @safe @nogc {
539 				this = rotation(angle, Vector!(T, 3)(0, 0, 1), this);
540 			}
541 			///
542 			pure nothrow @safe @nogc unittest {
543 			}
544 			///
545 			static Matrix rotation(T angle, Vector!(T, 3) axis, Matrix m = identity()) pure nothrow @safe @nogc {
546 				import liberty.math.functions : sin, cos;
547 				Matrix ret = m;
548 				const T c = cos(angle);
549 				const oneMinusC = 1 - c;
550 				const T s = sin(angle);
551 				axis = axis.normalized();
552 				T x = axis.x,
553 				y = axis.y,
554 				z = axis.z;
555 				T xy = x * y,
556 				yz = y * z,
557 				xz = x * z;
558 				ret.c[0][0] = x * x * oneMinusC + c;
559 				ret.c[0][1] = x * y * oneMinusC - z * s;
560 				ret.c[0][2] = x * z * oneMinusC + y * s;
561 				ret.c[1][0] = y * x * oneMinusC + z * s;
562 				ret.c[1][1] = y * y * oneMinusC + c;
563 				ret.c[1][2] = y * z * oneMinusC - x * s;
564 				ret.c[2][0] = z * x * oneMinusC - y * s;
565 				ret.c[2][1] = z * y * oneMinusC + x * s;
566 				ret.c[2][2] = z * z * oneMinusC + c;
567 				return ret;
568 			}
569 			///
570 			pure nothrow @safe @nogc unittest {
571 			}
572 		}
573 		static if (isSquare && R == 4 && isFloatingPoint!T) {
574 			/// Returns orthographic projection.
575 			static Matrix orthographic(T left, T right, T bottom, T top, T near, T far) pure nothrow @safe @nogc {
576 				T dx = right - left,
577 				dy = top - bottom,
578 				dz = far - near;
579 				T tx = -(right + left) / dx;
580 				T ty = -(top + bottom) / dy;
581 				T tz = -(far + near) / dz;
582 				return Matrix(2 / dx, 0, 0, tx, 0, 2 / dy, 0, ty, 0, 0, -2 / dz, tz, 0, 0, 0, 1);
583 			}
584 			///
585 			pure nothrow @safe @nogc unittest {
586 			}
587 			/// Returns perspective projection.
588 			static Matrix perspective(T FOVInRadians, T aspect, T zNear, T zFar) pure nothrow @safe @nogc {
589 				import liberty.math.functions : tan;
590 				T f = 1 / tan(FOVInRadians / 2);
591 				T d = 1 / (zNear - zFar);
592 				return Matrix(f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (zFar + zNear) * d, 2 * d * zFar * zNear, 0, 0, -1, 0);
593 			}
594 			///
595 			pure nothrow @safe @nogc unittest {
596 			}
597 			/// Returns lookAt projection.
598 			static Matrix lookAt(Vector!(T, 3) eye, Vector!(T, 3) target, Vector!(T, 3) up) pure nothrow @safe @nogc {
599 				import liberty.math.vector : dot, cross;
600 				Vector!(T, 3) Z = (eye - target).normalized();
601 				Vector!(T, 3) X = cross(-up, Z).normalized();
602 				Vector!(T, 3) Y = cross(Z, -X);
603 				return Matrix(-X.x, -X.y, -X.z, dot(X, eye), Y.x, Y.y, Y.z, -dot(Y, eye), Z.x, Z.y, Z.z, -dot(Z, eye), 0, 0, 0, 1);
604 			}
605 			///
606 			pure nothrow @safe @nogc unittest {
607 			}
608 		}
609 	} else {
610 
611 	}
612 }
613 ///
614 template isMatrixInstance(U) {
615 	private static void isMatrix(T, int R, int C, MatrixOrder O)(Matrix!(T, R, C, O) x) {}
616 	enum bool isMatrixInstance = is(typeof(isMatrix(U.init)));
617 }
618 ///
619 template Matrix2(T) {
620 	alias Matrix2 = Matrix!(T, 2);
621 }
622 ///
623 template Matrix3(T) {
624 	alias Matrix3 = Matrix!(T, 3);
625 }
626 ///
627 template Matrix4(T) {
628 	alias Matrix4 = Matrix!(T, 4);
629 }
630 ///
631 template Matrix2x3(T) {
632 	alias Matrix2x3 = Matrix!(T, 2, 3);
633 }
634 ///
635 template Matrix2x4(T) {
636 	alias Matrix2x4 = Matrix!(T, 2, 4);
637 }
638 ///
639 template Matrix3x2(T) {
640 	alias Matrix3x2 = Matrix!(T, 3, 2);
641 }
642 ///
643 template Matrix3x4(T) {
644 	alias Matrix3x4 = Matrix!(T, 3, 4);
645 }
646 ///
647 template Matrix4x2(T) {
648 	alias Matrix4x2 = Matrix!(T, 4, 2);
649 }
650 ///
651 template Matrix4x3(T) {
652 	alias Matrix4x3 = Matrix!(T, 4, 3);
653 }
654 ///
655 alias Matrix2I = Matrix2!int;
656 ///
657 alias Matrix3I = Matrix3!int;
658 ///
659 alias Matrix4I = Matrix4!int;
660 ///
661 alias Matrix2F = Matrix2!float;
662 ///
663 alias Matrix3F = Matrix3!float;
664 ///
665 alias Matrix4F = Matrix4!float;
666 ///
667 alias Matrix2D = Matrix2!double;
668 ///
669 alias Matrix3D = Matrix3!double;
670 ///
671 alias Matrix4D = Matrix4!double;
672 ///
673 alias Matrix2x3I = Matrix2x3!int;
674 ///
675 alias Matrix2x4I = Matrix2x4!int;
676 ///
677 alias Matrix3x2I = Matrix3x2!int;
678 ///
679 alias Matrix2x3F = Matrix2x3!float;
680 ///
681 alias Matrix2x4F = Matrix2x4!float;
682 ///
683 alias Matrix3x2F = Matrix3x2!float;
684 ///
685 alias Matrix2x3D = Matrix2x3!double;
686 ///
687 alias Matrix2x4D = Matrix2x4!double;
688 ///
689 alias Matrix3x2D = Matrix3x2!double;
690 ///
691 alias Matrix3x4I = Matrix3x4!int;
692 ///
693 alias Matrix4x2I = Matrix4x2!int;
694 ///
695 alias Matrix4x3I = Matrix4x3!int;
696 ///
697 alias Matrix3x4F = Matrix3x4!float;
698 ///
699 alias Matrix4x2F = Matrix4x2!float;
700 ///
701 alias Matrix4x3F = Matrix4x3!float;
702 ///
703 alias Matrix3x4D = Matrix3x4!double;
704 ///
705 alias Matrix4x2D = Matrix4x2!double;
706 ///
707 alias Matrix4x3D = Matrix4x3!double;
708 ///
709 final class MatrixStack(int R, T) if (R == 3 || R == 4) {
710 	private {
711 		size_t _top;
712 		size_t _depth;
713 		MatrixType* _matrices;
714 		MatrixType* _invMatrices;
715 	}
716 	///
717 	alias MatrixType = Matrix!(T, R, R);
718 	/// Creates a matrix stack.
719 	this(size_t depth = 32) nothrow @trusted @nogc
720 	in {
721 		assert(depth > 0);
722 	} do {
723 		import core.stdc.stdlib : malloc;
724 		size_t memory_needed = MatrixType.sizeof * depth * 2;
725 		void* data = malloc(memory_needed * 2);
726 		_matrices = cast(MatrixType*)data;
727 		_invMatrices = cast(MatrixType*)(data + memory_needed);
728 		_top = 0;
729 		_depth = depth;
730 		loadIdentity();
731 	}
732 	/// Relases the matrix stack memory.
733 	~this() {
734 		if (_matrices !is null) {
735 			import core.stdc.stdlib : free;
736 			import liberty.core.memory : ensureNotInGC;
737 			ensureNotInGC("MatrixStack");
738 			free(_matrices);
739 			_matrices = null;
740 		}
741 	}
742 	///
743 	void loadIdentity() pure nothrow @trusted @nogc {
744 		_matrices[_top] = MatrixType.identity();
745 		_invMatrices[_top] = MatrixType.identity();
746 	}
747 	///
748 	void push() pure nothrow @trusted @nogc {
749 		if(_top + 1 >= _depth) {
750 			assert(false, "Matrix stack is full!");
751 		}
752 		_matrices[_top + 1] = _matrices[_top];
753 		_invMatrices[_top + 1] = _invMatrices[_top];
754 		_top++;
755 	}
756 	/// Replacement for $(D glPopMatrix).
757 	void pop() pure nothrow @safe @nogc {
758 		if (_top <= 0) {
759 			assert(false, "Matrix stack is empty!");
760 		}
761 		_top--;
762 	}
763 
764 	///
765 	MatrixType top() pure const nothrow @trusted @nogc {
766 		return _matrices[_top];
767 	}
768 	///
769 	MatrixType invTop() pure const nothrow @trusted @nogc {
770 		return _invMatrices[_top];
771 	}
772 	///
773 	void top(MatrixType m) pure nothrow @trusted @nogc {
774 		_matrices[_top] = m;
775 		_invMatrices[_top] = m.inverse();
776 	}
777 	///
778 	void mult(MatrixType m) pure nothrow @trusted @nogc {
779 		mult(m, m.inverse());
780 	}
781 	///
782 	void mult(MatrixType m, MatrixType invM) pure nothrow @trusted @nogc {
783 		_matrices[_top] = _matrices[_top] * m;
784 		_invMatrices[_top] = invM *_invMatrices[_top];
785 	}
786 	///
787 	void translate(Vector!(T, R-1) v) pure nothrow @safe @nogc {
788 		mult(MatrixType.translation(v), MatrixType.translation(-v));
789 	}
790 	///
791 	void scale(Vector!(T, R-1) v) pure nothrow @safe @nogc {
792 		mult(MatrixType.scaling(v), MatrixType.scaling(1 / v));
793 	}
794 	static if (R == 4) {
795 		///
796 		void rotate(T angle, Vector!(T, 3) axis) pure nothrow @safe @nogc {
797 			MatrixType rot = MatrixType.rotation(angle, axis);
798 			mult(rot, rot.transposed());
799 		}
800 		///
801 		void perspective(T FOVInRadians, T aspect, T zNear, T zFar) pure nothrow @safe @nogc {
802 			mult(MatrixType.perspective(FOVInRadians, aspect, zNear, zFar));
803 		}
804 		///
805 		void orthographic(T left, T right, T bottom, T top, T near, T far) pure nothrow @safe @nogc {
806 			mult(MatrixType.orthographic(left, right, bottom, top, near, far));
807 		}
808 		///
809 		void lookAt(Vector!(T, 3) eye, Vector!(T, 3) target, Vector!(T, 3) up) pure nothrow @safe @nogc {
810 			mult(MatrixType.lookAt(eye, target, up));
811 		}
812 	}
813 }
814 ///
815 @trusted unittest {
816     auto m = new MatrixStack!(4, double)();
817     scope(exit) destroy(m);
818     m.loadIdentity();
819     m.push();
820     m.pop();
821     m.translate(Vector!(double, 3)(2.4, 3.3, 7.5));
822     m.scale(Vector!(double, 3)(0.7));
823     auto t = new MatrixStack!(3, float)();
824     scope(exit) destroy(t);
825     t.loadIdentity();
826     t.push();
827     t.pop();
828     t.translate(Vector!(float, 2)(1, -8));
829     t.scale(Vector!(float, 2)(0.2));
830 }