diff --git a/lib/type/matrix/CcsFormat.js b/lib/type/matrix/CcsFormat.js index 1029f1cc2..79bae34ed 100644 --- a/lib/type/matrix/CcsFormat.js +++ b/lib/type/matrix/CcsFormat.js @@ -6,6 +6,7 @@ var DimensionError = require('../../error/DimensionError'); var array = util.array; var object = util.object; var string = util.string; +var number = util.number; var isArray = Array.isArray; var validateIndex = array.validateIndex; @@ -47,21 +48,36 @@ module.exports = function (math) { for (var i = 0; i < rows; i++) { // current row var row = data[i]; - // update columns - if (j === 0 && columns < row.length) - columns = row.length; - // check row has column - if (j < row.length) { - // value - var v = row[j]; - // check value != 0 - if (!math.equal(v, 0)) { + // check row is an array + if (isArray(row)) { + // update columns if needed (only on first column) + if (j ===0 && columns < row.length) + columns = row.length; + // check row has column + if (j < row.length) { + // value + var v = row[j]; + // check value != 0 + if (!math.equal(v, 0)) { + // store value + this._values.push(v); + // index + this._index.push(i); + } + } + } + else { + // update columns if needed (only on first column) + if (j === 0 && columns < 1) + columns = 1; + // check value != 0 (row is a scalar) + if (!math.equal(row, 0)) { // store value - this._values.push(v); + this._values.push(row); // index this._index.push(i); } - } + } } // increment index j++; @@ -286,7 +302,7 @@ module.exports = function (math) { * var value = matrix.subset(index, replacement) // replace subset * * @param {Index} index - * @param {Array | DenseFormat | *} [replacement] + * @param {Array | CcsFormat | *} [replacement] * @param {*} [defaultValue=0] Default value, filled in on new entries when * the matrix is resized. If not provided, * new matrix elements will be filled with zeros. @@ -304,9 +320,9 @@ module.exports = function (math) { * each entry of the matrix. * @param {function} callback The callback function is invoked with three * parameters: the value of the element, the index - * of the element, and the DenseFormat being traversed. + * of the element, and the Matrix being traversed. * @param {Matrix} matrix The Matrix instance - * @return {DenseFormat} matrix + * @return {CcsFormat} matrix */ CcsFormat.prototype.map = function (callback, matrix) { // result arrays @@ -327,11 +343,13 @@ module.exports = function (math) { // store value values.push(v); // index - index.push(i); + index.push(x); } }; // loop columns for (var j = 0; j < columns; j++) { + // store pointer to values index + ptr.push(values.length); // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1] var k0 = this._ptr[j]; var k1 = this._ptr[j + 1]; @@ -349,7 +367,7 @@ module.exports = function (math) { // increment k k++; // update pointer - p = j + 1; + p = i + 1; } // zero values for (var y = p; y < rows; y++) @@ -366,6 +384,178 @@ module.exports = function (math) { }); }; + /** + * Execute a callback function on each entry of the matrix. + * @param {function} callback The callback function is invoked with three + * parameters: the value of the element, the index + * of the element, and the Matrix being traversed. + * @param {Matrix} matrix The Matrix instance + */ + CcsFormat.prototype.forEach = function (callback, matrix) { + // values index + var k = 0; + // rows and columns + var rows = this._size[0]; + var columns = this._size[1]; + // loop columns + for (var j = 0; j < columns; j++) { + // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1] + var k0 = this._ptr[j]; + var k1 = this._ptr[j + 1]; + // column pointer + var p = 0; + // check k is within [k0, k1[ + while (k >= k0 && k < k1) { + // row index + var i = this._index[k]; + // zero values + for (var x = p; x < i; x++) + callback(0, [x, j], matrix); + // value @ k + callback(this._values[k], [i, j], matrix); + // increment k + k++; + // update pointer + p = i + 1; + } + // zero values + for (var y = p; y < rows; y++) + callback(0, [y, j], matrix); + } + }; + + /** + * Resize the matrix + * @param {Number[]} size + * @param {*} [defaultValue=0] Default value, filled in on new entries. + * If not provided, the matrix elements will + * be filled with zeros. + * @return {CcsFormat} self The matrix itself is returned + */ + CcsFormat.prototype.resize = function (size, defaultValue) { + // validate arguments + if (!isArray(size)) + throw new TypeError('Array expected'); + if (size.length !== 2) + throw new Error('Only two dimensions matrix are supported'); + + // check sizes + size.forEach(function (value) { + if (!number.isNumber(value) || !number.isInteger(value) || value < 0) { + throw new TypeError('Invalid size, must contain positive integers ' + + '(size: ' + string.format(size) + ')'); + } + }); + + // value to inser at the time of growing matrix + var value = (defaultValue !== undefined) ? defaultValue : 0; + // should we insert the value? + var ins = !math.equal(value, 0); + + // old columns and rows + var r = this._size[0]; + var c = this._size[1]; + + // rows & columns + var rows = size[0]; + var columns = size[1]; + + var i, j, k; + + // check we need to increase columns + if (columns > c) { + // loop new columns + for (j = c; j < columns; j++) { + // update ptr for current column + this._ptr[j] = this._values.length; + // check we need to insert values + if (ins) { + // loop rows + for (i = 0; i < r; i++) { + // add new values + this._values.push(value); + // update index + this._index.push(i); + } + } + } + // store number of values in ptr + this._ptr[columns] = this._values.length; + } + else if (columns < c) { + // truncate ptr + this._ptr.splice(columns + 1, c - columns); + // truncate values and index + this._values.splice(this._ptr[columns], this._values.length); + this._index.splice(this._ptr[columns], this._index.length); + } + // update columns + c = columns; + + // check we need to increase rows + if (rows > r) { + // check we have to insert values + if (ins) { + // inserts + var n = 0; + // loop columns + for (j = 0; j < c; j++) { + // update ptr for current column + this._ptr[j] = this._ptr[j] + n; + // where to insert values + k = this._ptr[j + 1] + n; + // pointer + var p = 0; + // loop new rows, initialize pointer + for (i = r; i < rows; i++, p++) { + // add value + this._values.splice(k + p, 0, value); + // update index + this._index.splice(k + p, 0, i); + // increment inserts + n++; + } + } + // store number of values in ptr + this._ptr[c] = this._values.length; + } + } + else if (rows < r) { + // deletes + var d = 0; + // loop columns + for (j = 0; j < c; j++) { + // update ptr for current column + this._ptr[j] = this._ptr[j] - d; + // where values start for next column + var k0 = this._ptr[j]; + var k1 = this._ptr[j + 1] - d; + // loop index + for (k = k0; k < k1; k++) { + // row + i = this._index[k]; + // check we need to delete value and index + if (i > rows - 1) { + // remove value + this._values.splice(k, 1); + // remove item from index + this._index.splice(k, 1); + // increase deletes + d++; + } + } + } + // update ptr for current column + this._ptr[j] = this._values.length; + } + + // update size + this._size = [rows, columns]; + + // return the matrix itself + return this; + }; + CcsFormat.diagonal = function (rows, columns, value) { // create arrays var values = []; diff --git a/lib/type/matrix/DenseFormat.js b/lib/type/matrix/DenseFormat.js index 6043063db..e3965ec7c 100644 --- a/lib/type/matrix/DenseFormat.js +++ b/lib/type/matrix/DenseFormat.js @@ -416,7 +416,7 @@ module.exports = function (math) { * each entry of the matrix. * @param {function} callback The callback function is invoked with three * parameters: the value of the element, the index - * of the element, and the DenseFormat being traversed. + * of the element, and the Matrix being traversed. * @param {Matrix} matrix The Matrix instance * @return {DenseFormat} matrix */ @@ -442,7 +442,7 @@ module.exports = function (math) { * Execute a callback function on each entry of the matrix. * @param {function} callback The callback function is invoked with three * parameters: the value of the element, the index - * of the element, and the DenseFormat being traversed. + * of the element, and the Matrix being traversed. * @param {Matrix} matrix The Matrix instance */ DenseFormat.prototype.forEach = function (callback, matrix) { diff --git a/test/type/Matrix.test.js b/test/type/Matrix.test.js index 951db1552..88b3d687d 100644 --- a/test/type/Matrix.test.js +++ b/test/type/Matrix.test.js @@ -255,7 +255,7 @@ describe('matrix', function() { it('should set a value in a matrix', function() { var m = new Matrix([[0, 0], [0, 0]]); - m.set([1,0], 5); + m.set([1, 0], 5); assert.deepEqual(m, new Matrix([ [0, 0], [5, 0] @@ -512,26 +512,18 @@ describe('matrix', function() { var m, m2; m = new Matrix([ - [[1,2],[3,4]], - [[5,6],[7,8]], - [[9,10],[11,12]], - [[13,14],[15,16]] + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11,12], + [13, 14, 15,16] ]); m2 = m.map(function (value) { return value * 2; }); - assert.deepEqual(m2.valueOf(), [ - [[2,4],[6,8]], - [[10,12],[14,16]], - [[18,20],[22,24]], - [[26,28],[30,32]] + assert.deepEqual(m2.toArray(), [ + [2, 4, 6, 8], + [10, 12, 14, 16], + [18, 20, 22, 24], + [26, 28, 30, 32] ]); - - m = new Matrix([1]); - m2 = m.map(function (value) { return value * 2; }); - assert.deepEqual(m2.valueOf(), [2]); - - m = new Matrix([1,2,3]); - m2 = m.map(function (value) { return value * 2; }); - assert.deepEqual(m2.valueOf(), [2,4,6]); }); it('should work on empty matrices', function() { @@ -541,43 +533,38 @@ describe('matrix', function() { }); it('should invoke callback with parameters value, index, obj', function() { - var m = new Matrix([[1,2,3], [4,5,6]]); + var m = new Matrix([[1, 2, 3], [4, 5, 6]]); + + var m2 = m.map( + function (value, index, obj) { + return value + index[0] * 100 + index[1] * 10 + (obj === m ? 1000 : 0); + } + ); - var m2 = m.map(function (value, index, obj) { - return math.clone([value, index, obj === m]); - }); - assert.deepEqual( m2.toArray(), [ - [ - [1, [0, 0], true ], - [2, [0, 1], true ], - [3, [0, 2], true ] - ], - [ - [4, [1, 0], true ], - [5, [1, 1], true ], - [6, [1, 2], true ] - ] + [1001, 1012, 1023], + [1104, 1115, 1126] ]); }); }); describe('forEach', function() { - it('should run on all elements of the matrix, last dimension first', function() { + it('should run on all elements of the matrix', function() { var m, output; m = new Matrix([ - [[1,2],[3,4]], - [[5,6],[7,8]], - [[9,10],[11,12]], - [[13,14],[15,16]] + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + [13, 14, 15, 16] ]); output = []; m.forEach(function (value) { output.push(value); }); - assert.deepEqual(output, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]); + // assert all values were visited, order is Matrix storage format specific + assert.deepEqual(output.sort(), [1, 10, 11,12, 13, 14, 15, 16, 2, 3, 4, 5, 6, 7, 8, 9]); m = new Matrix([1]); output = []; @@ -587,7 +574,8 @@ describe('matrix', function() { m = new Matrix([1,2,3]); output = []; m.forEach(function (value) { output.push(value); }); - assert.deepEqual(output, [1,2,3]); + // assert all values were visited, order is Matrix storage format specific + assert.deepEqual(output.sort(), [1, 2, 3]); }); it('should work on empty matrices', function() { @@ -598,22 +586,21 @@ describe('matrix', function() { }); it('should invoke callback with parameters value, index, obj', function() { - var m = new Matrix([[1,2,3], [4,5,6]]); - + var m = new Matrix( + [ + [1, 2, 3], + [4, 5, 6] + ]); + var o = {}; var output = []; - m.forEach(function (value, index, obj) { - output.push(math.clone([value, index, obj === m])); - }); - assert.deepEqual(output, [ - [1, [0, 0], true ], - [2, [0, 1], true ], - [3, [0, 2], true ], - [4, [1, 0], true ], - [5, [1, 1], true ], - [6, [1, 2], true ] - ]); + m.forEach( + function (value, index, obj) { + output.push(value + index[0] * 100 + index[1] * 10 + (obj === m ? 1000 : 0)); + } + ); + // assert all values were visited, order is Matrix storage format specific + assert.deepEqual(output.sort(), [1001, 1012, 1023, 1104, 1115, 1126]); }); - }); describe('clone', function() { diff --git a/test/type/matrix/CcsFormat.test.js b/test/type/matrix/CcsFormat.test.js index 30636b309..6c3f03bb7 100644 --- a/test/type/matrix/CcsFormat.test.js +++ b/test/type/matrix/CcsFormat.test.js @@ -64,6 +64,15 @@ describe('CcsFormat', function() { assert.deepEqual(m._ptr, [0]); }); + it('should create a CCS from a vector', function () { + var m = new CcsFormat([1, 2, 3]); + assert.equal(m._format, 'ccs'); + assert.deepEqual(m._size, [3, 1]); + assert.deepEqual(m._values, [1, 2, 3]); + assert.deepEqual(m._index, [0, 1, 2]); + assert.deepEqual(m._ptr, [0, 3]); + }); + it('should throw an error when called without new keyword', function () { assert.throws(function () { CcsFormat(); }, /Constructor must be called with the new operator/); }); @@ -341,6 +350,212 @@ describe('CcsFormat', function() { }); }); + describe('resize', function() { + + it('should increase columns as needed, zero value', function() { + var m = new CcsFormat( + [ + [1, 2, 3], + [4, 5, 6] + ]); + m.resize([2, 4]); + assert.deepEqual(m._size, [2, 4]); + assert.deepEqual(m._values, [1, 4, 2, 5, 3, 6]); + assert.deepEqual(m._index, [0, 1, 0, 1, 0, 1]); + assert.deepEqual(m._ptr, [0, 2, 4, 6, 6]); + assert.deepEqual( + m.toArray(), + [ + [1, 2, 3, 0], + [4, 5, 6, 0] + ]); + }); + + it('should increase columns as needed, non zero value', function() { + var m = new CcsFormat( + [ + [1, 2, 3], + [4, 5, 6] + ]); + m.resize([2, 4], 100); + assert.deepEqual(m._size, [2, 4]); + assert.deepEqual(m._values, [1, 4, 2, 5, 3, 6, 100, 100]); + assert.deepEqual(m._index, [0, 1, 0, 1, 0, 1, 0, 1]); + assert.deepEqual(m._ptr, [0, 2, 4, 6, 8]); + assert.deepEqual( + m.toArray(), + [ + [1, 2, 3, 100], + [4, 5, 6, 100] + ]); + }); + + it('should increase rows as needed, zero value', function() { + var m = new CcsFormat( + [ + [1, 2, 3], + [4, 5, 6] + ]); + m.resize([3, 3]); + assert.deepEqual(m._size, [3, 3]); + assert.deepEqual(m._values, [1, 4, 2, 5, 3, 6]); + assert.deepEqual(m._index, [0, 1, 0, 1, 0, 1]); + assert.deepEqual(m._ptr, [0, 2, 4, 6]); + assert.deepEqual( + m.toArray(), + [ + [1, 2, 3], + [4, 5, 6], + [0, 0, 0] + ]); + }); + + it('should increase rows as needed, non zero value', function() { + var m = new CcsFormat( + [ + [1, 2, 3], + [4, 5, 6] + ]); + m.resize([3, 3], 100); + assert.deepEqual(m._size, [3, 3]); + assert.deepEqual(m._values, [1, 4, 100, 2, 5, 100, 3, 6, 100]); + assert.deepEqual(m._index, [0, 1, 2, 0, 1, 2, 0, 1, 2]); + assert.deepEqual(m._ptr, [0, 3, 6, 9]); + assert.deepEqual( + m.toArray(), + [ + [1, 2, 3], + [4, 5, 6], + [100, 100, 100] + ]); + }); + + it('should increase rows & columns as needed, zero value, empty CCS', function() { + var m = new CcsFormat([]); + m.resize([2, 2]); + assert.deepEqual(m._size, [2, 2]); + assert.deepEqual(m._values, []); + assert.deepEqual(m._index, []); + assert.deepEqual(m._ptr, [0, 0, 0]); + assert.deepEqual( + m.toArray(), + [ + [0, 0], + [0, 0] + ]); + }); + + it('should increase rows & columns as needed, non zero value, empty CCS', function() { + var m = new CcsFormat([]); + m.resize([2, 2], 100); + assert.deepEqual(m._size, [2, 2]); + assert.deepEqual(m._values, [100, 100, 100, 100]); + assert.deepEqual(m._index, [0, 1, 0, 1]); + assert.deepEqual(m._ptr, [0, 2, 4]); + assert.deepEqual( + m.toArray(), + [ + [100, 100], + [100, 100] + ]); + }); + + it('should decrease columns as needed', function() { + var m = new CcsFormat( + [ + [1, 2, 3], + [4, 5, 6] + ]); + m.resize([2, 2]); + assert.deepEqual(m._size, [2, 2]); + assert.deepEqual(m._values, [1, 4, 2, 5]); + assert.deepEqual(m._index, [0, 1, 0, 1]); + assert.deepEqual(m._ptr, [0, 2, 4]); + assert.deepEqual( + m.toArray(), + [ + [1, 2], + [4, 5] + ]); + }); + + it('should decrease columns as needed, zero matrix', function() { + var m = new CcsFormat( + [ + [0, 0, 0], + [0, 0, 0] + ]); + m.resize([2, 2]); + assert.deepEqual(m._size, [2, 2]); + assert.deepEqual(m._values, []); + assert.deepEqual(m._index, []); + assert.deepEqual(m._ptr, [0, 0, 0]); + assert.deepEqual( + m.toArray(), + [ + [0, 0], + [0, 0] + ]); + }); + + it('should decrease rows as needed', function() { + var m = new CcsFormat( + [ + [1, 2], + [3, 4] + ]); + m.resize([1, 2]); + assert.deepEqual(m._size, [1, 2]); + assert.deepEqual(m._values, [1, 2]); + assert.deepEqual(m._index, [0, 0]); + assert.deepEqual(m._ptr, [0, 1, 2]); + assert.deepEqual( + m.toArray(), + [ + [1, 2] + ]); + }); + + it('should decrease rows as needed, zero CCS', function() { + var m = new CcsFormat( + [ + [0, 0], + [0, 0] + ]); + m.resize([1, 2]); + assert.deepEqual(m._size, [1, 2]); + assert.deepEqual(m._values, []); + assert.deepEqual(m._index, []); + assert.deepEqual(m._ptr, [0, 0, 0]); + assert.deepEqual( + m.toArray(), + [ + [0, 0] + ]); + }); + + it('should decrease rows & columns as needed, zero CCS', function() { + var m = new CcsFormat( + [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0] + ]); + m.resize([2, 2]); + assert.deepEqual(m._size, [2, 2]); + assert.deepEqual(m._values, []); + assert.deepEqual(m._index, []); + assert.deepEqual(m._ptr, [0, 0, 0]); + assert.deepEqual( + m.toArray(), + [ + [0, 0], + [0, 0] + ]); + }); + }); + describe('clone', function() { it('should clone the matrix properly', function() { @@ -356,6 +571,107 @@ describe('CcsFormat', function() { }); }); + describe('map', function() { + + it('should apply the given function to all elements in the matrix', function() { + var m, m2; + + m = new CcsFormat([ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11,12], + [13, 14, 15,16] + ]); + m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual(m2.toArray(), [ + [2, 4, 6, 8], + [10, 12, 14, 16], + [18, 20, 22, 24], + [26, 28, 30, 32] + ]); + + m = new CcsFormat([1]); + m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual(m2.toArray(), [[2]]); + + m = new CcsFormat([1,2,3]); + m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual(m2.toArray(), [[2],[4],[6]]); + }); + + it('should work on empty matrices', function() { + var m = new CcsFormat([]); + var m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual(m2.toArray(), []); + }); + + it('should invoke callback with parameters value, index, obj', function() { + var m = new CcsFormat([[1, 2, 3], [4, 5, 6]]); + var o = {}; + + var m2 = m.map( + function (value, index, obj) { + return value + index[0] * 100 + index[1] * 10 + (obj === o ? 1000 : 0); + }, + o + ); + + assert.deepEqual( + m2.toArray(), + [ + [1001, 1012, 1023], + [1104, 1115, 1126] + ]); + }); + }); + + describe('forEach', function() { + + it('should run on all elements of the matrix', function() { + var m, output; + + m = new CcsFormat([ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11,12], + [13, 14, 15,16] + ]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16]); + + m = new CcsFormat([1]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, [1]); + + m = new CcsFormat([1,2,3]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, [1,2,3]); + }); + + it('should work on empty matrices', function() { + m = new CcsFormat([]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, []); + }); + + it('should invoke callback with parameters value, index, obj', function() { + var m = new CcsFormat([[1,2,3], [4,5,6]]); + var o = {}; + var output = []; + m.forEach( + function (value, index, obj) { + output.push(value + index[0] * 100 + index[1] * 10 + (obj === o ? 1000 : 0)); + }, + o + ); + assert.deepEqual(output, [1001, 1104, 1012, 1115, 1023, 1126]); + }); + }); + describe('toString', function() { it('should return string representation of matrix', function() { diff --git a/test/type/matrix/DenseFormat.test.js b/test/type/matrix/DenseFormat.test.js index 767ee47e7..2ecc1d754 100644 --- a/test/type/matrix/DenseFormat.test.js +++ b/test/type/matrix/DenseFormat.test.js @@ -314,6 +314,121 @@ describe('DenseFormat', function() { assert.deepEqual(m1._data, m2._data); }); }); + + describe('map', function() { + + it('should apply the given function to all elements in the matrix', function() { + var m = new DenseFormat([ + [[1,2],[3,4]], + [[5,6],[7,8]], + [[9,10],[11,12]], + [[13,14],[15,16]] + ]); + var m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual( + m2.valueOf(), + [ + [[2,4],[6,8]], + [[10,12],[14,16]], + [[18,20],[22,24]], + [[26,28],[30,32]] + ]); + + m = new DenseFormat([1]); + m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual(m2.valueOf(), [2]); + + m = new DenseFormat([1,2,3]); + m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual(m2.valueOf(), [2,4,6]); + }); + + it('should work on empty matrices', function() { + var m = new DenseFormat([]); + var m2 = m.map(function (value) { return value * 2; }); + assert.deepEqual(m2.toArray(), []); + }); + + it('should invoke callback with parameters value, index, obj', function() { + var m = new DenseFormat([[1,2,3], [4,5,6]]); + var o = {}; + var m2 = m.map( + function (value, index, obj) { + return math.clone([value, index, obj === o]); + }, + o + ); + + assert.deepEqual( + m2.toArray(), + [ + [ + [1, [0, 0], true ], + [2, [0, 1], true ], + [3, [0, 2], true ] + ], + [ + [4, [1, 0], true ], + [5, [1, 1], true ], + [6, [1, 2], true ] + ] + ]); + }); + }); + + describe('forEach', function() { + + it('should run on all elements of the matrix, last dimension first', function() { + var m, output; + + m = new DenseFormat([ + [[1,2],[3,4]], + [[5,6],[7,8]], + [[9,10],[11,12]], + [[13,14],[15,16]] + ]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]); + + m = new DenseFormat([1]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, [1]); + + m = new DenseFormat([1,2,3]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, [1,2,3]); + }); + + it('should work on empty matrices', function() { + m = new DenseFormat([]); + output = []; + m.forEach(function (value) { output.push(value); }); + assert.deepEqual(output, []); + }); + + it('should invoke callback with parameters value, index, obj', function() { + var m = new DenseFormat([[1,2,3], [4,5,6]]); + var o = {}; + var output = []; + m.forEach( + function (value, index, obj) { + output.push(math.clone([value, index, obj === o])); + }, + o + ); + assert.deepEqual(output, [ + [1, [0, 0], true ], + [2, [0, 1], true ], + [3, [0, 2], true ], + [4, [1, 0], true ], + [5, [1, 1], true ], + [6, [1, 2], true ] + ]); + }); + }); describe('toString', function() {