Compare commits
No commits in common. "next" and "master" have entirely different histories.
|
@ -0,0 +1 @@
|
|||
tests/dist/
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"mocha": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
|
@ -0,0 +1,27 @@
|
|||
name: node-js-ci
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
node: ['14', '16']
|
||||
|
||||
name: Node ${{ matrix.node }} on ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Test
|
||||
uses: actions/setup-node@v2.4.1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm install
|
||||
- run: npm test
|
|
@ -1,5 +1,13 @@
|
|||
node_modules
|
||||
bower_components
|
||||
.env
|
||||
*~
|
||||
dist/filer-issue225.js
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Parcel build dirs
|
||||
.cache
|
||||
tests/dist
|
||||
|
||||
# nyc code coverage
|
||||
.nyc_output
|
||||
coverage
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"hooks": {
|
||||
"before:init": ["npm run test"],
|
||||
"before:bump": ["npm run build"]
|
||||
},
|
||||
"git": {
|
||||
"pushRepo": "git@github.com:filerjs/filer.git",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": true
|
||||
},
|
||||
"github": {
|
||||
"pushRepo": "git@github.com:filerjs/filer.git",
|
||||
"release": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "lts/*"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- "node_modules"
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
# Setup headless Firefox and Chrome support
|
||||
# https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-the-Chrome-addon-in-the-headless-mode
|
||||
env:
|
||||
- MOZ_HEADLESS=1
|
||||
addons:
|
||||
chrome: stable
|
||||
firefox: latest
|
||||
before_install:
|
||||
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
|
||||
|
||||
after_success:
|
||||
- npm install -g codecov
|
||||
- npm run coverage
|
||||
- codecov
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
irc: "irc.mozilla.org#filer"
|
|
@ -0,0 +1,9 @@
|
|||
Alan K <ack@modeswitch.org> (blog.modeswitch.org)
|
||||
David Humphrey <david.humphrey@senecacollege.ca> (@humphd)
|
||||
Abir Viqar <abiviq@hushmail.com>
|
||||
Barry Tulchinsky <barry.tulchinsky@gmail.com> (@btulchinsky)
|
||||
Kieran Sedgwick <kieran.sedgwick@gmail.com> (@sedge)
|
||||
Yoav Gurevich <ygurevich@ymail.com>
|
||||
Gideon Thomas <r.gideonthomas@gmail.com>
|
||||
Abdirahman Guled <aguled2@myseneca.ca>
|
||||
Ben Heidemann <ben@heidemann.co.uk>
|
|
@ -0,0 +1,75 @@
|
|||
# How to Contribute
|
||||
|
||||
The best way to get started is to read through the `Getting Started` and `Example`
|
||||
sections before having a look through the open [issues](https://github.com/js-platform/filer/issues).
|
||||
Some of the issues are marked as `good first bug`, but feel free to contribute to
|
||||
any of the issues there, or open a new one if the thing you want to work on isn't
|
||||
there yet. If you would like to have an issue assigned to you, please send me a
|
||||
message and I'll update it.
|
||||
|
||||
## Setup
|
||||
|
||||
To get a working build system do the following:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
Next, make sure you have installed Chrome and Firefox, which are needed for
|
||||
running headless versions of the tests with `npm test`.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests are written using [Mocha](https://mochajs.org/) and [Chai](http://chaijs.com/api/bdd/).
|
||||
There are a number of ways to run the tests. The preferred way is:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
This will do a build, run the linting, start a server, and load the tests into
|
||||
headless versions of Chrome and Firefox.
|
||||
|
||||
If you want more control over how tests are run, you can use other scripts:
|
||||
|
||||
* Linting is done via `npm run lint` or `npm run eslint`, both of which will run `eslint` on the `src` and `tests` directories. You can also use `npm run lint:fix` or `npm run eslint:fix`, which will run `eslint` with `--fix` on the `src` and `tests` directories, automatically fixing minor issues. Linting is run by default as part of `npm test`
|
||||
|
||||
* In headless versions of Chrome and Firefox using `npm test`. A report at the end will tell you what happened with each browser. Browser tests are preferred because they also test our providers (e.g., IndexedDB). They do take longer to run. You can also use `npm run karma-mocha-firefox` or `npm run karma-mocha-chrome` to run the tests in only one of the two headless browsers.
|
||||
|
||||
* In node.js using the Memory provider using `npm run test:node`. These run much faster, but don't run all tests (e.g., providers, watches).
|
||||
|
||||
* If you need to debug browser tests, or want to run them in a different browser, use `npm run test:manual`, which will start a server and you can point your browser to [http://localhost:1234](http://localhost:1234). Running the tests this way will also automatically watch your files, and hot-reload your code and tests, which is useful for debugging and trial/error testing.
|
||||
|
||||
* If you need to debug node.js test runs, you can do so using `npm run test:node-debug`. Then, open Chrome and browse to [chrome://inspect](chrome://inspect) and click on your tests in the inspector. The easiest way to get a breakpoint is to manually add a `debugger` keyword to your test code where you want the tests to stop.
|
||||
|
||||
> Tip: you can add `skip()` to any `it()` or `describe()` in Mocha to skip a test, or `only()` to have only that test run. For example: `describe.skip(...)` or `it.only(...)`.
|
||||
|
||||
* If you want to run migration tests separate from unit tests, use `npm run test:migrations`. Migration tests run at the end of a typical `npm test` run. If you need to create a new migration test, see [`tools/fs-image.js`](tools/fs-image.js) for details on how to generate a filesystem image, and [tests/filesystems/images/README.md](tests/filesystems/images/README.md) for more docs.
|
||||
|
||||
* If you want to manually generate coverage info for the tests, use `npm run coverage`. This is done automatically in Travis, so you shouldn't need to do it. You can see [https://codecov.io/gh/filerjs/filer](https://codecov.io/gh/filerjs/filer) for detailed reports.
|
||||
|
||||
There are a number of configurable options for the test suite, which are set via query string params.
|
||||
First, you can choose which filer source to use (i.e., src/, dist/filer-test.js, dist/filer.js or dist/filer.min.js). The default is to use what is in /dist/filer-test.js, and you can switch to other versions like so:
|
||||
|
||||
* tests/index.html?filer-dist/filer.js
|
||||
* tests/index.html?filer-dist/filer.min.js
|
||||
* tests/index.html?filer-src/filer.js (from src)
|
||||
|
||||
Second, you can specify which provider to use for all non-provider specific tests (i.e., most of the tests).
|
||||
The default provider is `Memory`, and you can switch it like so:
|
||||
|
||||
* tests/index.html?filer-provider=memory
|
||||
* tests/index.html?filer-provider=indexeddb
|
||||
|
||||
If you're writing tests, make sure you write them in the same style as existing tests, which are
|
||||
provider agnostic. See [`tests/lib/test-utils.js`](tests/lib/test-utils.js) and how it gets used
|
||||
in various tests as an example.
|
||||
|
||||
## Releases
|
||||
|
||||
In order to perform a release, you'll need commit access to the main Filer repo,
|
||||
as well as access to publish to Filer's npm module. To do a release:
|
||||
|
||||
1. Make sure you have a .env file, with your `GITHUB_TOKEN` included. See [`env.sample`](env.sample) for more info on how to create one.
|
||||
1. Login to the `npm` registry if you haven't already using `npm login`
|
||||
1. Run `npm run release`. Releases are done interactively using [release-it](https://www.npmjs.com/package/release-it), and our config is defined in [`.release-it.json`](.release-it.json).
|
|
@ -0,0 +1,9 @@
|
|||
Copyright (c) 2013 - 2019 Alan Kligman and the Filer contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,9 @@
|
|||
###
|
||||
# Dev ENVIRONMENT file
|
||||
#
|
||||
# Copy to .env to use defaults when releasing via `npm release`
|
||||
###
|
||||
|
||||
# GitHub Personal Access Token (to push releases)
|
||||
# https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
|
||||
GITHUB_TOKEN=
|
|
@ -0,0 +1,28 @@
|
|||
module.exports = function(config) {
|
||||
config.set({
|
||||
singleRun: true,
|
||||
basePath: '',
|
||||
files: [
|
||||
'node_modules/regenerator-runtime/runtime.js',
|
||||
'tests/dist/index.js'
|
||||
],
|
||||
frameworks: ['mocha', 'chai'],
|
||||
reporters: ['mocha', 'summary'],
|
||||
client: {
|
||||
captureConsole: true,
|
||||
mocha: {
|
||||
ui: 'bdd',
|
||||
timeout: 5000,
|
||||
slow: 250
|
||||
}
|
||||
},
|
||||
summaryReporter: {
|
||||
// 'failed', 'skipped' or 'all'
|
||||
show: 'failed',
|
||||
// Limit the spec label to this length
|
||||
specLength: 50,
|
||||
// Show an 'all' column as a summary
|
||||
overviewColumn: true
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,85 @@
|
|||
/*global setImmediate: false, setTimeout: false, console: false */
|
||||
|
||||
/**
|
||||
* async.js shim, based on https://raw.github.com/caolan/async/master/lib/async.js Feb 18, 2014
|
||||
* Used under MIT - https://github.com/caolan/async/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
(function () {
|
||||
|
||||
var async = {};
|
||||
|
||||
// async.js functions used in Filer
|
||||
|
||||
//// nextTick implementation with browser-compatible fallback ////
|
||||
if (typeof process === 'undefined' || !(process.nextTick)) {
|
||||
if (typeof setImmediate === 'function') {
|
||||
async.nextTick = function (fn) {
|
||||
// not a direct alias for IE10 compatibility
|
||||
setImmediate(fn);
|
||||
};
|
||||
async.setImmediate = async.nextTick;
|
||||
}
|
||||
else {
|
||||
async.nextTick = function (fn) {
|
||||
setTimeout(fn, 0);
|
||||
};
|
||||
async.setImmediate = async.nextTick;
|
||||
}
|
||||
}
|
||||
else {
|
||||
async.nextTick = process.nextTick;
|
||||
if (typeof setImmediate !== 'undefined') {
|
||||
async.setImmediate = function (fn) {
|
||||
// not a direct alias for IE10 compatibility
|
||||
setImmediate(fn);
|
||||
};
|
||||
}
|
||||
else {
|
||||
async.setImmediate = async.nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
async.eachSeries = function (arr, iterator, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!arr.length) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
var iterate = function () {
|
||||
iterator(arr[completed], function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
if (completed >= arr.length) {
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
iterate();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
iterate();
|
||||
};
|
||||
async.forEachSeries = async.eachSeries;
|
||||
|
||||
// AMD / RequireJS
|
||||
if (typeof define !== 'undefined' && define.amd) {
|
||||
define([], function () {
|
||||
return async;
|
||||
});
|
||||
}
|
||||
// Node.js
|
||||
else if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = async;
|
||||
}
|
||||
// included directly via <script> tag
|
||||
else {
|
||||
root.async = async;
|
||||
}
|
||||
|
||||
}());
|
|
@ -0,0 +1,71 @@
|
|||
// Based on https://github.com/diy/intercom.js/blob/master/lib/events.js
|
||||
// Copyright 2012 DIY Co Apache License, Version 2.0
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
function removeItem(item, array) {
|
||||
for (var i = array.length - 1; i >= 0; i--) {
|
||||
if (array[i] === item) {
|
||||
array.splice(i, 1);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
var EventEmitter = function() {};
|
||||
|
||||
EventEmitter.createInterface = function(space) {
|
||||
var methods = {};
|
||||
|
||||
methods.on = function(name, fn) {
|
||||
if (typeof this[space] === 'undefined') {
|
||||
this[space] = {};
|
||||
}
|
||||
if (!this[space].hasOwnProperty(name)) {
|
||||
this[space][name] = [];
|
||||
}
|
||||
this[space][name].push(fn);
|
||||
};
|
||||
|
||||
methods.off = function(name, fn) {
|
||||
if (typeof this[space] === 'undefined') return;
|
||||
if (this[space].hasOwnProperty(name)) {
|
||||
removeItem(fn, this[space][name]);
|
||||
}
|
||||
};
|
||||
|
||||
methods.trigger = function(name) {
|
||||
if (typeof this[space] !== 'undefined' && this[space].hasOwnProperty(name)) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
for (var i = 0; i < this[space][name].length; i++) {
|
||||
this[space][name][i].apply(this[space][name][i], args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
methods.removeAllListeners = function(name) {
|
||||
if (typeof this[space] === 'undefined') return;
|
||||
var self = this;
|
||||
self[space][name].forEach(function(fn) {
|
||||
self.off(name, fn);
|
||||
});
|
||||
};
|
||||
|
||||
return methods;
|
||||
};
|
||||
|
||||
var pvt = EventEmitter.createInterface('_handlers');
|
||||
EventEmitter.prototype._on = pvt.on;
|
||||
EventEmitter.prototype._off = pvt.off;
|
||||
EventEmitter.prototype._trigger = pvt.trigger;
|
||||
|
||||
var pub = EventEmitter.createInterface('handlers');
|
||||
EventEmitter.prototype.on = function() {
|
||||
pub.on.apply(this, arguments);
|
||||
Array.prototype.unshift.call(arguments, 'on');
|
||||
this._trigger.apply(this, arguments);
|
||||
};
|
||||
EventEmitter.prototype.off = pub.off;
|
||||
EventEmitter.prototype.trigger = pub.trigger;
|
||||
EventEmitter.prototype.removeAllListeners = pub.removeAllListeners;
|
||||
|
||||
module.exports = EventEmitter;
|
|
@ -0,0 +1,318 @@
|
|||
// Based on https://github.com/diy/intercom.js/blob/master/lib/intercom.js
|
||||
// Copyright 2012 DIY Co Apache License, Version 2.0
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
var EventEmitter = require('./eventemitter.js');
|
||||
var guid = require('../src/shared.js').guid;
|
||||
|
||||
function throttle(delay, fn) {
|
||||
var last = 0;
|
||||
return function() {
|
||||
var now = Date.now();
|
||||
if (now - last > delay) {
|
||||
last = now;
|
||||
fn.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function extend(a, b) {
|
||||
if (typeof a === 'undefined' || !a) { a = {}; }
|
||||
if (typeof b === 'object') {
|
||||
for (var key in b) {
|
||||
if (b.hasOwnProperty(key)) {
|
||||
a[key] = b[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
var localStorage = (function(window) {
|
||||
if (typeof window === 'undefined' ||
|
||||
typeof window.localStorage === 'undefined') {
|
||||
return {
|
||||
getItem : function() {},
|
||||
setItem : function() {},
|
||||
removeItem : function() {}
|
||||
};
|
||||
}
|
||||
return window.localStorage;
|
||||
}(global));
|
||||
|
||||
function Intercom() {
|
||||
var self = this;
|
||||
var now = Date.now();
|
||||
|
||||
this.origin = guid();
|
||||
this.lastMessage = now;
|
||||
this.receivedIDs = {};
|
||||
this.previousValues = {};
|
||||
|
||||
var storageHandler = function() {
|
||||
self._onStorageEvent.apply(self, arguments);
|
||||
};
|
||||
|
||||
// If we're in node.js, skip event registration
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.attachEvent) {
|
||||
document.attachEvent('onstorage', storageHandler);
|
||||
} else {
|
||||
global.addEventListener('storage', storageHandler, false);
|
||||
}
|
||||
}
|
||||
|
||||
Intercom.prototype._transaction = function(fn) {
|
||||
var TIMEOUT = 1000;
|
||||
var WAIT = 20;
|
||||
var self = this;
|
||||
var executed = false;
|
||||
var listening = false;
|
||||
var waitTimer = null;
|
||||
|
||||
function lock() {
|
||||
if (executed) {
|
||||
return;
|
||||
}
|
||||
|
||||
var now = Date.now();
|
||||
var activeLock = localStorage.getItem(INDEX_LOCK)|0;
|
||||
if (activeLock && now - activeLock < TIMEOUT) {
|
||||
if (!listening) {
|
||||
self._on('storage', lock);
|
||||
listening = true;
|
||||
}
|
||||
waitTimer = setTimeout(lock, WAIT);
|
||||
return;
|
||||
}
|
||||
executed = true;
|
||||
localStorage.setItem(INDEX_LOCK, now);
|
||||
|
||||
fn();
|
||||
unlock();
|
||||
}
|
||||
|
||||
function unlock() {
|
||||
if (listening) {
|
||||
self._off('storage', lock);
|
||||
}
|
||||
if (waitTimer) {
|
||||
clearTimeout(waitTimer);
|
||||
}
|
||||
localStorage.removeItem(INDEX_LOCK);
|
||||
}
|
||||
|
||||
lock();
|
||||
};
|
||||
|
||||
Intercom.prototype._cleanup_emit = throttle(100, function() {
|
||||
var self = this;
|
||||
|
||||
self._transaction(function() {
|
||||
var now = Date.now();
|
||||
var threshold = now - THRESHOLD_TTL_EMIT;
|
||||
var changed = 0;
|
||||
var messages;
|
||||
|
||||
try {
|
||||
messages = JSON.parse(localStorage.getItem(INDEX_EMIT) || '[]');
|
||||
} catch(e) {
|
||||
messages = [];
|
||||
}
|
||||
for (var i = messages.length - 1; i >= 0; i--) {
|
||||
if (messages[i].timestamp < threshold) {
|
||||
messages.splice(i, 1);
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
if (changed > 0) {
|
||||
localStorage.setItem(INDEX_EMIT, JSON.stringify(messages));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Intercom.prototype._cleanup_once = throttle(100, function() {
|
||||
var self = this;
|
||||
|
||||
self._transaction(function() {
|
||||
var timestamp, ttl, key;
|
||||
var table;
|
||||
var now = Date.now();
|
||||
var changed = 0;
|
||||
|
||||
try {
|
||||
table = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}');
|
||||
} catch(e) {
|
||||
table = {};
|
||||
}
|
||||
for (key in table) {
|
||||
if (self._once_expired(key, table)) {
|
||||
delete table[key];
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed > 0) {
|
||||
localStorage.setItem(INDEX_ONCE, JSON.stringify(table));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Intercom.prototype._once_expired = function(key, table) {
|
||||
if (!table) {
|
||||
return true;
|
||||
}
|
||||
if (!table.hasOwnProperty(key)) {
|
||||
return true;
|
||||
}
|
||||
if (typeof table[key] !== 'object') {
|
||||
return true;
|
||||
}
|
||||
|
||||
var ttl = table[key].ttl || THRESHOLD_TTL_ONCE;
|
||||
var now = Date.now();
|
||||
var timestamp = table[key].timestamp;
|
||||
return timestamp < now - ttl;
|
||||
};
|
||||
|
||||
Intercom.prototype._localStorageChanged = function(event, field) {
|
||||
if (event && event.key) {
|
||||
return event.key === field;
|
||||
}
|
||||
|
||||
var currentValue = localStorage.getItem(field);
|
||||
if (currentValue === this.previousValues[field]) {
|
||||
return false;
|
||||
}
|
||||
this.previousValues[field] = currentValue;
|
||||
return true;
|
||||
};
|
||||
|
||||
Intercom.prototype._onStorageEvent = function(event) {
|
||||
event = event || global.event;
|
||||
var self = this;
|
||||
|
||||
if (this._localStorageChanged(event, INDEX_EMIT)) {
|
||||
this._transaction(function() {
|
||||
var now = Date.now();
|
||||
var data = localStorage.getItem(INDEX_EMIT);
|
||||
var messages;
|
||||
|
||||
try {
|
||||
messages = JSON.parse(data || '[]');
|
||||
} catch(e) {
|
||||
messages = [];
|
||||
}
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
if (messages[i].origin === self.origin) continue;
|
||||
if (messages[i].timestamp < self.lastMessage) continue;
|
||||
if (messages[i].id) {
|
||||
if (self.receivedIDs.hasOwnProperty(messages[i].id)) continue;
|
||||
self.receivedIDs[messages[i].id] = true;
|
||||
}
|
||||
self.trigger(messages[i].name, messages[i].payload);
|
||||
}
|
||||
self.lastMessage = now;
|
||||
});
|
||||
}
|
||||
|
||||
this._trigger('storage', event);
|
||||
};
|
||||
|
||||
Intercom.prototype._emit = function(name, message, id) {
|
||||
id = (typeof id === 'string' || typeof id === 'number') ? String(id) : null;
|
||||
if (id && id.length) {
|
||||
if (this.receivedIDs.hasOwnProperty(id)) return;
|
||||
this.receivedIDs[id] = true;
|
||||
}
|
||||
|
||||
var packet = {
|
||||
id : id,
|
||||
name : name,
|
||||
origin : this.origin,
|
||||
timestamp : Date.now(),
|
||||
payload : message
|
||||
};
|
||||
|
||||
var self = this;
|
||||
this._transaction(function() {
|
||||
var data = localStorage.getItem(INDEX_EMIT) || '[]';
|
||||
var delimiter = (data === '[]') ? '' : ',';
|
||||
data = [data.substring(0, data.length - 1), delimiter, JSON.stringify(packet), ']'].join('');
|
||||
localStorage.setItem(INDEX_EMIT, data);
|
||||
self.trigger(name, message);
|
||||
|
||||
setTimeout(function() {
|
||||
self._cleanup_emit();
|
||||
}, 50);
|
||||
});
|
||||
};
|
||||
|
||||
Intercom.prototype.emit = function(name, message) {
|
||||
this._emit.apply(this, arguments);
|
||||
this._trigger('emit', name, message);
|
||||
};
|
||||
|
||||
Intercom.prototype.once = function(key, fn, ttl) {
|
||||
if (!Intercom.supported) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this._transaction(function() {
|
||||
var data;
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}');
|
||||
} catch(e) {
|
||||
data = {};
|
||||
}
|
||||
if (!self._once_expired(key, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
data[key] = {};
|
||||
data[key].timestamp = Date.now();
|
||||
if (typeof ttl === 'number') {
|
||||
data[key].ttl = ttl * 1000;
|
||||
}
|
||||
|
||||
localStorage.setItem(INDEX_ONCE, JSON.stringify(data));
|
||||
fn();
|
||||
|
||||
setTimeout(function() {
|
||||
self._cleanup_once();
|
||||
}, 50);
|
||||
});
|
||||
};
|
||||
|
||||
extend(Intercom.prototype, EventEmitter.prototype);
|
||||
|
||||
Intercom.supported = (typeof localStorage !== 'undefined');
|
||||
|
||||
var INDEX_EMIT = 'intercom';
|
||||
var INDEX_ONCE = 'intercom_once';
|
||||
var INDEX_LOCK = 'intercom_lock';
|
||||
|
||||
var THRESHOLD_TTL_EMIT = 50000;
|
||||
var THRESHOLD_TTL_ONCE = 1000 * 3600;
|
||||
|
||||
Intercom.destroy = function() {
|
||||
localStorage.removeItem(INDEX_LOCK);
|
||||
localStorage.removeItem(INDEX_EMIT);
|
||||
localStorage.removeItem(INDEX_ONCE);
|
||||
};
|
||||
|
||||
Intercom.getInstance = (function() {
|
||||
var intercom;
|
||||
return function() {
|
||||
if (!intercom) {
|
||||
intercom = new Intercom();
|
||||
}
|
||||
return intercom;
|
||||
};
|
||||
})();
|
||||
|
||||
module.exports = Intercom;
|
File diff suppressed because it is too large
Load Diff
92
package.json
92
package.json
|
@ -5,43 +5,91 @@
|
|||
"fs",
|
||||
"node",
|
||||
"file",
|
||||
"system",
|
||||
"filesystem",
|
||||
"browser",
|
||||
"indexeddb",
|
||||
"idb",
|
||||
"websql"
|
||||
"idb"
|
||||
],
|
||||
"version": "1.0.0",
|
||||
"version": "1.4.1",
|
||||
"author": "Alan K <ack@modeswitch.org> (http://blog.modeswitch.org)",
|
||||
"homepage": "http://filerjs.github.io/filer",
|
||||
"bugs": "https://github.com/filerjs/filer/issues",
|
||||
"license": "BSD",
|
||||
"license": "BSD-2-Clause",
|
||||
"scripts": {
|
||||
"build": "rollup -c --environment=development",
|
||||
"test": "node node_modules/mocha/bin/mocha test/"
|
||||
"eslint": "npm run lint",
|
||||
"eslint:fix": "npm run lint:fix",
|
||||
"lint": "eslint src tests",
|
||||
"lint:fix": "eslint --fix src tests",
|
||||
"test:node": "mocha --timeout 5000 tests",
|
||||
"pretest:node-debug": "echo \"Open Chrome to chrome://inspect to debug tests...\"",
|
||||
"test:node-debug": "mocha --timeout 5000 --inspect-brk tests",
|
||||
"test:manual": "parcel tests/index.html --out-dir tests/dist",
|
||||
"test:migrations": "mocha tests/filesystems/migrations",
|
||||
"pretest": "npm run lint",
|
||||
"test": "npm run karma-mocha",
|
||||
"posttest": "npm run test:migrations",
|
||||
"prebuild": "parcel build --global Filer src/index.js --no-minify --out-file filer.js",
|
||||
"build": "parcel build --global Filer src/index.js --out-file filer.min.js --detailed-report",
|
||||
"build-tests": "parcel build tests/index.js --no-source-maps --out-dir tests/dist",
|
||||
"prekarma-mocha-firefox": "npm run build-tests",
|
||||
"karma-mocha-firefox": "karma start karma.conf.js --browsers FirefoxHeadless",
|
||||
"prekarma-mocha-chrome": "npm run build-tests",
|
||||
"karma-mocha-chrome": "karma start karma.conf.js --browsers ChromeHeadless",
|
||||
"prekarma-mocha": "npm run build-tests",
|
||||
"karma-mocha": "karma start karma.conf.js --browsers ChromeHeadless,FirefoxHeadless",
|
||||
"coverage": "nyc mocha tests/index.js",
|
||||
"release": "run.env release-it"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filerjs/filer.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^0.1.2",
|
||||
"buffer": "^5.1.0",
|
||||
"debug": "^3.1.0",
|
||||
"es6-promisify": "^7.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"mocha": "^5.2.0",
|
||||
"should": "^13.2.1",
|
||||
"source-map-support": "^0.5.6",
|
||||
"url-parse": "^1.4.1",
|
||||
"uuid-base62": "^0.1.0",
|
||||
"uuid-parse": "^1.0.0"
|
||||
"schema-utils": "^3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup": "^0.59.4",
|
||||
"rollup-plugin-node-resolve": "^3.3.0",
|
||||
"rollup-plugin-uglify": "^4.0.0",
|
||||
"semver": "^5.5.0"
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"chai": "^4.3.4",
|
||||
"chai-datetime": "^1.8.0",
|
||||
"eslint": "^7.32.0",
|
||||
"fake-indexeddb": "^3.1.7",
|
||||
"karma": "^6.3.8",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-firefox-launcher": "^2.1.2",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-summary-reporter": "^3.0.0",
|
||||
"meow": "^10.0.1",
|
||||
"mocha": "^9.1.3",
|
||||
"nyc": "^15.1.0",
|
||||
"parcel-bundler": "^1.12.5",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"release-it": "^14.11.6",
|
||||
"run.env": "^1.1.0",
|
||||
"unused-filename": "^3.0.1",
|
||||
"walk": "^2.3.15"
|
||||
},
|
||||
"main": "dist/filer.js",
|
||||
"module": "src/index.js"
|
||||
"main": "./src/index.js",
|
||||
"browser": "./dist/filer.min.js",
|
||||
"files": [
|
||||
"src",
|
||||
"lib",
|
||||
"dist",
|
||||
"shims",
|
||||
"webpack"
|
||||
],
|
||||
"nyc": {
|
||||
"exclude": [
|
||||
"tests/**/*.js",
|
||||
"lib/**/*.js",
|
||||
"src/providers/**/*.js"
|
||||
],
|
||||
"reporter": [
|
||||
"lcov",
|
||||
"text"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<progress id="progress" value=0></progress>
|
||||
<div id="output"></div>
|
||||
<div id="stderr"></div>
|
||||
</body>
|
||||
<script type="text/javascript" src="simple-statistics/src/simple_statistics.js"></script>
|
||||
<script src="../dist/filer-perf.js"></script>
|
||||
</html>
|
|
@ -0,0 +1,119 @@
|
|||
var util = require('../tests/lib/test-utils.js');
|
||||
|
||||
function setImmediate(cb) {
|
||||
setTimeout(cb, 0);
|
||||
}
|
||||
|
||||
function parse_query() {
|
||||
var query = window.location.search.substring(1);
|
||||
var parsed = {};
|
||||
query.split('&').forEach(function(pair) {
|
||||
pair = pair.split('=');
|
||||
var key = decodeURIComponent(pair[0]);
|
||||
var value = decodeURIComponent(pair[1]);
|
||||
parsed[key] = value;
|
||||
});
|
||||
return parsed;
|
||||
}
|
||||
|
||||
var query = parse_query();
|
||||
|
||||
function time(test, cb) {
|
||||
var start = performance.now();
|
||||
function done() {
|
||||
var end = performance.now();
|
||||
cb(end - start);
|
||||
}
|
||||
test(done);
|
||||
}
|
||||
|
||||
var random_data = new Buffer(1024); // 1kB buffer
|
||||
var read_buffer = new Buffer(1024);
|
||||
|
||||
function run(iter) {
|
||||
iter = (undefined == iter) ? 0 : iter;
|
||||
|
||||
function before() {
|
||||
util.setup(function() {
|
||||
setImmediate(during);
|
||||
});
|
||||
}
|
||||
|
||||
function during() {
|
||||
var fs = util.fs();
|
||||
|
||||
window.crypto.getRandomValues(random_data);
|
||||
time(function(done) {
|
||||
fs.mkdir('/tmp', function(err) {
|
||||
fs.stat('/tmp', function(err, stats) {
|
||||
fs.open('/tmp/test', 'w', function(err, fd) {
|
||||
fs.write(fd, random_data, null, null, null, function(err, nbytes) {
|
||||
fs.close(fd, function(err) {
|
||||
fs.stat('/tmp/test', function(err, stats) {
|
||||
fs.open('/tmp/test', 'r', function(err, fd) {
|
||||
fs.read(fd, read_buffer, null, null, null, function(err, nbytes) {
|
||||
fs.close(fd, function(err) {
|
||||
fs.unlink('/tmp/test', function(err) {
|
||||
done();
|
||||
});});});});});});});});});});
|
||||
}, after);
|
||||
}
|
||||
|
||||
function after(dt) {
|
||||
util.cleanup(complete.bind(null, iter, dt));
|
||||
}
|
||||
|
||||
before();
|
||||
}
|
||||
|
||||
var results = [];
|
||||
function complete(iter, result) {
|
||||
results.push(result);
|
||||
|
||||
if(++iter < iterations) {
|
||||
setImmediate(run.bind(null, iter));
|
||||
} else {
|
||||
do_stats();
|
||||
}
|
||||
|
||||
progress.value = iter;
|
||||
}
|
||||
|
||||
function do_stats() {
|
||||
var output = document.getElementById("output");
|
||||
var stats = {
|
||||
mean: ss.mean(results) + " ms",
|
||||
min: ss.min(results),
|
||||
max: ss.max(results),
|
||||
med_abs_dev: ss.median_absolute_deviation(results),
|
||||
};
|
||||
|
||||
var t = document.createElement("table");
|
||||
var tbody = document.createElement("tbody");
|
||||
var keys = Object.keys(stats);
|
||||
keys.forEach(function(key) {
|
||||
var row = document.createElement("tr");
|
||||
|
||||
var key_cell = document.createElement("td");
|
||||
var key_cell_text = document.createTextNode(key);
|
||||
key_cell.appendChild(key_cell_text);
|
||||
row.appendChild(key_cell);
|
||||
|
||||
var val_cell = document.createElement("td");
|
||||
var val_cell_text = document.createTextNode(stats[key]);
|
||||
val_cell.appendChild(val_cell_text);
|
||||
row.appendChild(val_cell);
|
||||
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
t.appendChild(tbody);
|
||||
output.appendChild(t);
|
||||
}
|
||||
|
||||
var query = parse_query();
|
||||
var iterations = query.iterations || 10;
|
||||
var progress = document.getElementById("progress");
|
||||
progress.max = iterations;
|
||||
|
||||
run();
|
|
@ -0,0 +1,3 @@
|
|||
components
|
||||
build
|
||||
node_modules
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"indent": 4,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"globals": {
|
||||
"require": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 0.10
|
||||
script:
|
||||
- npm install
|
||||
- npm test
|
||||
- npm run cov
|
|
@ -0,0 +1,242 @@
|
|||
Basic contracts of functions:
|
||||
|
||||
* Functions do not modify their arguments e.g. change their order
|
||||
* Invalid input, like empty lists to functions that need 1+ items to work, will cause functions to return `null`.
|
||||
|
||||
# Basic Array Operations
|
||||
|
||||
### .mixin(array)
|
||||
|
||||
_Optionally_ mix in the following functions into the `Array` prototype. Otherwise
|
||||
you can use them off of the simple-statistics object itself.
|
||||
|
||||
If given a particular array instance as an argument, this adds the functions
|
||||
only to that array rather than the global `Array.prototype`. Without an argument,
|
||||
it runs on the global `Array.prototype`.
|
||||
|
||||
### .mean(x)
|
||||
|
||||
Mean of a single-dimensional Array of numbers. _Also available as `.average(x)`_
|
||||
|
||||
### .sum(x)
|
||||
|
||||
Sum of a single-dimensional Array of numbers.
|
||||
|
||||
### .mode(x)
|
||||
|
||||
Returns the number that appears most frequently in a single-dimensional Array
|
||||
of numbers. If there are multiple modes, the one that appears last
|
||||
is returned.
|
||||
|
||||
### .variance(x)
|
||||
|
||||
[Variance](http://en.wikipedia.org/wiki/Variance) of a single-dimensional Array of numbers.
|
||||
|
||||
### .standard_deviation(x)
|
||||
|
||||
[Standard Deviation](http://en.wikipedia.org/wiki/Standard_deviation) of a single-dimensional Array of numbers.
|
||||
|
||||
### .sample(array, n)
|
||||
|
||||
Return a [simple random sample](http://en.wikipedia.org/wiki/Simple_random_sample)
|
||||
of the given array. The sampling is _without replacement_, and uses a Fisher-Yates
|
||||
sample to randomize.
|
||||
|
||||
### .median_absolute_deviation(x)
|
||||
|
||||
The Median Absolute Deviation (MAD) is a robust measure of statistical
|
||||
dispersion. It is more resilient to outliers than the standard deviation.
|
||||
Accepts a single-dimensional array of numbers and returns a dispersion value.
|
||||
|
||||
Also aliased to `.mad(x)` for brevity.
|
||||
|
||||
### .median(x)
|
||||
|
||||
[Median](http://en.wikipedia.org/wiki/Median) of a single-dimensional array of numbers.
|
||||
|
||||
### .geometric_mean(x)
|
||||
|
||||
[Geometric mean](http://en.wikipedia.org/wiki/Geometric_mean) of a single-dimensional array of **positive** numbers.
|
||||
|
||||
### .harmonic_mean(x)
|
||||
|
||||
[Harmonic mean](http://en.wikipedia.org/wiki/Harmonic_mean) of a single-dimensional array of **positive** numbers.
|
||||
|
||||
### .root_mean_square(x)
|
||||
|
||||
[Root mean square (RMS)](http://en.wikipedia.org/wiki/Root_mean_square) of a single-dimensional array of numbers.
|
||||
|
||||
Also aliased to `.rms(x)` for brevity.
|
||||
|
||||
### .min(x)
|
||||
|
||||
Finds the minimum of a single-dimensional array of numbers. This runs in linear `O(n)` time.
|
||||
|
||||
### .max(x)
|
||||
|
||||
Finds the maximum of a single-dimensional array of numbers. This runs in linear `O(n)` time.
|
||||
|
||||
### .t_test(sample, x)
|
||||
|
||||
Does a [student's t-test](http://en.wikipedia.org/wiki/Student's_t-test) of a dataset `sample`, represented by a single-dimensional array of numbers. `x` is the known value, and the result is a measure of [statistical significance](http://en.wikipedia.org/wiki/Statistical_significance).
|
||||
|
||||
### .t_test_two_sample(sample_x, sample_y, difference)
|
||||
|
||||
The two-sample t-test is used to compare samples from two populations or groups,
|
||||
confirming or denying the suspicion (null hypothesis) that the populations are
|
||||
the same. It returns a t-value that you can then look up to give certain
|
||||
judgements of confidence based on a t distribution table.
|
||||
|
||||
This implementation expects the samples `sample_x` and `sample_y` to be given
|
||||
as one-dimensional arrays of more than one number each.
|
||||
|
||||
### .sample_variance(x)
|
||||
|
||||
Produces [sample variance](http://mathworld.wolfram.com/SampleVariance.html)
|
||||
of a single-dimensional array of numbers.
|
||||
|
||||
### .sample_covariance(a, b)
|
||||
|
||||
Produces [sample covariance](http://en.wikipedia.org/wiki/Sample_mean_and_sample_covariance)
|
||||
of two single-dimensional arrays of numbers.
|
||||
|
||||
### .sample_correlation(a, b)
|
||||
|
||||
Produces [sample correlation](http://en.wikipedia.org/wiki/Correlation_and_dependence)
|
||||
of two single-dimensional arrays of numbers.
|
||||
|
||||
### .quantile(sample, p)
|
||||
|
||||
Does a [quantile](http://en.wikipedia.org/wiki/Quantile) of a dataset `sample`,
|
||||
at p. For those familiary with the `k/q` syntax, `p == k/q`. `sample` must
|
||||
be a single-dimensional array of numbers. p must be a number greater than or equal to
|
||||
than zero and less or equal to than one, or an array of numbers following that rule.
|
||||
If an array is given, an array of results will be returned instead of a single
|
||||
number.
|
||||
|
||||
### .chunk(sample, chunkSize)
|
||||
|
||||
Given a `sample` array, and a positive integer `chunkSize`, splits an array
|
||||
into chunks of `chunkSize` size and returns an array of those chunks. This
|
||||
does not change the input value. If the length of `sample` is not divisible
|
||||
by `chunkSize`, the last array will be shorter than the rest.
|
||||
|
||||
### .shuffle(sample)
|
||||
|
||||
Given a `sample` array (with any type of contents), return a random permutation
|
||||
of that array, using the [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)
|
||||
algorithm.
|
||||
|
||||
### .shuffle_in_place(sample)
|
||||
|
||||
Given a `sample` array (with any type of contents), return a random permutation
|
||||
of that array, using the [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)
|
||||
algorithm.
|
||||
|
||||
This changes the input array in-place, as well as returns it - unlike `.shuffle()`,
|
||||
it does not create a shallow copy of the array.
|
||||
|
||||
### .quantile_sorted(sample, p)
|
||||
|
||||
Does a [quantile](http://en.wikipedia.org/wiki/Quantile) of a dataset `sample`,
|
||||
at p. `sample` must be a one-dimensional _sorted_ array of numbers, and
|
||||
`p` must be a single number from zero to one.
|
||||
|
||||
### .iqr(sample)
|
||||
|
||||
Calculates the [Interquartile range](http://en.wikipedia.org/wiki/Interquartile_range) of
|
||||
a sample - the difference between the upper and lower quartiles. Useful
|
||||
as a measure of dispersion.
|
||||
|
||||
_Also available as `.interquartile_range(x)`_
|
||||
|
||||
### .sample_skewness(sample)
|
||||
|
||||
Calculates the [skewness](http://en.wikipedia.org/wiki/Skewness) of
|
||||
a sample, a measure of the extent to which a probability distribution of a
|
||||
real-valued random variable "leans" to one side of the mean.
|
||||
The skewness value can be positive or negative, or even undefined.
|
||||
|
||||
This implementation uses the [Fisher-Pearson standardized moment coefficient](http://en.wikipedia.org/wiki/Skewness#Pearson.27s_skewness_coefficients),
|
||||
which means that it behaves the same as Excel, Minitab, SAS, and SPSS.
|
||||
|
||||
Skewness is only valid for samples of over three values.
|
||||
|
||||
### .jenks(data, number_of_classes)
|
||||
|
||||
Find the [Jenks Natural Breaks](http://en.wikipedia.org/wiki/Jenks_natural_breaks_optimization) for
|
||||
a single-dimensional array of numbers as input and a desired `number_of_classes`.
|
||||
The result is a single-dimensional with class breaks, including the minimum
|
||||
and maximum of the input array.
|
||||
|
||||
### .r_squared(data, function)
|
||||
|
||||
Find the [r-squared](http://en.wikipedia.org/wiki/Coefficient_of_determination) value of a particular dataset, expressed as a two-dimensional `Array` of numbers, against a `Function`.
|
||||
|
||||
var r_squared = ss.r_squared([[1, 1]], function(x) { return x * 2; });
|
||||
|
||||
### .cumulative_std_normal_probability(z)
|
||||
|
||||
Look up the given `z` value in a [standard normal table](http://en.wikipedia.org/wiki/Standard_normal_table)
|
||||
to calculate the probability of a random variable appearing with a given value.
|
||||
|
||||
### .z_score(x, mean, standard_deviation)
|
||||
|
||||
The standard score is the number of standard deviations an observation
|
||||
or datum is above or below the mean.
|
||||
|
||||
### .standard_normal_table
|
||||
|
||||
A [standard normal table](http://en.wikipedia.org/wiki/Standard_normal_table) from
|
||||
which to pull values of Φ (phi).
|
||||
|
||||
## Regression
|
||||
|
||||
### .linear_regression()
|
||||
|
||||
Create a new linear regression solver.
|
||||
|
||||
#### .data([[1, 1], [2, 2]])
|
||||
|
||||
Set the data of a linear regression. The input is a two-dimensional array of numbers, which are treated as coordinates, like `[[x, y], [x1, y1]]`.
|
||||
|
||||
#### .line()
|
||||
|
||||
Get the linear regression line: this returns a function that you can
|
||||
give `x` values and it will return `y` values. Internally, this uses the `m()`
|
||||
and `b()` values and the classic `y = mx + b` equation.
|
||||
|
||||
var linear_regression_line = ss.linear_regression()
|
||||
.data([[0, 1], [2, 2], [3, 3]]).line();
|
||||
linear_regression_line(5);
|
||||
|
||||
#### .m()
|
||||
|
||||
Just get the slope of the fitted regression line, the `m` component of the full
|
||||
line equation. Returns a number.
|
||||
|
||||
#### .b()
|
||||
|
||||
Just get the y-intercept of the fitted regression line, the `b` component
|
||||
of the line equation. Returns a number.
|
||||
|
||||
## Classification
|
||||
|
||||
### .bayesian()
|
||||
|
||||
Create a naïve bayesian classifier.
|
||||
|
||||
### .train(item, category)
|
||||
|
||||
Train the classifier to classify a certain item, given as an object with keys,
|
||||
to be in a certain category, given as a string.
|
||||
|
||||
### .score(item)
|
||||
|
||||
Get the classifications of a certain item, given as an object of
|
||||
`category -> score` mappings.
|
||||
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({ species: 'Cat' }, 'animal');
|
||||
bayes.score({ species: 'Cat' });
|
||||
// { animal: 1 }
|
|
@ -0,0 +1,60 @@
|
|||
# CHANGELOG
|
||||
|
||||
## 0.9.0
|
||||
|
||||
* Adds `.sample` for simple random sampling
|
||||
* Adds `.shuffle` and `.shuffle_in_place` for random permutations
|
||||
* Adds `.chunk` for splitting arrays into chunked subsets
|
||||
|
||||
## 0.8.1
|
||||
|
||||
* fixes a bug in `mode` that favored the last new number
|
||||
|
||||
## 0.8.0
|
||||
|
||||
* `mixin` can now take an array in order to mixin functions into a single array
|
||||
instance rather than the global Array prototype.
|
||||
|
||||
## 0.7.0
|
||||
|
||||
* Adds `simple_statistics.harmonic_mean` thanks to [jseppi](https://github.com/jseppi)
|
||||
|
||||
## 0.6.0
|
||||
|
||||
* Adds `simple_statistics.quantile_sorted` thanks to [rluta](http://github.com/rluta)
|
||||
* `simple_statistics.quantile` now accepts a sorted list of quantiles as a second argument
|
||||
* Improved test coverage
|
||||
|
||||
## 0.5.0
|
||||
|
||||
* Adds `simple_statistics.cumulative_std_normal_probability` by [doronlinder](https://github.com/doronlinder)
|
||||
* Adds `simple_statistics.z_score` by doronlinder
|
||||
* Adds `simple_statistics.standard_normal_table`
|
||||
|
||||
## 0.4.0
|
||||
|
||||
* Adds `simple_statistics.median_absolute_deviation()` by siculars
|
||||
* Adds `simple_statistics.iqr()` by siculars
|
||||
* Adds `simple_statistics.skewness()` by Doron Linder
|
||||
* Lower-level accessors for linear regression allow users to do the line
|
||||
equation themselves
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* Adds `simple_statistics.jenks()`
|
||||
* Adds `simple_statistics.jenksMatrices()`
|
||||
* Improves test coverage and validation
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* Adds `simple_statistics.quantile()`
|
||||
* Adds `simple_statistics.mixin()`
|
||||
* Adds `simple_statistics.geometric_mean()`
|
||||
* Adds `simple_statistics.sample_variance()`
|
||||
* Adds `simple_statistics.sample_covariance()`
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* Adds `simple_statistics.t_test()`
|
||||
* Adds `simple_statistics.min()`
|
||||
* Adds `simple_statistics.max()`
|
|
@ -0,0 +1,99 @@
|
|||
# Contributing to simple-statistics
|
||||
|
||||
Simple statistics is a statistics library that can be both used and read.
|
||||
It should help programmers learn statistics and statisticians learn programming.
|
||||
In order to achieve this goal, it must be **simple** and **explanatory**.
|
||||
|
||||
## Simple
|
||||
|
||||
`simple-statistics` is written in a subset of JavaScript. Unused features
|
||||
include:
|
||||
|
||||
* [Conditional Operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)
|
||||
* [ES5 Array methods](http://ie.microsoft.com/TestDrive/HTML5/ECMAScript5Array/Default.html)
|
||||
* `with`, `eval`, and other forms of `eval`
|
||||
* Most micro-optimizations, like [alternative for loop forms](http://jsperf.com/loops/70)
|
||||
* [Shortcut branching](http://javascriptweblog.wordpress.com/2010/07/26/no-more-ifs-alternatives-to-statement-branching-in-javascript/)
|
||||
|
||||
## Explanatory
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// # harmonic mean
|
||||
//
|
||||
// a mean function typically used to find the average of rates
|
||||
//
|
||||
// this is the reciprocal of the arithmetic mean of the reciprocals
|
||||
// of the input numbers
|
||||
//
|
||||
// This runs on `O(n)`, linear time in respect to the array
|
||||
```
|
||||
|
||||
`simple-statistics` tries to stay away from speaking only in the language of math:
|
||||
for instance, while JavaScript supports UTF8 characters like π, they are not used
|
||||
in the source:
|
||||
|
||||
* UTF8 in JavaScript on pages without specific meta-tag or Content-Type encodings will fail
|
||||
* UTF8 can be hard to type, since users need to memorize key combinations or code points
|
||||
* Mathematical symbols have meanings that are often better communicated by words:
|
||||
in the form of code, we do not run out of space on the paper, and can afford
|
||||
to call a variable `reciprocal_sum` instead of `r`.
|
||||
|
||||
Every function has a comment that ideally includes:
|
||||
|
||||
* The English, long-form name of the method
|
||||
* What the method does
|
||||
* What purpose the method typically serves
|
||||
* A link to a longer description on Wikipedia, Mathematica, or another
|
||||
web-accessible, non-paywalled source
|
||||
* The efficiency of the function in terms of Big-O notation, if appropriate
|
||||
* If the function depends on another function in the library, a note of this, like
|
||||
`depends on mean()`
|
||||
|
||||
## Tests
|
||||
|
||||
`simple-statistics` has a testsuite located in `test/spec/`. Each test file
|
||||
covers a specific topic and tries to test against known values:
|
||||
|
||||
* Values produced by trusted statistics software like R or scipy
|
||||
* Common-sense results
|
||||
|
||||
Tests can be run in [node.js](http://nodejs.org/) and are run on every commit
|
||||
to GitHub by Travis-CI.
|
||||
|
||||
To run tests:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm test
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
While the code is meant to readable, it is not documentation. We maintain
|
||||
documentation in `API.md`, which has the simple form:
|
||||
|
||||
```md
|
||||
### .geometric_mean(x)
|
||||
|
||||
[Geometric mean](http://en.wikipedia.org/wiki/Geometric_mean) of a single-dimensional array of **positive** numbers.
|
||||
```
|
||||
|
||||
This file is written in [Markdown](https://daringfireball.net/projects/markdown/) and
|
||||
specifies which functions are available, what type of arguments they receive,
|
||||
what they compute, and what type of answer they return.
|
||||
|
||||
## Code Style
|
||||
|
||||
We use the [Airbnb style for Javascript](https://github.com/airbnb/javascript) with
|
||||
only one difference:
|
||||
|
||||
**4 space soft tabs always for Javascript, not 2.**
|
||||
|
||||
No aligned `=`, no aligned arguments, spaces are either indents or the 1
|
||||
space between expressions. No hard tabs.
|
||||
|
||||
* All comparisons should be as strict and obvious as possible: prefer `(foo === 0)` to
|
||||
`(!foo)`.
|
||||
* Straightforward code is more important than most optimizations.
|
|
@ -0,0 +1,13 @@
|
|||
Copyright (c) 2014, Tom MacWright
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
|||
docs:
|
||||
docco src/*.js
|
||||
|
||||
test:
|
||||
mocha -R spec test/spec/*.js
|
||||
|
||||
.PHONY: docs test
|
|
@ -0,0 +1,337 @@
|
|||
[![Build Status](https://secure.travis-ci.org/tmcw/simple-statistics.png?branch=master)](http://travis-ci.org/tmcw/simple-statistics) [![Coverage Status](https://coveralls.io/repos/tmcw/simple-statistics/badge.png)](https://coveralls.io/r/tmcw/simple-statistics)
|
||||
|
||||
A JavaScript implementation of descriptive, regression, and inference statistics.
|
||||
|
||||
Implemented in literate JavaScript with no dependencies, designed to work
|
||||
in all modern browsers (including IE) as well as in node.js.
|
||||
|
||||
## [API Documentation](API.md)
|
||||
|
||||
---
|
||||
|
||||
Basic contracts of functions:
|
||||
|
||||
* Functions do not modify their arguments e.g. change their order
|
||||
* Invalid input, like empty lists to functions that need 1+ items to work, will cause functions to return `null`.
|
||||
|
||||
# Basic Array Operations
|
||||
|
||||
### .mixin(array)
|
||||
|
||||
_Optionally_ mix in the following functions into the `Array` prototype. Otherwise
|
||||
you can use them off of the simple-statistics object itself.
|
||||
|
||||
If given a particular array instance as an argument, this adds the functions
|
||||
only to that array rather than the global `Array.prototype`. Without an argument,
|
||||
it runs on the global `Array.prototype`.
|
||||
|
||||
### .mean(x)
|
||||
|
||||
Mean of a single-dimensional Array of numbers. _Also available as `.average(x)`_
|
||||
|
||||
### .sum(x)
|
||||
|
||||
Sum of a single-dimensional Array of numbers.
|
||||
|
||||
### .mode(x)
|
||||
|
||||
Returns the number that appears most frequently in a single-dimensional Array
|
||||
of numbers. If there are multiple modes, the one that appears last
|
||||
is returned.
|
||||
|
||||
### .variance(x)
|
||||
|
||||
[Variance](http://en.wikipedia.org/wiki/Variance) of a single-dimensional Array of numbers.
|
||||
|
||||
### .standard_deviation(x)
|
||||
|
||||
[Standard Deviation](http://en.wikipedia.org/wiki/Standard_deviation) of a single-dimensional Array of numbers.
|
||||
|
||||
### .median_absolute_deviation(x)
|
||||
|
||||
The Median Absolute Deviation (MAD) is a robust measure of statistical
|
||||
dispersion. It is more resilient to outliers than the standard deviation.
|
||||
Accepts a single-dimensional array of numbers and returns a dispersion value.
|
||||
|
||||
Also aliased to `.mad(x)` for brevity.
|
||||
|
||||
### .median(x)
|
||||
|
||||
[Median](http://en.wikipedia.org/wiki/Median) of a single-dimensional array of numbers.
|
||||
|
||||
### .geometric_mean(x)
|
||||
|
||||
[Geometric mean](http://en.wikipedia.org/wiki/Geometric_mean) of a single-dimensional array of **positive** numbers.
|
||||
|
||||
### .harmonic_mean(x)
|
||||
|
||||
[Harmonic mean](http://en.wikipedia.org/wiki/Harmonic_mean) of a single-dimensional array of **positive** numbers.
|
||||
|
||||
### .root_mean_square(x)
|
||||
|
||||
[Root mean square (RMS)](http://en.wikipedia.org/wiki/Root_mean_square) of a single-dimensional array of numbers.
|
||||
|
||||
### .min(x)
|
||||
|
||||
Finds the minimum of a single-dimensional array of numbers. This runs in linear `O(n)` time.
|
||||
|
||||
### .max(x)
|
||||
|
||||
Finds the maximum of a single-dimensional array of numbers. This runs in linear `O(n)` time.
|
||||
|
||||
### .t_test(sample, x)
|
||||
|
||||
Does a [student's t-test](http://en.wikipedia.org/wiki/Student's_t-test) of a dataset `sample`, represented by a single-dimensional array of numbers. `x` is the known value, and the result is a measure of [statistical significance](http://en.wikipedia.org/wiki/Statistical_significance).
|
||||
|
||||
### .t_test_two_sample(sample_x, sample_y, difference)
|
||||
|
||||
The two-sample t-test is used to compare samples from two populations or groups,
|
||||
confirming or denying the suspicion (null hypothesis) that the populations are
|
||||
the same. It returns a t-value that you can then look up to give certain
|
||||
judgements of confidence based on a t distribution table.
|
||||
|
||||
This implementation expects the samples `sample_x` and `sample_y` to be given
|
||||
as one-dimensional arrays of more than one number each.
|
||||
|
||||
### .sample_variance(x)
|
||||
|
||||
Produces [sample variance](http://mathworld.wolfram.com/SampleVariance.html)
|
||||
of a single-dimensional array of numbers.
|
||||
|
||||
### .sample_covariance(a, b)
|
||||
|
||||
Produces [sample covariance](http://en.wikipedia.org/wiki/Sample_mean_and_sample_covariance)
|
||||
of two single-dimensional arrays of numbers.
|
||||
|
||||
### .sample_correlation(a, b)
|
||||
|
||||
Produces [sample correlation](http://en.wikipedia.org/wiki/Correlation_and_dependence)
|
||||
of two single-dimensional arrays of numbers.
|
||||
|
||||
### .quantile(sample, p)
|
||||
|
||||
Does a [quantile](http://en.wikipedia.org/wiki/Quantile) of a dataset `sample`,
|
||||
at p. For those familiary with the `k/q` syntax, `p == k/q`. `sample` must
|
||||
be a single-dimensional array of numbers. p must be a number greater than or equal to zero and less than or equal to one, or an array of numbers following that rule.
|
||||
If an array is given, an array of results will be returned instead of a single
|
||||
number.
|
||||
|
||||
### .chunk(sample, chunkSize)
|
||||
|
||||
Given a `sample` array, and a positive integer `chunkSize`, splits an array
|
||||
into chunks of `chunkSize` size and returns an array of those chunks. This
|
||||
does not change the input value. If the length of `sample` is not divisible
|
||||
by `chunkSize`, the last array will be shorter than the rest.
|
||||
|
||||
### .quantile_sorted(sample, p)
|
||||
|
||||
Does a [quantile](http://en.wikipedia.org/wiki/Quantile) of a dataset `sample`,
|
||||
at p. `sample` must be a one-dimensional _sorted_ array of numbers, and
|
||||
`p` must be a single number greater than or equal to zero and less than or equal to one.
|
||||
|
||||
### .iqr(sample)
|
||||
|
||||
Calculates the [Interquartile range](http://en.wikipedia.org/wiki/Interquartile_range) of
|
||||
a sample - the difference between the upper and lower quartiles. Useful
|
||||
as a measure of dispersion.
|
||||
|
||||
_Also available as `.interquartile_range(x)`_
|
||||
|
||||
### .sample_skewness(sample)
|
||||
|
||||
Calculates the [skewness](http://en.wikipedia.org/wiki/Skewness) of
|
||||
a sample, a measure of the extent to which a probability distribution of a
|
||||
real-valued random variable "leans" to one side of the mean.
|
||||
The skewness value can be positive or negative, or even undefined.
|
||||
|
||||
This implementation uses the [Fisher-Pearson standardized moment coefficient](http://en.wikipedia.org/wiki/Skewness#Pearson.27s_skewness_coefficients),
|
||||
which means that it behaves the same as Excel, Minitab, SAS, and SPSS.
|
||||
|
||||
Skewness is only valid for samples of over three values.
|
||||
|
||||
### .jenks(data, number_of_classes)
|
||||
|
||||
Find the [Jenks Natural Breaks](http://en.wikipedia.org/wiki/Jenks_natural_breaks_optimization) for
|
||||
a single-dimensional array of numbers as input and a desired `number_of_classes`.
|
||||
The result is a single-dimensional with class breaks, including the minimum
|
||||
and maximum of the input array.
|
||||
|
||||
### .r_squared(data, function)
|
||||
|
||||
Find the [r-squared](http://en.wikipedia.org/wiki/Coefficient_of_determination) value of a particular dataset, expressed as a two-dimensional `Array` of numbers, against a `Function`.
|
||||
|
||||
var r_squared = ss.r_squared([[1, 1]], function(x) { return x * 2; });
|
||||
|
||||
### .cumulative_std_normal_probability(z)
|
||||
|
||||
Look up the given `z` value in a [standard normal table](http://en.wikipedia.org/wiki/Standard_normal_table)
|
||||
to calculate the probability of a random variable appearing with a given value.
|
||||
|
||||
### .z_score(x, mean, standard_deviation)
|
||||
|
||||
The standard score is the number of standard deviations an observation
|
||||
or datum is above or below the mean.
|
||||
|
||||
### .standard_normal_table
|
||||
|
||||
A [standard normal table](http://en.wikipedia.org/wiki/Standard_normal_table) from
|
||||
which to pull values of Φ (phi).
|
||||
|
||||
## Regression
|
||||
|
||||
### .linear_regression()
|
||||
|
||||
Create a new linear regression solver.
|
||||
|
||||
#### .data([[1, 1], [2, 2]])
|
||||
|
||||
Set the data of a linear regression. The input is a two-dimensional array of numbers, which are treated as coordinates, like `[[x, y], [x1, y1]]`.
|
||||
|
||||
#### .line()
|
||||
|
||||
Get the linear regression line: this returns a function that you can
|
||||
give `x` values and it will return `y` values. Internally, this uses the `m()`
|
||||
and `b()` values and the classic `y = mx + b` equation.
|
||||
|
||||
var linear_regression_line = ss.linear_regression()
|
||||
.data([[0, 1], [2, 2], [3, 3]]).line();
|
||||
linear_regression_line(5);
|
||||
|
||||
#### .m()
|
||||
|
||||
Just get the slope of the fitted regression line, the `m` component of the full
|
||||
line equation. Returns a number.
|
||||
|
||||
#### .b()
|
||||
|
||||
Just get the y-intercept of the fitted regression line, the `b` component
|
||||
of the line equation. Returns a number.
|
||||
|
||||
## Classification
|
||||
|
||||
### .bayesian()
|
||||
|
||||
Create a naïve bayesian classifier.
|
||||
|
||||
### .train(item, category)
|
||||
|
||||
Train the classifier to classify a certain item, given as an object with keys,
|
||||
to be in a certain category, given as a string.
|
||||
|
||||
### .score(item)
|
||||
|
||||
Get the classifications of a certain item, given as an object of
|
||||
`category -> score` mappings.
|
||||
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({ species: 'Cat' }, 'animal');
|
||||
bayes.score({ species: 'Cat' });
|
||||
// { animal: 1 }
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## [Literate Source](http://macwright.org/simple-statistics/)
|
||||
|
||||
## Usage
|
||||
|
||||
To use it in browsers, grab [simple_statistics.js](https://raw.github.com/tmcw/simple-statistics/master/src/simple_statistics.js).
|
||||
To use it in node, install it with [npm](https://npmjs.org/) or add it to your package.json.
|
||||
|
||||
npm install simple-statistics
|
||||
|
||||
To use it with [component](https://github.com/component/component),
|
||||
|
||||
component install tmcw/simple-statistics
|
||||
|
||||
To use it with [bower](http://bower.io/),
|
||||
|
||||
bower install simple-statistics
|
||||
|
||||
## Basic Descriptive Statistics
|
||||
|
||||
```javascript
|
||||
// Require simple statistics
|
||||
var ss = require('simple-statistics');
|
||||
|
||||
// The input is a simple array
|
||||
var list = [1, 2, 3];
|
||||
|
||||
// Many different descriptive statistics are supported
|
||||
var sum = ss.sum(list),
|
||||
mean = ss.mean(list),
|
||||
min = ss.min(list),
|
||||
geometric_mean = ss.geometric_mean(list),
|
||||
max = ss.max(list),
|
||||
quantile = ss.quantile(0.25);
|
||||
```
|
||||
|
||||
## Linear Regression
|
||||
|
||||
```javascript
|
||||
// For a linear regression, it's a two-dimensional array
|
||||
var data = [ [1, 2], [2, 3] ];
|
||||
|
||||
// simple-statistics can produce a linear regression and return
|
||||
// a friendly javascript function for the line.
|
||||
var line = ss.linear_regression()
|
||||
.data(data)
|
||||
.line();
|
||||
|
||||
// get a point along the line function
|
||||
line(0);
|
||||
|
||||
var line = ss.linear_regression()
|
||||
|
||||
// Get the r-squared value of the line estimation
|
||||
ss.r_squared(data, line);
|
||||
```
|
||||
|
||||
### Bayesian Classifier
|
||||
|
||||
```javascript
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({ species: 'Cat' }, 'animal');
|
||||
bayes.score({ species: 'Cat' });
|
||||
// { animal: 1 }
|
||||
```
|
||||
|
||||
### Mixin Style
|
||||
|
||||
_This is **optional** and not used by default. You can opt-in to mixins
|
||||
with `ss.mixin()`._
|
||||
|
||||
This mixes `simple-statistics` methods into the Array prototype - note that
|
||||
[extending native objects](http://perfectionkills.com/extending-native-builtins/) is a
|
||||
tricky move.
|
||||
|
||||
This will _only work_ if `defineProperty` is available, which means modern browsers
|
||||
and nodejs - on IE8 and below, calling `ss.mixin()` will throw an exception.
|
||||
|
||||
```javascript
|
||||
// mixin to Array class
|
||||
ss.mixin();
|
||||
|
||||
// The input is a simple array
|
||||
var list = [1, 2, 3];
|
||||
|
||||
// The same descriptive techniques as above, but in a simpler style
|
||||
var sum = list.sum(),
|
||||
mean = list.mean(),
|
||||
min = list.min(),
|
||||
max = list.max(),
|
||||
quantile = list.quantile(0.25);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [Linear regression with simple-statistics and d3js](http://bl.ocks.org/3931800)
|
||||
* [Jenks Natural Breaks with a choropleth map with d3js](http://bl.ocks.org/tmcw/4969184)
|
||||
|
||||
# Contributors
|
||||
|
||||
* Tom MacWright
|
||||
* [Matt Sacks](https://github.com/mattsacks)
|
||||
* Doron Linder
|
||||
* [Alexander Sicular](https://github.com/siculars)
|
|
@ -0,0 +1,157 @@
|
|||
[![Build Status](https://secure.travis-ci.org/tmcw/simple-statistics.png?branch=master)](http://travis-ci.org/tmcw/simple-statistics)
|
||||
|
||||
A JavaScript implementation of descriptive, regression, and inference statistics.
|
||||
|
||||
Implemented in literate JavaScript with no dependencies, designed to work
|
||||
in all modern browsers (including IE) as well as in node.js.
|
||||
|
||||
# [API](API.md)
|
||||
|
||||
[Full documentation](API.md)
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
Basic Array Operations
|
||||
.mixin()
|
||||
.mean(x)
|
||||
.sum(x)
|
||||
.variance(x)
|
||||
.standard_deviation(x)
|
||||
.median_absolute_deviation(x)
|
||||
.median(x)
|
||||
.geometric_mean(x)
|
||||
.harmonic_mean(x)
|
||||
.root_mean_square(x)
|
||||
.min(x)
|
||||
.max(x)
|
||||
.t_test(sample, x)
|
||||
.t_test_two_sample(sample_x, sample_y, difference)
|
||||
.sample_variance(x)
|
||||
.sample_covariance(x)
|
||||
.sample_correlation(x)
|
||||
.quantile(sample, p)
|
||||
.iqr(sample)
|
||||
.sample_skewness(sample)
|
||||
.jenks(data, number_of_classes)
|
||||
.r_squared(data, function)
|
||||
.cumulative_std_normal_probability(z)
|
||||
.z_score(x, mean, standard_deviation)
|
||||
.standard_normal_table
|
||||
Regression
|
||||
.linear_regression()
|
||||
.data([[1, 1], [2, 2]])
|
||||
.line()
|
||||
.m()
|
||||
.b()
|
||||
Classification
|
||||
.bayesian()
|
||||
.train(item, category)
|
||||
.score(item)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# [Literate Source](http://macwright.org/simple-statistics/)
|
||||
|
||||
## Usage
|
||||
|
||||
To use it in browsers, grab [simple_statistics.js](https://raw.github.com/tmcw/simple-statistics/master/src/simple_statistics.js).
|
||||
To use it in node, install it with [npm](https://npmjs.org/) or add it to your package.json.
|
||||
|
||||
npm install simple-statistics
|
||||
|
||||
To use it with [component](https://github.com/component/component),
|
||||
|
||||
component install tmcw/simple-statistics
|
||||
|
||||
To use it with [bower](http://bower.io/),
|
||||
|
||||
bower install simple-statistics
|
||||
|
||||
## Basic Descriptive Statistics
|
||||
|
||||
```javascript
|
||||
// Require simple statistics
|
||||
var ss = require('simple-statistics');
|
||||
|
||||
// The input is a simple array
|
||||
var list = [1, 2, 3];
|
||||
|
||||
// Many different descriptive statistics are supported
|
||||
var sum = ss.sum(list),
|
||||
mean = ss.mean(list),
|
||||
min = ss.min(list),
|
||||
geometric_mean = ss.geometric_mean(list),
|
||||
max = ss.max(list),
|
||||
quantile = ss.quantile(0.25);
|
||||
```
|
||||
|
||||
## Linear Regression
|
||||
|
||||
```javascript
|
||||
// For a linear regression, it's a two-dimensional array
|
||||
var data = [ [1, 2], [2, 3] ];
|
||||
|
||||
// simple-statistics can produce a linear regression and return
|
||||
// a friendly javascript function for the line.
|
||||
var line = ss.linear_regression()
|
||||
.data(data)
|
||||
.line();
|
||||
|
||||
// get a point along the line function
|
||||
line(0);
|
||||
|
||||
var line = ss.linear_regression()
|
||||
|
||||
// Get the r-squared value of the line estimation
|
||||
ss.r_squared(data, line);
|
||||
```
|
||||
|
||||
### Bayesian Classifier
|
||||
|
||||
```javascript
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({ species: 'Cat' }, 'animal');
|
||||
bayes.score({ species: 'Cat' });
|
||||
// { animal: 1 }
|
||||
```
|
||||
|
||||
### Mixin Style
|
||||
|
||||
_This is **optional** and not used by default. You can opt-in to mixins
|
||||
with `ss.mixin()`._
|
||||
|
||||
This mixes `simple-statistics` methods into the Array prototype - note that
|
||||
[extending native objects](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/) is a
|
||||
tricky move.
|
||||
|
||||
This will _only work_ if `defineProperty` is available, which means modern browsers
|
||||
and nodejs - on IE8 and below, calling `ss.mixin()` will throw an exception.
|
||||
|
||||
```javascript
|
||||
// mixin to Array class
|
||||
ss.mixin();
|
||||
|
||||
// The input is a simple array
|
||||
var list = [1, 2, 3];
|
||||
|
||||
// The same descriptive techniques as above, but in a simpler style
|
||||
var sum = list.sum(),
|
||||
mean = list.mean(),
|
||||
min = list.min(),
|
||||
max = list.max(),
|
||||
quantile = list.quantile(0.25);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [Linear regression with simple-statistics and d3js](http://bl.ocks.org/3931800)
|
||||
* [Jenks Natural Breaks with a choropleth map with d3js](http://bl.ocks.org/tmcw/4969184)
|
||||
|
||||
# Contributors
|
||||
|
||||
* Tom MacWright
|
||||
* [Matt Sacks](https://github.com/mattsacks)
|
||||
* Doron Linder
|
||||
* [Alexander Sicular](https://github.com/siculars)
|
|
@ -0,0 +1,23 @@
|
|||
## See Also
|
||||
|
||||
* [stream-statistics](https://github.com/tmcw/stream-statistics), a sister project that implements
|
||||
many of the same measures for streaming data - as online algorithms
|
||||
|
||||
### Javascript
|
||||
|
||||
* [science.js](https://github.com/jasondavies/science.js)
|
||||
* [atoll.js](https://github.com/nsfmc/atoll.js)
|
||||
* [descriptive_statistics](https://github.com/thirtysixthspan/descriptive_statistics)
|
||||
* [jStat](http://www.jstat.org/)
|
||||
* [classifier](https://github.com/harthur/classifier) is a naive bayesian classifier (though specialized for the words-spam case)
|
||||
* [underscore.math](https://github.com/syntagmatic/underscore.math/blob/master/underscore.math.js)
|
||||
|
||||
### Python
|
||||
|
||||
* [Pandas](http://pandas.pydata.org/)
|
||||
* [SciPy](http://www.scipy.org/)
|
||||
|
||||
### Their Own Language
|
||||
|
||||
* [Julia Language](http://julialang.org/)
|
||||
* [R language](http://www.r-project.org/)
|
|
@ -0,0 +1,20 @@
|
|||
var fs = require('fs');
|
||||
|
||||
var readme = fs.readFileSync('README.md', 'utf8')
|
||||
.split('\n');
|
||||
|
||||
var a = true, b = true;
|
||||
|
||||
fs.writeFileSync('README.md', readme.filter(function(f) {
|
||||
if (f === '---') {
|
||||
a = !a;
|
||||
return true;
|
||||
}
|
||||
return a;
|
||||
}).map(function(f) {
|
||||
if (f === '---' && b) {
|
||||
f = f + '\n\n' + fs.readFileSync('API.md', 'utf8') + '\n\n';
|
||||
b = false;
|
||||
}
|
||||
return f;
|
||||
}).join('\n'));
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "simple-statistics",
|
||||
"version": "0.9.0",
|
||||
"description": "Simple Statistics",
|
||||
"repo": "tmcw/simple-statistics",
|
||||
"keywords": [],
|
||||
"license": "ISC",
|
||||
"dependencies": {},
|
||||
"development": {},
|
||||
"main": "src/simple_statistics.js"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "simple-statistics",
|
||||
"version": "0.9.0",
|
||||
"description": "Simple Statistics",
|
||||
"repo": "tmcw/simple-statistics",
|
||||
"keywords": [],
|
||||
"license": "ISC",
|
||||
"dependencies": {},
|
||||
"development": {},
|
||||
"scripts": [
|
||||
"src/simple_statistics.js"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,506 @@
|
|||
/*--------------------- Typography ----------------------------*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'aller-light';
|
||||
src: url('public/fonts/aller-light.eot');
|
||||
src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
|
||||
url('public/fonts/aller-light.woff') format('woff'),
|
||||
url('public/fonts/aller-light.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'aller-bold';
|
||||
src: url('public/fonts/aller-bold.eot');
|
||||
src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
|
||||
url('public/fonts/aller-bold.woff') format('woff'),
|
||||
url('public/fonts/aller-bold.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'novecento-bold';
|
||||
src: url('public/fonts/novecento-bold.eot');
|
||||
src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'),
|
||||
url('public/fonts/novecento-bold.woff') format('woff'),
|
||||
url('public/fonts/novecento-bold.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/*--------------------- Layout ----------------------------*/
|
||||
html { height: 100%; }
|
||||
body {
|
||||
font-family: "aller-light";
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: #30404f;
|
||||
margin: 0; padding: 0;
|
||||
height:100%;
|
||||
}
|
||||
#container { min-height: 100%; }
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: normal;
|
||||
font-family: "aller-bold";
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 15px 0 0px;
|
||||
}
|
||||
.annotation ul, .annotation ol {
|
||||
margin: 25px 0;
|
||||
}
|
||||
.annotation ul li, .annotation ol li {
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #112233;
|
||||
line-height: 1em;
|
||||
font-weight: normal;
|
||||
font-family: "novecento-bold";
|
||||
text-transform: uppercase;
|
||||
margin: 30px 0 15px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
background: 1px #ddd;
|
||||
height: 1px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
pre, tt, code {
|
||||
font-size: 12px; line-height: 16px;
|
||||
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
.annotation pre {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 7px 10px;
|
||||
background: #fcfcfc;
|
||||
-moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
|
||||
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.annotation pre code {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
border-left: 5px solid #ccc;
|
||||
margin: 0;
|
||||
padding: 1px 0 1px 1em;
|
||||
}
|
||||
.sections blockquote p {
|
||||
font-family: Menlo, Consolas, Monaco, monospace;
|
||||
font-size: 12px; line-height: 16px;
|
||||
color: #999;
|
||||
margin: 10px 0 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
ul.sections {
|
||||
list-style: none;
|
||||
padding:0 0 5px 0;;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
/*
|
||||
Force border-box so that % widths fit the parent
|
||||
container without overlap because of margin/padding.
|
||||
|
||||
More Info : http://www.quirksmode.org/css/box.html
|
||||
*/
|
||||
ul.sections > li > div {
|
||||
-moz-box-sizing: border-box; /* firefox */
|
||||
-ms-box-sizing: border-box; /* ie */
|
||||
-webkit-box-sizing: border-box; /* webkit */
|
||||
-khtml-box-sizing: border-box; /* konqueror */
|
||||
box-sizing: border-box; /* css3 */
|
||||
}
|
||||
|
||||
|
||||
/*---------------------- Jump Page -----------------------------*/
|
||||
#jump_to, #jump_page {
|
||||
margin: 0;
|
||||
background: white;
|
||||
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
|
||||
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
|
||||
font: 16px Arial;
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#jump_to a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#jump_to a.large {
|
||||
display: none;
|
||||
}
|
||||
#jump_to a.small {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #676767;
|
||||
}
|
||||
|
||||
#jump_to, #jump_wrapper {
|
||||
position: fixed;
|
||||
right: 0; top: 0;
|
||||
padding: 10px 15px;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
#jump_wrapper {
|
||||
display: none;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
#jump_to:hover #jump_wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#jump_page {
|
||||
padding: 5px 0 3px;
|
||||
margin: 0 0 25px 25px;
|
||||
}
|
||||
|
||||
#jump_page .source {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
text-decoration: none;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
#jump_page .source:hover {
|
||||
background: #f5f5ff;
|
||||
}
|
||||
|
||||
#jump_page .source:first-child {
|
||||
}
|
||||
|
||||
/*---------------------- Low resolutions (> 320px) ---------------------*/
|
||||
@media only screen and (min-width: 320px) {
|
||||
.pilwrap { display: none; }
|
||||
|
||||
ul.sections > li > div {
|
||||
display: block;
|
||||
padding:5px 10px 0 10px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.content {
|
||||
overflow-x:auto;
|
||||
-webkit-box-shadow: inset 0 0 5px #e5e5ee;
|
||||
box-shadow: inset 0 0 5px #e5e5ee;
|
||||
border: 1px solid #dedede;
|
||||
margin:5px 10px 5px 10px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation pre {
|
||||
margin: 7px 0 7px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation p tt, .annotation code {
|
||||
background: #f8f8ff;
|
||||
border: 1px solid #dedede;
|
||||
font-size: 12px;
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------- (> 481px) ---------------------*/
|
||||
@media only screen and (min-width: 481px) {
|
||||
#container {
|
||||
position: relative;
|
||||
}
|
||||
body {
|
||||
background-color: #F5F5FF;
|
||||
font-size: 15px;
|
||||
line-height: 21px;
|
||||
}
|
||||
pre, tt, code {
|
||||
line-height: 18px;
|
||||
}
|
||||
p, ul, ol {
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
|
||||
|
||||
#jump_to {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#jump_wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
#jump_to, #jump_page {
|
||||
font: 10px Arial;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
#jump_page .source {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#jump_to a.large {
|
||||
display: inline-block;
|
||||
}
|
||||
#jump_to a.small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#background {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
width: 350px;
|
||||
background: #fff;
|
||||
border-right: 1px solid #e5e5ee;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
ul.sections > li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ul.sections > li > div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation {
|
||||
max-width: 350px;
|
||||
min-width: 350px;
|
||||
min-height: 5px;
|
||||
padding: 13px;
|
||||
overflow-x: hidden;
|
||||
white-space: normal;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
ul.sections > li > div.annotation pre {
|
||||
margin: 15px 0 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.content {
|
||||
padding: 13px;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.pilwrap {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.pilcrow {
|
||||
font: 12px Arial;
|
||||
text-decoration: none;
|
||||
color: #454545;
|
||||
position: absolute;
|
||||
top: 3px; left: -20px;
|
||||
padding: 1px 2px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
}
|
||||
.for-h1 .pilcrow {
|
||||
top: 47px;
|
||||
}
|
||||
.for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
|
||||
top: 35px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation:hover .pilcrow {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------- (> 1025px) ---------------------*/
|
||||
@media only screen and (min-width: 1025px) {
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
#background {
|
||||
width: 525px;
|
||||
}
|
||||
ul.sections > li > div.annotation {
|
||||
max-width: 525px;
|
||||
min-width: 525px;
|
||||
padding: 10px 25px 1px 50px;
|
||||
}
|
||||
ul.sections > li > div.content {
|
||||
padding: 9px 15px 16px 25px;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------- Syntax Highlighting -----------------------------*/
|
||||
|
||||
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
|
||||
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
|
||||
/*
|
||||
|
||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
||||
|
||||
*/
|
||||
|
||||
pre code {
|
||||
display: block; padding: 0.5em;
|
||||
color: #000;
|
||||
background: #f8f8ff
|
||||
}
|
||||
|
||||
pre .hljs-comment,
|
||||
pre .hljs-template_comment,
|
||||
pre .hljs-diff .hljs-header,
|
||||
pre .hljs-javadoc {
|
||||
color: #408080;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
pre .hljs-keyword,
|
||||
pre .hljs-assignment,
|
||||
pre .hljs-literal,
|
||||
pre .hljs-css .hljs-rule .hljs-keyword,
|
||||
pre .hljs-winutils,
|
||||
pre .hljs-javascript .hljs-title,
|
||||
pre .hljs-lisp .hljs-title,
|
||||
pre .hljs-subst {
|
||||
color: #954121;
|
||||
/*font-weight: bold*/
|
||||
}
|
||||
|
||||
pre .hljs-number,
|
||||
pre .hljs-hexcolor {
|
||||
color: #40a070
|
||||
}
|
||||
|
||||
pre .hljs-string,
|
||||
pre .hljs-tag .hljs-value,
|
||||
pre .hljs-phpdoc,
|
||||
pre .hljs-tex .hljs-formula {
|
||||
color: #219161;
|
||||
}
|
||||
|
||||
pre .hljs-title,
|
||||
pre .hljs-id {
|
||||
color: #19469D;
|
||||
}
|
||||
pre .hljs-params {
|
||||
color: #00F;
|
||||
}
|
||||
|
||||
pre .hljs-javascript .hljs-title,
|
||||
pre .hljs-lisp .hljs-title,
|
||||
pre .hljs-subst {
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
pre .hljs-class .hljs-title,
|
||||
pre .hljs-haskell .hljs-label,
|
||||
pre .hljs-tex .hljs-command {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .hljs-tag,
|
||||
pre .hljs-tag .hljs-title,
|
||||
pre .hljs-rules .hljs-property,
|
||||
pre .hljs-django .hljs-tag .hljs-keyword {
|
||||
color: #000080;
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
pre .hljs-attribute,
|
||||
pre .hljs-variable,
|
||||
pre .hljs-instancevar,
|
||||
pre .hljs-lisp .hljs-body {
|
||||
color: #008080
|
||||
}
|
||||
|
||||
pre .hljs-regexp {
|
||||
color: #B68
|
||||
}
|
||||
|
||||
pre .hljs-class {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .hljs-symbol,
|
||||
pre .hljs-ruby .hljs-symbol .hljs-string,
|
||||
pre .hljs-ruby .hljs-symbol .hljs-keyword,
|
||||
pre .hljs-ruby .hljs-symbol .hljs-keymethods,
|
||||
pre .hljs-lisp .hljs-keyword,
|
||||
pre .hljs-tex .hljs-special,
|
||||
pre .hljs-input_number {
|
||||
color: #990073
|
||||
}
|
||||
|
||||
pre .hljs-builtin,
|
||||
pre .hljs-constructor,
|
||||
pre .hljs-built_in,
|
||||
pre .hljs-lisp .hljs-title {
|
||||
color: #0086b3
|
||||
}
|
||||
|
||||
pre .hljs-preprocessor,
|
||||
pre .hljs-pi,
|
||||
pre .hljs-doctype,
|
||||
pre .hljs-shebang,
|
||||
pre .hljs-cdata {
|
||||
color: #999;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .hljs-deletion {
|
||||
background: #fdd
|
||||
}
|
||||
|
||||
pre .hljs-addition {
|
||||
background: #dfd
|
||||
}
|
||||
|
||||
pre .hljs-diff .hljs-change {
|
||||
background: #0086b3
|
||||
}
|
||||
|
||||
pre .hljs-chunk {
|
||||
color: #aaa
|
||||
}
|
||||
|
||||
pre .hljs-tex .hljs-formula {
|
||||
opacity: 0.5;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
<meta http-equiv="refresh" content="0;URL='docs/simple_statistics.html'">
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "simple-statistics",
|
||||
"version": "0.9.0",
|
||||
"description": "Simple Statistics",
|
||||
"author": "Tom MacWright <tom@macwright.org> (http://macwright.org/)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/tmcw/simple-statistics.git"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"jshint": "2.5.3",
|
||||
"coveralls": "~2.11.1",
|
||||
"istanbul": "~0.3.0",
|
||||
"tape": "~2.14.0",
|
||||
"random-js": "~1.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tape test/*.js",
|
||||
"cov": "istanbul cover ./node_modules/.bin/tape test/*.js && coveralls < ./coverage/lcov.info",
|
||||
"api": "node api.js"
|
||||
},
|
||||
"main": "src/simple_statistics.js",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"license": "ISC"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,114 @@
|
|||
var ss = require('../');
|
||||
var test = require('tape');
|
||||
|
||||
test('bayes', function(t) {
|
||||
test('makes an easy call with one training round', function(t) {
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
t.deepEqual(bayes.score({
|
||||
species: 'Cat'
|
||||
}), {
|
||||
animal: 1
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('makes fify-fifty call', function(t) {
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'chair');
|
||||
t.deepEqual(bayes.score({
|
||||
species: 'Cat'
|
||||
}), {
|
||||
animal: 0.5,
|
||||
chair: 0.5
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('makes seventy-five/twenty-five call', function(t) {
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'chair');
|
||||
t.deepEqual(bayes.score({
|
||||
species: 'Cat'
|
||||
}), {
|
||||
animal: 0.75,
|
||||
chair: 0.25
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('tests multiple properties', function(t) {
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'chair');
|
||||
bayes.train({
|
||||
species: 'Cat',
|
||||
color: 'white'
|
||||
}, 'chair');
|
||||
t.deepEqual(bayes.score({
|
||||
color: 'white'
|
||||
}), {
|
||||
animal: 0,
|
||||
chair: 0.2
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('classifies multiple things', function(t) {
|
||||
var bayes = ss.bayesian();
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Dog'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Dog'
|
||||
}, 'animal');
|
||||
bayes.train({
|
||||
species: 'Cat'
|
||||
}, 'chair');
|
||||
t.deepEqual(bayes.score({
|
||||
species: 'Cat'
|
||||
}), {
|
||||
animal: 0.25,
|
||||
chair: 0.25
|
||||
});
|
||||
t.deepEqual(bayes.score({
|
||||
species: 'Dog'
|
||||
}), {
|
||||
animal: 0.5,
|
||||
chair: 0
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('bernoulli_distribution', function(t) {
|
||||
test('can return generate probability and cumulative probability distributions for p = 0.3', function(t) {
|
||||
t.equal('object', typeof ss.bernoulli_distribution(0.3));
|
||||
t.equal(ss.bernoulli_distribution(0.3)[0], 0.7, ss.epsilon);
|
||||
t.equal(ss.bernoulli_distribution(0.3)[1], 0.3, ss.epsilon);
|
||||
t.end();
|
||||
});
|
||||
test('can return null when p is not a valid probability', function(t) {
|
||||
t.equal(null, ss.bernoulli_distribution(-0.01), 'p should be greater than 0.0');
|
||||
t.equal(null, ss.bernoulli_distribution(1.5), 'p should be less than 1.0');
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(n) {
|
||||
return parseFloat(n.toFixed(4));
|
||||
}
|
||||
|
||||
test('binomial_distribution', function(t) {
|
||||
// Data given in the [Wikipedia example](http://en.wikipedia.org/wiki/Binomial_distribution#Example) retrieved 29 Mar 2014
|
||||
// Cumulative probabilities worked by hand to mitigate accumulated rounding errors.
|
||||
test('can return generate probability and cumulative probability distributions for n = 6, p = 0.3', function(t) {
|
||||
t.equal('object', typeof ss.binomial_distribution(6, 0.3));
|
||||
t.equal(rnd(ss.binomial_distribution(6, 0.3)[0]), 0.1176, ss.epsilon);
|
||||
t.equal(rnd(ss.binomial_distribution(6, 0.3)[1]), 0.3025, ss.epsilon);
|
||||
t.equal(rnd(ss.binomial_distribution(6, 0.3)[2]), 0.3241, ss.epsilon);
|
||||
t.equal(rnd(ss.binomial_distribution(6, 0.3)[3]), 0.1852, ss.epsilon);
|
||||
t.equal(rnd(ss.binomial_distribution(6, 0.3)[4]), 0.0595, ss.epsilon);
|
||||
t.equal(rnd(ss.binomial_distribution(6, 0.3)[5]), 0.0102, ss.epsilon);
|
||||
t.equal(rnd(ss.binomial_distribution(6, 0.3)[6]), 0.0007, ss.epsilon);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can return null when p or n are not valid parameters', function(t) {
|
||||
t.equal(null, ss.binomial_distribution(0, 0.5), 'n should be strictly positive');
|
||||
t.equal(null, ss.binomial_distribution(1.5, 0.5), 'n should be an integer');
|
||||
t.equal(null, ss.binomial_distribution(2, -0.01), 'p should be greater than 0.0');
|
||||
t.equal(null, ss.binomial_distribution(2, 1.5), 'p should be less than 1.0');
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
// Data from Poisson goodness-of-fit example 10-19 in William W. Hines & Douglas C. Montgomery,
|
||||
// "Probability and Statistics in Engineering and Management Science", Wiley (1980).
|
||||
var data_10_19 = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3
|
||||
];
|
||||
|
||||
test('chi_squared_goodness_of_fit', function(t) {
|
||||
test('can reject the null hypothesis with level of confidence specified at 0.05', function(t) {
|
||||
t.equal(false, ss.chi_squared_goodness_of_fit(data_10_19, ss.poisson_distribution, 0.05));
|
||||
t.end();
|
||||
});
|
||||
test('can accept the null hypothesis with level of confidence specified at 0.10', function(t) {
|
||||
t.equal(true, ss.chi_squared_goodness_of_fit(data_10_19, ss.poisson_distribution, 0.10));
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('chunks', function(t) {
|
||||
test('can get chunks of an array', function(t) {
|
||||
t.deepEqual(ss.chunk([1, 2], 1), [[1], [2]]);
|
||||
t.deepEqual(ss.chunk([1, 2], 2), [[1, 2]]);
|
||||
t.deepEqual(ss.chunk([1, 2, 3, 4], 4), [[1, 2, 3, 4]]);
|
||||
t.deepEqual(ss.chunk([1, 2, 3, 4], 2), [[1, 2], [3, 4]]);
|
||||
t.deepEqual(ss.chunk([1, 2, 3, 4], 3), [[1, 2, 3], [4]]);
|
||||
t.deepEqual(ss.chunk([1, 2, 3, 4, 5, 6, 7], 2), [[1, 2], [3, 4], [5, 6], [7]]);
|
||||
t.deepEqual(ss.chunk([], 2), []);
|
||||
t.deepEqual(ss.chunk([], 0), null);
|
||||
t.deepEqual(ss.chunk([1, 2], 0), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('cumulative_std_normal_probability', function(t) {
|
||||
// https://en.wikipedia.org/wiki/Standard_normal_table#Examples_of_use
|
||||
test('wikipedia test example works', function(t) {
|
||||
for (var i = 0; i < ss.standard_normal_table.length; i++) {
|
||||
t.equal(ss.cumulative_std_normal_probability(0.4), 0.6554);
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('factorial', function(t) {
|
||||
test('can return null given a negative number', function(t) {
|
||||
t.equal(null, ss.factorial(-1));
|
||||
t.end();
|
||||
});
|
||||
test('can calculate 0! = 1', function(t) {
|
||||
t.equal(ss.factorial(0), 1);
|
||||
t.end();
|
||||
});
|
||||
test('can calculate 1! = 1', function(t) {
|
||||
t.equal(ss.factorial(1), 1);
|
||||
t.end();
|
||||
});
|
||||
test('can calculate 100! = 1', function(t) {
|
||||
t.equal(ss.factorial(100), 9.33262154439441e+157);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('geometric mean', function(t) {
|
||||
// From http://en.wikipedia.org/wiki/Geometric_mean
|
||||
test('can get the mean of two numbers', function(t) {
|
||||
t.equal(ss.geometric_mean([2, 8]), 4);
|
||||
t.equal(ss.geometric_mean([4, 1, 1 / 32]), 0.5);
|
||||
t.equal(Math.round(ss.geometric_mean([2, 32, 1])), 4);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('returns null for empty lists', function(t) {
|
||||
t.equal(ss.geometric_mean([]), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('returns null for lists with negative numbers', function(t) {
|
||||
t.equal(ss.geometric_mean([-1]), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('harmonic_mean', function(t) {
|
||||
// From http://en.wikipedia.org/wiki/Harmonic_mean
|
||||
test('can get the mean of two or more numbers', function(t) {
|
||||
t.equal(ss.harmonic_mean([1, 1]), 1);
|
||||
t.equal(rnd(ss.harmonic_mean([2, 3])), 2.4);
|
||||
t.equal(ss.harmonic_mean([1, 2, 4]), 12 / 7);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('returns null for empty lists', function(t) {
|
||||
t.equal(ss.harmonic_mean([]), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('returns null for lists with negative numbers', function(t) {
|
||||
t.equal(ss.harmonic_mean([-1]), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('interquartile range (iqr)', function(t) {
|
||||
// Data and results from
|
||||
// [Wikipedia](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population)
|
||||
test('can get proper iqr of an even-length list', function(t) {
|
||||
var even = [3, 6, 7, 8, 8, 10, 13, 15, 16, 20];
|
||||
t.equal(ss.quantile(even, 0.75) - ss.quantile(even, 0.25), ss.iqr(even));
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can get proper iqr of an odd-length list', function(t) {
|
||||
var odd = [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20];
|
||||
t.equal(ss.quantile(odd, 0.75) - ss.quantile(odd, 0.25), ss.iqr(odd));
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('an iqr of a zero-length list produces null', function(t) {
|
||||
t.equal(ss.iqr([]), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('jenks', function(t) {
|
||||
test('will not try to assign more classes than datapoints', function(t) {
|
||||
t.equal(ss.jenks([1, 2], 3), null);
|
||||
t.end();
|
||||
});
|
||||
test('assigns correct breaks', function(t) {
|
||||
t.deepEqual(ss.jenks([1, 2, 4, 5, 7, 9, 10, 20], 3), [1, 2, 5, 20]);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('linear regression', function(t) {
|
||||
test('correctly generates a line for a 0, 0 to 1, 1 dataset', function(t) {
|
||||
var l = ss.linear_regression().data([[0, 0], [1, 1]]);
|
||||
t.equal(l.line()(0), 0);
|
||||
t.equal(l.line()(0.5), 0.5);
|
||||
t.equal(l.line()(1), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('correctly generates a line for a 0, 0 to 1, 0 dataset', function(t) {
|
||||
var l = ss.linear_regression().data([[0, 0], [1, 0]]);
|
||||
t.equal(l.line()(0), 0);
|
||||
t.equal(l.line()(0.5), 0);
|
||||
t.equal(l.line()(1), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('returns the data assigned to it', function(t) {
|
||||
var l = ss.linear_regression().data([[0, 0], [1, 0]]);
|
||||
t.deepEqual(l.data(), [[0, 0], [1, 0]]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('handles a single-point sample', function(t) {
|
||||
var l = ss.linear_regression().data([[0, 0]]).line();
|
||||
t.deepEqual(l(10), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('a straight line will have a slope of 0', function(t) {
|
||||
var l = ss.linear_regression().data([[0, 0], [1, 0]]);
|
||||
t.equal(l.m(), 0);
|
||||
t.equal(l.b(), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('a line at 50% grade', function(t) {
|
||||
var l = ss.linear_regression().data([[0, 0], [1, 0.5]]);
|
||||
t.equal(l.m(), 0.5);
|
||||
t.equal(l.b(), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('a line with a high y-intercept', function(t) {
|
||||
var l = ss.linear_regression().data([[0, 20], [1, 10]]);
|
||||
t.equal(l.m(), -10);
|
||||
t.equal(l.b(), 20);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('median absolute deviation (mad)', function(t) {
|
||||
test('median absolute deviation of an example on wikipedia', function(t) {
|
||||
t.equal(ss.mad([1, 1, 2, 2, 4, 6, 9]), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// wolfram alpha: median absolute deviation {0,1,2,3,4,5,6,7,8,9,10}
|
||||
test('median absolute deviation of 0-10', function(t) {
|
||||
t.equal(ss.mad([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 3);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('median absolute deviation of one number is zero', function(t) {
|
||||
t.equal(ss.mad([1]), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('zero-length corner case', function(t) {
|
||||
t.equal(ss.mad([]), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('mean', function(t) {
|
||||
test('can get the mean of two numbers', function(t) {
|
||||
t.equal(ss.mean([1, 2]), 1.5);
|
||||
t.end();
|
||||
});
|
||||
test('can get the mean of one number', function(t) {
|
||||
t.equal(ss.mean([1]), 1);
|
||||
t.end();
|
||||
});
|
||||
test('an empty list has no average', function(t) {
|
||||
t.equal(ss.mean([]), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('median', function(t) {
|
||||
test('can get the median of three numbers', function(t) {
|
||||
t.equal(ss.median([1, 2, 3]), 2);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can get the median of two numbers', function(t) {
|
||||
t.equal(ss.median([1, 2]), 1.5);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can get the median of four numbers', function(t) {
|
||||
t.equal(ss.median([1, 2, 3, 4]), 2.5);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('gives null for the median of an empty list', function(t) {
|
||||
t.equal(ss.median([]), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('sorts numbers numerically', function(t) {
|
||||
t.equal(ss.median([8, 9, 10]), 9);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('does not change the sorting order of its input', function(t) {
|
||||
var x = [1, 0];
|
||||
t.equal(ss.median(x), 0.5);
|
||||
t.equal(x[0], 1);
|
||||
t.equal(x[1], 0);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('min', function(t) {
|
||||
test('can get the minimum of one number', function(t) {
|
||||
t.equal(ss.min([1]), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can get the minimum of three numbers', function(t) {
|
||||
t.equal(ss.min([1, 7, -1000]), -1000);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('max', function(t) {
|
||||
test('can get the maximum of three numbers', function(t) {
|
||||
t.equal(ss.max([1, 7, -1000]), 7);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('mixin', function(t) {
|
||||
test('can mix into a single array', function(t) {
|
||||
var even = ss.mixin([2, 4, 6, 8]);
|
||||
t.equal(even.sum(), 20);
|
||||
t.equal(even.mean(), 5);
|
||||
t.equal(even.max(), 8);
|
||||
t.equal(even.min(), 2);
|
||||
t.equal(even.sample_skewness(), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can mix into Array.prototype', function(t) {
|
||||
ss.mixin();
|
||||
var even = [2, 4, 6, 8];
|
||||
t.equal(even.sum(), 20);
|
||||
t.equal(even.mean(), 5);
|
||||
t.equal(even.max(), 8);
|
||||
t.equal(even.min(), 2);
|
||||
t.equal(even.sample_skewness(), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('mixins can take arguments', function(t) {
|
||||
ss.mixin();
|
||||
var even = [2, 4, 6, 8];
|
||||
t.equal(even.quantile(0.2), 2);
|
||||
t.equal(even.quantile(0.8), 8);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('mode', function(t) {
|
||||
test('the mode of a single-number array is that one number', function(t) {
|
||||
t.equal(ss.mode([1]), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the mode of a two-number array is that one number', function(t) {
|
||||
t.equal(ss.mode([1, 1]), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('other cases', function(t) {
|
||||
t.equal(ss.mode([1, 1, 2]), 1);
|
||||
t.equal(ss.mode([1, 1, 2, 3]), 1);
|
||||
t.equal(ss.mode([1, 1, 2, 3, 3]), 1);
|
||||
t.equal(ss.mode([1, 1, 2, 3, 3, 3]), 3);
|
||||
t.equal(ss.mode([1, 1, 2, 2, 2, 2, 3, 3, 3]), 2);
|
||||
t.equal(ss.mode([1, 2, 3, 4, 5]), 1);
|
||||
t.equal(ss.mode([1, 2, 3, 4, 5, 5]), 5);
|
||||
t.equal(ss.mode([1, 1, 1, 2, 2, 3, 3, 4, 4]), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the mode of an empty array is null', function(t) {
|
||||
t.equal(ss.mode([]), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the mode of a three-number array with two same numbers is the repeated one', function(t) {
|
||||
t.equal(ss.mode([1, 2, 2]), 2);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('natural distribution and z-score', function(t) {
|
||||
|
||||
test('normal table is exposed in the API', function(t) {
|
||||
t.equal(ss.standard_normal_table.length, 310);
|
||||
t.equal(ss.standard_normal_table[0], 0.5);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('P(Z <= 0.4) is 0.6554', function(t) {
|
||||
// Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table
|
||||
t.equal(ss.cumulative_std_normal_probability(0.4), 0.6554);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('P(Z <= -1.20) is 0.1151', function(t) {
|
||||
// Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table
|
||||
t.equal(ss.cumulative_std_normal_probability(-1.20), 0.1151);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('P(X <= 82) when X ~ N (80, 25) is 0.6554', function(t) {
|
||||
// Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table
|
||||
// A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5.
|
||||
// What is the probability that a student scores an 82 or less?
|
||||
t.equal(ss.cumulative_std_normal_probability(ss.z_score(82, 80, 5)), 0.6554);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('P(X >= 90) when X ~ N (80, 25) is 0.0228', function(t) {
|
||||
// Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table
|
||||
// A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5.
|
||||
// What is the probability that a student scores a 90 or more?
|
||||
t.equal(+(1 - ss.cumulative_std_normal_probability(ss.z_score(90, 80, 5))).toPrecision(5), 0.0228);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('P(X <= 74) when X ~ N (80, 25) is 0.1151', function(t) {
|
||||
// Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table
|
||||
// A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5.
|
||||
// What is the probability that a student scores a 74 or less?
|
||||
t.equal(ss.cumulative_std_normal_probability(ss.z_score(74, 80, 5)), 0.1151);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('P(78 <= X <= 88) when X ~ N (80, 25) is 0.6006', function(t) {
|
||||
// Taken from the examples of use in http://en.wikipedia.org/wiki/Standard_normal_table
|
||||
// A professor's exam scores are approximately distributed normally with mean 80 and standard deviation 5.
|
||||
// What is the probability that a student scores between 78 and 88?
|
||||
var prob88 = ss.cumulative_std_normal_probability(ss.z_score(88, 80, 5)),
|
||||
prob78 = ss.cumulative_std_normal_probability(ss.z_score(78, 80, 5));
|
||||
|
||||
t.equal(+(prob88 - prob78).toPrecision(5), 0.6006);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(n) {
|
||||
return parseFloat(n.toFixed(4));
|
||||
}
|
||||
|
||||
// expected cumulative probabilities taken from Appendix 1, Table I of William W. Hines & Douglas C.
|
||||
// Montgomery, "Probability and Statistics in Engineering and Management Science", Wiley (1980).
|
||||
test('poisson_distribution', function(t) {
|
||||
test('can return generate probability and cumulative probability distributions for lambda = 3.0', function(t) {
|
||||
t.equal('object', typeof ss.poisson_distribution(3.0));
|
||||
t.equal(rnd(ss.poisson_distribution(3.0)[3]), 0.2240, ss.epsilon);
|
||||
t.end();
|
||||
});
|
||||
test('can generate probability and cumulative probability distributions for lambda = 4.0', function(t) {
|
||||
t.equal('object', typeof ss.poisson_distribution(4.0));
|
||||
t.equal(rnd(ss.poisson_distribution(4.0)[2]), 0.1465, ss.epsilon);
|
||||
t.end();
|
||||
});
|
||||
test('can generate probability and cumulative probability distributions for lambda = 5.5', function(t) {
|
||||
t.equal('object', typeof ss.poisson_distribution(5.5));
|
||||
t.equal(rnd(ss.poisson_distribution(5.5)[7]), 0.1234, ss.epsilon);
|
||||
t.end();
|
||||
});
|
||||
test('can generate probability and cumulative probability distributions for lambda = 9.5', function(t) {
|
||||
t.equal('object', typeof ss.poisson_distribution(9.5));
|
||||
t.equal(rnd(ss.poisson_distribution(9.5)[17]), 0.0088, ss.epsilon);
|
||||
t.end();
|
||||
});
|
||||
test('can return null when lambda <= 0', function(t) {
|
||||
t.equal(null, ss.poisson_distribution(0));
|
||||
t.equal(null, ss.poisson_distribution(-10));
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('quantile', function(t) {
|
||||
// Data and results from
|
||||
// [Wikipedia](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population)
|
||||
test('can get proper quantiles of an even-length list', function(t) {
|
||||
var even = [3, 6, 7, 8, 8, 10, 13, 15, 16, 20];
|
||||
t.equal(ss.quantile(even, 0.25), 7);
|
||||
t.equal(ss.quantile(even, 0.5), 9);
|
||||
t.equal(ss.quantile(even, 0.75), 15);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can get proper quantiles of an odd-length list', function(t) {
|
||||
var odd = [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20];
|
||||
t.equal(ss.quantile(odd, 0.25), 7);
|
||||
t.equal(ss.quantile(odd, 0.5), 9);
|
||||
t.equal(ss.quantile(odd, 0.75), 15);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the median quantile is equal to the median', function(t) {
|
||||
var rand = [1, 4, 5, 8];
|
||||
t.equal(ss.quantile(rand, 0.5), ss.median(rand));
|
||||
var rand2 = [10, 50, 2, 4, 4, 5, 8];
|
||||
t.equal(ss.quantile(rand2, 0.5), ss.median(rand2));
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('a zero-length list produces null', function(t) {
|
||||
t.equal(ss.quantile([], 0.5), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('test odd-value case', function(t) {
|
||||
t.equal(ss.quantile([0, 1, 2, 3, 4], 0.2), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('bad bounds produce null', function(t) {
|
||||
t.equal(ss.quantile([1, 2, 3], 1.1), null);
|
||||
t.equal(ss.quantile([1, 2, 3], -0.5), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('max quantile is equal to the max', function(t) {
|
||||
t.equal(ss.quantile([1, 2, 3], 1), ss.max([1, 2, 3]));
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('min quantile is equal to the min', function(t) {
|
||||
t.equal(ss.quantile([1, 2, 3], 0), ss.min([1, 2, 3]));
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('if quantile arg is an array, response is an array of quantiles', function(t) {
|
||||
var odd = [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20];
|
||||
t.deepEqual(ss.quantile(odd, [0, 0.25, 0.5, 0.75, 1]), [3, 7, 9, 15, 20]);
|
||||
t.deepEqual(ss.quantile(odd, [0.75, 0.5]), [15, 9]);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('quantile_sorted', function(t) {
|
||||
// Data and results from
|
||||
// [Wikipedia](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population)
|
||||
test('can get proper quantiles of an even-length list', function(t) {
|
||||
var even = [3, 6, 7, 8, 8, 10, 13, 15, 16, 20];
|
||||
t.equal(ss.quantile_sorted(even, 0.25), 7);
|
||||
t.equal(ss.quantile_sorted(even, 0.5), 9);
|
||||
t.equal(ss.quantile_sorted(even, 0.75), 15);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('r-squared', function(t) {
|
||||
test('says that the r squared of a two-point line is perfect', function(t) {
|
||||
var d = [[0, 0], [1, 1]];
|
||||
var l = ss.linear_regression().data(d);
|
||||
t.equal(ss.r_squared(d, l.line()), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('says that the r squared of a three-point line is not perfect', function(t) {
|
||||
var d = [[0, 0], [0.5, 0.2], [1, 1]];
|
||||
var l = ss.linear_regression().data(d);
|
||||
t.notEqual(ss.r_squared(d, l.line()), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('r-squared of single sample is 1', function(t) {
|
||||
var d = [[0, 0]];
|
||||
var l = ss.linear_regression().data(d);
|
||||
t.equal(ss.r_squared(d, l.line()), 1);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('root_mean_square', function(t) {
|
||||
// From http://en.wikipedia.org/wiki/Root_mean_square
|
||||
test('can get the RMS of two or more numbers', function(t) {
|
||||
t.equal(ss.root_mean_square([1, 1]), 1);
|
||||
t.equal(rnd(ss.root_mean_square([3, 4, 5])), 4.082);
|
||||
t.equal(rnd(ss.root_mean_square([-0.1, 5, -2, 10])), 5.679);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('returns null for empty lists', function(t) {
|
||||
t.equal(ss.root_mean_square([]), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
var test = require('tape');
|
||||
var Random = require('random-js');
|
||||
var random = new Random(Random.engines.mt19937().seed(0));
|
||||
var ss = require('../');
|
||||
|
||||
function rng() { return random.real(0, 1); }
|
||||
|
||||
test('sample', function(t) {
|
||||
t.deepEqual(ss.sample([], 0, rng), [], 'edge case - zero array');
|
||||
t.deepEqual(ss.sample([], 2, rng), [], 'edge case - zero array');
|
||||
t.deepEqual(ss.sample([1,2,3], 0, rng, 0), [], 'edge case - zero array');
|
||||
t.deepEqual(ss.sample([1,2,3], 1, rng), [1], 'edge case - sample of 1');
|
||||
t.deepEqual(ss.sample([1,2,3], 1, rng), [2]);
|
||||
t.deepEqual(ss.sample([1,2,3], 3, rng), [2,3,1]);
|
||||
t.deepEqual(ss.sample([1,2,3,4], 2, rng), [3,1]);
|
||||
t.deepEqual(ss.sample([1,2,3,4,6,7,8], 2, rng), [8,7]);
|
||||
t.deepEqual(ss.sample(['foo', 'bar'], 1, rng), ['foo'], 'non-number contents');
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('sample correlation', function(t) {
|
||||
|
||||
test('can get the sample correlation of identical arrays', function(t) {
|
||||
var data = [1, 2, 3, 4, 5, 6];
|
||||
t.equal(rnd(ss.sample_correlation(data, data)), 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can get the sample correlation of different arrays', function(t) {
|
||||
var a = [1, 2, 3, 4, 5, 6];
|
||||
var b = [2, 2, 3, 4, 5, 60];
|
||||
t.equal(rnd(ss.sample_correlation(a, b)), 0.691);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('zero-length corner case', function(t) {
|
||||
t.equal(rnd(ss.sample_correlation([], [])), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('sample covariance', function(t) {
|
||||
test('can get perfect negative covariance', function(t) {
|
||||
var x = [1, 2, 3, 4, 5, 6];
|
||||
var y = [6, 5, 4, 3, 2, 1];
|
||||
t.equal(rnd(ss.sample_covariance(x, y)), -3.5);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('covariance of something with itself is its variance', function(t) {
|
||||
var x = [1, 2, 3, 4, 5, 6];
|
||||
t.equal(rnd(ss.sample_covariance(x, x)), 3.5);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('covariance is zero for something with no correlation', function(t) {
|
||||
var x = [1, 2, 3, 4, 5, 6];
|
||||
var y = [1, 1, 2, 2, 1, 1];
|
||||
t.equal(rnd(ss.sample_covariance(x, y)), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('zero-length corner case', function(t) {
|
||||
t.equal(rnd(ss.sample_covariance([], [])), 0);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('sample skewness', function(t) {
|
||||
|
||||
test('the skewness of an empty sample is null', function(t) {
|
||||
var data = [];
|
||||
t.equal(ss.sample_skewness(data), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the skewness of an sample with one number is null', function(t) {
|
||||
var data = [1];
|
||||
t.equal(ss.sample_skewness(data), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the skewness of an sample with two numbers is null', function(t) {
|
||||
var data = [1, 2];
|
||||
t.equal(ss.sample_skewness(data), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can calculate the skewness of SAS example 1', function(t) {
|
||||
// Data and answer taken from SKEWNESS function documentation at
|
||||
// http://support.sas.com/documentation/c../lrdict/64316/HTML/default/viewer.htm#a000245947.htm
|
||||
var data = [0, 1, 1];
|
||||
t.equal(+ss.sample_skewness(data).toPrecision(10), -1.732050808);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can calculate the skewness of SAS example 2', function(t) {
|
||||
// Data and answer taken from SKEWNESS function documentation at
|
||||
// http://support.sas.com/documentation/c../lrdict/64316/HTML/default/viewer.htm#a000245947.htm
|
||||
var data = [2, 4, 6, 3, 1];
|
||||
t.equal(+ss.sample_skewness(data).toPrecision(10), 0.5901286564);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can calculate the skewness of SAS example 3', function(t) {
|
||||
// Data and answer taken from SKEWNESS function documentation at
|
||||
// http://support.sas.com/documentation/c../lrdict/64316/HTML/default/viewer.htm#a000245947.htm
|
||||
var data = [2, 0, 0];
|
||||
t.equal(+ss.sample_skewness(data).toPrecision(10), 1.732050808);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('sample_standard_deviation', function(t) {
|
||||
test('can get the standard deviation of an example on wikipedia', function(t) {
|
||||
t.equal(rnd(ss.sample_standard_deviation([2, 4, 4, 4, 5, 5, 7, 9])), 2.138);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('zero-length corner case', function(t) {
|
||||
t.equal(rnd(ss.sample_standard_deviation([])), 0);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('sample variance', function(t) {
|
||||
test('can get the sample variance of a six-sided die', function(t) {
|
||||
t.equal(rnd(ss.sample_variance([1, 2, 3, 4, 5, 6])), 3.5);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// confirmed in R
|
||||
//
|
||||
// > var(1:10)
|
||||
// [1] 9.166667
|
||||
test('can get the sample variance of numbers 1-10', function(t) {
|
||||
t.equal(rnd(ss.sample_variance([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])), 9.167);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the sample variance of two numbers that are the same is 0', function(t) {
|
||||
t.equal(rnd(ss.sample_variance([1, 1])), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the sample variance of one number is null', function(t) {
|
||||
t.equal(ss.sample_variance([1]), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the sample variance of no numbers is null', function(t) {
|
||||
t.equal(ss.sample_variance([]), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
var test = require('tape');
|
||||
var Random = require('random-js');
|
||||
var random = new Random(Random.engines.mt19937().seed(0));
|
||||
var ss = require('../');
|
||||
|
||||
function rng() { return random.real(0, 1); }
|
||||
|
||||
test('shuffle', function(t) {
|
||||
var input = [1, 2, 3, 4, 5, 6];
|
||||
t.deepEqual(ss.shuffle([], rng), []);
|
||||
t.deepEqual(ss.shuffle(input, rng), [1, 5, 3, 2, 4, 6]);
|
||||
t.deepEqual(input, [1, 2, 3, 4, 5, 6], 'does not change original array');
|
||||
t.deepEqual(ss.shuffle(input, rng), [5, 4, 1, 3, 6, 2]);
|
||||
t.deepEqual(input, [1, 2, 3, 4, 5, 6], 'does not change original array');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('shuffle_in_place', function(t) {
|
||||
var input = [1, 2, 3, 4, 5, 6];
|
||||
t.deepEqual(ss.shuffle_in_place([], rng), []);
|
||||
t.deepEqual(ss.shuffle_in_place(input, rng), [6, 1, 5, 2, 4, 3]);
|
||||
t.deepEqual(input, [6, 1, 5, 2, 4, 3], 'changes original array');
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('standard_deviation', function(t) {
|
||||
test('can get the standard deviation of an example on wikipedia', function(t) {
|
||||
t.equal(rnd(ss.standard_deviation([2, 4, 4, 4, 5, 5, 7, 9])), 2);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// confirmed with numpy
|
||||
// In [4]: numpy.std([1,2,3])
|
||||
// Out[4]: 0.81649658092772603
|
||||
test('can get the standard deviation of 1-3', function(t) {
|
||||
t.equal(rnd(ss.standard_deviation([1, 2, 3])), 0.816);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('zero-length array corner case', function(t) {
|
||||
t.equal(rnd(ss.standard_deviation([])), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// In [6]: numpy.std([0,1,2,3,4,5,6,7,8,9,10])
|
||||
// Out[6]: 3.1622776601683795
|
||||
test('can get the standard deviation of 1-10', function(t) {
|
||||
t.equal(rnd(ss.standard_deviation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])), 3.162);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the standard deviation of one number is zero', function(t) {
|
||||
t.equal(rnd(ss.standard_deviation([1])), 0);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('standard_normal_table', function(t) {
|
||||
test('all entries are numeric', function(t) {
|
||||
for (var i = 0; i < ss.standard_normal_table.length; i++) {
|
||||
t.equal(typeof ss.standard_normal_table[i], 'number');
|
||||
t.ok(ss.standard_normal_table[i] >= 0);
|
||||
t.ok(ss.standard_normal_table[i] <= 1);
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
test('sum', function(t) {
|
||||
test('can get the sum of two numbers', function(t) {
|
||||
t.equal(ss.sum([1, 2]), 3);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the sum of no numbers is zero', function(t) {
|
||||
t.equal(ss.sum([]), 0);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
var test = require('tape'),
|
||||
ss = require('../');
|
||||
|
||||
test('t test', function(t) {
|
||||
|
||||
test('can compare a known value to the mean of samples', function(t) {
|
||||
var res = ss.t_test([1, 2, 3, 4, 5, 6], 3.385);
|
||||
t.equal(res, 0.1649415480881466);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can test independency of two samples', function(t) {
|
||||
var res = ss.t_test_two_sample([1, 2, 3, 4], [3, 4, 5, 6], 0);
|
||||
t.equal(res, -2.1908902300206643);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can test independency of two samples (mu == -2)', function(t) {
|
||||
var res = ss.t_test_two_sample([1, 2, 3, 4], [3, 4, 5, 6], -2);
|
||||
t.equal(res, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can test independency of two samples of different lengths', function(t) {
|
||||
var res = ss.t_test_two_sample([1, 2, 3, 4], [3, 4, 5, 6, 1, 2, 0]);
|
||||
t.equal(res, -0.4165977904505309);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('has an edge case for one sample being of size zero', function(t) {
|
||||
t.equal(ss.t_test_two_sample([1, 2, 3, 4], []), null);
|
||||
t.equal(ss.t_test_two_sample([], [1, 2, 3, 4]), null);
|
||||
t.equal(ss.t_test_two_sample([], []), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
var test = require('tape');
|
||||
var ss = require('../');
|
||||
|
||||
function rnd(x) {
|
||||
return Math.round(x * 1000) / 1000;
|
||||
}
|
||||
|
||||
test('variance', function(t) {
|
||||
test('can get the variance of a six-sided die', function(t) {
|
||||
t.equal(rnd(ss.variance([1, 2, 3, 4, 5, 6])), 2.917);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the variance of one number is zero', function(t) {
|
||||
t.equal(rnd(ss.variance([1])), 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('the variance of no numbers is null', function(t) {
|
||||
t.equal(ss.variance([]), null);
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
// import pkg from "./package.json";
|
||||
|
||||
export default [
|
||||
{
|
||||
input: "src/index.js",
|
||||
output: {
|
||||
name: "Filer",
|
||||
file: "dist/filer.js",
|
||||
format: "umd",
|
||||
sourcemap: "inline",
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,65 @@
|
|||
const { FileSystem } = require('../src/index');
|
||||
|
||||
let Provider;
|
||||
try {
|
||||
Provider = require('fsProvider');
|
||||
}
|
||||
catch (err) {
|
||||
Provider = require('./providers/default');
|
||||
}
|
||||
|
||||
const provider = new Provider();
|
||||
|
||||
let onFsReady;
|
||||
let onFsError;
|
||||
|
||||
let fsReady = new Promise((resolve, reject) => {
|
||||
onFsReady = resolve;
|
||||
onFsError = reject;
|
||||
});
|
||||
|
||||
var fsInstance = new FileSystem({ provider }, (err) => {
|
||||
if (err) {
|
||||
onFsError(err);
|
||||
} else {
|
||||
onFsReady(true);
|
||||
}
|
||||
});
|
||||
|
||||
function proxyHasProp(target, prop) {
|
||||
return prop in target;
|
||||
}
|
||||
|
||||
const fsPromises = new Proxy(fsInstance.promises, {
|
||||
get(target, prop) {
|
||||
if (!proxyHasProp(target, prop)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return async (...args) => {
|
||||
await fsReady;
|
||||
return await target[prop](...args);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const fs = new Proxy(fsInstance, {
|
||||
get(target, prop) {
|
||||
if (!proxyHasProp(target, prop)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop === 'promises') {
|
||||
return fsPromises;
|
||||
}
|
||||
|
||||
return (...args) => {
|
||||
(async () => {
|
||||
await fsReady;
|
||||
target[prop](...args);
|
||||
})();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = fs;
|
|
@ -0,0 +1,3 @@
|
|||
const { path } = require('../src/index');
|
||||
|
||||
module.exports = path;
|
|
@ -0,0 +1,2 @@
|
|||
const { Default } = require('../../src/providers/index');
|
||||
module.exports = Default;
|
|
@ -0,0 +1,2 @@
|
|||
const IndexedDB = require('../../src/providers/indexeddb');
|
||||
module.exports = IndexedDB;
|
|
@ -0,0 +1,2 @@
|
|||
const Memory = require('../../src/providers/memory');
|
||||
module.exports = Memory;
|
|
@ -1,76 +0,0 @@
|
|||
const INSPECT_MAX_BYTES = 50;
|
||||
const K_MAX_LENGTH = 0x7fffffff;
|
||||
|
||||
class Buffer extends Uint8Array
|
||||
{
|
||||
constructor(arg, encodingOrOffset, length)
|
||||
{
|
||||
if (typeof arg === "number") {
|
||||
if (typeof encodingOrOffset === "string") {
|
||||
throw new TypeError(`The "string" argument must be of type string. Received type number`);
|
||||
}
|
||||
return allocUnsafe(arg);
|
||||
}
|
||||
return from(arg, encodingOrOffset, length);
|
||||
}
|
||||
|
||||
static get INSPECT_MAX_BYTES() { return 50 }
|
||||
|
||||
static get K_MAX_LENGTH() { return 0x7fffffff }
|
||||
|
||||
static isSupported()
|
||||
{
|
||||
try {
|
||||
var arr = new Uint8Array(1)
|
||||
arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
|
||||
return arr.foo() === 42
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static from(value, encodingOrOffset, length)
|
||||
{
|
||||
if (typeof value === 'string') {
|
||||
return fromString(value, encodingOrOffset)
|
||||
}
|
||||
|
||||
if (ArrayBuffer.isView(value)) {
|
||||
return fromArrayLike(value)
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throw TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ${typeof value}`);
|
||||
}
|
||||
|
||||
if (isInstance(value, ArrayBuffer) ||
|
||||
(value && isInstance(value.buffer, ArrayBuffer))) {
|
||||
return fromArrayBuffer(value, encodingOrOffset, length)
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
throw new TypeError(
|
||||
'The "value" argument must not be of type number. Received type number'
|
||||
)
|
||||
}
|
||||
|
||||
var valueOf = value.valueOf && value.valueOf();
|
||||
if(valueOf != null && valueOf !== value) {
|
||||
return Buffer.from(valueOf, encodingOrOffset, length);
|
||||
}
|
||||
|
||||
var b = fromObject(value);
|
||||
if(b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if(typeof Symbol !== "undefined" && Symbol.toPrimitive != null &&
|
||||
typeof value[Symbol.toPrimitive] === "function") {
|
||||
return Buffer.from(value[Symbol.toPrimitive]("string"), encodingOrOffset, length);
|
||||
}
|
||||
|
||||
throw new TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ${typeof value}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default Buffer;
|
|
@ -1,28 +0,0 @@
|
|||
import Buffer from "./buffer";
|
||||
|
||||
export const SUPER_NODE_ID = "0000000000000000000000";
|
||||
|
||||
export const MODE_FILE = "FILE";
|
||||
export const MODE_DIRECTORY = "DIRECTORY";
|
||||
export const MODE_SYMBOLIC_LINK = "MODE_SYMBOLIC_LINK";
|
||||
export const MODE_META = "META";
|
||||
export const MODE_SOCKET = "SOCKET";
|
||||
export const MODE_FIFO = "FIFO";
|
||||
export const MODE_CHARACTER_DEVICE = "CHARACTER_DEVICE";
|
||||
export const MODE_BLOCK_DEVICE = "BLOCK_DEVICE";
|
||||
|
||||
export const ROOT_DIRECTORY_NAME = "/"; // basename(normalize(path))
|
||||
|
||||
export const STDIN = 0;
|
||||
export const STDOUT = 1;
|
||||
export const STDERR = 2;
|
||||
|
||||
export const FIRST_DESCRIPTOR = 3;
|
||||
|
||||
export const N_VFS_DESCRIPTORS = 1024;
|
||||
|
||||
export const DATA_BLOCK_SEPARATOR = "#";
|
||||
|
||||
export const MNT_READ_ONLY = "READ_ONLY";
|
||||
|
||||
export const SYMLOOP_MAX = 10;
|
|
@ -1,26 +0,0 @@
|
|||
import Platform from "./platform";
|
||||
import E from "./errors";
|
||||
|
||||
let Crypto;
|
||||
if(Platform.supportsWebCrypto()) {
|
||||
Crypto = class Crypto
|
||||
{
|
||||
static randomBytes(arrayBuffer)
|
||||
{
|
||||
return window.crypto.getRandomValues(arrayBuffer);
|
||||
}
|
||||
}
|
||||
} else if(Platform.supportsNodeCrypto()) {
|
||||
let nodeCrypto = require("crypto");
|
||||
Crypto = class Crypto
|
||||
{
|
||||
static randomBytes(arrayBuffer)
|
||||
{
|
||||
return nodeCrypto.randomFillSync(arrayBuffer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new E.ENOTSUPPORTED("crypto support is not available on this platform");
|
||||
}
|
||||
|
||||
export default Crypto;
|
|
@ -1,68 +0,0 @@
|
|||
class FilerError extends Error
|
||||
{
|
||||
constructor(message, path = null)
|
||||
{
|
||||
super(message);
|
||||
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
||||
const errors = {};
|
||||
const errorDefinitions =
|
||||
[
|
||||
{ errno: -1, name: "UNKNOWN", text: "unknown error" },
|
||||
{ errno: 0, name: "OK", text: "success" },
|
||||
{ errno: 1, name: "EOF", text: "end of file" },
|
||||
|
||||
{ errno: 9, name: "EBADF", text: "bad file descriptor" },
|
||||
{ errno: 10, name: "EBUSY", text: "resource busy or locked" },
|
||||
|
||||
{ errno: 18, name: "EINVAL", text: "invalid argument" },
|
||||
|
||||
{ errno: 27, name: "ENOTDIR", text: "not a directory" },
|
||||
{ errno: 28, name: "EISDIR", text: "illegal operation on directory" },
|
||||
|
||||
{ errno: 34, name: "ENOENT", text: "no such file or directory" },
|
||||
|
||||
{ errno: 47, name: "EEXIST", text: "file already exists" },
|
||||
|
||||
{ errno: 50, name: "EPERM", text: "operation not permitted" },
|
||||
{ errno: 51, name: "ELOOP", text: "too many symbolic links encountered" },
|
||||
|
||||
{ errno: 53, name: "ENOTEMPTY", text: "directory not empty" },
|
||||
|
||||
{ errno: 55, name: "EIO", text: "i/o error" },
|
||||
{ errno: 56, name: "EROFS", text: "read-only file system" },
|
||||
{ errno: 57, name: "ENODEV", text: "no such device" },
|
||||
|
||||
{ errno: 58, name: "ECANCELED", text: "operation canceled" },
|
||||
|
||||
{ errno: 1000, name: "ENOTSUPPORTED", text: "platform is not supported" },
|
||||
]
|
||||
|
||||
for (let error of errorDefinitions) {
|
||||
errors[error.errno] = errors[error.name] = class extends FilerError {
|
||||
constructor(message, path)
|
||||
{
|
||||
super(message || error.text, path);
|
||||
}
|
||||
|
||||
get name() { return error.name }
|
||||
|
||||
get code() { return error.name }
|
||||
|
||||
get errno() { return error.errno }
|
||||
|
||||
get message() { return this.message }
|
||||
|
||||
get stack() { return (new Error(this.message)).stack }
|
||||
|
||||
get toString() {
|
||||
pathInfo = this.path ? (', \'' + this.path + '\'') : '';
|
||||
return `${this.name}: ${this.message}${pathInfo}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default errors;
|
|
@ -1,204 +0,0 @@
|
|||
import E from "./errors";
|
||||
|
||||
function normalizeArray(parts, allowAboveRoot) {
|
||||
// if the path tries to go above the root, `up` ends up > 0
|
||||
var up = 0;
|
||||
for (var i = parts.length - 1; i >= 0; i--) {
|
||||
var last = parts[i];
|
||||
if (last === '.') {
|
||||
parts.splice(i, 1);
|
||||
} else if (last === '..') {
|
||||
parts.splice(i, 1);
|
||||
up++;
|
||||
} else if (up) {
|
||||
parts.splice(i, 1);
|
||||
up--;
|
||||
}
|
||||
}
|
||||
|
||||
// if the path is allowed to go above the root, restore leading ..s
|
||||
if (allowAboveRoot) {
|
||||
for (; up--; up) {
|
||||
parts.unshift('..');
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
// Split a filename into [root, dir, basename, ext], unix version
|
||||
// 'root' is just a slash, or nothing.
|
||||
var splitPathRe =
|
||||
/^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/;
|
||||
var splitPath = function(filename) {
|
||||
var result = splitPathRe.exec(filename);
|
||||
return [result[1] || '', result[2] || '', result[3] || '', result[4] || ''];
|
||||
};
|
||||
|
||||
// path.resolve([from ...], to)
|
||||
export function resolve() {
|
||||
var resolvedPath = '',
|
||||
resolvedAbsolute = false;
|
||||
|
||||
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
||||
// XXXfiler: we don't have process.cwd() so we use '/' as a fallback
|
||||
var path = (i >= 0) ? arguments[i] : '/';
|
||||
|
||||
// Skip empty and invalid entries
|
||||
if (typeof path !== 'string' || !path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
resolvedPath = path + '/' + resolvedPath;
|
||||
resolvedAbsolute = path.charAt(0) === '/';
|
||||
}
|
||||
|
||||
// At this point the path should be resolved to a full absolute path, but
|
||||
// handle relative paths to be safe (might happen when process.cwd() fails)
|
||||
|
||||
// Normalize the path
|
||||
resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) {
|
||||
return !!p;
|
||||
}), !resolvedAbsolute).join('/');
|
||||
|
||||
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
|
||||
}
|
||||
|
||||
// path.normalize(path)
|
||||
export function normalize(path) {
|
||||
var isAbsolute = path.charAt(0) === '/',
|
||||
trailingSlash = path.substr(-1) === '/';
|
||||
|
||||
// Normalize the path
|
||||
path = normalizeArray(path.split('/').filter(function(p) {
|
||||
return !!p;
|
||||
}), !isAbsolute).join('/');
|
||||
|
||||
if (!path && !isAbsolute) {
|
||||
path = '.';
|
||||
}
|
||||
/*
|
||||
if (path && trailingSlash) {
|
||||
path += '/';
|
||||
}
|
||||
*/
|
||||
|
||||
return (isAbsolute ? '/' : '') + path;
|
||||
}
|
||||
|
||||
export function join() {
|
||||
var paths = Array.prototype.slice.call(arguments, 0);
|
||||
return normalize(paths.filter(function(p, index) {
|
||||
return p && typeof p === 'string';
|
||||
}).join('/'));
|
||||
}
|
||||
|
||||
// path.relative(from, to)
|
||||
export function relative(from, to) {
|
||||
from = resolve(from).substr(1);
|
||||
to = resolve(to).substr(1);
|
||||
|
||||
function trim(arr) {
|
||||
var start = 0;
|
||||
for (; start < arr.length; start++) {
|
||||
if (arr[start] !== '') break;
|
||||
}
|
||||
|
||||
var end = arr.length - 1;
|
||||
for (; end >= 0; end--) {
|
||||
if (arr[end] !== '') break;
|
||||
}
|
||||
|
||||
if (start > end) return [];
|
||||
return arr.slice(start, end - start + 1);
|
||||
}
|
||||
|
||||
var fromParts = trim(from.split('/'));
|
||||
var toParts = trim(to.split('/'));
|
||||
|
||||
var length = Math.min(fromParts.length, toParts.length);
|
||||
var samePartsLength = length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (fromParts[i] !== toParts[i]) {
|
||||
samePartsLength = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var outputParts = [];
|
||||
for (var i = samePartsLength; i < fromParts.length; i++) {
|
||||
outputParts.push('..');
|
||||
}
|
||||
|
||||
outputParts = outputParts.concat(toParts.slice(samePartsLength));
|
||||
|
||||
return outputParts.join('/');
|
||||
}
|
||||
|
||||
export function dirname(path) {
|
||||
var result = splitPath(path),
|
||||
root = result[0],
|
||||
dir = result[1];
|
||||
|
||||
if (!root && !dir) {
|
||||
// No dirname whatsoever
|
||||
return '.';
|
||||
}
|
||||
|
||||
if (dir) {
|
||||
// It has a dirname, strip trailing slash
|
||||
dir = dir.substr(0, dir.length - 1);
|
||||
}
|
||||
|
||||
return root + dir;
|
||||
}
|
||||
|
||||
export function basename(path, ext) {
|
||||
var f = splitPath(path)[2];
|
||||
// TODO: make this comparison case-insensitive on windows?
|
||||
if (ext && f.substr(-1 * ext.length) === ext) {
|
||||
f = f.substr(0, f.length - ext.length);
|
||||
}
|
||||
// XXXfiler: node.js just does `return f`
|
||||
return f === "" ? "/" : f;
|
||||
}
|
||||
|
||||
export function extname(path) {
|
||||
return splitPath(path)[3];
|
||||
}
|
||||
|
||||
export function isAbsolute(path) {
|
||||
if(path.charAt(0) === '/') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isNull(path) {
|
||||
if (('' + path).indexOf('\u0000') !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure we don't double-add a trailing slash (e.g., '/' -> '//')
|
||||
export function addTrailing(path) {
|
||||
return path.replace(/\/*$/, '/');
|
||||
}
|
||||
|
||||
// Deal with multiple slashes at the end, one, or none
|
||||
// and make sure we don't return the empty string.
|
||||
export function removeTrailing(path) {
|
||||
path = path.replace(/\/*$/, '');
|
||||
return path === '' ? '/' : path;
|
||||
}
|
||||
|
||||
export function check(path) {
|
||||
if(!path) {
|
||||
throw new E.EINVAL('path must be a string', path);
|
||||
} else if(isNull(path)) {
|
||||
throw new E.EINVAL('path must be a string without null bytes', path);
|
||||
} else if(!isAbsolute(path)) {
|
||||
throw new E.EINVAL('path must be absolute', path);
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
class Platform
|
||||
{
|
||||
static supportsWebCrypto()
|
||||
{
|
||||
return ("undefined" !== typeof window &&
|
||||
"undefined" !== typeof window.crypto &&
|
||||
"function" === typeof window.crypto.getRandomValues);
|
||||
}
|
||||
|
||||
static supportsNodeCrypto()
|
||||
{
|
||||
if("undefined" !== typeof process) {
|
||||
try {
|
||||
require.resolve("crypto");
|
||||
return true;
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export default Platform;
|
|
@ -1,117 +0,0 @@
|
|||
const __ = new WeakMap();
|
||||
|
||||
const URL_REGEX = /^((\w+)\+(\w+):)?(\/\/((\w+)?(:(\w+))?@)?([^\/\?:]+)(:(\d+))?)?(\/?([^\/\?#][^\?#]*)?)?(\?([^#]+))?(#(\w*))?/i;
|
||||
|
||||
class URL
|
||||
{
|
||||
constructor(urlString)
|
||||
{
|
||||
__.set(this, {
|
||||
|
||||
});
|
||||
const self = __.get(this);
|
||||
|
||||
let match = urlString.match(URL_REGEX);
|
||||
|
||||
self.originalURL = match[0];
|
||||
|
||||
if(match[2]) {
|
||||
self.protocol = match[2];
|
||||
}
|
||||
|
||||
if(match[3]) {
|
||||
self.subprotocol = match[3];
|
||||
}
|
||||
|
||||
if(match[6]) {
|
||||
self.username = match[6];
|
||||
}
|
||||
|
||||
if(match[8]) {
|
||||
self.password = match[8];
|
||||
}
|
||||
|
||||
if(match[9]) {
|
||||
self.host = match[9];
|
||||
} else {
|
||||
self.host = "";
|
||||
}
|
||||
|
||||
if(match[11]) {
|
||||
self.port = match[11];
|
||||
}
|
||||
|
||||
if(match[12]) {
|
||||
self.path = match[12];
|
||||
} else {
|
||||
self.path = "";
|
||||
}
|
||||
|
||||
if(match[15]) {
|
||||
let queryList = match[15].split("&");
|
||||
let query = {};
|
||||
for(let item of queryList) {
|
||||
let [key, value] = item.split("=");
|
||||
if(!(query.hasOwnProperty(key))) {
|
||||
query[key] = [];
|
||||
}
|
||||
if(value) {
|
||||
query[key].push(value);
|
||||
}
|
||||
}
|
||||
self.query = query;
|
||||
} else {
|
||||
self.query = {};
|
||||
}
|
||||
|
||||
if(match[17]) {
|
||||
self.fragment = match[17];
|
||||
} else {
|
||||
self.fragment = "";
|
||||
}
|
||||
}
|
||||
|
||||
get protocol() { return __.get(this).protocol }
|
||||
set protocol(value) { return __.get(this).protocol = value }
|
||||
|
||||
get subprotocol() { return __.get(this).subprotocol }
|
||||
set subprotocol(value) { return __.get(this).subprotocol = value }
|
||||
|
||||
get username() { return __.get(this).username }
|
||||
set username(value) { return __.get(this).username = value }
|
||||
|
||||
get password() { return __.get(this).password }
|
||||
set password(value) { return __.get(this).password = value }
|
||||
|
||||
get host() { return __.get(this).host }
|
||||
set host(value) { return __.get(this).host = value }
|
||||
|
||||
get port() { return __.get(this).port }
|
||||
set port(value) { return __.get(this).port = value }
|
||||
|
||||
get path() { return __.get(this).path }
|
||||
set path(value) { return __.get(this).path = value }
|
||||
|
||||
get query() { return __.get(this).query }
|
||||
set query(value) { return __.get(this).query = value }
|
||||
|
||||
get fragment() { return __.get(this).fragment }
|
||||
set fragment(value) { return __.get(this).fragment = value }
|
||||
|
||||
toJSON()
|
||||
{
|
||||
return {
|
||||
protocol: this.protocol,
|
||||
subprotocol: this.subprotocol,
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
host: this.host,
|
||||
port: this.port,
|
||||
path: this.path,
|
||||
query: this.query,
|
||||
fragment: this.fragment,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default URL;
|
|
@ -1,63 +0,0 @@
|
|||
import Crypto from "./crypto";
|
||||
|
||||
const UUID_SHORT_REGEX = /^[0-9a-zA-Z]{22}$/;
|
||||
const BASE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split('');
|
||||
const BASE_MAP = {};
|
||||
|
||||
for (var z = 0; z < BASE.length; z += 1) {
|
||||
var x = BASE[z];
|
||||
|
||||
if (BASE_MAP[x] !== undefined) throw new TypeError(`${x} is ambiguous`)
|
||||
BASE_MAP[x] = z;
|
||||
}
|
||||
|
||||
function encode(source) {
|
||||
if (source.length === 0) return ''
|
||||
|
||||
var digits = [0]
|
||||
for (var i = 0; i < source.length; ++i) {
|
||||
for (var j = 0, carry = source[i]; j < digits.length; ++j) {
|
||||
carry += digits[j] << 8
|
||||
digits[j] = carry % BASE.length
|
||||
carry = (carry / BASE.length) | 0
|
||||
}
|
||||
|
||||
while (carry > 0) {
|
||||
digits.push(carry % BASE.length)
|
||||
carry = (carry / BASE.length) | 0
|
||||
}
|
||||
}
|
||||
|
||||
var string = "";
|
||||
|
||||
for (var k = 0; source[k] === 0 && k < source.length - 1; ++k)
|
||||
string += BASE[0];
|
||||
|
||||
for (var q = digits.length - 1; q >= 0; --q)
|
||||
string += BASE[digits[q]];
|
||||
|
||||
return string
|
||||
}
|
||||
|
||||
class UUID {
|
||||
static v4()
|
||||
{
|
||||
let buffer = new Uint8Array(16);
|
||||
Crypto.randomBytes(buffer);
|
||||
|
||||
buffer[6] &= 0b00001111;
|
||||
buffer[6] |= 0b01000000;
|
||||
|
||||
buffer[8] &= 0b00111111;
|
||||
buffer[8] |= 0b10000000;
|
||||
|
||||
return encode(buffer);
|
||||
}
|
||||
|
||||
static short()
|
||||
{
|
||||
return this.v4();
|
||||
}
|
||||
}
|
||||
|
||||
export default UUID;
|
|
@ -0,0 +1,130 @@
|
|||
var O_READ = 'READ';
|
||||
var O_WRITE = 'WRITE';
|
||||
var O_CREATE = 'CREATE';
|
||||
var O_EXCLUSIVE = 'EXCLUSIVE';
|
||||
var O_TRUNCATE = 'TRUNCATE';
|
||||
var O_APPEND = 'APPEND';
|
||||
var XATTR_CREATE = 'CREATE';
|
||||
var XATTR_REPLACE = 'REPLACE';
|
||||
|
||||
module.exports = {
|
||||
FILE_SYSTEM_NAME: 'local',
|
||||
|
||||
FILE_STORE_NAME: 'files',
|
||||
|
||||
IDB_RO: 'readonly',
|
||||
IDB_RW: 'readwrite',
|
||||
|
||||
WSQL_VERSION: '1',
|
||||
WSQL_SIZE: 5 * 1024 * 1024,
|
||||
WSQL_DESC: 'FileSystem Storage',
|
||||
|
||||
NODE_TYPE_FILE: 'FILE',
|
||||
NODE_TYPE_DIRECTORY: 'DIRECTORY',
|
||||
NODE_TYPE_SYMBOLIC_LINK: 'SYMLINK',
|
||||
NODE_TYPE_META: 'META',
|
||||
|
||||
|
||||
DEFAULT_DIR_PERMISSIONS: 0x1ED, // 755
|
||||
DEFAULT_FILE_PERMISSIONS: 0x1A4, // 644
|
||||
FULL_READ_WRITE_EXEC_PERMISSIONS: 0x1FF, // 777
|
||||
READ_WRITE_PERMISSIONS: 0x1B6, /// 666
|
||||
|
||||
SYMLOOP_MAX: 10,
|
||||
|
||||
BINARY_MIME_TYPE: 'application/octet-stream',
|
||||
JSON_MIME_TYPE: 'application/json',
|
||||
|
||||
ROOT_DIRECTORY_NAME: '/', // basename(normalize(path))
|
||||
|
||||
// FS Mount Flags
|
||||
FS_FORMAT: 'FORMAT',
|
||||
FS_NOCTIME: 'NOCTIME',
|
||||
FS_NOMTIME: 'NOMTIME',
|
||||
FS_NODUPEIDCHECK: 'FS_NODUPEIDCHECK',
|
||||
|
||||
// FS File Open Flags
|
||||
O_READ: O_READ,
|
||||
O_WRITE: O_WRITE,
|
||||
O_CREATE: O_CREATE,
|
||||
O_EXCLUSIVE: O_EXCLUSIVE,
|
||||
O_TRUNCATE: O_TRUNCATE,
|
||||
O_APPEND: O_APPEND,
|
||||
|
||||
O_FLAGS: {
|
||||
'r': [O_READ],
|
||||
'r+': [O_READ, O_WRITE],
|
||||
'w': [O_WRITE, O_CREATE, O_TRUNCATE],
|
||||
'w+': [O_WRITE, O_READ, O_CREATE, O_TRUNCATE],
|
||||
'wx': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_TRUNCATE],
|
||||
'wx+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_TRUNCATE],
|
||||
'a': [O_WRITE, O_CREATE, O_APPEND],
|
||||
'a+': [O_WRITE, O_READ, O_CREATE, O_APPEND],
|
||||
'ax': [O_WRITE, O_CREATE, O_EXCLUSIVE, O_APPEND],
|
||||
'ax+': [O_WRITE, O_READ, O_CREATE, O_EXCLUSIVE, O_APPEND]
|
||||
},
|
||||
|
||||
XATTR_CREATE: XATTR_CREATE,
|
||||
XATTR_REPLACE: XATTR_REPLACE,
|
||||
|
||||
FS_READY: 'READY',
|
||||
FS_PENDING: 'PENDING',
|
||||
FS_ERROR: 'ERROR',
|
||||
|
||||
SUPER_NODE_ID: '00000000-0000-0000-0000-000000000000',
|
||||
|
||||
// Reserved File Descriptors for streams
|
||||
STDIN: 0,
|
||||
STDOUT: 1,
|
||||
STDERR: 2,
|
||||
FIRST_DESCRIPTOR: 3,
|
||||
|
||||
ENVIRONMENT: {
|
||||
TMP: '/tmp',
|
||||
PATH: ''
|
||||
},
|
||||
|
||||
// Duplicate Node's fs.constants
|
||||
fsConstants: {
|
||||
O_RDONLY: 0,
|
||||
O_WRONLY: 1,
|
||||
O_RDWR: 2,
|
||||
S_IFMT: 61440,
|
||||
S_IFREG: 32768,
|
||||
S_IFDIR: 16384,
|
||||
S_IFCHR: 8192,
|
||||
S_IFBLK: 24576,
|
||||
S_IFIFO: 4096,
|
||||
S_IFLNK: 40960,
|
||||
S_IFSOCK: 49152,
|
||||
O_CREAT: 512,
|
||||
O_EXCL: 2048,
|
||||
O_NOCTTY: 131072,
|
||||
O_TRUNC: 1024,
|
||||
O_APPEND: 8,
|
||||
O_DIRECTORY: 1048576,
|
||||
O_NOFOLLOW: 256,
|
||||
O_SYNC: 128,
|
||||
O_DSYNC: 4194304,
|
||||
O_SYMLINK: 2097152,
|
||||
O_NONBLOCK: 4,
|
||||
S_IRWXU: 448,
|
||||
S_IRUSR: 256,
|
||||
S_IWUSR: 128,
|
||||
S_IXUSR: 64,
|
||||
S_IRWXG: 56,
|
||||
S_IRGRP: 32,
|
||||
S_IWGRP: 16,
|
||||
S_IXGRP: 8,
|
||||
S_IRWXO: 7,
|
||||
S_IROTH: 4,
|
||||
S_IWOTH: 2,
|
||||
S_IXOTH: 1,
|
||||
F_OK: 0,
|
||||
R_OK: 4,
|
||||
W_OK: 2,
|
||||
X_OK: 1,
|
||||
UV_FS_COPYFILE_EXCL: 1,
|
||||
COPYFILE_EXCL: 1
|
||||
}
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
var NODE_TYPE_FILE = require('./constants.js').NODE_TYPE_FILE;
|
||||
|
||||
module.exports = function DirectoryEntry(id, type) {
|
||||
this.id = id;
|
||||
this.type = type || NODE_TYPE_FILE;
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
const Stats = require('./stats.js');
|
||||
|
||||
function Dirent(path, fileNode, devName) {
|
||||
this.constructor = Dirent;
|
||||
Stats.call(this, path, fileNode, devName);
|
||||
}
|
||||
|
||||
Dirent.prototype = Stats.prototype;
|
||||
|
||||
module.exports = Dirent;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue