1 module matte.matrix;
2 
3 import std.conv;
4 
5 /**
6  * Creates a matrix with the given data
7  */
8 Matrix!T matrix(T = float)(T[][] data)
9 in
10 {
11 	assert(data.length > 0);
12 	assert(data[0].length > 0);
13 }
14 body
15 {
16 	auto mat = Matrix!T(data.length, data[0].length);
17 
18 	for(size_t i = 0; i < data.length; i++)
19 	{
20 		assert(data[i].length == data[0].length);
21 		mat.data[i * mat.columns .. (i + 1) * mat.columns] = data[i][];
22 	}
23 
24 	return mat;
25 }
26 
27 /**
28  * Creates a vector with the given number of dimensions.
29  */
30 Matrix!T vector(T = float)(size_t dims)
31 {
32 	return Matrix!T(dims, 1);
33 }
34 
35 /**
36  * Creates an identity matrix of the given size.
37  */
38 Matrix!T identity(T = float)(size_t size)
39 {
40 	auto mat = Matrix!T(size, size);
41 
42 	for(size_t i = 0; i < size; i++)
43 	{
44 		mat[i, i] = 1;
45 	}
46 
47 	return mat;
48 }
49 
50 /**
51  * Creates a matrix full of zeros.
52  */
53 Matrix!T zeros(T = float)(size_t numRows, size_t numColumns)
54 {
55 	return Matrix!T(numRows, numColumns);
56 }
57 
58 /**
59  * Creates a matrix full of ones.
60  */
61 Matrix!T ones(T = float)(size_t numRows, size_t numColumns)
62 {
63 	auto mat = Matrix!T(numRows, numColumns);
64 	mat.elements[] = 1;
65 
66 	return mat;
67 }
68 
69 struct Matrix(T)
70 {
71 	public
72 	{
73 		this(size_t numRows, size_t numColumns)
74 		in
75 		{
76 			assert(numRows > 0);
77 			assert(numColumns > 0);
78 		}
79 		body
80 		{
81 			this.numRows = numRows;
82 			this.numColumns = numColumns;
83 			elements = new T[numRows * numColumns];
84 			elements[] = 0;
85 		}
86 
87 		Matrix!T opBinary(string op)(Matrix!T rhs)
88 		{
89 			static if(op == "+" || op == "-")
90 			{
91 				assert(rhs.rows == numRows);
92 				assert(rhs.columns == numColumns);
93 
94 				Matrix!T result = Matrix!T(numRows, numColumns);
95 				mixin("result.elements[] = elements[] " ~ op ~ "rhs.elements[];");
96 
97 				return result;
98 			}
99 			else static if(op == "*")
100 			{
101 				assert(numColumns == rhs.rows);
102 
103 				Matrix!T result = Matrix!T(numRows, rhs.columns);
104 
105 				for(size_t j = 0; j < result.rows; j++)
106 				{
107 					for(size_t i = 0; i < result.columns; i++)
108 					{
109 						result[j, i] = 0;
110 
111 						for(size_t k = 0; k < numColumns; k++)
112 						{
113 							result[j, i] = result[j, i] + this[j, k] * rhs[k, i];
114 						}
115 					}
116 				}
117 
118 				return result;
119 			}
120 			else
121 			{
122 				static assert(0);
123 			}
124 		}
125 
126 		Matrix!T opBinary(string op)(T scalar)
127 		{
128 			static if(op == "*")
129 			{
130 				Matrix!T result = Matrix!T(numRows, numColumns);
131 				result.elements[] = elements[] * scalar;
132 
133 				return result;
134 			}
135 			else
136 			{
137 				static assert(0);
138 			}
139 		}
140 
141 		Matrix!T opBinaryRight(string op)(T scalar)
142 		{
143 			static if(op == "*")
144 			{
145 				return this * scalar;
146 			}
147 			else
148 			{
149 				static assert(0);
150 			}
151 		}
152 
153 		T opIndex(size_t j, size_t i)
154 		in
155 		{
156 			assert(j < numRows);
157 			assert(i < numColumns);
158 		}
159 		body
160 		{
161 			return elements[j * numColumns + i];
162 		}
163 
164 		T opIndexAssign(T value, size_t j, size_t i)
165 		in
166 		{
167 			assert(j < numRows);
168 			assert(i < numColumns);
169 		}
170 		body
171 		{
172 			return elements[j * numColumns + i] = value;
173 		}
174 
175 		string toString()
176 		{
177 			string result;
178 
179 			for(size_t j = 0; j < numRows; j++)
180 			{
181 				for(size_t i = 0; i < numColumns; i++)
182 				{
183 					result ~= this[j, i].to!string;
184 
185 					if(i == (numColumns - 1))
186 					{
187 						result ~= "\n";
188 					}
189 					else
190 					{
191 						result ~= "\t";
192 					}
193 				}
194 			}
195 
196 			return result;
197 		}
198 
199 		@property size_t rows()
200 		{
201 			return numRows;
202 		}
203 
204 		@property size_t columns()
205 		{
206 			return numColumns;
207 		}
208 
209 		@property float[] data()
210 		{
211 			return elements;
212 		}
213 
214 		@property bool isSquare()
215 		{
216 			return numRows == numColumns;
217 		}
218 
219 		@property Matrix!T transpose()
220 		{
221 			auto t = Matrix!T(numColumns, numRows);
222 
223 			for(size_t j = 0; j < numRows; j++)
224 			{
225 				for(size_t i = 0; i < numColumns; i++)
226 				{
227 					t[i, j] = this[j, i];
228 				}
229 			}
230 
231 			return t;
232 		}
233 
234 		@property T trace()
235 		{
236 			assert(numRows == numColumns);
237 
238 			T result = 0;
239 
240 			for(size_t i = 0; i < numRows; i++)
241 			{
242 				result += this[i, i];
243 			}
244 
245 			return result;
246 		}
247 	}
248 
249 	private
250 	{
251 		size_t numColumns;
252 		size_t numRows;
253 		T[] elements;
254 	}
255 }