├── .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 |
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 |
--------------------------------------------------------------------------------