From 04316a47392f5c0cd30acde82bd452a7d04cf342 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 23 Jun 2015 12:16:54 +0200 Subject: [PATCH 01/10] [UPDATE] Makefile, package.json --- Makefile | 2 +- package.json | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 3234fbb..c6f009c 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ ISTANBUL_HTML_REPORT_PATH ?= $(ISTANBUL_OUT)/lcov-report/index.html # JSHINT # JSHINT ?= ./node_modules/.bin/jshint -JSHINT_REPORTER ?= ./node_modules/jshint-stylish/stylish.js +JSHINT_REPORTER ?= ./node_modules/jshint-stylish diff --git a/package.json b/package.json index f1c1022..46e9b2c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,10 @@ { "name": "Athan Reines", "email": "kgryte@gmail.com" + }, + { + "name": "Philipp Burckhardt", + "email": "pburckhardt@outlook.com" } ], "scripts": { @@ -39,24 +43,27 @@ "url": "https://github.com/compute-io/divide/issues" }, "dependencies": { - "validate.io-array": "^1.0.5", + "compute-array-constructors": "^1.0.0", + "dstructs-matrix": "^2.0.0", + "utils-deep-get": "^1.0.0", + "utils-deep-set": "^1.0.1", + "validate.io-array": "^1.0.6", + "validate.io-array-like": "^1.0.1", "validate.io-boolean-primitive": "^1.0.0", "validate.io-function": "^1.0.2", + "validate.io-matrix-like": "^1.0.2", + "validate.io-nan": "^1.0.3", "validate.io-number-primitive": "^1.0.0", - "validate.io-object": "^1.0.3" + "validate.io-object": "^1.0.4", + "validate.io-string-primitive": "^1.0.0" }, "devDependencies": { - "chai": "2.x.x", + "chai": "3.x.x", "coveralls": "^2.11.1", "istanbul": "^0.3.0", "jshint": "2.x.x", - "jshint-stylish": "^1.0.0", + "jshint-stylish": "2.x.x", "mocha": "2.x.x" }, - "licenses": [ - { - "type": "MIT", - "url": "http://www.opensource.org/licenses/MIT" - } - ] + "license": "MIT" } From b7c4194b5c19d46331c17b7e5748b904d8b40220 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 23 Jun 2015 16:03:11 +0200 Subject: [PATCH 02/10] [UPDATE] implementation --- lib/accessor.js | 67 ++++++++++++++++++++++ lib/array.js | 49 ++++++++++++++++ lib/deepset.js | 60 ++++++++++++++++++++ lib/index.js | 145 +++++++++++++++++++++++++----------------------- lib/matrix.js | 49 ++++++++++++++++ lib/validate.js | 66 ++++++++++++++++++++++ 6 files changed, 368 insertions(+), 68 deletions(-) create mode 100644 lib/accessor.js create mode 100644 lib/array.js create mode 100644 lib/deepset.js create mode 100644 lib/matrix.js create mode 100644 lib/validate.js diff --git a/lib/accessor.js b/lib/accessor.js new file mode 100644 index 0000000..2a03eed --- /dev/null +++ b/lib/accessor.js @@ -0,0 +1,67 @@ +'use strict'; + +// MODULES // + +var isArrayLike = require( 'validate.io-array-like' ); + + +// DIVIDE // + +/** +* FUNCTION: divide( out, arr, y, clbk ) +* Computes an element-wise division of an array using an accessor. +* +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} out - output array +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Number} y - either an array of equal length or a scalar +* @param {Function} accessor - accessor function for accessing array values +* @returns {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} output array +*/ +function divide( out, arr, y, clbk ) { + var len = arr.length, + i, + arrVal, yVal; + + if ( isArrayLike( y ) ) { + if ( len !== y.length ) { + throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); + } + if ( typeof y[ 0 ] === 'number' ) { + // y is number array -> callback does not have to be applied + for ( i = 0; i < len; i++ ) { + arrVal = clbk( arr[ i ], i, 0 ); + if ( typeof y[ i ] === 'number' && typeof arrVal === 'number' ) { + out[ i ] = arrVal / y[ i ]; + } else { + out[ i ] = NaN; + } + } + } else { + // y is an object array, too -> callback is applied + for ( i = 0; i < len; i++ ) { + arrVal = clbk( arr[ i ], i, 0 ); + yVal = clbk( y[ i ], i, 1 ); + if ( typeof arrVal === 'number' && typeof yVal === 'number' ) { + out[ i ] = arrVal / yVal; + } else { + out[ i ] = NaN; + } + } + } + } else { + for ( i = 0; i < len; i++ ) { + arrVal = clbk( arr[ i ], i ); + if ( typeof arrVal === 'number' ) { + out[ i ] = arrVal / y; + } else { + out[ i ] = NaN; + } + } + } + return out; +} // end FUNCTION divide() + + +// EXPORTS // + +module.exports = divide; diff --git a/lib/array.js b/lib/array.js new file mode 100644 index 0000000..db038b1 --- /dev/null +++ b/lib/array.js @@ -0,0 +1,49 @@ +'use strict'; + +// MODULES // + +var isArrayLike = require( 'validate.io-array-like' ); + + +// DIVIDE // + +/** +* FUNCTION: divide( out, arr, y ) +* Computes an element-wise division of an array. +* +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} out - output array +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Number} y - either an array of equal length or a scalar +* @returns {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} output array +*/ +function divide( out, arr, y ) { + var len = arr.length, + i; + + if ( isArrayLike( y ) ) { + if ( len !== y.length ) { + throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); + } + for ( i = 0; i < len; i++ ) { + if ( typeof y[ i ] === 'number' && typeof arr[ i ] === 'number' ) { + out[ i ] = arr[ i ] / y[ i ]; + } else { + out[ i ] = NaN; + } + } + } else { + for ( i = 0; i < len; i++ ) { + if ( typeof arr[ i ] === 'number' ) { + out[ i ] = arr[ i ] / y; + } else { + out[ i ] = NaN; + } + } + } + return out; +} // end FUNCTION divide() + + +// EXPORTS // + +module.exports = divide; diff --git a/lib/deepset.js b/lib/deepset.js new file mode 100644 index 0000000..186f30d --- /dev/null +++ b/lib/deepset.js @@ -0,0 +1,60 @@ +'use strict'; + +// MODULES // + +var isArrayLike = require( 'validate.io-array-like' ), + deepSet = require( 'utils-deep-set' ).factory, + deepGet = require( 'utils-deep-get' ).factory; + + +// DIVIDE // + +/** +* FUNCTION: divide( arr, y, path[, sep] ) +* Computes an element-wise division or each element and deep sets the input array. +* +* @param {Array} arr - input array +* @param {String} path - key path used when deep getting and setting +* @param {String} [sep] - key path separator +* @returns {Array} input array +*/ +function divide( x, y, path, sep ) { + var len = x.length, + opts = {}, + dget, + dset, + v, i; + + if ( arguments.length > 3 ) { + opts.sep = sep; + } + if ( len ) { + dget = deepGet( path, opts ); + dset = deepSet( path, opts ); + if ( isArrayLike( y ) ) { + for ( i = 0; i < len; i++ ) { + v = dget( x[ i ] ); + if ( typeof v === 'number' ) { + dset( x[ i ], v * y[ i ] ); + } else { + dset( x[ i ], NaN ); + } + } + } else { + for ( i = 0; i < len; i++ ) { + v = dget( x[ i ] ); + if ( typeof v === 'number' ) { + dset( x[ i ], v * y ); + } else { + dset( x[ i ], NaN ); + } + } + } + } + return x; +} // end FUNCTION divide() + + +// EXPORTS // + +module.exports = divide; diff --git a/lib/index.js b/lib/index.js index 2a0dc90..874729f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,99 +2,108 @@ // MODULES // -var isArray = require( 'validate.io-array' ), - isObject = require( 'validate.io-object' ), - isFunction = require( 'validate.io-function' ), - isBoolean = require( 'validate.io-boolean-primitive' ), - isNumber = require( 'validate.io-number-primitive' ); +var isNumber = require( 'validate.io-number-primitive' ), + isnan = require( 'validate.io-nan' ), + isArray = require( 'validate.io-array' ), + isArrayLike = require( 'validate.io-array-like' ), + isMatrixLike = require( 'validate.io-matrix-like' ), + ctors = require( 'compute-array-constructors' ), + matrix = require( 'dstructs-matrix' ), + validate = require( './validate.js' ); -// DIVIDE // +// FUNCTIONS // + +var divide1 = require( './array.js' ), + divide2 = require( './accessor.js' ), + divide3 = require( './deepset.js' ), + divide4 = require( './matrix.js' ); + + +// MULTIPLY // /** -* FUNCTION: divide( arr, x[, opts] ) +* FUNCTION: divide( x, y[, opts] ) * Computes an element-wise division. * -* @param {Number[]|Array} arr - input array -* @param {Number[]|Array|Number} x - either an array of equal length or a scalar +* @param {Number|Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} x - input value +* @param {Number|Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} y - either an array or matrix of equal dimension or a scalar * @param {Object} [opts] - function options -* @param {Boolean} [opts.copy=true] - boolean indicating whether to return a new array +* @param {Boolean} [opts.copy=true] - boolean indicating if the function should return a new data structure * @param {Function} [opts.accessor] - accessor function for accessing array values -* @returns {Number[]} output array +* @param {String} [opts.path] - deep get/set key path +* @param {String} [opts.sep="."] - deep get/set key path separator +* @param {String} [opts.dtype="float64"] - output data type +* @returns {Number|Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} value(s) after division */ -function divide( arr, x, opts ) { - var isArr = isArray( x ), - copy = true, - arity, - clbk, +function divide( x, y, options ) { + /* jshint newcap:false */ + var opts = {}, + ctor, + err, out, - len, - i; - - if ( !isArray( arr ) ) { - throw new TypeError( 'divide()::invalid input argument. Must provide an array. Value: `' + arr + '`.' ); - } - if ( !isArr && !isNumber( x ) ) { - throw new TypeError( 'divide()::invalid input argument. Second argument must either be an array or number primitive. Value: `' + x + '`.' ); + dt, + d; + if ( isNumber( x ) || isnan( x ) ) { + if ( !isNumber( y ) ) { + throw new TypeError( 'divide()::invalid input argument. Second argument must a number primitive when x is a number. Value: `' + x + '`.' ); + } + return x / y; } if ( arguments.length > 2 ) { - if ( !isObject( opts ) ) { - throw new TypeError( 'divide()::invalid input argument. Options argument must be an object. Value: `' + opts + '`.' ); + err = validate( opts, options ); + if ( err ) { + throw err; } - if ( opts.hasOwnProperty( 'copy' ) ) { - copy = opts.copy; - if ( !isBoolean( copy ) ) { - throw new TypeError( 'divide()::invalid option. Copy option must be a boolean primitive. Option: `' + copy + '`.' ); - } + } + if ( isMatrixLike( x ) ) { + if ( !isMatrixLike( y ) && !isNumber( y ) ) { + throw new TypeError( 'divide()::invalid input argument. Second argument must be matrix-like or a number primitive when x is a matrix. Value: `' + x + '`.' ); } - if ( opts.hasOwnProperty( 'accessor' ) ) { - clbk = opts.accessor; - if ( !isFunction( clbk ) ) { - throw new TypeError( 'divide()::invalid option. Accessor must be a function. Option: `' + clbk + '`.' ); + if ( opts.copy !== false ) { + dt = opts.dtype || 'float64'; + ctor = ctors( dt ); + if ( ctor === null ) { + throw new Error( 'divide()::invalid option. Data type option does not have a corresponding array constructor. Option: `' + dt + '`.' ); } - arity = clbk.length; + // Create an output matrix: + d = new ctor( x.length ); + out = matrix( d, x.shape, dt ); + } else { + out = x; } + return divide4( out, x, y ); } - len = arr.length; - if ( copy ) { - out = new Array( len ); - } else { - out = arr; - } - // Case 1: x is an array - if ( isArr ) { - if ( len !== x.length ) { - throw new Error( 'divide()::invalid input argument. Array to be divideed must have a length equal to that of the input array.' ); + if ( isArrayLike( x ) ) { + if ( !isArrayLike( y ) && !isNumber( y ) ) { + throw new TypeError( 'divide()::invalid input argument. Second argument must be array-like or a number primitive when x is an array. Value: `' + x + '`.' ); } - if ( arity === 3 ) { // clbk implied - for ( i = 0; i < len; i++ ) { - out[ i ] = clbk( arr[i], i, 0 ) / clbk( x[i], i, 1 ); - } + // Handle deepset first... + if ( opts.path ) { + opts.sep = opts.sep || '.'; + return divide3( x, y, opts.path, opts.sep ); } - else if ( clbk ) { - for ( i = 0; i < len; i++ ) { - out[ i ] = clbk( arr[i], i ) / x[ i ]; - } + // Handle regular, typed, and accessor arrays next... + if ( opts.copy === false ) { + out = x; } - else { - for ( i = 0; i < len; i++ ) { - out[ i ] = arr[ i ] / x[ i ]; + else if ( opts.dtype || !isArray( x ) ) { + dt = opts.dtype || 'float64'; + ctor = ctors( dt ); + if ( ctor === null ) { + throw new TypeError( 'divide()::invalid input argument. Unrecognized/unsupported array-like object. Provide either a plain or typed array. Value: `' + x + '`.' ); } + out = new ctor( x.length ); } - } - // Case 2: accessor and scalar - else if ( clbk ) { - for ( i = 0; i < len; i++ ) { - out[ i ] = clbk( arr[i], i ) / x; + else { + out = new Array( x.length ); } - } - // Case 3: scalar - else { - for ( i = 0; i < len; i++ ) { - out[ i ] = arr[ i ] / x; + if ( opts.accessor ) { + return divide2( out, x, y, opts.accessor ); } + return divide1( out, x, y ); } - return out; + return NaN; } // end FUNCTION divide() diff --git a/lib/matrix.js b/lib/matrix.js new file mode 100644 index 0000000..26ee175 --- /dev/null +++ b/lib/matrix.js @@ -0,0 +1,49 @@ +'use strict'; + +// MODULES // + +var isMatrixLike = require( 'validate.io-matrix-like' ); + + +// DIVIDE // + +/** +* FUNCTION: divide( out, x, y ) +* Computes an element-wise multiplication of a matrix +* +* @param {Matrix} out - output matirx +* @param {Matrix} x - input matrix +* @param {Matrix|Number} y - either a matrix of equal dimensions or a scalar +* @returns {Matrix} output matrix +*/ +function divide( out, x, y ) { + var len = x.length, + i, j, + M, N; + + if ( out.length !== len ) { + throw new Error( 'divide()::invalid input arguments. Input and output matrices must be the same length.' ); + } + if ( isMatrixLike( y ) ) { + M = x.shape[0]; + N = x.shape[1]; + if ( M !== x.shape[0] || N !== y.shape[1] ) { + throw new Error( 'divide()::invalid input arguments. Matrix to be multiplied must have the same number of rows and columns as the input matrix.' ); + } + for ( i = 0; i < M; i++ ) { + for ( j = 0; j < N; j++ ) { + out.set( i, j, x.get( i, j ) * y.get( i, j ) ); + } + } + } else { + for ( i = 0; i < len; i++ ) { + out.data[ i ] = x.data[ i ] * y; + } + } + return out; +} // end FUNCTION divide() + + +// EXPORTS // + +module.exports = divide; diff --git a/lib/validate.js b/lib/validate.js new file mode 100644 index 0000000..f3f9ff0 --- /dev/null +++ b/lib/validate.js @@ -0,0 +1,66 @@ +'use strict'; + +// MODULES // + +var isObject = require( 'validate.io-object' ), + isBoolean = require( 'validate.io-boolean-primitive' ), + isFunction = require( 'validate.io-function' ), + isString = require( 'validate.io-string-primitive' ); + + +// VALIDATE // + +/** +* FUNCTION: validate( opts, options ) +* Validates function options. +* +* @param {Object} opts - destination for validated options +* @param {Object} options - function options +* @param {Boolean} [options.copy=true] - boolean indicating if the function should return a new data structure +* @param {Function} [options.accessor] - accessor function for accessing array values +* @param {String} [options.sep] - deep get/set key path separator +* @param {String} [options.path] - deep get/set key path +* @param {String} [options.dtype] - output data type +* @returns {Null|Error} null or an error +*/ +function validate( opts, options ) { + if ( !isObject( options ) ) { + return new TypeError( 'divide()::invalid input argument. Options argument must be an object. Value: `' + options + '`.' ); + } + if ( options.hasOwnProperty( 'copy' ) ) { + opts.copy = options.copy; + if ( !isBoolean( opts.copy ) ) { + return new TypeError( 'divide()::invalid option. Copy option must be a boolean primitive. Option: `' + opts.copy + '`.' ); + } + } + if ( options.hasOwnProperty( 'accessor' ) ) { + opts.accessor = options.accessor; + if ( !isFunction( opts.accessor ) ) { + return new TypeError( 'divide()::invalid option. Accessor must be a function. Option: `' + opts.accessor + '`.' ); + } + } + if ( options.hasOwnProperty( 'path' ) ) { + opts.path = options.path; + if ( !isString( opts.path ) ) { + return new TypeError( 'divide()::invalid option. Key path option must be a string primitive. Option: `' + opts.path + '`.' ); + } + } + if ( options.hasOwnProperty( 'sep' ) ) { + opts.sep = options.sep; + if ( !isString( opts.sep ) ) { + return new TypeError( 'divide()::invalid option. Separator option must be a string primitive. Option: `' + opts.sep + '`.' ); + } + } + if ( options.hasOwnProperty( 'dtype' ) ) { + opts.dtype = options.dtype; + if ( !isString( opts.dtype ) ) { + return new TypeError( 'divide()::invalid option. Data type option must be a string primitive. Option: `' + opts.dtype + '`.' ); + } + } + return null; +} // end FUNCTION validate() + + +// EXPORTS // + +module.exports = validate; From fb5a9b208a92497d4429166414f797183de7ff37 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 23 Jun 2015 16:49:41 +0200 Subject: [PATCH 03/10] [UPDATE] tests --- lib/deepset.js | 4 +- lib/index.js | 2 +- lib/matrix.js | 8 +- test/test.accessor.js | 190 +++++++++++++++++ test/test.array.js | 135 ++++++++++++ test/test.deepset.js | 154 ++++++++++++++ test/test.js | 484 ++++++++++++++++++++++++++++++++---------- test/test.matrix.js | 100 +++++++++ test/test.validate.js | 177 +++++++++++++++ 9 files changed, 1135 insertions(+), 119 deletions(-) create mode 100644 test/test.accessor.js create mode 100644 test/test.array.js create mode 100644 test/test.deepset.js create mode 100644 test/test.matrix.js create mode 100644 test/test.validate.js diff --git a/lib/deepset.js b/lib/deepset.js index 186f30d..f8e3e78 100644 --- a/lib/deepset.js +++ b/lib/deepset.js @@ -35,7 +35,7 @@ function divide( x, y, path, sep ) { for ( i = 0; i < len; i++ ) { v = dget( x[ i ] ); if ( typeof v === 'number' ) { - dset( x[ i ], v * y[ i ] ); + dset( x[ i ], v / y[ i ] ); } else { dset( x[ i ], NaN ); } @@ -44,7 +44,7 @@ function divide( x, y, path, sep ) { for ( i = 0; i < len; i++ ) { v = dget( x[ i ] ); if ( typeof v === 'number' ) { - dset( x[ i ], v * y ); + dset( x[ i ], v / y ); } else { dset( x[ i ], NaN ); } diff --git a/lib/index.js b/lib/index.js index 874729f..3c34bf1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -20,7 +20,7 @@ var divide1 = require( './array.js' ), divide4 = require( './matrix.js' ); -// MULTIPLY // +// DIVIDE // /** * FUNCTION: divide( x, y[, opts] ) diff --git a/lib/matrix.js b/lib/matrix.js index 26ee175..d9d18a4 100644 --- a/lib/matrix.js +++ b/lib/matrix.js @@ -9,7 +9,7 @@ var isMatrixLike = require( 'validate.io-matrix-like' ); /** * FUNCTION: divide( out, x, y ) -* Computes an element-wise multiplication of a matrix +* Computes an element-wise division of a matrix * * @param {Matrix} out - output matirx * @param {Matrix} x - input matrix @@ -28,16 +28,16 @@ function divide( out, x, y ) { M = x.shape[0]; N = x.shape[1]; if ( M !== x.shape[0] || N !== y.shape[1] ) { - throw new Error( 'divide()::invalid input arguments. Matrix to be multiplied must have the same number of rows and columns as the input matrix.' ); + throw new Error( 'divide()::invalid input arguments. Divisor matrix must have the same number of rows and columns as the input matrix.' ); } for ( i = 0; i < M; i++ ) { for ( j = 0; j < N; j++ ) { - out.set( i, j, x.get( i, j ) * y.get( i, j ) ); + out.set( i, j, x.get( i, j ) / y.get( i, j ) ); } } } else { for ( i = 0; i < len; i++ ) { - out.data[ i ] = x.data[ i ] * y; + out.data[ i ] = x.data[ i ] / y; } } return out; diff --git a/test/test.accessor.js b/test/test.accessor.js new file mode 100644 index 0000000..73c0fe8 --- /dev/null +++ b/test/test.accessor.js @@ -0,0 +1,190 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + divide = require( './../lib/accessor.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'accessor divide', function tests() { + + it( 'should export a function', function test() { + expect( divide ).to.be.a( 'function' ); + }); + + it( 'should perform an element-wise division by a scalar using an accessor', function test() { + var data, actual, expected; + + data = [ + {'x':0}, + {'x':2}, + {'x':4}, + {'x':6} + ]; + actual = new Array( data.length ); + actual = divide( actual, data, 2, getValue ); + + expected = [ + 0, + 1, + 2, + 3 + ]; + + assert.deepEqual( actual, expected ); + + function getValue( d ) { + return d.x; + } + + }); + + it( 'should perform an element-wise division of an array using an accessor', function test() { + var data, actual, expected, y; + + data = [ + {'x':0}, + {'x':1}, + {'x':2}, + {'x':3} + ]; + + y = [ + 0, + 1, + 2, + 3 + ]; + + actual = new Array( data.length ); + actual = divide( actual, data, y, getValue ); + + expected = [ + NaN, + 1, + 1, + 1 + ]; + + assert.deepEqual( actual, expected ); + + function getValue( d, i ) { + return d.x; + } + + }); + + it( 'should perform an element-wise multiplication of another object array using an accessor', function test() { + var data, actual, expected, y; + + data = [ + {'x':0}, + {'x':1}, + {'x':2}, + {'x':3} + ]; + + y = [ + {'y':0}, + {'y':1}, + {'y':2}, + {'y':3} + ]; + + actual = new Array( data.length ); + actual = divide( actual, data, y, getValue ); + + expected = [ + NaN, + 1, + 1, + 1 + ]; + + assert.deepEqual( actual, expected ); + + function getValue( d, i, j ) { + if ( j === 0 ) { + return d.x; + } else { + return d.y; + } + } + + }); + + it( 'should return empty array if provided an empty array', function test() { + assert.deepEqual( divide( [], [], 1, getValue ), [] ); + function getValue( d ) { + return d.x; + } + }); + + it( 'should handle non-numeric values by setting the element to NaN', function test() { + var data, actual, expected, y; + + data = [ + {'x':1}, + {'x':null}, + {'x':3} + ]; + actual = new Array( data.length ); + actual = divide( actual, data, 1, getValue ); + + expected = [ 1, NaN, 3 ]; + + assert.deepEqual( actual, expected ); + + // numeric array + y = [ 1, 2, 3 ]; + actual = new Array( data.length ); + actual = divide( actual, data, y, getValue ); + expected = [ 1, NaN, 1 ]; + + function getValue( d, i ) { + return d.x; + } + + // object array + y = [ + {'y':1}, + {'y':2}, + {'y':3} + ]; + actual = new Array( data.length ); + actual = divide( actual, data, y, getValue2 ); + expected = [ 1, NaN, 1 ]; + + function getValue2( d, i, j ) { + if ( j === 0 ) { + return d.x; + } else { + return d.y; + } + } + + }); + + it( 'should throw an error if provided an array to be multiplied which is not of equal length to the input array', function test() { + expect( foo ).to.throw( Error ); + function foo() { + divide( [], [1,2], [1,2,3], getValue ); + } + function getValue( d ) { + return d; + } + }); + +}); diff --git a/test/test.array.js b/test/test.array.js new file mode 100644 index 0000000..0f48922 --- /dev/null +++ b/test/test.array.js @@ -0,0 +1,135 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + divide = require( './../lib/array.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'array divide', function tests() { + + it( 'should export a function', function test() { + expect( divide).to.be.a( 'function' ); + }); + + it( 'should divide an array by a scalar', function test() { + var data, actual, expected; + + data = [ + 2, + 4, + 6, + 8, + 10 + ]; + actual = new Array( data.length ); + + actual = divide( actual, data, 2 ); + + expected = [ + 1, + 2, + 3, + 4, + 5 + ]; + + assert.deepEqual( actual, expected ); + + // Typed arrays... + data = new Int32Array( data ); + actual = new Int32Array( data.length ); + + actual = divide( actual, data, 2 ); + expected = new Int32Array( expected ); + + assert.deepEqual( actual, expected ); + }); + + it( 'should divide an array element-wise by another array', function test() { + var data, actual, expected, y; + + data = [ + 1, + 2, + 3, + 4, + 5 + ]; + + y = [ + 1, + 2, + 3, + 4, + 5 + ]; + actual = new Array( data.length ); + + actual = divide( actual, data, y ); + + expected = [ + 1, + 1, + 1, + 1, + 1 + ]; + + assert.deepEqual( actual, expected ); + + // Typed arrays... + data = new Int32Array( data ); + actual = new Int32Array( data.length ); + + actual = divide( actual, data, y ); + expected = new Int32Array( expected ); + + assert.deepEqual( actual, expected ); + }); + + it( 'should return an empty array if provided an empty array', function test() { + assert.deepEqual( divide( [], [], 1 ), [] ); + assert.deepEqual( divide( new Int8Array(), new Int8Array(), 1 ), new Int8Array() ); + }); + + it( 'should handle non-numeric values by setting the element to NaN', function test() { + var data, actual, expected, y; + + data = [ true, null, [], {} ]; + actual = new Array( data.length ); + actual = divide( actual, data, 1 ); + + expected = [ NaN, NaN, NaN, NaN ]; + + assert.deepEqual( actual, expected ); + + actual = new Array( data.length ); + y = [ 1, 2, 3, 4 ]; + actual = divide( actual, data, y ); + + expected = [ NaN, NaN, NaN, NaN ]; + + assert.deepEqual( actual, expected ); + }); + + it( 'should throw an error if provided an array to be divided which is not of equal length to the input array', function test() { + expect( foo ).to.throw( Error ); + function foo() { + divide( [], [1,2], [1,2,3] ); + } + }); + +}); diff --git a/test/test.deepset.js b/test/test.deepset.js new file mode 100644 index 0000000..b40a35f --- /dev/null +++ b/test/test.deepset.js @@ -0,0 +1,154 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + divide = require( './../lib/deepset.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'deepset divide', function tests() { + + it( 'should export a function', function test() { + expect( divide ).to.be.a( 'function' ); + }); + + it( 'should perform an element-wise division using a scalar and deep set', function test() { + var data, actual, expected; + + data = [ + {'x':0}, + {'x':2}, + {'x':4}, + {'x':6} + ]; + + actual = divide( data, 2, 'x' ); + + expected = [ + {'x':0}, + {'x':1}, + {'x':2}, + {'x':3} + ]; + + assert.strictEqual( data, actual ); + assert.deepEqual( data, expected); + + // Custom separator... + data = [ + {'x':[9,0]}, + {'x':[9,2]}, + {'x':[9,4]}, + {'x':[9,6]} + ]; + + data = divide( data, 2, 'x/1', '/' ); + expected = [ + {'x':[9,0]}, + {'x':[9,1]}, + {'x':[9,2]}, + {'x':[9,3]} + ]; + + assert.deepEqual( data, expected, 'custom separator' ); + }); + + it( 'should perform an element-wise division using an array and deep set', function test() { + var data, actual, expected, y; + + data = [ + {'x':0}, + {'x':1}, + {'x':2}, + {'x':3} + ]; + + y = [ 0, 1, 2, 3 ]; + + actual = divide( data, y, 'x' ); + + expected = [ + {'x':NaN}, + {'x':1}, + {'x':1}, + {'x':1} + ]; + + assert.strictEqual( data, actual ); + assert.deepEqual( data, expected); + + // Custom separator... + data = [ + {'x':[9,0]}, + {'x':[9,1]}, + {'x':[9,2]}, + {'x':[9,3]} + ]; + + data = divide( data, y, 'x/1', '/' ); + expected = [ + {'x':[9,NaN]}, + {'x':[9,1]}, + {'x':[9,1]}, + {'x':[9,1]} + ]; + + assert.deepEqual( data, expected, 'custom separator' ); + }); + + it( 'should return an empty array if provided an empty array', function test() { + var arr = []; + assert.deepEqual( divide( arr, 1, 'x' ), [] ); + assert.deepEqual( divide( arr, 1, 'x', '/' ), [] ); + }); + + it( 'should handle non-numeric values by setting the element to NaN', function test() { + var data, actual, expected, y; + + // multiplying with a scalar + data = [ + {'x':[9,null]}, + {'x':[9,1]}, + {'x':[9,true]}, + {'x':[9,3]} + ]; + actual = divide( data, 1, 'x.1' ); + expected = [ + {'x':[9,NaN]}, + {'x':[9,1]}, + {'x':[9,NaN]}, + {'x':[9,3]} + ]; + assert.deepEqual( data, expected ); + + // multiplying with an array + data = [ + {'x':[9,null]}, + {'x':[9,1]}, + {'x':[9,true]}, + {'x':[9,3]} + ]; + y = [ 0, 1, 2, 3]; + actual = divide( data, y, 'x.1' ); + expected = [ + {'x':[9,NaN]}, + {'x':[9,1]}, + {'x':[9,NaN]}, + {'x':[9,1]} + ]; + assert.deepEqual( data, expected ); + }); + +}); diff --git a/test/test.js b/test/test.js index 3174c1c..d0ed85c 100644 --- a/test/test.js +++ b/test/test.js @@ -6,6 +6,12 @@ var // Expectation library: chai = require( 'chai' ), + // Matrix data structure: + matrix = require( 'dstructs-matrix' ), + + // Validate if a value is NaN: + isnan = require( 'validate.io-nan' ), + // Module to be tested: divide = require( './../lib' ); @@ -24,243 +30,497 @@ describe( 'compute-divide', function tests() { expect( divide ).to.be.a( 'function' ); }); - it( 'should throw an error if not provided an array', function test() { + it( 'should throw an error if provided an invalid option', function test() { var values = [ '5', 5, - null, + true, undefined, + null, NaN, - true, - {}, - function(){} + [], + {} ]; for ( var i = 0; i < values.length; i++ ) { expect( badValue( values[i] ) ).to.throw( TypeError ); } - function badValue( value ) { return function() { - divide( value, 10 ); + divide( [1,2,3], 1, { + 'accessor': value + }); }; } }); - it( 'should throw an error if provided a second argument which is not an array or number primitive', function test() { + it( 'should throw an error if provided an array and an unrecognized/unsupported data type option', function test() { var values = [ - new Number( 5 ), - '5', - null, - undefined, - NaN, - true, - {}, - function(){} + 'beep', + 'boop' ]; for ( var i = 0; i < values.length; i++ ) { - expect( badValue( values[i] ) ).to.throw( TypeError ); + expect( badValue( values[i] ) ).to.throw( Error ); } - function badValue( value ) { return function() { - divide( [1,2,3], value ); + divide( [1,2,3], 1, { + 'dtype': value + }); }; } }); - it( 'should throw an error if provided an options argument which is not an object', function test() { + it( 'should throw an error if provided a matrix and an unrecognized/unsupported data type option', function test() { var values = [ - '5', - 5, - null, - undefined, - NaN, - true, - [], - function(){} + 'beep', + 'boop' ]; for ( var i = 0; i < values.length; i++ ) { - expect( badValue( values[i] ) ).to.throw( TypeError ); + expect( badValue( values[i] ) ).to.throw( Error ); } - function badValue( value ) { return function() { - divide( [1,2,3], 10, value ); + divide( matrix( [2,2] ), 1, { + 'dtype': value + }); }; } }); - it( 'should throw an error if provided a copy option which is not a boolean primitive', function test() { + + it( 'should throw an error if provided an array and an unsupported second factor', function test() { var values = [ '5', - 5, - null, + true, undefined, + null, NaN, - new Boolean( true ), {}, - [], - function(){} + function(){}, + matrix( [2,2] ) ]; for ( var i = 0; i < values.length; i++ ) { - expect( badValue( values[i] ) ).to.throw( TypeError ); + expect( badValue( values[i] ) ).to.throw( Error ); } - function badValue( value ) { return function() { - divide( [1,2,3], 10, { - 'copy': value - }); + divide( [1,2,3], value ); }; } }); - it( 'should throw an error if provided an accessor option which is not a function', function test() { + it( 'should throw an error if provided a matrix and an unsupported second factor', function test() { var values = [ '5', - 5, - null, + true, undefined, + null, NaN, {}, + function(){}, [] ]; for ( var i = 0; i < values.length; i++ ) { - expect( badValue( values[i] ) ).to.throw( TypeError ); + expect( badValue( values[i] ) ).to.throw( Error ); } - function badValue( value ) { return function() { - divide( [1,2,3], 10, { - 'accessor': value - }); + divide( matrix( [2,2] ), value ); }; } }); - it( 'should throw an error if provided an array as a second argument which is not of equal length to the input array', function test() { + it( 'should return NaN if the first argument is neither a number, array-like, or matrix-like', function test() { + var values = [ + // '5', // valid as is array-like (length) + true, + undefined, + null, + NaN, + function(){}, + {} + ]; + + for ( var i = 0; i < values.length; i++ ) { + assert.isTrue( isnan( divide( values[ i ], 1 ) ) ); + } + }); + + it( 'should divide two numbers', function test() { + assert.strictEqual( divide( 3, 2 ), 1.5 ); + assert.strictEqual( divide( 20, 10 ), 2 ); + }); + + it( 'should throw an error if provided a number and an array as the second argument', function test() { expect( foo ).to.throw( Error ); function foo() { - divide( [1,2], [1,2,3] ); + divide( 2, [ 1, 1 ]); } }); - it( 'should not mutate an input array by default', function test() { - var data, actual; + it( 'should perform an element-wise division when provided a plain array and a scalar', function test() { + var data, actual, expected; + + data = [ 0, 1, 2, 3 ]; + expected = [ + 0, + 2, + 4, + 6 + ]; + + actual = divide( data, 0.5 ); + assert.notEqual( actual, data ); + + assert.deepEqual( actual, expected ); + + // Mutate... + actual = divide( data, 0.5, { + 'copy': false + }); + assert.strictEqual( actual, data ); - data = [ 4, 5, 3, 6, 8 ]; - actual = divide( data, 4 ); + assert.deepEqual( data, expected ); - assert.ok( data !== actual ); }); - it( 'should perform element-wise division', function test() { - var data, expected, actual; + it( 'should perform an element-wise division when provided a plain array and another array', function test() { + var data, actual, expected; - // Scalar: - data = [ 5, 2, 4, 1, 2 ]; + data = [ 0, 1, 2, 3 ]; + expected = [ + NaN, + 1, + 1, + 1 + ]; - actual = divide( data, 4 ); - expected = [ 1.25, 0.5, 1, 0.25, 0.5 ]; + actual = divide( data, data ); + assert.notEqual( actual, data ); assert.deepEqual( actual, expected ); - // Array of numeric values: - data = [ 5, 2, 4, 1, 2 ]; + // Mutate... + actual = divide( data, data, { + 'copy': false + }); + assert.strictEqual( actual, data ); + + assert.deepEqual( data, expected ); + + }); + + it( 'should perform an element-wise division when provided a typed array and a scalar', function test() { + var data, actual, expected; + + data = new Int8Array( [ 0, 1, 2, 3 ] ); + + expected = new Float64Array( [ + 0, + 0.5, + 1, + 1.5 + ]); - actual = divide( data, [ 5, 2, 4, 1, 1 ] ); - expected = [ 1, 1, 1, 1, 2 ]; + actual = divide( data, 2 ); + assert.notEqual( actual, data ); assert.deepEqual( actual, expected ); + + // Mutate: + actual = divide( data, 2, { + 'copy': false + }); + assert.strictEqual( actual, data ); + expected = new Int8Array( [ 0, 0, 1, 1 ] ); + + assert.deepEqual( data, expected ); }); - it( 'should mutate an input array if the `copy` option is `false`', function test() { - var data, expected, actual; + it( 'should perform an element-wise division when provided a typed array and another typed array', function test() { + var data, actual, expected; - data = [ 4, 5, 3, 6, 8 ]; + data = new Int8Array( [ 3, 6, 9, 12 ] ); - actual = divide( data, 4, { + expected = new Float64Array( [ + 1, + 1, + 1, + 1 + ]); + + actual = divide( data, data ); + assert.notEqual( actual, data ); + assert.deepEqual( actual, expected ); + + // Mutate: + + actual = divide( data, data, { 'copy': false }); - expected = [ 1, 1.25, 0.75, 1.5, 2 ]; + expected = new Int8Array( [ 1, 1, 1, 1 ] ); + assert.strictEqual( actual, data ); + + assert.deepEqual( data, expected ); + }); - assert.ok( data === actual ); + it( 'should perform an element-wise division and return an array of a specific type', function test() { + var data, actual, expected; + + data = [ 0, 4, 8, 12 ]; + expected = new Int8Array( [ 0, 1, 2, 3 ] ); + + actual = divide( data, 4, { + 'dtype': 'int8' + }); + assert.notEqual( actual, data ); + assert.strictEqual( actual.BYTES_PER_ELEMENT, 1 ); assert.deepEqual( actual, expected ); }); - it( 'should perform element-wise division using an accessor', function test() { - var data, expected, actual; + it( 'should perform an element-wise division with a scalar using an accessor', function test() { + var data, actual, expected; data = [ - {'x':5}, - {'x':2}, - {'x':4}, - {'x':1}, - {'x':2} + [3,0], + [4,1], + [5,2], + [6,3] ]; - expected = [ 1.25, 0.5, 1, 0.25, 0.5 ]; - actual = divide( data, 4, { + expected = [ + 0, + 0.5, + 1, + 1.5 + ]; + + actual = divide( data, 2, { 'accessor': getValue }); + assert.notEqual( actual, data ); + assert.deepEqual( actual, expected ); + // Mutate: + actual = divide( data, 2, { + 'accessor': getValue, + 'copy': false + }); + assert.strictEqual( actual, data ); + + assert.deepEqual( data, expected ); + function getValue( d ) { - return d.x; + return d[ 1 ]; } }); - it( 'should perform element-wise division when provided an array and using an accessor', function test() { - var data, arr, expected, actual; + it( 'should perform an element-wise division two object arrays using an accessor', function test() { + var data, actual, expected, y; data = [ - {'x':5}, - {'x':2}, - {'x':4}, + {'x':0}, {'x':1}, - {'x':2} + {'x':2}, + {'x':3} ]; - // One array accessed... - arr = [ 5, 2, 4, 1, 1 ]; + y = [ + {'y':0}, + {'y':1}, + {'y':2}, + {'y':3} + ]; - actual = divide( data, arr, { - 'accessor': getValue1 + actual = divide( data, y, { + 'accessor': getValue }); - expected = [ 1, 1, 1, 1, 2 ]; + + expected = [ + NaN, + 1, + 1, + 1 + ]; assert.deepEqual( actual, expected ); + function getValue( d, i, j ) { + if ( j === 0 ) { + + return d.x; + } else { + return d.y; + } + } + + }); + + it( 'should perform an element-wise division with a scalar and deep set', function test() { + var data, actual, expected, i; - // Both arrays are accessed... - arr = [ - [0,5], - [1,2], - [2,4], - [3,1], - [4,1] + data = [ + {'x':[3,0]}, + {'x':[4,3]}, + {'x':[5,6]}, + {'x':[6,9]} + ]; + expected = [ + {'x':[3,0]}, + {'x':[4,1]}, + {'x':[5,2]}, + {'x':[6,3]} ]; - actual = divide( data, arr, { - 'accessor': getValue2 + actual = divide( data, 3, { + 'path': 'x.1' }); - expected = [ 1, 1, 1, 1, 2 ]; - assert.deepEqual( actual, expected ); + assert.strictEqual( actual, data ); - function getValue1( d ) { - return d.x; + for ( i = 0; i < data.length; i++ ) { + assert.closeTo( actual[ i ].x[ 1 ], expected[ i ].x[ 1 ], 1e-6 ); } - function getValue2( d, i, j ) { - if ( j === 0 ) { - return d.x; - } - return d[ 1 ]; + + // Specify a path with a custom separator... + data = [ + {'x':[3,0]}, + {'x':[4,3]}, + {'x':[5,6]}, + {'x':[6,9]} + ]; + actual = divide( data, 3, { + 'path': 'x/1', + 'sep': '/' + }); + assert.strictEqual( actual, data ); + + for ( i = 0; i < data.length; i++ ) { + assert.closeTo( actual[ i ].x[ 1 ], expected[ i ].x[ 1 ], 1e-6 ); } }); + it( 'should perform an element-wise division using an array and deep set', function test() { + var data, actual, expected, y; + + data = [ + {'x':6}, + {'x':3}, + {'x':2}, + {'x':1} + ]; + + y = [ 6, 3, 2, 1 ]; + + actual = divide( data, y, { + path: 'x' + }); + + expected = [ + {'x':1}, + {'x':1}, + {'x':1}, + {'x':1} + ]; + + assert.strictEqual( data, actual ); + assert.deepEqual( data, expected); + + // Custom separator... + data = [ + {'x':[9,6]}, + {'x':[9,3]}, + {'x':[9,2]}, + {'x':[9,1]} + ]; + + data = divide( data, y, { + 'path': 'x/1', + 'sep': '/' + }); + expected = [ + {'x':[9,1]}, + {'x':[9,1]}, + {'x':[9,1]}, + {'x':[9,1]} + ]; + + assert.deepEqual( data, expected, 'custom separator' ); + }); + + it( 'should perform an element-wise division when provided a matrix', function test() { + var mat, + out, + d1, + d2, + d3, + i; + + d1 = new Int32Array( 100 ); + d2 = new Int32Array( 100 ); + d3 = new Int32Array( 100 ); + for ( i = 0; i < d1.length; i++ ) { + d1[ i ] = ( i + 1 ); + d2[ i ] = ( i + 1) / ( i + 1); + d3[ i ] = ( i + 1) / 2; + } + + // Divide matrix by scalar + mat = matrix( d1, [10,10], 'int32' ); + out = divide( mat, 2, { + 'dtype': 'int32' + }); + + assert.deepEqual( out.data, d3 ); + + // Divide two matrices element-wise + mat = matrix( d1, [10,10], 'int32' ); + out = divide( mat, mat, { + 'dtype': 'int32' + }); + + assert.deepEqual( out.data, d2 ); + + // Multiply matrix by scalar and mutate... + out = divide( mat, 2, { + 'copy': false + }); + + assert.strictEqual( mat, out ); + assert.deepEqual( mat.data, d3 ); + }); + + it( 'should perform an element-wise division by a scalar and return a matrix of a specific type', function test() { + var mat, + out, + d1, + d2, + i; + + d1 = new Int16Array( 100 ); + d2 = new Uint16Array( 100 ); + for ( i = 0; i < d1.length; i++ ) { + d1[ i ] = i; + d2[ i ] = i / 2; + } + mat = matrix( d1, [10,10], 'int16' ); + out = divide( mat, 2, { + 'dtype': 'uint16' + }); + + assert.strictEqual( out.dtype, 'uint16' ); + assert.deepEqual( out.data, d2 ); + }); + + it( 'should return an empty data structure if provided an empty data structure', function test() { + assert.deepEqual( divide( [], 1 ), [] ); + assert.deepEqual( divide( matrix( [0,0] ), 1 ).data, matrix( [0,0] ).data ); + assert.deepEqual( divide( new Int8Array(), 1 ), new Float64Array() ); + }); + }); diff --git a/test/test.matrix.js b/test/test.matrix.js new file mode 100644 index 0000000..4abef0e --- /dev/null +++ b/test/test.matrix.js @@ -0,0 +1,100 @@ +/* global describe, it, require, beforeEach */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Matrix data structure: + matrix = require( 'dstructs-matrix' ), + + // Module to be tested: + divide = require( './../lib/matrix.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'matrix divide', function tests() { + + var out1, out2, + mat, + d1, + d2, + d3, + i; + + d1 = new Float64Array( 25 ); + d2 = new Float64Array( 25 ); + d3 = new Float64Array( 25 ); + for ( i = 0; i < d1.length; i++ ) { + d1[ i ] = i; + d2[ i ] = i / i; + d3[ i ] = i * 3; + } + + beforeEach( function before() { + mat = matrix( d1, [5,5], 'float64' ); + out1 = matrix( d2, [5,5], 'float64' ); + out2 = matrix( d3, [5,5], 'float64' ); + }); + + it( 'should export a function', function test() { + expect( divide ).to.be.a( 'function' ); + }); + + it( 'should throw an error if provided unequal length matrices', function test() { + expect( badValues ).to.throw( Error ); + function badValues() { + divide( matrix( [10,10] ), mat, 1 ); + } + }); + + it( 'should throw an error if provided a matrix to be multiplied which is not of equal length to the input matrix', function test() { + expect( badValues ).to.throw( Error ); + function badValues() { + divide( matrix( [5,5] ), mat, matrix( [10,10] ) ); + } + }); + + it( 'should divide each matrix element by a scalar', function test() { + var actual; + + actual = matrix( [5,5], 'float64' ); + actual = divide( actual, mat, 3 ); + + assert.deepEqual( actual.data, out2.data ); + }); + + it( 'should divide two matrices by each other element-wise', function test() { + var actual; + + actual = matrix( [5,5], 'float64' ); + actual = divide( actual, mat, mat ); + + assert.deepEqual( actual.data, out1.data ); + }); + + it( 'should return an empty matrix if provided an empty matrix', function test() { + var out, mat, expected; + + out = matrix( [0,0] ); + expected = matrix( [0,0] ).data; + + mat = matrix( [0,10] ); + assert.deepEqual( divide( out, mat, 1 ).data, expected ); + + mat = matrix( [10,0] ); + assert.deepEqual( divide( out, mat, 1 ).data, expected ); + + mat = matrix( [0,0] ); + assert.deepEqual( divide( out, mat, 1 ).data, expected ); + }); + +}); diff --git a/test/test.validate.js b/test/test.validate.js new file mode 100644 index 0000000..e21b68c --- /dev/null +++ b/test/test.validate.js @@ -0,0 +1,177 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + validate = require( './../lib/validate.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'validate', function tests() { + + it( 'should export a function', function test() { + expect( validate ).to.be.a( 'function' ); + }); + + it( 'should return an error if provided an options argument which is not an object', function test() { + var values = [ + '5', + 5, + true, + undefined, + null, + NaN, + function(){}, + [] + ]; + + for ( var i = 0; i < values.length; i++ ) { + assert.isTrue( validate( {}, values[ i ] ) instanceof TypeError ); + } + }); + + it( 'should return an error if provided an accessor which is not a function', function test() { + var values, err; + + values = [ + '5', + 5, + true, + undefined, + null, + NaN, + [], + {} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'accessor': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should return an error if provided a copy option which is not a boolean primitive', function test() { + var values, err; + + values = [ + '5', + 5, + new Boolean( true ), + undefined, + null, + NaN, + [], + {}, + function(){} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'copy': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should return an error if provided a path option which is not a string primitive', function test() { + var values, err; + + values = [ + 5, + true, + undefined, + null, + NaN, + [], + {}, + function(){} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'path': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should return an error if provided a separator option which is not a string primitive', function test() { + var values, err; + + values = [ + 5, + true, + undefined, + null, + NaN, + [], + {}, + function(){} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'sep': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should return an error if provided a dtype option which is not a string primitive', function test() { + var values, err; + + values = [ + 5, + true, + undefined, + null, + NaN, + [], + {}, + function(){} + ]; + + for ( var i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'dtype': values[ i ] + }); + assert.isTrue( err instanceof TypeError ); + } + }); + + it( 'should return null if all options are valid', function test() { + var err; + + err = validate( {}, { + 'accessor': function getValue(){}, + 'copy': false, + 'deepset': true, + 'path': 'x/y', + 'sep': '/', + 'dtype': 'int32' + }); + + assert.isNull( err ); + + err = validate( {}, { + 'beep': true, // misc options + 'boop': 'bop' + }); + + assert.isNull( err ); + }); + +}); From c173b0d0df4bdaa1167813b799365f1f394efd6e Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Tue, 23 Jun 2015 18:41:55 +0200 Subject: [PATCH 04/10] [FIX] matrix tests --- test/test.matrix.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test.matrix.js b/test/test.matrix.js index 4abef0e..4fd260e 100644 --- a/test/test.matrix.js +++ b/test/test.matrix.js @@ -34,9 +34,9 @@ describe( 'matrix divide', function tests() { d2 = new Float64Array( 25 ); d3 = new Float64Array( 25 ); for ( i = 0; i < d1.length; i++ ) { - d1[ i ] = i; - d2[ i ] = i / i; - d3[ i ] = i * 3; + d1[ i ] = ( i + 1 ); + d2[ i ] = 1; + d3[ i ] = ( i + 1 ) / 3; } beforeEach( function before() { From 74e5843ac5171c9965a589bfa186410488a5afbe Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Thu, 25 Jun 2015 18:02:56 +0200 Subject: [PATCH 05/10] [UPDATE] finish implementation --- lib/accessor.js | 12 +++++++----- lib/array.js | 6 ++++-- lib/deepset.js | 7 +++++-- lib/index.js | 38 +++++++++++++++++++++++++++++++------- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/lib/accessor.js b/lib/accessor.js index 2a03eed..ad03f23 100644 --- a/lib/accessor.js +++ b/lib/accessor.js @@ -2,7 +2,8 @@ // MODULES // -var isArrayLike = require( 'validate.io-array-like' ); +var isArrayLike = require( 'validate.io-array-like' ), + isObject = require( 'validate.io-object' ); // DIVIDE // @@ -20,14 +21,14 @@ var isArrayLike = require( 'validate.io-array-like' ); function divide( out, arr, y, clbk ) { var len = arr.length, i, - arrVal, yVal; + arrVal, yVal, yNumeric; if ( isArrayLike( y ) ) { if ( len !== y.length ) { throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); } - if ( typeof y[ 0 ] === 'number' ) { - // y is number array -> callback does not have to be applied + if ( !isObject( y[ 0 ] ) ) { + // Guess that y is a primitive array -> callback does not have to be applied for ( i = 0; i < len; i++ ) { arrVal = clbk( arr[ i ], i, 0 ); if ( typeof y[ i ] === 'number' && typeof arrVal === 'number' ) { @@ -49,9 +50,10 @@ function divide( out, arr, y, clbk ) { } } } else { + yNumeric = ( typeof y === 'number' ); for ( i = 0; i < len; i++ ) { arrVal = clbk( arr[ i ], i ); - if ( typeof arrVal === 'number' ) { + if ( yNumeric && typeof arrVal === 'number' ) { out[ i ] = arrVal / y; } else { out[ i ] = NaN; diff --git a/lib/array.js b/lib/array.js index db038b1..57ddb42 100644 --- a/lib/array.js +++ b/lib/array.js @@ -18,7 +18,8 @@ var isArrayLike = require( 'validate.io-array-like' ); */ function divide( out, arr, y ) { var len = arr.length, - i; + i, + yNumeric; if ( isArrayLike( y ) ) { if ( len !== y.length ) { @@ -32,8 +33,9 @@ function divide( out, arr, y ) { } } } else { + yNumeric = ( typeof y === 'number' ); for ( i = 0; i < len; i++ ) { - if ( typeof arr[ i ] === 'number' ) { + if ( yNumeric && typeof arr[ i ] === 'number' ) { out[ i ] = arr[ i ] / y; } else { out[ i ] = NaN; diff --git a/lib/deepset.js b/lib/deepset.js index f8e3e78..d334cf8 100644 --- a/lib/deepset.js +++ b/lib/deepset.js @@ -14,6 +14,7 @@ var isArrayLike = require( 'validate.io-array-like' ), * Computes an element-wise division or each element and deep sets the input array. * * @param {Array} arr - input array +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Number} y - either an array of equal length or a scalar * @param {String} path - key path used when deep getting and setting * @param {String} [sep] - key path separator * @returns {Array} input array @@ -23,7 +24,8 @@ function divide( x, y, path, sep ) { opts = {}, dget, dset, - v, i; + v, i, + yNumeric; if ( arguments.length > 3 ) { opts.sep = sep; @@ -41,9 +43,10 @@ function divide( x, y, path, sep ) { } } } else { + yNumeric = ( typeof y === 'number' ); for ( i = 0; i < len; i++ ) { v = dget( x[ i ] ); - if ( typeof v === 'number' ) { + if ( yNumeric && typeof v === 'number' ) { dset( x[ i ], v / y ); } else { dset( x[ i ], NaN ); diff --git a/lib/index.js b/lib/index.js index 3c34bf1..f52be87 100644 --- a/lib/index.js +++ b/lib/index.js @@ -19,6 +19,21 @@ var divide1 = require( './array.js' ), divide3 = require( './deepset.js' ), divide4 = require( './matrix.js' ); +/** +* FUNCTION: fill( n, val ) +* Creates an array of length n and fills it with the supplied value +* @param {Number} n - array length +* @param {*} val - value to fill the array with +* @returns {Array} array of length n +*/ +function fill( n, val ) { + var ret = new Array( n ); + for ( var i = 0; i < n; i++ ) { + ret[ i ] = val; + } + return ret; +} + // DIVIDE // @@ -44,9 +59,24 @@ function divide( x, y, options ) { out, dt, d; + // Handle cases when first argument is a number if ( isNumber( x ) || isnan( x ) ) { + for ( var key in options ) { + if ( key !== 'dtype' ){ + throw new Error( 'divide()::only dtype option is applicable when first argument is not array- or matrix-like. Keys: `' + Object.keys( options ) + '`.' ); + } + } + if ( isMatrixLike( y ) ) { + // Create a matrix holding x's: + d = new Float64Array( fill( y.length, x ) ); + x = matrix( d, y.shape, 'float64' ); + return options ? divide( x, y, options ) : divide( x, y ); + } + if ( isArrayLike( y ) ) { + return divide( fill( y.length, x ), y ); + } if ( !isNumber( y ) ) { - throw new TypeError( 'divide()::invalid input argument. Second argument must a number primitive when x is a number. Value: `' + x + '`.' ); + return NaN; } return x / y; } @@ -57,9 +87,6 @@ function divide( x, y, options ) { } } if ( isMatrixLike( x ) ) { - if ( !isMatrixLike( y ) && !isNumber( y ) ) { - throw new TypeError( 'divide()::invalid input argument. Second argument must be matrix-like or a number primitive when x is a matrix. Value: `' + x + '`.' ); - } if ( opts.copy !== false ) { dt = opts.dtype || 'float64'; ctor = ctors( dt ); @@ -75,9 +102,6 @@ function divide( x, y, options ) { return divide4( out, x, y ); } if ( isArrayLike( x ) ) { - if ( !isArrayLike( y ) && !isNumber( y ) ) { - throw new TypeError( 'divide()::invalid input argument. Second argument must be array-like or a number primitive when x is an array. Value: `' + x + '`.' ); - } // Handle deepset first... if ( opts.path ) { opts.sep = opts.sep || '.'; From 902c2d74a5f96bb354bdc7060526c455cf62e0bd Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Thu, 25 Jun 2015 18:03:07 +0200 Subject: [PATCH 06/10] [UPDATE] tests --- test/test.js | 67 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/test/test.js b/test/test.js index d0ed85c..c128b83 100644 --- a/test/test.js +++ b/test/test.js @@ -90,17 +90,11 @@ describe( 'compute-divide', function tests() { } }); - - it( 'should throw an error if provided an array and an unsupported second factor', function test() { + it( 'should throw an error if provided a number as the first argument and an not applicable option', function test() { var values = [ - '5', - true, - undefined, - null, - NaN, - {}, - function(){}, - matrix( [2,2] ) + {'accessor': function getValue( d ) { return d; } }, + {'copy': false}, + {'path': 'x'}, ]; for ( var i = 0; i < values.length; i++ ) { @@ -108,34 +102,29 @@ describe( 'compute-divide', function tests() { } function badValue( value ) { return function() { - divide( [1,2,3], value ); + divide( 12, [1,2,3], value ); }; } }); - it( 'should throw an error if provided a matrix and an unsupported second factor', function test() { + it( 'should return NaN if the first argument is neither a number, array-like, or matrix-like', function test() { var values = [ - '5', + // '5', // valid as is array-like (length) true, undefined, null, NaN, - {}, function(){}, - [] + {} ]; for ( var i = 0; i < values.length; i++ ) { - expect( badValue( values[i] ) ).to.throw( Error ); - } - function badValue( value ) { - return function() { - divide( matrix( [2,2] ), value ); - }; + assert.isTrue( isnan( divide( values[ i ], 1 ) ) ); } }); - it( 'should return NaN if the first argument is neither a number, array-like, or matrix-like', function test() { + + it( 'should return NaN if the first argument is a number and the second argument is neither numberic, array-like, or matrix-like', function test() { var values = [ // '5', // valid as is array-like (length) true, @@ -147,7 +136,7 @@ describe( 'compute-divide', function tests() { ]; for ( var i = 0; i < values.length; i++ ) { - assert.isTrue( isnan( divide( values[ i ], 1 ) ) ); + assert.isTrue( isnan( divide( 8, values[ i ] ) ) ); } }); @@ -156,11 +145,33 @@ describe( 'compute-divide', function tests() { assert.strictEqual( divide( 20, 10 ), 2 ); }); - it( 'should throw an error if provided a number and an array as the second argument', function test() { - expect( foo ).to.throw( Error ); - function foo() { - divide( 2, [ 1, 1 ]); - } + it( 'should calculate the division of a scalar and an array when the argument order is reversed', function test() { + var data, actual, expected; + data = [ 1, 2 ]; + actual = divide( 4, data ); + expected = [ 4, 2 ]; + assert.deepEqual( actual, expected ); + }); + + it( 'should calculate the division of a scalar and a matrix when the argument order is reversed', function test() { + var data, actual, expected; + data = matrix( new Int8Array( [ 1,2,3,4 ] ), [2,2] ); + actual = divide( 12, data ); + expected = matrix( new Float64Array( [12,6,4,3] ), [2,2] ); + + assert.deepEqual( actual.data, expected.data ); + }); + + it( 'should calculate the division of a scalar and a matrix and cast to a different dtype when the argument order is reversed', function test() { + var data, actual, expected; + data = matrix( new Int8Array( [ 1,2,3,4 ] ), [2,2] ); + actual = divide( 12, data, { + 'dtype': 'int32' + }); + expected = matrix( new Int32Array( [12,6,4,3] ), [2,2] ); + + assert.strictEqual( actual.dtype, 'int32' ); + assert.deepEqual( actual.data, expected.data ); }); it( 'should perform an element-wise division when provided a plain array and a scalar', function test() { From fb047d86761b1170c65137f7d247340096279300 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Thu, 25 Jun 2015 18:03:21 +0200 Subject: [PATCH 07/10] [UPDATE] README.md + examples --- README.md | 294 +++++++++++++++++++++++++++++++++++++++++----- examples/index.js | 82 ++++++++++++- 2 files changed, 339 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 542a316..f0f5563 100644 --- a/README.md +++ b/README.md @@ -20,41 +20,73 @@ For use in the browser, use [browserify](https://github.com/substack/node-browse var divide = require( 'compute-divide' ); ``` -#### divide( arr, x[, opts] ) +#### divide( x, y[, opts] ) -Computes an element-wise division. `x` may be either an `array` of equal length or a `numeric` value. +Computes an element-wise division. `x` can be a [`number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), [`array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), [`typed array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays) or [`matrix`](https://github.com/dstructs/matrix). `y` has to be either an `array` or `matrix` of equal dimensions as `x` or a single number. The function returns either an `array` with length equal to that of the input `array`, a `matrix` with equal dimensions as input `x` or a single number. ``` javascript -var arr = [ 2, 1, 4, 2 ], - out; +var matrix = require( 'dstructs-matrix' ), + data, + mat, + out, + i; -out = divide( arr, 2 ); -// returns [ 1, 0.5, 2, 1 ] +out = divide( 6, 3 ); +// returns 2 -out = divide( arr, [ 1, 2, 8, 8 ] ); -// returns [ 2, 1, 0.5, 0.25 ] -``` +out = divide( 8, 2 ); +// returns 4 -The function accepts the following `options`: +data = [ 2, 4, 6 ]; +out = divide( data, 2 ); +// returns [ 1, 2, 3 ] -* __copy__: `boolean` indicating whether to return a new `array`. Default: `true`. -* __accessor__: accessor `function` for accessing values in object `arrays`. +data = [ 1, 2, 3 ]; +out = divide( 6, data ); +// returns [ 6, 3, 2 ] -To mutate the input `array` (e.g., when input values can be discarded or when optimizing memory usage), set the `copy` option to `false`. +data = [ 12, 6, 4 ]; +out = divide( data, [ 6, 3, 2 ] ) +// returns [ 2, 2, 2 ] -``` javascript -var arr = [ 5, 3, 8, 3, 2 ]; -var out = divide( arr, 4, { - 'copy': false -}); -// returns [ 1.25, 0.75, 0.5, 0.75, 0.5 ] +data = new Int8Array( [2,4,6] ); +out = divide( data, 2 ); +// returns Float64Array( [1,2,3] ) -console.log( arr === out ); -// returns true +data = new Int16Array( 6 ); +for ( i = 0; i < 6; i++ ) { + data[ i ] = i; +} +mat = matrix( data, [3,2], 'int16' ); +/* + [ 0 1 + 2 3 + 4 5 ] +*/ + +out = divide( mat, 2 ); +/* + [ 0 0.5 + 1 1.5 + 2 2.5 ] +*/ + +out = divide( mat, mat ); +/* + [ NaN 1 + 1 1 + 1 1 ] +*/ ``` -__Note__: mutation is the `array` equivalent of a __slash-equal__ (`/=`). +The function accepts the following `options`: + +* __accessor__: accessor `function` for accessing `array` values. +* __dtype__: output [`typed array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays) or [`matrix`](https://github.com/dstructs/matrix) data type. Default: `float64`. +* __copy__: `boolean` indicating if the `function` should return a new data structure. Default: `true`. +* __path__: [deepget](https://github.com/kgryte/utils-deep-get)/[deepset](https://github.com/kgryte/utils-deep-set) key path. +* __sep__: [deepget](https://github.com/kgryte/utils-deep-get)/[deepset](https://github.com/kgryte/utils-deep-set) key path separator. Default: `'.'`. For object `arrays`, provide an accessor `function` for accessing `array` values. @@ -71,10 +103,10 @@ function getValue( d, i ) { return d[ 1 ]; } -var out = divide( data, 4, { +var out = divide( data, 2, { 'accessor': getValue }); -// returns [ 1.25, 0.75, 2, 0.75, 0.5 ] +// returns [ 2.5, 1.5, 4, 1.5, 1 ] ``` When dividing values between two object `arrays`, provide an accessor `function` which accepts `3` arguments. @@ -112,21 +144,219 @@ var out = divide( data, arr, { __Note__: `j` corresponds to the input `array` index, where `j=0` is the index for the first input `array` and `j=1` is the index for the second input `array`. +To [deepset](https://github.com/kgryte/utils-deep-set) an object `array`, provide a key path and, optionally, a key path separator. +``` javascript +var data = [ + {'x':[0,2]}, + {'x':[1,3]}, + {'x':[2,5]}, + {'x':[3,7]}, + {'x':[4,11]} +]; -## Examples +var out = divide( data, 2, 'x|1', '|' ); +/* + [ + {'x':[0,1]}, + {'x':[1,1.5]}, + {'x':[2,2.5]}, + {'x':[3,3.5}, + {'x':[4,5.5]} + ] +*/ + +var bool = ( data === out ); +// returns true +``` + +By default, when provided a [`typed array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays) or [`matrix`](https://github.com/dstructs/matrix), the output data structure is `float64` in order to preserve precision. To specify a different data type, set the `dtype` option (see [`matrix`](https://github.com/dstructs/matrix) for a list of acceptable data types). ``` javascript -var divide = require( 'compute-divide' ); +var data, out; + +data = new Int8Array( [ 4, 8, 12 ] ); + +out = divide( data, 2, { + 'dtype': 'int32' +}); +// returns Int32Array( [2,4,6] ) + +// Works for plain arrays, as well... +out = divide( [ 4, 8, 12 ], 2, { + 'dtype': 'uint8' +}); +// returns Uint8Array( [2,4,6] ) +``` + +By default, the function returns a new data structure. To mutate the input data structure, set the `copy` option to `false`. + +``` javascript +var data, + bool, + mat, + out, + i; + +data = [ 4, 8, 12 ]; + +out = divide( data, 2, { + 'copy': false +}); +// returns [ 2, 4, 6 ] + +bool = ( data === out ); +// returns true + +data = new Int16Array( 6 ); +for ( i = 0; i < 6; i++ ) { + data[ i ] = i; +} +mat = matrix( data, [3,2], 'int16' ); +/* + [ 0 1 + 2 3 + 4 5 ] +*/ + +out = divide( mat, 2, { + 'copy': false +}); +/* + [ 0 0.5 + 1 1.5 + 2 2.5 ] +*/ + +bool = ( mat === out ); +// returns true +``` + +__Note__: mutation is the `array` or `matrix` equivalent of a __slash-equal__ (`/=`). + + +## Notes + +* If an element is __not__ a numeric value, the result of the division is `NaN`. + + ``` javascript + var data, out; + + out = divide( null, 1 ); + // returns NaN + + out = divide( true, 1 ); + // returns NaN + + out = divide( {'a':'b'}, 1 ); + // returns NaN + + out = divide( [ true, null, [] ], 1 ); + // returns [ NaN, NaN, NaN ] + + function getValue( d, i ) { + return d.x; + } + data = [ + {'x':true}, + {'x':[]}, + {'x':{}}, + {'x':null} + ]; + + out = divide( data, 1, { + 'accessor': getValue + }); + // returns [ NaN, NaN, NaN, NaN ] + + out = divide( data, 1, { + 'path': 'x' + }); + /* + [ + {'x':NaN}, + {'x':NaN}, + {'x':NaN, + {'x':NaN} + ] + */ + ``` + +* Be careful when providing a data structure which contains non-numeric elements and specifying an `integer` output data type, as `NaN` values are cast to `0`. + + ``` javascript + var out = multiply( [ true, null, [] ], 1, { + 'dtype': 'int8' + }); + // returns Int8Array( [0,0,0] ); + ``` + -var data = new Array( 100 ); -for ( var i = 0; i < data.length; i++ ) { +## Examples + +``` javascript +var matrix = require( 'dstructs-matrix' ), + divide = require( 'compute-divide' ); + +var data, + mat, + out, + tmp, + i; + +// Plain arrays... +data = new Array( 10 ); +for ( i = 0; i < data.length; i++ ) { data[ i ] = Math.round( Math.random()*100 ); } +out = divide( data, 10 ); -var out = divide( data, 10 ); +// Object arrays (accessors)... +function getValue( d ) { + return d.x; +} +for ( i = 0; i < data.length; i++ ) { + data[ i ] = { + 'x': data[ i ] + }; +} +out = divide( data, 10, { + 'accessor': getValue +}); -console.log( out.join( '\n' ) ); +// Deep set arrays... +for ( i = 0; i < data.length; i++ ) { + data[ i ] = { + 'x': [ i, data[ i ].x ] + }; +} +out = divide( data, 10, { + 'path': 'x/1', + 'sep': '/' +}); + +// Typed arrays... +data = new Int32Array( 10 ); +for ( i = 0; i < data.length; i++ ) { + data[ i ] = Math.random() * 100; +} +tmp = divide( data, 10 ); +out = ''; +for ( i = 0; i < data.length; i++ ) { + out += tmp[ i ]; + if ( i < data.length-1 ) { + out += ','; + } +} + +// Matrices... +mat = matrix( data, [5,2], 'int32' ); +out = divide( mat, 10 ); + +// Matrices (custom output data type)... +out = divide( mat, 10, { + 'dtype': 'uint8' +}); ``` To run the example code from the top-level application directory, @@ -167,12 +397,12 @@ $ make view-cov --- ## License -[MIT license](http://opensource.org/licenses/MIT). +[MIT license](http://opensource.org/licenses/MIT). ## Copyright -Copyright © 2015. The Compute.io Authors. +Copyright © 2014-2015. The [Compute.io](https://github.com/compute-io) Authors. [npm-image]: http://img.shields.io/npm/v/compute-divide.svg diff --git a/examples/index.js b/examples/index.js index 3a934c4..0166332 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,11 +1,83 @@ 'use strict'; -var divide = require( './../lib' ); +var matrix = require( 'dstructs-matrix' ), + divide = require( './../lib' ); -var data = new Array( 100 ); -for ( var i = 0; i < data.length; i++ ) { +var data, + mat, + out, + tmp, + i; + +// ---- +// Plain arrays... +data = new Array( 10 ); +for ( i = 0; i < data.length; i++ ) { data[ i ] = Math.round( Math.random()*100 ); } -var out = divide( data, 10 ); +out = divide( data, 10 ); +console.log( 'Arrays: %s\n', out ); + + +// ---- +// Object arrays (accessors)... +function getValue( d ) { + return d.x; +} +for ( i = 0; i < data.length; i++ ) { + data[ i ] = { + 'x': data[ i ] + }; +} +out = divide( data, 10, { + 'accessor': getValue +}); +console.log( 'Accessors: %s\n', out ); + + +// ---- +// Deep set arrays... +for ( i = 0; i < data.length; i++ ) { + data[ i ] = { + 'x': [ i, data[ i ].x ] + }; +} +out = divide( data, 10, { + 'path': 'x/1', + 'sep': '/' +}); +console.log( 'Deepset:'); +console.dir( out ); +console.log( '\n' ); + + +// ---- +// Typed arrays... +data = new Int32Array( 10 ); +for ( i = 0; i < data.length; i++ ) { + data[ i ] = Math.random() * 100; +} +tmp = divide( data, 10 ); +out = ''; +for ( i = 0; i < data.length; i++ ) { + out += tmp[ i ]; + if ( i < data.length-1 ) { + out += ','; + } +} +console.log( 'Typed arrays: %s\n', out ); + + +// ---- +// Matrices... +mat = matrix( data, [5,2], 'int32' ); +out = divide( mat, 10 ); +console.log( 'Matrix: %s\n', out.toString() ); + -console.log( out.join( '\n' ) ); +// ---- +// Matrices (custom output data type)... +out = divide( mat, 10, { + 'dtype': 'uint8' +}); +console.log( 'Matrix (%s): %s\n', out.dtype, out.toString() ); From 5fad2ce61c082a5cd98e798e0af582249f3ac004 Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Thu, 25 Jun 2015 21:15:36 +0200 Subject: [PATCH 08/10] [UPDATE] full code coverage, README --- README.md | 18 ++++++++++++++++-- lib/index.js | 2 +- test/test.js | 16 +++++++++++++--- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f0f5563..259867a 100644 --- a/README.md +++ b/README.md @@ -285,12 +285,26 @@ __Note__: mutation is the `array` or `matrix` equivalent of a __slash-equal__ (` * Be careful when providing a data structure which contains non-numeric elements and specifying an `integer` output data type, as `NaN` values are cast to `0`. ``` javascript - var out = multiply( [ true, null, [] ], 1, { + var out = divide( [ true, null, [] ], 1, { 'dtype': 'int8' }); - // returns Int8Array( [0,0,0] ); + // returns Int8Array( [0,0,0] ) ``` +* When calling the function with a numeric value as the first argument and a `matrix` or `array` as the second argument, only the `dtype` option is applicable. + ``` javascript + // Valid: + var out = divide( 4, [ 1, 2, 3 ], { + 'dtype': 'int8' + }); + // returns Int8Array( [4,2,1] ) + + // Not valid: + var out = add( 4, [ 1, 2, 3 ], { + 'copy': false + }); + // throws an error + ``` ## Examples diff --git a/lib/index.js b/lib/index.js index f52be87..fe25520 100644 --- a/lib/index.js +++ b/lib/index.js @@ -73,7 +73,7 @@ function divide( x, y, options ) { return options ? divide( x, y, options ) : divide( x, y ); } if ( isArrayLike( y ) ) { - return divide( fill( y.length, x ), y ); + return options ? divide( fill( y.length, x ), y, options ) : divide( fill( y.length, x ), y ); } if ( !isNumber( y ) ) { return NaN; diff --git a/test/test.js b/test/test.js index c128b83..e36774a 100644 --- a/test/test.js +++ b/test/test.js @@ -145,7 +145,7 @@ describe( 'compute-divide', function tests() { assert.strictEqual( divide( 20, 10 ), 2 ); }); - it( 'should calculate the division of a scalar and an array when the argument order is reversed', function test() { + it( 'should calculate the division of a scalar by an array', function test() { var data, actual, expected; data = [ 1, 2 ]; actual = divide( 4, data ); @@ -153,7 +153,17 @@ describe( 'compute-divide', function tests() { assert.deepEqual( actual, expected ); }); - it( 'should calculate the division of a scalar and a matrix when the argument order is reversed', function test() { + it( 'should calculate the division of a scalar by an array and cast result to a different dtype', function test() { + var data, actual, expected; + data = [ 1, 2 ]; + actual = divide( 10, data, { + 'dtype':'int32' + }); + expected = new Int32Array( [10,5] ); + assert.deepEqual( actual, expected ); + }); + + it( 'should calculate the division of a scalar by a matrix', function test() { var data, actual, expected; data = matrix( new Int8Array( [ 1,2,3,4 ] ), [2,2] ); actual = divide( 12, data ); @@ -162,7 +172,7 @@ describe( 'compute-divide', function tests() { assert.deepEqual( actual.data, expected.data ); }); - it( 'should calculate the division of a scalar and a matrix and cast to a different dtype when the argument order is reversed', function test() { + it( 'should calculate the division of a scalar by a matrix and cast to a different dtype', function test() { var data, actual, expected; data = matrix( new Int8Array( [ 1,2,3,4 ] ), [2,2] ); actual = divide( 12, data, { From c555f345cf023526bb0b173a533aec1a29a9c128 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 25 Jun 2015 21:17:22 +0200 Subject: [PATCH 09/10] [FIX] copy-paste err --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 259867a..9c57b36 100644 --- a/README.md +++ b/README.md @@ -300,7 +300,7 @@ __Note__: mutation is the `array` or `matrix` equivalent of a __slash-equal__ (` // returns Int8Array( [4,2,1] ) // Not valid: - var out = add( 4, [ 1, 2, 3 ], { + var out = divide( 4, [ 1, 2, 3 ], { 'copy': false }); // throws an error From 3b582c56488396363c60114c1e7b51809504f2ec Mon Sep 17 00:00:00 2001 From: Planeshifter Date: Fri, 26 Jun 2015 14:25:13 +0200 Subject: [PATCH 10/10] [UPDATE] refactoring to handle typed arrays --- lib/accessor.js | 34 +++++++++++---- lib/array.js | 34 +++++++++++---- lib/deepset.js | 28 +++++++++---- lib/index.js | 24 ++++++++--- lib/typedarray.js | 59 ++++++++++++++++++++++++++ package.json | 3 +- test/test.accessor.js | 34 +++++++++++++-- test/test.array.js | 26 ++++++++++++ test/test.deepset.js | 38 ++++++++++++++++- test/test.js | 18 ++++++++ test/test.typedarray.js | 93 +++++++++++++++++++++++++++++++++++++++++ 11 files changed, 355 insertions(+), 36 deletions(-) create mode 100644 lib/typedarray.js create mode 100644 test/test.typedarray.js diff --git a/lib/accessor.js b/lib/accessor.js index ad03f23..ef10d6c 100644 --- a/lib/accessor.js +++ b/lib/accessor.js @@ -3,6 +3,7 @@ // MODULES // var isArrayLike = require( 'validate.io-array-like' ), + isTypedArrayLike = require( 'validate.io-typed-array-like' ), isObject = require( 'validate.io-object' ); @@ -21,9 +22,21 @@ var isArrayLike = require( 'validate.io-array-like' ), function divide( out, arr, y, clbk ) { var len = arr.length, i, - arrVal, yVal, yNumeric; + arrVal, yVal; - if ( isArrayLike( y ) ) { + if ( isTypedArrayLike( y ) ) { + if ( len !== y.length ) { + throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); + } + for ( i = 0; i < len; i++ ) { + arrVal = clbk( arr[ i ], i, 0 ); + if ( typeof arrVal === 'number' ) { + out[ i ] = arrVal / y[ i ]; + } else { + out[ i ] = NaN; + } + } + } else if ( isArrayLike( y ) ) { if ( len !== y.length ) { throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); } @@ -50,12 +63,17 @@ function divide( out, arr, y, clbk ) { } } } else { - yNumeric = ( typeof y === 'number' ); - for ( i = 0; i < len; i++ ) { - arrVal = clbk( arr[ i ], i ); - if ( yNumeric && typeof arrVal === 'number' ) { - out[ i ] = arrVal / y; - } else { + if ( typeof y === 'number' ) { + for ( i = 0; i < len; i++ ) { + arrVal = clbk( arr[ i ], i ); + if ( typeof arrVal === 'number' ) { + out[ i ] = arrVal / y; + } else { + out[ i ] = NaN; + } + } + } else { + for ( i = 0; i < len; i++ ) { out[ i ] = NaN; } } diff --git a/lib/array.js b/lib/array.js index 57ddb42..e65264c 100644 --- a/lib/array.js +++ b/lib/array.js @@ -2,7 +2,8 @@ // MODULES // -var isArrayLike = require( 'validate.io-array-like' ); +var isArrayLike = require( 'validate.io-array-like' ), + isTypedArrayLike = require( 'validate.io-typed-array-like' ); // DIVIDE // @@ -18,29 +19,44 @@ var isArrayLike = require( 'validate.io-array-like' ); */ function divide( out, arr, y ) { var len = arr.length, - i, - yNumeric; + i; - if ( isArrayLike( y ) ) { + if ( isTypedArrayLike( y ) ) { if ( len !== y.length ) { throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); } for ( i = 0; i < len; i++ ) { - if ( typeof y[ i ] === 'number' && typeof arr[ i ] === 'number' ) { + if ( typeof arr[ i ] === 'number' ) { out[ i ] = arr[ i ] / y[ i ]; } else { out[ i ] = NaN; } } - } else { - yNumeric = ( typeof y === 'number' ); + } else if ( isArrayLike( y ) ) { + if ( len !== y.length ) { + throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); + } for ( i = 0; i < len; i++ ) { - if ( yNumeric && typeof arr[ i ] === 'number' ) { - out[ i ] = arr[ i ] / y; + if ( typeof y[ i ] === 'number' && typeof arr[ i ] === 'number' ) { + out[ i ] = arr[ i ] / y[ i ]; } else { out[ i ] = NaN; } } + } else { + if ( typeof y === 'number' ) { + for ( i = 0; i < len; i++ ) { + if ( typeof arr[ i ] === 'number' ) { + out[ i ] = arr[ i ] / y; + } else { + out[ i ] = NaN; + } + } + } else { + for ( i = 0; i < len; i++ ) { + out[ i ] = NaN; + } + } } return out; } // end FUNCTION divide() diff --git a/lib/deepset.js b/lib/deepset.js index d334cf8..f9cd0c3 100644 --- a/lib/deepset.js +++ b/lib/deepset.js @@ -3,6 +3,7 @@ // MODULES // var isArrayLike = require( 'validate.io-array-like' ), + isTypedArrayLike = require( 'validate.io-typed-array-like' ), deepSet = require( 'utils-deep-set' ).factory, deepGet = require( 'utils-deep-get' ).factory; @@ -24,8 +25,7 @@ function divide( x, y, path, sep ) { opts = {}, dget, dset, - v, i, - yNumeric; + v, i; if ( arguments.length > 3 ) { opts.sep = sep; @@ -33,7 +33,7 @@ function divide( x, y, path, sep ) { if ( len ) { dget = deepGet( path, opts ); dset = deepSet( path, opts ); - if ( isArrayLike( y ) ) { + if ( isTypedArrayLike( y ) ) { for ( i = 0; i < len; i++ ) { v = dget( x[ i ] ); if ( typeof v === 'number' ) { @@ -42,16 +42,30 @@ function divide( x, y, path, sep ) { dset( x[ i ], NaN ); } } - } else { - yNumeric = ( typeof y === 'number' ); + } else if ( isArrayLike( y ) ) { for ( i = 0; i < len; i++ ) { v = dget( x[ i ] ); - if ( yNumeric && typeof v === 'number' ) { - dset( x[ i ], v / y ); + if ( typeof v === 'number' && typeof y[ i ] === 'number' ) { + dset( x[ i ], v / y[ i ] ); } else { dset( x[ i ], NaN ); } } + } else { + if ( typeof y === 'number' ) { + for ( i = 0; i < len; i++ ) { + v = dget( x[ i ] ); + if ( typeof v === 'number' ) { + dset( x[ i ], v / y ); + } else { + dset( x[ i ], NaN ); + } + } + } else { + for ( i = 0; i < len; i++ ) { + dset( x[ i ], NaN ); + } + } } } return x; diff --git a/lib/index.js b/lib/index.js index fe25520..6302ae0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -6,6 +6,7 @@ var isNumber = require( 'validate.io-number-primitive' ), isnan = require( 'validate.io-nan' ), isArray = require( 'validate.io-array' ), isArrayLike = require( 'validate.io-array-like' ), + isTypedArrayLike = require( 'validate.io-typed-array-like' ), isMatrixLike = require( 'validate.io-matrix-like' ), ctors = require( 'compute-array-constructors' ), matrix = require( 'dstructs-matrix' ), @@ -17,7 +18,8 @@ var isNumber = require( 'validate.io-number-primitive' ), var divide1 = require( './array.js' ), divide2 = require( './accessor.js' ), divide3 = require( './deepset.js' ), - divide4 = require( './matrix.js' ); + divide4 = require( './matrix.js' ), + divide5 = require( './typedarray.js' ); /** * FUNCTION: fill( n, val ) @@ -101,19 +103,31 @@ function divide( x, y, options ) { } return divide4( out, x, y ); } + if ( isTypedArrayLike( x ) ) { + if ( opts.copy === false ) { + out = x; + } else { + dt = opts.dtype || 'float64'; + ctor = ctors( dt ); + if ( ctor === null ) { + throw new Error( 'divide()::invalid option. Data type option does not have a corresponding array constructor. Option: `' + dt + '`.' ); + } + out = new ctor( x.length ); + } + return divide5( out, x, y ); + } if ( isArrayLike( x ) ) { // Handle deepset first... if ( opts.path ) { opts.sep = opts.sep || '.'; return divide3( x, y, opts.path, opts.sep ); } - // Handle regular, typed, and accessor arrays next... + // Handle regular and accessor arrays next... if ( opts.copy === false ) { out = x; } - else if ( opts.dtype || !isArray( x ) ) { - dt = opts.dtype || 'float64'; - ctor = ctors( dt ); + else if ( opts.dtype ) { + ctor = ctors( opts.dtype ); if ( ctor === null ) { throw new TypeError( 'divide()::invalid input argument. Unrecognized/unsupported array-like object. Provide either a plain or typed array. Value: `' + x + '`.' ); } diff --git a/lib/typedarray.js b/lib/typedarray.js new file mode 100644 index 0000000..3165cb7 --- /dev/null +++ b/lib/typedarray.js @@ -0,0 +1,59 @@ +'use strict'; + +// MODULES // + +var isArrayLike = require( 'validate.io-array-like' ), + isTypedArrayLike = require( 'validate.io-typed-array-like' ); + + +// DIVIDE // + +/** +* FUNCTION: divide( out, arr, y ) +* Computes an element-wise division of a typed array. +* +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} out - output array +* @param {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} arr - input array +* @param {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Number} y - either an array of equal length or a scalar +* @returns {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} output array +*/ +function divide( out, arr, y ) { + var len = arr.length, + i; + + if ( isTypedArrayLike( y ) ) { + if ( len !== y.length ) { + throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); + } + for ( i = 0; i < len; i++ ) { + out[ i ] = arr[ i ] / y[ i ]; + } + } else if ( isArrayLike( y ) ) { + if ( len !== y.length ) { + throw new Error( 'divide()::invalid input argument. Divisor array must have a length equal to that of the input array.' ); + } + for ( i = 0; i < len; i++ ) { + if ( typeof y[ i ] === 'number' ) { + out[ i ] = arr[ i ] / y[ i ]; + } else { + out[ i ] = NaN; + } + } + } else { + if ( typeof y === 'number' ) { + for ( i = 0; i < len; i++ ) { + out[ i ] = arr[ i ] / y; + } + } else { + for ( i = 0; i < len; i++ ) { + out[ i ] = NaN; + } + } + } + return out; +} // end FUNCTION divide() + + +// EXPORTS // + +module.exports = divide; diff --git a/package.json b/package.json index 46e9b2c..c506ca7 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,8 @@ "validate.io-nan": "^1.0.3", "validate.io-number-primitive": "^1.0.0", "validate.io-object": "^1.0.4", - "validate.io-string-primitive": "^1.0.0" + "validate.io-string-primitive": "^1.0.0", + "validate.io-typed-array-like": "^1.0.0" }, "devDependencies": { "chai": "3.x.x", diff --git a/test/test.accessor.js b/test/test.accessor.js index 73c0fe8..cfeb14e 100644 --- a/test/test.accessor.js +++ b/test/test.accessor.js @@ -147,15 +147,27 @@ describe( 'accessor divide', function tests() { assert.deepEqual( actual, expected ); + // single non-numeric value + y = false; + actual = new Array( data.length ); + actual = divide( actual, data, y, getValue ); + expected = [ NaN, NaN, NaN ]; + + assert.deepEqual( actual, expected ); + // numeric array y = [ 1, 2, 3 ]; actual = new Array( data.length ); actual = divide( actual, data, y, getValue ); expected = [ 1, NaN, 1 ]; - function getValue( d, i ) { - return d.x; - } + // typed array + y = new Int32Array( [1,2,3] ); + actual = new Array( data.length ); + actual = divide( actual, data, y, getValue ); + expected = [ 1, NaN, 1 ]; + + assert.deepEqual( actual, expected ); // object array y = [ @@ -167,6 +179,10 @@ describe( 'accessor divide', function tests() { actual = divide( actual, data, y, getValue2 ); expected = [ 1, NaN, 1 ]; + function getValue( d, i ) { + return d.x; + } + function getValue2( d, i, j ) { if ( j === 0 ) { return d.x; @@ -177,7 +193,7 @@ describe( 'accessor divide', function tests() { }); - it( 'should throw an error if provided an array to be multiplied which is not of equal length to the input array', function test() { + it( 'should throw an error if dividing by a matrix which is not of equal length to the input array', function test() { expect( foo ).to.throw( Error ); function foo() { divide( [], [1,2], [1,2,3], getValue ); @@ -187,4 +203,14 @@ describe( 'accessor divide', function tests() { } }); + it( 'should throw an error if dividing by a typed array which is not of equal length to the input array', function test() { + expect( foo ).to.throw( Error ); + function foo() { + divide( [], [1,2], new Int32Array( [1,2,3] ), getValue ); + } + function getValue( d ) { + return d; + } + }); + }); diff --git a/test/test.array.js b/test/test.array.js index 0f48922..9af51d4 100644 --- a/test/test.array.js +++ b/test/test.array.js @@ -98,6 +98,11 @@ describe( 'array divide', function tests() { expected = new Int32Array( expected ); assert.deepEqual( actual, expected ); + + // y being a typed array + y = new Int32Array( y ); + actual = divide( actual, data, y ); + assert.deepEqual( actual, expected ); }); it( 'should return an empty array if provided an empty array', function test() { @@ -123,6 +128,22 @@ describe( 'array divide', function tests() { expected = [ NaN, NaN, NaN, NaN ]; assert.deepEqual( actual, expected ); + + data = [ 1, 2, 3 ]; + y = null; + actual = new Array( data.length ); + actual = divide( actual, data, y ); + expected = [ NaN, NaN, NaN ]; + + assert.deepEqual( actual, expected ); + + data = [ 1, null, 3 ]; + y = new Int32Array( [1,2,3] ); + actual = new Array( data.length ); + actual = divide( actual, data, y ); + expected = [ 1, NaN, 1 ]; + + assert.deepEqual( actual, expected ); }); it( 'should throw an error if provided an array to be divided which is not of equal length to the input array', function test() { @@ -130,6 +151,11 @@ describe( 'array divide', function tests() { function foo() { divide( [], [1,2], [1,2,3] ); } + + expect( foo2 ).to.throw( Error ); + function foo2() { + divide( [], [1,2], new Int32Array( [1,2,3] ) ); + } }); }); diff --git a/test/test.deepset.js b/test/test.deepset.js index b40a35f..4f7f429 100644 --- a/test/test.deepset.js +++ b/test/test.deepset.js @@ -117,7 +117,24 @@ describe( 'deepset divide', function tests() { it( 'should handle non-numeric values by setting the element to NaN', function test() { var data, actual, expected, y; - // multiplying with a scalar + + // dividing by a non-numeric value + data = [ + {'x':[9,null]}, + {'x':[9,1]}, + {'x':[9,true]}, + {'x':[9,3]} + ]; + actual = divide( data, null, 'x.1' ); + expected = [ + {'x':[9,NaN]}, + {'x':[9,NaN]}, + {'x':[9,NaN]}, + {'x':[9,NaN]} + ]; + assert.deepEqual( data, expected ); + + // dividing by a scalar data = [ {'x':[9,null]}, {'x':[9,1]}, @@ -133,7 +150,7 @@ describe( 'deepset divide', function tests() { ]; assert.deepEqual( data, expected ); - // multiplying with an array + // dividing by an array data = [ {'x':[9,null]}, {'x':[9,1]}, @@ -149,6 +166,23 @@ describe( 'deepset divide', function tests() { {'x':[9,1]} ]; assert.deepEqual( data, expected ); + + // dividing by a typed array + data = [ + {'x':[9,null]}, + {'x':[9,1]}, + {'x':[9,true]}, + {'x':[9,3]} + ]; + y = new Int32Array( [0,1,2,3] ); + actual = divide( data, y, 'x.1' ); + expected = [ + {'x':[9,NaN]}, + {'x':[9,1]}, + {'x':[9,NaN]}, + {'x':[9,1]} + ]; + assert.deepEqual( data, expected ); }); }); diff --git a/test/test.js b/test/test.js index e36774a..2c872aa 100644 --- a/test/test.js +++ b/test/test.js @@ -72,6 +72,24 @@ describe( 'compute-divide', function tests() { } }); + it( 'should throw an error if provided a typed-array and an unrecognized/unsupported data type option', function test() { + var values = [ + 'beep', + 'boop' + ]; + + for ( var i = 0; i < values.length; i++ ) { + expect( badValue( values[i] ) ).to.throw( Error ); + } + function badValue( value ) { + return function() { + divide( new Int8Array([1,2,3]), 1, { + 'dtype': value + }); + }; + } + }); + it( 'should throw an error if provided a matrix and an unrecognized/unsupported data type option', function test() { var values = [ 'beep', diff --git a/test/test.typedarray.js b/test/test.typedarray.js new file mode 100644 index 0000000..431f12d --- /dev/null +++ b/test/test.typedarray.js @@ -0,0 +1,93 @@ +/* global describe, it, require */ +'use strict'; + +// MODULES // + +var // Expectation library: + chai = require( 'chai' ), + + // Module to be tested: + divide = require( './../lib/typedarray.js' ); + + +// VARIABLES // + +var expect = chai.expect, + assert = chai.assert; + + +// TESTS // + +describe( 'typed-array divide', function tests() { + + it( 'should export a function', function test() { + expect( divide ).to.be.a( 'function' ); + }); + + it( 'should divide elements of two typed arrays', function test() { + var data, actual, expected, y; + + data = new Float64Array([ + 12, + 6, + 4, + 3 + ]); + y = new Float64Array([ + 4, + 3, + 4, + 1 + ]); + actual = new Float64Array( data.length ); + + actual = divide( actual, data, y ); + + expected = new Float64Array( [3,2,1,3] ); + + assert.deepEqual( expected, actual ); + }); + + it( 'should throw an error if provided two typed arrays of differing lengths', function test() { + expect( foo ).to.throw( Error ); + function foo() { + divide( new Array(2), new Int8Array( [1,2] ), new Int8Array( [1,2,3] ) ); + } + + expect( foo2 ).to.throw( Error ); + function foo2() { + divide( new Array(2), new Int8Array( [1,2] ), [ 1, 2, 3 ] ); + } + }); + + it( 'should handle non-numeric y values by setting the respective element to NaN', function test() { + var data, actual, expected, y; + + data = new Float64Array([ + 1, + 2, + 3, + 4 + ]); + actual = new Array( data.length ); + actual = divide( actual, data, null ); + + expected = [ NaN, NaN, NaN, NaN ]; + + assert.deepEqual( actual, expected ); + + actual = new Array( data.length ); + y = [ 1, 2, 3, null ]; + actual = divide( actual, data, y ); + + expected = [ 1, 1, 1, NaN ]; + + assert.deepEqual( actual, expected ); + + }); + + it( 'should return an empty array if provided an empty array', function test() { + assert.deepEqual( divide( new Int8Array(), new Int8Array() ), new Int8Array() ); + }); + +});