# angusgibbs/matrix

1 parent 5f57b79 commit 0bc953600f7396795adf3b253f33831fba21cba2 committed Mar 2, 2013
Showing with 182 additions and 0 deletions.
1. +29 −0 docs/equals.md
2. +2 −0 docs/getting_started.md
3. +18 −0 docs/inverse.md
4. +69 −0 lib/matrix.js
5. +64 −0 test/test.js
 @@ -0,0 +1,29 @@ +# Matrix#equals(*n*) + +`Matrix#equals()` accepts one parameter, a Matrix object or a two dimensional array that will converted into a Matrix object. It will return true if the matrix passed has the same dimensions and contents as the current matrix, and false otherwise. + +## Errors + +None. + +## Examples + +```javascript +var a = new Matrix([[2, 2], [1, 1]]); +var b = new Matrix([[3, 3], [2, 2]]); +var c = new Matrix([[3, 3], [2, 2]]); +var d = new Matrix([[3], [2]]); + +a.equals(b); +// => false + +b.equals(c); +// => true + +// #equals() also accepts a two-dimensional array +b.equals([[3, 3], [2, 2]]); +// => true + +c.equals(d); +// => false +```
 @@ -77,6 +77,8 @@ Once you have created your Matrix object, these methods are available to you: * [raise](raise.md): Raises the current matrix to the *xth* power * `raise` comes with a couple helper functions—calling `square()` is the same thing as calling `raise(2)`, and calling `cube()` is the same thing as calling `raise(3)` * [scalar](scalar.md): Multiplies each element in the matrix by a scalar +* [inverse](inverse.md): Inverts an *n* by *n* matrix +* [equals](equals.md): Compares against another matrix ### Important note about all Matrix methods
 @@ -0,0 +1,18 @@ +# Matrix#inverse() + +`Matrix#inverse()` does not take any parameters. + +## Errors + +`#inverse()` will throw an error if the matrix is not square (i.e., does not have the same number of rows and columns), or if the matrix is a 1x1. + +## Examples + +```javascript +new Matrix([ + [2, 0, -1], + [2, 1, 1], + [3, 4, 4] +]).inverse().toArray(); +// => [[ 0, .8, -.2], [ 1, -2.2, .8], [-1, 1.6, -.4]] +```
 @@ -328,9 +328,78 @@ throwError('The matrix must be a square matrix with a size of at least 2x2'); } + // Declare variables + var ratio; + var a; + var n = this.rows; + + // Put an identity matrix to the right of matrix + this.cols = 2 * n; + for (var i = 0; i < n; i++) { + for (var j = n; j < 2 * n; j++) { + if (i === (j - n)) { + this[i][j] = 1; + } + else { + this[i][j] = 0; + } + } + } + + for (var i = 0; i < n; i++) { + for (var j = 0; j < n; j++) { + if (i !== j) { + ratio = this[j][i] / this[i][i]; + for (var k = 0; k < 2 * n; k++) { + this[j][k] -= ratio * this[i][k]; + } + } + } + } + + for (var i = 0; i < n; i++) { + a = this[i][i]; + for (var j = 0; j < 2 * n; j++) { + this[i][j] /= a; + } + } + + // Rmove the left-hand identity matrix + for (var i = 0; i < n; i++) { + this[i].splice(0, n); + } + this.cols = n; + return this; }; + // Public: Returns whether or not the current matrix equals + // the passed matrix. + // + // Returns a boolean. + Matrix.prototype.equals = function(m) { + // Convert the argument to a Matrix object if it was an array + if (Array.isArray(m)) { + m = new Matrix(m); + } + + // Return false if the dimensions do not match + if (this.rows !== m.rows || this.cols !== m.cols) { + return false; + } + + // Check each location + for (var row = 0; row < this.rows; row++) { + for (var col = 0; col < this.cols; col++) { + if (this[row][col] !== m[row][col]) { + return false; + } + } + } + + return true; + }; + // Public: Decides whether or not the matrix is square. // // Returns a boolean of whether or not the matrix is square.
 @@ -210,6 +210,39 @@ describe('Matrix', function() { }); }); + describe('#inverse()', function() { + it('should invert a matrix', function() { + expect(new Matrix([ + [2, 0, -1], + [2, 1, 1], + [3, 4, 4] + ]).inverse().toArray()).to.eql([ + [ 0, .8, -.2], + [ 1, -2.2, .8], + [-1, 1.6, -.4] + ]); + }); + }); + + describe('#equals()', function() { + it('should return true if the two matrices are equal', function() { + expect(m1.equals(m1.clone())).to.be(true); + }); + + it('should return false if the two matrices are not equal', function() { + expect(m1.equals(m3.clone())).to.be(false); + }); + + it('should return false if the dimensions are not the same', function() { + expect(m1.equals(m2.clone())).to.be(false); + }); + + it('should accept an array', function() { + expect(m1.equals(m1.toArray())).to.be(true); + expect(m1.equals(m3.toArray())).to.be(false); + }); + }); + describe('Matrix.identity', function() { it('should create an identity matrix of any size', function() { expect(new Matrix.identity(2).toArray()).to.eql([ @@ -242,4 +275,35 @@ describe('Matrix', function() { }).to.not.throwError(); }); }); + + describe('#_setData()', function() { + it('should accept a two dimensional array', function() { + expect(m1._setData([[1,2], [3,4]]).toArray()).to.eql([[1,2], [3,4]]); + }); + + it('should accept a matrix object', function() { + expect(m1._setData(m2).toArray()).to.eql([ + [2, 1], + [1, 1], + [3, 1] + ]); + }); + + it('should accept a function that takes the row and column numbers as parameters', function() { + expect(m1._setData(function(row, col) { + return row * col; + }).toArray()).to.eql([ + [0, 0, 0], + [0, 1, 2], + [0, 2, 4] + ]); + }); + + it('should update the row and column count of the matrix', function() { + m1._setData([[1,2], [3,4]]); + + expect(m1.rows).to.equal(2); + expect(m1.cols).to.equal(2); + }); + }); });