diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..8e74bf3
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..176a458
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto
diff --git a/.jshintignore b/.jshintignore
new file mode 100644
index 0000000..3163c22
--- /dev/null
+++ b/.jshintignore
@@ -0,0 +1,14 @@
+
+# Directories #
+###############
+build/
+reports/
+dist/
+
+# Node.js #
+###########
+/node_modules/
+
+# Git #
+#######
+.git*
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..0d8844e
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,71 @@
+{
+ "bitwise": false,
+ "camelcase": false,
+ "curly": true,
+ "eqeqeq": true,
+ "es3": false,
+ "forin": true,
+ "freeze": true,
+ "immed": true,
+ "indent": 4,
+ "latedef": "nofunc",
+ "newcap": true,
+ "noarg": true,
+ "noempty": false,
+ "nonbsp": true,
+ "nonew": true,
+ "plusplus": false,
+ "quotmark": "single",
+ "undef": true,
+ "unused": true,
+ "strict": true,
+ "maxparams": 10,
+ "maxdepth": 5,
+ "maxstatements": 100,
+ "maxcomplexity": false,
+ "maxlen": 1000,
+ "asi": false,
+ "boss": false,
+ "debug": false,
+ "eqnull": false,
+ "esnext": false,
+ "evil": false,
+ "expr": false,
+ "funcscope": false,
+ "globalstrict": false,
+ "iterator": false,
+ "lastsemic": false,
+ "laxbreak": false,
+ "laxcomma": false,
+ "loopfunc": false,
+ "maxerr": 1000,
+ "moz": false,
+ "multistr": false,
+ "notypeof": false,
+ "proto": false,
+ "scripturl": false,
+ "shadow": false,
+ "sub": true,
+ "supernew": false,
+ "validthis": false,
+ "noyield": false,
+ "browser": true,
+ "browserify": true,
+ "couch": false,
+ "devel": true,
+ "dojo": false,
+ "jasmine": false,
+ "jquery": false,
+ "mocha": true,
+ "mootools": false,
+ "node": true,
+ "nonstandard": false,
+ "prototypejs": false,
+ "qunit": false,
+ "rhino": false,
+ "shelljs": false,
+ "worker": false,
+ "wsh": false,
+ "yui": false,
+ "globals": {}
+}
diff --git a/.npmignore b/.npmignore
index 42781b3..9db298d 100644
--- a/.npmignore
+++ b/.npmignore
@@ -13,6 +13,7 @@ examples/
reports/
support/
test/
+benchmark/
# Node.js #
###########
@@ -46,4 +47,6 @@ Desktop.ini
# Utilities #
#############
.jshintrc
-.travis.yml
\ No newline at end of file
+.jshintignore
+.travis.yml
+.editorconfig
diff --git a/.travis.yml b/.travis.yml
index 7c16620..29cff27 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,12 @@
language: node_js
node_js:
- - "0.10"
+ - '0.12'
+ - '0.11'
+ - '0.10'
+ - '0.8'
+ - 'iojs'
+before_install:
+ - npm update -g npm
after_script:
- - npm run coveralls
\ No newline at end of file
+ - npm run coveralls
+
diff --git a/LICENSE b/LICENSE
index 2fe3939..93cf2ea 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2014 Athan Reines.
+Copyright (c) 2014-2015 The Compute.io Authors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+SOFTWARE.
diff --git a/Makefile b/Makefile
index e3cad30..c6f009c 100644
--- a/Makefile
+++ b/Makefile
@@ -5,25 +5,29 @@
# Set the node.js environment to test:
NODE_ENV ?= test
+# Kernel name:
+KERNEL ?= $(shell uname -s)
+
+ifeq ($(KERNEL), Darwin)
+ OPEN ?= open
+else
+ OPEN ?= xdg-open
+endif
# NOTES #
-NOTES ?= 'TODO|FIXME'
+NOTES ?= 'TODO|FIXME|WARNING|HACK|NOTE'
# MOCHA #
-# Specify the test framework bin locations:
MOCHA ?= ./node_modules/.bin/mocha
_MOCHA ?= ./node_modules/.bin/_mocha
-
-# Specify the mocha reporter:
MOCHA_REPORTER ?= spec
# ISTANBUL #
-# Istanbul configuration:
ISTANBUL ?= ./node_modules/.bin/istanbul
ISTANBUL_OUT ?= ./reports/coverage
ISTANBUL_REPORT ?= lcov
@@ -31,6 +35,12 @@ ISTANBUL_LCOV_INFO_PATH ?= $(ISTANBUL_OUT)/lcov.info
ISTANBUL_HTML_REPORT_PATH ?= $(ISTANBUL_OUT)/lcov-report/index.html
+# JSHINT #
+
+JSHINT ?= ./node_modules/.bin/jshint
+JSHINT_REPORTER ?= ./node_modules/jshint-stylish
+
+
# FILES #
@@ -81,7 +91,8 @@ test-istanbul-mocha: node_modules
NODE_ENV=$(NODE_ENV) \
NODE_PATH=$(NODE_PATH_TEST) \
$(ISTANBUL) cover \
- --dir $(ISTANBUL_OUT) --report $(ISTANBUL_REPORT) \
+ --dir $(ISTANBUL_OUT) \
+ --report $(ISTANBUL_REPORT) \
$(_MOCHA) -- \
--reporter $(MOCHA_REPORTER) \
$(TESTS)
@@ -95,9 +106,20 @@ test-istanbul-mocha: node_modules
view-cov: view-istanbul-report
view-istanbul-report:
- open $(ISTANBUL_HTML_REPORT_PATH)
+ $(OPEN) $(ISTANBUL_HTML_REPORT_PATH)
+# LINT #
+
+.PHONY: lint lint-jshint
+
+lint: lint-jshint
+
+lint-jshint: node_modules
+ $(JSHINT) \
+ --reporter $(JSHINT_REPORTER) \
+ ./
+
# NODE #
@@ -116,7 +138,6 @@ clean-node:
# CLEAN #
-
.PHONY: clean
clean:
diff --git a/README.md b/README.md
index 78734d7..8c6ff39 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,22 @@ erfc
===
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Dependencies][dependencies-image]][dependencies-url]
-> Complementary error function.
+> Computes the complementary error function.
+
+The [complementary error function](https://en.wikipedia.org/wiki/Error_function) is defined as
+
+
+

+
+
+
+Equivalently, the [complementary error function](https://en.wikipedia.org/wiki/Error_function) can be expressed using Craig's formula:
+
+
+

+
+
## Installation
@@ -16,33 +31,239 @@ For use in the browser, use [browserify](https://github.com/substack/node-browse
## Usage
-To use the module,
-
``` javascript
var erfc = require( 'compute-erfc' );
```
-The method accepts a single argument: either a single `numeric` value or an `array` of numeric values, which may include `NaN`, `+infinity`, and `-infinity`. For an input `array`, the complementary error function is evaluated for each value.
+#### erfc( x[, options] )
+
+Evaluates the [complementary error function](http://en.wikipedia.org/wiki/Error_function). `x` may be either a [`number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), an [`array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), a [`typed array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays), or a [`matrix`](https://github.com/dstructs/matrix). Values may include `NaN`, `+infinity`, and `-infinity`.
+
+
+``` javascript
+var matrix = require( 'dstructs-matrix' ),
+ data,
+ mat,
+ out,
+ i;
+
+out = erfc( -1 );
+// returns ~1.8427
+
+out = erfc( [ -10, -1, 0, 1, 10 ] );
+// returns [ 2, 1.8427, 1, 0.1573, 2.0885e-45 ]
+
+data = [ 0, 1, 2 ];
+out = erfc( data );
+// returns [ 1, ~0.1573, ~0.0047 ]
+
+data = new Int8Array( data );
+out = erfc( data );
+// returns Float64Array( [ 1, ~0.1573, ~0.0047 ] )
+
+data = new Float64Array( 6 );
+for ( i = 0; i < 6; i++ ) {
+ data[ i ] = i / 2;
+}
+mat = matrix( data, [3,2], 'float64' );
+/*
+ [ 0 0.5
+ 1 1.5
+ 2 2.5 ]
+*/
+
+out = erfc( mat );
+/*
+ [ 1 ~0.48
+ ~0.16 ~0.03
+ ~0 ~0 ]
+*/
+```
+
+
+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 non-numeric `arrays`, provide an accessor `function` for accessing `array` values.
``` javascript
-erfc( -1 );
-erfc( [ -10, -1, 0, 1, 10 ] );
+var data = [
+ ['beep', -10],
+ ['boop', -1],
+ ['bip', 0],
+ ['bap', 1],
+ ['baz', 10]
+];
+
+function getValue( d, i ) {
+ return d[ 1 ];
+}
+
+var out = erfc( data, {
+ 'accessor': getValue
+});
+// returns [ 2, 1.8427, 1, 0.1573, 2.0885e-45 ]
```
+To [deepset](https://github.com/kgryte/utils-deep-set) an object `array`, provide a key path and, optionally, a key path separator.
-## Examples
+``` javascript
+var data = [
+ {'x':[0,-10]},
+ {'x':[1,-1]},
+ {'x':[2,0]},
+ {'x':[3,1]},
+ {'x':[4,10]}
+];
+
+var out = erfc( data, 'x|1', '|' );
+/*
+ [
+ {'x':[0,2]},
+ {'x':[1,1.8427]},
+ {'x':[2,1]},
+ {'x':[3,0.1573]},
+ {'x':[4,2.0885e-45]}
+ ]
+*/
+
+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
-// Simulate some data...
-var data = new Array( 100 );
+var data, out;
-for ( var i = 0; i < data.length; i++ ) {
+data = new Int8Array( [0, 1, 2] );
+
+out = erfc( data, {
+ 'dtype': 'int32'
+});
+// returns Int32Array( [1,0,0] )
+
+// Works for plain arrays, as well...
+out = erfc( [0, 1, 2], {
+ 'dtype': 'uint8'
+});
+// returns Uint8Array( [1,0,0] )
+```
+
+By default, the function returns a new data structure. To mutate the input data structure (e.g., when input values can be discarded or when optimizing memory usage), set the `copy` option to `false`.
+
+``` javascript
+var data,
+ bool,
+ mat,
+ out,
+ i;
+
+var data = [ -10, -1, 0, 1, 10 ];
+
+var out = erfc( data, {
+ 'copy': false
+});
+// returns [ 2, 1.8427, 1, 0.1573, 2.0885e-45 ]
+
+bool = ( data === out );
+// returns true
+
+data = new Float64Array( 6 );
+for ( i = 0; i < 6; i++ ) {
+ data[ i ] = i / 2;
+}
+mat = matrix( data, [3,2], 'float64' );
+/*
+ [ 0 0.5
+ 1 1.5
+ 2 2.5 ]
+*/
+
+out = erfc( mat, {
+ 'copy': false
+});
+/*
+ [ 1 ~0.48
+ ~0.16 ~0.03
+ ~0 ~0 ]
+*/
+
+bool = ( mat === out );
+// returns true
+```
+
+
+## Examples
+
+``` javascript
+var matrix = require( 'dstructs-matrix' ),
+ erfc = require( 'compute-erfc' );
+
+var data,
+ mat,
+ out,
+ tmp,
+ i;
+
+// Plain arrays...
+data = new Array( 10 );
+for ( i = 0; i < data.length; i++ ) {
data[ i ] = Math.random()*20 - 10;
}
+out = erfc( data );
-// Evaluate the complementary error function for each datum:
-console.log( erfc( data ) );
-// returns [...]
+// Object arrays (accessors)...
+function getValue( d ) {
+ return d.x;
+}
+for ( i = 0; i < data.length; i++ ) {
+ data[ i ] = {
+ 'x': data[ i ]
+ };
+}
+out = erfc( data, {
+ 'accessor': getValue
+});
+
+// Deep set arrays...
+for ( i = 0; i < data.length; i++ ) {
+ data[ i ] = {
+ 'x': [ i, data[ i ].x ]
+ };
+}
+out = erfc( data, {
+ 'path': 'x/1',
+ 'sep': '/'
+});
+
+// Typed arrays...
+data = new Int32Array( 10 );
+for ( i = 0; i < data.length; i++ ) {
+ data[ i ] = Math.random() * 100;
+}
+tmp = erfc( data );
+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 = erfc( mat );
+
+// Matrices (custom output data type)...
+out = erfc( mat, {
+ 'dtype': 'uint8'
+});
```
To run the example code from the top-level application directory,
@@ -56,7 +277,7 @@ $ node ./examples/index.js
### Unit
-Unit tests use the [Mocha](http://visionmedia.github.io/mocha) test framework with [Chai](http://chaijs.com) assertions. To run the tests, execute the following command in the top-level application directory:
+Unit tests use the [Mocha](http://mochajs.org) test framework with [Chai](http://chaijs.com) assertions. To run the tests, execute the following command in the top-level application directory:
``` bash
$ make test
@@ -76,19 +297,19 @@ $ make test-cov
Istanbul creates a `./reports/coverage` directory. To access an HTML version of the report,
``` bash
-$ open reports/coverage/lcov-report/index.html
+$ make view-cov
```
+---
## License
-[MIT license](http://opensource.org/licenses/MIT).
+[MIT license](http://opensource.org/licenses/MIT).
----
## Copyright
-Copyright © 2014. Athan Reines.
+Copyright © 2014-2015. The [Compute.io](https://github.com/compute-io) Authors.
[npm-image]: http://img.shields.io/npm/v/compute-erfc.svg
@@ -107,4 +328,4 @@ Copyright © 2014. Athan Reines.
[dev-dependencies-url]: https://david-dm.org/dev/compute-io/erfc
[github-issues-image]: http://img.shields.io/github/issues/compute-io/erfc.svg
-[github-issues-url]: https://github.com/compute-io/erfc/issues
\ No newline at end of file
+[github-issues-url]: https://github.com/compute-io/erfc/issues
diff --git a/TODO.md b/TODO.md
index 27aeb4e..4d46999 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,4 +1,7 @@
TODO
====
+1. for matrices, should we take into consider that `y` might be a view?
+2.
+
diff --git a/docs/img/eqn.svg b/docs/img/eqn.svg
new file mode 100644
index 0000000..463a661
--- /dev/null
+++ b/docs/img/eqn.svg
@@ -0,0 +1,63 @@
+
+
+
+
+
diff --git a/docs/img/eqn2.svg b/docs/img/eqn2.svg
new file mode 100644
index 0000000..30d41a8
--- /dev/null
+++ b/docs/img/eqn2.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
diff --git a/examples/index.js b/examples/index.js
index 83844fa..4097c6f 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -1,12 +1,83 @@
-var erfc = require( './../lib' );
+'use strict';
-// Simulate some data...
-var data = new Array( 100 );
+var matrix = require( 'dstructs-matrix' ),
+ erfc = require( './../lib' );
-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.random()*20 - 10;
}
+out = erfc( data );
+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 = erfc( data, {
+ '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 = erfc( data, {
+ '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 = erfc( data );
+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 = erfc( mat );
+console.log( 'Matrix: %s\n', out.toString() );
+
-// Evaluate the complementary error function for each datum:
-console.log( erfc( data ) );
-// returns [...]
\ No newline at end of file
+// ----
+// Matrices (custom output data type)...
+out = erfc( mat, {
+ 'dtype': 'uint8'
+});
+console.log( 'Matrix (%s): %s\n', out.dtype, out.toString() );
diff --git a/lib/accessor.js b/lib/accessor.js
new file mode 100644
index 0000000..ab81adf
--- /dev/null
+++ b/lib/accessor.js
@@ -0,0 +1,34 @@
+'use strict';
+
+// FUNCTIONS
+
+var ERFC = require( './number.js' );
+
+
+// COMPLEMENTARY ERROR FUNCTION //
+
+/**
+* FUNCTION: erfc( out, arr, accessor )
+* Computes the complementary error function for each array element using an accessor function.
+*
+* @param {Array} out - output array
+* @param {Array} arr - input array
+* @param {Function} accessor - accessor function for accessing array values
+* @returns {Number[]|Null} output array or null
+*/
+function erfc( y, x, clbk ) {
+ var len = x.length,
+ i;
+ if ( !len ) {
+ return null;
+ }
+ for ( i = 0; i < len; i++ ) {
+ y[ i ] = ERFC( clbk( x[ i ], i ) );
+ }
+ return y;
+} // end FUNCTION erfc()
+
+
+// EXPORTS //
+
+module.exports = erfc;
diff --git a/lib/array.js b/lib/array.js
new file mode 100644
index 0000000..29e74b2
--- /dev/null
+++ b/lib/array.js
@@ -0,0 +1,33 @@
+'use strict';
+
+// MODULES //
+
+var ERFC = require( './number.js' );
+
+
+// COMPLEMENTARY ERROR FUNCTION //
+
+/**
+* FUNCTION: erfc( out, arr )
+* Computes the complementary error function for each array element.
+*
+* @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
+* @returns {Number[]|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Null} output array or null
+*/
+function erfc( y, x ) {
+ var len = x.length,
+ i;
+ if ( !len ) {
+ return null;
+ }
+ for ( i = 0; i < len; i++ ) {
+ y[ i ] = ERFC( x[ i ] );
+ }
+ return y;
+} // end FUNCTION erfc()
+
+
+// EXPORTS //
+
+module.exports = erfc;
diff --git a/lib/deepset.js b/lib/deepset.js
new file mode 100644
index 0000000..474c825
--- /dev/null
+++ b/lib/deepset.js
@@ -0,0 +1,44 @@
+'use strict';
+
+// MODULES //
+
+var deepSet = require( 'utils-deep-set' ).factory,
+ deepGet = require( 'utils-deep-get' ).factory,
+ ERFC = require( './number.js' );
+
+
+// COMPLEMENTARY ERROR FUNCTION //
+
+/**
+* FUNCTION: erfc( arr, path[, sep] )
+* Computes the complementary error function for each array 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|Null} input array or null
+*/
+function erfc( x, path, sep ) {
+ var len = x.length,
+ opts = {},
+ dget,
+ dset,
+ i;
+ if ( !len ) {
+ return null;
+ }
+ if ( arguments.length > 2 ) {
+ opts.sep = sep;
+ }
+ dget = deepGet( path, opts );
+ dset = deepSet( path, opts );
+ for ( i = 0; i < len; i++ ) {
+ dset( x[i], ERFC( dget( x[i] ) ) );
+ }
+ return x;
+} // end FUNCTION erfc()
+
+
+// EXPORTS //
+
+module.exports = erfc;
diff --git a/lib/index.js b/lib/index.js
index ad7f2e2..4a01b65 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,325 +1,103 @@
-/**
-*
-* COMPUTE: erfc
-*
-*
-* DESCRIPTION:
-* - Complementary error function.
-*
-*
-* NOTES:
-* [1]
-*
-*
-* TODO:
-* [1]
-*
-*
-* LICENSE:
-* MIT
-*
-* Copyright (c) 2014. Athan Reines.
-*
-*
-* AUTHOR:
-* Athan Reines. kgryte@gmail.com. 2014.
-*
-*/
-
-(function() {
- 'use strict';
-
- /**
- * NOTE: the following copyright and license, as well as the long comment were part of the original implementation available as part of [FreeBSD]{@link https://svnweb.freebsd.org/base/release/9.3.0/lib/msun/src/s_erf.c?revision=268523&view=co}.
- *
- * The implementation follows the original, but has been modified for JavaScript.
- */
-
- /**
- * ===========================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc business.
- * Permission to use, copy, modify, and distribute this software is freely granted, provided that this notice is preserved.
- * ===========================
- */
-
- /**
- * double erfc(double x)
- * x
- * 2 |\
- * erf(x) = ----------- | exp(-t*t)dt
- * sqrt(pi) \|
- * 0
- *
- * erfc(x) = 1-erf(x)
- * Note that
- * erf(-x) = -erf(x)
- * erfc(-x) = 2 - erfc(x)
- *
- * Method:
- * 1. For |x| in [0, 0.84375)
- * erf(x) = x + x*R(x^2)
- * erfc(x) = 1 - erf(x) if x in [-.84375,0.25]
- * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375]
- * where R = P/Q where P is an odd poly of degree 8 and Q is an odd poly of degree 10.
- * -57.90
- * | R - (erf(x)-x)/x | <= 2
- *
- *
- * Remark. The formula is derived by noting
- * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....)
- * and that
- * 2/sqrt(pi) = 1.128379167095512573896158903121545171688
- * is close to one. The interval is chosen because the fix point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is near 0.6174), and by some experiment, 0.84375 is chosen to guarantee the error is less than one ulp for erf.
- *
- * 2. For |x| in [0.84375,1.25), let s = |x| - 1, and c = 0.84506291151 rounded to single (24 bits)
- * erf(x) = sign(x) * (c + P1(s)/Q1(s))
- * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0
- * 1+(c+P1(s)/Q1(s)) if x < 0
- * |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06
- * Remark: here we use the taylor series expansion at x=1.
- * erf(1+s) = erf(1) + s*Poly(s)
- * = 0.845.. + P1(s)/Q1(s)
- * That is, we use rational approximation to approximate
- * erf(1+s) - (c = (single)0.84506291151)
- * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] where
- * P1(s) = degree 6 poly in s
- * Q1(s) = degree 6 poly in s
- *
- * 3. For x in [1.25,1/0.35(~2.857143)),
- * erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
- * erf(x) = 1 - erfc(x)
- * where
- * R1(z) = degree 7 poly in z, (z=1/x^2)
- * S1(z) = degree 8 poly in z
- *
- * 4. For x in [1/0.35,28]
- * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
- * = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 x >= 28
- * erf(x) = sign(x) *(1 - tiny) (raise inexact)
- * erfc(x) = tiny*tiny (raise underflow) if x > 0
- * = 2 - tiny if x<0
- *
- * 6. Special case:
- * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
- * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
- * erfc/erf(NaN) is NaN
- */
-
- // CONSTANTS //
-
- var INF = Number.POSITIVE_INFINITY,
- NINF = Number.NEGATIVE_INFINITY,
-
- TINY = 1e-300,
- SMALL = 1.0 / (1 << 56 ), /* 2**-56; equiv is Math.pow( 2, -56 ) */
- ERX = 8.45062911510467529297e-1, /* 0x3FEB0AC1, 0x60000000 */
+'use strict';
- // Coefficients for approximation to erfc on [0, 0.84375)
- PP0 = 1.28379167095512558561e-1, /* 0x3FC06EBA, 0x8214DB68 */
- PP1 = -3.25042107247001499370e-1, /* 0xBFD4CD7D, 0x691CB913 */
- PP2 = -2.84817495755985104766e-2, /* 0xBF9D2A51, 0xDBD7194F */
- PP3 = -5.77027029648944159157e-3, /* 0xBF77A291, 0x236668E4 */
- PP4 = -2.37630166566501626084e-5, /* 0xBEF8EAD6, 0x120016AC */
- QQ1 = 3.97917223959155352819e-1, /* 0x3FD97779, 0xCDDADC09 */
- QQ2 = 6.50222499887672944485e-2, /* 0x3FB0A54C, 0x5536CEBA */
- QQ3 = 5.08130628187576562776e-3, /* 0x3F74D022, 0xC4D36B0F */
- QQ4 = 1.32494738004321644526e-4, /* 0x3F215DC9, 0x221C1A10 */
- QQ5 = -3.96022827877536812320e-6, /* 0xBED09C43, 0x42A26120 */
+// MODULES //
- // Coefficients for approximation to erfc on [0.84375, 1.25)
- PA0 = -2.36211856075265944077e-3, /* 0xBF6359B8, 0xBEF77538 */
- PA1 = 4.14856118683748331666e-1, /* 0x3FDA8D00, 0xAD92B34D */
- PA2 = -3.72207876035701323847e-1, /* 0xBFD7D240, 0xFBB8C3F1 */
- PA3 = 3.18346619901161753674e-1, /* 0x3FD45FCA, 0x805120E4 */
- PA4 = -1.10894694282396677476e-1, /* 0xBFBC6398, 0x3D3E28EC */
- PA5 = 3.54783043256182359371e-2, /* 0x3FA22A36, 0x599795EB */
- PA6 = -2.16637559486879084300e-3, /* 0xBF61BF38, 0x0A96073F */
- QA1 = 1.06420880400844228286e-1, /* 0x3FBB3E66, 0x18EEE323 */
- QA2 = 5.40397917702171048937e-1, /* 0x3FE14AF0, 0x92EB6F33 */
- QA3 = 7.18286544141962662868e-2, /* 0x3FB2635C, 0xD99FE9A7 */
- QA4 = 1.26171219808761642112e-1, /* 0x3FC02660, 0xE763351F */
- QA5 = 1.36370839120290507362e-2, /* 0x3F8BEDC2, 0x6B51DD1C */
- QA6 = 1.19844998467991074170e-2, /* 0x3F888B54, 0x5735151D */
+var isNumber = require( 'validate.io-number-primitive' ),
+ 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' );
- // Coefficients for approximation to erfc on [1.25, 1/0.35)
- RA0 = -9.86494403484714822705e-3, /* 0xBF843412, 0x600D6435 */
- RA1 = -6.93858572707181764372e-1, /* 0xBFE63416, 0xE4BA7360 */
- RA2 = -1.05586262253232909814e1, /* 0xC0251E04, 0x41B0E726 */
- RA3 = -6.23753324503260060396e1, /* 0xC04F300A, 0xE4CBA38D */
- RA4 = -1.62396669462573470355e2, /* 0xC0644CB1, 0x84282266 */
- RA5 = -1.84605092906711035994e2, /* 0xC067135C, 0xEBCCABB2 */
- RA6 = -8.12874355063065934246e1, /* 0xC0545265, 0x57E4D2F2 */
- RA7 = -9.81432934416914548592, /* 0xC023A0EF, 0xC69AC25C */
- SA1 = 1.96512716674392571292e1, /* 0x4033A6B9, 0xBD707687 */
- SA2 = 1.37657754143519042600e2, /* 0x4061350C, 0x526AE721 */
- SA3 = 4.34565877475229228821e2, /* 0x407B290D, 0xD58A1A71 */
- SA4 = 6.45387271733267880336e2, /* 0x40842B19, 0x21EC2868 */
- SA5 = 4.29008140027567833386e2, /* 0x407AD021, 0x57700314 */
- SA6 = 1.08635005541779435134e2, /* 0x405B28A3, 0xEE48AE2C */
- SA7 = 6.57024977031928170135, /* 0x401A47EF, 0x8E484A93 */
- SA8 = -6.04244152148580987438e-2, /* 0xBFAEEFF2, 0xEE749A62 */
- // Coefficients for approximation to erfc on [1/0.35, 28]
- RB0 = -9.86494292470009928597e-3, /* 0xBF843412, 0x39E86F4A */
- RB1 = -7.99283237680523006574e-1, /* 0xBFE993BA, 0x70C285DE */
- RB2 = -1.77579549177547519889e1, /* 0xC031C209, 0x555F995A */
- RB3 = -1.60636384855821916062e2, /* 0xC064145D, 0x43C5ED98 */
- RB4 = -6.37566443368389627722e2, /* 0xC083EC88, 0x1375F228 */
- RB5 = -1.02509513161107724954e3, /* 0xC0900461, 0x6A2E5992 */
- RB6 = -4.83519191608651397019e2, /* 0xC07E384E, 0x9BDC383F */
- SB1 = 3.03380607434824582924e1, /* 0x403E568B, 0x261D5190 */
- SB2 = 3.25792512996573918826e2, /* 0x40745CAE, 0x221B9F0A */
- SB3 = 1.53672958608443695994e3, /* 0x409802EB, 0x189D5118 */
- SB4 = 3.19985821950859553908e3, /* 0x40A8FFB7, 0x688C246A */
- SB5 = 2.55305040643316442583e3, /* 0x40A3F219, 0xCEDF3BE6 */
- SB6 = 4.74528541206955367215e2, /* 0x407DA874, 0xE79FE763 */
- SB7 = -2.24409524465858183362e1; /* 0xC03670E2, 0x42712D62 */
+// FUNCTIONS //
+var erfc1 = require( './number.js' ),
+ erfc2 = require( './array.js' ),
+ erfc3 = require( './accessor.js' ),
+ erfc4 = require( './deepset.js' ),
+ erfc5 = require( './matrix.js' );
- // ERFC //
- /**
- * FUNCTION: erfc( x )
- * Evaluates the complementary error function for an input value.
- *
- * @private
- * @param {Number} x - input value
- * @returns {Number} evaluated complementary error function
- */
- function erfc( x ) {
- var exp = Math.exp,
- sign = false,
- tmp,
- z, r, s, y, p, q;
+// COMPLEMENTARY ERROR FUNCTION //
- if ( typeof x !== 'number' ) {
- throw new TypeError( 'erfc()::invalid input argument. Must provide a numeric value.' );
- }
-
- // [1] Special cases...
-
- // NaN:
- if ( x !== x ) {
- return NaN;
- }
- // Positive infinity:
- if ( x === INF ) {
- return 0;
+/**
+* FUNCTION: erfc( x[, opts] )
+* Computes the complementary error function.
+*
+* @param {Number|Number[]|Array|Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|Matrix} x - input value
+* @param {Object} [opts] - function options
+* @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
+* @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|Null} complementary error function value(s) or null
+*/
+function erfc( x, options ) {
+ /* jshint newcap:false */
+ var opts = {},
+ ctor,
+ err,
+ out,
+ dt,
+ d;
+
+ if ( isNumber( x ) ) {
+ return erfc1( x );
+ }
+ if ( arguments.length > 1 ) {
+ err = validate( opts, options );
+ if ( err ) {
+ throw err;
}
- // Negative infinity:
- if ( x === NINF ) {
- return 2;
+ }
+ if ( isMatrixLike( x ) ) {
+ if ( opts.copy !== false ) {
+ dt = opts.dtype || 'float64';
+ ctor = ctors( dt );
+ if ( ctor === null ) {
+ throw new Error( 'erfc()::invalid option. Data type option does not have a corresponding array constructor. Option: `' + dt + '`.' );
+ }
+ // Create an output matrix:
+ d = new ctor( x.length );
+ out = matrix( d, x.shape, dt );
+ } else {
+ out = x;
}
-
- // [2] Get the sign:
- if ( x < 0 ) {
- x = -x;
- sign = true;
+ return erfc5( out, x );
+ }
+ if ( isArrayLike( x ) ) {
+ // Handle deepset first...
+ if ( opts.path ) {
+ opts.sep = opts.sep || '.';
+ return erfc4( x, opts.path, opts.sep );
}
-
- // [3] |x| < 0.84375
- if ( x < 0.84375 ) {
- // |x| < 2**-56
- if ( x < SMALL ) {
- tmp = x;
- } else {
- z = x * x;
- r = PP0 + z*(PP1+z*(PP2+z*(PP3+z*PP4)));
- s = 1 + z*(QQ1+z*(QQ2+z*(QQ3+z*(QQ4+z*QQ5))));
- y = r / s;
- if ( x < 0.25 ) { // |x| < 1/4
- tmp = x + x*y;
- } else {
- tmp = 0.5 + (x*y + (x-0.5));
- }
- }
- if ( sign ) {
- return 1 + tmp;
- }
- return 1 - tmp;
+ // Handle regular, typed, and accessor arrays next...
+ if ( opts.copy === false ) {
+ out = x;
}
-
- // [4] 0.84375 <= |x| < 1.25
- if ( x < 1.25 ) {
- s = x - 1;
- p = PA0 + s*(PA1+s*(PA2+s*(PA3+s*(PA4+s*(PA5+s*PA6)))));
- q = 1 + s*(QA1+s*(QA2+s*(QA3+s*(QA4+s*(QA5+s*QA6)))));
- if ( sign ) {
- return 1 + ERX + p/q;
+ else if ( opts.dtype || !isArray( x ) ) {
+ dt = opts.dtype || 'float64';
+ ctor = ctors( dt );
+ if ( ctor === null ) {
+ throw new TypeError( 'erfc()::invalid input argument. Unrecognized/unsupported array-like object. Provide either a plain or typed array. Value: `' + x + '`.' );
}
- return 1 - ERX - p/q;
+ out = new ctor( x.length );
}
-
- // [5] |x| < 28
- if ( x < 28 ) {
- s = 1 / (x*x);
- // |x| < 1/0.35 ~ 2.857143
- if ( x < 1/0.35 ) {
- r = RA0 + s*(RA1+s*(RA2+s*(RA3+s*(RA4+s*(RA5+s*(RA6+s*RA7))))));
- s = 1 + s*(SA1+s*(SA2+s*(SA3+s*(SA4+s*(SA5+s*(SA6+s*SA7))))));
- } else { // |x| >= 1/0.35 ~ 2.857143
- if ( sign && x > 6 ) { // x < -6
- return 2 - TINY;
- }
- r = RB0 + s*(RB1+s*(RB2+s*(RB3+s*(RB4+s*(RB5+s*RB6)))));
- s = 1 + s*(SB1+s*(SB2+s*(SB3+s*(SB4+s*(SB5+s*(SB6+s*SB7))))));
- }
- z = x & 0xffffffff00000000; // pseudo-single (20-bit) precision x;
- r = exp( -z*z - 0.5625 ) * exp( (z-x)*(z+x) + r/s );
- if ( sign ) {
- return 2 - r/x;
- }
- return r/x;
+ else {
+ out = new Array( x.length );
}
-
- if ( sign ) {
- return 2 - TINY; // ~2
+ if ( opts.accessor ) {
+ return erfc3( out, x, opts.accessor );
}
- return TINY * TINY; // ~0
- } // end FUNCTION erfc()
+ return erfc2( out, x );
+ }
+ throw new TypeError( 'erfc()::invalid input argument. Input value type not currently supported. Value: `' + x + '`.' );
+} // end FUNCTION erfc()
- // EXPORTS //
-
- module.exports = function( x ) {
- if ( typeof x === 'number' ) {
- return erfc( x );
- }
- if ( !Array.isArray( x ) ) {
- throw new TypeError( 'erfc()::invalid input argument. Must provide an array.' );
- }
- var len = x.length,
- arr = new Array( len );
-
- for ( var i = 0; i < len; i++ ) {
- arr[ i ] = erfc( x[ i ] );
- }
- return arr;
- };
+// EXPORTS //
-})();
\ No newline at end of file
+module.exports = erfc;
diff --git a/lib/matrix.js b/lib/matrix.js
new file mode 100644
index 0000000..6e052d5
--- /dev/null
+++ b/lib/matrix.js
@@ -0,0 +1,36 @@
+'use strict';
+
+// MODULES //
+
+var ERFC = require( './number.js' );
+
+
+// COMPLEMENTARY ERROR FUNCTION //
+
+/**
+* FUNCTION: erfc( out, matrix )
+* Evaluates the complementary error function for each matrix element.
+*
+* @param {Matrix} out - output matirx
+* @param {Matrix} arr - input matrix
+* @returns {Matrix|Null} output matrix or null
+*/
+function erfc( y, x ) {
+ var len = x.length,
+ i;
+ if ( !len ) {
+ return null;
+ }
+ if ( y.length !== len ) {
+ throw new Error( 'erfc()::invalid input arguments. Input and output matrices must be the same length.' );
+ }
+ for ( i = 0; i < len; i++ ) {
+ y.data[ i ] = ERFC( x.data[ i ] );
+ }
+ return y;
+} // end FUNCTION erfc()
+
+
+// EXPORTS //
+
+module.exports = erfc;
diff --git a/lib/number.js b/lib/number.js
new file mode 100644
index 0000000..0f26fc2
--- /dev/null
+++ b/lib/number.js
@@ -0,0 +1,279 @@
+'use strict';
+
+/**
+* NOTE: the following copyright and license, as well as the long comment were part of the original implementation available as part of [FreeBSD]{@link https://svnweb.freebsd.org/base/release/9.3.0/lib/msun/src/s_erf.c?revision=268523&view=co}.
+*
+* The implementation follows the original, but has been modified for JavaScript.
+*/
+
+/**
+* ===========================
+* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+*
+* Developed at SunPro, a Sun Microsystems, Inc business.
+* Permission to use, copy, modify, and distribute this software is freely granted, provided that this notice is preserved.
+* ===========================
+*/
+
+/**
+* double erfc(double x)
+* x
+* 2 |\
+* erf(x) = ----------- | exp(-t*t)dt
+* sqrt(pi) \|
+* 0
+*
+* erfc(x) = 1 - erf(x)
+* Note that
+* erf(-x) = -erf(x)
+* erfc(-x) = 2 - erfc(x)
+*
+* Method:
+* 1. For |x| in [0, 0.84375)
+* erf(x) = x + x*R(x^2)
+* erfc(x) = 1 - erf(x) if x in [-.84375,0.25]
+* = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375]
+* where R = P/Q where P is an odd poly of degree 8 and Q is an odd poly of degree 10.
+* -57.90
+* | R - (erf(x)-x)/x | <= 2
+*
+*
+* Remark. The formula is derived by noting
+* erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....)
+* and that
+* 2/sqrt(pi) = 1.128379167095512573896158903121545171688
+* is close to one. The interval is chosen because the fix point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is near 0.6174), and by some experiment, 0.84375 is chosen to guarantee the error is less than one ulp for erf.
+*
+* 2. For |x| in [0.84375,1.25), let s = |x| - 1, and c = 0.84506291151 rounded to single (24 bits)
+* erf(x) = sign(x) * (c + P1(s)/Q1(s))
+* erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0
+* 1+(c+P1(s)/Q1(s)) if x < 0
+* |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06
+* Remark: here we use the taylor series expansion at x=1.
+* erf(1+s) = erf(1) + s*Poly(s)
+* = 0.845.. + P1(s)/Q1(s)
+* That is, we use rational approximation to approximate
+* erf(1+s) - (c = (single)0.84506291151)
+* Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] where
+* P1(s) = degree 6 poly in s
+* Q1(s) = degree 6 poly in s
+*
+* 3. For x in [1.25,1/0.35(~2.857143)),
+* erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
+* erf(x) = 1 - erfc(x)
+* where
+* R1(z) = degree 7 poly in z, (z=1/x^2)
+* S1(z) = degree 8 poly in z
+*
+* 4. For x in [1/0.35,28]
+* erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
+* = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 < x < 0
+* = 2.0 - tiny if x <= -6
+* erf(x) = sign(x)*(1.0 - erfc(x)) if x < 6, else
+* erf(x) = sign(x)*(1.0 - tiny)
+* where
+* R2(z) = degree 6 poly in z, (z=1/x^2)
+* S2(z) = degree 7 poly in z
+*
+* Note1:
+* To compute exp(-x*x-0.5625+R/S), let s be a single precision number and s := x; then
+* -x*x = -s*s + (s-x)*(s+x)
+* exp(-x*x-0.5626+R/S) = exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S);
+* Note2:
+* Here 4 and 5 make use of the asymptotic series
+* exp(-x*x)
+* erfc(x) ~ ----------- * ( 1 + Poly(1/x^2) )
+* x*sqrt(pi)
+* We use rational approximation to approximate
+* g(s)=f(1/x^2) = log(erfc(x)*x) - x*x + 0.5625
+* Here is the error bound for R1/S1 and R2/S2
+* |R1/S1 - f(x)| < 2**(-62.57)
+* |R2/S2 - f(x)| < 2**(-61.52)
+*
+* 5. For inf > x >= 28
+* erf(x) = sign(x) * (1 - tiny) (raise inexact)
+* erfc(x) = tiny*tiny (raise underflow) if x > 0
+* = 2 - tiny if x < 0
+*
+* 6. Special case:
+* erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
+* erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
+* erfc/erf(NaN) is NaN
+*/
+
+// CONSTANTS //
+
+var INF = Number.POSITIVE_INFINITY,
+ NINF = Number.NEGATIVE_INFINITY,
+
+ TINY = 1e-300,
+ SMALL = 1.0 / (1 << 56 ), /* 2**-56; equiv is Math.pow( 2, -56 ) */
+ ERX = 8.45062911510467529297e-1, /* 0x3FEB0AC1, 0x60000000 */
+
+ // Coefficients for approximation to erfc on [0, 0.84375)
+ PP0 = 1.28379167095512558561e-1, /* 0x3FC06EBA, 0x8214DB68 */
+ PP1 = -3.25042107247001499370e-1, /* 0xBFD4CD7D, 0x691CB913 */
+ PP2 = -2.84817495755985104766e-2, /* 0xBF9D2A51, 0xDBD7194F */
+ PP3 = -5.77027029648944159157e-3, /* 0xBF77A291, 0x236668E4 */
+ PP4 = -2.37630166566501626084e-5, /* 0xBEF8EAD6, 0x120016AC */
+ QQ1 = 3.97917223959155352819e-1, /* 0x3FD97779, 0xCDDADC09 */
+ QQ2 = 6.50222499887672944485e-2, /* 0x3FB0A54C, 0x5536CEBA */
+ QQ3 = 5.08130628187576562776e-3, /* 0x3F74D022, 0xC4D36B0F */
+ QQ4 = 1.32494738004321644526e-4, /* 0x3F215DC9, 0x221C1A10 */
+ QQ5 = -3.96022827877536812320e-6, /* 0xBED09C43, 0x42A26120 */
+
+ // Coefficients for approximation to erfc on [0.84375, 1.25)
+ PA0 = -2.36211856075265944077e-3, /* 0xBF6359B8, 0xBEF77538 */
+ PA1 = 4.14856118683748331666e-1, /* 0x3FDA8D00, 0xAD92B34D */
+ PA2 = -3.72207876035701323847e-1, /* 0xBFD7D240, 0xFBB8C3F1 */
+ PA3 = 3.18346619901161753674e-1, /* 0x3FD45FCA, 0x805120E4 */
+ PA4 = -1.10894694282396677476e-1, /* 0xBFBC6398, 0x3D3E28EC */
+ PA5 = 3.54783043256182359371e-2, /* 0x3FA22A36, 0x599795EB */
+ PA6 = -2.16637559486879084300e-3, /* 0xBF61BF38, 0x0A96073F */
+ QA1 = 1.06420880400844228286e-1, /* 0x3FBB3E66, 0x18EEE323 */
+ QA2 = 5.40397917702171048937e-1, /* 0x3FE14AF0, 0x92EB6F33 */
+ QA3 = 7.18286544141962662868e-2, /* 0x3FB2635C, 0xD99FE9A7 */
+ QA4 = 1.26171219808761642112e-1, /* 0x3FC02660, 0xE763351F */
+ QA5 = 1.36370839120290507362e-2, /* 0x3F8BEDC2, 0x6B51DD1C */
+ QA6 = 1.19844998467991074170e-2, /* 0x3F888B54, 0x5735151D */
+
+ // Coefficients for approximation to erfc on [1.25, 1/0.35)
+ RA0 = -9.86494403484714822705e-3, /* 0xBF843412, 0x600D6435 */
+ RA1 = -6.93858572707181764372e-1, /* 0xBFE63416, 0xE4BA7360 */
+ RA2 = -1.05586262253232909814e1, /* 0xC0251E04, 0x41B0E726 */
+ RA3 = -6.23753324503260060396e1, /* 0xC04F300A, 0xE4CBA38D */
+ RA4 = -1.62396669462573470355e2, /* 0xC0644CB1, 0x84282266 */
+ RA5 = -1.84605092906711035994e2, /* 0xC067135C, 0xEBCCABB2 */
+ RA6 = -8.12874355063065934246e1, /* 0xC0545265, 0x57E4D2F2 */
+ RA7 = -9.81432934416914548592, /* 0xC023A0EF, 0xC69AC25C */
+ SA1 = 1.96512716674392571292e1, /* 0x4033A6B9, 0xBD707687 */
+ SA2 = 1.37657754143519042600e2, /* 0x4061350C, 0x526AE721 */
+ SA3 = 4.34565877475229228821e2, /* 0x407B290D, 0xD58A1A71 */
+ SA4 = 6.45387271733267880336e2, /* 0x40842B19, 0x21EC2868 */
+ SA5 = 4.29008140027567833386e2, /* 0x407AD021, 0x57700314 */
+ SA6 = 1.08635005541779435134e2, /* 0x405B28A3, 0xEE48AE2C */
+ SA7 = 6.57024977031928170135, /* 0x401A47EF, 0x8E484A93 */
+ SA8 = -6.04244152148580987438e-2, /* 0xBFAEEFF2, 0xEE749A62 */
+
+ // Coefficients for approximation to erfc on [1/0.35, 28]
+ RB0 = -9.86494292470009928597e-3, /* 0xBF843412, 0x39E86F4A */
+ RB1 = -7.99283237680523006574e-1, /* 0xBFE993BA, 0x70C285DE */
+ RB2 = -1.77579549177547519889e1, /* 0xC031C209, 0x555F995A */
+ RB3 = -1.60636384855821916062e2, /* 0xC064145D, 0x43C5ED98 */
+ RB4 = -6.37566443368389627722e2, /* 0xC083EC88, 0x1375F228 */
+ RB5 = -1.02509513161107724954e3, /* 0xC0900461, 0x6A2E5992 */
+ RB6 = -4.83519191608651397019e2, /* 0xC07E384E, 0x9BDC383F */
+ SB1 = 3.03380607434824582924e1, /* 0x403E568B, 0x261D5190 */
+ SB2 = 3.25792512996573918826e2, /* 0x40745CAE, 0x221B9F0A */
+ SB3 = 1.53672958608443695994e3, /* 0x409802EB, 0x189D5118 */
+ SB4 = 3.19985821950859553908e3, /* 0x40A8FFB7, 0x688C246A */
+ SB5 = 2.55305040643316442583e3, /* 0x40A3F219, 0xCEDF3BE6 */
+ SB6 = 4.74528541206955367215e2, /* 0x407DA874, 0xE79FE763 */
+ SB7 = -2.24409524465858183362e1; /* 0xC03670E2, 0x42712D62 */
+
+
+// VARIABLES //
+
+var EXP = Math.exp;
+
+
+// ERFC //
+
+/**
+* FUNCTION: erfc( x )
+* Evaluates the complementary error function for an input value.
+*
+* @param {Number} x - input value
+* @returns {Number} evaluated complementary error function
+*/
+function erfc( x ) {
+ var sign = false,
+ tmp,
+ z, r, s, y, p, q;
+
+ // [1] Special cases...
+
+ // NaN:
+ if ( x !== x ) {
+ return NaN;
+ }
+ // Positive infinity:
+ if ( x === INF ) {
+ return 0;
+ }
+ // Negative infinity:
+ if ( x === NINF ) {
+ return 2;
+ }
+
+ // [2] Get the sign:
+ if ( x < 0 ) {
+ x = -x;
+ sign = true;
+ }
+
+ // [3] |x| < 0.84375
+ if ( x < 0.84375 ) {
+ // |x| < 2**-56
+ if ( x < SMALL ) {
+ tmp = x;
+ } else {
+ z = x * x;
+ r = PP0 + z*(PP1+z*(PP2+z*(PP3+z*PP4)));
+ s = 1 + z*(QQ1+z*(QQ2+z*(QQ3+z*(QQ4+z*QQ5))));
+ y = r / s;
+ if ( x < 0.25 ) { // |x| < 1/4
+ tmp = x + x*y;
+ } else {
+ tmp = 0.5 + (x*y + (x-0.5));
+ }
+ }
+ if ( sign ) {
+ return 1 + tmp;
+ }
+ return 1 - tmp;
+ }
+
+ // [4] 0.84375 <= |x| < 1.25
+ if ( x < 1.25 ) {
+ s = x - 1;
+ p = PA0 + s*(PA1+s*(PA2+s*(PA3+s*(PA4+s*(PA5+s*PA6)))));
+ q = 1 + s*(QA1+s*(QA2+s*(QA3+s*(QA4+s*(QA5+s*QA6)))));
+ if ( sign ) {
+ return 1 + ERX + p/q;
+ }
+ return 1 - ERX - p/q;
+ }
+
+ // [5] |x| < 28
+ if ( x < 28 ) {
+ s = 1 / (x*x);
+ // |x| < 1/0.35 ~ 2.857143
+ if ( x < 1/0.35 ) {
+ r = RA0 + s*(RA1+s*(RA2+s*(RA3+s*(RA4+s*(RA5+s*(RA6+s*RA7))))));
+ s = 1 + s*(SA1+s*(SA2+s*(SA3+s*(SA4+s*(SA5+s*(SA6+s*SA7))))));
+ } else { // |x| >= 1/0.35 ~ 2.857143
+ if ( sign && x > 6 ) { // x < -6
+ return 2 - TINY;
+ }
+ r = RB0 + s*(RB1+s*(RB2+s*(RB3+s*(RB4+s*(RB5+s*RB6)))));
+ s = 1 + s*(SB1+s*(SB2+s*(SB3+s*(SB4+s*(SB5+s*(SB6+s*SB7))))));
+ }
+ z = x & 0xffffffff00000000; // pseudo-single (20-bit) precision x;
+ r = EXP( -z*z - 0.5625 ) * EXP( (z-x)*(z+x) + r/s );
+ if ( sign ) {
+ return 2 - r/x;
+ }
+ return r/x;
+ }
+
+ if ( sign ) {
+ return 2 - TINY; // ~2
+ }
+ return TINY * TINY; // ~0
+} // end FUNCTION erfc()
+
+
+// EXPORTS
+
+module.exports = erfc;
diff --git a/lib/validate.js b/lib/validate.js
new file mode 100644
index 0000000..151fd48
--- /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( 'erfc()::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( 'erfc()::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( 'erfc()::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( 'erfc()::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( 'erfc()::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( 'erfc()::invalid option. Data type option must be a string primitive. Option: `' + opts.dtype + '`.' );
+ }
+ }
+ return null;
+} // end FUNCTION validate()
+
+
+// EXPORTS //
+
+module.exports = validate;
diff --git a/package.json b/package.json
index e3713c0..90c3d73 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": {
@@ -31,22 +35,40 @@
"error function",
"erf",
"complementary",
- "erfc"
+ "erfc",
+ "mathematics",
+ "math",
+ "matrix",
+ "matrices",
+ "array",
+ "arrays",
+ "accessor",
+ "dstructs"
],
"bugs": {
"url": "https://github.com/compute-io/erfc/issues"
},
- "dependencies": {},
+ "dependencies": {
+ "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-number-primitive": "^1.0.0",
+ "validate.io-object": "^1.0.4",
+ "validate.io-string-primitive": "^1.0.0"
+ },
"devDependencies": {
- "chai": "1.x.x",
- "mocha": "1.x.x",
+ "chai": "3.x.x",
"coveralls": "^2.11.1",
- "istanbul": "^0.3.0"
+ "istanbul": "^0.3.0",
+ "jshint": "2.x.x",
+ "jshint-stylish": "2.x.x",
+ "mocha": "2.x.x"
},
- "licenses": [
- {
- "type": "MIT",
- "url": "http://www.opensource.org/licenses/MIT"
- }
- ]
+ "license": "MIT"
}
diff --git a/test/test.accessor.js b/test/test.accessor.js
new file mode 100644
index 0000000..1a315d4
--- /dev/null
+++ b/test/test.accessor.js
@@ -0,0 +1,123 @@
+/* global describe, it, require */
+'use strict';
+
+// MODULES //
+
+var // Expectation library:
+ chai = require( 'chai' ),
+
+ // Module to be tested:
+ erfc = require( './../lib/accessor.js' );
+
+
+// VARIABLES //
+
+var expect = chai.expect,
+ assert = chai.assert;
+
+
+// TESTS //
+
+describe( 'accessor erfc', function tests() {
+
+ it( 'should export a function', function test() {
+ expect( erfc ).to.be.a( 'function' );
+ });
+
+ it( 'should evaluate the complementary error function using an accessor', function test() {
+ var data, actual, expected, i;
+
+ data = [
+ {'x':-3},
+ {'x':-2},
+ {'x':-1},
+ {'x':0},
+ {'x':1},
+ {'x':2},
+ {'x':3}
+ ];
+ actual = new Array( data.length );
+
+ actual = erfc( actual, data, getValue );
+
+ // Evaluated on Wolfram Alpha:
+ expected = [
+ 2,
+ 1.9953222,
+ 1.8427007,
+ 1,
+ 0.1572992,
+ 0.0046777,
+ 0
+ ];
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ], expected[ i ], 1e-4 );
+ }
+
+ function getValue( d ) {
+ return d.x;
+ }
+
+ data = [
+ [1,1e-306],
+ [2,-1e-306],
+ [3,1e-299],
+ [4,-1e-299],
+ [5,0.1],
+ [6,-0.1],
+ [7,0.8],
+ [8,-0.8],
+ [9,1],
+ [10,-1],
+ [11,10],
+ [12,-10],
+ [13,2],
+ [14,-2],
+ [15,3],
+ [16,-3],
+ [17,100],
+ [18,-100]
+ ];
+
+ // Evaluated on Wolfram Alpha:
+ expected = [
+ 1.00000000,
+ 1.00000000,
+ 1.00000000,
+ 1.00000000,
+ 0.887537,
+ 1.11246,
+ 0.257899,
+ 1.74210,
+ 0.15729920,
+ 1.84270079,
+ 2.08848758e-45,
+ 2.00000000,
+ 0.00467773,
+ 1.99532226,
+ 0.00002209,
+ 1.99997791,
+ 0.00000000,
+ 2.00000000
+ ];
+
+ actual = new Array( data.length );
+ actual = erfc( actual, data, getValue2 );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ], expected[ i ], 1e-4 );
+ }
+ function getValue2( d ) {
+ return d[ 1 ];
+ }
+ });
+
+ it( 'should return null if provided an empty array', function test() {
+ assert.isNull( erfc( [], [], getValue ) );
+ function getValue( d ) {
+ return d.x;
+ }
+ });
+
+});
diff --git a/test/test.array.js b/test/test.array.js
new file mode 100644
index 0000000..d308bd0
--- /dev/null
+++ b/test/test.array.js
@@ -0,0 +1,98 @@
+/* global describe, it, require */
+'use strict';
+
+// MODULES //
+
+var // Expectation library:
+ chai = require( 'chai' ),
+
+ // Module to be tested:
+ erfc = require( './../lib/array.js' );
+
+
+// VARIABLES //
+
+var expect = chai.expect,
+ assert = chai.assert;
+
+
+// TESTS //
+
+describe( 'array erfc', function tests() {
+
+ it( 'should export a function', function test() {
+ expect( erfc ).to.be.a( 'function' );
+ });
+
+ it( 'should evaluate the complementary error function', function test() {
+ var data, actual, expected, i;
+
+ data = [
+ 1e-306,
+ -1e-306,
+ 1e-299,
+ -1e-299,
+ 0.1,
+ -0.1,
+ 0.8,
+ -0.8,
+ 1,
+ -1,
+ 10,
+ -10,
+ 2,
+ -2,
+ 3,
+ -3,
+ 100,
+ -100
+ ];
+ actual = new Array( data.length );
+
+ actual = erfc( actual, data );
+
+ // Evaluated on Wolfram Alpha:
+ expected = [
+ 1.00000000,
+ 1.00000000,
+ 1.00000000,
+ 1.00000000,
+ 0.887537,
+ 1.11246,
+ 0.257899,
+ 1.74210,
+ 0.15729920,
+ 1.84270079,
+ 2.08848758e-45,
+ 2.00000000,
+ 0.00467773,
+ 1.99532226,
+ 0.00002209,
+ 1.99997791,
+ 0.00000000,
+ 2.00000000
+ ];
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ], expected[ i ], 1e-4, i );
+ }
+
+ // Typed arrays...
+ data = new Float64Array( data );
+ actual = new Float64Array( data.length );
+
+ actual = erfc( actual, data );
+ expected = new Float64Array( expected );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ], expected[ i ], 1e-4, i );
+ }
+
+ });
+
+ it( 'should return null if provided an empty array', function test() {
+ assert.isNull( erfc( [], [] ) );
+ assert.isNull( erfc( new Int8Array(), new Int8Array() ) );
+ });
+
+});
diff --git a/test/test.deepset.js b/test/test.deepset.js
new file mode 100644
index 0000000..d2a32e4
--- /dev/null
+++ b/test/test.deepset.js
@@ -0,0 +1,89 @@
+/* global describe, it, require */
+'use strict';
+
+// MODULES //
+
+var // Expectation library:
+ chai = require( 'chai' ),
+
+ // Module to be tested:
+ erfc = require( './../lib/deepset.js' );
+
+
+// VARIABLES //
+
+var expect = chai.expect,
+ assert = chai.assert;
+
+
+// TESTS //
+
+describe( 'deepset erfc', function tests() {
+
+ it( 'should export a function', function test() {
+ expect( erfc ).to.be.a( 'function' );
+ });
+
+ it( 'should compute the error function and deep set', function test() {
+ var data, expected, i;
+
+ data = [
+ {'x':-3},
+ {'x':-2},
+ {'x':-1},
+ {'x':0},
+ {'x':1},
+ {'x':2},
+ {'x':3}
+ ];
+
+ data = erfc( data, 'x' );
+
+ // Evaluated on Wolfram Alpha:
+ expected = [
+ {'x':2},
+ {'x':1.9953222},
+ {'x':1.8427007},
+ {'x':1},
+ {'x':0.1572992},
+ {'x':0.0046777},
+ {'x':0}
+ ];
+
+ for ( i = 0; i < data.length; i++ ) {
+ assert.closeTo( data[ i ].x, expected[ i ].x, 1e-4, i );
+ }
+
+ // Custom separator...
+ data = [
+ {'x':[9,-3]},
+ {'x':[9,-2]},
+ {'x':[9,-1]},
+ {'x':[9,0]},
+ {'x':[9,1]},
+ {'x':[9,2]},
+ {'x':[9,3]}
+ ];
+
+ data = erfc( data, 'x/1', '/' );
+ expected = [
+ {'x':[9,2]},
+ {'x':[9,1.9953222]},
+ {'x':[9,1.8427007]},
+ {'x':[9,1]},
+ {'x':[9,0.1572992]},
+ {'x':[9,0.0046777]},
+ {'x':[9,0]}
+ ];
+
+ for ( i = 0; i < data.length; i++ ) {
+ assert.closeTo( data[ i ].x[ 1 ], expected[ i ].x[ 1 ], 1e-4, 'Custom: ' + i );
+ }
+ });
+
+ it( 'should return null if provided an empty array', function test() {
+ assert.isNull( erfc( [], 'x' ) );
+ assert.isNull( erfc( [], 'x', '/' ) );
+ });
+
+});
diff --git a/test/test.js b/test/test.js
index 3004ddb..b0278eb 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1,11 +1,19 @@
+/* global require, describe, it */
+'use strict';
// MODULES //
var // Expectation library:
chai = require( 'chai' ),
+ // Matrix data structure:
+ matrix = require( 'dstructs-matrix' ),
+
// Module to be tested:
- erfc = require( './../lib' );
+ erfc = require( './../lib' ),
+
+ // Error function:
+ ERFC = require( './../lib/number.js' );
// VARIABLES //
@@ -17,26 +25,25 @@ var expect = chai.expect,
// TESTS //
describe( 'compute-erfc', function tests() {
- 'use strict';
it( 'should export a function', function test() {
expect( erfc ).to.be.a( 'function' );
});
- it( 'should throw an error if not provided a numeric value or an array', function test() {
+ it( 'should throw an error if the first argument is neither a number or array-like or matrix-like', function test() {
var values = [
- '5',
- true,
- undefined,
- null,
- {},
- function(){}
- ];
+ // '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( TypeError );
}
-
function badValue( value ) {
return function() {
erfc( value );
@@ -44,127 +51,308 @@ describe( 'compute-erfc', function tests() {
}
});
- it( 'should throw an error if a data array contains non-numeric values', function test() {
+ it( 'should throw an error if provided an invalid option', function test() {
var values = [
- '5',
- true,
- undefined,
- null,
- [],
- {},
- function(){}
- ];
+ '5',
+ 5,
+ true,
+ undefined,
+ null,
+ NaN,
+ [],
+ {}
+ ];
for ( var i = 0; i < values.length; i++ ) {
- expect( badValue( [ values[i] ] ) ).to.throw( TypeError );
+ expect( badValue( values[i] ) ).to.throw( TypeError );
}
-
function badValue( value ) {
return function() {
- erfc( value );
+ erfc( [1,2,3], {
+ 'accessor': value
+ });
};
}
});
- it( 'should return NaN if provided a NaN', function test() {
- var val = erfc( NaN );
- assert.isNumber( val );
- assert.ok( val !== val );
- });
+ it( 'should throw an error if provided an array and an unrecognized/unsupported data type option', function test() {
+ var values = [
+ 'beep',
+ 'boop'
+ ];
- it( 'should return 0 if provided positive infinity', function test() {
- var inf = Number.POSITIVE_INFINITY,
- val = erfc( inf );
- assert.strictEqual( val, 0 );
+ for ( var i = 0; i < values.length; i++ ) {
+ expect( badValue( values[i] ) ).to.throw( Error );
+ }
+ function badValue( value ) {
+ return function() {
+ erfc( [1,2,3], {
+ 'dtype': value
+ });
+ };
+ }
});
- it( 'should return 2 if provided negative infinity', function test() {
- var ninf = Number.NEGATIVE_INFINITY,
- val = erfc( ninf );
- assert.strictEqual( val, 2 );
+ it( 'should throw an error if provided a matrix 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() {
+ erfc( matrix( [2,2] ), {
+ 'dtype': value
+ });
+ };
+ }
});
- it( 'should return a numeric value if provided a numeric value', function test() {
- assert.isNumber( erfc( 1 ) );
+ it( 'should compute the error function when provided a number', function test() {
+ assert.strictEqual( erfc( 0 ), 1 );
+ assert.closeTo( erfc( 0.5 ), 0.479500, 1e-4 );
});
- it( 'should return an array of numbers if provided an array', function test() {
- var values = [
- 1e-306,
- -1e-306,
- 1e-299,
- -1e-299,
- 0.8,
- -0.8,
- 1,
- -1,
- 10,
- -10,
- 2,
- -2,
- 3,
- -3
- ],
- val;
+ it( 'should evaluate the complementary error function when provided a plain array', function test() {
+ var data, actual, expected, i;
- for ( var i = 0; i < values.length; i++ ) {
- val = erfc( [ values[ i ] ] );
- assert.isArray( val );
- assert.isNumber( val[ 0 ] );
+ data = [ -3, -2, -1, 0, 1, 2, 3 ];
+
+ // Evaluated on Wolfram Alpha:
+ expected = [
+ 2,
+ 1.9953222,
+ 1.8427007,
+ 1,
+ 0.1572992,
+ 0.0046777,
+ 0
+ ];
+
+ actual = erfc( data );
+ assert.notEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ], expected[ i ], 1e-4, i );
+ }
+
+ // Mutate...
+ actual = erfc( data, {
+ 'copy': false
+ });
+ assert.strictEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( data[ i ], expected[ i ], 1e-4, 'Mutated: ' + i );
}
});
- it( 'should evaluate the complementary error function', function test() {
- var values, expected, actual;
-
- values = [
- 1e-306,
- -1e-306,
- 1e-299,
- -1e-299,
- 0.1,
- -0.1,
- 0.8,
- -0.8,
- 1,
- -1,
- 10,
- -10,
+ it( 'should evaluate the complementary error function when provided a typed array', function test() {
+ var data, actual, expected, i;
+
+ data = new Int8Array( [ -3, -2, -1, 0, 1, 2, 3 ] );
+
+ expected = new Float64Array( [
2,
- -2,
- 3,
- -3,
- 100,
- -100
+ 1.9953222,
+ 1.8427007,
+ 1,
+ 0.1572992,
+ 0.0046777,
+ 0
+ ]);
+
+ actual = erfc( data );
+ assert.notEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ], expected[ i ], 1e-4 );
+ }
+
+ // Mutate:
+ actual = erfc( data, {
+ 'copy': false
+ });
+ expected = new Int8Array( [ 1, 1, 1, 1, 0, 0, 0 ] );
+ assert.strictEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( data[ i ], expected[ i ], 1e-7 );
+ }
+ });
+
+ it( 'should evaluate the complementary error function element-wise and return an array of a specific type', function test() {
+ var data, actual, expected;
+
+ data = [ -3, -2, -1, 0, 1, 2, 3 ];
+ expected = new Int8Array( [ 1, 1, 1, 1, 0, 0, 0 ] );
+
+ actual = erfc( data, {
+ 'dtype': 'int8'
+ });
+
+ assert.notEqual( actual, data );
+ assert.strictEqual( actual.BYTES_PER_ELEMENT, 1 );
+ assert.deepEqual( actual, expected );
+ });
+
+ it( 'should evaluate the complementary error function element-wise using an accessor', function test() {
+ var data, actual, expected, i;
+
+ data = [
+ [0,-3],
+ [1,-2],
+ [2,-1],
+ [3,0],
+ [4,1],
+ [5,2],
+ [6,3]
];
// Evaluated on Wolfram Alpha:
expected = [
- 1.00000000,
- 1.00000000,
- 1.00000000,
- 1.00000000,
- 0.887537,
- 1.11246,
- 0.257899,
- 1.74210,
- 0.15729920,
- 1.84270079,
- 2.08848758e-45,
- 2.00000000,
- 0.00467773,
- 1.99532226,
- 0.00002209,
- 1.99997791,
- 0.00000000,
- 2.00000000
+ 2,
+ 1.9953222,
+ 1.8427007,
+ 1,
+ 0.1572992,
+ 0.0046777,
+ 0
];
- actual = erfc( values );
+ actual = erfc( data, {
+ 'accessor': getValue
+ });
+ assert.notEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ], expected[ i ], 1e-4 );
+ }
+
+ // Mutate:
+ actual = erfc( data, {
+ 'accessor': getValue,
+ 'copy': false
+ });
+ assert.strictEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( data[ i ], expected[ i ], 1e-4 );
+ }
- for ( var i = 0; i < actual.length; i++ ) {
- assert.closeTo( actual[ i ], expected[ i ], 1e-5 );
+ function getValue( d ) {
+ return d[ 1 ];
}
});
-});
\ No newline at end of file
+ it( 'should evaluate the complementary error function element-wise and deep set', function test() {
+ var data, actual, expected, i;
+
+ data = [
+ {'x':[0,-3]},
+ {'x':[1,-2]},
+ {'x':[2,-1]},
+ {'x':[3,0]},
+ {'x':[4,1]},
+ {'x':[5,2]},
+ {'x':[6,3]}
+ ];
+ expected = [
+ {'x':[0,2]},
+ {'x':[1,1.9953222]},
+ {'x':[2,1.8427007]},
+ {'x':[3,1]},
+ {'x':[4,0.1572992]},
+ {'x':[5,0.0046777]},
+ {'x':[6,0]}
+ ];
+
+ actual = erfc( data, {
+ 'path': 'x.1'
+ });
+
+ assert.strictEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( data[ i ].x[ 1 ], expected[ i ].x[ 1 ], 1e-4 );
+ }
+
+ // Specify a path with a custom separator...
+ data = [
+ {'x':[0,-3]},
+ {'x':[1,-2]},
+ {'x':[2,-1]},
+ {'x':[3,0]},
+ {'x':[4,1]},
+ {'x':[5,2]},
+ {'x':[6,3]}
+ ];
+ actual = erfc( data, {
+ 'path': 'x/1',
+ 'sep': '/'
+ });
+ assert.strictEqual( actual, data );
+
+ for ( i = 0; i < actual.length; i++ ) {
+ assert.closeTo( actual[ i ].x[ 1 ], expected[ i ].x[ 1 ], 1e-4 );
+ }
+ });
+
+ it( 'should evaluate the complementary error function element-wise when provided a matrix', function test() {
+ var mat,
+ out,
+ d1,
+ d2,
+ i;
+
+ d1 = new Float64Array( 25 );
+ d2 = new Float64Array( 25 );
+ for ( i = 0; i < d1.length; i++ ) {
+ d1[ i ] = i / 5;
+ d2[ i ] = ERFC( i / 5);
+ }
+ mat = matrix( d1, [5,5], 'float64' );
+ out = erfc( mat );
+
+ assert.deepEqual( out.data, d2 );
+
+ // Mutate...
+ out = erfc( mat, {
+ 'copy': false
+ });
+ assert.strictEqual( mat, out );
+ assert.deepEqual( mat.data, d2 );
+ });
+
+ it( 'should evaluate the complementary error function element-wise and return a matrix of a specific type', function test() {
+ var mat,
+ out,
+ d1,
+ d2,
+ i;
+
+ d1 = new Float64Array( 25 );
+ d2 = new Float32Array( 25 );
+ for ( i = 0; i < d1.length; i++ ) {
+ d1[ i ] = i / 5;
+ d2[ i ] = ERFC( i / 5 );
+ }
+ mat = matrix( d1, [5,5], 'float64' );
+ out = erfc( mat, {
+ 'dtype': 'float32'
+ });
+
+ assert.strictEqual( out.dtype, 'float32' );
+ assert.deepEqual( out.data, d2 );
+ });
+
+ it( 'should return `null` if provided an empty data structure', function test() {
+ assert.isNull( erfc( [] ) );
+ assert.isNull( erfc( matrix( [0,0] ) ) );
+ assert.isNull( erfc( new Int8Array() ) );
+ });
+
+});
diff --git a/test/test.matrix.js b/test/test.matrix.js
new file mode 100644
index 0000000..098823e
--- /dev/null
+++ b/test/test.matrix.js
@@ -0,0 +1,82 @@
+/* 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:
+ erfc = require( './../lib/matrix.js' ),
+
+ // Error function:
+ ERFC = require( './../lib/number.js' );
+
+
+// VARIABLES //
+
+var expect = chai.expect,
+ assert = chai.assert;
+
+
+// TESTS //
+
+describe( 'matrix erfc', function tests() {
+
+ var out,
+ mat,
+ d1,
+ d2,
+ i;
+
+ d1 = new Float64Array( 25 );
+ d2 = new Float64Array( 25 );
+ for ( i = 0; i < d1.length; i++ ) {
+ d1[ i ] = i / 5;
+ d2[ i ] = ERFC( i / 5 );
+ }
+
+ beforeEach( function before() {
+ mat = matrix( d1, [5,5], 'float64' );
+ out = matrix( d2, [5,5], 'float64' );
+ });
+
+ it( 'should export a function', function test() {
+ expect( erfc ).to.be.a( 'function' );
+ });
+
+ it( 'should throw an error if provided unequal length matrices', function test() {
+ expect( badValues ).to.throw( Error );
+ function badValues() {
+ erfc( matrix( [10,10] ), mat );
+ }
+ });
+
+ it( 'should evaluate the error function for each matrix element', function test() {
+ var actual;
+
+ actual = matrix( [5,5], 'float64' );
+ actual = erfc( actual, mat );
+
+ assert.deepEqual( actual.data, out.data );
+ });
+
+ it( 'should return null if provided an empty matrix', function test() {
+ var out, mat;
+
+ out = matrix( [0,0] );
+
+ mat = matrix( [0,10] );
+ assert.isNull( erfc( out, mat ) );
+
+ mat = matrix( [10,0] );
+ assert.isNull( erfc( out, mat ) );
+
+ mat = matrix( [0,0] );
+ assert.isNull( erfc( out, mat ) );
+ });
+
+});
diff --git a/test/test.number.js b/test/test.number.js
new file mode 100644
index 0000000..e8f5fd2
--- /dev/null
+++ b/test/test.number.js
@@ -0,0 +1,56 @@
+/* global describe, it, require */
+'use strict';
+
+// MODULES //
+
+var // Expectation library:
+ chai = require( 'chai' ),
+
+ // Module to be tested:
+ erfc = require( './../lib/number.js' );
+
+
+// VARIABLES //
+
+var expect = chai.expect,
+ assert = chai.assert;
+
+
+// TESTS //
+
+describe( 'number erfc', function tests() {
+
+ it( 'should export a function', function test() {
+ expect( erfc ).to.be.a( 'function' );
+ });
+
+ it( 'should evaluate the error function', function test() {
+ assert.closeTo( erfc( 9 ), 0, 1e-4 );
+ assert.closeTo( erfc( 900 ), 0, 1e-4 );
+ assert.closeTo( erfc( 1 ), 0.157299, 1e-4 );
+ assert.closeTo( erfc( 0.1 ), 0.887537, 1e-4 );
+ });
+
+ it( 'should return NaN if provided a NaN', function test() {
+ var val = erfc( NaN );
+ assert.isNumber( val );
+ assert.ok( val !== val );
+ });
+
+ it( 'should return 0 if provided positive infinity', function test() {
+ var inf = Number.POSITIVE_INFINITY,
+ val = erfc( inf );
+ assert.strictEqual( val, 0 );
+ });
+
+ it( 'should return 2 if provided negative infinity', function test() {
+ var ninf = Number.NEGATIVE_INFINITY,
+ val = erfc( ninf );
+ assert.strictEqual( val, 2 );
+ });
+
+ it( 'should return a numeric value if provided a numeric value', function test() {
+ assert.isNumber( erfc( 1 ) );
+ });
+
+});
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 );
+ });
+
+});