├── .gitattributes ├── lib ├── index.js ├── dtypes.js ├── btypes.js ├── get.raw.js ├── set.raw.js ├── iget.raw.js ├── iset.raw.js ├── mset1.js ├── mset5.js ├── get.js ├── tostring.js ├── iget.js ├── mset4.js ├── tojson.js ├── mset2.js ├── mset6.js ├── set.js ├── iset.js ├── mset3.js ├── sget.raw.js ├── ctor.raw.js ├── sget.js ├── mget.raw.js ├── matrix.raw.js ├── sset.raw.js ├── ctor.js ├── mset.raw.js ├── mget.js ├── sset.js ├── matrix.js └── mset.js ├── .jshintignore ├── .editorconfig ├── .travis.yml ├── TODO.md ├── test ├── test.btypes.js ├── test.dtypes.js ├── test.js ├── test.get.raw.js ├── test.toString.js ├── test.sget.raw.js ├── test.set.raw.js ├── test.iget.raw.js ├── test.sget.js ├── test.get.js ├── test.tojson.js ├── test.iget.js ├── test.ctor.raw.js ├── test.matrix.raw.js ├── test.iset.raw.js ├── test.ctor.js ├── test.mget.raw.js ├── test.sset.raw.js ├── test.set.js ├── test.iset.js ├── test.sset.js ├── test.matrix.js ├── test.mget.js ├── test.mset.raw.js └── test.mset.js ├── examples ├── index.js ├── fancy_setters.js └── fancy_getters.js ├── .npmignore ├── benchmark ├── b.iget.js ├── b.toString.js ├── b.set.js ├── b.get.js ├── b.sget.js ├── b.mget.js └── b.create.js ├── LICENSE ├── .jshintrc ├── .gitignore ├── package.json ├── Makefile └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // EXPORTS // 4 | 5 | module.exports = require( './matrix.js' ); 6 | module.exports.raw = require( './matrix.raw.js' ); 7 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | 2 | # Directories # 3 | ############### 4 | build/ 5 | reports/ 6 | dist/ 7 | 8 | # Node.js # 9 | ########### 10 | /node_modules/ 11 | 12 | # Git # 13 | ####### 14 | .git* 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | - '5' 5 | - '4' 6 | # - '0.12' 7 | # - '0.10' 8 | # - 'iojs' 9 | before_install: 10 | - npm update -g npm 11 | after_script: 12 | - npm run codecov 13 | -------------------------------------------------------------------------------- /lib/dtypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // DATA TYPES // 4 | 5 | var DTYPES = [ 6 | 'int8', 7 | 'uint8', 8 | 'uint8_clamped', 9 | 'int16', 10 | 'uint16', 11 | 'int32', 12 | 'uint32', 13 | 'float32', 14 | 'float64' 15 | ]; 16 | 17 | 18 | // EXPORTS // 19 | 20 | module.exports = DTYPES; 21 | -------------------------------------------------------------------------------- /lib/btypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // BASE TYPES // 4 | 5 | var BTYPES = { 6 | 'int8': Int8Array, 7 | 'uint8': Uint8Array, 8 | 'uint8_clamped': Uint8ClampedArray, 9 | 'int16': Int16Array, 10 | 'uint16': Uint16Array, 11 | 'int32': Int32Array, 12 | 'uint32': Uint32Array, 13 | 'float32': Float32Array, 14 | 'float64': Float64Array 15 | }; 16 | 17 | 18 | // EXPORTS // 19 | 20 | module.exports = BTYPES; 21 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | 1. Should we at least validate if `matrix-like` in `mset.js`? 5 | 2. for methods accepting index `arrays` as input, generalize for `array-like` objects 6 | - e.g., the case where `linspace` outputs a typed-array 7 | - currently, only plain arrays are accepted 8 | 3. allow for specifying column major order? 9 | 4. fix `toString` and `toJSON` tests by only using `call` 10 | - explicitly provide the context 11 | 5. 12 | -------------------------------------------------------------------------------- /test/test.btypes.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | BTYPES = require( './../lib/btypes.js' ); 8 | 9 | 10 | // VARIABLES // 11 | 12 | var expect = chai.expect, 13 | assert = chai.assert; 14 | 15 | 16 | // TESTS // 17 | 18 | describe( 'btypes', function tests() { 19 | 20 | it( 'should export an object', function test() { 21 | expect( BTYPES ).to.be.an( 'object' ); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /test/test.dtypes.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | DTYPES = require( './../lib/dtypes.js' ); 8 | 9 | 10 | // VARIABLES // 11 | 12 | var expect = chai.expect, 13 | assert = chai.assert; 14 | 15 | 16 | // TESTS // 17 | 18 | describe( 'dtypes', function tests() { 19 | 20 | it( 'should export an array', function test() { 21 | expect( DTYPES ).to.be.an( 'array' ); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /lib/get.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: get( i, j ) 5 | * Returns a matrix element based on the provided row and column indices. 6 | * 7 | * @param {Number} i - row index 8 | * @param {Number} j - column index 9 | * @returns {Number|Undefined} matrix element 10 | */ 11 | function get( i, j ) { 12 | /*jshint validthis:true */ 13 | return this.data[ this.offset + i*this.strides[0] + j*this.strides[1] ]; 14 | } // end FUNCTION get() 15 | 16 | 17 | // EXPORTS // 18 | 19 | module.exports = get; 20 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var matrix = require( './../lib' ); 4 | 5 | // Create a new 2x2 matrix: 6 | var mat = matrix( [2,2] ); 7 | console.log( mat ); 8 | 9 | // Inspect the initialized matrix elements: 10 | console.log( mat.get( 1, 1 ) ); 11 | 12 | // Set a matrix element: 13 | mat.set( 1, 1, 5 ); 14 | 15 | // Confirm that the matrix element was set: 16 | console.log( mat.get( 1, 1 ) ); 17 | 18 | // Convert the matrix to a string: 19 | console.log( mat.toString() ); 20 | 21 | // Convert the matrix to JSON: 22 | console.log( mat.toJSON() ); 23 | -------------------------------------------------------------------------------- /examples/fancy_setters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var matrix = require( './../lib' ); 4 | 5 | var data, A, B, i; 6 | 7 | data = new Int8Array( 10*10 ); 8 | for ( i = 0; i < data.length; i++ ) { 9 | data[ i ] = i; 10 | } 11 | // Create a 10x10 matrix: 12 | A = matrix( data, [10,10] ); 13 | 14 | // Extract a 4x4 submatrix from A: 15 | console.log( A.sget( '3:7,5:9' ) ); 16 | 17 | // Create a zero-filled matrix: 18 | B = matrix( [2,2], 'int8' ); 19 | 20 | // Set a submatrix in A to the zero-filled matrix B: 21 | A.sset( '4:6,6:8', B ); 22 | console.log( A.sget( '3:7,5:9' ) ); 23 | -------------------------------------------------------------------------------- /lib/set.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: set( i, j, value ) 5 | * Sets a matrix element based on the provided row and column indices. 6 | * 7 | * @param {Number} i - row index 8 | * @param {Number} j - column index 9 | * @param {Number} value - value to set 10 | * @returns {Matrix} Matrix instance 11 | */ 12 | function set( i, j, v ) { 13 | /* jshint validthis: true */ 14 | i = this.offset + i*this.strides[0] + j*this.strides[1]; 15 | if ( i >= 0 ) { 16 | this.data[ i ] = v; 17 | } 18 | return this; 19 | } // end FUNCTION set() 20 | 21 | 22 | // EXPORTS // 23 | 24 | module.exports = set; 25 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ); 8 | 9 | 10 | // VARIABLES // 11 | 12 | var expect = chai.expect, 13 | assert = chai.assert; 14 | 15 | 16 | // TESTS // 17 | 18 | describe( 'dstructs-matrix', function tests() { 19 | 20 | it( 'should export a function', function test() { 21 | expect( matrix ).to.be.a( 'function' ); 22 | }); 23 | 24 | it( 'should export a lower-level (raw) interface', function test() { 25 | expect( matrix.raw ).to.be.a( 'function' ); 26 | }); 27 | 28 | }); 29 | -------------------------------------------------------------------------------- /lib/iget.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: iget( idx ) 5 | * Returns a matrix element located at a specified index. 6 | * 7 | * @param {Number} idx - linear index 8 | * @returns {Number|Undefined} matrix element 9 | */ 10 | function iget( idx ) { 11 | /*jshint validthis:true */ 12 | var r, j; 13 | if ( idx < 0 ) { 14 | idx += this.length; 15 | if ( idx < 0 ) { 16 | return; 17 | } 18 | } 19 | j = idx % this.strides[ 0 ]; 20 | r = idx - j; 21 | if ( this.strides[ 0 ] < 0 ) { 22 | r = -r; 23 | } 24 | return this.data[ this.offset + r + j*this.strides[1] ]; 25 | } // end FUNCTION iget() 26 | 27 | 28 | // EXPORTS // 29 | 30 | module.exports = iget; 31 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Files # 3 | ######### 4 | Makefile 5 | README.md 6 | TODO.md 7 | 8 | # Directories # 9 | ############### 10 | build/ 11 | docs/ 12 | examples/ 13 | reports/ 14 | support/ 15 | test/ 16 | benchmark/ 17 | 18 | # Node.js # 19 | ########### 20 | .npmignore 21 | /node_modules/ 22 | 23 | # Logs # 24 | ######## 25 | *.log 26 | 27 | # OS generated files # 28 | ###################### 29 | .DS_Store 30 | .DS_Store? 31 | ._* 32 | .Spotlight-V100 33 | .Trashes 34 | Icon? 35 | ehthumbs.db 36 | Thumbs.db 37 | Desktop.ini 38 | 39 | # Temporary files # 40 | ################### 41 | *~ 42 | 43 | # Git # 44 | ####### 45 | .git* 46 | 47 | # Utilities # 48 | ############# 49 | .jshintrc 50 | .jshintignore 51 | .travis.yml 52 | .editorconfig 53 | -------------------------------------------------------------------------------- /lib/iset.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: iset( idx, value ) 5 | * Sets a matrix element located at a specified index. 6 | * 7 | * @param {Number} idx - linear index 8 | * @param {Number} value - value to set 9 | * @returns {Matrix} Matrix instance 10 | */ 11 | function iset( idx, v ) { 12 | /* jshint validthis: true */ 13 | var r, j; 14 | if ( idx < 0 ) { 15 | idx += this.length; 16 | if ( idx < 0 ) { 17 | return this; 18 | } 19 | } 20 | j = idx % this.strides[ 0 ]; 21 | r = idx - j; 22 | if ( this.strides[ 0 ] < 0 ) { 23 | r = -r; 24 | } 25 | this.data[ this.offset + r + j*this.strides[1] ] = v; 26 | return this; 27 | } // end FUNCTION iset() 28 | 29 | 30 | // EXPORTS // 31 | 32 | module.exports = iset; 33 | -------------------------------------------------------------------------------- /lib/mset1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: mset1( mat, idx, v ) 5 | * Sets multiple matrix elements to a numeric value `v`. 6 | * 7 | * @private 8 | * @param {Matrix} mat - Matrix instance 9 | * @param {Number[]} idx - linear indices 10 | * @param {Number} v - numeric value 11 | * @returns {Void} 12 | */ 13 | function mset1( mat, idx, v ) { 14 | var s0 = mat.strides[ 0 ], 15 | s1 = mat.strides[ 1 ], 16 | len = idx.length, 17 | o = mat.offset, 18 | sgn, 19 | r, j, n; 20 | 21 | sgn = ( s0 < 0 ) ? -1 : 1; 22 | for ( n = 0; n < len; n++ ) { 23 | j = idx[ n ] % s0; 24 | r = sgn * ( idx[n] - j ); 25 | mat.data[ o + r + j*s1 ] = v; 26 | } 27 | } // end FUNCTION mset1() 28 | 29 | 30 | // EXPORTS // 31 | 32 | module.exports = mset1; 33 | -------------------------------------------------------------------------------- /lib/mset5.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: mset5( mat, rows, cols, v ) 5 | * Sets multiple matrix elements to a numeric value `v`. 6 | * 7 | * @private 8 | * @param {Matrix} mat - Matrix instance 9 | * @param {Number[]} rows - row indices 10 | * @param {Number[]} cols - column indices 11 | * @param {Number} v - numeric value 12 | * @returns {Void} 13 | */ 14 | function mset5( mat, rows, cols, v ) { 15 | var s0 = mat.strides[ 0 ], 16 | s1 = mat.strides[ 1 ], 17 | nRows = rows.length, 18 | nCols = cols.length, 19 | o = mat.offset, 20 | r, 21 | i, j; 22 | 23 | for ( i = 0; i < nRows; i++ ) { 24 | r = o + rows[i]*s0; 25 | for ( j = 0; j < nCols; j++ ) { 26 | mat.data[ r + cols[j]*s1 ] = v; 27 | } 28 | } 29 | } // end FUNCTION mset5() 30 | 31 | 32 | // EXPORTS // 33 | 34 | module.exports = mset5; 35 | -------------------------------------------------------------------------------- /examples/fancy_getters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var matrix = require( './../lib' ); 4 | 5 | var data, mat, i; 6 | 7 | data = new Int8Array( 5*2 ); 8 | for ( i = 0; i < data.length; i++ ) { 9 | data[ i ] = i; 10 | } 11 | 12 | mat = matrix( data, [5,2] ); 13 | 14 | // Copy a matrix: 15 | console.log( mat.sget( ':,:' ) ); 16 | 17 | // Extract a submatrix: 18 | console.log( mat.sget( '1:4,:' ) ); 19 | 20 | // Flip a matrix top-to-bottom: 21 | console.log( mat.sget( '::-1,:' ) ); 22 | 23 | // Flip a matrix left-to-right: 24 | console.log( mat.sget( ':,::-1' ) ); 25 | 26 | // Out-of-bounds subsequence: 27 | console.log( mat.sget( '50:100,:' ) ); 28 | 29 | // Replicate a column: 30 | console.log( mat.mget( null, [1,1,1,1,1] ) ); 31 | 32 | // Tile extracted rows and columns: 33 | console.log( mat.mget( [1,2,1,2,1,2,1,2], [0,1,0,1,0,1,0,1] ) ); 34 | -------------------------------------------------------------------------------- /lib/get.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isNonNegativeInteger = require( 'validate.io-nonnegative-integer' ); 6 | 7 | 8 | // GET // 9 | 10 | /** 11 | * FUNCTION: get( i, j ) 12 | * Returns a matrix element based on the provided row and column indices. 13 | * 14 | * @param {Number} i - row index 15 | * @param {Number} j - column index 16 | * @returns {Number|Undefined} matrix element 17 | */ 18 | function get( i, j ) { 19 | /*jshint validthis:true */ 20 | if ( !isNonNegativeInteger( i ) || !isNonNegativeInteger( j ) ) { 21 | throw new TypeError( 'invalid input argument. Indices must be nonnegative integers. Values: `[' + i + ','+ j + ']`.' ); 22 | } 23 | return this.data[ this.offset + i*this.strides[0] + j*this.strides[1] ]; 24 | } // end FUNCTION get() 25 | 26 | 27 | // EXPORTS // 28 | 29 | module.exports = get; 30 | -------------------------------------------------------------------------------- /lib/tostring.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: toString() 5 | * Returns a string representation of Matrix elements. Rows are delineated by semicolons. Column values are comma-delimited. 6 | * 7 | * @returns {String} string representation 8 | */ 9 | function toString() { 10 | /* jshint validthis: true */ 11 | var nRows = this.shape[ 0 ], 12 | nCols = this.shape[ 1 ], 13 | s0 = this.strides[ 0 ], 14 | s1 = this.strides[ 1 ], 15 | m = nRows - 1, 16 | n = nCols - 1, 17 | str = '', 18 | o, 19 | i, j; 20 | 21 | for ( i = 0; i < nRows; i++ ) { 22 | o = this.offset + i*s0; 23 | for ( j = 0; j < nCols; j++ ) { 24 | str += this.data[ o + j*s1 ]; 25 | if ( j < n ) { 26 | str += ','; 27 | } 28 | } 29 | if ( i < m ) { 30 | str += ';'; 31 | } 32 | } 33 | return str; 34 | } // end FUNCTION toString() 35 | 36 | 37 | // EXPORTS // 38 | 39 | module.exports = toString; 40 | -------------------------------------------------------------------------------- /lib/iget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isInteger = require( 'validate.io-integer-primitive' ); 6 | 7 | 8 | // IGET // 9 | 10 | /** 11 | * FUNCTION: iget( idx ) 12 | * Returns a matrix element located at a specified index. 13 | * 14 | * @param {Number} idx - linear index 15 | * @returns {Number|Undefined} matrix element 16 | */ 17 | function iget( idx ) { 18 | /*jshint validthis:true */ 19 | var r, j; 20 | if ( !isInteger( idx ) ) { 21 | throw new TypeError( 'invalid input argument. Must provide a integer. Value: `' + idx + '`.' ); 22 | } 23 | if ( idx < 0 ) { 24 | idx += this.length; 25 | if ( idx < 0 ) { 26 | return; 27 | } 28 | } 29 | j = idx % this.strides[ 0 ]; 30 | r = idx - j; 31 | if ( this.strides[ 0 ] < 0 ) { 32 | r = -r; 33 | } 34 | return this.data[ this.offset + r + j*this.strides[1] ]; 35 | } // end FUNCTION iget() 36 | 37 | 38 | // EXPORTS // 39 | 40 | module.exports = iget; 41 | -------------------------------------------------------------------------------- /benchmark/b.iget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var matrix = require( './../lib' ).raw; 6 | 7 | 8 | // VARIABLES // 9 | 10 | var start, 11 | stop, 12 | nRows, 13 | nCols, 14 | res, 15 | len, 16 | m, 17 | v, 18 | i, j; 19 | 20 | 21 | // -------------------------------------- 22 | // WARM-UP 23 | 24 | len = 1e6; 25 | for ( i = 0; i < len; i++ ) { 26 | i = i; 27 | } 28 | 29 | 30 | // -------------------------------------- 31 | // BENCHMARK 32 | 33 | len = 1e6; 34 | nRows = 128; 35 | nCols = 128; 36 | 37 | res = new Array( 1 ); 38 | 39 | m = matrix( [nRows,nCols] ); 40 | j = 64*m.strides[0] + 1*m.strides[1]; 41 | 42 | start = process.hrtime(); 43 | for ( i = 0; i < len; i++ ) { 44 | v = m.iget( j ); 45 | } 46 | stop = process.hrtime( start ); 47 | 48 | res[ 0 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 49 | 50 | 51 | // -------------------------------------- 52 | // RESULTS 53 | 54 | console.log( 'iget:\t%d ops/sec', Math.floor( len/res[ 0 ] ) ); 55 | console.log( '\n' ); 56 | 57 | -------------------------------------------------------------------------------- /lib/mset4.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: mset4( mat, rows, cols, clbk, ctx ) 5 | * Sets multiple matrix elements using a callback function. 6 | * 7 | * @private 8 | * @param {Matrix} mat - Matrix instance 9 | * @param {Number[]} rows - row indices 10 | * @param {Number[]} cols - column indices 11 | * @param {Function} clbk - callback function 12 | * @param {Object} ctx - `this` context when invoking the provided callback 13 | * @returns {Void} 14 | */ 15 | function mset4( mat, rows, cols, clbk, ctx ) { 16 | var s0 = mat.strides[ 0 ], 17 | s1 = mat.strides[ 1 ], 18 | nRows = rows.length, 19 | nCols = cols.length, 20 | o = mat.offset, 21 | r, 22 | i, j, k; 23 | 24 | for ( i = 0; i < nRows; i++ ) { 25 | r = o + rows[i]*s0; 26 | for ( j = 0; j < nCols; j++ ) { 27 | k = r + cols[j]*s1; 28 | mat.data[ k ] = clbk.call( ctx, mat.data[ k ], rows[ i ], cols[ j ], k ); 29 | } 30 | } 31 | } // end FUNCTION mset4() 32 | 33 | 34 | // EXPORTS // 35 | 36 | module.exports = mset4; 37 | -------------------------------------------------------------------------------- /lib/tojson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var cast = require( 'dstructs-cast-arrays' ), 6 | copy = require( 'utils-copy' ); 7 | 8 | 9 | // TOJSON // 10 | 11 | /** 12 | * FUNCTION: toJSON() 13 | * Returns a JSON representation of a Matrix. 14 | * 15 | * @returns {Object} JSON representation 16 | */ 17 | function toJSON() { 18 | /* jshint validthis: true */ 19 | var prop, 20 | out; 21 | 22 | // Build an object containing all Matrix properties needed to revive a serialized Matrix... 23 | out = {}; 24 | out.type = 'Matrix'; 25 | out.dtype = this.dtype; 26 | out.shape = copy( this.shape ); 27 | out.offset = this.offset; 28 | out.strides = copy( this.strides ); 29 | 30 | prop = Object.getOwnPropertyDescriptor( this, 'data' ); 31 | out.raw = prop.writable && prop.configurable && prop.enumerable; 32 | 33 | // Cast data to a generic array: 34 | out.data = cast( this.data, 'generic' ); 35 | 36 | return out; 37 | } // end FUNCTION toJSON() 38 | 39 | 40 | // EXPORTS // 41 | 42 | module.exports = toJSON; 43 | -------------------------------------------------------------------------------- /benchmark/b.toString.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var matrix = require( './../lib' ); 6 | 7 | 8 | // VARIABLES // 9 | 10 | var start, 11 | stop, 12 | iArr, 13 | nRows, 14 | nCols, 15 | res, 16 | len, 17 | m, s, 18 | i; 19 | 20 | 21 | // -------------------------------------- 22 | // WARM-UP 23 | 24 | len = 1e6; 25 | for ( i = 0; i < len; i++ ) { 26 | i = i; 27 | } 28 | 29 | 30 | // -------------------------------------- 31 | // BENCHMARK 32 | 33 | len = 1e3; 34 | nRows = 128; 35 | nCols = 128; 36 | 37 | res = new Array( 1 ); 38 | 39 | iArr = new Float64Array( nRows*nCols ); 40 | for ( i = 0; i < nRows*nCols; i++ ) { 41 | iArr[ i ] = i; 42 | } 43 | 44 | m = matrix( iArr, [nRows,nCols] ); 45 | 46 | start = process.hrtime(); 47 | for ( i = 0; i < len; i++ ) { 48 | s = m.toString(); 49 | } 50 | stop = process.hrtime( start ); 51 | 52 | res[ 0 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 53 | 54 | 55 | // -------------------------------------- 56 | // RESULTS 57 | 58 | console.log( 'toString:\t%d ops/sec', Math.floor( len/res[ 0 ] ) ); 59 | console.log( '\n' ); 60 | 61 | -------------------------------------------------------------------------------- /lib/mset2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: mset2( mat, idx, clbk, ctx ) 5 | * Sets multiple matrix elements using a callback function. 6 | * 7 | * @private 8 | * @param {Matrix} mat - Matrix instance 9 | * @param {Number[]} idx - linear indices 10 | * @param {Function} clbk - callback function 11 | * @param {Object} ctx - `this` context when invoking the provided callback 12 | * @returns {Void} 13 | */ 14 | function mset2( mat, idx, clbk, ctx ) { 15 | var s0 = mat.strides[ 0 ], 16 | s1 = mat.strides[ 1 ], 17 | len = idx.length, 18 | o = mat.offset, 19 | sgn, 20 | r, c, 21 | i, k, n; 22 | 23 | sgn = ( s0 < 0 ) ? -1 : 1; 24 | for ( n = 0; n < len; n++ ) { 25 | // Get the column number: 26 | c = idx[ n ] % s0; 27 | 28 | // Determine the row offset: 29 | i = sgn * ( idx[n] - c ); 30 | 31 | // Get the row number: 32 | r = i / s0; 33 | 34 | // Calculate the index: 35 | k = o + i + c*s1; 36 | 37 | // Set the value: 38 | mat.data[ k ] = clbk.call( ctx, mat.data[ k ], r, c, k ); 39 | } 40 | } // end FUNCTION mset2() 41 | 42 | 43 | // EXPORTS // 44 | 45 | module.exports = mset2; 46 | -------------------------------------------------------------------------------- /lib/mset6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: mset6( mat, rows, cols, m ) 5 | * Sets multiple matrix elements using elements from another matrix. 6 | * 7 | * @private 8 | * @param {Matrix} mat - Matrix instance 9 | * @param {Number[]} rows - row indices 10 | * @param {Number[]} cols - column indices 11 | * @param {Matrix} m - Matrix instance 12 | * @returns {Void} 13 | */ 14 | function mset6( mat, rows, cols, m ) { 15 | var s0 = mat.strides[ 0 ], 16 | s1 = mat.strides[ 1 ], 17 | s2 = m.strides[ 0 ], 18 | s3 = m.strides[ 1 ], 19 | nRows = rows.length, 20 | nCols = cols.length, 21 | o0 = mat.offset, 22 | o1 = m.offset, 23 | r0, r1, 24 | i, j; 25 | 26 | if ( m.shape[ 0 ] !== nRows || m.shape[ 1 ] !== nCols ) { 27 | throw new Error( 'invalid input argument. The dimensions given by the row and column indices do not match the value matrix dimensions.' ); 28 | } 29 | for ( i = 0; i < nRows; i++ ) { 30 | r0 = o0 + rows[i]*s0; 31 | r1 = o1 + i*s2; 32 | for ( j = 0; j < nCols; j++ ) { 33 | mat.data[ r0 + cols[j]*s1 ] = m.data[ r1 + j*s3 ]; 34 | } 35 | } 36 | } // end FUNCTION mset6() 37 | 38 | 39 | // EXPORTS // 40 | 41 | module.exports = mset6; 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 The Compute.io Authors. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/set.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isNonNegativeInteger = require( 'validate.io-nonnegative-integer' ), 6 | isnan = require( 'validate.io-nan' ), 7 | isNumber = require( 'validate.io-number-primitive' ); 8 | 9 | 10 | // SET // 11 | 12 | /** 13 | * FUNCTION: set( i, j, value ) 14 | * Sets a matrix element based on the provided row and column indices. 15 | * 16 | * @param {Number} i - row index 17 | * @param {Number} j - column index 18 | * @param {Number} value - value to set 19 | * @returns {Matrix} Matrix instance 20 | */ 21 | function set( i, j, v ) { 22 | /* jshint validthis: true */ 23 | if ( !isNonNegativeInteger( i ) || !isNonNegativeInteger( j ) ) { 24 | throw new TypeError( 'invalid input argument. Row and column indices must be nonnegative integers. Values: `[' + i + ',' + j + ']`.' ); 25 | } 26 | if ( !isNumber( v ) && !isnan( v ) ) { 27 | throw new TypeError( 'invalid input argument. An input value must be a number primitive. Value: `' + v + '`.' ); 28 | } 29 | i = this.offset + i*this.strides[0] + j*this.strides[1]; 30 | if ( i >= 0 ) { 31 | this.data[ i ] = v; 32 | } 33 | return this; 34 | } // end FUNCTION set() 35 | 36 | 37 | // EXPORTS // 38 | 39 | module.exports = set; 40 | -------------------------------------------------------------------------------- /lib/iset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isInteger = require( 'validate.io-integer-primitive' ), 6 | isnan = require( 'validate.io-nan' ), 7 | isNumber = require( 'validate.io-number-primitive' ); 8 | 9 | 10 | // ISET // 11 | 12 | /** 13 | * FUNCTION: iset( idx, value ) 14 | * Sets a matrix element located at a specified index. 15 | * 16 | * @param {Number} idx - linear index 17 | * @param {Number} value - value to set 18 | * @returns {Matrix} Matrix instance 19 | */ 20 | function iset( idx, v ) { 21 | /* jshint validthis: true */ 22 | var r, j; 23 | if ( !isInteger( idx ) ) { 24 | throw new TypeError( 'invalid input argument. An index must be an integer. Value: `' + idx + '`.' ); 25 | } 26 | if ( !isNumber( v ) && !isnan( v ) ) { 27 | throw new TypeError( 'invalid input argument. An input value must be a number primitive. Value: `' + v + '`.' ); 28 | } 29 | if ( idx < 0 ) { 30 | idx += this.length; 31 | if ( idx < 0 ) { 32 | return this; 33 | } 34 | } 35 | j = idx % this.strides[ 0 ]; 36 | r = idx - j; 37 | if ( this.strides[ 0 ] < 0 ) { 38 | r = -r; 39 | } 40 | this.data[ this.offset + r + j*this.strides[1] ] = v; 41 | return this; 42 | } // end FUNCTION iset() 43 | 44 | 45 | // EXPORTS // 46 | 47 | module.exports = iset; 48 | -------------------------------------------------------------------------------- /lib/mset3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * FUNCTION: mset3( mat, idx, m ) 5 | * Sets multiple matrix elements using elements from another matrix. 6 | * 7 | * @private 8 | * @param {Matrix} mat - Matrix instance 9 | * @param {Number[]} idx - linear indices 10 | * @param {Matrix} m - Matrix instance 11 | * @returns {Void} 12 | */ 13 | function mset3( mat, idx, m ) { 14 | var s0 = mat.strides[ 0 ], 15 | s1 = mat.strides[ 1 ], 16 | s2 = m.strides[ 0 ], 17 | s3 = m.strides[ 1 ], 18 | len = idx.length, 19 | o0 = mat.offset, 20 | o1 = m.offset, 21 | sgn0, sgn1, 22 | r0, r1, 23 | j0, j1, 24 | n; 25 | 26 | if ( m.length !== len ) { 27 | throw new Error( 'invalid input argument. Number of indices does not match the number of elements in the value matrix.' ); 28 | } 29 | sgn0 = ( s0 < 0 ) ? -1 : 1; 30 | sgn1 = ( s2 < 0 ) ? -1 : 1; 31 | for ( n = 0; n < len; n++ ) { 32 | // Get the column number and row offset for the first matrix: 33 | j0 = idx[ n ] % s0; 34 | r0 = sgn0 * ( idx[n] - j0 ); 35 | 36 | // Get the column number and row offset for the value matrix: 37 | j1 = n % s2; 38 | r1 = sgn1 * ( n - j1 ); 39 | 40 | mat.data[ o0 + r0 + j0*s1 ] = m.data[ o1 + r1 + j1*s3 ]; 41 | } 42 | } // end FUNCTION mset3() 43 | 44 | 45 | // EXPORTS // 46 | 47 | module.exports = mset3; 48 | -------------------------------------------------------------------------------- /benchmark/b.set.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var matrix = require( './../lib' ).raw; 6 | 7 | 8 | // VARIABLES // 9 | 10 | var start, 11 | stop, 12 | nRows, 13 | nCols, 14 | res, 15 | len, 16 | m, 17 | i, j; 18 | 19 | 20 | // -------------------------------------- 21 | // WARM-UP 22 | 23 | len = 1e6; 24 | for ( i = 0; i < len; i++ ) { 25 | i = i; 26 | } 27 | 28 | 29 | // -------------------------------------- 30 | // BENCHMARK 31 | 32 | len = 1e6; 33 | nRows = 128; 34 | nCols = 128; 35 | 36 | res = new Array( 2 ); 37 | 38 | // Array of Arrays: 39 | m = new Array( nRows ); 40 | for ( i = 0; i < nRows; i++ ) { 41 | m[ i ] = new Array( nCols ); 42 | for ( j = 0; j < nCols; j++ ) { 43 | m[ i ][ j ] = 0; 44 | } 45 | } 46 | 47 | start = process.hrtime(); 48 | for ( i = 0; i < len; i++ ) { 49 | m[ 64 ][ 64 ] = i; 50 | } 51 | stop = process.hrtime( start ); 52 | 53 | res[ 0 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 54 | 55 | 56 | // Matrix: 57 | m = matrix( [nRows,nCols] ); 58 | 59 | start = process.hrtime(); 60 | for ( i = 0; i < len; i++ ) { 61 | m.set( 64, 64, i ); 62 | } 63 | stop = process.hrtime( start ); 64 | 65 | res[ 1 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 66 | 67 | 68 | // -------------------------------------- 69 | // RESULTS 70 | 71 | console.log( 'Arrays:\t%d ops/sec', Math.floor( len/res[ 0 ] ) ); 72 | console.log( 'Set:\t%d ops/sec', Math.floor( len/res[ 1 ] ) ); 73 | console.log( '\n' ); 74 | 75 | -------------------------------------------------------------------------------- /lib/sget.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var ispace = require( 'compute-indexspace' ); 6 | 7 | 8 | // VARIABLES // 9 | 10 | var BTYPES = require( './btypes.js' ); 11 | 12 | 13 | // SUBSEQUENCE GET // 14 | 15 | /** 16 | * FUNCTION: sget( subsequence ) 17 | * Returns matrix elements according to a specified subsequence. 18 | * 19 | * @param {String} subsequence - subsequence string 20 | * @returns {Matrix} Matrix instance 21 | */ 22 | function sget( seq ) { 23 | /*jshint validthis:true */ 24 | var nRows, 25 | nCols, 26 | rows, 27 | cols, 28 | seqs, 29 | mat, 30 | len, 31 | s0, s1, 32 | o, 33 | d, 34 | r, dr, 35 | i, j; 36 | 37 | seqs = seq.split( ',' ); 38 | rows = ispace( seqs[ 0 ], this.shape[ 0 ] ); 39 | cols = ispace( seqs[ 1 ], this.shape[ 1 ] ); 40 | 41 | nRows = rows.length; 42 | nCols = cols.length; 43 | len = nRows * nCols; 44 | 45 | d = new BTYPES[ this.dtype ]( len ); 46 | mat = new this.constructor( d, this.dtype, [nRows,nCols], 0, [nCols,1] ); 47 | 48 | if ( len ) { 49 | s0 = this.strides[ 0 ]; 50 | s1 = this.strides[ 1 ]; 51 | o = this.offset; 52 | for ( i = 0; i < nRows; i++ ) { 53 | r = o + rows[i]*s0; 54 | dr = i * nCols; 55 | for ( j = 0; j < nCols; j++ ) { 56 | d[ dr + j ] = this.data[ r + cols[j]*s1 ]; 57 | } 58 | } 59 | } 60 | return mat; 61 | } // end FUNCTION sget() 62 | 63 | 64 | // EXPORTS // 65 | 66 | module.exports = sget; 67 | -------------------------------------------------------------------------------- /benchmark/b.get.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var matrix = require( './../lib' ).raw; 6 | 7 | 8 | // VARIABLES // 9 | 10 | var start, 11 | stop, 12 | nRows, 13 | nCols, 14 | res, 15 | len, 16 | m, 17 | v, 18 | i, j; 19 | 20 | 21 | // -------------------------------------- 22 | // WARM-UP 23 | 24 | len = 1e6; 25 | for ( i = 0; i < len; i++ ) { 26 | i = i; 27 | } 28 | 29 | 30 | // -------------------------------------- 31 | // BENCHMARK 32 | 33 | len = 1e6; 34 | nRows = 128; 35 | nCols = 128; 36 | 37 | res = new Array( 2 ); 38 | 39 | // Array of Arrays: 40 | m = new Array( nRows ); 41 | for ( i = 0; i < nRows; i++ ) { 42 | m[ i ] = new Array( nCols ); 43 | for ( j = 0; j < nCols; j++ ) { 44 | m[ i ][ j ] = 0; 45 | } 46 | } 47 | 48 | start = process.hrtime(); 49 | for ( i = 0; i < len; i++ ) { 50 | v = m[ 64 ][ 64 ]; 51 | } 52 | stop = process.hrtime( start ); 53 | 54 | res[ 0 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 55 | 56 | 57 | // Matrix: 58 | m = matrix( [nRows,nCols] ); 59 | 60 | start = process.hrtime(); 61 | for ( i = 0; i < len; i++ ) { 62 | v = m.get( 64, 64 ); 63 | } 64 | stop = process.hrtime( start ); 65 | 66 | res[ 1 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 67 | 68 | 69 | // -------------------------------------- 70 | // RESULTS 71 | 72 | console.log( 'Arrays:\t%d ops/sec', Math.floor( len/res[ 0 ] ) ); 73 | console.log( 'Get:\t%d ops/sec', Math.floor( len/res[ 1 ] ) ); 74 | console.log( '\n' ); 75 | 76 | -------------------------------------------------------------------------------- /benchmark/b.sget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var matrix = require( './../lib' ).raw; 6 | 7 | 8 | // VARIABLES // 9 | 10 | var start, 11 | stop, 12 | nRows, 13 | nCols, 14 | res, 15 | len, 16 | m, n, 17 | i, j, k; 18 | 19 | 20 | // -------------------------------------- 21 | // WARM-UP 22 | 23 | len = 1e6; 24 | for ( i = 0; i < len; i++ ) { 25 | i = i; 26 | } 27 | 28 | 29 | // -------------------------------------- 30 | // BENCHMARK 31 | 32 | len = 1e6; 33 | nRows = 128; 34 | nCols = 128; 35 | 36 | res = new Array( 2 ); 37 | 38 | // Basic get: 39 | m = matrix( [nRows,nCols] ); 40 | 41 | nRows = 10; 42 | nCols = 10; 43 | n = matrix( [nRows,nCols] ); 44 | 45 | start = process.hrtime(); 46 | for ( i = 0; i < len; i++ ) { 47 | // NOTE: new allocation of memory is slow 48 | // n = matrix( [nRows,nCols] ); 49 | for ( j = 0; j < nRows; j++ ) { 50 | for ( k = 0; k < nCols; k++ ) { 51 | n.set( j, k, m.get( 64+j, 64+k ) ); 52 | } 53 | } 54 | } 55 | stop = process.hrtime( start ); 56 | 57 | res[ 0 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 58 | 59 | // Subsequence get: 60 | start = process.hrtime(); 61 | for ( i = 0; i < len; i++ ) { 62 | n = m.sget( '64:74,64:74' ); 63 | } 64 | stop = process.hrtime( start ); 65 | 66 | res[ 1 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 67 | 68 | 69 | // -------------------------------------- 70 | // RESULTS 71 | 72 | console.log( 'get:\t%d ops/sec', Math.floor( len/res[ 0 ] ) ); 73 | console.log( 'sget:\t%d ops/sec', Math.floor( len/res[ 1 ] ) ); 74 | console.log( '\n' ); 75 | 76 | -------------------------------------------------------------------------------- /benchmark/b.mget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var matrix = require( './../lib' ).raw; 6 | 7 | 8 | // VARIABLES // 9 | 10 | var start, 11 | stop, 12 | nRows, 13 | nCols, 14 | res, 15 | len, 16 | m, n, 17 | i, j, k; 18 | 19 | 20 | // -------------------------------------- 21 | // WARM-UP 22 | 23 | len = 1e6; 24 | for ( i = 0; i < len; i++ ) { 25 | i = i; 26 | } 27 | 28 | 29 | // -------------------------------------- 30 | // BENCHMARK 31 | 32 | len = 1e6; 33 | nRows = 128; 34 | nCols = 128; 35 | 36 | res = new Array( 2 ); 37 | 38 | // Basic get: 39 | m = matrix( [nRows,nCols] ); 40 | 41 | nRows = 10; 42 | nCols = 10; 43 | n = matrix( [nRows,nCols] ); 44 | 45 | start = process.hrtime(); 46 | for ( i = 0; i < len; i++ ) { 47 | // NOTE: new allocation of memory is slow 48 | // n = matrix( [nRows,nCols] ); 49 | for ( j = 0; j < nRows; j++ ) { 50 | for ( k = 0; k < nCols; k++ ) { 51 | n.set( j, k, m.get( 64+j, 64+k ) ); 52 | } 53 | } 54 | } 55 | stop = process.hrtime( start ); 56 | 57 | res[ 0 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 58 | 59 | // Multiple get: 60 | start = process.hrtime(); 61 | for ( i = 0; i < len; i++ ) { 62 | n = m.mget( [1,1,5,4,4,2,3,1,9,8], [2,0,0,4,5,6,3,8,4,4] ); 63 | } 64 | stop = process.hrtime( start ); 65 | 66 | res[ 1 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 67 | 68 | 69 | // -------------------------------------- 70 | // RESULTS 71 | 72 | console.log( 'get:\t%d ops/sec', Math.floor( len/res[ 0 ] ) ); 73 | console.log( 'mget:\t%d ops/sec', Math.floor( len/res[ 1 ] ) ); 74 | console.log( '\n' ); 75 | 76 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": false, 3 | "camelcase": false, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "es3": false, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": "nofunc", 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": false, 15 | "nonbsp": true, 16 | "nonew": true, 17 | "plusplus": false, 18 | "quotmark": "single", 19 | "undef": true, 20 | "unused": true, 21 | "strict": true, 22 | "maxparams": 10, 23 | "maxdepth": 5, 24 | "maxstatements": 100, 25 | "maxcomplexity": false, 26 | "maxlen": 1000, 27 | "asi": false, 28 | "boss": false, 29 | "debug": false, 30 | "eqnull": false, 31 | "esnext": false, 32 | "evil": false, 33 | "expr": false, 34 | "funcscope": false, 35 | "globalstrict": false, 36 | "iterator": false, 37 | "lastsemic": false, 38 | "laxbreak": false, 39 | "laxcomma": false, 40 | "loopfunc": false, 41 | "maxerr": 1000, 42 | "moz": false, 43 | "multistr": false, 44 | "notypeof": false, 45 | "proto": false, 46 | "scripturl": false, 47 | "shadow": false, 48 | "sub": true, 49 | "supernew": false, 50 | "validthis": false, 51 | "noyield": false, 52 | "browser": true, 53 | "browserify": true, 54 | "couch": false, 55 | "devel": true, 56 | "dojo": false, 57 | "jasmine": false, 58 | "jquery": false, 59 | "mocha": true, 60 | "mootools": false, 61 | "node": true, 62 | "nonstandard": false, 63 | "prototypejs": false, 64 | "qunit": false, 65 | "rhino": false, 66 | "shelljs": false, 67 | "worker": false, 68 | "wsh": false, 69 | "yui": false, 70 | "globals": {} 71 | } -------------------------------------------------------------------------------- /lib/ctor.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MATRIX // 4 | 5 | /** 6 | * FUNCTION: Matrix( data, dtype, shape, offset, strides ) 7 | * Matrix constructor. 8 | * 9 | * @constructor 10 | * @param {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} data - input typed array 11 | * @param {String} dtype - matrix data type 12 | * @param {Number[]} shape - matrix dimensions/shape 13 | * @param {Number} offset - matrix offset 14 | * @param {Number[]} strides - matrix strides 15 | * @returns {Matrix} Matrix instance 16 | */ 17 | function Matrix( data, dtype, shape, offset, strides ) { 18 | if ( !( this instanceof Matrix ) ) { 19 | return new Matrix( data, dtype, shape, offset, strides ); 20 | } 21 | this.dtype = dtype; 22 | this.shape = shape; 23 | this.strides = strides; 24 | this.offset = offset; 25 | this.ndims = shape.length; 26 | this.length = data.length; 27 | this.nbytes = data.byteLength; 28 | this.data = data; 29 | return this; 30 | } // end FUNCTION Matrix() 31 | 32 | 33 | // METHODS // 34 | 35 | Matrix.prototype.set = require( './set.raw.js' ); 36 | Matrix.prototype.iset = require( './iset.raw.js' ); 37 | Matrix.prototype.mset = require( './mset.raw.js' ); 38 | Matrix.prototype.sset = require( './sset.raw.js' ); 39 | 40 | Matrix.prototype.get = require( './get.raw.js' ); 41 | Matrix.prototype.iget = require( './iget.raw.js' ); 42 | Matrix.prototype.mget = require( './mget.raw.js' ); 43 | Matrix.prototype.sget = require( './sget.raw.js' ); 44 | 45 | Matrix.prototype.toString = require( './tostring.js' ); 46 | Matrix.prototype.toJSON = require( './tojson.js' ); 47 | 48 | // EXPORTS // 49 | 50 | module.exports = Matrix; 51 | -------------------------------------------------------------------------------- /lib/sget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isString = require( 'validate.io-string-primitive' ), 6 | ispace = require( 'compute-indexspace' ); 7 | 8 | 9 | // VARIABLES // 10 | 11 | var BTYPES = require( './btypes.js' ); 12 | 13 | 14 | // SUBSEQUENCE GET // 15 | 16 | /** 17 | * FUNCTION: sget( subsequence ) 18 | * Returns matrix elements according to a specified subsequence. 19 | * 20 | * @param {String} subsequence - subsequence string 21 | * @returns {Matrix} Matrix instance 22 | */ 23 | function sget( seq ) { 24 | /*jshint validthis:true */ 25 | var nRows, 26 | nCols, 27 | rows, 28 | cols, 29 | seqs, 30 | mat, 31 | len, 32 | s0, s1, 33 | o, 34 | d, 35 | r, dr, 36 | i, j; 37 | 38 | if ( !isString( seq ) ) { 39 | throw new TypeError( 'invalid input argument. Must provide a string primitive. Value: `' + seq + '`.' ); 40 | } 41 | seqs = seq.split( ',' ); 42 | if ( seqs.length !== 2 ) { 43 | throw new Error( 'invalid input argument. Subsequence string must specify row and column subsequences. Value: `' + seq + '`.' ); 44 | } 45 | rows = ispace( seqs[ 0 ], this.shape[ 0 ] ); 46 | cols = ispace( seqs[ 1 ], this.shape[ 1 ] ); 47 | 48 | nRows = rows.length; 49 | nCols = cols.length; 50 | len = nRows * nCols; 51 | 52 | d = new BTYPES[ this.dtype ]( len ); 53 | mat = new this.constructor( d, this.dtype, [nRows,nCols], 0, [nCols,1] ); 54 | 55 | if ( len ) { 56 | s0 = this.strides[ 0 ]; 57 | s1 = this.strides[ 1 ]; 58 | o = this.offset; 59 | for ( i = 0; i < nRows; i++ ) { 60 | r = o + rows[i]*s0; 61 | dr = i * nCols; 62 | for ( j = 0; j < nCols; j++ ) { 63 | d[ dr + j ] = this.data[ r + cols[j]*s1 ]; 64 | } 65 | } 66 | } 67 | return mat; 68 | } // end FUNCTION sget() 69 | 70 | 71 | // EXPORTS // 72 | 73 | module.exports = sget; 74 | -------------------------------------------------------------------------------- /test/test.get.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | get = require( './../lib/get.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#get', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int8Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10], 'int8' ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( get ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should return a Matrix element', function test() { 37 | var actual, expected; 38 | 39 | actual = mat.get( 5, 6 ); 40 | expected = 56; 41 | 42 | assert.strictEqual( actual, expected ); 43 | 44 | // Flip the matrix left-to-right: 45 | mat.strides[ 1 ] *= -1; 46 | mat.offset = mat.strides[ 0 ] - 1; 47 | 48 | actual = mat.get( 5, 6 ); 49 | expected = 53; 50 | 51 | assert.strictEqual( actual, expected, 'fliplr' ); 52 | 53 | // Flip the matrix top-to-bottom: 54 | mat.strides[ 0 ] *= -1; 55 | mat.offset = mat.length - 1; 56 | 57 | actual = mat.get( 5, 6 ); 58 | expected = 43; 59 | 60 | assert.strictEqual( actual, expected, 'fliplrud' ); 61 | 62 | // Flip the matrix left-to-right: 63 | mat.strides[ 1 ] *= -1; 64 | mat.offset = mat.length + mat.strides[ 0 ]; 65 | 66 | actual = mat.get( 5, 6 ); 67 | expected = 46; 68 | 69 | assert.strictEqual( actual, expected, 'flipud' ); 70 | }); 71 | 72 | it( 'should return undefined if provided an out-of-bounds index', function test() { 73 | assert.isUndefined( mat.get( 500, 100 ) ); 74 | }); 75 | 76 | }); 77 | -------------------------------------------------------------------------------- /test/test.toString.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | toString = require( './../lib/tostring.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#toString', function tests() { 20 | 21 | it( 'should export a function', function test() { 22 | expect( toString ).to.be.a( 'function' ); 23 | }); 24 | 25 | it( 'should convert a Matrix instance to a string', function test() { 26 | var mat, data, actual, expected; 27 | 28 | // Zero-filled matrix: 29 | mat = matrix( [4,4], 'int8' ); 30 | 31 | actual = mat.toString(); 32 | expected = '0,0,0,0;0,0,0,0;0,0,0,0;0,0,0,0'; 33 | 34 | assert.strictEqual( actual, expected ); 35 | 36 | // Full matrix: 37 | data = new Float32Array( 6 ); 38 | for ( var i = 0; i < data.length; i++ ) { 39 | data[ i ] = i * 2; 40 | } 41 | 42 | mat = matrix( data, [3,2], 'float32' ); 43 | 44 | actual = mat.toString(); 45 | expected = '0,2;4,6;8,10'; 46 | 47 | assert.strictEqual( actual, expected ); 48 | }); 49 | 50 | it( 'should convert a raw Matrix instance to a string', function test() { 51 | var mat, data, actual, expected; 52 | 53 | // Zero-filled matrix: 54 | mat = matrix.raw( [4,4], 'int8' ); 55 | 56 | actual = mat.toString(); 57 | expected = '0,0,0,0;0,0,0,0;0,0,0,0;0,0,0,0'; 58 | 59 | assert.strictEqual( actual, expected ); 60 | 61 | // Full matrix: 62 | data = new Float32Array( 6 ); 63 | for ( var i = 0; i < data.length; i++ ) { 64 | data[ i ] = i * 2; 65 | } 66 | 67 | mat = matrix.raw( data, [3,2], 'float32' ); 68 | 69 | actual = mat.toString(); 70 | expected = '0,2;4,6;8,10'; 71 | 72 | assert.strictEqual( actual, expected ); 73 | }); 74 | 75 | }); 76 | -------------------------------------------------------------------------------- /test/test.sget.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | sget = require( './../lib/sget.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#sget', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int8Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10] ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( sget ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should return values according to a specified subsequence', function test() { 37 | var mat1; 38 | 39 | mat1 = mat.sget( '1:3,2:4' ); 40 | 41 | assert.deepEqual( mat1.shape, [2,2] ); 42 | assert.strictEqual( mat1.toString(), '12,13;22,23' ); 43 | 44 | // Flip up-down: 45 | mat1 = mat.sget( '2:0:-1,2:4' ); 46 | 47 | assert.deepEqual( mat1.shape, [2,2] ); 48 | assert.strictEqual( mat1.toString(), '22,23;12,13' ); 49 | 50 | // Flip left-right: 51 | mat1 = mat.sget( '1:3,3:1:-1' ); 52 | 53 | assert.deepEqual( mat1.shape, [2,2] ); 54 | assert.strictEqual( mat1.toString(), '13,12;23,22' ); 55 | 56 | // Flip the matrix instance up-down and then flip left-right: 57 | mat.strides[ 0 ] *= -1; 58 | mat.offset = mat.length + mat.strides[0]; 59 | 60 | mat1 = mat.sget( '1:3,3:1:-1' ); 61 | 62 | assert.deepEqual( mat1.shape, [2,2] ); 63 | assert.strictEqual( mat1.toString(), '83,82;73,72' ); 64 | }); 65 | 66 | it( 'should return an empty matrix if a subsequence does not have any corresponding matrix elements', function test() { 67 | var mat1 = mat.sget( '999:,998:' ); 68 | 69 | assert.strictEqual( mat1.length, 0 ); 70 | }); 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Files # 3 | ######### 4 | 5 | 6 | # Directories # 7 | ############### 8 | reports/ 9 | build/ 10 | 11 | # Compiled source # 12 | ################### 13 | *.com 14 | *.class 15 | *.dll 16 | *.exe 17 | *.o 18 | *.so 19 | *.slo 20 | *.lo 21 | *.obj 22 | *.dylib 23 | *.dll 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | *.ko 29 | *.elf 30 | 31 | # Precompiled headers # 32 | ####################### 33 | *.gch 34 | *.pch 35 | 36 | # Executables # 37 | ############### 38 | *.exe 39 | *.out 40 | *.app 41 | 42 | # Packages # 43 | ############ 44 | # it's better to unpack these files and commit the raw source 45 | # git has its own built in compression methods 46 | *.7z 47 | *.dmg 48 | *.gz 49 | *.iso 50 | *.jar 51 | *.rar 52 | *.tar 53 | *.zip 54 | 55 | # Logs and databases # 56 | ###################### 57 | *.log 58 | *.sql 59 | *.sqlite 60 | 61 | # OS generated files # 62 | ###################### 63 | .DS_Store 64 | .DS_Store? 65 | ._* 66 | .Spotlight-V100 67 | .Trashes 68 | Icon? 69 | ehthumbs.db 70 | Thumbs.db 71 | Desktop.ini 72 | 73 | # Temporary files # 74 | ################### 75 | *~ 76 | 77 | # Node.js # 78 | ########### 79 | /node_modules/ 80 | pids 81 | *.pid 82 | *.seed 83 | 84 | # Matlab # 85 | ########## 86 | *.asv 87 | *.mex* 88 | 89 | # Fortran # 90 | ########### 91 | *.mod 92 | 93 | # R # 94 | ##### 95 | .Rhistory 96 | .Rapp.history 97 | .Rproj.user/ 98 | 99 | # TeX # 100 | ####### 101 | *.aux 102 | *.lof 103 | *.log 104 | *.lot 105 | *.fls 106 | *.out 107 | *.toc 108 | *.dvi 109 | *-converted-to.* 110 | *.bbl 111 | *.bcf 112 | *.blg 113 | *-blx.aux 114 | *-blx.bib 115 | *.brf 116 | *.run.xml 117 | *.fdb_latexmk 118 | *.synctex 119 | *.synctex.gz 120 | *.synctex.gz(busy) 121 | *.pdfsync 122 | *.alg 123 | *.loa 124 | acs-*.bib 125 | *.thm 126 | *.nav 127 | *.snm 128 | *.vrb 129 | *.acn 130 | *.acr 131 | *.glg 132 | *.glo 133 | *.gls 134 | *.brf 135 | *-concordance.tex 136 | *.tikz 137 | *-tikzDictionary 138 | *.idx 139 | *.ilg 140 | *.ind 141 | *.ist 142 | -------------------------------------------------------------------------------- /lib/mget.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // VARIABLES // 4 | 5 | var BTYPES = require( './btypes.js' ); 6 | 7 | 8 | // MGET // 9 | 10 | /** 11 | * FUNCTION: mget( i[, j] ) 12 | * Returns multiple matrix elements. If provided a single argument, `i` is treated as an array of linear indices. 13 | * 14 | * @param {Number[]|Null} i - linear/row indices 15 | * @param {Number[]|Null} [j] - column indices 16 | * @returns {Matrix} a new Matrix instance 17 | */ 18 | function mget( rows, cols ) { 19 | /*jshint validthis:true */ 20 | var nRows, 21 | nCols, 22 | out, 23 | sgn, 24 | d, 25 | s0, s1, s2, s3, 26 | o, 27 | r, dr, 28 | i, j, m, n; 29 | 30 | s0 = this.strides[ 0 ]; 31 | s1 = this.strides[ 1 ]; 32 | o = this.offset; 33 | 34 | if ( arguments.length < 2 ) { 35 | i = rows; 36 | m = i.length; 37 | 38 | // Create a row vector (matrix): 39 | d = new BTYPES[ this.dtype ]( m ); 40 | out = new this.constructor( d, this.dtype, [1,m], 0, [m,1] ); 41 | 42 | sgn = ( s0 < 0 ) ? -1 : 1; 43 | for ( n = 0; n < m; n++ ) { 44 | j = i[ n ] % s0; 45 | r = sgn * ( i[n] - j ); 46 | d[ n ] = this.data[ o + r + j*s1 ]; 47 | } 48 | } else { 49 | if ( rows === null ) { 50 | nRows = this.shape[ 0 ]; 51 | i = new Array( nRows ); 52 | for ( n = 0; n < nRows; n++ ) { 53 | i[ n ] = n; 54 | } 55 | } else { 56 | nRows = rows.length; 57 | i = rows; 58 | } 59 | 60 | if ( cols === null ) { 61 | nCols = this.shape[ 1 ]; 62 | j = new Array( nCols ); 63 | for ( n = 0; n < nCols; n++ ) { 64 | j[ n ] = n; 65 | } 66 | } else { 67 | nCols = cols.length; 68 | j = cols; 69 | } 70 | 71 | d = new BTYPES[ this.dtype ]( nRows*nCols ); 72 | out = new this.constructor( d, this.dtype, [nRows,nCols], 0, [nCols,1] ); 73 | 74 | s2 = out.strides[ 0 ]; 75 | s3 = out.strides[ 1 ]; 76 | for ( m = 0; m < nRows; m++ ) { 77 | r = o + i[m]*s0; 78 | dr = m * s2; 79 | for ( n = 0; n < nCols; n++ ) { 80 | d[ dr + n*s3 ] = this.data[ r + j[n]*s1 ]; 81 | } 82 | } 83 | } 84 | return out; 85 | } // end FUNCTION mget() 86 | 87 | 88 | // EXPORTS // 89 | 90 | module.exports = mget; 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dstructs-matrix", 3 | "version": "2.1.2", 4 | "description": "Matrices.", 5 | "author": { 6 | "name": "Athan Reines", 7 | "email": "kgryte@gmail.com" 8 | }, 9 | "contributors": [ 10 | { 11 | "name": "Athan Reines", 12 | "email": "kgryte@gmail.com" 13 | }, 14 | { 15 | "name": "Philipp Burckhardt", 16 | "email": "pburckhardt@outlook.com" 17 | } 18 | ], 19 | "scripts": { 20 | "test": "mocha", 21 | "test-cov": "istanbul cover ./node_modules/.bin/_mocha --dir ./reports/coverage -- -R spec", 22 | "codecov": "istanbul cover ./node_modules/.bin/_mocha --dir ./reports/codecov/coverage --report lcovonly -- -R spec && cat ./reports/codecov/coverage/lcov.info | codecov && rm -rf ./reports/codecov" 23 | }, 24 | "main": "./lib", 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/dstructs/matrix.git" 28 | }, 29 | "keywords": [ 30 | "compute.io", 31 | "compute", 32 | "computation", 33 | "mathematics", 34 | "math", 35 | "linear", 36 | "algebra", 37 | "two dimensional", 38 | "2d", 39 | "matrix", 40 | "matrices", 41 | "data", 42 | "structure", 43 | "data structure", 44 | "array", 45 | "typed", 46 | "dstructs" 47 | ], 48 | "bugs": { 49 | "url": "https://github.com/dstructs/matrix/issues" 50 | }, 51 | "dependencies": { 52 | "compute-dtype": "^1.0.0", 53 | "compute-indexspace": "^1.0.1", 54 | "dstructs-cast-arrays": "^1.0.2", 55 | "utils-copy": "^1.0.0", 56 | "validate.io-array": "^1.0.6", 57 | "validate.io-contains": "^1.0.0", 58 | "validate.io-function": "^1.0.2", 59 | "validate.io-integer-primitive": "^1.0.0", 60 | "validate.io-nan": "^1.0.3", 61 | "validate.io-nonnegative-integer": "^1.0.0", 62 | "validate.io-nonnegative-integer-array": "^1.0.1", 63 | "validate.io-number-primitive": "^1.0.0", 64 | "validate.io-string-primitive": "^1.0.0" 65 | }, 66 | "devDependencies": { 67 | "chai": "3.x.x", 68 | "codecov": "^3.6.5", 69 | "istanbul": "^0.3.0", 70 | "jshint": "2.x.x", 71 | "jshint-stylish": "2.x.x", 72 | "mocha": "2.x.x", 73 | "type-name": "^1.0.1", 74 | "validate.io-typed-array": "^1.0.0" 75 | }, 76 | "license": "MIT" 77 | } 78 | -------------------------------------------------------------------------------- /lib/matrix.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isString = require( 'validate.io-string-primitive' ), 6 | contains = require( 'validate.io-contains' ), 7 | getType = require( 'compute-dtype' ), 8 | Matrix = require( './ctor.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var BTYPES = require( './btypes.js' ), 14 | DTYPES = require( './dtypes.js' ); 15 | 16 | 17 | // CREATE MATRIX // 18 | 19 | /** 20 | * FUNCTION: matrix( [data,] shape[, dtype] ) 21 | * Returns a Matrix instance. 22 | * 23 | * @constructor 24 | * @param {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} [data] - input typed array 25 | * @param {Number[]} shape - matrix dimensions/shape 26 | * @param {String} [dtype="float64"] - matrix data type 27 | * @returns {Matrix} Matrix instance 28 | */ 29 | function matrix() { 30 | var dtype, 31 | ndims, 32 | shape, 33 | data, 34 | len, 35 | i; 36 | 37 | if ( arguments.length === 1 ) { 38 | shape = arguments[ 0 ]; 39 | } 40 | else if ( arguments.length === 2 ) { 41 | if ( isString( arguments[ 1 ] ) ) { 42 | shape = arguments[ 0 ]; 43 | dtype = arguments[ 1 ]; 44 | } else { 45 | data = arguments[ 0 ]; 46 | shape = arguments[ 1 ]; 47 | } 48 | } 49 | else { 50 | data = arguments[ 0 ]; 51 | shape = arguments[ 1 ]; 52 | dtype = arguments[ 2 ]; 53 | } 54 | ndims = shape.length; 55 | if ( ndims !== 2 ) { 56 | throw new Error( 'invalid input argument. Shape must be a 2-element array. Value: `' + shape + '`.' ); 57 | } 58 | len = 1; 59 | for ( i = 0; i < ndims; i++ ) { 60 | len *= shape[ i ]; 61 | } 62 | if ( data ) { 63 | if ( !dtype ) { 64 | dtype = getType( data ); 65 | if ( !contains( DTYPES, dtype ) ) { 66 | throw new TypeError( 'invalid input argument. Input data must be a valid type. Consult the documentation for a list of valid data types. Value: `' + data + '`.' ); 67 | } 68 | } 69 | if ( len !== data.length ) { 70 | throw new Error( 'invalid input argument. Matrix shape does not match the input data length.' ); 71 | } 72 | } else { 73 | // Initialize a zero-filled typed array... 74 | if ( !dtype ) { 75 | dtype = 'float64'; 76 | } 77 | data = new BTYPES[ dtype ]( len ); 78 | } 79 | // Return a new Matrix instance: 80 | return new Matrix( data, dtype, shape, 0, [shape[1],1] ); 81 | } // end FUNCTION matrix() 82 | 83 | 84 | // EXPORTS // 85 | 86 | module.exports = matrix; 87 | -------------------------------------------------------------------------------- /test/test.set.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | set = require( './../lib/set.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#set', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int32Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10], 'int32' ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( set ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should set a Matrix element', function test() { 37 | var prev, actual, expected; 38 | 39 | prev = mat.get( 5, 6 ); 40 | mat.set( 5, 6, 1000 ); 41 | 42 | actual = mat.get( 5, 6 ); 43 | expected = 1000; 44 | 45 | assert.notEqual( actual, prev ); 46 | assert.strictEqual( actual, expected ); 47 | 48 | // Flip the matrix left-to-right: 49 | mat.strides[ 1 ] *= -1; 50 | mat.offset = mat.strides[ 0 ] - 1; 51 | 52 | prev = mat.get( 5, 6 ); 53 | mat.set( 5, 6, 1001 ); 54 | 55 | actual = mat.get( 5, 6 ); 56 | expected = 1001; 57 | 58 | assert.notEqual( actual, prev ); 59 | assert.strictEqual( actual, expected, 'fliplr' ); 60 | 61 | // Flip the matrix top-to-bottom: 62 | mat.strides[ 0 ] *= -1; 63 | mat.offset = mat.length - 1; 64 | 65 | prev = mat.get( 5, 6 ); 66 | mat.set( 5, 6, 1002 ); 67 | 68 | actual = mat.get( 5, 6 ); 69 | expected = 1002; 70 | 71 | assert.notEqual( actual, prev ); 72 | assert.strictEqual( actual, expected, 'fliplrud' ); 73 | 74 | // Flip the matrix left-to-right: 75 | mat.strides[ 1 ] *= -1; 76 | mat.offset = mat.length + mat.strides[ 0 ]; 77 | 78 | prev = mat.get( 5, 6 ); 79 | mat.set( 5, 6, 1003 ); 80 | 81 | actual = mat.get( 5, 6 ); 82 | expected = 1003; 83 | 84 | assert.notEqual( actual, prev ); 85 | assert.strictEqual( actual, expected, 'flipud' ); 86 | }); 87 | 88 | it( 'should return the Matrix instance', function test() { 89 | assert.strictEqual( mat.set( 5, 6, 999 ), mat ); 90 | }); 91 | 92 | it( 'should silently fail if provided an out-of-bounds index', function test() { 93 | mat.set( 500, 100, 1000 ); 94 | assert.isUndefined( mat.get( 500, 100 ) ); 95 | 96 | mat.strides[ 0 ] *= -1; 97 | mat.set( 500, 100, 1000 ); 98 | assert.isUndefined( mat.get( 500, 100 ) ); 99 | }); 100 | 101 | }); 102 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ############# 3 | # VARIABLES # 4 | 5 | # Set the node.js environment to test: 6 | NODE_ENV ?= test 7 | 8 | # Kernel name: 9 | KERNEL ?= $(shell uname -s) 10 | 11 | ifeq ($(KERNEL), Darwin) 12 | OPEN ?= open 13 | else 14 | OPEN ?= xdg-open 15 | endif 16 | 17 | 18 | # NOTES # 19 | 20 | NOTES ?= 'TODO|FIXME|WARNING|HACK|NOTE' 21 | 22 | 23 | # MOCHA # 24 | 25 | MOCHA ?= ./node_modules/.bin/mocha 26 | _MOCHA ?= ./node_modules/.bin/_mocha 27 | MOCHA_REPORTER ?= spec 28 | 29 | 30 | # ISTANBUL # 31 | 32 | ISTANBUL ?= ./node_modules/.bin/istanbul 33 | ISTANBUL_OUT ?= ./reports/coverage 34 | ISTANBUL_REPORT ?= lcov 35 | ISTANBUL_LCOV_INFO_PATH ?= $(ISTANBUL_OUT)/lcov.info 36 | ISTANBUL_HTML_REPORT_PATH ?= $(ISTANBUL_OUT)/lcov-report/index.html 37 | 38 | 39 | # JSHINT # 40 | 41 | JSHINT ?= ./node_modules/.bin/jshint 42 | JSHINT_REPORTER ?= ./node_modules/jshint-stylish 43 | 44 | 45 | 46 | # FILES # 47 | 48 | # Source files: 49 | SOURCES ?= lib/*.js 50 | 51 | # Test files: 52 | TESTS ?= test/*.js 53 | 54 | 55 | 56 | 57 | ########### 58 | # TARGETS # 59 | 60 | 61 | # NOTES # 62 | 63 | .PHONY: notes 64 | 65 | notes: 66 | grep -Ern $(NOTES) $(SOURCES) $(TESTS) 67 | 68 | 69 | 70 | # UNIT TESTS # 71 | 72 | .PHONY: test test-mocha 73 | 74 | test: test-mocha 75 | 76 | test-mocha: node_modules 77 | NODE_ENV=$(NODE_ENV) \ 78 | NODE_PATH=$(NODE_PATH_TEST) \ 79 | $(MOCHA) \ 80 | --reporter $(MOCHA_REPORTER) \ 81 | $(TESTS) 82 | 83 | 84 | 85 | # CODE COVERAGE # 86 | 87 | .PHONY: test-cov test-istanbul-mocha 88 | 89 | test-cov: test-istanbul-mocha 90 | 91 | test-istanbul-mocha: node_modules 92 | NODE_ENV=$(NODE_ENV) \ 93 | NODE_PATH=$(NODE_PATH_TEST) \ 94 | $(ISTANBUL) cover \ 95 | --dir $(ISTANBUL_OUT) \ 96 | --report $(ISTANBUL_REPORT) \ 97 | $(_MOCHA) -- \ 98 | --reporter $(MOCHA_REPORTER) \ 99 | $(TESTS) 100 | 101 | 102 | 103 | # COVERAGE REPORT # 104 | 105 | .PHONY: view-cov view-istanbul-report 106 | 107 | view-cov: view-istanbul-report 108 | 109 | view-istanbul-report: 110 | $(OPEN) $(ISTANBUL_HTML_REPORT_PATH) 111 | 112 | 113 | # LINT # 114 | 115 | .PHONY: lint lint-jshint 116 | 117 | lint: lint-jshint 118 | 119 | lint-jshint: node_modules 120 | $(JSHINT) \ 121 | --reporter $(JSHINT_REPORTER) \ 122 | ./ 123 | 124 | 125 | # NODE # 126 | 127 | # Install node_modules: 128 | .PHONY: install 129 | 130 | install: 131 | npm install 132 | 133 | # Clean node: 134 | .PHONY: clean-node 135 | 136 | clean-node: 137 | rm -rf node_modules 138 | 139 | 140 | 141 | # CLEAN # 142 | .PHONY: clean 143 | 144 | clean: 145 | rm -rf build 146 | -------------------------------------------------------------------------------- /test/test.iget.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | iget = require( './../lib/iget.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#iget', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int32Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i * 2; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10], 'int32' ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( iget ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should return a Matrix element', function test() { 37 | var actual, expected; 38 | 39 | actual = mat.iget( 56 ); 40 | expected = 112; 41 | 42 | assert.strictEqual( actual, expected ); 43 | 44 | // Flip the matrix left-to-right: 45 | mat.strides[ 1 ] *= -1; 46 | mat.offset = mat.strides[ 0 ] - 1; 47 | 48 | actual = mat.iget( 56 ); 49 | expected = 106; 50 | 51 | assert.strictEqual( actual, expected, 'fliplr' ); 52 | 53 | // Flip the matrix top-to-bottom: 54 | mat.strides[ 0 ] *= -1; 55 | mat.offset = mat.length - 1; 56 | 57 | actual = mat.iget( 56 ); 58 | expected = 86; 59 | 60 | assert.strictEqual( actual, expected, 'fliplrud' ); 61 | 62 | // Flip the matrix left-to-right: 63 | mat.strides[ 1 ] *= -1; 64 | mat.offset = mat.length + mat.strides[ 0 ]; 65 | 66 | actual = mat.iget( 56 ); 67 | expected = 92; 68 | 69 | assert.strictEqual( actual, expected, 'flipud' ); 70 | }); 71 | 72 | it( 'should accept negative indices', function test() { 73 | var actual, expected; 74 | 75 | actual = mat.iget( -2 ); 76 | expected = 196; 77 | 78 | assert.strictEqual( actual, expected ); 79 | 80 | // Flip the matrix left-to-right: 81 | mat.strides[ 1 ] *= -1; 82 | mat.offset = mat.strides[ 0 ] - 1; 83 | 84 | actual = mat.iget( -2 ); 85 | expected = 182; 86 | 87 | assert.strictEqual( actual, expected, 'fliplr' ); 88 | 89 | // Flip the matrix top-to-bottom: 90 | mat.strides[ 0 ] *= -1; 91 | mat.offset = mat.length - 1; 92 | 93 | actual = mat.iget( -2 ); 94 | expected = 2; 95 | 96 | assert.strictEqual( actual, expected, 'fliplrud' ); 97 | 98 | // Flip the matrix left-to-right: 99 | mat.strides[ 1 ] *= -1; 100 | mat.offset = mat.length + mat.strides[ 0 ]; 101 | 102 | actual = mat.iget( -2 ); 103 | expected = 16; 104 | 105 | assert.strictEqual( actual, expected, 'flipud' ); 106 | }); 107 | 108 | it( 'should return undefined if provided an out-of-bounds index', function test() { 109 | assert.isUndefined( mat.iget( 1e5 ) ); 110 | assert.isUndefined( mat.iget( -1e5 ) ); 111 | }); 112 | 113 | }); 114 | -------------------------------------------------------------------------------- /test/test.sget.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | sget = require( './../lib/sget.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#sget', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int8Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i; 26 | } 27 | mat = matrix( data, [10,10] ); 28 | 29 | it( 'should export a function', function test() { 30 | expect( sget ).to.be.a( 'function' ); 31 | }); 32 | 33 | it( 'should throw an error if not provided a string', function test() { 34 | var values = [ 35 | 5, 36 | NaN, 37 | null, 38 | true, 39 | undefined, 40 | [], 41 | {}, 42 | function(){} 43 | ]; 44 | 45 | for ( var i = 0; i < values.length; i++ ) { 46 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 47 | } 48 | function badValue( value ) { 49 | return function() { 50 | mat.sget( value ); 51 | }; 52 | } 53 | }); 54 | 55 | it( 'should throw an error if provided a string which does not have row and column subsequences', function test() { 56 | var values = [ 57 | '5:', 58 | '5:;', 59 | '5:;::1', 60 | 5, 61 | NaN, 62 | null, 63 | true, 64 | undefined, 65 | [], 66 | {}, 67 | function(){} 68 | ]; 69 | 70 | for ( var i = 0; i < values.length; i++ ) { 71 | expect( badValue( values[ i ] ) ).to.throw( Error ); 72 | } 73 | function badValue( value ) { 74 | return function() { 75 | mat.sget( value ); 76 | }; 77 | } 78 | }); 79 | 80 | it( 'should return values according to a specified subsequence', function test() { 81 | var mat1; 82 | 83 | mat1 = mat.sget( '1:3,2:4' ); 84 | 85 | assert.deepEqual( mat1.shape, [2,2] ); 86 | assert.strictEqual( mat1.toString(), '12,13;22,23' ); 87 | 88 | // Flip up-down: 89 | mat1 = mat.sget( '2:0:-1,2:4' ); 90 | 91 | assert.deepEqual( mat1.shape, [2,2] ); 92 | assert.strictEqual( mat1.toString(), '22,23;12,13' ); 93 | 94 | // Flip left-right: 95 | mat1 = mat.sget( '1:3,3:1:-1' ); 96 | 97 | assert.deepEqual( mat1.shape, [2,2] ); 98 | assert.strictEqual( mat1.toString(), '13,12;23,22' ); 99 | 100 | // Flip the matrix instance up-down and then flip left-right: 101 | mat.strides[ 0 ] *= -1; 102 | mat.offset = mat.length + mat.strides[0]; 103 | 104 | mat1 = mat.sget( '1:3,3:1:-1' ); 105 | 106 | assert.deepEqual( mat1.shape, [2,2] ); 107 | assert.strictEqual( mat1.toString(), '83,82;73,72' ); 108 | }); 109 | 110 | it( 'should return an empty matrix if a subsequence does not have any corresponding matrix elements', function test() { 111 | var mat1 = mat.sget( '999:,998:' ); 112 | 113 | assert.strictEqual( mat1.length, 0 ); 114 | }); 115 | 116 | }); 117 | -------------------------------------------------------------------------------- /lib/sset.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var ispace = require( 'compute-indexspace' ); 6 | 7 | 8 | // SUBSEQUENCE SET // 9 | 10 | /** 11 | * FUNCTION: sset( subsequence, value[, thisArg] ) 12 | * Sets matrix elements according to a specified subsequence. 13 | * 14 | * @param {String} subsequence - subsequence string 15 | * @param {Number|Matrix|Function} value - either a single numeric value, a matrix containing the values to set, or a function which returns a numeric value 16 | * @param {Object} [thisArg] - `this` context when executing a callback 17 | * @returns {Matrix} Matrix instance 18 | */ 19 | function sset( seq, val, thisArg ) { 20 | /* jshint validthis: true */ 21 | var nRows, 22 | nCols, 23 | clbk, 24 | rows, 25 | cols, 26 | seqs, 27 | mat, 28 | ctx, 29 | s0, s1, s2, s3, 30 | o0, o1, 31 | r0, r1, 32 | i, j, k; 33 | 34 | seqs = seq.split( ',' ); 35 | if ( typeof val === 'function' ) { 36 | clbk = val; 37 | } 38 | else if ( typeof val !== 'number' ) { 39 | mat = val; 40 | } 41 | rows = ispace( seqs[ 0 ], this.shape[ 0 ] ); 42 | cols = ispace( seqs[ 1 ], this.shape[ 1 ] ); 43 | 44 | nRows = rows.length; 45 | nCols = cols.length; 46 | 47 | if ( !( nRows && nCols ) ) { 48 | return this; 49 | } 50 | s0 = this.strides[ 0 ]; 51 | s1 = this.strides[ 1 ]; 52 | o0 = this.offset; 53 | 54 | // Callback... 55 | if ( clbk ) { 56 | if ( arguments.length > 2 ) { 57 | ctx = thisArg; 58 | } else { 59 | ctx = this; 60 | } 61 | for ( i = 0; i < nRows; i++ ) { 62 | r0 = o0 + rows[i]*s0; 63 | for ( j = 0; j < nCols; j++ ) { 64 | k = r0 + cols[j]*s1; 65 | this.data[ k ] = clbk.call( ctx, this.data[ k ], rows[i], cols[j], k ); 66 | } 67 | } 68 | } 69 | // Input matrix... 70 | else if ( mat ) { 71 | if ( nRows !== mat.shape[ 0 ] ) { 72 | throw new Error( 'invalid input arguments. Row subsequence does not match input matrix dimensions. Expected a [' + nRows + ',' + nCols + '] matrix and instead received a [' + mat.shape.join( ',' ) + '] matrix.' ); 73 | } 74 | if ( nCols !== mat.shape[ 1 ] ) { 75 | throw new Error( 'invalid input arguments. Column subsequence does not match input matrix dimensions. Expected a [' + nRows + ',' + nCols + '] matrix and instead received a [' + mat.shape.join( ',' ) + '] matrix.' ); 76 | } 77 | s2 = mat.strides[ 0 ]; 78 | s3 = mat.strides[ 1 ]; 79 | o1 = mat.offset; 80 | for ( i = 0; i < nRows; i++ ) { 81 | r0 = o0 + rows[i]*s0; 82 | r1 = o1 + i*s2; 83 | for ( j = 0; j < nCols; j++ ) { 84 | this.data[ r0 + cols[j]*s1 ] = mat.data[ r1 + j*s3 ]; 85 | } 86 | } 87 | } 88 | // Single numeric value... 89 | else { 90 | for ( i = 0; i < nRows; i++ ) { 91 | r0 = o0 + rows[i]*s0; 92 | for ( j = 0; j < nCols; j++ ) { 93 | this.data[ r0 + cols[j]*s1 ] = val; 94 | } 95 | } 96 | } 97 | return this; 98 | } // end FUNCTION sset() 99 | 100 | 101 | // EXPORTS // 102 | 103 | module.exports = sset; 104 | -------------------------------------------------------------------------------- /test/test.get.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | get = require( './../lib/get.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#get', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int8Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10], 'int8' ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( get ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should throw an error if provided a row index which is not a nonnegative integer', function test() { 37 | var values = [ 38 | '5', 39 | -1, 40 | Math.PI, 41 | NaN, 42 | null, 43 | true, 44 | undefined, 45 | [], 46 | {}, 47 | function(){} 48 | ]; 49 | 50 | for ( var i = 0; i < values.length; i++ ) { 51 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 52 | } 53 | function badValue( value ) { 54 | return function() { 55 | mat.get( value, 0 ); 56 | }; 57 | } 58 | }); 59 | 60 | it( 'should throw an error if provided a column index which is not a nonnegative integer', function test() { 61 | var values = [ 62 | '5', 63 | -1, 64 | Math.PI, 65 | NaN, 66 | null, 67 | true, 68 | undefined, 69 | [], 70 | {}, 71 | function(){} 72 | ]; 73 | 74 | for ( var i = 0; i < values.length; i++ ) { 75 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 76 | } 77 | function badValue( value ) { 78 | return function() { 79 | mat.get( 0, value ); 80 | }; 81 | } 82 | }); 83 | 84 | it( 'should return a Matrix element', function test() { 85 | var actual, expected; 86 | 87 | actual = mat.get( 5, 6 ); 88 | expected = 56; 89 | 90 | assert.strictEqual( actual, expected ); 91 | 92 | // Flip the matrix left-to-right: 93 | mat.strides[ 1 ] *= -1; 94 | mat.offset = mat.strides[ 0 ] - 1; 95 | 96 | actual = mat.get( 5, 6 ); 97 | expected = 53; 98 | 99 | assert.strictEqual( actual, expected, 'fliplr' ); 100 | 101 | // Flip the matrix top-to-bottom: 102 | mat.strides[ 0 ] *= -1; 103 | mat.offset = mat.length - 1; 104 | 105 | actual = mat.get( 5, 6 ); 106 | expected = 43; 107 | 108 | assert.strictEqual( actual, expected, 'fliplrud' ); 109 | 110 | // Flip the matrix left-to-right: 111 | mat.strides[ 1 ] *= -1; 112 | mat.offset = mat.length + mat.strides[ 0 ]; 113 | 114 | actual = mat.get( 5, 6 ); 115 | expected = 46; 116 | 117 | assert.strictEqual( actual, expected, 'flipud' ); 118 | }); 119 | 120 | it( 'should return undefined if provided an out-of-bounds index', function test() { 121 | assert.isUndefined( mat.get( 500, 100 ) ); 122 | }); 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /lib/ctor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MATRIX // 4 | 5 | /** 6 | * FUNCTION: Matrix( data, dtype, shape, offset, strides ) 7 | * Matrix constructor. 8 | * 9 | * @constructor 10 | * @param {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} data - input typed array 11 | * @param {String} dtype - matrix data type 12 | * @param {Number[]} shape - matrix dimensions/shape 13 | * @param {Number} offset - matrix offset 14 | * @param {Number[]} strides - matrix strides 15 | * @returns {Matrix} Matrix instance 16 | */ 17 | function Matrix( data, dtype, shape, offset, strides ) { 18 | if ( !( this instanceof Matrix ) ) { 19 | return new Matrix( data, dtype, shape, offset, strides ); 20 | } 21 | // Underlying data type: 22 | Object.defineProperty( this, 'dtype', { 23 | 'value': dtype, 24 | 'configurable': false, 25 | 'enumerable': true, 26 | 'writable': false 27 | }); 28 | 29 | // Matrix dimensions: 30 | Object.defineProperty( this, 'shape', { 31 | 'value': shape, 32 | 'configurable': false, 33 | 'enumerable': true, 34 | 'writable': false 35 | }); 36 | 37 | // Matrix strides: 38 | Object.defineProperty( this, 'strides', { 39 | 'value': strides, 40 | 'configurable': false, 41 | 'enumerable': true, 42 | 'writable': false 43 | }); 44 | 45 | // Matrix offset: 46 | Object.defineProperty( this, 'offset', { 47 | 'value': offset, 48 | 'configurable': false, 49 | 'enumerable': true, 50 | 'writable': true 51 | }); 52 | 53 | // Number of matrix dimensions: 54 | Object.defineProperty( this, 'ndims', { 55 | 'value': shape.length, 56 | 'configurable': false, 57 | 'enumerable': true, 58 | 'writable': false 59 | }); 60 | 61 | // Matrix length: 62 | Object.defineProperty( this, 'length', { 63 | 'value': data.length, 64 | 'configurable': false, 65 | 'enumerable': true, 66 | 'writable': false 67 | }); 68 | 69 | // Number of bytes used by the matrix elements: 70 | Object.defineProperty( this, 'nbytes', { 71 | 'value': data.byteLength, 72 | 'configurable': false, 73 | 'enumerable': true, 74 | 'writable': false 75 | }); 76 | 77 | // Matrix data store: 78 | Object.defineProperty( this, 'data', { 79 | 'value': data, 80 | 'configurable': false, 81 | 'enumerable': true, 82 | 'writable': false 83 | }); 84 | 85 | return this; 86 | } // end FUNCTION Matrix() 87 | 88 | 89 | // METHODS // 90 | 91 | Matrix.prototype.set = require( './set.js' ); 92 | Matrix.prototype.iset = require( './iset.js' ); 93 | Matrix.prototype.mset = require( './mset.js' ); 94 | Matrix.prototype.sset = require( './sset.js' ); 95 | 96 | Matrix.prototype.get = require( './get.js' ); 97 | Matrix.prototype.iget = require( './iget.js' ); 98 | Matrix.prototype.mget = require( './mget.js' ); 99 | Matrix.prototype.sget = require( './sget.js' ); 100 | 101 | Matrix.prototype.toString = require( './tostring.js' ); 102 | Matrix.prototype.toJSON = require( './tojson.js' ); 103 | 104 | 105 | // EXPORTS // 106 | 107 | module.exports = Matrix; 108 | -------------------------------------------------------------------------------- /lib/mset.raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // FUNCTIONS // 4 | 5 | var mset1 = require( './mset1.js' ), 6 | mset2 = require( './mset2.js' ), 7 | mset3 = require( './mset3.js' ), 8 | mset4 = require( './mset4.js' ), 9 | mset5 = require( './mset5.js' ), 10 | mset6 = require( './mset6.js' ); 11 | 12 | /** 13 | * FUNCTION: getIndices( idx, len ) 14 | * Returns an array of indices. 15 | * 16 | * @private 17 | * @param {Number[]|Null} idx - indices 18 | * @param {Number} len - max index 19 | * @returns {Number[]} indices 20 | */ 21 | function getIndices( idx, len ) { 22 | var out, 23 | i; 24 | if ( idx === null ) { 25 | out = new Array( len ); 26 | for ( i = 0; i < len; i++ ) { 27 | out[ i ] = i; 28 | } 29 | } else { 30 | out = idx; 31 | } 32 | return out; 33 | } // end FUNCTION getIndices() 34 | 35 | 36 | // MSET // 37 | 38 | /** 39 | * FUNCTION: mset( i[, j], value[, thisArg] ) 40 | * Sets multiple matrix elements. If provided a single array, `i` is treated as an array of linear indices. 41 | * 42 | * @param {Number[]|Null} i - linear/row indices 43 | * @param {Number[]|Null} [j] - column indices 44 | * @param {Number|Matrix|Function} value - either a single numeric value, a matrix containing the values to set, or a function which returns a numeric value 45 | * @returns {Matrix} Matrix instance 46 | */ 47 | function mset() { 48 | /*jshint validthis:true */ 49 | var nargs = arguments.length, 50 | args, 51 | rows, 52 | cols, 53 | i; 54 | 55 | args = new Array( nargs ); 56 | for ( i = 0; i < nargs; i++ ) { 57 | args[ i ] = arguments[ i ]; 58 | } 59 | 60 | // 2 input arguments... 61 | if ( nargs < 3 ) { 62 | // indices, clbk 63 | if ( typeof args[ 1 ] === 'function' ) { 64 | mset2( this, args[ 0 ], args[ 1 ] ); 65 | } 66 | // indices, number 67 | else if ( typeof args[ 1 ] === 'number' ) { 68 | mset1( this, args[ 0 ], args[ 1 ] ); 69 | } 70 | // indices, matrix 71 | else { 72 | mset3( this, args[ 0 ], args[ 1 ] ); 73 | } 74 | } 75 | // 3 input arguments... 76 | else if ( nargs === 3 ) { 77 | // indices, clbk, context 78 | if ( typeof args[ 1 ] === 'function' ) { 79 | mset2( this, args[ 0 ], args[ 1 ], args[ 2 ] ); 80 | } 81 | // rows, cols, function 82 | else if ( typeof args[ 2 ] === 'function' ) { 83 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 84 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 85 | mset4( this, rows, cols, args[ 2 ], this ); 86 | } 87 | // rows, cols, number 88 | else if ( typeof args[ 2 ] === 'number' ) { 89 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 90 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 91 | mset5( this, rows, cols, args[ 2 ] ); 92 | } 93 | // rows, cols, matrix 94 | else { 95 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 96 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 97 | mset6( this, rows, cols, args[ 2 ] ); 98 | } 99 | } 100 | // 4 input arguments... 101 | else { 102 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 103 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 104 | mset4( this, rows, cols, args[ 2 ], args[ 3 ] ); 105 | } 106 | return this; 107 | } // end FUNCTION mset() 108 | 109 | 110 | // EXPORTS // 111 | 112 | module.exports = mset; 113 | -------------------------------------------------------------------------------- /test/test.tojson.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | toJSON = require( './../lib/tojson.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#toJSON', function tests() { 20 | 21 | it( 'should export a function', function test() { 22 | expect( toJSON ).to.be.a( 'function' ); 23 | }); 24 | 25 | it( 'should return a Matrix instance in JSON representation', function test() { 26 | var mat, data, actual, expected; 27 | 28 | // Zero-filled matrix: 29 | mat = matrix( [4,4], 'int8' ); 30 | 31 | actual = mat.toJSON(); 32 | expected = { 33 | 'type': 'Matrix', 34 | 'dtype': 'int8', 35 | 'shape': [4,4], 36 | 'offset': 0, 37 | 'strides': [4,1], 38 | 'raw': false, 39 | 'data': [ 40 | 0,0,0,0, 41 | 0,0,0,0, 42 | 0,0,0,0, 43 | 0,0,0,0 44 | ] 45 | }; 46 | 47 | assert.deepEqual( actual, expected ); 48 | 49 | // Full matrix: 50 | data = new Float32Array( 6 ); 51 | for ( var i = 0; i < data.length; i++ ) { 52 | data[ i ] = i * 2; 53 | } 54 | 55 | mat = matrix( data, [3,2] ); 56 | 57 | actual = mat.toJSON(); 58 | expected = { 59 | 'type': 'Matrix', 60 | 'dtype': 'float32', 61 | 'shape': [3,2], 62 | 'offset': 0, 63 | 'strides': [2,1], 64 | 'raw': false, 65 | 'data': [ 66 | 0,2, 67 | 4,6, 68 | 8,10 69 | ] 70 | }; 71 | 72 | assert.deepEqual( actual, expected ); 73 | }); 74 | 75 | it( 'should return a raw Matrix instance in JSON presentation', function test() { 76 | var mat, data, actual, expected; 77 | 78 | // Zero-filled matrix: 79 | mat = matrix.raw( [4,4], 'int8' ); 80 | 81 | actual = mat.toJSON(); 82 | expected = { 83 | 'type': 'Matrix', 84 | 'dtype': 'int8', 85 | 'shape': [4,4], 86 | 'offset': 0, 87 | 'strides': [4,1], 88 | 'raw': true, 89 | 'data': [ 90 | 0,0,0,0, 91 | 0,0,0,0, 92 | 0,0,0,0, 93 | 0,0,0,0 94 | ] 95 | }; 96 | 97 | assert.deepEqual( actual, expected ); 98 | 99 | // Full matrix: 100 | data = new Float32Array( 6 ); 101 | for ( var i = 0; i < data.length; i++ ) { 102 | data[ i ] = i * 2; 103 | } 104 | 105 | mat = matrix.raw( data, [3,2] ); 106 | 107 | actual = mat.toJSON(); 108 | expected = { 109 | 'type': 'Matrix', 110 | 'dtype': 'float32', 111 | 'shape': [3,2], 112 | 'offset': 0, 113 | 'strides': [2,1], 114 | 'raw': true, 115 | 'data': [ 116 | 0,2, 117 | 4,6, 118 | 8,10 119 | ] 120 | }; 121 | 122 | assert.deepEqual( actual, expected ); 123 | }); 124 | 125 | it( 'should deep copy properties to prevent accidental mutation', function test() { 126 | var mat, json; 127 | 128 | mat = matrix( [4,4], 'int8' ); 129 | json = mat.toJSON(); 130 | 131 | assert.deepEqual( mat.dtype, json.dtype ); 132 | assert.deepEqual( mat.shape, json. shape ); 133 | assert.deepEqual( mat.offset, json.offset ); 134 | assert.deepEqual( mat.strides, json.strides ); 135 | 136 | assert.isTrue( mat.shape !== json.shape ); 137 | assert.isTrue( mat.strides !== json.strides ); 138 | }); 139 | 140 | }); 141 | -------------------------------------------------------------------------------- /lib/mget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isNonNegativeIntegerArray = require( 'validate.io-nonnegative-integer-array' ); 6 | 7 | 8 | // VARIABLES // 9 | 10 | var BTYPES = require( './btypes.js' ); 11 | 12 | 13 | // MGET // 14 | 15 | /** 16 | * FUNCTION: mget( i[, j] ) 17 | * Returns multiple matrix elements. If provided a single argument, `i` is treated as an array of linear indices. 18 | * 19 | * @param {Number[]|Null} i - linear/row indices 20 | * @param {Number[]|Null} [j] - column indices 21 | * @returns {Matrix} a new Matrix instance 22 | */ 23 | function mget( rows, cols ) { 24 | /*jshint validthis:true */ 25 | var nRows, 26 | nCols, 27 | out, 28 | sgn, 29 | d, 30 | s0, s1, s2, s3, 31 | o, 32 | r, dr, 33 | i, j, m, n; 34 | 35 | s0 = this.strides[ 0 ]; 36 | s1 = this.strides[ 1 ]; 37 | o = this.offset; 38 | 39 | if ( arguments.length < 2 ) { 40 | if ( !isNonNegativeIntegerArray( rows ) ) { 41 | throw new TypeError( 'invalid input argument. Linear indices must be specified as a nonnegative integer array. Value: `' + rows + '`.' ); 42 | } 43 | // Filter the input indices to ensure within bounds... 44 | i = []; 45 | for ( n = 0; n < rows.length; n++ ) { 46 | if ( rows[ n ] < this.length ) { 47 | i.push( rows[ n ] ); 48 | } 49 | } 50 | m = i.length; 51 | 52 | // Create a row vector (matrix): 53 | d = new BTYPES[ this.dtype ]( m ); 54 | out = new this.constructor( d, this.dtype, [1,m], 0, [m,1] ); 55 | 56 | sgn = ( s0 < 0 ) ? -1 : 1; 57 | for ( n = 0; n < m; n++ ) { 58 | j = i[ n ] % s0; 59 | r = sgn * ( i[n] - j ); 60 | d[ n ] = this.data[ o + r + j*s1 ]; 61 | } 62 | } else { 63 | nRows = this.shape[ 0 ]; 64 | if ( rows === null ) { 65 | i = new Array( nRows ); 66 | for ( n = 0; n < nRows; n++ ) { 67 | i[ n ] = n; 68 | } 69 | } 70 | else if ( isNonNegativeIntegerArray( rows ) ) { 71 | i = []; 72 | for ( n = 0; n < rows.length; n++ ) { 73 | if ( rows[ n ] < nRows ) { 74 | i.push( rows[ n ] ); 75 | } 76 | } 77 | } 78 | else { 79 | throw new TypeError( 'invalid input argument. Row indices must be specified as a nonnegative integer array. Value: `' + rows + '`.' ); 80 | } 81 | 82 | nCols = this.shape[ 1 ]; 83 | if ( cols === null ) { 84 | j = new Array( nCols ); 85 | for ( n = 0; n < nCols; n++ ) { 86 | j[ n ] = n; 87 | } 88 | } 89 | else if ( isNonNegativeIntegerArray( cols ) ) { 90 | j = []; 91 | for ( n = 0; n < cols.length; n++ ) { 92 | if ( cols[ n ] < nCols ) { 93 | j.push( cols[ n ] ); 94 | } 95 | } 96 | } 97 | else { 98 | throw new TypeError( 'invalid input argument. Column indices must be specified as a nonnegative integer array. Value: `' + cols + '`.' ); 99 | } 100 | nRows = i.length; 101 | nCols = j.length; 102 | 103 | d = new BTYPES[ this.dtype ]( nRows*nCols ); 104 | out = new this.constructor( d, this.dtype, [nRows,nCols], 0, [nCols,1]); 105 | 106 | s2 = out.strides[ 0 ]; 107 | s3 = out.strides[ 1 ]; 108 | for ( m = 0; m < nRows; m++ ) { 109 | r = o + i[m]*s0; 110 | dr = m * s2; 111 | for ( n = 0; n < nCols; n++ ) { 112 | d[ dr + n*s3 ] = this.data[ r + j[n]*s1 ]; 113 | } 114 | } 115 | } 116 | return out; 117 | } // end FUNCTION mget() 118 | 119 | 120 | // EXPORTS // 121 | 122 | module.exports = mget; 123 | -------------------------------------------------------------------------------- /test/test.iget.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | iget = require( './../lib/iget.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#iget', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int32Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i * 2; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10], 'int32' ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( iget ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should throw an error if not provided an integer', function test() { 37 | var values = [ 38 | '5', 39 | Math.PI, 40 | NaN, 41 | null, 42 | true, 43 | undefined, 44 | [], 45 | {}, 46 | function(){} 47 | ]; 48 | 49 | for ( var i = 0; i < values.length; i++ ) { 50 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 51 | } 52 | function badValue( value ) { 53 | return function() { 54 | mat.iget( value ); 55 | }; 56 | } 57 | }); 58 | 59 | it( 'should return a Matrix element', function test() { 60 | var actual, expected; 61 | 62 | actual = mat.iget( 56 ); 63 | expected = 112; 64 | 65 | assert.strictEqual( actual, expected ); 66 | 67 | // Flip the matrix left-to-right: 68 | mat.strides[ 1 ] *= -1; 69 | mat.offset = mat.strides[ 0 ] - 1; 70 | 71 | actual = mat.iget( 56 ); 72 | expected = 106; 73 | 74 | assert.strictEqual( actual, expected, 'fliplr' ); 75 | 76 | // Flip the matrix top-to-bottom: 77 | mat.strides[ 0 ] *= -1; 78 | mat.offset = mat.length - 1; 79 | 80 | actual = mat.iget( 56 ); 81 | expected = 86; 82 | 83 | assert.strictEqual( actual, expected, 'fliplrud' ); 84 | 85 | // Flip the matrix left-to-right: 86 | mat.strides[ 1 ] *= -1; 87 | mat.offset = mat.length + mat.strides[ 0 ]; 88 | 89 | actual = mat.iget( 56 ); 90 | expected = 92; 91 | 92 | assert.strictEqual( actual, expected, 'flipud' ); 93 | }); 94 | 95 | it( 'should accept negative indices', function test() { 96 | var actual, expected; 97 | 98 | actual = mat.iget( -2 ); 99 | expected = 196; 100 | 101 | assert.strictEqual( actual, expected ); 102 | 103 | // Flip the matrix left-to-right: 104 | mat.strides[ 1 ] *= -1; 105 | mat.offset = mat.strides[ 0 ] - 1; 106 | 107 | actual = mat.iget( -2 ); 108 | expected = 182; 109 | 110 | assert.strictEqual( actual, expected, 'fliplr' ); 111 | 112 | // Flip the matrix top-to-bottom: 113 | mat.strides[ 0 ] *= -1; 114 | mat.offset = mat.length - 1; 115 | 116 | actual = mat.iget( -2 ); 117 | expected = 2; 118 | 119 | assert.strictEqual( actual, expected, 'fliplrud' ); 120 | 121 | // Flip the matrix left-to-right: 122 | mat.strides[ 1 ] *= -1; 123 | mat.offset = mat.length + mat.strides[ 0 ]; 124 | 125 | actual = mat.iget( -2 ); 126 | expected = 16; 127 | 128 | assert.strictEqual( actual, expected, 'flipud' ); 129 | }); 130 | 131 | it( 'should return undefined if provided an out-of-bounds index', function test() { 132 | assert.isUndefined( mat.iget( 1e5 ) ); 133 | assert.isUndefined( mat.iget( -1e5 ) ); 134 | }); 135 | 136 | }); 137 | -------------------------------------------------------------------------------- /lib/sset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isString = require( 'validate.io-string-primitive' ), 6 | isNumber = require( 'validate.io-number-primitive' ), 7 | isFunction = require( 'validate.io-function' ), 8 | ispace = require( 'compute-indexspace' ); 9 | 10 | 11 | // SUBSEQUENCE SET // 12 | 13 | /** 14 | * FUNCTION: sset( subsequence, value[, thisArg] ) 15 | * Sets matrix elements according to a specified subsequence. 16 | * 17 | * @param {String} subsequence - subsequence string 18 | * @param {Number|Matrix|Function} value - either a single numeric value, a matrix containing the values to set, or a function which returns a numeric value 19 | * @param {Object} [thisArg] - `this` context when executing a callback 20 | * @returns {Matrix} Matrix instance 21 | */ 22 | function sset( seq, val, thisArg ) { 23 | /* jshint validthis: true */ 24 | var nRows, 25 | nCols, 26 | clbk, 27 | rows, 28 | cols, 29 | seqs, 30 | mat, 31 | ctx, 32 | s0, s1, s2, s3, 33 | o0, o1, 34 | r0, r1, 35 | i, j, k; 36 | 37 | if ( !isString( seq ) ) { 38 | throw new TypeError( 'invalid input argument. Must provide a string primitive. Value: `' + seq + '`.' ); 39 | } 40 | seqs = seq.split( ',' ); 41 | if ( seqs.length !== 2 ) { 42 | throw new Error( 'invalid input argument. Subsequence string must specify row and column subsequences. Value: `' + seq + '`.' ); 43 | } 44 | if ( isFunction( val ) ) { 45 | clbk = val; 46 | } 47 | else if ( !isNumber( val ) ) { 48 | mat = val; 49 | } 50 | rows = ispace( seqs[ 0 ], this.shape[ 0 ] ); 51 | cols = ispace( seqs[ 1 ], this.shape[ 1 ] ); 52 | 53 | nRows = rows.length; 54 | nCols = cols.length; 55 | 56 | if ( !( nRows && nCols ) ) { 57 | return this; 58 | } 59 | s0 = this.strides[ 0 ]; 60 | s1 = this.strides[ 1 ]; 61 | o0 = this.offset; 62 | 63 | // Callback... 64 | if ( clbk ) { 65 | if ( arguments.length > 2 ) { 66 | ctx = thisArg; 67 | } else { 68 | ctx = this; 69 | } 70 | for ( i = 0; i < nRows; i++ ) { 71 | r0 = o0 + rows[i]*s0; 72 | for ( j = 0; j < nCols; j++ ) { 73 | k = r0 + cols[j]*s1; 74 | this.data[ k ] = clbk.call( ctx, this.data[ k ], rows[i], cols[j], k ); 75 | } 76 | } 77 | } 78 | // Input matrix... 79 | else if ( mat ) { 80 | if ( nRows !== mat.shape[ 0 ] ) { 81 | throw new Error( 'invalid input arguments. Row subsequence does not match input matrix dimensions. Expected a [' + nRows + ',' + nCols + '] matrix and instead received a [' + mat.shape.join( ',' ) + '] matrix.' ); 82 | } 83 | if ( nCols !== mat.shape[ 1 ] ) { 84 | throw new Error( 'invalid input arguments. Column subsequence does not match input matrix dimensions. Expected a [' + nRows + ',' + nCols + '] matrix and instead received a [' + mat.shape.join( ',' ) + '] matrix.' ); 85 | } 86 | s2 = mat.strides[ 0 ]; 87 | s3 = mat.strides[ 1 ]; 88 | o1 = mat.offset; 89 | for ( i = 0; i < nRows; i++ ) { 90 | r0 = o0 + rows[i]*s0; 91 | r1 = o1 + i*s2; 92 | for ( j = 0; j < nCols; j++ ) { 93 | this.data[ r0 + cols[j]*s1 ] = mat.data[ r1 + j*s3 ]; 94 | } 95 | } 96 | } 97 | // Single numeric value... 98 | else { 99 | for ( i = 0; i < nRows; i++ ) { 100 | r0 = o0 + rows[i]*s0; 101 | for ( j = 0; j < nCols; j++ ) { 102 | this.data[ r0 + cols[j]*s1 ] = val; 103 | } 104 | } 105 | } 106 | return this; 107 | } // end FUNCTION sset() 108 | 109 | 110 | // EXPORTS // 111 | 112 | module.exports = sset; 113 | -------------------------------------------------------------------------------- /test/test.ctor.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | isTypedArray = require( 'validate.io-typed-array' ), 8 | ctor = require( './../lib/ctor.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'Matrix.raw', function tests() { 20 | 21 | var mat = ctor( new Float32Array( 10 ), 'float32', [5,2], 0, [2,1] ); 22 | 23 | it( 'should export a function', function test() { 24 | expect( ctor ).to.be.a( 'function' ); 25 | }); 26 | 27 | it( 'should have an arity of 5', function test() { 28 | assert.strictEqual( ctor.length, 5 ); 29 | }); 30 | 31 | it( 'should create a new Matrix instance', function test() { 32 | assert.isTrue( mat instanceof ctor ); 33 | }); 34 | 35 | it( 'should not require a `new` operator', function test() { 36 | var A, B; 37 | 38 | A = ctor( mat.data, mat.shape, mat.dtype, mat.offset, mat.strides ); 39 | B = new ctor( mat.data, mat.shape, mat.dtype, mat.offset, mat.strides ); 40 | 41 | assert.isTrue( A instanceof ctor ); 42 | 43 | assert.isTrue( B instanceof ctor ); 44 | }); 45 | 46 | it( 'should create a Matrix having setters', function test() { 47 | assert.isFunction( mat.set ); 48 | assert.isFunction( mat.iset ); 49 | assert.isFunction( mat.mset ); 50 | assert.isFunction( mat.sset ); 51 | }); 52 | 53 | it( 'should create a Matrix having getters', function test() { 54 | assert.isFunction( mat.get ); 55 | assert.isFunction( mat.iget ); 56 | assert.isFunction( mat.mget ); 57 | assert.isFunction( mat.sget ); 58 | }); 59 | 60 | it( 'should create a Matrix having a custom toString method', function test() { 61 | assert.isFunction( mat.toString ); 62 | }); 63 | 64 | it( 'should create a Matrix having a custom toJSON method', function test() { 65 | assert.isFunction( mat.toJSON ); 66 | }); 67 | 68 | it( 'should create a Matrix having a dtype property', function test() { 69 | assert.isTrue( mat.hasOwnProperty( 'dtype' ) ); 70 | assert.isString( mat.dtype ); 71 | }); 72 | 73 | it( 'should create a Matrix having a shape property', function test() { 74 | assert.isTrue( mat.hasOwnProperty( 'shape' ) ); 75 | assert.isArray( mat.shape ); 76 | }); 77 | 78 | it( 'should create a Matrix having a strides property', function test() { 79 | assert.isTrue( mat.hasOwnProperty( 'strides' ) ); 80 | assert.isArray( mat.strides ); 81 | assert.deepEqual( mat.strides, [2,1] ); 82 | }); 83 | 84 | it( 'should create a Matrix having an offset property', function test() { 85 | assert.isTrue( mat.hasOwnProperty( 'offset' ) ); 86 | assert.isNumber( mat.offset ); 87 | assert.deepEqual( mat.offset, 0 ); 88 | }); 89 | 90 | it( 'should create a Matrix having a ndims property', function test() { 91 | assert.isTrue( mat.hasOwnProperty( 'ndims' ) ); 92 | assert.isNumber( mat.ndims ); 93 | assert.strictEqual( mat.ndims, 2 ); 94 | }); 95 | 96 | it( 'should create a Matrix having a length property', function test() { 97 | assert.isTrue( mat.hasOwnProperty( 'length' ) ); 98 | assert.isNumber( mat.length ); 99 | assert.strictEqual( mat.length, 10 ); 100 | }); 101 | 102 | it( 'should create a Matrix having a nbytes property', function test() { 103 | assert.isTrue( mat.hasOwnProperty( 'nbytes' ) ); 104 | assert.isNumber( mat.nbytes ); 105 | }); 106 | 107 | it( 'should create a Matrix having a data property', function test() { 108 | assert.isTrue( mat.hasOwnProperty( 'data' ) ); 109 | assert.isTrue( isTypedArray( mat.data ) ); 110 | }); 111 | 112 | }); 113 | -------------------------------------------------------------------------------- /lib/matrix.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isString = require( 'validate.io-string-primitive' ), 6 | isNonNegativeIntegerArray = require( 'validate.io-nonnegative-integer-array' ), 7 | contains = require( 'validate.io-contains' ), 8 | isArray = require( 'validate.io-array' ), 9 | cast = require( 'dstructs-cast-arrays' ), 10 | getType = require( 'compute-dtype' ), 11 | Matrix = require( './ctor.js' ); 12 | 13 | 14 | // VARIABLES // 15 | 16 | var BTYPES = require( './btypes.js' ), 17 | DTYPES = require( './dtypes.js' ); 18 | 19 | 20 | // CREATE MATRIX // 21 | 22 | /** 23 | * FUNCTION: matrix( [data,] shape[, dtype] ) 24 | * Returns a Matrix instance. 25 | * 26 | * @constructor 27 | * @param {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} [data] - input typed array 28 | * @param {Number[]} shape - matrix dimensions/shape 29 | * @param {String} [dtype="float64"] - matrix data type 30 | * @returns {Matrix} Matrix instance 31 | */ 32 | function matrix() { 33 | var dtype, 34 | ndims, 35 | shape, 36 | data, 37 | vFLG, 38 | len, 39 | dt, 40 | i; 41 | 42 | // Parse the input arguments (polymorphic interface)... 43 | if ( arguments.length === 1 ) { 44 | shape = arguments[ 0 ]; 45 | vFLG = 2; // arg #s 46 | } 47 | else if ( arguments.length === 2 ) { 48 | if ( isString( arguments[ 1 ] ) ) { 49 | shape = arguments[ 0 ]; 50 | dtype = arguments[ 1 ]; 51 | vFLG = 23; // arg #s 52 | } else { 53 | data = arguments[ 0 ]; 54 | shape = arguments[ 1 ]; 55 | vFLG = 12; // arg #s 56 | } 57 | } 58 | else { 59 | data = arguments[ 0 ]; 60 | shape = arguments[ 1 ]; 61 | dtype = arguments[ 2 ]; 62 | vFLG = 123; // arg #s 63 | } 64 | 65 | // Input argument validation... 66 | if ( !isNonNegativeIntegerArray( shape ) ) { 67 | throw new TypeError( 'invalid input argument. A matrix shape must be an array of nonnegative integers. Value: `' + shape + '`.' ); 68 | } 69 | ndims = shape.length; 70 | if ( ndims !== 2 ) { 71 | throw new Error( 'invalid input argument. Shape must be a 2-element array. Value: `' + shape + '`.' ); 72 | } 73 | // If a `dtype` has been provided, validate... 74 | if ( vFLG === 123 || vFLG === 23 ) { 75 | if ( !contains( DTYPES, dtype ) ) { 76 | throw new TypeError( 'invalid input argument. Unrecognized/unsupported data type. Value: `' + dtype + '`.' ); 77 | } 78 | } else { 79 | dtype = 'float64'; 80 | } 81 | len = 1; 82 | for ( i = 0; i < ndims; i++ ) { 83 | len *= shape[ i ]; 84 | } 85 | // If a `data` argument has been provided, validate... 86 | if ( vFLG === 123 || vFLG === 12 ) { 87 | dt = getType( data ); 88 | if ( !contains( DTYPES, dt ) && !isArray( data ) ) { 89 | throw new TypeError( 'invalid input argument. Input data must be a valid type. Consult the documentation for a list of valid data types. Value: `' + data + '`.' ); 90 | } 91 | if ( len !== data.length ) { 92 | throw new Error( 'invalid input argument. Matrix shape does not match the input data length.' ); 93 | } 94 | // Only cast if either 1) both a `data` and `dtype` argument have been provided and they do not agree or 2) when provided a plain Array... 95 | if ( ( vFLG === 123 && dt !== dtype ) || dt === 'generic' ) { 96 | data = cast( data, dtype ); 97 | } else { 98 | dtype = dt; 99 | } 100 | } else { 101 | // Initialize a zero-filled typed array: 102 | data = new BTYPES[ dtype ]( len ); 103 | } 104 | // Return a new Matrix instance: 105 | return new Matrix( data, dtype, shape, 0, [shape[1],1] ); 106 | } // end FUNCTION matrix() 107 | 108 | 109 | // EXPORTS // 110 | 111 | module.exports = matrix; 112 | -------------------------------------------------------------------------------- /test/test.matrix.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | typeName = require( 'type-name' ), 8 | matrix = require( './../lib/matrix.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw', function tests() { 20 | 21 | it( 'should export a function', function test() { 22 | expect( matrix ).to.be.a( 'function' ); 23 | }); 24 | 25 | it( 'should throw an error if provided a shape array which does not contain exactly 2 elements', function test() { 26 | expect( badValue1 ).to.throw( Error ); 27 | expect( badValue2 ).to.throw( Error ); 28 | expect( badValue3 ).to.throw( Error ); 29 | function badValue1() { 30 | matrix( [1,2,3] ); 31 | } 32 | function badValue2() { 33 | matrix( new Int8Array( 12 ), [1,2,6] ); 34 | } 35 | function badValue3() { 36 | matrix( new Int8Array( 10 ), [10], 'int8' ); 37 | } 38 | }); 39 | 40 | it( 'should throw an error if provided data which is an unrecognized/unsupported data type', function test() { 41 | var values = [ 42 | '5', 43 | 5, 44 | // NaN, 45 | // null, 46 | // undefined, 47 | true, 48 | [], 49 | [ 1 ], 50 | {}, 51 | function(){} 52 | ]; 53 | 54 | for ( var i = 0; i < values.length; i++ ) { 55 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 56 | } 57 | function badValue( value ) { 58 | return function() { 59 | matrix( value, [1,1] ); 60 | }; 61 | } 62 | }); 63 | 64 | it( 'should throw an error if the matrix shape does not match the input data length', function test() { 65 | expect( badValue2 ).to.throw( Error ); 66 | expect( badValue3 ).to.throw( Error ); 67 | function badValue2() { 68 | matrix( new Int8Array( 10 ), [2,2] ); 69 | } 70 | function badValue3() { 71 | matrix( new Int8Array( 10 ), [5,1], 'int8' ); 72 | } 73 | }); 74 | 75 | it( 'should return a new Matrix instance', function test() { 76 | var mat; 77 | 78 | mat = matrix( [1,1] ); 79 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 80 | 81 | mat = matrix( new Int8Array( 1 ), [1,1] ); 82 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 83 | 84 | mat = matrix( [1,1], 'int8' ); 85 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 86 | 87 | mat = matrix( new Int8Array( 1 ), [1,1], 'int8' ); 88 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 89 | }); 90 | 91 | it( 'should return a new Matrix instance of a specified type', function test() { 92 | var mat; 93 | 94 | mat = matrix( new Int8Array( 1 ), [1,1] ); 95 | assert.strictEqual( mat.dtype, 'int8' ); 96 | 97 | mat = matrix( new Int8Array( 1 ), [1,1], 'int8' ); 98 | assert.strictEqual( mat.dtype, 'int8' ); 99 | }); 100 | 101 | it( 'should initialize a zero-filled Matrix if not provided input data (default dtype float64)', function test() { 102 | var mat = matrix( [1,1] ); 103 | 104 | for ( var i = 0; i < mat.data.length; i++ ) { 105 | assert.strictEqual( mat.data[ i], 0 ); 106 | } 107 | 108 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 109 | assert.strictEqual( typeName( mat.data ), 'Float64Array' ); 110 | assert.strictEqual( mat.dtype, 'float64' ); 111 | }); 112 | 113 | it( 'should initialize a zero-filled Matrix having a specified data type if not provided input data', function test() { 114 | var mat = matrix( [1,1], 'uint32' ); 115 | 116 | for ( var i = 0; i < mat.data.length; i++ ) { 117 | assert.strictEqual( mat.data[ i], 0 ); 118 | } 119 | 120 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 121 | assert.strictEqual( typeName( mat.data ), 'Uint32Array' ); 122 | assert.strictEqual( mat.dtype, 'uint32' ); 123 | }); 124 | 125 | }); 126 | -------------------------------------------------------------------------------- /benchmark/b.create.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var matrix = require( './../lib' ), 6 | rawMatrix = matrix.raw; 7 | 8 | 9 | // VARIABLES // 10 | 11 | var start, 12 | stop, 13 | iArr, 14 | nRows, 15 | nCols, 16 | res, 17 | len, 18 | m, 19 | i, j, k; 20 | 21 | 22 | // -------------------------------------- 23 | // WARM-UP 24 | 25 | len = 1e6; 26 | for ( i = 0; i < len; i++ ) { 27 | i = i; 28 | } 29 | 30 | 31 | // -------------------------------------- 32 | // BENCHMARK 33 | 34 | nRows = 128; 35 | nCols = 128; 36 | iArr = new Float32Array( nRows*nCols ); 37 | len = 1e6; 38 | 39 | res = new Array( 8 ); 40 | 41 | // [0] Array of Arrays: 42 | start = process.hrtime(); 43 | for ( i = 0; i < len; i++ ) { 44 | m = new Array( nRows ); 45 | for ( j = 0; j < nRows; j++ ) { 46 | m[ j ] = new Array( nCols ); 47 | for ( k = 0; k < nCols; k++ ) { 48 | m[ j ][ k ] = 0; 49 | } 50 | } 51 | m.ndims = 2; 52 | m.dtype = 'float64'; 53 | m.shape = [ nRows, nCols ]; 54 | m.length = nRows * nCols; 55 | m.data = m; 56 | m.nbytes = m.length * 8; 57 | } 58 | stop = process.hrtime( start ); 59 | 60 | res[ 0 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 61 | 62 | 63 | // [1] Array of Arrays: 64 | start = process.hrtime(); 65 | for ( i = 0; i < len; i++ ) { 66 | m.ndims = 2; 67 | m.dtype = 'float64'; 68 | m.shape = [ nRows, nCols ]; 69 | m.length = nRows * nCols; 70 | m.data = m; 71 | m.nbytes = m.length * 8; 72 | } 73 | stop = process.hrtime( start ); 74 | 75 | res[ 1 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 76 | 77 | 78 | // [2] Base matrix creator (w/o input data)... 79 | start = process.hrtime(); 80 | for ( i = 0; i < len; i++ ) { 81 | m = matrix( [nRows,nCols], 'float32' ); 82 | } 83 | stop = process.hrtime( start ); 84 | 85 | res[ 2 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 86 | 87 | 88 | // [3] Base matrix creator... 89 | start = process.hrtime(); 90 | for ( i = 0; i < len; i++ ) { 91 | m = matrix( iArr, [nRows,nCols] ); 92 | } 93 | stop = process.hrtime( start ); 94 | 95 | res[ 3 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 96 | 97 | 98 | // [4] Base matrix constructor: 99 | start = process.hrtime(); 100 | for ( i = 0; i < len; i++ ) { 101 | m = new m.constructor( iArr, 'float32', [nRows,nCols], 0, [nCols,1] ); 102 | } 103 | stop = process.hrtime( start ); 104 | 105 | res[ 4 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 106 | 107 | 108 | // [5] Raw matrix creator (w/o input data)... 109 | start = process.hrtime(); 110 | for ( i = 0; i < len; i++ ) { 111 | m = rawMatrix( [nRows,nCols], 'float32' ); 112 | } 113 | stop = process.hrtime( start ); 114 | 115 | res[ 5 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 116 | 117 | 118 | // [6] Raw matrix creator... 119 | start = process.hrtime(); 120 | for ( i = 0; i < len; i++ ) { 121 | m = rawMatrix( iArr, [nRows,nCols], 'float32' ); 122 | } 123 | stop = process.hrtime( start ); 124 | 125 | res[ 6 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 126 | 127 | 128 | // [7] Raw matrix constructor: 129 | start = process.hrtime(); 130 | for ( i = 0; i < len; i++ ) { 131 | m = new m.constructor( iArr, 'float32', [nRows,nCols], 0, [nCols,1] ); 132 | } 133 | stop = process.hrtime( start ); 134 | 135 | res[ 7 ] = stop[ 0 ] + stop[ 1 ]*1e-9; 136 | 137 | 138 | // -------------------------------------- 139 | // RESULTS 140 | 141 | console.log( 'Arrays:\t\t%d ops/sec', Math.floor( len/res[ 0 ] ) ); 142 | console.log( 'Arrays (data):\t%d ops/sec', Math.floor( len/res[ 1 ] ) ); 143 | console.log( 'Create:\t\t%d ops/sec', Math.floor( len/res[ 2 ] ) ); 144 | console.log( 'Create (data):\t%d ops/sec', Math.floor( len/res[ 3 ] ) ); 145 | console.log( 'Create (ctor):\t%d ops/sec', Math.floor( len/res[ 4 ] ) ); 146 | console.log( 'Create (raw):\t%d ops/sec', Math.floor( len/res[ 5 ] ) ); 147 | console.log( 'Create (rdata):\t%d ops/sec', Math.floor( len/res[ 6 ] ) ); 148 | console.log( 'Create (rctor):\t%d ops/sec', Math.floor( len/res[ 7 ] ) ); 149 | console.log( '\n' ); 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /test/test.iset.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | iset = require( './../lib/iset.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#iset', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int32Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i * 2; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10], 'int32' ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( iset ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should set a Matrix element', function test() { 37 | var prev, actual, expected; 38 | 39 | prev = mat.iget( 56 ); 40 | mat.iset( 56, 365 ); 41 | 42 | actual = mat.iget( 56 ); 43 | expected = 365; 44 | 45 | assert.notEqual( actual, prev ); 46 | assert.strictEqual( actual, expected ); 47 | 48 | // Flip the matrix left-to-right: 49 | mat.strides[ 1 ] *= -1; 50 | mat.offset = mat.strides[ 0 ] - 1; 51 | 52 | prev = mat.iget( 56 ); 53 | mat.iset( 56, 499 ); 54 | 55 | actual = mat.iget( 56 ); 56 | expected = 499; 57 | 58 | assert.notEqual( actual, prev ); 59 | assert.strictEqual( actual, expected, 'fliplr' ); 60 | 61 | // Flip the matrix top-to-bottom: 62 | mat.strides[ 0 ] *= -1; 63 | mat.offset = mat.length - 1; 64 | 65 | prev = mat.iget( 56 ); 66 | mat.iset( 56, 1001 ); 67 | 68 | actual = mat.iget( 56 ); 69 | expected = 1001; 70 | 71 | assert.notEqual( actual, prev ); 72 | assert.strictEqual( actual, expected, 'fliplrud' ); 73 | 74 | // Flip the matrix left-to-right: 75 | mat.strides[ 1 ] *= -1; 76 | mat.offset = mat.length + mat.strides[ 0 ]; 77 | 78 | prev = mat.iget( 56 ); 79 | mat.iset( 56, 782 ); 80 | 81 | actual = mat.iget( 56 ); 82 | expected = 782; 83 | 84 | assert.notEqual( actual, prev ); 85 | assert.strictEqual( actual, expected, 'flipud' ); 86 | }); 87 | 88 | it( 'should accept negative indices', function test() { 89 | var prev, actual, expected; 90 | 91 | prev = mat.iget( -2 ); 92 | mat.iset( -2, 999 ); 93 | 94 | actual = mat.iget( -2 ); 95 | expected = 999; 96 | 97 | assert.notEqual( actual, prev ); 98 | assert.strictEqual( actual, expected ); 99 | 100 | // Flip the matrix left-to-right: 101 | mat.strides[ 1 ] *= -1; 102 | mat.offset = mat.strides[ 0 ] - 1; 103 | 104 | prev = mat.iget( -2 ); 105 | mat.iset( -2, 499 ); 106 | 107 | actual = mat.iget( -2 ); 108 | expected = 499; 109 | 110 | assert.notEqual( actual, prev ); 111 | assert.strictEqual( actual, expected, 'fliplr' ); 112 | 113 | // Flip the matrix top-to-bottom: 114 | mat.strides[ 0 ] *= -1; 115 | mat.offset = mat.length - 1; 116 | 117 | prev = mat.iget( -2 ); 118 | mat.iset( -2, 1001 ); 119 | 120 | actual = mat.iget( -2 ); 121 | expected = 1001; 122 | 123 | assert.notEqual( actual, prev ); 124 | assert.strictEqual( actual, expected, 'fliplrud' ); 125 | 126 | // Flip the matrix left-to-right: 127 | mat.strides[ 1 ] *= -1; 128 | mat.offset = mat.length + mat.strides[ 0 ]; 129 | 130 | prev = mat.iget( -2 ); 131 | mat.iset( -2, 782 ); 132 | 133 | actual = mat.iget( -2 ); 134 | expected = 782; 135 | 136 | assert.notEqual( actual, prev ); 137 | assert.strictEqual( actual, expected, 'flipud' ); 138 | }); 139 | 140 | it( 'should return the Matrix instance', function test() { 141 | assert.strictEqual( mat.iset( 5, 6, 999 ), mat ); 142 | }); 143 | 144 | it( 'should silently fail if provided an out-of-bounds index', function test() { 145 | mat.iset( 1e5, 987 ); 146 | assert.isUndefined( mat.iget( 1e5 ) ); 147 | 148 | mat.iset( -1e5, 789 ); 149 | assert.isUndefined( mat.iget( -1e5 ) ); 150 | }); 151 | 152 | }); 153 | -------------------------------------------------------------------------------- /test/test.ctor.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | isTypedArray = require( 'validate.io-typed-array' ), 8 | ctor = require( './../lib/ctor.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'Matrix', function tests() { 20 | 21 | var mat = ctor( new Float32Array( 10 ), 'float32', [5,2], 0, [2,1] ); 22 | 23 | it( 'should export a function', function test() { 24 | expect( ctor ).to.be.a( 'function' ); 25 | }); 26 | 27 | it( 'should have an arity of 5', function test() { 28 | assert.strictEqual( ctor.length, 5 ); 29 | }); 30 | 31 | it( 'should create a new Matrix instance', function test() { 32 | assert.isTrue( mat instanceof ctor ); 33 | }); 34 | 35 | it( 'should not require a `new` operator', function test() { 36 | var A, B; 37 | 38 | A = ctor( mat.data, mat.shape, mat.dtype, mat.offset, mat.strides ); 39 | B = new ctor( mat.data, mat.shape, mat.dtype, mat.offset, mat.strides ); 40 | 41 | assert.isTrue( A instanceof ctor ); 42 | 43 | assert.isTrue( B instanceof ctor ); 44 | }); 45 | 46 | it( 'should create a Matrix having setters', function test() { 47 | assert.isFunction( mat.set ); 48 | assert.isFunction( mat.iset ); 49 | assert.isFunction( mat.mset ); 50 | assert.isFunction( mat.sset ); 51 | }); 52 | 53 | it( 'should create a Matrix having getters', function test() { 54 | assert.isFunction( mat.get ); 55 | assert.isFunction( mat.iget ); 56 | assert.isFunction( mat.mget ); 57 | assert.isFunction( mat.sget ); 58 | }); 59 | 60 | it( 'should create a Matrix having a custom toString method', function test() { 61 | assert.isFunction( mat.toString ); 62 | }); 63 | 64 | it( 'should create a Matrix having a custom toJSON method', function test() { 65 | assert.isFunction( mat.toJSON ); 66 | }); 67 | 68 | it( 'should create a Matrix having a protected dtype property', function test() { 69 | assert.isTrue( mat.hasOwnProperty( 'dtype' ) ); 70 | assert.isString( mat.dtype ); 71 | 72 | expect( foo ).to.throw( Error ); 73 | function foo() { 74 | mat.dtype = 'beep'; 75 | } 76 | }); 77 | 78 | it( 'should create a Matrix having a protected shape property', function test() { 79 | assert.isTrue( mat.hasOwnProperty( 'shape' ) ); 80 | assert.isArray( mat.shape ); 81 | 82 | expect( foo ).to.throw( Error ); 83 | function foo() { 84 | mat.shape = [ 4, 5 ]; 85 | } 86 | }); 87 | 88 | it( 'should create a Matrix having a protected strides property', function test() { 89 | assert.isTrue( mat.hasOwnProperty( 'strides' ) ); 90 | assert.isArray( mat.strides ); 91 | assert.deepEqual( mat.strides, [2,1] ); 92 | 93 | expect( foo ).to.throw( Error ); 94 | function foo() { 95 | mat.strides = [ -4, 1 ]; 96 | } 97 | }); 98 | 99 | it( 'should create a Matrix having an offset property', function test() { 100 | assert.isTrue( mat.hasOwnProperty( 'offset' ) ); 101 | assert.isNumber( mat.offset ); 102 | assert.deepEqual( mat.offset, 0 ); 103 | }); 104 | 105 | it( 'should create a Matrix having a protected ndims property', function test() { 106 | assert.isTrue( mat.hasOwnProperty( 'ndims' ) ); 107 | assert.isNumber( mat.ndims ); 108 | assert.strictEqual( mat.ndims, 2 ); 109 | 110 | expect( foo ).to.throw( Error ); 111 | function foo() { 112 | mat.ndims = NaN; 113 | } 114 | }); 115 | 116 | it( 'should create a Matrix having a protected length property', function test() { 117 | assert.isTrue( mat.hasOwnProperty( 'length' ) ); 118 | assert.isNumber( mat.length ); 119 | assert.strictEqual( mat.length, 10 ); 120 | 121 | expect( foo ).to.throw( Error ); 122 | function foo() { 123 | mat.length = 1e10; 124 | } 125 | }); 126 | 127 | it( 'should create a Matrix having a protected nbytes property', function test() { 128 | assert.isTrue( mat.hasOwnProperty( 'nbytes' ) ); 129 | assert.isNumber( mat.nbytes ); 130 | 131 | expect( foo ).to.throw( Error ); 132 | function foo() { 133 | mat.nbytes = NaN; 134 | } 135 | }); 136 | 137 | it( 'should create a Matrix having a protected data property', function test() { 138 | assert.isTrue( mat.hasOwnProperty( 'data' ) ); 139 | assert.isTrue( isTypedArray( mat.data ) ); 140 | 141 | expect( foo ).to.throw( Error ); 142 | function foo() { 143 | mat.data = [1,2,3]; 144 | } 145 | }); 146 | 147 | }); 148 | -------------------------------------------------------------------------------- /test/test.mget.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | mget = require( './../lib/mget.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#mget', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int8Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10], 'int8' ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( mget ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should return values located at specified linear indices', function test() { 37 | var m, expected; 38 | 39 | m = mat.mget( [14,28,47] ); 40 | expected = '14,28,47'; 41 | 42 | assert.deepEqual( m.shape, [1,3] ); 43 | assert.strictEqual( m.toString(), expected ); 44 | 45 | // Flip the matrix left-to-right: 46 | mat.strides[ 1 ] *= -1; 47 | mat.offset = mat.strides[ 0 ] - 1; 48 | 49 | m = mat.mget( [14,28,47] ); 50 | expected = '15,21,42'; 51 | 52 | assert.strictEqual( m.toString(), expected, 'fliplr' ); 53 | 54 | // Flip the matrix top-to-bottom: 55 | mat.strides[ 0 ] *= -1; 56 | mat.offset = mat.length - 1; 57 | 58 | m = mat.mget( [14,28,47] ); 59 | expected = '85,71,52'; 60 | 61 | assert.strictEqual( m.toString(), expected, 'fliplrud' ); 62 | 63 | // Flip the matrix left-to-right: 64 | mat.strides[ 1 ] *= -1; 65 | mat.offset = mat.length + mat.strides[ 0 ]; 66 | 67 | m = mat.mget( [14,28,47] ); 68 | expected = '84,78,57'; 69 | 70 | assert.strictEqual( m.toString(), expected, 'flipud' ); 71 | }); 72 | 73 | it( 'should return all rows and select columns', function test() { 74 | var m, expected; 75 | 76 | m = mat.mget( null, [1] ); 77 | expected = '1;11;21;31;41;51;61;71;81;91'; 78 | 79 | assert.deepEqual( m.shape, [10,1] ); 80 | assert.strictEqual( m.toString(), expected ); 81 | 82 | // Flip the matrix left-to-right: 83 | mat.strides[ 1 ] *= -1; 84 | mat.offset = mat.strides[ 0 ] - 1; 85 | 86 | m = mat.mget( null, [1] ); 87 | expected = '8;18;28;38;48;58;68;78;88;98'; 88 | 89 | assert.strictEqual( m.toString(), expected, 'fliplr' ); 90 | 91 | // Flip the matrix top-to-bottom: 92 | mat.strides[ 0 ] *= -1; 93 | mat.offset = mat.length - 1; 94 | 95 | m = mat.mget( null, [1] ); 96 | expected = '98;88;78;68;58;48;38;28;18;8'; 97 | 98 | assert.strictEqual( m.toString(), expected, 'fliplrud' ); 99 | 100 | // Flip the matrix left-to-right: 101 | mat.strides[ 1 ] *= -1; 102 | mat.offset = mat.length + mat.strides[ 0 ]; 103 | 104 | m = mat.mget( null, [1] ); 105 | expected = '91;81;71;61;51;41;31;21;11;1'; 106 | 107 | assert.strictEqual( m.toString(), expected, 'flipud' ); 108 | }); 109 | 110 | it( 'should return all columns and select rows', function test() { 111 | var m, expected; 112 | 113 | m = mat.mget( [1], null ); 114 | expected = '10,11,12,13,14,15,16,17,18,19'; 115 | 116 | assert.deepEqual( m.shape, [1,10] ); 117 | assert.strictEqual( m.toString(), expected ); 118 | 119 | // Flip the matrix left-to-right: 120 | mat.strides[ 1 ] *= -1; 121 | mat.offset = mat.strides[ 0 ] - 1; 122 | 123 | m = mat.mget( [1], null ); 124 | expected = '19,18,17,16,15,14,13,12,11,10'; 125 | 126 | assert.strictEqual( m.toString(), expected, 'fliplr' ); 127 | 128 | // Flip the matrix top-to-bottom: 129 | mat.strides[ 0 ] *= -1; 130 | mat.offset = mat.length - 1; 131 | 132 | m = mat.mget( [1], null ); 133 | expected = '89,88,87,86,85,84,83,82,81,80'; 134 | 135 | assert.strictEqual( m.toString(), expected, 'fliplrud' ); 136 | 137 | // Flip the matrix left-to-right: 138 | mat.strides[ 1 ] *= -1; 139 | mat.offset = mat.length + mat.strides[ 0 ]; 140 | 141 | m = mat.mget( [1], null ); 142 | expected = '80,81,82,83,84,85,86,87,88,89'; 143 | 144 | assert.strictEqual( m.toString(), expected, 'flipud' ); 145 | }); 146 | 147 | it( 'should not dedupe indices', function test() { 148 | var mat1; 149 | 150 | mat1 = mat.mget( [1,1,1,1,2,2] ); 151 | assert.strictEqual( mat1.toString(), '1,1,1,1,2,2' ); 152 | 153 | mat1 = mat.mget( [1,1], [1,2] ); 154 | assert.strictEqual( mat1.toString(), '11,12;11,12' ); 155 | }); 156 | 157 | }); 158 | -------------------------------------------------------------------------------- /lib/mset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // MODULES // 4 | 5 | var isFunction = require( 'validate.io-function' ), 6 | isnan = require( 'validate.io-nan' ), 7 | isNumber = require( 'validate.io-number-primitive' ), 8 | isNonNegativeIntegerArray = require( 'validate.io-nonnegative-integer-array' ); 9 | 10 | 11 | // FUNCTIONS // 12 | 13 | var mset1 = require( './mset1.js' ), 14 | mset2 = require( './mset2.js' ), 15 | mset3 = require( './mset3.js' ), 16 | mset4 = require( './mset4.js' ), 17 | mset5 = require( './mset5.js' ), 18 | mset6 = require( './mset6.js' ); 19 | 20 | /** 21 | * FUNCTION: getIndices( idx, len ) 22 | * Validates and returns an array of indices. 23 | * 24 | * @private 25 | * @param {Number[]|Null} idx - indices 26 | * @param {Number} len - max index 27 | * @returns {Number[]} indices 28 | */ 29 | function getIndices( idx, len ) { 30 | var out, 31 | i; 32 | if ( idx === null ) { 33 | out = new Array( len ); 34 | for ( i = 0; i < len; i++ ) { 35 | out[ i ] = i; 36 | } 37 | } 38 | else if ( isNonNegativeIntegerArray( idx ) ) { 39 | out = []; 40 | for ( i = 0; i < idx.length; i++ ) { 41 | if ( idx[ i ] < len ) { 42 | out.push( idx[ i ] ); 43 | } 44 | } 45 | } 46 | else { 47 | throw new TypeError( 'invalid input argument. Row and column indices must be arrays of nonnegative integers. Value: `' + idx + '`.' ); 48 | } 49 | return out; 50 | } // end FUNCTION getIndices() 51 | 52 | 53 | // MSET // 54 | 55 | /** 56 | * FUNCTION: mset( i[, j], value[, thisArg] ) 57 | * Sets multiple matrix elements. If provided a single array, `i` is treated as an array of linear indices. 58 | * 59 | * @param {Number[]|Null} i - linear/row indices 60 | * @param {Number[]|Null} [j] - column indices 61 | * @param {Number|Matrix|Function} value - either a single numeric value, a matrix containing the values to set, or a function which returns a numeric value 62 | * @returns {Matrix} Matrix instance 63 | */ 64 | function mset() { 65 | /*jshint validthis:true */ 66 | var nargs = arguments.length, 67 | args, 68 | rows, 69 | cols, 70 | i; 71 | 72 | args = new Array( nargs ); 73 | for ( i = 0; i < nargs; i++ ) { 74 | args[ i ] = arguments[ i ]; 75 | } 76 | 77 | // 2 input arguments... 78 | if ( nargs < 3 ) { 79 | if ( !isNonNegativeIntegerArray( args[ 0 ] ) ) { 80 | throw new TypeError( 'invalid input argument. First argument must be an array of nonnegative integers. Value: `' + args[ 0 ] + '`.' ); 81 | } 82 | // indices, clbk 83 | if ( isFunction( args[ 1 ] ) ) { 84 | mset2( this, args[ 0 ], args[ 1 ] ); 85 | } 86 | // indices, number 87 | else if ( isNumber( args[ 1 ] ) || isnan( args[ 1 ] ) ) { 88 | mset1( this, args[ 0 ], args[ 1 ] ); 89 | } 90 | // indices, matrix 91 | else { 92 | // NOTE: no validation for Matrix instance. 93 | mset3( this, args[ 0 ], args[ 1 ] ); 94 | } 95 | } 96 | // 3 input arguments... 97 | else if ( nargs === 3 ) { 98 | // indices, clbk, context 99 | if ( isFunction( args[ 1 ] ) ) { 100 | if ( !isNonNegativeIntegerArray( args[ 0 ] ) ) { 101 | throw new TypeError( 'invalid input argument. First argument must be an array of nonnegative integers. Value: `' + args[ 0 ] + '`.' ); 102 | } 103 | mset2( this, args[ 0 ], args[ 1 ], args[ 2 ] ); 104 | } 105 | // rows, cols, function 106 | else if ( isFunction( args[ 2 ] ) ) { 107 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 108 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 109 | mset4( this, rows, cols, args[ 2 ], this ); 110 | } 111 | // rows, cols, number 112 | else if ( isNumber( args[ 2 ] ) ) { 113 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 114 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 115 | mset5( this, rows, cols, args[ 2 ] ); 116 | } 117 | // rows, cols, matrix 118 | else { 119 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 120 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 121 | 122 | // NOTE: no validation for Matrix instance. 123 | mset6( this, rows, cols, args[ 2 ] ); 124 | } 125 | } 126 | // 4 input arguments... 127 | else { 128 | // rows, cols, function, context 129 | if ( !isFunction( args[ 2 ] ) ) { 130 | throw new TypeError( 'invalid input argument. Callback argument must be a function. Value: `' + args[ 2 ] + '`.' ); 131 | } 132 | rows = getIndices( args[ 0 ], this.shape[ 0 ] ); 133 | cols = getIndices( args[ 1 ], this.shape[ 1 ] ); 134 | mset4( this, rows, cols, args[ 2 ], args[ 3 ] ); 135 | } 136 | return this; 137 | } // end FUNCTION mset() 138 | 139 | 140 | // EXPORTS // 141 | 142 | module.exports = mset; 143 | -------------------------------------------------------------------------------- /test/test.sset.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | sset = require( './../lib/sset.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#sset', function tests() { 20 | 21 | var mat; 22 | 23 | beforeEach( function before() { 24 | var data = new Int8Array( 100 ); 25 | for ( var i = 0; i < data.length; i++ ) { 26 | data[ i ] = i; 27 | } 28 | mat = matrix( data, [10,10] ); 29 | }); 30 | 31 | it( 'should export a function', function test() { 32 | expect( sset ).to.be.a( 'function' ); 33 | }); 34 | 35 | it( 'should throw an error if provided a value matrix with dimensions which do not match the dimensions determined by the row and column subsequences', function test() { 36 | var m; 37 | 38 | m = matrix( new Int8Array( 2 ), [1,2] ); 39 | expect( badValue( m ) ).to.throw( Error ); 40 | 41 | m = matrix( new Int8Array( 2 ), [2,1] ); 42 | expect( badValue( m ) ).to.throw( Error ); 43 | 44 | function badValue( value ) { 45 | return function() { 46 | mat.sset( '4:6,6:8', value ); 47 | }; 48 | } 49 | }); 50 | 51 | it( 'should return the Matrix instance', function test() { 52 | var m = mat.sset( '4:6,6:8', 5 ); 53 | assert.strictEqual( mat, m ); 54 | }); 55 | 56 | it( 'should silently ignore out-of-bounds subsequences', function test() { 57 | var actual, expected; 58 | 59 | expected = mat.toString(); 60 | 61 | mat.sset( '400:600,6:8', 5 ); 62 | actual = mat.toString(); 63 | 64 | assert.strictEqual( actual, expected ); 65 | 66 | mat.sset( '4:6,600:800', 5 ); 67 | actual = mat.toString(); 68 | 69 | assert.strictEqual( actual, expected ); 70 | }); 71 | 72 | it( 'should set Matrix elements using a callback', function test() { 73 | var submat; 74 | 75 | mat.sset( '4:6,6:8', set ); 76 | submat = mat.sget( '3:7,5:9' ); 77 | 78 | assert.strictEqual( submat.toString(), '35,36,37,38;45,64,74,48;55,65,75,58;65,66,67,68' ); 79 | 80 | // Flip the matrix up-down: 81 | mat.strides[ 0 ] *= -1; 82 | mat.offset = mat.length + mat.strides[ 0 ]; 83 | 84 | mat.sset( '4:6,6:8', set ); 85 | submat = mat.sget( '3:7,5:9' ); 86 | 87 | assert.strictEqual( submat.toString(), '65,66,67,68;55,64,74,58;45,65,75,48;35,36,37,38' ); 88 | 89 | function set( d, i, j, idx ) { 90 | assert.isTrue( i >= 0 ); 91 | assert.isTrue( j >= 0 ); 92 | assert.isTrue( idx >= 0 ); 93 | return '' + j + i; 94 | } 95 | }); 96 | 97 | it( 'should set Matrix elements using a callback and a provided context', function test() { 98 | var submat; 99 | 100 | mat.sset( '4:6,6:8', set, null ); 101 | submat = mat.sget( '3:7,5:9' ); 102 | 103 | assert.strictEqual( submat.toString(), '35,36,37,38;45,64,74,48;55,65,75,58;65,66,67,68' ); 104 | 105 | function set( d, i, j ) { 106 | /* jshint validthis:true */ 107 | assert.isNull( this ); 108 | return '' + j + i; 109 | } 110 | }); 111 | 112 | it( 'should set Matrix elements to a single numeric value', function test() { 113 | var submat; 114 | 115 | mat.sset( '4:6,6:8', 5 ); 116 | submat = mat.sget( '3:7,5:9' ); 117 | 118 | assert.strictEqual( submat.toString(), '35,36,37,38;45,5,5,48;55,5,5,58;65,66,67,68' ); 119 | 120 | // Flip the matrix up-down: 121 | mat.strides[ 0 ] *= -1; 122 | mat.offset = mat.length + mat.strides[ 0 ]; 123 | 124 | mat.sset( '4:6,6:8', 5 ); 125 | submat = mat.sget( '3:7,5:9' ); 126 | 127 | assert.strictEqual( submat.toString(), '65,66,67,68;55,5,5,58;45,5,5,48;35,36,37,38' ); 128 | }); 129 | 130 | it( 'should set Matrix elements to elements in a different Matrix', function test() { 131 | var submat, m; 132 | 133 | m = matrix( new Int8Array( [1,2,3,4] ), [2,2], 'int8' ); 134 | 135 | mat.sset( '4:6,6:8', m ); 136 | submat = mat.sget( '3:7,5:9' ); 137 | 138 | assert.strictEqual( submat.toString(), '35,36,37,38;45,1,2,48;55,3,4,58;65,66,67,68' ); 139 | 140 | // Flip the value matrix left-right: 141 | m.strides[ 1 ] *= -1; 142 | m.offset = m.strides[ 0 ] - 1; 143 | 144 | mat.sset( '4:6,6:8', m ); 145 | submat = mat.sget( '3:7,5:9' ); 146 | 147 | assert.strictEqual( submat.toString(), '35,36,37,38;45,2,1,48;55,4,3,58;65,66,67,68' ); 148 | 149 | // Flip the matrix top-down: 150 | mat.strides[ 0 ] *= -1; 151 | mat.offset = mat.length + mat.strides[ 0 ]; 152 | 153 | mat.sset( '4:6,6:8', m ); 154 | submat = mat.sget( '3:7,5:9' ); 155 | 156 | assert.strictEqual( submat.toString(), '65,66,67,68;55,2,1,58;45,4,3,48;35,36,37,38' ); 157 | }); 158 | 159 | }); 160 | -------------------------------------------------------------------------------- /test/test.set.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | isnan = require( 'validate.io-nan' ), 8 | matrix = require( './../lib' ), 9 | set = require( './../lib/set.js' ); 10 | 11 | 12 | // VARIABLES // 13 | 14 | var expect = chai.expect, 15 | assert = chai.assert; 16 | 17 | 18 | // TESTS // 19 | 20 | describe( 'matrix#set', function tests() { 21 | 22 | var mat, data; 23 | 24 | data = new Int32Array( 100 ); 25 | for ( var i = 0; i < data.length; i++ ) { 26 | data[ i ] = i; 27 | } 28 | 29 | beforeEach( function before() { 30 | mat = matrix( data, [10,10] ); 31 | }); 32 | 33 | it( 'should export a function', function test() { 34 | expect( set ).to.be.a( 'function' ); 35 | }); 36 | 37 | it( 'should throw an error if provided a row index which is not a nonnegative integer', function test() { 38 | var values = [ 39 | '5', 40 | -1, 41 | Math.PI, 42 | NaN, 43 | null, 44 | true, 45 | undefined, 46 | [], 47 | {}, 48 | function(){} 49 | ]; 50 | 51 | for ( var i = 0; i < values.length; i++ ) { 52 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 53 | } 54 | function badValue( value ) { 55 | return function() { 56 | mat.set( value, 0, 5 ); 57 | }; 58 | } 59 | }); 60 | 61 | it( 'should throw an error if provided a column index which is not a nonnegative integer', function test() { 62 | var values = [ 63 | '5', 64 | -1, 65 | Math.PI, 66 | NaN, 67 | null, 68 | true, 69 | undefined, 70 | [], 71 | {}, 72 | function(){} 73 | ]; 74 | 75 | for ( var i = 0; i < values.length; i++ ) { 76 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 77 | } 78 | function badValue( value ) { 79 | return function() { 80 | mat.set( 0, value, 5 ); 81 | }; 82 | } 83 | }); 84 | 85 | it( 'should throw an error if provided a non-numeric value to set', function test() { 86 | var values = [ 87 | '5', 88 | null, 89 | true, 90 | undefined, 91 | [], 92 | {}, 93 | function(){} 94 | ]; 95 | 96 | for ( var i = 0; i < values.length; i++ ) { 97 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 98 | } 99 | function badValue( value ) { 100 | return function() { 101 | mat.set( 0, 0, value ); 102 | }; 103 | } 104 | }); 105 | 106 | it( 'should set a Matrix element', function test() { 107 | var prev, actual, expected; 108 | 109 | prev = mat.get( 5, 6 ); 110 | mat.set( 5, 6, 1000 ); 111 | 112 | actual = mat.get( 5, 6 ); 113 | expected = 1000; 114 | 115 | assert.notEqual( actual, prev ); 116 | assert.strictEqual( actual, expected ); 117 | 118 | // Flip the matrix left-to-right: 119 | mat.strides[ 1 ] *= -1; 120 | mat.offset = mat.strides[ 0 ] - 1; 121 | 122 | prev = mat.get( 5, 6 ); 123 | mat.set( 5, 6, 1001 ); 124 | 125 | actual = mat.get( 5, 6 ); 126 | expected = 1001; 127 | 128 | assert.notEqual( actual, prev ); 129 | assert.strictEqual( actual, expected, 'fliplr' ); 130 | 131 | // Flip the matrix top-to-bottom: 132 | mat.strides[ 0 ] *= -1; 133 | mat.offset = mat.length - 1; 134 | 135 | prev = mat.get( 5, 6 ); 136 | mat.set( 5, 6, 1002 ); 137 | 138 | actual = mat.get( 5, 6 ); 139 | expected = 1002; 140 | 141 | assert.notEqual( actual, prev ); 142 | assert.strictEqual( actual, expected, 'fliplrud' ); 143 | 144 | // Flip the matrix left-to-right: 145 | mat.strides[ 1 ] *= -1; 146 | mat.offset = mat.length + mat.strides[ 0 ]; 147 | 148 | prev = mat.get( 5, 6 ); 149 | mat.set( 5, 6, 1003 ); 150 | 151 | actual = mat.get( 5, 6 ); 152 | expected = 1003; 153 | 154 | assert.notEqual( actual, prev ); 155 | assert.strictEqual( actual, expected, 'flipud' ); 156 | }); 157 | 158 | it( 'should set a Matrix element to NaN', function test() { 159 | var mat1, mat2, 160 | prev, actual, expected; 161 | 162 | mat1 = matrix( [1,2,3,4], [2,2], 'float32' ); 163 | prev = mat1.get( 1, 1 ); 164 | mat1.set( 1, 1, NaN ); 165 | 166 | actual = mat1.get( 1, 1 ); 167 | 168 | assert.notEqual( actual, prev ); 169 | assert.isTrue( isnan( actual ) ); 170 | 171 | // For integer matrices, NaN will be casted to 0: 172 | mat2 = matrix( [1,2,3,4], [2,2], 'int32' ); 173 | prev = mat2.get( 1, 1 ); 174 | mat2.set( 1, 1, NaN ); 175 | 176 | actual = mat2.get( 1, 1 ); 177 | expected = 0; 178 | 179 | assert.notEqual( actual, prev ); 180 | assert.strictEqual( actual, expected ); 181 | }); 182 | 183 | it( 'should return the Matrix instance', function test() { 184 | assert.strictEqual( mat.set( 5, 6, 999 ), mat ); 185 | }); 186 | 187 | it( 'should silently fail if provided an out-of-bounds index', function test() { 188 | mat.set( 500, 100, 1000 ); 189 | assert.isUndefined( mat.get( 500, 100 ) ); 190 | 191 | mat.strides[ 0 ] *= -1; 192 | mat.set( 500, 100, 1000 ); 193 | assert.isUndefined( mat.get( 500, 100 ) ); 194 | }); 195 | 196 | }); 197 | -------------------------------------------------------------------------------- /test/test.iset.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | isnan = require( 'validate.io-nan' ), 8 | matrix = require( './../lib' ), 9 | iset = require( './../lib/iset.js' ); 10 | 11 | 12 | // VARIABLES // 13 | 14 | var expect = chai.expect, 15 | assert = chai.assert; 16 | 17 | 18 | // TESTS // 19 | 20 | describe( 'matrix#iset', function tests() { 21 | 22 | var mat, data; 23 | 24 | data = new Int32Array( 100 ); 25 | for ( var i = 0; i < data.length; i++ ) { 26 | data[ i ] = i * 2; 27 | } 28 | 29 | beforeEach( function before() { 30 | mat = matrix( data, [10,10], 'int32' ); 31 | }); 32 | 33 | it( 'should export a function', function test() { 34 | expect( iset ).to.be.a( 'function' ); 35 | }); 36 | 37 | it( 'should throw an error if not provided an integer', function test() { 38 | var values = [ 39 | '5', 40 | Math.PI, 41 | NaN, 42 | null, 43 | true, 44 | undefined, 45 | [], 46 | {}, 47 | function(){} 48 | ]; 49 | 50 | for ( var i = 0; i < values.length; i++ ) { 51 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 52 | } 53 | function badValue( value ) { 54 | return function() { 55 | mat.iset( value, 999 ); 56 | }; 57 | } 58 | }); 59 | 60 | it( 'should throw an error if provided a non-numeric value to set', function test() { 61 | var values = [ 62 | '5', 63 | null, 64 | true, 65 | undefined, 66 | [], 67 | {}, 68 | function(){} 69 | ]; 70 | 71 | for ( var i = 0; i < values.length; i++ ) { 72 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 73 | } 74 | function badValue( value ) { 75 | return function() { 76 | mat.iset( 0, value ); 77 | }; 78 | } 79 | }); 80 | 81 | it( 'should set a Matrix element', function test() { 82 | var prev, actual, expected; 83 | 84 | prev = mat.iget( 56 ); 85 | mat.iset( 56, 365 ); 86 | 87 | actual = mat.iget( 56 ); 88 | expected = 365; 89 | 90 | assert.notEqual( actual, prev ); 91 | assert.strictEqual( actual, expected ); 92 | 93 | // Flip the matrix left-to-right: 94 | mat.strides[ 1 ] *= -1; 95 | mat.offset = mat.strides[ 0 ] - 1; 96 | 97 | prev = mat.iget( 56 ); 98 | mat.iset( 56, 499 ); 99 | 100 | actual = mat.iget( 56 ); 101 | expected = 499; 102 | 103 | assert.notEqual( actual, prev ); 104 | assert.strictEqual( actual, expected, 'fliplr' ); 105 | 106 | // Flip the matrix top-to-bottom: 107 | mat.strides[ 0 ] *= -1; 108 | mat.offset = mat.length - 1; 109 | 110 | prev = mat.iget( 56 ); 111 | mat.iset( 56, 1001 ); 112 | 113 | actual = mat.iget( 56 ); 114 | expected = 1001; 115 | 116 | assert.notEqual( actual, prev ); 117 | assert.strictEqual( actual, expected, 'fliplrud' ); 118 | 119 | // Flip the matrix left-to-right: 120 | mat.strides[ 1 ] *= -1; 121 | mat.offset = mat.length + mat.strides[ 0 ]; 122 | 123 | prev = mat.iget( 56 ); 124 | mat.iset( 56, 782 ); 125 | 126 | actual = mat.iget( 56 ); 127 | expected = 782; 128 | 129 | assert.notEqual( actual, prev ); 130 | assert.strictEqual( actual, expected, 'flipud' ); 131 | }); 132 | 133 | it( 'should set a Matrix element to NaN', function test() { 134 | var mat1, mat2, 135 | prev, actual, expected; 136 | 137 | mat1 = matrix( [1,2,3,4], [2,2], 'float32' ); 138 | prev = mat1.iget( 2 ); 139 | mat1.iset( 2, NaN ); 140 | 141 | actual = mat1.iget( 2 ); 142 | 143 | assert.notEqual( actual, prev ); 144 | assert.isTrue( isnan( actual ) ); 145 | 146 | // For integer matrices, NaN will be cast to 0: 147 | mat2 = matrix( [1,2,3,4], [2,2], 'int32' ); 148 | prev = mat2.iget( 2 ); 149 | mat2.iset( 2, NaN ); 150 | 151 | actual = mat2.iget( 2 ); 152 | expected = 0; 153 | 154 | assert.notEqual( actual, prev ); 155 | assert.strictEqual( actual, expected ); 156 | 157 | }); 158 | 159 | it( 'should accept negative indices', function test() { 160 | var prev, actual, expected; 161 | 162 | prev = mat.iget( -2 ); 163 | mat.iset( -2, 999 ); 164 | 165 | actual = mat.iget( -2 ); 166 | expected = 999; 167 | 168 | assert.notEqual( actual, prev ); 169 | assert.strictEqual( actual, expected ); 170 | 171 | // Flip the matrix left-to-right: 172 | mat.strides[ 1 ] *= -1; 173 | mat.offset = mat.strides[ 0 ] - 1; 174 | 175 | prev = mat.iget( -2 ); 176 | mat.iset( -2, 499 ); 177 | 178 | actual = mat.iget( -2 ); 179 | expected = 499; 180 | 181 | assert.notEqual( actual, prev ); 182 | assert.strictEqual( actual, expected, 'fliplr' ); 183 | 184 | // Flip the matrix top-to-bottom: 185 | mat.strides[ 0 ] *= -1; 186 | mat.offset = mat.length - 1; 187 | 188 | prev = mat.iget( -2 ); 189 | mat.iset( -2, 1001 ); 190 | 191 | actual = mat.iget( -2 ); 192 | expected = 1001; 193 | 194 | assert.notEqual( actual, prev ); 195 | assert.strictEqual( actual, expected, 'fliplrud' ); 196 | 197 | // Flip the matrix left-to-right: 198 | mat.strides[ 1 ] *= -1; 199 | mat.offset = mat.length + mat.strides[ 0 ]; 200 | 201 | prev = mat.iget( -2 ); 202 | mat.iset( -2, 782 ); 203 | 204 | actual = mat.iget( -2 ); 205 | expected = 782; 206 | 207 | assert.notEqual( actual, prev ); 208 | assert.strictEqual( actual, expected, 'flipud' ); 209 | }); 210 | 211 | it( 'should return the Matrix instance', function test() { 212 | assert.strictEqual( mat.iset( 5, 6, 999 ), mat ); 213 | }); 214 | 215 | it( 'should silently fail if provided an out-of-bounds index', function test() { 216 | mat.iset( 1e5, 987 ); 217 | assert.isUndefined( mat.iget( 1e5 ) ); 218 | 219 | mat.iset( -1e5, 789 ); 220 | assert.isUndefined( mat.iget( -1e5 ) ); 221 | }); 222 | 223 | }); 224 | -------------------------------------------------------------------------------- /test/test.sset.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | sset = require( './../lib/sset.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#sset', function tests() { 20 | 21 | var mat; 22 | 23 | beforeEach( function before() { 24 | var data = new Int8Array( 100 ); 25 | for ( var i = 0; i < data.length; i++ ) { 26 | data[ i ] = i; 27 | } 28 | mat = matrix( data, [10,10] ); 29 | }); 30 | 31 | it( 'should export a function', function test() { 32 | expect( sset ).to.be.a( 'function' ); 33 | }); 34 | 35 | it( 'should throw an error if not provided a string', function test() { 36 | var values = [ 37 | 5, 38 | NaN, 39 | null, 40 | true, 41 | undefined, 42 | [], 43 | {}, 44 | function(){} 45 | ]; 46 | 47 | for ( var i = 0; i < values.length; i++ ) { 48 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 49 | } 50 | function badValue( value ) { 51 | return function() { 52 | mat.sset( value, 5 ); 53 | }; 54 | } 55 | }); 56 | 57 | it( 'should throw an error if provided a string which does not have row and column subsequences', function test() { 58 | var values = [ 59 | '5:', 60 | '5:;', 61 | '5:;::1', 62 | 5, 63 | NaN, 64 | null, 65 | true, 66 | undefined, 67 | [], 68 | {}, 69 | function(){} 70 | ]; 71 | 72 | for ( var i = 0; i < values.length; i++ ) { 73 | expect( badValue( values[ i ] ) ).to.throw( Error ); 74 | } 75 | function badValue( value ) { 76 | return function() { 77 | mat.sset( value, 5 ); 78 | }; 79 | } 80 | }); 81 | 82 | it( 'should throw an error if provided a value matrix with dimensions which do not match the dimensions determined by the row and column subsequences', function test() { 83 | var m; 84 | 85 | m = matrix( new Int8Array( 2 ), [1,2] ); 86 | expect( badValue( m ) ).to.throw( Error ); 87 | 88 | m = matrix( new Int8Array( 2 ), [2,1] ); 89 | expect( badValue( m ) ).to.throw( Error ); 90 | 91 | function badValue( value ) { 92 | return function() { 93 | mat.sset( '4:6,6:8', value ); 94 | }; 95 | } 96 | }); 97 | 98 | it( 'should return the Matrix instance', function test() { 99 | var m = mat.sset( '4:6,6:8', 5 ); 100 | assert.strictEqual( mat, m ); 101 | }); 102 | 103 | it( 'should silently ignore out-of-bounds subsequences', function test() { 104 | var actual, expected; 105 | 106 | expected = mat.toString(); 107 | 108 | mat.sset( '400:600,6:8', 5 ); 109 | actual = mat.toString(); 110 | 111 | assert.strictEqual( actual, expected ); 112 | 113 | mat.sset( '4:6,600:800', 5 ); 114 | actual = mat.toString(); 115 | 116 | assert.strictEqual( actual, expected ); 117 | }); 118 | 119 | it( 'should set Matrix elements using a callback', function test() { 120 | var submat; 121 | 122 | mat.sset( '4:6,6:8', set ); 123 | submat = mat.sget( '3:7,5:9' ); 124 | 125 | assert.strictEqual( submat.toString(), '35,36,37,38;45,64,74,48;55,65,75,58;65,66,67,68' ); 126 | 127 | // Flip the matrix up-down: 128 | mat.strides[ 0 ] *= -1; 129 | mat.offset = mat.length + mat.strides[ 0 ]; 130 | 131 | mat.sset( '4:6,6:8', set ); 132 | submat = mat.sget( '3:7,5:9' ); 133 | 134 | assert.strictEqual( submat.toString(), '65,66,67,68;55,64,74,58;45,65,75,48;35,36,37,38' ); 135 | 136 | function set( d, i, j, idx ) { 137 | assert.isTrue( i >= 0 ); 138 | assert.isTrue( j >= 0 ); 139 | assert.isTrue( idx >= 0 ); 140 | return '' + j + i; 141 | } 142 | }); 143 | 144 | it( 'should set Matrix elements using a callback and a provided context', function test() { 145 | var submat; 146 | 147 | mat.sset( '4:6,6:8', set, null ); 148 | submat = mat.sget( '3:7,5:9' ); 149 | 150 | assert.strictEqual( submat.toString(), '35,36,37,38;45,64,74,48;55,65,75,58;65,66,67,68' ); 151 | 152 | function set( d, i, j ) { 153 | /* jshint validthis:true */ 154 | assert.isNull( this ); 155 | return '' + j + i; 156 | } 157 | }); 158 | 159 | it( 'should set Matrix elements to a single numeric value', function test() { 160 | var submat; 161 | 162 | mat.sset( '4:6,6:8', 5 ); 163 | submat = mat.sget( '3:7,5:9' ); 164 | 165 | assert.strictEqual( submat.toString(), '35,36,37,38;45,5,5,48;55,5,5,58;65,66,67,68' ); 166 | 167 | // Flip the matrix up-down: 168 | mat.strides[ 0 ] *= -1; 169 | mat.offset = mat.length + mat.strides[ 0 ]; 170 | 171 | mat.sset( '4:6,6:8', 5 ); 172 | submat = mat.sget( '3:7,5:9' ); 173 | 174 | assert.strictEqual( submat.toString(), '65,66,67,68;55,5,5,58;45,5,5,48;35,36,37,38' ); 175 | }); 176 | 177 | it( 'should set Matrix elements to NaN', function test() { 178 | var mat; 179 | 180 | mat = matrix( [1,2,3,4,5,6,7,8,9], [3,3], 'float64' ); 181 | assert.strictEqual( mat.toString(), '1,2,3;4,5,6;7,8,9' ); 182 | 183 | mat.sset( '0:2,0:2', NaN ); 184 | assert.strictEqual( mat.toString(), 'NaN,NaN,3;NaN,NaN,6;7,8,9' ); 185 | }); 186 | 187 | it( 'should set Matrix elements to elements in a different Matrix', function test() { 188 | var submat, m; 189 | 190 | m = matrix( new Int8Array( [1,2,3,4] ), [2,2], 'int8' ); 191 | 192 | mat.sset( '4:6,6:8', m ); 193 | submat = mat.sget( '3:7,5:9' ); 194 | 195 | assert.strictEqual( submat.toString(), '35,36,37,38;45,1,2,48;55,3,4,58;65,66,67,68' ); 196 | 197 | // Flip the value matrix left-right: 198 | m.strides[ 1 ] *= -1; 199 | m.offset = m.strides[ 0 ] - 1; 200 | 201 | mat.sset( '4:6,6:8', m ); 202 | submat = mat.sget( '3:7,5:9' ); 203 | 204 | assert.strictEqual( submat.toString(), '35,36,37,38;45,2,1,48;55,4,3,58;65,66,67,68' ); 205 | 206 | // Flip the matrix top-down: 207 | mat.strides[ 0 ] *= -1; 208 | mat.offset = mat.length + mat.strides[ 0 ]; 209 | 210 | mat.sset( '4:6,6:8', m ); 211 | submat = mat.sget( '3:7,5:9' ); 212 | 213 | assert.strictEqual( submat.toString(), '65,66,67,68;55,2,1,58;45,4,3,48;35,36,37,38' ); 214 | }); 215 | 216 | }); 217 | -------------------------------------------------------------------------------- /test/test.matrix.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | typeName = require( 'type-name' ), 8 | matrix = require( './../lib/matrix.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix', function tests() { 20 | 21 | it( 'should export a function', function test() { 22 | expect( matrix ).to.be.a( 'function' ); 23 | }); 24 | 25 | it( 'should throw an error if provided a shape argument which is not a nonnegative integer array', function test() { 26 | var values = [ 27 | '5', 28 | 5, 29 | NaN, 30 | null, 31 | undefined, 32 | true, 33 | [1,-1], 34 | [], 35 | {}, 36 | function(){} 37 | ]; 38 | 39 | for ( var i = 0; i < values.length; i++ ) { 40 | expect( badValue1( values[ i ] ) ).to.throw( TypeError ); 41 | expect( badValue2( values[ i ] ) ).to.throw( TypeError ); 42 | expect( badValue3( values[ i ] ) ).to.throw( TypeError ); 43 | } 44 | function badValue1( value ) { 45 | return function() { 46 | matrix( value ); 47 | }; 48 | } 49 | function badValue2( value ) { 50 | return function() { 51 | matrix( new Int8Array( 10 ), value ); 52 | }; 53 | } 54 | function badValue3( value ) { 55 | return function() { 56 | matrix( new Int8Array( 10 ), value, 'int8' ); 57 | }; 58 | } 59 | }); 60 | 61 | it( 'should throw an error if provided a shape array which does not contain exactly 2 elements', function test() { 62 | expect( badValue1 ).to.throw( Error ); 63 | expect( badValue2 ).to.throw( Error ); 64 | expect( badValue3 ).to.throw( Error ); 65 | function badValue1() { 66 | matrix( [1,2,3] ); 67 | } 68 | function badValue2() { 69 | matrix( new Int8Array( 12 ), [1,2,6] ); 70 | } 71 | function badValue3() { 72 | matrix( new Int8Array( 10 ), [10], 'int8' ); 73 | } 74 | }); 75 | 76 | it( 'should throw an error if provided an unrecognized/unsupported data type', function test() { 77 | var values = [ 78 | '5', 79 | 'double', 80 | 'single', 81 | 'int64', 82 | 'int128', 83 | 'doubledouble', 84 | 'boolean', 85 | 'binary', 86 | 'string', 87 | 'number', 88 | 5, 89 | NaN, 90 | null, 91 | undefined, 92 | true, 93 | [], 94 | {}, 95 | function(){} 96 | ]; 97 | 98 | for ( var i = 0; i < values.length; i++ ) { 99 | expect( badValue2( values[ i ] ) ).to.throw( TypeError ); 100 | expect( badValue3( values[ i ] ) ).to.throw( TypeError ); 101 | } 102 | function badValue2( value ) { 103 | return function() { 104 | matrix( [1,1], value ); 105 | }; 106 | } 107 | function badValue3( value ) { 108 | return function() { 109 | matrix( new Int8Array( 10 ), [10,1], value ); 110 | }; 111 | } 112 | }); 113 | 114 | it( 'should throw an error if provided data which is an unrecognized/unsupported data type', function test() { 115 | var values = [ 116 | '5', 117 | 5, 118 | NaN, 119 | null, 120 | undefined, 121 | true, 122 | // [], // Arrays are supported, but cast to `float64` 123 | {}, 124 | function(){} 125 | ]; 126 | 127 | for ( var i = 0; i < values.length; i++ ) { 128 | expect( badValue2( values[ i ] ) ).to.throw( TypeError ); 129 | expect( badValue3( values[ i ] ) ).to.throw( TypeError ); 130 | } 131 | function badValue2( value ) { 132 | return function() { 133 | matrix( value, [1,1] ); 134 | }; 135 | } 136 | function badValue3( value ) { 137 | return function() { 138 | matrix( value, [10,1], 'int8' ); 139 | }; 140 | } 141 | }); 142 | 143 | it( 'should throw an error if the matrix shape does not match the input data length', function test() { 144 | expect( badValue2 ).to.throw( Error ); 145 | expect( badValue3 ).to.throw( Error ); 146 | function badValue2() { 147 | matrix( new Int8Array( 10 ), [2,2] ); 148 | } 149 | function badValue3() { 150 | matrix( new Int8Array( 10 ), [5,1], 'int8' ); 151 | } 152 | }); 153 | 154 | it( 'should return a new Matrix instance', function test() { 155 | var mat; 156 | 157 | mat = matrix( [1,1] ); 158 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 159 | 160 | mat = matrix( new Int8Array( 1 ), [1,1] ); 161 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 162 | 163 | mat = matrix( [1,1], 'int8' ); 164 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 165 | 166 | mat = matrix( new Int8Array( 1 ), [1,1], 'int8' ); 167 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 168 | }); 169 | 170 | it( 'should return a new Matrix instance of a specified type', function test() { 171 | var mat; 172 | 173 | mat = matrix( new Int8Array( 1 ), [1,1] ); 174 | assert.strictEqual( mat.dtype, 'int8' ); 175 | 176 | mat = matrix( new Int8Array( 1 ), [1,1], 'int8' ); 177 | assert.strictEqual( mat.dtype, 'int8' ); 178 | }); 179 | 180 | it( 'should initialize a zero-filled Matrix if not provided input data (default dtype float64)', function test() { 181 | var mat = matrix( [1,1] ); 182 | 183 | for ( var i = 0; i < mat.data.length; i++ ) { 184 | assert.strictEqual( mat.data[ i], 0 ); 185 | } 186 | 187 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 188 | assert.strictEqual( typeName( mat.data ), 'Float64Array' ); 189 | assert.strictEqual( mat.dtype, 'float64' ); 190 | }); 191 | 192 | it( 'should initialize a zero-filled Matrix having a specified data type if not provided input data', function test() { 193 | var mat = matrix( [1,1], 'uint32' ); 194 | 195 | for ( var i = 0; i < mat.data.length; i++ ) { 196 | assert.strictEqual( mat.data[ i], 0 ); 197 | } 198 | 199 | assert.strictEqual( mat.constructor.name, 'Matrix' ); 200 | assert.strictEqual( typeName( mat.data ), 'Uint32Array' ); 201 | assert.strictEqual( mat.dtype, 'uint32' ); 202 | }); 203 | 204 | it( 'should cast input data to a specified type', function test() { 205 | var mat; 206 | 207 | mat = matrix( new Int8Array( 10 ), [5,2], 'uint16' ); 208 | assert.strictEqual( mat.dtype, 'uint16' ); 209 | assert.strictEqual( mat.data.BYTES_PER_ELEMENT, 2 ); 210 | 211 | mat = matrix( new Float32Array( 10 ), [5,2], 'uint8_clamped' ); 212 | assert.strictEqual( mat.dtype, 'uint8_clamped' ); 213 | assert.strictEqual( mat.data.BYTES_PER_ELEMENT, 1 ); 214 | 215 | mat = matrix( new Uint32Array( 10 ), [5,2], 'float32' ); 216 | assert.strictEqual( mat.dtype, 'float32' ); 217 | assert.strictEqual( mat.data.BYTES_PER_ELEMENT, 4 ); 218 | }); 219 | 220 | it( 'should cast a plain array to a float64', function test() { 221 | var mat, arr; 222 | 223 | arr = new Array(1,2,3,4,5,6); 224 | mat = matrix( arr, [3,2] ); 225 | 226 | assert.strictEqual( mat.dtype, 'float64' ); 227 | assert.strictEqual( mat.data.BYTES_PER_ELEMENT, 8 ); 228 | assert.strictEqual( mat.data.length, arr.length ); 229 | 230 | for ( var i = 0; i < arr.length; i++ ) { 231 | assert.strictEqual( arr[ i ], mat.data[ i ] ); 232 | } 233 | }); 234 | 235 | }); 236 | -------------------------------------------------------------------------------- /test/test.mget.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | mget = require( './../lib/mget.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#mget', function tests() { 20 | 21 | var mat, data; 22 | 23 | data = new Int8Array( 100 ); 24 | for ( var i = 0; i < data.length; i++ ) { 25 | data[ i ] = i; 26 | } 27 | 28 | beforeEach( function before() { 29 | mat = matrix( data, [10,10] ); 30 | }); 31 | 32 | it( 'should export a function', function test() { 33 | expect( mget ).to.be.a( 'function' ); 34 | }); 35 | 36 | it( 'should throw an error if provided a linear indices argument which is not a nonnegative integer array', function test() { 37 | var values = [ 38 | '5', 39 | 5, 40 | NaN, 41 | null, 42 | true, 43 | undefined, 44 | [], 45 | [1,-1], 46 | [1,Math.PI], 47 | {}, 48 | function(){} 49 | ]; 50 | 51 | for ( var i = 0; i < values.length; i++ ) { 52 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 53 | } 54 | function badValue( value ) { 55 | return function() { 56 | mat.mget( value ); 57 | }; 58 | } 59 | }); 60 | 61 | it( 'should throw an error if provided a row indices argument which is not a nonnegative integer array', function test() { 62 | var values = [ 63 | '5', 64 | 5, 65 | NaN, 66 | // null, // allowed 67 | true, 68 | undefined, 69 | [], 70 | [1,-1], 71 | [1,Math.PI], 72 | {}, 73 | function(){} 74 | ]; 75 | 76 | for ( var i = 0; i < values.length; i++ ) { 77 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 78 | } 79 | function badValue( value ) { 80 | return function() { 81 | mat.mget( value, [1,2,3] ); 82 | }; 83 | } 84 | }); 85 | 86 | it( 'should throw an error if provided a column indices argument which is not a nonnegative integer array', function test() { 87 | var values = [ 88 | '5', 89 | 5, 90 | NaN, 91 | // null, // allowed 92 | true, 93 | undefined, 94 | [], 95 | [1,-1], 96 | [1,Math.PI], 97 | {}, 98 | function(){} 99 | ]; 100 | 101 | for ( var i = 0; i < values.length; i++ ) { 102 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 103 | } 104 | function badValue( value ) { 105 | return function() { 106 | mat.mget( [1,2,3], value ); 107 | }; 108 | } 109 | }); 110 | 111 | it( 'should return values located at specified linear indices', function test() { 112 | var m, expected; 113 | 114 | m = mat.mget( [14,28,47] ); 115 | expected = '14,28,47'; 116 | 117 | assert.deepEqual( m.shape, [1,3] ); 118 | assert.strictEqual( m.toString(), expected ); 119 | 120 | // Flip the matrix left-to-right: 121 | mat.strides[ 1 ] *= -1; 122 | mat.offset = mat.strides[ 0 ] - 1; 123 | 124 | m = mat.mget( [14,28,47] ); 125 | expected = '15,21,42'; 126 | 127 | assert.strictEqual( m.toString(), expected, 'fliplr' ); 128 | 129 | // Flip the matrix top-to-bottom: 130 | mat.strides[ 0 ] *= -1; 131 | mat.offset = mat.length - 1; 132 | 133 | m = mat.mget( [14,28,47] ); 134 | expected = '85,71,52'; 135 | 136 | assert.strictEqual( m.toString(), expected, 'fliplrud' ); 137 | 138 | // Flip the matrix left-to-right: 139 | mat.strides[ 1 ] *= -1; 140 | mat.offset = mat.length + mat.strides[ 0 ]; 141 | 142 | m = mat.mget( [14,28,47] ); 143 | expected = '84,78,57'; 144 | 145 | assert.strictEqual( m.toString(), expected, 'flipud' ); 146 | }); 147 | 148 | it( 'should return values located at specified linear indices and ignore any indices which are out-of-bounds', function test() { 149 | var mat1 = mat.mget( [14,28,9999,47] ); 150 | 151 | assert.strictEqual( mat1.toString(), '14,28,47' ); 152 | }); 153 | 154 | it( 'should return all rows and select columns', function test() { 155 | var m, expected; 156 | 157 | m = mat.mget( null, [1] ); 158 | expected = '1;11;21;31;41;51;61;71;81;91'; 159 | 160 | assert.deepEqual( m.shape, [10,1] ); 161 | assert.strictEqual( m.toString(), expected ); 162 | 163 | // Flip the matrix left-to-right: 164 | mat.strides[ 1 ] *= -1; 165 | mat.offset = mat.strides[ 0 ] - 1; 166 | 167 | m = mat.mget( null, [1] ); 168 | expected = '8;18;28;38;48;58;68;78;88;98'; 169 | 170 | assert.strictEqual( m.toString(), expected, 'fliplr' ); 171 | 172 | // Flip the matrix top-to-bottom: 173 | mat.strides[ 0 ] *= -1; 174 | mat.offset = mat.length - 1; 175 | 176 | m = mat.mget( null, [1] ); 177 | expected = '98;88;78;68;58;48;38;28;18;8'; 178 | 179 | assert.strictEqual( m.toString(), expected, 'fliplrud' ); 180 | 181 | // Flip the matrix left-to-right: 182 | mat.strides[ 1 ] *= -1; 183 | mat.offset = mat.length + mat.strides[ 0 ]; 184 | 185 | m = mat.mget( null, [1] ); 186 | expected = '91;81;71;61;51;41;31;21;11;1'; 187 | 188 | assert.strictEqual( m.toString(), expected, 'flipud' ); 189 | }); 190 | 191 | it( 'should return all columns and select rows', function test() { 192 | var m, expected; 193 | 194 | m = mat.mget( [1], null ); 195 | expected = '10,11,12,13,14,15,16,17,18,19'; 196 | 197 | assert.deepEqual( m.shape, [1,10] ); 198 | assert.strictEqual( m.toString(), expected ); 199 | 200 | // Flip the matrix left-to-right: 201 | mat.strides[ 1 ] *= -1; 202 | mat.offset = mat.strides[ 0 ] - 1; 203 | 204 | m = mat.mget( [1], null ); 205 | expected = '19,18,17,16,15,14,13,12,11,10'; 206 | 207 | assert.strictEqual( m.toString(), expected, 'fliplr' ); 208 | 209 | // Flip the matrix top-to-bottom: 210 | mat.strides[ 0 ] *= -1; 211 | mat.offset = mat.length - 1; 212 | 213 | m = mat.mget( [1], null ); 214 | expected = '89,88,87,86,85,84,83,82,81,80'; 215 | 216 | assert.strictEqual( m.toString(), expected, 'fliplrud' ); 217 | 218 | // Flip the matrix left-to-right: 219 | mat.strides[ 1 ] *= -1; 220 | mat.offset = mat.length + mat.strides[ 0 ]; 221 | 222 | m = mat.mget( [1], null ); 223 | expected = '80,81,82,83,84,85,86,87,88,89'; 224 | 225 | assert.strictEqual( m.toString(), expected, 'flipud' ); 226 | }); 227 | 228 | it( 'should ignore out-of-bounds row and column indices', function test() { 229 | var mat1 = mat.mget( [2,999,4], [2,999,4] ); 230 | 231 | assert.deepEqual( mat1.shape, [2,2] ); 232 | assert.strictEqual( mat1.toString(), '22,24;42,44' ); 233 | }); 234 | 235 | it( 'should return an empty matrix if provided indices which have no corresponding matrix elements', function test() { 236 | var mat1; 237 | 238 | mat1 = mat.mget( [999] ); 239 | assert.strictEqual( mat1.length, 0 ); 240 | 241 | mat1 = mat.mget( [999,998], [998,999] ); 242 | assert.strictEqual( mat1.length, 0 ); 243 | }); 244 | 245 | it( 'should not dedupe indices', function test() { 246 | var mat1; 247 | 248 | mat1 = mat.mget( [1,1,1,1,2,2] ); 249 | assert.strictEqual( mat1.toString(), '1,1,1,1,2,2' ); 250 | 251 | mat1 = mat.mget( [1,1], [1,2] ); 252 | assert.strictEqual( mat1.toString(), '11,12;11,12' ); 253 | }); 254 | 255 | }); 256 | -------------------------------------------------------------------------------- /test/test.mset.raw.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ).raw, 8 | mset = require( './../lib/mset.raw.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix.raw#mset', function tests() { 20 | 21 | var mat; 22 | 23 | beforeEach( function before() { 24 | var data = new Int32Array( 100 ); 25 | for ( var i = 0; i < data.length; i++ ) { 26 | data[ i ] = i; 27 | } 28 | mat = matrix( data, [10,10], 'int32' ); 29 | }); 30 | 31 | it( 'should export a function', function test() { 32 | expect( mset ).to.be.a( 'function' ); 33 | }); 34 | 35 | it( 'should throw an error if the number of linear indices and the number of matrix elements do not agree', function test() { 36 | expect( badValue ).to.throw( Error ); 37 | function badValue() { 38 | mat.mset( [1,2,3], matrix( [10,1] ) ); 39 | } 40 | }); 41 | 42 | it( 'should throw an error if dimensions defined by row and column indices do not agree with value matrix dimensions', function test() { 43 | expect( badValue1 ).to.throw( Error ); 44 | expect( badValue2 ).to.throw( Error ); 45 | function badValue1() { 46 | mat.mset( [1,2,3], [1,2,3], matrix( [1,3] ) ); 47 | } 48 | function badValue2() { 49 | mat.mset( [1,2,3], [1,2,3], matrix( [3,1] ) ); 50 | } 51 | }); 52 | 53 | it( 'should set Matrix values located at specified linear indices to a numeric value', function test() { 54 | var idx, m, prev, actual, expected; 55 | 56 | idx = [ 14, 28, 47 ]; 57 | expected = '999,999,999'; 58 | 59 | prev = mat.mget( idx ); 60 | mat.mset( idx, 999 ); 61 | 62 | m = mat.mget( idx ); 63 | actual = m.toString(); 64 | 65 | assert.notEqual( actual, prev.toString() ); 66 | assert.strictEqual( actual, expected ); 67 | 68 | // Flip the matrix left-to-right: 69 | mat.strides[ 1 ] *= -1; 70 | mat.offset = mat.strides[ 0 ] - 1; 71 | 72 | prev = mat.mget( idx ); 73 | mat.mset( idx, 999 ); 74 | 75 | m = mat.mget( idx ); 76 | actual = m.toString(); 77 | 78 | assert.notEqual( actual, prev.toString() ); 79 | assert.strictEqual( actual, expected, 'fliplr' ); 80 | 81 | // Flip the matrix top-to-bottom: 82 | mat.strides[ 0 ] *= -1; 83 | mat.offset = mat.length - 1; 84 | 85 | prev = mat.mget( idx ); 86 | mat.mset( idx, 999 ); 87 | 88 | m = mat.mget( idx ); 89 | actual = m.toString(); 90 | 91 | assert.notEqual( actual, prev.toString() ); 92 | assert.strictEqual( actual, expected, 'fliplrud' ); 93 | 94 | // Flip the matrix left-to-right: 95 | mat.strides[ 1 ] *= -1; 96 | mat.offset = mat.length + mat.strides[ 0 ]; 97 | 98 | prev = mat.mget( idx ); 99 | mat.mset( idx, 999 ); 100 | 101 | m = mat.mget( idx ); 102 | actual = m.toString(); 103 | 104 | assert.notEqual( actual, prev.toString() ); 105 | assert.strictEqual( actual, expected, 'flipud' ); 106 | }); 107 | 108 | it( 'should set Matrix values located at specified linear indices using a callback', function test() { 109 | var idx, m, prev, actual, expected; 110 | 111 | idx = [ 14, 28, 47 ]; 112 | expected = '999,999,999'; 113 | 114 | prev = mat.mget( idx ); 115 | mat.mset( idx, set ); 116 | 117 | m = mat.mget( idx ); 118 | actual = m.toString(); 119 | 120 | assert.notEqual( actual, prev.toString() ); 121 | assert.strictEqual( actual, expected ); 122 | 123 | // Flip the matrix left-to-right: 124 | mat.strides[ 1 ] *= -1; 125 | mat.offset = mat.strides[ 0 ] - 1; 126 | 127 | prev = mat.mget( idx ); 128 | mat.mset( idx, set ); 129 | 130 | m = mat.mget( idx ); 131 | actual = m.toString(); 132 | 133 | assert.notEqual( actual, prev.toString() ); 134 | assert.strictEqual( actual, expected, 'fliplr' ); 135 | 136 | // Flip the matrix top-to-bottom: 137 | mat.strides[ 0 ] *= -1; 138 | mat.offset = mat.length - 1; 139 | 140 | prev = mat.mget( idx ); 141 | mat.mset( idx, set ); 142 | 143 | m = mat.mget( idx ); 144 | actual = m.toString(); 145 | 146 | assert.notEqual( actual, prev.toString() ); 147 | assert.strictEqual( actual, expected, 'fliplrud' ); 148 | 149 | // Flip the matrix left-to-right: 150 | mat.strides[ 1 ] *= -1; 151 | mat.offset = mat.length + mat.strides[ 0 ]; 152 | 153 | prev = mat.mget( idx ); 154 | mat.mset( idx, set ); 155 | 156 | m = mat.mget( idx ); 157 | actual = m.toString(); 158 | 159 | assert.notEqual( actual, prev.toString() ); 160 | assert.strictEqual( actual, expected, 'flipud' ); 161 | 162 | function set( d, i, j, k ) { 163 | assert.isTrue( i >= 0 ); 164 | assert.isTrue( j >= 0 ); 165 | assert.isTrue( k >= 0 ); 166 | return 999; 167 | } 168 | }); 169 | 170 | it( 'should set Matrix values located at specified linear indices using a callback and a provided context', function test() { 171 | var idx, m, prev, actual, expected; 172 | 173 | idx = [ 14, 28, 47 ]; 174 | expected = '999,999,999'; 175 | 176 | prev = mat.mget( idx ); 177 | mat.mset( idx, set, null ); 178 | 179 | m = mat.mget( idx ); 180 | actual = m.toString(); 181 | 182 | assert.notEqual( actual, prev.toString() ); 183 | assert.strictEqual( actual, expected ); 184 | 185 | // Flip the matrix left-to-right: 186 | mat.strides[ 1 ] *= -1; 187 | mat.offset = mat.strides[ 0 ] - 1; 188 | 189 | prev = mat.mget( idx ); 190 | mat.mset( idx, set, null ); 191 | 192 | m = mat.mget( idx ); 193 | actual = m.toString(); 194 | 195 | assert.notEqual( actual, prev.toString() ); 196 | assert.strictEqual( actual, expected, 'fliplr' ); 197 | 198 | // Flip the matrix top-to-bottom: 199 | mat.strides[ 0 ] *= -1; 200 | mat.offset = mat.length - 1; 201 | 202 | prev = mat.mget( idx ); 203 | mat.mset( idx, set, null ); 204 | 205 | m = mat.mget( idx ); 206 | actual = m.toString(); 207 | 208 | assert.notEqual( actual, prev.toString() ); 209 | assert.strictEqual( actual, expected, 'fliplrud' ); 210 | 211 | // Flip the matrix left-to-right: 212 | mat.strides[ 1 ] *= -1; 213 | mat.offset = mat.length + mat.strides[ 0 ]; 214 | 215 | prev = mat.mget( idx ); 216 | mat.mset( idx, set, null ); 217 | 218 | m = mat.mget( idx ); 219 | actual = m.toString(); 220 | 221 | assert.notEqual( actual, prev.toString() ); 222 | assert.strictEqual( actual, expected, 'flipud' ); 223 | 224 | function set() { 225 | /* jshint validthis:true */ 226 | assert.isNull( this ); 227 | return 999; 228 | } 229 | }); 230 | 231 | it( 'should set Matrix values located at specified linear indices to values from a different Matrix', function test() { 232 | var idx, m, vmat; 233 | 234 | idx = [ 5, 53, 23 ]; 235 | 236 | vmat = matrix( new Int32Array( [1,2,3] ), [1,3], 'int32' ); 237 | 238 | mat.mset( idx, vmat ); 239 | m = mat.mget( idx ); 240 | 241 | assert.strictEqual( m.toString(), '1,2,3' ); 242 | 243 | // Flip the value matrix top-to-bottom... 244 | vmat.offset = vmat.length - vmat.strides[ 0 ]; 245 | vmat.strides[ 0 ] *= -1; 246 | 247 | mat.mset( idx, vmat ); 248 | m = mat.mget( idx ); 249 | 250 | assert.strictEqual( m.toString(), '1,2,3', 'vmat flipud' ); 251 | 252 | // Flip the matrix top-to-bottom... 253 | mat.offset = mat.length - mat.strides[ 0 ]; 254 | mat.strides[ 0 ] *= -1; 255 | 256 | mat.mset( idx, vmat ); 257 | m = mat.mget( idx ); 258 | 259 | assert.strictEqual( m.toString(), '1,2,3', 'flipud' ); 260 | }); 261 | 262 | it( 'should set all rows and select columns', function test() { 263 | var prev, actual, expected; 264 | 265 | // Numeric value: 266 | prev = mat.mget( null, [1] ).toString(); 267 | mat.mset( null, [1], 5 ); 268 | 269 | actual = mat.mget( null, [1] ).toString(); 270 | expected = '5;5;5;5;5;5;5;5;5;5'; 271 | 272 | assert.notEqual( prev, actual ); 273 | assert.strictEqual( actual, expected ); 274 | 275 | // Flip left-to-right... 276 | mat.strides[ 1 ] *= -1; 277 | mat.offset = mat.strides[ 0 ] - 1; 278 | 279 | prev = mat.mget( null, [1] ).toString(); 280 | mat.mset( null, [1], 5 ); 281 | 282 | actual = mat.mget( null, [1] ).toString(); 283 | expected = '5;5;5;5;5;5;5;5;5;5'; 284 | 285 | assert.notEqual( prev, actual ); 286 | assert.strictEqual( actual, expected ); 287 | 288 | // Callback: 289 | prev = mat.mget( null, [9] ).toString(); 290 | mat.mset( null, [9], set ); 291 | 292 | actual = mat.mget( null, [9] ).toString(); 293 | expected = '20;20;20;20;20;20;20;20;20;20'; 294 | 295 | assert.notEqual( prev, actual ); 296 | assert.strictEqual( actual, expected ); 297 | 298 | // Callback with context: 299 | prev = mat.mget( null, [2] ).toString(); 300 | mat.mset( null, [2], set, null ); 301 | 302 | actual = mat.mget( null, [2] ).toString(); 303 | expected = '20;20;20;20;20;20;20;20;20;20'; 304 | 305 | assert.notEqual( prev, actual ); 306 | assert.strictEqual( actual, expected ); 307 | 308 | // Flip top-to-bottom: 309 | mat.strides[ 0 ] *= -1; 310 | mat.offset = mat.length - 1; 311 | 312 | prev = mat.mget( null, [8] ).toString(); 313 | mat.mset( null, [8], set ); 314 | 315 | actual = mat.mget( null, [8] ).toString(); 316 | expected = '20;20;20;20;20;20;20;20;20;20'; 317 | 318 | assert.notEqual( prev, actual ); 319 | assert.strictEqual( actual, expected ); 320 | 321 | // Matrix: 322 | prev = mat.mget( null, [3] ).toString(); 323 | mat.mset( null, [3], matrix( [10,1], 'int32' ) ); 324 | 325 | actual = mat.mget( null, [3] ).toString(); 326 | expected = '0;0;0;0;0;0;0;0;0;0'; 327 | 328 | assert.notEqual( prev, actual ); 329 | assert.strictEqual( actual, expected ); 330 | 331 | function set( d, i, j, k ) { 332 | assert.isTrue( i >= 0 ); 333 | assert.isTrue( j >= 0 ); 334 | assert.isTrue( k >= 0 ); 335 | return 20; 336 | } 337 | }); 338 | 339 | it( 'should set select rows and all columns', function test() { 340 | var prev, actual, expected; 341 | 342 | // Numeric value: 343 | prev = mat.mget( [1], null ).toString(); 344 | mat.mset( [1], null, 5 ); 345 | 346 | actual = mat.mget( [1], null ).toString(); 347 | expected = '5,5,5,5,5,5,5,5,5,5'; 348 | 349 | assert.notEqual( prev, actual ); 350 | assert.strictEqual( actual, expected ); 351 | 352 | // Flip left-to-right... 353 | mat.strides[ 1 ] *= -1; 354 | mat.offset = mat.strides[ 0 ] - 1; 355 | 356 | prev = mat.mget( null, [1] ).toString(); 357 | mat.mset( null, [1], 5 ); 358 | 359 | actual = mat.mget( null, [1] ).toString(); 360 | expected = '5;5;5;5;5;5;5;5;5;5'; 361 | 362 | assert.notEqual( prev, actual ); 363 | assert.strictEqual( actual, expected ); 364 | 365 | // Callback: 366 | prev = mat.mget( [9], null ).toString(); 367 | mat.mset( [9], null, set ); 368 | 369 | actual = mat.mget( [9], null ).toString(); 370 | expected = '20,20,20,20,20,20,20,20,20,20'; 371 | 372 | assert.notEqual( prev, actual ); 373 | assert.strictEqual( actual, expected ); 374 | 375 | // Callback with context: 376 | prev = mat.mget( [2], null ).toString(); 377 | mat.mset( [2], null, set, null ); 378 | 379 | actual = mat.mget( [2], null ).toString(); 380 | expected = '20,20,20,20,20,20,20,20,20,20'; 381 | 382 | assert.notEqual( prev, actual ); 383 | assert.strictEqual( actual, expected ); 384 | 385 | // Flip top-to-bottom: 386 | mat.strides[ 0 ] *= -1; 387 | mat.offset = mat.length - 1; 388 | 389 | prev = mat.mget( null, [8] ).toString(); 390 | mat.mset( null, [8], set ); 391 | 392 | actual = mat.mget( null, [8] ).toString(); 393 | expected = '20;20;20;20;20;20;20;20;20;20'; 394 | 395 | assert.notEqual( prev, actual ); 396 | assert.strictEqual( actual, expected ); 397 | 398 | // Matrix: 399 | prev = mat.mget( [3], null ).toString(); 400 | mat.mset( [3], null, matrix( [1,10], 'int32' ) ); 401 | 402 | actual = mat.mget( [3], null ).toString(); 403 | expected = '0,0,0,0,0,0,0,0,0,0'; 404 | 405 | assert.notEqual( prev, actual ); 406 | assert.strictEqual( actual, expected ); 407 | 408 | function set() { 409 | return 20; 410 | } 411 | }); 412 | 413 | it( 'should not dedupe indices', function test() { 414 | var mat1, i; 415 | 416 | i = 0; 417 | 418 | mat.mset( [1,1,1,1,2,2], set ); 419 | mat1 = mat.mget( [1,2] ); 420 | 421 | assert.strictEqual( mat1.toString(), '4,6' ); 422 | 423 | function set() { 424 | return ++i; 425 | } 426 | }); 427 | 428 | }); 429 | -------------------------------------------------------------------------------- /test/test.mset.js: -------------------------------------------------------------------------------- 1 | /* global require, describe, it, beforeEach */ 2 | 'use strict'; 3 | 4 | // MODULES // 5 | 6 | var chai = require( 'chai' ), 7 | matrix = require( './../lib' ), 8 | mset = require( './../lib/mset.js' ); 9 | 10 | 11 | // VARIABLES // 12 | 13 | var expect = chai.expect, 14 | assert = chai.assert; 15 | 16 | 17 | // TESTS // 18 | 19 | describe( 'matrix#mset', function tests() { 20 | 21 | var mat; 22 | 23 | beforeEach( function before() { 24 | var data = new Int32Array( 100 ); 25 | for ( var i = 0; i < data.length; i++ ) { 26 | data[ i ] = i; 27 | } 28 | mat = matrix( data, [10,10] ); 29 | }); 30 | 31 | it( 'should export a function', function test() { 32 | expect( mset ).to.be.a( 'function' ); 33 | }); 34 | 35 | it( 'should throw an error if provided a linear indices argument which is not a nonnegative integer array', function test() { 36 | var values = [ 37 | '5', 38 | 5, 39 | NaN, 40 | null, 41 | true, 42 | undefined, 43 | [], 44 | [1,-1], 45 | [1,Math.PI], 46 | {}, 47 | function(){} 48 | ]; 49 | 50 | for ( var i = 0; i < values.length; i++ ) { 51 | expect( badValue1( values[ i ] ) ).to.throw( TypeError ); 52 | expect( badValue2( values[ i ] ) ).to.throw( TypeError ); 53 | expect( badValue3( values[ i ] ) ).to.throw( TypeError ); 54 | expect( badValue4( values[ i ] ) ).to.throw( TypeError ); 55 | } 56 | function badValue1( value ) { 57 | return function() { 58 | mat.mset( value, 5 ); 59 | }; 60 | } 61 | function badValue2( value ) { 62 | return function() { 63 | mat.mset( value, function set() { 64 | return 0; 65 | }); 66 | }; 67 | } 68 | function badValue3( value ) { 69 | return function() { 70 | mat.mset( value, function set() { 71 | return 0; 72 | }, null ); 73 | }; 74 | } 75 | function badValue4( value ) { 76 | return function() { 77 | mat.mset( value, matrix( [1,1] ) ); 78 | }; 79 | } 80 | }); 81 | 82 | it( 'should throw an error if provided a row indices argument which is not a nonnegative integer array', function test() { 83 | var values = [ 84 | '5', 85 | 5, 86 | NaN, 87 | // null, // allowed 88 | true, 89 | undefined, 90 | [], 91 | [1,-1], 92 | [1,Math.PI], 93 | {}, 94 | function(){} 95 | ]; 96 | 97 | for ( var i = 0; i < values.length; i++ ) { 98 | expect( badValue1( values[ i ] ) ).to.throw( TypeError ); 99 | expect( badValue2( values[ i ] ) ).to.throw( TypeError ); 100 | expect( badValue3( values[ i ] ) ).to.throw( TypeError ); 101 | expect( badValue4( values[ i ] ) ).to.throw( TypeError ); 102 | } 103 | function badValue1( value ) { 104 | return function() { 105 | mat.mset( value, [1,2,3], 5 ); 106 | }; 107 | } 108 | function badValue2( value ) { 109 | return function() { 110 | mat.mset( value, [1,2,3], function set() { 111 | return 0; 112 | }); 113 | }; 114 | } 115 | function badValue3( value ) { 116 | return function() { 117 | mat.mset( value, [1,2,3], function set() { 118 | return 0; 119 | }, null ); 120 | }; 121 | } 122 | function badValue4( value ) { 123 | return function() { 124 | mat.mset( value, [1,2,3], matrix( [1,3] ) ); 125 | }; 126 | } 127 | }); 128 | 129 | it( 'should throw an error if provided a column indices argument which is not a nonnegative integer array', function test() { 130 | var values = [ 131 | '5', 132 | 5, 133 | NaN, 134 | // null, // allowed 135 | true, 136 | undefined, 137 | [], 138 | [1,-1], 139 | [1,Math.PI], 140 | {}, 141 | // function(){} // 2nd arg allowed (callback) 142 | ]; 143 | 144 | for ( var i = 0; i < values.length; i++ ) { 145 | expect( badValue1( values[ i ] ) ).to.throw( TypeError ); 146 | expect( badValue2( values[ i ] ) ).to.throw( TypeError ); 147 | expect( badValue3( values[ i ] ) ).to.throw( TypeError ); 148 | expect( badValue4( values[ i ] ) ).to.throw( TypeError ); 149 | } 150 | function badValue1( value ) { 151 | return function() { 152 | mat.mset( [1,2,3], value, 5 ); 153 | }; 154 | } 155 | function badValue2( value ) { 156 | return function() { 157 | mat.mset( [1,2,3], value, function set() { 158 | return 0; 159 | }); 160 | }; 161 | } 162 | function badValue3( value ) { 163 | return function() { 164 | mat.mset( [1,2,3], value, function set() { 165 | return 0; 166 | }, null ); 167 | }; 168 | } 169 | function badValue4( value ) { 170 | return function() { 171 | mat.mset( [1,2,3], value, matrix( [1,3] ) ); 172 | }; 173 | } 174 | }); 175 | 176 | it( 'should throw an error if provided 4 arguments and the third argument is not a function', function test() { 177 | var values = [ 178 | '5', 179 | 5, 180 | NaN, 181 | null, 182 | true, 183 | undefined, 184 | [], 185 | {} 186 | ]; 187 | 188 | for ( var i = 0; i < values.length; i++ ) { 189 | expect( badValue( values[ i ] ) ).to.throw( TypeError ); 190 | } 191 | function badValue( value ) { 192 | return function() { 193 | mat.mset( [1,2,3], [1,2,3], value, null ); 194 | }; 195 | } 196 | }); 197 | 198 | it( 'should throw an error if the number of linear indices and the number of matrix elements do not agree', function test() { 199 | expect( badValue ).to.throw( Error ); 200 | function badValue() { 201 | mat.mset( [1,2,3], matrix( [10,1] ) ); 202 | } 203 | }); 204 | 205 | it( 'should throw an error if dimensions defined by row and column indices do not agree with value matrix dimensions', function test() { 206 | expect( badValue1 ).to.throw( Error ); 207 | expect( badValue2 ).to.throw( Error ); 208 | function badValue1() { 209 | mat.mset( [1,2,3], [1,2,3], matrix( [1,3] ) ); 210 | } 211 | function badValue2() { 212 | mat.mset( [1,2,3], [1,2,3], matrix( [3,1] ) ); 213 | } 214 | }); 215 | 216 | it( 'should set Matrix values located at specified linear indices to a numeric value', function test() { 217 | var idx, m, prev, actual, expected; 218 | 219 | idx = [ 14, 28, 47 ]; 220 | expected = '999,999,999'; 221 | 222 | prev = mat.mget( idx ); 223 | mat.mset( idx, 999 ); 224 | 225 | m = mat.mget( idx ); 226 | actual = m.toString(); 227 | 228 | assert.notEqual( actual, prev.toString() ); 229 | assert.strictEqual( actual, expected ); 230 | 231 | // Flip the matrix left-to-right: 232 | mat.strides[ 1 ] *= -1; 233 | mat.offset = mat.strides[ 0 ] - 1; 234 | 235 | prev = mat.mget( idx ); 236 | mat.mset( idx, 999 ); 237 | 238 | m = mat.mget( idx ); 239 | actual = m.toString(); 240 | 241 | assert.notEqual( actual, prev.toString() ); 242 | assert.strictEqual( actual, expected, 'fliplr' ); 243 | 244 | // Flip the matrix top-to-bottom: 245 | mat.strides[ 0 ] *= -1; 246 | mat.offset = mat.length - 1; 247 | 248 | prev = mat.mget( idx ); 249 | mat.mset( idx, 999 ); 250 | 251 | m = mat.mget( idx ); 252 | actual = m.toString(); 253 | 254 | assert.notEqual( actual, prev.toString() ); 255 | assert.strictEqual( actual, expected, 'fliplrud' ); 256 | 257 | // Flip the matrix left-to-right: 258 | mat.strides[ 1 ] *= -1; 259 | mat.offset = mat.length + mat.strides[ 0 ]; 260 | 261 | prev = mat.mget( idx ); 262 | mat.mset( idx, 999 ); 263 | 264 | m = mat.mget( idx ); 265 | actual = m.toString(); 266 | 267 | assert.notEqual( actual, prev.toString() ); 268 | assert.strictEqual( actual, expected, 'flipud' ); 269 | }); 270 | 271 | it( 'should set Matrix values located at specified linear indices to NaN', function test() { 272 | var idx, mat, m, prev, actual, expected; 273 | 274 | mat = matrix( [1,2,3,4,5,6,7,8,9], [3,3], 'float64' ); 275 | 276 | idx = [ 2, 5, 8 ]; 277 | expected = 'NaN,NaN,NaN'; 278 | 279 | prev = mat.mget( idx ); 280 | mat.mset( idx, NaN ); 281 | 282 | m = mat.mget( idx ); 283 | actual = m.toString(); 284 | 285 | assert.notEqual( actual, prev.toString() ); 286 | assert.strictEqual( actual, expected ); 287 | 288 | // For integer-typed matrices, elements are set to 0: 289 | mat = matrix( [1,2,3,4,5,6,7,8,9], [3,3], 'int32' ); 290 | 291 | idx = [ 2, 5, 8 ]; 292 | expected = '0,0,0'; 293 | 294 | prev = mat.mget( idx ); 295 | mat.mset( idx, NaN ); 296 | 297 | m = mat.mget( idx ); 298 | actual = m.toString(); 299 | 300 | assert.notEqual( actual, prev.toString() ); 301 | assert.strictEqual( actual, expected ); 302 | 303 | }); 304 | 305 | it( 'should set Matrix values located at specified linear indices using a callback', function test() { 306 | var idx, m, prev, actual, expected; 307 | 308 | idx = [ 14, 28, 47 ]; 309 | expected = '999,999,999'; 310 | 311 | prev = mat.mget( idx ); 312 | mat.mset( idx, set ); 313 | 314 | m = mat.mget( idx ); 315 | actual = m.toString(); 316 | 317 | assert.notEqual( actual, prev.toString() ); 318 | assert.strictEqual( actual, expected ); 319 | 320 | // Flip the matrix left-to-right: 321 | mat.strides[ 1 ] *= -1; 322 | mat.offset = mat.strides[ 0 ] - 1; 323 | 324 | prev = mat.mget( idx ); 325 | mat.mset( idx, set ); 326 | 327 | m = mat.mget( idx ); 328 | actual = m.toString(); 329 | 330 | assert.notEqual( actual, prev.toString() ); 331 | assert.strictEqual( actual, expected, 'fliplr' ); 332 | 333 | // Flip the matrix top-to-bottom: 334 | mat.strides[ 0 ] *= -1; 335 | mat.offset = mat.length - 1; 336 | 337 | prev = mat.mget( idx ); 338 | mat.mset( idx, set ); 339 | 340 | m = mat.mget( idx ); 341 | actual = m.toString(); 342 | 343 | assert.notEqual( actual, prev.toString() ); 344 | assert.strictEqual( actual, expected, 'fliplrud' ); 345 | 346 | // Flip the matrix left-to-right: 347 | mat.strides[ 1 ] *= -1; 348 | mat.offset = mat.length + mat.strides[ 0 ]; 349 | 350 | prev = mat.mget( idx ); 351 | mat.mset( idx, set ); 352 | 353 | m = mat.mget( idx ); 354 | actual = m.toString(); 355 | 356 | assert.notEqual( actual, prev.toString() ); 357 | assert.strictEqual( actual, expected, 'flipud' ); 358 | 359 | function set( d, i, j, k ) { 360 | assert.isTrue( i >= 0 ); 361 | assert.isTrue( j >= 0 ); 362 | assert.isTrue( k >= 0 ); 363 | return 999; 364 | } 365 | }); 366 | 367 | it( 'should set Matrix values located at specified linear indices using a callback and a provided context', function test() { 368 | var idx, m, prev, actual, expected; 369 | 370 | idx = [ 14, 28, 47 ]; 371 | expected = '999,999,999'; 372 | 373 | prev = mat.mget( idx ); 374 | mat.mset( idx, set, null ); 375 | 376 | m = mat.mget( idx ); 377 | actual = m.toString(); 378 | 379 | assert.notEqual( actual, prev.toString() ); 380 | assert.strictEqual( actual, expected ); 381 | 382 | // Flip the matrix left-to-right: 383 | mat.strides[ 1 ] *= -1; 384 | mat.offset = mat.strides[ 0 ] - 1; 385 | 386 | prev = mat.mget( idx ); 387 | mat.mset( idx, set, null ); 388 | 389 | m = mat.mget( idx ); 390 | actual = m.toString(); 391 | 392 | assert.notEqual( actual, prev.toString() ); 393 | assert.strictEqual( actual, expected, 'fliplr' ); 394 | 395 | // Flip the matrix top-to-bottom: 396 | mat.strides[ 0 ] *= -1; 397 | mat.offset = mat.length - 1; 398 | 399 | prev = mat.mget( idx ); 400 | mat.mset( idx, set, null ); 401 | 402 | m = mat.mget( idx ); 403 | actual = m.toString(); 404 | 405 | assert.notEqual( actual, prev.toString() ); 406 | assert.strictEqual( actual, expected, 'fliplrud' ); 407 | 408 | // Flip the matrix left-to-right: 409 | mat.strides[ 1 ] *= -1; 410 | mat.offset = mat.length + mat.strides[ 0 ]; 411 | 412 | prev = mat.mget( idx ); 413 | mat.mset( idx, set, null ); 414 | 415 | m = mat.mget( idx ); 416 | actual = m.toString(); 417 | 418 | assert.notEqual( actual, prev.toString() ); 419 | assert.strictEqual( actual, expected, 'flipud' ); 420 | 421 | function set() { 422 | /* jshint validthis:true */ 423 | assert.isNull( this ); 424 | return 999; 425 | } 426 | }); 427 | 428 | it( 'should set Matrix values located at specified linear indices to values from a different Matrix', function test() { 429 | var idx, m, vmat; 430 | 431 | idx = [ 5, 53, 23 ]; 432 | 433 | vmat = matrix( new Int32Array( [1,2,3] ), [1,3], 'int32' ); 434 | 435 | mat.mset( idx, vmat ); 436 | m = mat.mget( idx ); 437 | 438 | assert.strictEqual( m.toString(), '1,2,3' ); 439 | 440 | // Flip the value matrix top-to-bottom... 441 | vmat.offset = vmat.length - vmat.strides[ 0 ]; 442 | vmat.strides[ 0 ] *= -1; 443 | 444 | mat.mset( idx, vmat ); 445 | m = mat.mget( idx ); 446 | 447 | assert.strictEqual( m.toString(), '1,2,3', 'vmat flipud' ); 448 | 449 | // Flip the matrix top-to-bottom... 450 | mat.offset = mat.length - mat.strides[ 0 ]; 451 | mat.strides[ 0 ] *= -1; 452 | 453 | mat.mset( idx, vmat ); 454 | m = mat.mget( idx ); 455 | 456 | assert.strictEqual( m.toString(), '1,2,3', 'flipud' ); 457 | }); 458 | 459 | it( 'should set values located at specified linear indices and ignore any indices which are out-of-bounds', function test() { 460 | var mat1; 461 | 462 | // Numeric value: 463 | mat.mset( [14,28,9999,47], 5 ); 464 | mat1 = mat.mget( [14,28,9999,47] ); 465 | assert.strictEqual( mat1.toString(), '5,5,5' ); 466 | 467 | // Callback: 468 | mat.mset( [14,28,745,47], set ); 469 | mat1 = mat.mget( [14,28,745,47] ); 470 | assert.strictEqual( mat1.toString(), '25,25,25' ); 471 | 472 | // Callback with context: 473 | mat.mset( [14,1092,47,28], set, null ); 474 | mat1 = mat.mget( [14,1092,47,28] ); 475 | assert.strictEqual( mat1.toString(), '25,25,25' ); 476 | 477 | // Matrix: 478 | mat.mset( [1534,14,28,9999,47], matrix( [1,5], 'int32' ) ); 479 | mat1 = mat.mget( [1534,14,28,9999,47] ); 480 | assert.strictEqual( mat1.toString(), '0,0,0' ); 481 | 482 | function set() { 483 | return 25; 484 | } 485 | }); 486 | 487 | it( 'should set all rows and select columns', function test() { 488 | var prev, actual, expected; 489 | 490 | // Numeric value: 491 | prev = mat.mget( null, [1] ).toString(); 492 | mat.mset( null, [1], 5 ); 493 | 494 | actual = mat.mget( null, [1] ).toString(); 495 | expected = '5;5;5;5;5;5;5;5;5;5'; 496 | 497 | assert.notEqual( prev, actual ); 498 | assert.strictEqual( actual, expected ); 499 | 500 | // Flip left-to-right... 501 | mat.strides[ 1 ] *= -1; 502 | mat.offset = mat.strides[ 0 ] - 1; 503 | 504 | prev = mat.mget( null, [1] ).toString(); 505 | mat.mset( null, [1], 5 ); 506 | 507 | actual = mat.mget( null, [1] ).toString(); 508 | expected = '5;5;5;5;5;5;5;5;5;5'; 509 | 510 | assert.notEqual( prev, actual ); 511 | assert.strictEqual( actual, expected ); 512 | 513 | // Callback: 514 | prev = mat.mget( null, [9] ).toString(); 515 | mat.mset( null, [9], set ); 516 | 517 | actual = mat.mget( null, [9] ).toString(); 518 | expected = '20;20;20;20;20;20;20;20;20;20'; 519 | 520 | assert.notEqual( prev, actual ); 521 | assert.strictEqual( actual, expected ); 522 | 523 | // Callback with context: 524 | prev = mat.mget( null, [2] ).toString(); 525 | mat.mset( null, [2], set, null ); 526 | 527 | actual = mat.mget( null, [2] ).toString(); 528 | expected = '20;20;20;20;20;20;20;20;20;20'; 529 | 530 | assert.notEqual( prev, actual ); 531 | assert.strictEqual( actual, expected ); 532 | 533 | // Flip top-to-bottom: 534 | mat.strides[ 0 ] *= -1; 535 | mat.offset = mat.length - 1; 536 | 537 | prev = mat.mget( null, [8] ).toString(); 538 | mat.mset( null, [8], set ); 539 | 540 | actual = mat.mget( null, [8] ).toString(); 541 | expected = '20;20;20;20;20;20;20;20;20;20'; 542 | 543 | assert.notEqual( prev, actual ); 544 | assert.strictEqual( actual, expected ); 545 | 546 | // Matrix: 547 | prev = mat.mget( null, [3] ).toString(); 548 | mat.mset( null, [3], matrix( [10,1], 'int32' ) ); 549 | 550 | actual = mat.mget( null, [3] ).toString(); 551 | expected = '0;0;0;0;0;0;0;0;0;0'; 552 | 553 | assert.notEqual( prev, actual ); 554 | assert.strictEqual( actual, expected ); 555 | 556 | function set( d, i, j, k ) { 557 | assert.isTrue( i >= 0 ); 558 | assert.isTrue( j >= 0 ); 559 | assert.isTrue( k >= 0 ); 560 | return 20; 561 | } 562 | }); 563 | 564 | it( 'should set select rows and all columns', function test() { 565 | var prev, actual, expected; 566 | 567 | // Numeric value: 568 | prev = mat.mget( [1], null ).toString(); 569 | mat.mset( [1], null, 5 ); 570 | 571 | actual = mat.mget( [1], null ).toString(); 572 | expected = '5,5,5,5,5,5,5,5,5,5'; 573 | 574 | assert.notEqual( prev, actual ); 575 | assert.strictEqual( actual, expected ); 576 | 577 | // Flip left-to-right... 578 | mat.strides[ 1 ] *= -1; 579 | mat.offset = mat.strides[ 0 ] - 1; 580 | 581 | prev = mat.mget( null, [1] ).toString(); 582 | mat.mset( null, [1], 5 ); 583 | 584 | actual = mat.mget( null, [1] ).toString(); 585 | expected = '5;5;5;5;5;5;5;5;5;5'; 586 | 587 | assert.notEqual( prev, actual ); 588 | assert.strictEqual( actual, expected ); 589 | 590 | // Callback: 591 | prev = mat.mget( [9], null ).toString(); 592 | mat.mset( [9], null, set ); 593 | 594 | actual = mat.mget( [9], null ).toString(); 595 | expected = '20,20,20,20,20,20,20,20,20,20'; 596 | 597 | assert.notEqual( prev, actual ); 598 | assert.strictEqual( actual, expected ); 599 | 600 | // Callback with context: 601 | prev = mat.mget( [2], null ).toString(); 602 | mat.mset( [2], null, set, null ); 603 | 604 | actual = mat.mget( [2], null ).toString(); 605 | expected = '20,20,20,20,20,20,20,20,20,20'; 606 | 607 | assert.notEqual( prev, actual ); 608 | assert.strictEqual( actual, expected ); 609 | 610 | // Flip top-to-bottom: 611 | mat.strides[ 0 ] *= -1; 612 | mat.offset = mat.length - 1; 613 | 614 | prev = mat.mget( null, [8] ).toString(); 615 | mat.mset( null, [8], set ); 616 | 617 | actual = mat.mget( null, [8] ).toString(); 618 | expected = '20;20;20;20;20;20;20;20;20;20'; 619 | 620 | assert.notEqual( prev, actual ); 621 | assert.strictEqual( actual, expected ); 622 | 623 | // Matrix: 624 | prev = mat.mget( [3], null ).toString(); 625 | mat.mset( [3], null, matrix( [1,10], 'int32' ) ); 626 | 627 | actual = mat.mget( [3], null ).toString(); 628 | expected = '0,0,0,0,0,0,0,0,0,0'; 629 | 630 | assert.notEqual( prev, actual ); 631 | assert.strictEqual( actual, expected ); 632 | 633 | function set() { 634 | return 20; 635 | } 636 | }); 637 | 638 | it( 'should ignore out-of-bounds row and column indices', function test() { 639 | var prev, mat1, actual; 640 | 641 | // Number: 642 | prev = mat.mget( [2,4], [8,7,6] ).toString(); 643 | mat.mset( [2,999,4], [8,7,6], 5 ); 644 | mat1 = mat.mget( [2,999,4], [8,7,6] ); 645 | actual = mat1.toString(); 646 | 647 | assert.notEqual( prev, actual ); 648 | assert.strictEqual( mat.mget( [999], [8,7,6] ).length, 0 ); 649 | 650 | // Callback: 651 | prev = mat.mget( [2,4], [8,7,6] ).toString(); 652 | mat.mset( [2,1999,4], [8,7,6], set ); 653 | mat1 = mat.mget( [2,1999,4], [8,7,6] ); 654 | actual = mat1.toString(); 655 | 656 | assert.notEqual( prev, actual ); 657 | assert.strictEqual( mat.mget( [1999], [8,7,6] ).length, 0 ); 658 | 659 | // Callback with context: 660 | prev = mat.mget( [3,2,1], [8,7,6] ).toString(); 661 | mat.mset( [3,2999,2,1], [8,7,6], set, null ); 662 | mat1 = mat.mget( [3,2999,2,1], [8,7,6] ); 663 | actual = mat1.toString(); 664 | 665 | assert.notEqual( prev, actual ); 666 | assert.strictEqual( mat.mget( [2999], [8,7,6] ).length, 0 ); 667 | 668 | // Matrix: 669 | prev = mat.mget( [2,4], [8,7,6] ).toString(); 670 | mat.mset( [2,10999,4], [8,7,6], matrix( [2,3], 'int32' ) ); 671 | mat1 = mat.mget( [2,10999,4], [8,7,6] ); 672 | actual = mat1.toString(); 673 | 674 | assert.notEqual( prev, actual ); 675 | assert.strictEqual( mat.mget( [10999], [8,7,6] ).length, 0 ); 676 | 677 | function set() { 678 | return 25; 679 | } 680 | }); 681 | 682 | it( 'should not dedupe indices', function test() { 683 | var mat1, i; 684 | 685 | i = 0; 686 | 687 | mat.mset( [1,1,1,1,2,2], set ); 688 | mat1 = mat.mget( [1,2] ); 689 | 690 | assert.strictEqual( mat1.toString(), '4,6' ); 691 | 692 | function set() { 693 | return ++i; 694 | } 695 | }); 696 | 697 | }); 698 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Matrix 2 | === 3 | [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependencies][dependencies-image]][dependencies-url] 4 | 5 | > Matrices. 6 | 7 | Sponsor 8 | 9 | This module exports a [`Matrix`][matrix] data structure for efficient storage and computation of numeric values. The data structure provides an interface for accessing and modifying one or more stored values. Matrices find common use in linear algebra, numerical analysis, image manipulation, machine learning, and data processing. 10 | 11 | --- 12 | 1. [Installation](#installation) 13 | 1. [Usage](#usage) 14 | - [Matrix](#matrix) 15 | * [matrix()](#matrix) 16 | - [Properties](#properties) 17 | * [dtype](#matrix-dtype) 18 | * [ndims](#matrix-ndims) 19 | * [shape](#matrix-shape) 20 | * [offset](#matrix-offset) 21 | * [strides](#matrix-strides) 22 | * [length](#matrix-length) 23 | * [nbytes](#matrix-nbytes) 24 | * [data](#matrix-data) 25 | - [Methods](#methods) 26 | * [Set](#set-methods) 27 | - [Matrix.prototype.set()](#matrix-set) 28 | - [Matrix.prototype.iset()](#matrix-iset) 29 | - [Matrix.prototype.mset()](#matrix-mset) 30 | - [Matrix.prototype.sset()](#matrix-sset) 31 | * [Get](#get-methods) 32 | - [Matrix.prototype.get()](#matrix-get) 33 | - [Matrix.prototype.iget()](#matrix-iget) 34 | - [Matrix.prototype.mget()](#matrix-mget) 35 | - [Matrix.prototype.sget()](#matrix-sget) 36 | * [Accessor](#accessor-methods) 37 | - [Matrix.prototype.toString()](#matrix-tostring) 38 | - [Matrix.prototype.toJSON()](#matrix-tojson) 39 | - [Constructor](#matrix-constructor) 40 | - [Raw](#raw) 41 | * [matrix.raw()](#matrix-raw) 42 | 1. [Notes](#notes) 43 | - [Linear Indexing](#linear-indexing) 44 | 1. [Examples](#examples) 45 | 1. [Tests](#tests) 46 | - [Unit](#unit) 47 | - [Coverage](#test-coverage) 48 | 1. [License](#license) 49 | 50 | --- 51 | ## Installation 52 | 53 | ``` bash 54 | $ npm install dstructs-matrix 55 | ``` 56 | 57 | For use in the browser, use [browserify][browserify]. 58 | 59 | 60 | ## Usage 61 | 62 | ``` javascript 63 | var matrix = require( 'dstructs-matrix' ); 64 | ``` 65 | 66 | 67 | #### matrix( [data,] shape[, dtype] ) 68 | 69 | Creates a new `Matrix` having a specified `shape` (dimensions => `[rows,cols]`). 70 | 71 | ``` javascript 72 | var mat = matrix( [3,2] ); 73 | /* 74 | [ 0 0 75 | 0 0 76 | 0 0 ] 77 | */ 78 | ``` 79 | 80 | By default, the matrix elements are floating-point 64-bit numbers (`float64`). To specify a different data type, provide a `dtype`. 81 | 82 | ``` javascript 83 | var mat = matrix( [2,2], 'int8' ); 84 | /* 85 | [ 0 0 86 | 0 0 ] 87 | */ 88 | ``` 89 | 90 | The following `dtypes` are accepted: 91 | 92 | * `int8` 93 | * `uint8` 94 | * `uint8_clamped` 95 | * `int16` 96 | * `uint16` 97 | * `int32` 98 | * `uint32` 99 | * `float32` 100 | * `float64` 101 | 102 | 103 | If a __linear__ `numeric array` is not provided, the function initializes a __zero-filled__ matrix. To initialize a matrix, provide a typed input `data` array, whose length matches the specified `shape`. 104 | 105 | ``` javascript 106 | var data = new Int8Array( 6 ); 107 | 108 | for ( var i = 0; i < data.length; i++ ) { 109 | data[ i ] = i; 110 | } 111 | 112 | var mat = matrix( data, [2,3] ); // 2*3 = 6 113 | /* 114 | [ 0 1 2 115 | 3 4 5 ] 116 | */ 117 | ``` 118 | 119 | To cast an input `data` array to a different data type, provide a `dtype`. 120 | 121 | ``` javascript 122 | var mat = matrix( data, [2,2], 'uint32' ); 123 | /* 124 | [ 0 1 125 | 2 3 ] 126 | */ 127 | ``` 128 | 129 | If provided an `Array` instead of a typed array and no `dtype` is specified, the input `data` array is [cast][cast-arrays] to `float64`. 130 | 131 | ``` javascript 132 | var data = [ 10, 20, 30, 40, 50, 60 ]; 133 | 134 | var mat = matrix( data, [3,2] ); 135 | /* 136 | [ 10 20 137 | 30 40 138 | 50 60 ] 139 | */ 140 | 141 | var dtype = mat.dtype; 142 | // returns 'float64' 143 | ``` 144 | 145 | 146 | --- 147 | ## Properties 148 | 149 | A `Matrix` has the following properties... 150 | 151 | 152 | 153 | #### dtype 154 | 155 | A __read-only__ property returning the underlying storage data type. 156 | 157 | ``` javascript 158 | var dtype = mat.dtype; 159 | // returns 160 | ``` 161 | 162 | 163 | #### ndims 164 | 165 | A __read-only__ property returning the number of dimensions. 166 | 167 | ``` javascript 168 | var ndims = mat.ndims; 169 | // returns 2 170 | ``` 171 | 172 | 173 | #### shape 174 | 175 | A __read-only__ property returning the matrix `shape`. 176 | 177 | ``` javascript 178 | var shape = mat.shape; 179 | // returns [...] 180 | ``` 181 | 182 | 183 | #### offset 184 | 185 | A property returning the `offset` used to index into the underlying data store. 186 | 187 | ``` javascript 188 | var offset = mat.offset; 189 | // returns 0 190 | ``` 191 | 192 | By default, the `offset` is `0`. While not __read-only__, most consumers should treat the `offset` as a __read-only__ property. 193 | 194 | 195 | 196 | #### strides 197 | 198 | A __read-only__ property returning the `strides` used to index into the underlying data store. 199 | 200 | ``` javascript 201 | var strides = mat.strides; 202 | // returns [...] 203 | ``` 204 | 205 | While not __frozen__, most consumers should treat the `strides` elements as __read-only__ elements. 206 | 207 | 208 | 209 | #### length 210 | 211 | A __read-only__ property returning the matrix `length`; i.e., how many elements are in the `Matrix`, similar to [`Array#length`][array-length]. 212 | 213 | ``` javascript 214 | var len = mat.length; 215 | // returns 216 | ``` 217 | 218 | __Note__: while a `Matrix` has a `length` property, a `Matrix` should __not__ be considered `array-like`, as `array` indexing with __not__ work as expected. 219 | 220 | ``` javascript 221 | var data = new Float32Array( 10 ); 222 | 223 | var mat = matrix( data, [1,10] ); 224 | /* 225 | [ 0 0 0 0 0 0 0 0 0 0 ] 226 | */ 227 | 228 | var value = mat.get( 1, 3 ); 229 | // returns 0 230 | 231 | value = mat[ 3 ]; 232 | // returns undefined 233 | ``` 234 | 235 | 236 | #### nbytes 237 | 238 | A __read-only__ property returning the number of bytes consumed by the `Matrix` elements. 239 | 240 | ``` javascript 241 | var nbytes = mat.nbytes; 242 | // returns 243 | ``` 244 | 245 | 246 | #### data 247 | 248 | A __read-only__ property pointing to the underlying storage array. 249 | 250 | ``` javascript 251 | var data = mat.data; 252 | // returns 253 | ``` 254 | 255 | --- 256 | ## Methods 257 | 258 | A `Matrix` has the following methods... 259 | 260 | 261 | ### Set Methods 262 | 263 | These methods mutate a `Matrix`: 264 | 265 | 266 | #### Matrix.prototype.set( i, j, value ) 267 | 268 | Sets a `Matrix` element located at a row and column index. 269 | 270 | ``` javascript 271 | mat.set( 3, 1, 20 ); 272 | /* 273 | [ 0 1 274 | 2 3 275 | 4 5 276 | 6 20 277 | 8 9 ] 278 | */ 279 | ``` 280 | 281 | Set methods return the `Matrix` instance and are thus chainable. 282 | 283 | ``` javascript 284 | mat 285 | .set( 3, 1, 21 ) 286 | .set( 3, 1, 22 ) 287 | .set( 3, 1, 23 ) 288 | .set( 3, 1, 24 ) 289 | .get( 3, 1 ); 290 | // returns 24 291 | ``` 292 | 293 | __Note__: out-of-bounds row and column indices will silently fail. 294 | 295 | 296 | 297 | #### Matrix.prototype.iset( index, value ) 298 | 299 | Sets a `Matrix` element located at a specified [`index`](#linear-indexing). If `index < 0`, the index refers to a position relative to the `Matrix` length, where `index = -1` corresponds to the last element. 300 | 301 | ``` javascript 302 | mat.iset( 7, 25 ); 303 | /* 304 | [ 0 1 305 | 2 3 306 | 4 5 307 | 6 25 308 | 8 9 ] 309 | */ 310 | 311 | mat.iset( -3, 20 ); 312 | /* 313 | [ 0 1 314 | 2 3 315 | 4 5 316 | 6 20 317 | 8 9 ] 318 | */ 319 | ``` 320 | 321 | __Note__: out-of-bounds indices will silently fail. 322 | 323 | 324 | 325 | #### Matrix.prototype.mset( idx[, cols], value[, thisArg] ) 326 | 327 | Sets multiple `Matrix` elements. If provided a single `array`, `idx` is treated as an `array` of [linear indices](#linear-indexing). The `value` argument may be either a `number` primitive, a `Matrix` containing values to set, or a callback `function`. 328 | 329 | ``` javascript 330 | var data = new Int8Array( 10*10 ); 331 | 332 | for ( var i = 0; i < data.length; i++ ) { 333 | data[ i ] = i; 334 | } 335 | // Create a 10x10 matrix: 336 | var mat = matrix( data, [10,10] ); 337 | 338 | var submat = mat.mget( [0,2,4], [1,4,5] ); 339 | /* 340 | [ 1 4 5 341 | 21 24 25 342 | 41 44 45 ] 343 | */ 344 | 345 | mat.mset( [1,4,5,21,24,25,41,44,45], 5 ); 346 | 347 | submat = mat.mget( [0,2,4], [1,4,5] ); 348 | /* 349 | [ 5 5 5 350 | 5 5 5 351 | 5 5 5 ] 352 | */ 353 | 354 | var zeros = matrix( [1,3], 'int8' ); 355 | /* 356 | [ 0 0 0 ] 357 | */ 358 | 359 | mat.mset( [2], [1,4,5], zeros ); 360 | 361 | submat = mat.mget( [0,2,4], [1,4,5] ); 362 | /* 363 | [ 5 5 5 364 | 0 0 0 365 | 5 5 5 ] 366 | */ 367 | ``` 368 | 369 | A callback is provided four arguments: 370 | * __d__: current value 371 | * __i__: row index 372 | * __j__: column index 373 | * __idx__: linear index 374 | 375 | and is __expected__ to return a `number` primitive or a value which can be cast to a `number` primitive. 376 | 377 | ``` javascript 378 | function set( d, i, j, idx ) { 379 | return '' + j + i; 380 | } 381 | 382 | mat.mset( [0], [1,4,5], set ); 383 | 384 | mat.mget( [0,2,4], [1,4,5] ); 385 | /* 386 | [ 10 40 50 387 | 0 0 0 388 | 5 5 5 ] 389 | */ 390 | ``` 391 | 392 | By default, the callback `this` context is set to the `Matrix` instance. To specify a different `this` context, provide a `thisArg`. 393 | 394 | ``` javascript 395 | function set( d, i, j, idx ) { 396 | console.log( this ); 397 | // returns null 398 | return '' + j + i; 399 | } 400 | 401 | mat.mset( [0], [1,4,5], set, null ); 402 | ``` 403 | 404 | 405 | __Notes__: 406 | * Negative indices are __not__ permitted. 407 | * Out-of-bounds row and column indices will silently fail. 408 | * Values which are set are cast to the target `Matrix` data type. 409 | * A value `Matrix` must have dimensions which match the submatrix defined by row and column indices. 410 | * If linear indices are provided, a value `Matrix` must have a `length` equal to the number of provided indices. 411 | 412 | 413 | 414 | 415 | #### Matrix.prototype.sset( subsequence, value[, thisArg] ) 416 | 417 | Sets `Matrix` elements according to a specified [`subsequence`][indexspace]. The `subsequence` must specify __both__ row and column subsequences; e.g., `'3:7,5:9'`, where `3:7` corresponds to row indices `3,4,5,6` and `5:9` corresponds to column indices `5,6,7,8`. The second argument may be either a `number` primitive, a `Matrix` containing values to set, or a callback `function`. 418 | 419 | ``` javascript 420 | var data = new Float32Array( 10*10 ); 421 | 422 | for ( var i = 0; i < data.length; i++ ) { 423 | data[ i ] = i; 424 | } 425 | // Create a 10x10 matrix: 426 | var mat = matrix( data, [10,10] ); 427 | 428 | var submat = mat.sget( '3:7,5:9' ); 429 | /* 430 | [ 35 36 37 38 431 | 45 46 47 48 432 | 55 56 57 58 433 | 65 66 67 68 ] 434 | */ 435 | 436 | var zeros = matrix( [2,2], 'float32' ); 437 | /* 438 | [ 0 0 439 | 0 0 ] 440 | */ 441 | 442 | mat.sset( '4:6,6:8', zeros ); 443 | 444 | submat = mat.sget( '3:7,5:9' ); 445 | /* 446 | [ 35 36 37 38 447 | 45 0 0 48 448 | 55 0 0 58 449 | 65 66 67 68 ] 450 | */ 451 | ``` 452 | 453 | A callback is provided four arguments: 454 | * __d__: value at a subsequence index 455 | * __i__: row index 456 | * __j__: column index 457 | * __idx__: linear index 458 | 459 | and is __expected__ to return a `number` primitive or a value which can be cast to a `number` primitive. 460 | 461 | ``` javascript 462 | function set( d, i, j, idx ) { 463 | return '' + j + i; 464 | } 465 | 466 | mat.sset( '4:6,6:8', set ); 467 | 468 | submat = mat.sget( '3:7,5:9' ); 469 | /* 470 | [ 35 36 37 38 471 | 45 64 74 48 472 | 55 65 75 58 473 | 65 66 67 68 ] 474 | */ 475 | ``` 476 | 477 | By default, the callback `this` context is set to the `Matrix` instance. To specify a different `this` context, provide a `thisArg`. 478 | 479 | ``` javascript 480 | function set( d, i, j, idx ) { 481 | console.log( this ); 482 | // returns null 483 | return '' + j + i; 484 | } 485 | 486 | mat.sset( '4:6,6:8', set, null ); 487 | ``` 488 | 489 | 490 | __Notes__: 491 | * Values which are set are cast to the target `Matrix` data type. 492 | * Out-of-bounds row and column indices will silently fail. 493 | * A provided `Matrix` must have dimensions which match the submatrix defined by row and column subsequences. 494 | * For further subsequence documentation, see [compute-indexspace][indexspace]. 495 | 496 | 497 | === 498 | ### Get Methods 499 | 500 | These methods provide access to `Matrix` elements: 501 | 502 | 503 | #### Matrix.prototype.get( i, j ) 504 | 505 | Returns a `Matrix` element located at a row and column index. 506 | 507 | ``` javascript 508 | var data = new Float32Array( 10 ); 509 | 510 | for ( var i = 0; i < data.length; i++ ) { 511 | data[ i ] = i; 512 | } 513 | 514 | var mat = matrix( data, [5,2] ); 515 | /* 516 | [ 0 1 517 | 2 3 518 | 4 5 519 | 6 7 520 | 8 9 ] 521 | */ 522 | 523 | var values = mat.get( 3, 1 ); 524 | // returns 7 525 | ``` 526 | 527 | __Note__: out-of-bounds row and column indices will return a value of `undefined`. 528 | 529 | 530 | 531 | #### Matrix.prototype.iget( index ) 532 | 533 | Returns a `Matrix` element located at a specified [`index`](#linear-indexing). If `index < 0`, the index refers to a position relative to the `Matrix` length, where `index = -1` corresponds to the last element. 534 | 535 | ``` javascript 536 | var value = mat.iget( 7 ); 537 | // returns 7 538 | 539 | value = mat.iget( -3 ); 540 | // returns 7 541 | ``` 542 | 543 | __Note__: out-of-bounds indices will return a value of `undefined`. 544 | 545 | 546 | 547 | #### Matrix.prototype.mget( idx[, cols] ) 548 | 549 | Returns multiple `Matrix` elements. If provided a single argument, the method treats `idx` as an `array` of [linear indices](#linear-indexing) (`idx[i] >= 0`) and returns a new `Matrix` instance having a single row. Otherwise, `idx` and `cols` are `integer` arrays which specify row and column indices and the method returns a new `Matrix` instance having dimensions determined by the number of defined rows and columns. 550 | 551 | ``` javascript 552 | var data = new Int8Array( 10 ); 553 | for ( var i = 0; i < data.length; i++ ) { 554 | data[ i ] = i*2; 555 | } 556 | 557 | var mat = matrix( data, [5,2] ); 558 | /* 559 | [ 0 2 560 | 4 6 561 | 8 10 562 | 12 14 563 | 16 18 ] 564 | */ 565 | 566 | // Scramble the second column: 567 | var vals = mat.mget( [1,5,3,9,7] ); 568 | /* 569 | [ 2, 10, 6, 18, 14 ] 570 | */ 571 | 572 | // Extract select rows and columns in arbitrary order: 573 | var mat1 = mat.mget( [1,3,2], [1] ); 574 | /* 575 | [ 4 576 | 14 577 | 8 ] 578 | */ 579 | ``` 580 | 581 | If `idx` and/or `cols` is `null`, all rows (columns) are extracted. 582 | 583 | ``` javascript 584 | // Replicate a column: 585 | var rep = mat.mget( null, [1,1,1,1,1] ); 586 | /* 587 | [ 2 2 2 2 2 588 | 6 6 6 6 6 589 | 10 10 10 10 10 590 | 14 14 14 14 14 591 | 18 18 18 18 18 ] 592 | */ 593 | 594 | // Tile select rows and columns: 595 | var tile = mat.mget( [1,2,1,2], [0,1,0,1] ); 596 | /* 597 | [ 598 | 4 6 4 6 599 | 8 10 8 10 600 | 4 6 4 6 601 | 8 10 8 10 602 | ] 603 | */ 604 | ``` 605 | 606 | __Note__: out-of-bounds indices are ignored. 607 | 608 | 609 | 610 | #### Matrix.prototype.sget( subsequence ) 611 | 612 | Returns `Matrix` elements in a new `Matrix` according to a specified [`subsequence`][indexspace]. The `subsequence` must specify __both__ row and column subsequences; e.g., `'3:7,5:9'`, where `3:7` corresponds to row indices `3,4,5,6` and `5:9` corresponds to column indices `5,6,7,8`. If a `subsequence` does not correspond to any `Matrix` elements, the method returns an empty `Matrix`. 613 | 614 | ``` javascript 615 | var submatrix; 616 | 617 | submatrix = mat.sget( ':,:' ); // Copy a matrix 618 | /* 619 | [ 0 1 620 | 2 3 621 | 4 5 622 | 6 7 623 | 8 9 ] 624 | */ 625 | 626 | submatrix = mat.sget( '1:4,:' ); 627 | /* 628 | [ 2 3 629 | 4 5 630 | 6 7 ] 631 | */ 632 | 633 | submatrix = mat.sget( '::-1,:' ); // flip top-to-bottom 634 | /* 635 | [ 8 9 636 | 6 7 637 | 4 5 638 | 2 3 639 | 0 1 ] 640 | */ 641 | 642 | submatrix = mat.sget( ':,::-1' ); // flip left-to-right 643 | /* 644 | [ 1 0 645 | 3 2 646 | 5 4 647 | 7 6 648 | 9 8 ] 649 | */ 650 | 651 | submatrix = mat.sget( '50:100,:' ); 652 | /* 653 | [] 654 | */ 655 | ``` 656 | 657 | __Notes__: 658 | * Out-of-bounds indices are ignored. 659 | * For further subsequence documentation, see [compute-indexspace][indexspace]. 660 | 661 | 662 | === 663 | ### Accessor Methods 664 | 665 | These methods do **not** mutate a `Matrix` and return some representation of a `Matrix`: 666 | 667 | 668 | #### Matrix.prototype.toString() 669 | 670 | Returns a `string` representation of a `Matrix`. This method is similar to [`Array#toString`][array-string], except that rows are delineated by __semicolons__ and column values are delineated by __commas__. 671 | 672 | ``` javascript 673 | var data = new Int8Array( 10 ); 674 | for ( var i = 0; i < data.length; i++ ) { 675 | data[ i ] = i; 676 | } 677 | 678 | var mat = matrix( data, [5,2] ); 679 | 680 | var str = mat.toString(); 681 | // 0,1;2,3;4,5;6,7;8,9 682 | ``` 683 | 684 | To construct an `array` of `arrays` from the `string` representation, 685 | 686 | ``` javascript 687 | var rows, 688 | cols, 689 | i, j; 690 | 691 | rows = str.split( ';' ); 692 | for ( i = 0; i < rows.length; i++ ) { 693 | cols = rows[ i ].split( ',' ); 694 | rows[ i ] = new Array( cols.length ); 695 | for ( j = 0; j < cols.length; j++ ) { 696 | rows[ i ][ j ] = parseFloat( cols[ j ] ); 697 | } 698 | } 699 | ``` 700 | 701 | 702 | 703 | #### Matrix.prototype.toJSON() 704 | 705 | Returns a [`JSON`][json] representation of a `Matrix`. [`JSON#stringify`][json-stringify] implicitly calls this method when stringifying a `Matrix` instance. 706 | 707 | ``` javascript 708 | var data = new Int8Array( 10 ); 709 | for ( var i = 0; i < data.length; i++ ) { 710 | data[ i ] = i; 711 | } 712 | 713 | var mat = matrix( data, [5,2] ); 714 | /* 715 | [ 0 1 716 | 2 3 717 | 4 5 718 | 6 7 719 | 8 9 ] 720 | */ 721 | 722 | var json = mat.toJSON(); 723 | /* 724 | { 725 | "type": "Matrix", 726 | "dtype": "int8", 727 | "shape": [5,2], 728 | "offset": 0, 729 | "strides": [2,1], 730 | "raw": false, 731 | "data": [0,1,2,3,4,5,6,7,8,9] 732 | } 733 | */ 734 | ``` 735 | 736 | To a [revive][json-parse] a `Matrix` from a [`JSON`][json] string, 737 | 738 | ``` javascript 739 | // Matrix reviver: 740 | var reviver = require( 'dstructs-matrix-reviver' ); 741 | 742 | // Stringify a matrix (implicitly calls `.toJSON`): 743 | var str = JSON.stringify( mat ); 744 | // returns '{"type":"Matrix","dtype":"int8","shape":[5,2],"offset":0,"strides":[2,1],"raw":false,"data":[0,1,2,3,4,5,6,7,8,9]}' 745 | 746 | // Revive a Matrix from a JSON string: 747 | var mat = JSON.parse( str, reviver ); 748 | /* 749 | [ 0 1 750 | 2 3 751 | 4 5 752 | 6 7 753 | 8 9 ] 754 | */ 755 | ``` 756 | 757 | 758 | --- 759 | 760 | ## Constructor 761 | 762 | A `Matrix` has a constructor having the following interface... 763 | 764 | 765 | #### mat.constructor( data, dtype, shape, offset, strides ) 766 | 767 | Creates a new `Matrix` having a specified `shape`, `offset`, `strides`, `dtype`, and underlying typed `data` store. 768 | 769 | ``` javascript 770 | var data = new Float32Array( 10 ); 771 | 772 | var mat1 = matrix( data, [5,2] ); 773 | /* 774 | [ 0 0 775 | 0 0 776 | 0 0 777 | 0 0 778 | 0 0 ] 779 | */ 780 | 781 | var mat2 = new mat1.constructor( data, mat1.dtype, [2,5], 0, [5,1] ); 782 | /* 783 | [ 0 0 0 0 0 784 | 0 0 0 0 0 ] 785 | */ 786 | ``` 787 | 788 | __Note__: while more performant, constructing a `Matrix` in this manner should be carefully considered. Arguments are not validated or sanity checked. 789 | 790 | 791 | --- 792 | ## Raw 793 | 794 | For performance, a lower-level interface is provided which forgoes some of the guarantees of the above API, such as input argument validation and measures to prevent `Matrices` from becoming corrupted. While use of the above API is encouraged in REPL environments, use of the lower-level interface may be warranted when arguments are of a known type or when many `Matrices` must be created. 795 | 796 | 797 | 798 | 799 | #### matrix.raw( [data,] shape[, dtype] ) 800 | 801 | Creates a new `Matrix` having a specified `shape`. 802 | 803 | ``` javascript 804 | var data = new Float32Array( 10 ); 805 | 806 | var mat = matrix.raw( data, [5,2] ); 807 | /* 808 | [ 0 0 809 | 0 0 810 | 0 0 811 | 0 0 812 | 0 0 ] 813 | */ 814 | ``` 815 | 816 | If the input `data` type is known, `Matrix` creation is significantly faster. 817 | 818 | ``` javascript 819 | var mat = matrix.raw( data, [5,2], 'float32' ); 820 | /* 821 | [ 0 0 822 | 0 0 823 | 0 0 824 | 0 0 825 | 0 0 ] 826 | */ 827 | ``` 828 | 829 | __Notes__: 830 | * The `shape` and `dtype` parameters are the same as for the higher-level `Matrix` interface. 831 | * Specifying a `dtype` does __not__ cast the data to a different storage type. Instead, providing the argument circumvents the need to determine the input `data` type, resulting in increased performance. 832 | * Input `data` __must__ be a typed array. Unlike the higher-level `Matrix` interface, plain `arrays` are __not__ cast to `float64`. Providing a plain `array` can lead to subtle bugs and affect performance. 833 | * `Matrix` properties and methods are the same as for the higher-level API, with the exception that `Matrix` properties are __no__ longer read-only and methods do __not__ perform input argument validation. 834 | * Setting properties is __not__ recommended as the `Matrix` can become corrupted; e.g., incompatible dimensions, out-of-bounds indexing, etc. In contrast to the strict API above, setting `Matrix` properties will __not__ result in an `error` being thrown. Accordingly, property modification may introduce silent bugs. 835 | * The lower-level `Matrix` constructor has the same interface as the higher-level `Matrix` constructor. 836 | 837 | 838 | 839 | --- 840 | ## Notes 841 | 842 | #### Linear Indexing 843 | 844 | A linear `index` corresponds to an element position in a flattened `Matrix` arranged in [__row-major__][row-major-order] order. For example, consider a [__zero-filled__][zeros] 5x2 matrix, its subscripts, and its corresponding linear indices. 845 | 846 | ``` javascript 847 | /* 848 | Matrix Subscripts Indices 849 | 850 | [ 0 0 [ a00 a01 [ 0 1 851 | 0 0 a10 a11 2 3 852 | A = 0 0 => a20 a21 => 4 5 853 | 0 0 a30 a31 6 7 854 | 0 0 ] a40 a41 ] 8 9 ] 855 | */ 856 | ``` 857 | 858 | 859 | --- 860 | ## Examples 861 | 862 | ``` javascript 863 | var matrix = require( 'dstructs-matrix' ); 864 | 865 | // Create a new 2x2 matrix: 866 | var mat = matrix( [2,2] ); 867 | console.log( mat ); 868 | 869 | // Inspect the initialized matrix elements: 870 | console.log( mat.get( 1, 1 ) ); 871 | 872 | // Set a matrix element: 873 | console.log( mat.set( 1, 1, 5 ) ); 874 | 875 | // Confirm that the matrix element was set: 876 | console.log( mat.get( 1, 1 ) ); 877 | 878 | // Convert the matrix to a string: 879 | console.log( mat.toString() ); 880 | 881 | // Convert the matrix to JSON: 882 | console.log( mat.toJSON() ); 883 | ``` 884 | 885 | To run the example code from the top-level application directory, 886 | 887 | ``` bash 888 | $ node ./examples/index.js 889 | ``` 890 | 891 | 892 | --- 893 | ## Tests 894 | 895 | ### Unit 896 | 897 | Unit tests use the [Mocha][mocha] test framework with [Chai][chai] assertions. To run the tests, execute the following command in the top-level application directory: 898 | 899 | ``` bash 900 | $ make test 901 | ``` 902 | 903 | All new feature development should have corresponding unit tests to validate correct functionality. 904 | 905 | 906 | ### Test Coverage 907 | 908 | This repository uses [Istanbul][istanbul] as its code coverage tool. To generate a test coverage report, execute the following command in the top-level application directory: 909 | 910 | ``` bash 911 | $ make test-cov 912 | ``` 913 | 914 | Istanbul creates a `./reports/coverage` directory. To access an HTML version of the report, 915 | 916 | ``` bash 917 | $ make view-cov 918 | ``` 919 | 920 | 921 | --- 922 | ## License 923 | 924 | [MIT license][mit-license]. 925 | 926 | 927 | ## Copyright 928 | 929 | Copyright © 2015. The [Compute.io][compute-io] Authors. 930 | 931 | 932 | [npm-image]: http://img.shields.io/npm/v/dstructs-matrix.svg 933 | [npm-url]: https://npmjs.org/package/dstructs-matrix 934 | 935 | [travis-image]: http://img.shields.io/travis/dstructs/matrix/master.svg 936 | [travis-url]: https://travis-ci.org/dstructs/matrix 937 | 938 | [codecov-image]: https://img.shields.io/codecov/c/github/dstructs/matrix/master.svg 939 | [codecov-url]: https://codecov.io/github/dstructs/matrix?branch=master 940 | 941 | [dependencies-image]: http://img.shields.io/david/dstructs/matrix.svg 942 | [dependencies-url]: https://david-dm.org/dstructs/matrix 943 | 944 | [dev-dependencies-image]: http://img.shields.io/david/dev/dstructs/matrix.svg 945 | [dev-dependencies-url]: https://david-dm.org/dev/dstructs/matrix 946 | 947 | [github-issues-image]: http://img.shields.io/github/issues/dstructs/matrix.svg 948 | [github-issues-url]: https://github.com/dstructs/matrix/issues 949 | 950 | [matrix]: https://en.wikipedia.org/wiki/Matrix_(mathematics) 951 | [browserify]: https://github.com/substack/node-browserify 952 | [cast-arrays]: https://github.com/compute-io/cast-arrays 953 | [array-length]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length 954 | [indexspace]: https://github.com/compute-io/indexspace 955 | [array-string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString 956 | [json]: http://www.json.org/ 957 | [json-stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify 958 | [json-parse]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse 959 | [row-major-order]: https://en.wikipedia.org/wiki/Row-major_order 960 | [zeros]: https://github.com/compute-io/zeros 961 | [mocha]: http://mochajs.org/ 962 | [chai]: http://chaijs.com 963 | [istanbul]: https://github.com/gotwarlost/istanbul 964 | [mit-license]: http://opensource.org/licenses/MIT 965 | [compute-io]: https://github.com/compute-io 966 | --------------------------------------------------------------------------------