mathjs
Version:
Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser and offers an integrated solution to work with numbers, big numbers, complex numbers, units, and matrices.
1,855 lines (1,627 loc) • 693 kB
JavaScript
/**
* math.js
* https://github.com/josdejong/mathjs
*
* Math.js is an extensive math library for JavaScript and Node.js,
* It features real and complex numbers, units, matrices, a large set of
* mathematical functions, and a flexible expression parser.
*
* @version 0.20.0
* @date 2014-04-16
*
* @license
* Copyright (C) 2013-2014 Jos de Jong <wjosdejong@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define(factory);
else if(typeof exports === 'object')
exports["mathjs"] = factory();
else
root["mathjs"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
var object = __webpack_require__(3);
/**
* math.js factory function.
*
* Usage:
*
* var math = mathjs();
* var math = mathjs(config);
*
* @param {Object} [config] Available configuration options:
* {String} matrix
* A string 'matrix' (default) or 'array'.
* {String} number
* A string 'number' (default) or 'bignumber'
* {Number} precision
* The number of significant digits for BigNumbers.
* Not applicable for Numbers.
*/
function mathjs (config) {
// simple test for ES5 support
if (typeof Object.create !== 'function') {
throw new Error('ES5 not supported by this JavaScript engine. ' +
'Please load the es5-shim and es5-sham library for compatibility.');
}
// create new namespace
var math = {};
// create configuration options. These are private
var _config = {
// type of default matrix output. Choose 'matrix' (default) or 'array'
matrix: 'matrix',
// type of default number output. Choose 'number' (default) or 'bignumber'
number: 'number',
// number of significant digits in BigNumbers
precision: 20,
// minimum relative difference between two compared values,
// used by all comparison functions
epsilon: 1e-14
};
/**
* Set configuration options for math.js, and get current options
* @param {Object} [options] Available options:
* {String} matrix
* A string 'matrix' (default) or 'array'.
* {String} number
* A string 'number' (default) or 'bignumber'
* {Number} precision
* The number of significant digits for BigNumbers.
* Not applicable for Numbers.
* @return {Object} Returns the current configuration
*/
math.config = function config (options) {
if (options) {
// merge options
object.deepExtend(_config, options);
if (options.precision) {
math.type.BigNumber.config({
precision: options.precision
});
}
// TODO: remove deprecated setting some day (deprecated since version 0.17.0)
if (options.number && options.number.defaultType) {
throw new Error('setting `number.defaultType` is deprecated. Use `number` instead.')
}
// TODO: remove deprecated setting some day (deprecated since version 0.17.0)
if (options.number && options.number.precision) {
throw new Error('setting `number.precision` is deprecated. Use `precision` instead.')
}
// TODO: remove deprecated setting some day (deprecated since version 0.17.0)
if (options.matrix && options.matrix.defaultType) {
throw new Error('setting `matrix.defaultType` is deprecated. Use `matrix` instead.')
}
// TODO: remove deprecated setting some day (deprecated since version 0.15.0)
if (options.matrix && options.matrix['default']) {
throw new Error('setting `matrix.default` is deprecated. Use `matrix` instead.')
}
// TODO: remove deprecated setting some day (deprecated since version 0.20.0)
if (options.decimals) {
throw new Error('setting `decimals` is deprecated. Use `precision` instead.')
}
}
// return a clone of the settings
return object.clone(_config);
};
// create a new BigNumber factory for this instance of math.js
var BigNumber = __webpack_require__(112).constructor();
// extend BigNumber with a function clone
if (typeof BigNumber.prototype.clone !== 'function') {
/**
* Clone a bignumber
* @return {BigNumber} clone
*/
BigNumber.prototype.clone = function clone () {
return new BigNumber(this);
};
}
// extend BigNumber with a function convert
if (typeof BigNumber.convert !== 'function') {
/**
* Try to convert a Number in to a BigNumber.
* If the number has 15 or mor significant digits, the Number cannot be
* converted to BigNumber and will return the original number.
* @param {Number} number
* @return {BigNumber | Number} bignumber
*/
BigNumber.convert = function convert(number) {
if (digits(number) > 15) {
return number;
}
else {
return new BigNumber(number);
}
};
}
else {
throw new Error('Cannot add function convert to BigNumber: function already exists');
}
// errors
math.error = __webpack_require__(4);
// types (Matrix, Complex, Unit, ...)
math.type = {};
math.type.Complex = __webpack_require__(5);
math.type.Range = __webpack_require__(6);
math.type.Index = __webpack_require__(7);
math.type.Matrix = __webpack_require__(8);
math.type.Unit = __webpack_require__(9);
math.type.Help = __webpack_require__(10);
math.type.BigNumber = BigNumber;
math.collection = __webpack_require__(11);
// expression (parse, Parser, nodes, docs)
math.expression = {};
math.expression.node = __webpack_require__(14);
math.expression.parse = __webpack_require__(12);
math.expression.Scope = function () {
throw new Error('Scope is deprecated. Use a regular Object instead');
};
math.expression.Parser = __webpack_require__(13);
math.expression.docs = __webpack_require__(15);
// expression parser
__webpack_require__(17)(math, _config);
__webpack_require__(18)(math, _config);
__webpack_require__(19)(math, _config);
__webpack_require__(20)(math, _config);
// functions - arithmetic
__webpack_require__(21)(math, _config);
__webpack_require__(22)(math, _config);
__webpack_require__(23)(math, _config);
__webpack_require__(24)(math, _config);
__webpack_require__(25)(math, _config);
__webpack_require__(26)(math, _config);
__webpack_require__(27)(math, _config);
__webpack_require__(28)(math, _config);
__webpack_require__(29)(math, _config);
__webpack_require__(30)(math, _config);
__webpack_require__(31)(math, _config);
__webpack_require__(32)(math, _config);
__webpack_require__(33)(math, _config);
__webpack_require__(34)(math, _config);
__webpack_require__(35)(math, _config);
__webpack_require__(36)(math, _config);
__webpack_require__(37)(math, _config);
__webpack_require__(38)(math, _config);
__webpack_require__(39)(math, _config);
__webpack_require__(40)(math, _config);
__webpack_require__(41)(math, _config);
__webpack_require__(42)(math, _config);
__webpack_require__(43)(math, _config);
__webpack_require__(44)(math, _config);
__webpack_require__(45)(math, _config);
__webpack_require__(46)(math, _config);
__webpack_require__(47)(math, _config);
__webpack_require__(48)(math, _config);
__webpack_require__(49)(math, _config);
__webpack_require__(50)(math, _config);
__webpack_require__(51)(math, _config);
__webpack_require__(52)(math, _config);
__webpack_require__(53)(math, _config);
// functions - complex
__webpack_require__(54)(math, _config);
__webpack_require__(55)(math, _config);
__webpack_require__(56)(math, _config);
__webpack_require__(57)(math, _config);
// functions - construction
__webpack_require__(58)(math, _config);
__webpack_require__(59)(math, _config);
__webpack_require__(60)(math, _config);
__webpack_require__(61)(math, _config);
__webpack_require__(62)(math, _config);
__webpack_require__(63)(math, _config);
__webpack_require__(64)(math, _config);
__webpack_require__(65)(math, _config);
__webpack_require__(66)(math, _config);
__webpack_require__(67)(math, _config);
// functions - matrix
__webpack_require__(68)(math, _config);
__webpack_require__(69)(math, _config);
__webpack_require__(70)(math, _config);
__webpack_require__(71)(math, _config);
__webpack_require__(72)(math, _config);
__webpack_require__(73)(math, _config);
__webpack_require__(74)(math, _config);
__webpack_require__(75)(math, _config);
__webpack_require__(76)(math, _config);
__webpack_require__(77)(math, _config);
__webpack_require__(78)(math, _config);
__webpack_require__(79)(math, _config);
__webpack_require__(80)(math, _config);
// functions - probability
__webpack_require__(81)(math, _config);
__webpack_require__(82)(math, _config);
__webpack_require__(83)(math, _config);
__webpack_require__(84)(math, _config);
// functions - statistics
__webpack_require__(85)(math, _config);
__webpack_require__(86)(math, _config);
__webpack_require__(87)(math, _config);
__webpack_require__(88)(math, _config);
__webpack_require__(89)(math, _config);
__webpack_require__(90)(math, _config);
__webpack_require__(91)(math, _config);
__webpack_require__(92)(math, _config);
// functions - trigonometry
__webpack_require__(93)(math, _config);
__webpack_require__(94)(math, _config);
__webpack_require__(95)(math, _config);
__webpack_require__(96)(math, _config);
__webpack_require__(97)(math, _config);
__webpack_require__(98)(math, _config);
__webpack_require__(99)(math, _config);
__webpack_require__(100)(math, _config);
__webpack_require__(101)(math, _config);
__webpack_require__(102)(math, _config);
// functions - units
__webpack_require__(103)(math, _config);
// functions - utils
__webpack_require__(104)(math, _config);
__webpack_require__(105)(math, _config);
__webpack_require__(106)(math, _config);
__webpack_require__(107)(math, _config);
__webpack_require__(108)(math, _config);
__webpack_require__(109)(math, _config);
__webpack_require__(110)(math, _config);
__webpack_require__(111)(math, _config);
// constants
__webpack_require__(2)(math, _config);
// selector (we initialize after all functions are loaded)
math.chaining = {};
math.chaining.Selector = __webpack_require__(16)(math, _config);
// apply provided configuration options
math.config(config);
// return the new instance
return math;
}
// return the mathjs factory
module.exports = mathjs;
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
module.exports = function (math) {
var Complex = __webpack_require__(5);
math.pi = Math.PI;
math.e = Math.E;
math.tau = Math.PI * 2;
math.i = new Complex(0, 1);
math['Infinity'] = Infinity;
math['NaN'] = NaN;
math['true'] = true;
math['false'] = false;
// uppercase constants (for compatibility with built-in Math)
math.E = Math.E;
math.LN2 = Math.LN2;
math.LN10 = Math.LN10;
math.LOG2E = Math.LOG2E;
math.LOG10E = Math.LOG10E;
math.PI = Math.PI;
math.SQRT1_2 = Math.SQRT1_2;
math.SQRT2 = Math.SQRT2;
};
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
/**
* Clone an object
*
* clone(x)
*
* Can clone any primitive type, array, and object.
* If x has a function clone, this function will be invoked to clone the object.
*
* @param {*} x
* @return {*} clone
*/
exports.clone = function clone(x) {
var type = typeof x;
// immutable primitive types
if (type === 'number' || type === 'string' || type === 'boolean' ||
x === null || x === undefined) {
return x;
}
// use clone function of the object when available
if (typeof x.clone === 'function') {
return x.clone();
}
// array
if (Array.isArray(x)) {
return x.map(function (value) {
return clone(value);
});
}
if (x instanceof Number) return new Number(x.valueOf());
if (x instanceof String) return new String(x.valueOf());
if (x instanceof Boolean) return new Boolean(x.valueOf());
if (x instanceof Date) return new Date(x.valueOf());
if (x instanceof RegExp) throw new TypeError('Cannot clone ' + x); // TODO: clone a RegExp
// object
var m = {};
for (var key in x) {
if (x.hasOwnProperty(key)) {
m[key] = clone(x[key]);
}
}
return m;
};
/**
* Extend object a with the properties of object b
* @param {Object} a
* @param {Object} b
* @return {Object} a
*/
exports.extend = function extend (a, b) {
for (var prop in b) {
if (b.hasOwnProperty(prop)) {
a[prop] = b[prop];
}
}
return a;
};
/**
* Deep extend an object a with the properties of object b
* @param {Object} a
* @param {Object} b
* @returns {Object}
*/
exports.deepExtend = function deepExtend (a, b) {
// TODO: add support for Arrays to deepExtend
if (Array.isArray(b)) {
throw new TypeError('Arrays are not supported by deepExtend');
}
for (var prop in b) {
if (b.hasOwnProperty(prop)) {
if (b[prop] && b[prop].constructor === Object) {
if (a[prop] === undefined) {
a[prop] = {};
}
if (a[prop].constructor === Object) {
deepExtend(a[prop], b[prop]);
}
else {
a[prop] = b[prop];
}
} else if (Array.isArray(b[prop])) {
throw new TypeError('Arrays are not supported by deepExtend');
} else {
a[prop] = b[prop];
}
}
}
return a;
};
/**
* Deep test equality of all fields in two pairs of arrays or objects.
* @param {Array | Object} a
* @param {Array | Object} b
* @returns {boolean}
*/
exports.deepEqual = function deepEqual (a, b) {
var prop, i, len;
if (Array.isArray(a)) {
if (!Array.isArray(b)) {
return false;
}
if (a.length != b.length) {
return false;
}
for (i = 0, len = a.length; i < len; i++) {
if (!exports.deepEqual(a[i], b[i])) {
return false;
}
}
return true;
}
else if (a instanceof Object) {
if (Array.isArray(b) || !(b instanceof Object)) {
return false;
}
for (prop in a) {
//noinspection JSUnfilteredForInLoop
if (!exports.deepEqual(a[prop], b[prop])) {
return false;
}
}
for (prop in b) {
//noinspection JSUnfilteredForInLoop
if (!exports.deepEqual(a[prop], b[prop])) {
return false;
}
}
return true;
}
else {
return (typeof a === typeof b) && (a == b);
}
};
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
exports.ArgumentsError = __webpack_require__(113);
exports.DimensionError = __webpack_require__(114);
exports.IndexError = __webpack_require__(115);
exports.UnsupportedTypeError = __webpack_require__(116);
// TODO: implement an InvalidValueError?
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
var util = __webpack_require__(117),
Unit = __webpack_require__(9),
number = util.number,
isNumber = util.number.isNumber,
isUnit = Unit.isUnit,
isString = util.string.isString;
/**
* @constructor Complex
*
* A complex value can be constructed in the following ways:
* var a = new Complex();
* var b = new Complex(re, im);
* var c = Complex.parse(str);
*
* Example usage:
* var a = new Complex(3, -4); // 3 - 4i
* a.re = 5; // a = 5 - 4i
* var i = a.im; // -4;
* var b = Complex.parse('2 + 6i'); // 2 + 6i
* var c = new Complex(); // 0 + 0i
* var d = math.add(a, b); // 5 + 2i
*
* @param {Number} re The real part of the complex value
* @param {Number} [im] The imaginary part of the complex value
*/
function Complex(re, im) {
if (!(this instanceof Complex)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
switch (arguments.length) {
case 0:
this.re = 0;
this.im = 0;
break;
case 1:
var arg = arguments[0];
if (typeof arg === 'object') {
if('re' in arg && 'im' in arg) {
var construct = new Complex(arg.re, arg.im); // pass on input validation
this.re = construct.re;
this.im = construct.im;
break;
} else if ('r' in arg && 'phi' in arg) {
var construct = Complex.fromPolar(arg.r, arg.phi);
this.re = construct.re;
this.im = construct.im;
break;
}
}
throw new SyntaxError('Object with the re and im or r and phi properties expected.');
case 2:
if (!isNumber(re) || !isNumber(im)) {
throw new TypeError('Two numbers expected in Complex constructor');
}
this.re = re;
this.im = im;
break;
default:
throw new SyntaxError('One, two or three arguments expected in Complex constructor');
}
}
/**
* Test whether value is a Complex value
* @param {*} value
* @return {Boolean} isComplex
*/
Complex.isComplex = function isComplex(value) {
return (value instanceof Complex);
};
// private variables and functions for the parser
var text, index, c;
function skipWhitespace() {
while (c == ' ' || c == '\t') {
next();
}
}
function isDigitDot (c) {
return ((c >= '0' && c <= '9') || c == '.');
}
function isDigit (c) {
return ((c >= '0' && c <= '9'));
}
function next() {
index++;
c = text.charAt(index);
}
function revert(oldIndex) {
index = oldIndex;
c = text.charAt(index);
}
function parseNumber () {
var number = '';
var oldIndex;
oldIndex = index;
if (c == '+') {
next();
}
else if (c == '-') {
number += c;
next();
}
if (!isDigitDot(c)) {
// a + or - must be followed by a digit
revert(oldIndex);
return null;
}
// get number, can have a single dot
if (c == '.') {
number += c;
next();
if (!isDigit(c)) {
// this is no legal number, it is just a dot
revert(oldIndex);
return null;
}
}
else {
while (isDigit(c)) {
number += c;
next();
}
if (c == '.') {
number += c;
next();
}
}
while (isDigit(c)) {
number += c;
next();
}
// check for exponential notation like "2.3e-4" or "1.23e50"
if (c == 'E' || c == 'e') {
number += c;
next();
if (c == '+' || c == '-') {
number += c;
next();
}
// Scientific notation MUST be followed by an exponent
if (!isDigit(c)) {
// this is no legal number, exponent is missing.
revert(oldIndex);
return null;
}
while (isDigit(c)) {
number += c;
next();
}
}
return number;
}
function parseComplex () {
// check for 'i', '-i', '+i'
var cnext = text.charAt(index + 1);
if (c == 'I' || c == 'i') {
next();
return '1';
}
else if ((c == '+' || c == '-') && (cnext == 'I' || cnext == 'i')) {
var number = (c == '+') ? '1' : '-1';
next();
next();
return number;
}
return null;
}
/**
* Parse a complex number from a string. For example Complex.parse("2 + 3i")
* will return a Complex value where re = 2, im = 3.
* Returns null if provided string does not contain a valid complex number.
* @param {String} str
* @returns {Complex | null} complex
*/
Complex.parse = function parse (str) {
text = str;
index = -1;
c = '';
if (!isString(text)) {
return null;
}
next();
skipWhitespace();
var first = parseNumber();
if (first) {
if (c == 'I' || c == 'i') {
// pure imaginary number
next();
skipWhitespace();
if (c) {
// garbage at the end. not good.
return null;
}
return new Complex(0, Number(first));
}
else {
// complex and real part
skipWhitespace();
var separator = c;
if (separator != '+' && separator != '-') {
// pure real number
skipWhitespace();
if (c) {
// garbage at the end. not good.
return null;
}
return new Complex(Number(first), 0);
}
else {
// complex and real part
next();
skipWhitespace();
var second = parseNumber();
if (second) {
if (c != 'I' && c != 'i') {
// 'i' missing at the end of the complex number
return null;
}
next();
}
else {
second = parseComplex();
if (!second) {
// imaginary number missing after separator
return null;
}
}
if (separator == '-') {
if (second[0] == '-') {
second = '+' + second.substring(1);
}
else {
second = '-' + second;
}
}
next();
skipWhitespace();
if (c) {
// garbage at the end. not good.
return null;
}
return new Complex(Number(first), Number(second));
}
}
}
else {
// check for 'i', '-i', '+i'
first = parseComplex();
if (first) {
skipWhitespace();
if (c) {
// garbage at the end. not good.
return null;
}
return new Complex(0, Number(first));
}
}
return null;
};
/**
* Create a complex number from polar coordinates
*
* Usage:
*
* Complex.fromPolar(r: Number, phi: Number) : Complex
* Complex.fromPolar({r: Number, phi: Number}) : Complex
*
* @param {*} args...
* @return {Complex}
*/
Complex.fromPolar = function fromPolar(args) {
switch (arguments.length) {
case 1:
var arg = arguments[0];
if(typeof arg === 'object') {
return Complex.fromPolar(arg.r, arg.phi);
}
throw new TypeError('Input has to be an object with r and phi keys.');
case 2:
var r = arguments[0],
phi = arguments[1];
if(isNumber(r)) {
if (isUnit(phi) && phi.hasBase(Unit.BASE_UNITS.ANGLE)) {
// convert unit to a number in radians
phi = phi.toNumber('rad');
}
if(isNumber(phi)) {
return new Complex(r * Math.cos(phi), r * Math.sin(phi));
}
throw new TypeError('Phi is not a number nor an angle unit.');
} else {
throw new TypeError('Radius r is not a number.');
}
default:
throw new SyntaxError('Wrong number of arguments in function fromPolar');
}
};
/*
* Return the value of the complex number in polar notation
* The angle phi will be set in the interval of [-pi, pi].
* @return {{r: number, phi: number}} Returns and object with properties r and phi.
*/
Complex.prototype.toPolar = function() {
return {
r: Math.sqrt(this.re * this.re + this.im * this.im),
phi: Math.atan2(this.im, this.re)
};
};
/**
* Create a copy of the complex value
* @return {Complex} clone
*/
Complex.prototype.clone = function clone () {
return new Complex(this.re, this.im);
};
/**
* Test whether this complex number equals an other complex value.
* Two complex numbers are equal when both their real and imaginary parts
* are equal.
* @param {Complex} other
* @return {boolean} isEqual
*/
Complex.prototype.equals = function equals (other) {
return (this.re === other.re) && (this.im === other.im);
};
/**
* Get a string representation of the complex number,
* with optional formatting options.
* @param {Object | Number | Function} [options] Formatting options. See
* lib/util/number:format for a
* description of the available
* options.
* @return {String} str
*/
Complex.prototype.format = function format (options) {
var str = '',
strRe = number.format(this.re, options),
strIm = number.format(this.im, options);
if (this.im == 0) {
// real value
str = strRe;
}
else if (this.re == 0) {
// purely complex value
if (this.im == 1) {
str = 'i';
}
else if (this.im == -1) {
str = '-i';
}
else {
str = strIm + 'i';
}
}
else {
// complex value
if (this.im > 0) {
if (this.im == 1) {
str = strRe + ' + i';
}
else {
str = strRe + ' + ' + strIm + 'i';
}
}
else {
if (this.im == -1) {
str = strRe + ' - i';
}
else {
str = strRe + ' - ' + strIm.substring(1) + 'i';
}
}
}
return str;
};
/**
* Get a string representation of the complex number.
* @return {String} str
*/
Complex.prototype.toString = function toString () {
return this.format();
};
// exports
module.exports = Complex;
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
var util = __webpack_require__(117),
number = util.number,
string = util.string,
array = util.array;
/**
* @constructor Range
* Create a range. A range has a start, step, and end, and contains functions
* to iterate over the range.
*
* A range can be constructed as:
* var range = new Range(start, end);
* var range = new Range(start, end, step);
*
* To get the result of the range:
* range.forEach(function (x) {
* console.log(x);
* });
* range.map(function (x) {
* return math.sin(x);
* });
* range.toArray();
*
* Example usage:
* var c = new Range(2, 6); // 2:1:5
* c.toArray(); // [2, 3, 4, 5]
* var d = new Range(2, -3, -1); // 2:-1:-2
* d.toArray(); // [2, 1, 0, -1, -2]
*
* @param {Number} start included lower bound
* @param {Number} end excluded upper bound
* @param {Number} [step] step size, default value is 1
*/
function Range(start, end, step) {
if (!(this instanceof Range)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
if (start != null && !number.isNumber(start)) {
throw new TypeError('Parameter start must be a number');
}
if (end != null && !number.isNumber(end)) {
throw new TypeError('Parameter end must be a number');
}
if (step != null && !number.isNumber(step)) {
throw new TypeError('Parameter step must be a number');
}
this.start = (start != null) ? parseFloat(start) : 0;
this.end = (end != null) ? parseFloat(end) : 0;
this.step = (step != null) ? parseFloat(step) : 1;
}
/**
* Parse a string into a range,
* The string contains the start, optional step, and end, separated by a colon.
* If the string does not contain a valid range, null is returned.
* For example str='0:2:11'.
* @param {String} str
* @return {Range | null} range
*/
Range.parse = function parse (str) {
if (!string.isString(str)) {
return null;
}
var args = str.split(':');
var nums = args.map(function (arg) {
return parseFloat(arg);
});
var invalid = nums.some(function (num) {
return isNaN(num);
});
if(invalid) {
return null;
}
switch (nums.length) {
case 2: return new Range(nums[0], nums[1]);
case 3: return new Range(nums[0], nums[2], nums[1]);
default: return null;
}
};
/**
* Create a clone of the range
* @return {Range} clone
*/
Range.prototype.clone = function clone() {
return new Range(this.start, this.end, this.step);
};
/**
* Test whether an object is a Range
* @param {*} object
* @return {Boolean} isRange
*/
Range.isRange = function isRange(object) {
return (object instanceof Range);
};
/**
* Retrieve the size of the range.
* Returns an array containing one number, the number of elements in the range.
* @returns {Number[]} size
*/
Range.prototype.size = function size() {
var len = 0,
start = this.start,
step = this.step,
end = this.end,
diff = end - start;
if (number.sign(step) == number.sign(diff)) {
len = Math.ceil((diff) / step);
}
else if (diff == 0) {
len = 0;
}
if (isNaN(len)) {
len = 0;
}
return [len];
};
/**
* Calculate the minimum value in the range
* @return {Number | undefined} min
*/
Range.prototype.min = function min () {
var size = this.size()[0];
if (size > 0) {
if (this.step > 0) {
// positive step
return this.start;
}
else {
// negative step
return this.start + (size - 1) * this.step;
}
}
else {
return undefined;
}
};
/**
* Calculate the maximum value in the range
* @return {Number | undefined} max
*/
Range.prototype.max = function max () {
var size = this.size()[0];
if (size > 0) {
if (this.step > 0) {
// positive step
return this.start + (size - 1) * this.step;
}
else {
// negative step
return this.start;
}
}
else {
return undefined;
}
};
/**
* Execute a callback function for each value in the range.
* @param {function} callback The callback method is invoked with three
* parameters: the value of the element, the index
* of the element, and the Matrix being traversed.
*/
Range.prototype.forEach = function forEach(callback) {
var x = this.start;
var step = this.step;
var end = this.end;
var i = 0;
if (step > 0) {
while (x < end) {
callback(x, i, this);
x += step;
i++;
}
}
else if (step < 0) {
while (x > end) {
callback(x, i, this);
x += step;
i++;
}
}
};
/**
* Execute a callback function for each value in the Range, and return the
* results as an array
* @param {function} callback The callback method is invoked with three
* parameters: the value of the element, the index
* of the element, and the Matrix being traversed.
* @returns {Array} array
*/
Range.prototype.map = function map(callback) {
var array = [];
this.forEach(function (value, index, obj) {
array[index] = callback(value, index, obj);
});
return array;
};
/**
* Create an Array with a copy of the Ranges data
* @returns {Array} array
*/
Range.prototype.toArray = function toArray() {
var array = [];
this.forEach(function (value, index) {
array[index] = value;
});
return array;
};
/**
* Get the primitive value of the Range, a one dimensional array
* @returns {Array} array
*/
Range.prototype.valueOf = function valueOf() {
// TODO: implement a caching mechanism for range.valueOf()
return this.toArray();
};
/**
* Get a string representation of the range, with optional formatting options.
* Output is formatted as 'start:step:end', for example '2:6' or '0:0.2:11'
* @param {Object | Number | Function} [options] Formatting options. See
* lib/util/number:format for a
* description of the available
* options.
* @returns {String} str
*/
Range.prototype.format = function format(options) {
var str = number.format(this.start, options);
if (this.step != 1) {
str += ':' + number.format(this.step, options);
}
str += ':' + number.format(this.end, options);
return str;
};
/**
* Get a string representation of the range.
* @returns {String}
*/
Range.prototype.toString = function toString() {
return this.format();
};
// exports
module.exports = Range;
/***/ },
/* 7 */
/***/ function(module, exports, __webpack_require__) {
var util = __webpack_require__(117),
Range = __webpack_require__(6),
number = util.number,
isNumber = number.isNumber,
isInteger = number.isInteger,
isArray = Array.isArray,
validateIndex = util.array.validateIndex;
/**
* @Constructor Index
* Create an index. An Index can store ranges having start, step, and end
* for multiple dimensions.
* Matrix.get, Matrix.set, and math.subset accept an Index as input.
*
* Usage:
* var index = new Index(range1, range2, ...);
*
* Where each range can be any of:
* An array [start, end]
* An array [start, end, step]
* A number
* An instance of Range
*
* The parameters start, end, and step must be integer numbers.
*
* @param {...*} ranges
*/
function Index(ranges) {
if (!(this instanceof Index)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
this._ranges = [];
for (var i = 0, ii = arguments.length; i < ii; i++) {
var arg = arguments[i];
if (arg instanceof Range) {
this._ranges.push(arg);
}
else {
if (isArray(arg)) {
this._ranges.push(_createRange(arg));
}
else if (isNumber(arg)) {
this._ranges.push(_createRange([arg, arg + 1]));
}
// TODO: implement support for wildcard '*'
else {
throw new TypeError('Ranges must be an Array, Number, or Range');
}
}
}
}
/**
* Parse an argument into a range and validate the range
* @param {Array} arg An array with [start: Number, end: Number] and
* optional a third element step:Number
* @return {Range} range
* @private
*/
function _createRange(arg) {
// TODO: make function _createRange simpler/faster
// test whether all arguments are integers
var num = arg.length;
for (var i = 0; i < num; i++) {
if (!isNumber(arg[i]) || !isInteger(arg[i])) {
throw new TypeError('Index parameters must be integer numbers');
}
}
switch (arg.length) {
case 2:
return new Range(arg[0], arg[1]); // start, end
case 3:
return new Range(arg[0], arg[1], arg[2]); // start, end, step
default:
// TODO: improve error message
throw new SyntaxError('Wrong number of arguments in Index (2 or 3 expected)');
}
}
/**
* Create a clone of the index
* @return {Index} clone
*/
Index.prototype.clone = function clone () {
var index = new Index();
index._ranges = util.object.clone(this._ranges);
return index;
};
/**
* Test whether an object is an Index
* @param {*} object
* @return {Boolean} isIndex
*/
Index.isIndex = function isIndex(object) {
return (object instanceof Index);
};
/**
* Create an index from an array with ranges/numbers
* @param {Array.<Array | Number>} ranges
* @return {Index} index
* @private
*/
Index.create = function create(ranges) {
var index = new Index();
Index.apply(index, ranges);
return index;
};
/**
* Retrieve the size of the index, the number of elements for each dimension.
* @returns {Number[]} size
*/
Index.prototype.size = function size () {
var size = [];
for (var i = 0, ii = this._ranges.length; i < ii; i++) {
var range = this._ranges[i];
size[i] = range.size()[0];
}
return size;
};
/**
* Get the maximum value for each of the indexes ranges.
* @returns {Number[]} max
*/
Index.prototype.max = function max () {
var values = [];
for (var i = 0, ii = this._ranges.length; i < ii; i++) {
var range = this._ranges[i];
values[i] = range.max();
}
return values;
};
/**
* Get the minimum value for each of the indexes ranges.
* @returns {Number[]} min
*/
Index.prototype.min = function min () {
var values = [];
for (var i = 0, ii = this._ranges.length; i < ii; i++) {
var range = this._ranges[i];
values[i] = range.min();
}
return values;
};
/**
* Loop over each of the ranges of the index
* @param {function} callback Called for each range with a Range as first
* argument, the dimension as second, and the
* index object as third.
*/
Index.prototype.forEach = function forEach(callback) {
for (var i = 0, ii = this._ranges.length; i < ii; i++) {
callback(this._ranges[i], i, this);
}
};
/**
* Retrieve the range for a given dimension number from the index
* @param {Number} dim Number of the dimension
* @returns {Range | null} range
*/
Index.prototype.range = function range (dim) {
return this._ranges[dim] || null;
};
/**
* Test whether this index contains only a single value
* @return {boolean} isScalar
*/
Index.prototype.isScalar = function isScalar () {
var size = this.size();
for (var i = 0, ii = size.length; i < ii; i++) {
if (size[i] !== 1) {
return false;
}
}
return true;
};
/**
* Expand the Index into an array.
* For example new Index([0,3], [2,7]) returns [[0,1,2], [2,3,4,5,6]]
* @returns {Array} array
*/
Index.prototype.toArray = function toArray() {
var array = [];
for (var i = 0, ii = this._ranges.length; i < ii; i++) {
var range = this._ranges[i],
row = [],
x = range.start,
end = range.end,
step = range.step;
if (step > 0) {
while (x < end) {
row.push(x);
x += step;
}
}
else if (step < 0) {
while (x > end) {
row.push(x);
x += step;
}
}
array.push(row);
}
return array;
};
/**
* Get the primitive value of the Index, a two dimensional array.
* Equivalent to Index.toArray().
* @returns {Array} array
*/
Index.prototype.valueOf = Index.prototype.toArray;
/**
* Get the string representation of the index, for example '[2:6]' or '[0:2:10, 4:7]'
* @returns {String} str
*/
Index.prototype.toString = function () {
var strings = [];
for (var i = 0, ii = this._ranges.length; i < ii; i++) {
var range = this._ranges[i];
var str = number.format(range.start);
if (range.step != 1) {
str += ':' + number.format(range.step);
}
str += ':' + number.format(range.end);
strings.push(str);
}
return '[' + strings.join(', ') + ']';
};
// exports
module.exports = Index;
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
var util = __webpack_require__(117),
DimensionError = __webpack_require__(114),
Index = __webpack_require__(7),
number = util.number,
string = util.string,
array = util.array,
object = util.object,
isArray = Array.isArray,
validateIndex = array.validateIndex;
/**
* @constructor Matrix
*
* A Matrix is a wrapper around an Array. A matrix can hold a multi dimensional
* array. A matrix can be constructed as:
* var matrix = new Matrix(data)
*
* Matrix contains the functions to resize, get and set values, get the size,
* clone the matrix and to convert the matrix to a vector, array, or scalar.
* Furthermore, one can iterate over the matrix using map and forEach.
* The internal Array of the Matrix can be accessed using the function valueOf.
*
* Example usage:
* var matrix = new Matrix([[1, 2], [3, 4]);
* matix.size(); // [2, 2]
* matrix.resize([3, 2], 5);
* matrix.valueOf(); // [[1, 2], [3, 4], [5, 5]]
* matrix.subset([1,2]) // 3 (indexes are zero-based)
*
* @param {Array | Matrix} [data] A multi dimensional array
*/
function Matrix(data) {
if (!(this instanceof Matrix)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
if (data instanceof Matrix) {
// clone data from a Matrix
this._data = data.clone()._data;
}
else if (isArray(data)) {
// use array
// replace nested Matrices with Arrays
this._data = preprocess(data);
}
else if (data != null) {
// unsupported type
throw new TypeError('Unsupported type of data (' + util.types.type(data) + ')');
}
else {
// nothing provided
this._data = [];
}
// verify the size of the array
this._size = array.size(this._data);
}
/**
* Test whether an object is a Matrix
* @param {*} object
* @return {Boolean} isMatrix
*/
Matrix.isMatrix = function isMatrix(object) {
return (object instanceof Matrix);
};
/**
* Get a subset of the matrix, or replace a subset of the matrix.
*
* Usage:
* var subset = matrix.subset(index) // retrieve subset
* var value = matrix.subset(index, replacement) // replace subset
*
* @param {Index} index
* @param {Array | Matrix | *} [replacement]
* @param {*} [defaultValue] Default value, filled in on new entries when
* the matrix is resized. If not provided,
* new matrix elements will be left undefined.
*/
Matrix.prototype.subset = function subset(index, replacement, defaultValue) {
switch (arguments.length) {
case 1:
return _get(this, index);
// intentional fall through
case 2:
case 3:
return _set(this, index, replacement, defaultValue);
default:
throw new SyntaxError('Wrong number of arguments');
}
};
/**
* Get a single element from the matrix.
* @param {Number[]} index Zero-based index
* @return {*} value
*/
Matrix.prototype.get = function get(index) {
if (!isArray(index)) {
throw new TypeError('Array expected');
}
if (index.length != this._size.length) {
throw new DimensionError(index.length, this._size.length);
}
var data = this._data;
for (var i = 0, ii = index.length; i < ii; i++) {
var index_i = index[i];
validateIndex(index_i, data.length);
data = data[index_i];
}
return object.clone(data);
};
/**
* Replace a single element in the matrix.
* @param {Number[]} index Zero-based index
* @param {*} value
* @param {*} [defaultValue] Default value, filled in on new entries when
* the matrix is resized. If not provided,
* new matrix elements will be left undefined.
* @return {Matrix} self
*/
Matrix.prototype.set = function set (index, value, defaultValue) {
var i, ii;
// validate input type and dimensions
if (!isArray(index)) {
throw new Error('Array expected');
}
if (index.length < this._size.length) {
throw new DimensionError(index.length, this._size.length, '<');
}
// enlarge matrix when needed
var size = index.map(function (i) {
return i + 1;
});
_fit(this, size, defaultValue);
// traverse over the dimensions
var data = this._data;
for (i = 0, ii = index.length - 1; i < ii; i++) {
var index_i = index[i];
validateIndex(index_i, data.length);
data = data[index_i];
}
// set new value
index_i = index[index.length - 1];
validateIndex(index_i, data.length);
data[index_i] = value;
return this;
};
/**
* Get a submatrix of this matrix
* @param {Matrix} matrix
* @param {Index} index Zero-based index
* @private
*/
function _get (matrix, index) {
if (!(index instanceof Index)) {
throw new TypeError('Invalid index');
}
var isScalar = index.isScalar();
if (isScalar) {
// return a scalar
return matrix.get(index.min());
}
else {
// validate dimensions
var size = index.size();
if (size.length != matrix._size.length) {
throw new DimensionError(size.length, matrix._size.length);
}
// retrieve submatrix
var submatrix = new Matrix(_getSubmatrix(matrix._data, index, size.length, 0));
// TODO: more efficient when creating an empty matrix and setting _data and _size manually
// squeeze matrix output
while (isArray(submatrix._data) && submatrix._data.length == 1) {
submatrix._data = submatrix._data[0];
submatrix._size.shift();
}
return submatrix;
}
}
/**
* Recursively get a submatrix of a multi dimensional matrix.
* Index is not checked for correct number of dimensions.
* @param {Array} data
* @param {Index} index
* @param {number} dims Total number of dimensions
* @param {number} dim Current dimension
* @return {Array} submatrix
* @private
*/
function _getSubmatrix (data, index, dims, dim) {
var last = (dim == dims - 1);
var range = index.range(dim);
if (last) {
return range.map(function (i) {
validateIndex(i, data.length);
return data[i];
});
}
else {
return range.map(function (i) {
validateIndex(i, data.length);
var child = data[i];
return _getSubmatrix(child, index, dims, dim + 1);
});
}
}
/**
* Replace a submatrix in this matrix
* Indexes are zero-based.
* @param {Matrix} matrix
* @param {Index} index
* @param {Matrix | Array | *} submatrix
* @param {*} [defaultValue] Default value, filled in on new entries when
* the matrix is resized. If not provided,
* new matrix elements will be left undefined.
* @return {Matrix} matrix
* @private
*/
function _set (matrix, index, submatrix, defaultValue) {
if (!(index instanceof Index)) {
throw new TypeError('Invalid index');
}
// get index size and check whether the index contains a single value
var iSize = index.size(),
isScalar = index.isScalar();
// calculate the size of the submatrix, and convert it into an Array if needed
var sSize;
if (submatrix instanceof Matrix) {
sSize = submatrix.size();
submatrix = submatrix.valueOf();
}
else {
sSize = array.size(submatrix);
}
if (isScalar) {
// set a scalar
// check whether submatrix is a scalar
if (sSize.length != 0) {
throw new TypeError('Scalar expected');
}
matrix.set(index.min(), submatrix, defaultValue);
}
else {
// set a submatrix
// validate dimensions
if (iSize.length < matrix._size.length) {
throw new DimensionError(iSize.length, matrix._size.length, '<');
}
// unsqueeze the submatrix when needed
for (var i = 0, ii = iSize.length - sSize.length; i < ii; i++) {
submatrix = [submatrix];
sSize.unshift(1);
}
// check whether the size of the submatrix matches the index size
if (!object.deepEqual(iSize, sSize)) {
throw new DimensionError(iSize, sSize);
}
// enlarge matrix when needed
var size = index.max().map(function (i) {
return i + 1;
});
_fit(matrix, size, defaultValue);
// insert the sub matrix
var dims = iSiz